@bitpoolos/edge-bacnet 1.3.1 → 1.4.0
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 +33 -0
- package/bacnet_client.js +27 -33
- package/bacnet_gateway.html +13 -13
- package/bacnet_gateway.js +140 -21
- package/bacnet_read.html +61 -59
- package/bacnet_read.js +2 -0
- package/bacnet_server.js +143 -17
- package/bitpool_inject.html +492 -462
- package/common.js +0 -29
- package/package.json +1 -1
- package/treeBuilder.js +0 -7
package/bacnet_read.html
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
readDevices: { value: [] },
|
|
21
21
|
devicesToRead: [],
|
|
22
22
|
object_property_simplePayload: { value: false },
|
|
23
|
+
object_property_simpleWithStatus: { value: false },
|
|
23
24
|
object_property_fullObject: { value: true },
|
|
24
25
|
},
|
|
25
26
|
inputs: 1,
|
|
@@ -96,9 +97,6 @@
|
|
|
96
97
|
mounted() {
|
|
97
98
|
let app = this;
|
|
98
99
|
this.getData(true);
|
|
99
|
-
if (!node.reloadTimer) {
|
|
100
|
-
node.reloadTimer = setInterval(() => app.getData(false), 7000);
|
|
101
|
-
}
|
|
102
100
|
|
|
103
101
|
$("#readlist-file-upload").on("change", function (event) {
|
|
104
102
|
const input = event.target.files[0];
|
|
@@ -408,29 +406,31 @@
|
|
|
408
406
|
return ele.address == ipAddress && ele.deviceId == deviceId;
|
|
409
407
|
}
|
|
410
408
|
});
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
409
|
+
if (device) {
|
|
410
|
+
const deviceName = device.deviceName;
|
|
411
|
+
const points = Object.keys(deviceObject);
|
|
412
|
+
for (point in points) {
|
|
413
|
+
csvContent +=
|
|
414
|
+
ipAddress +
|
|
415
|
+
"," +
|
|
416
|
+
deviceId +
|
|
417
|
+
"," +
|
|
418
|
+
deviceName +
|
|
419
|
+
"," +
|
|
420
|
+
points[point] +
|
|
421
|
+
"," +
|
|
422
|
+
deviceObject[points[point]].meta.objectId.type +
|
|
423
|
+
"\r\n";
|
|
424
|
+
|
|
425
|
+
if (parseInt(point) == points.length - 1 && parseInt(key) == keys.length - 1) {
|
|
426
|
+
// last iteration
|
|
427
|
+
var encodedUri = encodeURI(csvContent);
|
|
428
|
+
var link = document.createElement("a");
|
|
429
|
+
link.setAttribute("href", encodedUri);
|
|
430
|
+
link.setAttribute("download", "pointslist.csv");
|
|
431
|
+
document.body.appendChild(link);
|
|
432
|
+
link.click();
|
|
433
|
+
}
|
|
434
434
|
}
|
|
435
435
|
}
|
|
436
436
|
}
|
|
@@ -661,28 +661,22 @@
|
|
|
661
661
|
}
|
|
662
662
|
|
|
663
663
|
const devicePoints = Object.keys(device);
|
|
664
|
-
let pointIndex = 0;
|
|
665
|
-
doPoints(pointIndex);
|
|
666
664
|
|
|
667
|
-
|
|
665
|
+
for (let pointIndex = 0; pointIndex <= devicePoints.length; pointIndex++) {
|
|
668
666
|
let pointName = devicePoints[pointIndex];
|
|
669
667
|
let pointObject = device[pointName];
|
|
670
668
|
|
|
671
669
|
//formatting json payload, still in progress
|
|
672
670
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
671
|
+
if (pointObject) {
|
|
672
|
+
exportJson[key][pointName] = {
|
|
673
|
+
meta: pointObject.meta,
|
|
674
|
+
objectName: pointObject.objectName,
|
|
675
|
+
displayName: pointObject.displayName,
|
|
676
|
+
};
|
|
677
|
+
}
|
|
680
678
|
|
|
681
|
-
if (pointIndex
|
|
682
|
-
//more points to read for device
|
|
683
|
-
pointIndex++;
|
|
684
|
-
doPoints(pointIndex);
|
|
685
|
-
} else if (pointIndex == devicePoints.length - 1) {
|
|
679
|
+
if (pointIndex == devicePoints.length - 1) {
|
|
686
680
|
//all points have been iterated
|
|
687
681
|
|
|
688
682
|
if (deviceIndex < devicesToExport.length - 1) {
|
|
@@ -873,6 +867,8 @@
|
|
|
873
867
|
|
|
874
868
|
document.getElementById("node-input-object_property_simplePayload").checked = node.object_property_simplePayload;
|
|
875
869
|
document.getElementById("node-input-object_property_simplePayload").onclick = handleCheckboxClick;
|
|
870
|
+
document.getElementById("node-input-object_property_simpleWithStatus").checked = node.object_property_simpleWithStatus;
|
|
871
|
+
document.getElementById("node-input-object_property_simpleWithStatus").onclick = handleCheckboxClick;
|
|
876
872
|
document.getElementById("node-input-object_property_fullObject").checked = node.object_property_fullObject;
|
|
877
873
|
document.getElementById("node-input-object_property_fullObject").onclick = handleCheckboxClick;
|
|
878
874
|
|
|
@@ -894,14 +890,16 @@
|
|
|
894
890
|
|
|
895
891
|
function handleCheckboxClick() {
|
|
896
892
|
if (this.id == "node-input-object_property_simplePayload") {
|
|
897
|
-
document.getElementById("node-input-object_property_fullObject").checked =
|
|
898
|
-
|
|
899
|
-
|
|
893
|
+
document.getElementById("node-input-object_property_fullObject").checked = false;
|
|
894
|
+
document.getElementById("node-input-object_property_simpleWithStatus").checked = false;
|
|
895
|
+
}
|
|
896
|
+
if (this.id == "node-input-object_property_simpleWithStatus") {
|
|
897
|
+
document.getElementById("node-input-object_property_fullObject").checked = false;
|
|
898
|
+
document.getElementById("node-input-object_property_simplePayload").checked = false;
|
|
900
899
|
}
|
|
901
900
|
if (this.id == "node-input-object_property_fullObject") {
|
|
902
|
-
document.getElementById("node-input-object_property_simplePayload").checked =
|
|
903
|
-
|
|
904
|
-
).checked;
|
|
901
|
+
document.getElementById("node-input-object_property_simplePayload").checked = false;
|
|
902
|
+
document.getElementById("node-input-object_property_simpleWithStatus").checked = false;
|
|
905
903
|
}
|
|
906
904
|
}
|
|
907
905
|
|
|
@@ -948,25 +946,13 @@
|
|
|
948
946
|
},
|
|
949
947
|
oneditsave: function () {
|
|
950
948
|
let node = this;
|
|
951
|
-
if (node.vm.$data.devices) node.devices = node.vm.$data.devices;
|
|
952
949
|
if (node.vm.$data.readDevices) node.readDevices = node.vm.$data.readDevices;
|
|
953
950
|
if (node.vm.$data.pointsToRead) node.pointsToRead = node.vm.$data.pointsToRead;
|
|
954
|
-
//clear reload data function
|
|
955
|
-
if (node.reloadTimer) {
|
|
956
|
-
clearInterval(node.reloadTimer);
|
|
957
|
-
node.reloadTimer = null;
|
|
958
|
-
}
|
|
959
951
|
},
|
|
960
952
|
oneditcancel: function () {
|
|
961
953
|
let node = this;
|
|
962
|
-
if (node.vm.$data.devices) node.devices = node.vm.$data.devices;
|
|
963
954
|
if (node.vm.$data.readDevices) node.readDevices = node.vm.$data.readDevices;
|
|
964
955
|
if (node.vm.$data.pointsToRead) node.pointsToRead = node.vm.$data.pointsToRead;
|
|
965
|
-
//clear reload data function
|
|
966
|
-
if (node.reloadTimer) {
|
|
967
|
-
clearInterval(node.reloadTimer);
|
|
968
|
-
node.reloadTimer = null;
|
|
969
|
-
}
|
|
970
956
|
},
|
|
971
957
|
});
|
|
972
958
|
</script>
|
|
@@ -1264,6 +1250,21 @@
|
|
|
1264
1250
|
</label>
|
|
1265
1251
|
</div>
|
|
1266
1252
|
|
|
1253
|
+
<div class="objectPropertiesLabel">
|
|
1254
|
+
<label
|
|
1255
|
+
for="node-input-object_property_simpleWithStatus"
|
|
1256
|
+
style="padding-left: 4px; width: auto; align-items: start;"
|
|
1257
|
+
class="objectPropertiesLabel">
|
|
1258
|
+
<i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_simpleWithStatus"></span>
|
|
1259
|
+
<input
|
|
1260
|
+
style="margin-left: 5px;"
|
|
1261
|
+
class=" objectProp"
|
|
1262
|
+
type="checkbox"
|
|
1263
|
+
id="node-input-object_property_simpleWithStatus" />
|
|
1264
|
+
<a style="white-space: nowrap; padding-left: 20px;">Simple With Status</a>
|
|
1265
|
+
</label>
|
|
1266
|
+
</div>
|
|
1267
|
+
|
|
1267
1268
|
<div class="objectPropertiesLabel">
|
|
1268
1269
|
<label
|
|
1269
1270
|
for="node-input-object_property_fullObject"
|
|
@@ -1278,6 +1279,7 @@
|
|
|
1278
1279
|
<a style="white-space: nowrap; padding-left: 20px;">Full Object</a>
|
|
1279
1280
|
</label>
|
|
1280
1281
|
</div>
|
|
1282
|
+
|
|
1281
1283
|
</div>
|
|
1282
1284
|
</div>
|
|
1283
1285
|
|
package/bacnet_read.js
CHANGED
|
@@ -22,6 +22,7 @@ module.exports = function (RED) {
|
|
|
22
22
|
this.nodeName = config.name;
|
|
23
23
|
|
|
24
24
|
this.object_property_simplePayload = config.object_property_simplePayload;
|
|
25
|
+
this.object_property_simpleWithStatus = config.object_property_simpleWithStatus;
|
|
25
26
|
this.object_property_fullObject = config.object_property_fullObject;
|
|
26
27
|
|
|
27
28
|
|
|
@@ -62,6 +63,7 @@ module.exports = function (RED) {
|
|
|
62
63
|
options: readConfig,
|
|
63
64
|
objectPropertyType: {
|
|
64
65
|
simplePayload: node.object_property_simplePayload,
|
|
66
|
+
simpleWithStatus: node.object_property_simpleWithStatus,
|
|
65
67
|
fullObject: node.object_property_fullObject
|
|
66
68
|
},
|
|
67
69
|
outputType: {
|
package/bacnet_server.js
CHANGED
|
@@ -2,6 +2,7 @@ const bacnet = require('./resources/node-bacstack-ts/dist/index.js');
|
|
|
2
2
|
const pjson = require('./package.json');
|
|
3
3
|
const baEnum = bacnet.enum;
|
|
4
4
|
const { Store_Config_Server, Read_Config_Sync_Server } = require('./common');
|
|
5
|
+
const { EventEmitter } = require("events");
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Class representing a BACnet Server.
|
|
@@ -16,9 +17,10 @@ const { Store_Config_Server, Read_Config_Sync_Server } = require('./common');
|
|
|
16
17
|
* @param {number} deviceId - The ID of the device.
|
|
17
18
|
* @param {string} nodeRedVersion - The version of Node-Red.
|
|
18
19
|
*/
|
|
19
|
-
class BacnetServer {
|
|
20
|
+
class BacnetServer extends EventEmitter {
|
|
20
21
|
|
|
21
22
|
constructor(client, deviceId, nodeRedVersion) {
|
|
23
|
+
super();
|
|
22
24
|
let that = this;
|
|
23
25
|
that.bacnetClient = client;
|
|
24
26
|
|
|
@@ -175,6 +177,69 @@ class BacnetServer {
|
|
|
175
177
|
}
|
|
176
178
|
});
|
|
177
179
|
|
|
180
|
+
that.bacnetClient.client.on('writeProperty', (data) => {
|
|
181
|
+
let objectId = data.request.objectId.type;
|
|
182
|
+
let objectInstance = data.request.objectId.instance;
|
|
183
|
+
let propId = data.request.value.property.id.toString();
|
|
184
|
+
let newValue = data.request.value.value[0].value;
|
|
185
|
+
|
|
186
|
+
if (!that.modifyObject(objectId, propId, objectInstance, newValue)) {
|
|
187
|
+
that.bacnetClient.errorResponse(data.address, data.service, data.invokeId, bacnet.enum.ErrorClass.OBJECT, bacnet.enum.ErrorCode.UNKNOWN_OBJECT);
|
|
188
|
+
}
|
|
189
|
+
that.getServerPoints(objectId, objectInstance).then(function (result) {
|
|
190
|
+
that.bacnetClient.client.simpleAckResponse(data.address, data.service, data.invokeId);
|
|
191
|
+
that.emit("writeProperty", result[0].name, newValue);
|
|
192
|
+
}).catch(function (error) {
|
|
193
|
+
that.bacnetClient.errorResponse(data.address, data.service, data.invokeId, bacnet.enum.ErrorClass.OBJECT, bacnet.enum.ErrorCode.UNKNOWN_OBJECT);
|
|
194
|
+
that.logOut("Error getting server points: ", error);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
that.bacnetClient.client.on('createObject', (data) => {
|
|
199
|
+
var defaultValue;
|
|
200
|
+
switch (data.request.values[0].value[0].type) {
|
|
201
|
+
case baEnum.ObjectType.CHARACTERSTRING_VALUE:
|
|
202
|
+
defaultValue = "";
|
|
203
|
+
break;
|
|
204
|
+
case baEnum.ObjectType.BINARY_VALUE:
|
|
205
|
+
defaultValue = 0;
|
|
206
|
+
break;
|
|
207
|
+
case baEnum.ObjectType.ANALOG_VALUE:
|
|
208
|
+
defaultValue = false;
|
|
209
|
+
break;
|
|
210
|
+
default:
|
|
211
|
+
defaultValue = 0;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
that.addObject(data.request.values[0].value[0].value, defaultValue);
|
|
215
|
+
that.bacnetClient.client.simpleAckResponse(data.address, data.service, data.invokeId);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
that.bacnetClient.client.on('deleteObject', (data) => {
|
|
219
|
+
var type;
|
|
220
|
+
switch (data.request.objectType) {
|
|
221
|
+
case baEnum.ObjectType.CHARACTERSTRING_VALUE:
|
|
222
|
+
type = 'SV';
|
|
223
|
+
break;
|
|
224
|
+
case baEnum.ObjectType.BINARY_VALUE:
|
|
225
|
+
type = 'BV';
|
|
226
|
+
break;
|
|
227
|
+
case baEnum.ObjectType.ANALOG_VALUE:
|
|
228
|
+
type = 'AV';
|
|
229
|
+
break;
|
|
230
|
+
default:
|
|
231
|
+
type = 'AV';
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
var req = { body: { type: type, instance: data.request.instance } };
|
|
236
|
+
that.clearServerPoint(req).then(function (result) {
|
|
237
|
+
that.bacnetClient.client.simpleAckResponse(data.address, data.service, data.invokeId);
|
|
238
|
+
}).catch(function (error) {
|
|
239
|
+
console.log(error)
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
178
243
|
//do initial iAm broadcast when BACnet server starts
|
|
179
244
|
that.bacnetClient.client.iAmResponse(that.deviceId, baEnum.Segmentation.SEGMENTED_BOTH, that.vendorId);
|
|
180
245
|
}
|
|
@@ -336,6 +401,37 @@ class BacnetServer {
|
|
|
336
401
|
return null;
|
|
337
402
|
}
|
|
338
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Retrieves a specific property of an object based on the object ID, property ID, and instance number and modify his value.
|
|
406
|
+
*
|
|
407
|
+
* @param {number} objectId - The ID of the object type.
|
|
408
|
+
* @param {number} propId - The ID of the property to retrieve.
|
|
409
|
+
* @param {number} instance - The instance number of the object.
|
|
410
|
+
* @param {number} newValue - The the new value for update.
|
|
411
|
+
* @returns {any} The requested property value if found, otherwise null.
|
|
412
|
+
*/
|
|
413
|
+
modifyObject(objectId, propId, instance, newValue) { //TODO factorise with getObject
|
|
414
|
+
let that = this;
|
|
415
|
+
let objectGroup = that.objectStore[objectId];
|
|
416
|
+
|
|
417
|
+
if (Array.isArray(objectGroup)) {
|
|
418
|
+
for (let i = 0; i < objectGroup.length; i++) {
|
|
419
|
+
let object = objectGroup[i];
|
|
420
|
+
if (object[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER][0].value.instance == instance) {
|
|
421
|
+
let requestedProperty = object[propId];
|
|
422
|
+
if (requestedProperty !== null && requestedProperty !== undefined && typeof requestedProperty !== "undefined") {
|
|
423
|
+
that.objectStore[objectId][i][propId][0].value = newValue;
|
|
424
|
+
return requestedProperty;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
return objectGroup[propId];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
|
|
339
435
|
/**
|
|
340
436
|
* Retrieves the properties of a specific object instance from the objectStore based on the provided parameters.
|
|
341
437
|
*
|
|
@@ -503,7 +599,7 @@ class BacnetServer {
|
|
|
503
599
|
* @returns {Promise<Array>} A promise that resolves with an array of points sorted by instance number.
|
|
504
600
|
* @throws {Error} If an error occurs during the retrieval process.
|
|
505
601
|
*/
|
|
506
|
-
getServerPoints() {
|
|
602
|
+
getServerPoints(typeFilter = null, instanceFilter = null) {
|
|
507
603
|
let that = this;
|
|
508
604
|
let points = [];
|
|
509
605
|
|
|
@@ -515,11 +611,21 @@ class BacnetServer {
|
|
|
515
611
|
let instance = point[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER][0].value.instance;
|
|
516
612
|
let objectName = point[baEnum.PropertyIdentifier.OBJECT_NAME][0].value;
|
|
517
613
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
614
|
+
if (typeFilter === null && instanceFilter === null) {
|
|
615
|
+
points.push({
|
|
616
|
+
name: objectName,
|
|
617
|
+
type: "AV",
|
|
618
|
+
instance
|
|
619
|
+
});
|
|
620
|
+
} else {
|
|
621
|
+
if ((typeFilter === baEnum.ObjectType.ANALOG_VALUE) && (instanceFilter === instance)) {
|
|
622
|
+
points.push({
|
|
623
|
+
name: objectName,
|
|
624
|
+
type: "AV",
|
|
625
|
+
instance
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}
|
|
523
629
|
});
|
|
524
630
|
}
|
|
525
631
|
|
|
@@ -529,11 +635,21 @@ class BacnetServer {
|
|
|
529
635
|
let instance = point[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER][0].value.instance;
|
|
530
636
|
let objectName = point[baEnum.PropertyIdentifier.OBJECT_NAME][0].value;
|
|
531
637
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
638
|
+
if (typeFilter === null && instanceFilter === null) {
|
|
639
|
+
points.push({
|
|
640
|
+
name: objectName,
|
|
641
|
+
type: "SV",
|
|
642
|
+
instance
|
|
643
|
+
});
|
|
644
|
+
} else {
|
|
645
|
+
if ((typeFilter === baEnum.ObjectType.CHARACTERSTRING_VALUE) && (instanceFilter === instance)) {
|
|
646
|
+
points.push({
|
|
647
|
+
name: objectName,
|
|
648
|
+
type: "SV",
|
|
649
|
+
instance
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
}
|
|
537
653
|
});
|
|
538
654
|
}
|
|
539
655
|
|
|
@@ -543,11 +659,21 @@ class BacnetServer {
|
|
|
543
659
|
let instance = point[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER][0].value.instance;
|
|
544
660
|
let objectName = point[baEnum.PropertyIdentifier.OBJECT_NAME][0].value;
|
|
545
661
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
662
|
+
if (typeFilter === null && instanceFilter === null) {
|
|
663
|
+
points.push({
|
|
664
|
+
name: objectName,
|
|
665
|
+
type: "BV",
|
|
666
|
+
instance
|
|
667
|
+
});
|
|
668
|
+
} else {
|
|
669
|
+
if ((typeFilter === baEnum.ObjectType.BINARY_VALUE) && (instanceFilter === instance)) {
|
|
670
|
+
points.push({
|
|
671
|
+
name: objectName,
|
|
672
|
+
type: "BV",
|
|
673
|
+
instance
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
}
|
|
551
677
|
});
|
|
552
678
|
}
|
|
553
679
|
|