@bitpoolos/edge-bacnet 1.4.0 → 1.4.2
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 +29 -0
- package/README.md +2 -0
- package/bacnet_client.js +1 -1
- package/bacnet_gateway.js +151 -30
- package/bacnet_read.html +95 -62
- package/bacnet_read.js +40 -5
- package/bacnet_server.js +3 -1
- package/bitpool_inject.html +150 -1
- package/bitpool_inject.js +42 -24
- package/package.json +1 -1
- package/treeBuilder.js +53 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.2] - 23-07-2024
|
|
4
|
+
### Summary
|
|
5
|
+
|
|
6
|
+
Improved UI tree generation, fixing some unique scenarios where devices were not being correctly added.
|
|
7
|
+
|
|
8
|
+
Added new payload type: Individual JSON. This publishes JSON payloads to a point level, rather than to a device or property level.
|
|
9
|
+
|
|
10
|
+
Added payload and output types to be configured via Inject node. Any checked options in the inject node will take priority over read node options. Note - to use read node output types only, please deselect all inject node output options.
|
|
11
|
+
|
|
12
|
+
Added dumb BACnet parent devices to UI tree, for unique situations where MSTP devices are on a network without a parent IP device.
|
|
13
|
+
|
|
14
|
+
Bug fixes:
|
|
15
|
+
- Importing read list was incorrectly generating UI
|
|
16
|
+
- Block per device JSON payloads were not using DisplayName and JSON key
|
|
17
|
+
- Added node-red context variable to monitor if writeProperty event has been subscribed to, avoiding a new subscription for every node-red deploy.
|
|
18
|
+
- Fixed issue requiring Bacnet server to be constantly enabled in order for the node to function.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
No nodes need to be deleted and replaced for this update.
|
|
22
|
+
|
|
23
|
+
## [1.4.1] - 09-07-2024
|
|
24
|
+
|
|
25
|
+
### Summary
|
|
26
|
+
|
|
27
|
+
Bug fixes
|
|
28
|
+
- incorrect variable used in doRead try catch
|
|
29
|
+
- setmaxlisteners on bacnet server event parent class
|
|
30
|
+
|
|
31
|
+
|
|
3
32
|
## [1.4.0] - 05-07-2024
|
|
4
33
|
|
|
5
34
|
### Summary
|
package/README.md
CHANGED
|
@@ -39,6 +39,8 @@ $ npm install @bitpoolos/edge-bacnet
|
|
|
39
39
|
The module can be updated via the Node-RED pallette manager, or via the npm cli.
|
|
40
40
|
|
|
41
41
|
```javascript
|
|
42
|
+
Note: the following is our reccommendation based on error encountered in our experience. The below steps are mostly critical for updates that modify or add new properties to the UI of a node. Refer to comments in changelog.
|
|
43
|
+
|
|
42
44
|
Upon updating to the latest version, we highly recommend:
|
|
43
45
|
- Check out the changelog for latest feature notes and updates
|
|
44
46
|
- Remove all @bitpoolos/edge-bacnet nodes from all flows
|
package/bacnet_client.js
CHANGED
|
@@ -773,7 +773,7 @@ class BacnetClient extends EventEmitter {
|
|
|
773
773
|
let pointRef = request.pointRef;
|
|
774
774
|
pointRef.status = "offline";
|
|
775
775
|
|
|
776
|
-
bacnetResults[deviceName][
|
|
776
|
+
bacnetResults[deviceName][request.pointName] = pointRef;
|
|
777
777
|
});
|
|
778
778
|
|
|
779
779
|
}
|
package/bacnet_gateway.js
CHANGED
|
@@ -45,8 +45,6 @@ module.exports = function (RED) {
|
|
|
45
45
|
//determines whether or not to log a found device on whoIs response
|
|
46
46
|
this.toLogIam = config.toLogIam;
|
|
47
47
|
|
|
48
|
-
this.websocketListener = null;
|
|
49
|
-
|
|
50
48
|
node.bacnetConfig = new BacnetClientConfig(
|
|
51
49
|
node.apduTimeout,
|
|
52
50
|
node.localDeviceAddress,
|
|
@@ -75,6 +73,8 @@ module.exports = function (RED) {
|
|
|
75
73
|
} else {
|
|
76
74
|
node.bacnetClient = new BacnetClient(node.bacnetConfig);
|
|
77
75
|
nodeContext.set("bacnetClient", node.bacnetClient);
|
|
76
|
+
|
|
77
|
+
nodeContext.set("serverWritePropEvent", false);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
node.bacnetClient.scanMatrix = node.deviceRangeRegisters.filter((ele) => ele.enabled === true);
|
|
@@ -107,7 +107,7 @@ module.exports = function (RED) {
|
|
|
107
107
|
node.status({});
|
|
108
108
|
}, 3000);
|
|
109
109
|
}
|
|
110
|
-
if (outputType.json && !outputType.mqtt) {
|
|
110
|
+
if (outputType.json && !outputType.mqtt && !outputType.pointJson) {
|
|
111
111
|
//json
|
|
112
112
|
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
|
|
113
113
|
//simpleWithStatus
|
|
@@ -119,7 +119,7 @@ module.exports = function (RED) {
|
|
|
119
119
|
//simplePayload
|
|
120
120
|
sendSimpleJson(values, readNodeName);
|
|
121
121
|
}
|
|
122
|
-
} else if (!outputType.json && outputType.
|
|
122
|
+
} else if (outputType.mqtt && !outputType.json && !outputType.pointJson) {
|
|
123
123
|
//mqtt
|
|
124
124
|
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
|
|
125
125
|
//simpleWithStatus
|
|
@@ -131,6 +131,18 @@ module.exports = function (RED) {
|
|
|
131
131
|
//simplePayload
|
|
132
132
|
sendSimpleMqtt(values, readNodeName);
|
|
133
133
|
}
|
|
134
|
+
} else if (outputType.pointJson && !outputType.json && !outputType.mqtt) {
|
|
135
|
+
//pointJson
|
|
136
|
+
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
|
|
137
|
+
//simpleWithStatus
|
|
138
|
+
sendSimpleWithStatus(values, readNodeName, false);
|
|
139
|
+
} else if (objectPropertyType.fullObject && !objectPropertyType.simplePayload && !objectPropertyType.simpleWithStatus) {
|
|
140
|
+
//fullObject
|
|
141
|
+
sendIndividualMsgJson(values, readNodeName);
|
|
142
|
+
} else if (objectPropertyType.simplePayload && !objectPropertyType.fullObject && !objectPropertyType.simpleWithStatus) {
|
|
143
|
+
//simplePayload
|
|
144
|
+
sendSimpleJsonPerPoint(values, readNodeName);
|
|
145
|
+
}
|
|
134
146
|
}
|
|
135
147
|
}
|
|
136
148
|
});
|
|
@@ -164,22 +176,25 @@ module.exports = function (RED) {
|
|
|
164
176
|
}
|
|
165
177
|
}
|
|
166
178
|
|
|
167
|
-
node.bacnetServer.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
179
|
+
if (node.bacnetServerEnabled == true && node.bacnetClient && node.bacnetServer && nodeContext.get("serverWritePropEvent") == false) {
|
|
180
|
+
node.bacnetServer.on("writeProperty", (topic, newValue) => {
|
|
181
|
+
let formattedTopic = topic;
|
|
182
|
+
if (
|
|
183
|
+
node.nodeName !== "gateway" &&
|
|
184
|
+
node.nodeName !== "" &&
|
|
185
|
+
node.nodeName !== "null" &&
|
|
186
|
+
node.nodeName !== "undefined" &&
|
|
187
|
+
typeof node.nodeName == "string"
|
|
188
|
+
) {
|
|
189
|
+
formattedTopic = `${node.nodeName}/BITPOOL_EDGE_BACNET_GATEWAY/BACNET_SERVER/${topic}`;
|
|
190
|
+
} else {
|
|
191
|
+
formattedTopic = `BITPOOL_EDGE_BACNET_GATEWAY/BACNET_SERVER/${topic}`;
|
|
192
|
+
}
|
|
180
193
|
|
|
181
|
-
|
|
182
|
-
|
|
194
|
+
node.send({ payload: newValue, topic: formattedTopic });
|
|
195
|
+
});
|
|
196
|
+
nodeContext.set("serverWritePropEvent", true);
|
|
197
|
+
}
|
|
183
198
|
|
|
184
199
|
node.on("input", function (msg) {
|
|
185
200
|
if (msg.topic && msg.payload !== null) {
|
|
@@ -441,7 +456,7 @@ module.exports = function (RED) {
|
|
|
441
456
|
node.status({});
|
|
442
457
|
}, 3000);
|
|
443
458
|
}
|
|
444
|
-
if (outputType.json && !outputType.mqtt) {
|
|
459
|
+
if (outputType.json && !outputType.mqtt && !outputType.pointJson) {
|
|
445
460
|
//json
|
|
446
461
|
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
|
|
447
462
|
//simpleWithStatus
|
|
@@ -453,7 +468,7 @@ module.exports = function (RED) {
|
|
|
453
468
|
//simplePayload
|
|
454
469
|
sendSimpleJson(values, readNodeName);
|
|
455
470
|
}
|
|
456
|
-
} else if (!outputType.json && outputType.
|
|
471
|
+
} else if (outputType.mqtt && !outputType.json && !outputType.pointJson) {
|
|
457
472
|
//mqtt
|
|
458
473
|
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
|
|
459
474
|
//simpleWithStatus
|
|
@@ -465,6 +480,18 @@ module.exports = function (RED) {
|
|
|
465
480
|
//simplePayload
|
|
466
481
|
sendSimpleMqtt(values, readNodeName);
|
|
467
482
|
}
|
|
483
|
+
} else if (outputType.pointJson && !outputType.json && !outputType.mqtt) {
|
|
484
|
+
//pointJson
|
|
485
|
+
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
|
|
486
|
+
//simpleWithStatus
|
|
487
|
+
sendSimpleWithStatus(values, readNodeName, false);
|
|
488
|
+
} else if (objectPropertyType.fullObject && !objectPropertyType.simplePayload && !objectPropertyType.simpleWithStatus) {
|
|
489
|
+
//fullObject
|
|
490
|
+
sendIndividualMsgJson(values, readNodeName);
|
|
491
|
+
} else if (objectPropertyType.simplePayload && !objectPropertyType.fullObject && !objectPropertyType.simpleWithStatus) {
|
|
492
|
+
//simplePayload
|
|
493
|
+
sendSimpleJsonPerPoint(values, readNodeName);
|
|
494
|
+
}
|
|
468
495
|
}
|
|
469
496
|
}
|
|
470
497
|
});
|
|
@@ -487,13 +514,6 @@ module.exports = function (RED) {
|
|
|
487
514
|
}
|
|
488
515
|
}
|
|
489
516
|
|
|
490
|
-
function getPointName(object, pointName) {
|
|
491
|
-
if (object.displayName) {
|
|
492
|
-
return object.displayName;
|
|
493
|
-
}
|
|
494
|
-
return pointName;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
517
|
sendSimpleWithStatus = function (values, readNodeName, isJson) {
|
|
498
518
|
let devices = Object.keys(values);
|
|
499
519
|
devices.forEach(function (device) {
|
|
@@ -739,6 +759,7 @@ module.exports = function (RED) {
|
|
|
739
759
|
keys.forEach(function (key) {
|
|
740
760
|
let points = values[key];
|
|
741
761
|
let msgg = {};
|
|
762
|
+
let structuredObject = {};
|
|
742
763
|
if (
|
|
743
764
|
node.nodeName !== "gateway" &&
|
|
744
765
|
node.nodeName !== "" &&
|
|
@@ -764,12 +785,112 @@ module.exports = function (RED) {
|
|
|
764
785
|
msgg.topic = `BITPOOL_BACNET_GATEWAY/${key}`;
|
|
765
786
|
}
|
|
766
787
|
}
|
|
767
|
-
|
|
788
|
+
for (let point in points) {
|
|
789
|
+
let pointName = getPointName(points[point], point);
|
|
790
|
+
structuredObject[pointName] = points[point];
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
msgg.payload = structuredObject;
|
|
768
794
|
node.send(msgg);
|
|
769
795
|
});
|
|
770
796
|
}
|
|
771
797
|
};
|
|
772
|
-
}
|
|
773
798
|
|
|
799
|
+
sendIndividualMsgJson = function (values, readNodeName) {
|
|
800
|
+
if (typeof values == "object") {
|
|
801
|
+
let keys = Object.keys(values);
|
|
802
|
+
keys.forEach(function (key) {
|
|
803
|
+
let points = values[key];
|
|
804
|
+
let msgg = {};
|
|
805
|
+
for (let point in points) {
|
|
806
|
+
let pointName = getPointName(points[point], point);
|
|
807
|
+
if (
|
|
808
|
+
node.nodeName !== "gateway" &&
|
|
809
|
+
node.nodeName !== "" &&
|
|
810
|
+
node.nodeName !== "null" &&
|
|
811
|
+
node.nodeName !== "undefined" &&
|
|
812
|
+
typeof node.nodeName == "string"
|
|
813
|
+
) {
|
|
814
|
+
if (readNodeName !== '' &&
|
|
815
|
+
readNodeName !== null &&
|
|
816
|
+
readNodeName !== undefined
|
|
817
|
+
) {
|
|
818
|
+
msgg.topic = `${node.nodeName}/${readNodeName}/${key}/${pointName}`;
|
|
819
|
+
} else {
|
|
820
|
+
msgg.topic = `${node.nodeName}/${key}/${pointName}`;
|
|
821
|
+
}
|
|
822
|
+
} else {
|
|
823
|
+
if (readNodeName !== '' &&
|
|
824
|
+
readNodeName !== null &&
|
|
825
|
+
readNodeName !== undefined
|
|
826
|
+
) {
|
|
827
|
+
msgg.topic = `BITPOOL_BACNET_GATEWAY/${readNodeName}/${key}/${pointName}`;
|
|
828
|
+
} else {
|
|
829
|
+
msgg.topic = `BITPOOL_BACNET_GATEWAY/${key}/${pointName}`;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
msgg.payload = points[point];
|
|
834
|
+
node.send(msgg);
|
|
835
|
+
msgg = {};
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
sendSimpleJsonPerPoint = function (values, readNodeName) {
|
|
842
|
+
let devices = Object.keys(values);
|
|
843
|
+
devices.forEach(function (device) {
|
|
844
|
+
if (device !== "_msgid") {
|
|
845
|
+
let points = values[device];
|
|
846
|
+
let msgg = {};
|
|
847
|
+
for (let point in points) {
|
|
848
|
+
if (points[point] && "presentValue" in points[point]) {
|
|
849
|
+
let pointName = getPointName(points[point], point);
|
|
850
|
+
if (
|
|
851
|
+
node.nodeName !== "gateway" &&
|
|
852
|
+
node.nodeName !== "" &&
|
|
853
|
+
node.nodeName !== "null" &&
|
|
854
|
+
node.nodeName !== "undefined" &&
|
|
855
|
+
typeof node.nodeName == "string"
|
|
856
|
+
) {
|
|
857
|
+
if (readNodeName !== '' &&
|
|
858
|
+
readNodeName !== null &&
|
|
859
|
+
readNodeName !== undefined
|
|
860
|
+
) {
|
|
861
|
+
msgg.topic = `${node.nodeName}/${readNodeName}/${device}/${pointName}`;
|
|
862
|
+
} else {
|
|
863
|
+
msgg.topic = `${node.nodeName}/${device}/${pointName}`;
|
|
864
|
+
}
|
|
865
|
+
} else {
|
|
866
|
+
if (readNodeName !== '' &&
|
|
867
|
+
readNodeName !== null &&
|
|
868
|
+
readNodeName !== undefined
|
|
869
|
+
) {
|
|
870
|
+
msgg.topic = `BITPOOL_BACNET_GATEWAY/${readNodeName}/${device}/${pointName}`;
|
|
871
|
+
} else {
|
|
872
|
+
msgg.topic = `BITPOOL_BACNET_GATEWAY/${device}/${pointName}`;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
let payload = {
|
|
876
|
+
presentValue: points[point]["presentValue"]
|
|
877
|
+
};
|
|
878
|
+
msgg.payload = payload;
|
|
879
|
+
node.send(msgg);
|
|
880
|
+
msgg = {};
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function getPointName(object, pointName) {
|
|
888
|
+
if (object.displayName) {
|
|
889
|
+
return object.displayName;
|
|
890
|
+
}
|
|
891
|
+
return pointName;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
}
|
|
774
895
|
RED.nodes.registerType("Bacnet-Gateway", BitpoolBacnetGatewayDevice);
|
|
775
896
|
};
|
package/bacnet_read.html
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
events: { value: true },
|
|
12
12
|
json: { value: true },
|
|
13
13
|
mqtt: { value: false },
|
|
14
|
+
pointJson: { value: false },
|
|
14
15
|
hiddenDeployToggle: { value: false },
|
|
15
16
|
prevHiddenToggleState: { value: false },
|
|
16
17
|
roundDecimal: { value: 2 },
|
|
@@ -18,7 +19,6 @@
|
|
|
18
19
|
pointList: [],
|
|
19
20
|
pointsToRead: { value: {} },
|
|
20
21
|
readDevices: { value: [] },
|
|
21
|
-
devicesToRead: [],
|
|
22
22
|
object_property_simplePayload: { value: false },
|
|
23
23
|
object_property_simpleWithStatus: { value: false },
|
|
24
24
|
object_property_fullObject: { value: true },
|
|
@@ -726,75 +726,21 @@
|
|
|
726
726
|
if (pointName == "deviceName") {
|
|
727
727
|
app.nodeService.setDeviceDisplayName(device, point);
|
|
728
728
|
treeDevice.label = point;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
);
|
|
733
|
-
|
|
734
|
-
if (pointInTree) {
|
|
735
|
-
pointInTree.label = point.displayName;
|
|
736
|
-
|
|
737
|
-
const isDeviceInReadList = app.readDevices
|
|
738
|
-
? app.readDevices.findIndex((ele) => ele.deviceId == treeDevice.deviceId)
|
|
739
|
-
: -1;
|
|
740
|
-
|
|
741
|
-
if (isDeviceInReadList == -1) {
|
|
742
|
-
//no read devices present, add new
|
|
743
|
-
let newReadParent = JSON.parse(JSON.stringify(treeDevice));
|
|
744
|
-
newReadParent.children[0].children = [];
|
|
745
|
-
newReadParent.children[0].children.push(pointInTree);
|
|
746
|
-
if (app.readDevices) {
|
|
747
|
-
app.readDevices.push(newReadParent);
|
|
748
|
-
} else {
|
|
749
|
-
app.readDevices = [newReadParent];
|
|
750
|
-
}
|
|
751
|
-
} else {
|
|
752
|
-
// read device found, add point to existing
|
|
753
|
-
let pointIndex = app.readDevices[isDeviceInReadList].children[0].children.findIndex(
|
|
754
|
-
(ele) => ele.data == point.objectName
|
|
755
|
-
);
|
|
756
|
-
if (pointIndex == -1) {
|
|
757
|
-
app.readDevices[isDeviceInReadList].children[0].children.push(pointInTree);
|
|
758
|
-
} else {
|
|
759
|
-
app.readDevices[isDeviceInReadList].children[0].children[pointIndex] = pointInTree;
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
} else {
|
|
765
|
-
//search mstp devices
|
|
766
|
-
let mstpIndex = app.devices[foundIndex].children[1].children.findIndex((ele) => ele.deviceId == id);
|
|
767
|
-
|
|
768
|
-
if (mstpIndex !== -1) {
|
|
769
|
-
let mstpDevice = app.devices[foundIndex].children[1].children[mstpIndex];
|
|
770
|
-
let device = app.deviceList.find((ele) => {
|
|
771
|
-
if (ele.address.address) {
|
|
772
|
-
return ele.address.address == ip && ele.deviceId == id;
|
|
773
|
-
} else {
|
|
774
|
-
return ele.address == ip && ele.deviceId == id;
|
|
775
|
-
}
|
|
776
|
-
});
|
|
777
|
-
|
|
778
|
-
for (let pointName in importedDevice) {
|
|
779
|
-
let point = importedDevice[pointName];
|
|
780
|
-
if (pointName == "deviceName") {
|
|
781
|
-
app.nodeService.setDeviceDisplayName(device, point);
|
|
782
|
-
mstpDevice.label = point;
|
|
783
|
-
}
|
|
784
|
-
let pointInTree = mstpDevice.children[0].children.find(
|
|
785
|
-
(ele) => ele.data == pointName && ele.bacnetType == point.meta.objectId.type
|
|
729
|
+
} else {
|
|
730
|
+
let pointInTree = treeDevice.children[0].children.find(
|
|
731
|
+
(ele) => ele.bacnetInstance == point.meta.objectId.instance && ele.bacnetType == point.meta.objectId.type
|
|
786
732
|
);
|
|
787
733
|
|
|
788
734
|
if (pointInTree) {
|
|
789
735
|
pointInTree.label = point.displayName;
|
|
790
736
|
|
|
791
737
|
const isDeviceInReadList = app.readDevices
|
|
792
|
-
? app.readDevices.findIndex((ele) => ele.deviceId ==
|
|
738
|
+
? app.readDevices.findIndex((ele) => ele.deviceId == treeDevice.deviceId)
|
|
793
739
|
: -1;
|
|
794
740
|
|
|
795
741
|
if (isDeviceInReadList == -1) {
|
|
796
742
|
//no read devices present, add new
|
|
797
|
-
let newReadParent = JSON.parse(JSON.stringify(
|
|
743
|
+
let newReadParent = JSON.parse(JSON.stringify(treeDevice));
|
|
798
744
|
newReadParent.children[0].children = [];
|
|
799
745
|
newReadParent.children[0].children.push(pointInTree);
|
|
800
746
|
if (app.readDevices) {
|
|
@@ -815,6 +761,74 @@
|
|
|
815
761
|
}
|
|
816
762
|
}
|
|
817
763
|
}
|
|
764
|
+
}
|
|
765
|
+
} else {
|
|
766
|
+
//search mstp devices
|
|
767
|
+
let mstpIndex = -1;
|
|
768
|
+
let folderIndex = -1;
|
|
769
|
+
|
|
770
|
+
app.devices[foundIndex].children.forEach(function (child, index) {
|
|
771
|
+
let temporaryIndex = -1;
|
|
772
|
+
if (child.label.includes("MSTP")) {
|
|
773
|
+
temporaryIndex = child.children.findIndex((ele) => ele.deviceId == id);
|
|
774
|
+
}
|
|
775
|
+
if (temporaryIndex !== -1) {
|
|
776
|
+
mstpIndex = temporaryIndex;
|
|
777
|
+
folderIndex = index;
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
if (mstpIndex !== -1 && folderIndex !== -1) {
|
|
782
|
+
let mstpDevice = app.devices[foundIndex].children[folderIndex].children[mstpIndex];
|
|
783
|
+
let device = app.deviceList.find((ele) => {
|
|
784
|
+
if (ele.address.address) {
|
|
785
|
+
return ele.address.address == ip && ele.deviceId == id;
|
|
786
|
+
} else {
|
|
787
|
+
return ele.address == ip && ele.deviceId == id;
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
for (let pointName in importedDevice) {
|
|
792
|
+
let point = importedDevice[pointName];
|
|
793
|
+
if (pointName == "deviceName") {
|
|
794
|
+
app.nodeService.setDeviceDisplayName(device, point);
|
|
795
|
+
mstpDevice.label = point;
|
|
796
|
+
} else {
|
|
797
|
+
let pointInTree = mstpDevice.children[0].children.find(
|
|
798
|
+
(ele) => ele.bacnetInstance == point.meta.objectId.instance && ele.bacnetType == point.meta.objectId.type
|
|
799
|
+
);
|
|
800
|
+
|
|
801
|
+
if (pointInTree) {
|
|
802
|
+
pointInTree.label = point.displayName;
|
|
803
|
+
|
|
804
|
+
const isDeviceInReadList = app.readDevices
|
|
805
|
+
? app.readDevices.findIndex((ele) => ele.deviceId == mstpDevice.deviceId)
|
|
806
|
+
: -1;
|
|
807
|
+
|
|
808
|
+
if (isDeviceInReadList == -1) {
|
|
809
|
+
//no read devices present, add new
|
|
810
|
+
let newReadParent = JSON.parse(JSON.stringify(mstpDevice));
|
|
811
|
+
newReadParent.children[0].children = [];
|
|
812
|
+
newReadParent.children[0].children.push(pointInTree);
|
|
813
|
+
if (app.readDevices) {
|
|
814
|
+
app.readDevices.push(newReadParent);
|
|
815
|
+
} else {
|
|
816
|
+
app.readDevices = [newReadParent];
|
|
817
|
+
}
|
|
818
|
+
} else {
|
|
819
|
+
// read device found, add point to existing
|
|
820
|
+
let pointIndex = app.readDevices[isDeviceInReadList].children[0].children.findIndex(
|
|
821
|
+
(ele) => ele.data == point.objectName
|
|
822
|
+
);
|
|
823
|
+
if (pointIndex == -1) {
|
|
824
|
+
app.readDevices[isDeviceInReadList].children[0].children.push(pointInTree);
|
|
825
|
+
} else {
|
|
826
|
+
app.readDevices[isDeviceInReadList].children[0].children[pointIndex] = pointInTree;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
818
832
|
} else {
|
|
819
833
|
// not part of device list at all, notify user
|
|
820
834
|
}
|
|
@@ -877,6 +891,8 @@
|
|
|
877
891
|
document.getElementById("node-input-json").onclick = handleMsgTypeClick;
|
|
878
892
|
document.getElementById("node-input-mqtt").checked = node.mqtt;
|
|
879
893
|
document.getElementById("node-input-mqtt").onclick = handleMsgTypeClick;
|
|
894
|
+
document.getElementById("node-input-pointJson").checked = node.pointJson;
|
|
895
|
+
document.getElementById("node-input-pointJson").onclick = handleMsgTypeClick;
|
|
880
896
|
|
|
881
897
|
var menu = document.querySelector(".context-menu");
|
|
882
898
|
window.addEventListener("click", (event) => {
|
|
@@ -905,10 +921,16 @@
|
|
|
905
921
|
|
|
906
922
|
function handleMsgTypeClick() {
|
|
907
923
|
if (this.id == "node-input-json") {
|
|
908
|
-
document.getElementById("node-input-mqtt").checked =
|
|
924
|
+
document.getElementById("node-input-mqtt").checked = false;
|
|
925
|
+
document.getElementById("node-input-pointJson").checked = false;
|
|
909
926
|
}
|
|
910
927
|
if (this.id == "node-input-mqtt") {
|
|
911
|
-
document.getElementById("node-input-json").checked =
|
|
928
|
+
document.getElementById("node-input-json").checked = false;
|
|
929
|
+
document.getElementById("node-input-pointJson").checked = false;
|
|
930
|
+
}
|
|
931
|
+
if (this.id == "node-input-pointJson") {
|
|
932
|
+
document.getElementById("node-input-json").checked = false;
|
|
933
|
+
document.getElementById("node-input-mqtt").checked = false;
|
|
912
934
|
}
|
|
913
935
|
}
|
|
914
936
|
|
|
@@ -1313,6 +1335,17 @@
|
|
|
1313
1335
|
>Individual msgs</label
|
|
1314
1336
|
>
|
|
1315
1337
|
</div>
|
|
1338
|
+
<div style="display: flex; flex-direction: row; align-items: flex-start;">
|
|
1339
|
+
<input
|
|
1340
|
+
class="checkbox-round"
|
|
1341
|
+
type="checkbox"
|
|
1342
|
+
id="node-input-pointJson"
|
|
1343
|
+
style="width: 13px; margin-left: 5px;" /><label
|
|
1344
|
+
for="node-input-pointJson"
|
|
1345
|
+
style="padding-left: 20px; width: fit-content;"
|
|
1346
|
+
>Individual JSON</label
|
|
1347
|
+
>
|
|
1348
|
+
</div>
|
|
1316
1349
|
</div>
|
|
1317
1350
|
</div>
|
|
1318
1351
|
|
package/bacnet_read.js
CHANGED
|
@@ -15,6 +15,7 @@ module.exports = function (RED) {
|
|
|
15
15
|
|
|
16
16
|
this.json = config.json;
|
|
17
17
|
this.mqtt = config.mqtt;
|
|
18
|
+
this.pointJson = config.pointJson;
|
|
18
19
|
this.roundDecimal = config.roundDecimal;
|
|
19
20
|
this.pointsToRead = config.pointsToRead;
|
|
20
21
|
this.readDevices = config.readDevices;
|
|
@@ -55,20 +56,54 @@ module.exports = function (RED) {
|
|
|
55
56
|
node.on('input', function (msg) {
|
|
56
57
|
node.status({ fill: "blue", shape: "dot", text: "Reading values" });
|
|
57
58
|
|
|
59
|
+
let object_property_simplePayload = false;
|
|
60
|
+
let object_property_simpleWithStatus = false;
|
|
61
|
+
let object_property_fullObject = false;
|
|
62
|
+
|
|
63
|
+
let jsonType = false;
|
|
64
|
+
let mqttType = false;
|
|
65
|
+
let pointJsonType = false;
|
|
66
|
+
|
|
67
|
+
if (msg.simplePayload) {
|
|
68
|
+
object_property_simplePayload = msg.simplePayload;
|
|
69
|
+
} else if (msg.simpleWithStatus) {
|
|
70
|
+
object_property_simpleWithStatus = msg.simpleWithStatus;
|
|
71
|
+
} else if (msg.fullObject) {
|
|
72
|
+
object_property_fullObject = msg.fullObject;
|
|
73
|
+
} else {
|
|
74
|
+
object_property_simplePayload = node.object_property_simplePayload;
|
|
75
|
+
object_property_simpleWithStatus = node.object_property_simpleWithStatus;
|
|
76
|
+
object_property_fullObject = node.object_property_fullObject;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (msg.json) {
|
|
80
|
+
jsonType = msg.json;
|
|
81
|
+
} else if (msg.mqtt) {
|
|
82
|
+
mqttType = msg.mqtt;
|
|
83
|
+
} else if (msg.pointJson) {
|
|
84
|
+
pointJsonType = msg.pointJson;
|
|
85
|
+
} else {
|
|
86
|
+
jsonType = node.json;
|
|
87
|
+
mqttType = node.mqtt;
|
|
88
|
+
pointJsonType = node.pointJson
|
|
89
|
+
}
|
|
90
|
+
|
|
58
91
|
let readConfig = new ReadCommandConfig(node.pointsToRead, node.object_props, node.roundDecimal);
|
|
92
|
+
|
|
59
93
|
let output = {
|
|
60
94
|
type: "Read",
|
|
61
95
|
id: node.id,
|
|
62
96
|
readNodeName: node.nodeName,
|
|
63
97
|
options: readConfig,
|
|
64
98
|
objectPropertyType: {
|
|
65
|
-
simplePayload:
|
|
66
|
-
simpleWithStatus:
|
|
67
|
-
fullObject:
|
|
99
|
+
simplePayload: object_property_simplePayload,
|
|
100
|
+
simpleWithStatus: object_property_simpleWithStatus,
|
|
101
|
+
fullObject: object_property_fullObject
|
|
68
102
|
},
|
|
69
103
|
outputType: {
|
|
70
|
-
json:
|
|
71
|
-
mqtt:
|
|
104
|
+
json: jsonType,
|
|
105
|
+
mqtt: mqttType,
|
|
106
|
+
pointJson: pointJsonType
|
|
72
107
|
}
|
|
73
108
|
};
|
|
74
109
|
|
package/bacnet_server.js
CHANGED
|
@@ -173,7 +173,7 @@ class BacnetServer extends EventEmitter {
|
|
|
173
173
|
);
|
|
174
174
|
}
|
|
175
175
|
} catch (e) {
|
|
176
|
-
console.log("Local BACnet device readProperty error: ", e);
|
|
176
|
+
//console.log("Local BACnet device readProperty error: ", e);
|
|
177
177
|
}
|
|
178
178
|
});
|
|
179
179
|
|
|
@@ -242,6 +242,8 @@ class BacnetServer extends EventEmitter {
|
|
|
242
242
|
|
|
243
243
|
//do initial iAm broadcast when BACnet server starts
|
|
244
244
|
that.bacnetClient.client.iAmResponse(that.deviceId, baEnum.Segmentation.SEGMENTED_BOTH, that.vendorId);
|
|
245
|
+
|
|
246
|
+
that.setMaxListeners(2);
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
/**
|
package/bitpool_inject.html
CHANGED
|
@@ -42,7 +42,104 @@
|
|
|
42
42
|
</div>
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
<div class="form-row" style="display: flex; width: fit-content; flex-wrap: nowrap; flex-direction: row; padding-top: 10px; margin-left: auto; margin-right: auto;">
|
|
46
|
+
|
|
47
|
+
<div
|
|
48
|
+
id="node-input-object_properties_group"
|
|
49
|
+
style="display: flex; align-items: flex-start; flex-direction: column;">
|
|
50
|
+
<label
|
|
51
|
+
for="node-input-object_properties_group"
|
|
52
|
+
style="display: flex; align-items: center; white-space: nowrap; text-decoration: underline;"
|
|
53
|
+
>
|
|
54
|
+
Object Properties:
|
|
55
|
+
</label>
|
|
56
|
+
|
|
57
|
+
<div class="objectPropertiesLabel">
|
|
58
|
+
<label
|
|
59
|
+
for="node-input-object_property_simplePayload"
|
|
60
|
+
style="padding-left: 4px; width: auto; align-items: start;"
|
|
61
|
+
class="objectPropertiesLabel">
|
|
62
|
+
<i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_simplePayload"></span>
|
|
63
|
+
<input
|
|
64
|
+
style="margin-left: 5px;"
|
|
65
|
+
class=" objectProp"
|
|
66
|
+
type="checkbox"
|
|
67
|
+
id="node-input-object_property_simplePayload" />
|
|
68
|
+
<a style="white-space: nowrap; padding-left: 20px;">Simple Payload</a>
|
|
69
|
+
</label>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div class="objectPropertiesLabel">
|
|
73
|
+
<label
|
|
74
|
+
for="node-input-object_property_simpleWithStatus"
|
|
75
|
+
style="padding-left: 4px; width: auto; align-items: start;"
|
|
76
|
+
class="objectPropertiesLabel">
|
|
77
|
+
<i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_simpleWithStatus"></span>
|
|
78
|
+
<input
|
|
79
|
+
style="margin-left: 5px;"
|
|
80
|
+
class=" objectProp"
|
|
81
|
+
type="checkbox"
|
|
82
|
+
id="node-input-object_property_simpleWithStatus" />
|
|
83
|
+
<a style="white-space: nowrap; padding-left: 20px;">Simple With Status</a>
|
|
84
|
+
</label>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class="objectPropertiesLabel">
|
|
88
|
+
<label
|
|
89
|
+
for="node-input-object_property_fullObject"
|
|
90
|
+
style="padding-left: 4px; width: auto; align-items: start;"
|
|
91
|
+
class="objectPropertiesLabel">
|
|
92
|
+
<i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_fullObject"></span>
|
|
93
|
+
<input
|
|
94
|
+
style="margin-left: 5px;"
|
|
95
|
+
class=" objectProp"
|
|
96
|
+
type="checkbox"
|
|
97
|
+
id="node-input-object_property_fullObject" />
|
|
98
|
+
<a style="white-space: nowrap; padding-left: 20px;">Full Object</a>
|
|
99
|
+
</label>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
</div>
|
|
103
|
+
<div
|
|
104
|
+
id="node-input-msgType"
|
|
105
|
+
style="display: flex; align-items: flex-start; flex-direction: column; padding-left: 50px;">
|
|
106
|
+
<label for="node-input-msgType" style="text-decoration: underline;"> Message type: </label>
|
|
107
|
+
<!-- class= checkbox-round -->
|
|
108
|
+
<div style="display: flex; flex-direction: row; align-items: flex-start;">
|
|
109
|
+
<input
|
|
110
|
+
class="checkbox-round"
|
|
111
|
+
type="checkbox"
|
|
112
|
+
id="node-input-json"
|
|
113
|
+
style="width: 13px; margin-left: 5px;" /><label
|
|
114
|
+
for="node-input-json"
|
|
115
|
+
style="padding-left: 20px; width: fit-content;"
|
|
116
|
+
>Block per device</label
|
|
117
|
+
>
|
|
118
|
+
</div>
|
|
119
|
+
<div style="display: flex; flex-direction: row; align-items: flex-start;">
|
|
120
|
+
<input
|
|
121
|
+
class="checkbox-round"
|
|
122
|
+
type="checkbox"
|
|
123
|
+
id="node-input-mqtt"
|
|
124
|
+
style="width: 13px; margin-left: 5px;" /><label
|
|
125
|
+
for="node-input-mqtt"
|
|
126
|
+
style="padding-left: 20px; width: fit-content;"
|
|
127
|
+
>Individual msgs</label
|
|
128
|
+
>
|
|
129
|
+
</div>
|
|
130
|
+
<div style="display: flex; flex-direction: row; align-items: flex-start;">
|
|
131
|
+
<input
|
|
132
|
+
class="checkbox-round"
|
|
133
|
+
type="checkbox"
|
|
134
|
+
id="node-input-pointJson"
|
|
135
|
+
style="width: 13px; margin-left: 5px;" /><label
|
|
136
|
+
for="node-input-pointJson"
|
|
137
|
+
style="padding-left: 20px; width: fit-content;"
|
|
138
|
+
>Individual JSON</label
|
|
139
|
+
>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
46
143
|
</div>
|
|
47
144
|
|
|
48
145
|
<div class="injectHeading" style="margin-top: 20px;">
|
|
@@ -373,6 +470,12 @@
|
|
|
373
470
|
payloadType: { value: "date" },
|
|
374
471
|
doPoll: { value: true },
|
|
375
472
|
doDiscover: { value: false },
|
|
473
|
+
json: { value: false },
|
|
474
|
+
mqtt: { value: false },
|
|
475
|
+
pointJson: { value: true },
|
|
476
|
+
object_property_simplePayload: { value: false },
|
|
477
|
+
object_property_simpleWithStatus: { value: false },
|
|
478
|
+
object_property_fullObject: { value: true },
|
|
376
479
|
},
|
|
377
480
|
icon: "bitpool.svg",
|
|
378
481
|
inputs: 0,
|
|
@@ -669,10 +772,26 @@
|
|
|
669
772
|
$("#inject-time-type-select").trigger("change");
|
|
670
773
|
$("#inject-time-interval-time-start").trigger("change");
|
|
671
774
|
|
|
775
|
+
document.getElementById("node-input-object_property_simplePayload").checked = node.object_property_simplePayload;
|
|
776
|
+
document.getElementById("node-input-object_property_simplePayload").onclick = handlePayloadCheckboxClick;
|
|
777
|
+
document.getElementById("node-input-object_property_simpleWithStatus").checked = node.object_property_simpleWithStatus;
|
|
778
|
+
document.getElementById("node-input-object_property_simpleWithStatus").onclick = handlePayloadCheckboxClick;
|
|
779
|
+
document.getElementById("node-input-object_property_fullObject").checked = node.object_property_fullObject;
|
|
780
|
+
document.getElementById("node-input-object_property_fullObject").onclick = handlePayloadCheckboxClick;
|
|
781
|
+
|
|
672
782
|
|
|
673
783
|
document.getElementById("node-input-doDiscover").onclick = handleCheckboxClick;
|
|
674
784
|
document.getElementById("node-input-doPoll").onclick = handleCheckboxClick;
|
|
675
785
|
|
|
786
|
+
document.getElementById("node-input-json").checked = node.json;
|
|
787
|
+
document.getElementById("node-input-json").onclick = handleMsgTypeClick;
|
|
788
|
+
document.getElementById("node-input-mqtt").checked = node.mqtt;
|
|
789
|
+
document.getElementById("node-input-mqtt").onclick = handleMsgTypeClick;
|
|
790
|
+
document.getElementById("node-input-pointJson").checked = node.pointJson;
|
|
791
|
+
document.getElementById("node-input-pointJson").onclick = handleMsgTypeClick;
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
|
|
676
795
|
function handleCheckboxClick() {
|
|
677
796
|
if (this.id == "node-input-doDiscover") {
|
|
678
797
|
document.getElementById("node-input-doPoll").checked = false;
|
|
@@ -682,6 +801,36 @@
|
|
|
682
801
|
}
|
|
683
802
|
}
|
|
684
803
|
|
|
804
|
+
function handlePayloadCheckboxClick() {
|
|
805
|
+
if (this.id == "node-input-object_property_simplePayload") {
|
|
806
|
+
document.getElementById("node-input-object_property_fullObject").checked = false;
|
|
807
|
+
document.getElementById("node-input-object_property_simpleWithStatus").checked = false;
|
|
808
|
+
}
|
|
809
|
+
if (this.id == "node-input-object_property_simpleWithStatus") {
|
|
810
|
+
document.getElementById("node-input-object_property_fullObject").checked = false;
|
|
811
|
+
document.getElementById("node-input-object_property_simplePayload").checked = false;
|
|
812
|
+
}
|
|
813
|
+
if (this.id == "node-input-object_property_fullObject") {
|
|
814
|
+
document.getElementById("node-input-object_property_simplePayload").checked = false;
|
|
815
|
+
document.getElementById("node-input-object_property_simpleWithStatus").checked = false;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function handleMsgTypeClick() {
|
|
820
|
+
if (this.id == "node-input-json") {
|
|
821
|
+
document.getElementById("node-input-mqtt").checked = false;
|
|
822
|
+
document.getElementById("node-input-pointJson").checked = false;
|
|
823
|
+
}
|
|
824
|
+
if (this.id == "node-input-mqtt") {
|
|
825
|
+
document.getElementById("node-input-json").checked = false;
|
|
826
|
+
document.getElementById("node-input-pointJson").checked = false;
|
|
827
|
+
}
|
|
828
|
+
if (this.id == "node-input-pointJson") {
|
|
829
|
+
document.getElementById("node-input-json").checked = false;
|
|
830
|
+
document.getElementById("node-input-mqtt").checked = false;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
685
834
|
},
|
|
686
835
|
oneditsave: function () {
|
|
687
836
|
var repeat = "";
|
package/bitpool_inject.js
CHANGED
|
@@ -19,28 +19,28 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
module.exports = function (RED) {
|
|
23
23
|
"use strict";
|
|
24
|
-
const {scheduleTask} = require("cronosjs");
|
|
24
|
+
const { scheduleTask } = require("cronosjs");
|
|
25
25
|
|
|
26
26
|
function BitpoolInjectNode(n) {
|
|
27
|
-
RED.nodes.createNode(this,n);
|
|
27
|
+
RED.nodes.createNode(this, n);
|
|
28
28
|
|
|
29
29
|
/* Handle legacy */
|
|
30
|
-
if(!Array.isArray(n.props)){
|
|
30
|
+
if (!Array.isArray(n.props)) {
|
|
31
31
|
n.props = [];
|
|
32
32
|
n.props.push({
|
|
33
|
-
p:'payload',
|
|
34
|
-
v:n.payload,
|
|
35
|
-
vt:n.payloadType
|
|
33
|
+
p: 'payload',
|
|
34
|
+
v: n.payload,
|
|
35
|
+
vt: n.payloadType
|
|
36
36
|
});
|
|
37
37
|
n.props.push({
|
|
38
|
-
p:'topic',
|
|
39
|
-
v:n.topic,
|
|
40
|
-
vt:'str'
|
|
38
|
+
p: 'topic',
|
|
39
|
+
v: n.topic,
|
|
40
|
+
vt: 'str'
|
|
41
41
|
});
|
|
42
42
|
} else {
|
|
43
|
-
for (var i=0,l=n.props.length; i<l; i++) {
|
|
43
|
+
for (var i = 0, l = n.props.length; i < l; i++) {
|
|
44
44
|
if (n.props[i].p === 'payload' && !n.props[i].hasOwnProperty('v')) {
|
|
45
45
|
n.props[i].v = n.payload;
|
|
46
46
|
n.props[i].vt = n.payloadType;
|
|
@@ -56,6 +56,14 @@
|
|
|
56
56
|
this.once = n.once;
|
|
57
57
|
this.doPoll = n.doPoll;
|
|
58
58
|
this.doDiscover = n.doDiscover;
|
|
59
|
+
|
|
60
|
+
this.object_property_simplePayload = n.object_property_simplePayload;
|
|
61
|
+
this.object_property_simpleWithStatus = n.object_property_simpleWithStatus;
|
|
62
|
+
this.object_property_fullObject = n.object_property_fullObject;
|
|
63
|
+
this.json = n.json;
|
|
64
|
+
this.mqtt = n.mqtt;
|
|
65
|
+
this.pointJson = n.pointJson;
|
|
66
|
+
|
|
59
67
|
this.onceDelay = (n.onceDelay || 0.1) * 1000;
|
|
60
68
|
this.interval_id = null;
|
|
61
69
|
this.cronjob = null;
|
|
@@ -68,7 +76,7 @@
|
|
|
68
76
|
prop.exp = RED.util.prepareJSONataExpression(val, node);
|
|
69
77
|
}
|
|
70
78
|
catch (err) {
|
|
71
|
-
node.error(RED._("inject.errors.invalid-expr", {error:err.message}));
|
|
79
|
+
node.error(RED._("inject.errors.invalid-expr", { error: err.message }));
|
|
72
80
|
prop.exp = null;
|
|
73
81
|
}
|
|
74
82
|
}
|
|
@@ -83,27 +91,37 @@
|
|
|
83
91
|
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
|
|
84
92
|
this.repeat = this.repeat * 1000;
|
|
85
93
|
this.debug(RED._("inject.repeat", this));
|
|
86
|
-
this.interval_id = setInterval(function() {
|
|
94
|
+
this.interval_id = setInterval(function () {
|
|
87
95
|
node.emit("input", {});
|
|
88
96
|
}, this.repeat);
|
|
89
97
|
} else if (this.crontab) {
|
|
90
98
|
this.debug(RED._("inject.crontab", this));
|
|
91
|
-
this.cronjob = scheduleTask(this.crontab,() => { node.emit("input", {})});
|
|
99
|
+
this.cronjob = scheduleTask(this.crontab, () => { node.emit("input", {}) });
|
|
92
100
|
}
|
|
93
101
|
};
|
|
94
102
|
|
|
95
103
|
if (this.once) {
|
|
96
|
-
this.onceTimeout = setTimeout(
|
|
97
|
-
node.emit("input",{});
|
|
104
|
+
this.onceTimeout = setTimeout(function () {
|
|
105
|
+
node.emit("input", {});
|
|
98
106
|
node.repeaterSetup();
|
|
99
107
|
}, this.onceDelay);
|
|
100
108
|
} else {
|
|
101
109
|
node.repeaterSetup();
|
|
102
110
|
}
|
|
103
111
|
|
|
104
|
-
this.on("input", function(msg, send, done) {
|
|
112
|
+
this.on("input", function (msg, send, done) {
|
|
105
113
|
msg.doDiscover = node.doDiscover;
|
|
106
114
|
msg.doPoll = node.doPoll;
|
|
115
|
+
|
|
116
|
+
msg.simplePayload = node.object_property_simplePayload;
|
|
117
|
+
msg.simpleWithStatus = node.object_property_simpleWithStatus;
|
|
118
|
+
msg.fullObject = node.object_property_fullObject;
|
|
119
|
+
|
|
120
|
+
msg.json = node.json;
|
|
121
|
+
msg.mqtt = node.mqtt;
|
|
122
|
+
msg.pointJson = node.pointJson;
|
|
123
|
+
|
|
124
|
+
|
|
107
125
|
var errors = [];
|
|
108
126
|
var props = this.props;
|
|
109
127
|
if (msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) {
|
|
@@ -124,14 +142,14 @@
|
|
|
124
142
|
var val = RED.util.evaluateJSONataExpression(exp, msg);
|
|
125
143
|
RED.util.setMessageProperty(msg, property, val, true);
|
|
126
144
|
}
|
|
127
|
-
catch
|
|
145
|
+
catch (err) {
|
|
128
146
|
errors.push(err.message);
|
|
129
147
|
}
|
|
130
148
|
}
|
|
131
149
|
return;
|
|
132
150
|
}
|
|
133
151
|
try {
|
|
134
|
-
RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true);
|
|
152
|
+
RED.util.setMessageProperty(msg, property, RED.util.evaluateNodeProperty(value, valueType, this, msg), true);
|
|
135
153
|
} catch (err) {
|
|
136
154
|
errors.push(err.toString());
|
|
137
155
|
}
|
|
@@ -146,9 +164,9 @@
|
|
|
146
164
|
});
|
|
147
165
|
}
|
|
148
166
|
|
|
149
|
-
RED.nodes.registerType("Bitpool-Inject",BitpoolInjectNode);
|
|
167
|
+
RED.nodes.registerType("Bitpool-Inject", BitpoolInjectNode);
|
|
150
168
|
|
|
151
|
-
BitpoolInjectNode.prototype.close = function() {
|
|
169
|
+
BitpoolInjectNode.prototype.close = function () {
|
|
152
170
|
if (this.onceTimeout) {
|
|
153
171
|
clearTimeout(this.onceTimeout);
|
|
154
172
|
}
|
|
@@ -160,7 +178,7 @@
|
|
|
160
178
|
}
|
|
161
179
|
};
|
|
162
180
|
|
|
163
|
-
RED.httpAdmin.post("/inject/:id", RED.auth.needsPermission("inject.write"), function(req,res) {
|
|
181
|
+
RED.httpAdmin.post("/inject/:id", RED.auth.needsPermission("inject.write"), function (req, res) {
|
|
164
182
|
var node = RED.nodes.getNode(req.params.id);
|
|
165
183
|
if (node != null) {
|
|
166
184
|
try {
|
|
@@ -170,9 +188,9 @@
|
|
|
170
188
|
node.receive();
|
|
171
189
|
}
|
|
172
190
|
res.sendStatus(200);
|
|
173
|
-
} catch(err) {
|
|
191
|
+
} catch (err) {
|
|
174
192
|
res.sendStatus(500);
|
|
175
|
-
node.error(RED._("inject.failed",{error:err.toString()}));
|
|
193
|
+
node.error(RED._("inject.failed", { error: err.toString() }));
|
|
176
194
|
}
|
|
177
195
|
} else {
|
|
178
196
|
res.sendStatus(404);
|
package/package.json
CHANGED
package/treeBuilder.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { Store_Config } = require("./common");
|
|
2
|
+
const { BacnetDevice } = require("./bacnet_device");
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The `treeBuilder` class is responsible for building and processing the network tree structure.
|
|
@@ -111,6 +112,42 @@ class treeBuilder {
|
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
addEmptyIpRootDevice(childDevice) {
|
|
116
|
+
|
|
117
|
+
const ipAddress = this.getDeviceIpAddress(childDevice);
|
|
118
|
+
|
|
119
|
+
let deviceIndex = this.deviceList.findIndex(ele => ele.address === ipAddress && ele.deviceId === null && ele.deviceName === ipAddress && ele.displayName === ipAddress);
|
|
120
|
+
|
|
121
|
+
if (deviceIndex === -1) {
|
|
122
|
+
let newDevice = {
|
|
123
|
+
address: ipAddress,
|
|
124
|
+
isMstp: false,
|
|
125
|
+
deviceId: null,
|
|
126
|
+
maxApdu: childDevice.getMaxApdu(),
|
|
127
|
+
segmentation: childDevice.getSegmentation(),
|
|
128
|
+
vendorId: childDevice.getVendorId(),
|
|
129
|
+
lastSeen: null,
|
|
130
|
+
deviceName: ipAddress,
|
|
131
|
+
pointsList: [],
|
|
132
|
+
pointListUpdateTs: null,
|
|
133
|
+
manualDiscoveryMode: false,
|
|
134
|
+
pointListRetryCount: 0,
|
|
135
|
+
priorityQueueIsActive: false,
|
|
136
|
+
priorityQueue: [],
|
|
137
|
+
lastPriorityQueueTS: null,
|
|
138
|
+
childDevices: [childDevice.getDeviceId()],
|
|
139
|
+
parentDeviceId: null,
|
|
140
|
+
displayName: ipAddress,
|
|
141
|
+
protocolServicesSupported: [],
|
|
142
|
+
isProtocolServicesSet: false,
|
|
143
|
+
isInitialQuery: true,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
let newBacnetDevice = new BacnetDevice(true, newDevice);
|
|
147
|
+
this.deviceList.push(newBacnetDevice);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
114
151
|
/**
|
|
115
152
|
* Process the points of a device and add them to the render list.
|
|
116
153
|
*
|
|
@@ -124,7 +161,7 @@ class treeBuilder {
|
|
|
124
161
|
*/
|
|
125
162
|
async processDevicePoints(device, deviceObject, deviceName, ipAddress, deviceId, index) {
|
|
126
163
|
// Initialize the list of children points
|
|
127
|
-
|
|
164
|
+
let children = [];
|
|
128
165
|
|
|
129
166
|
// Process each point in the device object
|
|
130
167
|
for (const pointName in deviceObject) {
|
|
@@ -171,6 +208,7 @@ class treeBuilder {
|
|
|
171
208
|
parentDeviceId: device.getDeviceId(),
|
|
172
209
|
showAdded: false,
|
|
173
210
|
bacnetType: point.meta.objectId.type,
|
|
211
|
+
bacnetInstance: point.meta.objectId.instance,
|
|
174
212
|
};
|
|
175
213
|
}
|
|
176
214
|
|
|
@@ -305,7 +343,21 @@ class treeBuilder {
|
|
|
305
343
|
} else {
|
|
306
344
|
mstpNetworkFolder.children[mstpDeviceIndex] = newDeviceEntry;
|
|
307
345
|
}
|
|
346
|
+
} else {
|
|
347
|
+
//no parent found in render list
|
|
348
|
+
|
|
349
|
+
if (parentDeviceId !== null) {
|
|
350
|
+
let parentDeviceListIndex = this.deviceList.findIndex(ele => ele.getDeviceId == parentDeviceId);
|
|
351
|
+
|
|
352
|
+
if (parentDeviceListIndex !== -1) {
|
|
353
|
+
let parentDevice = this.deviceList[parentDeviceListIndex];
|
|
354
|
+
this.addRootDeviceFolder(parentDevice, parentDevice.getDeviceName(), parentDeviceListIndex, parentDevice.getAddress(), parentDeviceId);
|
|
355
|
+
}
|
|
356
|
+
} else {
|
|
357
|
+
this.addEmptyIpRootDevice(device);
|
|
358
|
+
}
|
|
308
359
|
}
|
|
360
|
+
|
|
309
361
|
} else {
|
|
310
362
|
// Add the new device entry to the root of the render list
|
|
311
363
|
if (foundIndex === -1) {
|