@bitpoolos/edge-bacnet 1.4.1 → 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 +21 -0
- package/README.md +2 -0
- package/bacnet_gateway.js +151 -30
- package/bacnet_read.html +95 -61
- package/bacnet_read.js +40 -5
- 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,4 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
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
|
+
|
|
2
23
|
## [1.4.1] - 09-07-2024
|
|
3
24
|
|
|
4
25
|
### 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_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 },
|
|
@@ -725,75 +726,21 @@
|
|
|
725
726
|
if (pointName == "deviceName") {
|
|
726
727
|
app.nodeService.setDeviceDisplayName(device, point);
|
|
727
728
|
treeDevice.label = point;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
);
|
|
732
|
-
|
|
733
|
-
if (pointInTree) {
|
|
734
|
-
pointInTree.label = point.displayName;
|
|
735
|
-
|
|
736
|
-
const isDeviceInReadList = app.readDevices
|
|
737
|
-
? app.readDevices.findIndex((ele) => ele.deviceId == treeDevice.deviceId)
|
|
738
|
-
: -1;
|
|
739
|
-
|
|
740
|
-
if (isDeviceInReadList == -1) {
|
|
741
|
-
//no read devices present, add new
|
|
742
|
-
let newReadParent = JSON.parse(JSON.stringify(treeDevice));
|
|
743
|
-
newReadParent.children[0].children = [];
|
|
744
|
-
newReadParent.children[0].children.push(pointInTree);
|
|
745
|
-
if (app.readDevices) {
|
|
746
|
-
app.readDevices.push(newReadParent);
|
|
747
|
-
} else {
|
|
748
|
-
app.readDevices = [newReadParent];
|
|
749
|
-
}
|
|
750
|
-
} else {
|
|
751
|
-
// read device found, add point to existing
|
|
752
|
-
let pointIndex = app.readDevices[isDeviceInReadList].children[0].children.findIndex(
|
|
753
|
-
(ele) => ele.data == point.objectName
|
|
754
|
-
);
|
|
755
|
-
if (pointIndex == -1) {
|
|
756
|
-
app.readDevices[isDeviceInReadList].children[0].children.push(pointInTree);
|
|
757
|
-
} else {
|
|
758
|
-
app.readDevices[isDeviceInReadList].children[0].children[pointIndex] = pointInTree;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
} else {
|
|
764
|
-
//search mstp devices
|
|
765
|
-
let mstpIndex = app.devices[foundIndex].children[1].children.findIndex((ele) => ele.deviceId == id);
|
|
766
|
-
|
|
767
|
-
if (mstpIndex !== -1) {
|
|
768
|
-
let mstpDevice = app.devices[foundIndex].children[1].children[mstpIndex];
|
|
769
|
-
let device = app.deviceList.find((ele) => {
|
|
770
|
-
if (ele.address.address) {
|
|
771
|
-
return ele.address.address == ip && ele.deviceId == id;
|
|
772
|
-
} else {
|
|
773
|
-
return ele.address == ip && ele.deviceId == id;
|
|
774
|
-
}
|
|
775
|
-
});
|
|
776
|
-
|
|
777
|
-
for (let pointName in importedDevice) {
|
|
778
|
-
let point = importedDevice[pointName];
|
|
779
|
-
if (pointName == "deviceName") {
|
|
780
|
-
app.nodeService.setDeviceDisplayName(device, point);
|
|
781
|
-
mstpDevice.label = point;
|
|
782
|
-
}
|
|
783
|
-
let pointInTree = mstpDevice.children[0].children.find(
|
|
784
|
-
(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
|
|
785
732
|
);
|
|
786
733
|
|
|
787
734
|
if (pointInTree) {
|
|
788
735
|
pointInTree.label = point.displayName;
|
|
789
736
|
|
|
790
737
|
const isDeviceInReadList = app.readDevices
|
|
791
|
-
? app.readDevices.findIndex((ele) => ele.deviceId ==
|
|
738
|
+
? app.readDevices.findIndex((ele) => ele.deviceId == treeDevice.deviceId)
|
|
792
739
|
: -1;
|
|
793
740
|
|
|
794
741
|
if (isDeviceInReadList == -1) {
|
|
795
742
|
//no read devices present, add new
|
|
796
|
-
let newReadParent = JSON.parse(JSON.stringify(
|
|
743
|
+
let newReadParent = JSON.parse(JSON.stringify(treeDevice));
|
|
797
744
|
newReadParent.children[0].children = [];
|
|
798
745
|
newReadParent.children[0].children.push(pointInTree);
|
|
799
746
|
if (app.readDevices) {
|
|
@@ -814,6 +761,74 @@
|
|
|
814
761
|
}
|
|
815
762
|
}
|
|
816
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
|
+
}
|
|
817
832
|
} else {
|
|
818
833
|
// not part of device list at all, notify user
|
|
819
834
|
}
|
|
@@ -876,6 +891,8 @@
|
|
|
876
891
|
document.getElementById("node-input-json").onclick = handleMsgTypeClick;
|
|
877
892
|
document.getElementById("node-input-mqtt").checked = node.mqtt;
|
|
878
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;
|
|
879
896
|
|
|
880
897
|
var menu = document.querySelector(".context-menu");
|
|
881
898
|
window.addEventListener("click", (event) => {
|
|
@@ -904,10 +921,16 @@
|
|
|
904
921
|
|
|
905
922
|
function handleMsgTypeClick() {
|
|
906
923
|
if (this.id == "node-input-json") {
|
|
907
|
-
document.getElementById("node-input-mqtt").checked =
|
|
924
|
+
document.getElementById("node-input-mqtt").checked = false;
|
|
925
|
+
document.getElementById("node-input-pointJson").checked = false;
|
|
908
926
|
}
|
|
909
927
|
if (this.id == "node-input-mqtt") {
|
|
910
|
-
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;
|
|
911
934
|
}
|
|
912
935
|
}
|
|
913
936
|
|
|
@@ -1312,6 +1335,17 @@
|
|
|
1312
1335
|
>Individual msgs</label
|
|
1313
1336
|
>
|
|
1314
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>
|
|
1315
1349
|
</div>
|
|
1316
1350
|
</div>
|
|
1317
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/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) {
|