@bitpoolos/edge-bacnet 1.2.5 → 1.2.6

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_server.js CHANGED
@@ -8,7 +8,13 @@ class BacnetServer {
8
8
  constructor(client, deviceId, nodeRedVersion) {
9
9
  let that = this;
10
10
  that.bacnetClient = client;
11
- that.objectIdNumber = 1;
11
+
12
+ // object identifier init
13
+ that.objectIdNumber = {};
14
+ that.objectIdNumber[baEnum.ObjectType.ANALOG_VALUE] = 0;
15
+ that.objectIdNumber[baEnum.ObjectType.CHARACTERSTRING_VALUE] = 0;
16
+ that.objectIdNumber[baEnum.ObjectType.BINARY_VALUE] = 0;
17
+
12
18
  that.nodeRedVersion = nodeRedVersion;
13
19
  that.deviceId = deviceId;
14
20
  that.vendorId = 1401;
@@ -63,7 +69,8 @@ class BacnetServer {
63
69
  ],
64
70
  },
65
71
  [baEnum.ObjectType.ANALOG_VALUE]: [],
66
- [baEnum.ObjectType.CHARACTERSTRING_VALUE]: []
72
+ [baEnum.ObjectType.CHARACTERSTRING_VALUE]: [],
73
+ [baEnum.ObjectType.BINARY_VALUE]: []
67
74
  };
68
75
 
69
76
  try {
@@ -74,6 +81,7 @@ class BacnetServer {
74
81
  if(cachedData.objectStore) {
75
82
  that.objectStore[baEnum.ObjectType.ANALOG_VALUE] = cachedData.objectStore[baEnum.ObjectType.ANALOG_VALUE];
76
83
  that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE] = cachedData.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE];
84
+ that.objectStore[baEnum.ObjectType.BINARY_VALUE] = cachedData.objectStore[baEnum.ObjectType.BINARY_VALUE];
77
85
  }
78
86
  }
79
87
  } catch (error) {
@@ -121,8 +129,6 @@ class BacnetServer {
121
129
  }
122
130
 
123
131
  } catch(e) {
124
- console.log("Bacnet server readPropertyMultiple error: ", e);
125
-
126
132
  that.bacnetClient.client.errorResponse(
127
133
  data.address,
128
134
  baEnum.ConfirmedServiceChoice.READ_PROPERTY_MULTIPLE,
@@ -160,7 +166,7 @@ class BacnetServer {
160
166
  );
161
167
  }
162
168
  } catch(e) {
163
- console.log("Local BACnet device readProperty error: ", e);
169
+ //console.log("Local BACnet device readProperty error: ", e);
164
170
  }
165
171
 
166
172
  });
