From 320a81cbd71266c80cad7843ac2c19b430f375b3 Mon Sep 17 00:00:00 2001 From: jzimmen Date: Mon, 26 Aug 2019 16:55:46 -0500 Subject: [PATCH 1/8] Version 20 - Support unicast responses when acting as a bacnet server - Passes through addressing to more layers for server handling - Support option for disabling port re-use (avoids silent takeovers) --- lib/client.js | 47 +++++++++++++++++-------- lib/services/write-property-multiple.js | 3 ++ lib/transport.js | 10 ++++-- package.json | 2 +- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/lib/client.js b/lib/client.js index be8f21e1..3a0a462a 100644 --- a/lib/client.js +++ b/lib/client.js @@ -54,10 +54,13 @@ class Client extends EventEmitter { apduTimeout: options.apduTimeout || 3000 }; + options.reuseAddr = options.reuseAddr == null ? true: options.reuseAddr; + this._transport = this._settings.transport || new baTransport({ port: this._settings.port, interface: this._settings.interface, - broadcastAddress: this._settings.broadcastAddress + broadcastAddress: this._settings.broadcastAddress, + reuseAddr: options.reuseAddr }); // Setup code @@ -166,25 +169,25 @@ class Client extends EventEmitter { this._performDefaultSegmentHandling(this, receiver, type, service, invokeId, maxSegments, maxApdu, sequencenumber, first, moreFollows, buffer, offset, length); } - _processConfirmedServiceRequest(address, type, service, maxSegments, maxApdu, invokeId, buffer, offset, length) { + _processConfirmedServiceRequest(address, type, service, maxSegments, maxApdu, invokeId, buffer, offset, length, srcAddress,destAddress) { let result; debug('Handle this._processConfirmedServiceRequest'); if (service === baEnum.ConfirmedServiceChoice.READ_PROPERTY) { result = baServices.readProperty.decode(buffer, offset, length); if (!result) return debug('Received invalid readProperty message'); - this.emit('readProperty', {address: address, invokeId: invokeId, request: result}); + this.emit('readProperty', {address: address, invokeId: invokeId, request: result, srcAddress: srcAddress, destAddress:destAddress}); } else if (service === baEnum.ConfirmedServiceChoice.WRITE_PROPERTY) { result = baServices.writeProperty.decode(buffer, offset, length); if (!result) return debug('Received invalid writeProperty message'); - this.emit('writeProperty', {address: address, invokeId: invokeId, request: result}); + this.emit('writeProperty', {address: address, invokeId: invokeId, request: result, srcAddress: srcAddress, destAddress:destAddress}); } else if (service === baEnum.ConfirmedServiceChoice.READ_PROPERTY_MULTIPLE) { result = baServices.readPropertyMultiple.decode(buffer, offset, length); if (!result) return debug('Received invalid readPropertyMultiple message'); - this.emit('readPropertyMultiple', {address: address, invokeId: invokeId, request: result}); + this.emit('readPropertyMultiple', {address: address, invokeId: invokeId, request: result, srcAddress: srcAddress, destAddress:destAddress}); } else if (service === baEnum.ConfirmedServiceChoice.WRITE_PROPERTY_MULTIPLE) { result = baServices.writePropertyMultiple.decode(buffer, offset, length); if (!result) return debug('Received invalid writePropertyMultiple message'); - this.emit('writePropertyMultiple', {address: address, invokeId: invokeId, request: result}); + this.emit('writePropertyMultiple', {address: address, invokeId: invokeId, request: result, srcAddress: srcAddress, destAddress:destAddress}); } else if (service === baEnum.ConfirmedServiceChoice.CONFIRMED_COV_NOTIFICATION) { result = baServices.covNotify.decode(buffer, offset, length); if (!result) return debug('Received invalid covNotify message'); @@ -374,7 +377,7 @@ class Client extends EventEmitter { } } - _handlePdu(address, type, buffer, offset, length, addressObject) { + _handlePdu(address, type, buffer, offset, length, addressObject, destAddressObject) { let result; // Handle different PDU types switch (type & baEnum.PDU_TYPE_MASK) { @@ -413,7 +416,7 @@ class Client extends EventEmitter { case baEnum.PduTypes.CONFIRMED_REQUEST: result = baApdu.decodeConfirmedServiceRequest(buffer, offset); if ((type & baEnum.PduConReqBits.SEGMENTED_MESSAGE) === 0) { - this._processConfirmedServiceRequest(address, result.type, result.service, result.maxSegments, result.maxApdu, result.invokeId, buffer, offset + result.len, length - result.len); + this._processConfirmedServiceRequest(address, result.type, result.service, result.maxSegments, result.maxApdu, result.invokeId, buffer, offset + result.len, length - result.len, addressObject , destAddressObject ); } else { this._processSegment(address, result.type, result.service, result.invokeId, result.maxSegments, result.maxApdu, true, result.sequencenumber, result.proposedWindowNumber, buffer, offset + result.len, length - result.len); } @@ -429,13 +432,21 @@ class Client extends EventEmitter { if (msgLength <= 0) return debug('No NPDU data -> Drop package'); // Parse baNpdu header const result = baNpdu.decode(buffer, offset); - var addressObject; + var addressObject, destAddress; + // @todo a MSTP Dest & Source could occur. this case is not handled. if(typeof(result.source) != 'undefined'){ addressObject = result.source; addressObject.ip = remoteAddress; - }else{ + }else{ addressObject = remoteAddress; - } + } + + if(typeof(result.destination) != 'undefined'){ + destAddress = result.destination; + destAddress.ip = remoteAddress; + }else{ + destAddress = remoteAddress; + } if (!result) return debug('Received invalid NPDU header -> Drop package'); if (result.funct & baEnum.NpduControlBits.NETWORK_LAYER_MESSAGE) { @@ -445,7 +456,7 @@ class Client extends EventEmitter { msgLength -= result.len; if (msgLength <= 0) return debug('No APDU data -> Drop package'); const apduType = baApdu.getDecodedType(buffer, offset); - this._handlePdu(remoteAddress, apduType, buffer, offset, msgLength,addressObject); + this._handlePdu(remoteAddress, apduType, buffer, offset, msgLength,addressObject,destAddress); } _receiveData(buffer, remoteAddress) { @@ -1160,13 +1171,19 @@ class Client extends EventEmitter { this._transport.send(buffer.buffer, buffer.offset, receiver); } - iAmResponse(deviceId, segmentation, vendorId) { + /** + * Responds to both broadcast and unicast I-AM responses + * @function bacstack.iAmResponse + */ + iAmResponse(deviceId, segmentation, vendorId, destAddress,srcAddress) { const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, this._transport.getBroadcastAddress()); + var dAddress = destAddress == undefined ? this._transport.getBroadcastAddress() : destAddress; + var sAddress = srcAddress == undefined ? this._transport.getBroadcastAddress() : srcAddress; + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, dAddress , sAddress ); baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.I_AM); baServices.iAmBroadcast.encode(buffer, deviceId, this._transport.getMaxPayload(), segmentation, vendorId); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_BROADCAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, this._transport.getBroadcastAddress()); + this._transport.send(buffer.buffer, buffer.offset, dAddress); } iHaveResponse(deviceId, objectId, objectName) { diff --git a/lib/services/write-property-multiple.js b/lib/services/write-property-multiple.js index 6f5dd4d3..bb3536c6 100644 --- a/lib/services/write-property-multiple.js +++ b/lib/services/write-property-multiple.js @@ -84,6 +84,9 @@ module.exports.decode = (buffer, offset, apduLen) => { } newEntry.priority = priority; _values.push(newEntry); + if (baAsn1.decodeIsClosingTagNumber(buffer, offset + len, 1)){ + len++; + } } if (!baAsn1.decodeIsClosingTagNumber(buffer, offset + len, 1)) return; len++; diff --git a/lib/transport.js b/lib/transport.js index 721574d9..43334582 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -7,9 +7,15 @@ class Transport extends EventEmitter { constructor(settings) { super(); this._settings = settings; - this._server = createSocket({type: 'udp4', reuseAddr: true}); + this._server = createSocket({type: 'udp4', reuseAddr: settings.reuseAddr}); this._server.on('message', (msg, rinfo) => this.emit('message', msg, rinfo.address)); - this._server.on('error', (err) => this.emit('message', err)); + this._server.on('error', (err) => { + if(err.message.includes('bind') || err.message.includes('EADDRINUSE')){ + console.log('Error, failed to bind bacnet port',err); + throw(err); + } + this.emit('message', err); + }); } getBroadcastAddress() { diff --git a/package.json b/package.json index efad8fca..cfa422ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "natezimmer_bacstack", - "version": "0.0.1-beta.19", + "version": "0.0.1-beta.20", "description": "A BACnet protocol stack written in pure JavaScript.", "main": "index.js", "scripts": { From 43df1dfa9b313011485a16afd9ce301798743e0f Mon Sep 17 00:00:00 2001 From: jzimmen Date: Wed, 13 Nov 2019 16:38:43 -0600 Subject: [PATCH 2/8] RP array index 0 support for encoding MSTP related fixes for adding Sender Addressing to outgoing packets --- lib/client.js | 16 ++++++++++------ lib/services/read-property.js | 4 +++- package.json | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/client.js b/lib/client.js index 3a0a462a..1370d575 100644 --- a/lib/client.js +++ b/lib/client.js @@ -594,6 +594,9 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId(), arrayIndex: options.arrayIndex || baEnum.ASN1_ARRAY_ALL }; + if(options.arrayIndex != undefined){ + settings.arrayIndex = options.arrayIndex; + } const buffer = this._getBuffer(); baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); const type = baEnum.PduTypes.CONFIRMED_REQUEST | (settings.maxSegments !== baEnum.MaxSegmentsAccepted.SEGMENTS_0 ? baEnum.PduConReqBits.SEGMENTED_RESPONSE_ACCEPTED : 0); @@ -1153,18 +1156,18 @@ class Client extends EventEmitter { } // Public Device Functions - readPropertyResponse(receiver, invokeId, objectId, property, value) { + readPropertyResponse(receiver, invokeId, objectId, property, value,source) { const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE,receiver,source); baApdu.encodeComplexAck(buffer, baEnum.PduTypes.COMPLEX_ACK, baEnum.ConfirmedServiceChoice.READ_PROPERTY, invokeId); baServices.readProperty.encodeAcknowledge(buffer, objectId, property.id, property.index, value); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); this._transport.send(buffer.buffer, buffer.offset, receiver); } - readPropertyMultipleResponse(receiver, invokeId, values) { + readPropertyMultipleResponse(receiver, invokeId, values,source) { const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver,source); baApdu.encodeComplexAck(buffer, baEnum.PduTypes.COMPLEX_ACK, baEnum.ConfirmedServiceChoice.READ_PROPERTY_MULTIPLE, invokeId); baServices.readPropertyMultiple.encodeAcknowledge(buffer, values); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); @@ -1203,9 +1206,10 @@ class Client extends EventEmitter { this._transport.send(buffer.buffer, buffer.offset, receiver); } - errorResponse(receiver, service, invokeId, errorClass, errorCode) { + errorResponse(receiver, service, invokeId, errorClass, errorCode,srcAddress) { const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver); + var sAddress = srcAddress == undefined ? this._transport.getBroadcastAddress() : srcAddress; + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver,sAddress); baApdu.encodeError(buffer, baEnum.PduTypes.ERROR, service, invokeId); baServices.error.encode(buffer, errorClass, errorCode); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); diff --git a/lib/services/read-property.js b/lib/services/read-property.js index 63e877cb..9db37ab1 100644 --- a/lib/services/read-property.js +++ b/lib/services/read-property.js @@ -11,7 +11,9 @@ module.exports.encode = (buffer, objectType, objectInstance, propertyId, arrayIn baAsn1.encodeContextEnumerated(buffer, 1, propertyId); } if (arrayIndex !== baEnum.ASN1_ARRAY_ALL) { - baAsn1.encodeContextUnsigned(buffer, 2, arrayIndex || baEnum.ASN1_ARRAY_ALL); + //baAsn1.encodeContextUnsigned(buffer, 2, arrayIndex || baEnum.ASN1_ARRAY_ALL); + // Want to encode 0 + baAsn1.encodeContextUnsigned(buffer, 2, arrayIndex) } }; diff --git a/package.json b/package.json index cfa422ec..264ed4ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "natezimmer_bacstack", - "version": "0.0.1-beta.20", + "version": "0.0.1-beta.21", "description": "A BACnet protocol stack written in pure JavaScript.", "main": "index.js", "scripts": { From 71df9f4e269339f1a511da1658a5814c49756df7 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerman Date: Tue, 1 Sep 2020 11:51:12 -0500 Subject: [PATCH 3/8] Fix for stale fragments / segments --- lib/client.js | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/client.js b/lib/client.js index 1370d575..88156951 100644 --- a/lib/client.js +++ b/lib/client.js @@ -44,6 +44,7 @@ class Client extends EventEmitter { this._invokeStore = {}; this._lastSequenceNumber = 0; + this._sequenceTimeout = null this._segmentStore = []; this._settings = { @@ -149,6 +150,13 @@ class Client extends EventEmitter { } } + _handleSequenceTimeout(invokeId){ + if(this._sequenceTimeout){ + clearInterval(this._sequenceTimeout); + } + this._sequenceTimeout = setTimeout(()=>{this._lastSequenceNumber = 0},this._settings.apduTimeout); + } + _processSegment(receiver, type, service, invokeId, maxSegments, maxApdu, server, sequencenumber, proposedWindowNumber, buffer, offset, length) { let first = false; if (sequencenumber === 0 && this._lastSequenceNumber === 0) { @@ -159,6 +167,7 @@ class Client extends EventEmitter { } } this._lastSequenceNumber = sequencenumber; + this._handleSequenceTimeout(); const moreFollows = type & baEnum.PduConReqBits.MORE_FOLLOWS; if (!moreFollows) { this._lastSequenceNumber = 0; diff --git a/package.json b/package.json index 264ed4ba..6272916c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "natezimmer_bacstack", - "version": "0.0.1-beta.21", + "version": "0.0.1-beta.22", "description": "A BACnet protocol stack written in pure JavaScript.", "main": "index.js", "scripts": { From 785c36b25db8d33f2378aed34fd787dc16721ce3 Mon Sep 17 00:00:00 2001 From: jzimmen Date: Tue, 26 Jan 2021 06:33:30 -0600 Subject: [PATCH 4/8] adding routing support for more bacnet services --- lib/client.js | 91 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/lib/client.js b/lib/client.js index 88156951..6eec64f5 100644 --- a/lib/client.js +++ b/lib/client.js @@ -840,11 +840,12 @@ class Client extends EventEmitter { password: options.password }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0 ); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.REINITIALIZE_DEVICE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.reinitializeDevice.encode(buffer, state, settings.password); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { next(err); }); @@ -858,11 +859,13 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0 ); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ATOMIC_WRITE_FILE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); - baServices.atomicWriteFile.encode(buffer, false, objectId, position, fileBuffer); + // Edit, enforcing 'stream' + baServices.atomicWriteFile.encode(buffer, true, objectId, position, fileBuffer); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); const result = baServices.atomicWriteFile.decodeAcknowledge(data.buffer, data.offset, data.length); @@ -879,11 +882,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ATOMIC_READ_FILE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.atomicReadFile.encode(buffer, true, objectId, position, count); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); const result = baServices.atomicReadFile.decodeAcknowledge(data.buffer, data.offset, data.length); @@ -900,11 +904,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.READ_RANGE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.readRange.encode(buffer, objectId, baEnum.PropertyIdentifier.LOG_BUFFER, baEnum.ASN1_ARRAY_ALL, baEnum.ReadRangeType.BY_POSITION, idxBegin, new Date(), quantity); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); const result = baServices.readRange.decodeAcknowledge(data.buffer, data.offset, data.length); @@ -921,11 +926,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.subscribeCov.encode(buffer, subscribeId, objectId, cancel, issueConfirmedNotifications, lifetime); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); next(); @@ -940,11 +946,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV_PROPERTY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.subscribeProperty.encode(buffer, subscribeId, objectId, cancel, issueConfirmedNotifications, 0, monitoredProperty, false, 0x0f); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); next(); @@ -959,11 +966,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CREATE_OBJECT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.createObject.encode(buffer, objectId, values); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); next(); @@ -978,11 +986,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.DELETE_OBJECT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.deleteObject.encode(buffer, objectId); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); next(); @@ -997,11 +1006,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.REMOVE_LIST_ELEMENT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.addListElement.encode(buffer, objectId, reference.id, reference.index, values); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); next(); @@ -1016,11 +1026,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ADD_LIST_ELEMENT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.addListElement.encode(buffer, objectId, reference.id, reference.index, values); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); next(); @@ -1035,10 +1046,11 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.GET_ALARM_SUMMARY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); const result = baServices.alarmSummary.decode(data.buffer, data.offset, data.length); @@ -1055,11 +1067,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.GET_EVENT_INFORMATION, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baAsn1.encodeContextObjectId(buffer, 0, objectId.type, objectId.instance); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); const result = baServices.eventInformation.decode(data.buffer, data.offset, data.length); @@ -1076,11 +1089,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ACKNOWLEDGE_ALARM, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.alarmAcknowledge.encode(buffer, 57, objectId, eventState, ackText, evTimeStamp, ackTimeStamp); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); next(); @@ -1095,11 +1109,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CONFIRMED_PRIVATE_TRANSFER, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.privateTransfer.encode(buffer, vendorId, serviceNumber, data); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); next(); @@ -1108,11 +1123,12 @@ class Client extends EventEmitter { unconfirmedPrivateTransfer(address, vendorId, serviceNumber, data) { const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.UNCONFIRMED_PRIVATE_TRANSFER); baServices.privateTransfer.encode(buffer, vendorId, serviceNumber, data); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); } getEnrollmentSummary(address, acknowledgmentFilter, options, next) { @@ -1123,11 +1139,12 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.GET_ENROLLMENT_SUMMARY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.getEnrollmentSummary.encode(buffer, acknowledgmentFilter, options.enrollmentFilter, options.eventStateFilter, options.eventTypeFilter, options.priorityFilter, options.notificationClassFilter); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); const result = baServices.getEnrollmentSummary.decodeAcknowledge(data.buffer, data.offset, data.length); @@ -1138,11 +1155,12 @@ class Client extends EventEmitter { unconfirmedEventNotification(address, eventNotification) { const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.UNCONFIRMED_EVENT_NOTIFICATION); baServices.eventNotifyData.encode(buffer, eventNotification); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, address); + var sendAddress = typeof(address) != 'string' ? address.ip : address; + this._transport.send(buffer.buffer, buffer.offset, sendAddress); } confirmedEventNotification(address, eventNotification, options, next) { @@ -1153,10 +1171,11 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CONFIRMED_EVENT_NOTIFICATION, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.eventNotifyData.encode(buffer, eventNotification); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); + var sendAddress = typeof(address) != 'string' ? address.ip : address; this._transport.send(buffer.buffer, buffer.offset, address); this._addCallback(settings.invokeId, (err, data) => { if (err) return next(err); From eb5b2300e2fc959dbfa66fad03b70a06a8e141f5 Mon Sep 17 00:00:00 2001 From: jzimmen Date: Tue, 26 Jan 2021 06:37:27 -0600 Subject: [PATCH 5/8] version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6272916c..7d5ac7d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "natezimmer_bacstack", - "version": "0.0.1-beta.22", + "version": "0.0.1-beta.23", "description": "A BACnet protocol stack written in pure JavaScript.", "main": "index.js", "scripts": { From c52894fc1d1fe5fab055e445a1b59d015d915b4d Mon Sep 17 00:00:00 2001 From: Nathan Zimmerman Date: Wed, 3 Feb 2021 23:29:00 -0600 Subject: [PATCH 6/8] source listen port option added --- lib/client.js | 3 +++ lib/transport.js | 2 +- package.json | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/client.js b/lib/client.js index 6eec64f5..8ae17699 100644 --- a/lib/client.js +++ b/lib/client.js @@ -49,16 +49,19 @@ class Client extends EventEmitter { this._settings = { port: options.port || 47808, + listenPort: options.listenPort || options.port || 47808, interface: options.interface, transport: options.transport, broadcastAddress: options.broadcastAddress || '255.255.255.255', apduTimeout: options.apduTimeout || 3000 + }; options.reuseAddr = options.reuseAddr == null ? true: options.reuseAddr; this._transport = this._settings.transport || new baTransport({ port: this._settings.port, + listenPort: this._settings.listenPort, interface: this._settings.interface, broadcastAddress: this._settings.broadcastAddress, reuseAddr: options.reuseAddr diff --git a/lib/transport.js b/lib/transport.js index 43334582..6a3c9c83 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -31,7 +31,7 @@ class Transport extends EventEmitter { } open() { - this._server.bind(this._settings.port, this._settings.interface, () => { + this._server.bind(this._settings.listenPort, this._settings.interface, () => { this._server.setBroadcast(true); }); } diff --git a/package.json b/package.json index 7d5ac7d9..475bb7ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "natezimmer_bacstack", - "version": "0.0.1-beta.23", + "version": "0.0.1-beta.24", "description": "A BACnet protocol stack written in pure JavaScript.", "main": "index.js", "scripts": { From 4ade47ff023e974e5fdf2d90825e667597371c19 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerman Date: Fri, 5 Feb 2021 14:48:52 -0600 Subject: [PATCH 7/8] Better default case for tags when property type is unknown --- lib/asn1.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/asn1.js b/lib/asn1.js index 6579798a..7d2eb2ab 100644 --- a/lib/asn1.js +++ b/lib/asn1.js @@ -1077,7 +1077,7 @@ const bacappDecodeData = (buffer, offset, maxLength, tagDataType, lenValueType) }; const bacappContextTagType = (property, tagNumber) => { - let tag = 0; + let tag = tagNumber; switch (property) { case baEnum.PropertyIdentifier.ACTUAL_SHED_LEVEL: case baEnum.PropertyIdentifier.REQUESTED_SHED_LEVEL: diff --git a/package.json b/package.json index 475bb7ba..a603559b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "natezimmer_bacstack", - "version": "0.0.1-beta.24", + "version": "0.0.1-beta.25", "description": "A BACnet protocol stack written in pure JavaScript.", "main": "index.js", "scripts": { From d482f4c1c488c8804659e9f841c3ce2b7728351a Mon Sep 17 00:00:00 2001 From: Nathan Zimmerman Date: Sun, 16 May 2021 16:25:50 -0500 Subject: [PATCH 8/8] Fixing hop count and expanding service support for MS/TP devices --- lib/client.js | 52 +++++++++++++++++++++++++-------------------------- package.json | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/client.js b/lib/client.js index 8ae17699..92b2519c 100644 --- a/lib/client.js +++ b/lib/client.js @@ -203,77 +203,77 @@ class Client extends EventEmitter { } else if (service === baEnum.ConfirmedServiceChoice.CONFIRMED_COV_NOTIFICATION) { result = baServices.covNotify.decode(buffer, offset, length); if (!result) return debug('Received invalid covNotify message'); - this.emit('covNotify', {address: address, invokeId: invokeId, request: result}); + this.emit('covNotify', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.ATOMIC_WRITE_FILE) { result = baServices.atomicWriteFile.decode(buffer, offset, length); if (!result) return debug('Received invalid atomicWriteFile message'); - this.emit('atomicWriteFile', {address: address, invokeId: invokeId, request: result}); + this.emit('atomicWriteFile', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.ATOMIC_READ_FILE) { result = baServices.atomicReadFile.decode(buffer, offset, length); if (!result) return debug('Received invalid atomicReadFile message'); - this.emit('atomicReadFile', {address: address, invokeId: invokeId, request: result}); + this.emit('atomicReadFile', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV) { result = baServices.subscribeCov.decode(buffer, offset, length); if (!result) return debug('Received invalid subscribeCOV message'); - this.emit('subscribeCOV', {address: address, invokeId: invokeId, request: result}); + this.emit('subscribeCOV', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV_PROPERTY) { result = baServices.subscribeProperty.decode(buffer, offset, length); if (!result) return debug('Received invalid subscribeProperty message'); - this.emit('subscribeProperty', {address: address, invokeId: invokeId, request: result}); + this.emit('subscribeProperty', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.DEVICE_COMMUNICATION_CONTROL) { result = baServices.deviceCommunicationControl.decode(buffer, offset, length); if (!result) return debug('Received invalid deviceCommunicationControl message'); - this.emit('deviceCommunicationControl', {address: address, invokeId: invokeId, request: result}); + this.emit('deviceCommunicationControl', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.REINITIALIZE_DEVICE) { result = baServices.reinitializeDevice.decode(buffer, offset, length); if (!result) return debug('Received invalid reinitializeDevice message'); - this.emit('reinitializeDevice', {address: address, invokeId: invokeId, request: result}); + this.emit('reinitializeDevice', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.CONFIRMED_EVENT_NOTIFICATION) { result = baServices.eventNotifyData.decode(buffer, offset, length); if (!result) return debug('Received invalid eventNotifyData message'); - this.emit('eventNotifyData', {address: address, invokeId: invokeId, request: result}); + this.emit('eventNotifyData', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.READ_RANGE) { result = baServices.readRange.decode(buffer, offset, length); if (!result) return debug('Received invalid readRange message'); - this.emit('readRange', {address: address, invokeId: invokeId, request: result}); + this.emit('readRange', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.CREATE_OBJECT) { result = baServices.createObject.decode(buffer, offset, length); if (!result) return debug('Received invalid createObject message'); - this.emit('createObject', {address: address, invokeId: invokeId, request: result}); + this.emit('createObject', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.DELETE_OBJECT) { result = baServices.deleteObject.decode(buffer, offset, length); if (!result) return debug('Received invalid deleteObject message'); - this.emit('deleteObject', {address: address, invokeId: invokeId, request: result}); + this.emit('deleteObject', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.ACKNOWLEDGE_ALARM) { result = baServices.alarmAcknowledge.decode(buffer, offset, length); if (!result) return debug('Received invalid alarmAcknowledge message'); - this.emit('alarmAcknowledge', {address: address, invokeId: invokeId, request: result}); + this.emit('alarmAcknowledge', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.GET_ALARM_SUMMARY) { this.emit('getAlarmSummary', {address: address, invokeId: invokeId}); } else if (service === baEnum.ConfirmedServiceChoice.GET_ENROLLMENT_SUMMARY) { result = baServices.getEnrollmentSummary.decode(buffer, offset, length); if (!result) return debug('Received invalid getEntrollmentSummary message'); - this.emit('getEntrollmentSummary', {address: address, invokeId: invokeId, request: result}); + this.emit('getEntrollmentSummary', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.GET_EVENT_INFORMATION) { result = baServices.getEventInformation.decode(buffer, offset, length); if (!result) return debug('Received invalid getEventInformation message'); - this.emit('getEventInformation', {address: address, invokeId: invokeId, request: result}); + this.emit('getEventInformation', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.LIFE_SAFETY_OPERATION) { result = baServices.lifeSafetyOperation.decode(buffer, offset, length); if (!result) return debug('Received invalid lifeSafetyOperation message'); - this.emit('lifeSafetyOperation', {address: address, invokeId: invokeId, request: result}); + this.emit('lifeSafetyOperation', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.ADD_LIST_ELEMENT) { result = baServices.addListElement.decode(buffer, offset, length); if (!result) return debug('Received invalid addListElement message'); - this.emit('addListElement', {address: address, invokeId: invokeId, request: result}); + this.emit('addListElement', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.REMOVE_LIST_ELEMENT) { result = baServices.addListElement.decode(buffer, offset, length); if (!result) return debug('Received invalid removeListElement message'); - this.emit('removeListElement', {address: address, invokeId: invokeId, request: result}); + this.emit('removeListElement', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else if (service === baEnum.ConfirmedServiceChoice.CONFIRMED_PRIVATE_TRANSFER) { result = baServices.privateTransfer.decode(buffer, offset, length); if (!result) return debug('Received invalid privateTransfer message'); - this.emit('privateTransfer', {address: address, invokeId: invokeId, request: result}); + this.emit('privateTransfer', {address: address, invokeId: invokeId, request: result, srcAddress:srcAddress}); } else { debug('Received unsupported confirmed service request'); } @@ -805,7 +805,7 @@ class Client extends EventEmitter { password: options.password }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.DEVICE_COMMUNICATION_CONTROL, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.deviceCommunicationControl.encode(buffer, timeDuration, enableDisable, settings.password); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); @@ -885,7 +885,7 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ATOMIC_READ_FILE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.atomicReadFile.encode(buffer, true, objectId, position, count); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); @@ -907,7 +907,7 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.READ_RANGE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.readRange.encode(buffer, objectId, baEnum.PropertyIdentifier.LOG_BUFFER, baEnum.ASN1_ARRAY_ALL, baEnum.ReadRangeType.BY_POSITION, idxBegin, new Date(), quantity); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); @@ -929,7 +929,7 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.subscribeCov.encode(buffer, subscribeId, objectId, cancel, issueConfirmedNotifications, lifetime); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); @@ -949,7 +949,7 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV_PROPERTY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.subscribeProperty.encode(buffer, subscribeId, objectId, cancel, issueConfirmedNotifications, 0, monitoredProperty, false, 0x0f); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); @@ -969,7 +969,7 @@ class Client extends EventEmitter { invokeId: options.invokeId || this._getInvokeId() }; const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null , DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0); baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CREATE_OBJECT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0); baServices.createObject.encode(buffer, objectId, values); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); @@ -1231,10 +1231,10 @@ class Client extends EventEmitter { simpleAckResponse(receiver, service, invokeId) { const buffer = this._getBuffer(); - baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver); + baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver, null, DEFAULT_HOP_COUNT); baApdu.encodeSimpleAck(buffer, baEnum.PduTypes.SIMPLE_ACK, service, invokeId); baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset); - this._transport.send(buffer.buffer, buffer.offset, receiver); + this._transport.send(buffer.buffer, buffer.offset, receiver.ip || receiver.address ); } errorResponse(receiver, service, invokeId, errorClass, errorCode,srcAddress) { diff --git a/package.json b/package.json index a603559b..6742e6cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "natezimmer_bacstack", - "version": "0.0.1-beta.25", + "version": "0.0.1-beta.26", "description": "A BACnet protocol stack written in pure JavaScript.", "main": "index.js", "scripts": {