@bitpoolos/edge-bacnet 1.0.9 → 1.1.0
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/bacnet_client.js +299 -315
- package/bacnet_device.js +41 -15
- package/bacnet_gateway.html +88 -42
- package/bacnet_gateway.js +80 -27
- package/bacnet_read.html +110 -34
- package/bacnet_server.js +321 -0
- package/bacnet_write.html +14 -9
- package/common.js +34 -3
- package/edge-bacnet-datastore.cfg +0 -0
- package/package.json +1 -1
package/bacnet_client.js
CHANGED
|
@@ -2,19 +2,24 @@
|
|
|
2
2
|
MIT License Copyright 2021, 2022 - Bitpool Pty Ltd
|
|
3
3
|
*/
|
|
4
4
|
const bacnet = require('./resources/node-bacnet/index.js');
|
|
5
|
+
const { BacnetServer } = require("./bacnet_server.js");
|
|
5
6
|
const baEnum = bacnet.enum;
|
|
6
7
|
const bacnetIdMax = baEnum.ASN1_MAX_PROPERTY_ID;
|
|
7
8
|
const { EventEmitter, captureRejectionSymbol } = require('events');
|
|
8
|
-
const { DeviceObjectId, DeviceObject, logger, getUnit, roundDecimalPlaces } = require('./common');
|
|
9
|
-
const { ToadScheduler, SimpleIntervalJob, Task } = require('toad-scheduler')
|
|
9
|
+
const { DeviceObjectId, DeviceObject, logger, getUnit, roundDecimalPlaces, Store_Config, Read_Config_Sync } = require('./common');
|
|
10
|
+
const { ToadScheduler, SimpleIntervalJob, Task } = require('toad-scheduler');
|
|
10
11
|
const { BacnetDevice } = require('./bacnet_device');
|
|
11
12
|
const {Mutex, Semaphore, withTimeout} = require("async-mutex");
|
|
13
|
+
|
|
12
14
|
class BacnetClient extends EventEmitter {
|
|
13
15
|
|
|
14
16
|
//client constructor
|
|
15
17
|
constructor(config) {
|
|
16
18
|
super();
|
|
17
19
|
let that = this;
|
|
20
|
+
|
|
21
|
+
let cachedData = JSON.parse(Read_Config_Sync());
|
|
22
|
+
|
|
18
23
|
that.deviceList = [];
|
|
19
24
|
that.networkTree = {};
|
|
20
25
|
that.lastWhoIs = null;
|
|
@@ -23,6 +28,17 @@ class BacnetClient extends EventEmitter {
|
|
|
23
28
|
that.scheduler = new ToadScheduler();
|
|
24
29
|
that.mutex = new Mutex();
|
|
25
30
|
|
|
31
|
+
if(typeof cachedData == "object") {
|
|
32
|
+
if(cachedData.renderList) that.renderList = cachedData.renderList;
|
|
33
|
+
if(cachedData.deviceList) {
|
|
34
|
+
cachedData.deviceList.forEach(function(device) {
|
|
35
|
+
let newBacnetDevice = new BacnetDevice(true, device);
|
|
36
|
+
that.deviceList.push(newBacnetDevice);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if(cachedData.pointList) that.networkTree = cachedData.pointList;
|
|
40
|
+
}
|
|
41
|
+
|
|
26
42
|
try {
|
|
27
43
|
that.config = config;
|
|
28
44
|
that.roundDecimal = config.roundDecimal;
|
|
@@ -37,31 +53,13 @@ class BacnetClient extends EventEmitter {
|
|
|
37
53
|
that.manual_instance_range_enabled = config.manual_instance_range_enabled;
|
|
38
54
|
that.manual_instance_range_start = config.manual_instance_range_start;
|
|
39
55
|
that.manual_instance_range_end = config.manual_instance_range_end;
|
|
56
|
+
that.bacnetServerEnabled = config.bacnetServerEnabled;
|
|
40
57
|
|
|
41
58
|
that.readPropertyMultipleOptions = {
|
|
42
59
|
maxSegments: that.maxSegments,
|
|
43
60
|
maxApdu: that.apduSize
|
|
44
61
|
};
|
|
45
62
|
|
|
46
|
-
that.selfObjectList = [
|
|
47
|
-
{value: {type: baEnum.ObjectType.DEVICE, instance: that.deviceId}, type: 12}
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
that.selfData = {
|
|
51
|
-
8: {
|
|
52
|
-
[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER]: [{value: {type: baEnum.ObjectType.DEVICE, instance: that.deviceId}, type: 12}], // OBJECT_IDENTIFIER
|
|
53
|
-
[baEnum.PropertyIdentifier.OBJECT_LIST]: that.selfObjectList, // OBJECT_IDENTIFIER
|
|
54
|
-
[baEnum.PropertyIdentifier.OBJECT_NAME]: [{value: 'Bitpool Edge BACnet Gateway', type: 7}], // OBJECT_NAME
|
|
55
|
-
[baEnum.PropertyIdentifier.OBJECT_TYPE]: [{value: 8, type: 9}], // OBJECT_TYPE
|
|
56
|
-
[baEnum.PropertyIdentifier.DESCRIPTION]: [{value: 'Bitpool Edge BACnet gateway', type: 7}], // DESCRIPTION
|
|
57
|
-
[baEnum.PropertyIdentifier.SYSTEM_STATUS]: [{value: 0, type: 9}], // SYSTEM_STATUS
|
|
58
|
-
[baEnum.PropertyIdentifier.VENDOR_NAME]: [{value: "Bitpool", type: 7}], //VENDOR_NAME
|
|
59
|
-
[baEnum.PropertyIdentifier.VENDOR_IDENTIFIER]: [{value: 9999, type: 7}], //VENDOR_IDENTIFIER
|
|
60
|
-
[baEnum.PropertyIdentifier.MODEL_NAME]: [{value: "bitpool-edge", type: 7}], //MODEL_NAME
|
|
61
|
-
[baEnum.PropertyIdentifier.FIRMWARE_REVISION]: [{value: "Node-Red v3.0.2", type: 7}], //FIRMWARE_REVISION
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
63
|
try {
|
|
66
64
|
|
|
67
65
|
that.client = new bacnet({ apduTimeout: config.apduTimeout, interface: config.localIpAdrress, port: config.port, broadcastAddress: config.broadCastAddr});
|
|
@@ -73,21 +71,20 @@ class BacnetClient extends EventEmitter {
|
|
|
73
71
|
|
|
74
72
|
const job = new SimpleIntervalJob({ seconds: parseInt(that.discover_polling_schedule), }, task)
|
|
75
73
|
|
|
76
|
-
that.scheduler.addSimpleIntervalJob(job)
|
|
74
|
+
that.scheduler.addSimpleIntervalJob(job);
|
|
77
75
|
|
|
78
76
|
//query device task
|
|
79
|
-
const queryDevices = new Task('simple task', () => {
|
|
77
|
+
const queryDevices = new Task('simple task', () => {
|
|
80
78
|
that.queryDevices();
|
|
79
|
+
that.sanitizeDeviceList();
|
|
81
80
|
});
|
|
82
81
|
|
|
83
82
|
const queryJob = new SimpleIntervalJob({ seconds: parseInt(that.discover_polling_schedule), }, queryDevices)
|
|
84
|
-
//const queryJob = new SimpleIntervalJob({ seconds: 10, }, queryDevices)
|
|
85
|
-
|
|
86
83
|
|
|
87
84
|
that.scheduler.addSimpleIntervalJob(queryJob);
|
|
88
85
|
|
|
89
86
|
//buildNetworkTreeData task
|
|
90
|
-
const buildNetworkTree = new Task('simple task', () => {
|
|
87
|
+
const buildNetworkTree = new Task('simple task', () => {
|
|
91
88
|
that.buildNetworkTreeData();
|
|
92
89
|
});
|
|
93
90
|
|
|
@@ -102,99 +99,72 @@ class BacnetClient extends EventEmitter {
|
|
|
102
99
|
}, "5000")
|
|
103
100
|
|
|
104
101
|
} catch(e) {
|
|
105
|
-
|
|
102
|
+
that.logOut("Issue initializing client: ", e)
|
|
106
103
|
}
|
|
107
104
|
|
|
108
105
|
//who is callback
|
|
109
106
|
that.client.on('iAm', (device) => {
|
|
110
107
|
if(device.header.sender.address !== that.config.localIpAdrress) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
that.client.on('whoIs', (device) => {
|
|
131
|
-
that.client.iAmResponse(that.broadCastAddr, that.deviceId, baEnum.Segmentation.SEGMENTED_BOTH, 27823);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
that.client.on('readPropertyMultiple', (data) => {
|
|
135
|
-
try {
|
|
136
|
-
if(data.payload.properties[0]){
|
|
137
|
-
let objectId = data.payload.properties[0].objectId.type;
|
|
138
|
-
let objectInstance = data.payload.properties[0].objectId.instance;
|
|
139
|
-
let propId = data.payload.properties[0].properties[0].id.toString();
|
|
140
|
-
let responseObj = that.selfData[objectId][propId];
|
|
141
|
-
|
|
142
|
-
if(responseObj !== null && responseObj !== "undefined") {
|
|
143
|
-
that.client.readPropertyMultipleResponse(data.address, data.invokeId, responseObj);
|
|
144
|
-
} else {
|
|
145
|
-
this.client.errorResponse(
|
|
146
|
-
data.address,
|
|
147
|
-
baEnum.ConfirmedServices.SERVICE_CONFIRMED_READ_PROPERTY,
|
|
148
|
-
data.invokeId,
|
|
149
|
-
baEnum.ErrorClasses.ERROR_CLASS_PROPERTY,
|
|
150
|
-
baEnum.ErrorCodes.ERROR_CODE_UNKNOWN_PROPERTY
|
|
151
|
-
);
|
|
108
|
+
if(that.device_id_range_enabled) {
|
|
109
|
+
if(device.payload.deviceId >= that.device_id_range_start && device.payload.deviceId <= that.device_id_range_end) {
|
|
110
|
+
//only add unique device to array
|
|
111
|
+
let foundIndex = that.deviceList.findIndex(ele => ele.getDeviceId() == device.payload.deviceId);
|
|
112
|
+
if(foundIndex == -1) {
|
|
113
|
+
let newBacnetDevice = new BacnetDevice(false, device);
|
|
114
|
+
newBacnetDevice.setLastSeen(Date.now());
|
|
115
|
+
that.updateDeviceName(newBacnetDevice);
|
|
116
|
+
that.deviceList.push(newBacnetDevice);
|
|
117
|
+
|
|
118
|
+
} else if(foundIndex !== -1) {
|
|
119
|
+
that.deviceList[foundIndex].updateDeviceConfig(device);
|
|
120
|
+
that.deviceList[foundIndex].setLastSeen(Date.now());
|
|
121
|
+
that.updateDeviceName(that.deviceList[foundIndex]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
//emit event for node-red to log
|
|
125
|
+
that.emit('deviceFound', device);
|
|
152
126
|
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
} catch(e) {
|
|
156
|
-
//console.log("Local BACnet device readPropertyMultiple error: ");
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
that.client.on('readProperty', (data) => {
|
|
161
|
-
try {
|
|
162
|
-
let objectId = data.payload.objectId.type;
|
|
163
|
-
let propId = data.payload.property.id.toString();
|
|
164
|
-
let responseObj = that.selfData[objectId][propId];
|
|
165
|
-
if(responseObj !== null && responseObj !== undefined && typeof responseObj !== "undefined") {
|
|
166
|
-
that.client.readPropertyResponse(data.header.sender.address, data.invokeId, objectId, data.payload.property, responseObj);
|
|
167
127
|
} else {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
128
|
+
//only add unique device to array
|
|
129
|
+
let foundIndex = that.deviceList.findIndex(ele => ele.getDeviceId() == device.payload.deviceId);
|
|
130
|
+
if(foundIndex == -1) {
|
|
131
|
+
let newBacnetDevice = new BacnetDevice(false, device);
|
|
132
|
+
newBacnetDevice.setLastSeen(Date.now());
|
|
133
|
+
that.updateDeviceName(newBacnetDevice);
|
|
134
|
+
that.deviceList.push(newBacnetDevice);
|
|
135
|
+
|
|
136
|
+
} else if(foundIndex !== -1) {
|
|
137
|
+
that.deviceList[foundIndex].updateDeviceConfig(device);
|
|
138
|
+
that.deviceList[foundIndex].setLastSeen(Date.now());
|
|
139
|
+
that.updateDeviceName(that.deviceList[foundIndex]);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//emit event for node-red to log
|
|
143
|
+
that.emit('deviceFound', device);
|
|
175
144
|
}
|
|
176
|
-
} catch(e){
|
|
177
|
-
console.log("Local BACnet device readProperty error: ", e);
|
|
178
145
|
}
|
|
179
|
-
|
|
180
146
|
});
|
|
181
147
|
|
|
182
148
|
} catch(e) {
|
|
183
|
-
|
|
149
|
+
that.logOut("Issue with creating bacnet client, see error: ", e);
|
|
184
150
|
}
|
|
185
151
|
|
|
186
152
|
that.client.on('error', (err) => {
|
|
187
|
-
|
|
153
|
+
that.logOut('Error occurred: ', err);
|
|
188
154
|
|
|
189
155
|
if(err.errno == -4090){
|
|
190
|
-
|
|
156
|
+
that.logOut("Invalid Client information or incorrect IP address provided");
|
|
191
157
|
} else if(err.errno == -49) {
|
|
192
|
-
|
|
158
|
+
that.logOut("Invalid IP address provided");
|
|
193
159
|
} else {
|
|
194
160
|
that.reinitializeClient(that.config);
|
|
195
161
|
}
|
|
196
162
|
});
|
|
163
|
+
}
|
|
197
164
|
|
|
165
|
+
logOut(param1, param2) {
|
|
166
|
+
let that = this;
|
|
167
|
+
that.emit('bacnetErrorLog', param1, param2);
|
|
198
168
|
}
|
|
199
169
|
|
|
200
170
|
rebuildDataModel() {
|
|
@@ -206,7 +176,7 @@ class BacnetClient extends EventEmitter {
|
|
|
206
176
|
that.networkTree = {};
|
|
207
177
|
resolve(true);
|
|
208
178
|
} catch(e) {
|
|
209
|
-
|
|
179
|
+
that.logOut("Error clearing BACnet data model: ", e);
|
|
210
180
|
reject(e);
|
|
211
181
|
}
|
|
212
182
|
});
|
|
@@ -220,16 +190,6 @@ class BacnetClient extends EventEmitter {
|
|
|
220
190
|
.then(function(release) {
|
|
221
191
|
try {
|
|
222
192
|
|
|
223
|
-
// that.getDevicePointListWithoutObjectList(device).then(function() {
|
|
224
|
-
// that.buildJsonObject(device).then(function() {
|
|
225
|
-
// release();
|
|
226
|
-
// }).catch(function(e) {
|
|
227
|
-
// release();
|
|
228
|
-
// });
|
|
229
|
-
// }).catch(function(e) {
|
|
230
|
-
// release();
|
|
231
|
-
// });
|
|
232
|
-
|
|
233
193
|
that.getDevicePointList(device).then(function() {
|
|
234
194
|
that.buildJsonObject(device).then(function() {
|
|
235
195
|
release();
|
|
@@ -249,13 +209,35 @@ class BacnetClient extends EventEmitter {
|
|
|
249
209
|
release();
|
|
250
210
|
});
|
|
251
211
|
|
|
252
|
-
|
|
253
212
|
} catch(e) {
|
|
254
|
-
|
|
213
|
+
that.logOut("Error while querying devices: ", e);
|
|
255
214
|
release();
|
|
256
215
|
}
|
|
257
216
|
});
|
|
258
217
|
});
|
|
218
|
+
Store_Config(JSON.stringify({renderList: that.renderList, deviceList: that.deviceList, pointList: that.networkTree}));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
sanitizeDeviceList() {
|
|
222
|
+
let that = this;
|
|
223
|
+
|
|
224
|
+
that.deviceList.forEach(function(device, index) {
|
|
225
|
+
if(((Date.now() - device.getLastSeen()) / 1000) > 3600) {
|
|
226
|
+
//device hasnt responded to whoIs for over an hour
|
|
227
|
+
|
|
228
|
+
let deviceKey = (typeof device.getAddress() == "object") ? device.getAddress().address + "-" + device.getDeviceId() : device.getAddress() + "-" + device.getDeviceId();
|
|
229
|
+
delete that.networkTree[deviceKey];
|
|
230
|
+
|
|
231
|
+
if(that.renderList){
|
|
232
|
+
let foundIndex = that.renderList.findIndex(ele => ele.ipAddr == device.getAddress() && ele.deviceId == device.getDeviceId());
|
|
233
|
+
if(foundIndex !== -1) {
|
|
234
|
+
that.renderList.splice(foundIndex, 1);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
that.deviceList.splice(index, 1);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
259
241
|
}
|
|
260
242
|
|
|
261
243
|
updateDeviceName(device) {
|
|
@@ -268,21 +250,34 @@ class BacnetClient extends EventEmitter {
|
|
|
268
250
|
reinitializeClient(config) {
|
|
269
251
|
let that = this;
|
|
270
252
|
|
|
253
|
+
that.config = config;
|
|
254
|
+
that.roundDecimal = config.roundDecimal;
|
|
255
|
+
that.apduSize = config.apduSize;
|
|
256
|
+
that.maxSegments = config.maxSegments;
|
|
257
|
+
that.discover_polling_schedule = config.discover_polling_schedule;
|
|
271
258
|
that.device_id_range_enabled = config.device_id_range_enabled;
|
|
272
259
|
that.device_id_range_start = config.device_id_range_start;
|
|
273
260
|
that.device_id_range_end = config.device_id_range_end;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
261
|
+
that.deviceId = config.deviceId;
|
|
262
|
+
that.broadCastAddr = config.broadCastAddr;
|
|
263
|
+
that.manual_instance_range_enabled = config.manual_instance_range_enabled;
|
|
264
|
+
that.manual_instance_range_start = config.manual_instance_range_start;
|
|
265
|
+
that.manual_instance_range_end = config.manual_instance_range_end;
|
|
266
|
+
that.bacnetServerEnabled = config.bacnetServerEnabled;
|
|
279
267
|
|
|
280
268
|
if(that.scheduler !== null) {
|
|
281
269
|
that.scheduler.stop();
|
|
282
270
|
}
|
|
283
271
|
|
|
284
|
-
try{
|
|
285
|
-
that.client =
|
|
272
|
+
try {
|
|
273
|
+
that.client._settings.apduTimeout = config.apduTimeout;
|
|
274
|
+
that.client._settings.interface = config.localIpAdrress;
|
|
275
|
+
that.client._settings.port = config.port;
|
|
276
|
+
that.client._settings.broadcastAddress = config.broadCastAddr;
|
|
277
|
+
|
|
278
|
+
that.client._transport.interface = config.localIpAdrress;
|
|
279
|
+
that.client._transport.port = config.port;
|
|
280
|
+
that.client._transport.broadcastAddress = config.broadCastAddr;
|
|
286
281
|
|
|
287
282
|
const task = new Task('simple task', () => {
|
|
288
283
|
that.globalWhoIs();
|
|
@@ -295,11 +290,12 @@ class BacnetClient extends EventEmitter {
|
|
|
295
290
|
that.globalWhoIs();
|
|
296
291
|
|
|
297
292
|
} catch(e){
|
|
298
|
-
|
|
293
|
+
that.logOut("Error reinitializing bacnet client: ", e)
|
|
299
294
|
}
|
|
300
295
|
};
|
|
301
296
|
|
|
302
|
-
getValidPointProperties(point, requestedProps){
|
|
297
|
+
getValidPointProperties(point, requestedProps) {
|
|
298
|
+
let that = this;
|
|
303
299
|
let availableProps = point.propertyList;
|
|
304
300
|
let newProps = [];
|
|
305
301
|
|
|
@@ -311,7 +307,7 @@ class BacnetClient extends EventEmitter {
|
|
|
311
307
|
//add object name for use in formatting
|
|
312
308
|
newProps.push({id: baEnum.PropertyIdentifier.OBJECT_NAME});
|
|
313
309
|
} catch(e){
|
|
314
|
-
|
|
310
|
+
that.logOut("Issue finding valid object properties, see error: ", e);
|
|
315
311
|
}
|
|
316
312
|
|
|
317
313
|
return newProps;
|
|
@@ -319,49 +315,31 @@ class BacnetClient extends EventEmitter {
|
|
|
319
315
|
|
|
320
316
|
doRead(readConfig, outputType, objectPropertyType, msgId) {
|
|
321
317
|
let that = this;
|
|
322
|
-
|
|
323
318
|
that.roundDecimal = readConfig.precision;
|
|
324
319
|
let devicesToRead = Object.keys(readConfig.pointsToRead);
|
|
325
|
-
let propertiesToRead = readConfig.objectProperties;
|
|
326
320
|
|
|
327
321
|
try {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
// this.client.readPropertyMultiple("10.0.8.202", requestArray1, that.readPropertyMultipleOptions, (error, value) => {
|
|
336
|
-
// console.log("error: ", error);
|
|
337
|
-
// console.log("value: ", value.values[0].values[0]);
|
|
338
|
-
// console.log("value: ", value.values[0].values[1]);
|
|
339
|
-
// //console.log(JSON.stringify(value));
|
|
340
|
-
// });
|
|
341
|
-
|
|
342
|
-
// that.client.readRange("10.0.8.202", 300002, 1, 5, {}, (error, value) => {
|
|
343
|
-
// console.log("that.client.readRange: ");
|
|
344
|
-
// console.log("error: ", error);
|
|
345
|
-
// console.log("value: ", value);
|
|
346
|
-
// });
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
devicesToRead.forEach(function(deviceId) {
|
|
351
|
-
let readPromiseArray = [];
|
|
352
|
-
let pointsToReadNames = Object.keys(readConfig.pointsToRead[deviceId]);
|
|
353
|
-
let device = that.deviceList.find(ele => ele.getDeviceId() == deviceId);
|
|
354
|
-
pointsToReadNames.forEach(function(pointName, index) {
|
|
355
|
-
let point = readConfig.pointsToRead[deviceId][pointName];
|
|
356
|
-
readPromiseArray.push(that._readObjectFull(device.getAddress(), point.meta.objectId.type, point.meta.objectId.instance));
|
|
357
|
-
});
|
|
358
|
-
that.readDeviceAndEmitJSON(readPromiseArray, device, outputType, propertiesToRead, objectPropertyType, msgId);
|
|
322
|
+
let bacnetResults = {};
|
|
323
|
+
devicesToRead.forEach(function(key, index) {
|
|
324
|
+
let device = that.deviceList.find(ele => `${that.getDeviceAddress(ele)}-${ele.getDeviceId()}` == key);
|
|
325
|
+
let deviceName = device.getDeviceName();
|
|
326
|
+
bacnetResults[deviceName] = readConfig.pointsToRead[key];
|
|
327
|
+
if(index == devicesToRead.length - 1) that.emit('values', bacnetResults, outputType, objectPropertyType);
|
|
359
328
|
});
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
console.log("Issue doing read, see error: ", e);
|
|
329
|
+
} catch(e) {
|
|
330
|
+
that.logOut("Issue doing read, see error: ", e);
|
|
363
331
|
}
|
|
332
|
+
}
|
|
364
333
|
|
|
334
|
+
getDeviceAddress(device){
|
|
335
|
+
switch(typeof device.getAddress()) {
|
|
336
|
+
case "object":
|
|
337
|
+
return device.getAddress().address;
|
|
338
|
+
case "string":
|
|
339
|
+
return device.getAddress();
|
|
340
|
+
default:
|
|
341
|
+
return device.getAddress();
|
|
342
|
+
}
|
|
365
343
|
}
|
|
366
344
|
|
|
367
345
|
readDeviceAndEmitJSON(readPromiseArray, device, outputType, propertiesToRead, objectPropertyType, msgId) {
|
|
@@ -390,15 +368,6 @@ class BacnetClient extends EventEmitter {
|
|
|
390
368
|
let bac_obj = that.getObjectType(currobjectId);
|
|
391
369
|
let objectId;
|
|
392
370
|
objectId = objectName;
|
|
393
|
-
|
|
394
|
-
/*
|
|
395
|
-
if(objectName !== null) {
|
|
396
|
-
objectName = objectName.split(" ").join("_");
|
|
397
|
-
objectId = objectName + "_" + bac_obj + '_' + point.objectId.instance;
|
|
398
|
-
} else {
|
|
399
|
-
objectId = bac_obj + '_' + point.objectId.instance;
|
|
400
|
-
}
|
|
401
|
-
*/
|
|
402
371
|
|
|
403
372
|
//init json object
|
|
404
373
|
if(!values[objectId]) values[objectId] = {};
|
|
@@ -408,7 +377,14 @@ class BacnetClient extends EventEmitter {
|
|
|
408
377
|
if(object.value[0] &&
|
|
409
378
|
object.value[0].value !== "undefined" &&
|
|
410
379
|
object.value[0].value !== null &&
|
|
411
|
-
typeof object.value[0].value == "number")
|
|
380
|
+
typeof object.value[0].value == "number") {
|
|
381
|
+
values[objectId].presentValue = roundDecimalPlaces(object.value[0].value, that.roundDecimal);
|
|
382
|
+
} else if(object.value[0] &&
|
|
383
|
+
object.value[0].value !== "undefined" &&
|
|
384
|
+
object.value[0].value !== null &&
|
|
385
|
+
typeof object.value[0].value == "string") {
|
|
386
|
+
values[objectId].presentValue = object.value[0].value;
|
|
387
|
+
}
|
|
412
388
|
break;
|
|
413
389
|
case baEnum.PropertyIdentifier.DESCRIPTION:
|
|
414
390
|
if(object.value[0]) values[objectId].description = object.value[0].value;
|
|
@@ -455,7 +431,7 @@ class BacnetClient extends EventEmitter {
|
|
|
455
431
|
bacnetResults[deviceName] = values;
|
|
456
432
|
|
|
457
433
|
} catch(e) {
|
|
458
|
-
|
|
434
|
+
that.logOut("issue resolving bacnet payload, see error: ", e);
|
|
459
435
|
}
|
|
460
436
|
});
|
|
461
437
|
if(Object.keys(bacnetResults[deviceName]).length !== 0) {
|
|
@@ -467,7 +443,7 @@ class BacnetClient extends EventEmitter {
|
|
|
467
443
|
});
|
|
468
444
|
|
|
469
445
|
} catch(e){
|
|
470
|
-
|
|
446
|
+
that.logOut("Issue reading from device, see error: ", e);
|
|
471
447
|
}
|
|
472
448
|
}
|
|
473
449
|
|
|
@@ -481,10 +457,10 @@ class BacnetClient extends EventEmitter {
|
|
|
481
457
|
if(object.value[0]) {
|
|
482
458
|
resolve(object.value[0].value);
|
|
483
459
|
} else {
|
|
484
|
-
|
|
460
|
+
that.logOut("Issue with deviceName payload, see object: ", object);
|
|
485
461
|
}
|
|
486
462
|
} catch(e){
|
|
487
|
-
|
|
463
|
+
that.logOut("Unable to get device name: ", e);
|
|
488
464
|
}
|
|
489
465
|
});
|
|
490
466
|
}
|
|
@@ -493,9 +469,10 @@ class BacnetClient extends EventEmitter {
|
|
|
493
469
|
}
|
|
494
470
|
|
|
495
471
|
getPropertiesForType(props, type) {
|
|
472
|
+
let that = this;
|
|
496
473
|
let newProps = [];
|
|
497
474
|
props.forEach(function(prop) {
|
|
498
|
-
//
|
|
475
|
+
//that.logOut(prop);
|
|
499
476
|
switch(type){
|
|
500
477
|
case 0: //analog-input
|
|
501
478
|
newProps.push(prop);
|
|
@@ -533,11 +510,12 @@ class BacnetClient extends EventEmitter {
|
|
|
533
510
|
let that = this;
|
|
534
511
|
return new Promise(async function(resolve, reject) {
|
|
535
512
|
try {
|
|
536
|
-
|
|
513
|
+
device.setManualDiscoveryMode(false);
|
|
514
|
+
let result = await that.scanDevice(device);
|
|
537
515
|
device.setPointsList(result);
|
|
538
516
|
resolve(result);
|
|
539
517
|
} catch(e) {
|
|
540
|
-
|
|
518
|
+
that.logOut(`Error getting point list for ${device.getAddress()} - ${device.getDeviceId()}: `, e);
|
|
541
519
|
reject(e);
|
|
542
520
|
}
|
|
543
521
|
});
|
|
@@ -547,6 +525,7 @@ class BacnetClient extends EventEmitter {
|
|
|
547
525
|
let that = this;
|
|
548
526
|
return new Promise(function(resolve, reject) {
|
|
549
527
|
try {
|
|
528
|
+
device.setManualDiscoveryMode(true);
|
|
550
529
|
that.scanDeviceManually(device).then(function(result) {
|
|
551
530
|
device.setPointsList(result);
|
|
552
531
|
resolve(result);
|
|
@@ -556,7 +535,7 @@ class BacnetClient extends EventEmitter {
|
|
|
556
535
|
|
|
557
536
|
|
|
558
537
|
} catch(e) {
|
|
559
|
-
|
|
538
|
+
that.logOut("Error getting point list: ", e);
|
|
560
539
|
reject(e);
|
|
561
540
|
}
|
|
562
541
|
|
|
@@ -565,12 +544,9 @@ class BacnetClient extends EventEmitter {
|
|
|
565
544
|
|
|
566
545
|
scanDeviceManually(device) {
|
|
567
546
|
let that = this;
|
|
568
|
-
// console.log("scanning device: ", device.getAddress());
|
|
569
547
|
return new Promise(function(resolve, reject) {
|
|
570
548
|
let objectNameProperty = [{ id: baEnum.PropertyIdentifier.OBJECT_NAME }];
|
|
571
549
|
let address = device.getAddress();
|
|
572
|
-
//let deviceInstance = device.deviceId();
|
|
573
|
-
//let objectTypeList = [0, 1, 2, 3, 4, 5, 8, 13, 14, 19];
|
|
574
550
|
let objectTypeList = [0, 1, 2, 3];
|
|
575
551
|
let instanceRange = {start: 0, end: 100};
|
|
576
552
|
let requestArray = [];
|
|
@@ -579,8 +555,6 @@ class BacnetClient extends EventEmitter {
|
|
|
579
555
|
let requestBuffer = [];
|
|
580
556
|
let sendBuffer = [];
|
|
581
557
|
|
|
582
|
-
//requestBuffer.push(that._readObjectFull(address, 8, deviceInstance));
|
|
583
|
-
|
|
584
558
|
if(that.manual_instance_range_enabled == true) {
|
|
585
559
|
instanceRange.start = that.manual_instance_range_start;
|
|
586
560
|
maxRequestThreshold = that.manual_instance_range_end;
|
|
@@ -616,8 +590,7 @@ class BacnetClient extends EventEmitter {
|
|
|
616
590
|
instanceRange.end = 100;
|
|
617
591
|
};
|
|
618
592
|
|
|
619
|
-
|
|
620
|
-
//console.log("request buffer size: ", requestBuffer.length);
|
|
593
|
+
function send() {
|
|
621
594
|
|
|
622
595
|
for(let index = 0; index < requestBuffer.length; index++) {
|
|
623
596
|
let promise = requestBuffer[index];
|
|
@@ -630,7 +603,6 @@ class BacnetClient extends EventEmitter {
|
|
|
630
603
|
let ele = value.values[x];
|
|
631
604
|
let valueRoot = ele.values[0].value[0];
|
|
632
605
|
if(!valueRoot.value.errorClass && !valueRoot.value.errorCode) {
|
|
633
|
-
// console.log("pushing to send buffer");
|
|
634
606
|
sendBuffer.push({"value": ele.objectId, "type": 12});
|
|
635
607
|
}
|
|
636
608
|
}
|
|
@@ -638,14 +610,12 @@ class BacnetClient extends EventEmitter {
|
|
|
638
610
|
}
|
|
639
611
|
|
|
640
612
|
if(index == requestBuffer.length - 1) {
|
|
641
|
-
// console.log("resolving send buffer");
|
|
642
613
|
resolve(sendBuffer);
|
|
643
614
|
}
|
|
644
615
|
|
|
645
616
|
}).catch(function(error) {
|
|
646
617
|
reject(error);
|
|
647
618
|
});
|
|
648
|
-
|
|
649
619
|
} catch(e) {
|
|
650
620
|
reject(e)
|
|
651
621
|
}
|
|
@@ -656,7 +626,6 @@ class BacnetClient extends EventEmitter {
|
|
|
656
626
|
|
|
657
627
|
_readObjectWithRequestArray(deviceAddress, requestArray) {
|
|
658
628
|
let that = this;
|
|
659
|
-
|
|
660
629
|
return new Promise((resolve, reject) => {
|
|
661
630
|
this.client.readPropertyMultiple(deviceAddress, requestArray, that.readPropertyMultipleOptions, (error, value) => {
|
|
662
631
|
resolve({
|
|
@@ -706,30 +675,16 @@ class BacnetClient extends EventEmitter {
|
|
|
706
675
|
try {
|
|
707
676
|
that.client.readPropertyMultiple(deviceAddress, requestArray, that.readPropertyMultipleOptions, callback);
|
|
708
677
|
} catch(e) {
|
|
709
|
-
|
|
678
|
+
that.logOut("Error reading object list: ", e);
|
|
710
679
|
}
|
|
711
680
|
}
|
|
712
681
|
|
|
713
682
|
_readObjectFull(deviceAddress, type, instance) {
|
|
714
683
|
|
|
715
684
|
return this._readObject(deviceAddress, type, instance, [
|
|
716
|
-
{ id: baEnum.PropertyIdentifier.
|
|
717
|
-
{ id: baEnum.PropertyIdentifier.OBJECT_NAME },
|
|
718
|
-
{ id: baEnum.PropertyIdentifier.OBJECT_TYPE },
|
|
719
|
-
{ id: baEnum.PropertyIdentifier.DESCRIPTION },
|
|
720
|
-
{ id: baEnum.PropertyIdentifier.UNITS },
|
|
721
|
-
{ id: baEnum.PropertyIdentifier.PRESENT_VALUE },
|
|
722
|
-
{ id: baEnum.PropertyIdentifier.PROPERTY_LIST },
|
|
723
|
-
{ id: baEnum.PropertyIdentifier.STATUS_FLAGS },
|
|
724
|
-
{ id: baEnum.PropertyIdentifier.RELIABILITY },
|
|
725
|
-
{ id: baEnum.PropertyIdentifier.OUT_OF_SERVICE },
|
|
726
|
-
|
|
727
|
-
{ id: baEnum.PropertyIdentifier.SYSTEM_STATUS },
|
|
728
|
-
{ id: baEnum.PropertyIdentifier.MODIFICATION_DATE },
|
|
729
|
-
{ id: baEnum.PropertyIdentifier.PROGRAM_STATE },
|
|
730
|
-
{ id: baEnum.PropertyIdentifier.RECORD_COUNT }
|
|
731
|
-
|
|
685
|
+
{ id: baEnum.PropertyIdentifier.ALL }
|
|
732
686
|
]);
|
|
687
|
+
|
|
733
688
|
};
|
|
734
689
|
|
|
735
690
|
_readObjectPropList(deviceAddress, type, instance) {
|
|
@@ -761,7 +716,7 @@ class BacnetClient extends EventEmitter {
|
|
|
761
716
|
options.pointsToWrite.forEach(function(point){
|
|
762
717
|
|
|
763
718
|
let deviceAddress = point.deviceAddress;
|
|
764
|
-
|
|
719
|
+
|
|
765
720
|
if(valuesArray[deviceAddress] == null || valuesArray[deviceAddress] == undefined){
|
|
766
721
|
valuesArray[deviceAddress] = [];
|
|
767
722
|
}
|
|
@@ -807,11 +762,11 @@ class BacnetClient extends EventEmitter {
|
|
|
807
762
|
Promise.all(writePromises).then(function(result) {
|
|
808
763
|
resolve(result);
|
|
809
764
|
}).catch(function(e) {
|
|
810
|
-
|
|
765
|
+
that.logOut("Error writing: ", e);
|
|
811
766
|
});
|
|
812
767
|
});
|
|
813
768
|
} catch (error) {
|
|
814
|
-
|
|
769
|
+
that.logOut(error);
|
|
815
770
|
}
|
|
816
771
|
}
|
|
817
772
|
|
|
@@ -873,16 +828,17 @@ class BacnetClient extends EventEmitter {
|
|
|
873
828
|
}
|
|
874
829
|
|
|
875
830
|
scanDevice(device) {
|
|
831
|
+
let that = this;
|
|
876
832
|
return new Promise((resolve, reject) => {
|
|
877
833
|
this._readObjectList(device.getAddress(), device.getDeviceId(), (err, result) => {
|
|
878
834
|
if (!err) {
|
|
879
835
|
try {
|
|
880
836
|
resolve(result.values[0].values[0].value);
|
|
881
837
|
} catch(e) {
|
|
882
|
-
|
|
838
|
+
that.logOut("Issue with getting device point list, see error: ", e);
|
|
883
839
|
}
|
|
884
840
|
} else {
|
|
885
|
-
|
|
841
|
+
that.logOut(`Error while fetching objects: ${err}`);
|
|
886
842
|
reject(err);
|
|
887
843
|
}
|
|
888
844
|
});
|
|
@@ -893,7 +849,7 @@ class BacnetClient extends EventEmitter {
|
|
|
893
849
|
shutDownClient() {
|
|
894
850
|
let that = this;
|
|
895
851
|
if(that.client) that.client.close((err, result) => {
|
|
896
|
-
|
|
852
|
+
that.logOut(err, result);
|
|
897
853
|
});
|
|
898
854
|
};
|
|
899
855
|
|
|
@@ -912,24 +868,6 @@ class BacnetClient extends EventEmitter {
|
|
|
912
868
|
|
|
913
869
|
if(that.client) {
|
|
914
870
|
that.client.whoIs({'net': 65535});
|
|
915
|
-
|
|
916
|
-
//that.client.whoIs(options);
|
|
917
|
-
//that.client.whoIs({'net':3});
|
|
918
|
-
|
|
919
|
-
// that.client.whoIs({'net':3001});
|
|
920
|
-
|
|
921
|
-
// read a point through a router to an MSTP device
|
|
922
|
-
// that.client.readProperty({address: '10.0.8.212',net:3001,adr:[20]} , {type: 8, instance: 2002}, 77, (err, value) => {
|
|
923
|
-
// console.log('value: ', value);
|
|
924
|
-
// console.log('err: ', err);
|
|
925
|
-
// });
|
|
926
|
-
|
|
927
|
-
// that.client.readProperty({address: '10.0.8.212',net:3001,adr:[20]} , {type: 8, instance: 2002}, 77, (err, value) => {
|
|
928
|
-
// console.log('value: ', value);
|
|
929
|
-
// console.log('err: ', err);
|
|
930
|
-
// });
|
|
931
|
-
|
|
932
|
-
|
|
933
871
|
} else {
|
|
934
872
|
that.reinitializeClient(that.config);
|
|
935
873
|
}
|
|
@@ -962,83 +900,94 @@ class BacnetClient extends EventEmitter {
|
|
|
962
900
|
|
|
963
901
|
buildNetworkTreeData() {
|
|
964
902
|
let that = this;
|
|
903
|
+
that.buildTreeMutex = new Mutex();
|
|
965
904
|
let displayNameCharThreshold = 40;
|
|
966
905
|
return new Promise(async function(resolve, reject) {
|
|
967
906
|
if(!that.renderList) that.renderList = [];
|
|
968
907
|
if(that.deviceList && that.networkTree) {
|
|
969
908
|
that.deviceList.forEach(function(deviceInfo, index) {
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
let
|
|
978
|
-
let
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
let
|
|
984
|
-
let
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
let
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
909
|
+
that.buildTreeMutex
|
|
910
|
+
.acquire()
|
|
911
|
+
.then(function(release) {
|
|
912
|
+
|
|
913
|
+
let ipAddr = typeof deviceInfo.getAddress() == "object" ? deviceInfo.getAddress().address : deviceInfo.getAddress();
|
|
914
|
+
let deviceId = deviceInfo.getDeviceId();
|
|
915
|
+
let deviceName = deviceInfo.getDeviceName() == null ? ipAddr : deviceInfo.getDeviceName();
|
|
916
|
+
let deviceKey = ipAddr + "-" + deviceInfo.getDeviceId();
|
|
917
|
+
let deviceObject = that.networkTree[deviceKey];
|
|
918
|
+
let isMstpDevice = deviceInfo.getIsMstpDevice();
|
|
919
|
+
let manualDiscoveryMode = deviceInfo.getManualDiscoveryMode();
|
|
920
|
+
|
|
921
|
+
if(deviceObject) {
|
|
922
|
+
let children = [];
|
|
923
|
+
let pointIndex = 0;
|
|
924
|
+
|
|
925
|
+
for(const pointName in deviceObject) {
|
|
926
|
+
let pointProperties = [];
|
|
927
|
+
let values = deviceObject[pointName];
|
|
928
|
+
let displayName = pointName;
|
|
929
|
+
if(pointName.length > displayNameCharThreshold) {
|
|
930
|
+
displayName = "";
|
|
931
|
+
let charArray = pointName.split("");
|
|
932
|
+
for(let i = 0; i < charArray.length; i++) {
|
|
933
|
+
if(i < displayNameCharThreshold){
|
|
934
|
+
displayName += charArray[i];
|
|
935
|
+
}
|
|
991
936
|
}
|
|
937
|
+
displayName += "...";
|
|
992
938
|
}
|
|
993
|
-
displayName += "...";
|
|
994
|
-
}
|
|
995
939
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
940
|
+
if(values.objectName){
|
|
941
|
+
pointProperties.push({"key": `${index}-${pointIndex}-0`, "label": `Name: ${values.objectName}`, "data": values.objectName, "icon": "pi pi-bolt", "children": null});
|
|
942
|
+
}
|
|
943
|
+
if(values.objectType){
|
|
944
|
+
pointProperties.push({"key": `${index}-${pointIndex}-1`, "label": `Object Type: ${values.objectType}`, "data": values.objectType, "icon": "pi pi-bolt", "children": null});
|
|
945
|
+
}
|
|
946
|
+
if(values.objectID && values.objectID.instance) {
|
|
947
|
+
pointProperties.push({"key": `${index}-${pointIndex}-2`, "label": `Object Instance: ${values.objectID.instance}`, "data": values.objectID.instance, "icon": "pi pi-bolt", "children": null});
|
|
948
|
+
}
|
|
949
|
+
if(values.description){
|
|
950
|
+
pointProperties.push({"key": `${index}-${pointIndex}-3`, "label": `Description: ${values.description}`, "data": `${values.description}`, "icon": "pi pi-bolt", "children": null});
|
|
951
|
+
}
|
|
952
|
+
if(values.units){
|
|
953
|
+
pointProperties.push({"key": `${index}-${pointIndex}-4`, "label": `Units: ${values.units}`, "data": `${values.units}`, "icon": "pi pi-bolt", "children": null});
|
|
954
|
+
}
|
|
955
|
+
if(values.presentValue !== "undefined" && values.presentValue !== null && typeof values.presentValue !== "undefined") {
|
|
956
|
+
pointProperties.push({"key": `${index}-${pointIndex}-5`, "label": `Present Value: ${values.presentValue}`, "data": `${values.presentValue}`, "icon": "pi pi-bolt", "children": null});
|
|
957
|
+
}
|
|
958
|
+
if(values.systemStatus !== null && typeof values.systemStatus !== "undefined" && values.systemStatus !== ""){
|
|
959
|
+
pointProperties.push({"key": `${index}-${pointIndex}-6`, "label": `System Status: ${values.systemStatus}`, "data": `${values.systemStatus}`, "icon": "pi pi-bolt", "children": null});
|
|
960
|
+
}
|
|
961
|
+
if(values.modificationDate && !values.modificationDate.errorClass) {
|
|
962
|
+
pointProperties.push({"key": `${index}-${pointIndex}-7`, "label": `Modification Date: ${values.modificationDate}`, "data": `${values.modificationDate}`, "icon": "pi pi-bolt", "children": null});
|
|
963
|
+
}
|
|
964
|
+
if(values.programState){
|
|
965
|
+
pointProperties.push({"key": `${index}-${pointIndex}-8`, "label": `Program State: ${values.programState}`, "data": `${values.programState}`, "icon": "pi pi-bolt", "children": null});
|
|
966
|
+
}
|
|
967
|
+
if(values.recordCount && !values.recordCount.errorClass){
|
|
968
|
+
pointProperties.push({"key": `${index}-${pointIndex}-9`, "label": `Record Count: ${values.recordCount}`, "data": `${values.recordCount}`, "icon": "pi pi-bolt", "children": null});
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
children.push({"key": `${index}-${pointIndex}`, "label": displayName, "data": displayName, "pointName": pointName, "icon": that.getPointIcon(values.meta.objectId.type), "children": pointProperties, "type": "point", "parentDevice": deviceName, "showAdded": false, "bacnetType": values.meta.objectId.type})
|
|
972
|
+
pointIndex++;
|
|
1016
973
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
974
|
+
let foundIndex = that.renderList.findIndex(ele => ele.key == index && ele.deviceId == deviceId);
|
|
975
|
+
if(foundIndex !== -1) {
|
|
976
|
+
that.renderList[foundIndex] = {"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice, manualDiscoveryMode), "children": children.sort(that.sortPoints), "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice};
|
|
977
|
+
} else if(foundIndex == -1) {
|
|
978
|
+
that.renderList.push({"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice, manualDiscoveryMode), "children": children.sort(that.sortPoints), "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice});
|
|
1019
979
|
}
|
|
1020
|
-
if(
|
|
1021
|
-
|
|
980
|
+
if(index == that.deviceList.length - 1) {
|
|
981
|
+
resolve({renderList: that.renderList, deviceList: that.deviceList, pointList: that.networkTree, pollFrequency: that.discover_polling_schedule});
|
|
1022
982
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
983
|
+
} else {
|
|
984
|
+
if(index == that.deviceList.length - 1) {
|
|
985
|
+
resolve({renderList: that.renderList, deviceList: that.deviceList, pointList: that.networkTree, pollFrequency: that.discover_polling_schedule});
|
|
1025
986
|
}
|
|
1026
|
-
|
|
1027
|
-
children.push({"key": `${index}-${pointIndex}`, "label": displayName, "data": displayName, "pointName": pointName, "icon": that.getPointIcon(values.meta.objectId.type), "children": pointProperties, "type": "point", "parentDevice": deviceName, "showAdded": false, "bacnetType": values.meta.objectId.type})
|
|
1028
|
-
pointIndex++;
|
|
1029
987
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
} else if(foundIndex == -1) {
|
|
1034
|
-
that.renderList.push({"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice), "children": children.sort(that.sortPoints), "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice});
|
|
1035
|
-
}
|
|
1036
|
-
if(index == that.deviceList.length - 1) {
|
|
1037
|
-
resolve({renderList: that.renderList, deviceList: that.deviceList, pointList: that.networkTree, pollFrequency: that.discover_polling_schedule});
|
|
1038
|
-
}
|
|
1039
|
-
} else {
|
|
1040
|
-
if(index == that.deviceList.length - 1) resolve({renderList: that.renderList, deviceList: that.deviceList, pointList: that.networkTree, pollFrequency: that.discover_polling_schedule});
|
|
1041
|
-
}
|
|
988
|
+
|
|
989
|
+
release();
|
|
990
|
+
});
|
|
1042
991
|
});
|
|
1043
992
|
}
|
|
1044
993
|
});
|
|
@@ -1048,26 +997,49 @@ class BacnetClient extends EventEmitter {
|
|
|
1048
997
|
let that = this;
|
|
1049
998
|
let address = device.address;
|
|
1050
999
|
let pointList = device.getPointsList();
|
|
1000
|
+
let requestMutex = new Mutex();
|
|
1051
1001
|
|
|
1052
1002
|
return new Promise(function(resolve, reject) {
|
|
1053
1003
|
let promiseArray = [];
|
|
1054
1004
|
if(typeof pointList !== "undefined" && pointList.length > 0) {
|
|
1055
1005
|
pointList.forEach(function(point, pointListIndex) {
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1006
|
+
requestMutex
|
|
1007
|
+
.acquire()
|
|
1008
|
+
.then(function(release) {
|
|
1009
|
+
that._readObjectFull(address, point.value.type, point.value.instance).then(function(result) {
|
|
1010
|
+
|
|
1011
|
+
if(!result.error) {
|
|
1012
|
+
promiseArray.push(result);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
release();
|
|
1016
|
+
|
|
1017
|
+
if(pointListIndex == pointList.length - 1) {
|
|
1018
|
+
that.buildResponse(promiseArray, device).then(function() {
|
|
1019
|
+
that.lastNetworkPoll = Date.now();
|
|
1020
|
+
resolve({deviceList: that.deviceList, pointList: that.networkTree});
|
|
1021
|
+
}).catch(function(e){
|
|
1022
|
+
that.logOut("Error while building json object: ", e);
|
|
1023
|
+
reject(e);
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1066
1027
|
}).catch(function(e) {
|
|
1067
|
-
|
|
1068
|
-
|
|
1028
|
+
release();
|
|
1029
|
+
that.logOut("_readObjectFull error: ", e);
|
|
1030
|
+
|
|
1031
|
+
if(pointListIndex == pointList.length - 1) {
|
|
1032
|
+
that.buildResponse(promiseArray, device).then(function() {
|
|
1033
|
+
that.lastNetworkPoll = Date.now();
|
|
1034
|
+
resolve({deviceList: that.deviceList, pointList: that.networkTree});
|
|
1035
|
+
}).catch(function(e){
|
|
1036
|
+
that.logOut("Error while building json object: ", e);
|
|
1037
|
+
reject(e);
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1069
1041
|
});
|
|
1070
|
-
}
|
|
1042
|
+
});
|
|
1071
1043
|
});
|
|
1072
1044
|
} else {
|
|
1073
1045
|
reject("Unable to build network tree, empty point list");
|
|
@@ -1079,7 +1051,8 @@ class BacnetClient extends EventEmitter {
|
|
|
1079
1051
|
buildResponse(fullObjects, device) {
|
|
1080
1052
|
let that = this;
|
|
1081
1053
|
return new Promise(function(resolve, reject) {
|
|
1082
|
-
let
|
|
1054
|
+
let deviceKey = (typeof device.getAddress() == "object") ? device.getAddress().address + "-" + device.getDeviceId() : device.getAddress() + "-" + device.getDeviceId();
|
|
1055
|
+
let values = that.networkTree[deviceKey] ? that.networkTree[deviceKey] : {};
|
|
1083
1056
|
for(let i = 0; i < fullObjects.length; i++) {
|
|
1084
1057
|
let obj = fullObjects[i];
|
|
1085
1058
|
let successfulResult = !obj.error ? obj.value : null;
|
|
@@ -1091,7 +1064,6 @@ class BacnetClient extends EventEmitter {
|
|
|
1091
1064
|
let objectType = that._findValueById(pointProperty.values, baEnum.PropertyIdentifier.OBJECT_TYPE);
|
|
1092
1065
|
let objectId;
|
|
1093
1066
|
if(objectName !== null) {
|
|
1094
|
-
//objectName = objectName.split(" ").join("_");
|
|
1095
1067
|
objectId = objectName + "_" + bac_obj + '_' + pointProperty.objectId.instance;
|
|
1096
1068
|
} else {
|
|
1097
1069
|
objectId = bac_obj + '_' + pointProperty.objectId.instance;
|
|
@@ -1099,7 +1071,9 @@ class BacnetClient extends EventEmitter {
|
|
|
1099
1071
|
try {
|
|
1100
1072
|
pointProperty.values.forEach(function(object, objectIndex) {
|
|
1101
1073
|
//checks for error code json structure, returned for invalid bacnet requests
|
|
1102
|
-
if(!object.value.value && !object.value[0].value.errorClass && !object.value.errorClass) {
|
|
1074
|
+
//if(!object.value.value && !object.value[0].value.errorClass && !object.value.errorClass) {
|
|
1075
|
+
if(object && object.value && !object.value.errorClass) {
|
|
1076
|
+
|
|
1103
1077
|
|
|
1104
1078
|
if(!values[objectId]) values[objectId] = {};
|
|
1105
1079
|
values[objectId].meta = {
|
|
@@ -1116,6 +1090,9 @@ class BacnetClient extends EventEmitter {
|
|
|
1116
1090
|
} else if(object.value[0].value == 1) {
|
|
1117
1091
|
values[objectId].presentValue = true;
|
|
1118
1092
|
}
|
|
1093
|
+
} else if(objectType == 40) {
|
|
1094
|
+
//character string
|
|
1095
|
+
values[objectId].presentValue = object.value[0].value;
|
|
1119
1096
|
} else {
|
|
1120
1097
|
values[objectId].presentValue = roundDecimalPlaces(object.value[0].value, 2);
|
|
1121
1098
|
}
|
|
@@ -1163,23 +1140,23 @@ class BacnetClient extends EventEmitter {
|
|
|
1163
1140
|
if(object.value[0] ) {
|
|
1164
1141
|
values[objectId].recordCount = object.value[0].value;
|
|
1165
1142
|
}
|
|
1166
|
-
break;
|
|
1143
|
+
break;
|
|
1167
1144
|
}
|
|
1168
1145
|
}
|
|
1169
1146
|
if(pointPropertyIndex == successfulResult.values.length - 1 && objectIndex == pointProperty.values.length - 1 && i == fullObjects.length - 1) {
|
|
1170
|
-
that.networkTree[
|
|
1147
|
+
that.networkTree[deviceKey] = values;
|
|
1171
1148
|
resolve(that.networkTree);
|
|
1172
1149
|
}
|
|
1173
1150
|
});
|
|
1174
1151
|
} catch(e) {
|
|
1175
|
-
|
|
1152
|
+
that.logOut("issue resolving bacnet payload, see error: ", e);
|
|
1176
1153
|
reject(e);
|
|
1177
1154
|
}
|
|
1178
1155
|
});
|
|
1179
1156
|
} else {
|
|
1180
1157
|
//error found in point property
|
|
1181
1158
|
if(i == fullObjects.length - 1) {
|
|
1182
|
-
that.networkTree[
|
|
1159
|
+
that.networkTree[deviceKey] = values;
|
|
1183
1160
|
resolve(that.networkTree);
|
|
1184
1161
|
}
|
|
1185
1162
|
}
|
|
@@ -1316,6 +1293,8 @@ class BacnetClient extends EventEmitter {
|
|
|
1316
1293
|
return "MO";
|
|
1317
1294
|
case 19:
|
|
1318
1295
|
return "MV";
|
|
1296
|
+
case 40:
|
|
1297
|
+
return "CS";
|
|
1319
1298
|
default:
|
|
1320
1299
|
return "";
|
|
1321
1300
|
}
|
|
@@ -1360,14 +1339,19 @@ class BacnetClient extends EventEmitter {
|
|
|
1360
1339
|
return flags.value[0].value;
|
|
1361
1340
|
}
|
|
1362
1341
|
|
|
1363
|
-
getDeviceIcon(isMstp){
|
|
1364
|
-
if(
|
|
1365
|
-
return "pi pi-
|
|
1366
|
-
} else if(
|
|
1367
|
-
|
|
1342
|
+
getDeviceIcon(isMstp, manualDiscoveryMode) {
|
|
1343
|
+
if(manualDiscoveryMode == true) {
|
|
1344
|
+
return "pi pi-exclamation-triangle"
|
|
1345
|
+
} else if(manualDiscoveryMode == false) {
|
|
1346
|
+
if(isMstp == true) {
|
|
1347
|
+
return "pi pi-share-alt"
|
|
1348
|
+
} else if(isMstp == false) {
|
|
1349
|
+
return "pi pi-server"
|
|
1350
|
+
}
|
|
1368
1351
|
}
|
|
1352
|
+
|
|
1369
1353
|
return "pi pi-server";
|
|
1370
|
-
}
|
|
1354
|
+
};
|
|
1371
1355
|
}
|
|
1372
1356
|
|
|
1373
1357
|
module.exports = { BacnetClient };
|