@bitpoolos/edge-bacnet 1.6.9 → 1.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +1 -1
  3. package/resources/node-bacstack-ts/dist/index.js +3 -0
  4. package/resources/node-bacstack-ts/dist/lib/apdu.js +193 -0
  5. package/resources/node-bacstack-ts/dist/lib/asn1.js +1671 -0
  6. package/resources/node-bacstack-ts/dist/lib/bvlc.js +47 -0
  7. package/resources/node-bacstack-ts/dist/lib/client.js +1679 -0
  8. package/resources/node-bacstack-ts/dist/lib/enum.js +2114 -0
  9. package/resources/node-bacstack-ts/dist/lib/npdu.js +112 -0
  10. package/resources/node-bacstack-ts/dist/lib/services/add-list-element.js +58 -0
  11. package/resources/node-bacstack-ts/dist/lib/services/alarm-acknowledge.js +93 -0
  12. package/resources/node-bacstack-ts/dist/lib/services/alarm-summary.js +42 -0
  13. package/resources/node-bacstack-ts/dist/lib/services/atomic-read-file.js +157 -0
  14. package/resources/node-bacstack-ts/dist/lib/services/atomic-write-file.js +136 -0
  15. package/resources/node-bacstack-ts/dist/lib/services/cov-notify.js +119 -0
  16. package/resources/node-bacstack-ts/dist/lib/services/create-object.js +104 -0
  17. package/resources/node-bacstack-ts/dist/lib/services/delete-object.js +21 -0
  18. package/resources/node-bacstack-ts/dist/lib/services/device-communication-control.js +46 -0
  19. package/resources/node-bacstack-ts/dist/lib/services/error.js +27 -0
  20. package/resources/node-bacstack-ts/dist/lib/services/event-information.js +100 -0
  21. package/resources/node-bacstack-ts/dist/lib/services/event-notify-data.js +219 -0
  22. package/resources/node-bacstack-ts/dist/lib/services/get-enrollment-summary.js +172 -0
  23. package/resources/node-bacstack-ts/dist/lib/services/get-event-information.js +135 -0
  24. package/resources/node-bacstack-ts/dist/lib/services/i-am-broadcast.js +59 -0
  25. package/resources/node-bacstack-ts/dist/lib/services/i-have-broadcast.js +34 -0
  26. package/resources/node-bacstack-ts/dist/lib/services/index.js +32 -0
  27. package/resources/node-bacstack-ts/dist/lib/services/life-safety-operation.js +40 -0
  28. package/resources/node-bacstack-ts/dist/lib/services/private-transfer.js +43 -0
  29. package/resources/node-bacstack-ts/dist/lib/services/read-property-multiple.js +44 -0
  30. package/resources/node-bacstack-ts/dist/lib/services/read-property.js +122 -0
  31. package/resources/node-bacstack-ts/dist/lib/services/read-range.js +201 -0
  32. package/resources/node-bacstack-ts/dist/lib/services/reinitialize-device.js +35 -0
  33. package/resources/node-bacstack-ts/dist/lib/services/subscribe-cov.js +55 -0
  34. package/resources/node-bacstack-ts/dist/lib/services/subscribe-property.js +93 -0
  35. package/resources/node-bacstack-ts/dist/lib/services/time-sync.js +31 -0
  36. package/resources/node-bacstack-ts/dist/lib/services/who-has.js +56 -0
  37. package/resources/node-bacstack-ts/dist/lib/services/who-is.js +45 -0
  38. package/resources/node-bacstack-ts/dist/lib/services/write-property-multiple.js +105 -0
  39. package/resources/node-bacstack-ts/dist/lib/services/write-property.js +90 -0
  40. package/resources/node-bacstack-ts/dist/lib/transport.js +86 -0
  41. package/resources/node-bacstack-ts/dist/lib/types.js +2 -0
