@bitpoolos/edge-bacnet 1.0.9 → 1.1.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/bacnet_client.js +299 -315
- package/bacnet_device.js +41 -15
- package/bacnet_gateway.html +88 -42
- package/bacnet_gateway.js +80 -27
- package/bacnet_read.html +110 -34
- package/bacnet_server.js +321 -0
- package/bacnet_write.html +14 -9
- package/common.js +34 -3
- package/edge-bacnet-datastore.cfg +0 -0
- package/package.json +1 -1
package/bacnet_read.html
CHANGED
|
@@ -118,9 +118,17 @@
|
|
|
118
118
|
});
|
|
119
119
|
|
|
120
120
|
|
|
121
|
+
},
|
|
122
|
+
addAllDevices() {
|
|
123
|
+
let app = this;
|
|
124
|
+
app.devices.forEach(function (device) {
|
|
125
|
+
app.addAllClicked({"node": device});
|
|
126
|
+
});
|
|
127
|
+
|
|
121
128
|
},
|
|
122
129
|
addAllClicked(slotProps) {
|
|
123
130
|
//update UI
|
|
131
|
+
let app = this;
|
|
124
132
|
|
|
125
133
|
let clone = JSON.parse(JSON.stringify(slotProps.node));
|
|
126
134
|
|
|
@@ -144,48 +152,71 @@
|
|
|
144
152
|
|
|
145
153
|
//update node-red data structure to forward to gateway
|
|
146
154
|
let device = this.deviceList.find(ele => ele.deviceName == slotProps.node.label);
|
|
147
|
-
let
|
|
155
|
+
let deviceAddress = app.getDeviceAddress(device.address);
|
|
156
|
+
let key = `${deviceAddress}-${device.deviceId}`;
|
|
157
|
+
let points = this.pointList[key];
|
|
148
158
|
|
|
149
|
-
if (!this.pointsToRead[
|
|
150
|
-
this.pointsToRead[
|
|
159
|
+
if (!this.pointsToRead[key]) {
|
|
160
|
+
this.pointsToRead[key] = {};
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
for (let pointName in points) {
|
|
154
|
-
let point = this.pointList[
|
|
155
|
-
this.pointsToRead[
|
|
164
|
+
let point = this.pointList[key][pointName];
|
|
165
|
+
this.pointsToRead[key][point.objectName] = point;
|
|
156
166
|
}
|
|
157
167
|
|
|
158
168
|
//force a deploy state
|
|
159
169
|
node.hiddenDeployToggle = !node.prevHiddenToggleState;
|
|
160
170
|
},
|
|
171
|
+
removeAllDevices() {
|
|
172
|
+
let app = this;
|
|
173
|
+
|
|
174
|
+
let clone = JSON.parse(JSON.stringify(app.readDevices));
|
|
175
|
+
|
|
176
|
+
clone.forEach(function (device) {
|
|
177
|
+
app.removeAllClicked({"node": device});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
app.$forceUpdate();
|
|
181
|
+
|
|
182
|
+
},
|
|
161
183
|
removeAllClicked(slotProps) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (
|
|
166
|
-
|
|
184
|
+
let app = this;
|
|
185
|
+
try {
|
|
186
|
+
//update UI
|
|
187
|
+
if (this.readDevices.length > 0) {
|
|
188
|
+
let foundIndex = this.readDevices.findIndex(ele => ele.key == slotProps.node.key && ele.label == slotProps.node.label);
|
|
189
|
+
if (foundIndex !== -1) this.readDevices.splice(foundIndex, 1);
|
|
190
|
+
}
|
|
167
191
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
192
|
+
let deviceSlot = this.devices.find(ele => ele.label == slotProps.node.label);
|
|
193
|
+
if(deviceSlot) {
|
|
194
|
+
deviceSlot.showAdded = false;
|
|
195
|
+
deviceSlot.children.forEach(function(child) {
|
|
196
|
+
child.showAdded = false;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
slotProps.node.showAdded = false;
|
|
200
|
+
this.$forceUpdate();
|
|
201
|
+
|
|
202
|
+
//update node-red data structure
|
|
203
|
+
let device = this.deviceList.find(ele => ele.deviceName == slotProps.node.label);
|
|
204
|
+
let deviceAddress = app.getDeviceAddress(device.address);
|
|
205
|
+
let key = `${deviceAddress}-${device.deviceId}`;
|
|
206
|
+
if (this.pointsToRead[key]) {
|
|
207
|
+
delete this.pointsToRead[key];
|
|
208
|
+
}
|
|
177
209
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
210
|
+
//force a deploy state
|
|
211
|
+
node.hiddenDeployToggle = !node.prevHiddenToggleState;
|
|
212
|
+
|
|
213
|
+
} catch(e){
|
|
214
|
+
console.log("removeAllClicked error: ", e);
|
|
182
215
|
}
|
|
183
216
|
|
|
184
|
-
//force a deploy state
|
|
185
|
-
node.hiddenDeployToggle = !node.prevHiddenToggleState;
|
|
186
217
|
},
|
|
187
218
|
addPointClicked(slotProps) {
|
|
188
|
-
|
|
219
|
+
let app = this;
|
|
189
220
|
//update UI
|
|
190
221
|
let parentDeviceName = slotProps.node.parentDevice;
|
|
191
222
|
let foundDeviceIndex = this.readDevices ? this.readDevices.findIndex(ele => ele.label == parentDeviceName) : -1;
|
|
@@ -215,17 +246,21 @@
|
|
|
215
246
|
|
|
216
247
|
//update node-red data structure
|
|
217
248
|
let device = this.deviceList.find(ele => ele.deviceName == parentDeviceName);
|
|
218
|
-
|
|
219
|
-
|
|
249
|
+
let deviceAddress = app.getDeviceAddress(device.address);
|
|
250
|
+
let key = `${deviceAddress}-${device.deviceId}`;
|
|
251
|
+
|
|
252
|
+
if (!this.pointsToRead[key]) {
|
|
253
|
+
this.pointsToRead[key] = {};
|
|
220
254
|
}
|
|
221
255
|
|
|
222
|
-
let point = this.pointList[
|
|
223
|
-
this.pointsToRead[
|
|
256
|
+
let point = this.pointList[key][slotProps.node.pointName];
|
|
257
|
+
this.pointsToRead[key][point.objectName] = point;
|
|
224
258
|
|
|
225
259
|
//force a deploy state
|
|
226
260
|
node.hiddenDeployToggle = !node.prevHiddenToggleState;
|
|
227
261
|
},
|
|
228
262
|
removePointClicked(slotProps) {
|
|
263
|
+
let app = this;
|
|
229
264
|
//update UI
|
|
230
265
|
let parentDeviceName = slotProps.node.parentDevice;
|
|
231
266
|
let foundDeviceIndex = this.readDevices ? this.readDevices.findIndex(ele => ele.label == parentDeviceName) : -1;
|
|
@@ -247,12 +282,26 @@
|
|
|
247
282
|
|
|
248
283
|
//update node-red data stucture
|
|
249
284
|
let device = this.deviceList.find(ele => ele.deviceName == parentDeviceName);
|
|
250
|
-
let
|
|
251
|
-
|
|
285
|
+
let deviceAddress = app.getDeviceAddress(device.address);
|
|
286
|
+
let key = `${deviceAddress}-${device.deviceId}`;
|
|
287
|
+
let point = this.pointList[key][slotProps.node.pointName];
|
|
288
|
+
if (this.pointsToRead[key][point.objectName]) delete this.pointsToRead[key][point.objectName];
|
|
289
|
+
//if last point is removed, deleted whole entry
|
|
290
|
+
if(Object.keys(this.pointsToRead[key]).length == 0) delete this.pointsToRead[key];
|
|
252
291
|
|
|
253
292
|
//force a deploy state
|
|
254
293
|
node.hiddenDeployToggle = !node.prevHiddenToggleState;
|
|
255
294
|
},
|
|
295
|
+
getDeviceAddress(addr) {
|
|
296
|
+
switch(typeof addr) {
|
|
297
|
+
case "object":
|
|
298
|
+
return addr.address;
|
|
299
|
+
case "string":
|
|
300
|
+
return addr;
|
|
301
|
+
default:
|
|
302
|
+
return addr;
|
|
303
|
+
}
|
|
304
|
+
},
|
|
256
305
|
isDeviceActive(slotProps) {
|
|
257
306
|
let app = this;
|
|
258
307
|
if (((Date.now() - slotProps.node.lastSeen) / 1000) < (app.pollFrequency + 5)) {
|
|
@@ -498,7 +547,7 @@
|
|
|
498
547
|
outline: none !important;
|
|
499
548
|
}
|
|
500
549
|
.p-tree-filter:focus, .p-tree-filter:focus-visible, .p-inputtext:enabled:hover {
|
|
501
|
-
box-shadow: inset 0 0 0 0.15rem #
|
|
550
|
+
box-shadow: inset 0 0 0 0.15rem #dfdcdc !important;
|
|
502
551
|
border-color: transparent !important;
|
|
503
552
|
}
|
|
504
553
|
.bacnetbutton > .pi {
|
|
@@ -510,6 +559,18 @@
|
|
|
510
559
|
.reloadButtonIcon {
|
|
511
560
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
|
|
512
561
|
}
|
|
562
|
+
.removeAllDevicesButton {
|
|
563
|
+
border: none;
|
|
564
|
+
background: none;
|
|
565
|
+
font-size: 14px !important;
|
|
566
|
+
float: right;
|
|
567
|
+
display: flex;
|
|
568
|
+
align-items: center;
|
|
569
|
+
}
|
|
570
|
+
.removeAllDevicesButton:hover {
|
|
571
|
+
background-color: #d5d5d5;
|
|
572
|
+
border-radius: 10px;
|
|
573
|
+
}
|
|
513
574
|
.reloadButton {
|
|
514
575
|
border: none;
|
|
515
576
|
background: none;
|
|
@@ -553,6 +614,14 @@
|
|
|
553
614
|
.buttonGroup {
|
|
554
615
|
padding-left: 7px;
|
|
555
616
|
}
|
|
617
|
+
#read-readList-tab {
|
|
618
|
+
display: flex;
|
|
619
|
+
flex-direction: column;
|
|
620
|
+
}
|
|
621
|
+
.removeAllDevicesDiv {
|
|
622
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
623
|
+
padding-right: 20px;
|
|
624
|
+
}
|
|
556
625
|
|
|
557
626
|
</style>
|
|
558
627
|
|
|
@@ -588,6 +657,9 @@
|
|
|
588
657
|
<button @click="getData()" class="reloadButton" title="Reload Data">
|
|
589
658
|
<i class="pi pi-refresh" style="color: #00AEEF;"></i>
|
|
590
659
|
</button>
|
|
660
|
+
<button @click="addAllDevices()" class="reloadButton" title="Add all devices">
|
|
661
|
+
<i class="pi pi-plus" style="color: #00AEEF;"></i>
|
|
662
|
+
</button>
|
|
591
663
|
</div>
|
|
592
664
|
</div>
|
|
593
665
|
|
|
@@ -655,7 +727,11 @@
|
|
|
655
727
|
*
|
|
656
728
|
-->
|
|
657
729
|
<div id='read-readList-tab' style='display:none'>
|
|
658
|
-
|
|
730
|
+
<div class="removeAllDevicesDiv" >
|
|
731
|
+
<button @click="removeAllDevices()" class="removeAllDevicesButton" title="Remove all devices">
|
|
732
|
+
<i class="pi pi-minus-circle" style="color: #ff0000; padding-right: 5px;"> </i><a style="color: #ff0000;">Remove All Devices</a>
|
|
733
|
+
</button>
|
|
734
|
+
</div>
|
|
659
735
|
|
|
660
736
|
<p-tree :value="readDevices">
|
|
661
737
|
<template #device="slotProps">
|
package/bacnet_server.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
const bacnet = require('./resources/node-bacnet/index.js');
|
|
2
|
+
const baEnum = bacnet.enum;
|
|
3
|
+
const { ToadScheduler, SimpleIntervalJob, Task } = require('toad-scheduler')
|
|
4
|
+
|
|
5
|
+
class BacnetServer {
|
|
6
|
+
|
|
7
|
+
constructor(client, deviceId, rebuildSchedule, nodeRedVersion){
|
|
8
|
+
let that = this;
|
|
9
|
+
that.bacnetClient = client;
|
|
10
|
+
that.rebuildScheduleSeconds = rebuildSchedule;
|
|
11
|
+
that.scheduler = new ToadScheduler();
|
|
12
|
+
that.objectIdNumber = 1;
|
|
13
|
+
that.nodeRedVersion = nodeRedVersion;
|
|
14
|
+
that.deviceId = deviceId;
|
|
15
|
+
that.objectList = [
|
|
16
|
+
{value: {type: baEnum.ObjectType.DEVICE, instance: that.deviceId}, type: 12}
|
|
17
|
+
];
|
|
18
|
+
that.objectStore = {
|
|
19
|
+
[baEnum.ObjectType.DEVICE]: {
|
|
20
|
+
[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER]: [{value: {type: baEnum.ObjectType.DEVICE, instance: that.deviceId}, type: 12}], // OBJECT_IDENTIFIER
|
|
21
|
+
[baEnum.PropertyIdentifier.OBJECT_LIST]: that.objectList, // OBJECT_IDENTIFIER
|
|
22
|
+
[baEnum.PropertyIdentifier.OBJECT_NAME]: [{value: 'Bitpool Edge BACnet Gateway', type: 7}], // OBJECT_NAME
|
|
23
|
+
[baEnum.PropertyIdentifier.OBJECT_TYPE]: [{value: 8, type: 9}], // OBJECT_TYPE
|
|
24
|
+
[baEnum.PropertyIdentifier.DESCRIPTION]: [{value: 'Bitpool Edge BACnet gateway', type: 7}], // DESCRIPTION
|
|
25
|
+
[baEnum.PropertyIdentifier.SYSTEM_STATUS]: [{value: 0, type: 9}], // SYSTEM_STATUS
|
|
26
|
+
[baEnum.PropertyIdentifier.VENDOR_NAME]: [{value: "Bitpool", type: 7}], //VENDOR_NAME
|
|
27
|
+
[baEnum.PropertyIdentifier.VENDOR_IDENTIFIER]: [{value: 1401, type: 7}], //VENDOR_IDENTIFIER
|
|
28
|
+
[baEnum.PropertyIdentifier.MODEL_NAME]: [{value: "bitpool-edge", type: 7}], //MODEL_NAME
|
|
29
|
+
[baEnum.PropertyIdentifier.FIRMWARE_REVISION]: [{value: "Node-Red " + that.nodeRedVersion, type: 7}], //FIRMWARE_REVISION
|
|
30
|
+
},
|
|
31
|
+
[baEnum.ObjectType.ANALOG_VALUE]: [],
|
|
32
|
+
[baEnum.ObjectType.CHARACTERSTRING_VALUE]: []
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
that.bacnetClient.client.on('whoIs', (device) => {
|
|
36
|
+
that.bacnetClient.client.iAmResponse(that.bacnetClient.broadCastAddr, that.deviceId, baEnum.Segmentation.SEGMENTED_BOTH, 27823);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
that.bacnetClient.client.on('readPropertyMultiple', (data) => {
|
|
40
|
+
|
|
41
|
+
let senderAddress = data.header.sender.address;
|
|
42
|
+
let requestProps = data.payload.properties;
|
|
43
|
+
let responseObject = [];
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
if(requestProps) {
|
|
47
|
+
|
|
48
|
+
for(let i = 0; i < requestProps.length; i++) {
|
|
49
|
+
|
|
50
|
+
let prop = requestProps[i].properties[0].id;
|
|
51
|
+
let type = requestProps[i].objectId.type;
|
|
52
|
+
let instance = requestProps[i].objectId.instance;
|
|
53
|
+
let foundObject = that.getObjectMultiple(type, prop, instance, requestProps[i].properties);
|
|
54
|
+
|
|
55
|
+
if(foundObject !== null && foundObject !== undefined && foundObject !== "undefined") {
|
|
56
|
+
responseObject.push({objectId: {type: type, instance: instance}, values: foundObject});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if(i == requestProps.length - 1) {
|
|
60
|
+
if(responseObject.length > 0) {
|
|
61
|
+
that.bacnetClient.client.readPropertyMultipleResponse(senderAddress, data.invokeId, responseObject);
|
|
62
|
+
} else {
|
|
63
|
+
that.bacnetClient.client.errorResponse(
|
|
64
|
+
data.address,
|
|
65
|
+
baEnum.ConfirmedServiceChoice.READ_PROPERTY_MULTIPLE,
|
|
66
|
+
data.invokeId,
|
|
67
|
+
baEnum.ErrorClass.PROPERTY,
|
|
68
|
+
baEnum.ErrorCode.UNKNOWN_PROPERTY
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
} catch(e) {
|
|
76
|
+
console.log("Bacnet server readPropertyMultiple error: ", e);
|
|
77
|
+
|
|
78
|
+
that.bacnetClient.client.errorResponse(
|
|
79
|
+
data.address,
|
|
80
|
+
baEnum.ConfirmedServiceChoice.READ_PROPERTY_MULTIPLE,
|
|
81
|
+
data.invokeId,
|
|
82
|
+
baEnum.ErrorClass.PROPERTY,
|
|
83
|
+
baEnum.ErrorCode.UNKNOWN_PROPERTY
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
that.bacnetClient.client.on('readProperty', (data) => {
|
|
89
|
+
try {
|
|
90
|
+
let objectId = data.payload.objectId.type;
|
|
91
|
+
let objectInstance = data.payload.objectId.instance;
|
|
92
|
+
let propId = data.payload.property.id.toString();
|
|
93
|
+
let responseObj = that.getObject(objectId, propId, objectInstance);
|
|
94
|
+
|
|
95
|
+
if(responseObj !== null && responseObj !== undefined && typeof responseObj !== "undefined") {
|
|
96
|
+
that.bacnetClient.client.readPropertyResponse(data.header.sender.address, data.invokeId, objectId, data.payload.property, responseObj);
|
|
97
|
+
} else {
|
|
98
|
+
that.bacnetClient.client.errorResponse(
|
|
99
|
+
data.address,
|
|
100
|
+
baEnum.ConfirmedServiceChoice.READ_PROPERTY,
|
|
101
|
+
data.invokeId,
|
|
102
|
+
baEnum.ErrorClass.PROPERTY,
|
|
103
|
+
baEnum.ErrorCode.UNKNOWN_PROPERTY
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
} catch(e) {
|
|
107
|
+
console.log("Local BACnet device readProperty error: ", e);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
//do initial iAm broadcast when BACnet server starts
|
|
113
|
+
that.bacnetClient.client.iAmResponse(that.bacnetClient.broadCastAddr, that.deviceId, baEnum.Segmentation.SEGMENTED_BOTH, 27823);
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
//add clear server job to schedule
|
|
117
|
+
const task = new Task('simple task', () => {
|
|
118
|
+
that.clearServerPoints();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const job = new SimpleIntervalJob({ seconds: parseInt(that.rebuildScheduleSeconds), }, task)
|
|
122
|
+
|
|
123
|
+
that.scheduler.addSimpleIntervalJob(job)
|
|
124
|
+
|
|
125
|
+
} catch (error) {
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
addObject(name, value) {
|
|
131
|
+
let that = this;
|
|
132
|
+
if(name && value) {
|
|
133
|
+
let formattedName = name.replaceAll('.', '');
|
|
134
|
+
let objectType = that.getBacnetObjectType(value);
|
|
135
|
+
|
|
136
|
+
if(objectType == "number") {
|
|
137
|
+
let foundIndex = that.objectStore[baEnum.ObjectType.ANALOG_VALUE].findIndex(ele => ele[baEnum.PropertyIdentifier.OBJECT_NAME][0].value == formattedName);
|
|
138
|
+
if(foundIndex == -1) {
|
|
139
|
+
let objectId = that.getObjectIdentifier();
|
|
140
|
+
that.objectStore[baEnum.ObjectType.ANALOG_VALUE].push({
|
|
141
|
+
[baEnum.PropertyIdentifier.OBJECT_NAME]: [{value: formattedName, type: 7}],
|
|
142
|
+
[baEnum.PropertyIdentifier.OBJECT_TYPE]: [{value: baEnum.ObjectType.ANALOG_VALUE, type: 9}],
|
|
143
|
+
[baEnum.PropertyIdentifier.DESCRIPTION]: [{value: '', type: 7}],
|
|
144
|
+
[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER]: [{value: {type: baEnum.ObjectType.ANALOG_VALUE, instance: objectId}, type: 12}],
|
|
145
|
+
[baEnum.PropertyIdentifier.PRESENT_VALUE]: [{value: value, type: 4}],
|
|
146
|
+
[baEnum.PropertyIdentifier.PROPERTY_LIST]:
|
|
147
|
+
[
|
|
148
|
+
{value: baEnum.PropertyIdentifier.OBJECT_NAME, type: 9 },
|
|
149
|
+
{value: baEnum.PropertyIdentifier.OBJECT_TYPE, type: 9 },
|
|
150
|
+
{value: baEnum.PropertyIdentifier.DESCRIPTION, type: 9 },
|
|
151
|
+
{value: baEnum.PropertyIdentifier.OBJECT_IDENTIFIER, type: 9 },
|
|
152
|
+
{value: baEnum.PropertyIdentifier.PROPERTY_LIST, type: 9 },
|
|
153
|
+
{value: baEnum.PropertyIdentifier.PRESENT_VALUE, type: 9 }
|
|
154
|
+
],
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
that.objectList.push({value: {type: baEnum.ObjectType.ANALOG_VALUE, instance: objectId}, type: 12})
|
|
158
|
+
} else if(foundIndex !== -1) {
|
|
159
|
+
|
|
160
|
+
let foundObject = that.objectStore[baEnum.ObjectType.ANALOG_VALUE][foundIndex];
|
|
161
|
+
foundObject[baEnum.PropertyIdentifier.PRESENT_VALUE][0].value = value;
|
|
162
|
+
}
|
|
163
|
+
} else if(objectType == "string") {
|
|
164
|
+
let foundIndex = that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE].findIndex(ele => ele[baEnum.PropertyIdentifier.OBJECT_NAME][0].value == formattedName);
|
|
165
|
+
if(foundIndex == -1) {
|
|
166
|
+
let objectId = that.getObjectIdentifier();
|
|
167
|
+
that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE].push({
|
|
168
|
+
[baEnum.PropertyIdentifier.OBJECT_NAME]: [{value: formattedName, type: 7}],
|
|
169
|
+
[baEnum.PropertyIdentifier.OBJECT_TYPE]: [{value: baEnum.ObjectType.CHARACTERSTRING_VALUE, type: 9}],
|
|
170
|
+
[baEnum.PropertyIdentifier.DESCRIPTION]: [{value: '', type: 7}],
|
|
171
|
+
[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER]: [{value: {type: baEnum.ObjectType.CHARACTERSTRING_VALUE, instance: objectId}, type: 12}],
|
|
172
|
+
[baEnum.PropertyIdentifier.PRESENT_VALUE]: [{value: value, type: 7}],
|
|
173
|
+
[baEnum.PropertyIdentifier.PROPERTY_LIST]:
|
|
174
|
+
[
|
|
175
|
+
{value: baEnum.PropertyIdentifier.OBJECT_NAME, type: 9 },
|
|
176
|
+
{value: baEnum.PropertyIdentifier.OBJECT_TYPE, type: 9 },
|
|
177
|
+
{value: baEnum.PropertyIdentifier.DESCRIPTION, type: 9 },
|
|
178
|
+
{value: baEnum.PropertyIdentifier.OBJECT_IDENTIFIER, type: 9 },
|
|
179
|
+
{value: baEnum.PropertyIdentifier.PROPERTY_LIST, type: 9 },
|
|
180
|
+
{value: baEnum.PropertyIdentifier.PRESENT_VALUE, type: 9 }
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
that.objectList.push({value: {type: baEnum.ObjectType.CHARACTERSTRING_VALUE, instance: objectId}, type: 12})
|
|
185
|
+
} else if(foundIndex !== -1) {
|
|
186
|
+
let foundObject = that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE][foundIndex];
|
|
187
|
+
foundObject[baEnum.PropertyIdentifier.PRESENT_VALUE][0].value = value;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
getObject(objectId, propId, instance) {
|
|
194
|
+
let that = this;
|
|
195
|
+
let objectGroup = that.objectStore[objectId];
|
|
196
|
+
|
|
197
|
+
if(Array.isArray(objectGroup)) {
|
|
198
|
+
for(let i = 0; i < objectGroup.length; i++) {
|
|
199
|
+
let object = objectGroup[i];
|
|
200
|
+
if(object[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER][0].value.instance == instance) {
|
|
201
|
+
let requestedProperty = object[propId];
|
|
202
|
+
if(requestedProperty !== null && requestedProperty !== undefined && typeof requestedProperty !== "undefined") {
|
|
203
|
+
return requestedProperty;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
return objectGroup[propId];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
getObjectMultiple(objectId, propId, instance, properties) {
|
|
215
|
+
let that = this;
|
|
216
|
+
let objectGroup = that.objectStore[objectId];
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
|
|
220
|
+
if(Array.isArray(objectGroup)) {
|
|
221
|
+
for(let i = 0; i < objectGroup.length; i++) {
|
|
222
|
+
let object = objectGroup[i];
|
|
223
|
+
if(object[baEnum.PropertyIdentifier.OBJECT_IDENTIFIER][0].value.instance == instance) {
|
|
224
|
+
if(propId == baEnum.PropertyIdentifier.ALL) {
|
|
225
|
+
let propList = [];
|
|
226
|
+
let keys = Object.keys(object);
|
|
227
|
+
keys.forEach(function(key) {
|
|
228
|
+
propList.push({property: {id: key, index: 0xFFFFFFFF}, value: object[key]});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
return propList;
|
|
232
|
+
|
|
233
|
+
} else if(properties && properties.length > 1) {
|
|
234
|
+
let propList = [];
|
|
235
|
+
properties.forEach(function(p) {
|
|
236
|
+
if(object[p.id]){
|
|
237
|
+
propList.push({property: {id: p.id, index: 0xFFFFFFFF}, value: object[p.id]});
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
return propList;
|
|
242
|
+
|
|
243
|
+
} else {
|
|
244
|
+
return [{property: {id: propId, index: 0xFFFFFFFF}, value: object[propId]}];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
if(propId == baEnum.PropertyIdentifier.ALL) {
|
|
250
|
+
let propList = [];
|
|
251
|
+
let keys = Object.keys(objectGroup);
|
|
252
|
+
keys.forEach(function(key) {
|
|
253
|
+
propList.push({property: {id: key, index: 0xFFFFFFFF}, value: objectGroup[key]});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return propList;
|
|
257
|
+
|
|
258
|
+
} else if(properties && properties.length > 1) {
|
|
259
|
+
let propList = [];
|
|
260
|
+
properties.forEach(function(p) {
|
|
261
|
+
if(objectGroup[p.id]){
|
|
262
|
+
propList.push({property: {id: p.id, index: 0xFFFFFFFF}, value: objectGroup[p.id]});
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
return propList;
|
|
267
|
+
|
|
268
|
+
} else {
|
|
269
|
+
return [{property: {id: propId, index: 0xFFFFFFFF}, value: objectGroup[propId]}];
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
} catch(e){
|
|
274
|
+
//console.log("properties error: ", e);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
clearServerPoints() {
|
|
281
|
+
let that = this;
|
|
282
|
+
|
|
283
|
+
that.objectStore[baEnum.ObjectType.ANALOG_VALUE] = [];
|
|
284
|
+
that.objectStore[baEnum.ObjectType.CHARACTERSTRING_VALUE] = [];
|
|
285
|
+
|
|
286
|
+
that.objectList = [
|
|
287
|
+
{value: {type: baEnum.ObjectType.DEVICE, instance: that.deviceId}, type: 12}
|
|
288
|
+
];
|
|
289
|
+
|
|
290
|
+
that.objectIdNumber = 1;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
getRandomArbitrary(min, max) {
|
|
294
|
+
return Math.random() * (max - min) + min;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
getBacnetObjectType(value) {
|
|
298
|
+
let type = typeof value;
|
|
299
|
+
|
|
300
|
+
switch (type) {
|
|
301
|
+
case "string":
|
|
302
|
+
return "string"
|
|
303
|
+
case "number":
|
|
304
|
+
return "number"
|
|
305
|
+
default:
|
|
306
|
+
return null
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
getObjectIdentifier() {
|
|
311
|
+
let that = this;
|
|
312
|
+
let objectId = that.objectIdNumber;
|
|
313
|
+
that.objectIdNumber++;
|
|
314
|
+
|
|
315
|
+
return objectId;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
module.exports = { BacnetServer };
|
package/bacnet_write.html
CHANGED
|
@@ -109,15 +109,16 @@
|
|
|
109
109
|
|
|
110
110
|
//update node-red data structure to forward to gateway
|
|
111
111
|
let device = this.deviceList.find(ele => ele.deviceName == slotProps.node.label);
|
|
112
|
-
let
|
|
112
|
+
let key = `${device.address}-${device.deviceId}`;
|
|
113
|
+
let points = this.pointList[key];
|
|
113
114
|
|
|
114
|
-
if (!this.pointsToWrite[
|
|
115
|
-
this.pointsToWrite[
|
|
115
|
+
if (!this.pointsToWrite[key] || typeof this.pointsToWrite[key] == 'undefined') {
|
|
116
|
+
this.pointsToWrite[key] = {};
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
for (let pointName in points) {
|
|
119
|
-
let point = this.pointList[
|
|
120
|
-
this.pointsToWrite[
|
|
120
|
+
let point = this.pointList[key][pointName];
|
|
121
|
+
this.pointsToWrite[key][point.objectName] = point;
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
//force a deploy state
|
|
@@ -133,8 +134,9 @@
|
|
|
133
134
|
|
|
134
135
|
//update node-red data structure
|
|
135
136
|
let device = this.deviceList.find(ele => ele.deviceName == slotProps.node.label);
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
let key = `${device.address}-${device.deviceId}`;
|
|
138
|
+
if (this.pointsToWrite[key]) {
|
|
139
|
+
delete this.pointsToWrite[key];
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
//force a deploy state
|
|
@@ -170,8 +172,10 @@
|
|
|
170
172
|
|
|
171
173
|
//update node-red data structure
|
|
172
174
|
let device = this.deviceList.find(ele => ele.deviceName == parentDeviceName);
|
|
173
|
-
let
|
|
175
|
+
let key = `${device.address}-${device.deviceId}`;
|
|
176
|
+
let point = this.pointList[key][slotProps.node.pointName];
|
|
174
177
|
point.deviceId = device.deviceId;
|
|
178
|
+
point.deviceAddress = device.address;
|
|
175
179
|
|
|
176
180
|
if (!this.pointsToWrite || this.pointsToWrite.length == 'undefined') {
|
|
177
181
|
this.pointsToWrite = [];
|
|
@@ -208,7 +212,8 @@
|
|
|
208
212
|
|
|
209
213
|
//update node-red data stucture
|
|
210
214
|
let device = this.deviceList.find(ele => ele.deviceName == parentDeviceName);
|
|
211
|
-
let
|
|
215
|
+
let key = `${device.address}-${device.deviceId}`;
|
|
216
|
+
let point = this.pointList[key][slotProps.node.pointName];
|
|
212
217
|
point.deviceId = device.deviceId;
|
|
213
218
|
|
|
214
219
|
let foundIndex = this.pointsToWrite.findIndex(ele =>
|