@bitpoolos/edge-bacnet 1.3.1 → 1.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.4.0] - 05-07-2024
4
+
5
+ ### Summary
6
+
7
+ Fixed read list export for sites with large point counts.
8
+
9
+ Removed auto tree reload on read node UI. UI tree must be manually reloaded now.
10
+
11
+ Fixed export to CSV bug.
12
+
13
+ Fixed state array text not found bug
14
+
15
+ Removed unecessary debug.
16
+
17
+ Updated required json saved to node JSON file.
18
+
19
+ Added github pull requests
20
+ - Added create object and delete object to bacnet server
21
+ - Added write property to bacnet server
22
+
23
+ Added Simple With Status read property type.
24
+ - This type sends a msg.payload that consists of {presentValue, timestamp, status}
25
+ - Status is currently online and offline
26
+
27
+ Changed inject node to only 1 selectable type of inject (Discover or Poll)
28
+
29
+
30
+ ## [1.3.2] - 18-06-2024
31
+
32
+ ### Summary
33
+
34
+ Bug fix for API request URL paths. Updated for using node-red host setting.
35
+
3
36
  ## [1.3.1] - 06-06-2024
4
37
 
5
38
  ### Summary
package/bacnet_client.js CHANGED
@@ -322,31 +322,13 @@ class BacnetClient extends EventEmitter {
322
322
 
323
323
  updatePointsForDevice(deviceObject) {
324
324
  let that = this;
325
- return new Promise((resolve, reject) => {
325
+ return new Promise(async (resolve, reject) => {
326
326
  try {
327
327
  let device = that.deviceList.find((ele) => ele.getDeviceId() == deviceObject.deviceId);
328
- that.updateDeviceName(device);
329
-
330
-
331
- //test
332
-
333
- that.getProtocolSupported(device).then(function (result) {
334
- console.log("updatePointsForDevice getProtocolSupported ", result.values[0].originalBitString);
335
- console.log(result.values[0]);
336
- console.log(result);
337
- console.log(result.values[0]);
338
- let decodedValues = decodeBitArray(8, result.values[0].originalBitString.value);
339
- device.setProtocolServicesSupported(decodedValues);
340
- }).catch(function (error) {
341
- that.logOut("getProtocolSupported error: ", error);
342
- });
343
-
344
- //test
345
-
328
+ await that.updateDeviceName(device);
346
329
 
347
330
  if (device.getIsProtocolServicesSet() == false) {
348
331
  that.getProtocolSupported(device).then(function (result) {
349
- console.log("updatePointsForDevice getProtocolSupported ", result.values[0].originalBitString);
350
332
  let decodedValues = decodeBitArray(8, result.values[0].originalBitString.value);
351
333
  device.setProtocolServicesSupported(decodedValues);
352
334
  }).catch(function (error) {
@@ -685,14 +667,17 @@ class BacnetClient extends EventEmitter {
685
667
  // Process points for the current device
686
668
  const pointsToRead = readConfig.pointsToRead[key];
687
669
  const pointNames = Object.keys(pointsToRead);
688
- const totalPoints = pointNames.length;
670
+ let totalPoints = pointNames.length;
689
671
  let requestArray = [];
690
672
  let processedPoints = 0; // Counter for processed points
691
673
 
692
674
  // Process each point for the device in batches
693
675
  for (let i = 0; i < pointNames.length; i++) {
694
676
  const pointName = pointNames[i];
695
- if (pointName === "deviceName") continue;
677
+ if (pointName === "deviceName") {
678
+ totalPoints = totalPoints - 1;
679
+ continue;
680
+ }
696
681
 
697
682
  const pointConfig = pointsToRead[pointName];
698
683
  const objectId = that.getObjectId(pointName, pointConfig, that);
@@ -760,10 +745,12 @@ class BacnetClient extends EventEmitter {
760
745
  if (isNumber(val)) {
761
746
  pointRef.presentValue = roundDecimalPlaces(val, roundDecimal);
762
747
  if (pointRef.meta.objectId.type == 19 || pointRef.meta.objectId.type == 13 || pointRef.meta.objectId.type == 14) {
763
- if (val != 0) {
764
- pointRef.presentValue = pointRef.stateTextArray[val - 1].value;
765
- } else {
766
- pointRef.presentValue = pointRef.stateTextArray[val].value;
748
+ if (pointRef.stateTextArray && typeof pointRef.stateTextArray[0].value !== "object") {
749
+ if (val != 0) {
750
+ pointRef.presentValue = pointRef.stateTextArray[val - 1].value;
751
+ } else {
752
+ pointRef.presentValue = pointRef.stateTextArray[val].value;
753
+ }
767
754
  }
768
755
  }
769
756
  } else {
@@ -772,12 +759,23 @@ class BacnetClient extends EventEmitter {
772
759
  }
773
760
  }
774
761
 
762
+ pointRef.timestamp = Date.now();
763
+ pointRef.status = "online";
764
+
775
765
  // Store the point data in results
776
766
  bacnetResults[deviceName][pointNameRef] = pointRef;
777
767
  }
778
768
  });
779
769
  } catch (err) {
780
770
  that.logOut("Error processing batch:", err);
771
+
772
+ requestArray.forEach(request => {
773
+ let pointRef = request.pointRef;
774
+ pointRef.status = "offline";
775
+
776
+ bacnetResults[deviceName][cacheRef.pointName] = pointRef;
777
+ });
778
+
781
779
  }
782
780
  }
783
781
 
@@ -798,6 +796,9 @@ class BacnetClient extends EventEmitter {
798
796
  bacnetResults[deviceName][pointName] = pointRef;
799
797
  } catch (err) {
800
798
  that.logOut(`Error updating point ${pointName}:`, err);
799
+
800
+ pointRef.status = "offline";
801
+ bacnetResults[deviceName][pointName] = pointRef;
801
802
  }
802
803
  }
803
804
  }
@@ -1501,8 +1502,6 @@ class BacnetClient extends EventEmitter {
1501
1502
 
1502
1503
  treeWorker.cacheData();
1503
1504
 
1504
- //const missingDevices = await that.getDevicesNotRenderedYet();
1505
-
1506
1505
  for (let i = 0; i < that.deviceList.length; i++) {
1507
1506
  let device = that.deviceList[i];
1508
1507
  await treeWorker.processDevice(device, i);
@@ -1736,9 +1735,6 @@ class BacnetClient extends EventEmitter {
1736
1735
  case baEnum.PropertyIdentifier.OBJECT_TYPE:
1737
1736
  if (object.value[0] && object.value[0].value) values[objectId].objectType = object.value[0].value;
1738
1737
  break;
1739
- case baEnum.PropertyIdentifier.OBJECT_IDENTIFIER:
1740
- if (object.value[0] && object.value[0].value) values[objectId].objectID = object.value[0].value;
1741
- break;
1742
1738
  case baEnum.PropertyIdentifier.PROPERTY_LIST:
1743
1739
  if (object.value) values[objectId].propertyList = that.mapPropsToArray(object.value);
1744
1740
  break;
@@ -1752,13 +1748,11 @@ class BacnetClient extends EventEmitter {
1752
1748
  values[objectId].modificationDate = object.value[0].value;
1753
1749
  }
1754
1750
  break;
1755
-
1756
1751
  case baEnum.PropertyIdentifier.PROGRAM_STATE:
1757
1752
  if (object.value[0]) {
1758
1753
  values[objectId].programState = that.getPROP_PROGRAM_STATE(object.value[0].value);
1759
1754
  }
1760
1755
  break;
1761
-
1762
1756
  case baEnum.PropertyIdentifier.RECORD_COUNT:
1763
1757
  if (object.value[0]) {
1764
1758
  values[objectId].recordCount = object.value[0].value;
@@ -75,19 +75,19 @@
75
75
  <script type="text/javascript">
76
76
  class NodeService {
77
77
  getNetworkData() {
78
- return fetch("/bitpool-bacnet-data/getNetworkTree").then((res) => res.json());
78
+ return fetch(RED.settings.httpNodeRoot + "bitpool-bacnet-data/getNetworkTree").then((res) => res.json());
79
79
  }
80
80
  rebuildDataModel() {
81
- return fetch("/bitpool-bacnet-data/rebuildDataModel").then((res) => res.json());
81
+ return fetch(RED.settings.httpNodeRoot + "bitpool-bacnet-data/rebuildDataModel").then((res) => res.json());
82
82
  }
83
83
  clearBacnetServerPoints() {
84
- return fetch("/bitpool-bacnet-data/clearBacnetServerPoints").then((res) => res.json());
84
+ return fetch(RED.settings.httpNodeRoot + "bitpool-bacnet-data/clearBacnetServerPoints").then((res) => res.json());
85
85
  }
86
86
  getBacnetServerPoints() {
87
- return fetch('/bitpool-bacnet-data/getBacnetServerPoints').then(res => res.json());
87
+ return fetch(RED.settings.httpNodeRoot + 'bitpool-bacnet-data/getBacnetServerPoints').then(res => res.json());
88
88
  }
89
89
  purgeDevice(device) {
90
- return fetch('/bitpool-bacnet-data/purgeDevice', {
90
+ return fetch(RED.settings.httpNodeRoot + 'bitpool-bacnet-data/purgeDevice', {
91
91
  method: 'POST',
92
92
  headers: {
93
93
  'Accept': 'application/json',
@@ -97,7 +97,7 @@
97
97
  }).then(res => res.json());
98
98
  }
99
99
  updatePointsForDevice(device) {
100
- return fetch('/bitpool-bacnet-data/updatePointsForDevice', {
100
+ return fetch(RED.settings.httpNodeRoot + 'bitpool-bacnet-data/updatePointsForDevice', {
101
101
  method: 'POST',
102
102
  headers: {
103
103
  'Accept': 'application/json',
@@ -107,7 +107,7 @@
107
107
  }).then(res => res.json());
108
108
  }
109
109
  setDeviceDisplayName(device, displayName) {
110
- return fetch('/bitpool-bacnet-data/setDeviceDisplayName', {
110
+ return fetch(RED.settings.httpNodeRoot + 'bitpool-bacnet-data/setDeviceDisplayName', {
111
111
  method: 'POST',
112
112
  headers: {
113
113
  'Accept': 'application/json',
@@ -117,7 +117,7 @@
117
117
  }).then(res => res.json());
118
118
  }
119
119
  setPointDisplayName(deviceKey, pointName, pointDisplayName) {
120
- return fetch('/bitpool-bacnet-data/setPointDisplayName', {
120
+ return fetch(RED.settings.httpNodeRoot + 'bitpool-bacnet-data/setPointDisplayName', {
121
121
  method: 'POST',
122
122
  headers: {
123
123
  'Accept': 'application/json',
@@ -127,7 +127,7 @@
127
127
  }).then(res => res.json());
128
128
  }
129
129
  importReadList(payload) {
130
- return fetch('/bitpool-bacnet-data/importReadList', {
130
+ return fetch(RED.settings.httpNodeRoot + 'bitpool-bacnet-data/importReadList', {
131
131
  method: 'POST',
132
132
  headers: {
133
133
  'Accept': 'application/json',
@@ -213,7 +213,7 @@
213
213
  function queryAdapters() {
214
214
  let nicSelector = document.getElementById("node-input-local_device_address");
215
215
  $.ajax({
216
- url: "/bitpool-bacnet-data/getNetworkInterfaces",
216
+ url: RED.settings.httpNodeRoot + "bitpool-bacnet-data/getNetworkInterfaces",
217
217
  success: function (data) {
218
218
  let keys = Object.keys(data);
219
219
 
@@ -289,7 +289,7 @@
289
289
  let app = this;
290
290
  $.ajax({
291
291
  type: "POST",
292
- url: '/bitpool-bacnet-data/clearBacnetServerPoint',
292
+ url: RED.settings.httpNodeRoot + 'bitpool-bacnet-data/clearBacnetServerPoint',
293
293
  dataType: 'json',
294
294
  contentType: 'application/json',
295
295
  data: JSON.stringify(json),
@@ -342,7 +342,7 @@
342
342
 
343
343
  $.ajax({
344
344
  type: "POST",
345
- url: "/bitpool-bacnet-data/updateDeviceList",
345
+ url: RED.settings.httpNodeRoot + "bitpool-bacnet-data/updateDeviceList",
346
346
  dataType: "json",
347
347
  contentType: "application/json",
348
348
  data: JSON.stringify(jsonPayload),
@@ -356,7 +356,7 @@
356
356
 
357
357
  $("#file-export").click(function (params) {
358
358
  $.ajax({
359
- url: "/bitpool-bacnet-data/getDeviceList",
359
+ url: RED.settings.httpNodeRoot + "bitpool-bacnet-data/getDeviceList",
360
360
  success: function (deviceList) {
361
361
  let data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(deviceList));
362
362
  let aEle = document.getElementById("exportJSON");
package/bacnet_gateway.js CHANGED
@@ -5,7 +5,6 @@
5
5
  module.exports = function (RED) {
6
6
  const { BacnetClient } = require("./bacnet_client");
7
7
  const { BacnetClientConfig, getIpAddress } = require("./common");
8
- const { exec } = require("child_process");
9
8
  const { BacnetServer } = require("./bacnet_server.js");
10
9
 
11
10
  function BitpoolBacnetGatewayDevice(config) {
@@ -109,21 +108,27 @@ module.exports = function (RED) {
109
108
  }, 3000);
110
109
  }
111
110
  if (outputType.json && !outputType.mqtt) {
112
- if (objectPropertyType.fullObject && objectPropertyType.simplePayload) {
113
- sendSimpleJson(values, readNodeName);
114
- sendJsonAsMqtt(values, readNodeName);
115
- } else if (objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
111
+ //json
112
+ if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
113
+ //simpleWithStatus
114
+ sendSimpleWithStatus(values, readNodeName, true);
115
+ } else if (objectPropertyType.fullObject && !objectPropertyType.simplePayload && !objectPropertyType.simpleWithStatus) {
116
+ //fullObject
116
117
  sendJsonAsMqtt(values, readNodeName);
117
- } else if (!objectPropertyType.fullObject && objectPropertyType.simplePayload) {
118
+ } else if (objectPropertyType.simplePayload && !objectPropertyType.fullObject && !objectPropertyType.simpleWithStatus) {
119
+ //simplePayload
118
120
  sendSimpleJson(values, readNodeName);
119
121
  }
120
122
  } else if (!outputType.json && outputType.mqtt) {
121
- if (objectPropertyType.fullObject && objectPropertyType.simplePayload) {
123
+ //mqtt
124
+ if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
125
+ //simpleWithStatus
126
+ sendSimpleWithStatus(values, readNodeName, false);
127
+ } else if (objectPropertyType.fullObject && !objectPropertyType.simplePayload && !objectPropertyType.simpleWithStatus) {
128
+ //fullObject
122
129
  sendAsMqtt(values, readNodeName);
123
- sendSimpleMqtt(values, readNodeName);
124
- } else if (objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
125
- sendAsMqtt(values, readNodeName);
126
- } else if (!objectPropertyType.fullObject && objectPropertyType.simplePayload) {
130
+ } else if (objectPropertyType.simplePayload && !objectPropertyType.fullObject && !objectPropertyType.simpleWithStatus) {
131
+ //simplePayload
127
132
  sendSimpleMqtt(values, readNodeName);
128
133
  }
129
134
  }
@@ -159,6 +164,23 @@ module.exports = function (RED) {
159
164
  }
160
165
  }
161
166
 
167
+ node.bacnetServer.on("writeProperty", (topic, newValue) => {
168
+ let formattedTopic = topic;
169
+ if (
170
+ node.nodeName !== "gateway" &&
171
+ node.nodeName !== "" &&
172
+ node.nodeName !== "null" &&
173
+ node.nodeName !== "undefined" &&
174
+ typeof node.nodeName == "string"
175
+ ) {
176
+ formattedTopic = `${node.nodeName}/BITPOOL_EDGE_BACNET_GATEWAY/BACNET_SERVER/${topic}`;
177
+ } else {
178
+ formattedTopic = `BITPOOL_EDGE_BACNET_GATEWAY/BACNET_SERVER/${topic}`;
179
+ }
180
+
181
+ node.send({ payload: newValue, topic: formattedTopic });
182
+ });
183
+
162
184
  node.on("input", function (msg) {
163
185
  if (msg.topic && msg.payload !== null) {
164
186
  if (node.bacnetServer) {
@@ -420,21 +442,27 @@ module.exports = function (RED) {
420
442
  }, 3000);
421
443
  }
422
444
  if (outputType.json && !outputType.mqtt) {
423
- if (objectPropertyType.fullObject && objectPropertyType.simplePayload) {
424
- sendSimpleJson(values, readNodeName);
425
- sendJsonAsMqtt(values, readNodeName);
426
- } else if (objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
445
+ //json
446
+ if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
447
+ //simpleWithStatus
448
+ sendSimpleWithStatus(values, readNodeName, true);
449
+ } else if (objectPropertyType.fullObject && !objectPropertyType.simplePayload && !objectPropertyType.simpleWithStatus) {
450
+ //fullObject
427
451
  sendJsonAsMqtt(values, readNodeName);
428
- } else if (!objectPropertyType.fullObject && objectPropertyType.simplePayload) {
452
+ } else if (objectPropertyType.simplePayload && !objectPropertyType.fullObject && !objectPropertyType.simpleWithStatus) {
453
+ //simplePayload
429
454
  sendSimpleJson(values, readNodeName);
430
455
  }
431
456
  } else if (!outputType.json && outputType.mqtt) {
432
- if (objectPropertyType.fullObject && objectPropertyType.simplePayload) {
433
- sendAsMqtt(values, readNodeName);
434
- sendSimpleMqtt(values, readNodeName);
435
- } else if (objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
457
+ //mqtt
458
+ if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
459
+ //simpleWithStatus
460
+ sendSimpleWithStatus(values, readNodeName, false);
461
+ } else if (objectPropertyType.fullObject && !objectPropertyType.simplePayload && !objectPropertyType.simpleWithStatus) {
462
+ //fullObject
436
463
  sendAsMqtt(values, readNodeName);
437
- } else if (!objectPropertyType.fullObject && objectPropertyType.simplePayload) {
464
+ } else if (objectPropertyType.simplePayload && !objectPropertyType.fullObject && !objectPropertyType.simpleWithStatus) {
465
+ //simplePayload
438
466
  sendSimpleMqtt(values, readNodeName);
439
467
  }
440
468
  }
@@ -466,6 +494,97 @@ module.exports = function (RED) {
466
494
  return pointName;
467
495
  }
468
496
 
497
+ sendSimpleWithStatus = function (values, readNodeName, isJson) {
498
+ let devices = Object.keys(values);
499
+ devices.forEach(function (device) {
500
+ if (device !== "_msgid") {
501
+ let points = values[device];
502
+ let structuredObject = {};
503
+ let msgg = {};
504
+ for (let point in points) {
505
+ if (points[point] && "presentValue" in points[point]) {
506
+ let pointName = getPointName(points[point], point);
507
+ if (
508
+ node.nodeName !== "gateway" &&
509
+ node.nodeName !== "" &&
510
+ node.nodeName !== "null" &&
511
+ node.nodeName !== "undefined" &&
512
+ typeof node.nodeName == "string"
513
+ ) {
514
+ if (readNodeName !== '' &&
515
+ readNodeName !== null &&
516
+ readNodeName !== undefined
517
+ ) {
518
+ msgg.topic = `${node.nodeName}/${readNodeName}/${device}/${pointName}`;
519
+ } else {
520
+ msgg.topic = `${node.nodeName}/${device}/${pointName}`;
521
+ }
522
+ } else {
523
+ if (readNodeName !== '' &&
524
+ readNodeName !== null &&
525
+ readNodeName !== undefined
526
+ ) {
527
+ msgg.topic = `BITPOOL_BACNET_GATEWAY/${readNodeName}/${device}/${pointName}`;
528
+ } else {
529
+ msgg.topic = `BITPOOL_BACNET_GATEWAY/${device}/${pointName}`;
530
+ }
531
+ }
532
+ let payload = {
533
+ presentValue: points[point]["presentValue"],
534
+ timestamp: points[point]["timestamp"],
535
+ status: points[point]["status"]
536
+ };
537
+
538
+ if (isJson) {
539
+ //json payload
540
+ structuredObject[point] = payload;
541
+ } else {
542
+ //mqtt payload
543
+ msgg.payload = payload;
544
+ node.send(msgg);
545
+ msgg = {};
546
+ }
547
+ }
548
+ }
549
+
550
+ if (isJson) {
551
+ //json payload
552
+ let topic = "";
553
+ if (
554
+ node.nodeName !== "gateway" &&
555
+ node.nodeName !== "" &&
556
+ node.nodeName !== "null" &&
557
+ node.nodeName !== "undefined" &&
558
+ typeof node.nodeName == "string"
559
+ ) {
560
+ if (readNodeName !== '' &&
561
+ readNodeName !== null &&
562
+ readNodeName !== undefined
563
+ ) {
564
+ topic = `${node.nodeName}/${readNodeName}/${device}`;
565
+ } else {
566
+ topic = `${node.nodeName}/${device}`;
567
+ }
568
+ } else {
569
+ if (readNodeName !== '' &&
570
+ readNodeName !== null &&
571
+ readNodeName !== undefined
572
+ ) {
573
+ topic = `BITPOOL_BACNET_GATEWAY/${readNodeName}/${device}`;
574
+ } else {
575
+ topic = `BITPOOL_BACNET_GATEWAY/${device}`;
576
+ }
577
+ }
578
+
579
+ msgg.topic = topic;
580
+ msgg.payload = structuredObject;
581
+ node.send(msgg);
582
+ msgg = {};
583
+ }
584
+ }
585
+ });
586
+ }
587
+
469
588
  sendSimpleMqtt = function (values, readNodeName) {
470
589
  let devices = Object.keys(values);
471
590
  devices.forEach(function (device) {