@bitpoolos/edge-bacnet 1.0.6 → 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.
Files changed (181) hide show
  1. package/bacnet_client.js +650 -233
  2. package/bacnet_device.js +65 -16
  3. package/bacnet_gateway.html +242 -99
  4. package/bacnet_gateway.js +211 -27
  5. package/bacnet_object.js +1 -1
  6. package/bacnet_read.html +211 -133
  7. package/bacnet_read.js +24 -24
  8. package/bacnet_server.js +321 -0
  9. package/bacnet_write.html +24 -15
  10. package/bacnet_write.js +0 -2
  11. package/common.js +95 -9
  12. package/edge-bacnet-datastore.cfg +0 -0
  13. package/package.json +6 -4
  14. package/resources/confirmationservice.min.js +1 -0
  15. package/resources/confirmdialog.min.js +1 -0
  16. package/resources/fonts/primeicons.woff2 +0 -0
  17. package/resources/node-bacnet/CHANGELOG.md +481 -0
  18. package/resources/{bacstack → node-bacnet}/LICENSE.md +3 -1
  19. package/resources/node-bacnet/README.md +91 -0
  20. package/resources/node-bacnet/docs/Client.html +4422 -0
  21. package/resources/node-bacnet/docs/bacnet-icon-quad.png +0 -0
  22. package/resources/node-bacnet/docs/bacnet-icon-quad128.png +0 -0
  23. package/resources/node-bacnet/docs/bacnet-icon-quad64.png +0 -0
  24. package/resources/node-bacnet/docs/bacnet-icon-small.xcf +0 -0
  25. package/resources/node-bacnet/docs/bacnet-icon.xcf +0 -0
  26. package/resources/node-bacnet/docs/bacnet.html +7032 -0
  27. package/resources/node-bacnet/docs/client.js.html +1759 -0
  28. package/resources/node-bacnet/docs/enum.js.html +2530 -0
  29. package/resources/node-bacnet/docs/global.html +2068 -0
  30. package/resources/node-bacnet/docs/images/mocha-logo.svg +65 -0
  31. package/resources/node-bacnet/docs/index.html +283 -0
  32. package/resources/node-bacnet/docs/scripts/collapse.js +11 -0
  33. package/resources/node-bacnet/docs/scripts/jquery-3.1.1.min.js +4 -0
  34. package/resources/node-bacnet/docs/scripts/linenumber.js +26 -0
  35. package/resources/node-bacnet/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  36. package/resources/node-bacnet/docs/scripts/prettify/lang-css.js +2 -0
  37. package/resources/node-bacnet/docs/scripts/prettify/prettify.js +28 -0
  38. package/resources/node-bacnet/docs/scripts/search.js +47 -0
  39. package/resources/node-bacnet/docs/services_i-am.js.html +157 -0
  40. package/resources/node-bacnet/docs/services_time-sync.js.html +118 -0
  41. package/resources/node-bacnet/docs/services_who-is.js.html +138 -0
  42. package/resources/node-bacnet/docs/styles/jsdoc.css +683 -0
  43. package/resources/node-bacnet/docs/styles/prettify.css +82 -0
  44. package/resources/node-bacnet/examples/discover-devices.js +66 -0
  45. package/resources/node-bacnet/examples/read-device.js +510 -0
  46. package/resources/node-bacnet/examples/subscribe-cov.js +75 -0
  47. package/resources/{bacstack → node-bacnet}/index.js +3 -0
  48. package/resources/{bacstack → node-bacnet}/lib/apdu.js +56 -39
  49. package/resources/{bacstack → node-bacnet}/lib/asn1.js +550 -532
  50. package/resources/node-bacnet/lib/bvlc.js +90 -0
  51. package/resources/node-bacnet/lib/client.js +1695 -0
  52. package/resources/node-bacnet/lib/enum.js +2463 -0
  53. package/resources/node-bacnet/lib/npdu.js +123 -0
  54. package/resources/{bacstack → node-bacnet}/lib/services/add-list-element.js +12 -6
  55. package/resources/{bacstack → node-bacnet}/lib/services/alarm-acknowledge.js +3 -3
  56. package/resources/{bacstack → node-bacnet}/lib/services/alarm-summary.js +5 -4
  57. package/resources/{bacstack → node-bacnet}/lib/services/atomic-read-file.js +49 -26
  58. package/resources/{bacstack → node-bacnet}/lib/services/atomic-write-file.js +40 -23
  59. package/resources/{bacstack → node-bacnet}/lib/services/cov-notify.js +33 -17
  60. package/resources/{bacstack → node-bacnet}/lib/services/create-object.js +23 -13
  61. package/resources/{bacstack → node-bacnet}/lib/services/delete-object.js +7 -2
  62. package/resources/{bacstack → node-bacnet}/lib/services/device-communication-control.js +8 -3
  63. package/resources/{bacstack → node-bacnet}/lib/services/error.js +7 -0
  64. package/resources/{bacstack → node-bacnet}/lib/services/event-information.js +10 -9
  65. package/resources/{bacstack → node-bacnet}/lib/services/event-notify-data.js +38 -16
  66. package/resources/{bacstack → node-bacnet}/lib/services/get-enrollment-summary.js +24 -11
  67. package/resources/{bacstack → node-bacnet}/lib/services/get-event-information.js +28 -13
  68. package/resources/node-bacnet/lib/services/i-am.js +90 -0
  69. package/resources/{bacstack/lib/services/i-have-broadcast.js → node-bacnet/lib/services/i-have.js} +3 -2
  70. package/resources/{bacstack → node-bacnet}/lib/services/index.js +7 -4
  71. package/resources/{bacstack → node-bacnet}/lib/services/life-safety-operation.js +3 -2
  72. package/resources/{bacstack → node-bacnet}/lib/services/private-transfer.js +3 -2
  73. package/resources/{bacstack → node-bacnet}/lib/services/read-property-multiple.js +11 -6
  74. package/resources/{bacstack → node-bacnet}/lib/services/read-property.js +42 -24
  75. package/resources/{bacstack → node-bacnet}/lib/services/read-range.js +37 -27
  76. package/resources/node-bacnet/lib/services/register-foreign-device.js +18 -0
  77. package/resources/{bacstack → node-bacnet}/lib/services/reinitialize-device.js +9 -4
  78. package/resources/{bacstack → node-bacnet}/lib/services/subscribe-cov.js +9 -4
  79. package/resources/{bacstack → node-bacnet}/lib/services/subscribe-property.js +18 -8
  80. package/resources/{bacstack → node-bacnet}/lib/services/time-sync.js +28 -5
  81. package/resources/{bacstack → node-bacnet}/lib/services/who-has.js +3 -3
  82. package/resources/{bacstack → node-bacnet}/lib/services/who-is.js +42 -9
  83. package/resources/{bacstack → node-bacnet}/lib/services/write-property-multiple.js +33 -16
  84. package/resources/{bacstack → node-bacnet}/lib/services/write-property.js +23 -13
  85. package/resources/node-bacnet/lib/transport.js +82 -0
  86. package/resources/node-bacnet/package.json +92 -0
  87. package/resources/primeicons.css +90 -2
  88. package/resources/bacstack/.codeclimate.yml +0 -15
  89. package/resources/bacstack/.dockerignore +0 -5
  90. package/resources/bacstack/.editorconfig +0 -13
  91. package/resources/bacstack/.eslintrc.yml +0 -13
  92. package/resources/bacstack/.github/ISSUE_TEMPLATE.md +0 -26
  93. package/resources/bacstack/.github/PULL_REQUEST_TEMPLATE.md +0 -14
  94. package/resources/bacstack/.github/workflows/ci.yml +0 -39
  95. package/resources/bacstack/.jscsrc +0 -8
  96. package/resources/bacstack/.jshintrc +0 -50
  97. package/resources/bacstack/.travis.yml +0 -27
  98. package/resources/bacstack/CHANGELOG.md +0 -232
  99. package/resources/bacstack/CODE_OF_CONDUCT.md +0 -74
  100. package/resources/bacstack/CONTRIBUTING.md +0 -77
  101. package/resources/bacstack/Dockerfile +0 -15
  102. package/resources/bacstack/FAQ.md +0 -64
  103. package/resources/bacstack/README.md +0 -157
  104. package/resources/bacstack/docker-compose.yml +0 -9
  105. package/resources/bacstack/lib/adpu.js +0 -190
  106. package/resources/bacstack/lib/bvlc.js +0 -43
  107. package/resources/bacstack/lib/client.js +0 -1028
  108. package/resources/bacstack/lib/enum.js +0 -1314
  109. package/resources/bacstack/lib/npdu.js +0 -119
  110. package/resources/bacstack/lib/services/i-am-broadcast.js +0 -51
  111. package/resources/bacstack/lib/services.js +0 -1963
  112. package/resources/bacstack/lib/transport.js +0 -52
  113. package/resources/bacstack/package-lock.json +0 -7974
  114. package/resources/bacstack/package.json +0 -84
  115. package/resources/bacstack/test/compliance/who-is.spec.js +0 -37
  116. package/resources/bacstack/test/integration/acknowledge-alarm.spec.js +0 -14
  117. package/resources/bacstack/test/integration/add-list-element.spec.js +0 -16
  118. package/resources/bacstack/test/integration/confirmed-event-notification.spec.js +0 -30
  119. package/resources/bacstack/test/integration/confirmed-private-transfer.spec.js +0 -15
  120. package/resources/bacstack/test/integration/create-object.spec.js +0 -16
  121. package/resources/bacstack/test/integration/delete-object.spec.js +0 -14
  122. package/resources/bacstack/test/integration/device-communication-control.spec.js +0 -14
  123. package/resources/bacstack/test/integration/get-alarm-summary.spec.js +0 -14
  124. package/resources/bacstack/test/integration/get-enrollment-summary.spec.js +0 -15
  125. package/resources/bacstack/test/integration/get-event-information.spec.js +0 -14
  126. package/resources/bacstack/test/integration/read-file.spec.js +0 -14
  127. package/resources/bacstack/test/integration/read-property-multiple.spec.js +0 -110
  128. package/resources/bacstack/test/integration/read-property.spec.js +0 -14
  129. package/resources/bacstack/test/integration/read-range.spec.js +0 -14
  130. package/resources/bacstack/test/integration/reinitialize-sevice.spec.js +0 -14
  131. package/resources/bacstack/test/integration/remove-list-element.spec.js +0 -16
  132. package/resources/bacstack/test/integration/subscribe-cov.spec.js +0 -14
  133. package/resources/bacstack/test/integration/subscribe-property.spec.js +0 -14
  134. package/resources/bacstack/test/integration/time-sync-utc.spec.js +0 -10
  135. package/resources/bacstack/test/integration/time-sync.spec.js +0 -10
  136. package/resources/bacstack/test/integration/unconfirmed-event-notification.spec.js +0 -28
  137. package/resources/bacstack/test/integration/unconfirmed-private-transfer.spec.js +0 -11
  138. package/resources/bacstack/test/integration/utils.js +0 -30
  139. package/resources/bacstack/test/integration/who-is.spec.js +0 -17
  140. package/resources/bacstack/test/integration/write-file.spec.js +0 -14
  141. package/resources/bacstack/test/integration/write-property-multiple.spec.js +0 -19
  142. package/resources/bacstack/test/integration/write-property.spec.js +0 -14
  143. package/resources/bacstack/test/unit/apdu.spec.js +0 -162
  144. package/resources/bacstack/test/unit/asn1.spec.js +0 -39
  145. package/resources/bacstack/test/unit/bacnet-apdu.spec.js +0 -161
  146. package/resources/bacstack/test/unit/bacnet-asn1.spec.js +0 -32
  147. package/resources/bacstack/test/unit/bacnet-bvlc.spec.js +0 -57
  148. package/resources/bacstack/test/unit/bacnet-npdu.spec.js +0 -118
  149. package/resources/bacstack/test/unit/bacnet-services.spec.js +0 -2052
  150. package/resources/bacstack/test/unit/bvlc.spec.js +0 -58
  151. package/resources/bacstack/test/unit/npdu.spec.js +0 -119
  152. package/resources/bacstack/test/unit/service-add-list-element.spec.js +0 -24
  153. package/resources/bacstack/test/unit/service-alarm-acknowledge.spec.js +0 -71
  154. package/resources/bacstack/test/unit/service-alarm-summary.spec.js +0 -22
  155. package/resources/bacstack/test/unit/service-atomic-read-file.spec.js +0 -54
  156. package/resources/bacstack/test/unit/service-atomic-write-file.spec.js +0 -56
  157. package/resources/bacstack/test/unit/service-cov-notify.spec.js +0 -98
  158. package/resources/bacstack/test/unit/service-create-object.spec.js +0 -90
  159. package/resources/bacstack/test/unit/service-delete-object.spec.js +0 -17
  160. package/resources/bacstack/test/unit/service-device-communication-control.spec.js +0 -29
  161. package/resources/bacstack/test/unit/service-error.spec.js +0 -17
  162. package/resources/bacstack/test/unit/service-event-information.spec.js +0 -48
  163. package/resources/bacstack/test/unit/service-event-notify-data.spec.js +0 -310
  164. package/resources/bacstack/test/unit/service-get-enrollment-summary.spec.js +0 -45
  165. package/resources/bacstack/test/unit/service-get-event-information.spec.js +0 -62
  166. package/resources/bacstack/test/unit/service-i-am.spec.js +0 -19
  167. package/resources/bacstack/test/unit/service-i-have-broadcast.spec.js +0 -18
  168. package/resources/bacstack/test/unit/service-life-safety-operation.spec.js +0 -19
  169. package/resources/bacstack/test/unit/service-private-transfer.spec.js +0 -18
  170. package/resources/bacstack/test/unit/service-read-property-multiple.spec.js +0 -131
  171. package/resources/bacstack/test/unit/service-read-property.spec.js +0 -541
  172. package/resources/bacstack/test/unit/service-read-range.spec.js +0 -97
  173. package/resources/bacstack/test/unit/service-reinitialize-device.spec.js +0 -27
  174. package/resources/bacstack/test/unit/service-subscribe-cov.spec.js +0 -32
  175. package/resources/bacstack/test/unit/service-subscribe-property.spec.js +0 -50
  176. package/resources/bacstack/test/unit/service-time-sync.spec.js +0 -18
  177. package/resources/bacstack/test/unit/service-who-has.spec.js +0 -33
  178. package/resources/bacstack/test/unit/service-who-is.spec.js +0 -17
  179. package/resources/bacstack/test/unit/service-write-property-multiple.spec.js +0 -143
  180. package/resources/bacstack/test/unit/service-write-property.spec.js +0 -198
  181. package/resources/bacstack/test/unit/utils.js +0 -6
