From 059c9160a57bf004d21d9e060d95d504af976b80 Mon Sep 17 00:00:00 2001 From: Frankie Chow Date: Fri, 21 Aug 2026 07:31:45 +0100 Subject: [PATCH] fix(client): keep array index 0 in ReadProperty and WriteProperty requests Index 0 of a BACnet array property holds the number of elements, which is how a client discovers the size of a property such as object-list before reading its entries individually. Both the client option handling and the ReadProperty encoder passed the index through 'arrayIndex || ASN1_ARRAY_ALL', which turns the valid index 0 into ASN1_ARRAY_ALL and therefore requests the entire array instead. On a device with a large object-list that whole-array response does not fit into a single APDU, so the request is aborted or times out and the client never learns the object count. --- lib/client.js | 4 ++-- lib/services/read-property.js | 2 +- test/unit/service-read-property.spec.js | 11 +++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/client.js b/lib/client.js index 58e6607f..c7ca9012 100644 --- a/lib/client.js +++ b/lib/client.js @@ -567,7 +567,7 @@ class Client extends EventEmitter { maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65, maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476, invokeId: options.invokeId || this._getInvokeId(), - arrayIndex: options.arrayIndex || baEnum.ASN1_ARRAY_ALL + arrayIndex: options.arrayIndex !== undefined ? options.arrayIndex : baEnum.ASN1_ARRAY_ALL }; 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); @@ -618,7 +618,7 @@ class Client extends EventEmitter { maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65, maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476, invokeId: options.invokeId || this._getInvokeId(), - arrayIndex: options.arrayIndex || baEnum.ASN1_ARRAY_ALL, + arrayIndex: options.arrayIndex !== undefined ? options.arrayIndex : baEnum.ASN1_ARRAY_ALL, priority: options.priority }; const buffer = this._getBuffer(); diff --git a/lib/services/read-property.js b/lib/services/read-property.js index c427b7aa..6c9f25de 100644 --- a/lib/services/read-property.js +++ b/lib/services/read-property.js @@ -11,7 +11,7 @@ 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); } }; diff --git a/test/unit/service-read-property.spec.js b/test/unit/service-read-property.spec.js index 4878b8bf..6980ffb9 100644 --- a/test/unit/service-read-property.spec.js +++ b/test/unit/service-read-property.spec.js @@ -37,6 +37,17 @@ describe('bacstack - Services layer ReadProperty unit', () => { property: {id: 85, index: 2} }); }); + + it('should successfully encode and decode with array index 0', () => { + const buffer = utils.getBuffer(); + baServices.readProperty.encode(buffer, 4, 630, 85, 0); + const result = baServices.readProperty.decode(buffer.buffer, 0, buffer.offset); + delete result.len; + expect(result).toEqual({ + objectId: {type: 4, instance: 630}, + property: {id: 85, index: 0} + }); + }); }); describe('ReadPropertyAcknowledge', () => {