@bitpoolos/edge-bacnet 1.4.5 → 1.4.7

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,233 @@ 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
+ device.setIsInitialQuery(false);
1641
+ } else {
1642
+ result = await this._readObjectFull(device, address, point.value.type, point.value.instance);
1643
+ }
1644
+
1645
+ if (!result.error) {
1646
+ device.setLastSeen(Date.now());
1647
+ if (result.length > 0 && Array.isArray(result)) {
1648
+ promiseArray.push(...result);
1649
+ } else {
1650
+ promiseArray.push(result);
1651
+ }
1652
+ }
1653
+
1654
+ requestMutex.release();
1655
+ }
1656
+
1657
+ await this.buildResponse(promiseArray, device);
1658
+ this.lastNetworkPoll = Date.now();
1659
+
1660
+ return { deviceList: this.deviceList, pointList: this.networkTree };
1661
+ } catch (error) {
1662
+ this.logOut("Error while building json object: ", error);
1663
+ throw error;
1664
+ }
1726
1665
  }
1727
1666
 
1728
1667
  // Builds response object for a fully qualified
1729
1668
  buildResponse(fullObjects, device) {
1730
1669
  let that = this;
1731
- const reg = /[$#\/\\+]/gi;
1670
+ const reg = /[$#\/\\+,]/gi;
1732
1671
  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;
1672
+ try {
1673
+ let deviceKey =
1674
+ typeof device.getAddress() == "object"
1675
+ ? device.getAddress().address + "-" + device.getDeviceId()
1676
+ : device.getAddress() + "-" + device.getDeviceId();
1677
+ let values = that.networkTree[deviceKey] ? that.networkTree[deviceKey] : {};
1678
+ for (let i = 0; i < fullObjects.length; i++) {
1679
+ let obj = fullObjects[i];
1680
+ let successfulResult = !obj.error ? obj.value : null;
1681
+ if (successfulResult) {
1682
+ successfulResult.values.forEach(function (pointProperty, pointPropertyIndex) {
1683
+ if (!pointProperty.objectId && successfulResult.objectId && !pointProperty.values && successfulResult.values) {
1684
+ pointProperty = successfulResult;
1685
+ }
1756
1686
 
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) {
1687
+ let currobjectId = pointProperty.objectId.type;
1688
+ let bac_obj = that.getObjectType(currobjectId);
1689
+ let objectName = that._findValueById(pointProperty.values, baEnum.PropertyIdentifier.OBJECT_NAME);
1690
+ let objectType = pointProperty.objectId.type;
1691
+
1692
+ let objectId;
1693
+ if (objectName !== null && typeof objectName == "string") {
1694
+ objectName = objectName.replace(reg, "");
1695
+ objectId = objectName + "_" + bac_obj + "_" + pointProperty.objectId.instance;
1696
+
1697
+ try {
1698
+ pointProperty.values.forEach(function (object, objectIndex) {
1699
+ //checks for error code json structure, returned for invalid bacnet requests
1700
+ if (object && object.value && !object.value.errorClass) {
1701
+ if (!values[objectId]) values[objectId] = {};
1702
+ values[objectId].meta = {
1703
+ objectId: pointProperty.objectId,
1704
+ };
1705
+
1706
+ switch (object.id) {
1707
+ case baEnum.PropertyIdentifier.PRESENT_VALUE:
1708
+ if (object.value[0] && object.value[0].value !== "undefined" && object.value[0].value !== null) {
1709
+ //check for binary object type
1710
+ if (objectType == 3 || objectType == 4 || objectType == 5) {
1782
1711
  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;
1712
+ values[objectId].presentValue = false;
1713
+ } else if (object.value[0].value == 1) {
1714
+ values[objectId].presentValue = true;
1715
+ }
1716
+ } else if (objectType == 40) {
1717
+ //character string
1718
+ values[objectId].presentValue = object.value[0].value;
1719
+ } else if (objectType == 13 || objectType == 14 || objectType == 19) {
1720
+ //check for MSV MSI MSO - for enum state text
1721
+ if (values[objectId].stateTextArray && values[objectId].stateTextArray.length > 0) {
1722
+ if (object.value[0].value == 0) {
1723
+ values[objectId].presentValue = values[objectId].stateTextArray[object.value[0].value].value;
1724
+ } else if (object.value[0].value !== 0) {
1725
+ values[objectId].presentValue =
1726
+ values[objectId].stateTextArray[object.value[0].value - 1].value;
1727
+ }
1787
1728
  }
1729
+ } else if (objectType !== 8) {
1730
+ values[objectId].presentValue = roundDecimalPlaces(object.value[0].value, 2);
1731
+ }
1732
+ }
1733
+ values[objectId].meta.arrayIndex = object.index;
1734
+ break;
1735
+ case baEnum.PropertyIdentifier.DESCRIPTION:
1736
+ if (object.value[0]) values[objectId].description = object.value[0].value;
1737
+ break;
1738
+ case baEnum.PropertyIdentifier.UNITS:
1739
+ if (object.value[0] && object.value[0].value) values[objectId].units = getUnit(object.value[0].value);
1740
+ break;
1741
+ case baEnum.PropertyIdentifier.OBJECT_NAME:
1742
+ if (object.value[0] && object.value[0].value) {
1743
+ values[objectId].objectName = object.value[0].value.replace(reg, "");
1744
+ if (!values[objectId].displayName) {
1745
+ values[objectId].displayName = object.value[0].value.replace(reg, "");
1788
1746
  }
1789
- } else if (objectType !== 8) {
1790
- values[objectId].presentValue = roundDecimalPlaces(object.value[0].value, 2);
1791
1747
  }
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, "");
1748
+ break;
1749
+ case baEnum.PropertyIdentifier.OBJECT_TYPE:
1750
+ if (object.value[0] && object.value[0].value) values[objectId].objectType = object.value[0].value;
1751
+ break;
1752
+ case baEnum.PropertyIdentifier.PROPERTY_LIST:
1753
+ if (object.value) values[objectId].propertyList = that.mapPropsToArray(object.value);
1754
+ break;
1755
+ case baEnum.PropertyIdentifier.SYSTEM_STATUS:
1756
+ if (object.value[0]) {
1757
+ values[objectId].systemStatus = that.getPROP_SYSTEM_STATUS(object.value[0].value);
1807
1758
  }
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;
1759
+ break;
1760
+ case baEnum.PropertyIdentifier.MODIFICATION_DATE:
1761
+ if (object.value[0]) {
1762
+ values[objectId].modificationDate = object.value[0].value;
1763
+ }
1764
+ break;
1765
+ case baEnum.PropertyIdentifier.PROGRAM_STATE:
1766
+ if (object.value[0]) {
1767
+ values[objectId].programState = that.getPROP_PROGRAM_STATE(object.value[0].value);
1768
+ }
1769
+ break;
1770
+ case baEnum.PropertyIdentifier.RECORD_COUNT:
1771
+ if (object.value[0]) {
1772
+ values[objectId].recordCount = object.value[0].value;
1773
+ }
1774
+ break;
1775
+ case baEnum.PropertyIdentifier.PRIORITY_ARRAY:
1776
+ if (object.value.length > 0) {
1777
+ values[objectId].hasPriorityArray = true;
1778
+ }
1779
+ break;
1780
+ case baEnum.PropertyIdentifier.STATE_TEXT:
1781
+ if (object.value) {
1782
+ values[objectId].stateTextArray = object.value;
1783
+ if (
1784
+ typeof values[objectId].presentValue == "number" &&
1785
+ values[objectId].presentValue !== null &&
1786
+ values[objectId].presentValue !== undefined
1787
+ ) {
1788
+ const tempIndex = values[objectId].presentValue;
1789
+ if (tempIndex == 0) {
1790
+ values[objectId].presentValue = values[objectId].stateTextArray[tempIndex].value;
1791
+ } else if (tempIndex !== 0) {
1792
+ values[objectId].presentValue = values[objectId].stateTextArray[tempIndex - 1].value;
1793
+ }
1854
1794
  }
1855
1795
  }
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;
1796
+ break;
1797
+ case baEnum.PropertyIdentifier.VENDOR_NAME:
1798
+ if (object.value) {
1799
+ if (object.value[0].value && typeof object.value[0].value == "string") {
1800
+ values[objectId].vendorName = object.value[0].value;
1801
+ }
1862
1802
  }
1863
- }
1864
- break;
1803
+ break;
1804
+ }
1865
1805
  }
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);
1806
+ if (
1807
+ pointPropertyIndex == successfulResult.values.length - 1 &&
1808
+ objectIndex == pointProperty.values.length - 1 &&
1809
+ i == fullObjects.length - 1
1810
+ ) {
1811
+ that.networkTree[deviceKey] = values;
1812
+ resolve(that.networkTree);
1813
+ }
1814
+ });
1815
+ } catch (e) {
1816
+ that.logOut("issue resolving bacnet payload, see error: ", e);
1817
+ reject(e);
1818
+ }
1879
1819
  }
1820
+ });
1821
+ } else {
1822
+ //error found in point property
1823
+ if (i == fullObjects.length - 1) {
1824
+ that.networkTree[deviceKey] = values;
1825
+ resolve(that.networkTree);
1880
1826
  }
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
1827
  }
1888
1828
  }
