@bitpoolos/edge-bacnet 1.5.1 → 1.5.3

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,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.5.3] - 23-01-2025
4
+
5
+ New feature:
6
+ - import / export buttons added to new tab in gateway node, used to manage the complete data model for backup or restore
7
+ - associated API end points for programatic backing up or restoring - /bitpool-bacnet-data/getDataModel; /bitpool-bacnet-data/updateDataModel;
8
+
9
+ Further async / await refactoring
10
+
11
+ Bug fixes:
12
+ - incorrect device name in read list export
13
+ - read command indexing unhandled scenario
14
+ - duplicating points in read list after pressing refresh tree button
15
+ - Multi State Values and other state text based points not being handled correctly in large volume scenarios
16
+
17
+ NOTE:
18
+ New importing and exporting feature handles a .json file instead of the .cfg file used in the back end. This is due to browsers flagging .cfg files as malicious. The contents of the file are unchanged.
19
+
20
+ ## [1.5.2] - 10-01-2025
21
+
22
+ Mismatched network request hot fix
23
+
24
+
3
25
  ## [1.5.1] - 13-11-2024
4
26
 
5
27
  ### Summary
package/bacnet_client.js CHANGED
@@ -365,6 +365,20 @@ class BacnetClient extends EventEmitter {
365
365
  });
366
366
  }
367
367
 
368
+ forceUpdateDevices(deviceArray) {
369
+ let that = this;
370
+ try {
371
+ deviceArray.forEach(async function (deviceId) {
372
+ let device = that.deviceList.find((ele) => ele.getDeviceId() === deviceId);
373
+ if (device) {
374
+ await that.buildJsonObject(device);
375
+ }
376
+ });
377
+ } catch (e) {
378
+ that.logOut("forceUpdateDevices error: ", e);
379
+ }
380
+ }
381
+
368
382
  async updatePointsForDevice(deviceObject) {
369
383
  try {
370
384
  let device = this.deviceList.find((ele) => ele.getDeviceId() === deviceObject.deviceId);
@@ -396,6 +410,7 @@ class BacnetClient extends EventEmitter {
396
410
  await this.getDevicePointListWithoutObjectList(device);
397
411
  await this.buildJsonObject(device);
398
412
  } catch (e) {
413
+ await this.buildJsonObject(device);
399
414
  this.logOut(`Update points list error 4: ${this.getDeviceAddress(device)}`, e);
400
415
  }
401
416
  }
@@ -495,11 +510,11 @@ class BacnetClient extends EventEmitter {
495
510
  });
496
511
  }
497
512
 