@@ -180,55 +186,87 @@ class BacnetServer {
180
186
  let that = this;
181
187
  let objectType = that.getBacnetObjectType(value);
182
188
  if(name && objectType) {
189
+ let instanceNumber;
190
+ if (name.includes('|')) {
191
+ // split name, assign last part to instanceNumber and the rest to name
192
+ let nameParts = name.split('|');
193
+ instanceNumber = nameParts[nameParts.length - 1];
194
+ nameParts.pop();
195
+ name = nameParts.join('|');
196
+ }
183
197
  let formattedName = name.replaceAll('.', '_');
184
198
  formattedName = formattedName.replaceAll('/', '_');
185
199
  if(objectType == "number") {
186
200
  let foundIndex = that.objectStore[baEnum.ObjectType.ANALOG_VALUE].findIndex(ele => ele[baEnum.PropertyIdentifier.OBJECT_NAME][0].value == formattedName);
187
201
  if(foundIndex == -1) {
188
- let objectId = that.getObjectIdentifier();
189
- that.objectStore[baEnum.ObjectType.ANALOG_VALUE].push({
202
+ let objectId = that.getObjectIdentifier(baEnum.ObjectType.ANALOG_VALUE, instanceNumber);
203
+ that.objectStore[baEnum.ObjectType.ANALOG_VALUE].push({
204
+ [baEnum.PropertyIdentifier.OBJECT_NAME]: [{value: formattedName, type: 7}],
205
+ [baEnum.PropertyIdentifier.OBJECT_TYPE]: [{value: baEnum.ObjectType.ANALOG_VALUE, type: 9}],
206
+ [baEnum.PropertyIdentifier.DESCRIPTION]: [{value: '', type: 7}],
207
+ [baEnum.PropertyIdentifier.OBJECT_IDENTIFIER]: [{value: {type: baEnum.ObjectType.ANALOG_VALUE, instance: objectId}, type: 12}],
208
+ [baEnum.PropertyIdentifier.PRESENT_VALUE]: [{value: value, type: 4}],
209
+ [baEnum.PropertyIdentifier.STATUS_FLAGS]: [{value: 0, type: 8}],
210
+ [baEnum.PropertyIdentifier.EVENT_STATE]: [{value: 0, type: 9}],
211
+ [baEnum.PropertyIdentifier.OUT_OF_SERVICE]: [{value: 0, type: 9}],
212
+ [baEnum.PropertyIdentifier.UNITS]: [{value: 95, type: 9}],
213
+ [baEnum.PropertyIdentifier.PRIORITY_ARRAY]: [{value: 0, type: 9}],
214
+ [baEnum.PropertyIdentifier.MAX_PRES_VALUE]: [{value: value, type: 4}],
215
+ [baEnum.PropertyIdentifier.MIN_PRES_VALUE]: [{value: value, type: 4}],
216
+ [baEnum.PropertyIdentifier.RESOLUTION]: [{value: 0, type: 4}],
217
+ [baEnum.PropertyIdentifier.PROPERTY_LIST]:
218
+ [
219
+ {value: baEnum.PropertyIdentifier.OBJECT_NAME, type: 9 },
220
+ {value: baEnum.PropertyIdentifier.OBJECT_TYPE, type: 9 },
221
+ {value: baEnum.PropertyIdentifier.DESCRIPTION, type: 9 },
222
+ {value: baEnum.PropertyIdentifier.OBJECT_IDENTIFIER, type: 9 },
223
+ {value: baEnum.PropertyIdentifier.PRESENT_VALUE, type: 9 },
224
+ {value: baEnum.PropertyIdentifier.STATUS_FLAGS, type: 9 },
225
+ {value: baEnum.PropertyIdentifier.EVENT_STATE, type: 9 },
226
+ {value: baEnum.PropertyIdentifier.OUT_OF_SERVICE, type: 9 },
227
+ {value: baEnum.PropertyIdentifier.UNITS, type: 9 },
228
+ {value: baEnum.PropertyIdentifier.PRIORITY_ARRAY, type: 9 },
229
+ {value: baEnum.PropertyIdentifier.MAX_PRES_VALUE, type: 9 },
230
+ {value: baEnum.PropertyIdentifier.MIN_PRES_VALUE, type: 9 },
231
+ {value: baEnum.PropertyIdentifier.RESOLUTION, type: 9 },
232
+ ],
233
+ });
234
+
235
+ that.objectList.push({value: {type: baEnum.ObjectType.ANALOG_VALUE, instance: objectId}, type: 12})
236
+ that.objectStore[baEnum.ObjectType.DEVICE][baEnum.PropertyIdentifier.OBJECT_LIST] = that.objectList;
237
+ } else if(foundIndex !== -1) {
238
+ let foundObject = that.objectStore[baEnum.ObjectType.ANALOG_VALUE][foundIndex];
239
+ foundObject[baEnum.PropertyIdentifier.PRESENT_VALUE][0].value = value;
240
+ that.objectStore[baEnum.ObjectType.DEVICE][baEnum.PropertyIdentifier.OBJECT_LIST] = that.objectList;
241
+ }
242
+ } else if (objectType == "boolean") {
243
+ let foundIndex = that.objectStore[baEnum.ObjectType.BINARY_VALUE].findIndex(ele => ele[baEnum.PropertyIdentifier.OBJECT_NAME][0].value == formattedName);
244
+ if(foundIndex == -1) {
245
+ let objectId = that.getObjectIdentifier(baEnum.ObjectType.BINARY_VALUE);
246
+ that.objectStore[baEnum.ObjectType.BINARY_VALUE].push({
190
247
  [baEnum.PropertyIdentifier.OBJECT_NAME]: [{value: formattedName, type: 7}],
191
- [baEnum.PropertyIdentifier.OBJECT_TYPE]: [{value: baEnum.ObjectType.ANALOG_VALUE, type: 9}],
248
+ [baEnum.PropertyIdentifier.OBJECT_TYPE]: [{value: baEnum.ObjectType.BINARY_VALUE, type: 9}],
192
249
  [baEnum.PropertyIdentifier.DESCRIPTION]: [{value: '', type: 7}],
193
- [baEnum.PropertyIdentifier.OBJECT_IDENTIFIER]: [{value: {type: baEnum.ObjectType.ANALOG_VALUE, instance: objectId}, type: 12}],
194
- [baEnum.PropertyIdentifier.PRESENT_VALUE]: [{value: value, type: 4}],
250
+ [baEnum.PropertyIdentifier.OBJECT_IDENTIFIER]: [{value: {type: baEnum.ObjectType.BINARY_VALUE, instance: objectId}, type: 12}],
251
+ [baEnum.PropertyIdentifier.PRESENT_VALUE]: [{value: value, type: 1}],
195
252
  [baEnum.PropertyIdentifier.STATUS_FLAGS]: [{value: 0, type: 8}],
196
253
  [baEnum.PropertyIdentifier.EVENT_STATE]: [{value: 0, type: 9}],
197
254
  [baEnum.PropertyIdentifier.OUT_OF_SERVICE]: [{value: 0, type: 9}],
198
- [baEnum.PropertyIdentifier.UNITS]: [{value: 95, type: 9}],
199
- [baEnum.PropertyIdentifier.PRIORITY_ARRAY]: [{value: 0, type: 9}],
200
- [baEnum.PropertyIdentifier.MAX_PRES_VALUE]: [{value: value, type: 4}],
201
- [baEnum.PropertyIdentifier.MIN_PRES_VALUE]: [{value: value, type: 4}],
202
- [baEnum.PropertyIdentifier.RESOLUTION]: [{value: 0, type: 4}],
203
- [baEnum.PropertyIdentifier.PROPERTY_LIST]:
204
- [
205
- {value: baEnum.PropertyIdentifier.OBJECT_NAME, type: 9 },
206
- {value: baEnum.PropertyIdentifier.OBJECT_TYPE, type: 9 },
207
- {value: baEnum.PropertyIdentifier.DESCRIPTION, type: 9 },
208
- {value: baEnum.PropertyIdentifier.OBJECT_IDENTIFIER, type: 9 },
209
- {value: baEnum.PropertyIdentifier.PRESENT_VALUE, type: 9 },
210
- {value: baEnum.PropertyIdentifier.STATUS_FLAGS, type: 9 },
211
- {value: baEnum.PropertyIdentifier.EVENT_STATE, type: 9 },
212
- {value: baEnum.PropertyIdentifier.OUT_OF_SERVICE, type: 9 },
213
- {value: baEnum.PropertyIdentifier.UNITS, type: 9 },
214
- {value: baEnum.PropertyIdentifier.PRIORITY_ARRAY, type: 9 },
215
- {value: baEnum.PropertyIdentifier.MAX_PRES_VALUE, type: 9 },
216
- {value: baEnum.PropertyIdentifier.MIN_PRES_VALUE, type: 9 },
217
- {value: baEnum.PropertyIdentifier.RESOLUTION, type: 9 },
218
- ],
255
+ [baEnum.PropertyIdentifier.ACTIVE_TEXT]: [{value: 'ACTIVE', type: 7}],
256
+ [baEnum.PropertyIdentifier.INACTIVE_TEXT]: [{value: 'INACTIVE', type: 7}],
219
257
  });
220
258
 
221
- that.objectList.push({value: {type: baEnum.ObjectType.ANALOG_VALUE, instance: objectId}, type: 12})
259
+ that.objectList.push({value: {type: baEnum.ObjectType.BINARY_VALUE, instance: objectId}, type: 12})
222
260
  that.objectStore[baEnum.ObjectType.DEVICE][baEnum.PropertyIdentifier.OBJECT_LIST] = that.objectList;
223
261
  } else if(foundIndex !== -1) {
224
- let foundObject = that.objectStore[baEnum.ObjectType.ANALOG_VALUE][foundIndex];
262
+ let foundObject = that.objectStore[baEnum.ObjectType.BINARY_VALUE][foundIndex];
225
263
  foundObject[baEnum.PropertyIdentifier.PRESENT_VALUE][0].value = value;
226
264
  that.objectStore[baEnum.ObjectType.DEVICE][baEnum.PropertyIdentifier.OBJECT_LIST] = that.objectList;
227
265
  }
228
266
  } else if(objectType == "string") {
229
267
  let foundIndex = that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE].findIndex(ele => ele[baEnum.PropertyIdentifier.OBJECT_NAME][0].value == formattedName);
230
268
  if(foundIndex == -1) {
231
- let objectId = that.getObjectIdentifier();
269
+ let objectId = that.getObjectIdentifier(baEnum.ObjectType.CHARACTERSTRING_VALUE);
232
270
  that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE].push({
233
271
  [baEnum.PropertyIdentifier.OBJECT_NAME]: [{value: formattedName, type: 7}],
234
272
  [baEnum.PropertyIdentifier.OBJECT_TYPE]: [{value: baEnum.ObjectType.CHARACTERSTRING_VALUE, type: 9}],
@@ -350,11 +388,119 @@ class BacnetServer {
350
388
  that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE] = [];
351
389
  that.objectStore[baEnum.ObjectType.ANALOG_VALUE] = [];
352
390
 
353
- that.objectIdNumber = 1;
391
+ that.objectIdNumber = {};
392
+ that.objectIdNumber[baEnum.ObjectType.ANALOG_VALUE] = 0;
393
+ that.objectIdNumber[baEnum.ObjectType.CHARACTERSTRING_VALUE] = 0;
394
+ that.objectIdNumber[baEnum.ObjectType.BINARY_VALUE] = 0;
354
395
 
355
396
  Store_Config_Server(JSON.stringify({objectList: that.objectList, objectStore: that.objectStore}));
356
397
  }
357
398
 
399
+ clearServerPoint(json) {
400
+ let that = this;
401
+ return new Promise(async function (resolve, reject) {
402
+ try {
403
+ let type;
404
+ switch (json.body.type) {
405
+ case 'SV':
406
+ type = baEnum.ObjectType.CHARACTERSTRING_VALUE;
407
+ break;
408
+ case 'BV':
409
+ type = baEnum.ObjectType.BINARY_VALUE;
410
+ break;
411
+ default:
412
+ type = baEnum.ObjectType.ANALOG_VALUE;
413
+ break;
414
+ }
415
+
416
+ // remove object from objectStore
417
+ let objectGroup = that.objectStore[type];
418
+ if (Array.isArray(objectGroup)) {
419
+ for (let i = 0; i < objectGroup.length; i++) {
420
+ let object = objectGroup[i];
421
+ if (object[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER][0].value.instance == json.body.instance) {
422
+ that.objectStore[type].splice(i, 1);
423
+ break;
424
+ }
425
+ }
426
+ } else {
427
+ delete that.objectStore[type];
428
+ }
429
+
430
+ // remove object from objectList
431
+ let objectIndex = that.objectList.findIndex(ele =>
432
+ ele.value.instance == json.body.instance && ele.value.type == type);
433
+ if (objectIndex !== -1) {
434
+ that.objectList.splice(objectIndex, 1);
435
+ }
436
+
437
+ // update objectList in device object
438
+ that.objectStore[baEnum.ObjectType.DEVICE][baEnum.PropertyIdentifier.OBJECT_LIST] = that.objectList;
439
+
440
+ Store_Config_Server(JSON.stringify({ objectList: that.objectList, objectStore: that.objectStore }));
441
+
442
+ resolve(true);
443
+ } catch (e) {
444
+ reject(e);
445
+ }
446
+ });
447
+ }
448
+
449
+ getServerPoints() {
450
+ let that = this;
451
+ let points = [];
452
+
453
+ return new Promise(async function (resolve, reject) {
454
+ try {
455
+ // iterate analog value objects
456
+ if(that.objectStore[baEnum.ObjectType.ANALOG_VALUE] && that.objectStore[baEnum.ObjectType.ANALOG_VALUE].length > 0) {
457
+ that.objectStore[baEnum.ObjectType.ANALOG_VALUE].forEach((point) => {
458
+ let instance = point[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER][0].value.instance;
459
+ let objectName = point[baEnum.PropertyIdentifier.OBJECT_NAME][0].value;
460
+
461
+ points.push({
462
+ name: objectName,
463
+ type: "AV",
464
+ instance
465
+ });
466
+ });
467
+ }
468
+
469
+ // iterate character string value objects
470
+ if(that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE] && that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE].length > 0) {
471
+ that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE].forEach((point) => {
472
+ let instance = point[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER][0].value.instance;
473
+ let objectName = point[baEnum.PropertyIdentifier.OBJECT_NAME][0].value;
474
+
475
+ points.push({
476
+ name: objectName,
477
+ type: "SV",
478
+ instance
479
+ });
480
+ });
481
+ }
482
+
483
+ // iterate binary value objects
484
+ if(that.objectStore[baEnum.ObjectType.BINARY_VALUE] && that.objectStore[baEnum.ObjectType.BINARY_VALUE].length > 0) {
485
+ that.objectStore[baEnum.ObjectType.BINARY_VALUE].forEach((point) => {
486
+ let instance = point[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER][0].value.instance;
487
+ let objectName = point[baEnum.PropertyIdentifier.OBJECT_NAME][0].value;
488
+
489
+ points.push({
490
+ name: objectName,
491
+ type: "BV",
492
+ instance
493
+ });
494
+ });
495
+ }
496
+
497
+ resolve(points.sort((a, b) => (a.instance > b.instance) ? 1 : -1));
498
+ } catch (e) {
499
+ reject(e);
500
+ }
501
+ });
502
+ }
503
+
358
504
  getRandomArbitrary(min, max) {
359
505
  return Math.random() * (max - min) + min;
360
506
  }
@@ -367,16 +513,23 @@ class BacnetServer {
367
513
  return "string"
368
514
  case "number":
369
515
  return "number"
516
+ case "boolean":
517
+ return "boolean"
370
518
  default:
371
519
  return null
372
520
  }
373
521
  }
374
522
 
375
- getObjectIdentifier() {
376
- let that = this;
377
- let objectId = that.objectIdNumber;
378
- that.objectIdNumber++;
379
-
523
+ getObjectIdentifier(type, instanceNumber) {
524
+ let that = this;
525
+ // manual instance numbering
526
+ if (instanceNumber) {
527
+ that.objectIdNumber[type] = instanceNumber + 1;
528
+ return instanceNumber;
529
+ }
530
+ // auto instance numbering
531
+ let objectId = that.objectIdNumber[type];
532
+ that.objectIdNumber[type]++;
380
533
  return objectId;
381
534
  }
382
535
 
package/bacnet_write.html CHANGED
@@ -96,7 +96,7 @@
96
96
  });
