@bitpoolos/edge-bacnet 1.6.1 → 1.6.2
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 +23 -0
- package/bacnet_client.js +14 -7
- package/bacnet_gateway.html +7 -0
- package/bacnet_gateway.js +5 -3
- package/bacnet_inspector_worker.js +2 -2
- package/common.js +7 -5
- package/inspector.html +25 -12
- package/package.json +1 -1
- package/resources/downloadAsHtml.js +8 -8
- package/resources/inspectorStyles.css +33 -21
- package/ssrHtmlExporter.js +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.6.2] - 07-05-2025
|
|
4
|
+
|
|
5
|
+
Minor feature:
|
|
6
|
+
- Added "Enable device discovery" check box to gateway settings, discovery tab.
|
|
7
|
+
- This check box controls whether on not the auto point discovery and property discovery is enabled.
|
|
8
|
+
- This can be used to turn off unecessary network traffic once you have discovered all of the desired devices and points.
|
|
9
|
+
- IMPORTANT - if you are updating a existing deployment, the new property will be unticked, however new installs / dragging from the pallete will by default have the option checked. So if you want to keep this enabled on an existing deployment, please check the setting.
|
|
10
|
+
- Note - This does not turn off the whoIs task schedule.
|
|
11
|
+
- A user can use Right Click -> Update points on desired deviced in the read node tree if you would like to manually discover devices.
|
|
12
|
+
|
|
13
|
+
Minor update:
|
|
14
|
+
- Adjusted initial whoIs broadcast delay from 5seconds to 15seconds after node-red is started with a deployed gateway. This is to ensure large cached files are completely read and loaded.
|
|
15
|
+
|
|
16
|
+
Bug fixes:
|
|
17
|
+
- Inspector:
|
|
18
|
+
- Table resized to avoid scroll bars
|
|
19
|
+
- Percentage rounding to nearest 2 decimal places rather than whole integer for more detailed data.
|
|
20
|
+
- Loading animation added
|
|
21
|
+
- NAN years ago - removed as invalid time differential
|
|
22
|
+
- Last seen for device points adjusted to be more accurate
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
3
26
|
## [1.6.1] - 14-04-2025
|
|
4
27
|
|
|
5
28
|
Bug fixes:
|
package/bacnet_client.js
CHANGED
|
@@ -52,6 +52,7 @@ class BacnetClient extends EventEmitter {
|
|
|
52
52
|
that.deviceRetryCount = parseInt(config.retries);
|
|
53
53
|
that.sanitise_device_schedule = config.sanitise_device_schedule;
|
|
54
54
|
that.buildTreeException = false;
|
|
55
|
+
that.enable_device_discovery = config.enable_device_discovery;
|
|
55
56
|
|
|
56
57
|
that.readPropertyMultipleOptions = {
|
|
57
58
|
maxSegments: 112,
|
|
@@ -80,11 +81,11 @@ class BacnetClient extends EventEmitter {
|
|
|
80
81
|
|
|
81
82
|
//query device task
|
|
82
83
|
const queryDevices = new Task("simple task", () => {
|
|
83
|
-
if (!that.pollInProgress) {
|
|
84
|
+
if (!that.pollInProgress && that.enable_device_discovery) {
|
|
84
85
|
that.queryDevices();
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
if (!that.buildJsonInProgress) {
|
|
88
|
+
if (!that.buildJsonInProgress && that.enable_device_discovery) {
|
|
88
89
|
that.buildJsonTree();
|
|
89
90
|
}
|
|
90
91
|
});
|
|
@@ -106,10 +107,15 @@ class BacnetClient extends EventEmitter {
|
|
|
106
107
|
setTimeout(() => {
|
|
107
108
|
that.globalWhoIs();
|
|
108
109
|
setTimeout(() => {
|
|
109
|
-
that.
|
|
110
|
-
|
|
110
|
+
if (!that.pollInProgress && that.enable_device_discovery) {
|
|
111
|
+
that.queryDevices();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!that.buildJsonInProgress && that.enable_device_discovery) {
|
|
115
|
+
that.buildJsonTree();
|
|
116
|
+
}
|
|
111
117
|
}, "4000");
|
|
112
|
-
}, "
|
|
118
|
+
}, "15000");
|
|
113
119
|
|
|
114
120
|
} catch (e) {
|
|
115
121
|
that.logOut("Issue initializing client: ", e);
|
|
@@ -615,6 +621,7 @@ class BacnetClient extends EventEmitter {
|
|
|
615
621
|
that.deviceId = config.deviceId;
|
|
616
622
|
that.broadCastAddr = config.broadCastAddr;
|
|
617
623
|
that.device_read_schedule = config.device_read_schedule;
|
|
624
|
+
that.enable_device_discovery = config.enable_device_discovery;
|
|
618
625
|
|
|
619
626
|
if (that.scheduler !== null) {
|
|
620
627
|
that.scheduler.stop();
|
|
@@ -640,11 +647,11 @@ class BacnetClient extends EventEmitter {
|
|
|
640
647
|
|
|
641
648
|
// //query device task
|
|
642
649
|
const queryDevices = new Task("simple task", () => {
|
|
643
|
-
if (!that.pollInProgress) {
|
|
650
|
+
if (!that.pollInProgress && that.enable_device_discovery) {
|
|
644
651
|
that.queryDevices();
|
|
645
652
|
}
|
|
646
653
|
|
|
647
|
-
if (!that.buildJsonInProgress) {
|
|
654
|
+
if (!that.buildJsonInProgress && that.enable_device_discovery) {
|
|
648
655
|
that.buildJsonTree();
|
|
649
656
|
}
|
|
650
657
|
});
|
package/bacnet_gateway.html
CHANGED
|
@@ -177,6 +177,7 @@
|
|
|
177
177
|
sanitise_device_schedule: { value: "60", required: false },
|
|
178
178
|
sanitise_device_schedule_value: { value: "1", required: false },
|
|
179
179
|
sanitise_device_schedule_options: { value: "Hours", required: false },
|
|
180
|
+
enable_device_discovery: { value: true, required: true },
|
|
180
181
|
},
|
|
181
182
|
networkInterfaces: [],
|
|
182
183
|
inputs: 1,
|
|
@@ -985,6 +986,11 @@
|
|
|
985
986
|
</select>
|
|
986
987
|
</div>
|
|
987
988
|
|
|
989
|
+
<div class="form-row bp-checkbox-row">
|
|
990
|
+
<input type="checkbox" id="node-input-enable_device_discovery" class="bp-checkbox" />
|
|
991
|
+
<label for="node-input-enable_device_discovery"> Enable device discovery </label>
|
|
992
|
+
</div>
|
|
993
|
+
|
|
988
994
|
<div class="form-row bp-checkbox-row">
|
|
989
995
|
<input type="checkbox" id="node-input-cacheFileEnabled" class="bp-checkbox" />
|
|
990
996
|
<label for="node-input-cacheFileEnabled"> Enable cache file </label>
|
|
@@ -1100,6 +1106,7 @@
|
|
|
1100
1106
|
the this bacnet client will enter into manual discovery mode, where it iterates through types and instnace ranges. This
|
|
1101
1107
|
range can be used to limit this manual scanning
|
|
1102
1108
|
</li>
|
|
1109
|
+
<li>Enable device discovery - toggles whether automatic device discovery, object scanning, and tree building is enabled.</li>
|
|
1103
1110
|
<li>Log Device Found - toggles logging of found devices to the node-red debug tab.</li>
|
|
1104
1111
|
<li>Log BACnet Errors to Console - toggles logging of BACnet related errors to the node-red console</li>
|
|
1105
1112
|
</ul>
|
package/bacnet_gateway.js
CHANGED
|
@@ -37,6 +37,7 @@ module.exports = function (RED) {
|
|
|
37
37
|
this.portRangeRegisters = config.portRangeRegisters;
|
|
38
38
|
this.cacheFileEnabled = config.cacheFileEnabled;
|
|
39
39
|
this.sanitise_device_schedule = config.sanitise_device_schedule;
|
|
40
|
+
this.enable_device_discovery = config.enable_device_discovery;
|
|
40
41
|
|
|
41
42
|
//client and config store
|
|
42
43
|
this.bacnetConfig = nodeContext.get("bacnetConfig");
|
|
@@ -65,7 +66,8 @@ module.exports = function (RED) {
|
|
|
65
66
|
node.retries,
|
|
66
67
|
node.cacheFileEnabled,
|
|
67
68
|
node.sanitise_device_schedule,
|
|
68
|
-
node.portRangeRegisters.filter((ele) => ele.enabled === true)
|
|
69
|
+
node.portRangeRegisters.filter((ele) => ele.enabled === true),
|
|
70
|
+
node.enable_device_discovery
|
|
69
71
|
);
|
|
70
72
|
|
|
71
73
|
if (typeof node.bacnetClient !== "undefined") {
|
|
@@ -263,7 +265,7 @@ module.exports = function (RED) {
|
|
|
263
265
|
} else if (msg.doUpdatePriorityDevices == true && msg.priorityDevices !== null) {
|
|
264
266
|
node.bacnetClient
|
|
265
267
|
.updatePriorityQueue(msg.priorityDevices)
|
|
266
|
-
.then(function (result) {})
|
|
268
|
+
.then(function (result) { })
|
|
267
269
|
.catch(function (error) {
|
|
268
270
|
logOut("Error updating priorityQueue: ", error);
|
|
269
271
|
});
|
|
@@ -277,7 +279,7 @@ module.exports = function (RED) {
|
|
|
277
279
|
|
|
278
280
|
node.bacnetClient
|
|
279
281
|
.applyDisplayNames(msg.pointsToRead)
|
|
280
|
-
.then(function (result) {})
|
|
282
|
+
.then(function (result) { })
|
|
281
283
|
.catch(function (error) {
|
|
282
284
|
logOut("Error in applyDisplayNames: ", error);
|
|
283
285
|
});
|
|
@@ -3,10 +3,10 @@ const { Worker } = require("worker_threads");
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
|
|
5
5
|
function getRelativeTime(timestamp) {
|
|
6
|
-
if (!timestamp) return "N/A";
|
|
6
|
+
if (!timestamp || isNaN(timestamp) || typeof timestamp !== 'number') return "N/A";
|
|
7
7
|
let now = Date.now();
|
|
8
8
|
let timeDiff = now - timestamp;
|
|
9
|
-
if (timeDiff < 0) return "
|
|
9
|
+
if (isNaN(timeDiff) || timeDiff < 0) return "Invalid timestamp";
|
|
10
10
|
|
|
11
11
|
let seconds = Math.floor(timeDiff / 1000);
|
|
12
12
|
let minutes = Math.floor(seconds / 60);
|
package/common.js
CHANGED
|
@@ -65,7 +65,8 @@ class BacnetClientConfig {
|
|
|
65
65
|
retries,
|
|
66
66
|
cacheFileEnabled,
|
|
67
67
|
sanitise_device_schedule,
|
|
68
|
-
portRangeMatrix
|
|
68
|
+
portRangeMatrix,
|
|
69
|
+
enable_device_discovery
|
|
69
70
|
) {
|
|
70
71
|
this.apduTimeout = apduTimeout;
|
|
71
72
|
this.localIpAdrress = localIpAdrress;
|
|
@@ -84,6 +85,7 @@ class BacnetClientConfig {
|
|
|
84
85
|
this.cacheFileEnabled = cacheFileEnabled;
|
|
85
86
|
this.sanitise_device_schedule = sanitise_device_schedule;
|
|
86
87
|
this.portRangeMatrix = this.generatePortRangeArray(portRangeMatrix);
|
|
88
|
+
this.enable_device_discovery = enable_device_discovery;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
generatePortRangeArray(rangeMatrix) {
|
|
@@ -255,7 +257,7 @@ async function Store_Config(data) {
|
|
|
255
257
|
JSON.parse(tempContent);
|
|
256
258
|
} catch (verifyError) {
|
|
257
259
|
console.error("Temporary file validation failed:", verifyError);
|
|
258
|
-
await fs2.unlink(tempFile).catch(() => {});
|
|
260
|
+
await fs2.unlink(tempFile).catch(() => { });
|
|
259
261
|
return false;
|
|
260
262
|
}
|
|
261
263
|
|
|
@@ -275,8 +277,8 @@ async function Store_Config(data) {
|
|
|
275
277
|
|
|
276
278
|
// Cleanup temporary file if it exists
|
|
277
279
|
try {
|
|
278
|
-
await fs2.unlink(tempFile).catch(() => {});
|
|
279
|
-
} catch (cleanupError) {}
|
|
280
|
+
await fs2.unlink(tempFile).catch(() => { });
|
|
281
|
+
} catch (cleanupError) { }
|
|
280
282
|
|
|
281
283
|
// If main file is corrupted and backup exists, restore from backup
|
|
282
284
|
try {
|
|
@@ -396,7 +398,7 @@ async function Store_Config_Server(data) {
|
|
|
396
398
|
//console.log("Store_Config_Server writeFile error: ", err);
|
|
397
399
|
}
|
|
398
400
|
});
|
|
399
|
-
} catch (err) {}
|
|
401
|
+
} catch (err) { }
|
|
400
402
|
}
|
|
401
403
|
|
|
402
404
|
// READ CONFIG SYNC FUNCTION - BACNET SERVER ======================================
|
package/inspector.html
CHANGED
|
@@ -133,9 +133,9 @@
|
|
|
133
133
|
<div class="center">
|
|
134
134
|
<p class="headertext">{{siteName}} - Point Status Display</p>
|
|
135
135
|
</div>
|
|
136
|
-
<p-datatable :value="displayData" paginator :rows="12" filterDisplay="menu"
|
|
137
|
-
v-model:filters="filters" :sortMode="'multiple'" @filter="onFilter"
|
|
138
|
-
class="datatable fixed-height-table">
|
|
136
|
+
<p-datatable :value="displayData" paginator scrollable scrollHeight="800px" :rows="12" filterDisplay="menu"
|
|
137
|
+
:filters="filters" v-model:filters="filters" :sortMode="'multiple'" @filter="onFilter"
|
|
138
|
+
class="datatable fixed-height-table" :loading="loading">
|
|
139
139
|
<template #header>
|
|
140
140
|
<div class="tableHeaderDiv">
|
|
141
141
|
<div style="margin-right: 5px">
|
|
@@ -177,6 +177,14 @@
|
|
|
177
177
|
</div>
|
|
178
178
|
</div>
|
|
179
179
|
</template>
|
|
180
|
+
<template #loading>
|
|
181
|
+
<div class="loading-overlay">
|
|
182
|
+
<div class="loading-spinner">
|
|
183
|
+
<i class="pi pi-spin pi-spinner" style="font-size: 2rem"></i>
|
|
184
|
+
<p>Loading data...</p>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
</template>
|
|
180
188
|
<p-column v-for="col in visibleColumns" :key="col.field" :field="col.field" :header="col.header" sortable
|
|
181
189
|
filter></p-column>
|
|
182
190
|
<template #paginatorstart>
|
|
@@ -188,6 +196,7 @@
|
|
|
188
196
|
</span>
|
|
189
197
|
</template>
|
|
190
198
|
</p-datatable>
|
|
199
|
+
|
|
191
200
|
</div>
|
|
192
201
|
</div>
|
|
193
202
|
</div>
|
|
@@ -199,7 +208,7 @@
|
|
|
199
208
|
data() {
|
|
200
209
|
return {
|
|
201
210
|
tableData: null,
|
|
202
|
-
loading:
|
|
211
|
+
loading: true,
|
|
203
212
|
totalRecords: 0,
|
|
204
213
|
first: 0,
|
|
205
214
|
lazyParams: {},
|
|
@@ -263,6 +272,7 @@
|
|
|
263
272
|
this.observeHeaderHeight();
|
|
264
273
|
|
|
265
274
|
this.selectedColumns = JSON.parse(JSON.stringify(this.allColumns));
|
|
275
|
+
this.loading = true;
|
|
266
276
|
|
|
267
277
|
fetch("/getModelStats")
|
|
268
278
|
.then((response) => {
|
|
@@ -288,19 +298,22 @@
|
|
|
288
298
|
// Calculate percentages
|
|
289
299
|
const total = tableData.length || 1; // Avoid division by zero
|
|
290
300
|
app.statPercentages = {
|
|
291
|
-
readCount: Math.round((data.statCounts.readCount / total) * 100
|
|
292
|
-
ok: Math.round((data.statCounts.statBlock.ok / total) * 100
|
|
293
|
-
error: Math.round((data.statCounts.statBlock.error / total) * 100
|
|
294
|
-
missing: Math.round((data.statCounts.statBlock.missing / total) * 100
|
|
295
|
-
warnings: Math.round((data.statCounts.statBlock.warnings / total) * 100
|
|
296
|
-
deviceIdChange: Math.round((data.statCounts.statBlock.deviceIdChange / total) * 100
|
|
297
|
-
deviceIdConflict: Math.round((data.statCounts.statBlock.deviceIdConflict / total) * 100
|
|
298
|
-
unmapped: Math.round((data.statCounts.statBlock.unmapped / total) * 100
|
|
301
|
+
readCount: Math.round((data.statCounts.readCount / total) * 10000) / 100,
|
|
302
|
+
ok: Math.round((data.statCounts.statBlock.ok / total) * 10000) / 100,
|
|
303
|
+
error: Math.round((data.statCounts.statBlock.error / total) * 10000) / 100,
|
|
304
|
+
missing: Math.round((data.statCounts.statBlock.missing / total) * 10000) / 100,
|
|
305
|
+
warnings: Math.round((data.statCounts.statBlock.warnings / total) * 10000) / 100,
|
|
306
|
+
deviceIdChange: Math.round((data.statCounts.statBlock.deviceIdChange / total) * 10000) / 100,
|
|
307
|
+
deviceIdConflict: Math.round((data.statCounts.statBlock.deviceIdConflict / total) * 10000) / 100,
|
|
308
|
+
unmapped: Math.round((data.statCounts.statBlock.unmapped / total) * 10000) / 100
|
|
299
309
|
};
|
|
300
310
|
})
|
|
301
311
|
.catch((error) => {
|
|
302
312
|
// Handle any errors
|
|
303
313
|
console.error("Error:", error);
|
|
314
|
+
})
|
|
315
|
+
.finally(() => {
|
|
316
|
+
app.loading = false;
|
|
304
317
|
});
|
|
305
318
|
},
|
|
306
319
|
computed: {
|
package/package.json
CHANGED
|
@@ -146,14 +146,14 @@ async function processPrimeVueHtml(appData) {
|
|
|
146
146
|
// Calculate percentages
|
|
147
147
|
const total = this.tableData.length || 1; // Avoid division by zero
|
|
148
148
|
this.statPercentages = {
|
|
149
|
-
readCount: Math.round((this.statCounts.readCount / total) * 100
|
|
150
|
-
ok: Math.round((this.statCounts.statBlock.ok / total) * 100
|
|
151
|
-
error: Math.round((this.statCounts.statBlock.error / total) * 100
|
|
152
|
-
missing: Math.round((this.statCounts.statBlock.missing / total) * 100
|
|
153
|
-
warnings: Math.round((this.statCounts.statBlock.warnings / total) * 100
|
|
154
|
-
deviceIdChange: Math.round((this.statCounts.statBlock.deviceIdChange / total) * 100
|
|
155
|
-
deviceIdConflict: Math.round((this.statCounts.statBlock.deviceIdConflict / total) *
|
|
156
|
-
unmapped: Math.round((this.statCounts.statBlock.unmapped / total) * 100
|
|
149
|
+
readCount: Math.round((this.statCounts.readCount / total) * 10000) / 100,
|
|
150
|
+
ok: Math.round((this.statCounts.statBlock.ok / total) * 10000) / 100,
|
|
151
|
+
error: Math.round((this.statCounts.statBlock.error / total) * 10000) / 100,
|
|
152
|
+
missing: Math.round((this.statCounts.statBlock.missing / total) * 10000) / 100,
|
|
153
|
+
warnings: Math.round((this.statCounts.statBlock.warnings / total) * 10000) / 100,
|
|
154
|
+
deviceIdChange: Math.round((this.statCounts.statBlock.deviceIdChange / total) * 10000) / 100,
|
|
155
|
+
deviceIdConflict: Math.round((this.statCounts.statBlock.deviceIdConflict / total) * 10000) / 100,
|
|
156
|
+
unmapped: Math.round((this.statCounts.statBlock.unmapped / total) * 10000) / 100
|
|
157
157
|
};
|
|
158
158
|
},
|
|
159
159
|
computed: {
|
|
@@ -183,32 +183,15 @@ body {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
|
|
186
|
-
/* changes start */
|
|
187
|
-
|
|
188
|
-
/* Add these rules to forcefully control table height */
|
|
189
|
-
.fixed-height-table {
|
|
190
|
-
height: 800px !important; /* Fixed height matching scrollHeight */
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.fixed-height-table .p-datatable-wrapper {
|
|
194
|
-
height: calc(800px - 92px) !important; /* Subtract header and paginator height */
|
|
195
|
-
min-height: calc(800px - 92px) !important;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
186
|
.fixed-height-table .p-datatable-table {
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/* Make sure the table body expands to fill available space */
|
|
203
|
-
.fixed-height-table .p-datatable-tbody {
|
|
204
|
-
min-height: 760px !important; /* Approximate calculation for body only */
|
|
187
|
+
height: 70vh !important;
|
|
205
188
|
}
|
|
206
189
|
|
|
207
|
-
/* Add an empty row to maintain height when filtered results are few */
|
|
208
190
|
.p-datatable-emptymessage td {
|
|
209
191
|
height: 760px !important;
|
|
210
192
|
}
|
|
211
193
|
|
|
194
|
+
|
|
212
195
|
.row-missing {
|
|
213
196
|
background-color: #00adef;
|
|
214
197
|
}
|
|
@@ -234,7 +217,7 @@ body {
|
|
|
234
217
|
width: -webkit-fill-available;
|
|
235
218
|
margin: 5px;
|
|
236
219
|
left: 0px;
|
|
237
|
-
height: 1000px !important;
|
|
220
|
+
/* height: 1000px !important; */
|
|
238
221
|
}
|
|
239
222
|
|
|
240
223
|
.datatable-card {
|
|
@@ -242,7 +225,7 @@ body {
|
|
|
242
225
|
padding-bottom: 1rem !important;
|
|
243
226
|
padding-left: 2rem;
|
|
244
227
|
padding-right: 2rem;
|
|
245
|
-
height: 1000px !important;
|
|
228
|
+
/* height: 1000px !important; */
|
|
246
229
|
}
|
|
247
230
|
|
|
248
231
|
.p-multiselect-overlay {
|
|
@@ -500,4 +483,33 @@ body {
|
|
|
500
483
|
/* Ensure table takes minimum space when empty */
|
|
501
484
|
.p-datatable-empty-message {
|
|
502
485
|
height: 576px !important;
|
|
486
|
+
}
|
|
487
|
+
.loading-overlay {
|
|
488
|
+
position: absolute;
|
|
489
|
+
top: 0;
|
|
490
|
+
left: 0;
|
|
491
|
+
width: 100%;
|
|
492
|
+
height: 100%;
|
|
493
|
+
display: flex;
|
|
494
|
+
justify-content: center;
|
|
495
|
+
align-items: center;
|
|
496
|
+
background-color: rgba(255, 255, 255, 0.7);
|
|
497
|
+
z-index: 1000;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.loading-spinner {
|
|
501
|
+
display: flex;
|
|
502
|
+
flex-direction: column;
|
|
503
|
+
align-items: center;
|
|
504
|
+
gap: 1rem;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.loading-spinner p {
|
|
508
|
+
margin: 0;
|
|
509
|
+
color: #495057;
|
|
510
|
+
font-weight: 500;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.p-datatable .p-datatable-loading-overlay {
|
|
514
|
+
position: relative;
|
|
503
515
|
}
|
package/ssrHtmlExporter.js
CHANGED
|
@@ -278,14 +278,14 @@ async function generatePrimeVueAppHtmlStatic(appData, filename = "bacnet-inspect
|
|
|
278
278
|
// Calculate percentages
|
|
279
279
|
const total = this.tableData.length || 1; // Avoid division by zero
|
|
280
280
|
this.statPercentages = {
|
|
281
|
-
readCount: Math.round((this.statCounts.readCount / total) *
|
|
282
|
-
ok: Math.round((this.statCounts.statBlock
|
|
283
|
-
error: Math.round((this.statCounts.statBlock
|
|
284
|
-
missing: Math.round((this.statCounts.statBlock
|
|
285
|
-
warnings: Math.round((this.statCounts.statBlock
|
|
286
|
-
deviceIdChange: Math.round((this.statCounts.statBlock
|
|
287
|
-
deviceIdConflict: Math.round((this.statCounts.statBlock
|
|
288
|
-
unmapped: Math.round((this.statCounts.statBlock
|
|
281
|
+
readCount: Math.round((this.statCounts.readCount / total) * 10000) / 100 || 0,
|
|
282
|
+
ok: Math.round((this.statCounts.statBlock.ok / total) * 10000) / 100 || 0,
|
|
283
|
+
error: Math.round((this.statCounts.statBlock.error / total) * 10000) / 100 || 0,
|
|
284
|
+
missing: Math.round((this.statCounts.statBlock.missing / total) * 10000) / 100 || 0,
|
|
285
|
+
warnings: Math.round((this.statCounts.statBlock.warnings / total) * 10000) / 100 || 0,
|
|
286
|
+
deviceIdChange: Math.round((this.statCounts.statBlock.deviceIdChange / total) * 10000) / 100 || 0,
|
|
287
|
+
deviceIdConflict: Math.round((this.statCounts.statBlock.deviceIdConflict / total) * 10000) / 100 || 0,
|
|
288
|
+
unmapped: Math.round((this.statCounts.statBlock.unmapped / total) * 10000) / 100 || 0
|
|
289
289
|
};
|
|
290
290
|
},
|
|
291
291
|
computed: {
|