@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/bacnet_read.html
CHANGED
|
@@ -3,26 +3,24 @@
|
|
|
3
3
|
-->
|
|
4
4
|
|
|
5
5
|
<script type="text/javascript">
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
color: '#00aeef',
|
|
6
|
+
RED.nodes.registerType("Bacnet-Discovery", {
|
|
7
|
+
category: "Bitpool BACnet",
|
|
8
|
+
color: "#00aeef",
|
|
10
9
|
defaults: {
|
|
11
|
-
name: {value: ""},
|
|
12
|
-
events: {value: true},
|
|
13
|
-
json: {value: true},
|
|
14
|
-
mqtt: {value: false},
|
|
15
|
-
hiddenDeployToggle: {value: false},
|
|
16
|
-
prevHiddenToggleState: {value: false},
|
|
17
|
-
roundDecimal: {value: 2},
|
|
10
|
+
name: { value: "" },
|
|
11
|
+
events: { value: true },
|
|
12
|
+
json: { value: true },
|
|
13
|
+
mqtt: { value: false },
|
|
14
|
+
hiddenDeployToggle: { value: false },
|
|
15
|
+
prevHiddenToggleState: { value: false },
|
|
16
|
+
roundDecimal: { value: 2 },
|
|
18
17
|
deviceList: [],
|
|
19
18
|
pointList: [],
|
|
20
|
-
pointsToRead: {value: {}},
|
|
21
|
-
readDevices: {value: []},
|
|
19
|
+
pointsToRead: { value: {} },
|
|
20
|
+
readDevices: { value: [] },
|
|
22
21
|
devicesToRead: [],
|
|
23
|
-
object_property_simplePayload: {value: false},
|
|
24
|
-
object_property_fullObject: {value: true}
|
|
25
|
-
|
|
22
|
+
object_property_simplePayload: { value: false },
|
|
23
|
+
object_property_fullObject: { value: true },
|
|
26
24
|
},
|
|
27
25
|
inputs: 1,
|
|
28
26
|
outputs: 1,
|
|
@@ -38,7 +36,7 @@
|
|
|
38
36
|
|
|
39
37
|
node.prevHiddenToggleState = node.hiddenDeployToggle;
|
|
40
38
|
|
|
41
|
-
const {createApp, ref, onMounted} = Vue;
|
|
39
|
+
const { createApp, ref, onMounted } = Vue;
|
|
42
40
|
|
|
43
41
|
//prime vue app
|
|
44
42
|
const App = {
|
|
@@ -53,7 +51,13 @@
|
|
|
53
51
|
pollFrequency: ref(),
|
|
54
52
|
deviceCount: ref(),
|
|
55
53
|
progressBarValue: ref(),
|
|
56
|
-
|
|
54
|
+
rightClickedDevice: ref(),
|
|
55
|
+
rightClickedPoint: ref(),
|
|
56
|
+
showDeviceNameDialog: ref(false),
|
|
57
|
+
showPointNameDialog: ref(false),
|
|
58
|
+
deviceDisplayNameValue: ref(),
|
|
59
|
+
pointDisplayNameValue: ref(),
|
|
60
|
+
};
|
|
57
61
|
},
|
|
58
62
|
setup() {
|
|
59
63
|
let pointList;
|
|
@@ -66,7 +70,7 @@
|
|
|
66
70
|
expandNode(node);
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
expandedKeys.value = {...expandedKeys.value};
|
|
73
|
+
expandedKeys.value = { ...expandedKeys.value };
|
|
70
74
|
};
|
|
71
75
|
const collapseAll = () => {
|
|
72
76
|
expandedKeys.value = {};
|
|
@@ -87,27 +91,49 @@
|
|
|
87
91
|
expandAll,
|
|
88
92
|
collapseAll,
|
|
89
93
|
expandNode,
|
|
90
|
-
}
|
|
94
|
+
};
|
|
91
95
|
},
|
|
92
96
|
mounted() {
|
|
93
97
|
let app = this;
|
|
94
|
-
this.getData();
|
|
95
|
-
if(!node.reloadTimer) {
|
|
96
|
-
node.reloadTimer = setInterval(() => app.getData(),
|
|
98
|
+
this.getData(true);
|
|
99
|
+
if (!node.reloadTimer) {
|
|
100
|
+
node.reloadTimer = setInterval(() => app.getData(false), 7000);
|
|
97
101
|
}
|
|
102
|
+
|
|
103
|
+
$("#readlist-file-upload").on("change", function (event) {
|
|
104
|
+
const input = event.target.files[0];
|
|
105
|
+
const reader = new FileReader();
|
|
106
|
+
|
|
107
|
+
reader.readAsText(input);
|
|
108
|
+
|
|
109
|
+
reader.onload = function (e) {
|
|
110
|
+
const text = e.target.result;
|
|
111
|
+
let jsonPayload = JSON.parse(text);
|
|
112
|
+
|
|
113
|
+
app.nodeService.importReadList(jsonPayload).then(function (result) {
|
|
114
|
+
if (result) {
|
|
115
|
+
app.pointsToRead = jsonPayload;
|
|
116
|
+
app.addToReadDevices(jsonPayload);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
});
|
|
98
121
|
},
|
|
99
122
|
methods: {
|
|
100
|
-
getData() {
|
|
123
|
+
getData(initial) {
|
|
101
124
|
let app = this;
|
|
102
125
|
this.nodeService.getNetworkData().then(function (result) {
|
|
126
|
+
//remove after below fixed
|
|
103
127
|
app.devices = result.renderList;
|
|
128
|
+
//end remove
|
|
129
|
+
|
|
104
130
|
app.deviceList = result.deviceList;
|
|
105
131
|
app.pointList = result.pointList;
|
|
106
132
|
app.pollFrequency = parseInt(result.pollFrequency);
|
|
107
133
|
app.deviceCount = result.deviceList.length;
|
|
108
134
|
//progress bar percentage
|
|
109
135
|
let progressVal = parseInt((result.renderListCount / result.deviceList.length) * 100);
|
|
110
|
-
if(typeof progressVal == "number" && !isNaN(progressVal)) {
|
|
136
|
+
if (typeof progressVal == "number" && !isNaN(progressVal)) {
|
|
111
137
|
app.progressBarValue = progressVal;
|
|
112
138
|
} else {
|
|
113
139
|
app.progressBarValue = 0;
|
|
@@ -121,7 +147,7 @@
|
|
|
121
147
|
rebuildDataModel() {
|
|
122
148
|
let app = this;
|
|
123
149
|
app.nodeService.rebuildDataModel().then(function (result) {
|
|
124
|
-
if(result == true){
|
|
150
|
+
if (result == true) {
|
|
125
151
|
app.progressBarValue = 0;
|
|
126
152
|
}
|
|
127
153
|
});
|
|
@@ -130,37 +156,47 @@
|
|
|
130
156
|
addAllDevices() {
|
|
131
157
|
let app = this;
|
|
132
158
|
app.devices.forEach(function (device) {
|
|
133
|
-
app.addAllClicked({
|
|
159
|
+
app.addAllClicked({ node: device });
|
|
134
160
|
});
|
|
135
161
|
app.$forceUpdate();
|
|
136
162
|
},
|
|
137
163
|
addAllClicked(slotProps) {
|
|
138
|
-
//update UI
|
|
164
|
+
//update UI
|
|
139
165
|
let app = this;
|
|
140
|
-
|
|
141
166
|
let clone = JSON.parse(JSON.stringify(slotProps.node));
|
|
142
167
|
|
|
168
|
+
while (clone.children.length > 1) {
|
|
169
|
+
let child = clone.children[1];
|
|
170
|
+
if (child.label.includes('MSTP')) {
|
|
171
|
+
child.children.forEach(function (mstpDevice) {
|
|
172
|
+
app.addAllClicked({ node: mstpDevice });
|
|
173
|
+
});
|
|
174
|
+
clone.children.splice(1, 1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
143
178
|
if (this.readDevices) {
|
|
144
|
-
let foundIndex = this.readDevices.findIndex(ele => ele.
|
|
145
|
-
if (foundIndex == -1)
|
|
179
|
+
let foundIndex = this.readDevices.findIndex((ele) => ele.initialName == slotProps.node.initialName);
|
|
180
|
+
if (foundIndex == -1) {
|
|
181
|
+
this.readDevices.push(clone);
|
|
182
|
+
} else if (foundIndex !== -1) {
|
|
183
|
+
this.readDevices[foundIndex] = clone;
|
|
184
|
+
}
|
|
146
185
|
} else {
|
|
147
186
|
this.readDevices = [clone];
|
|
148
187
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if(deviceSlot) {
|
|
188
|
+
let deviceSlot = this.devices.find((ele) => ele.initialName == slotProps.node.initialName);
|
|
189
|
+
if (deviceSlot) {
|
|
152
190
|
deviceSlot.showAdded = true;
|
|
153
|
-
deviceSlot.children.forEach(function(child) {
|
|
191
|
+
deviceSlot.children[0].children.forEach(function (child) {
|
|
154
192
|
child.showAdded = true;
|
|
155
193
|
});
|
|
156
194
|
}
|
|
157
195
|
slotProps.node.showAdded = true;
|
|
158
|
-
|
|
159
|
-
this.$forceUpdate()
|
|
160
|
-
|
|
196
|
+
this.$forceUpdate();
|
|
161
197
|
//update node-red data structure to forward to gateway
|
|
162
|
-
let device = this.deviceList.find(ele => {
|
|
163
|
-
if(ele.address.address) {
|
|
198
|
+
let device = this.deviceList.find((ele) => {
|
|
199
|
+
if (ele.address.address) {
|
|
164
200
|
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
165
201
|
} else {
|
|
166
202
|
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
@@ -169,101 +205,47 @@
|
|
|
169
205
|
let deviceAddress = app.getDeviceAddress(device.address);
|
|
170
206
|
let key = `${deviceAddress}-${device.deviceId}`;
|
|
171
207
|
let points = this.pointList[key];
|
|
172
|
-
|
|
173
208
|
if (!this.pointsToRead[key]) {
|
|
174
209
|
this.pointsToRead[key] = {};
|
|
175
210
|
}
|
|
176
|
-
|
|
177
211
|
for (let pointName in points) {
|
|
178
212
|
let point = this.pointList[key][pointName];
|
|
179
213
|
this.pointsToRead[key][point.objectName] = point;
|
|
180
214
|
}
|
|
181
|
-
|
|
182
215
|
//force a deploy state
|
|
183
216
|
node.hiddenDeployToggle = !node.prevHiddenToggleState;
|
|
184
217
|
},
|
|
185
|
-
removeAllDevices() {
|
|
186
|
-
let app = this;
|
|
187
|
-
|
|
188
|
-
let clone = JSON.parse(JSON.stringify(app.readDevices));
|
|
189
|
-
|
|
190
|
-
clone.forEach(function (device) {
|
|
191
|
-
app.removeAllClicked({"node": device});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
app.$forceUpdate();
|
|
195
|
-
|
|
196
|
-
},
|
|
197
|
-
removeAllClicked(slotProps) {
|
|
198
|
-
let app = this;
|
|
199
|
-
try {
|
|
200
|
-
//update UI
|
|
201
|
-
if (this.readDevices.length > 0) {
|
|
202
|
-
let foundIndex = this.readDevices.findIndex(ele => ele.key == slotProps.node.key && ele.label == slotProps.node.label);
|
|
203
|
-
if (foundIndex !== -1) this.readDevices.splice(foundIndex, 1);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
let deviceSlot = this.devices.find(ele => ele.label == slotProps.node.label);
|
|
207
|
-
if(deviceSlot) {
|
|
208
|
-
deviceSlot.showAdded = false;
|
|
209
|
-
deviceSlot.children.forEach(function(child) {
|
|
210
|
-
child.showAdded = false;
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
slotProps.node.showAdded = false;
|
|
214
|
-
this.$forceUpdate();
|
|
215
|
-
|
|
216
|
-
//update node-red data structure
|
|
217
|
-
let device = this.deviceList.find(ele => {
|
|
218
|
-
if(ele.address.address) {
|
|
219
|
-
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
220
|
-
} else{
|
|
221
|
-
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
let deviceAddress = app.getDeviceAddress(device.address);
|
|
225
|
-
let key = `${deviceAddress}-${device.deviceId}`;
|
|
226
|
-
if (this.pointsToRead[key]) {
|
|
227
|
-
delete this.pointsToRead[key];
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
//force a deploy state
|
|
231
|
-
node.hiddenDeployToggle = !node.prevHiddenToggleState;
|
|
232
|
-
|
|
233
|
-
} catch(e) {
|
|
234
|
-
//do nothing
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
app.$forceUpdate();
|
|
238
|
-
},
|
|
239
218
|
addPointClicked(slotProps) {
|
|
240
219
|
let app = this;
|
|
241
220
|
//update UI
|
|
242
221
|
let parentDeviceName = slotProps.node.parentDevice;
|
|
243
|
-
let
|
|
244
|
-
let
|
|
222
|
+
let device = this.deviceList.find((ele) => ele.deviceName == parentDeviceName);
|
|
223
|
+
let foundDeviceIndex = this.readDevices
|
|
224
|
+
? this.readDevices.findIndex((ele) => ele.deviceId == device.deviceId)
|
|
225
|
+
: -1;
|
|
245
226
|
let deviceAddress = app.getDeviceAddress(device.address);
|
|
246
227
|
let key = `${deviceAddress}-${device.deviceId}`;
|
|
247
|
-
let parentDevice = this.devices.find(ele => ele.ipAddr == deviceAddress);
|
|
228
|
+
let parentDevice = this.devices.find((ele) => ele.ipAddr == deviceAddress);
|
|
248
229
|
let childDevice;
|
|
249
|
-
if(device.isMstp){
|
|
250
|
-
let foundChildIndex = parentDevice.children[1].children.findIndex(
|
|
251
|
-
|
|
230
|
+
if (device.isMstp) {
|
|
231
|
+
let foundChildIndex = parentDevice.children[1].children.findIndex(
|
|
232
|
+
(ele) => ele.initialName == parentDeviceName
|
|
233
|
+
);
|
|
234
|
+
if (foundChildIndex !== -1) {
|
|
252
235
|
childDevice = parentDevice.children[1].children[foundChildIndex];
|
|
253
236
|
}
|
|
254
|
-
}
|
|
237
|
+
}
|
|
255
238
|
|
|
256
239
|
if (foundDeviceIndex == -1) {
|
|
257
240
|
//no read devices present, add new
|
|
258
241
|
let newReadParent;
|
|
259
|
-
if(childDevice) {
|
|
260
|
-
newReadParent =
|
|
261
|
-
} else{
|
|
262
|
-
newReadParent =
|
|
263
|
-
}
|
|
264
|
-
newReadParent.children = [];
|
|
265
|
-
newReadParent.children.push(slotProps.node);
|
|
266
|
-
|
|
242
|
+
if (childDevice) {
|
|
243
|
+
newReadParent = JSON.parse(JSON.stringify(childDevice));
|
|
244
|
+
} else {
|
|
245
|
+
newReadParent = JSON.parse(JSON.stringify(parentDevice));
|
|
246
|
+
}
|
|
247
|
+
newReadParent.children[0].children = [];
|
|
248
|
+
newReadParent.children[0].children.push(slotProps.node);
|
|
267
249
|
if (this.readDevices) {
|
|
268
250
|
this.readDevices.push(newReadParent);
|
|
269
251
|
} else {
|
|
@@ -271,10 +253,15 @@
|
|
|
271
253
|
}
|
|
272
254
|
} else {
|
|
273
255
|
// read device found, add point to existing
|
|
274
|
-
this.readDevices[foundDeviceIndex].children.
|
|
256
|
+
let pointIndex = this.readDevices[foundDeviceIndex].children[0].children.findIndex(
|
|
257
|
+
(ele) => ele.pointName == slotProps.node.pointName
|
|
258
|
+
);
|
|
259
|
+
if (pointIndex == -1) {
|
|
260
|
+
this.readDevices[foundDeviceIndex].children[0].children.push(slotProps.node);
|
|
261
|
+
}
|
|
275
262
|
}
|
|
276
263
|
|
|
277
|
-
//set show added to true
|
|
264
|
+
//set show added to true
|
|
278
265
|
slotProps.node.showAdded = true;
|
|
279
266
|
this.$forceUpdate();
|
|
280
267
|
|
|
@@ -291,25 +278,85 @@
|
|
|
291
278
|
|
|
292
279
|
app.$forceUpdate();
|
|
293
280
|
},
|
|
281
|
+
removeAllDevices() {
|
|
282
|
+
let app = this;
|
|
283
|
+
let clone = JSON.parse(JSON.stringify(app.readDevices));
|
|
284
|
+
clone.forEach(function (device) {
|
|
285
|
+
app.removeAllClicked({ node: device });
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
app.pointsToRead = {};
|
|
289
|
+
|
|
290
|
+
app.$forceUpdate();
|
|
291
|
+
},
|
|
292
|
+
removeAllClicked(slotProps) {
|
|
293
|
+
let app = this;
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
//update UI
|
|
297
|
+
if (app.readDevices.length > 0) {
|
|
298
|
+
let foundIndex = app.readDevices.findIndex(
|
|
299
|
+
(ele) => ele.key == slotProps.node.key && ele.label == slotProps.node.label
|
|
300
|
+
);
|
|
301
|
+
if (foundIndex !== -1) app.readDevices.splice(foundIndex, 1);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let deviceSlot = app.devices.find((ele) => ele.label == slotProps.node.label);
|
|
305
|
+
if (deviceSlot) {
|
|
306
|
+
deviceSlot.showAdded = false;
|
|
307
|
+
deviceSlot.children.forEach(function (child) {
|
|
308
|
+
child.showAdded = false;
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
slotProps.node.showAdded = false;
|
|
312
|
+
app.$forceUpdate();
|
|
313
|
+
|
|
314
|
+
//update node-red data structure
|
|
315
|
+
let device = app.deviceList.find((ele) => {
|
|
316
|
+
if (ele.address.address) {
|
|
317
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
318
|
+
} else {
|
|
319
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
let deviceAddress = app.getDeviceAddress(device.address);
|
|
323
|
+
let key = `${deviceAddress}-${device.deviceId}`;
|
|
324
|
+
if (app.pointsToRead[key]) {
|
|
325
|
+
delete app.pointsToRead[key];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
//force a deploy state
|
|
329
|
+
node.hiddenDeployToggle = !node.prevHiddenToggleState;
|
|
330
|
+
} catch (e) {
|
|
331
|
+
//do nothing
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
app.$forceUpdate();
|
|
335
|
+
},
|
|
294
336
|
removePointClicked(slotProps) {
|
|
295
337
|
let app = this;
|
|
338
|
+
|
|
296
339
|
//update UI
|
|
297
340
|
let parentDeviceName = slotProps.node.parentDevice;
|
|
298
|
-
let
|
|
299
|
-
let
|
|
300
|
-
let
|
|
341
|
+
let parentDevice = this.devices.find((ele) => ele.label == parentDeviceName);
|
|
342
|
+
let device = this.deviceList.find((ele) => ele.deviceName == parentDeviceName);
|
|
343
|
+
let foundDeviceIndex = this.readDevices
|
|
344
|
+
? this.readDevices.findIndex((ele) => ele.deviceId == device.deviceId)
|
|
345
|
+
: -1;
|
|
301
346
|
let deviceAddress = app.getDeviceAddress(device.address);
|
|
302
347
|
let key = `${deviceAddress}-${device.deviceId}`;
|
|
303
348
|
|
|
304
349
|
if (foundDeviceIndex !== -1) {
|
|
305
|
-
let foundIndex = this.readDevices[foundDeviceIndex].children.findIndex(
|
|
306
|
-
|
|
307
|
-
|
|
350
|
+
let foundIndex = this.readDevices[foundDeviceIndex].children[0].children.findIndex(
|
|
351
|
+
(ele) => ele.pointName == slotProps.node.pointName
|
|
352
|
+
);
|
|
353
|
+
if (foundIndex !== -1) this.readDevices[foundDeviceIndex].children[0].children.splice(foundIndex, 1);
|
|
354
|
+
if (this.readDevices[foundDeviceIndex].children[0].children.length == 0) {
|
|
308
355
|
this.readDevices.splice(foundDeviceIndex, 1);
|
|
309
356
|
}
|
|
310
357
|
}
|
|
311
358
|
|
|
312
|
-
//set show added to true
|
|
359
|
+
//set show added to true
|
|
313
360
|
slotProps.node.showAdded = false;
|
|
314
361
|
this.$forceUpdate();
|
|
315
362
|
|
|
@@ -317,26 +364,69 @@
|
|
|
317
364
|
let point = this.pointList[key][slotProps.node.pointName];
|
|
318
365
|
if (this.pointsToRead[key][point.objectName]) delete this.pointsToRead[key][point.objectName];
|
|
319
366
|
//if last point is removed, deleted whole entry
|
|
320
|
-
if(Object.keys(this.pointsToRead[key]).length == 0) delete this.pointsToRead[key];
|
|
367
|
+
if (Object.keys(this.pointsToRead[key]).length == 0) delete this.pointsToRead[key];
|
|
321
368
|
|
|
322
369
|
//force a deploy state
|
|
323
370
|
node.hiddenDeployToggle = !node.prevHiddenToggleState;
|
|
324
371
|
|
|
325
372
|
app.$forceUpdate();
|
|
326
373
|
},
|
|
374
|
+
exportPointListCsv() {
|
|
375
|
+
let app = this;
|
|
376
|
+
let csvContent = "data:text/csv;charset=utf-8,ipAddress,deviceId,deviceName,pointName,objectType" + "\r\n";
|
|
377
|
+
const keys = Object.keys(app.pointList);
|
|
378
|
+
for (key in keys) {
|
|
379
|
+
const guid = keys[key];
|
|
380
|
+
const ipAddress = guid.split("-")[0];
|
|
381
|
+
const deviceId = guid.split("-")[1];
|
|
382
|
+
const deviceObject = app.pointList[keys[key]];
|
|
383
|
+
const device = app.deviceList.find((ele) => {
|
|
384
|
+
if (ele.address.address) {
|
|
385
|
+
return ele.address.address == ipAddress && ele.deviceId == deviceId;
|
|
386
|
+
} else {
|
|
387
|
+
return ele.address == ipAddress && ele.deviceId == deviceId;
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
const deviceName = device.deviceName;
|
|
391
|
+
const points = Object.keys(deviceObject);
|
|
392
|
+
for (point in points) {
|
|
393
|
+
csvContent +=
|
|
394
|
+
ipAddress +
|
|
395
|
+
"," +
|
|
396
|
+
deviceId +
|
|
397
|
+
"," +
|
|
398
|
+
deviceName +
|
|
399
|
+
"," +
|
|
400
|
+
points[point] +
|
|
401
|
+
"," +
|
|
402
|
+
deviceObject[points[point]].meta.objectId.type +
|
|
403
|
+
"\r\n";
|
|
404
|
+
|
|
405
|
+
if (parseInt(point) == points.length - 1 && parseInt(key) == keys.length - 1) {
|
|
406
|
+
// last iteration
|
|
407
|
+
var encodedUri = encodeURI(csvContent);
|
|
408
|
+
var link = document.createElement("a");
|
|
409
|
+
link.setAttribute("href", encodedUri);
|
|
410
|
+
link.setAttribute("download", "pointslist.csv");
|
|
411
|
+
document.body.appendChild(link);
|
|
412
|
+
link.click();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
},
|
|
327
417
|
getDeviceAddress(addr) {
|
|
328
|
-
switch(typeof addr) {
|
|
418
|
+
switch (typeof addr) {
|
|
329
419
|
case "object":
|
|
330
420
|
return addr.address;
|
|
331
421
|
case "string":
|
|
332
422
|
return addr;
|
|
333
|
-
default:
|
|
423
|
+
default:
|
|
334
424
|
return addr;
|
|
335
425
|
}
|
|
336
426
|
},
|
|
337
427
|
isDeviceActive(slotProps) {
|
|
338
428
|
let app = this;
|
|
339
|
-
if ((
|
|
429
|
+
if ((Date.now() - slotProps.node.lastSeen) / 1000 < app.pollFrequency + 5) {
|
|
340
430
|
return true;
|
|
341
431
|
}
|
|
342
432
|
return false;
|
|
@@ -348,23 +438,400 @@
|
|
|
348
438
|
return false;
|
|
349
439
|
}
|
|
350
440
|
},
|
|
441
|
+
hasMstpDevices(slotProps) {
|
|
442
|
+
if (slotProps.node.children[1] && slotProps.node.children[1].children.length > 0) {
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
return false;
|
|
446
|
+
},
|
|
351
447
|
isSlotAdded(slotProps) {
|
|
352
448
|
return slotProps.node.showAdded;
|
|
353
449
|
},
|
|
354
450
|
hasData() {
|
|
355
451
|
if (this.devices && this.devices.length > 0) {
|
|
356
|
-
return true
|
|
452
|
+
return true;
|
|
357
453
|
} else {
|
|
358
454
|
return false;
|
|
359
455
|
}
|
|
360
|
-
}
|
|
456
|
+
},
|
|
457
|
+
emptyTree() {
|
|
458
|
+
if (this.devices.length > 0) {
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
return false;
|
|
462
|
+
},
|
|
463
|
+
onDeviceRightClick(slotProps, event) {
|
|
464
|
+
let app = this;
|
|
465
|
+
app.rightClickedDevice = slotProps;
|
|
466
|
+
event.preventDefault();
|
|
467
|
+
menu.style.setProperty("--mouse-x", event.clientX + "px");
|
|
468
|
+
menu.style.setProperty("--mouse-y", event.clientY + "px");
|
|
469
|
+
menu.style.display = "block";
|
|
470
|
+
},
|
|
471
|
+
onPointRightClick(slotProps, event) {
|
|
472
|
+
let app = this;
|
|
473
|
+
app.rightClickedPoint = slotProps;
|
|
474
|
+
event.preventDefault();
|
|
475
|
+
pointMenu.style.setProperty("--mouse-x", event.clientX + "px");
|
|
476
|
+
pointMenu.style.setProperty("--mouse-y", event.clientY + "px");
|
|
477
|
+
pointMenu.style.display = "block";
|
|
478
|
+
},
|
|
479
|
+
handleContextMenuClick(type) {
|
|
480
|
+
let app = this;
|
|
481
|
+
switch (type) {
|
|
482
|
+
case "purgeDevice":
|
|
483
|
+
app.purgeDevice(app.rightClickedDevice);
|
|
484
|
+
break;
|
|
485
|
+
case "updatePoints":
|
|
486
|
+
app.updatePointsForDevice(app.rightClickedDevice);
|
|
487
|
+
break;
|
|
488
|
+
case "addAllPoints":
|
|
489
|
+
app.addAllClicked(app.rightClickedDevice);
|
|
490
|
+
break;
|
|
491
|
+
case "removeAllPoints":
|
|
492
|
+
app.removeAllClicked(app.rightClickedDevice);
|
|
493
|
+
break;
|
|
494
|
+
case "setDeviceName":
|
|
495
|
+
app.showDeviceNameDialog = true;
|
|
496
|
+
app.deviceDisplayNameValue = app.rightClickedDevice.node.label;
|
|
497
|
+
break;
|
|
498
|
+
default:
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
handlePointContextMenuClick(type) {
|
|
503
|
+
let app = this;
|
|
504
|
+
switch (type) {
|
|
505
|
+
case "setPointName":
|
|
506
|
+
app.showPointNameDialog = true;
|
|
507
|
+
app.pointDisplayNameValue = app.rightClickedPoint.node.label;
|
|
508
|
+
break;
|
|
509
|
+
case "updatePoint":
|
|
510
|
+
app.updatePoint(app.rightClickedDevice);
|
|
511
|
+
break;
|
|
512
|
+
default:
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
purgeDevice(slotProps) {
|
|
517
|
+
let app = this;
|
|
518
|
+
let device = app.deviceList.find((ele) => {
|
|
519
|
+
if (ele.address.address) {
|
|
520
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
521
|
+
} else {
|
|
522
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
if (device) {
|
|
526
|
+
app.nodeService.purgeDevice(device).then(function (result) { });
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
updatePointsForDevice(slotProps) {
|
|
530
|
+
let app = this;
|
|
531
|
+
let device = app.deviceList.find((ele) => {
|
|
532
|
+
if (ele.address.address) {
|
|
533
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
534
|
+
} else {
|
|
535
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
if (device) {
|
|
539
|
+
app.nodeService.updatePointsForDevice(device).then(function (result) { });
|
|
540
|
+
}
|
|
541
|
+
},
|
|
542
|
+
updatePoint(slotProps) {
|
|
543
|
+
let app = this;
|
|
544
|
+
let device = app.deviceList.find((ele) => {
|
|
545
|
+
if (ele.address.address) {
|
|
546
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
547
|
+
} else {
|
|
548
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
if (device) {
|
|
552
|
+
app.nodeService.updatePoint(device).then(function (result) { });
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
setDeviceName() {
|
|
556
|
+
let app = this;
|
|
557
|
+
const slotProps = app.rightClickedDevice;
|
|
558
|
+
const displayName = app.deviceDisplayNameValue;
|
|
559
|
+
let device = app.deviceList.find((ele) => {
|
|
560
|
+
if (ele.address.address) {
|
|
561
|
+
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
562
|
+
} else {
|
|
563
|
+
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
if (device) {
|
|
568
|
+
app.nodeService.setDeviceDisplayName(device, displayName).then(function (result) {
|
|
569
|
+
if (result) {
|
|
570
|
+
slotProps.node.label = displayName;
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
app.showDeviceNameDialog = false;
|
|
576
|
+
},
|
|
577
|
+
setPointName() {
|
|
578
|
+
let app = this;
|
|
579
|
+
|
|
580
|
+
const slotProps = app.rightClickedPoint;
|
|
581
|
+
const pointDisplayName = app.pointDisplayNameValue;
|
|
582
|
+
const pointName = slotProps.node.pointName;
|
|
583
|
+
|
|
584
|
+
let device = app.deviceList.find((ele) => {
|
|
585
|
+
return ele.deviceName == slotProps.node.parentDevice;
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
if (device) {
|
|
589
|
+
let deviceAddress = app.getDeviceAddress(device.address);
|
|
590
|
+
let deviceKey = `${deviceAddress}-${device.deviceId}`;
|
|
591
|
+
|
|
592
|
+
app.nodeService.setPointDisplayName(deviceKey, pointName, pointDisplayName).then(function (result) {
|
|
593
|
+
if (result) {
|
|
594
|
+
slotProps.node.label = pointDisplayName;
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
app.showPointNameDialog = false;
|
|
600
|
+
},
|
|
601
|
+
settingDeviceName(slotProps) {
|
|
602
|
+
let app = this;
|
|
603
|
+
if (slotProps.node.settingDisplayName) {
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
return false;
|
|
607
|
+
},
|
|
608
|
+
cancelDisplayNameDialog() {
|
|
609
|
+
let app = this;
|
|
610
|
+
app.deviceDisplayNameValue = "";
|
|
611
|
+
app.showDeviceNameDialog = false;
|
|
612
|
+
},
|
|
613
|
+
cancelPointNameDialog() {
|
|
614
|
+
let app = this;
|
|
615
|
+
app.pointDisplayNameValue = "";
|
|
616
|
+
app.showPointNameDialog = false;
|
|
617
|
+
},
|
|
618
|
+
exportReadList() {
|
|
619
|
+
let app = this;
|
|
620
|
+
let exportJson = {};
|
|
621
|
+
let deviceIndex = 0;
|
|
622
|
+
const devicesToExport = Object.keys(app.pointsToRead);
|
|
623
|
+
|
|
624
|
+
doDevice(deviceIndex);
|
|
625
|
+
|
|
626
|
+
function doDevice(deviceIndex) {
|
|
627
|
+
const key = devicesToExport[deviceIndex];
|
|
628
|
+
const device = app.pointsToRead[key];
|
|
629
|
+
|
|
630
|
+
let readDevice = app.readDevices.find(ele => ele.ipAddr == key.split("-")[0] && ele.deviceId == key.split("-")[1]);
|
|
631
|
+
let deviceName;
|
|
632
|
+
|
|
633
|
+
if (readDevice) {
|
|
634
|
+
deviceName = readDevice.label
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
exportJson[key] = {};
|
|
638
|
+
if (deviceName) {
|
|
639
|
+
exportJson[key]["deviceName"] = deviceName;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const devicePoints = Object.keys(device);
|
|
643
|
+
let pointIndex = 0;
|
|
644
|
+
doPoints(pointIndex);
|
|
645
|
+
|
|
646
|
+
function doPoints(pointIndex) {
|
|
647
|
+
let pointName = devicePoints[pointIndex];
|
|
648
|
+
let pointObject = device[pointName];
|
|
649
|
+
|
|
650
|
+
//formatting json payload, still in progress
|
|
651
|
+
|
|
652
|
+
exportJson[key][pointName] = {
|
|
653
|
+
meta: pointObject.meta,
|
|
654
|
+
objectName: pointObject.objectName,
|
|
655
|
+
displayName: pointObject.displayName,
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
// exportJson[key][pointName] = pointObject;
|
|
659
|
+
|
|
660
|
+
if (pointIndex < devicePoints.length - 1) {
|
|
661
|
+
//more points to read for device
|
|
662
|
+
pointIndex++;
|
|
663
|
+
doPoints(pointIndex);
|
|
664
|
+
} else if (pointIndex == devicePoints.length - 1) {
|
|
665
|
+
//all points have been iterated
|
|
666
|
+
|
|
667
|
+
if (deviceIndex < devicesToExport.length - 1) {
|
|
668
|
+
//more work to do
|
|
669
|
+
deviceIndex++;
|
|
670
|
+
doDevice(deviceIndex);
|
|
671
|
+
} else if (deviceIndex == devicesToExport.length - 1) {
|
|
672
|
+
//all read devices complete
|
|
673
|
+
let data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportJson));
|
|
674
|
+
let aEle = document.getElementById("exportJSON");
|
|
675
|
+
aEle.setAttribute("href", "data:" + data);
|
|
676
|
+
aEle.setAttribute("download", "bacnet-read-list.json");
|
|
677
|
+
aEle.click();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
},
|
|
683
|
+
importReadList() {
|
|
684
|
+
let app = this;
|
|
685
|
+
$("#readlist-file-upload").click();
|
|
686
|
+
},
|
|
687
|
+
addToReadDevices(pointsToRead) {
|
|
688
|
+
let app = this;
|
|
689
|
+
|
|
690
|
+
for (let key in pointsToRead) {
|
|
691
|
+
let ip = key.split("-")[0];
|
|
692
|
+
let id = key.split("-")[1];
|
|
693
|
+
const importedDevice = pointsToRead[key];
|
|
694
|
+
let foundIndex = app.devices.findIndex((ele) => ele.ipAddr == ip);
|
|
695
|
+
|
|
696
|
+
if (foundIndex !== -1) {
|
|
697
|
+
// match ip, check id
|
|
698
|
+
if (app.devices[foundIndex].deviceId == id) {
|
|
699
|
+
//found device
|
|
700
|
+
let treeDevice = app.devices[foundIndex];
|
|
701
|
+
let device = app.deviceList.find((ele) => {
|
|
702
|
+
if (ele.address.address) {
|
|
703
|
+
return ele.address.address == ip && ele.deviceId == id;
|
|
704
|
+
} else {
|
|
705
|
+
return ele.address == ip && ele.deviceId == id;
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
for (let pointName in importedDevice) {
|
|
710
|
+
let point = importedDevice[pointName];
|
|
711
|
+
if (pointName == "deviceName") {
|
|
712
|
+
app.nodeService.setDeviceDisplayName(device, point);
|
|
713
|
+
treeDevice.label = point;
|
|
714
|
+
}
|
|
715
|
+
let pointInTree = treeDevice.children[0].children.find(
|
|
716
|
+
(ele) => ele.data == pointName && ele.bacnetType == point.meta.objectId.type
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
if (pointInTree) {
|
|
720
|
+
pointInTree.label = point.displayName;
|
|
721
|
+
|
|
722
|
+
const isDeviceInReadList = app.readDevices
|
|
723
|
+
? app.readDevices.findIndex((ele) => ele.deviceId == treeDevice.deviceId)
|
|
724
|
+
: -1;
|
|
725
|
+
|
|
726
|
+
if (isDeviceInReadList == -1) {
|
|
727
|
+
//no read devices present, add new
|
|
728
|
+
let newReadParent = JSON.parse(JSON.stringify(treeDevice));
|
|
729
|
+
newReadParent.children[0].children = [];
|
|
730
|
+
newReadParent.children[0].children.push(pointInTree);
|
|
731
|
+
if (app.readDevices) {
|
|
732
|
+
app.readDevices.push(newReadParent);
|
|
733
|
+
} else {
|
|
734
|
+
app.readDevices = [newReadParent];
|
|
735
|
+
}
|
|
736
|
+
} else {
|
|
737
|
+
// read device found, add point to existing
|
|
738
|
+
let pointIndex = app.readDevices[isDeviceInReadList].children[0].children.findIndex(
|
|
739
|
+
(ele) => ele.data == point.objectName
|
|
740
|
+
);
|
|
741
|
+
if (pointIndex == -1) {
|
|
742
|
+
app.readDevices[isDeviceInReadList].children[0].children.push(pointInTree);
|
|
743
|
+
} else {
|
|
744
|
+
app.readDevices[isDeviceInReadList].children[0].children[pointIndex] = pointInTree;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
} else {
|
|
750
|
+
//search mstp devices
|
|
751
|
+
let mstpIndex = app.devices[foundIndex].children[1].children.findIndex((ele) => ele.deviceId == id);
|
|
752
|
+
|
|
753
|
+
if (mstpIndex !== -1) {
|
|
754
|
+
let mstpDevice = app.devices[foundIndex].children[1].children[mstpIndex];
|
|
755
|
+
let device = app.deviceList.find((ele) => {
|
|
756
|
+
if (ele.address.address) {
|
|
757
|
+
return ele.address.address == ip && ele.deviceId == id;
|
|
758
|
+
} else {
|
|
759
|
+
return ele.address == ip && ele.deviceId == id;
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
for (let pointName in importedDevice) {
|
|
764
|
+
let point = importedDevice[pointName];
|
|
765
|
+
if (pointName == "deviceName") {
|
|
766
|
+
app.nodeService.setDeviceDisplayName(device, point);
|
|
767
|
+
mstpDevice.label = point;
|
|
768
|
+
}
|
|
769
|
+
let pointInTree = mstpDevice.children[0].children.find(
|
|
770
|
+
(ele) => ele.data == pointName && ele.bacnetType == point.meta.objectId.type
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
if (pointInTree) {
|
|
774
|
+
pointInTree.label = point.displayName;
|
|
775
|
+
|
|
776
|
+
const isDeviceInReadList = app.readDevices
|
|
777
|
+
? app.readDevices.findIndex((ele) => ele.deviceId == mstpDevice.deviceId)
|
|
778
|
+
: -1;
|
|
779
|
+
|
|
780
|
+
if (isDeviceInReadList == -1) {
|
|
781
|
+
//no read devices present, add new
|
|
782
|
+
let newReadParent = JSON.parse(JSON.stringify(mstpDevice));
|
|
783
|
+
newReadParent.children[0].children = [];
|
|
784
|
+
newReadParent.children[0].children.push(pointInTree);
|
|
785
|
+
if (app.readDevices) {
|
|
786
|
+
app.readDevices.push(newReadParent);
|
|
787
|
+
} else {
|
|
788
|
+
app.readDevices = [newReadParent];
|
|
789
|
+
}
|
|
790
|
+
} else {
|
|
791
|
+
// read device found, add point to existing
|
|
792
|
+
let pointIndex = app.readDevices[isDeviceInReadList].children[0].children.findIndex(
|
|
793
|
+
(ele) => ele.data == point.objectName
|
|
794
|
+
);
|
|
795
|
+
if (pointIndex == -1) {
|
|
796
|
+
app.readDevices[isDeviceInReadList].children[0].children.push(pointInTree);
|
|
797
|
+
} else {
|
|
798
|
+
app.readDevices[isDeviceInReadList].children[0].children[pointIndex] = pointInTree;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
} else {
|
|
804
|
+
// not part of device list at all, notify user
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
app.$forceUpdate();
|
|
811
|
+
},
|
|
812
|
+
calculateMstpCount(slotProps) {
|
|
813
|
+
let count = 0;
|
|
814
|
+
slotProps.node.children.forEach(function (child) {
|
|
815
|
+
if (child.label.includes("MSTP")) {
|
|
816
|
+
count += child.children.length;
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
return count;
|
|
821
|
+
},
|
|
822
|
+
calculatePointCount(slotProps) {
|
|
823
|
+
let count = 0;
|
|
824
|
+
return count;
|
|
825
|
+
},
|
|
361
826
|
},
|
|
362
827
|
components: {
|
|
363
828
|
"p-tree": primevue.tree,
|
|
364
829
|
"p-button": primevue.button,
|
|
365
830
|
"p-timeline": primevue.timeline,
|
|
366
|
-
"p-progressbar": primevue.progressbar
|
|
367
|
-
|
|
831
|
+
"p-progressbar": primevue.progressbar,
|
|
832
|
+
"p-dialog": primevue.dialog,
|
|
833
|
+
"p-input-text": primevue.inputtext,
|
|
834
|
+
},
|
|
368
835
|
};
|
|
369
836
|
|
|
370
837
|
let vueapp = createApp(App);
|
|
@@ -394,13 +861,26 @@
|
|
|
394
861
|
document.getElementById("node-input-mqtt").checked = node.mqtt;
|
|
395
862
|
document.getElementById("node-input-mqtt").onclick = handleMsgTypeClick;
|
|
396
863
|
|
|
397
|
-
|
|
864
|
+
var menu = document.querySelector(".context-menu");
|
|
865
|
+
window.addEventListener("click", (event) => {
|
|
866
|
+
menu.style.display = "none";
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
var pointMenu = document.querySelector(".point-context-menu");
|
|
870
|
+
window.addEventListener("click", (event) => {
|
|
871
|
+
pointMenu.style.display = "none";
|
|
872
|
+
});
|
|
398
873
|
|
|
399
|
-
|
|
400
|
-
|
|
874
|
+
function handleCheckboxClick() {
|
|
875
|
+
if (this.id == "node-input-object_property_simplePayload") {
|
|
876
|
+
document.getElementById("node-input-object_property_fullObject").checked = !document.getElementById(
|
|
877
|
+
"node-input-object_property_fullObject"
|
|
878
|
+
).checked;
|
|
401
879
|
}
|
|
402
|
-
if(this.id == "node-input-object_property_fullObject") {
|
|
403
|
-
document.getElementById("node-input-object_property_simplePayload").checked = !document.getElementById(
|
|
880
|
+
if (this.id == "node-input-object_property_fullObject") {
|
|
881
|
+
document.getElementById("node-input-object_property_simplePayload").checked = !document.getElementById(
|
|
882
|
+
"node-input-object_property_simplePayload"
|
|
883
|
+
).checked;
|
|
404
884
|
}
|
|
405
885
|
}
|
|
406
886
|
|
|
@@ -413,33 +893,29 @@
|
|
|
413
893
|
}
|
|
414
894
|
}
|
|
415
895
|
|
|
416
|
-
//create tabs
|
|
417
|
-
let tabs = RED.tabs.create(
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
{
|
|
440
|
-
id: "read-properties-tab",
|
|
441
|
-
label: "Properties"
|
|
442
|
-
});
|
|
896
|
+
//create tabs
|
|
897
|
+
let tabs = RED.tabs.create({
|
|
898
|
+
id: "node-input-read-tabs",
|
|
899
|
+
onchange: function (tab) {
|
|
900
|
+
$("#node-input-tabs-content").children().hide();
|
|
901
|
+
$("#" + tab.id).show();
|
|
902
|
+
},
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
tabs.addTab({
|
|
906
|
+
id: "read-networkTree-tab",
|
|
907
|
+
label: "Device List",
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
tabs.addTab({
|
|
911
|
+
id: "read-readList-tab",
|
|
912
|
+
label: "Read List",
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
tabs.addTab({
|
|
916
|
+
id: "read-properties-tab",
|
|
917
|
+
label: "Properties",
|
|
918
|
+
});
|
|
443
919
|
|
|
444
920
|
//remove loading animation
|
|
445
921
|
let loadingGif = document.getElementById("loadingGif");
|
|
@@ -448,7 +924,6 @@
|
|
|
448
924
|
loadingGif.style.display = "none";
|
|
449
925
|
loadingText.style.display = "none";
|
|
450
926
|
table.style.display = "inherit";
|
|
451
|
-
|
|
452
927
|
},
|
|
453
928
|
oneditsave: function () {
|
|
454
929
|
let node = this;
|
|
@@ -456,7 +931,7 @@
|
|
|
456
931
|
if (node.vm.$data.readDevices) node.readDevices = node.vm.$data.readDevices;
|
|
457
932
|
if (node.vm.$data.pointsToRead) node.pointsToRead = node.vm.$data.pointsToRead;
|
|
458
933
|
//clear reload data function
|
|
459
|
-
if(node.reloadTimer) {
|
|
934
|
+
if (node.reloadTimer) {
|
|
460
935
|
clearInterval(node.reloadTimer);
|
|
461
936
|
node.reloadTimer = null;
|
|
462
937
|
}
|
|
@@ -467,470 +942,424 @@
|
|
|
467
942
|
if (node.vm.$data.readDevices) node.readDevices = node.vm.$data.readDevices;
|
|
468
943
|
if (node.vm.$data.pointsToRead) node.pointsToRead = node.vm.$data.pointsToRead;
|
|
469
944
|
//clear reload data function
|
|
470
|
-
if(node.reloadTimer) {
|
|
945
|
+
if (node.reloadTimer) {
|
|
471
946
|
clearInterval(node.reloadTimer);
|
|
472
947
|
node.reloadTimer = null;
|
|
473
948
|
}
|
|
474
|
-
}
|
|
949
|
+
},
|
|
475
950
|
});
|
|
476
|
-
|
|
477
951
|
</script>
|
|
478
952
|
|
|
479
953
|
<script type="text/html" data-template-name="Bacnet-Discovery">
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
}
|
|
549
|
-
.deviceStatus {
|
|
550
|
-
font-size: 10px;
|
|
551
|
-
}
|
|
552
|
-
.statusOnline {
|
|
553
|
-
color: #11c511;
|
|
554
|
-
}
|
|
555
|
-
.statusOffline {
|
|
556
|
-
color: red;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
.deviceLabel {
|
|
560
|
-
position: absolute;
|
|
561
|
-
}
|
|
562
|
-
.objectPropertiesLabel {
|
|
563
|
-
display: flex !important;
|
|
564
|
-
flex-direction: row;
|
|
565
|
-
align-items: center;
|
|
566
|
-
}
|
|
567
|
-
.p-tree .p-tree-container .p-treenode .p-treenode-content:focus {
|
|
568
|
-
outline: 0 none !important;
|
|
569
|
-
outline-offset: 0 !important;
|
|
570
|
-
box-shadow: inset 0 0 0 1px #bdbdbd !important;
|
|
571
|
-
}
|
|
572
|
-
.checkbox-round {
|
|
573
|
-
width: 13px !important;
|
|
574
|
-
height: 13px !important;
|
|
575
|
-
background-color: white;
|
|
576
|
-
border-radius: 50%;
|
|
577
|
-
vertical-align: middle;
|
|
578
|
-
border: 1px solid #ddd;
|
|
579
|
-
appearance: none;
|
|
580
|
-
-webkit-appearance: none;
|
|
581
|
-
outline: none;
|
|
582
|
-
cursor: pointer;
|
|
583
|
-
}
|
|
584
|
-
.checkbox-round:checked {
|
|
585
|
-
background-color: #00AEEF;
|
|
586
|
-
}
|
|
587
|
-
.checkbox-round:focus {
|
|
588
|
-
outline: none !important;
|
|
589
|
-
}
|
|
590
|
-
.p-tree-filter:focus, .p-tree-filter:focus-visible, .p-inputtext:enabled:hover {
|
|
591
|
-
box-shadow: inset 0 0 0 0.15rem #dfdcdc !important;
|
|
592
|
-
border-color: transparent !important;
|
|
593
|
-
}
|
|
594
|
-
.bacnetbutton > .pi {
|
|
595
|
-
display: flex;
|
|
596
|
-
}
|
|
597
|
-
.p-tree-container {
|
|
598
|
-
margin: 0 !important;
|
|
599
|
-
}
|
|
600
|
-
.reloadButtonIcon {
|
|
601
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
|
|
602
|
-
}
|
|
603
|
-
.removeAllDevicesButton {
|
|
604
|
-
border: none;
|
|
605
|
-
background: none;
|
|
606
|
-
font-size: 14px !important;
|
|
607
|
-
float: right;
|
|
608
|
-
display: flex;
|
|
609
|
-
align-items: center;
|
|
610
|
-
}
|
|
611
|
-
.removeAllDevicesButton:hover {
|
|
612
|
-
background-color: #d5d5d5;
|
|
613
|
-
border-radius: 10px;
|
|
614
|
-
}
|
|
615
|
-
.reloadButton {
|
|
616
|
-
border: none;
|
|
617
|
-
background: none;
|
|
618
|
-
font-size: 14px !important;
|
|
619
|
-
float: right;
|
|
620
|
-
}
|
|
621
|
-
.rebuildDataButton {
|
|
622
|
-
border: none;
|
|
623
|
-
background: none;
|
|
624
|
-
font-size: 14px !important;
|
|
625
|
-
float: right;
|
|
626
|
-
}
|
|
627
|
-
.reloadButton:hover {
|
|
628
|
-
background-color: #d5d5d5;
|
|
629
|
-
border-radius: 10px;
|
|
630
|
-
}
|
|
631
|
-
.rebuildDataButton:hover {
|
|
632
|
-
background-color: #d5d5d5;
|
|
633
|
-
border-radius: 10px;
|
|
634
|
-
}
|
|
635
|
-
.headerDiv {
|
|
636
|
-
display: flex;
|
|
637
|
-
flex-direction: row;
|
|
638
|
-
flex-wrap: nowrap;
|
|
639
|
-
justify-content: space-around;
|
|
640
|
-
height: 20px;
|
|
641
|
-
}
|
|
642
|
-
.msgTypeDiv {
|
|
643
|
-
display: flex;
|
|
644
|
-
align-items: flex-start;
|
|
645
|
-
flex-direction: column;
|
|
646
|
-
}
|
|
647
|
-
.p-progressbar .p-progressbar-value {
|
|
648
|
-
background: #00AEEF !important;
|
|
649
|
-
}
|
|
650
|
-
.p-treenode-label {
|
|
651
|
-
overflow: hidden;
|
|
652
|
-
white-space: nowrap;
|
|
653
|
-
text-overflow: ellipsis;
|
|
654
|
-
}
|
|
655
|
-
.buttonGroup {
|
|
656
|
-
padding-left: 7px;
|
|
657
|
-
}
|
|
658
|
-
#read-readList-tab {
|
|
659
|
-
display: flex;
|
|
660
|
-
flex-direction: column;
|
|
661
|
-
}
|
|
662
|
-
.removeAllDevicesDiv {
|
|
663
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
664
|
-
padding-right: 20px;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
.p-tree .p-treenode-children {
|
|
668
|
-
padding: 0 !important;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
.p-tree-toggler .p-link {
|
|
672
|
-
width: 25px !important;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
/*
|
|
677
|
-
.p-treenode-content {
|
|
678
|
-
height: 15px;
|
|
679
|
-
}
|
|
680
|
-
*/
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
</style>
|
|
684
|
-
|
|
685
|
-
<div class="form-row">
|
|
686
|
-
<label for="node-input-name"><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.name"></span> Name</label>
|
|
687
|
-
<input type="text" id="node-input-name" placeholder="Name">
|
|
688
|
-
</div>
|
|
689
|
-
|
|
690
|
-
<div class='form-row node-input-read-tabs-row'>
|
|
691
|
-
<ul style='min-width:600px;margin-bottom:20px' id='node-input-read-tabs'></ul>
|
|
692
|
-
</div>
|
|
693
|
-
|
|
694
|
-
<div id='node-input-tabs-content'>
|
|
695
|
-
|
|
696
|
-
<!--
|
|
954
|
+
<div class="form-row">
|
|
955
|
+
<label for="node-input-name"><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.name"></span> Name</label>
|
|
956
|
+
<input type="text" id="node-input-name" placeholder="Name" />
|
|
957
|
+
</div>
|
|
958
|
+
|
|
959
|
+
<div class="form-row node-input-read-tabs-row">
|
|
960
|
+
<ul style="min-width:600px;margin-bottom:20px" id="node-input-read-tabs"></ul>
|
|
961
|
+
</div>
|
|
962
|
+
|
|
963
|
+
<div id="node-input-tabs-content">
|
|
964
|
+
<!-- Start Device Context Menu -->
|
|
965
|
+
<ul
|
|
966
|
+
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">
|
|
967
|
+
<li class="context-menu-item" @click="handleContextMenuClick('purgeDevice')">
|
|
968
|
+
<a class="red-ui-menu-label">
|
|
969
|
+
<i class="pi pi-wrench context-menu-icon"></i>
|
|
970
|
+
<span class="context-menu-item-text">Purge Device</span>
|
|
971
|
+
</a>
|
|
972
|
+
</li>
|
|
973
|
+
<li class="context-menu-item" @click="handleContextMenuClick('updatePoints')">
|
|
974
|
+
<a class="red-ui-menu-label">
|
|
975
|
+
<i class="pi pi-refresh context-menu-icon"></i>
|
|
976
|
+
<span class="context-menu-item-text">Update Points</span>
|
|
977
|
+
</a>
|
|
978
|
+
</li>
|
|
979
|
+
<li class="red-ui-menu-divider"></li>
|
|
980
|
+
<li class="context-menu-item" @click="handleContextMenuClick('addAllPoints')">
|
|
981
|
+
<a class="red-ui-menu-label">
|
|
982
|
+
<i class="pi pi-plus-circle context-menu-icon"></i>
|
|
983
|
+
<span class="context-menu-item-text">Add All Points </span>
|
|
984
|
+
</a>
|
|
985
|
+
</li>
|
|
986
|
+
<li class="context-menu-item" @click="handleContextMenuClick('removeAllPoints')">
|
|
987
|
+
<a class="red-ui-menu-label">
|
|
988
|
+
<i class="pi pi-minus-circle context-menu-icon"></i>
|
|
989
|
+
<span class="context-menu-item-text">Remove All Points</span>
|
|
990
|
+
</a>
|
|
991
|
+
</li>
|
|
992
|
+
<li class="red-ui-menu-divider"></li>
|
|
993
|
+
<li class="context-menu-item" @click="handleContextMenuClick('setDeviceName')">
|
|
994
|
+
<a class="red-ui-menu-label">
|
|
995
|
+
<i class="pi pi-pencil context-menu-icon"></i>
|
|
996
|
+
<span class="context-menu-item-text">Set Device Name</span>
|
|
997
|
+
</a>
|
|
998
|
+
</li>
|
|
999
|
+
</ul>
|
|
1000
|
+
<!-- End Device Context Menu -->
|
|
1001
|
+
|
|
1002
|
+
<!-- Start Point Context Menu -->
|
|
1003
|
+
<ul
|
|
1004
|
+
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">
|
|
1005
|
+
<li class="context-menu-item" @click="handlePointContextMenuClick('setPointName')">
|
|
1006
|
+
<a class="red-ui-menu-label">
|
|
1007
|
+
<i class="pi pi-pencil context-menu-icon"></i>
|
|
1008
|
+
<span class="context-menu-item-text">Set Point Name</span>
|
|
1009
|
+
</a>
|
|
1010
|
+
</li>
|
|
1011
|
+
<li class="red-ui-menu-divider"></li>
|
|
1012
|
+
<li class="context-menu-item" @click="handlePointContextMenuClick('updatePoint')">
|
|
1013
|
+
<a class="red-ui-menu-label">
|
|
1014
|
+
<i class="pi pi-refresh context-menu-icon"></i>
|
|
1015
|
+
<span class="context-menu-item-text">Update Point</span>
|
|
1016
|
+
</a>
|
|
1017
|
+
</li>
|
|
1018
|
+
</ul>
|
|
1019
|
+
<!-- End Point Context Menu -->
|
|
1020
|
+
|
|
1021
|
+
<!--
|
|
697
1022
|
*
|
|
698
1023
|
*
|
|
699
1024
|
* Network Tree tab
|
|
700
1025
|
*
|
|
701
1026
|
*
|
|
702
1027
|
-->
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
</div>
|
|
1028
|
+
<div id="read-networkTree-tab" style="display:none">
|
|
1029
|
+
<div id="read-networkTree-tab-content" class="networkTreeContent" style="display:none">
|
|
1030
|
+
<p-dialog
|
|
1031
|
+
v-model:visible="showDeviceNameDialog"
|
|
1032
|
+
modal
|
|
1033
|
+
header="Set Display Name"
|
|
1034
|
+
:style="{ width: '25rem' }"
|
|
1035
|
+
class="deviceNameDialog">
|
|
1036
|
+
<div class="flex align-items-center gap-3 mb-3">
|
|
1037
|
+
<label for="displayName" class="font-semibold">Name</label>
|
|
1038
|
+
<p-input-text id="displayName" class="flex-auto" autocomplete="off" v-model="deviceDisplayNameValue" />
|
|
1039
|
+
</div>
|
|
1040
|
+
<div class="flex justify-content-end gap-2">
|
|
1041
|
+
<p-button
|
|
1042
|
+
class="diplayNameDialogCancel"
|
|
1043
|
+
type="button"
|
|
1044
|
+
label="Cancel"
|
|
1045
|
+
severity="secondary"
|
|
1046
|
+
@click="cancelDisplayNameDialog"></p-button>
|
|
1047
|
+
<p-button class="diplayNameDialogSave" type="button" label="Save" @click="setDeviceName"></p-button>
|
|
1048
|
+
</div>
|
|
1049
|
+
</p-dialog>
|
|
1050
|
+
|
|
1051
|
+
<p-dialog
|
|
1052
|
+
v-model:visible="showPointNameDialog"
|
|
1053
|
+
modal
|
|
1054
|
+
header="Set Point Name"
|
|
1055
|
+
:style="{ width: '25rem' }"
|
|
1056
|
+
class="deviceNameDialog">
|
|
1057
|
+
<div class="flex align-items-center gap-3 mb-3">
|
|
1058
|
+
<label for="displayName" class="font-semibold">Name</label>
|
|
1059
|
+
<p-input-text id="displayName" class="flex-auto" autocomplete="off" v-model="pointDisplayNameValue" />
|
|
1060
|
+
</div>
|
|
1061
|
+
<div class="flex justify-content-end gap-2">
|
|
1062
|
+
<p-button
|
|
1063
|
+
class="diplayNameDialogCancel"
|
|
1064
|
+
type="button"
|
|
1065
|
+
label="Cancel"
|
|
1066
|
+
severity="secondary"
|
|
1067
|
+
@click="cancelPointNameDialog"></p-button>
|
|
1068
|
+
<p-button class="diplayNameDialogSave" type="button" label="Save" @click="setPointName"></p-button>
|
|
1069
|
+
</div>
|
|
1070
|
+
</p-dialog>
|
|
1071
|
+
|
|
1072
|
+
<div class="headerDiv">
|
|
1073
|
+
<a class="countStatus" style="margin-left: 15px;">Count: {{deviceCount}} device(s)</a>
|
|
1074
|
+
<p-progressbar :value="progressBarValue" :show-value="true" style="width: 300px; height: 20px;"></p-progressbar>
|
|
1075
|
+
<div class="buttonGroup" style="padding-left: 15px;">
|
|
1076
|
+
<button @click="rebuildDataModel()" class="rebuildDataButton" title="Rebuild Data Model">
|
|
1077
|
+
<i class="pi pi-wrench" style="color: #ff0000;"></i>
|
|
1078
|
+
</button>
|
|
1079
|
+
<button @click="getData()" class="reloadButton" title="Reload Data">
|
|
1080
|
+
<i class="pi pi-refresh" style="color: #00AEEF;"></i>
|
|
1081
|
+
</button>
|
|
1082
|
+
<button @click="addAllDevices()" class="reloadButton" title="Add all devices">
|
|
1083
|
+
<i class="pi pi-plus" style="color: #00AEEF;"></i>
|
|
1084
|
+
</button>
|
|
1085
|
+
</div>
|
|
1086
|
+
</div>
|
|
1087
|
+
|
|
1088
|
+
<div id="deviceListApp" class="readDeviceList">
|
|
1089
|
+
<p-tree
|
|
1090
|
+
:value="devices"
|
|
1091
|
+
selectable="false"
|
|
1092
|
+
:filter="true"
|
|
1093
|
+
filterMode="lenient"
|
|
1094
|
+
filterPlaceholder="No results found."
|
|
1095
|
+
v-if="hasData()">
|
|
1096
|
+
<template #device="slotProps">
|
|
1097
|
+
<div @contextmenu="onDeviceRightClick(slotProps, $event)" class="p-treenode-label">
|
|
1098
|
+
<div v-if="isDeviceActive(slotProps)" class="deviceLabelParent">
|
|
1099
|
+
<b class="deviceLabel">
|
|
1100
|
+
<span class="statusOnline deviceStatus dotOnline dot"></span>
|
|
1101
|
+
{{slotProps.node.label}}
|
|
1102
|
+
<span v-if="hasMstpDevices(slotProps)" class="mstpDeviceCount"
|
|
1103
|
+
> {{calculateMstpCount(slotProps)}} </span
|
|
1104
|
+
>
|
|
1105
|
+
</b>
|
|
782
1106
|
</div>
|
|
783
1107
|
|
|
784
|
-
<
|
|
785
|
-
|
|
786
|
-
|
|
1108
|
+
<div v-else>
|
|
1109
|
+
<b class="deviceLabel">
|
|
1110
|
+
<span class="statusOffline deviceStatus dotOffline dot"></span>
|
|
1111
|
+
{{slotProps.node.label}}
|
|
1112
|
+
<span v-if="hasMstpDevices(slotProps)" class="mstpDeviceCount"
|
|
1113
|
+
> {{calculateMstpCount(slotProps)}} </span
|
|
1114
|
+
>
|
|
1115
|
+
</b>
|
|
1116
|
+
</div>
|
|
1117
|
+
|
|
1118
|
+
<template v-if="isDeviceAdded(slotProps)">
|
|
1119
|
+
<button class="addAllButton bacnetbutton">
|
|
1120
|
+
<i class="pi pi-check-circle " style="color: #00AEEF;"></i>
|
|
1121
|
+
</button>
|
|
1122
|
+
</template>
|
|
1123
|
+
<template v-else>
|
|
1124
|
+
<button @click="addAllClicked(slotProps)" class="addAllButton bacnetbutton">
|
|
1125
|
+
<i class="pi pi-plus-circle "><a class="allFunctionsText">Add all</a></i>
|
|
1126
|
+
</button>
|
|
1127
|
+
</template>
|
|
1128
|
+
|
|
1129
|
+
<button @click="removeAllClicked(slotProps, this)" class="removeAllButton bacnetbutton">
|
|
1130
|
+
<i class="pi pi-minus-circle "><a class="allFunctionsText">Remove all</a></i>
|
|
1131
|
+
</button>
|
|
1132
|
+
</div>
|
|
1133
|
+
</template>
|
|
1134
|
+
|
|
1135
|
+
<template #point="slotProps" v-model:class="pointContent">
|
|
1136
|
+
<div @contextmenu="onPointRightClick(slotProps, $event)">
|
|
1137
|
+
<b class="pointLabel">{{slotProps.node.label}}</b>
|
|
1138
|
+
|
|
1139
|
+
<template v-if="isSlotAdded(slotProps)">
|
|
1140
|
+
<button class="addPointButton bacnetbutton">
|
|
1141
|
+
<i class="pi pi-check-circle " style="color: #00AEEF;"></i>
|
|
1142
|
+
</button>
|
|
1143
|
+
</template>
|
|
1144
|
+
<template v-else>
|
|
1145
|
+
<button @click="addPointClicked(slotProps, this)" class="addPointButton bacnetbutton">
|
|
1146
|
+
<i class="pi pi-plus-circle "></i>
|
|
1147
|
+
</button>
|
|
1148
|
+
</template>
|
|
1149
|
+
|
|
1150
|
+
<button @click="removePointClicked(slotProps, this)" class="minusPointButton bacnetbutton">
|
|
1151
|
+
<i class="pi pi-minus-circle "></i>
|
|
1152
|
+
</button>
|
|
1153
|
+
</div>
|
|
1154
|
+
</template>
|
|
1155
|
+
</p-tree>
|
|
1156
|
+
<div v-else style="text-align: center; padding-top: 20px;">
|
|
1157
|
+
<a>Building Tree...</a>
|
|
1158
|
+
</div>
|
|
1159
|
+
</div>
|
|
1160
|
+
</div>
|
|
1161
|
+
|
|
1162
|
+
<img
|
|
1163
|
+
id="loadingGif"
|
|
1164
|
+
src="resources/@bitpoolos/edge-bacnet/BitpoolCube_Blue_350.gif"
|
|
1165
|
+
style="width: 70px; display: block; margin-left: auto; margin-right: auto;" />
|
|
1166
|
+
<p id="loadingText" style="display: block; margin-left: auto; margin-right: auto; text-align: center; color: #505050;">
|
|
1167
|
+
Loading current network info
|
|
1168
|
+
</p>
|
|
1169
|
+
</div>
|
|
787
1170
|
|
|
788
|
-
|
|
1171
|
+
<!--
|
|
789
1172
|
*
|
|
790
1173
|
*
|
|
791
1174
|
* Read list tab
|
|
792
1175
|
*
|
|
793
1176
|
*
|
|
794
1177
|
-->
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
</
|
|
819
|
-
|
|
1178
|
+
<div id="read-readList-tab" style="display:none">
|
|
1179
|
+
<div class="removeAllDevicesDiv">
|
|
1180
|
+
<button @click="exportReadList()" class="export-points-button" title="Export Read List">
|
|
1181
|
+
<i class="fa fa-cloud-download"> </i><a id="fileLabelText" style="padding-left: 10px;">Export</a>
|
|
1182
|
+
</button>
|
|
1183
|
+
<a id="exportJSON" style="display: none"></a>
|
|
1184
|
+
|
|
1185
|
+
<button @click="importReadList()" class="export-points-button" title="Import Read List">
|
|
1186
|
+
<i class="fa fa-cloud-upload" id="importReadListButton"></i
|
|
1187
|
+
><a id="fileLabelText" style="padding-left: 10px;">Import</a>
|
|
1188
|
+
</button>
|
|
1189
|
+
<input type="file" id="readlist-file-upload" accept="application/JSON" class="inputStyle" style="display: none;" />
|
|
1190
|
+
|
|
1191
|
+
<button @click="removeAllDevices()" class="removeAllDevicesButton" title="Remove all devices">
|
|
1192
|
+
<i class="pi pi-minus-circle" style="color: #ff0000; padding-right: 5px;"> </i
|
|
1193
|
+
><a style="color: #ff0000;">Remove All Devices</a>
|
|
1194
|
+
</button>
|
|
1195
|
+
</div>
|
|
1196
|
+
|
|
1197
|
+
<p-tree :value="readDevices">
|
|
1198
|
+
<template #device="slotProps">
|
|
1199
|
+
<b class="deviceLabel">{{slotProps.node.label}} </b>
|
|
1200
|
+
<button @click="removeAllClicked(slotProps, this)" class="removeAllButton bacnetbutton">
|
|
1201
|
+
<i class="pi pi-minus-circle "><a class="allFunctionsText">Remove all</a></i>
|
|
1202
|
+
</button>
|
|
1203
|
+
</template>
|
|
1204
|
+
|
|
1205
|
+
<template #point="slotProps" v-model:class="pointContent">
|
|
1206
|
+
<b class="pointLabel">{{slotProps.node.label}}</b>
|
|
1207
|
+
<button @click="removePointClicked(slotProps, this)" class="minusPointButton bacnetbutton">
|
|
1208
|
+
<i class="pi pi-minus-circle "></i>
|
|
1209
|
+
</button>
|
|
1210
|
+
</template>
|
|
1211
|
+
</p-tree>
|
|
1212
|
+
</div>
|
|
1213
|
+
<!--
|
|
820
1214
|
*
|
|
821
1215
|
*
|
|
822
1216
|
* Read Properties tab
|
|
823
1217
|
*
|
|
824
1218
|
*
|
|
825
1219
|
-->
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
</div>
|
|
850
|
-
</div>
|
|
851
|
-
|
|
852
|
-
<hr>
|
|
853
|
-
|
|
854
|
-
<div class="form-row msgTypeDiv" style="display: flex;">
|
|
855
|
-
<label for="node-input-msgType" style="text-decoration: underline;">
|
|
856
|
-
Message type:
|
|
857
|
-
</label>
|
|
858
|
-
<div id="node-input-msgType" style="display: flex; align-items: flex-start; flex-direction: column; padding-left: 50px;">
|
|
859
|
-
|
|
860
|
-
<!-- class= checkbox-round -->
|
|
861
|
-
<div style="display: flex; flex-direction: row; align-items: flex-start;"><input class="checkbox-round" type="checkbox" id="node-input-json" style="width: 13px; margin-left: 5px;"/><label for="node-input-json" style="padding-left: 20px; width: fit-content;">Block per device</label> </div>
|
|
862
|
-
<div style="display: flex; flex-direction: row; align-items: flex-start;"><input class="checkbox-round" type="checkbox" id="node-input-mqtt" style="width: 13px; margin-left: 5px;"/><label for="node-input-mqtt" style="padding-left: 20px; width: fit-content;">Individual msgs</label> </div>
|
|
863
|
-
|
|
864
|
-
</div>
|
|
865
|
-
</div>
|
|
866
|
-
|
|
867
|
-
<div class="form-row">
|
|
868
|
-
<label for="node-input-roundDecimal">
|
|
869
|
-
Decimal place precision
|
|
870
|
-
</label>
|
|
871
|
-
<input type="number" id="node-input-roundDecimal" placeholder="None">
|
|
872
|
-
</div>
|
|
873
|
-
|
|
874
|
-
<label for="node-input-hiddenDeployToggle" style="display: none !important;" >
|
|
875
|
-
<i class="icon-tag" style="display: none !important;"></i> <span data-i18n="bitpool-bacnet.label.hiddenDeployToggle" style="display: none !important;"></span>
|
|
876
|
-
<input style="display: none !important;" type="checkbox" id="node-input-hiddenDeployToggle">
|
|
1220
|
+
<div id="read-properties-tab" style="display:none">
|
|
1221
|
+
<div class="form-row" style="display: flex; width: fit-content; flex-wrap: nowrap; flex-direction: column;">
|
|
1222
|
+
<label
|
|
1223
|
+
for="node-input-object_properties_group"
|
|
1224
|
+
style="display: flex; align-items: center; white-space: nowrap; text-decoration: underline;"
|
|
1225
|
+
>Object Properties:</label
|
|
1226
|
+
>
|
|
1227
|
+
|
|
1228
|
+
<div
|
|
1229
|
+
id="node-input-object_properties_group"
|
|
1230
|
+
style="display: flex; align-items: flex-start; flex-direction: column; padding-left: 50px;">
|
|
1231
|
+
<div class="objectPropertiesLabel">
|
|
1232
|
+
<label
|
|
1233
|
+
for="node-input-object_property_simplePayload"
|
|
1234
|
+
style="padding-left: 4px; width: auto; align-items: start;"
|
|
1235
|
+
class="objectPropertiesLabel">
|
|
1236
|
+
<i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_simplePayload"></span>
|
|
1237
|
+
<input
|
|
1238
|
+
style="margin-left: 5px;"
|
|
1239
|
+
class=" objectProp"
|
|
1240
|
+
type="checkbox"
|
|
1241
|
+
id="node-input-object_property_simplePayload" />
|
|
1242
|
+
<a style="white-space: nowrap; padding-left: 20px;">Simple Payload</a>
|
|
877
1243
|
</label>
|
|
1244
|
+
</div>
|
|
1245
|
+
|
|
1246
|
+
<div class="objectPropertiesLabel">
|
|
1247
|
+
<label
|
|
1248
|
+
for="node-input-object_property_fullObject"
|
|
1249
|
+
style="padding-left: 4px; width: auto; align-items: start;"
|
|
1250
|
+
class="objectPropertiesLabel">
|
|
1251
|
+
<i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_fullObject"></span>
|
|
1252
|
+
<input
|
|
1253
|
+
style="margin-left: 5px;"
|
|
1254
|
+
class=" objectProp"
|
|
1255
|
+
type="checkbox"
|
|
1256
|
+
id="node-input-object_property_fullObject" />
|
|
1257
|
+
<a style="white-space: nowrap; padding-left: 20px;">Full Object</a>
|
|
1258
|
+
</label>
|
|
1259
|
+
</div>
|
|
1260
|
+
</div>
|
|
1261
|
+
</div>
|
|
1262
|
+
|
|
1263
|
+
<hr />
|
|
1264
|
+
|
|
1265
|
+
<div class="form-row msgTypeDiv" style="display: flex;">
|
|
1266
|
+
<label for="node-input-msgType" style="text-decoration: underline;"> Message type: </label>
|
|
1267
|
+
<div
|
|
1268
|
+
id="node-input-msgType"
|
|
1269
|
+
style="display: flex; align-items: flex-start; flex-direction: column; padding-left: 50px;">
|
|
1270
|
+
<!-- class= checkbox-round -->
|
|
1271
|
+
<div style="display: flex; flex-direction: row; align-items: flex-start;">
|
|
1272
|
+
<input
|
|
1273
|
+
class="checkbox-round"
|
|
1274
|
+
type="checkbox"
|
|
1275
|
+
id="node-input-json"
|
|
1276
|
+
style="width: 13px; margin-left: 5px;" /><label
|
|
1277
|
+
for="node-input-json"
|
|
1278
|
+
style="padding-left: 20px; width: fit-content;"
|
|
1279
|
+
>Block per device</label
|
|
1280
|
+
>
|
|
1281
|
+
</div>
|
|
1282
|
+
<div style="display: flex; flex-direction: row; align-items: flex-start;">
|
|
1283
|
+
<input
|
|
1284
|
+
class="checkbox-round"
|
|
1285
|
+
type="checkbox"
|
|
1286
|
+
id="node-input-mqtt"
|
|
1287
|
+
style="width: 13px; margin-left: 5px;" /><label
|
|
1288
|
+
for="node-input-mqtt"
|
|
1289
|
+
style="padding-left: 20px; width: fit-content;"
|
|
1290
|
+
>Individual msgs</label
|
|
1291
|
+
>
|
|
1292
|
+
</div>
|
|
878
1293
|
</div>
|
|
879
|
-
|
|
1294
|
+
</div>
|
|
1295
|
+
|
|
1296
|
+
<div class="form-row">
|
|
1297
|
+
<label for="node-input-roundDecimal"> Decimal place precision </label>
|
|
1298
|
+
<input type="number" id="node-input-roundDecimal" placeholder="None" />
|
|
1299
|
+
</div>
|
|
1300
|
+
|
|
1301
|
+
<label for="node-input-hiddenDeployToggle" style="display: none !important;">
|
|
1302
|
+
<i class="icon-tag" style="display: none !important;"></i>
|
|
1303
|
+
<span data-i18n="bitpool-bacnet.label.hiddenDeployToggle" style="display: none !important;"></span>
|
|
1304
|
+
<input style="display: none !important;" type="checkbox" id="node-input-hiddenDeployToggle" />
|
|
1305
|
+
</label>
|
|
1306
|
+
|
|
1307
|
+
<div class="form-row">
|
|
1308
|
+
<label for="node-input-exportCsv"> Points List </label>
|
|
1309
|
+
<label for="points-export" class="export-points-button" @click="exportPointListCsv()">
|
|
1310
|
+
<i class="fa fa-cloud-download" id="fileLabel"></i> <a id="points-export" style="padding-left: 10px;">Export</a>
|
|
1311
|
+
</label>
|
|
1312
|
+
</div>
|
|
880
1313
|
</div>
|
|
881
|
-
|
|
1314
|
+
</div>
|
|
882
1315
|
</script>
|
|
883
1316
|
<script type="text/html" data-help-name="Bacnet-Discovery">
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
<li><a href="https://app.bitpool.com/">app.bitpool.com</a> - set up your account.</li>
|
|
933
|
-
<li><a href="https://wiki.bitpool.com/">wiki.bitpool.com</a> - find more documentation.</li>
|
|
934
|
-
<li><a href="https://bacnet.org/">BACnet</a> - find more about the protocol.</li>
|
|
935
|
-
</ul>
|
|
1317
|
+
<p>A node used to view, select devices, device points, and point properties to add to the Read polling list.</p>
|
|
1318
|
+
|
|
1319
|
+
<h3><strong>Device List</strong></h3>
|
|
1320
|
+
<ol class="node-ports">
|
|
1321
|
+
<p>
|
|
1322
|
+
This tab displays the devices and device points that are a result of a network Discover. The data is broken down and
|
|
1323
|
+
listed as Devices, Points for the Device, and Point properties for the Points. On this tab the user may choose specific
|
|
1324
|
+
points to read, or all of the points present in a device. The reload button may be used to show any new data that may
|
|
1325
|
+
have been recieved by the bitpool BACnet node. Please note: Data can only be shown here once a Discover sucessfully
|
|
1326
|
+
recieves a response from online devices on the network
|
|
1327
|
+
</p>
|
|
1328
|
+
</ol>
|
|
1329
|
+
|
|
1330
|
+
<h3><strong>Read List</strong></h3>
|
|
1331
|
+
<ol class="node-ports">
|
|
1332
|
+
<p>This tab shows all of the devices and points that have been chosen to read.</p>
|
|
1333
|
+
</ol>
|
|
1334
|
+
|
|
1335
|
+
<h3><strong>Properties</strong></h3>
|
|
1336
|
+
<ol class="node-ports">
|
|
1337
|
+
<p>
|
|
1338
|
+
This tab shows all of the point properties the user may choose to read from the points detailed in the Read List tab.
|
|
1339
|
+
Here the user may also choose the output format of the Read data, as MQTT compatible Individual msgs, or a JSON block
|
|
1340
|
+
per device.
|
|
1341
|
+
</p>
|
|
1342
|
+
</ol>
|
|
1343
|
+
|
|
1344
|
+
<h3><strong>Examples</strong></h3>
|
|
1345
|
+
<p>
|
|
1346
|
+
For example flows, please use the examples section for this node. These examples can be found at: Node-red hamburger menu
|
|
1347
|
+
on top right -> Import -> Examples -> @bitpoolos/edge-bacnet
|
|
1348
|
+
</p>
|
|
1349
|
+
<p>
|
|
1350
|
+
To find captured examples of settings and flows, please go to our wiki
|
|
1351
|
+
<a href="https://wiki.bitpool.com/en/edge/apps/bitpool-edge/nr-bacnet">here</a>
|
|
1352
|
+
</p>
|
|
1353
|
+
|
|
1354
|
+
<h3>Resources:</h3>
|
|
1355
|
+
|
|
1356
|
+
<p><a href="https://youtu.be/4K7mVxfvfbc">Video Walk-through </a></p>
|
|
1357
|
+
|
|
1358
|
+
<h4><strong>Online Docs:</strong></h4>
|
|
1359
|
+
<ul type="1">
|
|
1360
|
+
<li><a href="https://www.bitpool.com/">bitpool.com</a> - check us out here.</li>
|
|
1361
|
+
<li><a href="https://app.bitpool.com/">app.bitpool.com</a> - set up your account.</li>
|
|
1362
|
+
<li><a href="https://wiki.bitpool.com/">wiki.bitpool.com</a> - find more documentation.</li>
|
|
1363
|
+
<li><a href="https://bacnet.org/">BACnet</a> - find more about the protocol.</li>
|
|
1364
|
+
</ul>
|
|
936
1365
|
</script>
|