97
97
  },
98
98
  addAllClicked(slotProps) {
99
-
99
+ let app = this;
100
100
  //update UI
101
101
  if (this.writeDevices) {
102
102
  let foundIndex = this.writeDevices.findIndex(ele => ele.key == slotProps.node.key && ele.label == slotProps.node.label);
@@ -115,7 +115,8 @@
115
115
  return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
116
116
  }
117
117
  });
118
- let key = `${device.address}-${device.deviceId}`;
118
+ let deviceAddress = app.getDeviceAddress(device.address);
119
+ let key = `${deviceAddress}-${device.deviceId}`;
119
120
  let points = this.pointList[key];
120
121
 
121
122
  if (!this.pointsToWrite[key] || typeof this.pointsToWrite[key] == 'undefined') {
@@ -131,6 +132,7 @@
131
132
  node.hiddenDeployToggle = !node.prevHiddenToggleState;
132
133
  },
133
134
  removeAllClicked(slotProps) {
135
+ let app = this;
134
136
  //update UI
135
137
  if (this.writeDevices.length > 0) {
136
138
  let foundIndex = this.writeDevices.findIndex(ele => ele.key == slotProps.node.key && ele.label == slotProps.node.label);
@@ -146,7 +148,8 @@
146
148
  return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
147
149
  }
148
150
  });
