@bitpoolos/edge-bacnet 1.0.9 → 1.1.1
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 +309 -314
- 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,42 @@ 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 {
|
|
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
|
+
let deviceKey = (typeof device.getAddress() == "object") ? device.getAddress().address + "-" + device.getDeviceId() : device.getAddress() + "-" + device.getDeviceId();
|
|
327
|
+
let deviceObject = that.networkTree[deviceKey];
|
|
328
|
+
if(!bacnetResults[deviceName]) bacnetResults[deviceName] = {};
|
|
329
|
+
if(deviceObject) {
|
|
330
|
+
for(const pointName in readConfig.pointsToRead[key]) {
|
|
331
|
+
let bac_obj = that.getObjectType(readConfig.pointsToRead[key][pointName].objectID.type);
|
|
332
|
+
let objectId = pointName + "_" + bac_obj + '_' + readConfig.pointsToRead[key][pointName].objectID.instance;
|
|
333
|
+
let point = deviceObject[objectId];
|
|
334
|
+
bacnetResults[deviceName][pointName] = point;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
328
337
|
|
|
329
|
-
|
|
330
|
-
// objectId: { type: baEnum.ObjectType.OBJECT_TRENDLOG, instance: 300002 },
|
|
331
|
-
// properties: [{ id: baEnum.PropertyIdentifier.PRESENT_VALUE },
|
|
332
|
-
// { id: baEnum.PropertyIdentifier.LOG_BUFFER }]
|
|
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);
|
|
338
|
+
if(index == devicesToRead.length - 1) that.emit('values', bacnetResults, outputType, objectPropertyType);
|
|
359
339
|
});
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
console.log("Issue doing read, see error: ", e);
|
|
340
|
+
} catch(e) {
|
|
341
|
+
that.logOut("Issue doing read, see error: ", e);
|
|
363
342
|
}
|
|
343
|
+
}
|
|
364
344
|
|
|
345
|
+
getDeviceAddress(device) {
|
|
346
|
+
switch(typeof device.getAddress()) {
|
|
347
|
+
case "object":
|
|
348
|
+
return device.getAddress().address;
|
|
349
|
+
case "string":
|
|
350
|
+
return device.getAddress();
|
|
351
|
+
default:
|
|
352
|
+
return device.getAddress();
|
|
353
|
+
}
|
|
365
354
|
}
|
|
366
355
|
|
|
367
356
|
readDeviceAndEmitJSON(readPromiseArray, device, outputType, propertiesToRead, objectPropertyType, msgId) {
|
|
@@ -390,15 +379,6 @@ class BacnetClient extends EventEmitter {
|
|
|
390
379
|
let bac_obj = that.getObjectType(currobjectId);
|
|
391
380
|
let objectId;
|
|
392
381
|
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
382
|
|
|
403
383
|
//init json object
|
|
404
384
|
if(!values[objectId]) values[objectId] = {};
|
|
@@ -408,7 +388,14 @@ class BacnetClient extends EventEmitter {
|
|
|
408
388
|
if(object.value[0] &&
|
|
409
389
|
object.value[0].value !== "undefined" &&
|
|
410
390
|
object.value[0].value !== null &&
|
|
411
|
-
typeof object.value[0].value == "number")
|
|
391
|
+
typeof object.value[0].value == "number") {
|
|
392
|
+
values[objectId].presentValue = roundDecimalPlaces(object.value[0].value, that.roundDecimal);
|
|
393
|
+
} else if(object.value[0] &&
|
|
394
|
+
object.value[0].value !== "undefined" &&
|
|
395
|
+
object.value[0].value !== null &&
|
|
396
|
+
typeof object.value[0].value == "string") {
|
|
397
|
+
values[objectId].presentValue = object.value[0].value;
|
|
398
|
+
}
|
|
412
399
|
break;
|
|
413
400
|
case baEnum.PropertyIdentifier.DESCRIPTION:
|
|
414
401
|
if(object.value[0]) values[objectId].description = object.value[0].value;
|
|
@@ -455,7 +442,7 @@ class BacnetClient extends EventEmitter {
|
|
|
455
442
|
bacnetResults[deviceName] = values;
|
|
456
443
|
|
|
457
444
|
} catch(e) {
|
|
458
|
-
|
|
445
|
+
that.logOut("issue resolving bacnet payload, see error: ", e);
|
|
459
446
|
}
|
|
460
447
|
});
|
|
461
448
|
if(Object.keys(bacnetResults[deviceName]).length !== 0) {
|
|
@@ -467,7 +454,7 @@ class BacnetClient extends EventEmitter {
|
|
|
467
454
|
});
|
|
468
455
|
|
|
469
456
|
} catch(e){
|
|
470
|
-
|
|
457
|
+
that.logOut("Issue reading from device, see error: ", e);
|
|
471
458
|
}
|
|
472
459
|
}
|
|
473
460
|
|
|
@@ -481,10 +468,10 @@ class BacnetClient extends EventEmitter {
|
|
|
481
468
|
if(object.value[0]) {
|
|
482
469
|
resolve(object.value[0].value);
|
|
483
470
|
} else {
|
|
484
|
-
|
|
471
|
+
that.logOut("Issue with deviceName payload, see object: ", object);
|
|
485
472
|
}
|
|
486
473
|
} catch(e){
|
|
487
|
-
|
|
474
|
+
that.logOut("Unable to get device name: ", e);
|
|
488
475
|
}
|
|
489
476
|
});
|
|
490
477
|
}
|
|
@@ -493,9 +480,10 @@ class BacnetClient extends EventEmitter {
|
|
|
493
480
|
}
|
|
494
481
|
|
|
495
482
|
getPropertiesForType(props, type) {
|
|
483
|
+
let that = this;
|
|
496
484
|
let newProps = [];
|
|
497
485
|
props.forEach(function(prop) {
|
|
498
|
-
//
|
|
486
|
+
//that.logOut(prop);
|
|
499
487
|
switch(type){
|
|
500
488
|
case 0: //analog-input
|
|
501
489
|
newProps.push(prop);
|
|
@@ -533,11 +521,12 @@ class BacnetClient extends EventEmitter {
|
|
|
533
521
|
let that = this;
|
|
534
522
|
return new Promise(async function(resolve, reject) {
|
|
535
523
|
try {
|
|
536
|
-
|
|
524
|
+
device.setManualDiscoveryMode(false);
|
|
525
|
+
let result = await that.scanDevice(device);
|
|
537
526
|
device.setPointsList(result);
|
|
538
527
|
resolve(result);
|
|
539
528
|
} catch(e) {
|
|
540
|
-
|
|
529
|
+
that.logOut(`Error getting point list for ${device.getAddress()} - ${device.getDeviceId()}: `, e);
|
|
541
530
|
reject(e);
|
|
542
531
|
}
|
|
543
532
|
});
|
|
@@ -547,6 +536,7 @@ class BacnetClient extends EventEmitter {
|
|
|
547
536
|
let that = this;
|
|
548
537
|
return new Promise(function(resolve, reject) {
|
|
549
538
|
try {
|
|
539
|
+
device.setManualDiscoveryMode(true);
|
|
550
540
|
that.scanDeviceManually(device).then(function(result) {
|
|
551
541
|
device.setPointsList(result);
|
|
552
542
|
resolve(result);
|
|
@@ -556,7 +546,7 @@ class BacnetClient extends EventEmitter {
|
|
|
556
546
|
|
|
557
547
|
|
|
558
548
|
} catch(e) {
|
|
559
|
-
|
|
549
|
+
that.logOut("Error getting point list: ", e);
|
|
560
550
|
reject(e);
|
|
561
551
|
}
|
|
562
552
|
|
|
@@ -565,12 +555,9 @@ class BacnetClient extends EventEmitter {
|
|
|
565
555
|
|
|
566
556
|
scanDeviceManually(device) {
|
|
567
557
|
let that = this;
|
|
568
|
-
// console.log("scanning device: ", device.getAddress());
|
|
569
558
|
return new Promise(function(resolve, reject) {
|
|
570
559
|
let objectNameProperty = [{ id: baEnum.PropertyIdentifier.OBJECT_NAME }];
|
|
571
560
|
let address = device.getAddress();
|
|
572
|
-
//let deviceInstance = device.deviceId();
|
|
573
|
-
//let objectTypeList = [0, 1, 2, 3, 4, 5, 8, 13, 14, 19];
|
|
574
561
|
let objectTypeList = [0, 1, 2, 3];
|
|
575
562
|
let instanceRange = {start: 0, end: 100};
|
|
576
563
|
let requestArray = [];
|
|
@@ -579,8 +566,6 @@ class BacnetClient extends EventEmitter {
|
|
|
579
566
|
let requestBuffer = [];
|
|
580
567
|
let sendBuffer = [];
|
|
581
568
|
|
|
582
|
-
//requestBuffer.push(that._readObjectFull(address, 8, deviceInstance));
|
|
583
|
-
|
|
584
569
|
if(that.manual_instance_range_enabled == true) {
|
|
585
570
|
instanceRange.start = that.manual_instance_range_start;
|
|
586
571
|
maxRequestThreshold = that.manual_instance_range_end;
|
|
@@ -616,8 +601,7 @@ class BacnetClient extends EventEmitter {
|
|
|
616
601
|
instanceRange.end = 100;
|
|
617
602
|
};
|
|
618
603
|
|
|
619
|
-
|
|
620
|
-
//console.log("request buffer size: ", requestBuffer.length);
|
|
604
|
+
function send() {
|
|
621
605
|
|
|
622
606
|
for(let index = 0; index < requestBuffer.length; index++) {
|
|
623
607
|
let promise = requestBuffer[index];
|
|
@@ -630,7 +614,6 @@ class BacnetClient extends EventEmitter {
|
|
|
630
614
|
let ele = value.values[x];
|
|
631
615
|
let valueRoot = ele.values[0].value[0];
|
|
632
616
|
if(!valueRoot.value.errorClass && !valueRoot.value.errorCode) {
|
|
633
|
-
// console.log("pushing to send buffer");
|
|
634
617
|
sendBuffer.push({"value": ele.objectId, "type": 12});
|
|
635
618
|
}
|
|
636
619
|
}
|
|
@@ -638,14 +621,12 @@ class BacnetClient extends EventEmitter {
|
|
|
638
621
|
}
|
|
639
622
|
|
|
640
623
|
if(index == requestBuffer.length - 1) {
|
|
641
|
-
// console.log("resolving send buffer");
|
|
642
624
|
resolve(sendBuffer);
|
|
643
625
|
}
|
|
644
626
|
|
|
645
627
|
}).catch(function(error) {
|
|
646
628
|
reject(error);
|
|
647
629
|
});
|
|
648
|
-
|
|
649
630
|
} catch(e) {
|
|
650
631
|
reject(e)
|
|
651
632
|
}
|
|
@@ -656,7 +637,6 @@ class BacnetClient extends EventEmitter {
|
|
|
656
637
|
|
|
657
638
|
_readObjectWithRequestArray(deviceAddress, requestArray) {
|
|
658
639
|
let that = this;
|
|
659
|
-
|
|
660
640
|
return new Promise((resolve, reject) => {
|
|
661
641
|
this.client.readPropertyMultiple(deviceAddress, requestArray, that.readPropertyMultipleOptions, (error, value) => {
|
|
662
642
|
resolve({
|
|
@@ -706,30 +686,16 @@ class BacnetClient extends EventEmitter {
|
|
|
706
686
|
try {
|
|
707
687
|
that.client.readPropertyMultiple(deviceAddress, requestArray, that.readPropertyMultipleOptions, callback);
|
|
708
688
|
} catch(e) {
|
|
709
|
-
|
|
689
|
+
that.logOut("Error reading object list: ", e);
|
|
710
690
|
}
|
|
711
691
|
}
|
|
712
692
|
|
|
713
693
|
_readObjectFull(deviceAddress, type, instance) {
|
|
714
694
|
|
|
715
695
|
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
|
-
|
|
696
|
+
{ id: baEnum.PropertyIdentifier.ALL }
|
|
732
697
|
]);
|
|
698
|
+
|
|
733
699
|
};
|
|
734
700
|
|
|
735
701
|
_readObjectPropList(deviceAddress, type, instance) {
|
|
@@ -761,7 +727,7 @@ class BacnetClient extends EventEmitter {
|
|
|
761
727
|
options.pointsToWrite.forEach(function(point){
|
|
762
728
|
|
|
763
729
|
let deviceAddress = point.deviceAddress;
|
|
764
|
-
|
|
730
|
+
|
|
765
731
|
if(valuesArray[deviceAddress] == null || valuesArray[deviceAddress] == undefined){
|
|
766
732
|
valuesArray[deviceAddress] = [];
|
|
767
733
|
}
|
|
@@ -807,11 +773,11 @@ class BacnetClient extends EventEmitter {
|
|
|
807
773
|
Promise.all(writePromises).then(function(result) {
|
|
808
774
|
resolve(result);
|
|
809
775
|
}).catch(function(e) {
|
|
810
|
-
|
|
776
|
+
that.logOut("Error writing: ", e);
|
|
811
777
|
});
|
|
812
778
|
});
|
|
813
779
|
} catch (error) {
|
|
814
|
-
|
|
780
|
+
that.logOut(error);
|
|
815
781
|
}
|
|
816
782
|
}
|
|
817
783
|
|
|
@@ -873,16 +839,17 @@ class BacnetClient extends EventEmitter {
|
|
|
873
839
|
}
|
|
874
840
|
|
|
875
841
|
scanDevice(device) {
|
|
842
|
+
let that = this;
|
|
876
843
|
return new Promise((resolve, reject) => {
|
|
877
844
|
this._readObjectList(device.getAddress(), device.getDeviceId(), (err, result) => {
|
|
878
845
|
if (!err) {
|
|
879
846
|
try {
|
|
880
847
|
resolve(result.values[0].values[0].value);
|
|
881
848
|
} catch(e) {
|
|
882
|
-
|
|
849
|
+
that.logOut("Issue with getting device point list, see error: ", e);
|
|
883
850
|
}
|
|
884
851
|
} else {
|
|
885
|
-
|
|
852
|
+
that.logOut(`Error while fetching objects: ${err}`);
|
|
886
853
|
reject(err);
|
|
887
854
|
}
|
|
888
855
|
});
|
|
@@ -893,7 +860,7 @@ class BacnetClient extends EventEmitter {
|
|
|
893
860
|
shutDownClient() {
|
|
894
861
|
let that = this;
|
|
895
862
|
if(that.client) that.client.close((err, result) => {
|
|
896
|
-
|
|
863
|
+
that.logOut(err, result);
|
|
897
864
|
});
|
|
898
865
|
};
|
|
899
866
|
|
|
@@ -912,24 +879,6 @@ class BacnetClient extends EventEmitter {
|
|
|
912
879
|
|
|
913
880
|
if(that.client) {
|
|
914
881
|
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
882
|
} else {
|
|
934
883
|
that.reinitializeClient(that.config);
|
|
935
884
|
}
|
|
@@ -962,83 +911,94 @@ class BacnetClient extends EventEmitter {
|
|
|
962
911
|
|
|
963
912
|
buildNetworkTreeData() {
|
|
964
913
|
let that = this;
|
|
914
|
+
that.buildTreeMutex = new Mutex();
|
|
965
915
|
let displayNameCharThreshold = 40;
|
|
966
916
|
return new Promise(async function(resolve, reject) {
|
|
967
917
|
if(!that.renderList) that.renderList = [];
|
|
968
918
|
if(that.deviceList && that.networkTree) {
|
|
969
919
|
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
|
-
|
|
920
|
+
that.buildTreeMutex
|
|
921
|
+
.acquire()
|
|
922
|
+
.then(function(release) {
|
|
923
|
+
|
|
924
|
+
let ipAddr = typeof deviceInfo.getAddress() == "object" ? deviceInfo.getAddress().address : deviceInfo.getAddress();
|
|
925
|
+
let deviceId = deviceInfo.getDeviceId();
|
|
926
|
+
let deviceName = deviceInfo.getDeviceName() == null ? ipAddr : deviceInfo.getDeviceName();
|
|
927
|
+
let deviceKey = ipAddr + "-" + deviceInfo.getDeviceId();
|
|
928
|
+
let deviceObject = that.networkTree[deviceKey];
|
|
929
|
+
let isMstpDevice = deviceInfo.getIsMstpDevice();
|
|
930
|
+
let manualDiscoveryMode = deviceInfo.getManualDiscoveryMode();
|
|
931
|
+
|
|
932
|
+
if(deviceObject) {
|
|
933
|
+
let children = [];
|
|
934
|
+
let pointIndex = 0;
|
|
935
|
+
|
|
936
|
+
for(const pointName in deviceObject) {
|
|
937
|
+
let pointProperties = [];
|
|
938
|
+
let values = deviceObject[pointName];
|
|
939
|
+
let displayName = pointName;
|
|
940
|
+
if(pointName.length > displayNameCharThreshold) {
|
|
941
|
+
displayName = "";
|
|
942
|
+
let charArray = pointName.split("");
|
|
943
|
+
for(let i = 0; i < charArray.length; i++) {
|
|
944
|
+
if(i < displayNameCharThreshold){
|
|
945
|
+
displayName += charArray[i];
|
|
946
|
+
}
|
|
991
947
|
}
|
|
948
|
+
displayName += "...";
|
|
992
949
|
}
|
|
993
|
-
displayName += "...";
|
|
994
|
-
}
|
|
995
950
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
951
|
+
if(values.objectName){
|
|
952
|
+
pointProperties.push({"key": `${index}-${pointIndex}-0`, "label": `Name: ${values.objectName}`, "data": values.objectName, "icon": "pi pi-bolt", "children": null});
|
|
953
|
+
}
|
|
954
|
+
if(values.objectType){
|
|
955
|
+
pointProperties.push({"key": `${index}-${pointIndex}-1`, "label": `Object Type: ${values.objectType}`, "data": values.objectType, "icon": "pi pi-bolt", "children": null});
|
|
956
|
+
}
|
|
957
|
+
if(values.objectID && values.objectID.instance) {
|
|
958
|
+
pointProperties.push({"key": `${index}-${pointIndex}-2`, "label": `Object Instance: ${values.objectID.instance}`, "data": values.objectID.instance, "icon": "pi pi-bolt", "children": null});
|
|
959
|
+
}
|
|
960
|
+
if(values.description){
|
|
961
|
+
pointProperties.push({"key": `${index}-${pointIndex}-3`, "label": `Description: ${values.description}`, "data": `${values.description}`, "icon": "pi pi-bolt", "children": null});
|
|
962
|
+
}
|
|
963
|
+
if(values.units){
|
|
964
|
+
pointProperties.push({"key": `${index}-${pointIndex}-4`, "label": `Units: ${values.units}`, "data": `${values.units}`, "icon": "pi pi-bolt", "children": null});
|
|
965
|
+
}
|
|
966
|
+
if(values.presentValue !== "undefined" && values.presentValue !== null && typeof values.presentValue !== "undefined") {
|
|
967
|
+
pointProperties.push({"key": `${index}-${pointIndex}-5`, "label": `Present Value: ${values.presentValue}`, "data": `${values.presentValue}`, "icon": "pi pi-bolt", "children": null});
|
|
968
|
+
}
|
|
969
|
+
if(values.systemStatus !== null && typeof values.systemStatus !== "undefined" && values.systemStatus !== ""){
|
|
970
|
+
pointProperties.push({"key": `${index}-${pointIndex}-6`, "label": `System Status: ${values.systemStatus}`, "data": `${values.systemStatus}`, "icon": "pi pi-bolt", "children": null});
|
|
971
|
+
}
|
|
972
|
+
if(values.modificationDate && !values.modificationDate.errorClass) {
|
|
973
|
+
pointProperties.push({"key": `${index}-${pointIndex}-7`, "label": `Modification Date: ${values.modificationDate}`, "data": `${values.modificationDate}`, "icon": "pi pi-bolt", "children": null});
|
|
974
|
+
}
|
|
975
|
+
if(values.programState){
|
|
976
|
+
pointProperties.push({"key": `${index}-${pointIndex}-8`, "label": `Program State: ${values.programState}`, "data": `${values.programState}`, "icon": "pi pi-bolt", "children": null});
|
|
977
|
+
}
|
|
978
|
+
if(values.recordCount && !values.recordCount.errorClass){
|
|
979
|
+
pointProperties.push({"key": `${index}-${pointIndex}-9`, "label": `Record Count: ${values.recordCount}`, "data": `${values.recordCount}`, "icon": "pi pi-bolt", "children": null});
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
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})
|
|
983
|
+
pointIndex++;
|
|
1016
984
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
985
|
+
let foundIndex = that.renderList.findIndex(ele => ele.key == index && ele.deviceId == deviceId);
|
|
986
|
+
if(foundIndex !== -1) {
|
|
987
|
+
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};
|
|
988
|
+
} else if(foundIndex == -1) {
|
|
989
|
+
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
990
|
}
|
|
1020
|
-
if(
|
|
1021
|
-
|
|
991
|
+
if(index == that.deviceList.length - 1) {
|
|
992
|
+
resolve({renderList: that.renderList, deviceList: that.deviceList, pointList: that.networkTree, pollFrequency: that.discover_polling_schedule});
|
|
1022
993
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
994
|
+
} else {
|
|
995
|
+
if(index == that.deviceList.length - 1) {
|
|
996
|
+
resolve({renderList: that.renderList, deviceList: that.deviceList, pointList: that.networkTree, pollFrequency: that.discover_polling_schedule});
|
|
1025
997
|
}
|
|
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
998
|
}
|
|
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
|
-
}
|
|
999
|
+
|
|
1000
|
+
release();
|
|
1001
|
+
});
|
|
1042
1002
|
});
|
|
1043
1003
|
}
|
|
1044
1004
|
});
|
|
@@ -1048,26 +1008,49 @@ class BacnetClient extends EventEmitter {
|
|
|
1048
1008
|
let that = this;
|
|
1049
1009
|
let address = device.address;
|
|
1050
1010
|
let pointList = device.getPointsList();
|
|
1011
|
+
let requestMutex = new Mutex();
|
|
1051
1012
|
|
|
1052
1013
|
return new Promise(function(resolve, reject) {
|
|
1053
1014
|
let promiseArray = [];
|
|
1054
1015
|
if(typeof pointList !== "undefined" && pointList.length > 0) {
|
|
1055
1016
|
pointList.forEach(function(point, pointListIndex) {
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1017
|
+
requestMutex
|
|
1018
|
+
.acquire()
|
|
1019
|
+
.then(function(release) {
|
|
1020
|
+
that._readObjectFull(address, point.value.type, point.value.instance).then(function(result) {
|
|
1021
|
+
|
|
1022
|
+
if(!result.error) {
|
|
1023
|
+
promiseArray.push(result);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
release();
|
|
1027
|
+
|
|
1028
|
+
if(pointListIndex == pointList.length - 1) {
|
|
1029
|
+
that.buildResponse(promiseArray, device).then(function() {
|
|
1030
|
+
that.lastNetworkPoll = Date.now();
|
|
1031
|
+
resolve({deviceList: that.deviceList, pointList: that.networkTree});
|
|
1032
|
+
}).catch(function(e){
|
|
1033
|
+
that.logOut("Error while building json object: ", e);
|
|
1034
|
+
reject(e);
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1066
1038
|
}).catch(function(e) {
|
|
1067
|
-
|
|
1068
|
-
|
|
1039
|
+
release();
|
|
1040
|
+
that.logOut("_readObjectFull error: ", e);
|
|
1041
|
+
|
|
1042
|
+
if(pointListIndex == pointList.length - 1) {
|
|
1043
|
+
that.buildResponse(promiseArray, device).then(function() {
|
|
1044
|
+
that.lastNetworkPoll = Date.now();
|
|
1045
|
+
resolve({deviceList: that.deviceList, pointList: that.networkTree});
|
|
1046
|
+
}).catch(function(e){
|
|
1047
|
+
that.logOut("Error while building json object: ", e);
|
|
1048
|
+
reject(e);
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1069
1052
|
});
|
|
1070
|
-
}
|
|
1053
|
+
});
|
|
1071
1054
|
});
|
|
1072
1055
|
} else {
|
|
1073
1056
|
reject("Unable to build network tree, empty point list");
|
|
@@ -1079,7 +1062,8 @@ class BacnetClient extends EventEmitter {
|
|
|
1079
1062
|
buildResponse(fullObjects, device) {
|
|
1080
1063
|
let that = this;
|
|
1081
1064
|
return new Promise(function(resolve, reject) {
|
|
1082
|
-
let
|
|
1065
|
+
let deviceKey = (typeof device.getAddress() == "object") ? device.getAddress().address + "-" + device.getDeviceId() : device.getAddress() + "-" + device.getDeviceId();
|
|
1066
|
+
let values = that.networkTree[deviceKey] ? that.networkTree[deviceKey] : {};
|
|
1083
1067
|
for(let i = 0; i < fullObjects.length; i++) {
|
|
1084
1068
|
let obj = fullObjects[i];
|
|
1085
1069
|
let successfulResult = !obj.error ? obj.value : null;
|
|
@@ -1091,7 +1075,6 @@ class BacnetClient extends EventEmitter {
|
|
|
1091
1075
|
let objectType = that._findValueById(pointProperty.values, baEnum.PropertyIdentifier.OBJECT_TYPE);
|
|
1092
1076
|
let objectId;
|
|
1093
1077
|
if(objectName !== null) {
|
|
1094
|
-
//objectName = objectName.split(" ").join("_");
|
|
1095
1078
|
objectId = objectName + "_" + bac_obj + '_' + pointProperty.objectId.instance;
|
|
1096
1079
|
} else {
|
|
1097
1080
|
objectId = bac_obj + '_' + pointProperty.objectId.instance;
|
|
@@ -1099,7 +1082,9 @@ class BacnetClient extends EventEmitter {
|
|
|
1099
1082
|
try {
|
|
1100
1083
|
pointProperty.values.forEach(function(object, objectIndex) {
|
|
1101
1084
|
//checks for error code json structure, returned for invalid bacnet requests
|
|
1102
|
-
if(!object.value.value && !object.value[0].value.errorClass && !object.value.errorClass) {
|
|
1085
|
+
//if(!object.value.value && !object.value[0].value.errorClass && !object.value.errorClass) {
|
|
1086
|
+
if(object && object.value && !object.value.errorClass) {
|
|
1087
|
+
|
|
1103
1088
|
|
|
1104
1089
|
if(!values[objectId]) values[objectId] = {};
|
|
1105
1090
|
values[objectId].meta = {
|
|
@@ -1116,6 +1101,9 @@ class BacnetClient extends EventEmitter {
|
|
|
1116
1101
|
} else if(object.value[0].value == 1) {
|
|
1117
1102
|
values[objectId].presentValue = true;
|
|
1118
1103
|
}
|
|
1104
|
+
} else if(objectType == 40) {
|
|
1105
|
+
//character string
|
|
1106
|
+
values[objectId].presentValue = object.value[0].value;
|
|
1119
1107
|
} else {
|
|
1120
1108
|
values[objectId].presentValue = roundDecimalPlaces(object.value[0].value, 2);
|
|
1121
1109
|
}
|
|
@@ -1163,23 +1151,23 @@ class BacnetClient extends EventEmitter {
|
|
|
1163
1151
|
if(object.value[0] ) {
|
|
1164
1152
|
values[objectId].recordCount = object.value[0].value;
|
|
1165
1153
|
}
|
|
1166
|
-
break;
|
|
1154
|
+
break;
|
|
1167
1155
|
}
|
|
1168
1156
|
}
|
|
1169
1157
|
if(pointPropertyIndex == successfulResult.values.length - 1 && objectIndex == pointProperty.values.length - 1 && i == fullObjects.length - 1) {
|
|
1170
|
-
that.networkTree[
|
|
1158
|
+
that.networkTree[deviceKey] = values;
|
|
1171
1159
|
resolve(that.networkTree);
|
|
1172
1160
|
}
|
|
1173
1161
|
});
|
|
1174
1162
|
} catch(e) {
|
|
1175
|
-
|
|
1163
|
+
that.logOut("issue resolving bacnet payload, see error: ", e);
|
|
1176
1164
|
reject(e);
|
|
1177
1165
|
}
|
|
1178
1166
|
});
|
|
1179
1167
|
} else {
|
|
1180
1168
|
//error found in point property
|
|
1181
1169
|
if(i == fullObjects.length - 1) {
|
|
1182
|
-
that.networkTree[
|
|
1170
|
+
that.networkTree[deviceKey] = values;
|
|
1183
1171
|
resolve(that.networkTree);
|
|
1184
1172
|
}
|
|
1185
1173
|
}
|
|
@@ -1316,6 +1304,8 @@ class BacnetClient extends EventEmitter {
|
|
|
1316
1304
|
return "MO";
|
|
1317
1305
|
case 19:
|
|
1318
1306
|
return "MV";
|
|
1307
|
+
case 40:
|
|
1308
|
+
return "CS";
|
|
1319
1309
|
default:
|
|
1320
1310
|
return "";
|
|
1321
1311
|
}
|
|
@@ -1360,14 +1350,19 @@ class BacnetClient extends EventEmitter {
|
|
|
1360
1350
|
return flags.value[0].value;
|
|
1361
1351
|
}
|
|
1362
1352
|
|
|
1363
|
-
getDeviceIcon(isMstp){
|
|
1364
|
-
if(
|
|
1365
|
-
return "pi pi-
|
|
1366
|
-
} else if(
|
|
1367
|
-
|
|
1353
|
+
getDeviceIcon(isMstp, manualDiscoveryMode) {
|
|
1354
|
+
if(manualDiscoveryMode == true) {
|
|
1355
|
+
return "pi pi-exclamation-triangle"
|
|
1356
|
+
} else if(manualDiscoveryMode == false) {
|
|
1357
|
+
if(isMstp == true) {
|
|
1358
|
+
return "pi pi-share-alt"
|
|
1359
|
+
} else if(isMstp == false) {
|
|
1360
|
+
return "pi pi-server"
|
|
1361
|
+
}
|
|
1368
1362
|
}
|
|
1363
|
+
|
|
1369
1364
|
return "pi pi-server";
|
|
1370
|
-
}
|
|
1365
|
+
};
|
|
1371
1366
|
}
|
|
1372
1367
|
|
|
1373
1368
|
module.exports = { BacnetClient };
|