@atelieruldetehnologii/node-red-custome-solutions 0.62.22

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 (107) hide show
  1. package/NOTICE +56 -0
  2. package/PROPRIETARY-LICENSE.txt +157 -0
  3. package/README.md +105 -0
  4. package/TERMS-AND-CONDITIONS.md +311 -0
  5. package/advantech-read/advantech-read.html +775 -0
  6. package/advantech-read/advantech-read.js +449 -0
  7. package/advantech-write/advantech-write.html +413 -0
  8. package/advantech-write/advantech-write.js +338 -0
  9. package/bess-control/bess-control.html +569 -0
  10. package/bess-control/bess-control.js +541 -0
  11. package/bess-control/lib/allocation.js +149 -0
  12. package/bess-control/lib/batteries.js +86 -0
  13. package/bess-control/lib/helpers.js +157 -0
  14. package/bess-control/lib/messages.js +131 -0
  15. package/bess-control/lib/runtime.js +156 -0
  16. package/carlo-gavazzi/carlo-gavazzi.html +4327 -0
  17. package/carlo-gavazzi/carlo-gavazzi.js +3081 -0
  18. package/control-export/control-export.html +3644 -0
  19. package/control-export/control-export.js +2192 -0
  20. package/control-reactive/control-reactive.html +3136 -0
  21. package/control-reactive/control-reactive.js +2124 -0
  22. package/fronius-emulator/README.md +258 -0
  23. package/fronius-emulator/fronius-emulator.html +170 -0
  24. package/fronius-emulator/fronius-emulator.js +520 -0
  25. package/fronius-emulator/lib/binary-codec.js +65 -0
  26. package/fronius-emulator/lib/fronius-register-map.js +185 -0
  27. package/fronius-emulator/lib/modbus-parser.js +61 -0
  28. package/fronius-emulator/lib/modbus-tcp-server.js +224 -0
  29. package/fronius-emulator/lib/validation.js +206 -0
  30. package/fronius-emulator/lib/virtual-meter.js +132 -0
  31. package/fronius-read/fronius-read.html +3944 -0
  32. package/fronius-read/fronius-read.js +1466 -0
  33. package/fronius-read/lib/inverter-profiles.js +711 -0
  34. package/fronius-read/lib/sunspec-core.js +303 -0
  35. package/fronius-read/lib/sunspec-model-parsers.js +845 -0
  36. package/fronius-write/fronius-write.html +1789 -0
  37. package/fronius-write/fronius-write.js +2531 -0
  38. package/influxdb/influxdb.html +1077 -0
  39. package/influxdb/influxdb.js +1487 -0
  40. package/janitza/janitza.html +2562 -0
  41. package/janitza/janitza.js +1500 -0
  42. package/janitza/lib/janitza-profiles.js +371 -0
  43. package/lib/a3-test-server.js +199 -0
  44. package/lib/adam-6000-profiles.js +173 -0
  45. package/lib/licensing/constants.js +103 -0
  46. package/lib/licensing/device-id.js +251 -0
  47. package/lib/licensing/errors.js +21 -0
  48. package/lib/licensing/license-client.js +377 -0
  49. package/lib/licensing/license-state.js +50 -0
  50. package/lib/licensing/node-guard.js +150 -0
  51. package/lib/licensing/package-integrity.js +167 -0
  52. package/lib/licensing/package-integrity.json +513 -0
  53. package/lib/licensing/public-key.pem +3 -0
  54. package/lib/licensing/public-key.pem.example +2 -0
  55. package/lib/licensing/public-keys.js +18 -0
  56. package/lib/licensing/storage.js +63 -0
  57. package/lib/licensing/token-verifier.js +84 -0
  58. package/lib/numbers.js +26 -0
  59. package/license/atelier-license.html +88 -0
  60. package/license/atelier-license.js +170 -0
  61. package/modbus-tcp-discover/modbus-tcp-discover.html +190 -0
  62. package/modbus-tcp-discover/modbus-tcp-discover.js +706 -0
  63. package/multi-battery/multi-battery.html +649 -0
  64. package/multi-battery/multi-battery.js +668 -0
  65. package/multi-grid/multi-grid.html +745 -0
  66. package/multi-grid/multi-grid.js +1392 -0
  67. package/multi-inverter/lib/engine-core.js +237 -0
  68. package/multi-inverter/multi-inverter.html +2674 -0
  69. package/multi-inverter/multi-inverter.js +1393 -0
  70. package/package.json +109 -0
  71. package/phoenix-contact/lib/eem-ma370-advanced.js +175 -0
  72. package/phoenix-contact/lib/eem-ma370.js +138 -0
  73. package/phoenix-contact/lib/integrations.js +149 -0
  74. package/phoenix-contact/lib/phoenix-contact-profile.js +35030 -0
  75. package/phoenix-contact/phoenix-contact.html +840 -0
  76. package/phoenix-contact/phoenix-contact.js +679 -0
  77. package/pv-generator/pv-generator.html +284 -0
  78. package/pv-generator/pv-generator.js +387 -0
  79. package/pylontech-read/pylontech-read.html +763 -0
  80. package/pylontech-read/pylontech-read.js +518 -0
  81. package/pylontech-write/pylontech-write.html +405 -0
  82. package/pylontech-write/pylontech-write.js +546 -0
  83. package/scada-aggregator/lib/aggregate.js +72 -0
  84. package/scada-aggregator/lib/groups.js +39 -0
  85. package/scada-aggregator/scada-aggregator.html +108 -0
  86. package/scada-aggregator/scada-aggregator.js +117 -0
  87. package/scada-server/lib/aggregator-map.js +58 -0
  88. package/scada-server/lib/encoding.js +235 -0
  89. package/scada-server/lib/modbus-server.js +492 -0
  90. package/scada-server/lib/object-path.js +18 -0
  91. package/scada-server/lib/register-map.js +288 -0
  92. package/scada-server/scada-server.html +1419 -0
  93. package/scada-server/scada-server.js +704 -0
  94. package/scripts/check-syntax.js +66 -0
  95. package/scripts/package-integrity.js +46 -0
  96. package/scripts/package-release.js +116 -0
  97. package/scripts/run-tests.js +2178 -0
  98. package/scripts/tgz-integrity.js +74 -0
  99. package/sma-read/lib/sunspec-core.js +83 -0
  100. package/sma-read/lib/sunspec-model-parsers.js +853 -0
  101. package/sma-read/sma-read.html +659 -0
  102. package/sma-read/sma-read.js +458 -0
  103. package/sma-write/sma-write.html +105 -0
  104. package/sma-write/sma-write.js +270 -0
  105. package/victron-read/lib/mqtt-state.js +176 -0
  106. package/victron-read/victron-read.html +1074 -0
  107. package/victron-read/victron-read.js +1222 -0
