@bitpoolos/edge-bacnet 1.2.8 → 1.3.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 +76 -0
- package/README.md +20 -2
- package/bacnet_client.js +592 -760
- package/bacnet_device.js +122 -60
- package/bacnet_gateway.html +91 -75
- package/bacnet_gateway.js +161 -46
- package/bacnet_read.html +1025 -596
- package/bacnet_read.js +68 -84
- package/bacnet_server.js +187 -186
- package/bacnet_write.html +268 -24
- package/common.js +22 -4
- 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 +5 -6
- package/resources/style.css +321 -0
- package/treeBuilder.js +533 -0
package/bacnet_write.html
CHANGED
|
@@ -291,6 +291,171 @@
|
|
|
291
291
|
return false;
|
|
292
292
|
}
|
|
293
293
|
},
|
|
294
|
+
calculateMstpCount(slotProps) {
|
|
295
|
+
let count = 0;
|
|
296
|
+
slotProps.node.children.forEach(function (child) {
|
|
297
|
+
if (child.label.includes("MSTP")) {
|
|
298
|
+
count += child.children.length;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
return count;
|
|
303
|
+
},
|
|
304
|
+
settingDeviceName(slotProps) {
|
|
305
|
+
let app = this;
|
|
306
|
+
if (slotProps.node.settingDisplayName) {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
return false;
|
|
310
|
+
},
|
|
311
|
+
cancelDisplayNameDialog() {
|
|
312
|
+
let app = this;
|
|
313
|
+
app.deviceDisplayNameValue = "";
|
|
314
|
+
app.showDeviceNameDialog = false;
|
|
315
|
+
},
|
|
316
|
+
cancelPointNameDialog() {
|
|
317
|
+
let app = this;
|
|
318
|
+
app.pointDisplayNameValue = "";
|
|
319
|
+
app.showPointNameDialog = false;
|
|
320
|
+
},
|
|
321
|
+
onDeviceRightClick(slotProps, event) {
|
|
322
|
+
let app = this;
|
|
323
|
+
app.rightClickedDevice = slotProps;
|
|
324
|
+
event.preventDefault();
|
|
325
|
+
menu.style.setProperty("--mouse-x", event.clientX + "px");
|
|
326
|
+
menu.style.setProperty("--mouse-y", event.clientY + "px");
|
|
327
|
+
menu.style.display = "block";
|
|
328
|
+
},
|
|
329
|
+
onPointRightClick(slotProps, event) {
|
|
330
|
+
let app = this;
|
|
331
|
+
app.rightClickedPoint = slotProps;
|
|
332
|
+
event.preventDefault();
|
|
333
|
+
pointMenu.style.setProperty("--mouse-x", event.clientX + "px");
|
|
334
|
+
pointMenu.style.setProperty("--mouse-y", event.clientY + "px");
|
|
335
|
+
pointMenu.style.display = "block";
|
|
336
|
+
},
|
|
337
|
+
handleContextMenuClick(type) {
|
|
338
|
+
let app = this;
|
|
339
|
+
switch (type) {
|
|
340
|
+
case "purgeDevice":
|
|
341
|
+
app.purgeDevice(app.rightClickedDevice);
|
|
342
|
+
break;
|
|
343
|
+
case "updatePoints":
|
|
344
|
+
app.updatePointsForDevice(app.rightClickedDevice);
|
|
345
|
+
break;
|
|
346
|
+
case "addAllPoints":
|
|
347
|
+
app.addAllClicked(app.rightClickedDevice);
|
|
348
|
+
break;
|
|
349
|
+
case "removeAllPoints":
|
|
350
|
+
app.removeAllClicked(app.rightClickedDevice);
|
|
351
|
+
break;
|
|
352
|
+
case "setDeviceName":
|
|
353
|
+
app.showDeviceNameDialog = true;
|
|
354
|
+
app.deviceDisplayNameValue = app.rightClickedDevice.node.label;
|
|
355
|
+
break;
|
|
356
|
+
default:
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
handlePointContextMenuClick(type) {
|
|
361
|
+
let app = this;
|
|
362
|
+
switch (type) {
|
|
363
|
+
case "setPointName":
|
|
364
|
+
app.showPointNameDialog = true;
|
|
365
|
+
app.pointDisplayNameValue = app.rightClickedPoint.node.label;
|
|
366
|
+
break;
|
|
367
|
+
case "updatePoint":
|
|
368
|
+
app.updatePoint(app.rightClickedDevice);
|
|
369
|
+
break;
|
|
370
|
+
default:
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
purgeDevice(slotProps) {
|
|
375
|
+
let app = this;
|
|
376
|
+
let device = app.deviceList.find((ele) => {
|
|
377
|
+
if (ele.address.address) {
|
|
378
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
379
|
+
} else {
|
|
380
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
if (device) {
|
|
384
|
+
app.nodeService.purgeDevice(device).then(function (result) { });
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
updatePointsForDevice(slotProps) {
|
|
388
|
+
let app = this;
|
|
389
|
+
let device = app.deviceList.find((ele) => {
|
|
390
|
+
if (ele.address.address) {
|
|
391
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
392
|
+
} else {
|
|
393
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
if (device) {
|
|
397
|
+
app.nodeService.updatePointsForDevice(device).then(function (result) { });
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
updatePoint(slotProps) {
|
|
401
|
+
let app = this;
|
|
402
|
+
let device = app.deviceList.find((ele) => {
|
|
403
|
+
if (ele.address.address) {
|
|
404
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
405
|
+
} else {
|
|
406
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
if (device) {
|
|
410
|
+
app.nodeService.updatePoint(device).then(function (result) { });
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
setDeviceName() {
|
|
414
|
+
let app = this;
|
|
415
|
+
const slotProps = app.rightClickedDevice;
|
|
416
|
+
const displayName = app.deviceDisplayNameValue;
|
|
417
|
+
let device = app.deviceList.find((ele) => {
|
|
418
|
+
if (ele.address.address) {
|
|
419
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
420
|
+
} else {
|
|
421
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
if (device) {
|
|
426
|
+
app.nodeService.setDeviceDisplayName(device, displayName).then(function (result) {
|
|
427
|
+
if (result) {
|
|
428
|
+
slotProps.node.label = displayName;
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
app.showDeviceNameDialog = false;
|
|
434
|
+
},
|
|
435
|
+
setPointName() {
|
|
436
|
+
let app = this;
|
|
437
|
+
|
|
438
|
+
const slotProps = app.rightClickedPoint;
|
|
439
|
+
const pointDisplayName = app.pointDisplayNameValue;
|
|
440
|
+
const pointName = slotProps.node.pointName;
|
|
441
|
+
|
|
442
|
+
let device = app.deviceList.find((ele) => {
|
|
443
|
+
return ele.deviceName == slotProps.node.parentDevice;
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
if (device) {
|
|
447
|
+
let deviceAddress = app.getDeviceAddress(device.address);
|
|
448
|
+
let deviceKey = `${deviceAddress}-${device.deviceId}`;
|
|
449
|
+
|
|
450
|
+
app.nodeService.setPointDisplayName(deviceKey, pointName, pointDisplayName).then(function (result) {
|
|
451
|
+
if (result) {
|
|
452
|
+
slotProps.node.label = pointDisplayName;
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
app.showPointNameDialog = false;
|
|
458
|
+
},
|
|
294
459
|
},
|
|
295
460
|
components: {
|
|
296
461
|
"p-tree": primevue.tree,
|
|
@@ -337,6 +502,16 @@
|
|
|
337
502
|
document.getElementById("node-input-applicationTag").value = node.applicationTag;
|
|
338
503
|
document.getElementById("node-input-priority").value = node.priority;
|
|
339
504
|
|
|
505
|
+
var menu = document.querySelector(".context-menu");
|
|
506
|
+
window.addEventListener("click", (event) => {
|
|
507
|
+
menu.style.display = "none";
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
var pointMenu = document.querySelector(".point-context-menu");
|
|
511
|
+
window.addEventListener("click", (event) => {
|
|
512
|
+
pointMenu.style.display = "none";
|
|
513
|
+
});
|
|
514
|
+
|
|
340
515
|
//remove loading animation
|
|
341
516
|
let loadingGif = document.getElementById("loadingGif");
|
|
342
517
|
let loadingText = document.getElementById("loadingText");
|
|
@@ -431,11 +606,7 @@
|
|
|
431
606
|
.statusOffline {
|
|
432
607
|
color: red;
|
|
433
608
|
}
|
|
434
|
-
|
|
435
|
-
.deviceLabel {
|
|
436
|
-
position: absolute;
|
|
437
|
-
}
|
|
438
|
-
*/
|
|
609
|
+
|
|
439
610
|
.objectPropertiesLabel {
|
|
440
611
|
display: flex !important;
|
|
441
612
|
flex-direction: row;
|
|
@@ -562,6 +733,65 @@
|
|
|
562
733
|
</div>
|
|
563
734
|
|
|
564
735
|
<div id="node-input-tabs-content">
|
|
736
|
+
|
|
737
|
+
<!-- Start Device Context Menu -->
|
|
738
|
+
<ul
|
|
739
|
+
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">
|
|
740
|
+
<li class="context-menu-item" @click="handleContextMenuClick('purgeDevice')">
|
|
741
|
+
<a class="red-ui-menu-label">
|
|
742
|
+
<i class="pi pi-wrench context-menu-icon"></i>
|
|
743
|
+
<span class="context-menu-item-text">Purge Device</span>
|
|
744
|
+
</a>
|
|
745
|
+
</li>
|
|
746
|
+
<li class="context-menu-item" @click="handleContextMenuClick('updatePoints')">
|
|
747
|
+
<a class="red-ui-menu-label">
|
|
748
|
+
<i class="pi pi-refresh context-menu-icon"></i>
|
|
749
|
+
<span class="context-menu-item-text">Update Points</span>
|
|
750
|
+
</a>
|
|
751
|
+
</li>
|
|
752
|
+
<li class="red-ui-menu-divider"></li>
|
|
753
|
+
<li class="context-menu-item" @click="handleContextMenuClick('addAllPoints')">
|
|
754
|
+
<a class="red-ui-menu-label">
|
|
755
|
+
<i class="pi pi-plus-circle context-menu-icon"></i>
|
|
756
|
+
<span class="context-menu-item-text">Add All Points </span>
|
|
757
|
+
</a>
|
|
758
|
+
</li>
|
|
759
|
+
<li class="context-menu-item" @click="handleContextMenuClick('removeAllPoints')">
|
|
760
|
+
<a class="red-ui-menu-label">
|
|
761
|
+
<i class="pi pi-minus-circle context-menu-icon"></i>
|
|
762
|
+
<span class="context-menu-item-text">Remove All Points</span>
|
|
763
|
+
</a>
|
|
764
|
+
</li>
|
|
765
|
+
<li class="red-ui-menu-divider"></li>
|
|
766
|
+
<li class="context-menu-item" @click="handleContextMenuClick('setDeviceName')">
|
|
767
|
+
<a class="red-ui-menu-label">
|
|
768
|
+
<i class="pi pi-pencil context-menu-icon"></i>
|
|
769
|
+
<span class="context-menu-item-text">Set Device Name</span>
|
|
770
|
+
</a>
|
|
771
|
+
</li>
|
|
772
|
+
</ul>
|
|
773
|
+
<!-- End Device Context Menu -->
|
|
774
|
+
|
|
775
|
+
<!-- Start Point Context Menu -->
|
|
776
|
+
<ul
|
|
777
|
+
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">
|
|
778
|
+
<li class="context-menu-item" @click="handlePointContextMenuClick('setPointName')">
|
|
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 Point Name</span>
|
|
782
|
+
</a>
|
|
783
|
+
</li>
|
|
784
|
+
<li class="red-ui-menu-divider"></li>
|
|
785
|
+
<li class="context-menu-item" @click="handlePointContextMenuClick('updatePoint')">
|
|
786
|
+
<a class="red-ui-menu-label">
|
|
787
|
+
<i class="pi pi-refresh context-menu-icon"></i>
|
|
788
|
+
<span class="context-menu-item-text">Update Point</span>
|
|
789
|
+
</a>
|
|
790
|
+
</li>
|
|
791
|
+
</ul>
|
|
792
|
+
<!-- End Point Context Menu -->
|
|
793
|
+
|
|
794
|
+
|
|
565
795
|
<div id="read-networkTree-tab" style="display:none">
|
|
566
796
|
<div id="read-networkTree-tab-content" class="networkTreeContent" style="display:none">
|
|
567
797
|
<div>
|
|
@@ -574,30 +804,44 @@
|
|
|
574
804
|
<div id="deviceListApp">
|
|
575
805
|
<p-tree :value="devices" selectable="false" :filter="true" filterMode="lenient" v-if="hasData()">
|
|
576
806
|
<template #device="slotProps">
|
|
577
|
-
<div
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
807
|
+
<div @contextmenu="onDeviceRightClick(slotProps, $event)" class="p-treenode-label">
|
|
808
|
+
|
|
809
|
+
<div v-if="isDeviceActive(slotProps)" class="deviceLabelParent">
|
|
810
|
+
<b class="deviceLabel">
|
|
811
|
+
<span class="statusOnline deviceStatus dotOnline dot"></span>
|
|
812
|
+
{{slotProps.node.label}}
|
|
813
|
+
</b>
|
|
814
|
+
</div>
|
|
815
|
+
<div v-else>
|
|
816
|
+
<b class="deviceLabel">
|
|
817
|
+
<span class="statusOffline deviceStatus dotOffline dot"></span>
|
|
818
|
+
{{slotProps.node.label}}
|
|
819
|
+
</b>
|
|
820
|
+
</div>
|
|
821
|
+
|
|
582
822
|
</div>
|
|
583
823
|
</template>
|
|
584
824
|
|
|
585
825
|
<template #point="slotProps" v-model:class="pointContent">
|
|
586
|
-
<
|
|
587
|
-
|
|
588
|
-
<
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
826
|
+
<div @contextmenu="onDeviceRightClick(slotProps, $event)" class="p-treenode-label">
|
|
827
|
+
|
|
828
|
+
<b class="pointLabel">{{slotProps.node.label}}</b>
|
|
829
|
+
<template v-if="isSlotAdded(slotProps)">
|
|
830
|
+
<button class="addPointButton bacnetbutton">
|
|
831
|
+
<i class="pi pi-check-circle " style="color: #00AEEF;"></i>
|
|
832
|
+
</button>
|
|
833
|
+
</template>
|
|
834
|
+
<template v-else>
|
|
835
|
+
<button @click="addPointClicked(slotProps, this)" class="addPointButton bacnetbutton">
|
|
836
|
+
<i class="pi pi-plus-circle "></i>
|
|
837
|
+
</button>
|
|
838
|
+
</template>
|
|
839
|
+
|
|
840
|
+
<button @click="removePointClicked(slotProps, this)" class="minusPointButton bacnetbutton">
|
|
841
|
+
<i class="pi pi-minus-circle "></i>
|
|
595
842
|
</button>
|
|
596
|
-
</
|
|
843
|
+
</div>
|
|
597
844
|
|
|
598
|
-
<button @click="removePointClicked(slotProps, this)" class="minusPointButton bacnetbutton">
|
|
599
|
-
<i class="pi pi-minus-circle "></i>
|
|
600
|
-
</button>
|
|
601
845
|
</template>
|
|
602
846
|
</p-tree>
|
|
603
847
|
<div v-else style="text-align: center; padding-top: 20px;">
|
|
@@ -755,4 +999,4 @@
|
|
|
755
999
|
<li><a href="https://wiki.bitpool.com/">wiki.bitpool.com</a> - find more documentation.</li>
|
|
756
1000
|
<li><a href="https://bacnet.org/">BACnet</a> - find more about the protocol.</li>
|
|
757
1001
|
</ul>
|
|
758
|
-
</script>
|
|
1002
|
+
</script>
|
package/common.js
CHANGED
|
@@ -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.0",
|
|
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 */
|
|
@@ -671,11 +671,15 @@ const bacappDecodeApplicationData = (buffer, offset, maxOffset, objectType, prop
|
|
|
671
671
|
const result = bacappDecodeData(buffer, offset + len, maxOffset, tag.tagNumber, tag.value);
|
|
672
672
|
if (!result)
|
|
673
673
|
return;
|
|
674
|
-
|
|
674
|
+
let resObj = {
|
|
675
675
|
len: len + result.len,
|
|
676
676
|
type: result.type,
|
|
677
677
|
value: result.value
|
|
678
678
|
};
|
|
679
|
+
if (result.originalBitString) {
|
|
680
|
+
//protocols supported addition
|
|
681
|
+
resObj.originalBitString = result.originalBitString;
|
|
682
|
+
}
|
|
679
683
|
// HACK: Drop string specific handling ASAP
|
|
680
684
|
if (result.encoding !== undefined)
|
|
681
685
|
resObj.encoding = result.encoding;
|
|
@@ -796,9 +800,9 @@ const decodeReadAccessResult = (buffer, offset, apduLen) => {
|
|
|
796
800
|
return;
|
|
797
801
|
len++;
|
|
798
802
|
newEntry.value = [{
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
803
|
+
type: baEnum.ApplicationTags.ERROR,
|
|
804
|
+
value: err
|
|
805
|
+
}];
|
|
802
806
|
}
|
|
803
807
|
values.push(newEntry);
|
|
804
808
|
}
|
|
@@ -876,11 +880,13 @@ const bitstringSetBitsUsed = (bitString, bytesUsed, unusedBits) => {
|
|
|
876
880
|
const decodeBitstring = (buffer, offset, lenValue) => {
|
|
877
881
|
let len = 0;
|
|
878
882
|
const bitString = { value: [], bitsUsed: 0 };
|
|
883
|
+
const originalBitString = { value: [] };
|
|
879
884
|
if (lenValue > 0) {
|
|
880
885
|
const bytesUsed = lenValue - 1;
|
|
881
886
|
if (bytesUsed <= baEnum.ASN1_MAX_BITSTRING_BYTES) {
|
|
882
887
|
len = 1;
|
|
883
888
|
for (let i = 0; i < bytesUsed; i++) {
|
|
889
|
+
originalBitString.value.push(buffer[offset + len]);
|
|
884
890
|
bitString.value.push(byteReverseBits(buffer[offset + len++]));
|
|
885
891
|
}
|
|
886
892
|
const unusedBits = buffer[offset] & 0x07;
|
|
@@ -889,7 +895,8 @@ const decodeBitstring = (buffer, offset, lenValue) => {
|
|
|
889
895
|
}
|
|
890
896
|
return {
|
|
891
897
|
len: len,
|
|
892
|
-
value: bitString
|
|
898
|
+
value: bitString,
|
|
899
|
+
originalBitString: originalBitString
|
|
893
900
|
};
|
|
894
901
|
};
|
|
895
902
|
exports.decodeBitstring = decodeBitstring;
|
|
@@ -1033,6 +1040,10 @@ const bacappDecodeData = (buffer, offset, maxLength, tagDataType, lenValueType)
|
|
|
1033
1040
|
result = (0, exports.decodeBitstring)(buffer, offset, lenValueType);
|
|
1034
1041
|
value.len += result.len;
|
|
1035
1042
|
value.value = result.value;
|
|
1043
|
+
if (result.originalBitString) {
|
|
1044
|
+
//protocols supported addition
|
|
1045
|
+
value.originalBitString = result.originalBitString;
|
|
1046
|
+
}
|
|
1036
1047
|
break;
|
|
1037
1048
|
case baEnum.ApplicationTags.ENUMERATED:
|
|
1038
1049
|
result = (0, exports.decodeEnumerated)(buffer, offset, lenValueType);
|