@@ -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 points = this.pointList[slotProps.node.ipAddr];
112
+ let key = `${device.address}-${device.deviceId}`;
113
+ let points = this.pointList[key];
113
114
 
114
- if (!this.pointsToWrite[device.address] || typeof this.pointsToWrite[device.address] == 'undefined') {
115
- this.pointsToWrite[device.address] = {};
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[device.address][pointName];
120
- this.pointsToWrite[device.address][point.objectName] = point;
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
- if (this.pointsToWrite[device.address]) {
137
- delete this.pointsToWrite[device.address];
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,7 +172,9 @@
170
172
 
171
173
  //update node-red data structure
172
174
  let device = this.deviceList.find(ele => ele.deviceName == parentDeviceName);
173
- let point = this.pointList[device.address][slotProps.node.label];
175
+ let key = `${device.address}-${device.deviceId}`;
176
+ let point = this.pointList[key][slotProps.node.pointName];
177
+ point.deviceId = device.deviceId;
174
178
  point.deviceAddress = device.address;
175
179
 
176
180
  if (!this.pointsToWrite || this.pointsToWrite.length == 'undefined') {
@@ -178,7 +182,7 @@
178
182
  }
179
183
 
180
184
  let found = this.pointsToWrite.find(ele =>
181
- ele.deviceAddress == point.deviceAddress &&
185
+ ele.deviceId == point.deviceId &&
182
186
  ele.objectName == point.objectName &&
183
187
  ele.meta.arrayIndex == point.meta.arrayIndex &&
184
188
  ele.meta.objectId.instance == point.meta.objectId.instance &&
@@ -208,11 +212,12 @@
208
212
 
209
213
  //update node-red data stucture
210
214
  let device = this.deviceList.find(ele => ele.deviceName == parentDeviceName);
211
- let point = this.pointList[parentDeviceName][slotProps.node.label];
212
- point.deviceAddress = device.address;
215
+ let key = `${device.address}-${device.deviceId}`;
216
+ let point = this.pointList[key][slotProps.node.pointName];
217
+ point.deviceId = device.deviceId;
213
218
 
214
219
  let foundIndex = this.pointsToWrite.findIndex(ele =>
215
- ele.deviceAddress == point.deviceAddress &&
220
+ ele.deviceId == point.deviceId &&
216
221
  ele.objectName == point.objectName &&
217
222
  ele.meta.arrayIndex == point.meta.arrayIndex &&
218
223
  ele.meta.objectId.instance == point.meta.objectId.instance &&
@@ -343,10 +348,9 @@
343
348
  .pointLabel {
344
349
  font-weight: 400;
345
350
  }
346
- .p-treenode-children {
347
- /* background-color: #21232e; */
351
+ /* .p-treenode-children {
348
352
  background-color: #f0f0f0;
349
- }
353
+ } */
350
354
  .deviceLabel {
351
355
  font-weight: 100;
352
356
  }
@@ -436,6 +440,11 @@
436
440
  background-color: #d5d5d5;
437
441
  border-radius: 10px;
438
442
  }
443
+ .p-treenode-label {
444
+ overflow: hidden;
445
+ white-space: nowrap;
446
+ text-overflow: ellipsis;
447
+ }
439
448
 
440
449
  </style>
441
450
 
package/bacnet_write.js CHANGED
@@ -5,9 +5,7 @@
5
5
  module.exports = function (RED) {
6
6
  function BitpoolBacnetWriteDevice (config) {
7
7
  RED.nodes.createNode(this, config);
8
-
9
8
  var node = this;
10
-
11
9
  node.priority = config.priority;
12
10
  node.appTag = config.applicationTag;
13
11
  node.pointsToWrite = config.pointsToWrite;
package/common.js CHANGED
@@ -5,8 +5,9 @@
5
5
  const { createLogger, format, transports } = require('winston');
6
6
  const { randomUUID } = require('crypto');
7
7
  const os = require('os');
8
-
9
- const baEnum = require('./resources/bacstack/lib/enum');
8
+ const { exec } = require("child_process");
9
+ const baEnum = require('./resources/node-bacnet/index.js').enum;
10
+ const fs = require('fs');
10
11
 
11
12
  const logger = createLogger({
12
13
  format: format.combine(
@@ -63,7 +64,24 @@ class BacnetConfig {
63
64
  };
64
65
 
65
66
  class BacnetClientConfig {
66
- constructor(apduTimeout, localIpAdrress, local_device_port, apduSize, maxSegments, broadCastAddr, discover_polling_schedule, device_id_range_enabled, device_id_range_start, device_id_range_end) {
67
+ constructor(
68
+ apduTimeout,
69
+ localIpAdrress,
70
+ local_device_port,
71
+ apduSize,
72
+ maxSegments,
73
+ broadCastAddr,
74
+ discover_polling_schedule,
75
+ device_id_range_enabled,
76
+ device_id_range_start,
77
+ device_id_range_end,
78
+ toRestartNodeRed,
79
+ deviceId,
80
+ manual_instance_range_enabled,
81
+ manual_instance_range_start,
82
+ manual_instance_range_end,
83
+ bacnetServerEnabled
84
+ ) {
67
85
  this.apduTimeout = apduTimeout;
68
86
  this.localIpAdrress = localIpAdrress;
69
87
  this.port = local_device_port;
@@ -74,6 +92,12 @@ class BacnetClientConfig {
74
92
  this.device_id_range_enabled = device_id_range_enabled;
75
93
  this.device_id_range_start = device_id_range_start;
76
94
  this.device_id_range_end = device_id_range_end;
95
+ this.toRestartNodeRed = toRestartNodeRed;
96
+ this.deviceId = deviceId;
97
+ this.manual_instance_range_enabled = manual_instance_range_enabled;
98
+ this.manual_instance_range_start = manual_instance_range_start;
99
+ this.manual_instance_range_end = manual_instance_range_end;
100
+ this.bacnetServerEnabled = bacnetServerEnabled;
77
101
  }
78
102
  };
79
103
 
@@ -85,7 +109,6 @@ class ReadCommandConfig {
85
109
  }
86
110
  };
87
111
 
88
-
89
112
  class WriteCommandConfig {
90
113
  constructor(device, objects) {
91
114
  this.device = {
@@ -103,10 +126,10 @@ class WriteCommandConfig {
103
126
  };
104
127
 
105
128
  const getUnit = function(id) {
106
- for (var key in baEnum.UnitsId) {
107
- if(baEnum.UnitsId[key] == id){
108
- if (baEnum.UnitsId.hasOwnProperty(key)) {
109
- let unitsArr = key.split("_"); unitsArr.shift();
129
+ for (var key in baEnum.EngineeringUnits) {
130
+ if(baEnum.EngineeringUnits[key] == id) {
131
+ if (baEnum.EngineeringUnits.hasOwnProperty(key)) {
132
+ let unitsArr = key.split("_");
110
133
  let unit;
111
134
  unitsArr.forEach((ele, index) =>{
112
135
  if(index == 0){
@@ -119,6 +142,7 @@ const getUnit = function(id) {
119
142
  }
120
143
  }
121
144
  }
145
+ return "no-units";
122
146
  };
123
147
 
124
148
  const generateId = function() {
@@ -161,5 +185,67 @@ const roundDecimalPlaces = function(value, decimals) {
161
185
  return value;
162
186
  }
163
187
 
188
+ const doNodeRedRestart = function() {
189
+ return new Promise(function(resolve, reject) {
190
+ try {
191
+ exec("restart", (error, stdout, stderr) => {
192
+ if (error) {
193
+ console.log(`Node-Red restart error: ${error.message}`);
194
+ reject(error.message);
195
+ }
196
+ if (stderr) {
197
+ console.log(`Node-Red restart stderr: ${stderr}`);
198
+ reject(stderr);
199
+ }
200
+ resolve(stdout);
201
+ });
202
+ } catch(e){
203
+ console.log(`Node-Red restart error: ${e}`);
204
+ reject(e);
205
+ }
206
+ });
207
+ };
208
+
209
+ // STORE CONFIG FUNCTION ==========================================================
210
+ //
211
+ // ================================================================================
212
+ async function Store_Config(data) {
213
+ await fs.writeFile("edge-bacnet-datastore.cfg", data, (err) => {
214
+ if (err) {
215
+ console.log("Store_Config writeFile error: ", err);
216
+ }
217
+ });
218
+ };
219
+
220
+ // READ CONFIG SYNC FUNCTION ======================================================
221
+ //
222
+ // ================================================================================
223
+ function Read_Config_Sync() {
224
+ var data = "{}";
225
+ try {
226
+ data = fs.readFileSync("edge-bacnet-datastore.cfg", {encoding:'utf8', flag:'r'});
227
+ }
228
+ catch(err) {
229
+ console.log("Read_Config_Sync error:", err);
230
+ data = '{}';
231
+ Store_Config(data);
232
+ }
233
+ return data;
234
+ };
164
235
 
165
- module.exports = { DeviceObjectId, DeviceObject, BacnetConfig, BacnetClientConfig, ReadCommandConfig, WriteCommandConfig, logger, getUnit, generateId, getIpAddress, roundDecimalPlaces };
236
+ module.exports = {
237
+ DeviceObjectId,
238
+ DeviceObject,
239
+ BacnetConfig,
240
+ BacnetClientConfig,
241
+ ReadCommandConfig,
242
+ WriteCommandConfig,
243
+ logger,
244
+ getUnit,
245
+ generateId,
246
+ getIpAddress,
247
+ roundDecimalPlaces,
248
+ doNodeRedRestart,
249
+ Store_Config,
250
+ Read_Config_Sync
251
+ };
File without changes
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "@bitpoolos/edge-bacnet",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "A bacnet gateway for node-red",
5
5
  "dependencies": {
6
6
  "async-mutex": "^0.4.0",
7
7
  "cronosjs": "^1.7.1",
8
+ "debug": "^4.1.1",
9
+ "iconv-lite": "^0.5.1",
10
+ "underscore": "^1.10.2",
11
+ "node-bacnet": "^0.2.4",
8
12
  "toad-scheduler": "^1.6.0",
9
- "winston": "^3.2.1",
10
- "debug": "^3.1.0",
11
- "iconv-lite": "^0.4.19"
13
+ "winston": "^3.2.1"
12
14
  },
13
15
  "node-red": {
14
16
  "version": ">=2.2.2",
@@ -0,0 +1 @@
1
+ this.primevue=this.primevue||{},this.primevue.confirmationservice=function(e,i){"use strict";function r(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var t=r(e);return{install:e=>{const r={require:e=>{t.default.emit("confirm",e)},close:()=>{t.default.emit("close")}};e.config.globalProperties.$confirm=r,e.provide(i.PrimeVueConfirmSymbol,r)}}}(primevue.confirmationeventbus,primevue.useconfirm);
@@ -0,0 +1 @@
1
+ this.primevue=this.primevue||{},this.primevue.confirmdialog=function(e,o,i,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var c=n(e),s=n(o),a={name:"ConfirmDialog",props:{group:String,breakpoints:{type:Object,default:null}},confirmListener:null,closeListener:null,data:()=>({visible:!1,confirmation:null}),mounted(){this.confirmListener=e=>{e&&e.group===this.group&&(this.confirmation=e,this.confirmation.onShow&&this.confirmation.onShow(),this.visible=!0)},this.closeListener=()=>{this.visible=!1,this.confirmation=null},s.default.on("confirm",this.confirmListener),s.default.on("close",this.closeListener)},beforeUnmount(){s.default.off("confirm",this.confirmListener),s.default.off("close",this.closeListener)},methods:{accept(){this.confirmation.accept&&this.confirmation.accept(),this.visible=!1},reject(){this.confirmation.reject&&this.confirmation.reject(),this.visible=!1},onHide(){this.confirmation.onHide&&this.confirmation.onHide(),this.visible=!1}},computed:{header(){return this.confirmation?this.confirmation.header:null},message(){return this.confirmation?this.confirmation.message:null},blockScroll(){return!this.confirmation||this.confirmation.blockScroll},position(){return this.confirmation?this.confirmation.position:null},iconClass(){return["p-confirm-dialog-icon",this.confirmation?this.confirmation.icon:null]},acceptLabel(){return this.confirmation?this.confirmation.acceptLabel||this.$primevue.config.locale.accept:null},rejectLabel(){return this.confirmation?this.confirmation.rejectLabel||this.$primevue.config.locale.reject:null},acceptIcon(){return this.confirmation?this.confirmation.acceptIcon:null},rejectIcon(){return this.confirmation?this.confirmation.rejectIcon:null},acceptClass(){return["p-confirm-dialog-accept",this.confirmation?this.confirmation.acceptClass:null]},rejectClass(){return["p-confirm-dialog-reject",this.confirmation?this.confirmation.rejectClass||"p-button-text":null]},autoFocusAccept(){return void 0===this.confirmation.defaultFocus||"accept"===this.confirmation.defaultFocus},autoFocusReject(){return"reject"===this.confirmation.defaultFocus},closeOnEscape(){return!this.confirmation||this.confirmation.closeOnEscape}},components:{CDialog:n(i).default,CDButton:c.default}};const r={class:"p-confirm-dialog-message"};return a.render=function(e,o,i,n,c,s){const a=t.resolveComponent("CDButton"),l=t.resolveComponent("CDialog");return t.openBlock(),t.createBlock(l,{visible:c.visible,"onUpdate:visible":[o[2]||(o[2]=e=>c.visible=e),s.onHide],role:"alertdialog",class:"p-confirm-dialog",modal:!0,header:s.header,blockScroll:s.blockScroll,position:s.position,breakpoints:i.breakpoints,closeOnEscape:s.closeOnEscape},{footer:t.withCtx((()=>[t.createVNode(a,{label:s.rejectLabel,icon:s.rejectIcon,class:t.normalizeClass(s.rejectClass),onClick:o[0]||(o[0]=e=>s.reject()),autofocus:s.autoFocusReject},null,8,["label","icon","class","autofocus"]),t.createVNode(a,{label:s.acceptLabel,icon:s.acceptIcon,class:t.normalizeClass(s.acceptClass),onClick:o[1]||(o[1]=e=>s.accept()),autofocus:s.autoFocusAccept},null,8,["label","icon","class","autofocus"])])),default:t.withCtx((()=>[e.$slots.message?(t.openBlock(),t.createBlock(t.resolveDynamicComponent(e.$slots.message),{key:1,message:c.confirmation},null,8,["message"])):(t.openBlock(),t.createElementBlock(t.Fragment,{key:0},[t.createElementVNode("i",{class:t.normalizeClass(s.iconClass)},null,2),t.createElementVNode("span",r,t.toDisplayString(s.message),1)],64))])),_:1},8,["visible","header","blockScroll","position","breakpoints","closeOnEscape","onUpdate:visible"])},a}(primevue.button,primevue.confirmationeventbus,primevue.dialog,Vue);
Binary file