@@ -0,0 +1,3081 @@
1
+ module.exports = function (RED) {
2
+
3
+
4
+ "use strict";
5
+
6
+ const { runLicensedNode } = require("../lib/licensing/node-guard");
7
+
8
+
9
+
10
+ const net = require("net");
11
+
12
+
13
+ const influxMeasurementRegistry = new Map();
14
+
15
+
16
+
17
+ const TRANSACTION_ID = 1;
18
+
19
+
20
+ const PROTOCOL_ID = 0;
21
+
22
+
23
+ const MESSAGE_LENGTH = 6;
24
+
25
+
26
+ const FUNCTION_CODE = 3;
27
+
28
+
29
+ const START_ADDRESS = 0;
30
+
31
+
32
+ const REGISTER_COUNT = 124;
33
+
34
+
35
+ const EXPECTED_BYTE_COUNT = 248;
36
+
37
+
38
+ const SOCKET_TIMEOUT_MS = 5000;
39
+
40
+
41
+ const MULTI_GRID_DEFAULT_FIELDS = [
42
+
43
+
44
+ "summary.power.total_w",
45
+
46
+
47
+ "summary.power.l1_w",
48
+
49
+
50
+ "summary.power.l2_w",
51
+
52
+
53
+ "summary.power.l3_w"
54
+
55
+
56
+ ];
57
+
58
+
59
+ const BESS_CONTROL_DEFAULT_FIELDS = [
60
+
61
+
62
+ "summary.power.total_w",
63
+
64
+
65
+ "summary.power.l1_w",
66
+
67
+
68
+ "summary.power.l2_w",
69
+
70
+
71
+ "summary.power.l3_w"
72
+
73
+
74
+ ];
75
+
76
+
77
+ const CONTROL_EXPORT_REACTIVE_DEFAULT_FIELDS = [
78
+
79
+
80
+ "summary.power.total_w",
81
+
82
+
83
+ "summary.power.l1_w",
84
+
85
+
86
+ "summary.power.l2_w",
87
+
88
+
89
+ "summary.power.l3_w",
90
+
91
+
92
+ "summary.reactive.total_var",
93
+
94
+
95
+ "summary.reactive.l1_var",
96
+
97
+
98
+ "summary.reactive.l2_var",
99
+
100
+
101
+ "summary.reactive.l3_var"
102
+
103
+
104
+ ];
105
+
106
+
107
+ const SCADA_DEFAULT_FIELDS = [
108
+
109
+
110
+ "summary.power.total_w",
111
+
112
+
113
+ "summary.reactive.total_var",
114
+
115
+
116
+ "summary.frequency_hz",
117
+
118
+
119
+ "summary.voltage.l1_l2_v",
120
+
121
+
122
+ "summary.voltage.l2_l3_v",
123
+
124
+
125
+ "summary.voltage.l3_l1_v"
126
+
127
+
128
+ ];
129
+
130
+
131
+
132
+ function toPositiveNumber(value, fallback) {
133
+
134
+
135
+ const n = Number(value);
136
+
137
+
138
+ return Number.isFinite(n) && n > 0 ? n : fallback;
139
+
140
+
141
+ }
142
+
143
+ function toPollInterval(value, fallback) {
144
+ const text = String(value || "").trim().toLowerCase();
145
+ if (text === "off" || text === "0") return 0;
146
+ const n = Number(value);
147
+ return Number.isFinite(n) && n > 0 ? n : fallback;
148
+ }
149
+
150
+
151
+
152
+ function getByPath(obj, path) {
153
+
154
+
155
+ if (!path || path === "full") {
156
+
157
+
158
+ return obj;
159
+
160
+
161
+ }
162
+
163
+
164
+
165
+ return path.split(".").reduce(function (acc, key) {
166
+
167
+
168
+ return acc !== null && acc !== undefined ? acc[key] : undefined;
169
+
170
+
171
+ }, obj);
172
+
173
+
174
+ }
175
+
176
+
177
+
178
+ function normalizePayloadFields(fields, allowEmpty) {
179
+
180
+
181
+ const keepEmpty = !!allowEmpty;
182
+
183
+
184
+
185
+ if (Array.isArray(fields) && fields.length) {
186
+
187
+
188
+ const normalized = Array.from(
189
+
190
+
191
+ new Set(
192
+
193
+
194
+ fields
195
+
196
+
197
+ .map(function (f) { return String(f || "").trim(); })
198
+
199
+
200
+ .filter(Boolean)
201
+
202
+
203
+ )
204
+
205
+
206
+ );
207
+
208
+
209
+
210
+ if (!normalized.length) {
211
+
212
+
213
+ return keepEmpty ? [] : ["full"];
214
+
215
+
216
+ }
217
+
218
+
219
+
220
+ if (normalized.includes("full")) {
221
+
222
+
223
+ return ["full"];
224
+
225
+
226
+ }
227
+
228
+
229
+
230
+ return normalized;
231
+
232
+
233
+ }
234
+
235
+
236
+
237
+ if (typeof fields === "string" && fields.trim()) {
238
+
239
+
240
+ const trimmed = fields.trim();
241
+
242
+
243
+
244
+ if (trimmed[0] === "[") {
245
+
246
+
247
+ try {
248
+
249
+
250
+ const parsed = JSON.parse(trimmed);
251
+
252
+
253
+ if (Array.isArray(parsed)) {
254
+
255
+
256
+ return normalizePayloadFields(parsed, keepEmpty);
257
+
258
+
259
+ }
260
+
261
+
262
+ } catch (e) {
263
+
264
+
265
+ // ignore
266
+
267
+
268
+ }
269
+
270
+
271
+ }
272
+
273
+
274
+
275
+ return [trimmed];
276
+
277
+
278
+ }
279
+
280
+
281
+
282
+ return keepEmpty ? [] : ["full"];
283
+
284
+
285
+ }
286
+
287
+
288
+
289
+ function sanitizePayloadFieldsForMode(fields, multiGrid, bessControl, controlExportReactive, scadaServer, influxDb, expertMode) {
290
+
291
+
292
+ const hasSpecialMode = !!(multiGrid || bessControl || controlExportReactive || scadaServer || influxDb);
293
+
294
+
295
+ const allowEmpty = !!(hasSpecialMode && expertMode);
296
+
297
+
298
+ const normalized = normalizePayloadFields(fields, allowEmpty);
299
+
300
+
301
+
302
+ if (multiGrid && normalized.includes("full")) {
303
+
304
+
305
+ return MULTI_GRID_DEFAULT_FIELDS.slice();
306
+
307
+
308
+ }
309
+
310
+
311
+
312
+ if (bessControl && normalized.includes("full")) {
313
+
314
+
315
+ return BESS_CONTROL_DEFAULT_FIELDS.slice();
316
+
317
+
318
+ }
319
+
320
+
321
+
322
+ if (controlExportReactive && normalized.includes("full")) {
323
+
324
+
325
+ return CONTROL_EXPORT_REACTIVE_DEFAULT_FIELDS.slice();
326
+
327
+
328
+ }
329
+
330
+
331
+
332
+ if (scadaServer && normalized.includes("full")) {
333
+
334
+
335
+ return SCADA_DEFAULT_FIELDS.slice();
336
+
337
+
338
+ }
339
+
340
+
341
+
342
+ if (influxDb && normalized.includes("full")) {
343
+
344
+
345
+ return ["full"];
346
+
347
+
348
+ }
349
+
350
+
351
+
352
+ return normalized;
353
+
354
+
355
+ }
356
+
357
+
358
+
359
+ function getPayloadLabel(path) {
360
+
361
+
362
+ const labels = {
363
+
364
+
365
+ "full": "Toate valorile",
366
+
367
+
368
+
369
+ "summary.voltage.l1_n_v": "Tensiune L1-N",
370
+
371
+
372
+ "summary.voltage.l2_n_v": "Tensiune L2-N",
373
+
374
+
375
+ "summary.voltage.l3_n_v": "Tensiune L3-N",
376
+
377
+
378
+ "summary.voltage.l1_l2_v": "Tensiune L1-L2",
379
+
380
+
381
+ "summary.voltage.l2_l3_v": "Tensiune L2-L3",
382
+
383
+
384
+ "summary.voltage.l3_l1_v": "Tensiune L3-L1",
385
+
386
+
387
+
388
+ "summary.current.l1_a": "Curent L1",
389
+
390
+
391
+ "summary.current.l2_a": "Curent L2",
392
+
393
+
394
+ "summary.current.l3_a": "Curent L3",
395
+
396
+
397
+
398
+ "summary.power.l1_w": "Putere L1",
399
+
400
+
401
+ "summary.power.l2_w": "Putere L2",
402
+
403
+
404
+ "summary.power.l3_w": "Putere L3",
405
+
406
+
407
+ "summary.power.total_w": "Putere totala",
408
+
409
+
410
+
411
+ "summary.apparent.l1_va": "Putere aparenta L1",
412
+
413
+
414
+ "summary.apparent.l2_va": "Putere aparenta L2",
415
+
416
+
417
+ "summary.apparent.l3_va": "Putere aparenta L3",
418
+
419
+
420
+ "summary.apparent.total_va": "Putere aparenta totala",
421
+
422
+
423
+
424
+ "summary.reactive.l1_var": "Putere reactiva L1",
425
+
426
+
427
+ "summary.reactive.l2_var": "Putere reactiva L2",
428
+
429
+
430
+ "summary.reactive.l3_var": "Putere reactiva L3",
431
+
432
+
433
+ "summary.reactive.total_var": "Putere reactiva totala",
434
+
435
+
436
+
437
+ "summary.power_factor.l1": "Factor putere L1",
438
+
439
+
440
+ "summary.power_factor.l2": "Factor putere L2",
441
+
442
+
443
+ "summary.power_factor.l3": "Factor putere L3",
444
+
445
+
446
+
447
+ "summary.frequency_hz": "Frecventa",
448
+
449
+
450
+ "summary.phase_sequence": "Ordine faze",
451
+
452
+
453
+
454
+ "summary.energy.import_kwh": "Energie importata",
455
+
456
+
457
+ "summary.energy.reactive_import_kvarh": "Energie reactiva importata",
458
+
459
+
460
+ "summary.energy.l1_kwh": "Energie L1",
461
+
462
+
463
+ "summary.energy.l2_kwh": "Energie L2",
464
+
465
+
466
+ "summary.energy.l3_kwh": "Energie L3",
467
+
468
+
469
+
470
+ "summary.energy.tariff.t1_kwh": "Energie T1",
471
+
472
+
473
+ "summary.energy.tariff.t2_kwh": "Energie T2",
474
+
475
+
476
+ "summary.energy.tariff.t3_kwh": "Energie T3",
477
+
478
+
479
+ "summary.energy.tariff.t4_kwh": "Energie T4",
480
+
481
+
482
+
483
+ "summary.energy.export_kwh": "Energie exportata",
484
+
485
+
486
+ "summary.energy.export_reactive_kvarh": "Energie reactiva exportata",
487
+
488
+
489
+
490
+ "summary.energy.reactive_tariff.t1_kvarh": "Energie reactiva T1",
491
+
492
+
493
+ "summary.energy.reactive_tariff.t2_kvarh": "Energie reactiva T2",
494
+
495
+
496
+ "summary.energy.reactive_tariff.t3_kvarh": "Energie reactiva T3",
497
+
498
+
499
+ "summary.energy.reactive_tariff.t4_kvarh": "Energie reactiva T4"
500
+
501
+
502
+ };
503
+
504
+
505
+
506
+ return labels[path] || path;
507
+
508
+
509
+ }
510
+
511
+
512
+
513
+ function getPayloadUnit(path) {
514
+
515
+
516
+ const units = {
517
+
518
+
519
+ "summary.voltage.l1_n_v": "V",
520
+
521
+
522
+ "summary.voltage.l2_n_v": "V",
523
+
524
+
525
+ "summary.voltage.l3_n_v": "V",
526
+
527
+
528
+ "summary.voltage.l1_l2_v": "V",
529
+
530
+
531
+ "summary.voltage.l2_l3_v": "V",
532
+
533
+
534
+ "summary.voltage.l3_l1_v": "V",
535
+
536
+
537
+
538
+ "summary.current.l1_a": "A",
539
+
540
+
541
+ "summary.current.l2_a": "A",
542
+
543
+
544
+ "summary.current.l3_a": "A",
545
+
546
+
547
+
548
+ "summary.power.l1_w": "W",
549
+
550
+
551
+ "summary.power.l2_w": "W",
552
+
553
+
554
+ "summary.power.l3_w": "W",
555
+
556
+
557
+ "summary.power.total_w": "W",
558
+
559
+
560
+
561
+ "summary.apparent.l1_va": "VA",
562
+
563
+
564
+ "summary.apparent.l2_va": "VA",
565
+
566
+
567
+ "summary.apparent.l3_va": "VA",
568
+
569
+
570
+ "summary.apparent.total_va": "VA",
571
+
572
+
573
+
574
+ "summary.reactive.l1_var": "var",
575
+
576
+
577
+ "summary.reactive.l2_var": "var",
578
+
579
+
580
+ "summary.reactive.l3_var": "var",
581
+
582
+
583
+ "summary.reactive.total_var": "var",
584
+
585
+
586
+
587
+ "summary.power_factor.l1": "",
588
+
589
+
590
+ "summary.power_factor.l2": "",
591
+
592
+
593
+ "summary.power_factor.l3": "",
594
+
595
+
596
+
597
+ "summary.frequency_hz": "Hz",
598
+
599
+
600
+ "summary.phase_sequence": "",
601
+
602
+
603
+
604
+ "summary.energy.import_kwh": "kWh",
605
+
606
+
607
+ "summary.energy.reactive_import_kvarh": "kvarh",
608
+
609
+
610
+ "summary.energy.l1_kwh": "kWh",
611
+
612
+
613
+ "summary.energy.l2_kwh": "kWh",
614
+
615
+
616
+ "summary.energy.l3_kwh": "kWh",
617
+
618
+
619
+ "summary.energy.tariff.t1_kwh": "kWh",
620
+
621
+
622
+ "summary.energy.tariff.t2_kwh": "kWh",
623
+
624
+
625
+ "summary.energy.tariff.t3_kwh": "kWh",
626
+
627
+
628
+ "summary.energy.tariff.t4_kwh": "kWh",
629
+
630
+
631
+ "summary.energy.export_kwh": "kWh",
632
+
633
+
634
+ "summary.energy.export_reactive_kvarh": "kvarh",
635
+
636
+
637
+ "summary.energy.reactive_tariff.t1_kvarh": "kvarh",
638
+
639
+
640
+ "summary.energy.reactive_tariff.t2_kvarh": "kvarh",
641
+
642
+
643
+ "summary.energy.reactive_tariff.t3_kvarh": "kvarh",
644
+
645
+
646
+ "summary.energy.reactive_tariff.t4_kvarh": "kvarh"
647
+
648
+
649
+ };
650
+
651
+
652
+
653
+ return units[path] || "";
654
+
655
+
656
+ }
657
+
658
+
659
+
660
+ function getCompactPayloadKey(path) {
661
+
662
+
663
+ const mapping = {
664
+
665
+
666
+ "summary.voltage.l1_n_v": "voltage_l1_n_v",
667
+
668
+
669
+ "summary.voltage.l2_n_v": "voltage_l2_n_v",
670
+
671
+
672
+ "summary.voltage.l3_n_v": "voltage_l3_n_v",
673
+
674
+
675
+ "summary.voltage.l1_l2_v": "voltage_l1_l2_v",
676
+
677
+
678
+ "summary.voltage.l2_l3_v": "voltage_l2_l3_v",
679
+
680
+
681
+ "summary.voltage.l3_l1_v": "voltage_l3_l1_v",
682
+
683
+
684
+
685
+ "summary.current.l1_a": "current_l1_a",
686
+
687
+
688
+ "summary.current.l2_a": "current_l2_a",
689
+
690
+
691
+ "summary.current.l3_a": "current_l3_a",
692
+
693
+
694
+
695
+ "summary.power.l1_w": "power_l1_w",
696
+
697
+
698
+ "summary.power.l2_w": "power_l2_w",
699
+
700
+
701
+ "summary.power.l3_w": "power_l3_w",
702
+
703
+
704
+ "summary.power.total_w": "power_w",
705
+
706
+
707
+
708
+ "summary.apparent.l1_va": "apparent_l1_va",
709
+
710
+
711
+ "summary.apparent.l2_va": "apparent_l2_va",
712
+
713
+
714
+ "summary.apparent.l3_va": "apparent_l3_va",
715
+
716
+
717
+ "summary.apparent.total_va": "apparent_total_va",
718
+
719
+
720
+
721
+ "summary.reactive.l1_var": "reactive_l1_var",
722
+
723
+
724
+ "summary.reactive.l2_var": "reactive_l2_var",
725
+
726
+
727
+ "summary.reactive.l3_var": "reactive_l3_var",
728
+
729
+
730
+ "summary.reactive.total_var": "reactive_total_var",
731
+
732
+
733
+
734
+ "summary.power_factor.l1": "power_factor_l1",
735
+
736
+
737
+ "summary.power_factor.l2": "power_factor_l2",
738
+
739
+
740
+ "summary.power_factor.l3": "power_factor_l3",
741
+
742
+
743
+
744
+ "summary.frequency_hz": "frequency_hz",
745
+
746
+
747
+ "summary.phase_sequence": "phase_sequence",
748
+
749
+
750
+
751
+ "summary.energy.import_kwh": "energy_import_kwh",
752
+
753
+
754
+ "summary.energy.reactive_import_kvarh": "energy_reactive_import_kvarh",
755
+
756
+
757
+ "summary.energy.l1_kwh": "energy_l1_kwh",
758
+
759
+
760
+ "summary.energy.l2_kwh": "energy_l2_kwh",
761
+
762
+
763
+ "summary.energy.l3_kwh": "energy_l3_kwh",
764
+
765
+
766
+ "summary.energy.tariff.t1_kwh": "energy_t1_kwh",
767
+
768
+
769
+ "summary.energy.tariff.t2_kwh": "energy_t2_kwh",
770
+
771
+
772
+ "summary.energy.tariff.t3_kwh": "energy_t3_kwh",
773
+
774
+
775
+ "summary.energy.tariff.t4_kwh": "energy_t4_kwh",
776
+
777
+
778
+ "summary.energy.export_kwh": "energy_export_kwh",
779
+
780
+
781
+ "summary.energy.export_reactive_kvarh": "energy_export_reactive_kvarh",
782
+
783
+
784
+ "summary.energy.reactive_tariff.t1_kvarh": "energy_reactive_t1_kvarh",
785
+
786
+
787
+ "summary.energy.reactive_tariff.t2_kvarh": "energy_reactive_t2_kvarh",
788
+
789
+
790
+ "summary.energy.reactive_tariff.t3_kvarh": "energy_reactive_t3_kvarh",
791
+
792
+
793
+ "summary.energy.reactive_tariff.t4_kvarh": "energy_reactive_t4_kvarh"
794
+
795
+
796
+ };
797
+
798
+
799
+
800
+ return mapping[path] || path.replace(/\./g, "_");
801
+
802
+
803
+ }
804
+
805
+
806
+
807
+ function getScadaPayloadKey(path) {
808
+
809
+
810
+ const mapping = {
811
+
812
+
813
+ "summary.power.total_w": "active_power_w",
814
+
815
+
816
+ "summary.reactive.total_var": "reactive_power_var",
817
+
818
+
819
+ "summary.frequency_hz": "frequency_hz",
820
+
821
+
822
+ "summary.voltage.l1_l2_v": "voltage_l1_l2_v",
823
+
824
+
825
+ "summary.voltage.l2_l3_v": "voltage_l2_l3_v",
826
+
827
+
828
+ "summary.voltage.l3_l1_v": "voltage_l1_l3_v",
829
+
830
+
831
+ "summary.power.l1_w": "power_l1_w",
832
+
833
+
834
+ "summary.power.l2_w": "power_l2_w",
835
+
836
+
837
+ "summary.power.l3_w": "power_l3_w",
838
+
839
+
840
+ "summary.current.l1_a": "current_l1_a",
841
+
842
+
843
+ "summary.current.l2_a": "current_l2_a",
844
+
845
+
846
+ "summary.current.l3_a": "current_l3_a",
847
+
848
+
849
+ "summary.voltage.l1_n_v": "voltage_l1_n_v",
850
+
851
+
852
+ "summary.voltage.l2_n_v": "voltage_l2_n_v",
853
+
854
+
855
+ "summary.voltage.l3_n_v": "voltage_l3_n_v"
856
+
857
+
858
+ };
859
+
860
+
861
+
862
+ return mapping[path] || getCompactPayloadKey(path);
863
+
864
+
865
+ }
866
+
867
+
868
+
869
+ function flattenNumericFields(source, prefix, target) {
870
+
871
+
872
+ Object.keys(source || {}).forEach(function (key) {
873
+
874
+
875
+ const value = source[key];
876
+
877
+
878
+ const nextKey = prefix ? prefix + "_" + key : key;
879
+
880
+
881
+
882
+ if (typeof value === "number" && Number.isFinite(value)) {
883
+
884
+
885
+ target[nextKey] = value;
886
+
887
+
888
+ return;
889
+
890
+
891
+ }
892
+
893
+
894
+
895
+ if (typeof value === "boolean") {
896
+
897
+
898
+ target[nextKey] = value;
899
+
900
+
901
+ return;
902
+
903
+
904
+ }
905
+
906
+
907
+
908
+ if (value && typeof value === "object" && !Array.isArray(value)) {
909
+
910
+
911
+ flattenNumericFields(value, nextKey, target);
912
+
913
+
914
+ }
915
+
916
+
917
+ });
918
+
919
+
920
+ }
921
+
922
+
923
+
924
+ function setInfluxField(target, key, value) {
925
+
926
+
927
+ if (!key) {
928
+
929
+
930
+ return;
931
+
932
+
933
+ }
934
+
935
+
936
+
937
+ if (typeof value === "number" && Number.isFinite(value)) {
938
+
939
+
940
+ target[key] = value;
941
+
942
+
943
+ return;
944
+
945
+
946
+ }
947
+
948
+
949
+
950
+ if (typeof value === "boolean") {
951
+
952
+
953
+ target[key] = value;
954
+
955
+
956
+ }
957
+
958
+
959
+ }
960
+
961
+
962
+
963
+ function formatSingleStatus(path, value) {
964
+
965
+
966
+ const label = getPayloadLabel(path);
967
+
968
+
969
+ const unit = getPayloadUnit(path);
970
+
971
+
972
+
973
+ if (value === undefined || value === null) {
974
+
975
+
976
+ return label + ": n/a";
977
+
978
+
979
+ }
980
+
981
+
982
+
983
+ if (unit) {
984
+
985
+
986
+ return label + ": " + String(value) + " " + unit;
987
+
988
+
989
+ }
990
+
991
+
992
+
993
+ return label + ": " + String(value);
994
+
995
+
996
+ }
997
+
998
+
999
+
1000
+ function registerInfluxMeasurement(nodeId, identityKey) {
1001
+
1002
+
1003
+ const key = String(identityKey || "").trim().toLowerCase();
1004
+
1005
+
1006
+ if (!key) {
1007
+
1008
+
1009
+ return;
1010
+
1011
+
1012
+ }
1013
+
1014
+
1015
+
1016
+ if (!influxMeasurementRegistry.has(key)) {
1017
+
1018
+
1019
+ influxMeasurementRegistry.set(key, new Set());
1020
+
1021
+
1022
+ }
1023
+
1024
+
1025
+
1026
+ influxMeasurementRegistry.get(key).add(nodeId);
1027
+
1028
+
1029
+ }
1030
+
1031
+
1032
+
1033
+ function unregisterInfluxMeasurement(nodeId, identityKey) {
1034
+
1035
+
1036
+ const key = String(identityKey || "").trim().toLowerCase();
1037
+
1038
+
1039
+ if (!key || !influxMeasurementRegistry.has(key)) {
1040
+
1041
+
1042
+ return;
1043
+
1044
+
1045
+ }
1046
+
1047
+
1048
+
1049
+ const ids = influxMeasurementRegistry.get(key);
1050
+
1051
+
1052
+ ids.delete(nodeId);
1053
+
1054
+
1055
+
1056
+ if (!ids.size) {
1057
+
1058
+
1059
+ influxMeasurementRegistry.delete(key);
1060
+
1061
+
1062
+ }
1063
+
1064
+
1065
+ }
1066
+
1067
+
1068
+
1069
+ function hasDuplicateInfluxMeasurement(nodeId, identityKey) {
1070
+
1071
+
1072
+ const key = String(identityKey || "").trim().toLowerCase();
1073
+
1074
+
1075
+ if (!key || !influxMeasurementRegistry.has(key)) {
1076
+
1077
+
1078
+ return false;
1079
+
1080
+
1081
+ }
1082
+
1083
+
1084
+
1085
+ const ids = influxMeasurementRegistry.get(key);
1086
+
1087
+
1088
+ return ids.size > 1 || (ids.size === 1 && !ids.has(nodeId));
1089
+
1090
+
1091
+ }
1092
+
1093
+
1094
+
1095
+ function dumpBuffer(buffer) {
1096
+
1097
+
1098
+ return buffer.toString("hex").toUpperCase();
1099
+
1100
+
1101
+ }
1102
+
1103
+
1104
+
1105
+ function readSwappedInt32BE(buffer, offset) {
1106
+
1107
+
1108
+ const lowWord = buffer.readUInt16BE(offset);
1109
+
1110
+
1111
+ const highWord = buffer.readUInt16BE(offset + 2);
1112
+
1113
+
1114
+ let value = (highWord * 0x10000) + lowWord;
1115
+
1116
+
1117
+
1118
+ if (value >= 0x80000000) {
1119
+
1120
+
1121
+ value = value - 0x100000000;
1122
+
1123
+
1124
+ }
1125
+
1126
+
1127
+
1128
+ return value;
1129
+
1130
+
1131
+ }
1132
+
1133
+
1134
+
1135
+ function parsePhaseSequence(raw) {
1136
+
1137
+
1138
+ if (raw === -1) {
1139
+
1140
+
1141
+ return "L1-L3-L2";
1142
+
1143
+
1144
+ }
1145
+
1146
+
1147
+ if (raw === 0) {
1148
+
1149
+
1150
+ return "L1-L2-L3";
1151
+
1152
+
1153
+ }
1154
+
1155
+
1156
+ return String(raw);
1157
+
1158
+
1159
+ }
1160
+
1161
+
1162
+
1163
+ function parseMeterResponse(buffer, expectedUnitId) {
1164
+
1165
+
1166
+ if (!Buffer.isBuffer(buffer)) {
1167
+
1168
+
1169
+ throw new Error("Raspuns invalid");
1170
+
1171
+
1172
+ }
1173
+
1174
+
1175
+
1176
+ if (buffer.length < 9) {
1177
+
1178
+
1179
+ throw new Error("Raspuns Modbus prea scurt");
1180
+
1181
+
1182
+ }
1183
+
1184
+
1185
+
1186
+ const transactionId = buffer.readUInt16BE(0);
1187
+
1188
+
1189
+ if (transactionId !== TRANSACTION_ID) {
1190
+
1191
+
1192
+ throw new Error("Transaction ID invalid: " + transactionId);
1193
+
1194
+
1195
+ }
1196
+
1197
+
1198
+
1199
+ const protocolId = buffer.readUInt16BE(2);
1200
+
1201
+
1202
+ if (protocolId !== PROTOCOL_ID) {
1203
+
1204
+
1205
+ throw new Error("Protocol ID invalid: " + protocolId);
1206
+
1207
+
1208
+ }
1209
+
1210
+
1211
+
1212
+ const messageLength = buffer.readUInt16BE(4);
1213
+
1214
+
1215
+ if (!Number.isFinite(messageLength) || messageLength <= 0) {
1216
+
1217
+
1218
+ throw new Error("Message length invalid");
1219
+
1220
+
1221
+ }
1222
+
1223
+
1224
+
1225
+ const expectedTotalLengthFromHeader = 6 + messageLength;
1226
+
1227
+
1228
+ if (buffer.length < expectedTotalLengthFromHeader) {
1229
+
1230
+
1231
+ throw new Error("Pachet Modbus incomplet");
1232
+
1233
+
1234
+ }
1235
+
1236
+
1237
+
1238
+ const unitId = buffer.readUInt8(6);
1239
+
1240
+
1241
+ if (unitId !== expectedUnitId) {
1242
+
1243
+
1244
+ throw new Error("Unit ID invalid: " + unitId);
1245
+
1246
+
1247
+ }
1248
+
1249
+
1250
+
1251
+ const functionCode = buffer.readUInt8(7);
1252
+
1253
+
1254
+
1255
+ if (functionCode === (FUNCTION_CODE | 0x80)) {
1256
+
1257
+
1258
+ const exceptionCode = buffer.length > 8 ? buffer.readUInt8(8) : -1;
1259
+
1260
+
1261
+ throw new Error("Modbus exception code: " + exceptionCode);
1262
+
1263
+
1264
+ }
1265
+
1266
+
1267
+
1268
+ if (functionCode !== FUNCTION_CODE) {
1269
+
1270
+
1271
+ throw new Error("Function code invalid: " + functionCode);
1272
+
1273
+
1274
+ }
1275
+
1276
+
1277
+
1278
+ const byteCount = buffer.readUInt8(8);
1279
+
1280
+
1281
+ if (byteCount !== EXPECTED_BYTE_COUNT) {
1282
+
1283
+
1284
+ throw new Error("Byte count invalid: " + byteCount);
1285
+
1286
+
1287
+ }
1288
+
1289
+
1290
+
1291
+ const expectedPayloadEnd = 9 + byteCount;
1292
+
1293
+
1294
+ if (buffer.length < expectedPayloadEnd) {
1295
+
1296
+
1297
+ throw new Error("Date Modbus incomplete");
1298
+
1299
+
1300
+ }
1301
+
1302
+
1303
+
1304
+ const data = buffer.slice(9, expectedPayloadEnd);
1305
+
1306
+
1307
+
1308
+ function read32(offset, scale) {
1309
+
1310
+
1311
+ return readSwappedInt32BE(data, offset) / scale;
1312
+
1313
+
1314
+ }
1315
+
1316
+
1317
+
1318
+ function read16(offset, scale) {
1319
+
1320
+
1321
+ return data.readInt16BE(offset) / scale;
1322
+
1323
+
1324
+ }
1325
+
1326
+
1327
+
1328
+ const l1_n_voltage = read32(0, 10); // 0000h
1329
+
1330
+
1331
+ const l2_n_voltage = read32(4, 10); // 0002h
1332
+
1333
+
1334
+ const l3_n_voltage = read32(8, 10); // 0004h
1335
+
1336
+
1337
+
1338
+ const l1_l2_voltage = read32(12, 10); // 0006h
1339
+
1340
+
1341
+ const l2_l3_voltage = read32(16, 10); // 0008h
1342
+
1343
+
1344
+ const l3_l1_voltage = read32(20, 10); // 000Ah
1345
+
1346
+
1347
+
1348
+ const l1_current = read32(24, 1000); // 000Ch
1349
+
1350
+
1351
+ const l2_current = read32(28, 1000); // 000Eh
1352
+
1353
+
1354
+ const l3_current = read32(32, 1000); // 0010h
1355
+
1356
+
1357
+
1358
+ const l1_power = read32(36, 10); // 0012h
1359
+
1360
+
1361
+ const l2_power = read32(40, 10); // 0014h
1362
+
1363
+
1364
+ const l3_power = read32(44, 10); // 0016h
1365
+
1366
+
1367
+
1368
+ const l1_va = read32(48, 10); // 0018h
1369
+
1370
+
1371
+ const l2_va = read32(52, 10); // 001Ah
1372
+
1373
+
1374
+ const l3_va = read32(56, 10); // 001Ch
1375
+
1376
+
1377
+
1378
+ const l1_var = read32(60, 10); // 001Eh
1379
+
1380
+
1381
+ const l2_var = read32(64, 10); // 0020h
1382
+
1383
+
1384
+ const l3_var = read32(68, 10); // 0022h
1385
+
1386
+
1387
+
1388
+ const total_power = read32(80, 10); // 0028h W Σ
1389
+
1390
+
1391
+ const total_va = read32(84, 10); // 002Ah VA Σ
1392
+
1393
+
1394
+ const total_var = read32(88, 10); // 002Ch VAR Σ
1395
+
1396
+
1397
+
1398
+ const pf_l1 = read16(92, 1000); // 002Eh
1399
+
1400
+
1401
+ const pf_l2 = read16(94, 1000); // 002Fh
1402
+
1403
+
1404
+ const pf_l3 = read16(96, 1000); // 0030h
1405
+
1406
+
1407
+
1408
+ const ph_sq = data.readInt16BE(100); // 0032h
1409
+
1410
+
1411
+ const ph_sq_msg = parsePhaseSequence(ph_sq);
1412
+
1413
+
1414
+
1415
+ const freq = data.readUInt16BE(102) / 10; // 0033h
1416
+
1417
+
1418
+
1419
+ const energy = read32(104, 10); // 0034h KWh(+) TOT
1420
+
1421
+
1422
+ const reactive_energy = read32(108, 10); // 0036h Kvarh(+) TOT
1423
+
1424
+
1425
+
1426
+ const l1_energy = read32(128, 10); // 0040h KWh(+) L1
1427
+
1428
+
1429
+ const l2_energy = read32(132, 10); // 0042h KWh(+) L2
1430
+
1431
+
1432
+ const l3_energy = read32(136, 10); // 0044h KWh(+) L3
1433
+
1434
+
1435
+
1436
+ const t1_energy = read32(140, 10); // 0046h KWh(+) T1
1437
+
1438
+
1439
+ const t2_energy = read32(144, 10); // 0048h KWh(+) T2
1440
+
1441
+
1442
+ const t3_energy = read32(148, 10); // 004Ah KWh(+) T3
1443
+
1444
+
1445
+ const t4_energy = read32(152, 10); // 004Ch KWh(+) T4
1446
+
1447
+
1448
+
1449
+ const exported_energy = read32(156, 10); // 004Eh KWh(-) TOT
1450
+
1451
+
1452
+ const exported_reactive = read32(160, 10); // 0050h Kvarh(-) TOT
1453
+
1454
+
1455
+
1456
+ const t1_reactive_energy = read32(220, 10); // 006Eh Kvarh(+) T1
1457
+
1458
+
1459
+ const t2_reactive_energy = read32(224, 10); // 0070h Kvarh(+) T2
1460
+
1461
+
1462
+ const t3_reactive_energy = read32(228, 10); // 0072h Kvarh(+) T3
1463
+
1464
+
1465
+ const t4_reactive_energy = read32(232, 10); // 0074h Kvarh(+) T4
1466
+
1467
+
1468
+
1469
+ return {
1470
+
1471
+
1472
+ summary: {
1473
+
1474
+
1475
+ voltage: {
1476
+
1477
+
1478
+ l1_n_v: l1_n_voltage,
1479
+
1480
+
1481
+ l2_n_v: l2_n_voltage,
1482
+
1483
+
1484
+ l3_n_v: l3_n_voltage,
1485
+
1486
+
1487
+ l1_l2_v: l1_l2_voltage,
1488
+
1489
+
1490
+ l2_l3_v: l2_l3_voltage,
1491
+
1492
+
1493
+ l3_l1_v: l3_l1_voltage
1494
+
1495
+
1496
+ },
1497
+
1498
+
1499
+ current: {
1500
+
1501
+
1502
+ l1_a: l1_current,
1503
+
1504
+
1505
+ l2_a: l2_current,
1506
+
1507
+
1508
+ l3_a: l3_current
1509
+
1510
+
1511
+ },
1512
+
1513
+
1514
+ power: {
1515
+
1516
+
1517
+ l1_w: l1_power,
1518
+
1519
+
1520
+ l2_w: l2_power,
1521
+
1522
+
1523
+ l3_w: l3_power,
1524
+
1525
+
1526
+ total_w: total_power
1527
+
1528
+
1529
+ },
1530
+
1531
+
1532
+ apparent: {
1533
+
1534
+
1535
+ l1_va: l1_va,
1536
+
1537
+
1538
+ l2_va: l2_va,
1539
+
1540
+
1541
+ l3_va: l3_va,
1542
+
1543
+
1544
+ total_va: total_va
1545
+
1546
+
1547
+ },
1548
+
1549
+
1550
+ reactive: {
1551
+
1552
+
1553
+ l1_var: l1_var,
1554
+
1555
+
1556
+ l2_var: l2_var,
1557
+
1558
+
1559
+ l3_var: l3_var,
1560
+
1561
+
1562
+ total_var: total_var
1563
+
1564
+
1565
+ },
1566
+
1567
+
1568
+ power_factor: {
1569
+
1570
+
1571
+ l1: pf_l1,
1572
+
1573
+
1574
+ l2: pf_l2,
1575
+
1576
+
1577
+ l3: pf_l3
1578
+
1579
+
1580
+ },
1581
+
1582
+
1583
+ frequency_hz: freq,
1584
+
1585
+
1586
+ phase_sequence: ph_sq_msg,
1587
+
1588
+
1589
+ energy: {
1590
+
1591
+
1592
+ import_kwh: energy,
1593
+
1594
+
1595
+ reactive_import_kvarh: reactive_energy,
1596
+
1597
+
1598
+ l1_kwh: l1_energy,
1599
+
1600
+
1601
+ l2_kwh: l2_energy,
1602
+
1603
+
1604
+ l3_kwh: l3_energy,
1605
+
1606
+
1607
+ tariff: {
1608
+
1609
+
1610
+ t1_kwh: t1_energy,
1611
+
1612
+
1613
+ t2_kwh: t2_energy,
1614
+
1615
+
1616
+ t3_kwh: t3_energy,
1617
+
1618
+
1619
+ t4_kwh: t4_energy
1620
+
1621
+
1622
+ },
1623
+
1624
+
1625
+ export_kwh: exported_energy,
1626
+
1627
+
1628
+ export_reactive_kvarh: exported_reactive,
1629
+
1630
+
1631
+ reactive_tariff: {
1632
+
1633
+
1634
+ t1_kvarh: t1_reactive_energy,
1635
+
1636
+
1637
+ t2_kvarh: t2_reactive_energy,
1638
+
1639
+
1640
+ t3_kvarh: t3_reactive_energy,
1641
+
1642
+
1643
+ t4_kvarh: t4_reactive_energy
1644
+
1645
+
1646
+ }
1647
+
1648
+
1649
+ }
1650
+
1651
+
1652
+ },
1653
+
1654
+
1655
+ meta: {
1656
+
1657
+
1658
+ unit_id: expectedUnitId,
1659
+
1660
+
1661
+ register_count: REGISTER_COUNT,
1662
+
1663
+
1664
+ start_address: START_ADDRESS,
1665
+
1666
+
1667
+ timestamp: new Date().toISOString(),
1668
+
1669
+ timestamp_ms: Date.now(),
1670
+
1671
+
1672
+ raw_hex: dumpBuffer(buffer.slice(0, expectedPayloadEnd))
1673
+
1674
+
1675
+ }
1676
+
1677
+
1678
+ };
1679
+
1680
+
1681
+ }
1682
+
1683
+
1684
+
1685
+ function buildRequestBuffer(unitId) {
1686
+
1687
+
1688
+ const buffer = Buffer.alloc(12);
1689
+
1690
+
1691
+
1692
+ buffer.writeUInt16BE(TRANSACTION_ID, 0);
1693
+
1694
+
1695
+ buffer.writeUInt16BE(PROTOCOL_ID, 2);
1696
+
1697
+
1698
+ buffer.writeUInt16BE(MESSAGE_LENGTH, 4);
1699
+
1700
+
1701
+ buffer.writeUInt8(unitId, 6);
1702
+
1703
+
1704
+ buffer.writeUInt8(FUNCTION_CODE, 7);
1705
+
1706
+
1707
+ buffer.writeUInt16BE(START_ADDRESS, 8);
1708
+
1709
+
1710
+ buffer.writeUInt16BE(REGISTER_COUNT, 10);
1711
+
1712
+
1713
+
1714
+ return buffer;
1715
+
1716
+
1717
+ }
1718
+
1719
+
1720
+
1721
+ function CarloGavazziNode(config) {
1722
+
1723
+
1724
+ RED.nodes.createNode(this, config);
1725
+
1726
+
1727
+ const node = this;
1728
+
1729
+ runLicensedNode(node, RED, config, function startLicensedRuntime() {
1730
+
1731
+
1732
+
1733
+ node.name = String(config.name || "").trim();
1734
+
1735
+
1736
+ node.host = String(config.host || "").trim();
1737
+
1738
+
1739
+ node.port = toPositiveNumber(config.port, 502);
1740
+
1741
+
1742
+ node.unitId = toPositiveNumber(config.unitId, 1);
1743
+
1744
+
1745
+ node.timestep = toPollInterval(config.timestep, 1000);
1746
+
1747
+
1748
+ node.expertMode = !!config.expertMode;
1749
+
1750
+
1751
+ node.payloadFields = sanitizePayloadFieldsForMode(config.payloadFields, !!config.multiGrid, !!config.bessControl, !!config.controlExportReactive, !!config.scadaServer, !!config.influxDb, node.expertMode);
1752
+
1753
+
1754
+ node.multiGrid = !!config.multiGrid;
1755
+
1756
+
1757
+ node.bessControl = !!config.bessControl;
1758
+
1759
+
1760
+ node.controlExportReactive = !!config.controlExportReactive;
1761
+
1762
+
1763
+ node.scadaServer = !!config.scadaServer;
1764
+
1765
+
1766
+ node.influxDb = !!config.influxDb;
1767
+
1768
+ if (node.multiGrid) {
1769
+ node.bessControl = false; node.controlExportReactive = false; node.scadaServer = false;
1770
+ } else if (node.bessControl) {
1771
+ node.controlExportReactive = false; node.scadaServer = false;
1772
+ } else if (node.controlExportReactive) {
1773
+ node.scadaServer = false;
1774
+ }
1775
+
1776
+
1777
+ node.influxMeasurement = String(config.influxMeasurement || "").trim();
1778
+ node.influxNode = String(config.influxNode || "").trim();
1779
+
1780
+
1781
+ node.meterId = String(config.meterId || "").trim();
1782
+
1783
+
1784
+
1785
+ let intervalHandle = null;
1786
+
1787
+
1788
+ let startupHandle = null;
1789
+
1790
+
1791
+ let running = false;
1792
+
1793
+
1794
+ let closed = false;
1795
+
1796
+
1797
+ let registeredInfluxIdentityKey = "";
1798
+
1799
+
1800
+
1801
+ function getEffectiveMeterId() {
1802
+
1803
+
1804
+ return node.meterId || ("meter" + String(node.unitId));
1805
+
1806
+
1807
+ }
1808
+
1809
+
1810
+
1811
+ function getInfluxMeasurement() {
1812
+
1813
+
1814
+ return node.influxMeasurement || getEffectiveMeterId();
1815
+
1816
+
1817
+ }
1818
+
1819
+
1820
+
1821
+ function getInfluxIdentityKey() {
1822
+
1823
+
1824
+ return String(getInfluxMeasurement() || "").trim().toLowerCase() + "::" + String(getEffectiveMeterId() || "").trim().toLowerCase();
1825
+
1826
+
1827
+ }
1828
+
1829
+
1830
+
1831
+ function hasInfluxMeasurementConflict() {
1832
+
1833
+
1834
+ return node.influxDb && hasDuplicateInfluxMeasurement(node.id, getInfluxIdentityKey());
1835
+
1836
+
1837
+ }
1838
+
1839
+
1840
+
1841
+ function buildInfluxFields(baseParsed, selectedFields) {
1842
+
1843
+
1844
+ const fields = {};
1845
+
1846
+
1847
+
1848
+ if (selectedFields.includes("full")) {
1849
+
1850
+
1851
+ const summary = baseParsed && baseParsed.summary ? baseParsed.summary : {};
1852
+
1853
+
1854
+ setInfluxField(fields, "voltage_l1_n_v", getByPath(summary, "voltage.l1_n_v"));
1855
+
1856
+
1857
+ setInfluxField(fields, "voltage_l2_n_v", getByPath(summary, "voltage.l2_n_v"));
1858
+
1859
+
1860
+ setInfluxField(fields, "voltage_l3_n_v", getByPath(summary, "voltage.l3_n_v"));
1861
+
1862
+
1863
+ setInfluxField(fields, "voltage_l1_l2_v", getByPath(summary, "voltage.l1_l2_v"));
1864
+
1865
+
1866
+ setInfluxField(fields, "voltage_l2_l3_v", getByPath(summary, "voltage.l2_l3_v"));
1867
+
1868
+
1869
+ setInfluxField(fields, "voltage_l3_l1_v", getByPath(summary, "voltage.l3_l1_v"));
1870
+
1871
+
1872
+ setInfluxField(fields, "current_l1_a", getByPath(summary, "current.l1_a"));
1873
+
1874
+
1875
+ setInfluxField(fields, "current_l2_a", getByPath(summary, "current.l2_a"));
1876
+
1877
+
1878
+ setInfluxField(fields, "current_l3_a", getByPath(summary, "current.l3_a"));
1879
+
1880
+
1881
+ setInfluxField(fields, "power_l1_w", getByPath(summary, "power.l1_w"));
1882
+
1883
+
1884
+ setInfluxField(fields, "power_l2_w", getByPath(summary, "power.l2_w"));
1885
+
1886
+
1887
+ setInfluxField(fields, "power_l3_w", getByPath(summary, "power.l3_w"));
1888
+
1889
+
1890
+ setInfluxField(fields, "power_w", getByPath(summary, "power.total_w"));
1891
+
1892
+
1893
+ setInfluxField(fields, "apparent_l1_va", getByPath(summary, "apparent.l1_va"));
1894
+
1895
+
1896
+ setInfluxField(fields, "apparent_l2_va", getByPath(summary, "apparent.l2_va"));
1897
+
1898
+
1899
+ setInfluxField(fields, "apparent_l3_va", getByPath(summary, "apparent.l3_va"));
1900
+
1901
+
1902
+ setInfluxField(fields, "apparent_total_va", getByPath(summary, "apparent.total_va"));
1903
+
1904
+
1905
+ setInfluxField(fields, "reactive_l1_var", getByPath(summary, "reactive.l1_var"));
1906
+
1907
+
1908
+ setInfluxField(fields, "reactive_l2_var", getByPath(summary, "reactive.l2_var"));
1909
+
1910
+
1911
+ setInfluxField(fields, "reactive_l3_var", getByPath(summary, "reactive.l3_var"));
1912
+
1913
+
1914
+ setInfluxField(fields, "reactive_total_var", getByPath(summary, "reactive.total_var"));
1915
+
1916
+
1917
+ setInfluxField(fields, "power_factor_l1", getByPath(summary, "power_factor.l1"));
1918
+
1919
+
1920
+ setInfluxField(fields, "power_factor_l2", getByPath(summary, "power_factor.l2"));
1921
+
1922
+
1923
+ setInfluxField(fields, "power_factor_l3", getByPath(summary, "power_factor.l3"));
1924
+
1925
+
1926
+ setInfluxField(fields, "frequency_hz", getByPath(summary, "frequency_hz"));
1927
+
1928
+
1929
+ setInfluxField(fields, "energy_import_kwh", getByPath(summary, "energy.import_kwh"));
1930
+
1931
+
1932
+ setInfluxField(fields, "energy_reactive_import_kvarh", getByPath(summary, "energy.reactive_import_kvarh"));
1933
+
1934
+
1935
+ setInfluxField(fields, "energy_l1_kwh", getByPath(summary, "energy.l1_kwh"));
1936
+
1937
+
1938
+ setInfluxField(fields, "energy_l2_kwh", getByPath(summary, "energy.l2_kwh"));
1939
+
1940
+
1941
+ setInfluxField(fields, "energy_l3_kwh", getByPath(summary, "energy.l3_kwh"));
1942
+
1943
+
1944
+ setInfluxField(fields, "energy_t1_kwh", getByPath(summary, "energy.tariff.t1_kwh"));
1945
+
1946
+
1947
+ setInfluxField(fields, "energy_t2_kwh", getByPath(summary, "energy.tariff.t2_kwh"));
1948
+
1949
+
1950
+ setInfluxField(fields, "energy_t3_kwh", getByPath(summary, "energy.tariff.t3_kwh"));
1951
+
1952
+
1953
+ setInfluxField(fields, "energy_t4_kwh", getByPath(summary, "energy.tariff.t4_kwh"));
1954
+
1955
+
1956
+ setInfluxField(fields, "energy_export_kwh", getByPath(summary, "energy.export_kwh"));
1957
+
1958
+
1959
+ setInfluxField(fields, "energy_export_reactive_kvarh", getByPath(summary, "energy.export_reactive_kvarh"));
1960
+
1961
+
1962
+ setInfluxField(fields, "energy_reactive_t1_kvarh", getByPath(summary, "energy.reactive_tariff.t1_kvarh"));
1963
+
1964
+
1965
+ setInfluxField(fields, "energy_reactive_t2_kvarh", getByPath(summary, "energy.reactive_tariff.t2_kvarh"));
1966
+
1967
+
1968
+ setInfluxField(fields, "energy_reactive_t3_kvarh", getByPath(summary, "energy.reactive_tariff.t3_kvarh"));
1969
+
1970
+
1971
+ setInfluxField(fields, "energy_reactive_t4_kvarh", getByPath(summary, "energy.reactive_tariff.t4_kvarh"));
1972
+
1973
+
1974
+ return fields;
1975
+
1976
+
1977
+ }
1978
+
1979
+
1980
+
1981
+ selectedFields.forEach(function (field) {
1982
+
1983
+
1984
+ const value = getByPath(baseParsed, field);
1985
+
1986
+
1987
+ if (typeof value === "number" && Number.isFinite(value)) {
1988
+
1989
+
1990
+ fields[getCompactPayloadKey(field)] = value;
1991
+
1992
+
1993
+ return;
1994
+
1995
+
1996
+ }
1997
+
1998
+
1999
+
2000
+ if (typeof value === "boolean") {
2001
+
2002
+
2003
+ fields[getCompactPayloadKey(field)] = value;
2004
+
2005
+
2006
+ return;
2007
+
2008
+
2009
+ }
2010
+
2011
+
2012
+
2013
+ if (value && typeof value === "object" && !Array.isArray(value)) {
2014
+
2015
+
2016
+ setInfluxField(fields, getCompactPayloadKey(field), getByPath(baseParsed, field));
2017
+
2018
+
2019
+ }
2020
+
2021
+
2022
+ });
2023
+
2024
+
2025
+
2026
+ return fields;
2027
+
2028
+
2029
+ }
2030
+
2031
+
2032
+
2033
+ function buildInfluxMessage(baseParsed, selectedFields) {
2034
+
2035
+
2036
+ const timestamp = baseParsed && baseParsed.meta ? baseParsed.meta.timestamp : new Date().toISOString();
2037
+
2038
+
2039
+ const fields = buildInfluxFields(baseParsed, selectedFields);
2040
+
2041
+
2042
+ const measurement = getInfluxMeasurement();
2043
+
2044
+
2045
+ const meterId = getEffectiveMeterId();
2046
+
2047
+
2048
+
2049
+ return {
2050
+
2051
+
2052
+ topic: meterId,
2053
+
2054
+
2055
+ meter_id: meterId,
2056
+
2057
+ source_id: meterId,
2058
+
2059
+
2060
+ payload_field: selectedFields.includes("full") ? "full" : (selectedFields[0] || "full"),
2061
+
2062
+
2063
+ carloGavazzi: baseParsed,
2064
+
2065
+
2066
+ timestamp: timestamp,
2067
+
2068
+ timestamp_ms: baseParsed && baseParsed.meta && Number.isFinite(Number(baseParsed.meta.timestamp_ms))
2069
+ ? Number(baseParsed.meta.timestamp_ms)
2070
+ : Date.parse(timestamp),
2071
+
2072
+
2073
+ payload: fields,
2074
+
2075
+
2076
+ influx: {
2077
+
2078
+
2079
+ measurement: measurement,
2080
+
2081
+
2082
+ tags: {
2083
+
2084
+
2085
+ device_type: "carlo_gavazzi",
2086
+
2087
+
2088
+ meter_id: meterId,
2089
+
2090
+
2091
+ host: node.host,
2092
+
2093
+
2094
+ unit_id: String(node.unitId)
2095
+
2096
+
2097
+ },
2098
+
2099
+
2100
+ fields: fields,
2101
+
2102
+
2103
+ timestamp: timestamp
2104
+
2105
+
2106
+ }
2107
+
2108
+
2109
+ };
2110
+
2111
+
2112
+ }
2113
+
2114
+
2115
+
2116
+ function buildSingleMessage(baseParsed, field, value) {
2117
+
2118
+
2119
+ const outMsg = {
2120
+
2121
+
2122
+ payload_field: field,
2123
+
2124
+
2125
+ carloGavazzi: baseParsed,
2126
+
2127
+
2128
+ timestamp: baseParsed && baseParsed.meta ? baseParsed.meta.timestamp : new Date().toISOString()
2129
+
2130
+
2131
+ };
2132
+
2133
+ const meterId = getEffectiveMeterId();
2134
+ outMsg.meter_id = meterId;
2135
+ outMsg.source_id = meterId;
2136
+ outMsg.ip = node.host;
2137
+ outMsg.host = node.host;
2138
+ outMsg.port = node.port;
2139
+ outMsg.unit_id = node.unitId;
2140
+ outMsg.device_type = "carlo_gavazzi";
2141
+ outMsg.timestamp_ms = baseParsed && baseParsed.meta && Number.isFinite(Number(baseParsed.meta.timestamp_ms))
2142
+ ? Number(baseParsed.meta.timestamp_ms)
2143
+ : Date.parse(outMsg.timestamp);
2144
+
2145
+
2146
+
2147
+ if (node.multiGrid) {
2148
+
2149
+
2150
+ outMsg.topic = getEffectiveMeterId();
2151
+
2152
+
2153
+ outMsg.meter_id = getEffectiveMeterId();
2154
+
2155
+
2156
+
2157
+ if (field === "full") {
2158
+
2159
+
2160
+ outMsg.payload = baseParsed;
2161
+
2162
+
2163
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
2164
+
2165
+
2166
+ outMsg.payload = value;
2167
+
2168
+
2169
+ } else {
2170
+
2171
+
2172
+ outMsg.payload = {};
2173
+
2174
+
2175
+ outMsg.payload[getCompactPayloadKey(field)] = value;
2176
+
2177
+
2178
+ }
2179
+
2180
+
2181
+ } else if (node.bessControl) {
2182
+
2183
+
2184
+ outMsg.topic = getEffectiveMeterId();
2185
+
2186
+
2187
+ outMsg.meter_id = getEffectiveMeterId();
2188
+
2189
+
2190
+
2191
+ if (field === "full") {
2192
+
2193
+
2194
+ outMsg.payload = baseParsed;
2195
+
2196
+
2197
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
2198
+
2199
+
2200
+ outMsg.payload = value;
2201
+
2202
+
2203
+ } else {
2204
+
2205
+
2206
+ outMsg.payload = {};
2207
+
2208
+
2209
+ outMsg.payload[getCompactPayloadKey(field)] = value;
2210
+
2211
+
2212
+ }
2213
+
2214
+
2215
+ } else if (node.controlExportReactive) {
2216
+
2217
+
2218
+ outMsg.topic = getEffectiveMeterId();
2219
+
2220
+
2221
+ outMsg.meter_id = getEffectiveMeterId();
2222
+
2223
+
2224
+
2225
+ if (field === "full") {
2226
+
2227
+
2228
+ outMsg.payload = baseParsed;
2229
+
2230
+
2231
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
2232
+
2233
+
2234
+ outMsg.payload = value;
2235
+
2236
+
2237
+ } else {
2238
+
2239
+
2240
+ outMsg.payload = {};
2241
+
2242
+
2243
+ outMsg.payload[getCompactPayloadKey(field)] = value;
2244
+
2245
+
2246
+ }
2247
+
2248
+
2249
+ } else if (node.scadaServer) {
2250
+
2251
+
2252
+ outMsg.topic = getEffectiveMeterId();
2253
+
2254
+
2255
+ outMsg.meter_id = getEffectiveMeterId();
2256
+
2257
+
2258
+
2259
+ if (field === "full") {
2260
+
2261
+
2262
+ outMsg.payload = baseParsed;
2263
+
2264
+
2265
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
2266
+
2267
+
2268
+ outMsg.payload = value;
2269
+
2270
+
2271
+ } else {
2272
+
2273
+
2274
+ outMsg.payload = {};
2275
+
2276
+
2277
+ outMsg.payload[getScadaPayloadKey(field)] = value;
2278
+
2279
+
2280
+ }
2281
+
2282
+
2283
+ } else {
2284
+
2285
+
2286
+ outMsg.topic = getPayloadLabel(field);
2287
+
2288
+
2289
+ outMsg.payload = value;
2290
+
2291
+
2292
+ }
2293
+
2294
+
2295
+
2296
+ return outMsg;
2297
+
2298
+
2299
+ }
2300
+
2301
+
2302
+
2303
+ function setDisconnectedStatus(message) {
2304
+
2305
+
2306
+ node.status({
2307
+
2308
+
2309
+ fill: "red",
2310
+
2311
+
2312
+ shape: "ring",
2313
+
2314
+
2315
+ text: message
2316
+
2317
+
2318
+ });
2319
+
2320
+
2321
+ }
2322
+
2323
+
2324
+
2325
+ function setReadingStatus() {
2326
+
2327
+
2328
+ node.status({
2329
+
2330
+
2331
+ fill: "yellow",
2332
+
2333
+
2334
+ shape: "dot",
2335
+
2336
+
2337
+ text: "citire..."
2338
+
2339
+
2340
+ });
2341
+
2342
+
2343
+ }
2344
+
2345
+
2346
+
2347
+ function setStatusForSelection(selectedFields, parsed) {
2348
+
2349
+
2350
+ if (selectedFields.includes("full")) {
2351
+
2352
+
2353
+ node.status({
2354
+
2355
+
2356
+ fill: "green",
2357
+
2358
+
2359
+ shape: "dot",
2360
+
2361
+
2362
+ text: "Toate valorile"
2363
+
2364
+
2365
+ });
2366
+
2367
+
2368
+ return;
2369
+
2370
+
2371
+ }
2372
+
2373
+
2374
+
2375
+ if (selectedFields.length === 1) {
2376
+
2377
+
2378
+ const field = selectedFields[0];
2379
+
2380
+
2381
+ const value = getByPath(parsed, field);
2382
+
2383
+
2384
+
2385
+ node.status({
2386
+
2387
+
2388
+ fill: "green",
2389
+
2390
+
2391
+ shape: "dot",
2392
+
2393
+
2394
+ text: formatSingleStatus(field, value)
2395
+
2396
+
2397
+ });
2398
+
2399
+
2400
+ return;
2401
+
2402
+
2403
+ }
2404
+
2405
+
2406
+
2407
+ node.status({
2408
+
2409
+
2410
+ fill: "green",
2411
+
2412
+
2413
+ shape: "dot",
2414
+
2415
+
2416
+ text: String(selectedFields.length) + " valori selectate"
2417
+
2418
+
2419
+ });
2420
+
2421
+
2422
+ }
2423
+
2424
+
2425
+
2426
+ function readMeter() {
2427
+
2428
+
2429
+ return new Promise(function (resolve, reject) {
2430
+
2431
+
2432
+ const socket = new net.Socket();
2433
+
2434
+
2435
+ let chunks = [];
2436
+
2437
+
2438
+ let finished = false;
2439
+
2440
+
2441
+
2442
+ function done(err, result) {
2443
+
2444
+
2445
+ if (finished) {
2446
+
2447
+
2448
+ return;
2449
+
2450
+
2451
+ }
2452
+
2453
+
2454
+ finished = true;
2455
+
2456
+
2457
+
2458
+ try {
2459
+
2460
+
2461
+ socket.destroy();
2462
+
2463
+
2464
+ } catch (e) {
2465
+
2466
+
2467
+ // ignore
2468
+
2469
+
2470
+ }
2471
+
2472
+
2473
+
2474
+ if (err) {
2475
+
2476
+
2477
+ reject(err);
2478
+
2479
+
2480
+ } else {
2481
+
2482
+
2483
+ resolve(result);
2484
+
2485
+
2486
+ }
2487
+
2488
+
2489
+ }
2490
+
2491
+
2492
+
2493
+ socket.setTimeout(SOCKET_TIMEOUT_MS);
2494
+
2495
+
2496
+
2497
+ socket.connect(node.port, node.host, function () {
2498
+
2499
+
2500
+ try {
2501
+
2502
+
2503
+ const request = buildRequestBuffer(node.unitId);
2504
+
2505
+
2506
+ socket.write(request);
2507
+
2508
+
2509
+ } catch (err) {
2510
+
2511
+
2512
+ done(err);
2513
+
2514
+
2515
+ }
2516
+
2517
+
2518
+ });
2519
+
2520
+
2521
+
2522
+ socket.on("data", function (data) {
2523
+
2524
+
2525
+ chunks.push(data);
2526
+
2527
+
2528
+
2529
+ const total = Buffer.concat(chunks);
2530
+
2531
+
2532
+
2533
+ if (total.length >= 9) {
2534
+
2535
+
2536
+ const functionCode = total.readUInt8(7);
2537
+
2538
+
2539
+
2540
+ if (functionCode === (FUNCTION_CODE | 0x80)) {
2541
+
2542
+
2543
+ try {
2544
+
2545
+
2546
+ parseMeterResponse(total, node.unitId);
2547
+
2548
+
2549
+ } catch (err) {
2550
+
2551
+
2552
+ done(err);
2553
+
2554
+
2555
+ }
2556
+
2557
+
2558
+ return;
2559
+
2560
+
2561
+ }
2562
+
2563
+
2564
+
2565
+ const byteCount = total.readUInt8(8);
2566
+
2567
+
2568
+ const expectedLength = 9 + byteCount;
2569
+
2570
+
2571
+
2572
+ if (total.length >= expectedLength) {
2573
+
2574
+
2575
+ try {
2576
+
2577
+
2578
+ const parsed = parseMeterResponse(total.slice(0, expectedLength), node.unitId);
2579
+
2580
+
2581
+ done(null, parsed);
2582
+
2583
+
2584
+ } catch (err) {
2585
+
2586
+
2587
+ done(err);
2588
+
2589
+
2590
+ }
2591
+
2592
+
2593
+ }
2594
+
2595
+
2596
+ }
2597
+
2598
+
2599
+ });
2600
+
2601
+
2602
+
2603
+ socket.on("timeout", function () {
2604
+
2605
+
2606
+ done(new Error("conexiune pierduta: timeout"));
2607
+
2608
+
2609
+ });
2610
+
2611
+
2612
+
2613
+ socket.on("error", function (err) {
2614
+
2615
+
2616
+ done(new Error("conexiune pierduta: " + err.message));
2617
+
2618
+
2619
+ });
2620
+
2621
+
2622
+
2623
+ socket.on("close", function () {
2624
+
2625
+
2626
+ if (!finished && chunks.length === 0) {
2627
+
2628
+
2629
+ done(new Error("conexiune pierduta: fara raspuns"));
2630
+
2631
+
2632
+ }
2633
+
2634
+
2635
+ });
2636
+
2637
+
2638
+ });
2639
+
2640
+
2641
+ }
2642
+
2643
+
2644
+
2645
+ function sendParsedResult(parsed) {
2646
+
2647
+
2648
+ const selectedFields = normalizePayloadFields(
2649
+
2650
+
2651
+ node.payloadFields,
2652
+
2653
+
2654
+ !!(node.expertMode && (node.multiGrid || node.bessControl || node.controlExportReactive || node.scadaServer || node.influxDb))
2655
+
2656
+
2657
+ );
2658
+
2659
+
2660
+
2661
+ setStatusForSelection(selectedFields, parsed);
2662
+
2663
+
2664
+
2665
+ const secondaryInflux = node.influxDb && !!(node.multiGrid || node.bessControl || node.controlExportReactive || node.scadaServer);
2666
+ let influxMessage = null;
2667
+
2668
+ if (node.influxDb) {
2669
+
2670
+
2671
+ if (hasInfluxMeasurementConflict()) {
2672
+
2673
+
2674
+ node.status({
2675
+
2676
+
2677
+ fill: "red",
2678
+
2679
+
2680
+ shape: "ring",
2681
+
2682
+
2683
+ text: "meter_id+measurement duplicat"
2684
+
2685
+
2686
+ });
2687
+
2688
+
2689
+ node.error("Carlo Gavazzi InfluxDB: combinatie duplicata Meter ID + Measurement ('" + getEffectiveMeterId() + "' @ '" + getInfluxMeasurement() + "'). Configureaza un Meter ID unic pentru acelasi measurement.");
2690
+
2691
+
2692
+ return;
2693
+
2694
+
2695
+ }
2696
+
2697
+
2698
+ influxMessage = buildInfluxMessage(parsed, selectedFields);
2699
+
2700
+ if (!secondaryInflux) {
2701
+ node.send(influxMessage);
2702
+ return;
2703
+ }
2704
+ }
2705
+
2706
+ function sendIntegrated(message) {
2707
+ if (influxMessage) {
2708
+ const target = RED.nodes.getNode(node.influxNode);
2709
+ if (target && typeof target.receive === "function") {
2710
+ target.receive(RED.util.cloneMessage(influxMessage));
2711
+ } else {
2712
+ node.warn("Carlo Gavazzi: selecteaza un nod adt-influxdb pentru integrarea secundara.");
2713
+ }
2714
+ }
2715
+ node.send(message);
2716
+ }
2717
+
2718
+
2719
+
2720
+ if (selectedFields.includes("full")) {
2721
+
2722
+
2723
+ node.send(buildSingleMessage(parsed, "full", parsed));
2724
+
2725
+
2726
+ return;
2727
+
2728
+
2729
+ }
2730
+
2731
+
2732
+
2733
+ if (node.multiGrid) {
2734
+
2735
+
2736
+ const aggregatedPayload = {};
2737
+
2738
+
2739
+
2740
+ selectedFields.forEach(function (field) {
2741
+
2742
+
2743
+ aggregatedPayload[getCompactPayloadKey(field)] = getByPath(parsed, field);
2744
+
2745
+
2746
+ });
2747
+
2748
+
2749
+
2750
+ sendIntegrated(buildSingleMessage(parsed, "multi", aggregatedPayload));
2751
+
2752
+
2753
+ return;
2754
+
2755
+
2756
+ }
2757
+
2758
+
2759
+
2760
+ if (node.bessControl) {
2761
+
2762
+
2763
+ const aggregatedPayload = {};
2764
+
2765
+
2766
+
2767
+ selectedFields.forEach(function (field) {
2768
+
2769
+
2770
+ aggregatedPayload[getCompactPayloadKey(field)] = getByPath(parsed, field);
2771
+
2772
+
2773
+ });
2774
+
2775
+
2776
+
2777
+ sendIntegrated(buildSingleMessage(parsed, "bess", aggregatedPayload));
2778
+
2779
+
2780
+ return;
2781
+
2782
+
2783
+ }
2784
+
2785
+
2786
+
2787
+ if (node.controlExportReactive) {
2788
+
2789
+
2790
+ const aggregatedPayload = {};
2791
+
2792
+
2793
+
2794
+ selectedFields.forEach(function (field) {
2795
+
2796
+
2797
+ aggregatedPayload[getCompactPayloadKey(field)] = getByPath(parsed, field);
2798
+
2799
+
2800
+ });
2801
+
2802
+
2803
+
2804
+ sendIntegrated(buildSingleMessage(parsed, "control_export_reactive", aggregatedPayload));
2805
+
2806
+
2807
+ return;
2808
+
2809
+
2810
+ }
2811
+
2812
+
2813
+
2814
+ if (node.scadaServer) {
2815
+
2816
+
2817
+ const aggregatedPayload = {};
2818
+
2819
+
2820
+
2821
+ selectedFields.forEach(function (field) {
2822
+
2823
+
2824
+ aggregatedPayload[getScadaPayloadKey(field)] = getByPath(parsed, field);
2825
+
2826
+
2827
+ });
2828
+
2829
+
2830
+
2831
+ sendIntegrated(buildSingleMessage(parsed, "scada", aggregatedPayload));
2832
+
2833
+
2834
+ return;
2835
+
2836
+
2837
+ }
2838
+
2839
+
2840
+
2841
+ const outputMessages = [];
2842
+
2843
+
2844
+ let i;
2845
+
2846
+
2847
+
2848
+ for (i = 0; i < selectedFields.length; i += 1) {
2849
+
2850
+
2851
+ outputMessages.push(null);
2852
+
2853
+
2854
+ }
2855
+
2856
+
2857
+
2858
+ selectedFields.forEach(function (field, index) {
2859
+
2860
+
2861
+ const value = getByPath(parsed, field);
2862
+
2863
+
2864
+ outputMessages[index] = buildSingleMessage(parsed, field, value);
2865
+
2866
+
2867
+ });
2868
+
2869
+
2870
+
2871
+ node.send(outputMessages);
2872
+
2873
+
2874
+ }
2875
+
2876
+
2877
+
2878
+ async function executeRead() {
2879
+
2880
+
2881
+ if (closed || running) {
2882
+
2883
+
2884
+ return;
2885
+
2886
+
2887
+ }
2888
+
2889
+
2890
+
2891
+ running = true;
2892
+
2893
+
2894
+
2895
+ try {
2896
+
2897
+
2898
+ setReadingStatus();
2899
+
2900
+
2901
+ const parsed = await readMeter();
2902
+
2903
+
2904
+ sendParsedResult(parsed);
2905
+
2906
+
2907
+ } catch (err) {
2908
+
2909
+
2910
+ setDisconnectedStatus(err.message);
2911
+
2912
+
2913
+ node.error(
2914
+
2915
+
2916
+ "Eroare Carlo Gavazzi (" +
2917
+
2918
+
2919
+ node.host + ":" + node.port +
2920
+
2921
+
2922
+ ", unitId=" + node.unitId +
2923
+
2924
+
2925
+ "): " + err.message
2926
+
2927
+
2928
+ );
2929
+
2930
+
2931
+ } finally {
2932
+
2933
+
2934
+ running = false;
2935
+
2936
+
2937
+ }
2938
+
2939
+
2940
+ }
2941
+
2942
+
2943
+
2944
+ if (!node.host) {
2945
+
2946
+
2947
+ setDisconnectedStatus("IP lipsa");
2948
+
2949
+
2950
+ node.error("Configuratie invalida: IP lipsa");
2951
+
2952
+
2953
+ } else {
2954
+
2955
+
2956
+ if (node.influxDb) {
2957
+
2958
+
2959
+ registeredInfluxIdentityKey = getInfluxIdentityKey();
2960
+
2961
+
2962
+ registerInfluxMeasurement(node.id, registeredInfluxIdentityKey);
2963
+
2964
+
2965
+ if (hasInfluxMeasurementConflict()) {
2966
+
2967
+
2968
+ node.status({
2969
+
2970
+
2971
+ fill: "red",
2972
+
2973
+
2974
+ shape: "ring",
2975
+
2976
+
2977
+ text: "meter_id+measurement duplicat"
2978
+
2979
+
2980
+ });
2981
+
2982
+
2983
+ node.error("Carlo Gavazzi InfluxDB: combinatie duplicata Meter ID + Measurement ('" + getEffectiveMeterId() + "' @ '" + getInfluxMeasurement() + "'). Configureaza un Meter ID unic pentru acelasi measurement.");
2984
+
2985
+
2986
+ }
2987
+
2988
+
2989
+ }
2990
+
2991
+
2992
+ if (node.timestep > 0) {
2993
+ startupHandle = setTimeout(function () {
2994
+
2995
+
2996
+ executeRead();
2997
+
2998
+
2999
+ }, 0);
3000
+
3001
+
3002
+
3003
+ intervalHandle = setInterval(function () {
3004
+
3005
+
3006
+ executeRead();
3007
+
3008
+
3009
+ }, node.timestep);
3010
+ }
3011
+
3012
+
3013
+ }
3014
+
3015
+ node.on("input", function () {
3016
+ executeRead();
3017
+ });
3018
+
3019
+
3020
+
3021
+ node.on("close", function (done) {
3022
+
3023
+
3024
+ closed = true;
3025
+
3026
+
3027
+ if (registeredInfluxIdentityKey) {
3028
+
3029
+
3030
+ unregisterInfluxMeasurement(node.id, registeredInfluxIdentityKey);
3031
+
3032
+
3033
+ registeredInfluxIdentityKey = "";
3034
+
3035
+
3036
+ }
3037
+
3038
+
3039
+
3040
+ if (startupHandle) {
3041
+
3042
+
3043
+ clearTimeout(startupHandle);
3044
+
3045
+
3046
+ startupHandle = null;
3047
+
3048
+
3049
+ }
3050
+
3051
+
3052
+
3053
+ if (intervalHandle) {
3054
+
3055
+
3056
+ clearInterval(intervalHandle);
3057
+
3058
+
3059
+ intervalHandle = null;
3060
+
3061
+
3062
+ }
3063
+
3064
+
3065
+
3066
+ done();
3067
+
3068
+
3069
+ });
3070
+
3071
+
3072
+
3073
+ });
3074
+ }
3075
+
3076
+
3077
+
3078
+ RED.nodes.registerType("carlo-gavazzi", CarloGavazziNode);
3079
+
3080
+
3081
+ };