@bitpoolos/edge-bacnet 1.2.6 → 1.2.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/bacnet_client.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /*
2
- MIT License Copyright 2021, 2022 - Bitpool Pty Ltd
2
+ MIT License Copyright 2021, 2024 - Bitpool Pty Ltd
3
3
  */
4
4
 
5
5
  const bacnet = require('./resources/node-bacstack-ts/dist/index.js');
6
6
  const baEnum = bacnet.enum;
7
7
  const bacnetIdMax = baEnum.ASN1_MAX_PROPERTY_ID;
8
8
  const { EventEmitter } = require('events');
9
- const { getUnit, roundDecimalPlaces, Store_Config, Read_Config_Sync } = require('./common');
9
+ const { getUnit, roundDecimalPlaces, Store_Config, Read_Config_Sync, isNumber } = require('./common');
10
10
  const { ToadScheduler, SimpleIntervalJob, Task } = require('toad-scheduler');
11
11
  const { BacnetDevice } = require('./bacnet_device');
12
12
  const {Mutex} = require("async-mutex");
@@ -28,22 +28,24 @@ class BacnetClient extends EventEmitter {
28
28
  that.manualMutex = new Mutex();
29
29
  that.pollInProgress = false;
30
30
  that.scanMatrix = [];
31
+ that.renderListCount = 0;
31
32
 
32
33
  try {
33
34
 
34
- let cachedData = JSON.parse(Read_Config_Sync());
35
- if(cachedData && typeof cachedData == "object") {
36
- if(cachedData.renderList) that.renderList = cachedData.renderList;
37
- if(cachedData.deviceList) {
38
- cachedData.deviceList.forEach(function(device) {
39
- let newBacnetDevice = new BacnetDevice(true, device);
40
- that.deviceList.push(newBacnetDevice);
41
- });
35
+ if(config.cacheFileEnabled) {
36
+ let cachedData = JSON.parse(Read_Config_Sync());
37
+ if(cachedData && typeof cachedData == "object") {
38
+ if(cachedData.renderList) that.renderList = cachedData.renderList;
39
+ if(cachedData.deviceList) {
40
+ cachedData.deviceList.forEach(function(device) {
41
+ let newBacnetDevice = new BacnetDevice(true, device);
42
+ that.deviceList.push(newBacnetDevice);
43
+ });
44
+ }
45
+ if(cachedData.pointList) that.networkTree = cachedData.pointList;
46
+ if(cachedData.renderListCount) that.renderListCount = cachedData.renderListCount;
42
47
  }
43
- if(cachedData.pointList) that.networkTree = cachedData.pointList;
44
48
  }
45
-
46
-
47
49
 
48
50
  that.config = config;
49
51
  that.roundDecimal = config.roundDecimal;
@@ -59,12 +61,10 @@ class BacnetClient extends EventEmitter {
59
61
  that.deviceRetryCount = parseInt(config.retries);
60
62
 
61
63
  that.readPropertyMultipleOptions = {
62
- maxSegments: that.maxSegments,
63
- maxApdu: that.apduSize
64
+ maxSegments: 112,
65
+ maxApdu: 5
64
66
  };
65
67
 
66
-
67
-
68
68
  try {
69
69
 
70
70
  that.client = new bacnet.Client({ apduTimeout: config.apduTimeout, interface: config.localIpAdrress, port: config.port, broadcastAddress: config.broadCastAddr});
@@ -90,7 +90,9 @@ class BacnetClient extends EventEmitter {
90
90
 
91
91
  //buildNetworkTreeData task
92
92
  const buildNetworkTree = new Task('simple task', () => {
93
- that.buildNetworkTreeData();
93
+ that.buildNetworkTreeData().then(function() {
94
+ that.countDevices();
95
+ });
94
96
  });
95
97
 
96
98
  const buildNetworkTreeJob = new SimpleIntervalJob({ seconds: 10, }, buildNetworkTree)
@@ -102,7 +104,9 @@ class BacnetClient extends EventEmitter {
102
104
  setTimeout(() => {
103
105
  that.queryDevices();
104
106
  that.sanitizeDeviceList();
105
- that.buildNetworkTreeData();
107
+ that.buildNetworkTreeData().then(function() {
108
+ that.countDevices();
109
+ });
106
110
  }, "5000")
107
111
 
108
112
  } catch(e) {
@@ -111,6 +115,7 @@ class BacnetClient extends EventEmitter {
111
115
 
112
116
  //who is callback
113
117
  that.client.on('iAm', (device) => {
118
+ //console.log("found iAm device: ", device);
114
119
  if(device.address !== that.config.localIpAdrress) {
115
120
  if(that.scanMatrix.length > 0) {
116
121
  let matrixMap = that.scanMatrix.filter(ele => device.deviceId >= ele.start && device.deviceId <= ele.end);
@@ -121,12 +126,19 @@ class BacnetClient extends EventEmitter {
121
126
  let newBacnetDevice = new BacnetDevice(false, device);
122
127
  newBacnetDevice.setLastSeen(Date.now());
123
128
  that.updateDeviceName(newBacnetDevice);
129
+ if(newBacnetDevice.getIsMstpDevice()) {
130
+ that.addToParentMstpNetwork(newBacnetDevice);
131
+ }
124
132
  that.deviceList.push(newBacnetDevice);
125
133
 
126
134
  } else if(foundIndex !== -1) {
127
135
  that.deviceList[foundIndex].updateDeviceConfig(device);
128
136
  that.deviceList[foundIndex].setLastSeen(Date.now());
129
137
  that.updateDeviceName(that.deviceList[foundIndex]);
138
+
139
+ if(that.deviceList[foundIndex].getIsMstpDevice()) {
140
+ that.addToParentMstpNetwork(that.deviceList[foundIndex]);
141
+ }
130
142
  }
131
143
 
132
144
  //emit event for node-red to log
@@ -139,11 +151,19 @@ class BacnetClient extends EventEmitter {
139
151
  let newBacnetDevice = new BacnetDevice(false, device);
140
152
  newBacnetDevice.setLastSeen(Date.now());
141
153
  that.updateDeviceName(newBacnetDevice);
154
+ if(newBacnetDevice.getIsMstpDevice()) {
155
+ that.addToParentMstpNetwork(newBacnetDevice);
156
+ }
142
157
  that.deviceList.push(newBacnetDevice);
143
158
  } else if(foundIndex !== -1) {
144
159
  that.deviceList[foundIndex].updateDeviceConfig(device);
145
160
  that.deviceList[foundIndex].setLastSeen(Date.now());
146
161
  that.updateDeviceName(that.deviceList[foundIndex]);
162
+
163
+ if(that.deviceList[foundIndex].getIsMstpDevice()) {
164
+ that.addToParentMstpNetwork(that.deviceList[foundIndex]);
165
+ }
166
+
147
167
  }
148
168
 
149
169
  //emit event for node-red to log
@@ -167,7 +187,24 @@ class BacnetClient extends EventEmitter {
167
187
  that.reinitializeClient(that.config);
168
188
  }
169
189
  });
170
-
190
+ }
191
+
192
+ testFunction() {
193
+ console.log("test function ");
194
+
195
+
196
+
197
+ }
198
+
199
+ addToParentMstpNetwork(device) {
200
+ let that = this;
201
+ let address = device.getAddress().address;
202
+ let deviceId = device.getDeviceId();
203
+ let foundParentIndex = that.deviceList.findIndex(ele => ele.getAddress() == address);
204
+ if(foundParentIndex !== -1) {
205
+ that.deviceList[foundParentIndex].addChildDevice(deviceId);
206
+ device.setParentDeviceId(that.deviceList[foundParentIndex].getDeviceId());
207
+ }
171
208
  }
172
209
 
173
210
  logOut(param1, param2) {
@@ -183,6 +220,7 @@ class BacnetClient extends EventEmitter {
183
220
  that.renderList = [];
184
221
  that.networkTree = {};
185
222
  that.pollInProgress = false;
223
+ that.renderListCount = 0;
186
224
  resolve(true);
187
225
  } catch(e) {
188
226
  that.logOut("Error clearing BACnet data model: ", e);
@@ -197,10 +235,11 @@ class BacnetClient extends EventEmitter {
197
235
  that.pollInProgress = true;
198
236
 
199
237
  let index = 0;
200
-
238
+
201
239
  query(index);
202
240
 
203
241
  function query(index) {
242
+
204
243
  that.queryPriorityDevices().then(function() {
205
244
 
206
245
  let device = that.deviceList[index];
@@ -221,8 +260,17 @@ class BacnetClient extends EventEmitter {
221
260
  });
222
261
  }).catch(function(e) {
223
262
  that.logOut(`getDevicePointList error: ${device.getAddress()}`, e);
224
- that.addDeviceToManualQueue(device);
225
- query(index);
263
+ device.setManualDiscoveryMode(true);
264
+ that.getDevicePointListWithoutObjectList(device).then(function() {
265
+ that.buildJsonObject(device, null).then(function() {
266
+ query(index);
267
+ }).catch(function(e) {
268
+ that.logOut(`getDevicePointList error: ${device.getAddress()}`, e);
269
+ query(index);
270
+ });
271
+ }).catch(function(e){
272
+ query(index);
273
+ });
226
274
  });
227
275
  } catch(e) {
228
276
  that.logOut("Error while querying devices: ", e);
@@ -242,10 +290,10 @@ class BacnetClient extends EventEmitter {
242
290
  } else {
243
291
  that.pollInProgress = false;
244
292
  }
245
-
246
293
  }
247
294
  });
248
295
  }
296
+
249
297
  }
250
298
 
251
299
  queryDevicesManually() {
@@ -444,6 +492,7 @@ class BacnetClient extends EventEmitter {
444
492
  //buildNetworkTreeData task
445
493
  const buildNetworkTree = new Task('simple task', () => {
446
494
  that.buildNetworkTreeData();
495
+ that.countDevices();
447
496
  });
448
497
 
449
498
  const buildNetworkTreeJob = new SimpleIntervalJob({ seconds: 10, }, buildNetworkTree)
@@ -474,11 +523,10 @@ class BacnetClient extends EventEmitter {
474
523
  return newProps;
475
524
  }
476
525
 
477
- doRead(readConfig, outputType, objectPropertyType, msgId) {
526
+ doRead(readConfig, outputType, objectPropertyType, readNodeName) {
478
527
  let that = this;
479
528
  that.roundDecimal = readConfig.precision;
480
529
  let devicesToRead = Object.keys(readConfig.pointsToRead);
481
-
482
530
  try {
483
531
  let bacnetResults = {};
484
532
  devicesToRead.forEach(function(key, index) {
@@ -493,18 +541,54 @@ class BacnetClient extends EventEmitter {
493
541
  let bac_obj = that.getObjectType(readConfig.pointsToRead[key][pointName].meta.objectId.type);
494
542
  let objectId = pointName + "_" + bac_obj + '_' + readConfig.pointsToRead[key][pointName].meta.objectId.instance;
495
543
  let point = deviceObject[objectId];
496
- bacnetResults[deviceName][pointName] = point;
544
+
545
+ that.updatePoint(device, point).then(function(result) {
546
+ if(isNumber(result.values[0].value)) {
547
+ point.presentValue = roundDecimalPlaces(result.values[0].value, that.roundDecimal);
548
+ } else {
549
+ point.presentValue = result.values[0].value;
550
+ }
551
+ bacnetResults[deviceName][pointName] = point;
552
+ }).catch(function(err) {
553
+ //do nothing for now
554
+ });
555
+
497
556
  }
498
557
  }
499
558
  }
500
559
 
501
- if(index == devicesToRead.length - 1 && Object.keys(readConfig.pointsToRead).length > 0) that.emit('values', bacnetResults, outputType, objectPropertyType);
560
+ setTimeout(() => {
561
+ if(index == devicesToRead.length - 1 && Object.keys(readConfig.pointsToRead).length > 0) that.emit('values', bacnetResults, outputType, objectPropertyType, readNodeName);
562
+ }, 3000);
502
563
  });
503
564
  } catch(e) {
504
565
  that.logOut("Issue doing read, see error: ", e);
505
566
  }
506
567
  }
507
568
 
569
+ updatePoint(device, point) {
570
+ let that = this;
571
+ return new Promise((resolve, reject) => {
572
+
573
+ that.client.readProperty(
574
+ device.getAddress(),
575
+ {type: point.objectID.type, instance: point.objectID.instance },
576
+ baEnum.PropertyIdentifier.PRESENT_VALUE,
577
+ {},
578
+ (err, value) => {
579
+ if(err) {
580
+ //console.log("err ", err);
581
+ reject(err);
582
+ }
583
+
584
+ if(value) {
585
+ resolve(value);
586
+ }
587
+ }
588
+ );
589
+ });
590
+ }
591
+
508
592
  getDeviceAddress(device) {
509
593
  switch(typeof device.getAddress()) {
510
594
  case "object":
@@ -615,91 +699,49 @@ class BacnetClient extends EventEmitter {
615
699
  let that = this;
616
700
 
617
701
  return new Promise(function(resolve, reject) {
618
-
619
- let objectNameProperty = [{ id: baEnum.PropertyIdentifier.OBJECT_NAME }];
620
- let address = device.getAddress();
621
- let objectTypeList = [0, 1, 2, 3, 4, 5, 13, 14, 19];
622
- //let objectTypeList = [0, 1, 2, 3, 4, 5, 6, 10, 13, 14, 15, 16, 17, ,19, 20, 56, 178];
623
- //let instanceRange = {start: 0, end: 100};
624
- let instanceRange = device.getmDiscoverInstanceRange();
625
- let requestArray = [];
626
- let maxRequestThreshold = 1000;
627
- let requestRate = 20;
628
- let requestBuffer = [];
629
- let sendBuffer = [];
630
-
631
- if(that.manual_instance_range_enabled == true) {
632
- instanceRange.start = that.manual_instance_range_start;
633
- maxRequestThreshold = that.manual_instance_range_end;
634
- if(that.manual_instance_range_end < requestRate) {
635
- requestRate = that.manual_instance_range_end;
636
- }
637
- }
638
702
 
639
- for(let typeListIndex = 0; typeListIndex < objectTypeList.length; typeListIndex++){
640
- let objectType = objectTypeList[typeListIndex];
641
- for(let i = instanceRange.start; i <= instanceRange.end; i++) {
642
-
643
- requestArray.push({
644
- objectId: { type: objectType, instance: i },
645
- properties: objectNameProperty
646
- })
647
-
648
- if(requestArray.length == requestRate ) {
649
- requestBuffer.push(that._readObjectWithRequestArray(address, requestArray));
650
- instanceRange.end += requestRate;
651
- requestArray = [];
652
- if(i >= maxRequestThreshold) {
653
- if(typeListIndex == objectTypeList.length-1) {
654
- device.setmDiscoverInstanceRange({start: instanceRange.end, end: instanceRange.end + 100})
655
- send();
656
- }
657
-
658
- break;
659
- }
660
- }
661
- }
703
+ let address = device.getAddress();
704
+ let deviceId = device.getDeviceId();
705
+ let discoveredPointList = [];
662
706
 
663
- };
707
+ let index = 1;
664
708
 
665
- function send() {
709
+ send(index);
710
+
666
711
 
667
- for(let index = 0; index < requestBuffer.length; index++) {
668
- let promise = requestBuffer[index];
669
- try {
670
- Promise.resolve(promise).then(function(result) {
671
- for (const [key, value] of Object.entries(result)) {
672
- if(key == "value" && typeof value == "object") {
673
- for(let x = 0; x < value.values.length; x++) {
674
- let ele = value.values[x];
675
- let valueRoot = ele.values[0].value[0];
676
- if(!valueRoot.value.errorClass && !valueRoot.value.errorCode) {
677
- sendBuffer.push({"value": ele.objectId, "type": 12});
678
- }
679
- }
680
- }
681
- }
712
+ function send(index) {
682
713
 
683
- if(index == requestBuffer.length - 1) {
684
- resolve(sendBuffer);
685
- }
714
+ let readOptions = {
715
+ maxSegments: that.readPropertyMultipleOptions.maxSegments,
716
+ maxApdu: that.readPropertyMultipleOptions.maxApdu,
717
+ arrayIndex: index
718
+ }
686
719
 
687
- }).catch(function(error) {
688
- reject(error);
689
- });
690
- } catch(e) {
691
- reject(e)
720
+ that.client.readProperty(
721
+ address,
722
+ {type: baEnum.ObjectType.DEVICE, instance: deviceId },
723
+ baEnum.PropertyIdentifier.OBJECT_LIST,
724
+ readOptions,
725
+ (err, value) => {
726
+ if(err) {
727
+ resolve(discoveredPointList);
728
+ }
729
+
730
+ if(value) {
731
+ discoveredPointList.push(value.values[0]);
732
+ index++;
733
+ send(index);
734
+ }
692
735
  }
693
- }
736
+ );
694
737
  }
695
-
696
738
  });
697
739
  }
698
740
 
699
- _readObjectWithRequestArray(deviceAddress, requestArray) {
741
+ _readObjectWithRequestArray(deviceAddress, requestArray, readOptions) {
700
742
  let that = this;
701
743
  return new Promise((resolve, reject) => {
702
- this.client.readPropertyMultiple(deviceAddress, requestArray, that.readPropertyMultipleOptions, (error, value) => {
744
+ this.client.readPropertyMultiple(deviceAddress, requestArray, readOptions, (error, value) => {
703
745
  resolve({
704
746
  error: error,
705
747
  value: value
@@ -708,7 +750,7 @@ class BacnetClient extends EventEmitter {
708
750
  });
709
751
  }
710
752
 
711
- _readDeviceName(deviceAddress, deviceId, callback){
753
+ _readDeviceName(deviceAddress, deviceId, callback) {
712
754
  let that = this;
713
755
  that.client.readProperty(
714
756
  deviceAddress,
@@ -719,7 +761,7 @@ class BacnetClient extends EventEmitter {
719
761
  );
720
762
  }
721
763
 
722
- _readObjectList(deviceAddress, deviceId, callback) {
764
+ _readObjectList(deviceAddress, deviceId, readOptions, callback) {
723
765
  let that = this;
724
766
 
725
767
  try {
@@ -727,7 +769,7 @@ class BacnetClient extends EventEmitter {
727
769
  deviceAddress,
728
770
  {type: baEnum.ObjectType.DEVICE, instance: deviceId },
729
771
  baEnum.PropertyIdentifier.OBJECT_LIST,
730
- that.readPropertyMultipleOptions,
772
+ readOptions,
731
773
  callback
732
774
  );
733
775
 
@@ -736,14 +778,14 @@ class BacnetClient extends EventEmitter {
736
778
  }
737
779
  }
738
780
 
739
- _readObject(deviceAddress, type, instance, properties) {
781
+ _readObject(deviceAddress, type, instance, properties, readOptions) {
740
782
  let that = this;
741
783
  return new Promise((resolve, reject) => {
742
784
  const requestArray = [{
743
785
  objectId: { type: type, instance: instance },
744
786
  properties: properties
745
787
  }];
746
- this.client.readPropertyMultiple(deviceAddress, requestArray, that.readPropertyMultipleOptions, (error, value) => {
788
+ this.client.readPropertyMultiple(deviceAddress, requestArray, readOptions, (error, value) => {
747
789
  resolve({
748
790
  error: error,
749
791
  value: value
@@ -752,8 +794,12 @@ class BacnetClient extends EventEmitter {
752
794
  });
753
795
  }
754
796
 
755
- _readObjectFull(deviceAddress, type, instance) {
797
+ _readObjectFull(device, deviceAddress, type, instance) {
756
798
  let that = this;
799
+ const readOptions = {
800
+ maxSegments: that.readPropertyMultipleOptions.maxSegments,
801
+ maxApdu: that.readPropertyMultipleOptions.maxApdu
802
+ };
757
803
 
758
804
  const allProperties = [
759
805
  { id: baEnum.PropertyIdentifier.PRESENT_VALUE },
@@ -769,33 +815,71 @@ class BacnetClient extends EventEmitter {
769
815
  ];
770
816
 
771
817
  return new Promise((resolve, reject) => {
772
- that._readObject(deviceAddress, type, instance, [{ id: baEnum.PropertyIdentifier.ALL }]).then(function(result) {
818
+ that._readObject(deviceAddress, type, instance, [{ id: baEnum.PropertyIdentifier.ALL }], readOptions).then(function(result) {
773
819
 
774
820
  if(result.value) {
775
821
  resolve(result);
776
822
  }
777
823
 
778
824
  if(result.error) {
779
- const requestArray = [{
780
- objectId: { type: type, instance: instance },
781
- properties: allProperties
782
- }];
783
-
784
- that._readObjectWithRequestArray(deviceAddress, requestArray).then(function(manualRequest) {
785
- if(manualRequest.value) {
786
- resolve(manualRequest);
787
- }
788
- if(manualRequest.error) {
789
- reject(manualRequest.error);
790
- }
791
- })
825
+ let i = 0;
826
+ readIndividualProperties(i);
792
827
  }
793
828
 
794
829
  }).catch(function(error) {
795
- reject(error);
830
+ let i = 0;
831
+ readIndividualProperties(i);
796
832
  });
797
- });
798
833
 
834
+ let resultArray = [];
835
+ let errorArray = [];
836
+
837
+ function readIndividualProperties(index) {
838
+
839
+ const property = allProperties[index];
840
+
841
+ that.client.readProperty(
842
+ deviceAddress,
843
+ {type: type, instance: instance },
844
+ property.id,
845
+ readOptions,
846
+ (err, value) => {
847
+ if(err) {
848
+ errorArray.push(err);
849
+ }
850
+
851
+ if(value) {
852
+ let formattedResult = {
853
+ len: value.len,
854
+ objectId: value.objectId,
855
+ values: [
856
+ {
857
+ id: value.property.id,
858
+ index: value.property.index,
859
+ value: value.values
860
+ }
861
+ ]
862
+ };
863
+
864
+ resultArray.push({error: null, value: formattedResult});
865
+ }
866
+
867
+ if(index == allProperties.length - 1) {
868
+ resolve(resultArray);
869
+
870
+ // if(resultArray.length > 0) {
871
+ // resolve(resultArray);
872
+ // } else if(errorArray.length > 0){
873
+ // reject(errorArray);
874
+ // }
875
+ } else if( index < allProperties.length - 1) {
876
+ index++;
877
+ readIndividualProperties(index);
878
+ }
879
+ }
880
+ );
881
+ };
882
+ });
799
883
  };
800
884
 
801
885
  _readObjectPropList(deviceAddress, type, instance) {
@@ -823,21 +907,15 @@ class BacnetClient extends EventEmitter {
823
907
  doWrite(value, options){
824
908
  let that = this;
825
909
  let valuesArray = [];
826
-
827
910
  options.pointsToWrite.forEach(function(point){
828
-
829
911
  let deviceAddress = point.deviceAddress;
830
-
831
- if(valuesArray[deviceAddress] == null || valuesArray[deviceAddress] == undefined){
832
- valuesArray[deviceAddress] = [];
833
- }
834
-
835
912
  let writeObject = {
913
+ address: deviceAddress,
836
914
  objectId: {
837
915
  type: point.meta.objectId.type,
838
916
  instance: point.meta.objectId.instance
839
917
  },
840
- values: [{
918
+ values: {
841
919
  property: {
842
920
  id: 85,
843
921
  index: point.meta.arrayIndex
@@ -846,11 +924,17 @@ class BacnetClient extends EventEmitter {
846
924
  type: options.appTag,
847
925
  value: value
848
926
  }],
927
+
928
+ },
929
+ options: {
930
+ maxSegments: that.readPropertyMultipleOptions.maxSegments,
931
+ maxApdu: that.readPropertyMultipleOptions.maxApdu,
932
+ arrayIndex: point.meta.arrayIndex,
849
933
  priority: options.priority
850
- }]
934
+ }
851
935
  };
852
936
 
853
- valuesArray[deviceAddress].push(writeObject);
937
+ valuesArray.push(writeObject);
854
938
  });
855
939
 
856
940
  return that._writePropertyMultiple(valuesArray);
@@ -858,24 +942,23 @@ class BacnetClient extends EventEmitter {
858
942
 
859
943
  _writePropertyMultiple(values) {
860
944
  let that = this;
861
- let writePromises = [];
862
945
  try {
863
- return new Promise((resolve, reject) => {
864
- for(const device in values) {
865
- writePromises.push(that.client.writePropertyMultiple(device, values[device], that.readPropertyMultipleOptions, (err, value) => {
866
- resolve({
867
- error: err,
868
- value: value
869
- })}
870
- ));
871
- }
872
-
873
- Promise.all(writePromises).then(function(result) {
874
- resolve(result);
875
- }).catch(function(e) {
876
- that.logOut("Error writing: ", e);
877
- });
946
+ values.forEach(function(point) {
947
+
948
+ that.client.writeProperty(
949
+ point.address,
950
+ point.objectId,
951
+ baEnum.PropertyIdentifier.PRESENT_VALUE,
952
+ point.values.value,
953
+ point.options,
954
+ (err, value) => {
955
+ if(err) {
956
+ that.logOut(err);
957
+ }
958
+ }
959
+ );
878
960
  });
961
+
879
962
  } catch (error) {
880
963
  that.logOut(error);
881
964
  }
@@ -895,7 +978,11 @@ class BacnetClient extends EventEmitter {
895
978
  scanDevice(device) {
896
979
  let that = this;
897
980
  return new Promise((resolve, reject) => {
898
- this._readObjectList(device.getAddress(), device.getDeviceId(), (err, result) => {
981
+ const readOptions = {
982
+ maxSegments: that.readPropertyMultipleOptions.maxSegments,
983
+ maxApdu: that.readPropertyMultipleOptions.maxApdu
984
+ };
985
+ this._readObjectList(device.getAddress(), device.getDeviceId(), readOptions, (err, result) => {
899
986
  if (!err) {
900
987
  try {
901
988
  resolve(result.values);
@@ -937,7 +1024,7 @@ class BacnetClient extends EventEmitter {
937
1024
  reducedDeviceList.forEach((device) => {
938
1025
  delete device["pointsList"];
939
1026
  });
940
- resolve({renderList: that.renderList, deviceList: reducedDeviceList, pointList: that.networkTree, pollFrequency: that.discover_polling_schedule});
1027
+ resolve({renderList: that.renderList, deviceList: reducedDeviceList, pointList: that.networkTree, pollFrequency: that.discover_polling_schedule, renderListCount: that.renderListCount});
941
1028
  } catch(e){
942
1029
  reject(e);
943
1030
  }
@@ -963,7 +1050,7 @@ class BacnetClient extends EventEmitter {
963
1050
  deviceL.forEach(function(device) {
964
1051
  let foundIndex = that.deviceList.findIndex(ele => ele.getDeviceId() == device.deviceId);
965
1052
  if(foundIndex == -1) {
966
- let newBacnetDevice = new BacnetDevice(false, device);
1053
+ let newBacnetDevice = new BacnetDevice(true, device);
967
1054
  newBacnetDevice.setLastSeen(Date.now());
968
1055
  that.deviceList.push(newBacnetDevice);
969
1056
 
@@ -1042,7 +1129,7 @@ class BacnetClient extends EventEmitter {
1042
1129
  that.buildTreeMutex = new Mutex();
1043
1130
  let displayNameCharThreshold = 40;
1044
1131
 
1045
- Store_Config(JSON.stringify({renderList: that.renderList, deviceList: that.deviceList, pointList: that.networkTree}));
1132
+ Store_Config(JSON.stringify({renderList: that.renderList, deviceList: that.deviceList, pointList: that.networkTree, renderListCount: that.renderListCount}));
1046
1133
 
1047
1134
  return new Promise(async function(resolve, reject) {
1048
1135
  if(!that.renderList) that.renderList = [];
@@ -1067,6 +1154,7 @@ class BacnetClient extends EventEmitter {
1067
1154
  for(const pointName in deviceObject) {
1068
1155
  let pointProperties = [];
1069
1156
  let values = deviceObject[pointName];
1157
+
1070
1158
  let displayName = pointName;
1071
1159
  if(pointName.length > displayNameCharThreshold) {
1072
1160
  displayName = "";
@@ -1080,45 +1168,150 @@ class BacnetClient extends EventEmitter {
1080
1168
  }
1081
1169
 
1082
1170
  if(values.objectName){
1083
- pointProperties.push({"key": `${index}-${pointIndex}-0`, "label": `Name: ${values.objectName}`, "data": values.objectName, "icon": "pi pi-bolt", "children": null});
1171
+ pointProperties.push({"key": `${index}-0-${pointIndex}-0`, "label": `Name: ${values.objectName}`, "data": values.objectName, "icon": "pi pi-cog", "children": null});
1084
1172
  }
1085
- if(values.objectType){
1086
- pointProperties.push({"key": `${index}-${pointIndex}-1`, "label": `Object Type: ${values.objectType}`, "data": values.objectType, "icon": "pi pi-bolt", "children": null});
1173
+ if(values.objectType && values.objectID.type !== 8){
1174
+ pointProperties.push({"key": `${index}-0-${pointIndex}-1`, "label": `Object Type: ${values.objectType}`, "data": values.objectType, "icon": "pi pi-cog", "children": null});
1087
1175
  }
1088
1176
  if(values.objectID && values.objectID.instance) {
1089
- pointProperties.push({"key": `${index}-${pointIndex}-2`, "label": `Object Instance: ${values.objectID.instance}`, "data": values.objectID.instance, "icon": "pi pi-bolt", "children": null});
1177
+ pointProperties.push({"key": `${index}-0-${pointIndex}-2`, "label": `Object Instance: ${values.objectID.instance}`, "data": values.objectID.instance, "icon": "pi pi-cog", "children": null});
1090
1178
  }
1091
1179
  if(values.description){
1092
- pointProperties.push({"key": `${index}-${pointIndex}-3`, "label": `Description: ${values.description}`, "data": `${values.description}`, "icon": "pi pi-bolt", "children": null});
1180
+ pointProperties.push({"key": `${index}-0-${pointIndex}-3`, "label": `Description: ${values.description}`, "data": `${values.description}`, "icon": "pi pi-cog", "children": null});
1093
1181
  }
1094
1182
  if(values.units){
1095
- pointProperties.push({"key": `${index}-${pointIndex}-4`, "label": `Units: ${values.units}`, "data": `${values.units}`, "icon": "pi pi-bolt", "children": null});
1183
+ pointProperties.push({"key": `${index}-0-${pointIndex}-4`, "label": `Units: ${values.units}`, "data": `${values.units}`, "icon": "pi pi-cog", "children": null});
1096
1184
  }
1097
1185
  if(values.presentValue !== "undefined" && values.presentValue !== null && typeof values.presentValue !== "undefined") {
1098
- pointProperties.push({"key": `${index}-${pointIndex}-5`, "label": `Present Value: ${values.presentValue}`, "data": `${values.presentValue}`, "icon": "pi pi-bolt", "children": null});
1186
+ pointProperties.push({"key": `${index}-0-${pointIndex}-5`, "label": `Present Value: ${values.presentValue}`, "data": `${values.presentValue}`, "icon": "pi pi-cog", "children": null});
1099
1187
  }
1100
1188
  if(values.systemStatus !== null && typeof values.systemStatus !== "undefined" && values.systemStatus !== ""){
1101
- pointProperties.push({"key": `${index}-${pointIndex}-6`, "label": `System Status: ${values.systemStatus}`, "data": `${values.systemStatus}`, "icon": "pi pi-bolt", "children": null});
1189
+ pointProperties.push({"key": `${index}-0-${pointIndex}-6`, "label": `System Status: ${values.systemStatus}`, "data": `${values.systemStatus}`, "icon": "pi pi-cog", "children": null});
1102
1190
  }
1103
1191
  if(values.modificationDate && !values.modificationDate.errorClass) {
1104
- pointProperties.push({"key": `${index}-${pointIndex}-7`, "label": `Modification Date: ${values.modificationDate}`, "data": `${values.modificationDate}`, "icon": "pi pi-bolt", "children": null});
1192
+ pointProperties.push({"key": `${index}-0-${pointIndex}-7`, "label": `Modification Date: ${values.modificationDate}`, "data": `${values.modificationDate}`, "icon": "pi pi-cog", "children": null});
1105
1193
  }
1106
1194
  if(values.programState){
1107
- pointProperties.push({"key": `${index}-${pointIndex}-8`, "label": `Program State: ${values.programState}`, "data": `${values.programState}`, "icon": "pi pi-bolt", "children": null});
1195
+ pointProperties.push({"key": `${index}-0-${pointIndex}-8`, "label": `Program State: ${values.programState}`, "data": `${values.programState}`, "icon": "pi pi-cog", "children": null});
1108
1196
  }
1109
1197
  if(values.recordCount && !values.recordCount.errorClass){
1110
- pointProperties.push({"key": `${index}-${pointIndex}-9`, "label": `Record Count: ${values.recordCount}`, "data": `${values.recordCount}`, "icon": "pi pi-bolt", "children": null});
1198
+ pointProperties.push({"key": `${index}-0-${pointIndex}-9`, "label": `Record Count: ${values.recordCount}`, "data": `${values.recordCount}`, "icon": "pi pi-cog", "children": null});
1199
+ }
1200
+ if(values.objectID && values.objectID.type == 8) {
1201
+ //device point, add segmentation supported, and apdu size
1202
+ pointProperties.push({"key": `${index}-0-${pointIndex}-10`, "label": `Segmentation Supported: ${deviceInfo.getSegmentation()}`, "data": `${deviceInfo.getSegmentation()}`, "icon": "pi pi-cog", "children": null});
1203
+ pointProperties.push({"key": `${index}-0-${pointIndex}-11`, "label": `APDU Size: ${deviceInfo.getMaxApdu()}`, "data": `${deviceInfo.getMaxApdu()}`, "icon": "pi pi-cog", "children": null});
1111
1204
  }
1112
1205
 
1113
- children.push({"key": `${index}-${pointIndex}`, "label": displayName, "data": displayName, "pointName": pointName, "icon": that.getPointIcon(values.meta.objectId.type), "children": pointProperties, "type": "point", "parentDevice": deviceName, "showAdded": false, "bacnetType": values.meta.objectId.type})
1206
+ children.push({"key": `${index}-0-${pointIndex}`, "label": displayName, "data": displayName, "pointName": pointName, "icon": that.getPointIcon(values.meta.objectId.type), "children": pointProperties, "type": "point", "parentDevice": deviceName, "showAdded": false, "bacnetType": values.meta.objectId.type})
1114
1207
  pointIndex++;
1115
1208
  }
1116
1209
  let foundIndex = that.renderList.findIndex(ele => ele.deviceId == deviceId && ele.ipAddr == ipAddr);
1117
1210
  if(foundIndex !== -1) {
1118
- that.renderList[foundIndex] = {"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice, manualDiscoveryMode), "children": children.sort(that.sortPoints), "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice};
1211
+ let folderJson = [];
1212
+ if(deviceInfo.hasChildDevices()) {
1213
+ folderJson = [
1214
+ {
1215
+ key: `${index}-0`,
1216
+ label: "Points",
1217
+ data: "Points Folder",
1218
+ icon: "pi pi-circle-fill",
1219
+ type: "pointFolder",
1220
+ children: children.sort(that.sortPoints)
1221
+ },
1222
+ {
1223
+ key: `${index}-1`,
1224
+ label: "MSTP Network",
1225
+ data: "Devices Folder",
1226
+ icon: "pi pi-database",
1227
+ type: "deviceFolder",
1228
+ children: []
1229
+ }
1230
+ ];
1231
+ } else {
1232
+ folderJson = [
1233
+ {
1234
+ key: `${index}-0`,
1235
+ label: "Points",
1236
+ data: "Points Folder",
1237
+ icon: "pi pi-circle-fill",
1238
+ type: "pointFolder",
1239
+ children: children.sort(that.sortPoints)
1240
+ }
1241
+ ];
1242
+ }
1243
+
1244
+ if(!isMstpDevice) {
1245
+ that.renderList[foundIndex] = {"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice, manualDiscoveryMode), "children": that.renderList[foundIndex].children, "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice};
1246
+ } else if(isMstpDevice) {
1247
+ let parentDeviceId = deviceInfo.getParentDeviceId();
1248
+ let parentDeviceIndex = that.renderList.findIndex(ele => ele.deviceId == parentDeviceId && ele.ipAddr == ipAddr);
1249
+
1250
+ if(parentDeviceIndex !== -1 && that.renderList[parentDeviceIndex].children[1].children) {
1251
+ let mstpDeviceIndex = that.renderList[parentDeviceIndex].children[1].children.findIndex(ele => ele.deviceId == deviceId && ele.ipAddr == ipAddr);
1252
+ if(mstpDeviceIndex == -1) {
1253
+ //that.renderListCount++;
1254
+ that.renderList[parentDeviceIndex].children[1].children.push({"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice, manualDiscoveryMode), "children": folderJson, "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice});
1255
+ } else {
1256
+ that.renderList[parentDeviceIndex].children[1].children[mstpDeviceIndex] = {"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice, manualDiscoveryMode), "children": folderJson, "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice};
1257
+ }
1258
+ }
1259
+ }
1260
+
1119
1261
  } else if(foundIndex == -1) {
1120
- that.renderList.push({"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice, manualDiscoveryMode), "children": children.sort(that.sortPoints), "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice});
1262
+ let folderJson = [];
1263
+ if(deviceInfo.hasChildDevices()) {
1264
+ folderJson = [
1265
+ {
1266
+ key: `${index}-0`,
1267
+ label: "Points",
1268
+ data: "Points Folder",
1269
+ icon: "pi pi-circle-fill",
1270
+ type: "pointFolder",
1271
+ children: children.sort(that.sortPoints)
1272
+ },
1273
+ {
1274
+ key: `${index}-1`,
1275
+ label: "MSTP Network",
1276
+ data: "Devices Folder",
1277
+ icon: "pi pi-database",
1278
+ type: "deviceFolder",
1279
+ children: []
1280
+ }
1281
+ ];
1282
+ } else {
1283
+ folderJson = [
1284
+ {
1285
+ key: `${index}-0`,
1286
+ label: "Points",
1287
+ data: "Points Folder",
1288
+ icon: "pi pi-circle-fill",
1289
+ type: "pointFolder",
1290
+ children: children.sort(that.sortPoints)
1291
+ }
1292
+ ];
1293
+ }
1294
+
1295
+ if(!isMstpDevice) {
1296
+ //that.renderListCount++;
1297
+ that.renderList.push({"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice, manualDiscoveryMode), "children": folderJson, "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice});
1298
+ } else if(isMstpDevice) {
1299
+ let parentDeviceId = deviceInfo.getParentDeviceId();
1300
+ let parentDeviceIndex = that.renderList.findIndex(ele => ele.deviceId == parentDeviceId && ele.ipAddr == ipAddr);
1301
+
1302
+ if(parentDeviceIndex !== -1 && that.renderList[parentDeviceIndex].children && that.renderList[parentDeviceIndex].children[1].children) {
1303
+ let mstpDeviceIndex = that.renderList[parentDeviceIndex].children[1].children.findIndex(ele => ele.deviceId == deviceId && ele.ipAddr == ipAddr);
1304
+
1305
+ if(mstpDeviceIndex == -1) {
1306
+ // that.renderListCount++;
1307
+ that.renderList[parentDeviceIndex].children[1].children.push({"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice, manualDiscoveryMode), "children": folderJson, "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice});
1308
+ } else {
1309
+ that.renderList[parentDeviceIndex].children[1].children[mstpDeviceIndex] = {"key": index, "label": deviceName, "data": deviceName, "icon": that.getDeviceIcon(isMstpDevice, manualDiscoveryMode), "children": folderJson, "type": "device", "lastSeen": deviceInfo.getLastSeen(), "showAdded": false, "ipAddr": ipAddr, "deviceId": deviceId, "isMstpDevice": isMstpDevice};
1310
+ }
1311
+ }
1312
+ }
1121
1313
  }
1314
+
1122
1315
  if(index == that.deviceList.length - 1) {
1123
1316
  that.renderList.sort(that.sortDevices);
1124
1317
  resolve({renderList: that.renderList, deviceList: that.deviceList, pointList: that.networkTree, pollFrequency: that.discover_polling_schedule});
@@ -1132,12 +1325,33 @@ class BacnetClient extends EventEmitter {
1132
1325
 
1133
1326
  release();
1134
1327
  });
1135
-
1136
1328
  });
1137
1329
  }
1138
1330
  });
1139
1331
  }
1140
1332
 
1333
+ countDevices() {
1334
+ let that = this;
1335
+
1336
+ let deviceCount = 0;
1337
+
1338
+ that.renderList.forEach(function(device, index) {
1339
+ if(device) {
1340
+ if(device.children[1] && device.children[1].label == 'MSTP Network' && device.children[1].children && device.children[1].children.length > 0) {
1341
+ //increment for parent device / mstp router
1342
+ deviceCount++;
1343
+ //increment for mstp device list
1344
+ deviceCount += device.children[1].children.length;
1345
+ } else {
1346
+ deviceCount++;
1347
+ }
1348
+ }
1349
+ if(index == that.renderList.length -1) {
1350
+ that.renderListCount = deviceCount;
1351
+ }
1352
+ });
1353
+ }
1354
+
1141
1355
  buildJsonObject(device, priorityQueue) {
1142
1356
  let that = this;
1143
1357
  let address = device.address;
@@ -1151,10 +1365,14 @@ class BacnetClient extends EventEmitter {
1151
1365
  requestMutex
1152
1366
  .acquire()
1153
1367
  .then(function(release) {
1154
- that._readObjectFull(address, point.value.type, point.value.instance).then(function(result) {
1368
+ that._readObjectFull(device, address, point.value.type, point.value.instance).then(function(result) {
1155
1369
 
1156
1370
  if(!result.error) {
1157
- promiseArray.push(result);
1371
+ if(result.length > 0 && Array.isArray(result)) {
1372
+ promiseArray = result;
1373
+ } else {
1374
+ promiseArray.push(result);
1375
+ }
1158
1376
  }
1159
1377
 
1160
1378
  release();
@@ -1203,6 +1421,11 @@ class BacnetClient extends EventEmitter {
1203
1421
  let successfulResult = !obj.error ? obj.value : null;
1204
1422
  if(successfulResult) {
1205
1423
  successfulResult.values.forEach(function(pointProperty, pointPropertyIndex) {
1424
+
1425
+ if(!pointProperty.objectId && successfulResult.objectId && !pointProperty.values && successfulResult.values) {
1426
+ pointProperty = successfulResult;
1427
+ }
1428
+
1206
1429
  let currobjectId = pointProperty.objectId.type
1207
1430
  let bac_obj = that.getObjectType(currobjectId);
1208
1431
  let objectName = that._findValueById(pointProperty.values, baEnum.PropertyIdentifier.OBJECT_NAME);
@@ -1358,34 +1581,34 @@ class BacnetClient extends EventEmitter {
1358
1581
  switch(objectId) {
1359
1582
  case 0:
1360
1583
  //AI
1361
- return "pi pi-tags";
1584
+ return "pi pi-circle";
1362
1585
  case 1:
1363
1586
  //AO
1364
- return "pi pi-tags";
1587
+ return "pi pi-circle";
1365
1588
  case 2:
1366
1589
  //AV
1367
- return "pi pi-tags";
1590
+ return "pi pi-circle";
1368
1591
  case 3:
1369
1592
  //BI
1370
- return "pi pi-tags";
1593
+ return "pi pi-circle";
1371
1594
  case 4:
1372
1595
  //BO
1373
- return "pi pi-tags";
1596
+ return "pi pi-circle";
1374
1597
  case 5:
1375
1598
  //BV
1376
- return "pi pi-tags";
1599
+ return "pi pi-circle";
1377
1600
  case 8:
1378
1601
  //Device
1379
1602
  return "pi pi-box";
1380
1603
  case 13:
1381
1604
  //MI
1382
- return "pi pi-tags";
1605
+ return "pi pi-circle";
1383
1606
  case 14:
1384
1607
  //MO
1385
- return "pi pi-tags";
1608
+ return "pi pi-circle";
1386
1609
  case 19:
1387
1610
  //MV
1388
- return "pi pi-tags";
1611
+ return "pi pi-circle";
1389
1612
  case 10:
1390
1613
  //File
1391
1614
  return "pi pi-file";
@@ -1408,7 +1631,7 @@ class BacnetClient extends EventEmitter {
1408
1631
  return "pi pi-calendar";
1409
1632
  default:
1410
1633
  //Return circle for all other types
1411
- return "pi pi-tags";
1634
+ return "pi pi-circle";
1412
1635
  }
1413
1636
  }
1414
1637
 
@@ -1482,16 +1705,14 @@ class BacnetClient extends EventEmitter {
1482
1705
 
1483
1706
  getDeviceIcon(isMstp, manualDiscoveryMode) {
1484
1707
  if(manualDiscoveryMode == true) {
1485
- //return "pi pi-server"
1486
- return "pi pi-exclamation-triangle"
1708
+ return "pi pi-question-circle"
1487
1709
  } else if(manualDiscoveryMode == false) {
1488
1710
  if(isMstp == true) {
1489
- return "pi pi-share-alt"
1711
+ return "pi pi-box"
1490
1712
  } else if(isMstp == false) {
1491
1713
  return "pi pi-server"
1492
1714
  }
1493
1715
  }
1494
-
1495
1716
  return "pi pi-server";
1496
1717
  };
1497
1718
  }