@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/inspector.html
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Bitpool BACnet Inspector</title>
|
|
8
|
+
<link rel="icon" type="image/x-icon" href="resources/@bitpoolos/edge-bacnet/icons/favicon.ico">
|
|
9
|
+
|
|
10
|
+
<!-- Vue 3 -->
|
|
11
|
+
<script src="resources/@bitpoolos/edge-bacnet/vue3513.global.prod.js"></script>
|
|
12
|
+
<script src="resources/@bitpoolos/edge-bacnet/primevue.min.js"></script>
|
|
13
|
+
|
|
14
|
+
<script src="resources/@bitpoolos/edge-bacnet/downloadAsHtml.js"></script>
|
|
15
|
+
|
|
16
|
+
<link href="resources/@bitpoolos/edge-bacnet/primevue-saga-blue-theme.css" rel="stylesheet" />
|
|
17
|
+
<link href="resources/@bitpoolos/edge-bacnet/primevue.min.css" rel="stylesheet" />
|
|
18
|
+
<link href="resources/@bitpoolos/edge-bacnet/primeflex.min.css" rel="stylesheet" />
|
|
19
|
+
<link href="resources/@bitpoolos/edge-bacnet/primeicons.css" rel="stylesheet" />
|
|
20
|
+
<link href="resources/@bitpoolos/edge-bacnet/inspectorStyles.css" rel="stylesheet" />
|
|
21
|
+
</head>
|
|
22
|
+
|
|
23
|
+
<body>
|
|
24
|
+
<div id="app">
|
|
25
|
+
<div class="card header-content">
|
|
26
|
+
<div class="header">
|
|
27
|
+
<div class="dividerRight">
|
|
28
|
+
<img src="resources/@bitpoolos/edge-bacnet/Logo_Simplified_Positive.svg" class="logo" alt="Bitpool" />
|
|
29
|
+
</div>
|
|
30
|
+
<div class="status">
|
|
31
|
+
<span @click="statusItemClicked" class="statusItem status-with-icon">
|
|
32
|
+
<span class="status-icon-wrapper" style="background-color: #10B981;">
|
|
33
|
+
<img src="resources/@bitpoolos/edge-bacnet/icons/points-ok-icon.svg" class="status-icon ok-icon"
|
|
34
|
+
alt="Points OK" />
|
|
35
|
+
</span>
|
|
36
|
+
<div class="status-text">
|
|
37
|
+
<span class="statBlockValue">
|
|
38
|
+
{{statCounts?.readCount}}
|
|
39
|
+
<span class="stat-percentage">{{statPercentages.readCount}}%</span>
|
|
40
|
+
</span>
|
|
41
|
+
<span class="statBlockKey">Points OK</span>
|
|
42
|
+
</div>
|
|
43
|
+
</span>
|
|
44
|
+
<span @click="statusItemClicked" class="statusItem status-with-icon">
|
|
45
|
+
<span class="status-icon-wrapper" style="background-color: #F1707B;">
|
|
46
|
+
<img src="resources/@bitpoolos/edge-bacnet/icons/points-error-icon.svg" class="status-icon error-icon"
|
|
47
|
+
alt="Points Error" />
|
|
48
|
+
</span>
|
|
49
|
+
<div class="status-text">
|
|
50
|
+
<span class="statBlockValue">
|
|
51
|
+
{{statCounts?.statBlock.error}}
|
|
52
|
+
<span class="stat-percentage">{{statPercentages.error}}%</span>
|
|
53
|
+
</span>
|
|
54
|
+
<span class="statBlockKey">Points Error</span>
|
|
55
|
+
</div>
|
|
56
|
+
</span>
|
|
57
|
+
<span @click="statusItemClicked" class="statusItem status-with-icon">
|
|
58
|
+
<span class="status-icon-wrapper" style="background-color: #133547;">
|
|
59
|
+
<img src="resources/@bitpoolos/edge-bacnet/icons/points-missing-icon.svg" class="status-icon missing-icon"
|
|
60
|
+
alt="Points Missing" />
|
|
61
|
+
</span>
|
|
62
|
+
<div class="status-text">
|
|
63
|
+
<span class="statBlockValue">
|
|
64
|
+
{{statCounts?.statBlock.missing}}
|
|
65
|
+
<span class="stat-percentage">{{statPercentages.missing}}%</span>
|
|
66
|
+
</span>
|
|
67
|
+
<span class="statBlockKey">Points Missing</span>
|
|
68
|
+
</div>
|
|
69
|
+
</span>
|
|
70
|
+
<span @click="statusItemClicked" class="statusItem status-with-icon">
|
|
71
|
+
<span class="status-icon-wrapper" style="background-color: #F59E0B;">
|
|
72
|
+
<img src="resources/@bitpoolos/edge-bacnet/icons/points-warning-icon.svg" class="status-icon warning-icon"
|
|
73
|
+
alt="Points Warning" />
|
|
74
|
+
</span>
|
|
75
|
+
<div class="status-text">
|
|
76
|
+
<span class="statBlockValue">
|
|
77
|
+
{{statCounts?.statBlock.warnings}}
|
|
78
|
+
<span class="stat-percentage">{{statPercentages.warnings}}%</span>
|
|
79
|
+
</span>
|
|
80
|
+
<span class="statBlockKey">Points Warnings</span>
|
|
81
|
+
</div>
|
|
82
|
+
</span>
|
|
83
|
+
<span @click="statusItemClicked" class="statusItem status-with-icon">
|
|
84
|
+
<span class="status-icon-wrapper" style="background-color: #00ADEF;">
|
|
85
|
+
<img src="resources/@bitpoolos/edge-bacnet/icons/points-unmapped-icon.svg"
|
|
86
|
+
class="status-icon unmapped-icon" alt="Points Unmapped" />
|
|
87
|
+
</span>
|
|
88
|
+
<div class="status-text">
|
|
89
|
+
<span class="statBlockValue">
|
|
90
|
+
{{statCounts?.statBlock.unmapped}}
|
|
91
|
+
<span class="stat-percentage">{{statPercentages.unmapped}}%</span>
|
|
92
|
+
</span>
|
|
93
|
+
<span class="statBlockKey">Points Unmapped</span>
|
|
94
|
+
</div>
|
|
95
|
+
</span>
|
|
96
|
+
<span @click="statusItemClicked" class="statusItem status-with-icon">
|
|
97
|
+
<span class="status-icon-wrapper" style="background-color: #0689BC;">
|
|
98
|
+
<img src="resources/@bitpoolos/edge-bacnet/icons/device-id-change-icon.svg"
|
|
99
|
+
class="status-icon device-id-change-icon" alt="Device ID Change" />
|
|
100
|
+
</span>
|
|
101
|
+
<div class="status-text">
|
|
102
|
+
<span class="statBlockValue">
|
|
103
|
+
{{statCounts?.statBlock.deviceIdChange}}
|
|
104
|
+
<span class="stat-percentage">{{statPercentages.deviceIdChange}}%</span>
|
|
105
|
+
</span>
|
|
106
|
+
<span class="statBlockKey">Changed Device ID's</span>
|
|
107
|
+
</div>
|
|
108
|
+
</span>
|
|
109
|
+
<span @click="statusItemClicked" class="statusItem status-with-icon">
|
|
110
|
+
<span class="status-icon-wrapper" style="background-color: #406C7D;">
|
|
111
|
+
<img src="resources/@bitpoolos/edge-bacnet/icons/device-id-conflict-icon.svg"
|
|
112
|
+
class="status-icon device-id-conflict-icon" alt="Device ID Conflict" />
|
|
113
|
+
</span>
|
|
114
|
+
<div class="status-text">
|
|
115
|
+
<span class="statBlockValue">
|
|
116
|
+
{{statCounts?.statBlock.deviceIdConflict}}
|
|
117
|
+
<span class="stat-percentage">{{statPercentages.deviceIdConflict}}%</span>
|
|
118
|
+
</span>
|
|
119
|
+
<span class="statBlockKey">Conflicting Device ID's</span>
|
|
120
|
+
</div>
|
|
121
|
+
</span>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="actionButtons">
|
|
124
|
+
<p-button icon="pi pi-refresh" onclick="window.location.reload()" class="refreshButton" label="Refresh">
|
|
125
|
+
</p-button>
|
|
126
|
+
<p-button icon="pi pi-download" @click="downloadCSV()" class="bitpool-blue" label="Download CSV"></p-button>
|
|
127
|
+
<p-button icon="pi pi-download" @click="downloadHTML()" class="bitpool-blue" label="Download HTML"></p-button>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="content-wrapper">
|
|
132
|
+
<div class="card datatable-card">
|
|
133
|
+
<div class="center">
|
|
134
|
+
<p class="headertext">{{siteName}} - Point Status Display</p>
|
|
135
|
+
</div>
|
|
136
|
+
<p-datatable :value="displayData" paginator :rows="12" filterDisplay="menu" :filters="filters"
|
|
137
|
+
v-model:filters="filters" :sortMode="'multiple'" @filter="onFilter" scrollable scrollHeight="800px"
|
|
138
|
+
class="datatable fixed-height-table">
|
|
139
|
+
<template #header>
|
|
140
|
+
<div class="tableHeaderDiv">
|
|
141
|
+
<div style="margin-right: 5px">
|
|
142
|
+
<p-multiselect v-model="selectedColumns" :options="allColumns" optionLabel="header" dataKey="field"
|
|
143
|
+
placeholder="Select Columns" :maxSelectedLabels="3" class="columnSelector w-full md:w-20rem"
|
|
144
|
+
display="chip">
|
|
145
|
+
<template #option="slotProps">
|
|
146
|
+
<div style="display: flex; align-items: center">
|
|
147
|
+
<div class="custom-checkbox">
|
|
148
|
+
<div :class="['custom-checkbox-box', {'selected': isSelected(slotProps.option)}]">
|
|
149
|
+
<i v-if="isSelected(slotProps.option)" class="pi pi-check custom-checkbox-icon"></i>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
<span>{{ slotProps.option.header }}</span>
|
|
153
|
+
</div>
|
|
154
|
+
</template>
|
|
155
|
+
|
|
156
|
+
<template #value="slotProps">
|
|
157
|
+
<template v-if="slotProps.value && slotProps.value.length > 0">
|
|
158
|
+
<template v-if="slotProps.value.length <= 3">
|
|
159
|
+
<div class="p-multiselect-token" v-for="(item, index) in slotProps.value" :key="item.field">
|
|
160
|
+
<span class="p-multiselect-token-label">{{ item.header }}</span>
|
|
161
|
+
<span v-if="index < slotProps.value.length - 1">, </span>
|
|
162
|
+
</div>
|
|
163
|
+
</template>
|
|
164
|
+
<template v-else>
|
|
165
|
+
<div class="p-multiselect-token-label">{{ slotProps.value.length }} items selected</div>
|
|
166
|
+
</template>
|
|
167
|
+
</template>
|
|
168
|
+
<span v-else class="p-multiselect-placeholder">{{ 'Select Columns' }}</span>
|
|
169
|
+
</template>
|
|
170
|
+
</p-multiselect>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<div style="width: 100%">
|
|
174
|
+
<i class="pi pi-search searchBarContainer"></i>
|
|
175
|
+
<p-input-text class="searchBar" type="text" pInputText v-model="filters['global'].value"
|
|
176
|
+
placeholder="Search"></p-input-text>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</template>
|
|
180
|
+
<p-column v-for="col in visibleColumns" :key="col.field" :field="col.field" :header="col.header" sortable
|
|
181
|
+
filter></p-column>
|
|
182
|
+
<template #paginatorstart>
|
|
183
|
+
|
|
184
|
+
</template>
|
|
185
|
+
<template #paginatorend="slotProps">
|
|
186
|
+
<span class="">
|
|
187
|
+
<span class="statBlockValue">{{displayedRowsCount}} results</span>
|
|
188
|
+
</span>
|
|
189
|
+
</template>
|
|
190
|
+
</p-datatable>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<script>
|
|
196
|
+
const { createApp, ref } = Vue;
|
|
197
|
+
// Create app
|
|
198
|
+
const app = createApp({
|
|
199
|
+
data() {
|
|
200
|
+
return {
|
|
201
|
+
tableData: null,
|
|
202
|
+
loading: false,
|
|
203
|
+
totalRecords: 0,
|
|
204
|
+
first: 0,
|
|
205
|
+
lazyParams: {},
|
|
206
|
+
siteName: ref(),
|
|
207
|
+
statCounts: ref(),
|
|
208
|
+
displayedRowsCount: 0, // Track how many rows are visible
|
|
209
|
+
filters: {
|
|
210
|
+
global: { value: null, matchMode: "contains" },
|
|
211
|
+
deviceID: { value: null, matchMode: "contains" },
|
|
212
|
+
ObjectType: { value: null, matchMode: "contains" },
|
|
213
|
+
objectInstance: { value: null, matchMode: "contains" },
|
|
214
|
+
presentValue: { value: null, matchMode: "contains" },
|
|
215
|
+
dataModelStatus: { value: null, matchMode: "contains" },
|
|
216
|
+
pointName: { value: null, matchMode: "contains" },
|
|
217
|
+
discoveredBACnetPointName: { value: null, matchMode: "contains" },
|
|
218
|
+
displayName: { value: null, matchMode: "contains" },
|
|
219
|
+
deviceName: { value: null, matchMode: "contains" },
|
|
220
|
+
ipAddress: { value: null, matchMode: "contains" },
|
|
221
|
+
area: { value: null, matchMode: "contains" },
|
|
222
|
+
key: { value: null, matchMode: "contains" },
|
|
223
|
+
topic: { value: null, matchMode: "contains" },
|
|
224
|
+
lastSeen: { value: null, matchMode: "contains" },
|
|
225
|
+
error: { value: null, matchMode: "contains" },
|
|
226
|
+
},
|
|
227
|
+
allColumns: [
|
|
228
|
+
{ field: "deviceID", header: "Device ID" },
|
|
229
|
+
{ field: "objectType", header: "Object Type" },
|
|
230
|
+
{ field: "objectInstance", header: "Object Instance" },
|
|
231
|
+
{ field: "presentValue", header: "Present Value" },
|
|
232
|
+
{ field: "dataModelStatus", header: "Data Model Status" },
|
|
233
|
+
{ field: "pointName", header: "Mapped Point Name" },
|
|
234
|
+
{ field: "discoveredBACnetPointName", header: "Discovered Point Name" },
|
|
235
|
+
{ field: "displayName", header: "Display Name" },
|
|
236
|
+
{ field: "deviceName", header: "Device Name" },
|
|
237
|
+
{ field: "ipAddress", header: "IP Address" },
|
|
238
|
+
{ field: "area", header: "Area" },
|
|
239
|
+
{ field: "key", header: "Key" },
|
|
240
|
+
{ field: "topic", header: "Topic" },
|
|
241
|
+
{ field: "lastSeen", header: "Last Seen" },
|
|
242
|
+
{ field: "error", header: "Error" },
|
|
243
|
+
],
|
|
244
|
+
selectedColumns: [],
|
|
245
|
+
statPercentages: {
|
|
246
|
+
readCount: 0,
|
|
247
|
+
error: 0,
|
|
248
|
+
missing: 0,
|
|
249
|
+
warnings: 0,
|
|
250
|
+
deviceIdChange: 0,
|
|
251
|
+
deviceIdConflict: 0,
|
|
252
|
+
unmapped: 0
|
|
253
|
+
},
|
|
254
|
+
activeFilter: null,
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
setup() {
|
|
258
|
+
return {};
|
|
259
|
+
},
|
|
260
|
+
mounted() {
|
|
261
|
+
let app = this;
|
|
262
|
+
this.observeHeaderHeight();
|
|
263
|
+
|
|
264
|
+
this.selectedColumns = JSON.parse(JSON.stringify(this.allColumns));
|
|
265
|
+
|
|
266
|
+
fetch("/getModelStats")
|
|
267
|
+
.then((response) => {
|
|
268
|
+
// Check if the response is successful
|
|
269
|
+
if (!response.ok) {
|
|
270
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
271
|
+
}
|
|
272
|
+
// Parse the JSON response
|
|
273
|
+
return response.json();
|
|
274
|
+
})
|
|
275
|
+
.then((data) => {
|
|
276
|
+
let tableData = [];
|
|
277
|
+
|
|
278
|
+
if (data.resultList) {
|
|
279
|
+
for (let item in data.resultList) {
|
|
280
|
+
tableData.push(data.resultList[item]);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
app.tableData = tableData;
|
|
284
|
+
app.siteName = data.siteName;
|
|
285
|
+
app.statCounts = data.statCounts;
|
|
286
|
+
|
|
287
|
+
// Calculate percentages
|
|
288
|
+
const total = tableData.length || 1; // Avoid division by zero
|
|
289
|
+
app.statPercentages = {
|
|
290
|
+
readCount: Math.round((data.statCounts.readCount / total) * 100),
|
|
291
|
+
error: Math.round((data.statCounts.statBlock.error / total) * 100),
|
|
292
|
+
missing: Math.round((data.statCounts.statBlock.missing / total) * 100),
|
|
293
|
+
warnings: Math.round((data.statCounts.statBlock.warnings / total) * 100),
|
|
294
|
+
deviceIdChange: Math.round((data.statCounts.statBlock.deviceIdChange / total) * 100),
|
|
295
|
+
deviceIdConflict: Math.round((data.statCounts.statBlock.deviceIdConflict / total) * 100),
|
|
296
|
+
unmapped: Math.round((data.statCounts.statBlock.unmapped / total) * 100)
|
|
297
|
+
};
|
|
298
|
+
})
|
|
299
|
+
.catch((error) => {
|
|
300
|
+
// Handle any errors
|
|
301
|
+
console.error("Error:", error);
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
computed: {
|
|
305
|
+
visibleColumns() {
|
|
306
|
+
return this.selectedColumns || []; // Handle null case
|
|
307
|
+
},
|
|
308
|
+
displayData() {
|
|
309
|
+
if (!this.tableData || this.tableData.length === 0) {
|
|
310
|
+
// Create empty rows to maintain height
|
|
311
|
+
return Array(20)
|
|
312
|
+
.fill({})
|
|
313
|
+
.map(() => ({}));
|
|
314
|
+
}
|
|
315
|
+
return this.tableData;
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
methods: {
|
|
319
|
+
statusItemClicked(e) {
|
|
320
|
+
// Get the filter category from the clicked item's text content
|
|
321
|
+
const clickedItem = e.currentTarget;
|
|
322
|
+
const statusType = clickedItem.querySelector('.statBlockKey').textContent.trim();
|
|
323
|
+
|
|
324
|
+
// Clear previous active status styling
|
|
325
|
+
document.querySelectorAll('.statusItem').forEach(item => {
|
|
326
|
+
item.classList.remove('active-filter');
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// If clicking the already active filter, clear it
|
|
330
|
+
if (this.activeFilter === statusType) {
|
|
331
|
+
this.activeFilter = null;
|
|
332
|
+
this.filters['dataModelStatus'].value = null;
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Set this item as active
|
|
337
|
+
clickedItem.classList.add('active-filter');
|
|
338
|
+
this.activeFilter = statusType;
|
|
339
|
+
|
|
340
|
+
// Apply the appropriate filter based on which status item was clicked
|
|
341
|
+
let filterValue = '';
|
|
342
|
+
switch (statusType) {
|
|
343
|
+
case 'Points OK':
|
|
344
|
+
filterValue = 'Point Ok';
|
|
345
|
+
break;
|
|
346
|
+
case 'Points Error':
|
|
347
|
+
filterValue = 'Point Error';
|
|
348
|
+
break;
|
|
349
|
+
case 'Points Missing':
|
|
350
|
+
filterValue = 'Point Missing';
|
|
351
|
+
break;
|
|
352
|
+
case 'Points Warnings':
|
|
353
|
+
filterValue = 'Point Warning';
|
|
354
|
+
break;
|
|
355
|
+
case 'Points Unmapped':
|
|
356
|
+
filterValue = 'Point Unmapped';
|
|
357
|
+
break;
|
|
358
|
+
case 'Changed Device ID\'s':
|
|
359
|
+
filterValue = 'Device ID Changed';
|
|
360
|
+
break;
|
|
361
|
+
case 'Conflicting Device ID\'s':
|
|
362
|
+
filterValue = 'Device ID Conflict';
|
|
363
|
+
break;
|
|
364
|
+
default:
|
|
365
|
+
filterValue = '';
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Apply the filter
|
|
369
|
+
this.filters['dataModelStatus'].value = filterValue;
|
|
370
|
+
},
|
|
371
|
+
observeHeaderHeight() {
|
|
372
|
+
const header = document.querySelector('.header-content');
|
|
373
|
+
const updateContentMargin = () => {
|
|
374
|
+
const headerHeight = header.offsetHeight;
|
|
375
|
+
document.documentElement.style.setProperty('--header-height', `${headerHeight}px`);
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// Update initially
|
|
379
|
+
updateContentMargin();
|
|
380
|
+
|
|
381
|
+
// Observe header size changes
|
|
382
|
+
const resizeObserver = new ResizeObserver(updateContentMargin);
|
|
383
|
+
resizeObserver.observe(header);
|
|
384
|
+
|
|
385
|
+
// Update on window resize
|
|
386
|
+
window.addEventListener('resize', updateContentMargin);
|
|
387
|
+
},
|
|
388
|
+
enforceTableHeight() {
|
|
389
|
+
// Force minimum height on table elements
|
|
390
|
+
const tableWrapper = document.querySelector(".p-datatable-wrapper");
|
|
391
|
+
if (tableWrapper) {
|
|
392
|
+
tableWrapper.style.minHeight = "800px";
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const tableBody = document.querySelector(".p-datatable-tbody");
|
|
396
|
+
if (tableBody) {
|
|
397
|
+
tableBody.style.minHeight = "760px";
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
isSelected(option) {
|
|
401
|
+
if (!this.selectedColumns) return false;
|
|
402
|
+
return this.selectedColumns.some((item) => item.field === option.field);
|
|
403
|
+
},
|
|
404
|
+
getRowClass(rowData) {
|
|
405
|
+
console.log("rowData", rowData);
|
|
406
|
+
if (rowData.dataModelStatus.includes("Point OK")) return "row-ok";
|
|
407
|
+
if (rowData.dataModelStatus.includes("Point Error")) return "row-error";
|
|
408
|
+
if (rowData.dataModelStatus.includes("Point Warning")) return "row-warning";
|
|
409
|
+
if (rowData.dataModelStatus.includes("Point Missing")) return "row-missing";
|
|
410
|
+
return ""; // Default, no class
|
|
411
|
+
},
|
|
412
|
+
downloadCSV() {
|
|
413
|
+
window.location.href = "/getmodelstatscsv";
|
|
414
|
+
},
|
|
415
|
+
downloadHTML() {
|
|
416
|
+
let filename = `${this.siteName}_BACnetStats_${Date.now()}.html`;
|
|
417
|
+
// Create data object with all necessary properties
|
|
418
|
+
const data = {
|
|
419
|
+
tableData: this.tableData,
|
|
420
|
+
siteName: this.siteName,
|
|
421
|
+
statCounts: this.statCounts,
|
|
422
|
+
displayedRowsCount: this.displayedRowsCount,
|
|
423
|
+
filters: this.filters,
|
|
424
|
+
selectedColumns: this.selectedColumns,
|
|
425
|
+
allColumns: this.allColumns,
|
|
426
|
+
};
|
|
427
|
+
downloadPrimeVueAppAsHtml(filename, data);
|
|
428
|
+
},
|
|
429
|
+
onFilter(e) {
|
|
430
|
+
// e.filteredValue contains the new filtered dataset
|
|
431
|
+
if (e.filteredValue) {
|
|
432
|
+
this.displayedRowsCount = e.filteredValue.length;
|
|
433
|
+
} else {
|
|
434
|
+
// if no filter is applied, revert to full tableData length
|
|
435
|
+
this.displayedRowsCount = this.tableData ? this.tableData.length : 0;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
this.$nextTick(() => {
|
|
439
|
+
this.enforceTableHeight();
|
|
440
|
+
});
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
components: {
|
|
444
|
+
"p-button": PrimeVue.Button,
|
|
445
|
+
"p-card": PrimeVue.Card,
|
|
446
|
+
"p-input-text": PrimeVue.InputText,
|
|
447
|
+
"p-datatable": PrimeVue.DataTable,
|
|
448
|
+
"p-column": PrimeVue.Column,
|
|
449
|
+
"p-icon-field": PrimeVue.IconField,
|
|
450
|
+
"p-input-icon": PrimeVue.InputIcon,
|
|
451
|
+
"p-multiselect": PrimeVue.MultiSelect,
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
app.use(PrimeVue.Config);
|
|
456
|
+
app.mount("#app");
|
|
457
|
+
</script>
|
|
458
|
+
</body>
|
|
459
|
+
|
|
460
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitpoolos/edge-bacnet",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "A bacnet gateway for node-red",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@plus4nodered/ts-node-bacnet": "^1.0.0-beta.2",
|
|
7
|
+
"@vue/server-renderer": "^3.5.13",
|
|
7
8
|
"async-mutex": "^0.4.0",
|
|
8
9
|
"cronosjs": "^1.7.1",
|
|
9
10
|
"debug": "^4.1.1",
|
|
10
11
|
"iconv-lite": "^0.6.3",
|
|
12
|
+
"primevue": "^4.3.2",
|
|
11
13
|
"toad-scheduler": "^1.6.0",
|
|
12
14
|
"underscore": "^1.10.2",
|
|
15
|
+
"vue": "^3.5.13",
|
|
13
16
|
"winston": "^3.2.1"
|
|
14
17
|
},
|
|
15
18
|
"node-red": {
|
|
@@ -18,7 +21,8 @@
|
|
|
18
21
|
"Bacnet-Gateway": "bacnet_gateway.js",
|
|
19
22
|
"Bacnet-Discovery": "bacnet_read.js",
|
|
20
23
|
"Bacnet-Write": "bacnet_write.js",
|
|
21
|
-
"Bitpool-Inject": "bitpool_inject.js"
|
|
24
|
+
"Bitpool-Inject": "bitpool_inject.js",
|
|
25
|
+
"Bacnet-Inspector": "bacnet_inspector.js"
|
|
22
26
|
}
|
|
23
27
|
},
|
|
24
28
|
"keywords": [
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg id="Logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 617.93 192.79">
|
|
3
|
+
<defs>
|
|
4
|
+
<style>
|
|
5
|
+
.cls-1 {
|
|
6
|
+
fill: #162732;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.cls-1, .cls-2 {
|
|
10
|
+
stroke-width: 0px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.cls-2 {
|
|
14
|
+
fill: url(#linear-gradient);
|
|
15
|
+
}
|
|
16
|
+
</style>
|
|
17
|
+
<linearGradient id="linear-gradient" x1="0" y1="96.39" x2="171.04" y2="96.39" gradientUnits="userSpaceOnUse">
|
|
18
|
+
<stop offset="0" stop-color="#83d7f7"/>
|
|
19
|
+
<stop offset="1" stop-color="#00adef"/>
|
|
20
|
+
</linearGradient>
|
|
21
|
+
</defs>
|
|
22
|
+
<g>
|
|
23
|
+
<path class="cls-1" d="M255.61,94.18c14.39,1.25,19.23,10.24,19.23,25.46,0,19.64-8.99,24.9-31.27,24.9-17.43,0-26.56-.14-38.04-1.1V47.97c10.24-.97,18.54-1.11,33.76-1.11,24.07,0,32.65,5.26,32.65,24.9,0,13.7-4.7,20.75-16.32,21.86v.55ZM239.84,87.68c11.62,0,15.49-2.77,15.49-13.7s-4.15-13.14-16.88-13.14h-15.91v26.84h17.29ZM240.67,130.57c12.87,0,16.88-2.63,16.88-14.11,0-12.73-4.29-16.05-17.43-16.19h-17.57v30.16l18.12.14Z"/>
|
|
24
|
+
<path class="cls-1" d="M295.64,46.17c3.32,0,4.84,1.8,4.84,4.84v7.61c0,3.18-1.52,4.84-4.84,4.84h-7.75c-3.18,0-4.84-1.66-4.84-4.84v-7.61c0-3.04,1.66-4.84,4.84-4.84h7.75ZM283.46,143.43v-67.79h16.74v67.79h-16.74Z"/>
|
|
25
|
+
<path class="cls-1" d="M337.38,122.27c0,5.95,2.07,7.88,8.16,7.88h9.27l1.8,12.45c-4.7,1.52-13.28,2.35-17.43,2.35-11.9,0-18.4-7.19-18.4-19.78v-37.35h-12.31v-11.48l12.17-.69v-19.51h16.74v19.51h19.92v12.17h-19.92v34.45Z"/>
|
|
26
|
+
<path class="cls-1" d="M380.95,85.74c6.36-7.06,17.85-11.9,27.81-11.9,16.46,0,23.1,13.28,23.1,36.25,0,26.56-8.85,35-26.01,35-8.3,0-16.6-1.94-23.52-6.64.41,4.7.55,9.27.41,14.11v17.85h-16.74v-94.76h13.83l1.11,10.1ZM382.75,128.49c6.78,1.52,11.62,2.9,18.12,2.9,9.96,0,13.69-3.46,13.69-21.44s-3.32-22.27-12.03-22.27c-6.5,0-11.9,3.04-19.78,8.44v32.37Z"/>
|
|
27
|
+
<path class="cls-1" d="M505.18,109.54c0,26.01-8.99,35.69-33.34,35.69s-33.62-9.68-33.62-35.69,9.13-35.83,33.62-35.83,33.34,9.82,33.34,35.83ZM455.8,109.54c0,17.43,3.6,22.27,16.05,22.27s15.91-4.84,15.91-22.27-3.74-22.41-15.91-22.41-16.05,4.84-16.05,22.41Z"/>
|
|
28
|
+
<path class="cls-1" d="M578.64,109.54c0,26.01-8.99,35.69-33.34,35.69s-33.62-9.68-33.62-35.69,9.13-35.83,33.62-35.83,33.34,9.82,33.34,35.83ZM529.25,109.54c0,17.43,3.6,22.27,16.05,22.27s15.91-4.84,15.91-22.27-3.74-22.41-15.91-22.41-16.05,4.84-16.05,22.41Z"/>
|
|
29
|
+
<path class="cls-1" d="M602.16,122.27c-.14,5.39,2.63,7.88,8.02,7.88h5.81l1.94,12.45c-3.04,1.52-10.52,2.35-14.39,2.35-11.21,0-18.26-6.64-18.26-18.54V46.59h16.88v75.67Z"/>
|
|
30
|
+
</g>
|
|
31
|
+
<path class="cls-2" d="M85.52,192.79c-2.6,0-5.1-.62-7.03-1.74L7.04,149.8C3.02,147.49,0,142.25,0,137.61V55.11C0,50.48,3.02,45.24,7.04,42.93L78.48,1.68c3.88-2.24,10.19-2.24,14.07,0l71.45,41.25c4.01,2.32,7.04,7.55,7.04,12.19v82.5c0,4.63-3.02,9.87-7.04,12.19l-71.45,41.25c-1.94,1.12-4.44,1.74-7.04,1.74ZM85.52,8.09c-1.16,0-2.26.24-2.96.65L11.11,49.98c-1.5.86-2.96,3.41-2.96,5.13v82.5c0,1.73,1.47,4.27,2.96,5.13l71.45,41.25c1.4.81,4.53.81,5.93,0l71.45-41.25c1.5-.86,2.96-3.4,2.96-5.13V55.11c0-1.73-1.47-4.27-2.96-5.13L88.48,8.73c-.7-.4-1.81-.65-2.96-.65ZM85.69,165.46h0s59.76-34.67,59.76-34.67v-19.74l-59.76,34.64-25.62-14.79v19.75l25.62,14.81h0s0,0,0,0h0ZM111.28,81.34l-25.6-14.83-25.6,14.83v29.74l25.58,14.8v.03l.02-.02.02.02v-.03l25.58-14.8v-29.61M145.45,61.72l-59.71-34.61-17.07,9.86,59.71,34.62v29.67l17.07-9.87v-29.67ZM25.9,61.77v68.85l17.07,9.88v-68.87l25.77-15.05-17.08-9.86-25.77,15.05Z"/>
|
|
32
|
+
</svg>
|