@bitpoolos/edge-bacnet 1.4.7 → 1.5.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 +22 -0
- package/bacnet_client.js +145 -95
- package/bacnet_device.js +18 -3
- package/bacnet_gateway.html +141 -7
- package/bacnet_gateway.js +4 -5
- package/bacnet_read.html +27 -14
- package/common.js +41 -1
- package/package.json +1 -1
- package/resources/node-bacstack-ts/dist/lib/client.js +161 -82
- package/resources/node-bacstack-ts/dist/lib/transport.js +57 -25
- package/resources/style.css +1 -6
package/bacnet_gateway.html
CHANGED
|
@@ -161,6 +161,7 @@
|
|
|
161
161
|
device_read_schedule_value: { value: "15", required: true },
|
|
162
162
|
device_read_schedule_options: { value: "Minutes", required: true },
|
|
163
163
|
deviceRangeRegisters: { value: [] },
|
|
164
|
+
portRangeRegisters: { value: [] },
|
|
164
165
|
cacheFileEnabled: { value: false, required: true },
|
|
165
166
|
sanitise_device_schedule: { value: "60", required: false },
|
|
166
167
|
sanitise_device_schedule_value: { value: "1", required: false },
|
|
@@ -347,7 +348,7 @@
|
|
|
347
348
|
contentType: "application/json",
|
|
348
349
|
data: JSON.stringify(jsonPayload),
|
|
349
350
|
success: function (result) { },
|
|
350
|
-
timeout:
|
|
351
|
+
timeout: 15000,
|
|
351
352
|
});
|
|
352
353
|
};
|
|
353
354
|
|
|
@@ -368,7 +369,8 @@
|
|
|
368
369
|
});
|
|
369
370
|
});
|
|
370
371
|
|
|
371
|
-
//device scan range matrix
|
|
372
|
+
//start device scan range matrix
|
|
373
|
+
|
|
372
374
|
$("#node-input-deviceIdRangeMatrix-container")
|
|
373
375
|
.css("min-width", "350px")
|
|
374
376
|
.editableList({
|
|
@@ -402,12 +404,13 @@
|
|
|
402
404
|
|
|
403
405
|
$(htmlRow).appendTo(fragment);
|
|
404
406
|
row[0].appendChild(fragment);
|
|
405
|
-
|
|
407
|
+
|
|
408
|
+
document.getElementById("node-input-reg-block-count-deviceIdRangeMatrix").innerHTML = $(
|
|
406
409
|
"#node-input-deviceIdRangeMatrix-container"
|
|
407
410
|
).editableList("length");
|
|
408
411
|
},
|
|
409
412
|
removeItem: function (data) {
|
|
410
|
-
document.getElementById("node-input-reg-block-count").innerHTML = $(
|
|
413
|
+
document.getElementById("node-input-reg-block-count-deviceIdRangeMatrix").innerHTML = $(
|
|
411
414
|
"#node-input-deviceIdRangeMatrix-container"
|
|
412
415
|
).editableList("length");
|
|
413
416
|
},
|
|
@@ -415,7 +418,7 @@
|
|
|
415
418
|
scrollOnAdd: false,
|
|
416
419
|
header: $("<div style='display:flex; padding:10px 10px 0px 5px; column-gap: 10px'>").append(
|
|
417
420
|
$.parseHTML(
|
|
418
|
-
"<div><span class='bp-matrix-heading'>Device ID Range(s)</span> </div><div class='bp-matrix-count' style='color: gray'><label id='node-input-reg-block-count' >0</label> </div>"
|
|
421
|
+
"<div><span class='bp-matrix-heading'>Device ID Range(s)</span> </div><div class='bp-matrix-count' style='color: gray'><label id='node-input-reg-block-count-deviceIdRangeMatrix' >0</label> </div>"
|
|
419
422
|
)
|
|
420
423
|
),
|
|
421
424
|
buttons: [
|
|
@@ -454,7 +457,7 @@
|
|
|
454
457
|
title: "Delete all registers",
|
|
455
458
|
click: function (evt) {
|
|
456
459
|
$("#node-input-deviceIdRangeMatrix-container").editableList("empty");
|
|
457
|
-
document.getElementById("node-input-reg-block-count").innerHTML = 0;
|
|
460
|
+
document.getElementById("node-input-reg-block-count-deviceIdRangeMatrix").innerHTML = 0;
|
|
458
461
|
},
|
|
459
462
|
},
|
|
460
463
|
],
|
|
@@ -472,7 +475,119 @@
|
|
|
472
475
|
});
|
|
473
476
|
}
|
|
474
477
|
|
|
475
|
-
document.getElementById("node-input-reg-block-count").innerHTML = node.deviceRangeRegisters.length;
|
|
478
|
+
document.getElementById("node-input-reg-block-count-deviceIdRangeMatrix").innerHTML = node.deviceRangeRegisters.length;
|
|
479
|
+
|
|
480
|
+
//end device id range matrix
|
|
481
|
+
|
|
482
|
+
//start port range matrix
|
|
483
|
+
|
|
484
|
+
$("#node-input-portRangeMatrix-container")
|
|
485
|
+
.css("min-width", "350px")
|
|
486
|
+
.editableList({
|
|
487
|
+
addItem: function (row, index, data) {
|
|
488
|
+
row.css({ overflow: "none", whiteSpace: "nowrap" });
|
|
489
|
+
let rowData = {};
|
|
490
|
+
if (data && typeof data.enabled == "boolean" && typeof data.start == "string" && typeof data.end == "string") {
|
|
491
|
+
if (data.enabled === true) {
|
|
492
|
+
rowData.enabled = "checked";
|
|
493
|
+
} else if (data.enabled === false) {
|
|
494
|
+
rowData.enabled = "";
|
|
495
|
+
}
|
|
496
|
+
rowData.start = data.start;
|
|
497
|
+
rowData.end = data.end;
|
|
498
|
+
} else {
|
|
499
|
+
rowData.enabled = "";
|
|
500
|
+
rowData.start = "";
|
|
501
|
+
rowData.end = "";
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
let itemNumber = index + 1;
|
|
505
|
+
let fragment = document.createDocumentFragment();
|
|
506
|
+
let htmlRow = `
|
|
507
|
+
<div class="form-row" id="node-input-port_range">
|
|
508
|
+
<span class='bp-matrix-itemIndex'> ${itemNumber} </span>
|
|
509
|
+
<a style="padding-left: 60px;">Start: </a><input type="number" id="node-input-port_range_entry_start" style="width: 125px;" min="0" max="64738" value="${rowData.start}" />
|
|
510
|
+
<a style="padding-left: 35px;">End: </a><input type="number" id="node-input-port_range_entry_end" style="width: 125px;" min="1" max="64738" value="${rowData.end}" />
|
|
511
|
+
<label class='bp-matrix-enable' for="node-input-port_range_entry"><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.port_range_entry"></span>
|
|
512
|
+
<input type="checkbox" id="node-input-port_range_entry_enabled" style="width: auto;" ${rowData.enabled} /> </label>
|
|
513
|
+
</div>`;
|
|
514
|
+
|
|
515
|
+
$(htmlRow).appendTo(fragment);
|
|
516
|
+
row[0].appendChild(fragment);
|
|
517
|
+
document.getElementById("node-input-reg-block-count-portRangeMatrix").innerHTML = $(
|
|
518
|
+
"#node-input-portRangeMatrix-container"
|
|
519
|
+
).editableList("length");
|
|
520
|
+
|
|
521
|
+
},
|
|
522
|
+
removeItem: function (data) {
|
|
523
|
+
document.getElementById("node-input-reg-block-count-portRangeMatrix").innerHTML = $(
|
|
524
|
+
"#node-input-portRangeMatrix-container"
|
|
525
|
+
).editableList("length");
|
|
526
|
+
},
|
|
527
|
+
removable: true,
|
|
528
|
+
scrollOnAdd: false,
|
|
529
|
+
header: $("<div style='display:flex; padding:10px 10px 0px 5px; column-gap: 10px'>").append(
|
|
530
|
+
$.parseHTML(
|
|
531
|
+
"<div><span class='bp-matrix-heading'>Port Range(s)</span> </div><div class='bp-matrix-count' style='color: gray'><label id='node-input-reg-block-count-portRangeMatrix' >0</label> </div>"
|
|
532
|
+
)
|
|
533
|
+
),
|
|
534
|
+
buttons: [
|
|
535
|
+
{
|
|
536
|
+
label: "json",
|
|
537
|
+
icon: "fa fa-share",
|
|
538
|
+
title: "Export tags as JSON to browser",
|
|
539
|
+
click: function (evt) {
|
|
540
|
+
var portRangeRegisters = $("#node-input-portRangeMatrix-container").editableList("items");
|
|
541
|
+
var mapItems = [];
|
|
542
|
+
portRangeRegisters.each(function (i) {
|
|
543
|
+
var registerMap = $(this);
|
|
544
|
+
var mapItem = {
|
|
545
|
+
enabled: registerMap.find("#node-input-port_range_entry_enabled").val(),
|
|
546
|
+
start: registerMap.find("#node-input-port_range_entry_start").val(),
|
|
547
|
+
end: registerMap.find("#node-input-port_range_entry_end").val(),
|
|
548
|
+
};
|
|
549
|
+
mapItems.push(mapItem);
|
|
550
|
+
});
|
|
551
|
+
var oMyBlob = new Blob(
|
|
552
|
+
[
|
|
553
|
+
JSON.stringify(mapItems, null, 0)
|
|
554
|
+
.replaceAll(/\[{/gi, "[\n{")
|
|
555
|
+
.replaceAll(/}\]/gi, "}\n]")
|
|
556
|
+
.replaceAll(/},/gi, "},\n")
|
|
557
|
+
.replaceAll(/{/gi, " {"),
|
|
558
|
+
],
|
|
559
|
+
{ type: "text/json" }
|
|
560
|
+
);
|
|
561
|
+
window.open(URL.createObjectURL(oMyBlob));
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
label: "delete",
|
|
566
|
+
icon: "fa-regular fa-trash-can",
|
|
567
|
+
title: "Delete all registers",
|
|
568
|
+
click: function (evt) {
|
|
569
|
+
$("#node-input-portRangeMatrix-container").editableList("empty");
|
|
570
|
+
document.getElementById("node-input-reg-block-count-portRangeMatrix").innerHTML = 0;
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
if (node.portRangeRegisters.length > 0) {
|
|
577
|
+
for (var i = 0; i < node.portRangeRegisters.length; i++) {
|
|
578
|
+
$("#node-input-portRangeMatrix-container").editableList("addItem", node.portRangeRegisters[i]);
|
|
579
|
+
}
|
|
580
|
+
} else {
|
|
581
|
+
$("#node-input-portRangeMatrix-container").editableList("addItem", {
|
|
582
|
+
enabled: true,
|
|
583
|
+
start: "47808",
|
|
584
|
+
end: "47808",
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
document.getElementById("node-input-reg-block-count-portRangeMatrix").innerHTML = node.portRangeRegisters.length;
|
|
589
|
+
//end port range matrix
|
|
590
|
+
|
|
476
591
|
},
|
|
477
592
|
oneditsave: function () {
|
|
478
593
|
let node = this;
|
|
@@ -503,6 +618,20 @@
|
|
|
503
618
|
};
|
|
504
619
|
node.deviceRangeRegisters.push(mapItem);
|
|
505
620
|
});
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
node.portRangeRegisters = [];
|
|
624
|
+
let portRanges = $("#node-input-portRangeMatrix-container").editableList("items");
|
|
625
|
+
portRanges.each(function (i) {
|
|
626
|
+
let map = $(this);
|
|
627
|
+
let mapItem = {
|
|
628
|
+
enabled: map.find("#node-input-port_range_entry_enabled").is(":checked"),
|
|
629
|
+
start: map.find("#node-input-port_range_entry_start").val(),
|
|
630
|
+
end: map.find("#node-input-port_range_entry_end").val(),
|
|
631
|
+
};
|
|
632
|
+
node.portRangeRegisters.push(mapItem);
|
|
633
|
+
});
|
|
634
|
+
|
|
506
635
|
},
|
|
507
636
|
});
|
|
508
637
|
|
|
@@ -652,6 +781,10 @@
|
|
|
652
781
|
<input type="text" id="node-input-local_device_port" placeholder="47808" />
|
|
653
782
|
</div>
|
|
654
783
|
|
|
784
|
+
<div class="form-row node-input-portRangeMatrix-container-row bp-row">
|
|
785
|
+
<ol id="node-input-portRangeMatrix-container"></ol>
|
|
786
|
+
</div>
|
|
787
|
+
|
|
655
788
|
<div class="form-row bp-row">
|
|
656
789
|
<label for="node-input-deviceId"
|
|
657
790
|
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.deviceId"></span> Device ID
|
|
@@ -662,6 +795,7 @@
|
|
|
662
795
|
<div class="form-row node-input-deviceIdRangeMatrix-container-row bp-row">
|
|
663
796
|
<ol id="node-input-deviceIdRangeMatrix-container"></ol>
|
|
664
797
|
</div>
|
|
798
|
+
|
|
665
799
|
</div>
|
|
666
800
|
|
|
667
801
|
<div id="read-discover-tab" style="display:none">
|
package/bacnet_gateway.js
CHANGED
|
@@ -33,6 +33,7 @@ module.exports = function (RED) {
|
|
|
33
33
|
this.retries = config.retries;
|
|
34
34
|
this.bacnetServer = nodeContext.get("bacnetServer") || null;
|
|
35
35
|
this.deviceRangeRegisters = config.deviceRangeRegisters;
|
|
36
|
+
this.portRangeRegisters = config.portRangeRegisters;
|
|
36
37
|
this.cacheFileEnabled = config.cacheFileEnabled;
|
|
37
38
|
this.sanitise_device_schedule = config.sanitise_device_schedule;
|
|
38
39
|
|
|
@@ -61,7 +62,8 @@ module.exports = function (RED) {
|
|
|
61
62
|
node.device_read_schedule,
|
|
62
63
|
node.retries,
|
|
63
64
|
node.cacheFileEnabled,
|
|
64
|
-
node.sanitise_device_schedule
|
|
65
|
+
node.sanitise_device_schedule,
|
|
66
|
+
node.portRangeRegisters.filter((ele) => ele.enabled === true)
|
|
65
67
|
);
|
|
66
68
|
|
|
67
69
|
nodeContext.set("bacnetConfig", node.bacnetConfig);
|
|
@@ -225,11 +227,9 @@ module.exports = function (RED) {
|
|
|
225
227
|
logOut("Error updating priorityQueue: ", error);
|
|
226
228
|
});
|
|
227
229
|
} else if (msg.testFunc == true) {
|
|
228
|
-
node.bacnetClient.testFunction(msg.address, msg.type, msg.instance, msg.property);
|
|
230
|
+
node.bacnetClient.testFunction(msg.address, msg.port, msg.type, msg.instance, msg.property);
|
|
229
231
|
} else if (msg.applyDisplayNames) {
|
|
230
|
-
|
|
231
232
|
node.status({ fill: "blue", shape: "dot", text: "Updating display names" });
|
|
232
|
-
|
|
233
233
|
setTimeout(() => {
|
|
234
234
|
node.status({});
|
|
235
235
|
}, 2000);
|
|
@@ -240,7 +240,6 @@ module.exports = function (RED) {
|
|
|
240
240
|
}).catch(function (error) {
|
|
241
241
|
logOut("Error in applyDisplayNames: ", error);
|
|
242
242
|
});
|
|
243
|
-
|
|
244
243
|
}
|
|
245
244
|
});
|
|
246
245
|
|
package/bacnet_read.html
CHANGED
|
@@ -422,20 +422,33 @@
|
|
|
422
422
|
if (device) {
|
|
423
423
|
const deviceName = device.deviceName;
|
|
424
424
|
const points = Object.keys(deviceObject);
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
425
|
+
if (points.length > 0) {
|
|
426
|
+
for (point in points) {
|
|
427
|
+
csvContent +=
|
|
428
|
+
ipAddress +
|
|
429
|
+
"," +
|
|
430
|
+
deviceId +
|
|
431
|
+
"," +
|
|
432
|
+
deviceName +
|
|
433
|
+
"," +
|
|
434
|
+
points[point] +
|
|
435
|
+
"," +
|
|
436
|
+
deviceObject[points[point]].meta.objectId.type +
|
|
437
|
+
"\r\n";
|
|
438
|
+
|
|
439
|
+
if (parseInt(point) == points.length - 1 && parseInt(key) == keys.length - 1) {
|
|
440
|
+
// last iteration
|
|
441
|
+
var csvBlob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
|
|
442
|
+
var link = document.createElement("a");
|
|
443
|
+
var url = URL.createObjectURL(csvBlob);
|
|
444
|
+
link.setAttribute("href", url);
|
|
445
|
+
link.setAttribute("download", "pointslist.csv");
|
|
446
|
+
document.body.appendChild(link);
|
|
447
|
+
link.click();
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
if (parseInt(key) == keys.length - 1) {
|
|
439
452
|
// last iteration
|
|
440
453
|
var csvBlob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
|
|
441
454
|
var link = document.createElement("a");
|
package/common.js
CHANGED
|
@@ -65,7 +65,8 @@ class BacnetClientConfig {
|
|
|
65
65
|
device_read_schedule,
|
|
66
66
|
retries,
|
|
67
67
|
cacheFileEnabled,
|
|
68
|
-
sanitise_device_schedule
|
|
68
|
+
sanitise_device_schedule,
|
|
69
|
+
portRangeMatrix
|
|
69
70
|
) {
|
|
70
71
|
this.apduTimeout = apduTimeout;
|
|
71
72
|
this.localIpAdrress = localIpAdrress;
|
|
@@ -83,9 +84,26 @@ class BacnetClientConfig {
|
|
|
83
84
|
this.retries = retries;
|
|
84
85
|
this.cacheFileEnabled = cacheFileEnabled;
|
|
85
86
|
this.sanitise_device_schedule = sanitise_device_schedule;
|
|
87
|
+
this.portRangeMatrix = this.generatePortRangeArray(portRangeMatrix);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
generatePortRangeArray(rangeMatrix) {
|
|
91
|
+
let portArray = [];
|
|
92
|
+
for (let x = 0; x < rangeMatrix.length; x++) {
|
|
93
|
+
let rangeEntry = rangeMatrix[x];
|
|
94
|
+
let start = parseInt(rangeEntry.start);
|
|
95
|
+
let end = parseInt(rangeEntry.end);
|
|
96
|
+
for (let i = start; i <= end; i++) {
|
|
97
|
+
portArray.push(i);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return portArray;
|
|
86
102
|
}
|
|
87
103
|
}
|
|
88
104
|
|
|
105
|
+
|
|
106
|
+
|
|
89
107
|
class ReadCommandConfig {
|
|
90
108
|
constructor(pointsToRead, objectProperties, decimalPrecision) {
|
|
91
109
|
this.pointsToRead = pointsToRead;
|
|
@@ -273,6 +291,26 @@ function decodeBitArray(size, bits) {
|
|
|
273
291
|
};
|
|
274
292
|
}
|
|
275
293
|
|
|
294
|
+
function getBacnetErrorString(classInt, codeInt) {
|
|
295
|
+
const classString = Object.keys(baEnum.ErrorClass).find(key => baEnum.ErrorClass[key] === classInt);
|
|
296
|
+
const codeString = Object.keys(baEnum.ErrorCode).find(key => baEnum.ErrorCode[key] === codeInt);
|
|
297
|
+
return `BacnetError - Class:${classString} - Code:${codeString}`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function parseBacnetError(error) {
|
|
301
|
+
let err = error.message;
|
|
302
|
+
if (err.includes("Class") && err.includes("Code")) {
|
|
303
|
+
const match = err.match(/Class:(\d+) - Code:(\d+)/);
|
|
304
|
+
if (match) {
|
|
305
|
+
err = getBacnetErrorString(parseInt(match[1], 10), parseInt(match[2], 10));
|
|
306
|
+
}
|
|
307
|
+
} else if (err.includes("ERR_TIMEOUT")) {
|
|
308
|
+
err = "Request TIMEOUT";
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return err;
|
|
312
|
+
};
|
|
313
|
+
|
|
276
314
|
module.exports = {
|
|
277
315
|
BacnetConfig,
|
|
278
316
|
BacnetClientConfig,
|
|
@@ -289,4 +327,6 @@ module.exports = {
|
|
|
289
327
|
Read_Config_Sync_Server,
|
|
290
328
|
isNumber,
|
|
291
329
|
decodeBitArray,
|
|
330
|
+
parseBacnetError,
|
|
331
|
+
getBacnetErrorString,
|
|
292
332
|
};
|