@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
|
@@ -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) {
|
|
@@ -430,7 +430,7 @@ class Client extends events_1.EventEmitter {
|
|
|
430
430
|
const result = baNpdu.decode(buffer, offset);
|
|
431
431
|
var addressObject, destAddress;
|
|
432
432
|
// @todo a MSTP Dest & Source could occur. this case is not handled.
|
|
433
|
-
if (typeof (result.source) != 'undefined') {
|
|
433
|
+
if (typeof (result.source) != 'undefined' && result && result.source) {
|
|
434
434
|
addressObject = result.source;
|
|
435
435
|
addressObject.ip = remoteAddress;
|
|
436
436
|
} else {
|
|
@@ -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) {
|
package/resources/style.css
CHANGED
|
@@ -8,4 +8,325 @@
|
|
|
8
8
|
background: url("icons/icon-read.svg") no-repeat !important;
|
|
9
9
|
width: 20px;
|
|
10
10
|
height: 20px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/* Read node styles */
|
|
17
|
+
|
|
18
|
+
.p-treenode-label {
|
|
19
|
+
color: black;
|
|
20
|
+
width: 100%;
|
|
21
|
+
}
|
|
22
|
+
.p-tree {
|
|
23
|
+
background: inherit !important;
|
|
24
|
+
border: inherit !important;
|
|
25
|
+
padding-right: 0px !important;
|
|
26
|
+
}
|
|
27
|
+
.p-button {
|
|
28
|
+
margin-right: .5rem;
|
|
29
|
+
}
|
|
30
|
+
.addPointButton {
|
|
31
|
+
float: right;
|
|
32
|
+
}
|
|
33
|
+
.minusPointButton {
|
|
34
|
+
float: right;
|
|
35
|
+
}
|
|
36
|
+
.addPointButton:hover, .minusPointButton:hover {
|
|
37
|
+
background: #d5d5d5 !important;
|
|
38
|
+
}
|
|
39
|
+
.pointLabel {
|
|
40
|
+
font-weight: 400;
|
|
41
|
+
}
|
|
42
|
+
.deviceLabel {
|
|
43
|
+
font-weight: 100;
|
|
44
|
+
}
|
|
45
|
+
.removeAllButton {
|
|
46
|
+
float: right;
|
|
47
|
+
}
|
|
48
|
+
.addAllButton {
|
|
49
|
+
float: right;
|
|
50
|
+
}
|
|
51
|
+
.bacnetbutton {
|
|
52
|
+
background: none;
|
|
53
|
+
border: none;
|
|
54
|
+
color: black;
|
|
55
|
+
font-weight: 400;
|
|
56
|
+
}
|
|
57
|
+
.bacnetbutton:hover {
|
|
58
|
+
background: #f0f0f0;
|
|
59
|
+
border-radius: 10px;
|
|
60
|
+
}
|
|
61
|
+
.allFunctionsText {
|
|
62
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
|
|
63
|
+
padding-left: 5px;
|
|
64
|
+
}
|
|
65
|
+
.p-treenode-children > li > .p-treenode-children > li {
|
|
66
|
+
font-size: 14px;
|
|
67
|
+
color: #b5b5b5 !important;
|
|
68
|
+
}
|
|
69
|
+
.p-treenode-children > li > .p-treenode-children > .p-treenode-label {
|
|
70
|
+
color: #b5b5b5 !important;
|
|
71
|
+
}
|
|
72
|
+
.p-tree-toggler:enabled:hover {
|
|
73
|
+
background: #d5d5d5 !important;
|
|
74
|
+
}
|
|
75
|
+
.p-tree-toggler:focus {
|
|
76
|
+
border: none !important;
|
|
77
|
+
box-shadow: none !important;
|
|
78
|
+
}
|
|
79
|
+
.deviceStatus {
|
|
80
|
+
font-size: 10px;
|
|
81
|
+
}
|
|
82
|
+
.statusOnline {
|
|
83
|
+
color: #11c511;
|
|
84
|
+
}
|
|
85
|
+
.statusOffline {
|
|
86
|
+
color: red;
|
|
87
|
+
}
|
|
88
|
+
.readDeviceList {
|
|
89
|
+
.deviceLabel {
|
|
90
|
+
position: absolute;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
.objectPropertiesLabel {
|
|
94
|
+
display: flex !important;
|
|
95
|
+
flex-direction: row;
|
|
96
|
+
align-items: center;
|
|
97
|
+
}
|
|
98
|
+
.p-tree .p-tree-container .p-treenode .p-treenode-content:focus {
|
|
99
|
+
outline: 0 none !important;
|
|
100
|
+
outline-offset: 0 !important;
|
|
101
|
+
box-shadow: inset 0 0 0 1px #bdbdbd !important;
|
|
102
|
+
}
|
|
103
|
+
.checkbox-round {
|
|
104
|
+
width: 13px !important;
|
|
105
|
+
height: 13px !important;
|
|
106
|
+
background-color: white;
|
|
107
|
+
border-radius: 50%;
|
|
108
|
+
vertical-align: middle;
|
|
109
|
+
border: 1px solid #ddd;
|
|
110
|
+
appearance: none;
|
|
111
|
+
-webkit-appearance: none;
|
|
112
|
+
outline: none;
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
}
|
|
115
|
+
.checkbox-round:checked {
|
|
116
|
+
background-color: #00AEEF;
|
|
117
|
+
}
|
|
118
|
+
.checkbox-round:focus {
|
|
119
|
+
outline: none !important;
|
|
120
|
+
}
|
|
121
|
+
.p-tree-filter:focus, .p-tree-filter:focus-visible, .p-inputtext:enabled:hover {
|
|
122
|
+
box-shadow: inset 0 0 0 0.15rem #dfdcdc !important;
|
|
123
|
+
border-color: transparent !important;
|
|
124
|
+
}
|
|
125
|
+
.bacnetbutton > .pi {
|
|
126
|
+
display: flex;
|
|
127
|
+
}
|
|
128
|
+
.p-tree-container {
|
|
129
|
+
margin: 0 !important;
|
|
130
|
+
}
|
|
131
|
+
.reloadButtonIcon {
|
|
132
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
|
|
133
|
+
}
|
|
134
|
+
.exportReadListButton {
|
|
135
|
+
border: none;
|
|
136
|
+
background: none;
|
|
137
|
+
font-size: 14px !important;
|
|
138
|
+
float: left;
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
}
|
|
142
|
+
.exportReadListButton:hover {
|
|
143
|
+
background-color: #d5d5d5;
|
|
144
|
+
border-radius: 5px;
|
|
145
|
+
}
|
|
146
|
+
.removeAllDevicesButton {
|
|
147
|
+
border: none;
|
|
148
|
+
background: none;
|
|
149
|
+
font-size: 14px !important;
|
|
150
|
+
float: right;
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
}
|
|
154
|
+
.removeAllDevicesButton:hover {
|
|
155
|
+
background-color: #d5d5d5;
|
|
156
|
+
border-radius: 10px;
|
|
157
|
+
}
|
|
158
|
+
.reloadButton {
|
|
159
|
+
border: none;
|
|
160
|
+
background: none;
|
|
161
|
+
font-size: 14px !important;
|
|
162
|
+
float: right;
|
|
163
|
+
}
|
|
164
|
+
.rebuildDataButton {
|
|
165
|
+
border: none;
|
|
166
|
+
background: none;
|
|
167
|
+
font-size: 14px !important;
|
|
168
|
+
float: right;
|
|
169
|
+
}
|
|
170
|
+
.reloadButton:hover {
|
|
171
|
+
background-color: #d5d5d5;
|
|
172
|
+
border-radius: 10px;
|
|
173
|
+
}
|
|
174
|
+
.rebuildDataButton:hover {
|
|
175
|
+
background-color: #d5d5d5;
|
|
176
|
+
border-radius: 10px;
|
|
177
|
+
}
|
|
178
|
+
.headerDiv {
|
|
179
|
+
display: flex;
|
|
180
|
+
flex-direction: row;
|
|
181
|
+
flex-wrap: nowrap;
|
|
182
|
+
justify-content: space-around;
|
|
183
|
+
height: 20px;
|
|
184
|
+
}
|
|
185
|
+
.msgTypeDiv {
|
|
186
|
+
display: flex;
|
|
187
|
+
align-items: flex-start;
|
|
188
|
+
flex-direction: column;
|
|
189
|
+
}
|
|
190
|
+
.p-progressbar .p-progressbar-value {
|
|
191
|
+
background: #00AEEF !important;
|
|
192
|
+
}
|
|
193
|
+
.p-treenode-label {
|
|
194
|
+
overflow: hidden;
|
|
195
|
+
white-space: nowrap;
|
|
196
|
+
text-overflow: ellipsis;
|
|
197
|
+
}
|
|
198
|
+
.buttonGroup {
|
|
199
|
+
padding-left: 7px;
|
|
200
|
+
}
|
|
201
|
+
#read-readList-tab {
|
|
202
|
+
display: flex;
|
|
203
|
+
flex-direction: column;
|
|
204
|
+
}
|
|
205
|
+
.removeAllDevicesDiv {
|
|
206
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
207
|
+
padding-right: 20px;
|
|
208
|
+
}
|
|
209
|
+
.p-tree .p-treenode-children {
|
|
210
|
+
padding: 0 !important;
|
|
211
|
+
}
|
|
212
|
+
.p-tree-toggler .p-link {
|
|
213
|
+
width: 25px !important;
|
|
214
|
+
}
|
|
215
|
+
.dot {
|
|
216
|
+
height: 13px;
|
|
217
|
+
width: 13px;
|
|
218
|
+
border: 0.75px solid #777777;
|
|
219
|
+
border-radius: 50%;
|
|
220
|
+
display: inline-block;
|
|
221
|
+
margin-right: 5px;
|
|
222
|
+
}
|
|
223
|
+
.dotOnline {
|
|
224
|
+
background-color: #11c511;
|
|
225
|
+
}
|
|
226
|
+
.dotOffline {
|
|
227
|
+
background-color: red;
|
|
228
|
+
}
|
|
229
|
+
.mstpDeviceCount {
|
|
230
|
+
margin-left: 10px;
|
|
231
|
+
background-color: #00aeef;
|
|
232
|
+
color: #ffffff;
|
|
233
|
+
border-radius: 45%;
|
|
234
|
+
font-size: 12px;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.point-context-menu {
|
|
238
|
+
--mouse-x: 0;
|
|
239
|
+
--mouse-y: 0;
|
|
240
|
+
display: none;
|
|
241
|
+
position: fixed;
|
|
242
|
+
margin: 0;
|
|
243
|
+
left: 0;
|
|
244
|
+
top: 0;
|
|
245
|
+
/* The following line is responsible for all the magic */
|
|
246
|
+
transform: translateX(min(var(--mouse-x), calc(100vw - 100%))) translateY(min(var(--mouse-y), calc(100vh - 100%)));
|
|
247
|
+
z-index: 10;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
.context-menu {
|
|
252
|
+
--mouse-x: 0;
|
|
253
|
+
--mouse-y: 0;
|
|
254
|
+
display: none;
|
|
255
|
+
position: fixed;
|
|
256
|
+
margin: 0;
|
|
257
|
+
left: 0;
|
|
258
|
+
top: 0;
|
|
259
|
+
/* The following line is responsible for all the magic */
|
|
260
|
+
transform: translateX(min(var(--mouse-x), calc(100vw - 100%))) translateY(min(var(--mouse-y), calc(100vh - 100%)));
|
|
261
|
+
z-index: 10;
|
|
262
|
+
}
|
|
263
|
+
.context-menu-container {
|
|
264
|
+
flex-direction: column;
|
|
265
|
+
justify-content: space-evenly;
|
|
266
|
+
align-items: flex-start;
|
|
267
|
+
width: 100%;
|
|
268
|
+
}
|
|
269
|
+
.context-menu-item {
|
|
270
|
+
width: 100%;
|
|
271
|
+
height: 30px;
|
|
272
|
+
display: flex;
|
|
273
|
+
flex-direction: column;
|
|
274
|
+
justify-content: space-around;
|
|
275
|
+
}
|
|
276
|
+
.context-menu-item:hover {
|
|
277
|
+
cursor: pointer;
|
|
278
|
+
}
|
|
279
|
+
.context-menu-item-text {
|
|
280
|
+
padding-left: 10px;
|
|
281
|
+
}
|
|
282
|
+
.context-menu-icon {
|
|
283
|
+
color: white;
|
|
284
|
+
}
|
|
285
|
+
.export-points-button {
|
|
286
|
+
border: 1px solid var(--red-ui-secondary-border-color);
|
|
287
|
+
border-radius: 5px !important;
|
|
288
|
+
padding: 6px 12px;
|
|
289
|
+
cursor: pointer;
|
|
290
|
+
margin-right: 10px !important;
|
|
291
|
+
}
|
|
292
|
+
.diplayNameDialogSave {
|
|
293
|
+
background-color: #3295BD !important;
|
|
294
|
+
border-radius: 0 !important;
|
|
295
|
+
margin-right: 0 !important;
|
|
296
|
+
}
|
|
297
|
+
.diplayNameDialogSave > .p-button-label {
|
|
298
|
+
color: white !important;
|
|
299
|
+
}
|
|
300
|
+
.diplayNameDialogCancel {
|
|
301
|
+
background-color: #ffffff !important;
|
|
302
|
+
color: #666 !important;
|
|
303
|
+
border: 1px solid var(--red-ui-secondary-border-color);
|
|
304
|
+
border-radius: 0 !important;
|
|
305
|
+
margin-right: 0 !important;
|
|
306
|
+
}
|
|
307
|
+
.diplayNameDialogCancel:hover {
|
|
308
|
+
background-color: #e6e6e6 !important;
|
|
309
|
+
color: #666 !important;
|
|
310
|
+
border: 1px solid var(--red-ui-secondary-border-color);
|
|
311
|
+
}
|
|
312
|
+
.deviceNameDialog {
|
|
313
|
+
|
|
314
|
+
.p-dialog-header {
|
|
315
|
+
background: #133547 !important;
|
|
316
|
+
padding: 0.5rem !important;
|
|
317
|
+
}
|
|
318
|
+
.p-dialog-title {
|
|
319
|
+
color: white;
|
|
320
|
+
font-size: 1rem !important;
|
|
321
|
+
}
|
|
322
|
+
.p-dialog-content {
|
|
323
|
+
padding: 0.5em 0.75rem 0.5rem 0.75rem;
|
|
324
|
+
|
|
325
|
+
& label {
|
|
326
|
+
font-size: 0.9rem !important;
|
|
327
|
+
}
|
|
328
|
+
& input {
|
|
329
|
+
font-size: 1rem !important;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
11
332
|
}
|