@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.
- package/NOTICE +56 -0
- package/PROPRIETARY-LICENSE.txt +157 -0
- package/README.md +105 -0
- package/TERMS-AND-CONDITIONS.md +311 -0
- package/advantech-read/advantech-read.html +775 -0
- package/advantech-read/advantech-read.js +449 -0
- package/advantech-write/advantech-write.html +413 -0
- package/advantech-write/advantech-write.js +338 -0
- package/bess-control/bess-control.html +569 -0
- package/bess-control/bess-control.js +541 -0
- package/bess-control/lib/allocation.js +149 -0
- package/bess-control/lib/batteries.js +86 -0
- package/bess-control/lib/helpers.js +157 -0
- package/bess-control/lib/messages.js +131 -0
- package/bess-control/lib/runtime.js +156 -0
- package/carlo-gavazzi/carlo-gavazzi.html +4327 -0
- package/carlo-gavazzi/carlo-gavazzi.js +3081 -0
- package/control-export/control-export.html +3644 -0
- package/control-export/control-export.js +2192 -0
- package/control-reactive/control-reactive.html +3136 -0
- package/control-reactive/control-reactive.js +2124 -0
- package/fronius-emulator/README.md +258 -0
- package/fronius-emulator/fronius-emulator.html +170 -0
- package/fronius-emulator/fronius-emulator.js +520 -0
- package/fronius-emulator/lib/binary-codec.js +65 -0
- package/fronius-emulator/lib/fronius-register-map.js +185 -0
- package/fronius-emulator/lib/modbus-parser.js +61 -0
- package/fronius-emulator/lib/modbus-tcp-server.js +224 -0
- package/fronius-emulator/lib/validation.js +206 -0
- package/fronius-emulator/lib/virtual-meter.js +132 -0
- package/fronius-read/fronius-read.html +3944 -0
- package/fronius-read/fronius-read.js +1466 -0
- package/fronius-read/lib/inverter-profiles.js +711 -0
- package/fronius-read/lib/sunspec-core.js +303 -0
- package/fronius-read/lib/sunspec-model-parsers.js +845 -0
- package/fronius-write/fronius-write.html +1789 -0
- package/fronius-write/fronius-write.js +2531 -0
- package/influxdb/influxdb.html +1077 -0
- package/influxdb/influxdb.js +1487 -0
- package/janitza/janitza.html +2562 -0
- package/janitza/janitza.js +1500 -0
- package/janitza/lib/janitza-profiles.js +371 -0
- package/lib/a3-test-server.js +199 -0
- package/lib/adam-6000-profiles.js +173 -0
- package/lib/licensing/constants.js +103 -0
- package/lib/licensing/device-id.js +251 -0
- package/lib/licensing/errors.js +21 -0
- package/lib/licensing/license-client.js +377 -0
- package/lib/licensing/license-state.js +50 -0
- package/lib/licensing/node-guard.js +150 -0
- package/lib/licensing/package-integrity.js +167 -0
- package/lib/licensing/package-integrity.json +513 -0
- package/lib/licensing/public-key.pem +3 -0
- package/lib/licensing/public-key.pem.example +2 -0
- package/lib/licensing/public-keys.js +18 -0
- package/lib/licensing/storage.js +63 -0
- package/lib/licensing/token-verifier.js +84 -0
- package/lib/numbers.js +26 -0
- package/license/atelier-license.html +88 -0
- package/license/atelier-license.js +170 -0
- package/modbus-tcp-discover/modbus-tcp-discover.html +190 -0
- package/modbus-tcp-discover/modbus-tcp-discover.js +706 -0
- package/multi-battery/multi-battery.html +649 -0
- package/multi-battery/multi-battery.js +668 -0
- package/multi-grid/multi-grid.html +745 -0
- package/multi-grid/multi-grid.js +1392 -0
- package/multi-inverter/lib/engine-core.js +237 -0
- package/multi-inverter/multi-inverter.html +2674 -0
- package/multi-inverter/multi-inverter.js +1393 -0
- package/package.json +109 -0
- package/phoenix-contact/lib/eem-ma370-advanced.js +175 -0
- package/phoenix-contact/lib/eem-ma370.js +138 -0
- package/phoenix-contact/lib/integrations.js +149 -0
- package/phoenix-contact/lib/phoenix-contact-profile.js +35030 -0
- package/phoenix-contact/phoenix-contact.html +840 -0
- package/phoenix-contact/phoenix-contact.js +679 -0
- package/pv-generator/pv-generator.html +284 -0
- package/pv-generator/pv-generator.js +387 -0
- package/pylontech-read/pylontech-read.html +763 -0
- package/pylontech-read/pylontech-read.js +518 -0
- package/pylontech-write/pylontech-write.html +405 -0
- package/pylontech-write/pylontech-write.js +546 -0
- package/scada-aggregator/lib/aggregate.js +72 -0
- package/scada-aggregator/lib/groups.js +39 -0
- package/scada-aggregator/scada-aggregator.html +108 -0
- package/scada-aggregator/scada-aggregator.js +117 -0
- package/scada-server/lib/aggregator-map.js +58 -0
- package/scada-server/lib/encoding.js +235 -0
- package/scada-server/lib/modbus-server.js +492 -0
- package/scada-server/lib/object-path.js +18 -0
- package/scada-server/lib/register-map.js +288 -0
- package/scada-server/scada-server.html +1419 -0
- package/scada-server/scada-server.js +704 -0
- package/scripts/check-syntax.js +66 -0
- package/scripts/package-integrity.js +46 -0
- package/scripts/package-release.js +116 -0
- package/scripts/run-tests.js +2178 -0
- package/scripts/tgz-integrity.js +74 -0
- package/sma-read/lib/sunspec-core.js +83 -0
- package/sma-read/lib/sunspec-model-parsers.js +853 -0
- package/sma-read/sma-read.html +659 -0
- package/sma-read/sma-read.js +458 -0
- package/sma-write/sma-write.html +105 -0
- package/sma-write/sma-write.js +270 -0
- package/victron-read/lib/mqtt-state.js +176 -0
- package/victron-read/victron-read.html +1074 -0
- package/victron-read/victron-read.js +1222 -0
package/package.json
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atelieruldetehnologii/node-red-custome-solutions",
|
|
3
|
+
"version": "0.62.22",
|
|
4
|
+
"description": "Proprietary Node-RED nodes developed by Atelierul de Tehnologii for energy, Modbus, BESS, MQTT, SCADA and industrial integrations",
|
|
5
|
+
"private": false,
|
|
6
|
+
"license": "SEE LICENSE IN PROPRIETARY-LICENSE.txt",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"node-red",
|
|
9
|
+
"node-red-node",
|
|
10
|
+
"modbus",
|
|
11
|
+
"mqtt",
|
|
12
|
+
"energy",
|
|
13
|
+
"bess",
|
|
14
|
+
"photovoltaic",
|
|
15
|
+
"scada"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"advantech-read/**/*",
|
|
22
|
+
"advantech-write/**/*",
|
|
23
|
+
"bess-control/**/*",
|
|
24
|
+
"carlo-gavazzi/**/*",
|
|
25
|
+
"control-export/**/*",
|
|
26
|
+
"control-reactive/**/*",
|
|
27
|
+
"fronius-emulator/**/*",
|
|
28
|
+
"fronius-read/**/*",
|
|
29
|
+
"fronius-write/**/*",
|
|
30
|
+
"influxdb/**/*",
|
|
31
|
+
"janitza/**/*",
|
|
32
|
+
"lib/**/*",
|
|
33
|
+
"license/**/*",
|
|
34
|
+
"modbus-tcp-discover/**/*",
|
|
35
|
+
"multi-battery/**/*",
|
|
36
|
+
"multi-grid/**/*",
|
|
37
|
+
"multi-inverter/**/*",
|
|
38
|
+
"phoenix-contact/**/*",
|
|
39
|
+
"pv-generator/**/*",
|
|
40
|
+
"pylontech-read/**/*",
|
|
41
|
+
"pylontech-write/**/*",
|
|
42
|
+
"scada-aggregator/**/*",
|
|
43
|
+
"scada-server/**/*",
|
|
44
|
+
"scripts/**/*",
|
|
45
|
+
"sma-read/**/*",
|
|
46
|
+
"sma-write/**/*",
|
|
47
|
+
"victron-read/**/*",
|
|
48
|
+
"README.md",
|
|
49
|
+
"PROPRIETARY-LICENSE.txt",
|
|
50
|
+
"TERMS-AND-CONDITIONS.md",
|
|
51
|
+
"NOTICE",
|
|
52
|
+
"package.json"
|
|
53
|
+
],
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"modbus-serial": "^8.0.25",
|
|
56
|
+
"mqtt": "^5.10.4"
|
|
57
|
+
},
|
|
58
|
+
"node-red": {
|
|
59
|
+
"version": ">=3.1.1",
|
|
60
|
+
"nodes": {
|
|
61
|
+
"atelier-license": "license/atelier-license.js",
|
|
62
|
+
"fronius-read": "fronius-read/fronius-read.js",
|
|
63
|
+
"sma-read": "sma-read/sma-read.js",
|
|
64
|
+
"fronius-write": "fronius-write/fronius-write.js",
|
|
65
|
+
"sma-write": "sma-write/sma-write.js",
|
|
66
|
+
"control-export": "control-export/control-export.js",
|
|
67
|
+
"multi-inverter": "multi-inverter/multi-inverter.js",
|
|
68
|
+
"carlo-gavazzi": "carlo-gavazzi/carlo-gavazzi.js",
|
|
69
|
+
"phoenix-contact": "phoenix-contact/phoenix-contact.js",
|
|
70
|
+
"advantech-read": "advantech-read/advantech-read.js",
|
|
71
|
+
"advantech-write": "advantech-write/advantech-write.js",
|
|
72
|
+
"control-reactive": "control-reactive/control-reactive.js",
|
|
73
|
+
"multi-grid": "multi-grid/multi-grid.js",
|
|
74
|
+
"pylontech-read": "pylontech-read/pylontech-read.js",
|
|
75
|
+
"pylontech-write": "pylontech-write/pylontech-write.js",
|
|
76
|
+
"bess-control": "bess-control/bess-control.js",
|
|
77
|
+
"scada-server": "scada-server/scada-server.js",
|
|
78
|
+
"scada-aggregator": "scada-aggregator/scada-aggregator.js",
|
|
79
|
+
"multi-battery": "multi-battery/multi-battery.js",
|
|
80
|
+
"pv-generator": "pv-generator/pv-generator.js",
|
|
81
|
+
"janitza": "janitza/janitza.js",
|
|
82
|
+
"adt-influxdb": "influxdb/influxdb.js",
|
|
83
|
+
"victron-read": "victron-read/victron-read.js",
|
|
84
|
+
"modbus-tcp-discover": "modbus-tcp-discover/modbus-tcp-discover.js",
|
|
85
|
+
"fronius-emulator": "fronius-emulator/fronius-emulator.js"
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"scripts": {
|
|
89
|
+
"check:syntax": "node scripts/check-syntax.js",
|
|
90
|
+
"lint": "node scripts/check-syntax.js",
|
|
91
|
+
"test": "node scripts/run-tests.js && node --test test/editor-ip-name-sync.test.js test/licensing-control.test.js test/package-integrity.test.js test/scada-aggregator-zero-average.test.js",
|
|
92
|
+
"check": "npm run lint && npm test",
|
|
93
|
+
"package:release": "node scripts/package-release.js"
|
|
94
|
+
},
|
|
95
|
+
"author": {
|
|
96
|
+
"name": "Atelierul de Tehnologii",
|
|
97
|
+
"url": "https://www.atelieruldetehnologii.ro/"
|
|
98
|
+
},
|
|
99
|
+
"homepage": "https://www.atelieruldetehnologii.ro/",
|
|
100
|
+
"publishConfig": {
|
|
101
|
+
"access": "public"
|
|
102
|
+
},
|
|
103
|
+
"atelierCommunity": {
|
|
104
|
+
"company": "Atelierul de Tehnologii",
|
|
105
|
+
"website": "https://www.atelieruldetehnologii.ro/",
|
|
106
|
+
"allNodes": true,
|
|
107
|
+
"nodeCount": 25
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { decodeFloat32, EEM_MA370_ENCODING } = require("./eem-ma370");
|
|
4
|
+
|
|
5
|
+
function floatField(path, address, scale) {
|
|
6
|
+
return { path, address, type: "float32", scale: scale === undefined ? 1 : scale };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function uint32Field(path, address, scale) {
|
|
10
|
+
return { path, address, type: "uint32", scale: scale === undefined ? 1 : scale };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function sint16Field(path, address, scale) {
|
|
14
|
+
return { path, address, type: "sint16", scale: scale === undefined ? 1 : scale };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const BLOCKS = [
|
|
18
|
+
{
|
|
19
|
+
id: "tariffs", start: 0x8200, count: 49, fields: (function () {
|
|
20
|
+
const fields = [];
|
|
21
|
+
for (let tariff = 1; tariff <= 4; tariff++) {
|
|
22
|
+
const base = 0x8201 + ((tariff - 1) * 12);
|
|
23
|
+
const prefix = "summary.energy.tariff.t" + tariff;
|
|
24
|
+
fields.push(floatField(prefix + "_import_kwh", base, 0.001));
|
|
25
|
+
fields.push(floatField(prefix + "_export_kwh", base + 2, 0.001));
|
|
26
|
+
fields.push(floatField(prefix + "_reactive_import_kvarh", base + 4, 0.001));
|
|
27
|
+
fields.push(floatField(prefix + "_reactive_export_kvarh", base + 6, 0.001));
|
|
28
|
+
fields.push(floatField(prefix + "_apparent_kvah", base + 8, 0.001));
|
|
29
|
+
fields.push(floatField(prefix + "_reactive_total_kvarh", base + 10, 0.001));
|
|
30
|
+
}
|
|
31
|
+
return fields;
|
|
32
|
+
})()
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "hours", start: 0x8300, count: 22, fields: [
|
|
36
|
+
uint32Field("summary.runtime.operating_hours", 0x8300),
|
|
37
|
+
uint32Field("summary.runtime.load_operating_hours", 0x8302),
|
|
38
|
+
uint32Field("summary.runtime.total_energy_seconds", 0x8308),
|
|
39
|
+
uint32Field("summary.runtime.load_energy_seconds", 0x830A),
|
|
40
|
+
uint32Field("summary.runtime.resettable_energy_seconds", 0x830C),
|
|
41
|
+
uint32Field("summary.runtime.tariff_1_seconds", 0x830E),
|
|
42
|
+
uint32Field("summary.runtime.tariff_2_seconds", 0x8310),
|
|
43
|
+
uint32Field("summary.runtime.tariff_3_seconds", 0x8312),
|
|
44
|
+
uint32Field("summary.runtime.tariff_4_seconds", 0x8314)
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "process_energy", start: 0x8360, count: 46, fields: [
|
|
49
|
+
uint32Field("summary.process_energy.current.runtime_s", 0x8361, 0.1),
|
|
50
|
+
floatField("summary.process_energy.current.import_kwh", 0x8363, 0.001),
|
|
51
|
+
floatField("summary.process_energy.current.export_kwh", 0x8365, 0.001),
|
|
52
|
+
floatField("summary.process_energy.current.reactive_import_kvarh", 0x8367, 0.001),
|
|
53
|
+
floatField("summary.process_energy.current.reactive_export_kvarh", 0x8369, 0.001),
|
|
54
|
+
floatField("summary.process_energy.current.apparent_kvah", 0x836B, 0.001),
|
|
55
|
+
floatField("summary.process_energy.current.reactive_total_kvarh", 0x836D, 0.001),
|
|
56
|
+
uint32Field("summary.process_energy.previous.runtime_s", 0x8380, 0.1),
|
|
57
|
+
floatField("summary.process_energy.previous.import_kwh", 0x8382, 0.001),
|
|
58
|
+
floatField("summary.process_energy.previous.export_kwh", 0x8384, 0.001),
|
|
59
|
+
floatField("summary.process_energy.previous.reactive_import_kvarh", 0x8386, 0.001),
|
|
60
|
+
floatField("summary.process_energy.previous.reactive_export_kvarh", 0x8388, 0.001),
|
|
61
|
+
floatField("summary.process_energy.previous.apparent_kvah", 0x838A, 0.001),
|
|
62
|
+
floatField("summary.process_energy.previous.reactive_total_kvarh", 0x838C, 0.001)
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "trend", start: 0x8500, count: 16, fields: [
|
|
67
|
+
floatField("summary.statistics.trend.active_power_w", 0x8500),
|
|
68
|
+
floatField("summary.statistics.trend.reactive_power_var", 0x8502),
|
|
69
|
+
floatField("summary.statistics.trend.apparent_power_va", 0x8504),
|
|
70
|
+
floatField("summary.statistics.interval.active_import_wh", 0x8506),
|
|
71
|
+
floatField("summary.statistics.interval.active_export_wh", 0x8508),
|
|
72
|
+
floatField("summary.statistics.interval.reactive_import_varh", 0x850A),
|
|
73
|
+
floatField("summary.statistics.interval.reactive_export_varh", 0x850C),
|
|
74
|
+
floatField("summary.statistics.interval.apparent_vah", 0x850E)
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "averages", start: 0x8540, count: 32, fields: [
|
|
79
|
+
floatField("summary.statistics.average.voltage_l1_l2_v", 0x8540),
|
|
80
|
+
floatField("summary.statistics.average.voltage_l2_l3_v", 0x8542),
|
|
81
|
+
floatField("summary.statistics.average.voltage_l3_l1_v", 0x8544),
|
|
82
|
+
floatField("summary.statistics.average.voltage_l1_n_v", 0x8546),
|
|
83
|
+
floatField("summary.statistics.average.voltage_l2_n_v", 0x8548),
|
|
84
|
+
floatField("summary.statistics.average.voltage_l3_n_v", 0x854A),
|
|
85
|
+
floatField("summary.statistics.average.frequency_hz", 0x854C),
|
|
86
|
+
floatField("summary.statistics.average.current_l1_a", 0x854E),
|
|
87
|
+
floatField("summary.statistics.average.current_l2_a", 0x8550),
|
|
88
|
+
floatField("summary.statistics.average.current_l3_a", 0x8552),
|
|
89
|
+
floatField("summary.statistics.average.current_neutral_a", 0x8554),
|
|
90
|
+
floatField("summary.statistics.average.active_import_w", 0x8556),
|
|
91
|
+
floatField("summary.statistics.average.active_export_w", 0x8558),
|
|
92
|
+
floatField("summary.statistics.average.reactive_import_var", 0x855A),
|
|
93
|
+
floatField("summary.statistics.average.reactive_export_var", 0x855C),
|
|
94
|
+
floatField("summary.statistics.average.apparent_va", 0x855E)
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "minmax", start: 0x8700, count: 64, fields: (function () {
|
|
99
|
+
const metrics = [
|
|
100
|
+
["voltage_l1_l2_v", 0], ["voltage_l2_l3_v", 2], ["voltage_l3_l1_v", 4],
|
|
101
|
+
["voltage_l1_n_v", 6], ["voltage_l2_n_v", 8], ["voltage_l3_n_v", 10],
|
|
102
|
+
["frequency_hz", 12], ["current_l1_a", 14], ["current_l2_a", 16],
|
|
103
|
+
["current_l3_a", 18], ["current_neutral_a", 20], ["active_import_w", 22],
|
|
104
|
+
["active_export_w", 24], ["reactive_import_var", 26], ["reactive_export_var", 28],
|
|
105
|
+
["apparent_va", 30]
|
|
106
|
+
];
|
|
107
|
+
const fields = [];
|
|
108
|
+
metrics.forEach(function (metric) {
|
|
109
|
+
fields.push(floatField("summary.statistics.maximum." + metric[0], 0x8700 + metric[1]));
|
|
110
|
+
fields.push(floatField("summary.statistics.minimum." + metric[0], 0x8720 + metric[1]));
|
|
111
|
+
});
|
|
112
|
+
return fields;
|
|
113
|
+
})()
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "thd", start: 0x8800, count: 10, fields: [
|
|
117
|
+
sint16Field("summary.thd.voltage_l1_l2_pct", 0x8800, 0.1),
|
|
118
|
+
sint16Field("summary.thd.voltage_l2_l3_pct", 0x8801, 0.1),
|
|
119
|
+
sint16Field("summary.thd.voltage_l3_l1_pct", 0x8802, 0.1),
|
|
120
|
+
sint16Field("summary.thd.voltage_l1_n_pct", 0x8803, 0.1),
|
|
121
|
+
sint16Field("summary.thd.voltage_l2_n_pct", 0x8804, 0.1),
|
|
122
|
+
sint16Field("summary.thd.voltage_l3_n_pct", 0x8805, 0.1),
|
|
123
|
+
sint16Field("summary.thd.current_l1_pct", 0x8806, 0.1),
|
|
124
|
+
sint16Field("summary.thd.current_l2_pct", 0x8807, 0.1),
|
|
125
|
+
sint16Field("summary.thd.current_l3_pct", 0x8808, 0.1),
|
|
126
|
+
sint16Field("summary.thd.current_neutral_pct", 0x8809, 0.1)
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
function setByPath(target, path, value) {
|
|
132
|
+
const parts = path.split(".");
|
|
133
|
+
let cursor = target;
|
|
134
|
+
parts.forEach(function (part, index) {
|
|
135
|
+
if (index === parts.length - 1) cursor[part] = value;
|
|
136
|
+
else cursor = cursor[part] || (cursor[part] = {});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function uint32(registers, offset) {
|
|
141
|
+
if (!Number.isInteger(registers[offset]) || !Number.isInteger(registers[offset + 1])) return null;
|
|
142
|
+
return ((registers[offset] * 0x10000) + registers[offset + 1]) >>> 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function sint16(value) {
|
|
146
|
+
if (!Number.isInteger(value)) return null;
|
|
147
|
+
return value > 0x7FFF ? value - 0x10000 : value;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function requiredBlocks(paths) {
|
|
151
|
+
const selected = new Set(Array.isArray(paths) ? paths : []);
|
|
152
|
+
return BLOCKS.filter(function (block) {
|
|
153
|
+
return block.fields.some(function (field) { return selected.has(field.path); });
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function parseAdvancedBlocks(blockValues) {
|
|
158
|
+
const result = {};
|
|
159
|
+
BLOCKS.forEach(function (block) {
|
|
160
|
+
const registers = blockValues && blockValues[block.id];
|
|
161
|
+
if (!Array.isArray(registers)) return;
|
|
162
|
+
block.fields.forEach(function (field) {
|
|
163
|
+
const offset = field.address - block.start;
|
|
164
|
+
let value = null;
|
|
165
|
+
if (field.type === "float32") value = decodeFloat32(registers, offset, EEM_MA370_ENCODING);
|
|
166
|
+
else if (field.type === "uint32") value = uint32(registers, offset);
|
|
167
|
+
else if (field.type === "sint16") value = sint16(registers[offset]);
|
|
168
|
+
if (value !== null) value *= field.scale;
|
|
169
|
+
setByPath(result, field.path.replace(/^summary\./, ""), value);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = { BLOCKS, parseAdvancedBlocks, requiredBlocks };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const INSTANTANEOUS_START = 0x8000;
|
|
4
|
+
const INSTANTANEOUS_COUNT = 88;
|
|
5
|
+
const ENERGY_START = 0x8100;
|
|
6
|
+
const ENERGY_COUNT = 24;
|
|
7
|
+
const EEM_MA370_ENCODING = Object.freeze({
|
|
8
|
+
wordOrder: "msw_first",
|
|
9
|
+
byteOrder: "big_endian"
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const FLOAT_FIELDS = [
|
|
13
|
+
["voltage.l1_l2_v", 0x8000],
|
|
14
|
+
["voltage.l2_l3_v", 0x8002],
|
|
15
|
+
["voltage.l3_l1_v", 0x8004],
|
|
16
|
+
["voltage.l1_n_v", 0x8006],
|
|
17
|
+
["voltage.l2_n_v", 0x8008],
|
|
18
|
+
["voltage.l3_n_v", 0x800A],
|
|
19
|
+
["frequency_hz", 0x800C],
|
|
20
|
+
["current.l1_a", 0x800E],
|
|
21
|
+
["current.l2_a", 0x8010],
|
|
22
|
+
["current.l3_a", 0x8012],
|
|
23
|
+
["current.neutral_a", 0x8014],
|
|
24
|
+
["power.total_w", 0x8016],
|
|
25
|
+
["reactive.total_var", 0x8018],
|
|
26
|
+
["apparent.total_va", 0x801A],
|
|
27
|
+
["power_factor.total", 0x801C],
|
|
28
|
+
["power.l1_w", 0x801E],
|
|
29
|
+
["power.l2_w", 0x8020],
|
|
30
|
+
["power.l3_w", 0x8022],
|
|
31
|
+
["reactive.l1_var", 0x8024],
|
|
32
|
+
["reactive.l2_var", 0x8026],
|
|
33
|
+
["reactive.l3_var", 0x8028],
|
|
34
|
+
["apparent.l1_va", 0x802A],
|
|
35
|
+
["apparent.l2_va", 0x802C],
|
|
36
|
+
["apparent.l3_va", 0x802E],
|
|
37
|
+
["power_factor.l1", 0x8030],
|
|
38
|
+
["power_factor.l2", 0x8032],
|
|
39
|
+
["power_factor.l3", 0x8034],
|
|
40
|
+
["system.current_a", 0x8039],
|
|
41
|
+
["system.line_voltage_v", 0x803B],
|
|
42
|
+
["system.phase_voltage_v", 0x803D],
|
|
43
|
+
["reactive.total_arithmetic_var", 0x803F],
|
|
44
|
+
["apparent.total_arithmetic_va", 0x8041],
|
|
45
|
+
["power_factor.total_arithmetic", 0x8043],
|
|
46
|
+
["phase_angle.l1_deg", 0x8045],
|
|
47
|
+
["phase_angle.l2_deg", 0x8047],
|
|
48
|
+
["phase_angle.l3_deg", 0x8049],
|
|
49
|
+
["cos_phi.l1", 0x804B],
|
|
50
|
+
["cos_phi.l2", 0x804D],
|
|
51
|
+
["cos_phi.l3", 0x804F],
|
|
52
|
+
["phase_angle_voltage.l1_deg", 0x8051],
|
|
53
|
+
["phase_angle_voltage.l2_deg", 0x8053],
|
|
54
|
+
["phase_angle_voltage.l3_deg", 0x8055]
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const ENERGY_FIELDS = [
|
|
58
|
+
["energy.import_kwh", 0x8100, 0.001],
|
|
59
|
+
["energy.reactive_import_kvarh", 0x8102, 0.001],
|
|
60
|
+
["energy.apparent_kvah", 0x8104, 0.001],
|
|
61
|
+
["energy.export_kwh", 0x8106, 0.001],
|
|
62
|
+
["energy.reactive_export_kvarh", 0x8108, 0.001],
|
|
63
|
+
["energy.reactive_total_kvarh", 0x810A, 0.001],
|
|
64
|
+
["energy.current_import_kwh", 0x810C, 0.001],
|
|
65
|
+
["energy.current_reactive_import_kvarh", 0x810E, 0.001],
|
|
66
|
+
["energy.current_apparent_kvah", 0x8110, 0.001],
|
|
67
|
+
["energy.current_export_kwh", 0x8112, 0.001],
|
|
68
|
+
["energy.current_reactive_export_kvarh", 0x8114, 0.001],
|
|
69
|
+
["energy.current_reactive_total_kvarh", 0x8116, 0.001]
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
function setByPath(target, path, value) {
|
|
73
|
+
const parts = path.split(".");
|
|
74
|
+
let cursor = target;
|
|
75
|
+
parts.forEach(function (part, index) {
|
|
76
|
+
if (index === parts.length - 1) cursor[part] = value;
|
|
77
|
+
else cursor = cursor[part] || (cursor[part] = {});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function decodeFloat32(registers, offset, options) {
|
|
82
|
+
const opts = options || {};
|
|
83
|
+
let first = registers[offset];
|
|
84
|
+
let second = registers[offset + 1];
|
|
85
|
+
if (!Number.isInteger(first) || !Number.isInteger(second)) return null;
|
|
86
|
+
if (opts.wordOrder === "lsw_first") [first, second] = [second, first];
|
|
87
|
+
|
|
88
|
+
const buffer = Buffer.allocUnsafe(4);
|
|
89
|
+
buffer.writeUInt16BE(first & 0xFFFF, 0);
|
|
90
|
+
buffer.writeUInt16BE(second & 0xFFFF, 2);
|
|
91
|
+
if (opts.byteOrder === "little_endian") {
|
|
92
|
+
[buffer[0], buffer[1]] = [buffer[1], buffer[0]];
|
|
93
|
+
[buffer[2], buffer[3]] = [buffer[3], buffer[2]];
|
|
94
|
+
}
|
|
95
|
+
const value = buffer.readFloatBE(0);
|
|
96
|
+
return Number.isFinite(value) ? value : null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function signed16(value) {
|
|
100
|
+
if (!Number.isInteger(value)) return null;
|
|
101
|
+
return value > 0x7FFF ? value - 0x10000 : value;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function parseEemMa370(instantaneous, energy) {
|
|
105
|
+
if (!Array.isArray(instantaneous) || instantaneous.length < INSTANTANEOUS_COUNT) {
|
|
106
|
+
throw new Error("Raspuns instantaneu incomplet pentru EEM-MA370");
|
|
107
|
+
}
|
|
108
|
+
if (!Array.isArray(energy) || energy.length < ENERGY_COUNT) {
|
|
109
|
+
throw new Error("Raspuns energie incomplet pentru EEM-MA370");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const summary = {};
|
|
113
|
+
FLOAT_FIELDS.forEach(function (field) {
|
|
114
|
+
setByPath(summary, field[0], decodeFloat32(instantaneous, field[1] - INSTANTANEOUS_START, EEM_MA370_ENCODING));
|
|
115
|
+
});
|
|
116
|
+
ENERGY_FIELDS.forEach(function (field) {
|
|
117
|
+
const raw = decodeFloat32(energy, field[1] - ENERGY_START, EEM_MA370_ENCODING);
|
|
118
|
+
setByPath(summary, field[0], raw === null ? null : raw * field[2]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
summary.load_type = {
|
|
122
|
+
l1: signed16(instantaneous[0x8036 - INSTANTANEOUS_START]),
|
|
123
|
+
l2: signed16(instantaneous[0x8037 - INSTANTANEOUS_START]),
|
|
124
|
+
l3: signed16(instantaneous[0x8038 - INSTANTANEOUS_START])
|
|
125
|
+
};
|
|
126
|
+
summary.phase_sequence = signed16(instantaneous[0x8057 - INSTANTANEOUS_START]);
|
|
127
|
+
return summary;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = {
|
|
131
|
+
ENERGY_COUNT,
|
|
132
|
+
ENERGY_START,
|
|
133
|
+
EEM_MA370_ENCODING,
|
|
134
|
+
INSTANTANEOUS_COUNT,
|
|
135
|
+
INSTANTANEOUS_START,
|
|
136
|
+
decodeFloat32,
|
|
137
|
+
parseEemMa370
|
|
138
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const PRESETS = {
|
|
4
|
+
multiGrid: [
|
|
5
|
+
"summary.power.total_w",
|
|
6
|
+
"summary.power.l1_w",
|
|
7
|
+
"summary.power.l2_w",
|
|
8
|
+
"summary.power.l3_w"
|
|
9
|
+
],
|
|
10
|
+
bessControl: [
|
|
11
|
+
"summary.power.total_w",
|
|
12
|
+
"summary.power.l1_w",
|
|
13
|
+
"summary.power.l2_w",
|
|
14
|
+
"summary.power.l3_w"
|
|
15
|
+
],
|
|
16
|
+
controlExportReactive: [
|
|
17
|
+
"summary.power.total_w",
|
|
18
|
+
"summary.power.l1_w",
|
|
19
|
+
"summary.power.l2_w",
|
|
20
|
+
"summary.power.l3_w",
|
|
21
|
+
"summary.reactive.total_var",
|
|
22
|
+
"summary.reactive.l1_var",
|
|
23
|
+
"summary.reactive.l2_var",
|
|
24
|
+
"summary.reactive.l3_var"
|
|
25
|
+
],
|
|
26
|
+
scadaServer: [
|
|
27
|
+
"summary.power.total_w",
|
|
28
|
+
"summary.reactive.total_var",
|
|
29
|
+
"summary.frequency_hz",
|
|
30
|
+
"summary.voltage.l1_l2_v",
|
|
31
|
+
"summary.voltage.l2_l3_v",
|
|
32
|
+
"summary.voltage.l3_l1_v"
|
|
33
|
+
]
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const COMPACT_KEYS = {
|
|
37
|
+
"summary.voltage.l1_n_v": "voltage_l1_n_v",
|
|
38
|
+
"summary.voltage.l2_n_v": "voltage_l2_n_v",
|
|
39
|
+
"summary.voltage.l3_n_v": "voltage_l3_n_v",
|
|
40
|
+
"summary.voltage.l1_l2_v": "voltage_l1_l2_v",
|
|
41
|
+
"summary.voltage.l2_l3_v": "voltage_l2_l3_v",
|
|
42
|
+
"summary.voltage.l3_l1_v": "voltage_l3_l1_v",
|
|
43
|
+
"summary.current.l1_a": "current_l1_a",
|
|
44
|
+
"summary.current.l2_a": "current_l2_a",
|
|
45
|
+
"summary.current.l3_a": "current_l3_a",
|
|
46
|
+
"summary.current.neutral_a": "current_neutral_a",
|
|
47
|
+
"summary.power.l1_w": "power_l1_w",
|
|
48
|
+
"summary.power.l2_w": "power_l2_w",
|
|
49
|
+
"summary.power.l3_w": "power_l3_w",
|
|
50
|
+
"summary.power.total_w": "power_w",
|
|
51
|
+
"summary.apparent.l1_va": "apparent_l1_va",
|
|
52
|
+
"summary.apparent.l2_va": "apparent_l2_va",
|
|
53
|
+
"summary.apparent.l3_va": "apparent_l3_va",
|
|
54
|
+
"summary.apparent.total_va": "apparent_total_va",
|
|
55
|
+
"summary.reactive.l1_var": "reactive_l1_var",
|
|
56
|
+
"summary.reactive.l2_var": "reactive_l2_var",
|
|
57
|
+
"summary.reactive.l3_var": "reactive_l3_var",
|
|
58
|
+
"summary.reactive.total_var": "reactive_total_var",
|
|
59
|
+
"summary.power_factor.l1": "power_factor_l1",
|
|
60
|
+
"summary.power_factor.l2": "power_factor_l2",
|
|
61
|
+
"summary.power_factor.l3": "power_factor_l3",
|
|
62
|
+
"summary.power_factor.total": "power_factor_total",
|
|
63
|
+
"summary.frequency_hz": "frequency_hz",
|
|
64
|
+
"summary.phase_sequence": "phase_sequence",
|
|
65
|
+
"summary.energy.import_kwh": "energy_import_kwh",
|
|
66
|
+
"summary.energy.reactive_import_kvarh": "energy_reactive_import_kvarh",
|
|
67
|
+
"summary.energy.export_kwh": "energy_export_kwh",
|
|
68
|
+
"summary.energy.reactive_export_kvarh": "energy_export_reactive_kvarh"
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const SCADA_KEYS = Object.assign({}, COMPACT_KEYS, {
|
|
72
|
+
"summary.power.total_w": "active_power_w",
|
|
73
|
+
"summary.reactive.total_var": "reactive_power_var",
|
|
74
|
+
"summary.voltage.l3_l1_v": "voltage_l1_l3_v"
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
function getByPath(object, path) {
|
|
78
|
+
if (path === "full") return object;
|
|
79
|
+
return path.split(".").reduce(function (value, key) {
|
|
80
|
+
return value === null || value === undefined ? undefined : value[key];
|
|
81
|
+
}, object);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function compactKey(path) {
|
|
85
|
+
return COMPACT_KEYS[path] || path.replace(/^summary\./, "").replace(/\./g, "_");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function scadaKey(path) {
|
|
89
|
+
return SCADA_KEYS[path] || compactKey(path);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function selectFields(mode, expertMode, configuredFields) {
|
|
93
|
+
if (mode && !expertMode && PRESETS[mode]) return PRESETS[mode].slice();
|
|
94
|
+
const fields = Array.isArray(configuredFields) ? configuredFields.filter(Boolean) : [];
|
|
95
|
+
return fields.length ? Array.from(new Set(fields)) : ["full"];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function aggregate(envelope, fields, mode) {
|
|
99
|
+
const result = {};
|
|
100
|
+
fields.forEach(function (field) {
|
|
101
|
+
result[mode === "scadaServer" ? scadaKey(field) : compactKey(field)] = getByPath(envelope, field);
|
|
102
|
+
});
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function flattenNumeric(source, prefix, target) {
|
|
107
|
+
Object.keys(source || {}).forEach(function (key) {
|
|
108
|
+
const value = source[key];
|
|
109
|
+
const next = prefix ? prefix + "_" + key : key;
|
|
110
|
+
if ((typeof value === "number" && Number.isFinite(value)) || typeof value === "boolean") target[next] = value;
|
|
111
|
+
else if (value && typeof value === "object" && !Array.isArray(value)) flattenNumeric(value, next, target);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function buildInfluxFields(envelope, fields) {
|
|
116
|
+
const result = {};
|
|
117
|
+
if (fields.includes("full")) {
|
|
118
|
+
function walkSummary(source, path) {
|
|
119
|
+
Object.keys(source || {}).forEach(function (key) {
|
|
120
|
+
const value = source[key];
|
|
121
|
+
const nextPath = path ? path + "." + key : key;
|
|
122
|
+
if ((typeof value === "number" && Number.isFinite(value)) || typeof value === "boolean") {
|
|
123
|
+
result[compactKey("summary." + nextPath)] = value;
|
|
124
|
+
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125
|
+
walkSummary(value, nextPath);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
walkSummary(envelope.summary || {}, "");
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
fields.forEach(function (field) {
|
|
133
|
+
const value = getByPath(envelope, field);
|
|
134
|
+
const key = compactKey(field);
|
|
135
|
+
if ((typeof value === "number" && Number.isFinite(value)) || typeof value === "boolean") result[key] = value;
|
|
136
|
+
else if (value && typeof value === "object" && !Array.isArray(value)) flattenNumeric(value, key, result);
|
|
137
|
+
});
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = {
|
|
142
|
+
PRESETS,
|
|
143
|
+
aggregate,
|
|
144
|
+
buildInfluxFields,
|
|
145
|
+
compactKey,
|
|
146
|
+
getByPath,
|
|
147
|
+
scadaKey,
|
|
148
|
+
selectFields
|
|
149
|
+
};
|