@bitpoolos/edge-bacnet 1.2.8 → 1.3.1
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 +105 -0
- package/README.md +23 -2
- package/bacnet_client.js +843 -803
- package/bacnet_device.js +132 -60
- package/bacnet_gateway.html +92 -76
- package/bacnet_gateway.js +161 -46
- package/bacnet_read.html +1048 -598
- package/bacnet_read.js +68 -84
- package/bacnet_server.js +270 -205
- package/bacnet_write.html +329 -24
- package/common.js +23 -5
- package/package.json +2 -2
- package/resources/bitArray.js +167 -0
- package/resources/node-bacstack-ts/dist/lib/asn1.js +16 -5
- package/resources/node-bacstack-ts/dist/lib/client.js +6 -7
- package/resources/style.css +321 -0
- package/treeBuilder.js +545 -0
package/bacnet_write.html
CHANGED
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
pointsToWrite: ref([]),
|
|
43
43
|
nodeService: ref(new NodeService()),
|
|
44
44
|
deviceCount: ref(),
|
|
45
|
+
showDeviceNameDialog: ref(false),
|
|
46
|
+
showPointNameDialog: ref(false),
|
|
47
|
+
deviceDisplayNameValue: ref(),
|
|
48
|
+
pointDisplayNameValue: ref(),
|
|
45
49
|
};
|
|
46
50
|
},
|
|
47
51
|
setup() {
|
|
@@ -291,11 +295,184 @@
|
|
|
291
295
|
return false;
|
|
292
296
|
}
|
|
293
297
|
},
|
|
298
|
+
calculateMstpCount(slotProps) {
|
|
299
|
+
let count = 0;
|
|
300
|
+
slotProps.node.children.forEach(function (child) {
|
|
301
|
+
if (child.label.includes("MSTP")) {
|
|
302
|
+
count += child.children.length;
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
return count;
|
|
307
|
+
},
|
|
308
|
+
hasMstpDevices(slotProps) {
|
|
309
|
+
if (slotProps.node.children[1] && slotProps.node.children[1].children.length > 0) {
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
return false;
|
|
313
|
+
},
|
|
314
|
+
settingDeviceName(slotProps) {
|
|
315
|
+
let app = this;
|
|
316
|
+
if (slotProps.node.settingDisplayName) {
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
return false;
|
|
320
|
+
},
|
|
321
|
+
cancelDisplayNameDialog() {
|
|
322
|
+
let app = this;
|
|
323
|
+
app.deviceDisplayNameValue = "";
|
|
324
|
+
app.showDeviceNameDialog = false;
|
|
325
|
+
},
|
|
326
|
+
cancelPointNameDialog() {
|
|
327
|
+
let app = this;
|
|
328
|
+
app.pointDisplayNameValue = "";
|
|
329
|
+
app.showPointNameDialog = false;
|
|
330
|
+
},
|
|
331
|
+
onDeviceRightClick(slotProps, event) {
|
|
332
|
+
let app = this;
|
|
333
|
+
app.rightClickedDevice = slotProps;
|
|
334
|
+
event.preventDefault();
|
|
335
|
+
menu.style.setProperty("--mouse-x", event.clientX + "px");
|
|
336
|
+
menu.style.setProperty("--mouse-y", event.clientY + "px");
|
|
337
|
+
menu.style.display = "block";
|
|
338
|
+
},
|
|
339
|
+
onPointRightClick(slotProps, event) {
|
|
340
|
+
let app = this;
|
|
341
|
+
app.rightClickedPoint = slotProps;
|
|
342
|
+
event.preventDefault();
|
|
343
|
+
pointMenu.style.setProperty("--mouse-x", event.clientX + "px");
|
|
344
|
+
pointMenu.style.setProperty("--mouse-y", event.clientY + "px");
|
|
345
|
+
pointMenu.style.display = "block";
|
|
346
|
+
},
|
|
347
|
+
handleContextMenuClick(type) {
|
|
348
|
+
let app = this;
|
|
349
|
+
switch (type) {
|
|
350
|
+
case "purgeDevice":
|
|
351
|
+
app.purgeDevice(app.rightClickedDevice);
|
|
352
|
+
break;
|
|
353
|
+
case "updatePoints":
|
|
354
|
+
app.updatePointsForDevice(app.rightClickedDevice);
|
|
355
|
+
break;
|
|
356
|
+
case "addAllPoints":
|
|
357
|
+
app.addAllClicked(app.rightClickedDevice);
|
|
358
|
+
break;
|
|
359
|
+
case "removeAllPoints":
|
|
360
|
+
app.removeAllClicked(app.rightClickedDevice);
|
|
361
|
+
break;
|
|
362
|
+
case "setDeviceName":
|
|
363
|
+
app.showDeviceNameDialog = true;
|
|
364
|
+
app.deviceDisplayNameValue = app.rightClickedDevice.node.label;
|
|
365
|
+
break;
|
|
366
|
+
default:
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
handlePointContextMenuClick(type) {
|
|
371
|
+
let app = this;
|
|
372
|
+
switch (type) {
|
|
373
|
+
case "setPointName":
|
|
374
|
+
app.showPointNameDialog = true;
|
|
375
|
+
app.pointDisplayNameValue = app.rightClickedPoint.node.label;
|
|
376
|
+
break;
|
|
377
|
+
case "updatePoint":
|
|
378
|
+
app.updatePoint(app.rightClickedDevice);
|
|
379
|
+
break;
|
|
380
|
+
default:
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
purgeDevice(slotProps) {
|
|
385
|
+
let app = this;
|
|
386
|
+
let device = app.deviceList.find((ele) => {
|
|
387
|
+
if (ele.address.address) {
|
|
388
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
389
|
+
} else {
|
|
390
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
if (device) {
|
|
394
|
+
app.nodeService.purgeDevice(device).then(function (result) { });
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
updatePointsForDevice(slotProps) {
|
|
398
|
+
let app = this;
|
|
399
|
+
let device = app.deviceList.find((ele) => {
|
|
400
|
+
if (ele.address.address) {
|
|
401
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
402
|
+
} else {
|
|
403
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
if (device) {
|
|
407
|
+
app.nodeService.updatePointsForDevice(device).then(function (result) { });
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
updatePoint(slotProps) {
|
|
411
|
+
let app = this;
|
|
412
|
+
let device = app.deviceList.find((ele) => {
|
|
413
|
+
if (ele.address.address) {
|
|
414
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
415
|
+
} else {
|
|
416
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
if (device) {
|
|
420
|
+
app.nodeService.updatePoint(device).then(function (result) { });
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
setDeviceName() {
|
|
424
|
+
let app = this;
|
|
425
|
+
const slotProps = app.rightClickedDevice;
|
|
426
|
+
const displayName = app.deviceDisplayNameValue;
|
|
427
|
+
let device = app.deviceList.find((ele) => {
|
|
428
|
+
if (ele.address.address) {
|
|
429
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
430
|
+
} else {
|
|
431
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
if (device) {
|
|
436
|
+
app.nodeService.setDeviceDisplayName(device, displayName).then(function (result) {
|
|
437
|
+
if (result) {
|
|
438
|
+
slotProps.node.label = displayName;
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
app.showDeviceNameDialog = false;
|
|
444
|
+
},
|
|
445
|
+
setPointName() {
|
|
446
|
+
let app = this;
|
|
447
|
+
|
|
448
|
+
const slotProps = app.rightClickedPoint;
|
|
449
|
+
const pointDisplayName = app.pointDisplayNameValue;
|
|
450
|
+
const pointName = slotProps.node.pointName;
|
|
451
|
+
|
|
452
|
+
let device = app.deviceList.find((ele) => {
|
|
453
|
+
return ele.deviceName == slotProps.node.parentDevice;
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
if (device) {
|
|
457
|
+
let deviceAddress = app.getDeviceAddress(device.address);
|
|
458
|
+
let deviceKey = `${deviceAddress}-${device.deviceId}`;
|
|
459
|
+
|
|
460
|
+
app.nodeService.setPointDisplayName(deviceKey, pointName, pointDisplayName).then(function (result) {
|
|
461
|
+
if (result) {
|
|
462
|
+
slotProps.node.label = pointDisplayName;
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
app.showPointNameDialog = false;
|
|
468
|
+
},
|
|
294
469
|
},
|
|
295
470
|
components: {
|
|
296
471
|
"p-tree": primevue.tree,
|
|
297
472
|
"p-button": primevue.button,
|
|
298
473
|
"p-timeline": primevue.timeline,
|
|
474
|
+
"p-dialog": primevue.dialog,
|
|
475
|
+
"p-input-text": primevue.inputtext,
|
|
299
476
|
},
|
|
300
477
|
};
|
|
301
478
|
|
|
@@ -337,6 +514,16 @@
|
|
|
337
514
|
document.getElementById("node-input-applicationTag").value = node.applicationTag;
|
|
338
515
|
document.getElementById("node-input-priority").value = node.priority;
|
|
339
516
|
|
|
517
|
+
var menu = document.querySelector(".context-menu");
|
|
518
|
+
window.addEventListener("click", (event) => {
|
|
519
|
+
menu.style.display = "none";
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
var pointMenu = document.querySelector(".point-context-menu");
|
|
523
|
+
window.addEventListener("click", (event) => {
|
|
524
|
+
pointMenu.style.display = "none";
|
|
525
|
+
});
|
|
526
|
+
|
|
340
527
|
//remove loading animation
|
|
341
528
|
let loadingGif = document.getElementById("loadingGif");
|
|
342
529
|
let loadingText = document.getElementById("loadingText");
|
|
@@ -431,11 +618,7 @@
|
|
|
431
618
|
.statusOffline {
|
|
432
619
|
color: red;
|
|
433
620
|
}
|
|
434
|
-
|
|
435
|
-
.deviceLabel {
|
|
436
|
-
position: absolute;
|
|
437
|
-
}
|
|
438
|
-
*/
|
|
621
|
+
|
|
439
622
|
.objectPropertiesLabel {
|
|
440
623
|
display: flex !important;
|
|
441
624
|
flex-direction: row;
|
|
@@ -562,8 +745,110 @@
|
|
|
562
745
|
</div>
|
|
563
746
|
|
|
564
747
|
<div id="node-input-tabs-content">
|
|
748
|
+
|
|
749
|
+
<!-- Start Device Context Menu -->
|
|
750
|
+
<ul
|
|
751
|
+
class="red-ui-menu red-ui-menu-dropdown red-ui-menu-dropdown-direction-right red-ui-menu-dropdown-noicons red-ui-menu-dropdown-submenus context-menu">
|
|
752
|
+
<li class="context-menu-item" @click="handleContextMenuClick('purgeDevice')">
|
|
753
|
+
<a class="red-ui-menu-label">
|
|
754
|
+
<i class="pi pi-wrench context-menu-icon"></i>
|
|
755
|
+
<span class="context-menu-item-text">Purge Device</span>
|
|
756
|
+
</a>
|
|
757
|
+
</li>
|
|
758
|
+
<li class="context-menu-item" @click="handleContextMenuClick('updatePoints')">
|
|
759
|
+
<a class="red-ui-menu-label">
|
|
760
|
+
<i class="pi pi-refresh context-menu-icon"></i>
|
|
761
|
+
<span class="context-menu-item-text">Update Points</span>
|
|
762
|
+
</a>
|
|
763
|
+
</li>
|
|
764
|
+
<li class="red-ui-menu-divider"></li>
|
|
765
|
+
<li class="context-menu-item" @click="handleContextMenuClick('addAllPoints')">
|
|
766
|
+
<a class="red-ui-menu-label">
|
|
767
|
+
<i class="pi pi-plus-circle context-menu-icon"></i>
|
|
768
|
+
<span class="context-menu-item-text">Add All Points </span>
|
|
769
|
+
</a>
|
|
770
|
+
</li>
|
|
771
|
+
<li class="context-menu-item" @click="handleContextMenuClick('removeAllPoints')">
|
|
772
|
+
<a class="red-ui-menu-label">
|
|
773
|
+
<i class="pi pi-minus-circle context-menu-icon"></i>
|
|
774
|
+
<span class="context-menu-item-text">Remove All Points</span>
|
|
775
|
+
</a>
|
|
776
|
+
</li>
|
|
777
|
+
<li class="red-ui-menu-divider"></li>
|
|
778
|
+
<li class="context-menu-item" @click="handleContextMenuClick('setDeviceName')">
|
|
779
|
+
<a class="red-ui-menu-label">
|
|
780
|
+
<i class="pi pi-pencil context-menu-icon"></i>
|
|
781
|
+
<span class="context-menu-item-text">Set Device Name</span>
|
|
782
|
+
</a>
|
|
783
|
+
</li>
|
|
784
|
+
</ul>
|
|
785
|
+
<!-- End Device Context Menu -->
|
|
786
|
+
|
|
787
|
+
<!-- Start Point Context Menu -->
|
|
788
|
+
<ul
|
|
789
|
+
class="red-ui-menu red-ui-menu-dropdown red-ui-menu-dropdown-direction-right red-ui-menu-dropdown-noicons red-ui-menu-dropdown-submenus point-context-menu">
|
|
790
|
+
<li class="context-menu-item" @click="handlePointContextMenuClick('setPointName')">
|
|
791
|
+
<a class="red-ui-menu-label">
|
|
792
|
+
<i class="pi pi-pencil context-menu-icon"></i>
|
|
793
|
+
<span class="context-menu-item-text">Set Point Name</span>
|
|
794
|
+
</a>
|
|
795
|
+
</li>
|
|
796
|
+
<li class="red-ui-menu-divider"></li>
|
|
797
|
+
<li class="context-menu-item" @click="handlePointContextMenuClick('updatePoint')">
|
|
798
|
+
<a class="red-ui-menu-label">
|
|
799
|
+
<i class="pi pi-refresh context-menu-icon"></i>
|
|
800
|
+
<span class="context-menu-item-text">Update Point</span>
|
|
801
|
+
</a>
|
|
802
|
+
</li>
|
|
803
|
+
</ul>
|
|
804
|
+
<!-- End Point Context Menu -->
|
|
805
|
+
|
|
806
|
+
|
|
565
807
|
<div id="read-networkTree-tab" style="display:none">
|
|
566
808
|
<div id="read-networkTree-tab-content" class="networkTreeContent" style="display:none">
|
|
809
|
+
<p-dialog
|
|
810
|
+
v-model:visible="showDeviceNameDialog"
|
|
811
|
+
modal
|
|
812
|
+
header="Set Display Name"
|
|
813
|
+
:style="{ width: '25rem' }"
|
|
814
|
+
class="deviceNameDialog">
|
|
815
|
+
<div class="flex align-items-center gap-3 mb-3">
|
|
816
|
+
<label for="displayName" class="font-semibold">Name</label>
|
|
817
|
+
<p-input-text id="displayName" class="flex-auto" autocomplete="off" v-model="deviceDisplayNameValue" />
|
|
818
|
+
</div>
|
|
819
|
+
<div class="flex justify-content-end gap-2">
|
|
820
|
+
<p-button
|
|
821
|
+
class="diplayNameDialogCancel"
|
|
822
|
+
type="button"
|
|
823
|
+
label="Cancel"
|
|
824
|
+
severity="secondary"
|
|
825
|
+
@click="cancelDisplayNameDialog"></p-button>
|
|
826
|
+
<p-button class="diplayNameDialogSave" type="button" label="Save" @click="setDeviceName"></p-button>
|
|
827
|
+
</div>
|
|
828
|
+
</p-dialog>
|
|
829
|
+
|
|
830
|
+
<p-dialog
|
|
831
|
+
v-model:visible="showPointNameDialog"
|
|
832
|
+
modal
|
|
833
|
+
header="Set Point Name"
|
|
834
|
+
:style="{ width: '25rem' }"
|
|
835
|
+
class="deviceNameDialog">
|
|
836
|
+
<div class="flex align-items-center gap-3 mb-3">
|
|
837
|
+
<label for="displayName" class="font-semibold">Name</label>
|
|
838
|
+
<p-input-text id="displayName" class="flex-auto" autocomplete="off" v-model="pointDisplayNameValue" />
|
|
839
|
+
</div>
|
|
840
|
+
<div class="flex justify-content-end gap-2">
|
|
841
|
+
<p-button
|
|
842
|
+
class="diplayNameDialogCancel"
|
|
843
|
+
type="button"
|
|
844
|
+
label="Cancel"
|
|
845
|
+
severity="secondary"
|
|
846
|
+
@click="cancelPointNameDialog"></p-button>
|
|
847
|
+
<p-button class="diplayNameDialogSave" type="button" label="Save" @click="setPointName"></p-button>
|
|
848
|
+
</div>
|
|
849
|
+
</p-dialog>
|
|
850
|
+
|
|
851
|
+
|
|
567
852
|
<div>
|
|
568
853
|
<a class="countStatus" style="margin-left: 15px;">Count: {{deviceCount}} device(s)</a>
|
|
569
854
|
<button @click="getData()" class="reloadButton">
|
|
@@ -574,30 +859,50 @@
|
|
|
574
859
|
<div id="deviceListApp">
|
|
575
860
|
<p-tree :value="devices" selectable="false" :filter="true" filterMode="lenient" v-if="hasData()">
|
|
576
861
|
<template #device="slotProps">
|
|
577
|
-
<div
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
862
|
+
<div @contextmenu="onDeviceRightClick(slotProps, $event)" class="p-treenode-label">
|
|
863
|
+
|
|
864
|
+
<div v-if="isDeviceActive(slotProps)" class="deviceLabelParent">
|
|
865
|
+
<b class="deviceLabel">
|
|
866
|
+
<span class="statusOnline deviceStatus dotOnline dot"></span>
|
|
867
|
+
{{slotProps.node.label}}
|
|
868
|
+
<span v-if="hasMstpDevices(slotProps)" class="mstpDeviceCount"
|
|
869
|
+
> {{calculateMstpCount(slotProps)}} </span
|
|
870
|
+
>
|
|
871
|
+
</b>
|
|
872
|
+
</div>
|
|
873
|
+
<div v-else>
|
|
874
|
+
<b class="deviceLabel">
|
|
875
|
+
<span class="statusOffline deviceStatus dotOffline dot"></span>
|
|
876
|
+
{{slotProps.node.label}}
|
|
877
|
+
<span v-if="hasMstpDevices(slotProps)" class="mstpDeviceCount"
|
|
878
|
+
> {{calculateMstpCount(slotProps)}} </span
|
|
879
|
+
>
|
|
880
|
+
</b>
|
|
881
|
+
</div>
|
|
882
|
+
|
|
582
883
|
</div>
|
|
583
884
|
</template>
|
|
584
885
|
|
|
585
886
|
<template #point="slotProps" v-model:class="pointContent">
|
|
586
|
-
<
|
|
587
|
-
|
|
588
|
-
<
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
887
|
+
<div @contextmenu="onDeviceRightClick(slotProps, $event)" class="p-treenode-label">
|
|
888
|
+
|
|
889
|
+
<b class="pointLabel">{{slotProps.node.label}}</b>
|
|
890
|
+
<template v-if="isSlotAdded(slotProps)">
|
|
891
|
+
<button class="addPointButton bacnetbutton">
|
|
892
|
+
<i class="pi pi-check-circle " style="color: #00AEEF;"></i>
|
|
893
|
+
</button>
|
|
894
|
+
</template>
|
|
895
|
+
<template v-else>
|
|
896
|
+
<button @click="addPointClicked(slotProps, this)" class="addPointButton bacnetbutton">
|
|
897
|
+
<i class="pi pi-plus-circle "></i>
|
|
898
|
+
</button>
|
|
899
|
+
</template>
|
|
900
|
+
|
|
901
|
+
<button @click="removePointClicked(slotProps, this)" class="minusPointButton bacnetbutton">
|
|
902
|
+
<i class="pi pi-minus-circle "></i>
|
|
595
903
|
</button>
|
|
596
|
-
</
|
|
904
|
+
</div>
|
|
597
905
|
|
|
598
|
-
<button @click="removePointClicked(slotProps, this)" class="minusPointButton bacnetbutton">
|
|
599
|
-
<i class="pi pi-minus-circle "></i>
|
|
600
|
-
</button>
|
|
601
906
|
</template>
|
|
602
907
|
</p-tree>
|
|
603
908
|
<div v-else style="text-align: center; padding-top: 20px;">
|
|
@@ -755,4 +1060,4 @@
|
|
|
755
1060
|
<li><a href="https://wiki.bitpool.com/">wiki.bitpool.com</a> - find more documentation.</li>
|
|
756
1061
|
<li><a href="https://bacnet.org/">BACnet</a> - find more about the protocol.</li>
|
|
757
1062
|
</ul>
|
|
758
|
-
</script>
|
|
1063
|
+
</script>
|
package/common.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
MIT License Copyright 2021,
|
|
2
|
+
MIT License Copyright 2021, 2024 - Bitpool Pty Ltd
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const { createLogger, format, transports } = require("winston");
|
|
@@ -8,6 +8,7 @@ const os = require("os");
|
|
|
8
8
|
const { exec } = require("child_process");
|
|
9
9
|
const baEnum = require("./resources/node-bacstack-ts/dist/index.js").enum;
|
|
10
10
|
const fs = require("fs");
|
|
11
|
+
const { BitArray } = require("./resources/bitArray.js");
|
|
11
12
|
|
|
12
13
|
const logger = createLogger({
|
|
13
14
|
format: format.combine(
|
|
@@ -181,7 +182,7 @@ const getIpAddress = function () {
|
|
|
181
182
|
}
|
|
182
183
|
}
|
|
183
184
|
|
|
184
|
-
if (os.version().includes("Ubuntu")) {
|
|
185
|
+
if (os.version().includes("Ubuntu") || os.version().includes("SMP")) {
|
|
185
186
|
let allInterfaceName = "All interfaces";
|
|
186
187
|
if (!results[allInterfaceName]) {
|
|
187
188
|
results[allInterfaceName] = [];
|
|
@@ -244,7 +245,6 @@ function Read_Config_Sync() {
|
|
|
244
245
|
try {
|
|
245
246
|
data = fs.readFileSync("edge-bacnet-datastore.cfg", { encoding: "utf8", flag: "r" });
|
|
246
247
|
} catch (err) {
|
|
247
|
-
console.log("Read_Config_Sync error:", err);
|
|
248
248
|
data = "{}";
|
|
249
249
|
Store_Config(data);
|
|
250
250
|
}
|
|
@@ -261,7 +261,7 @@ async function Store_Config_Server(data) {
|
|
|
261
261
|
//console.log("Store_Config_Server writeFile error: ", err);
|
|
262
262
|
}
|
|
263
263
|
});
|
|
264
|
-
} catch (err) {}
|
|
264
|
+
} catch (err) { }
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
// READ CONFIG SYNC FUNCTION - BACNET SERVER ======================================
|
|
@@ -273,7 +273,6 @@ function Read_Config_Sync_Server() {
|
|
|
273
273
|
data = fs.readFileSync("edge-bacnet-server-datastore.cfg", { encoding: "utf8", flag: "r" });
|
|
274
274
|
} catch (err) {
|
|
275
275
|
if (err.errno == -4058) {
|
|
276
|
-
console.log("Edge-BACnet Server: No save file found, creating new file");
|
|
277
276
|
data = "{}";
|
|
278
277
|
Store_Config_Server(data);
|
|
279
278
|
}
|
|
@@ -285,6 +284,24 @@ function isNumber(value) {
|
|
|
285
284
|
return value != null && typeof value === "number" && !isNaN(value);
|
|
286
285
|
}
|
|
287
286
|
|
|
287
|
+
function decodeBitArray(size, bits) {
|
|
288
|
+
let array = [];
|
|
289
|
+
for (let i = 0; i < bits.length; i++) {
|
|
290
|
+
let bit = bits[i];
|
|
291
|
+
let bitString = bit.toString(2);
|
|
292
|
+
if (bitString.length < size) {
|
|
293
|
+
const remainingLength = size - bitString.length;
|
|
294
|
+
const backFillString = "0".repeat(remainingLength);
|
|
295
|
+
array.push(backFillString + bitString);
|
|
296
|
+
} else if (bitString.length == size) {
|
|
297
|
+
array.push(bitString);
|
|
298
|
+
}
|
|
299
|
+
if (i == bits.length - 1) {
|
|
300
|
+
return array;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
288
305
|
module.exports = {
|
|
289
306
|
BacnetConfig,
|
|
290
307
|
BacnetClientConfig,
|
|
@@ -300,4 +317,5 @@ module.exports = {
|
|
|
300
317
|
Store_Config_Server,
|
|
301
318
|
Read_Config_Sync_Server,
|
|
302
319
|
isNumber,
|
|
320
|
+
decodeBitArray,
|
|
303
321
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitpoolos/edge-bacnet",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "A bacnet gateway for node-red",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@plus4nodered/ts-node-bacnet": "^1.0.0-beta.2",
|
|
@@ -50,4 +50,4 @@
|
|
|
50
50
|
"type": "github",
|
|
51
51
|
"url": "git+https://github.com/bitpool/edge-bacnet.git"
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* BitArray DataType */
|
|
2
|
+
|
|
3
|
+
// Constructor
|
|
4
|
+
function BitArray(size, bits) {
|
|
5
|
+
// Private field - array for our bits
|
|
6
|
+
this.m_bits = new Array();
|
|
7
|
+
|
|
8
|
+
//.ctor - initialize as a copy of an array of true/false or from a numeric value
|
|
9
|
+
if (bits && bits.length) {
|
|
10
|
+
for (var i = 0; i < bits.length; i++)
|
|
11
|
+
this.m_bits.push(bits[i] ? BitArray._ON : BitArray._OFF);
|
|
12
|
+
} else if (!isNaN(bits)) {
|
|
13
|
+
this.m_bits = BitArray.shred(bits).m_bits;
|
|
14
|
+
}
|
|
15
|
+
if (size && this.m_bits.length != size) {
|
|
16
|
+
if (this.m_bits.length < size) {
|
|
17
|
+
for (var i = this.m_bits.length; i < size; i++) {
|
|
18
|
+
this.m_bits.push(BitArray._OFF);
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
for (var i = size; i > this.m_bits.length; i--) {
|
|
22
|
+
this.m_bits.pop();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* BitArray PUBLIC INSTANCE METHODS */
|
|
29
|
+
|
|
30
|
+
// read-only property - number of bits
|
|
31
|
+
BitArray.prototype.getLength = function () { return this.m_bits.length; };
|
|
32
|
+
|
|
33
|
+
// accessor - get bit at index
|
|
34
|
+
BitArray.prototype.getAt = function (index) {
|
|
35
|
+
if (index < this.m_bits.length) {
|
|
36
|
+
return this.m_bits[index];
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
};
|
|
40
|
+
// accessor - set bit at index
|
|
41
|
+
BitArray.prototype.setAt = function (index, value) {
|
|
42
|
+
if (index < this.m_bits.length) {
|
|
43
|
+
this.m_bits[index] = value ? BitArray._ON : BitArray._OFF;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// resize the bit array (append new false/0 indexes)
|
|
48
|
+
BitArray.prototype.resize = function (newSize) {
|
|
49
|
+
var tmp = new Array();
|
|
50
|
+
for (var i = 0; i < newSize; i++) {
|
|
51
|
+
if (i < this.m_bits.length) {
|
|
52
|
+
tmp.push(this.m_bits[i]);
|
|
53
|
+
} else {
|
|
54
|
+
tmp.push(BitArray._OFF);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
this.m_bits = tmp;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Get the complimentary bit array (i.e., 01 compliments 10)
|
|
61
|
+
BitArray.prototype.getCompliment = function () {
|
|
62
|
+
var result = new BitArray(this.m_bits.length);
|
|
63
|
+
for (var i = 0; i < this.m_bits.length; i++) {
|
|
64
|
+
result.setAt(i, this.m_bits[i] ? BitArray._OFF : BitArray._ON);
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Get the string representation ("101010")
|
|
70
|
+
BitArray.prototype.toString = function () {
|
|
71
|
+
var s = new String();
|
|
72
|
+
for (var i = 0; i < this.m_bits.length; i++) {
|
|
73
|
+
s = s.concat(this.m_bits[i] === BitArray._ON ? "1" : "0");
|
|
74
|
+
}
|
|
75
|
+
return s;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Get the numeric value
|
|
79
|
+
BitArray.prototype.toNumber = function () {
|
|
80
|
+
var pow = 0;
|
|
81
|
+
var n = 0;
|
|
82
|
+
for (var i = this.m_bits.length - 1; i >= 0; i--) {
|
|
83
|
+
if (this.m_bits[i] === BitArray._ON) {
|
|
84
|
+
n += Math.pow(2, pow);
|
|
85
|
+
}
|
|
86
|
+
pow++;
|
|
87
|
+
}
|
|
88
|
+
return n;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/* STATIC METHODS */
|
|
92
|
+
|
|
93
|
+
// Get the union of two bit arrays
|
|
94
|
+
BitArray.getUnion = function (bitArray1, bitArray2) {
|
|
95
|
+
var len = BitArray._getLen(bitArray1, bitArray2, true);
|
|
96
|
+
var result = new BitArray(len);
|
|
97
|
+
for (var i = 0; i < len; i++) {
|
|
98
|
+
result.setAt(i, BitArray._union(bitArray1.getAt(i), bitArray2.getAt(i)));
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Get the intersection of two bit arrays
|
|
104
|
+
BitArray.getIntersection = function (bitArray1, bitArray2) {
|
|
105
|
+
var len = BitArray._getLen(bitArray1, bitArray2, true);
|
|
106
|
+
var result = new BitArray(len);
|
|
107
|
+
for (var i = 0; i < len; i++) {
|
|
108
|
+
result.setAt(i, BitArray._intersect(bitArray1.getAt(i), bitArray2.getAt(i)));
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Get the difference between to bit arrays
|
|
114
|
+
BitArray.getDifference = function (bitArray1, bitArray2) {
|
|
115
|
+
var len = BitArray._getLen(bitArray1, bitArray2, true);
|
|
116
|
+
var result = new BitArray(len);
|
|
117
|
+
for (var i = 0; i < len; i++) {
|
|
118
|
+
result.setAt(i, BitArray._difference(bitArray1.getAt(i), bitArray2.getAt(i)));
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Convert a number into a bit array
|
|
124
|
+
BitArray.shred = function (number) {
|
|
125
|
+
var bits = new Array();
|
|
126
|
+
var q = number;
|
|
127
|
+
do {
|
|
128
|
+
bits.push(q % 2);
|
|
129
|
+
q = Math.floor(q / 2);
|
|
130
|
+
} while (q > 0);
|
|
131
|
+
return new BitArray(bits.length, bits.reverse());
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/* BitArray PRIVATE STATIC CONSTANTS */
|
|
135
|
+
BitArray._ON = 1;
|
|
136
|
+
BitArray._OFF = 0;
|
|
137
|
+
|
|
138
|
+
/* BitArray PRIVATE STATIC METHODS */
|
|
139
|
+
|
|
140
|
+
// Calculate the intersection of two bits
|
|
141
|
+
BitArray._intersect = function (bit1, bit2) {
|
|
142
|
+
return bit1 === BitArray._ON && bit2 === BitArray._ON ? BitArray._ON : BitArray._OFF;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Calculate the union of two bits
|
|
146
|
+
BitArray._union = function (bit1, bit2) {
|
|
147
|
+
return bit1 === BitArray._ON || bit2 === BitArray._ON ? BitArray._ON : BitArray._OFF;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Calculate the difference of two bits
|
|
151
|
+
BitArray._difference = function (bit1, bit2) {
|
|
152
|
+
return bit1 === BitArray._ON && bit2 !== BitArray._ON ? BitArray._ON : BitArray._OFF;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Get the longest or shortest (smallest) length of the two bit arrays
|
|
156
|
+
BitArray._getLen = function (bitArray1, bitArray2, smallest) {
|
|
157
|
+
var l1 = bitArray1.getLength();
|
|
158
|
+
var l2 = bitArray2.getLength();
|
|
159
|
+
|
|
160
|
+
return l1 > l2 ? smallest ? l2 : l1 : smallest ? l2 : l1;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
module.exports = {
|
|
164
|
+
BitArray,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* END BitArray DataType */
|