498
- //test re write
499
513
  async queryDevices() {
500
514
  let that = this;
501
515
  try {
502
516
  that.pollInProgress = true;
517
+
503
518
  let index = 0;
504
519
  await query(index);
505
520
 
@@ -563,23 +578,35 @@ class BacnetClient extends EventEmitter {
563
578
  }
564
579
  }
565
580
 
566
- updateDeviceName(device) {
567
- let that = this;
568
- return new Promise((resolve, reject) => {
569
- that
570
- ._getDeviceName(device)
571
- .then(function (deviceObject) {
572
- if (typeof deviceObject.name == "string") {
573
- device.setDeviceName(deviceObject.name + " " + device.getDeviceId());
574
- device.setPointsList(deviceObject.devicePointEntry);
575
- }
576
- resolve();
577
- })
578
- .catch(function (e) {
579
- that.logOut("updateDeviceName error: ", e);
580
- resolve();
581
- });
582
- });
581
+ // updateDeviceName(device) {
582
+ // let that = this;
583
+ // return new Promise((resolve, reject) => {
584
+ // that
585
+ // ._getDeviceName(device)
586
+ // .then(function (deviceObject) {
587
+ // if (typeof deviceObject.name == "string") {
588
+ // device.setDeviceName(deviceObject.name + " " + device.getDeviceId());
589
+ // device.setPointsList(deviceObject.devicePointEntry);
590
+ // }
591
+ // resolve();
592
+ // })
593
+ // .catch(function (e) {
594
+ // that.logOut("updateDeviceName error: ", e);
595
+ // resolve();
596
+ // });
597
+ // });
598
+ // }
599
+
600
+ async updateDeviceName(device) {
601
+ try {
602
+ const deviceObject = await this._getDeviceName(device);
603
+ if (typeof deviceObject?.name === "string") {
604
+ device.setDeviceName(deviceObject.name + " " + device.getDeviceId());
605
+ device.setPointsList(deviceObject.devicePointEntry);
606
+ }
607
+ } catch (e) {
608
+ this.logOut("updateDeviceName error: ", e);
609
+ }
583
610
  }
584
611
 
585
612
  reinitializeClient(config) {
@@ -713,15 +740,15 @@ class BacnetClient extends EventEmitter {
713
740
  // Process points for the current device
714
741
  const pointsToRead = readConfig.pointsToRead[key];
715
742
  const pointNames = Object.keys(pointsToRead);
716
- let totalPoints = pointNames.length;
743
+ let totalPoints = pointNames.length - 1;
717
744
  let requestArray = [];
718
745
  let processedPoints = 0; // Counter for processed points
719
746
 
720
747
  // Process each point for the device in batches
721
748
  for (let i = 0; i < pointNames.length; i++) {
749
+
722
750
  const pointName = pointNames[i];
723
751
  if (pointName === "deviceName") {
724
- totalPoints = totalPoints - 1;
725
752
  continue;
726
753
  }
727
754
 
@@ -742,7 +769,7 @@ class BacnetClient extends EventEmitter {
742
769
  }
743
770
 
744
771
  // Process the batch when the request array is full or the last point is reached
745
- if (requestArray.length === maxObjectCount || i === pointNames.length - 1) {
772
+ if (requestArray.length === maxObjectCount) {
746
773
  if (device.getProtocolServiceSupport("ReadPropertyMultiple") == true) {
747
774
  await that.processBatch(device, requestArray, deviceName, bacnetResults, that, roundDecimal);
748
775
  } else {
@@ -752,11 +779,22 @@ class BacnetClient extends EventEmitter {
752
779
  requestArray = [];
753
780
  // Increment the processed points counter
754
781
  processedPoints += maxObjectCount;
782
+ } else if (i === pointNames.length - 1) {
783
+ if (device.getProtocolServiceSupport("ReadPropertyMultiple") == true) {
784
+ await that.processBatch(device, requestArray, deviceName, bacnetResults, that, roundDecimal);
785
+ } else {
786
+ await that.processIndividualPoints(device, requestArray, deviceName, bacnetResults, that, roundDecimal);
787
+ }
788
+
789
+ requestArray = [];
790
+ // Increment the processed points counter
791
+ processedPoints += i;
755
792
  }
756
793
 
757
794
  // Check if all points for the device have been processed
758
795
  if (processedPoints >= totalPoints) {
759
796
  pendingRequests++;
797
+
760
798
  // Emit the `values` event for the current device
761
799
  that.emit(
762
800
  "values",
@@ -851,22 +889,36 @@ class BacnetClient extends EventEmitter {
851
889
  for (const request of requestArray) {
852
890
  const { objectId, pointRef, pointName } = request;
853
891
  try {
892
+
854
893
  const result = await that.updatePoint(device, pointRef);
855
- const val = result.values[0].value;
856
894
 
857
- if (isNumber(val)) {
858
- pointRef.presentValue = roundDecimalPlaces(val, roundDecimal);
859
- } else {
860
- pointRef.presentValue = val;
861
- }
895
+ if (result.objectId.type == objectId.type && result.objectId.instance == objectId.instance) {
896
+ const val = result.values[0].value;
862
897
 
863
- pointRef.meta["device"] = deviceMetaInfo;
864
- pointRef.timestamp = Date.now();
865
- pointRef.status = "online";
866
- pointRef.error = "none";
898
+ if (isNumber(val)) {
899
+ pointRef.presentValue = roundDecimalPlaces(val, roundDecimal);
867
900
 
868
- // Store the point data in results
869
- bacnetResults[deviceName][pointName] = pointRef;
901
+ if (pointRef.meta.objectId.type == 19 || pointRef.meta.objectId.type == 13 || pointRef.meta.objectId.type == 14) {
902
+ if (pointRef.stateTextArray && typeof pointRef.stateTextArray[0].value !== "object") {
903
+ if (val != 0) {
904
+ pointRef.presentValue = pointRef.stateTextArray[val - 1].value;
905
+ } else {
906
+ pointRef.presentValue = pointRef.stateTextArray[val].value;
907
+ }
908
+ }
909
+ }
910
+ } else {
911
+ pointRef.presentValue = val;
912
+ }
913
+
914
+ pointRef.meta["device"] = deviceMetaInfo;
915
+ pointRef.timestamp = Date.now();
916
+ pointRef.status = "online";
917
+ pointRef.error = "none";
918
+
919
+ // Store the point data in results
920
+ bacnetResults[deviceName][pointName] = pointRef;
921
+ }
870
922
  } catch (err) {
871
923
  that.logOut(`Error updating point ${pointName}:`, err);
872
924
 
@@ -879,18 +931,53 @@ class BacnetClient extends EventEmitter {
879
931
  }
880
932
  }
881
933
 
882
- updateManyPoints(device, points) {
934
+ // updateManyPoints(device, points) {
935
+ // let that = this;
936
+ // return new Promise((resolve, reject) => {
937
+ // // let readOptions = {
938
+ // // maxSegments: device.getMaxSe,
939
+ // // maxApdu: that.readPropertyMultipleOptions.maxApdu,
940
+ // // };
941
+
942
+ // that
943
+ // ._readObjectWithRequestArray(device, points, that.readPropertyMultipleOptions)
944
+ // .then(function (results) {
945
+ // resolve(results);
946
+ // })
947
+ // .catch(function (err) {
948
+ // reject(err);
949
+ // });
950
+ // });
951
+ // }
952
+
953
+ async updateManyPoints(device, points) {
954
+ try {
955
+ // let readOptions = {
956
+ // maxSegments: device.getMaxSe,
957
+ // maxApdu: that.readPropertyMultipleOptions.maxApdu,
958
+ // };
959
+
960
+ const results = await this._readObjectWithRequestArray(device, points, this.readPropertyMultipleOptions);
961
+ return results;
962
+ } catch (error) {
963
+ throw error;
964
+ }
965
+ }
966
+
967
+ updatePointWithRetry(device, point, retryCount = 1) {
883
968
  let that = this;
884
- return new Promise((resolve, reject) => {
885
- that
886
- ._readObjectWithRequestArray(device, points, that.readPropertyMultipleOptions)
887
- .then(function (results) {
888
- resolve(results);
889
- })
890
- .catch(function (err) {
891
- reject(err);
892
- });
893
- });
969
+ const tryUpdate = (retriesLeft) => {
970
+ return that.updatePoint(device, point).catch((err) => {
971
+ if (retriesLeft > 0) {
972
+ that.logOut(`Retrying updatePoint... Attempts left: ${retriesLeft}`);
973
+ return tryUpdate(retriesLeft - 1);
974
+ }
975
+ // If no retries are left, reject with the original error
976
+ return Promise.reject(err);
977
+ });
978
+ };
979
+
980
+ return tryUpdate(retryCount);
894
981
  }
895
982
 
896
983
  updatePoint(device, point) {
@@ -1504,6 +1591,22 @@ class BacnetClient extends EventEmitter {
1504
1591
  });
1505
1592
  }
1506
1593
 
1594
+ getDataModel() {
1595
+ let that = this;
1596
+ return new Promise(async function (resolve, reject) {
1597
+ try {
1598
+ resolve({
1599
+ renderList: that.renderList,
1600
+ deviceList: that.deviceList,
1601
+ pointList: that.networkTree,
1602
+ renderListCount: that.renderListCount,
1603
+ });
1604
+ } catch (e) {
1605
+ reject(e);
1606
+ }
1607
+ });
1608
+ }
1609
+
1507
1610
  updatePointsList(json) {
1508
1611
  let that = this;
1509
1612
  json.deviceList.forEach(function (updatedDevice) {
@@ -1539,6 +1642,29 @@ class BacnetClient extends EventEmitter {
1539
1642
  });
1540
1643
  }
1541
1644
 
1645
+ updateDataModel(json) {
1646
+ let that = this;
1647
+ return new Promise(async function (resolve, reject) {
1648
+ try {
1649
+ if (json.body.renderList) {
1650
+ that.renderList = json.body.renderList;
1651
+ }
1652
+ if (json.body.deviceList) {
1653
+ await that.updateDeviceList(json);
1654
+ }
1655
+ if (json.body.pointList) {
1656
+ that.networkTree = json.body.pointList;
1657
+ }
1658
+ if (json.body.renderListCount) {
1659
+ that.renderListCount = json.body.renderListCount;
1660
+ }
1661
+ resolve();
1662
+ } catch (e) {
1663
+ reject(e);
1664
+ }
1665
+ });
1666
+ }
1667
+
1542
1668
  sortDevices(a, b) {
1543
1669
  if (a.deviceId < b.deviceId) {
1544
1670
  return -1;
@@ -1780,32 +1906,36 @@ class BacnetClient extends EventEmitter {
1780
1906
 
1781
1907
  switch (object.id) {
1782
1908
  case baEnum.PropertyIdentifier.PRESENT_VALUE:
1783
- if (object.value[0] && object.value[0].value !== "undefined" && object.value[0].value !== null) {
1784
- //check for binary object type
1785
- if (objectType == 3 || objectType == 4 || objectType == 5) {
1786
- if (object.value[0].value == 0) {
1787
- values[objectId].presentValue = false;
1788
- } else if (object.value[0].value == 1) {
1789
- values[objectId].presentValue = true;
1790
- }
1791
- } else if (objectType == 40) {
1792
- //character string
1793
- values[objectId].presentValue = object.value[0].value;
1794
- } else if (objectType == 13 || objectType == 14 || objectType == 19) {
1795
- //check for MSV MSI MSO - for enum state text
1796
- if (values[objectId].stateTextArray && values[objectId].stateTextArray.length > 0) {
1909
+ try {
1910
+ if (object.value[0] && object.value[0].value !== "undefined" && object.value[0].value !== null) {
1911
+ //check for binary object type
1912
+ if (objectType == 3 || objectType == 4 || objectType == 5) {
1797
1913
  if (object.value[0].value == 0) {
1798
- values[objectId].presentValue = values[objectId].stateTextArray[object.value[0].value].value;
1799
- } else if (object.value[0].value !== 0) {
1800
- values[objectId].presentValue =
1801
- values[objectId].stateTextArray[object.value[0].value - 1].value;
1914
+ values[objectId].presentValue = false;
1915
+ } else if (object.value[0].value == 1) {
1916
+ values[objectId].presentValue = true;
1802
1917
  }
1918
+ } else if (objectType == 40) {
1919
+ //character string
1920
+ values[objectId].presentValue = object.value[0].value;
1921
+ } else if (objectType == 13 || objectType == 14 || objectType == 19) {
1922
+ //check for MSV MSI MSO - for enum state text
1923
+ if (values[objectId].stateTextArray && values[objectId].stateTextArray.length > 0) {
1924
+ if (object.value[0].value == 0) {
1925
+ values[objectId].presentValue = values[objectId].stateTextArray[object.value[0].value].value;
1926
+ } else if (object.value[0].value !== 0) {
1927
+ values[objectId].presentValue =
1928
+ values[objectId].stateTextArray[object.value[0].value - 1].value;
1929
+ }
1930
+ }
1931
+ } else if (objectType !== 8) {
1932
+ values[objectId].presentValue = roundDecimalPlaces(object.value[0].value, 2);
1803
1933
  }
1804
- } else if (objectType !== 8) {
1805
- values[objectId].presentValue = roundDecimalPlaces(object.value[0].value, 2);
1806
1934
  }
1935
+ values[objectId].meta.arrayIndex = object.index;
1936
+ } catch (e) {
1937
+ that.logOut("buildResponse PRESENT_VALUE error: ", e);
1807
1938
  }
1808
- values[objectId].meta.arrayIndex = object.index;
1809
1939
  break;
1810
1940
  case baEnum.PropertyIdentifier.DESCRIPTION:
1811
1941
  if (object.value[0]) values[objectId].description = object.value[0].value;
@@ -1853,20 +1983,24 @@ class BacnetClient extends EventEmitter {
1853
1983
  }
1854
1984
  break;
1855
1985
  case baEnum.PropertyIdentifier.STATE_TEXT:
1856
- if (object.value) {
1857
- values[objectId].stateTextArray = object.value;
1858
- if (
1859
- typeof values[objectId].presentValue == "number" &&
1860
- values[objectId].presentValue !== null &&
1861
- values[objectId].presentValue !== undefined
1862
- ) {
1863
- const tempIndex = values[objectId].presentValue;
1864
- if (tempIndex == 0) {
1865
- values[objectId].presentValue = values[objectId].stateTextArray[tempIndex].value;
1866
- } else if (tempIndex !== 0) {
1867
- values[objectId].presentValue = values[objectId].stateTextArray[tempIndex - 1].value;
1986
+ try {
1987
+ if (object.value) {
1988
+ values[objectId].stateTextArray = object.value;
1989
+ if (
1990
+ typeof values[objectId].presentValue == "number" &&
1991
+ values[objectId].presentValue !== null &&
1992
+ values[objectId].presentValue !== undefined
1993
+ ) {
1994
+ const tempIndex = values[objectId].presentValue;
1995
+ if (tempIndex == 0) {
1996
+ values[objectId].presentValue = values[objectId].stateTextArray[tempIndex].value;
1997
+ } else if (tempIndex !== 0) {
1998
+ values[objectId].presentValue = values[objectId].stateTextArray[tempIndex - 1].value;
1999
+ }
1868
2000
  }
1869
2001
  }
2002
+ } catch (e) {
2003
+ that.logOut("buildResponse STATE_TEXT error: ", e);
1870
2004
  }
1871
2005
  break;
1872
2006
  case baEnum.PropertyIdentifier.VENDOR_NAME:
package/bacnet_device.js CHANGED
@@ -88,7 +88,9 @@ class BacnetDevice {
88
88
  }
89
89
 
90
90
  setDisplayName(displayName) {
91
- this.displayName = displayName;
91
+ if (typeof displayName == "string") {
92
+ this.displayName = displayName;
93
+ }
92
94
  }
93
95
 
94
96
  getDisplayName() {
@@ -203,6 +203,11 @@
203
203
  label: "Server",
204
204
  });
205
205
 
206
+ tabs.addTab({
207
+ id: "read-backup-tab",
208
+ label: "Backup & Restore",
209
+ });
210
+
206
211
  if (node.networkInterfaces && node.networkInterfaces.length > 0) {
207
212
  let nicSelector = document.getElementById("node-input-local_device_address");
208
213
  node.networkInterfaces.forEach(function (option) {
@@ -333,6 +338,8 @@
333
338
  vueapp.use(primevue.confirmationservice);
334
339
  node.vm1 = vueapp.mount("#serverParent");
335
340
 
341
+
342
+ // Import Device List
336
343
  $("#file-upload").on("change", function (event) {
337
344
  const input = event.target.files[0];
338
345
  const reader = new FileReader();
@@ -356,6 +363,7 @@
356
363
  reader.readAsText(input);
357
364
  });
358
365
 
366
+ //Export Device List
359
367
  $("#file-export").click(function (params) {
360
368
  $.ajax({
361
369
  url: RED.settings.httpNodeRoot + "bitpool-bacnet-data/getDeviceList",
@@ -370,6 +378,45 @@
370
378
  });
371
379
  });
372
380
 
381
+ //Import complete Data model
382
+ $("#file-upload-database").on("change", function (event) {
383
+ const input = event.target.files[0];
384
+ const reader = new FileReader();
385
+
386
+ reader.onload = function (e) {
387
+ const text = e.target.result;
388
+
389
+ let jsonPayload = JSON.parse(text);
390
+
391
+ $.ajax({
392
+ type: "POST",
393
+ url: RED.settings.httpNodeRoot + "bitpool-bacnet-data/updateDataModel",
394
+ dataType: "json",
395
+ contentType: "application/json",
396
+ data: JSON.stringify(jsonPayload),
397
+ success: function (result) { },
398
+ timeout: 15000,
399
+ });
400
+ };
401
+
402
+ reader.readAsText(input);
403
+ });
404
+
405
+ // Export complete Data model
406
+ $("#file-export-database").click(function (params) {
407
+ $.ajax({
408
+ url: RED.settings.httpNodeRoot + "bitpool-bacnet-data/getDataModel",
409
+ success: function (deviceList) {
410
+ let data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(deviceList));
411
+ let aEle = document.getElementById("exportJSON");
412
+ aEle.setAttribute("href", "data:" + data);
413
+ aEle.setAttribute("download", "edge-bacnet-datastore.json");
414
+ aEle.click();
415
+ },
416
+ timeout: 10000,
417
+ });
418
+ });
419
+
373
420
  //start device scan range matrix
374
421
 
375
422
  $("#node-input-deviceIdRangeMatrix-container")
@@ -739,6 +786,21 @@
739
786
  .point_name {
740
787
  font-weight: bold;
741
788
  }
789
+ .database-backup {
790
+ border-top: 1px solid grey;
791
+ padding-top: 25px;
792
+ margin-top: 5px !important;
793
+ }
794
+ .database-file-label {
795
+ color: black;
796
+ font-weight: bold;
797
+ font-size: 16px;
798
+ width: auto !important;
799
+
800
+ }
801
+ .database-file-label-div {
802
+ padding-top: 15px;
803
+ }
742
804
  </style>
743
805
 
744
806
  <div class="form-row node-input-read-tabs-row">
@@ -967,6 +1029,26 @@
967
1029
  </div>
968
1030
  </div>
969
1031
  </div>
1032
+ <div id="read-backup-tab" style="display:none">
1033
+ <div class="database-file-label-div">
1034
+ <span class="database-file-label">Database File</span>
1035
+ </div>
1036
+
1037
+ <div class="form-row bp-import-buttons database-backup" id="importDeviceList">
1038
+ <!-- <label> Device List: </label> -->
1039
+ <label for="file-upload-database" class="custom-file-upload">
1040
+ <i class="fa fa-arrow-circle-up" id="fileLabel"></i>
1041
+ <a id="fileLabelText" style="padding-left: 10px;">Import database</a>
1042
+ </label>
1043
+ <input type="file" id="file-upload-database" accept="application/JSON" class="inputStyle" style="width: 258px;" />
1044
+ <label for="file-export-database" class="custom-file-upload" style="margin-left: 5px;">
1045
+ <i class="fa fa-arrow-circle-down" id="fileLabel"></i>
1046
+ <a id="fileLabelText" style="padding-left: 10px;">Export database</a>
1047
+ </label>
1048
+ <input id="file-export-database" class="inputStyle" style="width: 258px; display: none;" />
1049
+ <a id="exportJSON" style="display: none"></a>
1050
+ </div>
1051
+ </div>
970
1052
  </div>
971
1053
  </script>
972
1054
  <script type="text/html" data-help-name="Bacnet-Gateway">
package/bacnet_gateway.js CHANGED
@@ -240,6 +240,8 @@ module.exports = function (RED) {
240
240
  }).catch(function (error) {
241
241
  logOut("Error in applyDisplayNames: ", error);
242
242
  });
243
+ } else if (msg.forceUpdateDevices == true) {
244
+ node.bacnetClient.forceUpdateDevices(msg.deviceIdArray);
243
245
  }
244
246
  });
245
247
 
@@ -369,6 +371,42 @@ module.exports = function (RED) {
369
371
  }
370
372
  });
371
373
 
374
+ //route handler for getting data model
375
+ RED.httpAdmin.get("/bitpool-bacnet-data/getDataModel", function (req, res) {
376
+ if (!node.bacnetClient) {
377
+ logOut("Issue with the bacnetClient while getting data model: ", node.bacnetClient);
378
+ res.send(false);
379
+ } else {
380
+ node.bacnetClient
381
+ .getDataModel()
382
+ .then(function (result) {
383
+ res.send(result);
384
+ })
385
+ .catch(function (error) {
386
+ res.send(error);
387
+ logOut("Error getting data model: ", error);
388
+ });
389
+ }
390
+ });
391
+
392
+ //route handler for updating data model
393
+ RED.httpAdmin.post("/bitpool-bacnet-data/updateDataModel", function (req, res) {
394
+ if (!node.bacnetClient) {
395
+ logOut("Issue with the bacnetClient while getting data model: ", node.bacnetClient);
396
+ res.send(false);
397
+ } else {
398
+ node.bacnetClient
399
+ .updateDataModel(req)
400
+ .then(function (result) {
401
+ res.send(result);
402
+ })
403
+ .catch(function (error) {
404
+ res.send(error);
405
+ logOut("Error getting data model: ", error);
406
+ });
407
+ }
408
+ });
409
+
372
410
  //route handler for purge device
373
411
  RED.httpAdmin.post("/bitpool-bacnet-data/purgeDevice", function (req, res) {
374
412
  if (!node.bacnetClient) {
package/bacnet_read.html CHANGED
@@ -634,14 +634,7 @@
634
634
  let app = this;
635
635
  const slotProps = app.rightClickedDevice;
636
636
  const displayName = app.deviceDisplayNameValue;
637
- let device = app.deviceList.find((ele) => {
638
- if (ele.address.address) {
639
- return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
640
- } else {
641
- return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
642
- }
643
- });
644
-
637
+ let device = app.getDeviceFromDeviceList(slotProps.node.ipAddr, slotProps.node.deviceId);
645
638
  if (device) {
646
639
  app.nodeService.setDeviceDisplayName(device, displayName).then(function (result) {
647
640
  if (result) {
@@ -660,7 +653,7 @@
660
653
  const pointName = slotProps.node.pointName;
661
654
 
662
655
  let device = app.deviceList.find((ele) => {
663
- return ele.deviceName == slotProps.node.parentDevice;
656
+ return ele.displayName == slotProps.node.parentDevice;
664
657
  });
665
658
 
666
659
  if (device) {
@@ -676,6 +669,18 @@
676
669
 
677
670
  app.showPointNameDialog = false;
678
671
  },
672
+ getDeviceFromDeviceList(ip, id) {
673
+ let app = this;
674
+ let device = app.deviceList.find((ele) => {
675
+ if (ele.address.address) {
676
+ return ele.address.address == ip && ele.deviceId == id;
677
+ } else {
678
+ return ele.address == ip && ele.deviceId == id;
679
+ }
680
+ });
681
+
682
+ return device;
683
+ },
679
684
  settingDeviceName(slotProps) {
680
685
  let app = this;
681
686
  if (slotProps.node.settingDisplayName) {
@@ -709,11 +714,9 @@
709
714
  (ele) => ele.ipAddr == key.split("-")[0] && ele.deviceId == key.split("-")[1]
710
715
  );
711
716
  let deviceName;
712
-
713
717
  if (readDevice) {
714
718
  deviceName = readDevice.label;
715
719
  }
716
-
717
720
  exportJson[key] = {};
718
721
  if (deviceName) {
719
722
  exportJson[key]["deviceName"] = deviceName;
@@ -725,9 +728,8 @@
725
728
  let pointName = devicePoints[pointIndex];
726
729
  let pointObject = device[pointName];
727
730
 
728
- //formatting json payload, still in progress
729
-
730
- if (pointObject) {
731
+ //formatting json payload
732
+ if (pointObject && pointName !== "deviceName") {
731
733
  exportJson[key][pointName] = {
732
734
  meta: pointObject.meta,
733
735
  objectName: pointObject.objectName,
@@ -765,6 +767,7 @@
765
767
  let ip = key.split("-")[0];
766
768
  let id = key.split("-")[1];
767
769
  const importedDevice = pointsToRead[key];
770
+ //match only by IP, to handle case of mstp device
768
771
  let foundIndex = app.devices.findIndex((ele) => ele.ipAddr == ip);
769
772
 
770
773
  if (foundIndex !== -1) {
@@ -772,17 +775,11 @@
772
775
  if (app.devices[foundIndex].deviceId == id) {
773
776
  //found device
774
777
  let treeDevice = app.devices[foundIndex];
775
- let device = app.deviceList.find((ele) => {
776
- if (ele.address.address) {
777
- return ele.address.address == ip && ele.deviceId == id;
778
- } else {
779
- return ele.address == ip && ele.deviceId == id;
780
- }
781
- });
778
+ let device = app.getDeviceFromDeviceList(ip, id);
782
779
 
783
780
  for (let pointName in importedDevice) {
784
781
  let point = importedDevice[pointName];
785
- if (pointName == "deviceName") {
782
+ if (pointName == "deviceName" && typeof point == "string") {
786
783
  app.nodeService.setDeviceDisplayName(device, point);
787
784
  treeDevice.label = point;
788
785
  } else {
@@ -849,7 +846,7 @@
849
846
 
850
847
  for (let pointName in importedDevice) {
851
848
  let point = importedDevice[pointName];
852
- if (pointName == "deviceName") {
849
+ if (pointName == "deviceName" && typeof point == "string") {
853
850
  app.nodeService.setDeviceDisplayName(device, point);
854
851
  mstpDevice.label = point;
855
852
  } else {
@@ -878,7 +875,7 @@
878
875
  } else {
879
876
  // read device found, add point to existing
880
877
  let pointIndex = app.readDevices[isDeviceInReadList].children[0].children.findIndex(
881
- (ele) => ele.data == point.objectName
878
+ (ele) => parseInt(ele.bacnetInstance) == parseInt(point.meta.objectId.instance) && parseInt(ele.bacnetType) == parseInt(point.meta.objectId.type)
882
879
  );
883
880
  if (pointIndex == -1) {
884
881
  app.readDevices[isDeviceInReadList].children[0].children.push(pointInTree);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitpoolos/edge-bacnet",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "A bacnet gateway for node-red",
5
5
  "dependencies": {
6
6
  "@plus4nodered/ts-node-bacnet": "^1.0.0-beta.2",
@@ -237,9 +237,13 @@ class Client extends events_1.EventEmitter {
237
237
  if (!result) return debug('Received invalid deleteObject message');
238
238
  this.emit('deleteObject', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
239
239
  } else if (service === baEnum.ConfirmedServiceChoice.ACKNOWLEDGE_ALARM) {
240
- result = baServices.alarmAcknowledge.decode(buffer, offset, length);
241
- if (!result) return debug('Received invalid alarmAcknowledge message');
242
- this.emit('alarmAcknowledge', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
240
+ try {
241
+ result = baServices.alarmAcknowledge.decode(buffer, offset, length);
242
+ if (!result) return debug('Received invalid alarmAcknowledge message');
243
+ this.emit('alarmAcknowledge', { address: address, invokeId: invokeId, request: result, srcAddress: srcAddress });
244
+ } catch (e) {
245
+ //console.log("Error in alarmAcknowledge: ", e);
246
+ }
243
247
  } else if (service === baEnum.ConfirmedServiceChoice.GET_ALARM_SUMMARY) {
244
248
  this.emit('getAlarmSummary', { address: address, invokeId: invokeId });
245
249
  } else if (service === baEnum.ConfirmedServiceChoice.GET_ENROLLMENT_SUMMARY) {