@bitpoolos/edge-bacnet 1.2.7 → 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 +1662 -1572
- package/bacnet_device.js +122 -60
- package/bacnet_gateway.html +115 -71
- package/bacnet_gateway.js +165 -48
- package/bacnet_read.html +1025 -596
- package/bacnet_read.js +68 -84
- package/bacnet_server.js +187 -186
- package/bacnet_write.html +971 -738
- package/common.js +40 -28
- package/package.json +1 -1
- 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/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(
|
|
@@ -92,7 +93,8 @@ class BacnetClientConfig {
|
|
|
92
93
|
manual_instance_range_end,
|
|
93
94
|
device_read_schedule,
|
|
94
95
|
retries,
|
|
95
|
-
cacheFileEnabled
|
|
96
|
+
cacheFileEnabled,
|
|
97
|
+
sanitise_device_schedule
|
|
96
98
|
) {
|
|
97
99
|
this.apduTimeout = apduTimeout;
|
|
98
100
|
this.localIpAdrress = localIpAdrress;
|
|
@@ -109,6 +111,7 @@ class BacnetClientConfig {
|
|
|
109
111
|
this.device_read_schedule = device_read_schedule;
|
|
110
112
|
this.retries = retries;
|
|
111
113
|
this.cacheFileEnabled = cacheFileEnabled;
|
|
114
|
+
this.sanitise_device_schedule = sanitise_device_schedule;
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
117
|
|
|
@@ -179,7 +182,7 @@ const getIpAddress = function () {
|
|
|
179
182
|
}
|
|
180
183
|
}
|
|
181
184
|
|
|
182
|
-
if (os.version().includes("Ubuntu")) {
|
|
185
|
+
if (os.version().includes("Ubuntu") || os.version().includes("SMP")) {
|
|
183
186
|
let allInterfaceName = "All interfaces";
|
|
184
187
|
if (!results[allInterfaceName]) {
|
|
185
188
|
results[allInterfaceName] = [];
|
|
@@ -222,25 +225,17 @@ const doNodeRedRestart = function () {
|
|
|
222
225
|
// STORE CONFIG FUNCTION ==========================================================
|
|
223
226
|
//
|
|
224
227
|
// ================================================================================
|
|
225
|
-
function Store_Config(data) {
|
|
228
|
+
async function Store_Config(data) {
|
|
226
229
|
try {
|
|
227
|
-
fs.
|
|
228
|
-
if(err){
|
|
229
|
-
console.log("Store_Config
|
|
230
|
-
} else {
|
|
231
|
-
await fs.writeFile("edge-bacnet-datastore.cfg", data, {encoding: "utf8", flag: "w"}, (err) => {
|
|
232
|
-
if (err) {
|
|
233
|
-
console.log("Store_Config writeFile error: ", err);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
230
|
+
await fs.writeFile("edge-bacnet-datastore.cfg", data, { encoding: "utf8", flag: "w" }, (err) => {
|
|
231
|
+
if (err) {
|
|
232
|
+
console.log("Store_Config writeFile error: ", err);
|
|
236
233
|
}
|
|
237
234
|
});
|
|
238
|
-
} catch(e){
|
|
235
|
+
} catch (e) {
|
|
239
236
|
//do nothing
|
|
240
237
|
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
|
|
238
|
+
}
|
|
244
239
|
|
|
245
240
|
// READ CONFIG SYNC FUNCTION ======================================================
|
|
246
241
|
//
|
|
@@ -248,15 +243,13 @@ function Store_Config(data) {
|
|
|
248
243
|
function Read_Config_Sync() {
|
|
249
244
|
var data = "{}";
|
|
250
245
|
try {
|
|
251
|
-
data = fs.readFileSync("edge-bacnet-datastore.cfg", { encoding:
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
console.log("Read_Config_Sync error:", err);
|
|
255
|
-
data = '{}';
|
|
246
|
+
data = fs.readFileSync("edge-bacnet-datastore.cfg", { encoding: "utf8", flag: "r" });
|
|
247
|
+
} catch (err) {
|
|
248
|
+
data = "{}";
|
|
256
249
|
Store_Config(data);
|
|
257
250
|
}
|
|
258
251
|
return data;
|
|
259
|
-
}
|
|
252
|
+
}
|
|
260
253
|
|
|
261
254
|
// STORE CONFIG FUNCTION - BACNET SERVER ==========================================
|
|
262
255
|
//
|
|
@@ -268,7 +261,7 @@ async function Store_Config_Server(data) {
|
|
|
268
261
|
//console.log("Store_Config_Server writeFile error: ", err);
|
|
269
262
|
}
|
|
270
263
|
});
|
|
271
|
-
} catch (err) {}
|
|
264
|
+
} catch (err) { }
|
|
272
265
|
}
|
|
273
266
|
|
|
274
267
|
// READ CONFIG SYNC FUNCTION - BACNET SERVER ======================================
|
|
@@ -279,16 +272,34 @@ function Read_Config_Sync_Server() {
|
|
|
279
272
|
try {
|
|
280
273
|
data = fs.readFileSync("edge-bacnet-server-datastore.cfg", { encoding: "utf8", flag: "r" });
|
|
281
274
|
} catch (err) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
275
|
+
if (err.errno == -4058) {
|
|
276
|
+
data = "{}";
|
|
277
|
+
Store_Config_Server(data);
|
|
278
|
+
}
|
|
286
279
|
}
|
|
287
280
|
return data;
|
|
288
281
|
}
|
|
289
282
|
|
|
290
283
|
function isNumber(value) {
|
|
291
|
-
return value != null && typeof value ===
|
|
284
|
+
return value != null && typeof value === "number" && !isNaN(value);
|
|
285
|
+
}
|
|
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
|
+
};
|
|
292
303
|
}
|
|
293
304
|
|
|
294
305
|
module.exports = {
|
|
@@ -306,4 +317,5 @@ module.exports = {
|
|
|
306
317
|
Store_Config_Server,
|
|
307
318
|
Read_Config_Sync_Server,
|
|
308
319
|
isNumber,
|
|
320
|
+
decodeBitArray,
|
|
309
321
|
};
|
package/package.json
CHANGED
|
@@ -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);
|
|
@@ -95,7 +95,7 @@ class Client extends events_1.EventEmitter {
|
|
|
95
95
|
if (!result)
|
|
96
96
|
return debug('Couldn`t decode Error');
|
|
97
97
|
this._invokeCallback(invokeId, new Error('BacnetError - Class:' + result.class + ' - Code:' + result.code));
|
|
98
|
-
} catch(e){
|
|
98
|
+
} catch (e) {
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
_processAbort(invokeId, reason) {
|
|
@@ -619,14 +619,14 @@ class Client extends events_1.EventEmitter {
|
|
|
619
619
|
baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
|
|
620
620
|
this.sendBvlc(address, buffer);
|
|
621
621
|
this._addCallback(settings.invokeId, (err, data) => {
|
|
622
|
-
try{
|
|
622
|
+
try {
|
|
623
623
|
if (err)
|
|
624
624
|
return next(err);
|
|
625
625
|
const result = baServices.readProperty.decodeAcknowledge(data.buffer, data.offset, data.length);
|
|
626
626
|
if (!result)
|
|
627
627
|
return next(new Error('INVALID_DECODING'));
|
|
628
628
|
next(null, result);
|
|
629
|
-
} catch(e){
|
|
629
|
+
} catch (e) {
|
|
630
630
|
return next(e);
|
|
631
631
|
}
|
|
632
632
|
});
|
|
@@ -716,7 +716,6 @@ class Client extends events_1.EventEmitter {
|
|
|
716
716
|
baServices.readPropertyMultiple.encode(buffer, propertiesArray);
|
|
717
717
|
baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
|
|
718
718
|
this.sendBvlc(address, buffer);
|
|
719
|
-
//this.sendBvlc(address, buffer);
|
|
720
719
|
this._addCallback(settings.invokeId, (err, data) => {
|
|
721
720
|
try {
|
|
722
721
|
if (err)
|
|
@@ -725,7 +724,7 @@ class Client extends events_1.EventEmitter {
|
|
|
725
724
|
if (!result)
|
|
726
725
|
return next(new Error('INVALID_DECODING'));
|
|
727
726
|
next(null, result);
|
|
728
|
-
} catch(e){
|
|
727
|
+
} catch (e) {
|
|
729
728
|
return next(e);
|
|
730
729
|
}
|
|
731
730
|
});
|
|
@@ -1369,7 +1368,7 @@ class Client extends events_1.EventEmitter {
|
|
|
1369
1368
|
baNpdu.encode(buffer, baEnum.NpduControlPriority.NORMAL_MESSAGE, receiver);
|
|
1370
1369
|
baApdu.encodeComplexAck(buffer, baEnum.PduTypes.COMPLEX_ACK, baEnum.ConfirmedServiceChoice.READ_PROPERTY, invokeId);
|
|
1371
1370
|
baServices.readProperty.encodeAcknowledge(buffer, objectId, property.id, property.index, value);
|
|
1372
|
-
|
|
1371
|
+
baBvlc.encode(buffer.buffer, baEnum.BvlcResultPurpose.ORIGINAL_UNICAST_NPDU, buffer.offset);
|
|
1373
1372
|
this.sendBvlc(receiver, buffer);
|
|
1374
1373
|
}
|
|
1375
1374
|
readPropertyMultipleResponse(receiver, invokeId, values) {
|