1829
+ that.networkTree[deviceKey] = values;
1830
+ resolve(that.networkTree);
1831
+ } catch (e) {
1832
+ reject(e);
1889
1833
  }
1890
- that.networkTree[deviceKey] = values;
1891
- resolve(that.networkTree);
1892
1834
  });
1893
1835
  }
1894
1836
 
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
 
@@ -394,7 +405,7 @@
394
405
  },
395
406
  exportPointListCsv() {
396
407
  let app = this;
397
- let csvContent = "data:text/csv;charset=utf-8,ipAddress,deviceId,deviceName,pointName,objectType" + "\r\n";
408
+ let csvContent = "ipAddress,deviceId,deviceName,pointName,objectType" + "\r\n";
398
409
  const keys = Object.keys(app.pointList);
399
410
  for (key in keys) {
400
411
  const guid = keys[key];
@@ -426,14 +437,26 @@
426
437
 
427
438
  if (parseInt(point) == points.length - 1 && parseInt(key) == keys.length - 1) {
428
439
  // last iteration
429
- var encodedUri = encodeURI(csvContent);
440
+ var csvBlob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
430
441
  var link = document.createElement("a");
431
- link.setAttribute("href", encodedUri);
442
+ var url = URL.createObjectURL(csvBlob);
443
+ link.setAttribute("href", url);
432
444
  link.setAttribute("download", "pointslist.csv");
433
445
  document.body.appendChild(link);
434
446
  link.click();
435
447
  }
436
448
  }
449
+ } else {
450
+ if (parseInt(key) == keys.length - 1) {
451
+ // last iteration
452
+ var csvBlob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
453
+ var link = document.createElement("a");
454
+ var url = URL.createObjectURL(csvBlob);
455
+ link.setAttribute("href", url);
456
+ link.setAttribute("download", "pointslist.csv");
457
+ document.body.appendChild(link);
458
+ link.click();
459
+ }
437
460
  }
