@bitpoolos/edge-bacnet 1.2.4 → 1.2.6
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/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
- package/bacnet_client.js +17 -24
- package/bacnet_gateway.html +828 -601
- package/bacnet_gateway.js +133 -94
- package/bacnet_read.html +39 -5
- package/bacnet_read.js +0 -1
- package/bacnet_server.js +193 -40
- package/bacnet_write.html +52 -11
- package/common.js +139 -134
- package/package.json +1 -2
package/bacnet_gateway.html
CHANGED
|
@@ -8,634 +8,861 @@
|
|
|
8
8
|
<link href="resources/@bitpoolos/edge-bacnet/primeicons.css" rel="stylesheet" />
|
|
9
9
|
|
|
10
10
|
<script>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
11
|
+
//custom script loader to ensure dependencies load every time
|
|
12
|
+
(function () {
|
|
13
|
+
LoadScripts();
|
|
14
|
+
|
|
15
|
+
function LoadScripts(async) {
|
|
16
|
+
if (async === undefined) {
|
|
17
|
+
async = false;
|
|
18
|
+
}
|
|
19
|
+
var scripts = [];
|
|
20
|
+
var _scripts = [
|
|
21
|
+
"resources/@bitpoolos/edge-bacnet/vue.global.prod.js",
|
|
22
|
+
"resources/@bitpoolos/edge-bacnet/core.min.js",
|
|
23
|
+
"resources/@bitpoolos/edge-bacnet/confirmdialog.min.js",
|
|
24
|
+
"resources/@bitpoolos/edge-bacnet/confirmationservice.min.js",
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
if (async) {
|
|
28
|
+
LoadScriptsAsync(_scripts, scripts);
|
|
29
|
+
} else {
|
|
30
|
+
LoadScriptsSync(_scripts, scripts);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (x < _scripts.length) {
|
|
45
|
-
loopArray(_scripts, scripts);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
|
34
|
+
// what you are looking for :
|
|
35
|
+
function LoadScriptsSync(_scripts, scripts) {
|
|
36
|
+
var x = 0;
|
|
37
|
+
var loopArray = function (_scripts, scripts) {
|
|
38
|
+
// call itself
|
|
39
|
+
loadScript(_scripts[x], scripts[x], function () {
|
|
40
|
+
// set x to next item
|
|
41
|
+
x++;
|
|
42
|
+
// any more items in array?
|
|
43
|
+
if (x < _scripts.length) {
|
|
49
44
|
loopArray(_scripts, scripts);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
loadScript(_scripts[i], scripts[i], function () { });
|
|
56
|
-
}
|
|
57
|
-
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
loopArray(_scripts, scripts);
|
|
49
|
+
}
|
|
58
50
|
|
|
59
|
-
|
|
60
|
-
|
|
51
|
+
// async load as in your code
|
|
52
|
+
function LoadScriptsAsync(_scripts, scripts) {
|
|
53
|
+
for (var i = 0; i < _scripts.length; i++) {
|
|
54
|
+
loadScript(_scripts[i], scripts[i], function () {});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
58
|
+
// load script function with callback to handle synchronicity
|
|
59
|
+
function loadScript(src, script, callback) {
|
|
60
|
+
script = document.createElement("script");
|
|
61
|
+
script.onerror = function () {
|
|
62
|
+
// handling error when loading script
|
|
63
|
+
console.log("Error - could not load BACnet node HTML dependencies");
|
|
64
|
+
};
|
|
65
|
+
script.onload = function () {
|
|
66
|
+
callback();
|
|
67
|
+
};
|
|
68
|
+
script.src = src;
|
|
69
|
+
document.getElementsByTagName("head")[0].appendChild(script);
|
|
70
|
+
}
|
|
71
|
+
})();
|
|
74
72
|
</script>
|
|
75
73
|
|
|
76
74
|
<script type="text/javascript">
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
75
|
+
class NodeService {
|
|
76
|
+
getNetworkData() {
|
|
77
|
+
return fetch("/bitpool-bacnet-data/getNetworkTree").then((res) => res.json());
|
|
78
|
+
}
|
|
79
|
+
rebuildDataModel() {
|
|
80
|
+
return fetch("/bitpool-bacnet-data/rebuildDataModel").then((res) => res.json());
|
|
81
|
+
}
|
|
82
|
+
clearBacnetServerPoints() {
|
|
83
|
+
return fetch("/bitpool-bacnet-data/clearBacnetServerPoints").then((res) => res.json());
|
|
84
|
+
}
|
|
85
|
+
getBacnetServerPoints() {
|
|
86
|
+
return fetch('/bitpool-bacnet-data/getBacnetServerPoints').then(res => res.json());
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
RED.nodes.registerType("Bacnet-Gateway", {
|
|
90
|
+
category: "Bitpool BACnet",
|
|
91
|
+
color: "#00aeef",
|
|
92
|
+
defaults: {
|
|
93
|
+
name: { value: "" },
|
|
94
|
+
local_device_address: { value: "", required: true },
|
|
95
|
+
apduTimeout: { value: 6000 },
|
|
96
|
+
roundDecimal: { value: 2 },
|
|
97
|
+
local_device_port: { value: 47808, required: true },
|
|
98
|
+
apduSize: { value: "5", required: true },
|
|
99
|
+
maxSegments: { value: "0x50", required: true },
|
|
100
|
+
retries: { value: "5", required: true },
|
|
101
|
+
broadCastAddr: { value: "255.255.255.255", required: true },
|
|
102
|
+
toLogIam: { value: false },
|
|
103
|
+
discover_polling_schedule: { value: "60" },
|
|
104
|
+
discover_polling_schedule_value: { value: "1", required: true },
|
|
105
|
+
discover_polling_schedule_options: { value: "Minutes", required: true },
|
|
106
|
+
deviceId: { value: 817001, required: true },
|
|
107
|
+
manual_instance_range_enabled: { value: false },
|
|
108
|
+
manual_instance_range_start: { value: 0 },
|
|
109
|
+
manual_instance_range_end: { value: 10000 },
|
|
110
|
+
logErrorToConsole: { value: false },
|
|
111
|
+
serverEnabled: { value: false },
|
|
112
|
+
device_read_schedule: { value: "60" },
|
|
113
|
+
device_read_schedule_value: { value: "1", required: true },
|
|
114
|
+
device_read_schedule_options: { value: "Minutes", required: true },
|
|
115
|
+
deviceRangeRegisters: { value: [] },
|
|
116
|
+
},
|
|
117
|
+
networkInterfaces: [],
|
|
118
|
+
inputs: 1,
|
|
119
|
+
outputs: 1,
|
|
120
|
+
icon: "bitpool.svg",
|
|
121
|
+
label: function () {
|
|
122
|
+
return this.name || "gateway";
|
|
123
|
+
},
|
|
124
|
+
paletteLabel: function () {
|
|
125
|
+
return "gateway";
|
|
126
|
+
},
|
|
127
|
+
oneditprepare: function () {
|
|
128
|
+
let node = this;
|
|
129
|
+
|
|
130
|
+
let tabs = RED.tabs.create({
|
|
131
|
+
id: "node-input-read-tabs",
|
|
132
|
+
onchange: function (tab) {
|
|
133
|
+
$("#node-input-tabs-content").children().hide();
|
|
134
|
+
$("#" + tab.id).show();
|
|
117
135
|
},
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
label:
|
|
123
|
-
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
tabs.addTab({
|
|
139
|
+
id: "read-properties-tab",
|
|
140
|
+
label: "Gateway",
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
tabs.addTab({
|
|
144
|
+
id: "read-discover-tab",
|
|
145
|
+
label: "Discovery",
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
tabs.addTab({
|
|
149
|
+
id: "read-server-tab",
|
|
150
|
+
label: "Server",
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (node.networkInterfaces && node.networkInterfaces.length > 0) {
|
|
154
|
+
let nicSelector = document.getElementById("node-input-local_device_address");
|
|
155
|
+
node.networkInterfaces.forEach(function (option) {
|
|
156
|
+
nicSelector.options[nicSelector.options.length] = option;
|
|
157
|
+
});
|
|
158
|
+
nicSelector.value = node.local_device_address;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function queryAdapters() {
|
|
162
|
+
let nicSelector = document.getElementById("node-input-local_device_address");
|
|
163
|
+
$.ajax({
|
|
164
|
+
url: "/bitpool-bacnet-data/getNetworkInterfaces",
|
|
165
|
+
success: function (data) {
|
|
166
|
+
let keys = Object.keys(data);
|
|
167
|
+
|
|
168
|
+
for (const key in keys) {
|
|
169
|
+
let nicName = keys[key];
|
|
170
|
+
let ipAddr = data[keys[key]][0];
|
|
171
|
+
let text = nicName + " : " + ipAddr;
|
|
172
|
+
if (!node.networkInterfaces) node.networkInterfaces = [];
|
|
173
|
+
let found = node.networkInterfaces.findIndex((ele) => ele.text == text && ele.value == ipAddr);
|
|
174
|
+
if (found == -1) {
|
|
175
|
+
let newOption = new Option(text, ipAddr);
|
|
176
|
+
nicSelector.options[nicSelector.options.length] = newOption;
|
|
177
|
+
node.networkInterfaces.push(newOption);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
nicSelector.value = node.local_device_address;
|
|
181
|
+
},
|
|
182
|
+
timeout: 10000,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
queryAdapters();
|
|
187
|
+
|
|
188
|
+
function setManualInstanceRangeState(state) {
|
|
189
|
+
let deviceIdRangeStart = $("#node-input-manual_instance_range_start");
|
|
190
|
+
let deviceIdRangeEnd = $("#node-input-manual_instance_range_end");
|
|
191
|
+
if (state == true) {
|
|
192
|
+
deviceIdRangeStart.removeAttr("readonly");
|
|
193
|
+
deviceIdRangeEnd.removeAttr("readonly");
|
|
194
|
+
} else if (state == false) {
|
|
195
|
+
deviceIdRangeStart.attr("readonly", true);
|
|
196
|
+
deviceIdRangeEnd.attr("readonly", true);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
setManualInstanceRangeState(node.manual_instance_range_enabled);
|
|
201
|
+
|
|
202
|
+
$("#node-input-manual_instance_range_enabled").change(function (e) {
|
|
203
|
+
setManualInstanceRangeState(this.checked);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (node.vm1 == undefined) {
|
|
207
|
+
const confirmDialog = document.createElement("p-confirm-dialog");
|
|
208
|
+
document.getElementById("serverParent").appendChild(confirmDialog);
|
|
209
|
+
//document.getElementById("clearServerContainer").appendChild(confirmDialog);
|
|
210
|
+
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const { createApp, ref, onMounted } = Vue;
|
|
214
|
+
const ConfirmDialog = primevue.confirmdialog;
|
|
215
|
+
const ConfirmationService = primevue.confirmationservice;
|
|
216
|
+
const ListBox = primevue.listbox;
|
|
217
|
+
|
|
218
|
+
//prime vue app
|
|
219
|
+
const App = {
|
|
220
|
+
data() {
|
|
221
|
+
return {
|
|
222
|
+
nodeService: ref(new NodeService()),
|
|
223
|
+
serverObjects: ref([]),
|
|
224
|
+
selectedServerObject: ref()
|
|
225
|
+
};
|
|
124
226
|
},
|
|
125
|
-
|
|
126
|
-
|
|
227
|
+
mounted() {
|
|
228
|
+
this.getServerObjects();
|
|
127
229
|
},
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
230
|
+
methods: {
|
|
231
|
+
confirmAll(event) {
|
|
232
|
+
let app = this;
|
|
233
|
+
this.$confirm.require({
|
|
234
|
+
message: "Do you want to clear all the BACnet server points? This action is not reversible",
|
|
235
|
+
header: "Delete Confirmation",
|
|
236
|
+
icon: "pi pi-info-circle",
|
|
237
|
+
acceptClass: "p-button-danger",
|
|
238
|
+
accept: () => {
|
|
239
|
+
//handle accept
|
|
240
|
+
this.nodeService.clearBacnetServerPoints().then(function() {
|
|
241
|
+
app.getServerObjects();
|
|
138
242
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
label: "Gateway"
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
tabs.addTab(
|
|
147
|
-
{
|
|
148
|
-
id: "read-discover-tab",
|
|
149
|
-
label: "Discovery"
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
tabs.addTab(
|
|
153
|
-
{
|
|
154
|
-
id: "read-server-tab",
|
|
155
|
-
label: "Server"
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
if (node.networkInterfaces && node.networkInterfaces.length > 0) {
|
|
159
|
-
let nicSelector = document.getElementById("node-input-local_device_address");
|
|
160
|
-
node.networkInterfaces.forEach(function (option) {
|
|
161
|
-
nicSelector.options[nicSelector.options.length] = option;
|
|
162
|
-
});
|
|
163
|
-
nicSelector.value = node.local_device_address
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function queryAdapters() {
|
|
167
|
-
let nicSelector = document.getElementById("node-input-local_device_address");
|
|
168
|
-
$.ajax({
|
|
169
|
-
url: '/bitpool-bacnet-data/getNetworkInterfaces',
|
|
170
|
-
success: function (data) {
|
|
171
|
-
let keys = Object.keys(data);
|
|
172
|
-
|
|
173
|
-
for (const key in keys) {
|
|
174
|
-
let nicName = keys[key];
|
|
175
|
-
let ipAddr = data[keys[key]][0];
|
|
176
|
-
let text = nicName + ' : ' + ipAddr;
|
|
177
|
-
if (!node.networkInterfaces) node.networkInterfaces = [];
|
|
178
|
-
let found = node.networkInterfaces.findIndex(ele => ele.text == text && ele.value == ipAddr);
|
|
179
|
-
if (found == -1) {
|
|
180
|
-
let newOption = new Option(text, ipAddr);
|
|
181
|
-
nicSelector.options[nicSelector.options.length] = newOption;
|
|
182
|
-
node.networkInterfaces.push(newOption);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
nicSelector.value = node.local_device_address;
|
|
186
|
-
},
|
|
187
|
-
timeout: 10000
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
queryAdapters();
|
|
192
|
-
|
|
193
|
-
function setDeviceIdRangeState(state) {
|
|
194
|
-
let deviceIdRangeStart = $("#node-input-device_id_range_start");
|
|
195
|
-
let deviceIdRangeEnd = $("#node-input-device_id_range_end");
|
|
196
|
-
if(state == true) {
|
|
197
|
-
deviceIdRangeStart.removeAttr("readonly");
|
|
198
|
-
deviceIdRangeEnd.removeAttr("readonly");
|
|
199
|
-
} else if(state == false) {
|
|
200
|
-
deviceIdRangeStart.attr("readonly", true);
|
|
201
|
-
deviceIdRangeEnd.attr("readonly", true);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
setDeviceIdRangeState(node.device_id_range_enabled);
|
|
206
|
-
|
|
207
|
-
$("#node-input-device_id_range_enabled").change(function(e) {
|
|
208
|
-
setDeviceIdRangeState(this.checked);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
function setManualInstanceRangeState(state) {
|
|
212
|
-
let deviceIdRangeStart = $("#node-input-manual_instance_range_start");
|
|
213
|
-
let deviceIdRangeEnd = $("#node-input-manual_instance_range_end");
|
|
214
|
-
if(state == true) {
|
|
215
|
-
deviceIdRangeStart.removeAttr("readonly");
|
|
216
|
-
deviceIdRangeEnd.removeAttr("readonly");
|
|
217
|
-
} else if(state == false) {
|
|
218
|
-
deviceIdRangeStart.attr("readonly", true);
|
|
219
|
-
deviceIdRangeEnd.attr("readonly", true);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
setManualInstanceRangeState(node.manual_instance_range_enabled);
|
|
224
|
-
|
|
225
|
-
$("#node-input-manual_instance_range_enabled").change(function(e) {
|
|
226
|
-
setManualInstanceRangeState(this.checked);
|
|
243
|
+
},
|
|
244
|
+
reject: () => {
|
|
245
|
+
//handle reject
|
|
246
|
+
},
|
|
227
247
|
});
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
//prime vue app
|
|
239
|
-
const App = {
|
|
240
|
-
data() {
|
|
241
|
-
return {
|
|
242
|
-
nodeService: ref(new NodeService())
|
|
243
|
-
}
|
|
244
|
-
},
|
|
245
|
-
methods: {
|
|
246
|
-
confirm(event) {
|
|
247
|
-
this.$confirm.require({
|
|
248
|
-
message: 'Do you want to clear all the BACnet server points? This action is not reversible',
|
|
249
|
-
header: 'Delete Confirmation',
|
|
250
|
-
icon: 'pi pi-info-circle',
|
|
251
|
-
acceptClass: 'p-button-danger',
|
|
252
|
-
accept: () => {
|
|
253
|
-
//handle accept
|
|
254
|
-
this.nodeService.clearBacnetServerPoints();
|
|
255
|
-
},
|
|
256
|
-
reject: () => {
|
|
257
|
-
//handle reject
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
components: {
|
|
263
|
-
"p-button": primevue.button,
|
|
264
|
-
"p-confirm-dialog": primevue.confirmdialog,
|
|
265
|
-
"p-confirm-popup": primevue.confirmpopup
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
let vueapp = createApp(App);
|
|
270
|
-
vueapp.use(primevue.config.default);
|
|
271
|
-
vueapp.use(primevue.confirmpopup);
|
|
272
|
-
vueapp.use(primevue.confirmationservice);
|
|
273
|
-
node.vm1 = vueapp.mount("#clearServerContainer");
|
|
274
|
-
|
|
275
|
-
$("#file-upload").on("change", function(event) {
|
|
276
|
-
const input = event.target.files[0];
|
|
277
|
-
const reader = new FileReader();
|
|
278
|
-
|
|
279
|
-
reader.onload = function (e) {
|
|
280
|
-
const text = e.target.result;
|
|
281
|
-
|
|
282
|
-
let jsonPayload = JSON.parse(text);
|
|
283
|
-
|
|
248
|
+
},
|
|
249
|
+
confirm(json) {
|
|
250
|
+
this.$confirm.require({
|
|
251
|
+
message: 'Do you want to clear this BACnet server point? This action is not reversible',
|
|
252
|
+
header: 'Delete Confirmation',
|
|
253
|
+
icon: 'pi pi-info-circle',
|
|
254
|
+
acceptClass: 'p-button-danger',
|
|
255
|
+
accept: () => {
|
|
256
|
+
//handle accept
|
|
257
|
+
let app = this;
|
|
284
258
|
$.ajax({
|
|
285
259
|
type: "POST",
|
|
286
|
-
url: '/bitpool-bacnet-data/
|
|
260
|
+
url: '/bitpool-bacnet-data/clearBacnetServerPoint',
|
|
287
261
|
dataType: 'json',
|
|
288
262
|
contentType: 'application/json',
|
|
289
|
-
data: JSON.stringify(
|
|
263
|
+
data: JSON.stringify(json),
|
|
290
264
|
success: function (result) {
|
|
265
|
+
app.getServerObjects();
|
|
291
266
|
},
|
|
292
267
|
timeout: 10000
|
|
293
268
|
});
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
269
|
+
},
|
|
270
|
+
reject: () => {
|
|
271
|
+
//handle reject
|
|
272
|
+
}
|
|
298
273
|
});
|
|
274
|
+
},
|
|
275
|
+
getServerObjects() {
|
|
276
|
+
let app = this;
|
|
277
|
+
this.nodeService.getBacnetServerPoints().then(function (result) {
|
|
278
|
+
app.serverObjects = result;
|
|
279
|
+
})
|
|
280
|
+
},
|
|
281
|
+
formatObjectName(name) {
|
|
282
|
+
//return shortened point name if longer than 50 characters
|
|
283
|
+
if(name.length > 45) {
|
|
284
|
+
return name.slice(0, 45).concat("...");
|
|
285
|
+
}
|
|
286
|
+
return name;
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
components: {
|
|
290
|
+
"p-button": primevue.button,
|
|
291
|
+
"p-confirm-dialog": primevue.confirmdialog,
|
|
292
|
+
"p-confirm-popup": primevue.confirmpopup,
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
let vueapp = createApp(App);
|
|
297
|
+
vueapp.use(primevue.config.default);
|
|
298
|
+
vueapp.use(primevue.confirmpopup);
|
|
299
|
+
vueapp.use(primevue.confirmationservice);
|
|
300
|
+
node.vm1 = vueapp.mount("#serverParent");
|
|
301
|
+
|
|
302
|
+
$("#file-upload").on("change", function (event) {
|
|
303
|
+
const input = event.target.files[0];
|
|
304
|
+
const reader = new FileReader();
|
|
305
|
+
|
|
306
|
+
reader.onload = function (e) {
|
|
307
|
+
const text = e.target.result;
|
|
308
|
+
|
|
309
|
+
let jsonPayload = JSON.parse(text);
|
|
310
|
+
|
|
311
|
+
$.ajax({
|
|
312
|
+
type: "POST",
|
|
313
|
+
url: "/bitpool-bacnet-data/updateDeviceList",
|
|
314
|
+
dataType: "json",
|
|
315
|
+
contentType: "application/json",
|
|
316
|
+
data: JSON.stringify(jsonPayload),
|
|
317
|
+
success: function (result) {},
|
|
318
|
+
timeout: 10000,
|
|
319
|
+
});
|
|
320
|
+
};
|
|
299
321
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
322
|
+
reader.readAsText(input);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
$("#file-export").click(function (params) {
|
|
326
|
+
$.ajax({
|
|
327
|
+
url: "/bitpool-bacnet-data/getDeviceList",
|
|
328
|
+
success: function (deviceList) {
|
|
329
|
+
let data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(deviceList));
|
|
330
|
+
let aEle = document.getElementById("exportJSON");
|
|
331
|
+
aEle.setAttribute("href", "data:" + data);
|
|
332
|
+
aEle.setAttribute("download", "bitpool-bacnet-devices.json");
|
|
333
|
+
aEle.click();
|
|
334
|
+
},
|
|
335
|
+
timeout: 10000,
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
//device scan range matrix
|
|
340
|
+
$("#node-input-deviceIdRangeMatrix-container")
|
|
341
|
+
.css("min-width", "350px")
|
|
342
|
+
.editableList({
|
|
343
|
+
addItem: function (row, index, data) {
|
|
344
|
+
row.css({ overflow: "none", whiteSpace: "nowrap" });
|
|
345
|
+
let rowData = {};
|
|
346
|
+
if (data && typeof data.enabled == "boolean" && typeof data.start == "string" && typeof data.end == "string") {
|
|
347
|
+
if (data.enabled === true) {
|
|
348
|
+
rowData.enabled = "checked";
|
|
349
|
+
} else if (data.enabled === false) {
|
|
350
|
+
rowData.enabled = "";
|
|
351
|
+
}
|
|
352
|
+
rowData.start = data.start;
|
|
353
|
+
rowData.end = data.end;
|
|
354
|
+
} else {
|
|
355
|
+
rowData.enabled = "";
|
|
356
|
+
rowData.start = "";
|
|
357
|
+
rowData.end = "";
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
let fragment = document.createDocumentFragment();
|
|
361
|
+
let htmlRow = `
|
|
362
|
+
<div class="form-row deviceIdRangeEntry" id="node-input-device_id_range">
|
|
363
|
+
<label for="node-input-device_id_range_entry"><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.device_id_range_entry"></span>Enabled</label>
|
|
364
|
+
<input type="checkbox" id="node-input-device_id_range_entry_enabled" style="width: auto;" ${rowData.enabled} />
|
|
365
|
+
<a style="padding-left: 60px;">Start: </a><input type="number" id="node-input-device_id_range_entry_start" style="width: 125px;" min="0" max="4194303" value="${rowData.start}" />
|
|
366
|
+
<a style="padding-left: 35px;">End: </a><input type="number" id="node-input-device_id_range_entry_end" style="width: 125px;" min="1" max="4194303" value="${rowData.end}" />
|
|
367
|
+
</div>`;
|
|
368
|
+
|
|
369
|
+
$(htmlRow).appendTo(fragment);
|
|
370
|
+
row[0].appendChild(fragment);
|
|
371
|
+
document.getElementById("node-input-reg-block-count").innerHTML = $(
|
|
372
|
+
"#node-input-deviceIdRangeMatrix-container"
|
|
373
|
+
).editableList("length");
|
|
374
|
+
},
|
|
375
|
+
removeItem: function (data) {
|
|
376
|
+
document.getElementById("node-input-reg-block-count").innerHTML = $(
|
|
377
|
+
"#node-input-deviceIdRangeMatrix-container"
|
|
378
|
+
).editableList("length");
|
|
379
|
+
},
|
|
380
|
+
removable: true,
|
|
381
|
+
scrollOnAdd: false,
|
|
382
|
+
header: $("<div style='display:flex; padding:10px 10px 0px 5px; column-gap: 10px'>").append(
|
|
383
|
+
$.parseHTML(
|
|
384
|
+
"<div><p>Count:</p></div><div style='color: gray'><label id='node-input-reg-block-count'>0</label> <span style='padding-left: 85px;'>Device ID Range(s)</span> </div>"
|
|
385
|
+
)
|
|
386
|
+
),
|
|
387
|
+
buttons: [
|
|
388
|
+
{
|
|
389
|
+
label: "json",
|
|
390
|
+
icon: "fa fa-share",
|
|
391
|
+
title: "Export tags as JSON to browser",
|
|
392
|
+
click: function (evt) {
|
|
393
|
+
var deviceRangeRegisters = $("#node-input-deviceIdRangeMatrix-container").editableList("items");
|
|
394
|
+
var mapItems = [];
|
|
395
|
+
deviceRangeRegisters.each(function (i) {
|
|
396
|
+
var registerMap = $(this);
|
|
397
|
+
var mapItem = {
|
|
398
|
+
enabled: registerMap.find("#node-input-device_id_range_entry_enabled").val(),
|
|
399
|
+
start: registerMap.find("#node-input-device_id_range_entry_start").val(),
|
|
400
|
+
end: registerMap.find("#node-input-device_id_range_entry_end").val(),
|
|
401
|
+
};
|
|
402
|
+
mapItems.push(mapItem);
|
|
311
403
|
});
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
case "Days":
|
|
341
|
-
return value * 86400;
|
|
342
|
-
break;
|
|
343
|
-
default:
|
|
344
|
-
// code block
|
|
404
|
+
var oMyBlob = new Blob(
|
|
405
|
+
[
|
|
406
|
+
JSON.stringify(mapItems, null, 0)
|
|
407
|
+
.replaceAll(/\[{/gi, "[\n{")
|
|
408
|
+
.replaceAll(/}\]/gi, "}\n]")
|
|
409
|
+
.replaceAll(/},/gi, "},\n")
|
|
410
|
+
.replaceAll(/{/gi, " {"),
|
|
411
|
+
],
|
|
412
|
+
{ type: "text/json" }
|
|
413
|
+
);
|
|
414
|
+
window.open(URL.createObjectURL(oMyBlob));
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
label: "delete",
|
|
419
|
+
icon: "fa-regular fa-trash-can",
|
|
420
|
+
title: "Delete all registers",
|
|
421
|
+
click: function (evt) {
|
|
422
|
+
$("#node-input-deviceIdRangeMatrix-container").editableList("empty");
|
|
423
|
+
document.getElementById("node-input-reg-block-count").innerHTML = 0;
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
if (node.deviceRangeRegisters.length > 0) {
|
|
430
|
+
for (var i = 0; i < node.deviceRangeRegisters.length; i++) {
|
|
431
|
+
$("#node-input-deviceIdRangeMatrix-container").editableList("addItem", node.deviceRangeRegisters[i]);
|
|
345
432
|
}
|
|
433
|
+
} else {
|
|
434
|
+
$("#node-input-deviceIdRangeMatrix-container").editableList("addItem", {
|
|
435
|
+
enabled: true,
|
|
436
|
+
start: "0",
|
|
437
|
+
end: "4194303",
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
document.getElementById("node-input-reg-block-count").innerHTML = node.deviceRangeRegisters.length;
|
|
442
|
+
},
|
|
443
|
+
oneditsave: function () {
|
|
444
|
+
let node = this;
|
|
445
|
+
|
|
446
|
+
document.getElementById("node-input-discover_polling_schedule").value = getTimePeriodInSeconds(
|
|
447
|
+
document.getElementById("node-input-discover_polling_schedule_value").value,
|
|
448
|
+
document.getElementById("node-input-discover_polling_schedule_options").value
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
document.getElementById("node-input-device_read_schedule").value = getTimePeriodInSeconds(
|
|
452
|
+
document.getElementById("node-input-device_read_schedule_value").value,
|
|
453
|
+
document.getElementById("node-input-device_read_schedule_options").value
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
node.deviceRangeRegisters = [];
|
|
457
|
+
let deviceRanges = $("#node-input-deviceIdRangeMatrix-container").editableList("items");
|
|
458
|
+
deviceRanges.each(function (i) {
|
|
459
|
+
let map = $(this);
|
|
460
|
+
let mapItem = {
|
|
461
|
+
enabled: map.find("#node-input-device_id_range_entry_enabled").is(":checked"),
|
|
462
|
+
start: map.find("#node-input-device_id_range_entry_start").val(),
|
|
463
|
+
end: map.find("#node-input-device_id_range_entry_end").val(),
|
|
464
|
+
};
|
|
465
|
+
node.deviceRangeRegisters.push(mapItem);
|
|
466
|
+
});
|
|
467
|
+
},
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
function getTimePeriodInSeconds(value, interval) {
|
|
471
|
+
switch (interval) {
|
|
472
|
+
case "Seconds":
|
|
473
|
+
return value;
|
|
474
|
+
break;
|
|
475
|
+
case "Minutes":
|
|
476
|
+
return value * 60;
|
|
477
|
+
break;
|
|
478
|
+
case "Hours":
|
|
479
|
+
return value * 3600;
|
|
480
|
+
break;
|
|
481
|
+
case "Days":
|
|
482
|
+
return value * 86400;
|
|
483
|
+
break;
|
|
484
|
+
default:
|
|
485
|
+
// code block
|
|
346
486
|
}
|
|
347
|
-
|
|
487
|
+
}
|
|
348
488
|
</script>
|
|
349
489
|
|
|
350
490
|
<script type="text/html" data-template-name="Bacnet-Gateway">
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
491
|
+
<style>
|
|
492
|
+
.deviceButton {
|
|
493
|
+
width: -webkit-fill-available;
|
|
494
|
+
background: space;
|
|
495
|
+
background-color: transparent !important;
|
|
496
|
+
color: inherit;
|
|
497
|
+
border: none;
|
|
498
|
+
display: flex;
|
|
499
|
+
align-items: center;
|
|
500
|
+
padding-top: 20px;
|
|
501
|
+
}
|
|
502
|
+
.point {
|
|
503
|
+
padding-left: 50px;
|
|
504
|
+
}
|
|
505
|
+
.pointButton {
|
|
506
|
+
color: inherit;
|
|
507
|
+
border: none;
|
|
508
|
+
display: flex;
|
|
509
|
+
align-items: center;
|
|
510
|
+
}
|
|
511
|
+
.networkTreeContent {
|
|
512
|
+
margin-top: 20px;
|
|
513
|
+
margin-left: 50px;
|
|
514
|
+
}
|
|
515
|
+
.p-confirm-dialog-accept > .p-button-label,
|
|
516
|
+
.bacnetServerRebuildSchedule_clearButton > .p-button-label {
|
|
517
|
+
color: white;
|
|
518
|
+
}
|
|
519
|
+
.red-ui-editor label {
|
|
520
|
+
font-size: 12px;
|
|
521
|
+
}
|
|
522
|
+
.bacnetServerRebuildSchedule_clearButton {
|
|
523
|
+
box-shadow: 0 0 0 0.2rem #edacac !important;
|
|
524
|
+
width: 200px;
|
|
525
|
+
border-radius: 3px;
|
|
526
|
+
margin-left: 140px !important;
|
|
527
|
+
}
|
|
528
|
+
.clearServerContainer {
|
|
529
|
+
position: absolute;
|
|
530
|
+
top: 55px;
|
|
531
|
+
margin-left: 280px;
|
|
532
|
+
}
|
|
533
|
+
.bacnetServerPoint_clearButton {
|
|
534
|
+
box-shadow: 0 0 0 0.2rem #edacac !important;
|
|
535
|
+
width: 200px;
|
|
536
|
+
border-radius: 3px;
|
|
537
|
+
margin-left: 420px !important;
|
|
538
|
+
margin-top: -75px !important;
|
|
539
|
+
}
|
|
540
|
+
.bacnetServerPoint_clearButton > .p-button-label {
|
|
541
|
+
color: white;
|
|
542
|
+
}
|
|
543
|
+
.clear_server_span {
|
|
544
|
+
width: 0px;
|
|
545
|
+
}
|
|
546
|
+
.read_server_parent_div {
|
|
547
|
+
display: flex;
|
|
548
|
+
align-items: flex-start;
|
|
549
|
+
justify-content: space-around;
|
|
550
|
+
flex-direction: column;
|
|
551
|
+
}
|
|
552
|
+
button {
|
|
553
|
+
height: 34px;
|
|
554
|
+
border-radius: 5px !important;
|
|
555
|
+
}
|
|
556
|
+
.inputStyle {
|
|
557
|
+
color: var(--red-ui-workspace-button-color-hover) !important;
|
|
558
|
+
background: var(--red-ui-workspace-button-background-hover);
|
|
559
|
+
border: 1px solid var(--red-ui-secondary-border-color);
|
|
560
|
+
border-radius: 5px !important;
|
|
561
|
+
}
|
|
562
|
+
input[type="file"] {
|
|
563
|
+
display: none !important;
|
|
564
|
+
}
|
|
565
|
+
.custom-file-upload {
|
|
566
|
+
border: 1px solid var(--red-ui-secondary-border-color);
|
|
567
|
+
border-radius: 5px !important;
|
|
568
|
+
padding: 6px 12px;
|
|
569
|
+
cursor: pointer;
|
|
570
|
+
}
|
|
571
|
+
.uploadButton {
|
|
572
|
+
margin-bottom: 5px !important;
|
|
573
|
+
margin-left: 30px !important;
|
|
574
|
+
}
|
|
575
|
+
.point_name {
|
|
576
|
+
font-weight: bold;
|
|
577
|
+
}
|
|
578
|
+
</style>
|
|
579
|
+
|
|
580
|
+
<div class="form-row node-input-read-tabs-row">
|
|
581
|
+
<ul style="min-width:600px;margin-bottom:20px" id="node-input-read-tabs"></ul>
|
|
582
|
+
</div>
|
|
583
|
+
|
|
584
|
+
<div id="node-input-tabs-content">
|
|
585
|
+
<div id="read-properties-tab" style="display:none">
|
|
586
|
+
<div class="form-row">
|
|
587
|
+
<label for="node-input-name"><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.name"></span> Name</label>
|
|
588
|
+
<input type="text" id="node-input-name" placeholder="Name" />
|
|
589
|
+
</div>
|
|
590
|
+
|
|
591
|
+
<div class="form-row node-input-read-tabs-row">
|
|
592
|
+
<ul style="min-width:600px;margin-bottom:20px" id="node-input-read-tabs"></ul>
|
|
593
|
+
</div>
|
|
594
|
+
|
|
595
|
+
<div class="form-row" id="networkInterfaceDiv">
|
|
596
|
+
<label for="node-input-local_device_address"
|
|
597
|
+
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.local_device_address"></span> Network
|
|
598
|
+
Interface</label
|
|
599
|
+
>
|
|
600
|
+
<select id="node-input-local_device_address" style="width: 70%;"></select>
|
|
601
|
+
</div>
|
|
602
|
+
|
|
603
|
+
<div class="form-row">
|
|
604
|
+
<label for="node-input-broadCastAddr"
|
|
605
|
+
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.broadCastAddr"></span> Broadcast Address
|
|
606
|
+
</label>
|
|
607
|
+
<input type="text" id="node-input-broadCastAddr" placeholder="255.255.255.255" />
|
|
608
|
+
</div>
|
|
609
|
+
|
|
610
|
+
<div class="form-row">
|
|
611
|
+
<label for="node-input-local_device_port"
|
|
612
|
+
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.local_device_port"></span> Local Device Port</label
|
|
613
|
+
>
|
|
614
|
+
<input type="text" id="node-input-local_device_port" placeholder="47808" />
|
|
615
|
+
</div>
|
|
616
|
+
|
|
617
|
+
<div class="form-row">
|
|
618
|
+
<label for="node-input-deviceId"
|
|
619
|
+
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.deviceId"></span> Device ID
|
|
620
|
+
</label>
|
|
621
|
+
<input type="text" id="node-input-deviceId" placeholder="817001" />
|
|
622
|
+
</div>
|
|
623
|
+
|
|
624
|
+
<div class="form-row node-input-deviceIdRangeMatrix-container-row">
|
|
625
|
+
<ol id="node-input-deviceIdRangeMatrix-container" style="width: 600px;"></ol>
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
628
|
+
|
|
629
|
+
<div id="read-discover-tab" style="display:none">
|
|
630
|
+
<div class="form-row">
|
|
631
|
+
<label for="node-input-apduTimeout"
|
|
632
|
+
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.apduTimeout"></span>Apdu Timeout</label
|
|
633
|
+
>
|
|
634
|
+
<input type="text" id="node-input-apduTimeout" placeholder="10000" />
|
|
635
|
+
</div>
|
|
636
|
+
|
|
637
|
+
<div class="form-row">
|
|
638
|
+
<label for="node-input-apduSize"
|
|
639
|
+
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.apduSize"></span>Max Apdu Size</label
|
|
640
|
+
>
|
|
641
|
+
<select id="node-input-apduSize">
|
|
642
|
+
<option value="0">0</option>
|
|
643
|
+
<option value="1">1</option>
|
|
644
|
+
<option value="2">2</option>
|
|
645
|
+
<option value="3">3</option>
|
|
646
|
+
<option value="4">4</option>
|
|
647
|
+
<option value="5">5</option>
|
|
648
|
+
</select>
|
|
649
|
+
</div>
|
|
650
|
+
|
|
651
|
+
<div class="form-row">
|
|
652
|
+
<label for="node-input-maxSegments"
|
|
653
|
+
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.maxSegments"></span>Max Segments</label
|
|
654
|
+
>
|
|
655
|
+
<select id="node-input-maxSegments">
|
|
656
|
+
<option value="0">0</option>
|
|
657
|
+
<option value="0x10">0x10</option>
|
|
658
|
+
<option value="0x20">0x20</option>
|
|
659
|
+
<option value="0x30">0x30</option>
|
|
660
|
+
<option value="0x40">0x40</option>
|
|
661
|
+
<option value="0x50">0x50</option>
|
|
662
|
+
<option value="0x60">0x60</option>
|
|
663
|
+
<option value="0x70">0x70</option>
|
|
664
|
+
</select>
|
|
665
|
+
</div>
|
|
666
|
+
|
|
667
|
+
<div class="form-row">
|
|
668
|
+
<label for="node-input-retries"
|
|
669
|
+
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.retries"></span>Number of Retries</label
|
|
670
|
+
>
|
|
671
|
+
<select id="node-input-retries">
|
|
672
|
+
<option value="0">0</option>
|
|
673
|
+
<option value="1">1</option>
|
|
674
|
+
<option value="2">2</option>
|
|
675
|
+
<option value="3">3</option>
|
|
676
|
+
<option value="4">4</option>
|
|
677
|
+
<option value="5">5</option>
|
|
678
|
+
<option value="6">6</option>
|
|
679
|
+
<option value="7">7</option>
|
|
680
|
+
<option value="8">8</option>
|
|
681
|
+
<option value="9">9</option>
|
|
682
|
+
<option value="10">10</option>
|
|
683
|
+
</select>
|
|
684
|
+
</div>
|
|
685
|
+
|
|
686
|
+
<div class="form-row" style="align-items: center; display: flex;">
|
|
687
|
+
<label for="node-input-discover_polling_schedule_value"
|
|
688
|
+
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.bacnet_polling_schedule"></span>Device Discover
|
|
689
|
+
Frequency</label
|
|
690
|
+
>
|
|
691
|
+
<p style="margin-right: 5px; margin-bottom: 0px; padding-left: 7px;">Every</p>
|
|
692
|
+
<input type="text" id="node-input-discover_polling_schedule" style="display: none;" />
|
|
693
|
+
<input
|
|
694
|
+
type="text"
|
|
695
|
+
id="node-input-discover_polling_schedule_value"
|
|
696
|
+
placeholder="5"
|
|
697
|
+
style="width: 70px; margin-right: 5px;" />
|
|
698
|
+
<select name="timePeriod" id="node-input-discover_polling_schedule_options" style="width: 120px; margin-right: 5px;">
|
|
699
|
+
<option value="Seconds">Seconds</option>
|
|
700
|
+
<option value="Minutes">Minutes</option>
|
|
701
|
+
<option value="Hours">Hours</option>
|
|
702
|
+
<option value="Days">Days</option>
|
|
703
|
+
</select>
|
|
704
|
+
</div>
|
|
705
|
+
|
|
706
|
+
<div class="form-row deviceIdRange">
|
|
707
|
+
<label for="node-input-device_id_range"
|
|
708
|
+
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.device_id_range"></span>Point Discovery Range
|
|
709
|
+
</label>
|
|
710
|
+
<input type="checkbox" id="node-input-manual_instance_range_enabled" style="width: auto;" />
|
|
711
|
+
<a style="padding-left: 60px;">Start: </a
|
|
712
|
+
><input type="number" id="node-input-manual_instance_range_start" style="width: 125px;" min="0" max="100000" />
|
|
713
|
+
<a style="padding-left: 35px;">End: </a
|
|
714
|
+
><input type="number" id="node-input-manual_instance_range_end" style="width: 125px;" min="1" max="100000" />
|
|
715
|
+
</div>
|
|
716
|
+
|
|
717
|
+
<div class="form-row" style="align-items: center; display: flex;">
|
|
718
|
+
<label for="node-input-device_read_schedule_value"
|
|
719
|
+
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.device_read_schedule"></span>Object Discover
|
|
720
|
+
Frequency</label
|
|
721
|
+
>
|
|
722
|
+
<p style="margin-right: 5px; margin-bottom: 0px; padding-left: 7px;">Every</p>
|
|
723
|
+
<input type="text" id="node-input-device_read_schedule" style="display: none;" />
|
|
724
|
+
<input
|
|
725
|
+
type="text"
|
|
726
|
+
id="node-input-device_read_schedule_value"
|
|
727
|
+
placeholder="5"
|
|
728
|
+
style="width: 70px; margin-right: 5px;" />
|
|
729
|
+
<select name="timePeriod" id="node-input-device_read_schedule_options" style="width: 120px; margin-right: 5px;">
|
|
730
|
+
<option value="Seconds">Seconds</option>
|
|
731
|
+
<option value="Minutes">Minutes</option>
|
|
732
|
+
<option value="Hours">Hours</option>
|
|
733
|
+
<option value="Days">Days</option>
|
|
734
|
+
</select>
|
|
735
|
+
</div>
|
|
736
|
+
|
|
737
|
+
<div class="form-row">
|
|
738
|
+
<label for="node-input-toLog"> Log found device: </label>
|
|
739
|
+
<input type="checkbox" id="node-input-toLogIam" style="width: auto;" />
|
|
740
|
+
</div>
|
|
741
|
+
|
|
742
|
+
<div class="form-row">
|
|
743
|
+
<label for="node-input-logErrorToConsole"> Log BACnet errors to console: </label>
|
|
744
|
+
<input type="checkbox" id="node-input-logErrorToConsole" style="width: auto;" />
|
|
745
|
+
</div>
|
|
746
|
+
|
|
747
|
+
<div class="form-row" id="importDeviceList">
|
|
748
|
+
<label> Device List: </label>
|
|
749
|
+
<label for="file-upload" class="custom-file-upload">
|
|
750
|
+
<i class="fa fa-cloud-upload" id="fileLabel"></i> <a id="fileLabelText" style="padding-left: 10px;">Import</a>
|
|
751
|
+
</label>
|
|
752
|
+
<input type="file" id="file-upload" accept="application/JSON" class="inputStyle" style="width: 258px;" />
|
|
753
|
+
<label for="file-export" class="custom-file-upload" style="margin-left: 30px;">
|
|
754
|
+
<i class="fa fa-cloud-download" id="fileLabel"></i> <a id="fileLabelText" style="padding-left: 10px;">Export</a>
|
|
755
|
+
</label>
|
|
756
|
+
<input id="file-export" class="inputStyle" style="width: 258px; display: none;" />
|
|
757
|
+
<a id="exportJSON" style="display: none"></a>
|
|
758
|
+
</div>
|
|
759
|
+
</div>
|
|
760
|
+
|
|
761
|
+
<div id="read-server-tab" style="display:none">
|
|
762
|
+
|
|
763
|
+
<div class="form-row">
|
|
764
|
+
<label for="node-input-serverEnabled"> Enabled: </label>
|
|
765
|
+
<input type="checkbox" id="node-input-serverEnabled" style="width: auto;" />
|
|
766
|
+
</div>
|
|
767
|
+
|
|
768
|
+
<div class="read_server_parent_div" id="serverParent">
|
|
769
|
+
|
|
770
|
+
<div class="form-row clearServerContainer" id="clearServerContainer" style="align-items: center; display: flex;">
|
|
771
|
+
<p-button
|
|
772
|
+
class="bacnetServerRebuildSchedule_clearButton p-button-danger"
|
|
773
|
+
@click="confirmAll($event)"
|
|
774
|
+
label="Reinitialize Server">
|
|
775
|
+
</p-button>
|
|
423
776
|
</div>
|
|
424
777
|
|
|
425
|
-
<div id=
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
<p style="margin-right: 5px; margin-bottom: 0px; padding-left: 7px;">Every</p>
|
|
517
|
-
<input type="text" id="node-input-discover_polling_schedule" style="display: none;">
|
|
518
|
-
<input type="text" id="node-input-discover_polling_schedule_value" placeholder="5" style="width: 70px; margin-right: 5px;">
|
|
519
|
-
<select name="timePeriod" id="node-input-discover_polling_schedule_options" style="width: 120px; margin-right: 5px;">
|
|
520
|
-
<option value="Seconds">Seconds</option>
|
|
521
|
-
<option value="Minutes">Minutes</option>
|
|
522
|
-
<option value="Hours">Hours</option>
|
|
523
|
-
<option value="Days">Days</option>
|
|
524
|
-
</select>
|
|
525
|
-
</div>
|
|
526
|
-
|
|
527
|
-
<div class="form-row deviceIdRange">
|
|
528
|
-
<label for="node-input-device_id_range"><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.device_id_range"></span>Point Discovery Range </label>
|
|
529
|
-
<input type="checkbox" id="node-input-manual_instance_range_enabled" style="width: auto;"/>
|
|
530
|
-
<a style="padding-left: 60px;">Start: </a><input type="number" id="node-input-manual_instance_range_start" style="width: 125px;" min="0" max="100000"/>
|
|
531
|
-
<a style="padding-left: 35px;">End: </a><input type="number" id="node-input-manual_instance_range_end" style="width: 125px;" min="1" max="100000"/>
|
|
532
|
-
</div>
|
|
533
|
-
|
|
534
|
-
<div class="form-row" style="align-items: center; display: flex;">
|
|
535
|
-
<label for="node-input-device_read_schedule_value"><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.device_read_schedule"></span>Object Discover Frequency</label>
|
|
536
|
-
<p style="margin-right: 5px; margin-bottom: 0px; padding-left: 7px;">Every</p>
|
|
537
|
-
<input type="text" id="node-input-device_read_schedule" style="display: none;">
|
|
538
|
-
<input type="text" id="node-input-device_read_schedule_value" placeholder="5" style="width: 70px; margin-right: 5px;">
|
|
539
|
-
<select name="timePeriod" id="node-input-device_read_schedule_options" style="width: 120px; margin-right: 5px;">
|
|
540
|
-
<option value="Seconds">Seconds</option>
|
|
541
|
-
<option value="Minutes">Minutes</option>
|
|
542
|
-
<option value="Hours">Hours</option>
|
|
543
|
-
<option value="Days">Days</option>
|
|
544
|
-
</select>
|
|
545
|
-
</div>
|
|
546
|
-
|
|
547
|
-
<div class="form-row">
|
|
548
|
-
<label for="node-input-toLog">
|
|
549
|
-
Log found device:
|
|
550
|
-
</label>
|
|
551
|
-
<input type="checkbox" id="node-input-toLogIam" style="width: auto;"/>
|
|
552
|
-
</div>
|
|
553
|
-
|
|
554
|
-
<div class="form-row">
|
|
555
|
-
<label for="node-input-logErrorToConsole">
|
|
556
|
-
Log BACnet errors to console:
|
|
557
|
-
</label>
|
|
558
|
-
<input type="checkbox" id="node-input-logErrorToConsole" style="width: auto;"/>
|
|
559
|
-
</div>
|
|
560
|
-
|
|
561
|
-
<div class="form-row" id="importDeviceList">
|
|
562
|
-
<label>
|
|
563
|
-
Device List:
|
|
564
|
-
</label>
|
|
565
|
-
<label for="file-upload" class="custom-file-upload">
|
|
566
|
-
<i class="fa fa-cloud-upload" id="fileLabel"></i> <a id="fileLabelText" style="padding-left: 10px;">Import</a>
|
|
567
|
-
</label>
|
|
568
|
-
<input type="file" id="file-upload" accept="application/JSON" class="inputStyle" style="width: 258px;">
|
|
569
|
-
<label for="file-export" class="custom-file-upload" style="margin-left: 30px;">
|
|
570
|
-
<i class="fa fa-cloud-download" id="fileLabel"></i> <a id="fileLabelText" style="padding-left: 10px;">Export</a>
|
|
571
|
-
</label>
|
|
572
|
-
<input id="file-export" class="inputStyle" style="width: 258px; display: none;">
|
|
573
|
-
<a id="exportJSON" style="display: none"></a>
|
|
574
|
-
</div>
|
|
575
|
-
</div>
|
|
576
|
-
|
|
577
|
-
<div id='read-server-tab' style='display:none'>
|
|
578
|
-
<div class="read_server_parent_div">
|
|
579
|
-
<div class="form-row">
|
|
580
|
-
<label for="node-input-serverEnabled">
|
|
581
|
-
Enabled:
|
|
582
|
-
</label>
|
|
583
|
-
<input type="checkbox" id="node-input-serverEnabled" style="width: auto;"/>
|
|
584
|
-
</div>
|
|
585
|
-
|
|
586
|
-
<div class="form-row" id="clearServerContainer" style="align-items: center; display: flex;">
|
|
587
|
-
<label for="node-input-bacnetServerRebuildSchedule_value" class="clear_server_span"><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.bacnetServerRebuildSchedule"></span> </label>
|
|
588
|
-
<p-button class="bacnetServerRebuildSchedule_clearButton p-button-danger" @click="confirm($event)" label="Reinitialize Server" ></p-button>
|
|
589
|
-
</div>
|
|
590
|
-
</div>
|
|
591
|
-
</div>
|
|
592
|
-
|
|
593
|
-
</script>
|
|
594
|
-
<script type="text/html" data-help-name="Bacnet-Gateway">
|
|
595
|
-
<p> This node is the brain of the Bitpool BACnet node collection. It acts are the gateway for all BACnet communications and functionality for the collection. </p>
|
|
596
|
-
|
|
597
|
-
<h3><strong>Gateway Tab</strong></h3>
|
|
598
|
-
<ul class="node-ports">
|
|
599
|
-
<li>Network Interface - the desired interface for the bacstack client to bind to. This interface must not have any other BACnet clients bound to it.</li>
|
|
600
|
-
<li>Broadcast Address - the desired subnet for global msgs to be broadcast and recieved on. This should be as strict as possible. Use 255.255.255.255 if unsure.</li>
|
|
601
|
-
<li>Local Device Port - the port to be used for BACnet comms. Default is 47808</li>
|
|
602
|
-
|
|
603
|
-
</ul>
|
|
604
|
-
|
|
605
|
-
<h3><strong>Discovery Tab</strong></h3>
|
|
606
|
-
<ul class="node-ports">
|
|
607
|
-
<li>APDU Timeout - BACnet msg timeout option</li>
|
|
608
|
-
<li>Max APDU Size - BACnet max apdu size</li>
|
|
609
|
-
<li>Max Segments - BACnet max segments</li>
|
|
610
|
-
<li>Global Discover Frequency - the frequency at which the gateway issues global WhoIs BACnet commands. This should be limited to the least amount possible, as over-loading a network can be a serious issue with BACnet commmunications.</li>
|
|
611
|
-
<li>Manual Point Discovery Instance Range - if a BACnet device doesnt have a Object list (BACnet objectType:propertyId - 8:76), the this bacnet client will enter into manual discovery mode, where it iterates through types and instnace ranges. This range can be used to limit this manual scanning </li>
|
|
612
|
-
<li>Log Device Found - toggles logging of found devices to the node-red debug tab.</li>
|
|
613
|
-
<li>Log BACnet Errors to Console - toggles logging of BACnet related errors to the node-red console</li>
|
|
614
|
-
</ul>
|
|
615
|
-
|
|
616
|
-
<h3><strong>Server Tab</strong></h3>
|
|
617
|
-
<p>This section provides the ability to simulate a BACnet device and BACnet points using node-red. </p>
|
|
618
|
-
<p>Injecting a msg.topic and msg.payload into the gateway node will create a virtual point that can be discovered by other devices via BACnet/IP</p>
|
|
619
|
-
<p>This node only supports 2 BACnet object types, Analog Value - to show numeric data, and a Character String - to show string data.</p>
|
|
620
|
-
<ul class="node-ports">
|
|
621
|
-
<li>Enabled - toggles whether or not the local BACnet server is started or not. </li>
|
|
622
|
-
<li>Clear Server Points - a schedule for the locally generated BACnet points to get cleared from the node object store</li>
|
|
623
|
-
</ul>
|
|
624
|
-
|
|
625
|
-
<h3><strong>Examples</strong></h3>
|
|
626
|
-
<p>For example flows, please use the examples section for this node. These examples can be found at: Node-red hamburger menu on top right -> Import -> Examples -> @bitpoolos/edge-bacnet</p>
|
|
627
|
-
<p>To find captured examples of settings and flows, please go to our wiki <a href="https://wiki.bitpool.com/en/edge/apps/bitpool-edge/nr-bacnet">here</a></p>
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
<h3>Resources:</h3>
|
|
631
|
-
|
|
632
|
-
<p><a href="https://youtu.be/4K7mVxfvfbc">Video Walk-through </a></p>
|
|
633
|
-
|
|
634
|
-
<h4><strong>Online Docs:</strong></h4>
|
|
635
|
-
<ul type="1">
|
|
636
|
-
<li><a href="https://www.bitpool.com/">bitpool.com</a> - check us out here.</li>
|
|
637
|
-
<li><a href="https://app.bitpool.com/">app.bitpool.com</a> - set up your account.</li>
|
|
638
|
-
<li><a href="https://wiki.bitpool.com/">wiki.bitpool.com</a> - find more documentation.</li>
|
|
639
|
-
<li><a href="https://bacnet.org/">BACnet</a> - find more about the protocol.</li>
|
|
640
|
-
</ul>
|
|
641
|
-
</script>
|
|
778
|
+
<div class="form-row" id="clearServerPointContainer">
|
|
779
|
+
<label>Server Objects</label>
|
|
780
|
+
<div v-for="object in serverObjects" :key="object.name">
|
|
781
|
+
<div><p class="point_name">[{{ object.type }}{{ object.instance }}] {{ formatObjectName(object.name) }}</p></div>
|
|
782
|
+
<p-button class="bacnetServerPoint_clearButton p-button-danger" @click="confirm({
|
|
783
|
+
type: object.type,
|
|
784
|
+
instance: object.instance,
|
|
785
|
+
name: object.name
|
|
786
|
+
})" label="Remove Object"></p-button>
|
|
787
|
+
</div>
|
|
788
|
+
</div>
|
|
789
|
+
</div>
|
|
790
|
+
</div>
|
|
791
|
+
</div>
|
|
792
|
+
</script>
|
|
793
|
+
<script type="text/html" data-help-name="Bacnet-Gateway">
|
|
794
|
+
<p>
|
|
795
|
+
This node is the brain of the Bitpool BACnet node collection. It acts are the gateway for all BACnet communications and
|
|
796
|
+
functionality for the collection.
|
|
797
|
+
</p>
|
|
798
|
+
|
|
799
|
+
<h3><strong>Gateway Tab</strong></h3>
|
|
800
|
+
<ul class="node-ports">
|
|
801
|
+
<li>
|
|
802
|
+
Network Interface - the desired interface for the bacstack client to bind to. This interface must not have any other
|
|
803
|
+
BACnet clients bound to it.
|
|
804
|
+
</li>
|
|
805
|
+
<li>
|
|
806
|
+
Broadcast Address - the desired subnet for global msgs to be broadcast and recieved on. This should be as strict as
|
|
807
|
+
possible. Use 255.255.255.255 if unsure.
|
|
808
|
+
</li>
|
|
809
|
+
<li>Local Device Port - the port to be used for BACnet comms. Default is 47808</li>
|
|
810
|
+
</ul>
|
|
811
|
+
|
|
812
|
+
<h3><strong>Discovery Tab</strong></h3>
|
|
813
|
+
<ul class="node-ports">
|
|
814
|
+
<li>APDU Timeout - BACnet msg timeout option</li>
|
|
815
|
+
<li>Max APDU Size - BACnet max apdu size</li>
|
|
816
|
+
<li>Max Segments - BACnet max segments</li>
|
|
817
|
+
<li>
|
|
818
|
+
Global Discover Frequency - the frequency at which the gateway issues global WhoIs BACnet commands. This should be
|
|
819
|
+
limited to the least amount possible, as over-loading a network can be a serious issue with BACnet commmunications.
|
|
820
|
+
</li>
|
|
821
|
+
<li>
|
|
822
|
+
Manual Point Discovery Instance Range - if a BACnet device doesnt have a Object list (BACnet objectType:propertyId -
|
|
823
|
+
8:76), the this bacnet client will enter into manual discovery mode, where it iterates through types and instnace
|
|
824
|
+
ranges. This range can be used to limit this manual scanning
|
|
825
|
+
</li>
|
|
826
|
+
<li>Log Device Found - toggles logging of found devices to the node-red debug tab.</li>
|
|
827
|
+
<li>Log BACnet Errors to Console - toggles logging of BACnet related errors to the node-red console</li>
|
|
828
|
+
</ul>
|
|
829
|
+
|
|
830
|
+
<h3><strong>Server Tab</strong></h3>
|
|
831
|
+
<p>This section provides the ability to simulate a BACnet device and BACnet points using node-red.</p>
|
|
832
|
+
<p>
|
|
833
|
+
Injecting a msg.topic and msg.payload into the gateway node will create a virtual point that can be discovered by other
|
|
834
|
+
devices via BACnet/IP
|
|
835
|
+
</p>
|
|
836
|
+
<p>
|
|
837
|
+
This node only supports 2 BACnet object types, Analog Value - to show numeric data, and a Character String - to show
|
|
838
|
+
string data.
|
|
839
|
+
</p>
|
|
840
|
+
<ul class="node-ports">
|
|
841
|
+
<li>Enabled - toggles whether or not the local BACnet server is started or not.</li>
|
|
842
|
+
<li>
|
|
843
|
+
Clear Server Points - a schedule for the locally generated BACnet points to get cleared from the node object store
|
|
844
|
+
</li>
|
|
845
|
+
</ul>
|
|
846
|
+
|
|
847
|
+
<h3><strong>Examples</strong></h3>
|
|
848
|
+
<p>
|
|
849
|
+
For example flows, please use the examples section for this node. These examples can be found at: Node-red hamburger menu
|
|
850
|
+
on top right -> Import -> Examples -> @bitpoolos/edge-bacnet
|
|
851
|
+
</p>
|
|
852
|
+
<p>
|
|
853
|
+
To find captured examples of settings and flows, please go to our wiki
|
|
854
|
+
<a href="https://wiki.bitpool.com/en/edge/apps/bitpool-edge/nr-bacnet">here</a>
|
|
855
|
+
</p>
|
|
856
|
+
|
|
857
|
+
<h3>Resources:</h3>
|
|
858
|
+
|
|
859
|
+
<p><a href="https://youtu.be/4K7mVxfvfbc">Video Walk-through </a></p>
|
|
860
|
+
|
|
861
|
+
<h4><strong>Online Docs:</strong></h4>
|
|
862
|
+
<ul type="1">
|
|
863
|
+
<li><a href="https://www.bitpool.com/">bitpool.com</a> - check us out here.</li>
|
|
864
|
+
<li><a href="https://app.bitpool.com/">app.bitpool.com</a> - set up your account.</li>
|
|
865
|
+
<li><a href="https://wiki.bitpool.com/">wiki.bitpool.com</a> - find more documentation.</li>
|
|
866
|
+
<li><a href="https://bacnet.org/">BACnet</a> - find more about the protocol.</li>
|
|
867
|
+
</ul>
|
|
868
|
+
</script>
|