@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.
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/resources/node-bacstack-ts/dist/index.js +3 -0
- package/resources/node-bacstack-ts/dist/lib/apdu.js +193 -0
- package/resources/node-bacstack-ts/dist/lib/asn1.js +1671 -0
- package/resources/node-bacstack-ts/dist/lib/bvlc.js +47 -0
- package/resources/node-bacstack-ts/dist/lib/client.js +1679 -0
- package/resources/node-bacstack-ts/dist/lib/enum.js +2114 -0
- package/resources/node-bacstack-ts/dist/lib/npdu.js +112 -0
- package/resources/node-bacstack-ts/dist/lib/services/add-list-element.js +58 -0
- package/resources/node-bacstack-ts/dist/lib/services/alarm-acknowledge.js +93 -0
- package/resources/node-bacstack-ts/dist/lib/services/alarm-summary.js +42 -0
- package/resources/node-bacstack-ts/dist/lib/services/atomic-read-file.js +157 -0
- package/resources/node-bacstack-ts/dist/lib/services/atomic-write-file.js +136 -0
- package/resources/node-bacstack-ts/dist/lib/services/cov-notify.js +119 -0
- package/resources/node-bacstack-ts/dist/lib/services/create-object.js +104 -0
- package/resources/node-bacstack-ts/dist/lib/services/delete-object.js +21 -0
- package/resources/node-bacstack-ts/dist/lib/services/device-communication-control.js +46 -0
- package/resources/node-bacstack-ts/dist/lib/services/error.js +27 -0
- package/resources/node-bacstack-ts/dist/lib/services/event-information.js +100 -0
- package/resources/node-bacstack-ts/dist/lib/services/event-notify-data.js +219 -0
- package/resources/node-bacstack-ts/dist/lib/services/get-enrollment-summary.js +172 -0
- package/resources/node-bacstack-ts/dist/lib/services/get-event-information.js +135 -0
- package/resources/node-bacstack-ts/dist/lib/services/i-am-broadcast.js +59 -0
- package/resources/node-bacstack-ts/dist/lib/services/i-have-broadcast.js +34 -0
- package/resources/node-bacstack-ts/dist/lib/services/index.js +32 -0
- package/resources/node-bacstack-ts/dist/lib/services/life-safety-operation.js +40 -0
- package/resources/node-bacstack-ts/dist/lib/services/private-transfer.js +43 -0
- package/resources/node-bacstack-ts/dist/lib/services/read-property-multiple.js +44 -0
- package/resources/node-bacstack-ts/dist/lib/services/read-property.js +122 -0
- package/resources/node-bacstack-ts/dist/lib/services/read-range.js +201 -0
- package/resources/node-bacstack-ts/dist/lib/services/reinitialize-device.js +35 -0
- package/resources/node-bacstack-ts/dist/lib/services/subscribe-cov.js +55 -0
- package/resources/node-bacstack-ts/dist/lib/services/subscribe-property.js +93 -0
- package/resources/node-bacstack-ts/dist/lib/services/time-sync.js +31 -0
- package/resources/node-bacstack-ts/dist/lib/services/who-has.js +56 -0
- package/resources/node-bacstack-ts/dist/lib/services/who-is.js +45 -0
- package/resources/node-bacstack-ts/dist/lib/services/write-property-multiple.js +105 -0
- package/resources/node-bacstack-ts/dist/lib/services/write-property.js +90 -0
- package/resources/node-bacstack-ts/dist/lib/transport.js +86 -0
- 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;
|