UNPKG

knxnetjs

Version:

A TypeScript library for KNXnet/IP communication

178 lines 6.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KNXNetRoutingImpl = void 0; const events_1 = require("events"); const dgram_1 = require("dgram"); const constants_1 = require("../constants"); const frames_1 = require("../frames"); class KNXNetRoutingImpl extends events_1.EventEmitter { constructor(multicastAddress, port) { super(); this.isConnected = false; this.busyCounter = 0; this.lastBusyTime = 0; this.options = { multicastAddress: multicastAddress || constants_1.KNX_CONSTANTS.DEFAULT_MULTICAST_ADDRESS, port: port || constants_1.KNX_CONSTANTS.DEFAULT_PORT, ttl: 16, }; } async open() { if (this.isConnected) { return; } return new Promise((resolve, reject) => { this.socket = (0, dgram_1.createSocket)({ type: "udp4", reuseAddr: true }); this.socket.on("message", (msg) => { this.handleIncomingMessage(msg); }); this.socket.on("error", (err) => { this.emit("error", err); reject(err); }); this.socket.bind(this.options.port, () => { if (!this.socket) return; this.socket.addMembership(this.options.multicastAddress); this.socket.setMulticastTTL(this.options.ttl); this.isConnected = true; resolve(); }); }); } async send(frame) { if (!this.isConnected || !this.socket) { throw new Error("Not connected"); } const routingFrame = this.createRoutingIndicationFrame(frame.toBuffer()); return new Promise((resolve, reject) => { this.socket.send(routingFrame, this.options.port, this.options.multicastAddress, (err) => { if (err) { reject(err); } else { resolve(); } }); }); } async close() { if (this.socket && this.isConnected) { this.socket.dropMembership(this.options.multicastAddress); this.socket.close(); this.isConnected = false; } } async writeProperty(interfaceObject, objectInstance, propertyId, numberOfElements, startIndex, data) { throw new Error("writeProperty is not implemented for KNX routing connections"); } async readProperty(interfaceObject, objectInstance, propertyId, numberOfElements, startIndex) { throw new Error("readProperty is not implemented for KNX routing connections"); } on(event, listener) { return super.on(event, listener); } handleIncomingMessage(msg) { try { const frame = this.parseKNXNetFrame(msg); switch (frame.serviceType) { case constants_1.KNX_CONSTANTS.SERVICE_TYPES.ROUTING_INDICATION: this.handleRoutingIndication(frame); break; case constants_1.KNX_CONSTANTS.SERVICE_TYPES.ROUTING_LOST_MESSAGE: this.handleRoutingLostMessage(frame); break; case constants_1.KNX_CONSTANTS.SERVICE_TYPES.ROUTING_BUSY: this.handleRoutingBusy(frame); break; } } catch (error) { this.emit("error", error); } } parseKNXNetFrame(buffer) { const frame = frames_1.KNXnetIPFrame.fromBuffer(buffer); const data = frame.payload; switch (frame.service) { case constants_1.KNX_CONSTANTS.SERVICE_TYPES.ROUTING_INDICATION: return { serviceType: frame.service, cemiFrame: data, }; case constants_1.KNX_CONSTANTS.SERVICE_TYPES.ROUTING_LOST_MESSAGE: return { serviceType: frame.service, deviceState: data.readUInt8(1), numberOfLostMessages: data.readUInt16BE(2), }; case constants_1.KNX_CONSTANTS.SERVICE_TYPES.ROUTING_BUSY: return { serviceType: frame.service, deviceState: data.readUInt8(1), waitTime: data.readUInt16BE(2), controlField: data.readUInt16BE(4), }; default: throw new Error(`Unknown service type: 0x${frame.service.toString(16)}`); } } createRoutingIndicationFrame(cemiFrame) { const frame = new frames_1.KNXnetIPFrame(constants_1.KNX_CONSTANTS.SERVICE_TYPES.ROUTING_INDICATION, cemiFrame); return frame.toBuffer(); } handleRoutingIndication(frame) { if (frames_1.CEMIFrame.isValidBuffer(frame.cemiFrame)) { const cemiFrame = frames_1.CEMIFrame.fromBuffer(frame.cemiFrame); const routingCounter = cemiFrame.routingCounter; if (routingCounter === constants_1.KNX_CONSTANTS.ROUTING_COUNTER.DONT_ROUTE) { return; } this.emit("recv", cemiFrame); } else { // Fallback to old extraction method for invalid frames const routingCounter = this.extractRoutingCounter(frame.cemiFrame); if (routingCounter === constants_1.KNX_CONSTANTS.ROUTING_COUNTER.DONT_ROUTE) { return; } // Try to create a CEMIFrame from invalid buffer, emit error if it fails try { const cemiFrame = frames_1.CEMIFrame.fromBuffer(frame.cemiFrame); this.emit("recv", cemiFrame); } catch (error) { this.emit("error", new Error(`Invalid cEMI frame received: ${error.message}`)); } } } handleRoutingLostMessage(frame) { this.emit("lostMessage", { deviceState: frame.deviceState, numberOfLostMessages: frame.numberOfLostMessages, }); } handleRoutingBusy(frame) { const now = Date.now(); if (now - this.lastBusyTime > constants_1.KNX_CONSTANTS.FLOW_CONTROL.BUSY_DETECTION_THRESHOLD) { this.busyCounter = 0; } this.busyCounter++; this.lastBusyTime = now; this.emit("busy", { waitTime: frame.waitTime, controlField: frame.controlField, busyCounter: this.busyCounter, }); } extractRoutingCounter(cemiFrame) { if (cemiFrame.length < 8) { return constants_1.KNX_CONSTANTS.ROUTING_COUNTER.DONT_ROUTE; } const ctrl1 = cemiFrame.readUInt8(1); return (ctrl1 >> 4) & 0x07; } } exports.KNXNetRoutingImpl = KNXNetRoutingImpl; //# sourceMappingURL=routing.js.map