UNPKG

sacn

Version:

💡 🎭 Send and Receive sACN data (DMX over IP)

146 lines (145 loc) • 6.88 kB
"use strict"; /** * this is the low-level implementation of the E1.31 (sACN) protocol */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Packet = void 0; const assert_1 = __importDefault(require("assert")); const util_1 = require("./util"); const constants_1 = require("./constants"); /** * This constructs a sACN Packet, either from an * existing `Buffer` or from `Options`. */ class Packet { constructor(input, sourceAddress) { this.sourceAddress = sourceAddress; /* eslint-disable lines-between-class-members */ /* root layer */ this.root_vector = constants_1.RootVector.DATA; this.preambleSize = 0x0010; // =16 (unit16 hence the redundant 00s) this.postambleSize = 0; this.acnPid = constants_1.ACN_PID; /* framing layer */ this.frame_vector = constants_1.FrameVector.DATA; /* DMP layer */ this.dmp_vector = constants_1.DmpVector.DATA; this.type = 0xa1; // = 61 this.firstAddress = 0; this.addressIncrement = 1; this.startCode = 0; /* eslint-enable lines-between-class-members */ this.useRawDmxValues = false; if (!input) throw new Error('Buffer packet instantiated with no value'); if (input instanceof Buffer) { const buf = input; // If a buffer is supplied, ascertain that the packet implements ACN // correctly, and that is it a data packet. Also asceratain that the // UDP overhead is valid. Then fill up the class values. /* root layer */ assert_1.default.strictEqual(buf.readUInt32BE(18), this.root_vector); this.root_fl = buf.readUInt16BE(16); assert_1.default.deepStrictEqual(buf.slice(4, 16), this.acnPid); assert_1.default.strictEqual(buf.readUInt16BE(0), this.preambleSize); assert_1.default.strictEqual(buf.readUInt16BE(2), this.postambleSize); this.cid = buf.slice(22, 38); /* frame layer */ assert_1.default.strictEqual(buf.readUInt32BE(40), this.frame_vector); this.frame_fl = buf.readUInt16BE(38); this.options = buf.readUInt8(112); this.sequence = buf.readUInt8(111); // eslint-disable-next-line no-control-regex, unicorn/no-hex-escape this.sourceName = buf.toString('ascii', 44, 107).replace(/\x00/g, ''); this.priority = buf.readUInt8(108); this.syncUniverse = buf.readUInt16BE(109); this.universe = buf.readUInt16BE(113); /* DMP layer */ assert_1.default.strictEqual(buf.readUInt8(117), this.dmp_vector); this.dmp_fl = buf.readUInt16BE(115); assert_1.default.strictEqual(buf.readUInt8(118), this.type); assert_1.default.strictEqual(buf.readUInt16BE(119), this.firstAddress); assert_1.default.strictEqual(buf.readUInt16BE(121), this.addressIncrement); this.propertyValueCount = buf.readUInt16BE(123); assert_1.default.strictEqual(buf.readUInt8(125), this.startCode); this.privatePayload = buf.slice(126); } else { // if input is not a buffer const options = input; // set constants this.preambleSize = 0x0010; this.root_fl = 0x726e; this.frame_fl = 0x7258; this.dmp_fl = 0x720b; this.syncUniverse = 0; // we as a sender don't implement this this.options = 0; // TODO: can we just set to 0? // set properties this.privatePayload = options.payload; this.sourceName = options.sourceName || 'sACN nodejs'; this.priority = options.priority || 100; this.sequence = options.sequence; this.universe = options.universe; this.cid = options.cid || constants_1.DEFAULT_CID; // set computed properties this.propertyValueCount = 0x0201; // "Indicates 1+ the number of slots in packet" // We set the highest possible value (1+512) so that channels with zero values are // treated as deliberately 0 (cf. undefined) if (options.useRawDmxValues) { this.useRawDmxValues = true; } } } get payload() { return this.privatePayload instanceof Buffer ? (0, util_1.objectify)(this.privatePayload) : this.privatePayload; } get payloadAsBuffer() { return this.privatePayload instanceof Buffer ? this.privatePayload : null; } get buffer() { const sourceNameBuf = Buffer.from(this.sourceName.padEnd(64, '\0')); // eslint-disable-next-line unicorn/prefer-spread -- more readabile this way const n = [].concat( /* root layer */ (0, util_1.bit)(16, this.preambleSize), // 0,1 = preable size (0, util_1.bit)(16, this.postambleSize), // 2,3 = postamble size [...this.acnPid], (0, util_1.bit)(16, this.root_fl), // 16,17 = root fl (0, util_1.bit)(32, this.root_vector), // 18,19,20,21 = Root_vector [...this.cid], // 22-37 = cid /* framing layer */ (0, util_1.bit)(16, this.frame_fl), // 38,39 = frame fl (0, util_1.bit)(32, this.frame_vector), // 40,41,42,43 = frame vector [...sourceNameBuf], // 44 - 107 = sourceName (0, util_1.bit)(8, this.priority), // 108 = priority (8bit) (0, util_1.bit)(16, this.syncUniverse), // 109,110 = syncUniverse (0, util_1.bit)(8, this.sequence), // 111 = sequence (0, util_1.bit)(8, this.options), // 112 = options (0, util_1.bit)(16, this.universe), // 113,114 = universe /* DMP layer */ (0, util_1.bit)(16, this.dmp_fl), // 115,116 = dmp_fl (0, util_1.bit)(8, this.dmp_vector), // 117 = dmp vector (0, util_1.bit)(8, this.type), // 118 = type (0, util_1.bit)(16, this.firstAddress), // 119,120 = first adddress (0, util_1.bit)(16, this.addressIncrement), // 121,122 = addressIncrement (0, util_1.bit)(16, this.propertyValueCount), // 123,124 = propertyValueCount (0, util_1.bit)(8, this.startCode), // 125 = startCode (0, util_1.empty)(512)); for (const ch in this.payload) { if (+ch >= 1 && +ch <= 512) { if (this.useRawDmxValues) { n[125 + +ch] = (0, util_1.inRange)(this.payload[ch]); } else { n[125 + +ch] = (0, util_1.inRange)(this.payload[ch] * 2.55); } } } return Buffer.from(n); } } exports.Packet = Packet;