149
- let key = `${device.address}-${device.deviceId}`;
151
+ let deviceAddress = app.getDeviceAddress(device.address);
152
+ let key = `${deviceAddress}-${device.deviceId}`;
150
153
  if (this.pointsToWrite[key]) {
151
154
  delete this.pointsToWrite[key];
152
155
  }
@@ -155,7 +158,7 @@
155
158
  node.hiddenDeployToggle = !node.prevHiddenToggleState;
156
159
  },
157
160
  addPointClicked(slotProps) {
158
-
161
+ let app = this;
159
162
  //update UI
160
163
  let parentDeviceName = slotProps.node.parentDevice;
161
164
  let foundDeviceIndex = this.writeDevices ? this.writeDevices.findIndex(ele => ele.label == parentDeviceName) : -1;
@@ -190,7 +193,8 @@
190
193
  return ele.address == parentDevice.ipAddr && ele.deviceId == parentDevice.deviceId;
191
194
  }
192
195
  });
193
- let key = `${device.address}-${device.deviceId}`;
196
+ let deviceAddress = app.getDeviceAddress(device.address);
197
+ let key = `${deviceAddress}-${device.deviceId}`;
194
198
  let point = this.pointList[key][slotProps.node.pointName];
195
199
  point.deviceId = device.deviceId;
