@bitpoolos/edge-bacnet 1.4.5 → 1.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.4.6] - 17-09-2024
4
+ ### Summary
5
+
6
+ Minor fixes.
7
+
8
+ Wrapped updateDeviceName function with promise to avoid network conflict for clients.
9
+
10
+ Fixed UI styling bugs, primarily with long names and numbers wrapping.
11
+
12
+ Added ability to set device name of dummy MSTP routers.
13
+
14
+ Improved error handling for querying devices and building point json structures.
15
+
16
+ Fixed read node point export bug.
17
+
18
+ Excluded commas via point name conditioning
19
+
20
+ Fixed gateway port assignment bug - ability to communicate on different ports working now.
21
+
22
+ More async / await refactoring.
23
+
24
+
3
25
  ## [1.4.5] - 16-08-2024
4
26
  ### Summary
5
27
 
package/bacnet_client.js CHANGED
@@ -27,6 +27,7 @@ class BacnetClient extends EventEmitter {
27
27
  that.mutex = new Mutex();
28
28
  that.manualMutex = new Mutex();
29
29
  that.pollInProgress = false;
30
+ that.buildJsonInProgress = false;
30
31
  that.scanMatrix = [];
31
32
  that.renderListCount = 0;
32
33
 
@@ -63,6 +64,7 @@ class BacnetClient extends EventEmitter {
63
64
  };
64
65
 
65
66
  try {
67
+
66
68
  that.client = new bacnet.Client({
67
69
  apduTimeout: config.apduTimeout,
68
70
  interface: config.localIpAdrress,
@@ -81,7 +83,13 @@ class BacnetClient extends EventEmitter {
81
83
 
82
84
  //query device task
83
85
  const queryDevices = new Task("simple task", () => {
84
- if (!that.pollInProgress) that.queryDevices();
86
+ if (!that.pollInProgress) {
87
+ that.queryDevices();
88
+ }
89
+
90
+ if (!that.buildJsonInProgress) {
91
+ that.buildJsonTree();
92
+ }
85
93
  });
86
94
 
87
95
  const queryJob = new SimpleIntervalJob({ seconds: parseInt(that.device_read_schedule) }, queryDevices);
@@ -102,6 +110,7 @@ class BacnetClient extends EventEmitter {
102
110
 
103
111
  setTimeout(() => {
104
112
  that.queryDevices();
113
+ that.buildJsonTree();
105
114
  }, "5000");
106
115
  } catch (e) {
107
116
  that.logOut("Issue initializing client: ", e);
@@ -288,6 +297,7 @@ class BacnetClient extends EventEmitter {
288
297
  that.renderList = [];
289
298
  that.networkTree = {};
290
299
  that.pollInProgress = false;
300
+ that.buildJsonInProgress = false;
291
301
  that.renderListCount = 0;
292
302
  resolve(true);
293
303
  } catch (e) {
@@ -301,9 +311,7 @@ class BacnetClient extends EventEmitter {
301
311
  let that = this;
302
312
  return new Promise((resolve, reject) => {
303
313
  try {
304
- let renderListIndex = that.renderList.findIndex(
305
- (ele) => ele.deviceId == device.deviceId && ele.ipAddr == device.address
306
- );
314
+ let renderListIndex = that.renderList.findIndex((ele) => ele.deviceId == device.deviceId && ele.ipAddr == device.address);
307
315
  let deviceListIndex = that.deviceList.findIndex((ele) => ele.getDeviceId() == device.deviceId);
308
316
  let deviceKey = device.address + "-" + device.deviceId;
309
317
  delete that.networkTree[deviceKey];
@@ -319,62 +327,46 @@ class BacnetClient extends EventEmitter {
319
327
  });
320
328
  }
321
329
 
322
- updatePointsForDevice(deviceObject) {
323
- let that = this;
324
- return new Promise(async (resolve, reject) => {
325
- try {
326
- let device = that.deviceList.find((ele) => ele.getDeviceId() == deviceObject.deviceId);
327
- await that.updateDeviceName(device);
328
-
329
- if (device.getIsProtocolServicesSet() == false) {
330
- that
331
- .getProtocolSupported(device)
332
- .then(function (result) {
333
- let decodedValues = decodeBitArray(8, result.values[0].originalBitString.value);
334
- device.setProtocolServicesSupported(decodedValues);
335
- })
336
- .catch(function (error) {
337
- that.logOut("getProtocolSupported error: ", error);
338
- });
330
+ async updatePointsForDevice(deviceObject) {
331
+ try {
332
+ let device = this.deviceList.find((ele) => ele.getDeviceId() === deviceObject.deviceId);
333
+
334
+ if (!device) {
335
+ throw new Error(`Device with ID ${deviceObject.deviceId} not found`);
336
+ }
337
+
338
+ await this.updateDeviceName(device);
339
+
340
+ if (!device.getIsProtocolServicesSet()) {
341
+ try {
342
+ const result = await this.getProtocolSupported(device);
343
+ const decodedValues = decodeBitArray(8, result.values[0].originalBitString.value);
344
+ device.setProtocolServicesSupported(decodedValues);
345
+ } catch (error) {
346
+ this.logOut("getProtocolSupported error: ", error);
339
347
  }
348
+ }
340
349
 
341
- that
342
- .getDevicePointList(device)
343
- .then(function () {
344
- that
345
- .buildJsonObject(device)
346
- .then(function () {
347
- // do nothing for now
348
- resolve(true);
349
- })
350
- .catch(function (e) {
351
- that.logOut(`Update points list error 1: ${that.getDeviceAddress(device)}`, e);
352
- });
353
- })
354
- .catch(function (e) {
355
- that.logOut(`Update points list error 2: ${that.getDeviceAddress(device)}`, e);
356
- device.setManualDiscoveryMode(true);
357
- that
358
- .getDevicePointListWithoutObjectList(device)
359
- .then(function () {
360
- that
361
- .buildJsonObject(device)
362
- .then(function () {
363
- // do nothing for now
364
- resolve(true);
365
- })
366
- .catch(function (e) {
367
- that.logOut(`Update points list error 3: ${that.getDeviceAddress(device)}`, e);
368
- });
369
- })
370
- .catch(function (e) {
371
- that.logOut(`Update points list error 4: ${that.getDeviceAddress(device)}`, e);
372
- });
373
- });
350
+ try {
351
+ await this.getDevicePointList(device);
352
+ await this.buildJsonObject(device);
374
353
  } catch (e) {
375
- reject(e);
354
+ this.logOut(`Update points list error 2: ${this.getDeviceAddress(device)}`, e);
355
+ device.setManualDiscoveryMode(true);
356
+
357
+ try {
358
+ await this.getDevicePointListWithoutObjectList(device);
359
+ await this.buildJsonObject(device);
360
+ } catch (e) {
361
+ this.logOut(`Update points list error 4: ${this.getDeviceAddress(device)}`, e);
362
+ }
376
363
  }
377
- });
364
+
365
+ return true;
366
+ } catch (e) {
367
+ this.logOut(`Error in updatePointsForDevice: ${e.message}`, e);
368
+ throw e; // Re-throw the error to be handled by the caller
369
+ }
378
370
  }
379
371
 
380
372
  applyDisplayNames(pointsToRead) {
@@ -407,13 +399,21 @@ class BacnetClient extends EventEmitter {
407
399
  let that = this;
408
400
  return new Promise((resolve, reject) => {
409
401
  try {
410
- let device = that.deviceList.find((ele) => ele.getDeviceId() == deviceObject.deviceId);
402
+ let address = "";
403
+ if (typeof deviceObject.address == "string") {
404
+ address = deviceObject.address;
405
+ } else if (typeof deviceObject.address == "object") {
406
+ address = deviceObject.address.address;
407
+ }
408
+
409
+ let device = that.deviceList.find(
410
+ (ele) => that.getDeviceAddress(ele) == address && ele.getDeviceId() == deviceObject.deviceId
411
+ );
411
412
  device.setDisplayName(displayName);
412
413
  that.buildTreeException = true;
413
414
  resolve(true);
414
415
  } catch (e) {
415
416
  that.logOut("setDeviceDisplayName error: ", e);
416
-
417
417
  reject(e);
418
418
  }
419
419
  });
@@ -457,108 +457,89 @@ class BacnetClient extends EventEmitter {
457
457
  });
458
458
  }
459
459
 
460
- queryDevices() {
460
+ //test re write
461
+ async queryDevices() {
461
462
  let that = this;
462
-
463
- that.pollInProgress = true;
464
-
465
- let index = 0;
466
-
467
- query(index);
468
-
469
- function query(index) {
470
- let device = that.deviceList[index];
471
-
472
- if (index < that.deviceList.length) {
473
- index++;
474
-
475
- if (typeof device == "object") {
476
- if (device.getIsProtocolServicesSet() == false) {
477
- that
478
- .getProtocolSupported(device)
479
- .then(function (result) {
463
+ try {
464
+ that.pollInProgress = true;
465
+ let index = 0;
466
+ await query(index);
467
+
468
+ async function query(index) {
469
+ if (index < that.deviceList.length) {
470
+ let device = that.deviceList[index];
471
+ if (typeof device == "object" && (device.getIsDumbMstpRouter() == false || device.getIsDumbMstpRouter() == undefined)) {
472
+ if (device.getIsProtocolServicesSet() == false) {
473
+ try {
474
+ let result = await that.getProtocolSupported(device);
480
475
  let decodedValues = decodeBitArray(8, result.values[0].originalBitString.value);
481
476
  device.setProtocolServicesSupported(decodedValues);
482
- })
483
- .catch(function (error) {
477
+ } catch (error) {
484
478
  that.logOut("getProtocolSupported error: ", error);
485
- });
486
- }
487
- try {
488
- if (device.getSegmentation() !== 3) {
489
- that.updateDeviceName(device);
490
- that
491
- .getDevicePointList(device)
492
- .then(function () {
493
- that
494
- .buildJsonObject(device)
495
- .then(function () {
496
- query(index);
497
- })
498
- .catch(function (e) {
499
- that.logOut(`getDevicePointList error: ${device.getAddress()}`, e);
500
- query(index);
501
- });
502
- })
503
- .catch(function (e) {
479
+ index++;
480
+ await query(index);
481
+ }
482
+ }
483
+ try {
484
+ await that.updateDeviceName(device);
485
+ if (device.getSegmentation() !== 3) {
486
+ try {
487
+ await that.getDevicePointList(device);
488
+
489
+ index++;
490
+ await query(index);
491
+ } catch (e) {
504
492
  that.logOut(`getDevicePointList error: ${device.getAddress()}`, e);
505
- that
506
- .getDevicePointListWithoutObjectList(device)
507
- .then(function () {
508
- that
509
- .buildJsonObject(device)
510
- .then(function () {
511
- query(index);
512
- })
513
- .catch(function (e) {
514
- that.logOut(`getDevicePointList error: ${device.getAddress()}`, e);
515
- query(index);
516
- });
517
- })
518
- .catch(function (e) {
519
- query(index);
520
- });
521
- });
522
- } else if (device.getSegmentation() == 3) {
523
- that.updateDeviceName(device);
524
- that
525
- .getDevicePointListWithoutObjectList(device)
526
- .then(function () {
527
- that
528
- .buildJsonObject(device)
529
- .then(function () {
530
- query(index);
531
- })
532
- .catch(function (e) {
533
- that.logOut(`getDevicePointList error: ${device.getAddress()}`, e);
534
- query(index);
535
- });
536
- })
537
- .catch(function (e) {
538
- query(index);
539
- });
493
+
494
+ index++;
495
+ await query(index);
496
+ }
497
+ } else if (device.getSegmentation() == 3) {
498
+ try {
499
+ await that.getDevicePointListWithoutObjectList(device);
500
+ index++;
501
+ await query(index);
502
+ } catch (e) {
503
+ that.logOut(`getDevicePointList error: ${device.getAddress()}`, e);
504
+
505
+ index++;
506
+ await query(index);
507
+ }
508
+ }
509
+ } catch (e) {
510
+ that.logOut("Error while querying devices: ", e);
511
+
512
+ index++;
513
+ await query(index);
540
514
  }
541
- } catch (e) {
542
- that.logOut("Error while querying devices: ", e);
543
- query(index);
515
+ } else {
516
+ index++;
517
+ await query(index);
544
518
  }
545
- } else {
546
- that.logOut("queryDevices: invalid device found: ", device);
547
- query(index);
519
+ } else if (index == that.deviceList.length) {
520
+ that.pollInProgress = false;
548
521
  }
549
- } else if (index == that.deviceList.length) {
550
- that.pollInProgress = false;
551
522
  }
523
+ } catch (e) {
524
+ that.logOut("Error while querying devices: ", e);
552
525
  }
553
526
  }
554
527
 
555
528
  updateDeviceName(device) {
556
529
  let that = this;
557
- that._getDeviceName(device.getAddress(), device.getDeviceId()).then(function (deviceObject) {
558
- if (typeof deviceObject.name == "string") {
559
- device.setDeviceName(deviceObject.name + " " + device.getDeviceId());
560
- device.setPointsList(deviceObject.devicePointEntry);
561
- }
530
+ return new Promise((resolve, reject) => {
531
+ that
532
+ ._getDeviceName(device.getAddress(), device.getDeviceId())
533
+ .then(function (deviceObject) {
534
+ if (typeof deviceObject.name == "string") {
535
+ device.setDeviceName(deviceObject.name + " " + device.getDeviceId());
536
+ device.setPointsList(deviceObject.devicePointEntry);
537
+ }
538
+ resolve();
539
+ })
540
+ .catch(function (e) {
541
+ reject(e);
542
+ });
562
543
  });
563
544
  }
564
545
 
@@ -598,7 +579,13 @@ class BacnetClient extends EventEmitter {
598
579
 
599
580
  // //query device task
600
581
  const queryDevices = new Task("simple task", () => {
601
- if (!that.pollInProgress) that.queryDevices();
582
+ if (!that.pollInProgress) {
583
+ that.queryDevices();
584
+ }
585
+
586
+ if (!that.buildJsonInProgress) {
587
+ that.buildJsonTree();
588
+ }
602
589
  });
603
590
 
604
591
  const queryJob = new SimpleIntervalJob({ seconds: parseInt(config.device_read_schedule) }, queryDevices);
@@ -780,11 +767,7 @@ class BacnetClient extends EventEmitter {
780
767
 
781
768
  if (isNumber(val)) {
782
769
  pointRef.presentValue = roundDecimalPlaces(val, roundDecimal);
783
- if (
784
- pointRef.meta.objectId.type == 19 ||
785
- pointRef.meta.objectId.type == 13 ||
786
- pointRef.meta.objectId.type == 14
787
- ) {
770
+ if (pointRef.meta.objectId.type == 19 || pointRef.meta.objectId.type == 13 || pointRef.meta.objectId.type == 14) {
788
771
  if (pointRef.stateTextArray && typeof pointRef.stateTextArray[0].value !== "object") {
789
772
  if (val != 0) {
790
773
  pointRef.presentValue = pointRef.stateTextArray[val - 1].value;
@@ -941,8 +924,12 @@ class BacnetClient extends EventEmitter {
941
924
  }
942
925
  } catch (e) {
943
926
  that.logOut("Unable to get device name: ", e);
927
+ reject(e);
944
928
  }
945
929
  }
930
+ if (err) {
931
+ reject(err);
932
+ }
946
933
  });
947
934
  });
948
935
  }
@@ -995,6 +982,7 @@ class BacnetClient extends EventEmitter {
995
982
  device.setManualDiscoveryMode(false);
996
983
  let result = await that.scanDevice(device);
997
984
  device.setPointsList(result);
985
+ device.setLastSeen(Date.now());
998
986
  resolve(result);
999
987
  } catch (e) {
1000
988
  that.logOut(`Error getting point list for ${device.getAddress().toString()} - ${device.getDeviceId()}: `, e);
@@ -1011,6 +999,7 @@ class BacnetClient extends EventEmitter {
1011
999
  .scanDeviceManually(device)
1012
1000
  .then(function (result) {
1013
1001
  device.setPointsList(result);
1002
+ device.setLastSeen(Date.now());
1014
1003
  resolve(result);
1015
1004
  })
1016
1005
  .catch(function (error) {
@@ -1615,280 +1604,232 @@ class BacnetClient extends EventEmitter {
1615
1604
  }
1616
1605
  }
1617
1606
 
1618
- buildJsonObject(device) {
1607
+ async buildJsonTree() {
1619
1608
  let that = this;
1620
- let address = device.address;
1621
- let pointList = device.getPointsList();
1622
- let requestMutex = new Mutex();
1609
+ that.buildJsonInProgress = true;
1610
+ for (let i = 0; i < that.deviceList.length; i++) {
1611
+ try {
1612
+ let device = that.deviceList[i];
1613
+ await that.buildJsonObject(device);
1614
+ } catch (e) {
1615
+ that.logOut("buildJsonTree error: ", e);
1616
+ }
1617
+ }
1623
1618
 
1624
- return new Promise(function (resolve, reject) {
1625
- let promiseArray = [];
1626
- if (typeof pointList !== "undefined" && pointList.length > 0) {
1627
- pointList.forEach(function (point, pointListIndex) {
1628
- requestMutex.acquire().then(function (release) {
1629
- if (device.getIsInitialQuery()) {
1630
- that
1631
- ._readObjectLite(device, address, point.value.type, point.value.instance)
1632
- .then(function (result) {
1633
- if (!result.error) {
1634
- if (result.length > 0 && Array.isArray(result)) {
1635
- promiseArray = result;
1636
- } else {
1637
- promiseArray.push(result);
1638
- }
1639
- }
1619
+ that.buildJsonInProgress = false;
1620
+ }
1640
1621
 
1641
- release();
1642
-
1643
- if (pointListIndex == pointList.length - 1) {
1644
- device.setIsInitialQuery(false);
1645
- that
1646
- .buildResponse(promiseArray, device)
1647
- .then(function () {
1648
- that.lastNetworkPoll = Date.now();
1649
- resolve({ deviceList: that.deviceList, pointList: that.networkTree });
1650
- })
1651
- .catch(function (e) {
1652
- that.logOut("Error while building json object: ", e);
1653
- reject(e);
1654
- });
1655
- }
1656
- })
1657
- .catch(function (e) {
1658
- release();
1659
- that.logOut("_readObjectLite error: ", e);
1660
-
1661
- if (pointListIndex == pointList.length - 1) {
1662
- device.setIsInitialQuery(false);
1663
- that
1664
- .buildResponse(promiseArray, device)
1665
- .then(function () {
1666
- that.lastNetworkPoll = Date.now();
1667
- resolve({ deviceList: that.deviceList, pointList: that.networkTree });
1668
- })
1669
- .catch(function (e) {
1670
- that.logOut("Error while building json object: ", e);
1671
- reject(e);
1672
- });
1673
- }
1674
- });
1675
- } else {
1676
- that
1677
- ._readObjectFull(device, address, point.value.type, point.value.instance)
1678
- .then(function (result) {
1679
- if (!result.error) {
1680
- if (result.length > 0 && Array.isArray(result)) {
1681
- promiseArray = result;
1682
- } else {
1683
- promiseArray.push(result);
1684
- }
1685
- }
1686
1622
 
1687
- release();
1688
-
1689
- if (pointListIndex == pointList.length - 1) {
1690
- that
1691
- .buildResponse(promiseArray, device)
1692
- .then(function () {
1693
- that.lastNetworkPoll = Date.now();
1694
- resolve({ deviceList: that.deviceList, pointList: that.networkTree });
1695
- })
1696
- .catch(function (e) {
1697
- that.logOut("Error while building json object: ", e);
1698
- reject(e);
1699
- });
1700
- }
1701
- })
1702
- .catch(function (e) {
1703
- release();
1704
- that.logOut("_readObjectFull error: ", e);
1705
-
1706
- if (pointListIndex == pointList.length - 1) {
1707
- that
1708
- .buildResponse(promiseArray, device)
1709
- .then(function () {
1710
- that.lastNetworkPoll = Date.now();
1711
- resolve({ deviceList: that.deviceList, pointList: that.networkTree });
1712
- })
1713
- .catch(function (e) {
1714
- that.logOut("Error while building json object: ", e);
1715
- reject(e);
1716
- });
1717
- }
1718
- });
1719
- }
1720
- });
1721
- });
1722
- } else {
1723
- reject("Unable to build network tree, empty point list");
1623
+ async buildJsonObject(device) {
1624
+ try {
1625
+ const address = device.address;
1626
+ const pointList = device.getPointsList();
1627
+ const requestMutex = new Mutex();
1628
+ const promiseArray = [];
1629
+
1630
+ if (typeof pointList === "undefined" || pointList.length === 0) {
1631
+ throw new Error("Unable to build network tree, empty point list");
1724
1632
  }
1725
- });
1633
+
1634
+ for (const point of pointList) {
1635
+ await requestMutex.acquire();
1636
+
1637
+ let result;
1638
+ if (device.getIsInitialQuery()) {
1639
+ result = await this._readObjectLite(device, address, point.value.type, point.value.instance);
1640
+ } else {
1641
+ result = await this._readObjectFull(device, address, point.value.type, point.value.instance);
1642
+ }
1643
+
1644
+ if (!result.error) {
1645
+ device.setLastSeen(Date.now());
1646
+ if (result.length > 0 && Array.isArray(result)) {
1647
+ promiseArray.push(...result);
1648
+ } else {
1649
+ promiseArray.push(result);
1650
+ }
1651
+ }
1652
+
1653
+ requestMutex.release();
1654
+ }
1655
+
1656
+ await this.buildResponse(promiseArray, device);
1657
+ this.lastNetworkPoll = Date.now();
1658
+
1659
+ return { deviceList: this.deviceList, pointList: this.networkTree };
1660
+ } catch (error) {
1661
+ this.logOut("Error while building json object: ", error);
1662
+ throw error;
1663
+ }
1726
1664
  }
1727
1665
 
1728
1666
  // Builds response object for a fully qualified
1729
1667
  buildResponse(fullObjects, device) {
1730
1668
  let that = this;
1731
- const reg = /[$#\/\\+]/gi;
1669
+ const reg = /[$#\/\\+,]/gi;
1732
1670
  return new Promise(function (resolve, reject) {
1733
- let deviceKey =
1734
- typeof device.getAddress() == "object"
1735
- ? device.getAddress().address + "-" + device.getDeviceId()
1736
- : device.getAddress() + "-" + device.getDeviceId();
1737
- let values = that.networkTree[deviceKey] ? that.networkTree[deviceKey] : {};
1738
- for (let i = 0; i < fullObjects.length; i++) {
1739
- let obj = fullObjects[i];
1740
- let successfulResult = !obj.error ? obj.value : null;
1741
- if (successfulResult) {
1742
- successfulResult.values.forEach(function (pointProperty, pointPropertyIndex) {
1743
- if (!pointProperty.objectId && successfulResult.objectId && !pointProperty.values && successfulResult.values) {
1744
- pointProperty = successfulResult;
1745
- }
1746
-
1747
- let currobjectId = pointProperty.objectId.type;
1748
- let bac_obj = that.getObjectType(currobjectId);
1749
- let objectName = that._findValueById(pointProperty.values, baEnum.PropertyIdentifier.OBJECT_NAME);
1750
- let objectType = pointProperty.objectId.type;
1751
-
1752
- let objectId;
1753
- if (objectName !== null && typeof objectName == "string") {
1754
- objectName = objectName.replace(reg, "");
1755
- objectId = objectName + "_" + bac_obj + "_" + pointProperty.objectId.instance;
1671
+ try {
1672
+ let deviceKey =
1673
+ typeof device.getAddress() == "object"
1674
+ ? device.getAddress().address + "-" + device.getDeviceId()
1675
+ : device.getAddress() + "-" + device.getDeviceId();
1676
+ let values = that.networkTree[deviceKey] ? that.networkTree[deviceKey] : {};
1677
+ for (let i = 0; i < fullObjects.length; i++) {
1678
+ let obj = fullObjects[i];
1679
+ let successfulResult = !obj.error ? obj.value : null;
1680
+ if (successfulResult) {
1681
+ successfulResult.values.forEach(function (pointProperty, pointPropertyIndex) {
1682
+ if (!pointProperty.objectId && successfulResult.objectId && !pointProperty.values && successfulResult.values) {
1683
+ pointProperty = successfulResult;
1684
+ }
1756
1685
 
1757
- try {
1758
- pointProperty.values.forEach(function (object, objectIndex) {
1759
- //checks for error code json structure, returned for invalid bacnet requests
1760
- if (object && object.value && !object.value.errorClass) {
1761
- if (!values[objectId]) values[objectId] = {};
1762
- values[objectId].meta = {
1763
- objectId: pointProperty.objectId,
1764
- };
1765
-
1766
- switch (object.id) {
1767
- case baEnum.PropertyIdentifier.PRESENT_VALUE:
1768
- if (object.value[0] && object.value[0].value !== "undefined" && object.value[0].value !== null) {
1769
- //check for binary object type
1770
- if (objectType == 3 || objectType == 4 || objectType == 5) {
1771
- if (object.value[0].value == 0) {
1772
- values[objectId].presentValue = false;
1773
- } else if (object.value[0].value == 1) {
1774
- values[objectId].presentValue = true;
1775
- }
1776
- } else if (objectType == 40) {
1777
- //character string
1778
- values[objectId].presentValue = object.value[0].value;
1779
- } else if (objectType == 13 || objectType == 14 || objectType == 19) {
1780
- //check for MSV MSI MSO - for enum state text
1781
- if (values[objectId].stateTextArray && values[objectId].stateTextArray.length > 0) {
1686
+ let currobjectId = pointProperty.objectId.type;
1687
+ let bac_obj = that.getObjectType(currobjectId);
1688
+ let objectName = that._findValueById(pointProperty.values, baEnum.PropertyIdentifier.OBJECT_NAME);
1689
+ let objectType = pointProperty.objectId.type;
1690
+
1691
+ let objectId;
1692
+ if (objectName !== null && typeof objectName == "string") {
1693
+ objectName = objectName.replace(reg, "");
1694
+ objectId = objectName + "_" + bac_obj + "_" + pointProperty.objectId.instance;
1695
+
1696
+ try {
1697
+ pointProperty.values.forEach(function (object, objectIndex) {
1698
+ //checks for error code json structure, returned for invalid bacnet requests
1699
+ if (object && object.value && !object.value.errorClass) {
1700
+ if (!values[objectId]) values[objectId] = {};
1701
+ values[objectId].meta = {
1702
+ objectId: pointProperty.objectId,
1703
+ };
1704
+
1705
+ switch (object.id) {
1706
+ case baEnum.PropertyIdentifier.PRESENT_VALUE:
1707
+ if (object.value[0] && object.value[0].value !== "undefined" && object.value[0].value !== null) {
1708
+ //check for binary object type
1709
+ if (objectType == 3 || objectType == 4 || objectType == 5) {
1782
1710
  if (object.value[0].value == 0) {
1783
- values[objectId].presentValue = values[objectId].stateTextArray[object.value[0].value].value;
1784
- } else if (object.value[0].value !== 0) {
1785
- values[objectId].presentValue =
1786
- values[objectId].stateTextArray[object.value[0].value - 1].value;
1711
+ values[objectId].presentValue = false;
1712
+ } else if (object.value[0].value == 1) {
1713
+ values[objectId].presentValue = true;
1714
+ }
1715
+ } else if (objectType == 40) {
1716
+ //character string
1717
+ values[objectId].presentValue = object.value[0].value;
1718
+ } else if (objectType == 13 || objectType == 14 || objectType == 19) {
1719
+ //check for MSV MSI MSO - for enum state text
1720
+ if (values[objectId].stateTextArray && values[objectId].stateTextArray.length > 0) {
1721
+ if (object.value[0].value == 0) {
1722
+ values[objectId].presentValue = values[objectId].stateTextArray[object.value[0].value].value;
1723
+ } else if (object.value[0].value !== 0) {
1724
+ values[objectId].presentValue =
1725
+ values[objectId].stateTextArray[object.value[0].value - 1].value;
1726
+ }
1787
1727
  }
1728
+ } else if (objectType !== 8) {
1729
+ values[objectId].presentValue = roundDecimalPlaces(object.value[0].value, 2);
1730
+ }
1731
+ }
1732
+ values[objectId].meta.arrayIndex = object.index;
1733
+ break;
1734
+ case baEnum.PropertyIdentifier.DESCRIPTION:
1735
+ if (object.value[0]) values[objectId].description = object.value[0].value;
1736
+ break;
1737
+ case baEnum.PropertyIdentifier.UNITS:
1738
+ if (object.value[0] && object.value[0].value) values[objectId].units = getUnit(object.value[0].value);
1739
+ break;
1740
+ case baEnum.PropertyIdentifier.OBJECT_NAME:
1741
+ if (object.value[0] && object.value[0].value) {
1742
+ values[objectId].objectName = object.value[0].value.replace(reg, "");
1743
+ if (!values[objectId].displayName) {
1744
+ values[objectId].displayName = object.value[0].value.replace(reg, "");
1788
1745
  }
1789
- } else if (objectType !== 8) {
1790
- values[objectId].presentValue = roundDecimalPlaces(object.value[0].value, 2);
1791
1746
  }
1792
- }
1793
- values[objectId].meta.arrayIndex = object.index;
1794
- break;
1795
- case baEnum.PropertyIdentifier.DESCRIPTION:
1796
- if (object.value[0]) values[objectId].description = object.value[0].value;
1797
- break;
1798
- case baEnum.PropertyIdentifier.UNITS:
1799
- if (object.value[0] && object.value[0].value)
1800
- values[objectId].units = getUnit(object.value[0].value);
1801
- break;
1802
- case baEnum.PropertyIdentifier.OBJECT_NAME:
1803
- if (object.value[0] && object.value[0].value) {
1804
- values[objectId].objectName = object.value[0].value.replace(reg, "");
1805
- if (!values[objectId].displayName) {
1806
- values[objectId].displayName = object.value[0].value.replace(reg, "");
1747
+ break;
1748
+ case baEnum.PropertyIdentifier.OBJECT_TYPE:
1749
+ if (object.value[0] && object.value[0].value) values[objectId].objectType = object.value[0].value;
1750
+ break;
1751
+ case baEnum.PropertyIdentifier.PROPERTY_LIST:
1752
+ if (object.value) values[objectId].propertyList = that.mapPropsToArray(object.value);
1753
+ break;
1754
+ case baEnum.PropertyIdentifier.SYSTEM_STATUS:
1755
+ if (object.value[0]) {
1756
+ values[objectId].systemStatus = that.getPROP_SYSTEM_STATUS(object.value[0].value);
1807
1757
  }
1808
- }
1809
- break;
1810
- case baEnum.PropertyIdentifier.OBJECT_TYPE:
1811
- if (object.value[0] && object.value[0].value) values[objectId].objectType = object.value[0].value;
1812
- break;
1813
- case baEnum.PropertyIdentifier.PROPERTY_LIST:
1814
- if (object.value) values[objectId].propertyList = that.mapPropsToArray(object.value);
1815
- break;
1816
- case baEnum.PropertyIdentifier.SYSTEM_STATUS:
1817
- if (object.value[0]) {
1818
- values[objectId].systemStatus = that.getPROP_SYSTEM_STATUS(object.value[0].value);
1819
- }
1820
- break;
1821
- case baEnum.PropertyIdentifier.MODIFICATION_DATE:
1822
- if (object.value[0]) {
1823
- values[objectId].modificationDate = object.value[0].value;
1824
- }
1825
- break;
1826
- case baEnum.PropertyIdentifier.PROGRAM_STATE:
1827
- if (object.value[0]) {
1828
- values[objectId].programState = that.getPROP_PROGRAM_STATE(object.value[0].value);
1829
- }
1830
- break;
1831
- case baEnum.PropertyIdentifier.RECORD_COUNT:
1832
- if (object.value[0]) {
1833
- values[objectId].recordCount = object.value[0].value;
1834
- }
1835
- break;
1836
- case baEnum.PropertyIdentifier.PRIORITY_ARRAY:
1837
- if (object.value.length > 0) {
1838
- values[objectId].hasPriorityArray = true;
1839
- }
1840
- break;
1841
- case baEnum.PropertyIdentifier.STATE_TEXT:
1842
- if (object.value) {
1843
- values[objectId].stateTextArray = object.value;
1844
- if (
1845
- typeof values[objectId].presentValue == "number" &&
1846
- values[objectId].presentValue !== null &&
1847
- values[objectId].presentValue !== undefined
1848
- ) {
1849
- const tempIndex = values[objectId].presentValue;
1850
- if (tempIndex == 0) {
1851
- values[objectId].presentValue = values[objectId].stateTextArray[tempIndex].value;
1852
- } else if (tempIndex !== 0) {
1853
- values[objectId].presentValue = values[objectId].stateTextArray[tempIndex - 1].value;
1758
+ break;
1759
+ case baEnum.PropertyIdentifier.MODIFICATION_DATE:
1760
+ if (object.value[0]) {
1761
+ values[objectId].modificationDate = object.value[0].value;
1762
+ }
1763
+ break;
1764
+ case baEnum.PropertyIdentifier.PROGRAM_STATE:
1765
+ if (object.value[0]) {
1766
+ values[objectId].programState = that.getPROP_PROGRAM_STATE(object.value[0].value);
1767
+ }
1768
+ break;
1769
+ case baEnum.PropertyIdentifier.RECORD_COUNT:
1770
+ if (object.value[0]) {
1771
+ values[objectId].recordCount = object.value[0].value;
1772
+ }
1773
+ break;
1774
+ case baEnum.PropertyIdentifier.PRIORITY_ARRAY:
1775
+ if (object.value.length > 0) {
1776
+ values[objectId].hasPriorityArray = true;
1777
+ }
1778
+ break;
1779
+ case baEnum.PropertyIdentifier.STATE_TEXT:
1780
+ if (object.value) {
1781
+ values[objectId].stateTextArray = object.value;
1782
+ if (
1783
+ typeof values[objectId].presentValue == "number" &&
1784
+ values[objectId].presentValue !== null &&
1785
+ values[objectId].presentValue !== undefined
1786
+ ) {
1787
+ const tempIndex = values[objectId].presentValue;
1788
+ if (tempIndex == 0) {
1789
+ values[objectId].presentValue = values[objectId].stateTextArray[tempIndex].value;
1790
+ } else if (tempIndex !== 0) {
1791
+ values[objectId].presentValue = values[objectId].stateTextArray[tempIndex - 1].value;
1792
+ }
1854
1793
  }
1855
1794
  }
1856
- }
1857
- break;
1858
- case baEnum.PropertyIdentifier.VENDOR_NAME:
1859
- if (object.value) {
1860
- if (object.value[0].value && typeof object.value[0].value == "string") {
1861
- values[objectId].vendorName = object.value[0].value;
1795
+ break;
1796
+ case baEnum.PropertyIdentifier.VENDOR_NAME:
1797
+ if (object.value) {
1798
+ if (object.value[0].value && typeof object.value[0].value == "string") {
1799
+ values[objectId].vendorName = object.value[0].value;
1800
+ }
1862
1801
  }
1863
- }
1864
- break;
1802
+ break;
1803
+ }
1865
1804
  }
1866
- }
1867
- if (
1868
- pointPropertyIndex == successfulResult.values.length - 1 &&
1869
- objectIndex == pointProperty.values.length - 1 &&
1870
- i == fullObjects.length - 1
1871
- ) {
1872
- that.networkTree[deviceKey] = values;
1873
- resolve(that.networkTree);
1874
- }
1875
- });
1876
- } catch (e) {
1877
- that.logOut("issue resolving bacnet payload, see error: ", e);
1878
- reject(e);
1805
+ if (
1806
+ pointPropertyIndex == successfulResult.values.length - 1 &&
1807
+ objectIndex == pointProperty.values.length - 1 &&
1808
+ i == fullObjects.length - 1
1809
+ ) {
1810
+ that.networkTree[deviceKey] = values;
1811
+ resolve(that.networkTree);
1812
+ }
1813
+ });
1814
+ } catch (e) {
1815
+ that.logOut("issue resolving bacnet payload, see error: ", e);
1816
+ reject(e);
1817
+ }
1879
1818
  }
1819
+ });
1820
+ } else {
1821
+ //error found in point property
1822
+ if (i == fullObjects.length - 1) {
1823
+ that.networkTree[deviceKey] = values;
1824
+ resolve(that.networkTree);
1880
1825
  }
1881
- });
1882
- } else {
1883
- //error found in point property
1884
- if (i == fullObjects.length - 1) {
1885
- that.networkTree[deviceKey] = values;
1886
- resolve(that.networkTree);
1887
1826
  }
1888
1827
  }
1828
+ that.networkTree[deviceKey] = values;
1829
+ resolve(that.networkTree);
1830
+ } catch (e) {
1831
+ reject(e);
1889
1832
  }
1890
- that.networkTree[deviceKey] = values;
1891
- resolve(that.networkTree);
1892
1833
  });
1893
1834
  }
1894
1835
 
package/bacnet_device.js CHANGED
@@ -35,6 +35,7 @@ class BacnetDevice {
35
35
  that.protocolServicesSupported = config.protocolServicesSupported;
36
36
  that.isProtocolServicesSet = config.isProtocolServicesSet;
37
37
  that.isInitialQuery = config.isInitialQuery;
38
+ that.isDumbMstpRouter = config.isDumbMstpRouter;
38
39
 
39
40
  } else if (fromImport == false) {
40
41
  if (config.net && config.adr) {
@@ -64,9 +65,18 @@ class BacnetDevice {
64
65
  that.protocolServicesSupported = [];
65
66
  that.isProtocolServicesSet = false;
66
67
  that.isInitialQuery = true;
68
+ that.isDumbMstpRouter = false;
67
69
  }
68
70
  }
69
71
 
72
+ setIsDumbMstpRouter(isDumbMstp) {
73
+ this.isDumbMstpRouter = isDumbMstp;
74
+ }
75
+
76
+ getIsDumbMstpRouter() {
77
+ return this.isDumbMstpRouter;
78
+ }
79
+
70
80
  setDisplayName(displayName) {
71
81
  this.displayName = displayName;
72
82
  }
package/bacnet_read.html CHANGED
@@ -59,6 +59,7 @@
59
59
  showPointNameDialog: ref(false),
60
60
  deviceDisplayNameValue: ref(),
61
61
  pointDisplayNameValue: ref(),
62
+ calculateProgressBarWidth: ref(),
62
63
  };
63
64
  },
64
65
  setup() {
@@ -130,6 +131,7 @@
130
131
  app.pointList = result.pointList;
131
132
  app.pollFrequency = parseInt(result.pollFrequency);
132
133
  app.deviceCount = result.deviceList.length;
134
+
133
135
  //progress bar percentage
134
136
  let progressVal = parseInt((result.renderListCount / result.deviceList.length) * 100);
135
137
  if (typeof progressVal == "number" && !isNaN(progressVal)) {
@@ -138,6 +140,15 @@
138
140
  app.progressBarValue = 0;
139
141
  }
140
142
 
143
+ //set progress bar width based on device count integer digit length
144
+ if (app.deviceCount.toString().length >= 5) {
145
+ app.calculateProgressBarWidth = "width: 200px;";
146
+ } else if (app.deviceCount.toString().length == 4) {
147
+ app.calculateProgressBarWidth = "width: 210px;";
148
+ } else if (app.deviceCount.toString().length < 4) {
149
+ app.calculateProgressBarWidth = "width: 220px;";
150
+ }
151
+
141
152
  app.$forceUpdate();
142
153
  });
143
154
 
@@ -434,6 +445,16 @@
434
445
  link.click();
435
446
  }
436
447
  }
448
+ } else {
449
+ if (parseInt(key) == keys.length - 1) {
450
+ // last iteration
451
+ var encodedUri = encodeURI(csvContent);
452
+ var link = document.createElement("a");
453
+ link.setAttribute("href", encodedUri);
454
+ link.setAttribute("download", "pointslist.csv");
455
+ document.body.appendChild(link);
456
+ link.click();
457
+ }
437
458
  }
438
459
  }
439
460
  },
@@ -451,6 +472,8 @@
451
472
  let app = this;
452
473
  if ((Date.now() - slotProps.node.lastSeen) / 1000 < app.pollFrequency + 5) {
453
474
  return true;
475
+ } else if (slotProps.node.isDumbMstpRouter == true || slotProps.node.isDumbMstpRouter == "true") {
476
+ return true;
454
477
  }
455
478
  return false;
456
479
  },
@@ -530,7 +553,7 @@
530
553
  app.pointDisplayNameValue = app.rightClickedPoint.node.label;
531
554
  break;
532
555
  case "updatePoint":
533
- app.updatePoint(app.rightClickedDevice);
556
+ app.updatePoint(app.rightClickedPoint);
534
557
  break;
535
558
  default:
536
559
  break;
@@ -1061,7 +1084,7 @@
1061
1084
  *
1062
1085
  *
1063
1086
  -->
1064
- <div id="read-networkTree-tab" style="display:none">
1087
+ <div id="read-networkTree-tab" style="display:none" class="bp-networktree-tab">
1065
1088
  <div id="read-networkTree-tab-content" class="networkTreeContent" style="display:none">
1066
1089
  <p-dialog
1067
1090
  v-model:visible="showDeviceNameDialog"
@@ -1107,8 +1130,8 @@
1107
1130
 
1108
1131
  <div class="headerDiv">
1109
1132
  <a class="countStatus" style="margin-left: 15px;"> <span class="bp-deviceCount">{{deviceCount}}</span> Device(s)</a>
1110
- <p-progressbar :value="progressBarValue" :show-value="true" style="width: 220px; height: 20px;"></p-progressbar>
1111
- <div class="buttonGroup" style="padding-left: 15px;">
1133
+ <p-progressbar class="bp-readNode-progressbar" :value="progressBarValue" :show-value="true" :style="calculateProgressBarWidth"></p-progressbar>
1134
+ <div class="buttonGroup">
1112
1135
  <button @click="rebuildDataModel()" class="rebuildDataButton" title="Rebuild Data Model">
1113
1136
  <i class="pi pi-wrench" style="color: #969696;"></i>
1114
1137
  </button>
@@ -1136,7 +1159,7 @@
1136
1159
  <div v-if="isDeviceActive(slotProps)" class="deviceLabelParent">
1137
1160
  <b class="deviceLabel">
1138
1161
  <span class="statusOnline deviceStatus dotOnline dot"></span>
1139
- {{slotProps.node.label}}
1162
+ <span class="deviceLabelSpan">{{slotProps.node.label}}</span>
1140
1163
  <span v-if="hasMstpDevices(slotProps)" class="mstpDeviceCount"
1141
1164
  >&nbsp; {{calculateMstpCount(slotProps)}} &nbsp;</span
1142
1165
  >
@@ -1146,7 +1169,7 @@
1146
1169
  <div v-else>
1147
1170
  <b class="deviceLabel">
1148
1171
  <span class="statusOffline deviceStatus dotOffline dot"></span>
1149
- {{slotProps.node.label}}
1172
+ <span class="deviceLabelSpan">{{slotProps.node.label}}</span>
1150
1173
  <span v-if="hasMstpDevices(slotProps)" class="mstpDeviceCount"
1151
1174
  >&nbsp; {{calculateMstpCount(slotProps)}} &nbsp;</span
1152
1175
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitpoolos/edge-bacnet",
3
- "version": "1.4.5",
3
+ "version": "1.4.6",
4
4
  "description": "A bacnet gateway for node-red",
5
5
  "dependencies": {
6
6
  "@plus4nodered/ts-node-bacnet": "^1.0.0-beta.2",
@@ -41,6 +41,7 @@ class Client extends events_1.EventEmitter {
41
41
  this._lastSequenceNumber = 0;
42
42
  this._segmentStore = [];
43
43
  options = options || {};
44
+
44
45
  this._settings = {
45
46
  port: options.port || 47808,
46
47
  interface: options.interface,
@@ -48,11 +49,13 @@ class Client extends events_1.EventEmitter {
48
49
  broadcastAddress: options.broadcastAddress || '255.255.255.255',
49
50
  apduTimeout: options.apduTimeout || 3000
50
51
  };
52
+
51
53
  this._transport = this._settings.transport || new transport_1.Transport({
52
54
  port: this._settings.port,
53
55
  interface: this._settings.interface,
54
56
  broadcastAddress: this._settings.broadcastAddress
55
57
  });
58
+
56
59
  // Setup code
57
60
  this._transport.on('message', this._receiveData.bind(this));
58
61
  this._transport.on('error', this._receiveError.bind(this));
@@ -7,11 +7,16 @@ const DEFAULT_BACNET_PORT = 47808;
7
7
  class Transport extends events_1.EventEmitter {
8
8
  constructor(settings) {
9
9
  super();
10
- this._lastSendMessages = {};
11
- this._settings = settings;
12
- this._server = (0, dgram_1.createSocket)({ type: 'udp4', reuseAddr: true });
13
- this._server.on('message', (msg, rinfo) => this.emit('message', msg, rinfo.address));
14
- this._server.on('error', (err) => this.emit('message', err));
10
+ try {
11
+ this._lastSendMessages = {};
12
+ this._settings = settings;
13
+ this._server = (0, dgram_1.createSocket)({ type: 'udp4', reuseAddr: true });
14
+ this._server.on('message', (msg, rinfo) => this.emit('message', msg, rinfo.address));
15
+ this._server.on('error', (err) => this.emit('message', err));
16
+
17
+ } catch (e) {
18
+ //console.log("Transport constructor error: ", e);
19
+ }
15
20
  }
16
21
  getBroadcastAddress() {
17
22
  return this._settings.broadcastAddress;
@@ -33,9 +38,9 @@ class Transport extends events_1.EventEmitter {
33
38
  delete this._lastSendMessages[messageKey];
34
39
  }, 10000); // delete after 10s, hopefully all cases are handled by that
35
40
  }
36
- const [address, port] = receiver.split(':');
37
- this._server.send(buffer, 0, offset, port || DEFAULT_BACNET_PORT, address);
38
41
 
42
+ const [address, port] = receiver.split(':');
43
+ this._server.send(buffer, 0, offset, this._settings.port || DEFAULT_BACNET_PORT, address);
39
44
  }
40
45
  open() {
41
46
  this._server.bind(this._settings.port, this._settings.interface, () => {
@@ -46,10 +46,17 @@
46
46
  }
47
47
  .pointLabel {
48
48
  font-weight: 400;
49
+ text-wrap: pretty;
50
+ display: inline-block;
51
+ max-width: 85%;
49
52
  }
50
53
  .deviceLabel {
51
54
  color: #1F2731;
52
55
  font-weight: 400;
56
+ text-wrap: pretty;
57
+ }
58
+ .deviceLabelSpan {
59
+ vertical-align: bottom;
53
60
  }
54
61
  .removeAllButton {
55
62
  float: right;
@@ -248,6 +255,7 @@
248
255
  flex-wrap: nowrap;
249
256
  justify-content: space-between;
250
257
  height: 20px;
258
+ min-width: max-content;
251
259
  }
252
260
  .msgTypeDiv {
253
261
  display: flex;
@@ -262,8 +270,11 @@
262
270
  white-space: nowrap;
263
271
  text-overflow: ellipsis;
264
272
  }
265
- .buttonGroup {
266
- padding-left: 7px;
273
+ .bp-readNode-progressbar {
274
+ height: 20px !important;
275
+ }
276
+ .bp-networktree-tab {
277
+ /* min-width: 590px; */
267
278
  }
268
279
  #read-readList-tab {
269
280
  display: flex;
@@ -296,6 +307,7 @@
296
307
  border-radius: 50%;
297
308
  display: inline-block;
298
309
  margin-right: 5px;
310
+ vertical-align: middle;
299
311
  }
300
312
  .dotOnline {
301
313
  background-color: #63B686;
@@ -405,7 +417,7 @@
405
417
  font-size: 1rem !important;
406
418
  }
407
419
  .p-dialog-content {
408
- padding: 0.5em 0.75rem 0.5rem 0.75rem;
420
+ padding: 0.5em 0.75rem 0.5rem 0.75rem !important;
409
421
 
410
422
  & label {
411
423
  font-size: 0.9rem !important;
@@ -500,14 +512,11 @@
500
512
  input[type=checkbox]:focus {
501
513
  outline: none;
502
514
  }
503
-
504
515
  }
505
-
506
516
  .bp-checkbox {
507
517
  accent-color: white;
508
518
  bottom: -3px;
509
519
  }
510
-
511
520
  .bp-import-buttons {
512
521
  margin-top: 25px;
513
522
  label {
@@ -527,10 +536,10 @@
527
536
  }
528
537
  }
529
538
 
530
- .red-ui-tab {
539
+ /* .red-ui-tab {
531
540
  border-top-left-radius: 5px;
532
541
  border-top-right-radius: 5px;
533
- }
542
+ } */
534
543
 
535
544
  .node-input-deviceIdRangeMatrix-container-row {
536
545
  border-radius: 10px;
package/treeBuilder.js CHANGED
@@ -63,11 +63,45 @@ class treeBuilder {
63
63
  // Check if the device object exists and the device name is valid
64
64
  if (deviceObject) {
65
65
  await this.processDevicePoints(device, deviceObject, deviceName, ipAddress, deviceId, index);
66
+
67
+ //delete dummy object if all conditions satisfied
68
+ if (deviceName !== null) {
69
+ if (deviceId !== null) {
70
+ let lastIndex = deviceName.lastIndexOf(deviceId);
71
+ if (lastIndex) {
72
+ let formattedName = deviceName.substring(0, lastIndex);
73
+ formattedName = `${formattedName.trim()}_Device_${deviceId}`;
74
+ if (
75
+ this.networkTree[deviceKey][formattedName] &&
76
+ Object.keys(this.networkTree[deviceKey][formattedName]).length > 0 &&
77
+ this.networkTree[deviceKey]["device"]
78
+ ) {
79
+ delete this.networkTree[deviceKey]["device"];
80
+ }
81
+ }
82
+ }
83
+ }
84
+
66
85
  } else {
67
- //console.log("Unable to find device object");
86
+ //invalid ip object, likely dumb mstp router
87
+ if (device.getIsDumbMstpRouter()) {
88
+ //update dumb mstp router name
89
+ await this.updateDumbMstpRouterName(deviceName, ipAddress, deviceId);
90
+ }
68
91
  }
69
92
  }
70
93
 
94
+ async updateDumbMstpRouterName(deviceName, ipAddress, deviceId) {
95
+ return new Promise((resolve, reject) => {
96
+ let listDeviceIndex = this.renderList.findIndex(item => item.deviceId == deviceId && item.ipAddr == ipAddress && item.isDumbMstpRouter == true);
97
+ if (listDeviceIndex !== -1) {
98
+ this.renderList[listDeviceIndex].label = deviceName;
99
+ this.renderList[listDeviceIndex].data = deviceName;
100
+ }
101
+ resolve();
102
+ });
103
+ }
104
+
71
105
  /**
72
106
  * Add the root device folder to the render list.
73
107
  *
@@ -88,8 +122,9 @@ class treeBuilder {
88
122
 
89
123
  // Check if the device already exists in the renderList
90
124
  const existingDeviceIndex = this.renderList.findIndex(item => item.deviceId === deviceId && item.ipAddr === ipAddress);
91
-
92
125
  if (existingDeviceIndex === -1) { // Device not found, add new entry
126
+ let isDumbMstpRouter = false;
127
+ if (device.getIsDumbMstpRouter() && deviceId == null) isDumbMstpRouter = true;
93
128
  const rootFolder = {
94
129
  key: index,
95
130
  label: displayName,
@@ -103,6 +138,7 @@ class treeBuilder {
103
138
  deviceId,
104
139
  isMstpDevice: device.getIsMstpDevice(),
105
140
  initialName: device.getDeviceName(),
141
+ isDumbMstpRouter: isDumbMstpRouter,
106
142
  };
107
143
 
108
144
  // Add the root folder to the render list
@@ -112,10 +148,10 @@ class treeBuilder {
112
148
  }
113
149
 
114
150
  addEmptyIpRootDevice(childDevice) {
115
-
116
151
  const ipAddress = this.getDeviceIpAddress(childDevice);
117
152
 
118
- let deviceIndex = this.deviceList.findIndex(ele => ele.address === ipAddress && ele.deviceId === null && ele.deviceName === ipAddress && ele.displayName === ipAddress);
153
+ //let deviceIndex = this.deviceList.findIndex(ele => ele.address === ipAddress && ele.deviceId === null && ele.deviceName === ipAddress && ele.displayName === ipAddress);
154
+ let deviceIndex = this.deviceList.findIndex(ele => ele.address === ipAddress && ele.deviceId === null);
119
155
 
120
156
  if (deviceIndex === -1) {
121
157
  let newDevice = {
@@ -140,6 +176,7 @@ class treeBuilder {
140
176
  protocolServicesSupported: [],
141
177
  isProtocolServicesSet: false,
142
178
  isInitialQuery: true,
179
+ isDumbMstpRouter: true,
143
180
  };
144
181
 
145
182
  let newBacnetDevice = new BacnetDevice(true, newDevice);
@@ -287,7 +324,6 @@ class treeBuilder {
287
324
  updateRenderList(children, device, deviceName, index, ipAddress, deviceId) {
288
325
  // Create the folder structure for the device
289
326
  const folderJson = this.createFolderJson(children, device.hasChildDevices(), deviceId);
290
-
291
327
  if (!this.renderList) {
292
328
  this.renderList = [];
293
329
  }