knxultimate
Advanced tools
Comparing version 2.3.5 to 3.0.0-beta.0
@@ -65,2 +65,3 @@ /// <reference types="node" /> | ||
localSocketAddress?: string; | ||
KNXQueueSendIntervalMilliseconds?: number; | ||
} & KNXLoggerOptions; | ||
@@ -95,2 +96,6 @@ export declare function getDecodedKeyring(): string; | ||
physAddr: KNXAddress; | ||
private limiter; | ||
private commandQueue; | ||
private exitProcessingKNXQueueLoop; | ||
private currentItemHandledByTheQueue; | ||
constructor(options: KNXClientOptions); | ||
@@ -104,3 +109,5 @@ get channelID(): number; | ||
private clearAllTimers; | ||
send(knxPacket: KNXPacket): void; | ||
processKnxPacketQueueItem: (_knxPacket: KNXPacket) => Promise<void>; | ||
handleKNXQueue: () => Promise<void>; | ||
send(_knxPacket: KNXPacket, _ACK: KNXTunnelingRequest, _priority: boolean, _expectedSeqNumberForACK: number): void; | ||
write(dstAddress: KNXAddress | string, data: any, dptid: string | number): void; | ||
@@ -123,2 +130,3 @@ respond(dstAddress: KNXAddress | string, data: Buffer, dptid: string | number): void; | ||
private getSeqNumber; | ||
private getCurrentItemHandledByTheQueue; | ||
private incSeqNumber; | ||
@@ -125,0 +133,0 @@ private setTimerWaitingForACK; |
@@ -51,2 +51,3 @@ "use strict"; | ||
const utils_1 = require("./utils"); | ||
const limiter_1 = require("limiter"); | ||
var ConncetionState; | ||
@@ -87,3 +88,3 @@ (function (ConncetionState) { | ||
ipPort: 3671, | ||
hostProtocol: 'TunnelUDP', | ||
hostProtocol: 'Multicast', | ||
isSecureKNXEnabled: false, | ||
@@ -96,2 +97,3 @@ suppress_ack_ldatareq: false, | ||
jKNXSecureKeyring: {}, | ||
KNXQueueSendIntervalMilliseconds: 25, | ||
}; | ||
@@ -114,4 +116,90 @@ function getDecodedKeyring() { | ||
constructor(options) { | ||
var _a; | ||
super(); | ||
this.commandQueue = []; | ||
this.processKnxPacketQueueItem = async (_knxPacket) => { | ||
var _a, _b, _c, _d, _e; | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.debug(`KNXClient: processKnxPacketQueueItem: Processing queued KNX. commandQueue.length: ${this.commandQueue.length} ${_knxPacket.header.service_type}`); | ||
if (_knxPacket instanceof KNXConnectRequest_1.default) { | ||
(_b = this.sysLogger) === null || _b === void 0 ? void 0 : _b.debug(`Sending KNX packet: ${_knxPacket.constructor.name} Host:${this._peerHost}:${this._peerPort}`); | ||
} | ||
if (_knxPacket instanceof KNXTunnelingRequest_1.default || | ||
_knxPacket instanceof KNXRoutingIndication_1.default) { | ||
let sTPCI = ''; | ||
if (_knxPacket.cEMIMessage.npdu.isGroupRead) { | ||
sTPCI = 'Read'; | ||
} | ||
if (_knxPacket.cEMIMessage.npdu.isGroupResponse) { | ||
sTPCI = 'Response'; | ||
} | ||
if (_knxPacket.cEMIMessage.npdu.isGroupWrite) { | ||
sTPCI = 'Write'; | ||
} | ||
let sDebugString = `Data: ${JSON.stringify(_knxPacket.cEMIMessage.npdu)}`; | ||
sDebugString += ` srcAddress: ${_knxPacket.cEMIMessage.srcAddress.toString()}`; | ||
sDebugString += ` dstAddress: ${_knxPacket.cEMIMessage.dstAddress.toString()}`; | ||
(_c = this.sysLogger) === null || _c === void 0 ? void 0 : _c.debug(`Sending KNX packet: ${_knxPacket.constructor.name} ${sDebugString} Host:${this._peerHost}:${this._peerPort} channelID:${_knxPacket.channelID} seqCounter:${_knxPacket.seqCounter} Dest:${_knxPacket.cEMIMessage.dstAddress.toString()}`, ` Data:${_knxPacket.cEMIMessage.npdu.dataValue.toString('hex')} TPCI:${sTPCI}`); | ||
} | ||
if (this._options.hostProtocol === 'Multicast' || | ||
this._options.hostProtocol === 'TunnelUDP') { | ||
try { | ||
; | ||
this._clientSocket.send(_knxPacket.toBuffer(), this._peerPort, this._peerHost, (err) => { | ||
var _a; | ||
if (err) { | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`Sending KNX packet: Send UDP sending error: ${err.message}`); | ||
this.emit(KNXClientEvents.error, err); | ||
} | ||
}); | ||
} | ||
catch (error) { | ||
(_d = this.sysLogger) === null || _d === void 0 ? void 0 : _d.error(`Sending KNX packet: Send UDP Catch error: ${error.message} ${typeof _knxPacket} seqCounter:${_knxPacket.seqCounter || ''}`); | ||
this.emit(KNXClientEvents.error, error); | ||
} | ||
} | ||
else { | ||
try { | ||
; | ||
this._clientSocket.write(_knxPacket.toBuffer(), (err) => { | ||
var _a; | ||
if (err) { | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`Sending KNX packet: Send TCP sending error: ${err.message}` || | ||
'Undef error'); | ||
this.emit(KNXClientEvents.error, err); | ||
} | ||
}); | ||
} | ||
catch (error) { | ||
(_e = this.sysLogger) === null || _e === void 0 ? void 0 : _e.error(`Sending KNX packet: Send TCP Catch error: ${error.message}` || | ||
'Undef error'); | ||
this.emit(KNXClientEvents.error, error); | ||
} | ||
} | ||
}; | ||
this.handleKNXQueue = async () => { | ||
var _a, _b, _c, _d; | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.debug(`KNXClient: handleKNXQueue: Start Processing queued KNX.`); | ||
do { | ||
const remainingRequests = await this.limiter.removeTokens(1); | ||
if (this.commandQueue.length > 0 && this._clearToSend) { | ||
(_b = this.sysLogger) === null || _b === void 0 ? void 0 : _b.debug(`\n\nKNXClient: START.`); | ||
const item = this.commandQueue.pop(); | ||
this.currentItemHandledByTheQueue = item; | ||
if (item.ACK !== undefined) { | ||
await this.processKnxPacketQueueItem(item.knxPacket); | ||
this.setTimerWaitingForACK(item.ACK); | ||
} | ||
else { | ||
await this.processKnxPacketQueueItem(item.knxPacket); | ||
} | ||
(_c = this.sysLogger) === null || _c === void 0 ? void 0 : _c.debug(`KNXClient: END.`); | ||
} | ||
if (this.exitProcessingKNXQueueLoop) | ||
return; | ||
} while (this.exitProcessingKNXQueueLoop === false); | ||
(_d = this.sysLogger) === null || _d === void 0 ? void 0 : _d.debug(`KNXClient: handleKNXQueue: Stop Processing queued KNX.`); | ||
}; | ||
this.timers = new Map(); | ||
this.commandQueue = []; | ||
this.exitProcessingKNXQueueLoop = false; | ||
if (options === undefined) { | ||
@@ -141,2 +229,6 @@ options = optionsDefaults; | ||
this.jKNXSecureKeyring = this._options.jKNXSecureKeyring; | ||
this.limiter = new limiter_1.RateLimiter({ | ||
tokensPerInterval: 1, | ||
interval: this._options.KNXQueueSendIntervalMilliseconds, | ||
}); | ||
this.on('error', (error) => { }); | ||
@@ -150,3 +242,3 @@ if (typeof this._options.physAddr === 'string') { | ||
catch (error) { | ||
this.sysLogger.error(`ipAddressHelper.getLocalAddress:${error.message}`); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`ipAddressHelper.getLocalAddress:${error.message}`); | ||
throw error; | ||
@@ -160,2 +252,3 @@ } | ||
this._clientSocket.bind({ port: null, address: this._options.localIPAddress }, () => { | ||
var _a; | ||
try { | ||
@@ -169,3 +262,3 @@ ; | ||
catch (error) { | ||
this.sysLogger.error(`UDP: Error setting SetTTL ${error.message}` || ''); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`UDP: Error setting SetTTL ${error.message}` || ''); | ||
} | ||
@@ -195,2 +288,3 @@ }); | ||
this._clientSocket.bind(this._peerPort, () => { | ||
var _a, _b; | ||
const client = this._clientSocket; | ||
@@ -202,3 +296,3 @@ try { | ||
catch (error) { | ||
this.sysLogger.error(`Multicast: Error setting SetTTL ${error.message}` || | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`Multicast: Error setting SetTTL ${error.message}` || | ||
''); | ||
@@ -210,3 +304,3 @@ } | ||
catch (err) { | ||
this.sysLogger.error('Multicast: cannot add membership (%s)', err); | ||
(_b = this.sysLogger) === null || _b === void 0 ? void 0 : _b.error('Multicast: cannot add membership (%s)', err); | ||
this.emit(KNXClientEvents.error, err); | ||
@@ -224,2 +318,3 @@ } | ||
getKNXDataBuffer(data, dptid) { | ||
var _a; | ||
if (typeof dptid === 'number') { | ||
@@ -232,3 +327,3 @@ dptid = dptid.toString(); | ||
const isSixBits = adpu.bitlength <= 6; | ||
this.sysLogger.trace(`isSixBits:${isSixBits} Includes (should be = isSixBits):${[ | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.trace(`isSixBits:${isSixBits} Includes (should be = isSixBits):${[ | ||
1, 2, 3, 5, 9, 10, 11, 14, 18, | ||
@@ -259,6 +354,7 @@ ].includes(iDatapointType)} ADPU BitLength:${adpu.bitlength}`); | ||
setTimer(type, cb, delay) { | ||
var _a; | ||
if (this.timers.has(type)) { | ||
clearTimeout(this.timers.get(type)); | ||
this.timers.delete(type); | ||
this.sysLogger.warn(`Timer "${type}" was already running`); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.warn(`Timer "${type}" was already running`); | ||
} | ||
@@ -283,56 +379,17 @@ this.timers.set(type, setTimeout(() => { | ||
} | ||
send(knxPacket) { | ||
if (knxPacket instanceof KNXConnectRequest_1.default) { | ||
this.sysLogger.debug(`Sending KNX packet: ${knxPacket.constructor.name} Host:${this._peerHost}:${this._peerPort}`); | ||
send(_knxPacket, _ACK, _priority, _expectedSeqNumberForACK) { | ||
var _a; | ||
const toBeAdded = { | ||
knxPacket: _knxPacket, | ||
ACK: _ACK, | ||
expectedSeqNumberForACK: _expectedSeqNumberForACK, | ||
}; | ||
if (_priority) { | ||
this.commandQueue.push(toBeAdded); | ||
this._clearToSend = true; | ||
} | ||
if (knxPacket instanceof KNXTunnelingRequest_1.default || | ||
knxPacket instanceof KNXRoutingIndication_1.default) { | ||
let sTPCI = ''; | ||
if (knxPacket.cEMIMessage.npdu.isGroupRead) { | ||
sTPCI = 'Read'; | ||
} | ||
if (knxPacket.cEMIMessage.npdu.isGroupResponse) { | ||
sTPCI = 'Response'; | ||
} | ||
if (knxPacket.cEMIMessage.npdu.isGroupWrite) { | ||
sTPCI = 'Write'; | ||
} | ||
let sDebugString = `Data: ${JSON.stringify(knxPacket.cEMIMessage.npdu)}`; | ||
sDebugString += ` srcAddress: ${knxPacket.cEMIMessage.srcAddress.toString()}`; | ||
sDebugString += ` dstAddress: ${knxPacket.cEMIMessage.dstAddress.toString()}`; | ||
this.sysLogger.debug(`Sending KNX packet: ${knxPacket.constructor.name} ${sDebugString} Host:${this._peerHost}:${this._peerPort} channelID:${knxPacket.channelID} seqCounter:${knxPacket.seqCounter} Dest:${knxPacket.cEMIMessage.dstAddress.toString()}`, ` Data:${knxPacket.cEMIMessage.npdu.dataValue.toString('hex')} TPCI:${sTPCI}`); | ||
} | ||
if (this._options.hostProtocol === 'Multicast' || | ||
this._options.hostProtocol === 'TunnelUDP') { | ||
try { | ||
; | ||
this._clientSocket.send(knxPacket.toBuffer(), this._peerPort, this._peerHost, (err) => { | ||
if (err) { | ||
this.sysLogger.error(`Sending KNX packet: Send UDP sending error: ${err.message}`); | ||
this.emit(KNXClientEvents.error, err); | ||
} | ||
}); | ||
} | ||
catch (error) { | ||
this.sysLogger.error(`Sending KNX packet: Send UDP Catch error: ${error.message} ${typeof knxPacket} seqCounter:${knxPacket.seqCounter || ''}`); | ||
this.emit(KNXClientEvents.error, error); | ||
} | ||
} | ||
else { | ||
try { | ||
; | ||
this._clientSocket.write(knxPacket.toBuffer(), (err) => { | ||
if (err) { | ||
this.sysLogger.error(`Sending KNX packet: Send TCP sending error: ${err.message}` || | ||
'Undef error'); | ||
this.emit(KNXClientEvents.error, err); | ||
} | ||
}); | ||
} | ||
catch (error) { | ||
this.sysLogger.error(`Sending KNX packet: Send TCP Catch error: ${error.message}` || | ||
'Undef error'); | ||
this.emit(KNXClientEvents.error, error); | ||
} | ||
this.commandQueue.unshift(toBeAdded); | ||
} | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.debug(`KNXClient: ADDED TELEGRAM TO COMMANDQUEUE. Len: ${this.commandQueue.length}`); | ||
} | ||
@@ -354,3 +411,3 @@ write(dstAddress, data, dptid) { | ||
const knxPacketRequest = KNXProtocol_1.default.newKNXRoutingIndication(cEMIMessage); | ||
this.send(knxPacketRequest); | ||
this.send(knxPacketRequest, undefined, false, this.getSeqNumber()); | ||
} | ||
@@ -367,4 +424,3 @@ else { | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest); | ||
this.send(knxPacketRequest); | ||
this.send(knxPacketRequest, knxPacketRequest, false, this.getSeqNumber()); | ||
if (this._options.localEchoInTunneling) | ||
@@ -389,3 +445,3 @@ this.emit(KNXClientEvents.indication, knxPacketRequest, true); | ||
const knxPacketRequest = KNXProtocol_1.default.newKNXRoutingIndication(cEMIMessage); | ||
this.send(knxPacketRequest); | ||
this.send(knxPacketRequest, undefined, false, this.getSeqNumber()); | ||
} | ||
@@ -402,4 +458,3 @@ else { | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest); | ||
this.send(knxPacketRequest); | ||
this.send(knxPacketRequest, knxPacketRequest, false, this.getSeqNumber()); | ||
if (this._options.localEchoInTunneling) | ||
@@ -423,3 +478,3 @@ this.emit(KNXClientEvents.indication, knxPacketRequest, true); | ||
const knxPacketRequest = KNXProtocol_1.default.newKNXRoutingIndication(cEMIMessage); | ||
this.send(knxPacketRequest); | ||
this.send(knxPacketRequest, undefined, false, this.getSeqNumber()); | ||
} | ||
@@ -436,4 +491,3 @@ else { | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest); | ||
this.send(knxPacketRequest); | ||
this.send(knxPacketRequest, knxPacketRequest, false, this.getSeqNumber()); | ||
if (this._options.localEchoInTunneling) | ||
@@ -444,6 +498,7 @@ this.emit(KNXClientEvents.indication, knxPacketRequest, true); | ||
writeRaw(dstAddress, rawDataBuffer, bitlength) { | ||
var _a; | ||
if (this._connectionState !== ConncetionState.CONNECTED) | ||
throw new Error('The socket is not connected. Unable to access the KNX BUS'); | ||
if (!Buffer.isBuffer(rawDataBuffer)) { | ||
this.sysLogger.error('KNXClient: writeRaw: Value must be a buffer! '); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error('KNXClient: writeRaw: Value must be a buffer! '); | ||
return; | ||
@@ -474,3 +529,3 @@ } | ||
const knxPacketRequest = KNXProtocol_1.default.newKNXRoutingIndication(cEMIMessage); | ||
this.send(knxPacketRequest); | ||
this.send(knxPacketRequest, undefined, false, this.getSeqNumber()); | ||
} | ||
@@ -489,4 +544,3 @@ else { | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest); | ||
this.send(knxPacketRequest); | ||
this.send(knxPacketRequest, knxPacketRequest, false, this.getSeqNumber()); | ||
if (this._options.localEchoInTunneling) | ||
@@ -551,2 +605,3 @@ this.emit(KNXClientEvents.indication, knxPacketRequest, true); | ||
} | ||
this.handleKNXQueue(); | ||
this._connectionState = ConncetionState.CONNECTING; | ||
@@ -585,7 +640,8 @@ this._numFailedTelegramACK = 0; | ||
async closeSocket() { | ||
this.exitProcessingKNXQueueLoop = true; | ||
return new Promise((resolve) => { | ||
var _a; | ||
if (!this._clientSocket) | ||
return; | ||
const cb = () => { | ||
this._clientSocket = null; | ||
resolve(); | ||
@@ -604,3 +660,3 @@ }; | ||
catch (error) { | ||
this.sysLogger.error(`KNXClient: into async closeSocket(): ${error.stack}`); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`KNXClient: into async closeSocket(): ${error.stack}`); | ||
resolve(); | ||
@@ -611,2 +667,4 @@ } | ||
async Disconnect() { | ||
var _a; | ||
this.exitProcessingKNXQueueLoop = true; | ||
if (this._clientSocket === null) { | ||
@@ -621,3 +679,3 @@ throw new Error('No client socket defined'); | ||
if (this._channelID === null) { | ||
this.sysLogger.debug(`KNXClient: into Disconnect(), channel id is not defined so skip disconnect packet and close socket`); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.debug(`KNXClient: into Disconnect(), channel id is not defined so skip disconnect packet and close socket`); | ||
await this.closeSocket(); | ||
@@ -637,3 +695,4 @@ return; | ||
async setDisconnected(_sReason = '') { | ||
this.sysLogger.debug(`KNXClient: called _setDisconnected ${this._options.ipAddr}:${this._options.ipPort} ${_sReason}`); | ||
var _a; | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.debug(`KNXClient: called _setDisconnected ${this._options.ipAddr}:${this._options.ipPort} ${_sReason}`); | ||
this._connectionState = ConncetionState.DISCONNECTED; | ||
@@ -656,3 +715,4 @@ this.clearAllTimers(); | ||
this.setTimer(KNXTimer.CONNECTION_STATE, () => { | ||
this.sysLogger.error(`KNXClient: getConnectionStatus Timeout ${this._heartbeatFailures} out of ${this.max_HeartbeatFailures}`); | ||
var _a; | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`KNXClient: getConnectionStatus Timeout ${this._heartbeatFailures} out of ${this.max_HeartbeatFailures}`); | ||
this._heartbeatFailures++; | ||
@@ -674,2 +734,5 @@ if (this._heartbeatFailures >= this.max_HeartbeatFailures) { | ||
} | ||
getCurrentItemHandledByTheQueue() { | ||
return this.currentItemHandledByTheQueue.expectedSeqNumberForACK; | ||
} | ||
incSeqNumber() { | ||
@@ -687,2 +750,3 @@ this._clientTunnelSeqNumber++; | ||
this.setTimer(KNXTimer.ACK, () => { | ||
var _a, _b; | ||
this._numFailedTelegramACK += 1; | ||
@@ -694,8 +758,9 @@ if (this._numFailedTelegramACK > 2) { | ||
this.emit(KNXClientEvents.error, timeoutErr); | ||
this.sysLogger.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. ABORT sending datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. ABORT sending datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
} | ||
else { | ||
this.setTimerWaitingForACK(knxTunnelingRequest); | ||
this.send(knxTunnelingRequest); | ||
this.sysLogger.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. Retransmit datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
(_b = this.sysLogger) === null || _b === void 0 ? void 0 : _b.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. Retransmit datagram with seqNumber ${this.currentItemHandledByTheQueue | ||
.expectedSeqNumberForACK} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
this.send(knxTunnelingRequest, knxTunnelingRequest, true, this.currentItemHandledByTheQueue | ||
.expectedSeqNumberForACK); | ||
} | ||
@@ -705,2 +770,3 @@ }, KNXConstants_1.KNX_CONSTANTS.TUNNELING_REQUEST_TIMEOUT * 1000); | ||
processInboundMessage(msg, rinfo) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q; | ||
let sProcessInboundLog = ''; | ||
@@ -710,3 +776,3 @@ try { | ||
sProcessInboundLog += ` srcAddress: ${JSON.stringify(rinfo)}`; | ||
this.sysLogger.trace(`Received KNX packet: _processInboundMessage, ${sProcessInboundLog} ChannelID:${this._channelID}` || | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.trace(`Received KNX packet: _processInboundMessage, ${sProcessInboundLog} ChannelID:${this._channelID}` || | ||
`??` + | ||
@@ -743,3 +809,3 @@ ` Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this._clearToSend = true; | ||
this.sysLogger.debug(`Received KNX packet: CONNECT_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_b = this.sysLogger) === null || _b === void 0 ? void 0 : _b.debug(`Received KNX packet: CONNECT_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this.emit(KNXClientEvents.connected, this._options); | ||
@@ -750,3 +816,3 @@ this.startHeartBeat(); | ||
else if (knxHeader.service_type === KNXConstants_1.KNX_CONSTANTS.DISCONNECT_RESPONSE) { | ||
this.sysLogger.debug(`Received KNX packet: DISCONNECT_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_c = this.sysLogger) === null || _c === void 0 ? void 0 : _c.debug(`Received KNX packet: DISCONNECT_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
if (this._connectionState !== ConncetionState.DISCONNECTING) { | ||
@@ -762,3 +828,3 @@ this.emit(KNXClientEvents.error, new Error('Unexpected Disconnect Response.')); | ||
} | ||
this.sysLogger.error(`Received KNX packet: DISCONNECT_REQUEST, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_d = this.sysLogger) === null || _d === void 0 ? void 0 : _d.error(`Received KNX packet: DISCONNECT_REQUEST, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this._connectionState = ConncetionState.DISCONNECTING; | ||
@@ -773,3 +839,3 @@ this.sendDisconnectResponseMessage(knxDisconnectRequest.channelID); | ||
if (knxTunnelingRequest.channelID !== this._channelID) { | ||
this.sysLogger.debug(`Received KNX packet: TUNNELING: L_DATA_IND, NOT FOR ME: MyChannelID:${this._channelID} ReceivedPacketChannelID: ${knxTunnelingRequest.channelID} ReceivedPacketseqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_e = this.sysLogger) === null || _e === void 0 ? void 0 : _e.debug(`Received KNX packet: TUNNELING: L_DATA_IND, NOT FOR ME: MyChannelID:${this._channelID} ReceivedPacketChannelID: ${knxTunnelingRequest.channelID} ReceivedPacketseqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
return; | ||
@@ -779,6 +845,6 @@ } | ||
const knxTunnelAck = KNXProtocol_1.default.newKNXTunnelingACK(knxTunnelingRequest.channelID, knxTunnelingRequest.seqCounter, KNXConstants_1.KNX_CONSTANTS.E_NO_ERROR); | ||
this.send(knxTunnelAck); | ||
this.send(knxTunnelAck, undefined, false, this.getSeqNumber()); | ||
} | ||
catch (error) { | ||
this.sysLogger.error(`Received KNX packet: TUNNELING: L_DATA_IND, ERROR BUOLDING THE TUNNELINK ACK: ${error.message} MyChannelID:${this._channelID} ReceivedPacketChannelID: ${knxTunnelingRequest.channelID} ReceivedPacketseqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_f = this.sysLogger) === null || _f === void 0 ? void 0 : _f.error(`Received KNX packet: TUNNELING: L_DATA_IND, ERROR BUOLDING THE TUNNELINK ACK: ${error.message} MyChannelID:${this._channelID} ReceivedPacketChannelID: ${knxTunnelingRequest.channelID} ReceivedPacketseqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
} | ||
@@ -790,3 +856,3 @@ if (knxTunnelingRequest.cEMIMessage.msgCode === | ||
sDebugString += ` dstAddress: ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`; | ||
this.sysLogger.debug(`Received KNX packet: TUNNELING: L_DATA_IND, ${sDebugString} ChannelID:${this._channelID} seqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_g = this.sysLogger) === null || _g === void 0 ? void 0 : _g.debug(`Received KNX packet: TUNNELING: L_DATA_IND, ${sDebugString} ChannelID:${this._channelID} seqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this.emit(KNXClientEvents.indication, knxTunnelingRequest, false); | ||
@@ -796,3 +862,3 @@ } | ||
CEMIConstants_1.default.L_DATA_CON) { | ||
this.sysLogger.debug(`Received KNX packet: TUNNELING: L_DATA_CON, ChannelID:${this._channelID} seqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_h = this.sysLogger) === null || _h === void 0 ? void 0 : _h.debug(`Received KNX packet: TUNNELING: L_DATA_CON, ChannelID:${this._channelID} seqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
} | ||
@@ -805,5 +871,6 @@ } | ||
} | ||
this.sysLogger.debug(`Received KNX packet: TUNNELING: TUNNELING_ACK, ChannelID:${this._channelID} seqCounter:${knxTunnelingAck.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_j = this.sysLogger) === null || _j === void 0 ? void 0 : _j.debug(`Received KNX packet: TUNNELING: TUNNELING_ACK, ChannelID:${this._channelID} seqCounter:${knxTunnelingAck.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
if (!this._options.suppress_ack_ldatareq) { | ||
if (knxTunnelingAck.seqCounter === this.getSeqNumber()) { | ||
if (knxTunnelingAck.seqCounter === | ||
this.getCurrentItemHandledByTheQueue()) { | ||
this.clearTimer(KNXTimer.ACK); | ||
@@ -813,6 +880,6 @@ this._numFailedTelegramACK = 0; | ||
this.emit(KNXClientEvents.ackReceived, knxTunnelingAck, true); | ||
this.sysLogger.debug(`Received KNX packet: TUNNELING: DELETED_TUNNELING_ACK FROM PENDING ACK's, ChannelID:${this._channelID} seqCounter:${knxTunnelingAck.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_k = this.sysLogger) === null || _k === void 0 ? void 0 : _k.debug(`Received KNX packet: TUNNELING: DELETED_TUNNELING_ACK FROM PENDING ACK's, ChannelID:${this._channelID} seqCounter:${knxTunnelingAck.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
} | ||
else { | ||
this.sysLogger.error(`Received KNX packet: TUNNELING: Unexpected Tunnel Ack with seqCounter = ${knxTunnelingAck.seqCounter}`); | ||
(_l = this.sysLogger) === null || _l === void 0 ? void 0 : _l.error(`Received KNX packet: TUNNELING: Unexpected Tunnel Ack with seqCounter = ${knxTunnelingAck.seqCounter}, expecting ${this.getSeqNumber()}`); | ||
} | ||
@@ -829,3 +896,3 @@ } | ||
sDebugString += ` dstAddress: ${knxRoutingInd.cEMIMessage.dstAddress.toString()}`; | ||
this.sysLogger.debug(`Received KNX packet: ROUTING: L_DATA_IND, ${sDebugString} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_m = this.sysLogger) === null || _m === void 0 ? void 0 : _m.debug(`Received KNX packet: ROUTING: L_DATA_IND, ${sDebugString} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this.emit(KNXClientEvents.indication, knxRoutingInd, false); | ||
@@ -835,3 +902,3 @@ } | ||
CEMIConstants_1.default.L_DATA_CON) { | ||
this.sysLogger.debug(`Received KNX packet: ROUTING: L_DATA_CON, Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_o = this.sysLogger) === null || _o === void 0 ? void 0 : _o.debug(`Received KNX packet: ROUTING: L_DATA_CON, Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
} | ||
@@ -843,3 +910,3 @@ } | ||
KNXConstants_1.KNX_CONSTANTS.CONNECTIONSTATE_RESPONSE) { | ||
this.sysLogger.debug(`Received KNX packet: CONNECTIONSTATE_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_p = this.sysLogger) === null || _p === void 0 ? void 0 : _p.debug(`Received KNX packet: CONNECTIONSTATE_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
const knxConnectionStateResponse = knxMessage; | ||
@@ -864,22 +931,22 @@ if (knxConnectionStateResponse.status !== | ||
catch (e) { | ||
this.sysLogger.error(`Received KNX packet: Error processing inbound message: ${e.message} ${sProcessInboundLog} ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}. This means that KNX-Ultimate received a malformed Header or CEMI message from your KNX Gateway.`); | ||
(_q = this.sysLogger) === null || _q === void 0 ? void 0 : _q.error(`Received KNX packet: Error processing inbound message: ${e.message} ${sProcessInboundLog} ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}. This means that KNX-Ultimate received a malformed Header or CEMI message from your KNX Gateway.`); | ||
} | ||
} | ||
sendDescriptionRequestMessage() { | ||
this.send(KNXProtocol_1.default.newKNXDescriptionRequest(new HPAI_1.default(this._options.localIPAddress))); | ||
this.send(KNXProtocol_1.default.newKNXDescriptionRequest(new HPAI_1.default(this._options.localIPAddress)), undefined, false, this.getSeqNumber()); | ||
} | ||
sendSearchRequestMessage() { | ||
this.send(KNXProtocol_1.default.newKNXSearchRequest(new HPAI_1.default(this._options.localIPAddress, this._peerPort))); | ||
this.send(KNXProtocol_1.default.newKNXSearchRequest(new HPAI_1.default(this._options.localIPAddress, this._peerPort)), undefined, false, this.getSeqNumber()); | ||
} | ||
sendConnectRequestMessage(cri) { | ||
this.send(KNXProtocol_1.default.newKNXConnectRequest(cri)); | ||
this.send(KNXProtocol_1.default.newKNXConnectRequest(cri), undefined, false, this.getSeqNumber()); | ||
} | ||
sendConnectionStateRequestMessage(channelID) { | ||
this.send(KNXProtocol_1.default.newKNXConnectionStateRequest(channelID)); | ||
this.send(KNXProtocol_1.default.newKNXConnectionStateRequest(channelID), undefined, false, this.getSeqNumber()); | ||
} | ||
sendDisconnectRequestMessage(channelID) { | ||
this.send(KNXProtocol_1.default.newKNXDisconnectRequest(channelID)); | ||
this.send(KNXProtocol_1.default.newKNXDisconnectRequest(channelID), undefined, false, this.getSeqNumber()); | ||
} | ||
sendDisconnectResponseMessage(channelID, status = KNXConstants_1.ConnectionStatus.E_NO_ERROR) { | ||
this.send(KNXProtocol_1.default.newKNXDisconnectResponse(channelID, status)); | ||
this.send(KNXProtocol_1.default.newKNXDisconnectResponse(channelID, status), undefined, false, this.getSeqNumber()); | ||
} | ||
@@ -890,3 +957,3 @@ sendSecureSessionRequestMessage(cri) { | ||
: KNXConstants_1.KNX_CONSTANTS.IPV4_UDP); | ||
this.send(KNXProtocol_1.default.newKNXSecureSessionRequest(cri, oHPAI)); | ||
this.send(KNXProtocol_1.default.newKNXSecureSessionRequest(cri, oHPAI), undefined, false, this.getSeqNumber()); | ||
} | ||
@@ -893,0 +960,0 @@ } |
@@ -65,2 +65,3 @@ /// <reference types="node" /> | ||
localSocketAddress?: string; | ||
KNXQueueSendIntervalMilliseconds?: number; | ||
} & KNXLoggerOptions; | ||
@@ -103,2 +104,4 @@ export declare function getDecodedKeyring(): string; | ||
private clearAllTimers; | ||
processKnxPacketQueueItem: () => Promise<void>; | ||
handleKNXQueue: () => Promise<void>; | ||
send(knxPacket: KNXPacket): void; | ||
@@ -105,0 +108,0 @@ write(dstAddress: KNXAddress | string, data: any, dptid: string | number): void; |
@@ -51,2 +51,3 @@ "use strict"; | ||
const utils_1 = require("./utils"); | ||
const limiter_1 = require("limiter"); | ||
var ConncetionState; | ||
@@ -82,2 +83,8 @@ (function (ConncetionState) { | ||
const jKNXSecureKeyring = ''; | ||
let limiter = new limiter_1.RateLimiter({ | ||
tokensPerInterval: 1, | ||
interval: 25, | ||
}); | ||
const commandQueue = []; | ||
let exitProcessingKNXQueueLoop = false; | ||
const optionsDefaults = { | ||
@@ -88,3 +95,3 @@ physAddr: '15.15.200', | ||
ipPort: 3671, | ||
hostProtocol: 'TunnelUDP', | ||
hostProtocol: 'Multicast', | ||
isSecureKNXEnabled: false, | ||
@@ -97,2 +104,3 @@ suppress_ack_ldatareq: false, | ||
jKNXSecureKeyring: {}, | ||
KNXQueueSendIntervalMilliseconds: 25, | ||
}; | ||
@@ -115,4 +123,83 @@ function getDecodedKeyring() { | ||
constructor(options) { | ||
var _a; | ||
super(); | ||
this.processKnxPacketQueueItem = async () => { | ||
var _a, _b, _c, _d, _e; | ||
const knxPacket = commandQueue.pop(); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.debug(`KNXClient: processKnxPacketQueueItem: Processing queued KNX. commandQueue.length: ${commandQueue.length} ${knxPacket.header.service_type}`); | ||
if (knxPacket instanceof KNXConnectRequest_1.default) { | ||
(_b = this.sysLogger) === null || _b === void 0 ? void 0 : _b.debug(`Sending KNX packet: ${knxPacket.constructor.name} Host:${this._peerHost}:${this._peerPort}`); | ||
} | ||
if (knxPacket instanceof KNXTunnelingRequest_1.default || | ||
knxPacket instanceof KNXRoutingIndication_1.default) { | ||
let sTPCI = ''; | ||
if (knxPacket.cEMIMessage.npdu.isGroupRead) { | ||
sTPCI = 'Read'; | ||
} | ||
if (knxPacket.cEMIMessage.npdu.isGroupResponse) { | ||
sTPCI = 'Response'; | ||
} | ||
if (knxPacket.cEMIMessage.npdu.isGroupWrite) { | ||
sTPCI = 'Write'; | ||
} | ||
let sDebugString = `Data: ${JSON.stringify(knxPacket.cEMIMessage.npdu)}`; | ||
sDebugString += ` srcAddress: ${knxPacket.cEMIMessage.srcAddress.toString()}`; | ||
sDebugString += ` dstAddress: ${knxPacket.cEMIMessage.dstAddress.toString()}`; | ||
(_c = this.sysLogger) === null || _c === void 0 ? void 0 : _c.debug(`Sending KNX packet: ${knxPacket.constructor.name} ${sDebugString} Host:${this._peerHost}:${this._peerPort} channelID:${knxPacket.channelID} seqCounter:${knxPacket.seqCounter} Dest:${knxPacket.cEMIMessage.dstAddress.toString()}`, ` Data:${knxPacket.cEMIMessage.npdu.dataValue.toString('hex')} TPCI:${sTPCI}`); | ||
} | ||
if (this._options.hostProtocol === 'Multicast' || | ||
this._options.hostProtocol === 'TunnelUDP') { | ||
try { | ||
; | ||
this._clientSocket.send(knxPacket.toBuffer(), this._peerPort, this._peerHost, (err) => { | ||
var _a; | ||
if (err) { | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`Sending KNX packet: Send UDP sending error: ${err.message}`); | ||
this.emit(KNXClientEvents.error, err); | ||
} | ||
}); | ||
} | ||
catch (error) { | ||
(_d = this.sysLogger) === null || _d === void 0 ? void 0 : _d.error(`Sending KNX packet: Send UDP Catch error: ${error.message} ${typeof knxPacket} seqCounter:${knxPacket.seqCounter || ''}`); | ||
this.emit(KNXClientEvents.error, error); | ||
} | ||
} | ||
else { | ||
try { | ||
; | ||
this._clientSocket.write(knxPacket.toBuffer(), (err) => { | ||
var _a; | ||
if (err) { | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`Sending KNX packet: Send TCP sending error: ${err.message}` || | ||
'Undef error'); | ||
this.emit(KNXClientEvents.error, err); | ||
} | ||
}); | ||
} | ||
catch (error) { | ||
(_e = this.sysLogger) === null || _e === void 0 ? void 0 : _e.error(`Sending KNX packet: Send TCP Catch error: ${error.message}` || | ||
'Undef error'); | ||
this.emit(KNXClientEvents.error, error); | ||
} | ||
} | ||
}; | ||
this.handleKNXQueue = async () => { | ||
var _a, _b, _c, _d, _e; | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.debug(`KNXClient: handleKNXQueue: Start Processing queued KNX.`); | ||
do { | ||
const remainingRequests = await limiter.removeTokens(1); | ||
if (commandQueue.length > 0 && this.clearToSend) { | ||
(_b = this.sysLogger) === null || _b === void 0 ? void 0 : _b.debug(`\n\nKNXClient: START.`); | ||
await this.processKnxPacketQueueItem(); | ||
(_c = this.sysLogger) === null || _c === void 0 ? void 0 : _c.debug(`KNXClient: END.`); | ||
} | ||
else if (!this.clearToSend) { | ||
(_d = this.sysLogger) === null || _d === void 0 ? void 0 : _d.warn(`KNXClient: NOT CLEAR TO SEND!`); | ||
} | ||
} while (exitProcessingKNXQueueLoop === false); | ||
(_e = this.sysLogger) === null || _e === void 0 ? void 0 : _e.debug(`KNXClient: handleKNXQueue: Stop Processing queued KNX.`); | ||
}; | ||
this.handleKNXQueue(); | ||
this.timers = new Map(); | ||
exitProcessingKNXQueueLoop = false; | ||
if (options === undefined) { | ||
@@ -142,2 +229,6 @@ options = optionsDefaults; | ||
this.jKNXSecureKeyring = this._options.jKNXSecureKeyring; | ||
limiter = new limiter_1.RateLimiter({ | ||
tokensPerInterval: 1, | ||
interval: this._options.KNXQueueSendIntervalMilliseconds, | ||
}); | ||
this.on('error', (error) => { }); | ||
@@ -151,3 +242,3 @@ if (typeof this._options.physAddr === 'string') { | ||
catch (error) { | ||
this.sysLogger.error(`ipAddressHelper.getLocalAddress:${error.message}`); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`ipAddressHelper.getLocalAddress:${error.message}`); | ||
throw error; | ||
@@ -161,2 +252,3 @@ } | ||
this._clientSocket.bind({ port: null, address: this._options.localIPAddress }, () => { | ||
var _a; | ||
try { | ||
@@ -170,3 +262,3 @@ ; | ||
catch (error) { | ||
this.sysLogger.error(`UDP: Error setting SetTTL ${error.message}` || ''); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`UDP: Error setting SetTTL ${error.message}` || ''); | ||
} | ||
@@ -196,2 +288,3 @@ }); | ||
this._clientSocket.bind(this._peerPort, () => { | ||
var _a, _b; | ||
const client = this._clientSocket; | ||
@@ -203,3 +296,3 @@ try { | ||
catch (error) { | ||
this.sysLogger.error(`Multicast: Error setting SetTTL ${error.message}` || | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`Multicast: Error setting SetTTL ${error.message}` || | ||
''); | ||
@@ -211,3 +304,3 @@ } | ||
catch (err) { | ||
this.sysLogger.error('Multicast: cannot add membership (%s)', err); | ||
(_b = this.sysLogger) === null || _b === void 0 ? void 0 : _b.error('Multicast: cannot add membership (%s)', err); | ||
this.emit(KNXClientEvents.error, err); | ||
@@ -225,2 +318,3 @@ } | ||
getKNXDataBuffer(data, dptid) { | ||
var _a; | ||
if (typeof dptid === 'number') { | ||
@@ -233,3 +327,3 @@ dptid = dptid.toString(); | ||
const isSixBits = adpu.bitlength <= 6; | ||
this.sysLogger.trace(`isSixBits:${isSixBits} Includes (should be = isSixBits):${[ | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.trace(`isSixBits:${isSixBits} Includes (should be = isSixBits):${[ | ||
1, 2, 3, 5, 9, 10, 11, 14, 18, | ||
@@ -260,6 +354,7 @@ ].includes(iDatapointType)} ADPU BitLength:${adpu.bitlength}`); | ||
setTimer(type, cb, delay) { | ||
var _a; | ||
if (this.timers.has(type)) { | ||
clearTimeout(this.timers.get(type)); | ||
this.timers.delete(type); | ||
this.sysLogger.warn(`Timer "${type}" was already running`); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.warn(`Timer "${type}" was already running`); | ||
} | ||
@@ -285,55 +380,5 @@ this.timers.set(type, setTimeout(() => { | ||
send(knxPacket) { | ||
if (knxPacket instanceof KNXConnectRequest_1.default) { | ||
this.sysLogger.debug(`Sending KNX packet: ${knxPacket.constructor.name} Host:${this._peerHost}:${this._peerPort}`); | ||
} | ||
if (knxPacket instanceof KNXTunnelingRequest_1.default || | ||
knxPacket instanceof KNXRoutingIndication_1.default) { | ||
let sTPCI = ''; | ||
if (knxPacket.cEMIMessage.npdu.isGroupRead) { | ||
sTPCI = 'Read'; | ||
} | ||
if (knxPacket.cEMIMessage.npdu.isGroupResponse) { | ||
sTPCI = 'Response'; | ||
} | ||
if (knxPacket.cEMIMessage.npdu.isGroupWrite) { | ||
sTPCI = 'Write'; | ||
} | ||
let sDebugString = `Data: ${JSON.stringify(knxPacket.cEMIMessage.npdu)}`; | ||
sDebugString += ` srcAddress: ${knxPacket.cEMIMessage.srcAddress.toString()}`; | ||
sDebugString += ` dstAddress: ${knxPacket.cEMIMessage.dstAddress.toString()}`; | ||
this.sysLogger.debug(`Sending KNX packet: ${knxPacket.constructor.name} ${sDebugString} Host:${this._peerHost}:${this._peerPort} channelID:${knxPacket.channelID} seqCounter:${knxPacket.seqCounter} Dest:${knxPacket.cEMIMessage.dstAddress.toString()}`, ` Data:${knxPacket.cEMIMessage.npdu.dataValue.toString('hex')} TPCI:${sTPCI}`); | ||
} | ||
if (this._options.hostProtocol === 'Multicast' || | ||
this._options.hostProtocol === 'TunnelUDP') { | ||
try { | ||
; | ||
this._clientSocket.send(knxPacket.toBuffer(), this._peerPort, this._peerHost, (err) => { | ||
if (err) { | ||
this.sysLogger.error(`Sending KNX packet: Send UDP sending error: ${err.message}`); | ||
this.emit(KNXClientEvents.error, err); | ||
} | ||
}); | ||
} | ||
catch (error) { | ||
this.sysLogger.error(`Sending KNX packet: Send UDP Catch error: ${error.message} ${typeof knxPacket} seqCounter:${knxPacket.seqCounter || ''}`); | ||
this.emit(KNXClientEvents.error, error); | ||
} | ||
} | ||
else { | ||
try { | ||
; | ||
this._clientSocket.write(knxPacket.toBuffer(), (err) => { | ||
if (err) { | ||
this.sysLogger.error(`Sending KNX packet: Send TCP sending error: ${err.message}` || | ||
'Undef error'); | ||
this.emit(KNXClientEvents.error, err); | ||
} | ||
}); | ||
} | ||
catch (error) { | ||
this.sysLogger.error(`Sending KNX packet: Send TCP Catch error: ${error.message}` || | ||
'Undef error'); | ||
this.emit(KNXClientEvents.error, error); | ||
} | ||
} | ||
var _a; | ||
commandQueue.unshift(knxPacket); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.debug(`KNXClient: ADDED PACKET TO COMMANDQUEUE. Len: ${commandQueue.length}`); | ||
} | ||
@@ -439,6 +484,7 @@ write(dstAddress, data, dptid) { | ||
writeRaw(dstAddress, rawDataBuffer, bitlength) { | ||
var _a; | ||
if (this._connectionState !== ConncetionState.CONNECTED) | ||
throw new Error('The socket is not connected. Unable to access the KNX BUS'); | ||
if (!Buffer.isBuffer(rawDataBuffer)) { | ||
this.sysLogger.error('KNXClient: writeRaw: Value must be a buffer! '); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error('KNXClient: writeRaw: Value must be a buffer! '); | ||
return; | ||
@@ -577,3 +623,5 @@ } | ||
async closeSocket() { | ||
exitProcessingKNXQueueLoop = true; | ||
return new Promise((resolve) => { | ||
var _a; | ||
if (!this._clientSocket) | ||
@@ -585,9 +633,15 @@ return; | ||
}; | ||
if (this._options.hostProtocol === 'TunnelTCP') { | ||
; | ||
this._clientSocket.destroy(); | ||
try { | ||
if (this._options.hostProtocol === 'TunnelTCP') { | ||
; | ||
this._clientSocket.destroy(); | ||
} | ||
else { | ||
; | ||
this._clientSocket.close(cb); | ||
} | ||
} | ||
else { | ||
; | ||
this._clientSocket.close(cb); | ||
catch (error) { | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`KNXClient: into async closeSocket(): ${error.stack}`); | ||
resolve(); | ||
} | ||
@@ -597,2 +651,3 @@ }); | ||
async Disconnect() { | ||
var _a; | ||
if (this._clientSocket === null) { | ||
@@ -607,3 +662,3 @@ throw new Error('No client socket defined'); | ||
if (this._channelID === null) { | ||
this.sysLogger.debug(`KNXClient: into Disconnect(), channel id is not defined so skip disconnect packet and close socket`); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.debug(`KNXClient: into Disconnect(), channel id is not defined so skip disconnect packet and close socket`); | ||
await this.closeSocket(); | ||
@@ -623,3 +678,4 @@ return; | ||
async setDisconnected(_sReason = '') { | ||
this.sysLogger.debug(`KNXClient: called _setDisconnected ${this._options.ipAddr}:${this._options.ipPort} ${_sReason}`); | ||
var _a; | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.debug(`KNXClient: called _setDisconnected ${this._options.ipAddr}:${this._options.ipPort} ${_sReason}`); | ||
this._connectionState = ConncetionState.DISCONNECTED; | ||
@@ -642,3 +698,4 @@ this.clearAllTimers(); | ||
this.setTimer(KNXTimer.CONNECTION_STATE, () => { | ||
this.sysLogger.error(`KNXClient: getConnectionStatus Timeout ${this._heartbeatFailures} out of ${this.max_HeartbeatFailures}`); | ||
var _a; | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`KNXClient: getConnectionStatus Timeout ${this._heartbeatFailures} out of ${this.max_HeartbeatFailures}`); | ||
this._heartbeatFailures++; | ||
@@ -672,2 +729,3 @@ if (this._heartbeatFailures >= this.max_HeartbeatFailures) { | ||
this.setTimer(KNXTimer.ACK, () => { | ||
var _a, _b; | ||
this._numFailedTelegramACK += 1; | ||
@@ -679,3 +737,3 @@ if (this._numFailedTelegramACK > 2) { | ||
this.emit(KNXClientEvents.error, timeoutErr); | ||
this.sysLogger.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. ABORT sending datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. ABORT sending datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
} | ||
@@ -685,3 +743,3 @@ else { | ||
this.send(knxTunnelingRequest); | ||
this.sysLogger.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. Retransmit datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
(_b = this.sysLogger) === null || _b === void 0 ? void 0 : _b.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. Retransmit datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
} | ||
@@ -691,2 +749,3 @@ }, KNXConstants_1.KNX_CONSTANTS.TUNNELING_REQUEST_TIMEOUT * 1000); | ||
processInboundMessage(msg, rinfo) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q; | ||
let sProcessInboundLog = ''; | ||
@@ -696,3 +755,3 @@ try { | ||
sProcessInboundLog += ` srcAddress: ${JSON.stringify(rinfo)}`; | ||
this.sysLogger.trace(`Received KNX packet: _processInboundMessage, ${sProcessInboundLog} ChannelID:${this._channelID}` || | ||
(_a = this.sysLogger) === null || _a === void 0 ? void 0 : _a.trace(`Received KNX packet: _processInboundMessage, ${sProcessInboundLog} ChannelID:${this._channelID}` || | ||
`??` + | ||
@@ -729,3 +788,3 @@ ` Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this._clearToSend = true; | ||
this.sysLogger.debug(`Received KNX packet: CONNECT_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_b = this.sysLogger) === null || _b === void 0 ? void 0 : _b.debug(`Received KNX packet: CONNECT_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this.emit(KNXClientEvents.connected, this._options); | ||
@@ -736,3 +795,3 @@ this.startHeartBeat(); | ||
else if (knxHeader.service_type === KNXConstants_1.KNX_CONSTANTS.DISCONNECT_RESPONSE) { | ||
this.sysLogger.debug(`Received KNX packet: DISCONNECT_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_c = this.sysLogger) === null || _c === void 0 ? void 0 : _c.debug(`Received KNX packet: DISCONNECT_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
if (this._connectionState !== ConncetionState.DISCONNECTING) { | ||
@@ -748,3 +807,3 @@ this.emit(KNXClientEvents.error, new Error('Unexpected Disconnect Response.')); | ||
} | ||
this.sysLogger.error(`Received KNX packet: DISCONNECT_REQUEST, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_d = this.sysLogger) === null || _d === void 0 ? void 0 : _d.error(`Received KNX packet: DISCONNECT_REQUEST, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this._connectionState = ConncetionState.DISCONNECTING; | ||
@@ -759,3 +818,3 @@ this.sendDisconnectResponseMessage(knxDisconnectRequest.channelID); | ||
if (knxTunnelingRequest.channelID !== this._channelID) { | ||
this.sysLogger.debug(`Received KNX packet: TUNNELING: L_DATA_IND, NOT FOR ME: MyChannelID:${this._channelID} ReceivedPacketChannelID: ${knxTunnelingRequest.channelID} ReceivedPacketseqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_e = this.sysLogger) === null || _e === void 0 ? void 0 : _e.debug(`Received KNX packet: TUNNELING: L_DATA_IND, NOT FOR ME: MyChannelID:${this._channelID} ReceivedPacketChannelID: ${knxTunnelingRequest.channelID} ReceivedPacketseqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
return; | ||
@@ -768,3 +827,3 @@ } | ||
catch (error) { | ||
this.sysLogger.error(`Received KNX packet: TUNNELING: L_DATA_IND, ERROR BUOLDING THE TUNNELINK ACK: ${error.message} MyChannelID:${this._channelID} ReceivedPacketChannelID: ${knxTunnelingRequest.channelID} ReceivedPacketseqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_f = this.sysLogger) === null || _f === void 0 ? void 0 : _f.error(`Received KNX packet: TUNNELING: L_DATA_IND, ERROR BUOLDING THE TUNNELINK ACK: ${error.message} MyChannelID:${this._channelID} ReceivedPacketChannelID: ${knxTunnelingRequest.channelID} ReceivedPacketseqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
} | ||
@@ -776,3 +835,3 @@ if (knxTunnelingRequest.cEMIMessage.msgCode === | ||
sDebugString += ` dstAddress: ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`; | ||
this.sysLogger.debug(`Received KNX packet: TUNNELING: L_DATA_IND, ${sDebugString} ChannelID:${this._channelID} seqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_g = this.sysLogger) === null || _g === void 0 ? void 0 : _g.debug(`Received KNX packet: TUNNELING: L_DATA_IND, ${sDebugString} ChannelID:${this._channelID} seqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this.emit(KNXClientEvents.indication, knxTunnelingRequest, false); | ||
@@ -782,3 +841,3 @@ } | ||
CEMIConstants_1.default.L_DATA_CON) { | ||
this.sysLogger.debug(`Received KNX packet: TUNNELING: L_DATA_CON, ChannelID:${this._channelID} seqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_h = this.sysLogger) === null || _h === void 0 ? void 0 : _h.debug(`Received KNX packet: TUNNELING: L_DATA_CON, ChannelID:${this._channelID} seqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
} | ||
@@ -791,3 +850,3 @@ } | ||
} | ||
this.sysLogger.debug(`Received KNX packet: TUNNELING: TUNNELING_ACK, ChannelID:${this._channelID} seqCounter:${knxTunnelingAck.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_j = this.sysLogger) === null || _j === void 0 ? void 0 : _j.debug(`Received KNX packet: TUNNELING: TUNNELING_ACK, ChannelID:${this._channelID} seqCounter:${knxTunnelingAck.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
if (!this._options.suppress_ack_ldatareq) { | ||
@@ -799,6 +858,6 @@ if (knxTunnelingAck.seqCounter === this.getSeqNumber()) { | ||
this.emit(KNXClientEvents.ackReceived, knxTunnelingAck, true); | ||
this.sysLogger.debug(`Received KNX packet: TUNNELING: DELETED_TUNNELING_ACK FROM PENDING ACK's, ChannelID:${this._channelID} seqCounter:${knxTunnelingAck.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_k = this.sysLogger) === null || _k === void 0 ? void 0 : _k.debug(`Received KNX packet: TUNNELING: DELETED_TUNNELING_ACK FROM PENDING ACK's, ChannelID:${this._channelID} seqCounter:${knxTunnelingAck.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
} | ||
else { | ||
this.sysLogger.error(`Received KNX packet: TUNNELING: Unexpected Tunnel Ack with seqCounter = ${knxTunnelingAck.seqCounter}`); | ||
(_l = this.sysLogger) === null || _l === void 0 ? void 0 : _l.error(`Received KNX packet: TUNNELING: Unexpected Tunnel Ack with seqCounter = ${knxTunnelingAck.seqCounter}`); | ||
} | ||
@@ -815,3 +874,3 @@ } | ||
sDebugString += ` dstAddress: ${knxRoutingInd.cEMIMessage.dstAddress.toString()}`; | ||
this.sysLogger.debug(`Received KNX packet: ROUTING: L_DATA_IND, ${sDebugString} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_m = this.sysLogger) === null || _m === void 0 ? void 0 : _m.debug(`Received KNX packet: ROUTING: L_DATA_IND, ${sDebugString} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this.emit(KNXClientEvents.indication, knxRoutingInd, false); | ||
@@ -821,3 +880,3 @@ } | ||
CEMIConstants_1.default.L_DATA_CON) { | ||
this.sysLogger.debug(`Received KNX packet: ROUTING: L_DATA_CON, Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_o = this.sysLogger) === null || _o === void 0 ? void 0 : _o.debug(`Received KNX packet: ROUTING: L_DATA_CON, Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
} | ||
@@ -829,3 +888,3 @@ } | ||
KNXConstants_1.KNX_CONSTANTS.CONNECTIONSTATE_RESPONSE) { | ||
this.sysLogger.debug(`Received KNX packet: CONNECTIONSTATE_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
(_p = this.sysLogger) === null || _p === void 0 ? void 0 : _p.debug(`Received KNX packet: CONNECTIONSTATE_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
const knxConnectionStateResponse = knxMessage; | ||
@@ -850,3 +909,3 @@ if (knxConnectionStateResponse.status !== | ||
catch (e) { | ||
this.sysLogger.error(`Received KNX packet: Error processing inbound message: ${e.message} ${sProcessInboundLog} ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}. This means that KNX-Ultimate received a malformed Header or CEMI message from your KNX Gateway.`); | ||
(_q = this.sysLogger) === null || _q === void 0 ? void 0 : _q.error(`Received KNX packet: Error processing inbound message: ${e.message} ${sProcessInboundLog} ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}. This means that KNX-Ultimate received a malformed Header or CEMI message from your KNX Gateway.`); | ||
} | ||
@@ -853,0 +912,0 @@ } |
# [3.0.0-beta.0](https://github.com/Supergiovane/KNXUltimate/compare/v2.3.5...v3.0.0-beta.0) (2024-09-08) | ||
### Features | ||
* transparent KNX queue. The sent telegrams are now queued and transmitted to the bus by obeying the time interval specified by the new property "KNXQueueSendIntervalMilliseconds" ([93a65c9](https://github.com/Supergiovane/KNXUltimate/commit/93a65c97c426977b69b8c4f3044755229ad89b3a)) | ||
## [2.3.5](https://github.com/Supergiovane/KNXUltimate/compare/v2.3.4...v2.3.5) (2024-07-08) | ||
@@ -4,0 +11,0 @@ |
{ | ||
"name": "knxultimate", | ||
"description": "KNX IP protocol implementation for Node. This is the ENGINE of Node-Red KNX-Ultimate node.", | ||
"version": "2.3.5", | ||
"version": "3.0.0-beta.0", | ||
"main": "./build/index.js", | ||
@@ -20,3 +20,4 @@ "engines": { | ||
"test-connection": "", | ||
"release": "read -p 'GITHUB_TOKEN: ' GITHUB_TOKEN && export GITHUB_TOKEN=$GITHUB_TOKEN && release-it" | ||
"release": "read -p 'GITHUB_TOKEN: ' GITHUB_TOKEN && export GITHUB_TOKEN=$GITHUB_TOKEN && release-it", | ||
"betarelease": "read -p 'GITHUB_TOKEN: ' GITHUB_TOKEN && export GITHUB_TOKEN=$GITHUB_TOKEN && release-it major --preRelease=beta" | ||
}, | ||
@@ -49,3 +50,3 @@ "release-it": { | ||
"author": { | ||
"name": "Max Supergiovane", | ||
"name": "Massimo 'Supergiovane' Saccani", | ||
"email": "maxsupergiovane@icloud.com" | ||
@@ -55,3 +56,3 @@ }, | ||
{ | ||
"name": "Max Supergiovane", | ||
"name": "Massimo 'Supergiovane' Saccani", | ||
"email": "maxsupergiovane@icloud.com" | ||
@@ -75,2 +76,3 @@ }, | ||
"crypto-js": "4.2.0", | ||
"limiter": "2.1.0", | ||
"log-driver": "1.2.7", | ||
@@ -77,0 +79,0 @@ "mkdirp": "3.0.1", |
@@ -10,9 +10,14 @@ ![Logo](img/logo-big.png) | ||
[![Youtube][youtube-image]][youtube-url] | ||
[![Donate via PayPal](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square)](https://www.paypal.me/techtoday) | ||
Control your KNX intallation via Node.js! | ||
> This is the official engine of Node-Red node KNX-Ultimate (<https://flows.nodered.org/node/node-red-contrib-knx-ultimate>) | ||
> I had many users asking for a node.js release of that engine, so here is it. | ||
Control your KNX intallation via Node.js! | ||
This is the official engine of Node-Red's node [node-red-contrib-knx-ultimate](https://flows.nodered.org/node/node-red-contrib-knx-ultimate) | ||
Many of you asked for a node.js release of that engine, so i decided to create this package. | ||
If you enjoy my work developing this package, do today a kind thing for someone too. This will be the reward for my work. | ||
<br/> | ||
![Logo](img/readmemain.png) | ||
## CHANGELOG | ||
@@ -48,4 +53,4 @@ | ||
| interface (string) | Specifies the local eth interface to be used to connect to the KNX Bus.| | ||
| KNXQueueSendIntervalMilliseconds | The KNX standard has a maximum transmit rate to the BUS, of about 1 telegram each 25ms (to stay safe). In case you've a lot of traffic on the BUS, you can increase this value, expressed in milliseconds. Be careful, because if you set it too high, the KNX engine could send a telegram with flag 'repeat', because the ACK from the device is coming too late.| | ||
## SUPPORTED DATAPOINTS | ||
@@ -52,0 +57,0 @@ |
@@ -29,2 +29,3 @@ // Made with love by Supergiovane | ||
import { wait } from './utils' | ||
import { RateLimiter } from 'limiter' | ||
@@ -109,2 +110,4 @@ export enum ConncetionState { | ||
localSocketAddress?: string | ||
// ** Local queue interval between each KNX telegram. Default is 1 telegram each 25ms | ||
KNXQueueSendIntervalMilliseconds?: number | ||
} & KNXLoggerOptions | ||
@@ -117,3 +120,3 @@ | ||
ipPort: 3671, | ||
hostProtocol: 'TunnelUDP', | ||
hostProtocol: 'Multicast', | ||
isSecureKNXEnabled: false, | ||
@@ -126,2 +129,3 @@ suppress_ack_ldatareq: false, | ||
jKNXSecureKeyring: {}, | ||
KNXQueueSendIntervalMilliseconds: 25, | ||
} | ||
@@ -150,2 +154,8 @@ | ||
interface KNXQueueItem { | ||
knxPacket: KNXPacket | ||
ACK: KNXTunnelingRequest | ||
expectedSeqNumberForACK: number | ||
} | ||
export default class KNXClient extends TypedEventEmitter<KNXClientEventCallbacks> { | ||
@@ -186,6 +196,16 @@ private _channelID: number | ||
private limiter: RateLimiter | ||
private commandQueue: Array<KNXQueueItem> = [] | ||
private exitProcessingKNXQueueLoop: boolean | ||
private currentItemHandledByTheQueue: KNXQueueItem | ||
constructor(options: KNXClientOptions) { | ||
super() | ||
this.timers = new Map() | ||
// This is the KNX telegram's queue list | ||
this.commandQueue = [] | ||
this.exitProcessingKNXQueueLoop = false | ||
@@ -220,3 +240,7 @@ if (options === undefined) { | ||
this.jKNXSecureKeyring = this._options.jKNXSecureKeyring | ||
// Reionfigura il rate limiter per coda KNX | ||
this.limiter = new RateLimiter({ | ||
tokensPerInterval: 1, | ||
interval: this._options.KNXQueueSendIntervalMilliseconds, | ||
}) | ||
// add an empty error listener, without this | ||
@@ -235,3 +259,3 @@ // every "error" emitted would throw an unhandled exception | ||
} catch (error) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`ipAddressHelper.getLocalAddress:${error.message}`, | ||
@@ -259,3 +283,3 @@ ) | ||
} catch (error) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`UDP: Error setting SetTTL ${error.message}` || '', | ||
@@ -311,3 +335,3 @@ ) | ||
} catch (error) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`Multicast: Error setting SetTTL ${error.message}` || | ||
@@ -323,3 +347,3 @@ '', | ||
} catch (err) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
'Multicast: cannot add membership (%s)', | ||
@@ -360,3 +384,3 @@ err, | ||
this.sysLogger.trace( | ||
this.sysLogger?.trace( | ||
`isSixBits:${isSixBits} Includes (should be = isSixBits):${[ | ||
@@ -398,3 +422,3 @@ 1, 2, 3, 5, 9, 10, 11, 14, 18, | ||
// TODO: should we throw error? | ||
this.sysLogger.warn(`Timer "${type}" was already running`) | ||
this.sysLogger?.warn(`Timer "${type}" was already running`) | ||
} | ||
@@ -429,39 +453,42 @@ | ||
/** | ||
* Write knxPacket to socket | ||
*/ | ||
send(knxPacket: KNXPacket): void { | ||
if (knxPacket instanceof KNXConnectRequest) { | ||
this.sysLogger.debug( | ||
`Sending KNX packet: ${knxPacket.constructor.name} Host:${this._peerHost}:${this._peerPort}`, | ||
processKnxPacketQueueItem = async (_knxPacket: KNXPacket) => { | ||
// await new Promise((f) => { | ||
// setTimeout(f, 2000) | ||
// }) // For debugging | ||
this.sysLogger?.debug( | ||
`KNXClient: processKnxPacketQueueItem: Processing queued KNX. commandQueue.length: ${this.commandQueue.length} ${_knxPacket.header.service_type}`, | ||
) | ||
if (_knxPacket instanceof KNXConnectRequest) { | ||
this.sysLogger?.debug( | ||
`Sending KNX packet: ${_knxPacket.constructor.name} Host:${this._peerHost}:${this._peerPort}`, | ||
) | ||
} | ||
if ( | ||
knxPacket instanceof KNXTunnelingRequest || | ||
knxPacket instanceof KNXRoutingIndication | ||
_knxPacket instanceof KNXTunnelingRequest || | ||
_knxPacket instanceof KNXRoutingIndication | ||
) { | ||
let sTPCI = '' | ||
if (knxPacket.cEMIMessage.npdu.isGroupRead) { | ||
if (_knxPacket.cEMIMessage.npdu.isGroupRead) { | ||
sTPCI = 'Read' | ||
} | ||
if (knxPacket.cEMIMessage.npdu.isGroupResponse) { | ||
if (_knxPacket.cEMIMessage.npdu.isGroupResponse) { | ||
sTPCI = 'Response' | ||
} | ||
if (knxPacket.cEMIMessage.npdu.isGroupWrite) { | ||
if (_knxPacket.cEMIMessage.npdu.isGroupWrite) { | ||
sTPCI = 'Write' | ||
} | ||
let sDebugString = `Data: ${JSON.stringify(knxPacket.cEMIMessage.npdu)}` | ||
sDebugString += ` srcAddress: ${knxPacket.cEMIMessage.srcAddress.toString()}` | ||
sDebugString += ` dstAddress: ${knxPacket.cEMIMessage.dstAddress.toString()}` | ||
let sDebugString = `Data: ${JSON.stringify(_knxPacket.cEMIMessage.npdu)}` | ||
sDebugString += ` srcAddress: ${_knxPacket.cEMIMessage.srcAddress.toString()}` | ||
sDebugString += ` dstAddress: ${_knxPacket.cEMIMessage.dstAddress.toString()}` | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Sending KNX packet: ${ | ||
knxPacket.constructor.name | ||
_knxPacket.constructor.name | ||
} ${sDebugString} Host:${this._peerHost}:${ | ||
this._peerPort | ||
} channelID:${(knxPacket as KNXTunnelingRequest).channelID} seqCounter:${ | ||
(knxPacket as KNXTunnelingRequest).seqCounter | ||
} Dest:${knxPacket.cEMIMessage.dstAddress.toString()}`, | ||
` Data:${knxPacket.cEMIMessage.npdu.dataValue.toString( | ||
} channelID:${(_knxPacket as KNXTunnelingRequest).channelID} seqCounter:${ | ||
(_knxPacket as KNXTunnelingRequest).seqCounter | ||
} Dest:${_knxPacket.cEMIMessage.dstAddress.toString()}`, | ||
` Data:${_knxPacket.cEMIMessage.npdu.dataValue.toString( | ||
'hex', | ||
@@ -478,3 +505,3 @@ )} TPCI:${sTPCI}`, | ||
;(this._clientSocket as UDPSocket).send( | ||
knxPacket.toBuffer(), | ||
_knxPacket.toBuffer(), | ||
this._peerPort, | ||
@@ -484,3 +511,3 @@ this._peerHost, | ||
if (err) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`Sending KNX packet: Send UDP sending error: ${err.message}`, | ||
@@ -493,7 +520,7 @@ ) | ||
} catch (error) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`Sending KNX packet: Send UDP Catch error: ${ | ||
error.message | ||
} ${typeof knxPacket} seqCounter:${ | ||
(knxPacket as any).seqCounter || '' | ||
} ${typeof _knxPacket} seqCounter:${ | ||
(_knxPacket as any).seqCounter || '' | ||
}`, | ||
@@ -506,6 +533,6 @@ ) | ||
;(this._clientSocket as TCPSocket).write( | ||
knxPacket.toBuffer(), | ||
_knxPacket.toBuffer(), | ||
(err) => { | ||
if (err) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`Sending KNX packet: Send TCP sending error: ${err.message}` || | ||
@@ -519,3 +546,3 @@ 'Undef error', | ||
} catch (error) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`Sending KNX packet: Send TCP Catch error: ${error.message}` || | ||
@@ -529,2 +556,56 @@ 'Undef error', | ||
handleKNXQueue = async () => { | ||
this.sysLogger?.debug( | ||
`KNXClient: handleKNXQueue: Start Processing queued KNX.`, | ||
) | ||
do { | ||
// Limiter: limits max telegrams per second | ||
const remainingRequests = await this.limiter.removeTokens(1) | ||
if (this.commandQueue.length > 0 && this._clearToSend) { | ||
this.sysLogger?.debug(`\n\nKNXClient: START.`) | ||
const item = this.commandQueue.pop() | ||
this.currentItemHandledByTheQueue = item | ||
if (item.ACK !== undefined) { | ||
await this.processKnxPacketQueueItem(item.knxPacket) | ||
this.setTimerWaitingForACK(item.ACK) | ||
} else { | ||
await this.processKnxPacketQueueItem(item.knxPacket) | ||
} | ||
this.sysLogger?.debug(`KNXClient: END.`) | ||
} // else if (!this.clearToSend) { | ||
// this.sysLogger?.warn(`KNXClient: NOT CLEAR TO SEND!`) | ||
// } | ||
if (this.exitProcessingKNXQueueLoop) return | ||
} while (this.exitProcessingKNXQueueLoop === false) | ||
this.sysLogger?.debug( | ||
`KNXClient: handleKNXQueue: Stop Processing queued KNX.`, | ||
) | ||
} | ||
/** | ||
* Write knxPacket to socket | ||
*/ | ||
send( | ||
_knxPacket: KNXPacket, | ||
_ACK: KNXTunnelingRequest, | ||
_priority: boolean, | ||
_expectedSeqNumberForACK: number, | ||
): void { | ||
const toBeAdded: KNXQueueItem = { | ||
knxPacket: _knxPacket, | ||
ACK: _ACK, | ||
expectedSeqNumberForACK: _expectedSeqNumberForACK, | ||
} | ||
if (_priority) { | ||
this.commandQueue.push(toBeAdded) | ||
this._clearToSend = true | ||
} else { | ||
this.commandQueue.unshift(toBeAdded) // Put the item as last to be sent | ||
} | ||
this.sysLogger?.debug( | ||
`KNXClient: ADDED TELEGRAM TO COMMANDQUEUE. Len: ${this.commandQueue.length}`, | ||
) | ||
} | ||
/** Sends a WRITE telegram to the BUS. | ||
@@ -570,3 +651,3 @@ * `dstAddress` is the group address (for example "0/0/1"), | ||
KNXProtocol.newKNXRoutingIndication(cEMIMessage) | ||
this.send(knxPacketRequest) | ||
this.send(knxPacketRequest, undefined, false, this.getSeqNumber()) | ||
// 06/12/2021 Multivast automaticalli echoes telegrams | ||
@@ -594,4 +675,8 @@ } else { | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest) | ||
this.send(knxPacketRequest) | ||
this.send( | ||
knxPacketRequest, | ||
knxPacketRequest, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
// 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false | ||
@@ -644,3 +729,3 @@ if (this._options.localEchoInTunneling) | ||
KNXProtocol.newKNXRoutingIndication(cEMIMessage) | ||
this.send(knxPacketRequest) | ||
this.send(knxPacketRequest, undefined, false, this.getSeqNumber()) | ||
// 06/12/2021 Multivast automaticalli echoes telegrams | ||
@@ -668,4 +753,8 @@ } else { | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest) | ||
this.send(knxPacketRequest) | ||
this.send( | ||
knxPacketRequest, | ||
knxPacketRequest, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
// 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false | ||
@@ -708,3 +797,3 @@ if (this._options.localEchoInTunneling) | ||
KNXProtocol.newKNXRoutingIndication(cEMIMessage) | ||
this.send(knxPacketRequest) | ||
this.send(knxPacketRequest, undefined, false, this.getSeqNumber()) | ||
// 06/12/2021 Multivast automaticalli echoes telegrams | ||
@@ -732,4 +821,8 @@ } else { | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest) | ||
this.send(knxPacketRequest) | ||
this.send( | ||
knxPacketRequest, | ||
knxPacketRequest, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
// 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false | ||
@@ -760,3 +853,3 @@ if (this._options.localEchoInTunneling) | ||
if (!Buffer.isBuffer(rawDataBuffer)) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
'KNXClient: writeRaw: Value must be a buffer! ', | ||
@@ -807,3 +900,3 @@ ) | ||
KNXProtocol.newKNXRoutingIndication(cEMIMessage) | ||
this.send(knxPacketRequest) | ||
this.send(knxPacketRequest, undefined, false, this.getSeqNumber()) | ||
// 06/12/2021 Multivast automaticalli echoes telegrams | ||
@@ -832,4 +925,8 @@ } else { | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest) | ||
this.send(knxPacketRequest) | ||
this.send( | ||
knxPacketRequest, | ||
knxPacketRequest, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
// 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false | ||
@@ -943,2 +1040,3 @@ if (this._options.localEchoInTunneling) | ||
this.handleKNXQueue() // Start the KNX queue processing loop | ||
this._connectionState = ConncetionState.CONNECTING | ||
@@ -1009,2 +1107,3 @@ this._numFailedTelegramACK = 0 // 25/12/2021 Reset the failed ACK counter | ||
private async closeSocket() { | ||
this.exitProcessingKNXQueueLoop = true // Exits KNX processing queue loop | ||
return new Promise<void>((resolve) => { | ||
@@ -1015,3 +1114,3 @@ // already closed | ||
const cb = () => { | ||
this._clientSocket = null | ||
// this._clientSocket = null | ||
resolve() | ||
@@ -1028,3 +1127,3 @@ } | ||
} catch (error) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`KNXClient: into async closeSocket(): ${error.stack}`, | ||
@@ -1041,2 +1140,3 @@ ) | ||
async Disconnect() { | ||
this.exitProcessingKNXQueueLoop = true // Exits KNX processing queue loop | ||
if (this._clientSocket === null) { | ||
@@ -1058,3 +1158,3 @@ throw new Error('No client socket defined') | ||
// 11/10/2022 Close the socket | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`KNXClient: into Disconnect(), channel id is not defined so skip disconnect packet and close socket`, | ||
@@ -1091,3 +1191,3 @@ ) | ||
private async setDisconnected(_sReason = '') { | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`KNXClient: called _setDisconnected ${this._options.ipAddr}:${this._options.ipPort} ${_sReason}`, | ||
@@ -1136,3 +1236,3 @@ ) | ||
() => { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`KNXClient: getConnectionStatus Timeout ${this._heartbeatFailures} out of ${this.max_HeartbeatFailures}`, | ||
@@ -1171,2 +1271,6 @@ ) | ||
private getCurrentItemHandledByTheQueue() { | ||
return this.currentItemHandledByTheQueue.expectedSeqNumberForACK | ||
} | ||
/** | ||
@@ -1215,3 +1319,3 @@ * Increment the tunneling sequence number | ||
this.emit(KNXClientEvents.error, timeoutErr) | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`KNXClient: _setTimerWaitingForACK: ${ | ||
@@ -1223,9 +1327,17 @@ timeoutErr.message || 'Undef error' | ||
// 26/12/2021 // If no ACK received, resend the datagram once with the same sequence number | ||
this.setTimerWaitingForACK(knxTunnelingRequest) | ||
this.send(knxTunnelingRequest) | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`KNXClient: _setTimerWaitingForACK: ${ | ||
timeoutErr.message || 'Undef error' | ||
} no ACK received. Retransmit datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`, | ||
} no ACK received. Retransmit datagram with seqNumber ${ | ||
this.currentItemHandledByTheQueue | ||
.expectedSeqNumberForACK | ||
} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`, | ||
) | ||
this.send( | ||
knxTunnelingRequest, | ||
knxTunnelingRequest, | ||
true, | ||
this.currentItemHandledByTheQueue | ||
.expectedSeqNumberForACK, | ||
) | ||
} | ||
@@ -1247,3 +1359,3 @@ }, | ||
sProcessInboundLog += ` srcAddress: ${JSON.stringify(rinfo)}` | ||
this.sysLogger.trace( | ||
this.sysLogger?.trace( | ||
`Received KNX packet: _processInboundMessage, ${sProcessInboundLog} ChannelID:${this._channelID}` || | ||
@@ -1315,3 +1427,3 @@ `??` + | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Received KNX packet: CONNECT_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1326,3 +1438,3 @@ ) | ||
) { | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Received KNX packet: DISCONNECT_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1348,3 +1460,3 @@ ) | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`Received KNX packet: DISCONNECT_REQUEST, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1373,3 +1485,3 @@ ) | ||
if (knxTunnelingRequest.channelID !== this._channelID) { | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Received KNX packet: TUNNELING: L_DATA_IND, NOT FOR ME: MyChannelID:${this._channelID} ReceivedPacketChannelID: ${knxTunnelingRequest.channelID} ReceivedPacketseqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1389,5 +1501,10 @@ ) | ||
) | ||
this.send(knxTunnelAck) | ||
this.send( | ||
knxTunnelAck, | ||
undefined, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
} catch (error) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`Received KNX packet: TUNNELING: L_DATA_IND, ERROR BUOLDING THE TUNNELINK ACK: ${error.message} MyChannelID:${this._channelID} ReceivedPacketChannelID: ${knxTunnelingRequest.channelID} ReceivedPacketseqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1411,3 +1528,3 @@ ) | ||
sDebugString += ` dstAddress: ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}` | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Received KNX packet: TUNNELING: L_DATA_IND, ${sDebugString} ChannelID:${this._channelID} seqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1425,3 +1542,3 @@ ) | ||
) { | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Received KNX packet: TUNNELING: L_DATA_CON, ChannelID:${this._channelID} seqCounter:${knxTunnelingRequest.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1436,3 +1553,3 @@ ) | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Received KNX packet: TUNNELING: TUNNELING_ACK, ChannelID:${this._channelID} seqCounter:${knxTunnelingAck.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1443,3 +1560,6 @@ ) | ||
if (!this._options.suppress_ack_ldatareq) { | ||
if (knxTunnelingAck.seqCounter === this.getSeqNumber()) { | ||
if ( | ||
knxTunnelingAck.seqCounter === | ||
this.getCurrentItemHandledByTheQueue() | ||
) { | ||
this.clearTimer(KNXTimer.ACK) | ||
@@ -1455,3 +1575,3 @@ this._numFailedTelegramACK = 0 // 25/12/2021 clear the current ACK failed telegram number | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Received KNX packet: TUNNELING: DELETED_TUNNELING_ACK FROM PENDING ACK's, ChannelID:${this._channelID} seqCounter:${knxTunnelingAck.seqCounter} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1462,4 +1582,4 @@ ) | ||
this.sysLogger.error( | ||
`Received KNX packet: TUNNELING: Unexpected Tunnel Ack with seqCounter = ${knxTunnelingAck.seqCounter}`, | ||
this.sysLogger?.error( | ||
`Received KNX packet: TUNNELING: Unexpected Tunnel Ack with seqCounter = ${knxTunnelingAck.seqCounter}, expecting ${this.getSeqNumber()}`, | ||
) | ||
@@ -1486,3 +1606,3 @@ // this.emit(KNXClientEvents.error, `Unexpected Tunnel Ack ${knxTunnelingAck.seqCounter}`); | ||
sDebugString += ` dstAddress: ${knxRoutingInd.cEMIMessage.dstAddress.toString()}` | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Received KNX packet: ROUTING: L_DATA_IND, ${sDebugString} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1496,3 +1616,3 @@ ) | ||
) { | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Received KNX packet: ROUTING: L_DATA_CON, Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1507,3 +1627,3 @@ ) | ||
) { | ||
this.sysLogger.debug( | ||
this.sysLogger?.debug( | ||
`Received KNX packet: CONNECTIONSTATE_RESPONSE, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1545,3 +1665,3 @@ ) | ||
} catch (e) { | ||
this.sysLogger.error( | ||
this.sysLogger?.error( | ||
`Received KNX packet: Error processing inbound message: ${e.message} ${sProcessInboundLog} ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}. This means that KNX-Ultimate received a malformed Header or CEMI message from your KNX Gateway.`, | ||
@@ -1562,2 +1682,5 @@ ) | ||
), | ||
undefined, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
@@ -1571,2 +1694,5 @@ } | ||
), | ||
undefined, | ||
false, | ||
this.getSeqNumber(), | ||
// KNX_CONSTANTS.KNX_PORT, | ||
@@ -1584,3 +1710,8 @@ // KNX_CONSTANTS.KNX_IP, | ||
// } | ||
this.send(KNXProtocol.newKNXConnectRequest(cri)) | ||
this.send( | ||
KNXProtocol.newKNXConnectRequest(cri), | ||
undefined, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
} | ||
@@ -1595,3 +1726,8 @@ | ||
// } | ||
this.send(KNXProtocol.newKNXConnectionStateRequest(channelID)) | ||
this.send( | ||
KNXProtocol.newKNXConnectionStateRequest(channelID), | ||
undefined, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
} | ||
@@ -1606,3 +1742,8 @@ | ||
// } | ||
this.send(KNXProtocol.newKNXDisconnectRequest(channelID)) | ||
this.send( | ||
KNXProtocol.newKNXDisconnectRequest(channelID), | ||
undefined, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
} | ||
@@ -1614,3 +1755,8 @@ | ||
) { | ||
this.send(KNXProtocol.newKNXDisconnectResponse(channelID, status)) | ||
this.send( | ||
KNXProtocol.newKNXDisconnectResponse(channelID, status), | ||
undefined, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
} | ||
@@ -1626,4 +1772,9 @@ | ||
) | ||
this.send(KNXProtocol.newKNXSecureSessionRequest(cri, oHPAI)) | ||
this.send( | ||
KNXProtocol.newKNXSecureSessionRequest(cri, oHPAI), | ||
undefined, | ||
false, | ||
this.getSeqNumber(), | ||
) | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1673778
31414
150
6
3
+ Addedlimiter@2.1.0
+ Addedjust-performance@4.3.0(transitive)
+ Addedlimiter@2.1.0(transitive)