@bitpoolos/edge-bacnet 1.6.2 → 1.6.4
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 +114 -66
- package/bacnet_client.js +65 -56
- package/bacnet_gateway.html +233 -18
- package/bacnet_gateway.js +41 -8
- package/bacnet_inspector.js +88 -12
- package/bacnet_inspector_worker.js +13 -3
- package/bacnet_read.html +223 -7
- package/bacnet_read.js +13 -1
- package/common.js +17 -78
- package/inspector.html +35 -1
- package/package.json +2 -2
- package/resources/style.css +28 -1
- package/treeBuilder.js +683 -567
package/bacnet_read.html
CHANGED
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
progressBarValue: ref(),
|
|
63
63
|
rightClickedDevice: ref(),
|
|
64
64
|
rightClickedPoint: ref(),
|
|
65
|
+
rightClickedMstpFolder: ref(),
|
|
65
66
|
showDeviceNameDialog: ref(false),
|
|
66
67
|
showPointNameDialog: ref(false),
|
|
67
68
|
deviceDisplayNameValue: ref(),
|
|
@@ -130,10 +131,7 @@
|
|
|
130
131
|
getData(initial) {
|
|
131
132
|
let app = this;
|
|
132
133
|
this.nodeService.getNetworkData().then(function (result) {
|
|
133
|
-
//remove after below fixed
|
|
134
134
|
app.devices = result.renderList;
|
|
135
|
-
//end remove
|
|
136
|
-
|
|
137
135
|
app.deviceList = result.deviceList;
|
|
138
136
|
app.pointList = result.pointList;
|
|
139
137
|
app.pollFrequency = parseInt(result.pollFrequency);
|
|
@@ -288,7 +286,13 @@
|
|
|
288
286
|
newReadParent = JSON.parse(JSON.stringify(parentDevice));
|
|
289
287
|
}
|
|
290
288
|
newReadParent.children[0].children = [];
|
|
291
|
-
|
|
289
|
+
|
|
290
|
+
// Create a copy of the point with preserved display name
|
|
291
|
+
let pointCopy = JSON.parse(JSON.stringify(slotProps.node));
|
|
292
|
+
// Ensure display name is preserved from the original point
|
|
293
|
+
pointCopy.label = slotProps.node.label;
|
|
294
|
+
newReadParent.children[0].children.push(pointCopy);
|
|
295
|
+
|
|
292
296
|
while (newReadParent.children.length > 1) {
|
|
293
297
|
newReadParent.children.forEach(function (child, index) {
|
|
294
298
|
if (child.label.includes("MSTP")) {
|
|
@@ -307,7 +311,10 @@
|
|
|
307
311
|
(ele) => ele.pointName == slotProps.node.pointName
|
|
308
312
|
);
|
|
309
313
|
if (pointIndex == -1) {
|
|
310
|
-
|
|
314
|
+
// Create a copy of the point with preserved display name
|
|
315
|
+
let pointCopy = JSON.parse(JSON.stringify(slotProps.node));
|
|
316
|
+
pointCopy.label = slotProps.node.label;
|
|
317
|
+
this.readDevices[foundDeviceIndex].children[0].children.push(pointCopy);
|
|
311
318
|
}
|
|
312
319
|
}
|
|
313
320
|
|
|
@@ -321,6 +328,10 @@
|
|
|
321
328
|
}
|
|
322
329
|
|
|
323
330
|
let point = this.pointList[key][slotProps.node.pointName];
|
|
331
|
+
// Preserve any existing display name when adding to pointsToRead
|
|
332
|
+
if (slotProps.node.label !== slotProps.node.pointName) {
|
|
333
|
+
point.displayName = slotProps.node.label;
|
|
334
|
+
}
|
|
324
335
|
this.pointsToRead[key][point.objectName] = point;
|
|
325
336
|
|
|
326
337
|
//force a deploy state
|
|
@@ -566,6 +577,27 @@
|
|
|
566
577
|
pointMenu.classList.remove("pointAddedToRead");
|
|
567
578
|
}
|
|
568
579
|
},
|
|
580
|
+
// NEW: Handle right-click on MSTP network folders
|
|
581
|
+
onMstpFolderRightClick(slotProps, event) {
|
|
582
|
+
let app = this;
|
|
583
|
+
app.rightClickedMstpFolder = slotProps;
|
|
584
|
+
event.preventDefault();
|
|
585
|
+
event.stopPropagation();
|
|
586
|
+
|
|
587
|
+
// Hide other context menus first
|
|
588
|
+
const menu = document.querySelector(".context-menu");
|
|
589
|
+
const pointMenu = document.querySelector(".point-context-menu");
|
|
590
|
+
if (menu) menu.style.display = "none";
|
|
591
|
+
if (pointMenu) pointMenu.style.display = "none";
|
|
592
|
+
|
|
593
|
+
const mstpFolderMenu = document.querySelector(".mstp-folder-context-menu");
|
|
594
|
+
|
|
595
|
+
if (mstpFolderMenu) {
|
|
596
|
+
mstpFolderMenu.style.setProperty("--mouse-x", event.clientX + "px");
|
|
597
|
+
mstpFolderMenu.style.setProperty("--mouse-y", event.clientY + "px");
|
|
598
|
+
mstpFolderMenu.style.display = "block";
|
|
599
|
+
}
|
|
600
|
+
},
|
|
569
601
|
handleContextMenuClick(type) {
|
|
570
602
|
let app = this;
|
|
571
603
|
switch (type) {
|
|
@@ -589,6 +621,17 @@
|
|
|
589
621
|
break;
|
|
590
622
|
}
|
|
591
623
|
},
|
|
624
|
+
// NEW: Handle context menu clicks for MSTP folders
|
|
625
|
+
handleMstpFolderContextMenuClick(type) {
|
|
626
|
+
let app = this;
|
|
627
|
+
switch (type) {
|
|
628
|
+
case "updateAllDevices":
|
|
629
|
+
app.updateAllMstpDevices(app.rightClickedMstpFolder);
|
|
630
|
+
break;
|
|
631
|
+
default:
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
},
|
|
592
635
|
handlePointContextMenuClick(type) {
|
|
593
636
|
let app = this;
|
|
594
637
|
switch (type) {
|
|
@@ -603,6 +646,43 @@
|
|
|
603
646
|
break;
|
|
604
647
|
}
|
|
605
648
|
},
|
|
649
|
+
// NEW: Update all devices within an MSTP network folder
|
|
650
|
+
updateAllMstpDevices(slotProps) {
|
|
651
|
+
let app = this;
|
|
652
|
+
|
|
653
|
+
if (!slotProps || !slotProps.node || !slotProps.node.children) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const mstpDevices = slotProps.node.children;
|
|
658
|
+
let updatedCount = 0;
|
|
659
|
+
|
|
660
|
+
// Iterate through all MSTP devices in the folder
|
|
661
|
+
mstpDevices.forEach(function (mstpDevice) {
|
|
662
|
+
// Find the device in the device list
|
|
663
|
+
let device = app.getDeviceFromDeviceList(mstpDevice.ipAddr, mstpDevice.deviceId);
|
|
664
|
+
if (device) {
|
|
665
|
+
app.nodeService
|
|
666
|
+
.updatePointsForDevice(device)
|
|
667
|
+
.then(function (result) {
|
|
668
|
+
updatedCount++;
|
|
669
|
+
})
|
|
670
|
+
.catch(function (error) {
|
|
671
|
+
// Handle error silently
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// Show user feedback
|
|
677
|
+
if (mstpDevices.length > 0) {
|
|
678
|
+
app.$toast?.add({
|
|
679
|
+
severity: "info",
|
|
680
|
+
summary: "Update Started",
|
|
681
|
+
detail: `Updating points for ${mstpDevices.length} devices in ${slotProps.node.label}`,
|
|
682
|
+
life: 3000,
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
},
|
|
606
686
|
purgeDevice(slotProps) {
|
|
607
687
|
let app = this;
|
|
608
688
|
let device = app.getDeviceFromDeviceList(slotProps.node.ipAddr, slotProps.node.deviceId);
|
|
@@ -660,13 +740,94 @@
|
|
|
660
740
|
|
|
661
741
|
app.nodeService.setPointDisplayName(deviceKey, pointName, pointDisplayName).then(function (result) {
|
|
662
742
|
if (result) {
|
|
663
|
-
|
|
743
|
+
// Update the display name across all UI representations
|
|
744
|
+
app.syncPointDisplayNameAcrossUI(deviceKey, pointName, pointDisplayName);
|
|
745
|
+
app.updatePointsToReadDisplayName(deviceKey, pointName, pointDisplayName);
|
|
664
746
|
}
|
|
665
747
|
});
|
|
666
748
|
}
|
|
667
749
|
|
|
668
750
|
app.showPointNameDialog = false;
|
|
669
751
|
},
|
|
752
|
+
// NEW: Centralized method to sync display names across all UI trees
|
|
753
|
+
syncPointDisplayNameAcrossUI(deviceKey, pointName, newDisplayName) {
|
|
754
|
+
let app = this;
|
|
755
|
+
|
|
756
|
+
// Update in main devices tree
|
|
757
|
+
app.updatePointDisplayNameInDevicesTree(deviceKey, pointName, newDisplayName);
|
|
758
|
+
|
|
759
|
+
// Update in read devices tree
|
|
760
|
+
app.updatePointDisplayNameInReadDevicesTree(deviceKey, pointName, newDisplayName);
|
|
761
|
+
|
|
762
|
+
// Force UI update
|
|
763
|
+
app.$forceUpdate();
|
|
764
|
+
},
|
|
765
|
+
// NEW: Update display name in main devices tree
|
|
766
|
+
updatePointDisplayNameInDevicesTree(deviceKey, pointName, newDisplayName) {
|
|
767
|
+
let app = this;
|
|
768
|
+
let [ipAddress, deviceId] = deviceKey.split("-");
|
|
769
|
+
|
|
770
|
+
// Find the device in main tree
|
|
771
|
+
let deviceIndex = app.devices
|
|
772
|
+
? app.devices.findIndex((device) => device.ipAddr === ipAddress && device.deviceId.toString() === deviceId)
|
|
773
|
+
: -1;
|
|
774
|
+
|
|
775
|
+
if (deviceIndex !== -1) {
|
|
776
|
+
// Update direct device points
|
|
777
|
+
let pointIndex = app.devices[deviceIndex].children[0].children.findIndex((point) => point.pointName === pointName);
|
|
778
|
+
if (pointIndex !== -1) {
|
|
779
|
+
app.devices[deviceIndex].children[0].children[pointIndex].label = newDisplayName;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Update MSTP device points if applicable
|
|
783
|
+
app.devices[deviceIndex].children.forEach((child) => {
|
|
784
|
+
if (child.label && child.label.includes("MSTP") && child.children) {
|
|
785
|
+
child.children.forEach((mstpDevice) => {
|
|
786
|
+
if (mstpDevice.deviceId.toString() === deviceId) {
|
|
787
|
+
let mstpPointIndex = mstpDevice.children[0].children.findIndex((point) => point.pointName === pointName);
|
|
788
|
+
if (mstpPointIndex !== -1) {
|
|
789
|
+
mstpDevice.children[0].children[mstpPointIndex].label = newDisplayName;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
// NEW: Update display name in read devices tree
|
|
798
|
+
updatePointDisplayNameInReadDevicesTree(deviceKey, pointName, newDisplayName) {
|
|
799
|
+
let app = this;
|
|
800
|
+
let [ipAddress, deviceId] = deviceKey.split("-");
|
|
801
|
+
|
|
802
|
+
if (!app.readDevices) return;
|
|
803
|
+
|
|
804
|
+
// Find the device in read devices tree
|
|
805
|
+
let readDeviceIndex = app.readDevices.findIndex((device) => device.deviceId.toString() === deviceId);
|
|
806
|
+
|
|
807
|
+
if (readDeviceIndex !== -1) {
|
|
808
|
+
let pointIndex = app.readDevices[readDeviceIndex].children[0].children.findIndex(
|
|
809
|
+
(point) => point.pointName === pointName
|
|
810
|
+
);
|
|
811
|
+
if (pointIndex !== -1) {
|
|
812
|
+
app.readDevices[readDeviceIndex].children[0].children[pointIndex].label = newDisplayName;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
},
|
|
816
|
+
// NEW: Update display name in pointsToRead data structure
|
|
817
|
+
updatePointsToReadDisplayName(deviceKey, pointName, newDisplayName) {
|
|
818
|
+
let app = this;
|
|
819
|
+
|
|
820
|
+
if (app.pointsToRead && app.pointsToRead[deviceKey]) {
|
|
821
|
+
// Find the point by objectName (since pointsToRead uses objectName as key)
|
|
822
|
+
for (let objectName in app.pointsToRead[deviceKey]) {
|
|
823
|
+
let point = app.pointsToRead[deviceKey][objectName];
|
|
824
|
+
if (point && point.objectName === pointName) {
|
|
825
|
+
point.displayName = newDisplayName;
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
},
|
|
670
831
|
getDeviceFromDeviceList(ip, id) {
|
|
671
832
|
let app = this;
|
|
672
833
|
let device = app.deviceList.find((ele) => {
|
|
@@ -896,7 +1057,30 @@
|
|
|
896
1057
|
app.$forceUpdate();
|
|
897
1058
|
},
|
|
898
1059
|
refreshReadListTree() {
|
|
899
|
-
|
|
1060
|
+
// Enhanced refresh that maintains display names
|
|
1061
|
+
let app = this;
|
|
1062
|
+
|
|
1063
|
+
// First apply any saved display names to the current pointsToRead
|
|
1064
|
+
app.applyStoredDisplayNames();
|
|
1065
|
+
|
|
1066
|
+
// Then rebuild the read list tree
|
|
1067
|
+
app.addToReadDevices(app.pointsToRead);
|
|
1068
|
+
},
|
|
1069
|
+
// NEW: Apply stored display names from backend to current data
|
|
1070
|
+
applyStoredDisplayNames() {
|
|
1071
|
+
let app = this;
|
|
1072
|
+
|
|
1073
|
+
if (!app.pointsToRead) return;
|
|
1074
|
+
|
|
1075
|
+
// Send current pointsToRead to backend to apply any stored display names
|
|
1076
|
+
let msg = { applyDisplayNames: true };
|
|
1077
|
+
// This will be handled by the backend node to apply stored display names
|
|
1078
|
+
app.nodeService.applyDisplayNames(app.pointsToRead).then(function (result) {
|
|
1079
|
+
if (result) {
|
|
1080
|
+
// Refresh the main tree with updated display names
|
|
1081
|
+
app.getData();
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
900
1084
|
},
|
|
901
1085
|
calculateMstpCount(slotProps) {
|
|
902
1086
|
let count = 0;
|
|
@@ -970,6 +1154,13 @@
|
|
|
970
1154
|
pointMenu.style.display = "none";
|
|
971
1155
|
});
|
|
972
1156
|
|
|
1157
|
+
var mstpFolderMenu = document.querySelector(".mstp-folder-context-menu");
|
|
1158
|
+
window.addEventListener("click", (event) => {
|
|
1159
|
+
if (menu) menu.style.display = "none";
|
|
1160
|
+
if (pointMenu) pointMenu.style.display = "none";
|
|
1161
|
+
if (mstpFolderMenu) mstpFolderMenu.style.display = "none";
|
|
1162
|
+
});
|
|
1163
|
+
|
|
973
1164
|
function handleCheckboxClick() {
|
|
974
1165
|
if (this.id == "node-input-object_property_simplePayload") {
|
|
975
1166
|
document.getElementById("node-input-object_property_fullObject").checked = false;
|
|
@@ -1113,6 +1304,18 @@
|
|
|
1113
1304
|
</ul>
|
|
1114
1305
|
<!-- End Point Context Menu -->
|
|
1115
1306
|
|
|
1307
|
+
<!-- Start MSTP Folder Context Menu -->
|
|
1308
|
+
<ul
|
|
1309
|
+
class="red-ui-menu red-ui-menu-dropdown red-ui-menu-dropdown-direction-right red-ui-menu-dropdown-noicons red-ui-menu-dropdown-submenus mstp-folder-context-menu">
|
|
1310
|
+
<li class="context-menu-item" @click="handleMstpFolderContextMenuClick('updateAllDevices')">
|
|
1311
|
+
<a class="red-ui-menu-label">
|
|
1312
|
+
<i class="pi pi-refresh context-menu-icon"></i>
|
|
1313
|
+
<span class="context-menu-item-text">Update All Devices</span>
|
|
1314
|
+
</a>
|
|
1315
|
+
</li>
|
|
1316
|
+
</ul>
|
|
1317
|
+
<!-- End MSTP Folder Context Menu -->
|
|
1318
|
+
|
|
1116
1319
|
<!--
|
|
1117
1320
|
*
|
|
1118
1321
|
*
|
|
@@ -1233,6 +1436,19 @@
|
|
|
1233
1436
|
</div>
|
|
1234
1437
|
</template>
|
|
1235
1438
|
|
|
1439
|
+
<template #mstpfolder="slotProps">
|
|
1440
|
+
<div @contextmenu="onMstpFolderRightClick(slotProps, $event)" class="p-treenode-label">
|
|
1441
|
+
<div class="deviceLabelParent">
|
|
1442
|
+
<b class="mstpLabel">
|
|
1443
|
+
<span>{{slotProps.node.label}}</span>
|
|
1444
|
+
<span class="mstpDeviceCount">
|
|
1445
|
+
{{slotProps.node.children ? slotProps.node.children.length : 0}}
|
|
1446
|
+
</span>
|
|
1447
|
+
</b>
|
|
1448
|
+
</div>
|
|
1449
|
+
</div>
|
|
1450
|
+
</template>
|
|
1451
|
+
|
|
1236
1452
|
<template #point="slotProps" v-model:class="pointContent">
|
|
1237
1453
|
<div @contextmenu="onPointRightClick(slotProps, $event)">
|
|
1238
1454
|
<b class="pointLabel">{{slotProps.node.label}}</b>
|
package/bacnet_read.js
CHANGED
|
@@ -99,7 +99,19 @@ module.exports = function (RED) {
|
|
|
99
99
|
useDeviceName = node.useDeviceName;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
// Ensure pointsToRead has the latest display names before processing
|
|
103
|
+
let pointsToReadWithDisplayNames = JSON.parse(JSON.stringify(node.pointsToRead));
|
|
104
|
+
|
|
105
|
+
// Apply any stored display names from the backend data model
|
|
106
|
+
for (let deviceKey in pointsToReadWithDisplayNames) {
|
|
107
|
+
for (let pointKey in pointsToReadWithDisplayNames[deviceKey]) {
|
|
108
|
+
let point = pointsToReadWithDisplayNames[deviceKey][pointKey];
|
|
109
|
+
// The display name should already be preserved in the point object
|
|
110
|
+
// from the setPointDisplayName operations and addPointClicked enhancements
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let readConfig = new ReadCommandConfig(pointsToReadWithDisplayNames, node.object_props, node.roundDecimal);
|
|
103
115
|
|
|
104
116
|
let output = {
|
|
105
117
|
type: "Read",
|
package/common.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const { randomUUID } = require("crypto");
|
|
6
6
|
const os = require("os");
|
|
7
|
+
const path = require("path");
|
|
7
8
|
const baEnum = require("./resources/node-bacstack-ts/dist/index.js").enum;
|
|
8
9
|
const fs = require("fs");
|
|
9
10
|
const fs2 = require("fs").promises;
|
|
@@ -189,27 +190,16 @@ const roundDecimalPlaces = function (value, decimals) {
|
|
|
189
190
|
return value;
|
|
190
191
|
};
|
|
191
192
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
await fs.writeFile("edge-bacnet-datastore.cfg", data, { encoding: "utf8", flag: "w" }, (err) => {
|
|
201
|
-
if (err) {
|
|
202
|
-
console.log("Store_Config writeFile error: ", err);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
} catch (e) {
|
|
206
|
-
//do nothing
|
|
193
|
+
const getStoragePath = (fileName) => {
|
|
194
|
+
const storagePath = process.env.BACNET_STORAGE_PATH;
|
|
195
|
+
if (storagePath) {
|
|
196
|
+
if (!fs.existsSync(storagePath)) {
|
|
197
|
+
fs.mkdirSync(storagePath, { recursive: true });
|
|
198
|
+
}
|
|
199
|
+
return path.join(storagePath, fileName);
|
|
207
200
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
*/
|
|
211
|
-
|
|
212
|
-
// refactor:
|
|
201
|
+
return fileName;
|
|
202
|
+
};
|
|
213
203
|
|
|
214
204
|
let storeQueue = [];
|
|
215
205
|
let isStoreProcessing = false;
|
|
@@ -235,9 +225,9 @@ async function queueConfigStore(data) {
|
|
|
235
225
|
}
|
|
236
226
|
|
|
237
227
|
async function Store_Config(data) {
|
|
238
|
-
const mainFile = "edge-bacnet-datastore.cfg";
|
|
239
|
-
const tempFile = "edge-bacnet-datastore.cfg.tmp";
|
|
240
|
-
const backupFile = "edge-bacnet-datastore.cfg.bak";
|
|
228
|
+
const mainFile = getStoragePath("edge-bacnet-datastore.cfg");
|
|
229
|
+
const tempFile = getStoragePath("edge-bacnet-datastore.cfg.tmp");
|
|
230
|
+
const backupFile = getStoragePath("edge-bacnet-datastore.cfg.bak");
|
|
241
231
|
|
|
242
232
|
try {
|
|
243
233
|
// First validate the JSON to ensure it's valid before writing
|
|
@@ -295,54 +285,10 @@ async function Store_Config(data) {
|
|
|
295
285
|
}
|
|
296
286
|
}
|
|
297
287
|
|
|
298
|
-
// READ CONFIG SYNC FUNCTION ======================================================
|
|
299
|
-
//
|
|
300
|
-
// ================================================================================
|
|
301
|
-
|
|
302
|
-
function Read_Config_Sync() {
|
|
303
|
-
const mainFile = "edge-bacnet-datastore.cfg";
|
|
304
|
-
const backupFile = "edge-bacnet-datastore.cfg.bak";
|
|
305
|
-
const defaultData = "{}";
|
|
306
|
-
|
|
307
|
-
try {
|
|
308
|
-
// Try to read the main file
|
|
309
|
-
let data = fsSync.readFileSync(mainFile, { encoding: "utf8" });
|
|
310
|
-
|
|
311
|
-
// Validate JSON
|
|
312
|
-
try {
|
|
313
|
-
JSON.parse(data);
|
|
314
|
-
return data;
|
|
315
|
-
} catch (jsonError) {
|
|
316
|
-
console.error("Main file contains invalid JSON, attempting backup recovery");
|
|
317
|
-
|
|
318
|
-
// Try to read backup file
|
|
319
|
-
try {
|
|
320
|
-
const backupData = fsSync.readFileSync(backupFile, { encoding: "utf8" });
|
|
321
|
-
JSON.parse(backupData); // Validate backup JSON
|
|
322
|
-
|
|
323
|
-
// Restore from backup
|
|
324
|
-
fsSync.copyFileSync(backupFile, mainFile);
|
|
325
|
-
console.log("Successfully restored from backup file");
|
|
326
|
-
return backupData;
|
|
327
|
-
} catch (backupError) {
|
|
328
|
-
console.error("Backup recovery failed, creating new file");
|
|
329
|
-
fsSync.writeFileSync(mainFile, defaultData, { encoding: "utf8" });
|
|
330
|
-
return defaultData;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
} catch (error) {
|
|
334
|
-
console.error("Error reading config:", error);
|
|
335
|
-
fsSync.writeFileSync(mainFile, defaultData, { encoding: "utf8" });
|
|
336
|
-
return defaultData;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// refactor:
|
|
341
|
-
|
|
342
288
|
async function Read_Config_Async() {
|
|
343
289
|
// todo rename function, not using sync
|
|
344
|
-
const mainFile = "edge-bacnet-datastore.cfg";
|
|
345
|
-
const backupFile = "edge-bacnet-datastore.cfg.bak";
|
|
290
|
+
const mainFile = getStoragePath("edge-bacnet-datastore.cfg");
|
|
291
|
+
const backupFile = getStoragePath("edge-bacnet-datastore.cfg.bak");
|
|
346
292
|
const defaultData = "{}";
|
|
347
293
|
|
|
348
294
|
try {
|
|
@@ -366,15 +312,11 @@ async function Read_Config_Async() {
|
|
|
366
312
|
await fs.copyFile(backupFile, mainFile);
|
|
367
313
|
console.log("Successfully restored from backup file");
|
|
368
314
|
|
|
369
|
-
console.log("log2");
|
|
370
|
-
|
|
371
315
|
return backupData;
|
|
372
316
|
} catch (backupError) {
|
|
373
317
|
console.error("Backup recovery failed, creating new file");
|
|
374
318
|
await Store_Config(defaultData);
|
|
375
319
|
|
|
376
|
-
console.log("log3");
|
|
377
|
-
|
|
378
320
|
return defaultData;
|
|
379
321
|
}
|
|
380
322
|
}
|
|
@@ -382,8 +324,6 @@ async function Read_Config_Async() {
|
|
|
382
324
|
console.error("Error reading config:", error);
|
|
383
325
|
await Store_Config(defaultData);
|
|
384
326
|
|
|
385
|
-
console.log("log4");
|
|
386
|
-
|
|
387
327
|
return defaultData;
|
|
388
328
|
}
|
|
389
329
|
}
|
|
@@ -393,7 +333,7 @@ async function Read_Config_Async() {
|
|
|
393
333
|
// ================================================================================
|
|
394
334
|
async function Store_Config_Server(data) {
|
|
395
335
|
try {
|
|
396
|
-
await fs.writeFile("edge-bacnet-server-datastore.cfg", data, (err) => {
|
|
336
|
+
await fs.writeFile(getStoragePath("edge-bacnet-server-datastore.cfg"), data, (err) => {
|
|
397
337
|
if (err) {
|
|
398
338
|
//console.log("Store_Config_Server writeFile error: ", err);
|
|
399
339
|
}
|
|
@@ -407,7 +347,7 @@ async function Store_Config_Server(data) {
|
|
|
407
347
|
function Read_Config_Sync_Server() {
|
|
408
348
|
var data = "{}";
|
|
409
349
|
try {
|
|
410
|
-
data = fs.readFileSync("edge-bacnet-server-datastore.cfg", { encoding: "utf8", flag: "r" });
|
|
350
|
+
data = fs.readFileSync(getStoragePath("edge-bacnet-server-datastore.cfg"), { encoding: "utf8", flag: "r" });
|
|
411
351
|
} catch (err) {
|
|
412
352
|
if (err.errno == -4058) {
|
|
413
353
|
data = "{}";
|
|
@@ -485,7 +425,6 @@ module.exports = {
|
|
|
485
425
|
roundDecimalPlaces,
|
|
486
426
|
queueConfigStore,
|
|
487
427
|
Store_Config,
|
|
488
|
-
Read_Config_Sync,
|
|
489
428
|
Read_Config_Async,
|
|
490
429
|
Store_Config_Server,
|
|
491
430
|
Read_Config_Sync_Server,
|
package/inspector.html
CHANGED
|
@@ -186,7 +186,11 @@
|
|
|
186
186
|
</div>
|
|
187
187
|
</template>
|
|
188
188
|
<p-column v-for="col in visibleColumns" :key="col.field" :field="col.field" :header="col.header" sortable
|
|
189
|
-
filter
|
|
189
|
+
filter>
|
|
190
|
+
<template #body="slotProps" v-if="col.field === 'objectType'">
|
|
191
|
+
{{ getObjectTypeString(slotProps.data.objectType) }}
|
|
192
|
+
</template>
|
|
193
|
+
</p-column>
|
|
190
194
|
<template #paginatorstart>
|
|
191
195
|
|
|
192
196
|
</template>
|
|
@@ -331,6 +335,36 @@
|
|
|
331
335
|
},
|
|
332
336
|
},
|
|
333
337
|
methods: {
|
|
338
|
+
getObjectTypeString(objectType) {
|
|
339
|
+
switch (objectType) {
|
|
340
|
+
case 0:
|
|
341
|
+
return "Analog Input";
|
|
342
|
+
case 1:
|
|
343
|
+
return "Analog Output";
|
|
344
|
+
case 2:
|
|
345
|
+
return "Analog Value";
|
|
346
|
+
case 3:
|
|
347
|
+
return "Binary Input";
|
|
348
|
+
case 4:
|
|
349
|
+
return "Binary Output";
|
|
350
|
+
case 5:
|
|
351
|
+
return "Binary Value";
|
|
352
|
+
case 8:
|
|
353
|
+
return "Device";
|
|
354
|
+
case 10:
|
|
355
|
+
return "File";
|
|
356
|
+
case 13:
|
|
357
|
+
return "Multistate Input";
|
|
358
|
+
case 14:
|
|
359
|
+
return "Multistate Output";
|
|
360
|
+
case 19:
|
|
361
|
+
return "Multistate Value";
|
|
362
|
+
case 40:
|
|
363
|
+
return "Character String";
|
|
364
|
+
default:
|
|
365
|
+
return "";
|
|
366
|
+
}
|
|
367
|
+
},
|
|
334
368
|
statusItemClicked(e) {
|
|
335
369
|
// Get the filter category from the clicked item's text content
|
|
336
370
|
const clickedItem = e.currentTarget;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitpoolos/edge-bacnet",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.4",
|
|
4
4
|
"description": "A bacnet gateway for node-red",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@plus4nodered/ts-node-bacnet": "^1.0.0-beta.2",
|
|
@@ -54,4 +54,4 @@
|
|
|
54
54
|
"type": "github",
|
|
55
55
|
"url": "git+https://github.com/bitpool/edge-bacnet.git"
|
|
56
56
|
}
|
|
57
|
-
}
|
|
57
|
+
}
|
package/resources/style.css
CHANGED
|
@@ -339,7 +339,9 @@
|
|
|
339
339
|
cursor: not-allowed;
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
|
-
|
|
342
|
+
.mstpLabel {
|
|
343
|
+
font-weight: 400;
|
|
344
|
+
}
|
|
343
345
|
.point-context-menu {
|
|
344
346
|
--mouse-x: 0;
|
|
345
347
|
--mouse-y: 0;
|
|
@@ -352,6 +354,18 @@
|
|
|
352
354
|
transform: translateX(min(var(--mouse-x), calc(100vw - 100%))) translateY(min(var(--mouse-y), calc(100vh - 100%)));
|
|
353
355
|
z-index: 10;
|
|
354
356
|
}
|
|
357
|
+
.mstp-folder-context-menu {
|
|
358
|
+
--mouse-x: 0;
|
|
359
|
+
--mouse-y: 0;
|
|
360
|
+
display: none;
|
|
361
|
+
position: fixed;
|
|
362
|
+
margin: 0;
|
|
363
|
+
left: 0;
|
|
364
|
+
top: 0;
|
|
365
|
+
/* The following line is responsible for all the magic */
|
|
366
|
+
transform: translateX(min(var(--mouse-x), calc(100vw - 100%))) translateY(min(var(--mouse-y), calc(100vh - 100%)));
|
|
367
|
+
z-index: 10;
|
|
368
|
+
}
|
|
355
369
|
.context-menu {
|
|
356
370
|
--mouse-x: 0;
|
|
357
371
|
--mouse-y: 0;
|
|
@@ -765,4 +779,17 @@
|
|
|
765
779
|
.p-confirm-dialog-accept > .p-button-label,
|
|
766
780
|
.bacnetServerRebuildSchedule_clearButton > .p-button-label {
|
|
767
781
|
color: white;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
.deviceFolderLabel {
|
|
785
|
+
display: flex;
|
|
786
|
+
align-items: center;
|
|
787
|
+
color: #333;
|
|
788
|
+
font-weight: 600;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
.deviceFolderLabel:hover {
|
|
792
|
+
background-color: #f8f9fa;
|
|
793
|
+
border-radius: 4px;
|
|
794
|
+
padding: 2px 4px;
|
|
768
795
|
}
|