438
461
  }
439
462
  },
@@ -451,6 +474,8 @@
451
474
  let app = this;
452
475
  if ((Date.now() - slotProps.node.lastSeen) / 1000 < app.pollFrequency + 5) {
453
476
  return true;
477
+ } else if (slotProps.node.isDumbMstpRouter == true || slotProps.node.isDumbMstpRouter == "true") {
478
+ return true;
454
479
  }
455
480
  return false;
456
481
  },
@@ -530,7 +555,7 @@
530
555
  app.pointDisplayNameValue = app.rightClickedPoint.node.label;
531
556
  break;
532
557
  case "updatePoint":
533
- app.updatePoint(app.rightClickedDevice);
558
+ app.updatePoint(app.rightClickedPoint);
534
559
  break;
535
560
  default:
536
561
  break;
@@ -1061,7 +1086,7 @@
1061
1086
  *
1062
1087
  *
1063
1088
  -->
1064
- <div id="read-networkTree-tab" style="display:none">
1089
+ <div id="read-networkTree-tab" style="display:none" class="bp-networktree-tab">
1065
1090
  <div id="read-networkTree-tab-content" class="networkTreeContent" style="display:none">
1066
1091
  <p-dialog
1067
1092
  v-model:visible="showDeviceNameDialog"
@@ -1107,8 +1132,8 @@
1107
1132
 
1108
1133
  <div class="headerDiv">
1109
1134
  <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;">
1135
+ <p-progressbar class="bp-readNode-progressbar" :value="progressBarValue" :show-value="true" :style="calculateProgressBarWidth"></p-progressbar>
1136
+ <div class="buttonGroup">
1112
1137
  <button @click="rebuildDataModel()" class="rebuildDataButton" title="Rebuild Data Model">
1113
1138
  <i class="pi pi-wrench" style="color: #969696;"></i>
1114
1139
  </button>
@@ -1136,7 +1161,7 @@
1136
1161
  <div v-if="isDeviceActive(slotProps)" class="deviceLabelParent">
1137
1162
  <b class="deviceLabel">
1138
1163
  <span class="statusOnline deviceStatus dotOnline dot"></span>
1139
- {{slotProps.node.label}}
1164
+ <span class="deviceLabelSpan">{{slotProps.node.label}}</span>
1140
1165
  <span v-if="hasMstpDevices(slotProps)" class="mstpDeviceCount"
1141
1166
  >&nbsp; {{calculateMstpCount(slotProps)}} &nbsp;</span
1142
1167
  >
@@ -1146,7 +1171,7 @@
1146
1171
  <div v-else>
1147
1172
  <b class="deviceLabel">
1148
1173
  <span class="statusOffline deviceStatus dotOffline dot"></span>
1149
- {{slotProps.node.label}}
1174
+ <span class="deviceLabelSpan">{{slotProps.node.label}}</span>
1150
1175
  <span v-if="hasMstpDevices(slotProps)" class="mstpDeviceCount"
1151
1176
  >&nbsp; {{calculateMstpCount(slotProps)}} &nbsp;</span
1152
1177
  >
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.7",
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
  }