1 |
|
2 |
|
3 |
|
4 |
|
5 | import * as assert from 'assert';
|
6 | import { objectify, inRange, empty, bit } from './util';
|
7 | import {
|
8 | RootVector,
|
9 | ACN_PID,
|
10 | FrameVector,
|
11 | DmpVector,
|
12 | DEFAULT_CID,
|
13 | } from './constants';
|
14 |
|
15 |
|
16 |
|
17 | export interface Options {
|
18 | universe: Packet['universe'];
|
19 | payload: Packet['payload'];
|
20 | sequence: Packet['sequence'];
|
21 | sourceName?: Packet['sourceName'];
|
22 | priority?: Packet['priority'];
|
23 | cid?: Packet['cid'];
|
24 | }
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | export class Packet {
|
31 |
|
32 | private readonly root_vector = RootVector.DATA;
|
33 | private readonly root_fl: number;
|
34 | private readonly preambleSize = 0x0010;
|
35 | private readonly postambleSize = 0;
|
36 | private readonly acnPid = ACN_PID;
|
37 | public readonly cid: Buffer;
|
38 |
|
39 |
|
40 | private readonly frame_vector = FrameVector.DATA;
|
41 | private readonly frame_fl: number;
|
42 | public readonly options: number;
|
43 | public readonly sequence: number;
|
44 | public readonly sourceName: string;
|
45 | public readonly priority: number;
|
46 | public readonly syncUniverse: number;
|
47 | public readonly universe: number;
|
48 |
|
49 |
|
50 | private readonly dmp_vector = DmpVector.DATA;
|
51 | private readonly dmp_fl: number;
|
52 | private readonly type = 0xa1;
|
53 | private readonly firstAddress = 0;
|
54 | private readonly addressIncrement = 1;
|
55 | public readonly propertyValueCount: number;
|
56 | private readonly startCode = 0;
|
57 | private readonly privatePayload: Buffer | Record<number, number>;
|
58 |
|
59 | public constructor(
|
60 | input: Buffer | Options,
|
61 | public readonly sourceAddress?: string,
|
62 | ) {
|
63 | if (!input) throw new Error('Buffer packet instantiated with no value');
|
64 | if (input instanceof Buffer) {
|
65 | const buf = input;
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 | assert.strictEqual(buf.readUInt32BE(18), this.root_vector);
|
72 | this.root_fl = buf.readUInt16BE(16);
|
73 | assert.deepStrictEqual(buf.slice(4, 16), this.acnPid);
|
74 | assert.strictEqual(buf.readUInt16BE(0), this.preambleSize);
|
75 | assert.strictEqual(buf.readUInt16BE(2), this.postambleSize);
|
76 | this.cid = buf.slice(22, 38);
|
77 |
|
78 |
|
79 | assert.strictEqual(buf.readUInt32BE(40), this.frame_vector);
|
80 | this.frame_fl = buf.readUInt16BE(38);
|
81 | this.options = buf.readUInt8(112);
|
82 | this.sequence = buf.readUInt8(111);
|
83 | this.sourceName = buf.toString('ascii', 44, 107).replace(/\x00/g, '');
|
84 | this.priority = buf.readUInt8(108);
|
85 | this.syncUniverse = buf.readUInt16BE(109);
|
86 | this.universe = buf.readUInt16BE(113);
|
87 |
|
88 |
|
89 | assert.strictEqual(buf.readUInt8(117), this.dmp_vector);
|
90 | this.dmp_fl = buf.readUInt16BE(115);
|
91 | assert.strictEqual(buf.readUInt8(118), this.type);
|
92 | assert.strictEqual(buf.readUInt16BE(119), this.firstAddress);
|
93 | assert.strictEqual(buf.readUInt16BE(121), this.addressIncrement);
|
94 | this.propertyValueCount = buf.readUInt16BE(123);
|
95 | assert.strictEqual(buf.readUInt8(125), this.startCode);
|
96 | this.privatePayload = buf.slice(126);
|
97 | } else {
|
98 |
|
99 | const options = input;
|
100 |
|
101 |
|
102 | this.preambleSize = 0x0010;
|
103 | this.root_fl = 0x726e;
|
104 | this.frame_fl = 0x7258;
|
105 | this.dmp_fl = 0x720b;
|
106 | this.syncUniverse = 0;
|
107 | this.options = 0;
|
108 |
|
109 |
|
110 | this.privatePayload = options.payload;
|
111 | this.sourceName = options.sourceName || 'sACN nodejs';
|
112 | this.priority = options.priority || 100;
|
113 | this.sequence = options.sequence;
|
114 | this.universe = options.universe;
|
115 | this.cid = options.cid || DEFAULT_CID;
|
116 |
|
117 |
|
118 | this.propertyValueCount = 0x0201;
|
119 |
|
120 |
|
121 | }
|
122 | }
|
123 |
|
124 | public get payload(): Record<number, number> {
|
125 | return this.privatePayload instanceof Buffer
|
126 | ? objectify(this.privatePayload)
|
127 | : this.privatePayload;
|
128 | }
|
129 |
|
130 | public get payloadAsBuffer(): Buffer {
|
131 | return this.privatePayload instanceof Buffer ? this.privatePayload : null;
|
132 | }
|
133 |
|
134 | public get buffer(): Buffer {
|
135 | const sourceNameBuf = Buffer.from(this.sourceName.padEnd(64, '\0'));
|
136 | const n: number[] = [].concat(
|
137 |
|
138 | bit(16, this.preambleSize),
|
139 | bit(16, this.postambleSize),
|
140 | [...this.acnPid],
|
141 | bit(16, this.root_fl),
|
142 | bit(32, this.root_vector),
|
143 | [...this.cid],
|
144 |
|
145 |
|
146 | bit(16, this.frame_fl),
|
147 | bit(32, this.frame_vector),
|
148 | [...sourceNameBuf],
|
149 | bit(8, this.priority),
|
150 | bit(16, this.syncUniverse),
|
151 | bit(8, this.sequence),
|
152 | bit(8, this.options),
|
153 | bit(16, this.universe),
|
154 |
|
155 |
|
156 | bit(16, this.dmp_fl),
|
157 | bit(8, this.dmp_vector),
|
158 | bit(8, this.type),
|
159 | bit(16, this.firstAddress),
|
160 | bit(16, this.addressIncrement),
|
161 | bit(16, this.propertyValueCount),
|
162 | bit(8, this.startCode),
|
163 | empty(512),
|
164 | );
|
165 |
|
166 | for (const ch in this.payload) {
|
167 | if (+ch >= 1 && +ch <= 512) {
|
168 | n[125 + +ch] = inRange(this.payload[ch] * 2.55);
|
169 | }
|
170 | }
|
171 |
|
172 | return Buffer.from(n);
|
173 | }
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 | }
|