@bitpoolos/edge-bacnet 1.5.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +151 -1
- package/bacnet_client.js +202 -108
- package/bacnet_device.js +3 -1
- package/bacnet_gateway.html +100 -2
- package/bacnet_gateway.js +382 -250
- package/bacnet_inspector.html +43 -0
- package/bacnet_inspector.js +1564 -0
- package/bacnet_inspector_worker.js +535 -0
- package/bacnet_read.html +47 -50
- package/bacnet_read.js +0 -3
- package/common.js +201 -38
- package/inspector.html +460 -0
- package/package.json +6 -2
- package/resources/Logo_Simplified_Positive.svg +32 -0
- package/resources/downloadAsHtml.js +654 -0
- package/resources/icons/device-id-change-icon.svg +4 -0
- package/resources/icons/device-id-conflict-icon.svg +4 -0
- package/resources/icons/favicon.ico +0 -0
- package/resources/icons/points-error-icon.svg +4 -0
- package/resources/icons/points-missing-icon.svg +4 -0
- package/resources/icons/points-ok-icon.svg +4 -0
- package/resources/icons/points-unmapped-icon.svg +5 -0
- package/resources/icons/points-warning-icon.svg +4 -0
- package/resources/inspector.css +25312 -0
- package/resources/inspectorStyle.css +254 -0
- package/resources/inspectorStyles.css +478 -0
- package/resources/node-bacstack-ts/dist/lib/client.js +7 -3
- package/resources/primevue.min.js +1 -0
- package/resources/style.css +17 -1
- package/resources/vue3513.global.prod.js +9 -0
- package/ssrHtmlExporter.js +535 -0
- package/treeBuilder.js +3 -3
package/bacnet_read.html
CHANGED
|
@@ -545,6 +545,12 @@
|
|
|
545
545
|
menu.style.setProperty("--mouse-x", event.clientX + "px");
|
|
546
546
|
menu.style.setProperty("--mouse-y", event.clientY + "px");
|
|
547
547
|
menu.style.display = "block";
|
|
548
|
+
|
|
549
|
+
if (app.rightClickedDevice.node.showAdded == true) {
|
|
550
|
+
menu.classList.add("deviceAddedToRead");
|
|
551
|
+
} else if (app.rightClickedDevice.node.showAdded == false) {
|
|
552
|
+
menu.classList.remove("deviceAddedToRead");
|
|
553
|
+
}
|
|
548
554
|
},
|
|
549
555
|
onPointRightClick(slotProps, event) {
|
|
550
556
|
let app = this;
|
|
@@ -553,6 +559,12 @@
|
|
|
553
559
|
pointMenu.style.setProperty("--mouse-x", event.clientX + "px");
|
|
554
560
|
pointMenu.style.setProperty("--mouse-y", event.clientY + "px");
|
|
555
561
|
pointMenu.style.display = "block";
|
|
562
|
+
|
|
563
|
+
if (app.rightClickedPoint.node.showAdded == true) {
|
|
564
|
+
pointMenu.classList.add("pointAddedToRead");
|
|
565
|
+
} else if (app.rightClickedPoint.node.showAdded == false) {
|
|
566
|
+
pointMenu.classList.remove("pointAddedToRead");
|
|
567
|
+
}
|
|
556
568
|
},
|
|
557
569
|
handleContextMenuClick(type) {
|
|
558
570
|
let app = this;
|
|
@@ -593,55 +605,34 @@
|
|
|
593
605
|
},
|
|
594
606
|
purgeDevice(slotProps) {
|
|
595
607
|
let app = this;
|
|
596
|
-
let device = app.
|
|
597
|
-
if (ele.address.address) {
|
|
598
|
-
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
599
|
-
} else {
|
|
600
|
-
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
601
|
-
}
|
|
602
|
-
});
|
|
608
|
+
let device = app.getDeviceFromDeviceList(slotProps.node.ipAddr, slotProps.node.deviceId);
|
|
603
609
|
if (device) {
|
|
604
|
-
app.nodeService.purgeDevice(device).then(function (result) {
|
|
610
|
+
app.nodeService.purgeDevice(device).then(function (result) {});
|
|
605
611
|
}
|
|
606
612
|
},
|
|
607
613
|
updatePointsForDevice(slotProps) {
|
|
608
614
|
let app = this;
|
|
609
|
-
let device = app.
|
|
610
|
-
if (ele.address.address) {
|
|
611
|
-
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
612
|
-
} else {
|
|
613
|
-
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
614
|
-
}
|
|
615
|
-
});
|
|
615
|
+
let device = app.getDeviceFromDeviceList(slotProps.node.ipAddr, slotProps.node.deviceId);
|
|
616
616
|
if (device) {
|
|
617
|
-
app.nodeService.updatePointsForDevice(device).then(function (result) {
|
|
617
|
+
app.nodeService.updatePointsForDevice(device).then(function (result) {});
|
|
618
618
|
}
|
|
619
619
|
},
|
|
620
620
|
updatePoint(slotProps) {
|
|
621
621
|
let app = this;
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
} else {
|
|
626
|
-
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
627
|
-
}
|
|
622
|
+
const pointKey = `${slotProps.node.bacnetType}:${slotProps.node.bacnetInstance}`;
|
|
623
|
+
const device = app.deviceList.find((ele) => {
|
|
624
|
+
return ele.displayName == slotProps.node.parentDevice;
|
|
628
625
|
});
|
|
626
|
+
const deviceKey = `${app.getDeviceAddress(device.address)}-${device.deviceId}`;
|
|
629
627
|
if (device) {
|
|
630
|
-
app.nodeService.updatePoint(
|
|
628
|
+
app.nodeService.updatePoint(deviceKey, pointKey).then(function (result) {});
|
|
631
629
|
}
|
|
632
630
|
},
|
|
633
631
|
setDeviceName() {
|
|
634
632
|
let app = this;
|
|
635
633
|
const slotProps = app.rightClickedDevice;
|
|
636
634
|
const displayName = app.deviceDisplayNameValue;
|
|
637
|
-
let device = app.
|
|
638
|
-
if (ele.address.address) {
|
|
639
|
-
return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
640
|
-
} else {
|
|
641
|
-
return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
|
-
|
|
635
|
+
let device = app.getDeviceFromDeviceList(slotProps.node.ipAddr, slotProps.node.deviceId);
|
|
645
636
|
if (device) {
|
|
646
637
|
app.nodeService.setDeviceDisplayName(device, displayName).then(function (result) {
|
|
647
638
|
if (result) {
|
|
@@ -660,7 +651,7 @@
|
|
|
660
651
|
const pointName = slotProps.node.pointName;
|
|
661
652
|
|
|
662
653
|
let device = app.deviceList.find((ele) => {
|
|
663
|
-
return ele.
|
|
654
|
+
return ele.displayName == slotProps.node.parentDevice;
|
|
664
655
|
});
|
|
665
656
|
|
|
666
657
|
if (device) {
|
|
@@ -676,6 +667,18 @@
|
|
|
676
667
|
|
|
677
668
|
app.showPointNameDialog = false;
|
|
678
669
|
},
|
|
670
|
+
getDeviceFromDeviceList(ip, id) {
|
|
671
|
+
let app = this;
|
|
672
|
+
let device = app.deviceList.find((ele) => {
|
|
673
|
+
if (ele.address.address) {
|
|
674
|
+
return ele.address.address == ip && ele.deviceId == id;
|
|
675
|
+
} else {
|
|
676
|
+
return ele.address == ip && ele.deviceId == id;
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
return device;
|
|
681
|
+
},
|
|
679
682
|
settingDeviceName(slotProps) {
|
|
680
683
|
let app = this;
|
|
681
684
|
if (slotProps.node.settingDisplayName) {
|
|
@@ -709,11 +712,9 @@
|
|
|
709
712
|
(ele) => ele.ipAddr == key.split("-")[0] && ele.deviceId == key.split("-")[1]
|
|
710
713
|
);
|
|
711
714
|
let deviceName;
|
|
712
|
-
|
|
713
715
|
if (readDevice) {
|
|
714
716
|
deviceName = readDevice.label;
|
|
715
717
|
}
|
|
716
|
-
|
|
717
718
|
exportJson[key] = {};
|
|
718
719
|
if (deviceName) {
|
|
719
720
|
exportJson[key]["deviceName"] = deviceName;
|
|
@@ -725,9 +726,8 @@
|
|
|
725
726
|
let pointName = devicePoints[pointIndex];
|
|
726
727
|
let pointObject = device[pointName];
|
|
727
728
|
|
|
728
|
-
//formatting json payload
|
|
729
|
-
|
|
730
|
-
if (pointObject) {
|
|
729
|
+
//formatting json payload
|
|
730
|
+
if (pointObject && pointName !== "deviceName") {
|
|
731
731
|
exportJson[key][pointName] = {
|
|
732
732
|
meta: pointObject.meta,
|
|
733
733
|
objectName: pointObject.objectName,
|
|
@@ -765,6 +765,7 @@
|
|
|
765
765
|
let ip = key.split("-")[0];
|
|
766
766
|
let id = key.split("-")[1];
|
|
767
767
|
const importedDevice = pointsToRead[key];
|
|
768
|
+
//match only by IP, to handle case of mstp device
|
|
768
769
|
let foundIndex = app.devices.findIndex((ele) => ele.ipAddr == ip);
|
|
769
770
|
|
|
770
771
|
if (foundIndex !== -1) {
|
|
@@ -772,17 +773,11 @@
|
|
|
772
773
|
if (app.devices[foundIndex].deviceId == id) {
|
|
773
774
|
//found device
|
|
774
775
|
let treeDevice = app.devices[foundIndex];
|
|
775
|
-
let device = app.
|
|
776
|
-
if (ele.address.address) {
|
|
777
|
-
return ele.address.address == ip && ele.deviceId == id;
|
|
778
|
-
} else {
|
|
779
|
-
return ele.address == ip && ele.deviceId == id;
|
|
780
|
-
}
|
|
781
|
-
});
|
|
776
|
+
let device = app.getDeviceFromDeviceList(ip, id);
|
|
782
777
|
|
|
783
778
|
for (let pointName in importedDevice) {
|
|
784
779
|
let point = importedDevice[pointName];
|
|
785
|
-
if (pointName == "deviceName") {
|
|
780
|
+
if (pointName == "deviceName" && typeof point == "string") {
|
|
786
781
|
app.nodeService.setDeviceDisplayName(device, point);
|
|
787
782
|
treeDevice.label = point;
|
|
788
783
|
} else {
|
|
@@ -849,7 +844,7 @@
|
|
|
849
844
|
|
|
850
845
|
for (let pointName in importedDevice) {
|
|
851
846
|
let point = importedDevice[pointName];
|
|
852
|
-
if (pointName == "deviceName") {
|
|
847
|
+
if (pointName == "deviceName" && typeof point == "string") {
|
|
853
848
|
app.nodeService.setDeviceDisplayName(device, point);
|
|
854
849
|
mstpDevice.label = point;
|
|
855
850
|
} else {
|
|
@@ -878,7 +873,9 @@
|
|
|
878
873
|
} else {
|
|
879
874
|
// read device found, add point to existing
|
|
880
875
|
let pointIndex = app.readDevices[isDeviceInReadList].children[0].children.findIndex(
|
|
881
|
-
(ele) =>
|
|
876
|
+
(ele) =>
|
|
877
|
+
parseInt(ele.bacnetInstance) == parseInt(point.meta.objectId.instance) &&
|
|
878
|
+
parseInt(ele.bacnetType) == parseInt(point.meta.objectId.type)
|
|
882
879
|
);
|
|
883
880
|
if (pointIndex == -1) {
|
|
884
881
|
app.readDevices[isDeviceInReadList].children[0].children.push(pointInTree);
|
|
@@ -1088,7 +1085,7 @@
|
|
|
1088
1085
|
</a>
|
|
1089
1086
|
</li>
|
|
1090
1087
|
<li class="red-ui-menu-divider"></li>
|
|
1091
|
-
<li class="context-menu-item" @click="handleContextMenuClick('setDeviceName')">
|
|
1088
|
+
<li class="context-menu-item set-device-name-option" @click="handleContextMenuClick('setDeviceName')">
|
|
1092
1089
|
<a class="red-ui-menu-label">
|
|
1093
1090
|
<i class="pi pi-pencil context-menu-icon"></i>
|
|
1094
1091
|
<span class="context-menu-item-text">Set Device Name</span>
|
|
@@ -1100,7 +1097,7 @@
|
|
|
1100
1097
|
<!-- Start Point Context Menu -->
|
|
1101
1098
|
<ul
|
|
1102
1099
|
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">
|
|
1103
|
-
<li class="context-menu-item" @click="handlePointContextMenuClick('setPointName')">
|
|
1100
|
+
<li class="context-menu-item set-point-name-option" @click="handlePointContextMenuClick('setPointName')">
|
|
1104
1101
|
<a class="red-ui-menu-label">
|
|
1105
1102
|
<i class="pi pi-pencil context-menu-icon"></i>
|
|
1106
1103
|
<span class="context-menu-item-text">Set Point Name</span>
|
|
@@ -1495,4 +1492,4 @@
|
|
|
1495
1492
|
<li><a href="https://wiki.bitpool.com/">wiki.bitpool.com</a> - find more documentation.</li>
|
|
1496
1493
|
<li><a href="https://bacnet.org/">BACnet</a> - find more about the protocol.</li>
|
|
1497
1494
|
</ul>
|
|
1498
|
-
</script>
|
|
1495
|
+
</script>
|
package/bacnet_read.js
CHANGED
package/common.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
|
-
MIT License Copyright 2021,
|
|
2
|
+
MIT License Copyright 2021, 2025 - Bitpool Pty Ltd
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const { createLogger, format, transports } = require("winston");
|
|
6
5
|
const { randomUUID } = require("crypto");
|
|
7
6
|
const os = require("os");
|
|
8
|
-
const { exec } = require("child_process");
|
|
9
7
|
const baEnum = require("./resources/node-bacstack-ts/dist/index.js").enum;
|
|
10
8
|
const fs = require("fs");
|
|
9
|
+
const fs2 = require("fs").promises;
|
|
11
10
|
|
|
12
11
|
class BacnetConfig {
|
|
13
12
|
constructor(
|
|
@@ -102,8 +101,6 @@ class BacnetClientConfig {
|
|
|
102
101
|
}
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
|
|
106
|
-
|
|
107
104
|
class ReadCommandConfig {
|
|
108
105
|
constructor(pointsToRead, objectProperties, decimalPrecision) {
|
|
109
106
|
this.pointsToRead = pointsToRead;
|
|
@@ -190,30 +187,12 @@ const roundDecimalPlaces = function (value, decimals) {
|
|
|
190
187
|
return value;
|
|
191
188
|
};
|
|
192
189
|
|
|
193
|
-
const doNodeRedRestart = function () {
|
|
194
|
-
return new Promise(function (resolve, reject) {
|
|
195
|
-
try {
|
|
196
|
-
exec("restart", (error, stdout, stderr) => {
|
|
197
|
-
if (error) {
|
|
198
|
-
console.log(`Node-Red restart error: ${error.message}`);
|
|
199
|
-
reject(error.message);
|
|
200
|
-
}
|
|
201
|
-
if (stderr) {
|
|
202
|
-
console.log(`Node-Red restart stderr: ${stderr}`);
|
|
203
|
-
reject(stderr);
|
|
204
|
-
}
|
|
205
|
-
resolve(stdout);
|
|
206
|
-
});
|
|
207
|
-
} catch (e) {
|
|
208
|
-
console.log(`Node-Red restart error: ${e}`);
|
|
209
|
-
reject(e);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
};
|
|
213
|
-
|
|
214
190
|
// STORE CONFIG FUNCTION ==========================================================
|
|
215
191
|
//
|
|
216
192
|
// ================================================================================
|
|
193
|
+
|
|
194
|
+
/*
|
|
195
|
+
|
|
217
196
|
async function Store_Config(data) {
|
|
218
197
|
try {
|
|
219
198
|
await fs.writeFile("edge-bacnet-datastore.cfg", data, { encoding: "utf8", flag: "w" }, (err) => {
|
|
@@ -226,18 +205,185 @@ async function Store_Config(data) {
|
|
|
226
205
|
}
|
|
227
206
|
}
|
|
228
207
|
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
// refactor:
|
|
211
|
+
|
|
212
|
+
let storeQueue = [];
|
|
213
|
+
let isStoreProcessing = false;
|
|
214
|
+
|
|
215
|
+
async function queueConfigStore(data) {
|
|
216
|
+
storeQueue.push(data);
|
|
217
|
+
|
|
218
|
+
if (!isStoreProcessing) {
|
|
219
|
+
isStoreProcessing = true;
|
|
220
|
+
|
|
221
|
+
while (storeQueue.length > 0) {
|
|
222
|
+
const nextData = storeQueue.pop(); // Get most recent data
|
|
223
|
+
storeQueue.length = 0; // Clear any accumulated data
|
|
224
|
+
|
|
225
|
+
await Store_Config(nextData);
|
|
226
|
+
|
|
227
|
+
// Add small delay between attempts
|
|
228
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
isStoreProcessing = false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function Store_Config(data) {
|
|
236
|
+
const mainFile = "edge-bacnet-datastore.cfg";
|
|
237
|
+
const tempFile = "edge-bacnet-datastore.cfg.tmp";
|
|
238
|
+
const backupFile = "edge-bacnet-datastore.cfg.bak";
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
// First validate the JSON to ensure it's valid before writing
|
|
242
|
+
try {
|
|
243
|
+
JSON.parse(JSON.stringify(data));
|
|
244
|
+
} catch (jsonError) {
|
|
245
|
+
console.error("Invalid JSON data detected:", jsonError);
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Write to temporary file first
|
|
250
|
+
await fs2.writeFile(tempFile, JSON.stringify(data, null, 2), { encoding: "utf8" });
|
|
251
|
+
|
|
252
|
+
// Verify the temporary file is valid JSON
|
|
253
|
+
try {
|
|
254
|
+
const tempContent = await fs2.readFile(tempFile, "utf8");
|
|
255
|
+
JSON.parse(tempContent);
|
|
256
|
+
} catch (verifyError) {
|
|
257
|
+
console.error("Temporary file validation failed:", verifyError);
|
|
258
|
+
await fs2.unlink(tempFile).catch(() => {});
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Create backup of current file if it exists
|
|
263
|
+
try {
|
|
264
|
+
await fs2.access(mainFile);
|
|
265
|
+
await fs2.copyFile(mainFile, backupFile);
|
|
266
|
+
} catch (backupError) {
|
|
267
|
+
// If main file doesn't exist, no backup needed
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Atomic rename of temporary file to main file
|
|
271
|
+
await fs2.rename(tempFile, mainFile);
|
|
272
|
+
return true;
|
|
273
|
+
} catch (error) {
|
|
274
|
+
console.error("Store_Config error:", error);
|
|
275
|
+
|
|
276
|
+
// Cleanup temporary file if it exists
|
|
277
|
+
try {
|
|
278
|
+
await fs2.unlink(tempFile).catch(() => {});
|
|
279
|
+
} catch (cleanupError) {}
|
|
280
|
+
|
|
281
|
+
// If main file is corrupted and backup exists, restore from backup
|
|
282
|
+
try {
|
|
283
|
+
const backupExists = await fs2.access(backupFile).catch(() => false);
|
|
284
|
+
if (backupExists) {
|
|
285
|
+
await fs2.copyFile(backupFile, mainFile);
|
|
286
|
+
console.log("Restored from backup file");
|
|
287
|
+
}
|
|
288
|
+
} catch (restoreError) {
|
|
289
|
+
console.error("Failed to restore from backup:", restoreError);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
229
296
|
// READ CONFIG SYNC FUNCTION ======================================================
|
|
230
297
|
//
|
|
231
298
|
// ================================================================================
|
|
299
|
+
|
|
232
300
|
function Read_Config_Sync() {
|
|
233
|
-
|
|
301
|
+
const mainFile = "edge-bacnet-datastore.cfg";
|
|
302
|
+
const backupFile = "edge-bacnet-datastore.cfg.bak";
|
|
303
|
+
const defaultData = "{}";
|
|
304
|
+
|
|
234
305
|
try {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
306
|
+
// Try to read the main file
|
|
307
|
+
let data = fsSync.readFileSync(mainFile, { encoding: "utf8" });
|
|
308
|
+
|
|
309
|
+
// Validate JSON
|
|
310
|
+
try {
|
|
311
|
+
JSON.parse(data);
|
|
312
|
+
return data;
|
|
313
|
+
} catch (jsonError) {
|
|
314
|
+
console.error("Main file contains invalid JSON, attempting backup recovery");
|
|
315
|
+
|
|
316
|
+
// Try to read backup file
|
|
317
|
+
try {
|
|
318
|
+
const backupData = fsSync.readFileSync(backupFile, { encoding: "utf8" });
|
|
319
|
+
JSON.parse(backupData); // Validate backup JSON
|
|
320
|
+
|
|
321
|
+
// Restore from backup
|
|
322
|
+
fsSync.copyFileSync(backupFile, mainFile);
|
|
323
|
+
console.log("Successfully restored from backup file");
|
|
324
|
+
return backupData;
|
|
325
|
+
} catch (backupError) {
|
|
326
|
+
console.error("Backup recovery failed, creating new file");
|
|
327
|
+
fsSync.writeFileSync(mainFile, defaultData, { encoding: "utf8" });
|
|
328
|
+
return defaultData;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error("Error reading config:", error);
|
|
333
|
+
fsSync.writeFileSync(mainFile, defaultData, { encoding: "utf8" });
|
|
334
|
+
return defaultData;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// refactor:
|
|
339
|
+
|
|
340
|
+
async function Read_Config_Async() {
|
|
341
|
+
// todo rename function, not using sync
|
|
342
|
+
const mainFile = "edge-bacnet-datastore.cfg";
|
|
343
|
+
const backupFile = "edge-bacnet-datastore.cfg.bak";
|
|
344
|
+
const defaultData = "{}";
|
|
345
|
+
|
|
346
|
+
try {
|
|
347
|
+
// Try to read the main file
|
|
348
|
+
const data = await fs2.readFile(mainFile, { encoding: "utf8" });
|
|
349
|
+
|
|
350
|
+
// Validate JSON
|
|
351
|
+
try {
|
|
352
|
+
JSON.parse(data);
|
|
353
|
+
|
|
354
|
+
return data;
|
|
355
|
+
} catch (jsonError) {
|
|
356
|
+
console.error("Main file contains invalid JSON, attempting backup recovery");
|
|
357
|
+
|
|
358
|
+
// Try to read backup file
|
|
359
|
+
try {
|
|
360
|
+
const backupData = await fs2.readFile(backupFile, { encoding: "utf8" });
|
|
361
|
+
JSON.parse(backupData); // Validate backup JSON
|
|
362
|
+
|
|
363
|
+
// Restore from backup
|
|
364
|
+
await fs.copyFile(backupFile, mainFile);
|
|
365
|
+
console.log("Successfully restored from backup file");
|
|
366
|
+
|
|
367
|
+
console.log("log2");
|
|
368
|
+
|
|
369
|
+
return backupData;
|
|
370
|
+
} catch (backupError) {
|
|
371
|
+
console.error("Backup recovery failed, creating new file");
|
|
372
|
+
await Store_Config(defaultData);
|
|
373
|
+
|
|
374
|
+
console.log("log3");
|
|
375
|
+
|
|
376
|
+
return defaultData;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} catch (error) {
|
|
380
|
+
console.error("Error reading config:", error);
|
|
381
|
+
await Store_Config(defaultData);
|
|
382
|
+
|
|
383
|
+
console.log("log4");
|
|
384
|
+
|
|
385
|
+
return defaultData;
|
|
239
386
|
}
|
|
240
|
-
return data;
|
|
241
387
|
}
|
|
242
388
|
|
|
243
389
|
// STORE CONFIG FUNCTION - BACNET SERVER ==========================================
|
|
@@ -250,7 +396,7 @@ async function Store_Config_Server(data) {
|
|
|
250
396
|
//console.log("Store_Config_Server writeFile error: ", err);
|
|
251
397
|
}
|
|
252
398
|
});
|
|
253
|
-
} catch (err) {
|
|
399
|
+
} catch (err) {}
|
|
254
400
|
}
|
|
255
401
|
|
|
256
402
|
// READ CONFIG SYNC FUNCTION - BACNET SERVER ======================================
|
|
@@ -288,12 +434,12 @@ function decodeBitArray(size, bits) {
|
|
|
288
434
|
if (i == bits.length - 1) {
|
|
289
435
|
return array;
|
|
290
436
|
}
|
|
291
|
-
}
|
|
437
|
+
}
|
|
292
438
|
}
|
|
293
439
|
|
|
294
440
|
function getBacnetErrorString(classInt, codeInt) {
|
|
295
|
-
const classString = Object.keys(baEnum.ErrorClass).find(key => baEnum.ErrorClass[key] === classInt);
|
|
296
|
-
const codeString = Object.keys(baEnum.ErrorCode).find(key => baEnum.ErrorCode[key] === codeInt);
|
|
441
|
+
const classString = Object.keys(baEnum.ErrorClass).find((key) => baEnum.ErrorClass[key] === classInt);
|
|
442
|
+
const codeString = Object.keys(baEnum.ErrorCode).find((key) => baEnum.ErrorCode[key] === codeInt);
|
|
297
443
|
return `BacnetError - Class:${classString} - Code:${codeString}`;
|
|
298
444
|
}
|
|
299
445
|
|
|
@@ -309,7 +455,22 @@ function parseBacnetError(error) {
|
|
|
309
455
|
}
|
|
310
456
|
|
|
311
457
|
return err;
|
|
312
|
-
}
|
|
458
|
+
}
|
|
459
|
+
function debounce(func, wait) {
|
|
460
|
+
let timeout;
|
|
461
|
+
|
|
462
|
+
return function (...args) {
|
|
463
|
+
const context = this;
|
|
464
|
+
|
|
465
|
+
// Clear the previous timeout
|
|
466
|
+
clearTimeout(timeout);
|
|
467
|
+
|
|
468
|
+
// Set a new timeout
|
|
469
|
+
timeout = setTimeout(() => {
|
|
470
|
+
func.apply(context, args);
|
|
471
|
+
}, wait);
|
|
472
|
+
};
|
|
473
|
+
}
|
|
313
474
|
|
|
314
475
|
module.exports = {
|
|
315
476
|
BacnetConfig,
|
|
@@ -320,13 +481,15 @@ module.exports = {
|
|
|
320
481
|
generateId,
|
|
321
482
|
getIpAddress,
|
|
322
483
|
roundDecimalPlaces,
|
|
323
|
-
|
|
484
|
+
queueConfigStore,
|
|
324
485
|
Store_Config,
|
|
325
486
|
Read_Config_Sync,
|
|
487
|
+
Read_Config_Async,
|
|
326
488
|
Store_Config_Server,
|
|
327
489
|
Read_Config_Sync_Server,
|
|
328
490
|
isNumber,
|
|
329
491
|
decodeBitArray,
|
|
330
492
|
parseBacnetError,
|
|
331
493
|
getBacnetErrorString,
|
|
494
|
+
debounce,
|
|
332
495
|
};
|