@@ -0,0 +1,1679 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Client = void 0;
4
+ // Util Modules
5
+ const events_1 = require("events");
6
+ const debug_1 = require("debug");
7
+ const debug = (0, debug_1.default)('bacstack');
8
+ // Local Modules
9
+ const transport_1 = require("./transport");
10
+ const baServices = require("./services");
11
+ const baAsn1 = require("./asn1");
12
+ const baApdu = require("./apdu");
13
+ const baNpdu = require("./npdu");
14
+ const baBvlc = require("./bvlc");
15
+ const baEnum = require("./enum");
16
+ const DEFAULT_HOP_COUNT = 0xFF;
17
+ const BVLC_HEADER_LENGTH = 4;
18
+ /**
19
+ * To be able to communicate to BACNET devices, you have to initialize a new bacstack instance.
20
+ * @class bacstack
21
+ * @param {object=} this._settings - The options object used for parameterizing the bacstack.
22
+ * @param {number=} [options.port=47808] - BACNET communication port for listening and sending.
23
+ * @param {string=} options.interface - Specific BACNET communication interface if different from primary one.
24
+ * @param {string=} [options.broadcastAddress=255.255.255.255] - The address used for broadcast messages.
25
+ * @param {number=} [options.apduTimeout=3000] - The timeout in milliseconds until a transaction should be interpreted as error.
26
+ * @example
27
+ * const bacnet = require('bacstack');
28
+ *
29
+ * const client = new bacnet({
30
+ * port: 47809, // Use BAC1 as communication port
31
+ * interface: '192.168.251.10', // Listen on a specific interface
32
+ * broadcastAddress: '192.168.251.255', // Use the subnet broadcast address
33
+ * apduTimeout: 6000 // Wait twice as long for response
34
+ * });
35
+ */
36
+ class Client extends events_1.EventEmitter {
37
+ constructor(options) {
38
+ super();
39
+ this._invokeStore = {};
40
+ // Per-device invoke ID counters: keyed by device address string.
41
+ // BACnet (ASHRAE 135 §5.4) scopes invoke IDs per originator-destination pair,
42
+ // so each device gets its own independent 0-255 counter rather than sharing one
43
+ // global counter across all devices, which would exhaust after only 256 total
44
+ // in-flight requests regardless of how many devices are being polled.
45
+ this._deviceInvokeCounters = {};
46
+ this._lastSequenceNumber = 0;
47
+ this._segmentStore = [];
48
+ options = options || {};
49
+
50
+ this.portRangeMatrix = options.portRangeMatrix;
51
+
52
+ this._settings = {
53
+ port: options.port || 47808,
54
+ portRangeMatrix: this.portRangeMatrix || [47808],
55
+ interface: options.interface,
56
+ transport: options.transport,
57
+ broadcastAddress: options.broadcastAddress || '255.255.255.255',
58
+ apduTimeout: options.apduTimeout || 3000,
59
+ maxConcurrentRequests: options.maxConcurrentRequests || 250
60
+ };
61
+
62
+ this._transport = this._settings.transport || new transport_1.Transport({
63
+ port: this._settings.port,
64
+ interface: this._settings.interface,
65
+ broadcastAddress: this._settings.broadcastAddress,
66
+ portRangeMatrix: this._settings.portRangeMatrix
67
+ });
68
+
69
+ // Setup code
70
+ this._transport.on('message', this._receiveData.bind(this));
71
+ this._transport.on('error', this._receiveError.bind(this));
72
+ this._transport.open();
73
+ }
74
+ // Helper utils
75
+ /**
76
+ * Returns the number of requests currently awaiting responses
77
+ * @returns {number} Active request count
78
+ */
79
+ getActiveRequestCount() {
80
+ return Object.keys(this._invokeStore).length;
81
+ }
82
+ /**
83
+ * Check if a new request can be sent without risking invoke ID exhaustion
84
+ * @returns {boolean} True if safe to send a new request
85
+ */
86
+ canSendRequest() {
87
+ return this.getActiveRequestCount() < this._settings.maxConcurrentRequests;
88
+ }
89
+ /**
90
+ * Returns the max concurrent requests setting
91
+ * @returns {number}
92
+ */
93
+ getMaxConcurrentRequests() {
94
+ return this._settings.maxConcurrentRequests;
95
+ }
96
+ /**
97
+ * Derives a stable, unique string key for a BACnet device from its address.
98
+ *
99
+ * BACnet (ASHRAE 135 §5.4) scopes invoke IDs per originator-destination pair.
100
+ * For IP devices the IP address is the discriminator.
101
+ * For MSTP devices routed through a BBMD/router, multiple devices share the
102
+ * same router IP but have distinct network+MAC addresses — those are used as
103
+ * the key so their invoke ID spaces remain independent.
104
+ *
105
+ * @param {string|object} addressOrObject - The address passed to a service method,
106
+ * or the addressObject reconstructed from an incoming NPDU.
107
+ * @returns {string} A stable device key suitable for indexing _deviceInvokeCounters.
108
+ */
109
+ _makeDeviceKey(addressOrObject) {
110
+ if (addressOrObject === null || addressOrObject === undefined) return 'default';
111
+ // Plain string IP address
112
+ if (typeof addressOrObject === 'string') return addressOrObject;
113
+ // Guard against port numbers accidentally being passed as the address
114
+ if (typeof addressOrObject === 'number') return 'default';
115
+ if (typeof addressOrObject === 'object') {
116
+ // MSTP routed device — has net (network number) and adr (MAC address array).
117
+ // These fields appear both in outgoing address objects and in the sourceAddress
118
+ // decoded by _handleNpdu from the NPDU header of incoming MSTP responses.
119
+ if (addressOrObject.net !== undefined && addressOrObject.adr !== undefined) {
120
+ let adrHex;
121
+ if (Buffer.isBuffer(addressOrObject.adr)) {
122
+ adrHex = addressOrObject.adr.toString('hex');
123
+ } else if (Array.isArray(addressOrObject.adr)) {
124
+ adrHex = Buffer.from(addressOrObject.adr).toString('hex');
125
+ } else {
126
+ adrHex = String(addressOrObject.adr);
127
+ }
128
+ return `mstp:${addressOrObject.net}:${adrHex}`;
129
+ }
130
+ // Wrapper object like {address: <ip-or-mstp-object>, port: N}
131
+ if (addressOrObject.address !== undefined) {
132
+ return this._makeDeviceKey(addressOrObject.address);
133
+ }
134
+ // Object from _handleNpdu with an ip field but no net/adr (pure IP)
135
+ if (addressOrObject.ip !== undefined) {
136
+ return addressOrObject.ip;
137
+ }
138
+ }
139
+ return String(addressOrObject);
140
+ }
141
+ /**
142
+ * Returns a free invoke ID scoped to the given device key.
143
+ *
144
+ * Each device now gets its own independent 0-255 counter, matching the
145
+ * BACnet spec (ASHRAE 135 §5.4). Previously a single shared counter meant
146
+ * exhaustion after only 256 total in-flight requests across ALL devices,
147
+ * causing the fallback branch to return an already-in-use ID and silently
148
+ * overwrite the existing callback — the direct source of value mismatches.
149
+ *
150
+ * @param {string} deviceKey - Result of _makeDeviceKey() for the target device.
151
+ * @returns {number} An invoke ID (0-255) not currently in use for that device.
152
+ */
153
+ _getInvokeId(deviceKey) {
154
+ if (!this._deviceInvokeCounters[deviceKey]) {
155
+ this._deviceInvokeCounters[deviceKey] = 1;
156
+ }
157
+ for (let attempts = 0; attempts < 256; attempts++) {
158
+ const id = this._deviceInvokeCounters[deviceKey]++;
159
+ if (id >= 256) this._deviceInvokeCounters[deviceKey] = 1;
160
+ const invokeId = id - 1;
161
+ if (!this._invokeStore[`${deviceKey}:${invokeId}`]) {
162
+ return invokeId;
163
+ }
164
+ }
165
+ // All 256 IDs in use for this device — caller should throttle before this point.
166
+ // Return the next counter value so at least we don't spin forever; the
167
+ // _waitForRequestSlot mechanism in bacnet_client.js prevents reaching here
168
+ // under normal operation.
169
+ const id = this._deviceInvokeCounters[deviceKey]++;
170
+ if (id >= 256) this._deviceInvokeCounters[deviceKey] = 1;
171
+ debug('All 256 invoke IDs exhausted for device', deviceKey, '- reusing ID which may cause value mismatch');
172
+ return id - 1;
173
+ }
174
+ /**
175
+ * Fires the stored callback for the given device+invokeId pair.
176
+ * Using a compound key prevents a response from device A triggering the
177
+ * callback registered for device B when both happen to share the same
178
+ * numeric invoke ID value.
179
+ */
180
+ _invokeCallback(deviceKey, id, err, result) {
181
+ const storeKey = `${deviceKey}:${id}`;
182
+ const callback = this._invokeStore[storeKey];
183
+ if (callback)
184
+ return callback(err, result);
185
+ debug('InvokeId', id, 'for device', deviceKey, 'not found -> drop package');
186
+ }
187
+ /**
188
+ * Registers a callback for the given device+invokeId, with a timeout guard.
189
+ * The compound store key means callbacks are isolated per device, so a late
190
+ * response from a slow MSTP device cannot accidentally resolve a callback
191
+ * that belongs to a different device.
192
+ */
193
+ _addCallback(deviceKey, id, callback) {
194
+ const storeKey = `${deviceKey}:${id}`;
195
+ const timeout = setTimeout(() => {
196
+ delete this._invokeStore[storeKey];
197
+ callback(new Error('ERR_TIMEOUT'));
198
+ this.emit('requestComplete', { activeCount: this.getActiveRequestCount(), timedOut: true });
199
+ }, this._settings.apduTimeout);
200
+ this._invokeStore[storeKey] = (err, data) => {
201
+ clearTimeout(timeout);
202
+ delete this._invokeStore[storeKey];
203
+ callback(err, data);
204
+ this.emit('requestComplete', { activeCount: this.getActiveRequestCount(), timedOut: false });
205
+ };
206
+ }
207
+ _getBuffer() {
208
+ return {
209
+ buffer: Buffer.alloc(this._transport.getMaxPayload()),
210
+ offset: BVLC_HEADER_LENGTH
211
+ };
212
+ }
213
+ // Service Handlers
214
+ _processError(deviceKey, invokeId, buffer, offset, length) {
215
+ try {
216
+ const result = baServices.error.decode(buffer, offset);
217
+ if (!result)
218
+ return debug('Couldn`t decode Error');
219
+ this._invokeCallback(deviceKey, invokeId, new Error('BacnetError - Class:' + result.class + ' - Code:' + result.code));
220
+ } catch (e) {
221
+ }
222
+ }
223
+ _processAbort(deviceKey, invokeId, reason) {
224
+ this._invokeCallback(deviceKey, invokeId, new Error('BacnetAbort - Reason:' + reason));
225
+ }
226
+ _segmentAckResponse(receiver, port, negative, server, originalInvokeId, sequencenumber, actualWindowSize) {
227
+ const buffer = this._getBuffer();
228
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
229
+ baApdu.encodeSegmentAck(buffer, baEnum.PduTypes.SEGMENT_ACK | (negative ? baEnum.PduSegAckBits.NEGATIVE_ACK : 0) | (server ? baEnum.PduSegAckBits.SERVER : 0), originalInvokeId, sequencenumber, actualWindowSize);
230
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
231
+ var sendAddress = typeof (receiver) != 'string' ? receiver.ip : receiver;
232
+ this.sendBvlc(sendAddress, port, buffer);
233
+ }
234
+ _performDefaultSegmentHandling(sender, adr, type, service, invokeId, maxSegments, maxApdu, sequencenumber, first, moreFollows, buffer, offset, length, port) {
235
+ if (first) {
236
+ this._segmentStore = [];
237
+ type &= ~baEnum.PduConReqBits.SEGMENTED_MESSAGE;
238
+ let apduHeaderLen = 3;
239
+ if ((type & baEnum.PDU_TYPE_MASK) === baEnum.PduTypes.CONFIRMED_REQUEST) {
240
+ apduHeaderLen = 4;
241
+ }
242
+ const apdubuffer = this._getBuffer();
243
+ apdubuffer.offset = 0;
244
+ buffer.copy(apdubuffer.buffer, apduHeaderLen, offset, offset + length);
245
+ if ((type & baEnum.PDU_TYPE_MASK) === baEnum.PduTypes.CONFIRMED_REQUEST) {
246
+ baApdu.encodeConfirmedServiceRequest(apdubuffer, type, service, maxSegments, maxApdu, invokeId, 0, 0);
247
+ }
248
+ else {
249
+ baApdu.encodeComplexAck(apdubuffer, type, service, invokeId, 0, 0);
250
+ }
251
+ this._segmentStore.push(apdubuffer.buffer.slice(0, length + apduHeaderLen));
252
+ }
253
+ else {
254
+ this._segmentStore.push(buffer.slice(offset, offset + length));
255
+ }
256
+ if (!moreFollows) {
257
+ const apduBuffer = Buffer.concat(this._segmentStore);
258
+ this._segmentStore = [];
259
+ type &= ~baEnum.PduConReqBits.SEGMENTED_MESSAGE;
260
+ this._handlePdu(adr, type, apduBuffer, 0, apduBuffer.length, port);
261
+ }
262
+ }
263
+ _handleSequenceTimeout(invokeId) {
264
+ if (this._sequenceTimeout) {
265
+ clearInterval(this._sequenceTimeout);
266
+ }
267
+ this._sequenceTimeout = setTimeout(() => { this._lastSequenceNumber = 0 }, this._settings.apduTimeout);
268
+ }
269
+
270
+ _processSegment(receiver, type, service, invokeId, maxSegments, maxApdu, server, sequencenumber, proposedWindowNumber, buffer, offset, length, port) {
271
+ let first = false;
272
+ if (sequencenumber === 0 && this._lastSequenceNumber === 0) {
273
+ first = true;
274
+ } else {
275
+ if (sequencenumber !== this._lastSequenceNumber + 1) {
276
+ return this._segmentAckResponse(receiver, port, true, server, invokeId, this._lastSequenceNumber, proposedWindowNumber);
277
+ }
278
+ }
279
+ this._lastSequenceNumber = sequencenumber;
280
+ this._handleSequenceTimeout();
281
+ const moreFollows = type & baEnum.PduConReqBits.MORE_FOLLOWS;
282
+ if (!moreFollows) {
283
+ this._lastSequenceNumber = 0;
284
+ }
285
+ if ((sequencenumber % proposedWindowNumber) === 0 || !moreFollows) {
286
+ this._segmentAckResponse(receiver, port, false, server, invokeId, sequencenumber, proposedWindowNumber);
287
+ }
288
+ this._performDefaultSegmentHandling(this, receiver, type, service, invokeId, maxSegments, maxApdu, sequencenumber, first, moreFollows, buffer, offset, length, port);
289
+ }
290
+
291
+ _processConfirmedServiceRequest(address, type, service, maxSegments, maxApdu, invokeId, buffer, offset, length, srcAddress, destAddress) {
292
+ let result;
293
+ debug('Handle this._processConfirmedServiceRequest');
294
+ if (service === baEnum.ConfirmedServiceChoice.READ_PROPERTY) {
295
+ result = baServices.readProperty.decode(buffer, offset, length);
296
+ if (!result) return debug('Received invalid readProperty message');
297
+ this.emit('readProperty', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress, destAddress: destAddress });
298
+ } else if (service === baEnum.ConfirmedServiceChoice.WRITE_PROPERTY) {
299
+ result = baServices.writeProperty.decode(buffer, offset, length);
300
+ if (!result) return debug('Received invalid writeProperty message');
301
+ this.emit('writeProperty', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress, destAddress: destAddress });
302
+ } else if (service === baEnum.ConfirmedServiceChoice.READ_PROPERTY_MULTIPLE) {
303
+ result = baServices.readPropertyMultiple.decode(buffer, offset, length);
304
+ if (!result) return debug('Received invalid readPropertyMultiple message');
305
+ this.emit('readPropertyMultiple', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress, destAddress: destAddress });
306
+ } else if (service === baEnum.ConfirmedServiceChoice.WRITE_PROPERTY_MULTIPLE) {
307
+ result = baServices.writePropertyMultiple.decode(buffer, offset, length);
308
+ if (!result) return debug('Received invalid writePropertyMultiple message');
309
+ this.emit('writePropertyMultiple', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress, destAddress: destAddress });
310
+ } else if (service === baEnum.ConfirmedServiceChoice.CONFIRMED_COV_NOTIFICATION) {
311
+ result = baServices.covNotify.decode(buffer, offset, length);
312
+ if (!result) return debug('Received invalid covNotify message');
313
+ this.emit('covNotify', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
314
+ } else if (service === baEnum.ConfirmedServiceChoice.ATOMIC_WRITE_FILE) {
315
+ result = baServices.atomicWriteFile.decode(buffer, offset, length);
316
+ if (!result) return debug('Received invalid atomicWriteFile message');
317
+ this.emit('atomicWriteFile', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
318
+ } else if (service === baEnum.ConfirmedServiceChoice.ATOMIC_READ_FILE) {
319
+ result = baServices.atomicReadFile.decode(buffer, offset, length);
320
+ if (!result) return debug('Received invalid atomicReadFile message');
321
+ this.emit('atomicReadFile', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
322
+ } else if (service === baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV) {
323
+ result = baServices.subscribeCov.decode(buffer, offset, length);
324
+ if (!result) return debug('Received invalid subscribeCOV message');
325
+ this.emit('subscribeCOV', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
326
+ } else if (service === baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV_PROPERTY) {
327
+ result = baServices.subscribeProperty.decode(buffer, offset, length);
328
+ if (!result) return debug('Received invalid subscribeProperty message');
329
+ this.emit('subscribeProperty', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
330
+ } else if (service === baEnum.ConfirmedServiceChoice.DEVICE_COMMUNICATION_CONTROL) {
331
+ result = baServices.deviceCommunicationControl.decode(buffer, offset, length);
332
+ if (!result) return debug('Received invalid deviceCommunicationControl message');
333
+ this.emit('deviceCommunicationControl', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
334
+ } else if (service === baEnum.ConfirmedServiceChoice.REINITIALIZE_DEVICE) {
335
+ result = baServices.reinitializeDevice.decode(buffer, offset, length);
336
+ if (!result) return debug('Received invalid reinitializeDevice message');
337
+ this.emit('reinitializeDevice', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
338
+ } else if (service === baEnum.ConfirmedServiceChoice.CONFIRMED_EVENT_NOTIFICATION) {
339
+ result = baServices.eventNotifyData.decode(buffer, offset, length);
340
+ if (!result) return debug('Received invalid eventNotifyData message');
341
+ this.emit('eventNotifyData', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
342
+ } else if (service === baEnum.ConfirmedServiceChoice.READ_RANGE) {
343
+ result = baServices.readRange.decode(buffer, offset, length);
344
+ if (!result) return debug('Received invalid readRange message');
345
+ this.emit('readRange', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
346
+ } else if (service === baEnum.ConfirmedServiceChoice.CREATE_OBJECT) {
347
+ result = baServices.createObject.decode(buffer, offset, length);
348
+ if (!result) return debug('Received invalid createObject message');
349
+ this.emit('createObject', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
350
+ } else if (service === baEnum.ConfirmedServiceChoice.DELETE_OBJECT) {
351
+ result = baServices.deleteObject.decode(buffer, offset, length);
352
+ if (!result) return debug('Received invalid deleteObject message');
353
+ this.emit('deleteObject', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
354
+ } else if (service === baEnum.ConfirmedServiceChoice.ACKNOWLEDGE_ALARM) {
355
+ try {
356
+ result = baServices.alarmAcknowledge.decode(buffer, offset, length);
357
+ if (!result) return debug('Received invalid alarmAcknowledge message');
358
+ this.emit('alarmAcknowledge', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
359
+ } catch (e) {
360
+ //console.log("Error in alarmAcknowledge: ", e);
361
+ }
362
+ } else if (service === baEnum.ConfirmedServiceChoice.GET_ALARM_SUMMARY) {
363
+ this.emit('getAlarmSummary', { address: address, invokeId: invokeId });
364
+ } else if (service === baEnum.ConfirmedServiceChoice.GET_ENROLLMENT_SUMMARY) {
365
+ result = baServices.getEnrollmentSummary.decode(buffer, offset, length);
366
+ if (!result) return debug('Received invalid getEntrollmentSummary message');
367
+ this.emit('getEntrollmentSummary', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
368
+ } else if (service === baEnum.ConfirmedServiceChoice.GET_EVENT_INFORMATION) {
369
+ result = baServices.getEventInformation.decode(buffer, offset, length);
370
+ if (!result) return debug('Received invalid getEventInformation message');
371
+ this.emit('getEventInformation', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
372
+ } else if (service === baEnum.ConfirmedServiceChoice.LIFE_SAFETY_OPERATION) {
373
+ result = baServices.lifeSafetyOperation.decode(buffer, offset, length);
374
+ if (!result) return debug('Received invalid lifeSafetyOperation message');
375
+ this.emit('lifeSafetyOperation', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
376
+ } else if (service === baEnum.ConfirmedServiceChoice.ADD_LIST_ELEMENT) {
377
+ result = baServices.addListElement.decode(buffer, offset, length);
378
+ if (!result) return debug('Received invalid addListElement message');
379
+ this.emit('addListElement', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
380
+ } else if (service === baEnum.ConfirmedServiceChoice.REMOVE_LIST_ELEMENT) {
381
+ result = baServices.addListElement.decode(buffer, offset, length);
382
+ if (!result) return debug('Received invalid removeListElement message');
383
+ this.emit('removeListElement', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
384
+ } else if (service === baEnum.ConfirmedServiceChoice.CONFIRMED_PRIVATE_TRANSFER) {
385
+ result = baServices.privateTransfer.decode(buffer, offset, length);
386
+ if (!result) return debug('Received invalid privateTransfer message');
387
+ this.emit('privateTransfer', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
388
+ } else {
389
+ debug('Received unsupported confirmed service request');
390
+ }
391
+ }
392
+
393
+ _processUnconfirmedServiceRequest(address, type, service, buffer, offset, length, addressObject, port) {
394
+ let result;
395
+ debug('Handle this._processUnconfirmedServiceRequest');
396
+ if (service === baEnum.UnconfirmedServiceChoice.I_AM) {
397
+ result = baServices.iAmBroadcast.decode(buffer, offset);
398
+ if (!result) return debug('Received invalid iAm message');
399
+
400
+ /**
401
+ * The iAm event represents the response to a whoIs request to detect all devices in a BACNET network.
402
+ * @event bacstack.iAm
403
+ * @param {object} device - An object representing the detected device.
404
+ * @param {string} device.address - The IP address of the detected device.
405
+ * @param {number} device.deviceId - The BACNET device-id of the detected device.
406
+ * @param {number} device.maxApdu - The max APDU size the detected device is supporting.
407
+ * @param {number} device.segmentation - The type of segmentation the detected device is supporting.
408
+ * @param {number} device.vendorId - The BACNET vendor-id of the detected device.
409
+ * @example
410
+ * const bacnet = require('bacstack');
411
+ * const client = new bacnet();
412
+ *
413
+ * client.on('iAm', (device) => {
414
+ * console.log('address: ', device.address, ' - deviceId: ', device.deviceId, ' - maxApdu: ', device.maxApdu, ' - segmentation: ', device.segmentation, ' - vendorId: ', device.vendorId);
415
+ * });
416
+ */
417
+
418
+ var net = typeof (addressObject) == 'object' ? addressObject.net : undefined;
419
+ var adr = typeof (addressObject) == 'object' ? addressObject.adr : undefined;
420
+ this.emit('iAm', { address: address, port: port, deviceId: result.deviceId, maxApdu: result.maxApdu, segmentation: result.segmentation, vendorId: result.vendorId, 'net': net, 'adr': adr });
421
+ } else if (service === baEnum.UnconfirmedServiceChoice.WHO_IS) {
422
+ result = baServices.whoIs.decode(buffer, offset, length);
423
+ if (!result) return debug('Received invalid WhoIs message');
424
+
425
+ /**
426
+ * The whoIs event represents the request for an IAm reponse to detect all devices in a BACNET network.
427
+ * @event bacstack.whoIs
428
+ * @param {object} request - An object representing the received request.
429
+ * @param {string} request.address - The IP address of the device sending the request.
430
+ * @param {number=} request.lowLimit - The lower limit of the BACNET device-id.
431
+ * @param {number=} request.highLimit - The higher limit of the BACNET device-id.
432
+ * @example
433
+ * const bacnet = require('bacstack');
434
+ * const client = new bacnet();
435
+ *
436
+ * client.on('whoIs', (request) => {
437
+ * console.log('address: ', device.address, ' - lowLimit: ', device.lowLimit, ' - highLimit: ', device.highLimit);
438
+ * });
439
+ */
440
+ this.emit('whoIs', { address: address, lowLimit: result.lowLimit, highLimit: result.highLimit });
441
+ } else if (service === baEnum.UnconfirmedServiceChoice.WHO_HAS) {
442
+ result = baServices.whoHas.decode(buffer, offset, length);
443
+ if (!result) return debug('Received invalid WhoHas message');
444
+ this.emit('whoHas', { address: address, lowLimit: result.lowLimit, highLimit: result.highLimit, objectId: result.objectId, objectName: result.objectName });
445
+ } else if (service === baEnum.UnconfirmedServiceChoice.UNCONFIRMED_COV_NOTIFICATION) {
446
+ result = baServices.covNotify.decode(buffer, offset, length);
447
+ if (!result) return debug('Received invalid covNotifyUnconfirmed message');
448
+ this.emit('covNotifyUnconfirmed', { address: address, request: result });
449
+ } else if (service === baEnum.UnconfirmedServiceChoice.TIME_SYNCHRONIZATION) {
450
+ result = baServices.timeSync.decode(buffer, offset, length);
451
+ if (!result) return debug('Received invalid TimeSync message');
452
+
453
+ /**
454
+ * The timeSync event represents the request to synchronize the local time to the received time.
455
+ * @event bacstack.timeSync
456
+ * @param {object} request - An object representing the received request.
457
+ * @param {string} request.address - The IP address of the device sending the request.
458
+ * @param {date} request.dateTime - The time to be synchronized to.
459
+ * @example
460
+ * const bacnet = require('bacstack');
461
+ * const client = new bacnet();
462
+ *
463
+ * client.on('timeSync', (request) => {
464
+ * console.log('address: ', device.address, ' - dateTime: ', device.dateTime);
465
+ * });
466
+ */
467
+ this.emit('timeSync', { address: address, dateTime: result.dateTime });
468
+ } else if (service === baEnum.UnconfirmedServiceChoice.UTC_TIME_SYNCHRONIZATION) {
469
+ result = baServices.timeSync.decode(buffer, offset, length);
470
+ if (!result) return debug('Received invalid TimeSyncUTC message');
471
+
472
+ /**
473
+ * The timeSyncUTC event represents the request to synchronize the local time to the received UTC time.
474
+ * @event bacstack.timeSyncUTC
475
+ * @param {object} request - An object representing the received request.
476
+ * @param {string} request.address - The IP address of the device sending the request.
477
+ * @param {date} request.dateTime - The time to be synchronized to.
478
+ * @example
479
+ * const bacnet = require('bacstack');
480
+ * const client = new bacnet();
481
+ *
482
+ * client.on('timeSyncUTC', (request) => {
483
+ * console.log('address: ', device.address, ' - dateTime: ', device.dateTime);
484
+ * });
485
+ */
486
+ this.emit('timeSyncUTC', { address: address, dateTime: result.dateTime });
487
+ } else if (service === baEnum.UnconfirmedServiceChoice.UNCONFIRMED_EVENT_NOTIFICATION) {
488
+ result = baServices.eventNotifyData.decode(buffer, offset, length);
489
+ if (!result) return debug('Received invalid EventNotify message');
490
+ this.emit('eventNotify', { address: address, eventData: result.eventData });
491
+ } else if (service === baEnum.UnconfirmedServiceChoice.I_HAVE) {
492
+ result = baServices.iHaveBroadcast.decode(buffer, offset, length);
493
+ if (!result) return debug('Received invalid ihaveBroadcast message');
494
+ this.emit('ihaveBroadcast', { address: address, eventData: result.eventData });
495
+ } else if (service === baEnum.UnconfirmedServiceChoice.UNCONFIRMED_PRIVATE_TRANSFER) {
496
+ result = baServices.privateTransfer.decode(buffer, offset, length);
497
+ if (!result) return debug('Received invalid privateTransfer message');
498
+ this.emit('privateTransfer', { address: address, eventData: result.eventData });
499
+ } else {
500
+ debug('Received unsupported unconfirmed service request');
501
+ }
502
+ }
503
+
504
+ _handlePdu(address, type, buffer, offset, length, addressObject, destAddressObject, port) {
505
+ let result;
506
+ // Derive the device key from the source address. For MSTP devices routed
507
+ // through a BBMD the addressObject carries the NPDU source (net + adr) which
508
+ // uniquely identifies the MSTP node. For plain IP the IP string is used.
509
+ const deviceKey = this._makeDeviceKey(addressObject);
510
+ // Handle different PDU types
511
+ switch (type & baEnum.PDU_TYPE_MASK) {
512
+ case baEnum.PduTypes.UNCONFIRMED_REQUEST:
513
+ result = baApdu.decodeUnconfirmedServiceRequest(buffer, offset);
514
+ this._processUnconfirmedServiceRequest(address, result.type, result.service, buffer, offset + result.len, length - result.len, addressObject, port);
515
+ break;
516
+ case baEnum.PduTypes.SIMPLE_ACK:
517
+ result = baApdu.decodeSimpleAck(buffer, offset);
518
+ offset += result.len;
519
+ length -= result.len;
520
+ this._invokeCallback(deviceKey, result.invokeId, null, { result: result, buffer: buffer, offset: offset + result.len, length: length - result.len });
521
+ break;
522
+ case baEnum.PduTypes.COMPLEX_ACK:
523
+ result = baApdu.decodeComplexAck(buffer, offset);
524
+ if ((type & baEnum.PduConReqBits.SEGMENTED_MESSAGE) === 0) {
525
+ this._invokeCallback(deviceKey, result.invokeId, null, { result: result, buffer: buffer, offset: offset + result.len, length: length - result.len });
526
+ } else {
527
+ this._processSegment(addressObject, result.type, result.service, result.invokeId, baEnum.MaxSegmentsAccepted.SEGMENTS_0, baEnum.MaxApduLengthAccepted.OCTETS_50, false, result.sequencenumber, result.proposedWindowNumber, buffer, offset + result.len, length - result.len, port);
528
+ }
529
+ break;
530
+ case baEnum.PduTypes.SEGMENT_ACK:
531
+ result = baApdu.decodeSegmentAck(buffer, offset);
532
+ //m_last_segment_ack.Set(address, result.originalInvokeId, result.sequencenumber, result.actualWindowSize);
533
+ //this._processSegmentAck(address, result.type, result.originalInvokeId, result.sequencenumber, result.actualWindowSize, buffer, offset + result.len, length - result.len);
534
+ break;
535
+ case baEnum.PduTypes.ERROR:
536
+ result = baApdu.decodeError(buffer, offset);
537
+ this._processError(deviceKey, result.invokeId, buffer, offset + result.len, length - result.len);
538
+ break;
539
+ case baEnum.PduTypes.REJECT:
540
+ case baEnum.PduTypes.ABORT:
541
+ result = baApdu.decodeAbort(buffer, offset);
542
+ this._processAbort(deviceKey, result.invokeId, result.reason);
543
+ break;
544
+ case baEnum.PduTypes.CONFIRMED_REQUEST:
545
+ result = baApdu.decodeConfirmedServiceRequest(buffer, offset);
546
+ if ((type & baEnum.PduConReqBits.SEGMENTED_MESSAGE) === 0) {
547
+ this._processConfirmedServiceRequest(address, result.type, result.service, result.maxSegments, result.maxApdu, result.invokeId, buffer, offset + result.len, length - result.len, addressObject, destAddressObject);
548
+ } else {
549
+ 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, port);
550
+ }
551
+ break;
552
+ default:
553
+ debug('Received unknown PDU type -> Drop package');
554
+ break;
555
+ }
556
+ }
557
+ _handleNpdu(buffer, offset, msgLength, remoteAddress, port) {
558
+ // Check data length
559
+ if (msgLength <= 0) return debug('No NPDU data -> Drop package');
560
+ // Parse baNpdu header
561
+ const result = baNpdu.decode(buffer, offset);
562
+ var addressObject, destAddress;
563
+ // @todo a MSTP Dest & Source could occur. this case is not handled.
564
+ if (typeof (result.source) != 'undefined' && result && result.source) {
565
+ addressObject = result.source;
566
+ addressObject.ip = remoteAddress;
567
+ } else {
568
+ addressObject = remoteAddress;
569
+ }
570
+
571
+ if (typeof (result.destination) != 'undefined') {
572
+ destAddress = result.destination;
573
+ destAddress.ip = remoteAddress;
574
+ } else {
575
+ destAddress = remoteAddress;
576
+ }
577
+
578
+ if (!result) return debug('Received invalid NPDU header -> Drop package');
579
+ if (result.funct & baEnum.NpduControlBits.NETWORK_LAYER_MESSAGE) {
580
+ return debug('Received network layer message -> Drop package');
581
+ }
582
+ offset += result.len;
583
+ msgLength -= result.len;
584
+ if (msgLength <= 0) return debug('No APDU data -> Drop package');
585
+ const apduType = baApdu.getDecodedType(buffer, offset);
586
+ this._handlePdu(remoteAddress, apduType, buffer, offset, msgLength, addressObject, destAddress, port);
587
+ }
588
+ _receiveData(buffer, remoteAddress, port) {
589
+ // Check data length
590
+ if (buffer.length < baEnum.BVLC_HEADER_LENGTH)
591
+ return debug('Received invalid data -> Drop package');
592
+ // Parse BVLC header
593
+ const result = baBvlc.decode(buffer, 0);
594
+ if (!result)
595
+ return debug('Received invalid BVLC header -> Drop package');
596
+ // Check BVLC function
597
+ if (result.func === baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU || result.func === baEnum.BvlcResultPurpose.ORIGINAL_BROADCAST_NPDU || result.func === baEnum.BvlcResultPurpose.FORWARDED_NPDU) {
598
+ this._handleNpdu(buffer, result.len, buffer.length - result.len, remoteAddress, port);
599
+ }
600
+ else {
601
+ debug('Received unknown BVLC function -> Drop package');
602
+ }
603
+ }
604
+ _receiveError(err) {
605
+ /**
606
+ * @event bacstack.error
607
+ * @param {error} err - The error object thrown by the underlying transport layer.
608
+ * @example
609
+ * const bacnet = require('bacstack');
610
+ * const client = new bacnet();
611
+ *
612
+ * client.on('error', (err) => {
613
+ * console.log('Error occurred: ', err);
614
+ * client.close();
615
+ * });
616
+ */
617
+ this.emit('error', err);
618
+ }
619
+ /**
620
+ * The whoIs command discovers all BACNET devices in a network.
621
+ * @function bacstack.whoIs
622
+ * @param {object=} options
623
+ * @param {number=} options.lowLimit - Minimal device instance number to search for.
624
+ * @param {number=} options.highLimit - Maximal device instance number to search for.
625
+ * @param {string=} options.address - Unicast address if command should address a device directly.
626
+ * @fires bacstack.iAm
627
+ * @example
628
+ * const bacnet = require('bacstack');
629
+ * const client = new bacnet();
630
+ *
631
+ * client.whoIs();
632
+
633
+ whoIs(options) {
634
+ options = options || {};
635
+ const settings = {
636
+ lowLimit: options.lowLimit,
637
+ highLimit: options.highLimit,
638
+ address: options.address || this._transport.getBroadcastAddress()
639
+ };
640
+ const buffer = this._getBuffer();
641
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, this._settings.address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
642
+ baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.WHO_IS);
643
+ baServices.whoIs.encode(buffer, settings.lowLimit, settings.highLimit);
644
+ const npduType = (this._settings.address !== this._transport.getBroadcastAddress()) ? baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU : baEnum.BvlcResultPurpose.ORIGINAL_BROADCAST_NPDU;
645
+ baBvlc.encode(buffer.buffer, npduType, buffer.offset);
646
+ this._transport.send(buffer.buffer, buffer.offset, settings.address);
647
+ }
648
+ */
649
+ whoIs(receiver, options) {
650
+ if (!options) {
651
+ if (
652
+ receiver &&
653
+ typeof receiver === 'object' &&
654
+ receiver.address === undefined &&
655
+ receiver.forwardedFrom === undefined &&
656
+ (receiver.lowLimit !== undefined || receiver.highLimit !== undefined)
657
+ ) {
658
+ // receiver seems to be an options object
659
+ options = receiver;
660
+ receiver = undefined;
661
+ }
662
+ }
663
+ options = options || {};
664
+
665
+ const settings = {
666
+ lowLimit: options.lowLimit,
667
+ highLimit: options.highLimit,
668
+ };
669
+ const buffer = this._getBuffer(receiver && receiver.forwardedFrom);
670
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
671
+ baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.WHO_IS);
672
+ baServices.whoIs.encode(buffer, settings.lowLimit, settings.highLimit);
673
+ this.sendBvlc(receiver, this._settings.portRangeMatrix, buffer);
674
+ }
675
+ /**
676
+ * The timeSync command sets the time of a target device.
677
+ * @function bacstack.timeSync
678
+ * @param {string} address - IP address of the target device.
679
+ * @param {date} dateTime - The date and time to set on the target device.
680
+ * @example
681
+ * const bacnet = require('bacstack');
682
+ * const client = new bacnet();
683
+ *
684
+ * client.timeSync('192.168.1.43', new Date());
685
+ */
686
+ timeSync(addressObject, dateTime) {
687
+ let address = addressObject.address ? addressObject.address : addressObject;
688
+ let port = addressObject.port ? addressObject.port : this._settings.port;
689
+
690
+ const buffer = this._getBuffer();
691
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, address);
692
+ baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.TIME_SYNCHRONIZATION);
693
+ baServices.timeSync.encode(buffer, dateTime);
694
+ const npduType = (address !== this._transport.getBroadcastAddress()) ? baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU : baEnum.BvlcResultPurpose.ORIGINAL_BROADCAST_NPDU;
695
+ //baBvlc.encode(buffer.buffer, npduType, buffer.offset);
696
+ this.sendBvlc(address, port, buffer);
697
+ }
698
+ /**
699
+ * The timeSyncUTC command sets the UTC time of a target device.
700
+ * @function bacstack.timeSyncUTC
701
+ * @param {string} address - IP address of the target device.
702
+ * @param {date} dateTime - The date and time to set on the target device.
703
+ * @example
704
+ * const bacnet = require('bacstack');
705
+ * const client = new bacnet();
706
+ *
707
+ * client.timeSyncUTC('192.168.1.43', new Date());
708
+ */
709
+ timeSyncUTC(addressObject, dateTime) {
710
+ let address = addressObject.address ? addressObject.address : addressObject;
711
+ let port = addressObject.port ? addressObject.port : this._settings.port;
712
+ const buffer = this._getBuffer();
713
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, address);
714
+ baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.UTC_TIME_SYNCHRONIZATION);
715
+ baServices.timeSync.encode(buffer, dateTime);
716
+ const npduType = (address !== this._transport.getBroadcastAddress()) ? baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU : baEnum.BvlcResultPurpose.ORIGINAL_BROADCAST_NPDU;
717
+ //baBvlc.encode(buffer.buffer, npduType, buffer.offset);
718
+ this.sendBvlc(address, port, buffer);
719
+ }
720
+ /**
721
+ * The readProperty command reads a single property of an object from a device.
722
+ * @function bacstack.readProperty
723
+ * @param {string} address - IP address of the target device.
724
+ * @param {object} objectId - The BACNET object ID to read.
725
+ * @param {number} objectId.type - The BACNET object type to read.
726
+ * @param {number} objectId.instance - The BACNET object instance to read.
727
+ * @param {number} propertyId - The BACNET property id in the specified object to read.
728
+ * @param {object=} options
729
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
730
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
731
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
732
+ * @param {number=} options.arrayIndex - The array index of the property to be read.
733
+ * @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
734
+ * @example
735
+ * const bacnet = require('bacstack');
736
+ * const client = new bacnet();
737
+ *
738
+ * client.readProperty('192.168.1.43', {type: 8, instance: 44301}, 28, (err, value) => {
739
+ * console.log('value: ', value);
740
+ * });
741
+ */
742
+ readProperty(addressObject, objectId, propertyId, options, next) {
743
+ next = next || options;
744
+
745
+ let address = addressObject.address ? addressObject.address : addressObject;
746
+ let port = addressObject.port ? addressObject.port : this._settings.port;
747
+ const deviceKey = this._makeDeviceKey(addressObject);
748
+
749
+ const settings = {
750
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
751
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
752
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey),
753
+ arrayIndex: options.arrayIndex || baEnum.ASN1_ARRAY_ALL
754
+ };
755
+ const buffer = this._getBuffer();
756
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
757
+ const type = baEnum.PduTypes.CONFIRMED_REQUEST | (settings.maxSegments !== baEnum.MaxSegmentsAccepted.SEGMENTS_0 ? baEnum.PduConReqBits.SEGMENTED_RESPONSE_ACCEPTED : 0);
758
+ baApdu.encodeConfirmedServiceRequest(buffer, type, baEnum.ConfirmedServiceChoice.READ_PROPERTY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
759
+ baServices.readProperty.encode(buffer, objectId.type, objectId.instance, propertyId, settings.arrayIndex);
760
+ baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
761
+ this.sendBvlc(address, port, buffer);
762
+ this._addCallback(deviceKey, settings.invokeId, (err, data) => {
763
+ try {
764
+ if (err)
765
+ return next(err);
766
+ const result = baServices.readProperty.decodeAcknowledge(data.buffer, data.offset, data.length);
767
+ if (!result)
768
+ return next(new Error('INVALID_DECODING'));
769
+ next(null, result);
770
+ } catch (e) {
771
+ return next(e);
772
+ }
773
+ });
774
+ }
775
+ /**
776
+ * The writeProperty command writes a single property of an object to a device.
777
+ * @function bacstack.writeProperty
778
+ * @param {string} address - IP address of the target device.
779
+ * @param {object} objectId - The BACNET object ID to write.
780
+ * @param {number} objectId.type - The BACNET object type to write.
781
+ * @param {number} objectId.instance - The BACNET object instance to write.
782
+ * @param {number} propertyId - The BACNET property id in the specified object to write.
783
+ * @param {object[]} values - A list of values to be written to the specified property.
784
+ * @param {ApplicationTags} values.tag - The data-type of the value to be written.
785
+ * @param {number} values.value - The actual value to be written.
786
+ * @param {object=} options
787
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
788
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
789
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
790
+ * @param {number=} options.arrayIndex - The array index of the property to be read.
791
+ * @param {number=} options.priority - The priority of the value to be written.
792
+ * @param {function} next - The callback containing an error, in case of a failure.
793
+ * @example
794
+ * const bacnet = require('bacstack');
795
+ * const client = new bacnet();
796
+ *
797
+ * client.writeProperty('192.168.1.43', {type: 8, instance: 44301}, 28, [
798
+ * {type: bacnet.enum.ApplicationTags.REAL, value: 100}
799
+ * ], (err) => {
800
+ * console.log('error: ', err);
801
+ * });
802
+ */
803
+ writeProperty(addressObject, objectId, propertyId, values, options, next) {
804
+ next = next || options;
805
+
806
+ let address = addressObject.address ? addressObject.address : addressObject;
807
+ let port = addressObject.port ? addressObject.port : this._settings.port;
808
+ const deviceKey = this._makeDeviceKey(addressObject);
809
+
810
+ const settings = {
811
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
812
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
813
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey),
814
+ arrayIndex: options.arrayIndex || baEnum.ASN1_ARRAY_ALL,
815
+ priority: options.priority
816
+ };
817
+ const buffer = this._getBuffer();
818
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
819
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.WRITE_PROPERTY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
820
+ baServices.writeProperty.encode(buffer, objectId.type, objectId.instance, propertyId, settings.arrayIndex, settings.priority, values);
821
+ baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
822
+ this.sendBvlc(address, port, buffer);
823
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
824
+ }
825
+ /**
826
+ * The readPropertyMultiple command reads multiple properties in multiple objects from a device.
827
+ * @function bacstack.readPropertyMultiple
828
+ * @param {string} address - IP address of the target device.
829
+ * @param {object[]} propertiesArray - List of object and property specifications to be read.
830
+ * @param {object} propertiesArray.objectId - Specifies which object to read.
831
+ * @param {number} propertiesArray.objectId.type - The BACNET object type to read.
832
+ * @param {number} propertiesArray.objectId.instance - The BACNET object instance to read.
833
+ * @param {object[]} propertiesArray.properties - List of properties to be read.
834
+ * @param {number} propertiesArray.properties.id - The BACNET property id in the specified object to read. Also supports 8 for all properties.
835
+ * @param {object=} options
836
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
837
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
838
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
839
+ * @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
840
+ * @example
841
+ * const bacnet = require('bacstack');
842
+ * const client = new bacnet();
843
+ *
844
+ * const propertiesArray = [
845
+ * {objectId: {type: 8, instance: 4194303}, properties: [{id: 8}]}
846
+ * ];
847
+ * client.readPropertyMultiple('192.168.1.43', propertiesArray, (err, value) => {
848
+ * console.log('value: ', value);
849
+ * });
850
+ */
851
+ readPropertyMultiple(addressObject, propertiesArray, options, next) {
852
+ next = next || options;
853
+
854
+ let address = addressObject.address ? addressObject.address : addressObject;
855
+ let port = addressObject.port ? addressObject.port : this._settings.port;
856
+ const deviceKey = this._makeDeviceKey(addressObject);
857
+
858
+ const settings = {
859
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
860
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
861
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
862
+ };
863
+ const buffer = this._getBuffer();
864
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address, null, DEFAULT_HOP_COUNT, baEnum.NetworkLayerMessageType.WHO_IS_ROUTER_TO_NETWORK, 0);
865
+ const type = baEnum.PduTypes.CONFIRMED_REQUEST | (settings.maxSegments !== baEnum.MaxSegmentsAccepted.SEGMENTS_0 ? baEnum.PduConReqBits.SEGMENTED_RESPONSE_ACCEPTED : 0);
866
+ baApdu.encodeConfirmedServiceRequest(buffer, type, baEnum.ConfirmedServiceChoice.READ_PROPERTY_MULTIPLE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
867
+ baServices.readPropertyMultiple.encode(buffer, propertiesArray);
868
+ baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
869
+ this.sendBvlc(address, port, buffer);
870
+ this._addCallback(deviceKey, settings.invokeId, (err, data) => {
871
+ try {
872
+ if (err)
873
+ return next(err);
874
+ const result = baServices.readPropertyMultiple.decodeAcknowledge(data.buffer, data.offset, data.length);
875
+ if (!result)
876
+ return next(new Error('INVALID_DECODING'));
877
+ next(null, result);
878
+ } catch (e) {
879
+ return next(e);
880
+ }
881
+ });
882
+ }
883
+ /**
884
+ * The writePropertyMultiple command writes multiple properties in multiple objects to a device.
885
+ * @function bacstack.writePropertyMultiple
886
+ * @param {string} address - IP address of the target device.
887
+ * @param {object[]} values - List of object and property specifications to be written.
888
+ * @param {object} values.objectId - Specifies which object to read.
889
+ * @param {number} values.objectId.type - The BACNET object type to read.
890
+ * @param {number} values.objectId.instance - The BACNET object instance to read.
891
+ * @param {object[]} values.values - List of properties to be written.
892
+ * @param {object} values.values.property - Property specifications to be written.
893
+ * @param {number} values.values.property.id - The BACNET property id in the specified object to write.
894
+ * @param {number} values.values.property.index - The array index of the property to be written.
895
+ * @param {object[]} values.values.value - A list of values to be written to the specified property.
896
+ * @param {ApplicationTags} values.values.value.tag - The data-type of the value to be written.
897
+ * @param {object} values.values.value.value - The actual value to be written.
898
+ * @param {number} values.values.priority - The priority to be used for writing to the property.
899
+ * @param {object=} options
900
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
901
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
902
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
903
+ * @param {function} next - The callback containing an error, in case of a failure.
904
+ * @example
905
+ * const bacnet = require('bacstack');
906
+ * const client = new bacnet();
907
+ *
908
+ * const values = [
909
+ * {objectId: {type: 8, instance: 44301}, values: [
910
+ * {property: {id: 28, index: 12}, value: [{type: bacnet.enum.ApplicationTags.BOOLEAN, value: true}], priority: 8}
911
+ * ]}
912
+ * ];
913
+ * client.writePropertyMultiple('192.168.1.43', values, (err) => {
914
+ * console.log('error: ', err);
915
+ * });
916
+ */
917
+ writePropertyMultiple(addressObject, values, options, next) {
918
+ next = next || options;
919
+
920
+ let address = addressObject.address ? addressObject.address : addressObject;
921
+ let port = addressObject.port ? addressObject.port : this._settings.port;
922
+ const deviceKey = this._makeDeviceKey(addressObject);
923
+
924
+ const settings = {
925
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
926
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
927
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
928
+ };
929
+ const buffer = this._getBuffer();
930
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
931
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.WRITE_PROPERTY_MULTIPLE, settings.maxSegments, settings.maxApdu, settings.invokeId);
932
+ baServices.writePropertyMultiple.encodeObject(buffer, values);
933
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
934
+ this.sendBvlc(address, port, buffer);
935
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
936
+ }
937
+ /**
938
+ * The deviceCommunicationControl command enables or disables network communication of the target device.
939
+ * @function bacstack.deviceCommunicationControl
940
+ * @param {string} address - IP address of the target device.
941
+ * @param {number} timeDuration - The time to hold the network communication state in seconds. 0 for infinite.
942
+ * @param {EnableDisable} enableDisable - The network communication state to set.
943
+ * @param {object=} options
944
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
945
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
946
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
947
+ * @param {string=} options.password - The optional password used to set the network communication state.
948
+ * @param {function} next - The callback containing an error, in case of a failure.
949
+ * @example
950
+ * const bacnet = require('bacstack');
951
+ * const client = new bacnet();
952
+ *
953
+ * client.deviceCommunicationControl('192.168.1.43', 0, bacnet.enum.EnableDisable.DISABLE, (err) => {
954
+ * console.log('error: ', err);
955
+ * });
956
+ */
957
+ deviceCommunicationControl(addressObject, timeDuration, enableDisable, options, next) {
958
+ next = next || options;
959
+ let address = addressObject.address ? addressObject.address : addressObject;
960
+ let port = addressObject.port ? addressObject.port : this._settings.port;
961
+ const deviceKey = this._makeDeviceKey(addressObject);
962
+ const settings = {
963
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
964
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
965
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey),
966
+ password: options.password
967
+ };
968
+ const buffer = this._getBuffer();
969
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
970
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.DEVICE_COMMUNICATION_CONTROL, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
971
+ baServices.deviceCommunicationControl.encode(buffer, timeDuration, enableDisable, settings.password);
972
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
973
+ this.sendBvlc(address, port, buffer);
974
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
975
+ }
976
+ /**
977
+ * The reinitializeDevice command initiates a restart of the target device.
978
+ * @function bacstack.reinitializeDevice
979
+ * @param {string} address - IP address of the target device.
980
+ * @param {ReinitializedState} state - The type of restart to be initiated.
981
+ * @param {object=} options
982
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
983
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
984
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
985
+ * @param {string=} options.password - The optional password used to restart the device.
986
+ * @param {function} next - The callback containing an error, in case of a failure.
987
+ * @example
988
+ * const bacnet = require('bacstack');
989
+ * const client = new bacnet();
990
+ *
991
+ * client.reinitializeDevice('192.168.1.43', bacnet.enum.ReinitializedState.COLDSTART, (err) => {
992
+ * console.log('error: ', err);
993
+ * });
994
+ */
995
+ reinitializeDevice(addressObject, state, options, next) {
996
+ next = next || options;
997
+ let address = addressObject.address ? addressObject.address : addressObject;
998
+ let port = addressObject.port ? addressObject.port : this._settings.port;
999
+ const deviceKey = this._makeDeviceKey(addressObject);
1000
+ const settings = {
1001
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1002
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1003
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey),
1004
+ password: options.password
1005
+ };
1006
+ const buffer = this._getBuffer();
1007
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1008
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.REINITIALIZE_DEVICE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1009
+ baServices.reinitializeDevice.encode(buffer, state, settings.password);
1010
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1011
+ this.sendBvlc(address, port, buffer);
1012
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
1013
+ }
1014
+ /**
1015
+ * The writeFile command writes a file buffer to a specific position of a file object.
1016
+ * @function bacstack.writeFile
1017
+ * @param {string} address - IP address of the target device.
1018
+ * @param {object} objectId - The BACNET object ID representing the file object.
1019
+ * @param {number} objectId.type - The BACNET object type representing the file object.
1020
+ * @param {number} objectId.instance - The BACNET object instance representing the file object.
1021
+ * @param {number} position - The position in the file to write at.
1022
+ * @param {Array.<number[]>} fileBuffer - The content to be written to the file.
1023
+ * @param {object=} options
1024
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
1025
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
1026
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
1027
+ * @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
1028
+ * @example
1029
+ * const bacnet = require('bacstack');
1030
+ * const client = new bacnet();
1031
+ *
1032
+ * client.writeFile('192.168.1.43', {type: 8, instance: 44301}, 0, [[5, 6, 7, 8], [5, 6, 7, 8]], (err, value) => {
1033
+ * console.log('value: ', value);
1034
+ * });
1035
+ */
1036
+ writeFile(addressObject, objectId, position, fileBuffer, options, next) {
1037
+ next = next || options;
1038
+ let address = addressObject.address ? addressObject.address : addressObject;
1039
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1040
+ const deviceKey = this._makeDeviceKey(addressObject);
1041
+ const settings = {
1042
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1043
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1044
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1045
+ };
1046
+ const buffer = this._getBuffer();
1047
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1048
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ATOMIC_WRITE_FILE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1049
+ baServices.atomicWriteFile.encode(buffer, false, objectId, position, fileBuffer);
1050
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1051
+ this.sendBvlc(address, port, buffer);
1052
+ this._addCallback(deviceKey, settings.invokeId, (err, data) => {
1053
+ if (err)
1054
+ return next(err);
1055
+ const result = baServices.atomicWriteFile.decodeAcknowledge(data.buffer, data.offset);
1056
+ if (!result)
1057
+ return next(new Error('INVALID_DECODING'));
1058
+ next(null, result);
1059
+ });
1060
+ }
1061
+ /**
1062
+ * The readFile command reads a number of bytes at a specific position of a file object.
1063
+ * @function bacstack.readFile
1064
+ * @param {string} address - IP address of the target device.
1065
+ * @param {object} objectId - The BACNET object ID representing the file object.
1066
+ * @param {number} objectId.type - The BACNET object type representing the file object.
1067
+ * @param {number} objectId.instance - The BACNET object instance representing the file object.
1068
+ * @param {number} position - The position in the file to read at.
1069
+ * @param {number} count - The number of octets to read.
1070
+ * @param {object=} options
1071
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
1072
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
1073
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
1074
+ * @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
1075
+ * @example
1076
+ * const bacnet = require('bacstack');
1077
+ * const client = new bacnet();
1078
+ *
1079
+ * client.readFile('192.168.1.43', {type: 8, instance: 44301}, 0, 100, (err, value) => {
1080
+ * console.log('value: ', value);
1081
+ * });
1082
+ */
1083
+ readFile(addressObject, objectId, position, count, options, next) {
1084
+ next = next || options;
1085
+ let address = addressObject.address ? addressObject.address : addressObject;
1086
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1087
+ const deviceKey = this._makeDeviceKey(addressObject);
1088
+ const settings = {
1089
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1090
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1091
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1092
+ };
1093
+ const buffer = this._getBuffer();
1094
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1095
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ATOMIC_READ_FILE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1096
+ baServices.atomicReadFile.encode(buffer, true, objectId, position, count);
1097
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1098
+ this.sendBvlc(address, port, buffer);
1099
+ this._addCallback(deviceKey, settings.invokeId, (err, data) => {
1100
+ if (err)
1101
+ return next(err);
1102
+ const result = baServices.atomicReadFile.decodeAcknowledge(data.buffer, data.offset);
1103
+ if (!result)
1104
+ return next(new Error('INVALID_DECODING'));
1105
+ next(null, result);
1106
+ });
1107
+ }
1108
+ /**
1109
+ * The readRange command reads a number if list items of an array or list object.
1110
+ * @function bacstack.readRange
1111
+ * @param {string} address - IP address of the target device.
1112
+ * @param {object} objectId - The BACNET object ID to read.
1113
+ * @param {number} objectId.type - The BACNET object type to read.
1114
+ * @param {number} objectId.instance - The BACNET object instance to read.
1115
+ * @param {number} idxBegin - The index of the first/last item to read.
1116
+ * @param {number} quantity - The number of records to read.
1117
+ * @param {object=} options
1118
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
1119
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
1120
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
1121
+ * @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
1122
+ * @example
1123
+ * const bacnet = require('bacstack');
1124
+ * const client = new bacnet();
1125
+ *
1126
+ * client.readRange('192.168.1.43', {type: 8, instance: 44301}, 0, 200, (err, value) => {
1127
+ * console.log('value: ', value);
1128
+ * });
1129
+ */
1130
+ readRange(addressObject, objectId, idxBegin, quantity, options, next) {
1131
+ next = next || options;
1132
+ let address = addressObject.address ? addressObject.address : addressObject;
1133
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1134
+ const deviceKey = this._makeDeviceKey(addressObject);
1135
+ const settings = {
1136
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1137
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1138
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1139
+ };
1140
+ const buffer = this._getBuffer();
1141
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1142
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.READ_RANGE, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1143
+ baServices.readRange.encode(buffer, objectId, baEnum.PropertyIdentifier.LOG_BUFFER, baEnum.ASN1_ARRAY_ALL, baEnum.ReadRangeType.BY_POSITION, idxBegin, new Date(), quantity);
1144
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1145
+ this.sendBvlc(address, port, buffer);
1146
+ this._addCallback(deviceKey, settings.invokeId, (err, data) => {
1147
+ if (err)
1148
+ return next(err);
1149
+ const result = baServices.readRange.decodeAcknowledge(data.buffer, data.offset, data.length);
1150
+ if (!result)
1151
+ return next(new Error('INVALID_DECODING'));
1152
+ next(null, result);
1153
+ });
1154
+ }
1155
+ /**
1156
+ * The subscribeCOV command subscribes to an object for "Change of Value" notifications.
1157
+ * @function bacstack.subscribeCOV
1158
+ * @param {string} address - IP address of the target device.
1159
+ * @param {object} objectId - The BACNET object ID to subscribe for.
1160
+ * @param {number} objectId.type - The BACNET object type to subscribe for.
1161
+ * @param {number} objectId.instance - The BACNET object instance to subscribe for.
1162
+ * @param {number} subscribeId - A unique identifier to map the subscription.
1163
+ * @param {boolean} cancel - Cancel an existing subscription instead of creating a new one.
1164
+ * @param {boolean} issueConfirmedNotifications - Identifies if unconfirmed/confirmed notifications shall be returned.
1165
+ * @param {number} lifetime - Number of seconds for the subscription to stay active, 0 for infinite.
1166
+ * @param {object=} options
1167
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
1168
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
1169
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
1170
+ * @param {function} next - The callback containing an error, in case of a failure.
1171
+ * @example
1172
+ * const bacnet = require('bacstack');
1173
+ * const client = new bacnet();
1174
+ *
1175
+ * client.subscribeCOV('192.168.1.43', {type: 8, instance: 44301}, 7, false, false, 0, (err) => {
1176
+ * console.log('error: ', err);
1177
+ * });
1178
+ */
1179
+ subscribeCOV(addressObject, objectId, subscribeId, cancel, issueConfirmedNotifications, lifetime, options, next) {
1180
+ next = next || options;
1181
+ let address = addressObject.address ? addressObject.address : addressObject;
1182
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1183
+ const deviceKey = this._makeDeviceKey(addressObject);
1184
+ const settings = {
1185
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1186
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1187
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1188
+ };
1189
+ const buffer = this._getBuffer();
1190
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1191
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1192
+ baServices.subscribeCov.encode(buffer, subscribeId, objectId, cancel, issueConfirmedNotifications, lifetime);
1193
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1194
+ this.sendBvlc(address, port, buffer);
1195
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
1196
+ }
1197
+ /**
1198
+ * The subscribeProperty command subscribes to a specific property of an object for "Change of Value" notifications.
1199
+ * @function bacstack.subscribeProperty
1200
+ * @param {string} address - IP address of the target device.
1201
+ * @param {object} objectId - The BACNET object ID to subscribe for.
1202
+ * @param {number} objectId.type - The BACNET object type to subscribe for.
1203
+ * @param {number} objectId.instance - The BACNET object instance to subscribe for.
1204
+ * @param {object} monitoredProperty
1205
+ * @param {object} monitoredProperty.id - The property ID to subscribe for.
1206
+ * @param {object} monitoredProperty.index - The property index to subscribe for.
1207
+ * @param {number} subscribeId - A unique identifier to map the subscription.
1208
+ * @param {boolean} cancel - Cancel an existing subscription instead of creating a new one.
1209
+ * @param {boolean} issueConfirmedNotifications - Identifies if unconfirmed/confirmed notifications shall be returned.
1210
+ * @param {object=} options
1211
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
1212
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
1213
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
1214
+ * @param {function} next - The callback containing an error, in case of a failure.
1215
+ * @example
1216
+ * const bacnet = require('bacstack');
1217
+ * const client = new bacnet();
1218
+ *
1219
+ * client.subscribeProperty('192.168.1.43', {type: 8, instance: 44301}, {id: 80, index: 0}, 8, false, false, (err) => {
1220
+ * console.log('error: ', err);
1221
+ * });
1222
+ */
1223
+ subscribeProperty(addressObject, objectId, monitoredProperty, subscribeId, cancel, issueConfirmedNotifications, options, next) {
1224
+ next = next || options;
1225
+ let address = addressObject.address ? addressObject.address : addressObject;
1226
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1227
+ const deviceKey = this._makeDeviceKey(addressObject);
1228
+ const settings = {
1229
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1230
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1231
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1232
+ };
1233
+ const buffer = this._getBuffer();
1234
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1235
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.SUBSCRIBE_COV_PROPERTY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1236
+ baServices.subscribeProperty.encode(buffer, subscribeId, objectId, cancel, issueConfirmedNotifications, 0, monitoredProperty, false, 0x0f);
1237
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1238
+ this.sendBvlc(address, port, buffer);
1239
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
1240
+ }
1241
+ createObject(addressObject, objectId, values, options, next) {
1242
+ next = next || options;
1243
+ let address = addressObject.address ? addressObject.address : addressObject;
1244
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1245
+ const deviceKey = this._makeDeviceKey(addressObject);
1246
+ const settings = {
1247
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1248
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1249
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1250
+ };
1251
+ const buffer = this._getBuffer();
1252
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1253
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CREATE_OBJECT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1254
+ baServices.createObject.encode(buffer, objectId, values);
1255
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1256
+ this.sendBvlc(address, port, buffer);
1257
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
1258
+ }
1259
+ /**
1260
+ * The deleteObject command removes an object instance from a target device.
1261
+ * @function bacstack.deleteObject
1262
+ * @param {string} address - IP address of the target device.
1263
+ * @param {object} objectId - The BACNET object ID to delete.
1264
+ * @param {number} objectId.type - The BACNET object type to delete.
1265
+ * @param {number} objectId.instance - The BACNET object instance to delete.
1266
+ * @param {object=} options
1267
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
1268
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
1269
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
1270
+ * @param {function} next - The callback containing an error, in case of a failure.
1271
+ * @example
1272
+ * const bacnet = require('bacstack');
1273
+ * const client = new bacnet();
1274
+ *
1275
+ * client.deleteObject('192.168.1.43', {type: 8, instance: 44301}, (err) => {
1276
+ * console.log('error: ', err);
1277
+ * });
1278
+ */
1279
+ deleteObject(addressObject, objectId, options, next) {
1280
+ next = next || options;
1281
+ let address = addressObject.address ? addressObject.address : addressObject;
1282
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1283
+ const deviceKey = this._makeDeviceKey(addressObject);
1284
+ const settings = {
1285
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1286
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1287
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1288
+ };
1289
+ const buffer = this._getBuffer();
1290
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1291
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.DELETE_OBJECT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1292
+ baServices.deleteObject.encode(buffer, objectId);
1293
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1294
+ this.sendBvlc(address, port, buffer);
1295
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
1296
+ }
1297
+ removeListElement(addressObject, objectId, reference, values, options, next) {
1298
+ next = next || options;
1299
+ let address = addressObject.address ? addressObject.address : addressObject;
1300
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1301
+ const deviceKey = this._makeDeviceKey(addressObject);
1302
+ const settings = {
1303
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1304
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1305
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1306
+ };
1307
+ const buffer = this._getBuffer();
1308
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1309
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.REMOVE_LIST_ELEMENT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1310
+ baServices.addListElement.encode(buffer, objectId, reference.id, reference.index, values);
1311
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1312
+ this.sendBvlc(address, port, buffer);
1313
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
1314
+ }
1315
+ addListElement(addressObject, objectId, reference, values, options, next) {
1316
+ next = next || options;
1317
+ let address = addressObject.address ? addressObject.address : addressObject;
1318
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1319
+ const deviceKey = this._makeDeviceKey(addressObject);
1320
+ const settings = {
1321
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1322
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1323
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1324
+ };
1325
+ const buffer = this._getBuffer();
1326
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1327
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ADD_LIST_ELEMENT, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1328
+ baServices.addListElement.encode(buffer, objectId, reference.id, reference.index, values);
1329
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1330
+ this.sendBvlc(address, port, buffer);
1331
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
1332
+ }
1333
+ /**
1334
+ * DEPRECATED The getAlarmSummary command returns a list of all active alarms on the target device.
1335
+ * @function bacstack.getAlarmSummary
1336
+ * @param {string} address - IP address of the target device.
1337
+ * @param {object=} options
1338
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
1339
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
1340
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
1341
+ * @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
1342
+ * @example
1343
+ * const bacnet = require('bacstack');
1344
+ * const client = new bacnet();
1345
+ *
1346
+ * client.getAlarmSummary('192.168.1.43', (err, value) => {
1347
+ * console.log('value: ', value);
1348
+ * });
1349
+ */
1350
+ getAlarmSummary(addressObject, options, next) {
1351
+ next = next || options;
1352
+ let address = addressObject.address ? addressObject.address : addressObject;
1353
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1354
+ const deviceKey = this._makeDeviceKey(addressObject);
1355
+ const settings = {
1356
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1357
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1358
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1359
+ };
1360
+ const buffer = this._getBuffer();
1361
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1362
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.GET_ALARM_SUMMARY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1363
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1364
+ this.sendBvlc(address, port, buffer);
1365
+ this._addCallback(deviceKey, settings.invokeId, (err, data) => {
1366
+ if (err)
1367
+ return next(err);
1368
+ const result = baServices.alarmSummary.decode(data.buffer, data.offset, data.length);
1369
+ if (!result)
1370
+ return next(new Error('INVALID_DECODING'));
1371
+ next(null, result);
1372
+ });
1373
+ }
1374
+ /**
1375
+ * The getEventInformation command returns a list of all active event states on the target device.
1376
+ * @function bacstack.getEventInformation
1377
+ * @param {string} address - IP address of the target device.
1378
+ * @param {object=} objectId - The optional BACNET object ID to continue preceding calls.
1379
+ * @param {number=} objectId.type - The optional BACNET object type to continue preceding calls.
1380
+ * @param {number=} objectId.instance - The optional BACNET object instance to continue preceding calls.
1381
+ * @param {object=} options
1382
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
1383
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
1384
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
1385
+ * @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
1386
+ * @example
1387
+ * const bacnet = require('bacstack');
1388
+ * const client = new bacnet();
1389
+ *
1390
+ * client.getEventInformation('192.168.1.43', {}, (err, value) => {
1391
+ * console.log('value: ', value);
1392
+ * });
1393
+ */
1394
+ getEventInformation(addressObject, objectId, options, next) {
1395
+ next = next || options;
1396
+ let address = addressObject.address ? addressObject.address : addressObject;
1397
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1398
+ const deviceKey = this._makeDeviceKey(addressObject);
1399
+ const settings = {
1400
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1401
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1402
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1403
+ };
1404
+ const buffer = this._getBuffer();
1405
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1406
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.GET_EVENT_INFORMATION, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1407
+ baAsn1.encodeContextObjectId(buffer, 0, objectId.type, objectId.instance);
1408
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1409
+ this.sendBvlc(address, port, buffer);
1410
+ this._addCallback(deviceKey, settings.invokeId, (err, data) => {
1411
+ if (err)
1412
+ return next(err);
1413
+ const result = baServices.eventInformation.decode(data.buffer, data.offset, data.length);
1414
+ if (!result)
1415
+ return next(new Error('INVALID_DECODING'));
1416
+ next(null, result);
1417
+ });
1418
+ }
1419
+ acknowledgeAlarm(addressObject, objectId, eventState, ackText, evTimeStamp, ackTimeStamp, options, next) {
1420
+ next = next || options;
1421
+ let address = addressObject.address ? addressObject.address : addressObject;
1422
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1423
+ const deviceKey = this._makeDeviceKey(addressObject);
1424
+ const settings = {
1425
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1426
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1427
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1428
+ };
1429
+ const buffer = this._getBuffer();
1430
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1431
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.ACKNOWLEDGE_ALARM, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1432
+ baServices.alarmAcknowledge.encode(buffer, 57, objectId, eventState, ackText, evTimeStamp, ackTimeStamp);
1433
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1434
+ this.sendBvlc(address, port, buffer);
1435
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
1436
+ }
1437
+ /**
1438
+ * The confirmedPrivateTransfer command invokes a confirmed proprietary/non-standard service.
1439
+ * @function bacstack.confirmedPrivateTransfer
1440
+ * @param {string} address - IP address of the target device.
1441
+ * @param {number} vendorId - The unique vendor identification code.
1442
+ * @param {number} serviceNumber - The unique service identifier.
1443
+ * @param {number[]} [data] - Optional additional payload data.
1444
+ * @param {object=} options
1445
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
1446
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
1447
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
1448
+ * @param {function} next - The callback containing an error, in case of a failure.
1449
+ * @example
1450
+ * const bacnet = require('bacstack');
1451
+ * const client = new bacnet();
1452
+ *
1453
+ * client.confirmedPrivateTransfer('192.168.1.43', 0, 7, [0x00, 0xaa, 0xfa, 0xb1, 0x00], (err) => {
1454
+ * console.log('error: ', err);
1455
+ * });
1456
+ */
1457
+ confirmedPrivateTransfer(addressObject, vendorId, serviceNumber, data, options, next) {
1458
+ next = next || options;
1459
+ let address = addressObject.address ? addressObject.address : addressObject;
1460
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1461
+ const deviceKey = this._makeDeviceKey(addressObject);
1462
+ const settings = {
1463
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1464
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1465
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1466
+ };
1467
+ const buffer = this._getBuffer();
1468
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1469
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CONFIRMED_PRIVATE_TRANSFER, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1470
+ baServices.privateTransfer.encode(buffer, vendorId, serviceNumber, data);
1471
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1472
+ this.sendBvlc(address, port, buffer);
1473
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
1474
+ }
1475
+ /**
1476
+ * The unconfirmedPrivateTransfer command invokes an unconfirmed proprietary/non-standard service.
1477
+ * @function bacstack.unconfirmedPrivateTransfer
1478
+ * @param {string} address - IP address of the target device.
1479
+ * @param {number} vendorId - The unique vendor identification code.
1480
+ * @param {number} serviceNumber - The unique service identifier.
1481
+ * @param {number[]} [data] - Optional additional payload data.
1482
+ * @example
1483
+ * const bacnet = require('bacstack');
1484
+ * const client = new bacnet();
1485
+ *
1486
+ * client.unconfirmedPrivateTransfer('192.168.1.43', 0, 7, [0x00, 0xaa, 0xfa, 0xb1, 0x00]);
1487
+ */
1488
+ unconfirmedPrivateTransfer(addressObject, vendorId, serviceNumber, data) {
1489
+ const buffer = this._getBuffer();
1490
+ let address = addressObject.address ? addressObject.address : addressObject;
1491
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1492
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, address);
1493
+ baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.UNCONFIRMED_PRIVATE_TRANSFER);
1494
+ baServices.privateTransfer.encode(buffer, vendorId, serviceNumber, data);
1495
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1496
+ this.sendBvlc(address, port, buffer);
1497
+ }
1498
+ /**
1499
+ * DEPRECATED The getEnrollmentSummary command returns a list of event-initiating objects on the target device.
1500
+ * @function bacstack.getEnrollmentSummary
1501
+ * @param {string} address - IP address of the target device.
1502
+ * @param {number} acknowledgmentFilter - Filter for ALL/ACKED/NOT-ACKED, 0/1/2.
1503
+ * @param {object=} options
1504
+ * @param {object=} options.enrollmentFilter - Filter for enrollment.
1505
+ * @param {EventState=} options.eventStateFilter - Filter for event state.
1506
+ * @param {EventType=} options.eventTypeFilter - Filter for event type.
1507
+ * @param {object=} options.priorityFilter
1508
+ * @param {number} options.priorityFilter.min - Filter for minimal priority
1509
+ * @param {number} options.priorityFilter.max - Filter for maximal priority
1510
+ * @param {number=} options.notificationClassFilter - Filter for notification class.
1511
+ * @param {MaxSegmentsAccepted=} options.maxSegments - The maximimal allowed number of segments.
1512
+ * @param {MaxApduLengthAccepted=} options.maxApdu - The maximal allowed APDU size.
1513
+ * @param {number=} options.invokeId - The invoke ID of the confirmed service telegram.
1514
+ * @param {function} next - The callback containing an error, in case of a failure and value object in case of success.
1515
+ * @example
1516
+ * const bacnet = require('bacstack');
1517
+ * const client = new bacnet();
1518
+ *
1519
+ * client.getEnrollmentSummary('192.168.1.43', 0, (err, value) => {
1520
+ * console.log('value: ', value);
1521
+ * });
1522
+ */
1523
+ getEnrollmentSummary(addressObject, acknowledgmentFilter, options, next) {
1524
+ next = next || options;
1525
+ let address = addressObject.address ? addressObject.address : addressObject;
1526
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1527
+ const deviceKey = this._makeDeviceKey(addressObject);
1528
+ const settings = {
1529
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1530
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1531
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1532
+ };
1533
+ const buffer = this._getBuffer();
1534
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1535
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.GET_ENROLLMENT_SUMMARY, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1536
+ baServices.getEnrollmentSummary.encode(buffer, acknowledgmentFilter, options.enrollmentFilter, options.eventStateFilter, options.eventTypeFilter, options.priorityFilter, options.notificationClassFilter);
1537
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1538
+ this.sendBvlc(address, port, buffer);
1539
+ this._addCallback(deviceKey, settings.invokeId, (err, data) => {
1540
+ if (err)
1541
+ return next(err);
1542
+ const result = baServices.getEnrollmentSummary.decodeAcknowledge(data.buffer, data.offset, data.length);
1543
+ if (!result)
1544
+ return next(new Error('INVALID_DECODING'));
1545
+ next(null, result);
1546
+ });
1547
+ }
1548
+ unconfirmedEventNotification(addressObject, eventNotification) {
1549
+ let address = addressObject.address ? addressObject.address : addressObject;
1550
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1551
+ const buffer = this._getBuffer();
1552
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, address);
1553
+ baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.UNCONFIRMED_EVENT_NOTIFICATION);
1554
+ baServices.eventNotifyData.encode(buffer, eventNotification);
1555
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1556
+ this.sendBvlc(address, port, buffer);
1557
+ }
1558
+ confirmedEventNotification(addressObject, eventNotification, options, next) {
1559
+ next = next || options;
1560
+ let address = addressObject.address ? addressObject.address : addressObject;
1561
+ let port = addressObject.port ? addressObject.port : this._settings.port;
1562
+ const deviceKey = this._makeDeviceKey(addressObject);
1563
+ const settings = {
1564
+ maxSegments: options.maxSegments || baEnum.MaxSegmentsAccepted.SEGMENTS_65,
1565
+ maxApdu: options.maxApdu || baEnum.MaxApduLengthAccepted.OCTETS_1476,
1566
+ invokeId: (options.invokeId !== undefined && options.invokeId !== null) ? options.invokeId : this._getInvokeId(deviceKey)
1567
+ };
1568
+ const buffer = this._getBuffer();
1569
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE | baEnum.NpduControlBits.EXPECTING_REPLY, address);
1570
+ baApdu.encodeConfirmedServiceRequest(buffer, baEnum.PduTypes.CONFIRMED_REQUEST, baEnum.ConfirmedServiceChoice.CONFIRMED_EVENT_NOTIFICATION, settings.maxSegments, settings.maxApdu, settings.invokeId, 0, 0);
1571
+ baServices.eventNotifyData.encode(buffer, eventNotification);
1572
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1573
+ this.sendBvlc(address, port, buffer);
1574
+ this._addCallback(deviceKey, settings.invokeId, (err) => next(err));
1575
+ }
1576
+ // Public Device Functions
1577
+ readPropertyResponse(receiver, invokeId, objectId, property, value) {
1578
+ const buffer = this._getBuffer();
1579
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
1580
+ baApdu.encodeComplexAck(buffer, baEnum.PduTypes.COMPLEX_ACK, baEnum.ConfirmedServiceChoice.READ_PROPERTY, invokeId);
1581
+ baServices.readProperty.encodeAcknowledge(buffer, objectId, property.id, property.index, value);
1582
+ baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1583
+ this.sendBvlc(receiver, this._settings.port, buffer);
1584
+ }
1585
+ readPropertyMultipleResponse(receiver, invokeId, values) {
1586
+ const buffer = this._getBuffer();
1587
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
1588
+ baApdu.encodeComplexAck(buffer, baEnum.PduTypes.COMPLEX_ACK, baEnum.ConfirmedServiceChoice.READ_PROPERTY_MULTIPLE, invokeId);
1589
+ baServices.readPropertyMultiple.encodeAcknowledge(buffer, values);
1590
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1591
+ this.sendBvlc(receiver, this._settings.port, buffer);
1592
+ }
1593
+ iAmResponse(deviceId, segmentation, vendorId) {
1594
+ const buffer = this._getBuffer();
1595
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, this._transport.getBroadcastAddress());
1596
+ baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.I_AM);
1597
+ baServices.iAmBroadcast.encode(buffer, deviceId, this._transport.getMaxPayload(), segmentation, vendorId);
1598
+ baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_BROADCAST_NPDU, buffer.offset);
1599
+ this._transport.send(buffer.buffer, buffer.offset, this._transport.getBroadcastAddress(), this._settings.port);
1600
+ }
1601
+ iHaveResponse(deviceId, objectId, objectName) {
1602
+ const buffer = this._getBuffer();
1603
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, this._transport.getBroadcastAddress());
1604
+ baApdu.encodeUnconfirmedServiceRequest(buffer, baEnum.PduTypes.UNCONFIRMED_REQUEST, baEnum.UnconfirmedServiceChoice.I_HAVE);
1605
+ baServices.iHaveBroadcast.encode(buffer, deviceId, objectId, objectName);
1606
+ baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_BROADCAST_NPDU, buffer.offset);
1607
+ this._transport.send(buffer.buffer, buffer.offset, this._transport.getBroadcastAddress(), this._settings.port);
1608
+ }
1609
+ simpleAckResponse(receiver, service, invokeId) {
1610
+ const buffer = this._getBuffer();
1611
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
1612
+ baApdu.encodeSimpleAck(buffer, baEnum.PduTypes.SIMPLE_ACK, service, invokeId);
1613
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1614
+ this.sendBvlc(receiver, this._settings.port, buffer);
1615
+ }
1616
+ errorResponse(receiver, service, invokeId, errorClass, errorCode) {
1617
+ const buffer = this._getBuffer();
1618
+ baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
1619
+ baApdu.encodeError(buffer, baEnum.PduTypes.ERROR, service, invokeId);
1620
+ baServices.error.encode(buffer, errorClass, errorCode);
1621
+ //baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1622
+ this.sendBvlc(receiver, this._settings.port, buffer);
1623
+ }
1624
+ /**
1625
+ *
1626
+ * @param receiver
1627
+ * @param buffer
1628
+ */
1629
+ sendBvlc(receiver, port, buffer) {
1630
+ let that = this;
1631
+ if (typeof receiver === 'string') {
1632
+ receiver = {
1633
+ address: receiver
1634
+ };
1635
+ }
1636
+ if (receiver && receiver.forwardedFrom) {
1637
+ // Remote node address given, forward to BBMD
1638
+ baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.FORWARDED_NPDU, buffer.offset, receiver.forwardedFrom);
1639
+ } else if (receiver && receiver.address) {
1640
+ // Specific address, unicast
1641
+ baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
1642
+ } else {
1643
+ // No address, broadcast
1644
+ baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_BROADCAST_NPDU, buffer.offset);
1645
+ }
1646
+
1647
+ //check if port is array, send on all specified ports if true
1648
+ if (Array.isArray(port) && port.length > 0) {
1649
+ port.forEach(function (p) {
1650
+ that._transport.send(
1651
+ buffer.buffer,
1652
+ buffer.offset,
1653
+ (receiver && receiver.address) || null,
1654
+ p
1655
+ );
1656
+ });
1657
+ } else {
1658
+ this._transport.send(
1659
+ buffer.buffer,
1660
+ buffer.offset,
1661
+ (receiver && receiver.address) || null,
1662
+ port
1663
+ );
1664
+ }
1665
+ }
1666
+ /**
1667
+ * Unloads the current BACstack instance and closes the underlying UDP socket.
1668
+ * @function bacstack.close
1669
+ * @example
1670
+ * const bacnet = require('bacstack');
1671
+ * const client = new bacnet();
1672
+ *
1673
+ * client.close();
1674
+ */
1675
+ close() {
1676
+ this._transport.close();
1677
+ }
1678
+ }
1679
+ exports.Client = Client;