196
200
  point.deviceAddress = device.address;
@@ -212,6 +216,7 @@
212
216
  node.hiddenDeployToggle = !node.prevHiddenToggleState;
213
217
  },
214
218
  removePointClicked(slotProps) {
219
+ let app = this;
215
220
  //update UI
216
221
  let parentDeviceName = slotProps.node.parentDevice;
217
222
  let foundDeviceIndex = this.writeDevices ? this.writeDevices.findIndex(ele => ele.label == parentDeviceName) : -1;
@@ -237,7 +242,8 @@
237
242
  return ele.address == parentDevice.ipAddr && ele.deviceId == parentDevice.deviceId;
238
243
  }
239
244
  });
240
- let key = `${device.address}-${device.deviceId}`;
245
+ let deviceAddress = app.getDeviceAddress(device.address);
246
+ let key = `${deviceAddress}-${device.deviceId}`;
241
247
  let point = this.pointList[key][slotProps.node.pointName];
242
248
  point.deviceId = device.deviceId;
243
249
 
@@ -255,6 +261,16 @@
255
261
  //force a deploy state
256
262
  node.hiddenDeployToggle = !node.prevHiddenToggleState;
257
263
  },
264
+ getDeviceAddress(addr) {
265
+ switch(typeof addr) {
266
+ case "object":
267
+ return addr.address;
268
+ case "string":
269
+ return addr;
270
+ default:
271
+ return addr;
272
+ }
273
+ },
258
274
  isDeviceActive(slotProps) {
259
275
  let app = this;
260
276
  if (((Date.now() - slotProps.node.lastSeen) / 1000) < app.pollFrequency) {