sacn
Version:
💡 🎠Send and Receive sACN data (DMX over IP)
156 lines (155 loc) • 5.77 kB
JavaScript
;
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MergingReceiver = void 0;
const perf_hooks_1 = require("perf_hooks");
const receiver_1 = require("./receiver");
class MergingReceiver extends receiver_1.Receiver {
constructor(_a) {
var { mode = 'HTP', timeout = 5000 } = _a, props = __rest(_a, ["mode", "timeout"]);
super(props);
this.data = new Map();
this.mode = mode;
this.timeout = timeout;
super.on('packet', (packet) => {
const data = this.prepareData(packet);
const mergedData = MergingReceiver[this.mode](data);
this.handleChanges(data, mergedData);
});
}
prepareData(packet) {
const currentTime = perf_hooks_1.performance.now();
const universe = parseInt(packet.universe.toString(36), 10);
const cid = packet.cid.toString();
// universe is unknown
if (!this.data.has(universe)) {
this.data.set(universe, {
referenceData: {},
servers: new Map(),
});
this.emit('newUniverse', {
universe,
firstPacket: packet,
});
}
const universeData = this.data.get(universe);
if (!universeData) {
throw new Error('[sACN] Internal Error: universeData is undefined');
}
// sender is unknown for this universe
if (!universeData.servers.has(cid)) {
this.emit('senderConnect', {
cid: packet.cid,
universe,
firstPacket: packet,
});
}
// register current package
universeData.servers.set(cid, {
packet,
timestamp: currentTime,
});
// check whether sender disconnects
setTimeout(() => {
var _a;
if (((_a = universeData.servers.get(cid)) === null || _a === void 0 ? void 0 : _a.timestamp) === currentTime) {
universeData.servers.delete(cid);
this.emit('senderDisconnect', {
cid: packet.cid,
universe,
lastPacket: packet,
});
}
}, this.timeout);
// detect which source has the highest per-universe priority
let maximumPriority = 0;
for (const [, { packet: thisPacket }] of universeData.servers) {
if (thisPacket.priority > maximumPriority &&
thisPacket.universe === packet.universe) {
maximumPriority = thisPacket.priority;
}
}
return {
universe,
maximumPriority,
universeData,
};
}
handleChanges(data, mergedData) {
const { referenceData } = data.universeData;
// only changes
let changesDetected = false;
for (let ch = 1; ch <= 512; ch += 1) {
if (referenceData[ch] !== mergedData[ch]) {
changesDetected = true;
const event = {
universe: data.universe,
address: ch,
newValue: mergedData[ch],
oldValue: referenceData[ch],
};
super.emit('changedValue', event);
}
}
if (changesDetected) {
this.data.get(data.universe).referenceData = mergedData;
const event = {
universe: data.universe,
payload: mergedData,
};
super.emit('changed', event);
}
}
static HTP(data) {
var _a;
const mergedData = {};
for (const [, { packet }] of data.universeData.servers) {
if (packet.priority === data.maximumPriority &&
packet.universe === data.universe) {
for (let ch = 1; ch <= 512; ch += 1) {
const newValue = packet.payload[ch] || 0;
if (((_a = mergedData[ch]) !== null && _a !== void 0 ? _a : 0) <= newValue) {
mergedData[ch] = newValue;
}
}
}
}
return mergedData;
}
/**
* LTP can only operate per-universe, not per-channel. There is no
* situation where LTP-per-channel would be useful.
*
* Therefore, this function just returns the packet with the highest
* priority and the latest timestamp.
*/
static LTP(data) {
let maximumTimestamp = -Infinity;
for (const [, { packet, timestamp }] of data.universeData.servers) {
if (packet.priority === data.maximumPriority &&
packet.universe === data.universe &&
timestamp > maximumTimestamp) {
maximumTimestamp = timestamp;
}
}
for (const [, { packet, timestamp }] of data.universeData.servers) {
if (packet.priority === data.maximumPriority &&
packet.universe === data.universe &&
timestamp === maximumTimestamp) {
return packet.payload;
}
}
throw new Error('Internal error');
}
}
exports.MergingReceiver = MergingReceiver;