@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/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 v-if="isDeviceActive(slotProps)" class="deviceLabelParent">
578
- <b class="deviceLabel">{{slotProps.node.label}} <b class="statusOnline deviceStatus">Online</b></b>
579
- </div>
580
- <div v-else>
581
- <b class="deviceLabel">{{slotProps.node.label}} <b class="statusOffline deviceStatus">Offline</b></b>
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
+ >&nbsp; {{calculateMstpCount(slotProps)}} &nbsp;</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
+ >&nbsp; {{calculateMstpCount(slotProps)}} &nbsp;</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
- <b class="pointLabel">{{slotProps.node.label}}</b>
587
- <template v-if="isSlotAdded(slotProps)">
588
- <button class="addPointButton bacnetbutton">
589
- <i class="pi pi-check-circle " style="color: #00AEEF;"></i>
590
- </button>
591
- </template>
592
- <template v-else>
593
- <button @click="addPointClicked(slotProps, this)" class="addPointButton bacnetbutton">
594
- <i class="pi pi-plus-circle "></i>
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
- </template>
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, 2022 - Bitpool Pty Ltd
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.2.8",
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 */