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