@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/common.js
CHANGED
|
@@ -2,80 +2,90 @@
|
|
|
2
2
|
MIT License Copyright 2021, 2022 - Bitpool Pty Ltd
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const { createLogger, format, transports } = require(
|
|
6
|
-
const { randomUUID } = require(
|
|
7
|
-
const os = require(
|
|
5
|
+
const { createLogger, format, transports } = require("winston");
|
|
6
|
+
const { randomUUID } = require("crypto");
|
|
7
|
+
const os = require("os");
|
|
8
8
|
const { exec } = require("child_process");
|
|
9
|
-
const baEnum = require(
|
|
10
|
-
const fs = require(
|
|
9
|
+
const baEnum = require("./resources/node-bacstack-ts/dist/index.js").enum;
|
|
10
|
+
const fs = require("fs");
|
|
11
11
|
|
|
12
12
|
const logger = createLogger({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
format: format.combine(
|
|
14
|
+
format.timestamp({
|
|
15
|
+
format: "YYYY-MM-DD HH:mm:ss",
|
|
16
|
+
}),
|
|
17
|
+
format.json()
|
|
18
|
+
),
|
|
19
|
+
transports: [new transports.Console()],
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
class DeviceObjectId {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
constructor(type, instance) {
|
|
24
|
+
this.type = type;
|
|
25
|
+
this.instance = instance;
|
|
26
|
+
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
class DeviceObject {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
constructor(objectId, name, description, type, units, presentValue) {
|
|
31
|
+
this.objectId = objectId;
|
|
32
|
+
this.name = name;
|
|
33
|
+
this.description = description;
|
|
34
|
+
this.type = type;
|
|
35
|
+
this.units = units;
|
|
36
|
+
this.presentValue = presentValue;
|
|
37
|
+
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
class BacnetConfig {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
41
|
+
constructor(
|
|
42
|
+
device,
|
|
43
|
+
objects,
|
|
44
|
+
bacnet_polling_schedule,
|
|
45
|
+
apduTimeout,
|
|
46
|
+
localIpAdrress,
|
|
47
|
+
roundDecimal,
|
|
48
|
+
local_device_port,
|
|
49
|
+
apduSize,
|
|
50
|
+
maxSegments,
|
|
51
|
+
broadCastAddr
|
|
52
|
+
) {
|
|
53
|
+
this.device = {
|
|
54
|
+
deviceId: device.deviceId,
|
|
55
|
+
address: device.address,
|
|
56
|
+
};
|
|
57
|
+
this.polling = {
|
|
58
|
+
schedule: bacnet_polling_schedule,
|
|
59
|
+
};
|
|
60
|
+
this.objects = [
|
|
61
|
+
{
|
|
62
|
+
objectId: {
|
|
63
|
+
type: objects.object_type,
|
|
64
|
+
instance: objects.instance,
|
|
65
|
+
properties: objects.object_props,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
this.apduTimeout = apduTimeout;
|
|
70
|
+
this.localIpAdrress = localIpAdrress;
|
|
71
|
+
this.roundDecimal = roundDecimal;
|
|
72
|
+
this.port = local_device_port;
|
|
73
|
+
this.apduSize = apduSize;
|
|
74
|
+
this.maxSegments = maxSegments;
|
|
75
|
+
this.broadCastAddr = broadCastAddr;
|
|
63
76
|
}
|
|
64
|
-
}
|
|
77
|
+
}
|
|
65
78
|
|
|
66
79
|
class BacnetClientConfig {
|
|
67
80
|
constructor(
|
|
68
|
-
apduTimeout,
|
|
69
|
-
localIpAdrress,
|
|
70
|
-
local_device_port,
|
|
71
|
-
apduSize,
|
|
72
|
-
maxSegments,
|
|
73
|
-
broadCastAddr,
|
|
81
|
+
apduTimeout,
|
|
82
|
+
localIpAdrress,
|
|
83
|
+
local_device_port,
|
|
84
|
+
apduSize,
|
|
85
|
+
maxSegments,
|
|
86
|
+
broadCastAddr,
|
|
74
87
|
discover_polling_schedule,
|
|
75
|
-
|
|
76
|
-
device_id_range_start,
|
|
77
|
-
device_id_range_end,
|
|
78
|
-
toRestartNodeRed,
|
|
88
|
+
toRestartNodeRed,
|
|
79
89
|
deviceId,
|
|
80
90
|
manual_instance_range_enabled,
|
|
81
91
|
manual_instance_range_start,
|
|
@@ -90,9 +100,6 @@ class BacnetClientConfig {
|
|
|
90
100
|
this.maxSegments = maxSegments;
|
|
91
101
|
this.broadCastAddr = broadCastAddr;
|
|
92
102
|
this.discover_polling_schedule = discover_polling_schedule;
|
|
93
|
-
this.device_id_range_enabled = device_id_range_enabled;
|
|
94
|
-
this.device_id_range_start = device_id_range_start;
|
|
95
|
-
this.device_id_range_end = device_id_range_end;
|
|
96
103
|
this.toRestartNodeRed = toRestartNodeRed;
|
|
97
104
|
this.deviceId = deviceId;
|
|
98
105
|
this.manual_instance_range_enabled = manual_instance_range_enabled;
|
|
@@ -101,7 +108,7 @@ class BacnetClientConfig {
|
|
|
101
108
|
this.device_read_schedule = device_read_schedule;
|
|
102
109
|
this.retries = retries;
|
|
103
110
|
}
|
|
104
|
-
}
|
|
111
|
+
}
|
|
105
112
|
|
|
106
113
|
class ReadCommandConfig {
|
|
107
114
|
constructor(pointsToRead, objectProperties, decimalPrecision) {
|
|
@@ -109,33 +116,35 @@ class ReadCommandConfig {
|
|
|
109
116
|
this.objectProperties = objectProperties;
|
|
110
117
|
this.precision = decimalPrecision;
|
|
111
118
|
}
|
|
112
|
-
}
|
|
119
|
+
}
|
|
113
120
|
|
|
114
121
|
class WriteCommandConfig {
|
|
115
122
|
constructor(device, objects) {
|
|
116
123
|
this.device = {
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
deviceId: device.deviceId,
|
|
125
|
+
address: device.address,
|
|
119
126
|
};
|
|
120
|
-
this.objects = [
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
this.objects = [
|
|
128
|
+
{
|
|
129
|
+
objectId: {
|
|
130
|
+
type: objects.object_type,
|
|
131
|
+
instance: objects.instance,
|
|
132
|
+
properties: objects.object_props,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
];
|
|
127
136
|
}
|
|
128
|
-
}
|
|
137
|
+
}
|
|
129
138
|
|
|
130
|
-
const getUnit = function(id) {
|
|
139
|
+
const getUnit = function (id) {
|
|
131
140
|
for (var key in baEnum.EngineeringUnits) {
|
|
132
|
-
if(baEnum.EngineeringUnits[key] == id) {
|
|
141
|
+
if (baEnum.EngineeringUnits[key] == id) {
|
|
133
142
|
if (baEnum.EngineeringUnits.hasOwnProperty(key)) {
|
|
134
143
|
let unitsArr = key.split("_");
|
|
135
144
|
let unit;
|
|
136
|
-
unitsArr.forEach((ele, index) =>{
|
|
137
|
-
if(index == 0){
|
|
138
|
-
|
|
145
|
+
unitsArr.forEach((ele, index) => {
|
|
146
|
+
if (index == 0) {
|
|
147
|
+
unit = ele.toLowerCase();
|
|
139
148
|
} else {
|
|
140
149
|
unit += "-" + ele.toLowerCase();
|
|
141
150
|
}
|
|
@@ -147,61 +156,61 @@ const getUnit = function(id) {
|
|
|
147
156
|
return "no-units";
|
|
148
157
|
};
|
|
149
158
|
|
|
150
|
-
const generateId = function() {
|
|
159
|
+
const generateId = function () {
|
|
151
160
|
return randomUUID();
|
|
152
161
|
};
|
|
153
162
|
|
|
154
|
-
const getIpAddress = function() {
|
|
155
|
-
return new Promise(function(resolve, reject) {
|
|
163
|
+
const getIpAddress = function () {
|
|
164
|
+
return new Promise(function (resolve, reject) {
|
|
156
165
|
const nets = os.networkInterfaces();
|
|
157
166
|
const results = Object.create(null); // Or just '{}', an empty object
|
|
158
167
|
for (const name of Object.keys(nets)) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
+
for (const net of nets[name]) {
|
|
169
|
+
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
|
170
|
+
let family = parseInt(net.family.toString().match(/[0-9]/));
|
|
171
|
+
if (family === 4 && !net.internal) {
|
|
172
|
+
if (!results[name]) {
|
|
173
|
+
results[name] = [];
|
|
174
|
+
}
|
|
175
|
+
results[name].push(net.address);
|
|
168
176
|
}
|
|
177
|
+
}
|
|
169
178
|
}
|
|
170
179
|
|
|
171
|
-
if(os.version().includes(
|
|
172
|
-
let allInterfaceName =
|
|
180
|
+
if (os.version().includes("Ubuntu")) {
|
|
181
|
+
let allInterfaceName = "All interfaces";
|
|
173
182
|
if (!results[allInterfaceName]) {
|
|
174
183
|
results[allInterfaceName] = [];
|
|
175
184
|
}
|
|
176
|
-
results[allInterfaceName].push(
|
|
177
|
-
} else if(os.version().includes(
|
|
185
|
+
results[allInterfaceName].push("0.0.0.0");
|
|
186
|
+
} else if (os.version().includes("Windows")) {
|
|
178
187
|
//do nothing
|
|
179
188
|
}
|
|
180
|
-
|
|
189
|
+
|
|
181
190
|
resolve(results);
|
|
182
191
|
});
|
|
183
|
-
}
|
|
192
|
+
};
|
|
184
193
|
|
|
185
|
-
const roundDecimalPlaces = function(value, decimals) {
|
|
186
|
-
if(decimals) return Number(Math.round(value+
|
|
194
|
+
const roundDecimalPlaces = function (value, decimals) {
|
|
195
|
+
if (decimals) return Number(Math.round(value + "e" + decimals) + "e-" + decimals);
|
|
187
196
|
return value;
|
|
188
|
-
}
|
|
197
|
+
};
|
|
189
198
|
|
|
190
|
-
const doNodeRedRestart = function() {
|
|
191
|
-
return new Promise(function(resolve, reject) {
|
|
199
|
+
const doNodeRedRestart = function () {
|
|
200
|
+
return new Promise(function (resolve, reject) {
|
|
192
201
|
try {
|
|
193
202
|
exec("restart", (error, stdout, stderr) => {
|
|
194
203
|
if (error) {
|
|
195
|
-
|
|
196
|
-
|
|
204
|
+
console.log(`Node-Red restart error: ${error.message}`);
|
|
205
|
+
reject(error.message);
|
|
197
206
|
}
|
|
198
207
|
if (stderr) {
|
|
199
|
-
|
|
200
|
-
|
|
208
|
+
console.log(`Node-Red restart stderr: ${stderr}`);
|
|
209
|
+
reject(stderr);
|
|
201
210
|
}
|
|
202
211
|
resolve(stdout);
|
|
203
212
|
});
|
|
204
|
-
} catch(e){
|
|
213
|
+
} catch (e) {
|
|
205
214
|
console.log(`Node-Red restart error: ${e}`);
|
|
206
215
|
reject(e);
|
|
207
216
|
}
|
|
@@ -218,10 +227,10 @@ async function Store_Config(data) {
|
|
|
218
227
|
console.log("Store_Config writeFile error: ", err);
|
|
219
228
|
}
|
|
220
229
|
});
|
|
221
|
-
} catch(e){
|
|
230
|
+
} catch (e) {
|
|
222
231
|
//do nothing
|
|
223
232
|
}
|
|
224
|
-
}
|
|
233
|
+
}
|
|
225
234
|
|
|
226
235
|
// READ CONFIG SYNC FUNCTION ======================================================
|
|
227
236
|
//
|
|
@@ -229,15 +238,14 @@ async function Store_Config(data) {
|
|
|
229
238
|
function Read_Config_Sync() {
|
|
230
239
|
var data = "{}";
|
|
231
240
|
try {
|
|
232
|
-
data = fs.readFileSync("edge-bacnet-datastore.cfg", { encoding:
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
data = '{}';
|
|
241
|
+
data = fs.readFileSync("edge-bacnet-datastore.cfg", { encoding: "utf8", flag: "r" });
|
|
242
|
+
} catch (err) {
|
|
243
|
+
//console.log("Read_Config_Sync error:", err);
|
|
244
|
+
data = "{}";
|
|
237
245
|
Store_Config(data);
|
|
238
246
|
}
|
|
239
247
|
return data;
|
|
240
|
-
}
|
|
248
|
+
}
|
|
241
249
|
|
|
242
250
|
// STORE CONFIG FUNCTION - BACNET SERVER ==========================================
|
|
243
251
|
//
|
|
@@ -246,13 +254,11 @@ async function Store_Config_Server(data) {
|
|
|
246
254
|
try {
|
|
247
255
|
await fs.writeFile("edge-bacnet-server-datastore.cfg", data, (err) => {
|
|
248
256
|
if (err) {
|
|
249
|
-
console.log("Store_Config_Server writeFile error: ", err);
|
|
257
|
+
//console.log("Store_Config_Server writeFile error: ", err);
|
|
250
258
|
}
|
|
251
259
|
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
};
|
|
260
|
+
} catch (err) {}
|
|
261
|
+
}
|
|
256
262
|
|
|
257
263
|
// READ CONFIG SYNC FUNCTION - BACNET SERVER ======================================
|
|
258
264
|
//
|
|
@@ -260,29 +266,28 @@ async function Store_Config_Server(data) {
|
|
|
260
266
|
function Read_Config_Sync_Server() {
|
|
261
267
|
var data = "{}";
|
|
262
268
|
try {
|
|
263
|
-
data = fs.readFileSync("edge-bacnet-server-datastore.cfg", {encoding:
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
console.log("
|
|
267
|
-
|
|
268
|
-
data = '{}';
|
|
269
|
+
data = fs.readFileSync("edge-bacnet-server-datastore.cfg", { encoding: "utf8", flag: "r" });
|
|
270
|
+
} catch (err) {
|
|
271
|
+
//console.log("Read_Config_Sync_Server error:", err);
|
|
272
|
+
if (err.errno == -4058) console.log("Edge-BACnet Server: No save file found, creating new file");
|
|
273
|
+
data = "{}";
|
|
269
274
|
Store_Config_Server(data);
|
|
270
275
|
}
|
|
271
276
|
return data;
|
|
272
|
-
}
|
|
277
|
+
}
|
|
273
278
|
|
|
274
279
|
module.exports = {
|
|
275
|
-
BacnetConfig,
|
|
276
|
-
BacnetClientConfig,
|
|
277
|
-
ReadCommandConfig,
|
|
280
|
+
BacnetConfig,
|
|
281
|
+
BacnetClientConfig,
|
|
282
|
+
ReadCommandConfig,
|
|
278
283
|
WriteCommandConfig,
|
|
279
|
-
getUnit,
|
|
280
|
-
generateId,
|
|
281
|
-
getIpAddress,
|
|
282
|
-
roundDecimalPlaces,
|
|
284
|
+
getUnit,
|
|
285
|
+
generateId,
|
|
286
|
+
getIpAddress,
|
|
287
|
+
roundDecimalPlaces,
|
|
283
288
|
doNodeRedRestart,
|
|
284
289
|
Store_Config,
|
|
285
290
|
Read_Config_Sync,
|
|
286
291
|
Store_Config_Server,
|
|
287
|
-
Read_Config_Sync_Server
|
|
288
|
-
};
|
|
292
|
+
Read_Config_Sync_Server,
|
|
293
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitpoolos/edge-bacnet",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "A bacnet gateway for node-red",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@plus4nodered/ts-node-bacnet": "^1.0.0-beta.2",
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
"cronosjs": "^1.7.1",
|
|
9
9
|
"debug": "^4.1.1",
|
|
10
10
|
"iconv-lite": "^0.5.1",
|
|
11
|
-
"node-fetch": "^2.6.1",
|
|
12
11
|
"toad-scheduler": "^1.6.0",
|
|
13
12
|
"underscore": "^1.10.2",
|
|
14
13
|
"winston": "^3.2.1"
|