@ncd-io/node-red-enterprise-sensors 2.0.1 → 2.0.3

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.
@@ -0,0 +1,452 @@
1
+ const { toMac, signInt, msbLsb } = require('../utils');
2
+
3
+ // --- 1. DEFINE LOCAL FUNCTIONS ---
4
+ // These are defined as local variables so they can call each other easily.
5
+ module.exports = (globalDevices) => {
6
+
7
+ const get_write_buffer_size = (firmware) => {
8
+ return 33;
9
+ };
10
+
11
+ const get_config_map = (firmware) => {
12
+ console.log('Generating sync map for firmware version', firmware);
13
+
14
+ return {
15
+ "core_version": {
16
+ "read_index": 3,
17
+ "descriptions": {
18
+ "title": "Core Version",
19
+ "main_caption": "The version of the core communication stack."
20
+ },
21
+ "validator": {
22
+ "type": "uint8"
23
+ },
24
+ "tags": [
25
+ "system"
26
+ ]
27
+ },
28
+ "firmware_version": {
29
+ "read_index": 4,
30
+ "descriptions": {
31
+ "title": "Firmware Version",
32
+ "main_caption": "The application-specific firmware version."
33
+ },
34
+ "validator": {
35
+ "type": "uint8"
36
+ },
37
+ "tags": [
38
+ "system"
39
+ ]
40
+ },
41
+ "sensor_type": {
42
+ "read_index": 5,
43
+ "descriptions": {
44
+ "title": "Sensor Type",
45
+ "main_caption": "The hardware identifier for the specific sensor model."
46
+ },
47
+ "validator": {
48
+ "type": "uint16be"
49
+ },
50
+ "tags": [
51
+ "system"
52
+ ]
53
+ },
54
+ "tx_lifetime_counter": {
55
+ "read_index": 7,
56
+ "descriptions": {
57
+ "title": "Sampling Interval",
58
+ "main_caption": "Set how often will the sensor transmit measurement data. Note: For this sensor, this value functions as the sampling interval rather than a traditional delay.",
59
+ "sub_caption": "Default value: 20 milliseconds."
60
+ },
61
+ "validator": {
62
+ "type": "uint32be"
63
+ },
64
+ "tags": [
65
+ "diagnostics"
66
+ ]
67
+ },
68
+ "hardware_id": {
69
+ "read_index": 11,
70
+ "length": 3,
71
+ "descriptions": {
72
+ "title": "Hardware ID",
73
+ "main_caption": "A unique 3-byte hardware identifier."
74
+ },
75
+ "validator": {
76
+ "type": "buffer"
77
+ },
78
+ "tags": [
79
+ "system"
80
+ ]
81
+ },
82
+ "network_id": {
83
+ "read_index": 14,
84
+ "write_index": 3,
85
+ "length": 2,
86
+ "descriptions": {
87
+ "title": "Network ID",
88
+ "main_caption": ""
89
+ },
90
+ "default_value": "7fff",
91
+ "validator": {
92
+ "type": "hex",
93
+ "length": 4
94
+ },
95
+ "html_id": "pan_id",
96
+ "tags": [
97
+ "communications"
98
+ ]
99
+ },
100
+ "destination_address": {
101
+ "read_index": 16,
102
+ "write_index": 5,
103
+ "length": 4,
104
+ "descriptions": {
105
+ "title": "Destination Address",
106
+ "main_caption": ""
107
+ },
108
+ "default_value": "0000ffff",
109
+ "validator": {
110
+ "type": "mac",
111
+ "length": 8
112
+ },
113
+ "html_id": "destination",
114
+ "tags": [
115
+ "communications"
116
+ ]
117
+ },
118
+ "node_id": {
119
+ "read_index": 20,
120
+ "write_index": 9,
121
+ "descriptions": {
122
+ "title": "Node ID",
123
+ "main_caption": ""
124
+ },
125
+ "default_value": "0",
126
+ "validator": {
127
+ "type": "uint8",
128
+ "min": 0,
129
+ "max": 255,
130
+ "generated": true
131
+ },
132
+ "html_id": "node_id",
133
+ "tags": [
134
+ "generic"
135
+ ]
136
+ },
137
+ "report_rate": {
138
+ "read_index": 21,
139
+ "write_index": 10,
140
+ "descriptions": {
141
+ "title": "Delay",
142
+ "main_caption": ""
143
+ },
144
+ "default_value": 3,
145
+ "validator": {
146
+ "type": "uint32be"
147
+ },
148
+ "html_id": "delay"
149
+ },
150
+ "fsr": {
151
+ "read_index": 25,
152
+ "write_index": 14,
153
+ "descriptions": {
154
+ "title": "Set FSR",
155
+ "main_caption": ""
156
+ },
157
+ "default_value": 0,
158
+ "validator": {
159
+ "type": "uint8",
160
+ "min": 0,
161
+ "max": 5,
162
+ "generated": true
163
+ },
164
+ "options": {
165
+ "0": "6.114",
166
+ "1": "4.096",
167
+ "2": "2.048",
168
+ "3": "1.024",
169
+ "4": "0.512",
170
+ "5": "0.256"
171
+ },
172
+ "html_id": "fsr_420ma"
173
+ },
174
+ "boot_up_time": {
175
+ "read_index": 26,
176
+ "write_index": 27,
177
+ "descriptions": {
178
+ "title": "Sensor Boot Time",
179
+ "main_caption": ""
180
+ },
181
+ "default_value": 0,
182
+ "validator": {
183
+ "type": "uint8",
184
+ "min": 0,
185
+ "max": 255
186
+ },
187
+ "html_id": "sensor_boot_time_420ma"
188
+ },
189
+ "adc_pin_reading": {
190
+ "read_index": 27,
191
+ "validator": {
192
+ "type": "uint16be"
193
+ },
194
+ "tags": [
195
+ "diagnostics"
196
+ ]
197
+ },
198
+ "auto_check_interval": {
199
+ "read_index": 29,
200
+ "write_index": 28,
201
+ "descriptions": {
202
+ "title": "Auto Check Interval",
203
+ "main_caption": "To disable the auto check interval feature make this setting active and use a value of 0."
204
+ },
205
+ "default_value": 60,
206
+ "validator": {
207
+ "type": "uint16be",
208
+ "min": 0,
209
+ "max": 65535
210
+ },
211
+ "html_id": "auto_check_interval_88"
212
+ },
213
+ "auto_check_threshold": {
214
+ "read_index": 31,
215
+ "write_index": 30,
216
+ "descriptions": {
217
+ "title": "Auto Check Threshold",
218
+ "main_caption": "This is a percent value. It will dictate a new transmission if the percentage change since last transmission exceeds the percentage set in this field."
219
+ },
220
+ "default_value": 20,
221
+ "validator": {
222
+ "type": "uint16be",
223
+ "min": 0,
224
+ "max": 65535
225
+ },
226
+ "html_id": "auto_check_threshold_88"
227
+ },
228
+ "always_on": {
229
+ "read_index": 33,
230
+ "write_index": 32,
231
+ "descriptions": {
232
+ "title": "Set Sensor Always On",
233
+ "main_caption": "This command will keep the external power to the sensor always enabled."
234
+ },
235
+ "default_value": 0,
236
+ "validator": {
237
+ "type": "uint8",
238
+ "min": 0,
239
+ "max": 1,
240
+ "generated": true
241
+ },
242
+ "options": {
243
+ "0": "Disable",
244
+ "1": "Enable"
245
+ },
246
+ "html_id": "always_on_420ma"
247
+ },
248
+ "calibration_one": {
249
+ "read_index": 34,
250
+ "write_index": 15,
251
+ "descriptions": {
252
+ "title": "Low Calibration Point",
253
+ "main_caption": ""
254
+ },
255
+ "default_value": 68805,
256
+ "validator": {
257
+ "type": "uint32be",
258
+ "min": 0,
259
+ "max": 4294967295
260
+ },
261
+ "html_id": "low_calibration_420ma"
262
+ },
263
+ "calibration_two": {
264
+ "read_index": 38,
265
+ "write_index": 19,
266
+ "descriptions": {
267
+ "title": "Mid Calibration Point",
268
+ "main_caption": ""
269
+ },
270
+ "default_value": 68724,
271
+ "validator": {
272
+ "type": "uint32be",
273
+ "min": 0,
274
+ "max": 4294967295
275
+ },
276
+ "html_id": "mid_calibration_420ma"
277
+ },
278
+ "calibration_three": {
279
+ "read_index": 42,
280
+ "write_index": 23,
281
+ "descriptions": {
282
+ "title": "High Calibration Point",
283
+ "main_caption": ""
284
+ },
285
+ "default_value": 68714,
286
+ "validator": {
287
+ "type": "uint32be",
288
+ "min": 0,
289
+ "max": 4294967295
290
+ },
291
+ "html_id": "high_calibration_420ma"
292
+ }
293
+ };
294
+ };
295
+
296
+ const sync_parse = (rep_buffer) => {
297
+ let response = {
298
+ 'human_readable': {},
299
+ 'machine_values': {}
300
+ };
301
+
302
+ // Get the map based on the sensor type byte
303
+ const sync_map = get_config_map(rep_buffer[4]);
304
+
305
+ for (const [key, config] of Object.entries(sync_map)) {
306
+ // Destructure 'type' from inside 'validator' and rename 'read_index' to 'idx'
307
+ const { read_index: idx, length, validator: { type } = {}, converter, options } = config;
308
+
309
+ // If for some reason a config doesn't have a validator/type, skip it
310
+ if (!type) continue;
311
+
312
+ switch (type) {
313
+ case 'uint8':
314
+ response.machine_values[key] = rep_buffer[idx];
315
+ break;
316
+ case 'uint16be':
317
+ response.machine_values[key] = rep_buffer.readUInt16BE(idx);
318
+ break;
319
+ case 'uint32be':
320
+ response.machine_values[key] = rep_buffer.readUInt32BE(idx);
321
+ break;
322
+ case 'buffer':
323
+ response.machine_values[key] = rep_buffer.subarray(idx, idx + length);
324
+ break;
325
+ case 'hex':
326
+ response.machine_values[key] = rep_buffer.subarray(idx, idx + length).toString('hex');
327
+ break;
328
+ case 'mac':
329
+ response.machine_values[key] = rep_buffer.subarray(idx, idx + length).toString('hex');
330
+ break;
331
+ }
332
+ let human_value = response.machine_values[key];
333
+ if(options && options[response.machine_values[key]]){
334
+ human_value = options[response.machine_values[key]];
335
+ }else{
336
+ if(converter && converter.multiplier){
337
+ human_value = human_value * converter.multiplier;
338
+ }
339
+ if(converter && converter.units){
340
+ human_value = human_value + converter.units;
341
+ }
342
+ }
343
+ response.human_readable[key] = human_value;
344
+ }
345
+ if (Object.hasOwn(response.machine_values, 'destination_address') && response.machine_values.destination_address.toLowerCase() === '00000000') {
346
+ console.log('##############################');
347
+ console.log('#########Dest Override########');
348
+ console.log('##############################');
349
+ response.destination_address = "0000ffff";
350
+ // response.auto_raw_destination_address = "0000ffff";
351
+ };
352
+ return response;
353
+ };
354
+
355
+ const parse_fly = (frame) => {
356
+ let firmware = frame[2];
357
+ if(firmware > 13){ // firmware 14 and above
358
+ let frame_data = {};
359
+ let auto_check_interval = frame.slice(20, 22).reduce(msbLsb);
360
+ if(!auto_check_interval){
361
+ frame_data.auto_check_interval = 'Disabled';
362
+ }else{
363
+ frame_data.auto_check_interval = auto_check_interval + " sec";
364
+ }
365
+ frame_data.always_on = frame[24]?"Enabled":"Disabled";
366
+ switch(frame[16]){
367
+ case 0:
368
+ frame_data.fsr = "+-6.114 V";
369
+ break;
370
+ case 1:
371
+ frame_data.fsr = "+-4.096 V";
372
+ break;
373
+ case 2:
374
+ frame_data.fsr = "+-2.048 V";
375
+ break;
376
+ case 3:
377
+ frame_data.fsr = "+-1.024 V";
378
+ break;
379
+ case 4:
380
+ frame_data.fsr = "+-0.512 V";
381
+ break;
382
+ case 5:
383
+ frame_data.fsr = "+-0.256 V";
384
+ break;
385
+ }
386
+ return {
387
+ 'firmware': frame[2],
388
+ 'fsr': frame_data.fsr,
389
+ 'boot_up_time': frame[17] + " sec",
390
+ 'adc_pin_reading': frame.slice(18, 20).reduce(msbLsb),
391
+ 'auto_check_interval': frame_data.auto_check_interval,
392
+ 'auto_check_threshold': frame.slice(22, 24).reduce(msbLsb),
393
+ 'always_on': frame_data.always_on,
394
+ 'calibration_one': frame.slice(25, 29).reduce(msbLsb),
395
+ 'calibration_two':frame.slice(29, 33).reduce(msbLsb),
396
+ 'calibration_three':frame.slice(33, 37).reduce(msbLsb),
397
+ 'hardware_id': frame.slice(37, 40),
398
+ 'report_rate': frame.slice(40, 44).reduce(msbLsb) + " sec",
399
+ 'tx_life_counter': frame.slice(44, 48).reduce(msbLsb),
400
+ 'machine_values': {
401
+ 'firmware': frame[2],
402
+ 'fsr': frame[16],
403
+ 'boot_up_time': frame[17],
404
+ 'adc_pin_reading': frame.slice(18, 20),
405
+ 'auto_check_interval': frame.slice(20, 22),
406
+ 'auto_check_percentage': frame.slice(22, 24),
407
+ 'always_on': frame[24],
408
+ 'calibration_one': frame.slice(25, 29),
409
+ 'calibration_two':frame.slice(29, 33),
410
+ 'calibration_three':frame.slice(33, 37),
411
+ 'hardware_id': frame.slice(37, 40),
412
+ 'report_rate': frame.slice(40, 44),
413
+ 'tx_life_counter': frame.slice(44, 48)
414
+ }
415
+ }
416
+ }
417
+ };
418
+
419
+ const parse = (d) => {
420
+ var adc1 = signInt(d.slice(0, 2).reduce(msbLsb));
421
+ var adc2 = signInt(d.slice(2, 4).reduce(msbLsb));
422
+ var adc3 = signInt(d.slice(4, 6).reduce(msbLsb));
423
+ var adc4 = signInt(d.slice(6, 8).reduce(msbLsb));
424
+ var ma1 = (signInt(d.slice(8, 10).reduce(msbLsb)))/100.0;
425
+ var ma2 = (signInt(d.slice(10, 12).reduce(msbLsb)))/100.0;
426
+ var ma3 = (signInt(d.slice(12, 14).reduce(msbLsb)))/100.0;
427
+ var ma4 = (signInt(d.slice(14, 16).reduce(msbLsb)))/100.0;
428
+ return {
429
+ adc1: adc1,
430
+ adc2: adc2,
431
+ adc3: adc3,
432
+ adc4: adc4,
433
+ ma1: ma1,
434
+ ma2: ma2,
435
+ ma3: ma3,
436
+ ma4: ma4
437
+ };
438
+ };
439
+
440
+ // --- 2. EXPORT THE MODULE ---
441
+ // Export the module with all the necessary functions and properties
442
+ // that need to be called from outside the scrip
443
+ return {
444
+ type: 107,
445
+ name: '16-Bit 4-Channel 4-20mA',
446
+ parse,
447
+ get_write_buffer_size,
448
+ get_config_map,
449
+ sync_parse,
450
+ parse_fly
451
+ };
452
+ };
@@ -1478,13 +1478,30 @@ module.exports = (globalDevices, emitter) => {
1478
1478
  var current_packet = msbLsb(payload[18], payload[19]);
1479
1479
  var sdata_start = 20;
1480
1480
  var probe;
1481
+
1482
+ // Sensor sends a 33 byte length packet when it fails to get sensor data for a probe.
1483
+ if(expected_packets == 0){
1484
+ if(payload[7] & 2){
1485
+ return {
1486
+ 'error': 'Probe is 1 unattached, unable to collect sensor data. Check probe connection.',
1487
+ 'probe': 1
1488
+ };
1489
+ }else{
1490
+ return {
1491
+ 'error': 'Probe is 2 unattached, unable to collect sensor data. Check probe connection.',
1492
+ 'probe': 2
1493
+ };
1494
+ }
1495
+ }
1481
1496
  if (payload[7] & 8) {
1482
1497
  probe = 2;
1483
1498
  } else {
1484
1499
  probe = 1;
1485
1500
  }
1486
-
1487
- if (globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1) {
1501
+ if(!Object.hasOwn(globalDevices, deviceAddr)){
1502
+ globalDevices[deviceAddr] = {};
1503
+ }
1504
+ if (globalDevices[deviceAddr].hasOwnProperty(probe) || expected_packets == 1) {
1488
1505
  if (expected_packets != 1) {
1489
1506
  if (globalDevices[deviceAddr][probe].last_packet_counter == current_packet) {
1490
1507
  console.log('Duplicated message');