@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,3644 @@
1
+ <script type="text/javascript">
2
+
3
+
4
+ const controlExportDefinition = {
5
+
6
+
7
+ category: 'Atelierul de Tehnologii',
8
+
9
+
10
+ color: '#2a9d8f',
11
+
12
+
13
+ defaults: {
14
+ license: { value: "", type: "atelier-license", required: true },
15
+
16
+
17
+ name: { value: "" },
18
+
19
+
20
+ mode: { value: "zero_feed_in" },
21
+
22
+ zeroFeedInMode: { value: "relative" },
23
+
24
+ requireAllPhases: { value: true },
25
+
26
+ tuningPreset: { value: "normal" },
27
+
28
+
29
+ gridSetPoint: { value: 0, validate: RED.validators.number() },
30
+
31
+
32
+ nominalPower: { value: 1000, validate: RED.validators.number() },
33
+
34
+ nominalPowerFromGlobal: { value: false },
35
+
36
+ nominalPowerGlobalVar: {
37
+
38
+ value: "nominal_power",
39
+
40
+ validate: function(v) {
41
+
42
+ const useGlobal = $("#node-input-nominalPowerFromGlobal").is(":checked");
43
+
44
+ return !useGlobal || String(v || "").trim().length > 0;
45
+
46
+ }
47
+
48
+ },
49
+
50
+
51
+ signConvention: { value: "positive_import" },
52
+
53
+
54
+ deadband: { value: 180, validate: RED.validators.number() },
55
+
56
+
57
+ minLimit: { value: 0, validate: RED.validators.number() },
58
+
59
+
60
+ maxLimit: { value: 1000, validate: RED.validators.number() },
61
+
62
+ maxLimitFromGlobal: { value: false },
63
+
64
+ maxLimitGlobalVar: {
65
+
66
+ value: "max_limit",
67
+
68
+ validate: function(v) {
69
+
70
+ const useGlobal = $("#node-input-maxLimitFromGlobal").is(":checked");
71
+
72
+ return !useGlobal || String(v || "").trim().length > 0;
73
+
74
+ }
75
+
76
+ },
77
+
78
+
79
+ outputTopic: { value: "site-limit" },
80
+
81
+
82
+ smoothingAlpha: {
83
+
84
+ value: 0.15,
85
+
86
+ validate: function(v) {
87
+
88
+ const n = Number(v);
89
+
90
+ return Number.isFinite(n) && n >= 0 && n <= 1;
91
+
92
+ }
93
+
94
+ },
95
+
96
+
97
+ filterTauMs: { value: 2800, validate: RED.validators.number() },
98
+
99
+
100
+ kp: { value: 0.22, validate: RED.validators.number() },
101
+
102
+
103
+ ki: { value: 0.01, validate: RED.validators.number() },
104
+
105
+
106
+ controlPeriodMs: { value: 800, validate: RED.validators.number() },
107
+
108
+ exportThresholdW: { value: 60, validate: RED.validators.number() },
109
+
110
+
111
+ recoverThresholdW: { value: 25, validate: RED.validators.number() },
112
+
113
+
114
+ recoveryDelayMs: { value: 3000, validate: RED.validators.number() },
115
+
116
+ transientIgnoreMs: { value: 1200, validate: RED.validators.number() },
117
+
118
+ maxStepUpW: { value: 500, validate: RED.validators.number() },
119
+
120
+ maxStepDownW: { value: 1500, validate: RED.validators.number() },
121
+
122
+ adaptiveGains: { value: true },
123
+
124
+ adaptiveErrorBandW: { value: 4000, validate: RED.validators.number() },
125
+
126
+ adaptiveMinScale: { value: 0.2, validate: RED.validators.number() },
127
+
128
+
129
+ maxStepW: { value: 0, validate: RED.validators.number() },
130
+
131
+
132
+ staleTimeoutMs: { value: 5000, validate: RED.validators.number() },
133
+
134
+ maxFutureTimestampMs: { value: 2000, validate: RED.validators.number() },
135
+
136
+ fastSetpointMaxTelemetryAgeMs: { value: 600, validate: RED.validators.number() },
137
+
138
+ outputHeartbeatMs: { value: 2000, validate: RED.validators.number() },
139
+
140
+
141
+ safeLimitOnStale: { value: 0, validate: RED.validators.number() },
142
+
143
+ debugLevel: { value: "off" },
144
+
145
+ startLimitMode: { value: "last_limit" },
146
+
147
+ useGlobalPvCap: { value: false },
148
+
149
+ globalPvTotalVar: { value: "total_pv" },
150
+
151
+ globalPvCapOffsetW: {
152
+
153
+ value: 500,
154
+
155
+ validate: function(v) {
156
+
157
+ return Number(v) >= 0;
158
+
159
+ }
160
+
161
+ },
162
+ pvCapOnlyBlocksIncrease: { value: true },
163
+ pvCapHoldMs: { value: 10000, validate: RED.validators.number() },
164
+ pvCapReleaseWPerSec: { value: 1000, validate: RED.validators.number() },
165
+ feedForwardEnabled: { value: true },
166
+ feedForwardGain: {
167
+ value: 0.85,
168
+ validate: function(v) {
169
+ const n = Number(v);
170
+ return Number.isFinite(n) && n >= 0 && n <= 1.5;
171
+ }
172
+ },
173
+
174
+ failSecureEnabled: { value: false },
175
+
176
+ failSecureTimeSec: {
177
+
178
+ value: 10,
179
+
180
+ validate: function(v) {
181
+
182
+ return Number(v) >= 1;
183
+
184
+ }
185
+
186
+ },
187
+
188
+ a3TestsEnabled: { value: false },
189
+
190
+ a3ModbusHost: { value: "0.0.0.0" },
191
+
192
+ a3ModbusPort: { value: 1502, validate: RED.validators.number() },
193
+
194
+ a3ModbusUnitId: { value: 1, validate: RED.validators.number() },
195
+
196
+ a3RampType: { value: "none" },
197
+
198
+ a3RampValue: { value: 0, validate: RED.validators.number() }
199
+
200
+
201
+ },
202
+
203
+
204
+ inputs: 1,
205
+
206
+
207
+ outputs: 1,
208
+
209
+
210
+ icon: "font-awesome/fa-exchange",
211
+
212
+
213
+ label: function () {
214
+
215
+ return this.name || "control-export";
216
+
217
+ },
218
+
219
+
220
+ oneditprepare: function () {
221
+
222
+
223
+ const tuningPresets = {
224
+
225
+ normal: {
226
+
227
+ deadband: 180,
228
+
229
+ filterTauMs: 2800,
230
+
231
+ kp: 0.22,
232
+
233
+ ki: 0.01,
234
+
235
+ controlPeriodMs: 800,
236
+
237
+ exportThresholdW: 60,
238
+
239
+ recoverThresholdW: 25,
240
+
241
+ recoveryDelayMs: 3000,
242
+
243
+ transientIgnoreMs: 1200,
244
+
245
+ maxStepUpW: 500,
246
+
247
+ maxStepDownW: 1500,
248
+
249
+ adaptiveGains: true,
250
+
251
+ adaptiveErrorBandW: 4000,
252
+
253
+ adaptiveMinScale: 0.2
254
+
255
+ },
256
+
257
+ stable_grid: {
258
+
259
+ deadband: 150,
260
+
261
+ filterTauMs: 1500,
262
+
263
+ kp: 0.24,
264
+
265
+ ki: 0.008,
266
+
267
+ controlPeriodMs: 1000,
268
+
269
+ exportThresholdW: 100,
270
+
271
+ recoverThresholdW: 40,
272
+
273
+ recoveryDelayMs: 2000,
274
+
275
+ transientIgnoreMs: 800,
276
+
277
+ maxStepUpW: 1000,
278
+
279
+ maxStepDownW: 1800,
280
+
281
+ adaptiveGains: true,
282
+
283
+ adaptiveErrorBandW: 5000,
284
+
285
+ adaptiveMinScale: 0.45
286
+
287
+ }
288
+
289
+ };
290
+
291
+ const tuningFieldSelector = "#node-input-deadband, #node-input-filterTauMs, #node-input-kp, #node-input-ki, #node-input-recoveryDelayMs, #node-input-transientIgnoreMs, #node-input-maxStepUpW, #node-input-maxStepDownW, #node-input-adaptiveErrorBandW, #node-input-adaptiveMinScale";
292
+
293
+ const validationFieldSelector = "#node-input-minLimit, #node-input-maxLimit, #node-input-exportThresholdW, #node-input-recoverThresholdW, #node-input-ki, #node-input-deadband, #node-input-staleTimeoutMs, #node-input-controlPeriodMs, #node-input-filterTauMs, #node-input-kp, #node-input-transientIgnoreMs, #node-input-maxStepUpW, #node-input-maxStepDownW, #node-input-adaptiveErrorBandW, #node-input-adaptiveMinScale, #node-input-outputHeartbeatMs";
294
+
295
+ let applyingPreset = false;
296
+
297
+ let editorReady = false;
298
+
299
+ let customDefaultsSeeded = false;
300
+
301
+
302
+ function applyPreset(presetKey) {
303
+
304
+ const preset = tuningPresets[presetKey];
305
+
306
+ if (!preset) return;
307
+
308
+
309
+ applyingPreset = true;
310
+
311
+ $("#node-input-deadband").val(preset.deadband);
312
+
313
+ $("#node-input-filterTauMs").val(preset.filterTauMs);
314
+
315
+ $("#node-input-kp").val(preset.kp);
316
+
317
+ $("#node-input-ki").val(preset.ki);
318
+
319
+ $("#node-input-controlPeriodMs").val(preset.controlPeriodMs);
320
+
321
+ $("#node-input-exportThresholdW").val(preset.exportThresholdW);
322
+
323
+ $("#node-input-recoverThresholdW").val(preset.recoverThresholdW);
324
+
325
+ $("#node-input-recoveryDelayMs").val(preset.recoveryDelayMs);
326
+
327
+ $("#node-input-transientIgnoreMs").val(preset.transientIgnoreMs);
328
+
329
+ $("#node-input-maxStepUpW").val(preset.maxStepUpW);
330
+
331
+ $("#node-input-maxStepDownW").val(preset.maxStepDownW);
332
+
333
+ $("#node-input-adaptiveGains").prop("checked", !!preset.adaptiveGains);
334
+
335
+ $("#node-input-adaptiveErrorBandW").val(preset.adaptiveErrorBandW);
336
+
337
+ $("#node-input-adaptiveMinScale").val(preset.adaptiveMinScale);
338
+
339
+ applyingPreset = false;
340
+
341
+ const adaptiveEnabled = $("#node-input-adaptiveGains").is(":checked");
342
+
343
+ $("#row-adaptive-error-band, #row-adaptive-min-scale").toggle(adaptiveEnabled);
344
+
345
+ showValidation();
346
+
347
+ }
348
+
349
+
350
+ function markCustomPreset() {
351
+
352
+ if (!editorReady) return;
353
+
354
+ if (applyingPreset) return;
355
+
356
+ if ($("#node-input-tuningPreset").val() !== "custom") {
357
+
358
+ $("#node-input-tuningPreset").val("custom");
359
+
360
+ toggleFields();
361
+
362
+ }
363
+
364
+ }
365
+
366
+
367
+ function showValidation() {
368
+
369
+ const minLimit = Number($("#node-input-minLimit").val() || 0);
370
+
371
+ const maxLimit = Number($("#node-input-maxLimit").val() || 0);
372
+
373
+ const exportThreshold = Number($("#node-input-exportThresholdW").val() || 0);
374
+
375
+ const recoverThreshold = Number($("#node-input-recoverThresholdW").val() || 0);
376
+
377
+ const ki = Number($("#node-input-ki").val() || 0);
378
+
379
+ const kp = Number($("#node-input-kp").val() || 0);
380
+
381
+ const deadband = Number($("#node-input-deadband").val() || 0);
382
+
383
+ const staleTimeoutMs = Number($("#node-input-staleTimeoutMs").val() || 0);
384
+
385
+ const controlPeriodMs = Number($("#node-input-controlPeriodMs").val() || 0);
386
+
387
+ const filterTauMs = Number($("#node-input-filterTauMs").val() || 0);
388
+
389
+ const transientIgnoreMs = Number($("#node-input-transientIgnoreMs").val() || 0);
390
+
391
+ const outputHeartbeatMs = Number($("#node-input-outputHeartbeatMs").val() || 0);
392
+
393
+ const maxStepUpW = Number($("#node-input-maxStepUpW").val() || 0);
394
+
395
+ const maxStepDownW = Number($("#node-input-maxStepDownW").val() || 0);
396
+
397
+ const adaptiveErrorBandW = Number($("#node-input-adaptiveErrorBandW").val() || 0);
398
+
399
+ const adaptiveMinScale = Number($("#node-input-adaptiveMinScale").val() || 0);
400
+
401
+ const failSecureTimeSec = Number($("#node-input-failSecureTimeSec").val() || 0);
402
+
403
+ const failSecureEnabled = $("#node-input-failSecureEnabled").is(":checked");
404
+ const a3Enabled = $("#node-input-a3TestsEnabled").is(":checked");
405
+ const a3Port = Number($("#node-input-a3ModbusPort").val() || 0);
406
+ const a3UnitId = Number($("#node-input-a3ModbusUnitId").val() || 0);
407
+ const a3RampValue = Number($("#node-input-a3RampValue").val() || 0);
408
+
409
+ const mode = $("#node-input-mode").val();
410
+
411
+ const pvCapSupported = mode !== "nominal_power";
412
+
413
+ const useGlobalPvCap = pvCapSupported && $("#node-input-useGlobalPvCap").is(":checked");
414
+ const feedForwardEnabled = pvCapSupported && $("#node-input-feedForwardEnabled").is(":checked");
415
+
416
+ const globalPvTotalVar = String($("#node-input-globalPvTotalVar").val() || "").trim();
417
+
418
+ const globalPvCapOffsetW = Number($("#node-input-globalPvCapOffsetW").val() || 0);
419
+ const pvCapHoldMs = Number($("#node-input-pvCapHoldMs").val() || 0);
420
+ const pvCapReleaseWPerSec = Number($("#node-input-pvCapReleaseWPerSec").val() || 0);
421
+ const feedForwardGain = Number($("#node-input-feedForwardGain").val() || 0);
422
+
423
+ const nominalPowerFromGlobal = $("#node-input-nominalPowerFromGlobal").is(":checked");
424
+ const maxLimitFromGlobal = $("#node-input-maxLimitFromGlobal").is(":checked");
425
+ const maxLimitGlobalVar = String($("#node-input-maxLimitGlobalVar").val() || "").trim();
426
+
427
+ const nominalPowerGlobalVar = String($("#node-input-nominalPowerGlobalVar").val() || "").trim();
428
+
429
+ const isCustom = $("#node-input-tuningPreset").val() === "custom";
430
+
431
+ const errors = [];
432
+
433
+ const warnings = [];
434
+
435
+
436
+ if (maxLimit < minLimit) {
437
+
438
+ errors.push("Max Limit trebuie sa fie >= Min Limit.");
439
+
440
+ }
441
+
442
+ if (recoverThreshold > exportThreshold) {
443
+
444
+ errors.push("Recover Threshold trebuie sa fie <= Export Threshold.");
445
+
446
+ }
447
+
448
+ if (isCustom && staleTimeoutMs > 0 && staleTimeoutMs < controlPeriodMs) {
449
+
450
+ errors.push("Stale Timeout trebuie sa fie >= Control Period.");
451
+
452
+ }
453
+
454
+ if (isCustom && deadband > 0 && deadband < 20) {
455
+
456
+ warnings.push("Deadband foarte mic (<20W) poate produce oscilatii.");
457
+
458
+ }
459
+
460
+ if (isCustom && ki > 0.08) {
461
+
462
+ warnings.push("Ki este ridicat; poti avea overshoot sau hunting.");
463
+
464
+ }
465
+
466
+ if (isCustom && kp > 0.8) {
467
+
468
+ warnings.push("Kp ridicat; verifica stabilitatea in sarcina variabila.");
469
+
470
+ }
471
+
472
+ if (isCustom && filterTauMs > 0 && filterTauMs < Math.max(50, controlPeriodMs / 2)) {
473
+
474
+ warnings.push("Filter Tau foarte mic fata de Control Period poate lasa zgomotul sa intre in regulator.");
475
+
476
+ }
477
+
478
+ if (failSecureEnabled && failSecureTimeSec < 1) {
479
+
480
+ errors.push("Fail Secure Time trebuie sa fie >= 1 sec.");
481
+
482
+ }
483
+
484
+ if (a3Enabled && a3Port < 1) {
485
+
486
+ errors.push("A3 Modbus Port trebuie sa fie >= 1.");
487
+
488
+ }
489
+
490
+ if (a3Enabled && a3UnitId < 0) {
491
+
492
+ errors.push("A3 Unit ID trebuie sa fie >= 0.");
493
+
494
+ }
495
+
496
+ if (a3Enabled && a3RampValue < 0) {
497
+
498
+ errors.push("A3 Ramp Value trebuie sa fie >= 0.");
499
+
500
+ }
501
+
502
+ if (isCustom && transientIgnoreMs < 0) {
503
+
504
+ errors.push("Transient Ignore trebuie sa fie >= 0 ms.");
505
+
506
+ }
507
+
508
+ if (outputHeartbeatMs < 250 || outputHeartbeatMs > 3500) {
509
+
510
+ errors.push("Output Heartbeat trebuie sa fie intre 250 si 3500 ms, sub timeout-ul implicit al multi-inverter.");
511
+
512
+ }
513
+
514
+ if (isCustom && (maxStepUpW < 0 || maxStepDownW < 0)) {
515
+
516
+ errors.push("Max Step Up/Down trebuie sa fie >= 0.");
517
+
518
+ }
519
+
520
+ if (isCustom && adaptiveErrorBandW < 1) {
521
+
522
+ errors.push("Adaptive Error Band trebuie sa fie >= 1 W.");
523
+
524
+ }
525
+
526
+ if (isCustom && (adaptiveMinScale <= 0 || adaptiveMinScale > 1)) {
527
+
528
+ errors.push("Adaptive Min Scale trebuie sa fie in intervalul (0, 1].");
529
+
530
+ }
531
+
532
+ if ((useGlobalPvCap || feedForwardEnabled) && !globalPvTotalVar) {
533
+
534
+ errors.push("PV Global Variable este obligatoriu pentru Dynamic PV Cap sau PV Feed-forward.");
535
+
536
+ }
537
+
538
+ if (pvCapSupported && globalPvCapOffsetW < 0) {
539
+
540
+ errors.push("PV Cap Offset trebuie sa fie >= 0.");
541
+
542
+ }
543
+ if (pvCapSupported && (pvCapHoldMs < 0 || pvCapReleaseWPerSec < 0)) {
544
+ errors.push("PV Availability Hold si PV Estimate Release trebuie sa fie >= 0.");
545
+ }
546
+ if (feedForwardGain < 0 || feedForwardGain > 1.5) {
547
+ errors.push("Feed-forward Gain trebuie sa fie intre 0 si 1.5.");
548
+ }
549
+
550
+ if (nominalPowerFromGlobal && !nominalPowerGlobalVar) {
551
+
552
+ errors.push("Global Variable Name este obligatoriu cand citirea Nominal Power din variabila globala este activa.");
553
+
554
+ }
555
+
556
+ if (maxLimitFromGlobal && !maxLimitGlobalVar) {
557
+
558
+ errors.push("Global Variable Name este obligatoriu cand citirea Max Limit din variabila globala este activa.");
559
+
560
+ }
561
+
562
+
563
+ const blocks = [];
564
+
565
+ if (errors.length) {
566
+
567
+ blocks.push('<div class="ce-validation-block ce-error"><i class="fa fa-exclamation-triangle"></i> ' + errors.join(" ") + "</div>");
568
+
569
+ }
570
+
571
+ if (warnings.length) {
572
+
573
+ blocks.push('<div class="ce-validation-block ce-warning"><i class="fa fa-exclamation-circle"></i> ' + warnings.join(" ") + "</div>");
574
+
575
+ }
576
+
577
+
578
+ $("#node-control-export-validation").html(blocks.join("")).toggle(blocks.length > 0);
579
+
580
+ }
581
+
582
+
583
+ function toggleFields() {
584
+
585
+ if ($("#node-input-a3TestsEnabled").is(":checked") && $("#node-input-mode").val() !== "nominal_power") {
586
+
587
+ $("#node-input-mode").val("nominal_power");
588
+
589
+ }
590
+
591
+ const mode = $("#node-input-mode").val();
592
+
593
+ const closedLoop = mode !== "nominal_power";
594
+
595
+ const pvCapSupported = mode !== "nominal_power";
596
+
597
+ const showAdvanced = closedLoop && $("#node-input-tuningPreset").val() === "custom";
598
+
599
+ const nominalPowerFromGlobal = $("#node-input-nominalPowerFromGlobal").is(":checked");
600
+ const maxLimitFromGlobal = $("#node-input-maxLimitFromGlobal").is(":checked");
601
+
602
+ const useGlobalPvCap = $("#node-input-useGlobalPvCap").is(":checked");
603
+ const feedForwardEnabled = $("#node-input-feedForwardEnabled").is(":checked");
604
+
605
+
606
+ $("#row-grid-setpoint").toggle(mode === "grid_set_point");
607
+
608
+ $("#row-nominal-power-global-checkbox").toggle(mode === "nominal_power");
609
+
610
+ $("#row-nominal-power-global-var").toggle(mode === "nominal_power" && nominalPowerFromGlobal);
611
+
612
+ $("#row-nominal-power").toggle(mode === "nominal_power" && !nominalPowerFromGlobal);
613
+
614
+ $("#row-max-limit").toggle(!maxLimitFromGlobal);
615
+
616
+ $("#row-max-limit-global-var").toggle(maxLimitFromGlobal);
617
+
618
+ $("#row-zero-feed-in-mode").toggle(mode === "zero_feed_in");
619
+
620
+ $(".advanced-section").toggle(closedLoop);
621
+
622
+ $("#row-load-normal-custom").toggle(closedLoop && $("#node-input-tuningPreset").val() === "custom");
623
+
624
+
625
+ $(".loop-row").toggle(showAdvanced);
626
+
627
+ $("#row-sign-convention").toggle(closedLoop);
628
+
629
+ $("#row-tuning-preset").toggle(closedLoop);
630
+
631
+ $("#row-legacy-max-step").toggle(false);
632
+
633
+ $("#row-fail-secure-time").toggle($("#node-input-failSecureEnabled").is(":checked"));
634
+ $("#row-a3-modbus-host, #row-a3-modbus-port, #row-a3-modbus-unit, #row-a3-ramp-type, #row-a3-ramp-value, #row-a3-map").toggle($("#node-input-a3TestsEnabled").is(":checked"));
635
+
636
+ $("#row-use-global-pv-cap").toggle(pvCapSupported);
637
+
638
+ $("#row-global-pv-var").toggle(pvCapSupported && (useGlobalPvCap || feedForwardEnabled));
639
+
640
+ $("#row-global-pv-offset").toggle(pvCapSupported && useGlobalPvCap);
641
+ $("#row-pv-cap-growth-only, #row-pv-cap-hold, #row-pv-cap-release").toggle(pvCapSupported && useGlobalPvCap);
642
+ $("#row-feed-forward-enabled").toggle(pvCapSupported);
643
+ $("#row-feed-forward-gain").toggle(pvCapSupported && feedForwardEnabled);
644
+
645
+ showValidation();
646
+
647
+ }
648
+
649
+
650
+ function normalizeZeroFeedInModeSelection() {
651
+
652
+ const current = String($("#node-input-zeroFeedInMode").val() || "").toLowerCase();
653
+
654
+ if (current !== "relative" && current !== "absolute") {
655
+
656
+ $("#node-input-zeroFeedInMode").val("relative");
657
+
658
+ }
659
+
660
+ }
661
+
662
+
663
+ $("#node-input-mode")
664
+
665
+ .off(".ceEditor")
666
+
667
+ .on("change.ceEditor", toggleFields);
668
+
669
+
670
+ $("#node-input-tuningPreset")
671
+
672
+ .off(".ceEditor")
673
+
674
+ .on("change.ceEditor", function () {
675
+
676
+ const presetKey = $("#node-input-tuningPreset").val();
677
+
678
+ if (presetKey === "custom" && !customDefaultsSeeded) {
679
+
680
+ // Entering custom for the first time: preload from Normal
681
+
682
+ // so user can tweak starting from the known normal behavior.
683
+
684
+ applyPreset("normal");
685
+
686
+ customDefaultsSeeded = true;
687
+
688
+ }
689
+
690
+ if (presetKey !== "custom") {
691
+
692
+ applyPreset(presetKey);
693
+
694
+ }
695
+
696
+ toggleFields();
697
+
698
+ });
699
+
700
+
701
+ $("#node-input-loadNormalCustom")
702
+
703
+ .off(".ceEditor")
704
+
705
+ .on("click.ceEditor", function (evt) {
706
+
707
+ evt.preventDefault();
708
+
709
+ $("#node-input-tuningPreset").val("custom");
710
+
711
+ applyPreset("normal");
712
+
713
+ customDefaultsSeeded = true;
714
+
715
+ toggleFields();
716
+
717
+ });
718
+
719
+
720
+ $(tuningFieldSelector)
721
+
722
+ .off(".ceEditor")
723
+
724
+ .on("keyup.ceEditor change.ceEditor", markCustomPreset);
725
+
726
+
727
+ $(validationFieldSelector)
728
+
729
+ .off(".ceEditor")
730
+
731
+ .on("keyup.ceEditor change.ceEditor", showValidation);
732
+
733
+
734
+ $("#node-input-failSecureEnabled")
735
+
736
+ .off(".ceEditor")
737
+
738
+ .on("change.ceEditor", function() {
739
+
740
+ toggleFields();
741
+
742
+ });
743
+
744
+
745
+ $("#node-input-failSecureTimeSec")
746
+
747
+ .off(".ceEditor")
748
+
749
+ .on("keyup.ceEditor change.ceEditor", showValidation);
750
+
751
+
752
+ $("#node-input-a3TestsEnabled")
753
+
754
+ .off(".ceEditor")
755
+
756
+ .on("change.ceEditor", function() {
757
+
758
+ if ($(this).is(":checked")) {
759
+
760
+ $("#node-input-mode").val("nominal_power");
761
+
762
+ }
763
+
764
+ toggleFields();
765
+
766
+ });
767
+
768
+
769
+ $("#node-input-a3ModbusPort, #node-input-a3ModbusUnitId, #node-input-a3RampValue")
770
+
771
+ .off(".ceEditor")
772
+
773
+ .on("keyup.ceEditor change.ceEditor", showValidation);
774
+
775
+
776
+ $("#node-input-useGlobalPvCap")
777
+
778
+ .off(".ceEditor")
779
+
780
+ .on("change.ceEditor", function() {
781
+
782
+ toggleFields();
783
+
784
+ });
785
+
786
+
787
+ $("#node-input-globalPvTotalVar")
788
+
789
+ .off(".ceEditor")
790
+
791
+ .on("blur.ceEditor", function() {
792
+
793
+ const key = String($(this).val() || "").trim();
794
+
795
+ $(this).val(key || "total_pv");
796
+
797
+ showValidation();
798
+
799
+ });
800
+
801
+
802
+ $("#node-input-globalPvCapOffsetW")
803
+
804
+ .off(".ceEditor")
805
+
806
+ .on("keyup.ceEditor change.ceEditor", showValidation);
807
+
808
+ $("#node-input-feedForwardEnabled")
809
+ .off(".ceEditor")
810
+ .on("change.ceEditor", toggleFields);
811
+
812
+ $("#node-input-pvCapHoldMs, #node-input-pvCapReleaseWPerSec, #node-input-feedForwardGain")
813
+ .off(".ceEditor")
814
+ .on("keyup.ceEditor change.ceEditor", showValidation);
815
+
816
+
817
+ $("#node-input-nominalPowerFromGlobal")
818
+
819
+ .off(".ceEditor")
820
+
821
+ .on("change.ceEditor", function() {
822
+
823
+ toggleFields();
824
+
825
+ });
826
+
827
+
828
+ $("#node-input-nominalPowerGlobalVar")
829
+
830
+ .off(".ceEditor")
831
+
832
+ .on("blur.ceEditor", function() {
833
+
834
+ const key = String($(this).val() || "").trim();
835
+
836
+ $(this).val(key || "nominal_power");
837
+
838
+ showValidation();
839
+
840
+ });
841
+
842
+ $("#node-input-maxLimitFromGlobal")
843
+
844
+ .off(".ceEditor")
845
+
846
+ .on("change.ceEditor", toggleFields);
847
+
848
+
849
+ $("#node-input-maxLimitGlobalVar")
850
+
851
+ .off(".ceEditor")
852
+
853
+ .on("blur.ceEditor", function() {
854
+
855
+ const key = String($(this).val() || "").trim();
856
+
857
+ $(this).val(key || "max_limit");
858
+
859
+ showValidation();
860
+
861
+ });
862
+
863
+
864
+ $("#node-input-adaptiveGains")
865
+
866
+ .off(".ceEditor")
867
+
868
+ .on("change.ceEditor", function() {
869
+
870
+ const enabled = $("#node-input-adaptiveGains").is(":checked");
871
+
872
+ $("#row-adaptive-error-band, #row-adaptive-min-scale").toggle(enabled);
873
+
874
+ showValidation();
875
+
876
+ });
877
+
878
+
879
+ if ($("#node-input-tuningPreset").val() === "tuning") {
880
+
881
+ $("#node-input-tuningPreset").val("normal");
882
+
883
+ }
884
+
885
+ if (!$("#node-input-tuningPreset").val()) {
886
+
887
+ $("#node-input-tuningPreset").val("normal");
888
+
889
+ }
890
+
891
+ customDefaultsSeeded = $("#node-input-tuningPreset").val() === "custom";
892
+
893
+ normalizeZeroFeedInModeSelection();
894
+
895
+ if ($("#node-input-adaptiveGains").is(":checked")) {
896
+
897
+ $("#row-adaptive-error-band, #row-adaptive-min-scale").show();
898
+
899
+ } else {
900
+
901
+ $("#row-adaptive-error-band, #row-adaptive-min-scale").hide();
902
+
903
+ }
904
+
905
+ toggleFields();
906
+
907
+ showValidation();
908
+
909
+ setTimeout(function () { editorReady = true; }, 0);
910
+
911
+ },
912
+
913
+
914
+ oneditsave: function () {
915
+
916
+ this.useGlobalPvCap = $("#node-input-useGlobalPvCap").is(":checked");
917
+
918
+ this.globalPvTotalVar = String($("#node-input-globalPvTotalVar").val() || "").trim() || "total_pv";
919
+
920
+ this.globalPvCapOffsetW = Number($("#node-input-globalPvCapOffsetW").val() || 0);
921
+ this.pvCapOnlyBlocksIncrease = $("#node-input-pvCapOnlyBlocksIncrease").is(":checked");
922
+ this.pvCapHoldMs = Number($("#node-input-pvCapHoldMs").val() || 0);
923
+ this.pvCapReleaseWPerSec = Number($("#node-input-pvCapReleaseWPerSec").val() || 0);
924
+ this.feedForwardEnabled = $("#node-input-feedForwardEnabled").is(":checked");
925
+ this.feedForwardGain = Number($("#node-input-feedForwardGain").val() || 0);
926
+
927
+ this.nominalPowerFromGlobal = $("#node-input-nominalPowerFromGlobal").is(":checked");
928
+
929
+ this.nominalPowerGlobalVar = String($("#node-input-nominalPowerGlobalVar").val() || "").trim() || "nominal_power";
930
+
931
+ this.failSecureEnabled = $("#node-input-failSecureEnabled").is(":checked");
932
+
933
+ this.failSecureTimeSec = Number($("#node-input-failSecureTimeSec").val() || 10);
934
+
935
+ this.a3TestsEnabled = $("#node-input-a3TestsEnabled").is(":checked");
936
+
937
+ this.a3ModbusHost = String($("#node-input-a3ModbusHost").val() || "").trim() || "0.0.0.0";
938
+
939
+ this.a3ModbusPort = Number($("#node-input-a3ModbusPort").val() || 1502);
940
+
941
+ this.a3ModbusUnitId = Number($("#node-input-a3ModbusUnitId").val() || 1);
942
+
943
+ this.a3RampType = $("#node-input-a3RampType").val() || "none";
944
+
945
+ this.a3RampValue = Number($("#node-input-a3RampValue").val() || 0);
946
+
947
+ if (this.a3TestsEnabled) {
948
+
949
+ $("#node-input-mode").val("nominal_power");
950
+
951
+ this.mode = "nominal_power";
952
+
953
+ }
954
+
955
+ this.outputs = this.failSecureEnabled ? 2 : 1;
956
+
957
+ $("#node-input-mode, #node-input-tuningPreset, #node-input-deadband, #node-input-filterTauMs, #node-input-kp, #node-input-ki, #node-input-recoveryDelayMs, #node-input-transientIgnoreMs, #node-input-maxStepUpW, #node-input-maxStepDownW, #node-input-adaptiveGains, #node-input-adaptiveErrorBandW, #node-input-adaptiveMinScale, #node-input-minLimit, #node-input-maxLimit, #node-input-exportThresholdW, #node-input-recoverThresholdW, #node-input-staleTimeoutMs, #node-input-outputHeartbeatMs, #node-input-controlPeriodMs, #node-input-useGlobalPvCap, #node-input-globalPvTotalVar, #node-input-globalPvCapOffsetW, #node-input-nominalPowerFromGlobal, #node-input-nominalPowerGlobalVar, #node-input-failSecureEnabled, #node-input-failSecureTimeSec, #node-input-a3TestsEnabled, #node-input-a3ModbusPort, #node-input-a3ModbusUnitId, #node-input-a3RampValue").off(".ceEditor");
958
+
959
+ },
960
+
961
+
962
+ oneditcancel: function () {
963
+
964
+ $("#node-input-mode, #node-input-tuningPreset, #node-input-deadband, #node-input-filterTauMs, #node-input-kp, #node-input-ki, #node-input-recoveryDelayMs, #node-input-transientIgnoreMs, #node-input-maxStepUpW, #node-input-maxStepDownW, #node-input-adaptiveGains, #node-input-adaptiveErrorBandW, #node-input-adaptiveMinScale, #node-input-minLimit, #node-input-maxLimit, #node-input-exportThresholdW, #node-input-recoverThresholdW, #node-input-staleTimeoutMs, #node-input-outputHeartbeatMs, #node-input-controlPeriodMs, #node-input-useGlobalPvCap, #node-input-globalPvTotalVar, #node-input-globalPvCapOffsetW, #node-input-nominalPowerFromGlobal, #node-input-nominalPowerGlobalVar, #node-input-failSecureEnabled, #node-input-failSecureTimeSec, #node-input-a3TestsEnabled, #node-input-a3ModbusPort, #node-input-a3ModbusUnitId, #node-input-a3RampValue").off(".ceEditor");
965
+
966
+ }
967
+
968
+
969
+ };
970
+
971
+
972
+ RED.nodes.registerType('control-export', controlExportDefinition);
973
+
974
+
975
+ </script>
976
+
977
+
978
+
979
+ <script type="text/html" data-template-name="control-export">
980
+ <div class="form-row"><label for="node-input-license"><i class="fa fa-key"></i> License</label><input type="text" id="node-input-license"></div>
981
+
982
+ <div class="control-export-editor">
983
+
984
+ <div class="pr-section">
985
+
986
+ <div class="pr-section-title"><i class="fa fa-sliders"></i> Basic</div>
987
+
988
+ <p class="pr-section-note">Setarile esentiale pentru functionarea nodului.</p>
989
+
990
+
991
+ <div class="pr-grid pr-grid-2">
992
+
993
+ <div class="form-row">
994
+
995
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
996
+
997
+ <input type="text" id="node-input-name" placeholder="control-export">
998
+
999
+ </div>
1000
+
1001
+
1002
+ <div class="form-row">
1003
+
1004
+ <label for="node-input-mode"><i class="fa fa-cogs"></i> Mode</label>
1005
+
1006
+ <select id="node-input-mode">
1007
+
1008
+ <option value="zero_feed_in">Zero Feed-In</option>
1009
+
1010
+ <option value="grid_set_point">Grid Set Point</option>
1011
+
1012
+ <option value="nominal_power">Nominal Power</option>
1013
+
1014
+ </select>
1015
+
1016
+ </div>
1017
+
1018
+
1019
+ <div class="form-row" id="row-grid-setpoint">
1020
+
1021
+ <label for="node-input-gridSetPoint"><i class="fa fa-bullseye"></i> Grid Target (W)</label>
1022
+
1023
+ <input type="text" id="node-input-gridSetPoint" placeholder="0">
1024
+
1025
+ </div>
1026
+
1027
+
1028
+ <div class="form-row" id="row-zero-feed-in-mode">
1029
+
1030
+ <label for="node-input-zeroFeedInMode"><i class="fa fa-crosshairs"></i> Zero Feed-In</label>
1031
+
1032
+ <select id="node-input-zeroFeedInMode">
1033
+
1034
+ <option value="relative">0 Relativ</option>
1035
+
1036
+ <option value="absolute">0 Absolut</option>
1037
+
1038
+ </select>
1039
+
1040
+ </div>
1041
+
1042
+
1043
+ <div class="form-row" id="row-nominal-power">
1044
+
1045
+ <label for="node-input-nominalPower"><i class="fa fa-bolt"></i> Fixed Power (W)</label>
1046
+
1047
+ <input type="text" id="node-input-nominalPower" placeholder="1000">
1048
+
1049
+ </div>
1050
+
1051
+
1052
+ <div class="form-row pr-checkbox-row" id="row-nominal-power-global-checkbox">
1053
+
1054
+ <label for="node-input-nominalPowerFromGlobal"><i class="fa fa-globe"></i> Read Nominal Power From Global</label>
1055
+
1056
+ <input type="checkbox" id="node-input-nominalPowerFromGlobal">
1057
+
1058
+ </div>
1059
+
1060
+
1061
+ <div class="form-row" id="row-nominal-power-global-var">
1062
+
1063
+ <label for="node-input-nominalPowerGlobalVar"><i class="fa fa-key"></i> Global Variable Name</label>
1064
+
1065
+ <input type="text" id="node-input-nominalPowerGlobalVar" placeholder="nominal_power">
1066
+
1067
+ </div>
1068
+
1069
+
1070
+ <div class="form-row pr-checkbox-row">
1071
+
1072
+ <label for="node-input-a3TestsEnabled"><i class="fa fa-flask"></i> Teste A3</label>
1073
+
1074
+ <input type="checkbox" id="node-input-a3TestsEnabled">
1075
+
1076
+ </div>
1077
+
1078
+
1079
+ <div class="form-row" id="row-a3-modbus-host">
1080
+
1081
+ <label for="node-input-a3ModbusHost"><i class="fa fa-server"></i> A3 Host</label>
1082
+
1083
+ <input type="text" id="node-input-a3ModbusHost" placeholder="0.0.0.0">
1084
+
1085
+ </div>
1086
+
1087
+
1088
+ <div class="form-row" id="row-a3-modbus-port">
1089
+
1090
+ <label for="node-input-a3ModbusPort"><i class="fa fa-plug"></i> A3 Port</label>
1091
+
1092
+ <input type="text" id="node-input-a3ModbusPort" placeholder="1502">
1093
+
1094
+ </div>
1095
+
1096
+
1097
+ <div class="form-row" id="row-a3-modbus-unit">
1098
+
1099
+ <label for="node-input-a3ModbusUnitId"><i class="fa fa-hashtag"></i> A3 Unit ID</label>
1100
+
1101
+ <input type="text" id="node-input-a3ModbusUnitId" placeholder="1">
1102
+
1103
+ </div>
1104
+
1105
+
1106
+ <div class="form-row" id="row-a3-ramp-type">
1107
+
1108
+ <label for="node-input-a3RampType"><i class="fa fa-sliders"></i> A3 Ramp Type</label>
1109
+
1110
+ <select id="node-input-a3RampType">
1111
+
1112
+ <option value="none">No ramp</option>
1113
+
1114
+ <option value="time">Ramp Time (s)</option>
1115
+
1116
+ <option value="gradient_pct">Gradient (%/s)</option>
1117
+
1118
+ <option value="gradient_w">Gradient (W/s)</option>
1119
+
1120
+ </select>
1121
+
1122
+ </div>
1123
+
1124
+
1125
+ <div class="form-row" id="row-a3-ramp-value">
1126
+
1127
+ <label for="node-input-a3RampValue"><i class="fa fa-tachometer"></i> A3 Ramp Value</label>
1128
+
1129
+ <input type="text" id="node-input-a3RampValue" placeholder="0">
1130
+
1131
+ </div>
1132
+
1133
+
1134
+ <div class="form-row pr-span-2" id="row-a3-map">
1135
+
1136
+ <div class="ce-meta-info">A3 Modbus: reg 2-3 = nominal W (int32), reg 4 = ramp type (0 none, 1 time, 2 %/s, 3 W/s), reg 5-6 = ramp value (int32).</div>
1137
+
1138
+ </div>
1139
+
1140
+
1141
+ <div class="form-row loop-row" id="row-tuning-preset">
1142
+
1143
+ <label for="node-input-tuningPreset"><i class="fa fa-magic"></i> Tuning Preset</label>
1144
+
1145
+ <select id="node-input-tuningPreset">
1146
+
1147
+ <option value="normal">Normal</option>
1148
+
1149
+ <option value="stable_grid">Stable Grid Setpoint</option>
1150
+
1151
+ <option value="custom">Custom</option>
1152
+
1153
+ </select>
1154
+
1155
+ </div>
1156
+
1157
+
1158
+ <div class="form-row" id="row-min-limit">
1159
+
1160
+ <label for="node-input-minLimit"><i class="fa fa-arrow-down"></i> Min Limit (W)</label>
1161
+
1162
+ <input type="text" id="node-input-minLimit" placeholder="0">
1163
+
1164
+ </div>
1165
+
1166
+
1167
+ <div class="form-row" id="row-max-limit">
1168
+
1169
+ <label for="node-input-maxLimit"><i class="fa fa-arrow-up"></i> Max Limit (W)</label>
1170
+
1171
+ <input type="text" id="node-input-maxLimit" placeholder="1000">
1172
+
1173
+ </div>
1174
+
1175
+
1176
+ <div class="form-row pr-checkbox-row">
1177
+
1178
+ <label for="node-input-maxLimitFromGlobal"><i class="fa fa-globe"></i> Read Max Limit From Global</label>
1179
+
1180
+ <input type="checkbox" id="node-input-maxLimitFromGlobal">
1181
+
1182
+ </div>
1183
+
1184
+
1185
+ <div class="form-row" id="row-max-limit-global-var">
1186
+
1187
+ <label for="node-input-maxLimitGlobalVar"><i class="fa fa-key"></i> Global Variable Name</label>
1188
+
1189
+ <input type="text" id="node-input-maxLimitGlobalVar" placeholder="max_limit">
1190
+
1191
+ </div>
1192
+
1193
+
1194
+ </div>
1195
+
1196
+ </div>
1197
+
1198
+
1199
+ <div class="pr-section advanced-section">
1200
+
1201
+ <div class="pr-section-header">
1202
+
1203
+ <div class="pr-section-title"><i class="fa fa-flask"></i> Advanced Parameters</div>
1204
+
1205
+ <div id="row-load-normal-custom" style="display:none;">
1206
+
1207
+ <a href="#" id="node-input-loadNormalCustom" class="ce-subtle-action"><i class="fa fa-undo"></i> Default values</a>
1208
+
1209
+ </div>
1210
+
1211
+ </div>
1212
+
1213
+ <p class="pr-section-note">Parametri de control, siguranta si diagnoza.</p>
1214
+
1215
+
1216
+ <div class="pr-grid pr-grid-2">
1217
+
1218
+ <div class="form-row loop-row" id="row-sign-convention">
1219
+
1220
+ <label for="node-input-signConvention"><i class="fa fa-random"></i> Sign Convention</label>
1221
+
1222
+ <select id="node-input-signConvention">
1223
+
1224
+ <option value="positive_import">+ = import, - = export</option>
1225
+
1226
+ <option value="positive_export">+ = export, - = import</option>
1227
+
1228
+ </select>
1229
+
1230
+ </div>
1231
+
1232
+
1233
+ <div class="form-row loop-row" id="row-deadband">
1234
+
1235
+ <label for="node-input-deadband"><i class="fa fa-arrows-h"></i> Deadband (W)</label>
1236
+
1237
+ <input type="text" id="node-input-deadband" placeholder="100">
1238
+
1239
+ </div>
1240
+
1241
+
1242
+ <div class="form-row loop-row" id="row-filter-tau">
1243
+
1244
+ <label for="node-input-filterTauMs"><i class="fa fa-sliders"></i> Filter Tau (ms)</label>
1245
+
1246
+ <input type="text" id="node-input-filterTauMs" placeholder="1500">
1247
+
1248
+ </div>
1249
+
1250
+
1251
+ <div class="form-row loop-row" id="row-control-period">
1252
+
1253
+ <label for="node-input-controlPeriodMs"><i class="fa fa-clock-o"></i> Control Period (ms)</label>
1254
+
1255
+ <input type="text" id="node-input-controlPeriodMs" placeholder="500">
1256
+
1257
+ </div>
1258
+
1259
+
1260
+ <div class="form-row loop-row" id="row-transient-ignore">
1261
+
1262
+ <label for="node-input-transientIgnoreMs"><i class="fa fa-hourglass-start"></i> Transient Ignore (ms)</label>
1263
+
1264
+ <input type="text" id="node-input-transientIgnoreMs" placeholder="2000">
1265
+
1266
+ </div>
1267
+
1268
+
1269
+ <div class="form-row loop-row" id="row-max-step-up">
1270
+
1271
+ <label for="node-input-maxStepUpW"><i class="fa fa-level-up"></i> Max Step Up (W/cycle)</label>
1272
+
1273
+ <input type="text" id="node-input-maxStepUpW" placeholder="0">
1274
+
1275
+ </div>
1276
+
1277
+
1278
+ <div class="form-row loop-row" id="row-max-step-down">
1279
+
1280
+ <label for="node-input-maxStepDownW"><i class="fa fa-level-down"></i> Max Step Down (W/cycle)</label>
1281
+
1282
+ <input type="text" id="node-input-maxStepDownW" placeholder="0">
1283
+
1284
+ </div>
1285
+
1286
+
1287
+ <div class="form-row loop-row pr-checkbox-row" id="row-adaptive-gains">
1288
+
1289
+ <label for="node-input-adaptiveGains"><i class="fa fa-magic"></i> Adaptive Gains</label>
1290
+
1291
+ <input type="checkbox" id="node-input-adaptiveGains">
1292
+
1293
+ </div>
1294
+
1295
+
1296
+ <div class="form-row loop-row" id="row-adaptive-error-band">
1297
+
1298
+ <label for="node-input-adaptiveErrorBandW"><i class="fa fa-arrows-h"></i> Adaptive Error Band (W)</label>
1299
+
1300
+ <input type="text" id="node-input-adaptiveErrorBandW" placeholder="2500">
1301
+
1302
+ </div>
1303
+
1304
+
1305
+ <div class="form-row loop-row" id="row-adaptive-min-scale">
1306
+
1307
+ <label for="node-input-adaptiveMinScale"><i class="fa fa-compress"></i> Adaptive Min Scale</label>
1308
+
1309
+ <input type="text" id="node-input-adaptiveMinScale" placeholder="0.25">
1310
+
1311
+ </div>
1312
+
1313
+
1314
+ <div class="form-row loop-row" id="row-kp">
1315
+
1316
+ <label for="node-input-kp"><i class="fa fa-line-chart"></i> Kp</label>
1317
+
1318
+ <input type="text" id="node-input-kp" placeholder="0.35">
1319
+
1320
+ </div>
1321
+
1322
+
1323
+ <div class="form-row loop-row" id="row-ki">
1324
+
1325
+ <label for="node-input-ki"><i class="fa fa-area-chart"></i> Ki</label>
1326
+
1327
+ <input type="text" id="node-input-ki" placeholder="0.03">
1328
+
1329
+ </div>
1330
+
1331
+ <div class="form-row loop-row" id="row-export-threshold">
1332
+
1333
+ <label for="node-input-exportThresholdW"><i class="fa fa-bell"></i> Export Threshold (W)</label>
1334
+
1335
+ <input type="text" id="node-input-exportThresholdW" placeholder="50">
1336
+
1337
+ </div>
1338
+
1339
+
1340
+ <div class="form-row loop-row" id="row-recover-threshold">
1341
+
1342
+ <label for="node-input-recoverThresholdW"><i class="fa fa-undo"></i> Recover Threshold (W)</label>
1343
+
1344
+ <input type="text" id="node-input-recoverThresholdW" placeholder="20">
1345
+
1346
+ </div>
1347
+
1348
+
1349
+ <div class="form-row loop-row" id="row-recovery-delay">
1350
+
1351
+ <label for="node-input-recoveryDelayMs"><i class="fa fa-hourglass-half"></i> Recovery Delay (ms)</label>
1352
+
1353
+ <input type="text" id="node-input-recoveryDelayMs" placeholder="1500">
1354
+
1355
+ </div>
1356
+
1357
+
1358
+ <div class="form-row loop-row" id="row-smoothing-alpha">
1359
+
1360
+ <label for="node-input-smoothingAlpha"><i class="fa fa-filter"></i> Legacy Smoothing Alpha</label>
1361
+
1362
+ <input type="text" id="node-input-smoothingAlpha" placeholder="0.15">
1363
+
1364
+ </div>
1365
+
1366
+
1367
+ <div class="form-row loop-row" id="row-legacy-max-step" style="display:none;">
1368
+
1369
+ <label for="node-input-maxStepW"><i class="fa fa-step-forward"></i> Legacy Max Step (W/ciclu)</label>
1370
+
1371
+ <input type="text" id="node-input-maxStepW" placeholder="0">
1372
+
1373
+ </div>
1374
+
1375
+ </div>
1376
+
1377
+ </div>
1378
+
1379
+
1380
+ <div class="pr-section">
1381
+
1382
+ <div class="pr-section-title"><i class="fa fa-shield"></i> Safety & Output</div>
1383
+
1384
+ <p class="pr-section-note">Limite, fallback pe stale telemetry si topic-ul de iesire.</p>
1385
+
1386
+
1387
+ <div class="pr-grid pr-grid-2">
1388
+
1389
+ <div class="form-row" id="row-stale-timeout">
1390
+
1391
+ <label for="node-input-staleTimeoutMs"><i class="fa fa-shield"></i> Stale Timeout (ms)</label>
1392
+
1393
+ <input type="text" id="node-input-staleTimeoutMs" placeholder="5000">
1394
+
1395
+ </div>
1396
+
1397
+ <div class="form-row">
1398
+ <label for="node-input-fastSetpointMaxTelemetryAgeMs"><i class="fa fa-bolt"></i> Setpoint Cache Max Telemetry Age (ms)</label>
1399
+ <input type="text" id="node-input-fastSetpointMaxTelemetryAgeMs" placeholder="600">
1400
+ </div>
1401
+
1402
+ <div class="form-row">
1403
+ <label for="node-input-outputHeartbeatMs"><i class="fa fa-heartbeat"></i> Output Heartbeat (ms)</label>
1404
+ <input type="text" id="node-input-outputHeartbeatMs" placeholder="2000">
1405
+ </div>
1406
+
1407
+
1408
+ <div class="form-row" id="row-safe-limit">
1409
+
1410
+ <label for="node-input-safeLimitOnStale"><i class="fa fa-life-ring"></i> Safe Limit on Stale (W)</label>
1411
+
1412
+ <input type="text" id="node-input-safeLimitOnStale" placeholder="0">
1413
+
1414
+ </div>
1415
+
1416
+
1417
+ <div class="form-row" id="row-debug-level">
1418
+
1419
+ <label for="node-input-debugLevel"><i class="fa fa-bug"></i> Debug</label>
1420
+
1421
+ <select id="node-input-debugLevel">
1422
+
1423
+ <option value="off">Off</option>
1424
+
1425
+ <option value="normal">Normal</option>
1426
+
1427
+ <option value="verbose">Verbose</option>
1428
+
1429
+ </select>
1430
+
1431
+ </div>
1432
+
1433
+
1434
+ <div class="form-row" id="row-start-limit-mode">
1435
+
1436
+ <label for="node-input-startLimitMode"><i class="fa fa-play"></i> Start Limit</label>
1437
+
1438
+ <select id="node-input-startLimitMode">
1439
+
1440
+ <option value="min_limit">Start from Min Limit</option>
1441
+
1442
+ <option value="last_limit">Start from Last Limit</option>
1443
+
1444
+ </select>
1445
+
1446
+ </div>
1447
+
1448
+
1449
+ <div class="form-row pr-span-2" id="row-output-topic">
1450
+
1451
+ <label for="node-input-outputTopic"><i class="fa fa-sign-out"></i> Output Topic</label>
1452
+
1453
+ <input type="text" id="node-input-outputTopic" placeholder="site-limit">
1454
+
1455
+ </div>
1456
+
1457
+
1458
+ <div class="form-row pr-checkbox-row" id="row-use-global-pv-cap">
1459
+
1460
+ <label for="node-input-useGlobalPvCap"><i class="fa fa-line-chart"></i> Dynamic PV Cap (global)</label>
1461
+
1462
+ <input type="checkbox" id="node-input-useGlobalPvCap">
1463
+
1464
+ </div>
1465
+
1466
+
1467
+ <div class="form-row" id="row-global-pv-var" style="display:none;">
1468
+
1469
+ <label for="node-input-globalPvTotalVar"><i class="fa fa-globe"></i> PV Global Variable</label>
1470
+
1471
+ <input type="text" id="node-input-globalPvTotalVar" placeholder="total_pv">
1472
+
1473
+ </div>
1474
+
1475
+
1476
+ <div class="form-row" id="row-global-pv-offset" style="display:none;">
1477
+
1478
+ <label for="node-input-globalPvCapOffsetW"><i class="fa fa-plus-circle"></i> PV Cap Offset (W)</label>
1479
+
1480
+ <input type="text" id="node-input-globalPvCapOffsetW" placeholder="500">
1481
+
1482
+ </div>
1483
+
1484
+ <div class="form-row pr-checkbox-row" id="row-pv-cap-growth-only" style="display:none;">
1485
+ <label for="node-input-pvCapOnlyBlocksIncrease"><i class="fa fa-level-up"></i> PV Cap Growth Only</label>
1486
+ <input type="checkbox" id="node-input-pvCapOnlyBlocksIncrease">
1487
+ </div>
1488
+
1489
+ <div class="form-row" id="row-pv-cap-hold" style="display:none;">
1490
+ <label for="node-input-pvCapHoldMs"><i class="fa fa-pause"></i> PV Availability Hold (ms)</label>
1491
+ <input type="text" id="node-input-pvCapHoldMs" placeholder="10000">
1492
+ </div>
1493
+
1494
+ <div class="form-row" id="row-pv-cap-release" style="display:none;">
1495
+ <label for="node-input-pvCapReleaseWPerSec"><i class="fa fa-level-down"></i> PV Estimate Release (W/s)</label>
1496
+ <input type="text" id="node-input-pvCapReleaseWPerSec" placeholder="1000">
1497
+ </div>
1498
+
1499
+ <div class="form-row pr-checkbox-row" id="row-feed-forward-enabled">
1500
+ <label for="node-input-feedForwardEnabled"><i class="fa fa-forward"></i> PV Feed-forward</label>
1501
+ <input type="checkbox" id="node-input-feedForwardEnabled">
1502
+ </div>
1503
+
1504
+ <div class="form-row" id="row-feed-forward-gain">
1505
+ <label for="node-input-feedForwardGain"><i class="fa fa-bullseye"></i> Feed-forward Gain</label>
1506
+ <input type="text" id="node-input-feedForwardGain" placeholder="0.85">
1507
+ </div>
1508
+
1509
+
1510
+ <div class="form-row pr-checkbox-row" id="row-fail-secure-enabled">
1511
+
1512
+ <label for="node-input-failSecureEnabled"><i class="fa fa-exclamation-triangle"></i> Fail Secure</label>
1513
+
1514
+ <input type="checkbox" id="node-input-failSecureEnabled">
1515
+
1516
+ </div>
1517
+
1518
+
1519
+ <div class="form-row" id="row-fail-secure-time" style="display:none;">
1520
+
1521
+ <label for="node-input-failSecureTimeSec"><i class="fa fa-clock-o"></i> Fail Secure Time (s)</label>
1522
+
1523
+ <input type="text" id="node-input-failSecureTimeSec" placeholder="10">
1524
+
1525
+ </div>
1526
+
1527
+ </div>
1528
+
1529
+
1530
+ <div class="form-row" id="node-control-export-validation" style="display:none;"></div>
1531
+
1532
+ </div>
1533
+
1534
+ </div>
1535
+
1536
+ </script>
1537
+
1538
+
1539
+ <style>
1540
+
1541
+ .control-export-editor {
1542
+
1543
+ --pr-border: #d7dde5;
1544
+
1545
+ --pr-muted: #667085;
1546
+
1547
+ --pr-surface: #f8fafc;
1548
+
1549
+ --ce-invalid: #b94a48;
1550
+
1551
+ --ce-warn: #8a6d3b;
1552
+
1553
+ }
1554
+
1555
+
1556
+ .control-export-editor .pr-section {
1557
+
1558
+ margin: 0 0 18px;
1559
+
1560
+ padding: 14px 16px 16px;
1561
+
1562
+ border: 1px solid var(--pr-border);
1563
+
1564
+ border-radius: 10px;
1565
+
1566
+ background: var(--pr-surface);
1567
+
1568
+ }
1569
+
1570
+
1571
+ .control-export-editor .pr-section-title {
1572
+
1573
+ display: flex;
1574
+
1575
+ align-items: center;
1576
+
1577
+ gap: 8px;
1578
+
1579
+ margin: 0 0 4px;
1580
+
1581
+ font-size: 15px;
1582
+
1583
+ font-weight: 600;
1584
+
1585
+ color: #243447;
1586
+
1587
+ }
1588
+
1589
+
1590
+ .control-export-editor .pr-section-note {
1591
+
1592
+ margin: 0 0 14px;
1593
+
1594
+ color: var(--pr-muted);
1595
+
1596
+ font-size: 12px;
1597
+
1598
+ line-height: 1.45;
1599
+
1600
+ }
1601
+
1602
+
1603
+ .control-export-editor .pr-section-header {
1604
+
1605
+ display: flex;
1606
+
1607
+ align-items: flex-start;
1608
+
1609
+ justify-content: space-between;
1610
+
1611
+ gap: 10px;
1612
+
1613
+ margin: 0 0 4px;
1614
+
1615
+ }
1616
+
1617
+
1618
+ .control-export-editor .pr-grid {
1619
+
1620
+ display: grid;
1621
+
1622
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
1623
+
1624
+ gap: 12px 16px;
1625
+
1626
+ }
1627
+
1628
+
1629
+ .control-export-editor .pr-grid-2 {
1630
+
1631
+ grid-template-columns: repeat(2, minmax(320px, 1fr));
1632
+
1633
+ }
1634
+
1635
+
1636
+ .control-export-editor .pr-span-2 {
1637
+
1638
+ grid-column: 1 / -1;
1639
+
1640
+ }
1641
+
1642
+
1643
+ .control-export-editor .form-row {
1644
+
1645
+ margin-bottom: 12px;
1646
+
1647
+ }
1648
+
1649
+
1650
+ .control-export-editor .pr-grid .form-row,
1651
+
1652
+ .control-export-editor .form-row:last-child {
1653
+
1654
+ margin-bottom: 0;
1655
+
1656
+ }
1657
+
1658
+
1659
+ .control-export-editor .pr-checkbox-row {
1660
+
1661
+ display: flex;
1662
+
1663
+ align-items: center;
1664
+
1665
+ justify-content: flex-start;
1666
+
1667
+ gap: 8px;
1668
+
1669
+ }
1670
+
1671
+
1672
+ .control-export-editor .form-row > label {
1673
+
1674
+ width: 170px;
1675
+
1676
+ font-weight: 600;
1677
+
1678
+ }
1679
+
1680
+
1681
+ .control-export-editor .form-row > input[type="text"],
1682
+
1683
+ .control-export-editor .form-row > input[type="number"],
1684
+
1685
+ .control-export-editor .form-row > select {
1686
+
1687
+ width: calc(100% - 180px);
1688
+
1689
+ }
1690
+
1691
+
1692
+ .control-export-editor .pr-checkbox-row > label {
1693
+
1694
+ width: auto;
1695
+
1696
+ margin: 0;
1697
+
1698
+ flex: 0 0 auto;
1699
+
1700
+ }
1701
+
1702
+
1703
+ .control-export-editor .pr-checkbox-row > input[type="checkbox"] {
1704
+
1705
+ width: auto;
1706
+
1707
+ margin: 0;
1708
+
1709
+ float: none;
1710
+
1711
+ flex: 0 0 auto;
1712
+
1713
+ }
1714
+
1715
+
1716
+ .control-export-editor .ce-meta-info {
1717
+
1718
+ border: 1px solid #d8e1ea;
1719
+
1720
+ border-radius: 6px;
1721
+
1722
+ background: #f7fbff;
1723
+
1724
+ color: #4b5563;
1725
+
1726
+ padding: 8px 10px;
1727
+
1728
+ line-height: 1.35;
1729
+
1730
+ }
1731
+
1732
+
1733
+ .control-export-editor #node-control-export-validation { margin-top: 8px; }
1734
+
1735
+
1736
+ .control-export-editor .ce-validation-block {
1737
+
1738
+ border-radius: 6px;
1739
+
1740
+ padding: 8px 10px;
1741
+
1742
+ margin-top: 6px;
1743
+
1744
+ font-weight: 600;
1745
+
1746
+ border: 1px solid transparent;
1747
+
1748
+ }
1749
+
1750
+
1751
+ .control-export-editor .ce-validation-block i { margin-right: 6px; }
1752
+
1753
+
1754
+ .control-export-editor .ce-validation-block.ce-error {
1755
+
1756
+ color: var(--ce-invalid);
1757
+
1758
+ background: #fdf2f2;
1759
+
1760
+ border-color: #f3c2c2;
1761
+
1762
+ }
1763
+
1764
+
1765
+ .control-export-editor .ce-validation-block.ce-warning {
1766
+
1767
+ color: var(--ce-warn);
1768
+
1769
+ background: #fff8e8;
1770
+
1771
+ border-color: #f1dfb3;
1772
+
1773
+ }
1774
+
1775
+
1776
+ .control-export-editor .ce-subtle-action {
1777
+
1778
+ display: inline-flex;
1779
+
1780
+ align-items: center;
1781
+
1782
+ gap: 6px;
1783
+
1784
+ padding: 6px 10px;
1785
+
1786
+ background: #eceff3;
1787
+
1788
+ color: #4b5563;
1789
+
1790
+ font-size: 13px;
1791
+
1792
+ text-decoration: none;
1793
+
1794
+ border: 1px solid #d6dbe3;
1795
+
1796
+ border-radius: 10px;
1797
+
1798
+ white-space: nowrap;
1799
+
1800
+ }
1801
+
1802
+
1803
+ .control-export-editor .ce-subtle-action:hover {
1804
+
1805
+ color: #243447;
1806
+
1807
+ background: #e5e9ef;
1808
+
1809
+ border-color: #c8d0db;
1810
+
1811
+ }
1812
+
1813
+
1814
+ @media (max-width: 1100px) {
1815
+
1816
+ .control-export-editor .pr-grid-2 {
1817
+
1818
+ grid-template-columns: 1fr;
1819
+
1820
+ }
1821
+
1822
+ }
1823
+
1824
+ </style>
1825
+
1826
+
1827
+
1828
+ <script type="text/html" data-help-name="control-export">
1829
+
1830
+ <p>
1831
+
1832
+ Nodul <b>control-export</b> calculeaza limita totala de putere care trebuie trimisa catre
1833
+
1834
+ sistemul fotovoltaic. Limita rezultata apare in <code>msg.payload.site_limit_w</code> si poate fi
1835
+
1836
+ folosita direct de <code>multi-inverter</code> sau de un nod de scriere catre invertor.
1837
+
1838
+ </p>
1839
+
1840
+
1841
+ <pre>meter / PCC telemetry -> control-export -> multi-inverter -> inverter write</pre>
1842
+
1843
+
1844
+ <p>
1845
+
1846
+ Nodul nu scrie singur in invertor. El calculeaza comanda de limitare pe baza puterii masurate
1847
+
1848
+ in punctul de cuplare la retea, a tintei configurate si a limitelor de siguranta.
1849
+
1850
+ </p>
1851
+
1852
+
1853
+ <h3>Ce face nodul</h3>
1854
+
1855
+ <ul>
1856
+
1857
+ <li>citeste puterea din retea din <code>msg.payload</code> sau din campuri cunoscute ale mesajului</li>
1858
+
1859
+ <li>aplica semnul corect al masurarii, in functie de conventia aleasa</li>
1860
+
1861
+ <li>filtreaza masurarea pentru a evita reactii la varfuri foarte scurte</li>
1862
+
1863
+ <li>calculeaza o corectie PI fata de tinta dorita</li>
1864
+
1865
+ <li>limiteaza viteza de crestere si scadere a comenzii</li>
1866
+
1867
+ <li>taie rezultatul intre <code>Min Limit</code> si <code>Max Limit</code></li>
1868
+
1869
+ <li>poate aplica un plafon dinamic in functie de productia PV disponibila</li>
1870
+
1871
+ <li>poate intra in fallback daca telemetria devine prea veche</li>
1872
+
1873
+ <li>poate emite un semnal separat de fail secure pe a doua iesire</li>
1874
+
1875
+ </ul>
1876
+
1877
+
1878
+ <h3>Input acceptat</h3>
1879
+
1880
+ <p>
1881
+
1882
+ Intrarea principala trebuie sa contina puterea masurata in retea sau in PCC. Valoarea poate fi
1883
+
1884
+ numerica direct in <code>msg.payload</code>:
1885
+
1886
+ </p>
1887
+
1888
+
1889
+ <pre>msg.payload = -250</pre>
1890
+
1891
+
1892
+ <p>
1893
+
1894
+ Sau poate fi intr-un obiect. Nodul cauta automat aceste campuri:
1895
+
1896
+ </p>
1897
+
1898
+
1899
+ <ul>
1900
+
1901
+ <li><code>payload.power_w</code></li>
1902
+
1903
+ <li><code>payload.summary.power.total_w</code></li>
1904
+
1905
+ <li><code>payload.grid_reference_w</code></li>
1906
+
1907
+ <li><code>payload.pcc_power</code></li>
1908
+
1909
+ <li><code>payload.value</code></li>
1910
+
1911
+ <li><code>payload.grid_power</code></li>
1912
+
1913
+ <li><code>payload.power</code></li>
1914
+
1915
+ <li><code>payload.pcc_power_w</code></li>
1916
+
1917
+ </ul>
1918
+
1919
+
1920
+ <pre>{
1921
+
1922
+ "payload": {
1923
+
1924
+ "power_w": -320,
1925
+
1926
+ "summary": {
1927
+
1928
+ "power": {
1929
+
1930
+ "total_w": -320
1931
+
1932
+ }
1933
+
1934
+ }
1935
+
1936
+ }
1937
+
1938
+ }</pre>
1939
+
1940
+
1941
+ <h3>Timestamp</h3>
1942
+
1943
+ <p>
1944
+
1945
+ Pentru verificarea vechimii masurarii, nodul foloseste primul timestamp valid gasit in:
1946
+
1947
+ <code>msg.timestamp</code>, <code>msg.ts</code>, <code>msg.time</code> sau variantele echivalente din
1948
+
1949
+ <code>msg.payload</code>, inclusiv campuri terminate in <code>_ms</code>.
1950
+
1951
+ Daca nu exista timestamp, se foloseste timpul curent.
1952
+
1953
+ </p>
1954
+
1955
+
1956
+ <p>
1957
+
1958
+ Valorile mici sunt interpretate ca secunde si convertite in milisecunde. Valorile mari sunt tratate
1959
+
1960
+ ca timestamp in milisecunde.
1961
+
1962
+ </p>
1963
+
1964
+
1965
+ <h3>Conventia de semn</h3>
1966
+
1967
+ <p>
1968
+
1969
+ Este foarte important ca semnul puterii din retea sa fie corect. In conventia uzuala
1970
+
1971
+ <code>positive_import</code>, importul este pozitiv si exportul este negativ:
1972
+
1973
+ </p>
1974
+
1975
+
1976
+ <ul>
1977
+
1978
+ <li><code>+500 W</code> = se importa 500 W din retea</li>
1979
+
1980
+ <li><code>-500 W</code> = se exporta 500 W in retea</li>
1981
+
1982
+ </ul>
1983
+
1984
+
1985
+ <p>
1986
+
1987
+ Daca meterul tau foloseste conventia inversa, selecteaza conventia potrivita in configuratie.
1988
+
1989
+ O conventie gresita face regulatorul sa corecteze in directia gresita.
1990
+
1991
+ </p>
1992
+
1993
+
1994
+ <h3>Moduri de lucru</h3>
1995
+
1996
+
1997
+ <h4>Zero Feed-In</h4>
1998
+
1999
+ <p>
2000
+
2001
+ Modul <b>Zero Feed-In</b> urmareste o tinta de <code>0 W</code> in punctul de retea. Este modul
2002
+
2003
+ folosit cand nu vrei injectie in retea.
2004
+
2005
+ </p>
2006
+
2007
+
2008
+ <ul>
2009
+
2010
+ <li><b>Relative</b> foloseste puterea totala masurata, de obicei <code>payload.power_w</code> sau <code>payload.summary.power.total_w</code>.</li>
2011
+
2012
+ <li><b>Absolute</b> foloseste faza cu cel mai mare export dintre L1, L2 si L3.</li>
2013
+
2014
+ </ul>
2015
+
2016
+
2017
+ <p>
2018
+
2019
+ Pentru <b>Absolute</b>, mesajul trebuie sa contina valori pe faze. Nodul cauta campuri de forma:
2020
+
2021
+ <code>power_l1_w</code>, <code>l1_power_w</code>, <code>l1_w</code> si echivalentele pentru L2/L3,
2022
+
2023
+ inclusiv in <code>payload.summary.power</code>.
2024
+
2025
+ </p>
2026
+
2027
+
2028
+ <h4>Grid Set Point</h4>
2029
+
2030
+ <p>
2031
+
2032
+ Modul <b>Grid Set Point</b> urmareste valoarea configurata in <b>Grid Set Point (W)</b>.
2033
+
2034
+ Este util cand vrei un mic import permanent sau un export controlat.
2035
+
2036
+ </p>
2037
+
2038
+
2039
+ <ul>
2040
+
2041
+ <li><code>0 W</code> = echilibru in PCC</li>
2042
+
2043
+ <li><code>+100 W</code> = tine aproximativ 100 W import, daca semnul este pozitiv la import</li>
2044
+
2045
+ <li><code>-500 W</code> = permite aproximativ 500 W export, daca semnul este pozitiv la import</li>
2046
+
2047
+ </ul>
2048
+
2049
+
2050
+ <h4>Nominal Power</h4>
2051
+
2052
+ <p>
2053
+
2054
+ Modul <b>Nominal Power</b> emite o limita fixa. Nu foloseste feedback din retea pentru control.
2055
+
2056
+ Valoarea este taiata intre <code>Min Limit</code> si <code>Max Limit</code>.
2057
+
2058
+ </p>
2059
+
2060
+
2061
+ <p>
2062
+
2063
+ Daca este activata optiunea de nominal power din global context, nodul citeste valoarea din variabila
2064
+
2065
+ globala configurata. Altfel foloseste valoarea introdusa in nod.
2066
+
2067
+ </p>
2068
+
2069
+
2070
+ <h3>Teste A3 prin Modbus TCP</h3>
2071
+
2072
+ <p>
2073
+
2074
+ Cand <b>Teste A3</b> este activ, nodul porneste un server Modbus TCP si trece automat in
2075
+
2076
+ <b>Nominal Power</b>. Comenzile primite prin Modbus schimba limita nominala, tipul de rampa si
2077
+
2078
+ valoarea rampei, apoi nodul emite imediat o comanda noua pe iesirea principala.
2079
+
2080
+ </p>
2081
+
2082
+ <h4>Setari recomandate pe dispozitivul testat</h4>
2083
+
2084
+ <ul>
2085
+
2086
+ <li><b>Teste A3</b>: activ</li>
2087
+
2088
+ <li><b>A3 Host</b>: <code>0.0.0.0</code> cand comanda vine de pe alt dispozitiv; <code>127.0.0.1</code> doar pentru test local pe acelasi dispozitiv</li>
2089
+
2090
+ <li><b>A3 Port</b>: <code>1502</code> pentru <code>control-export</code></li>
2091
+
2092
+ <li><b>A3 Unit ID</b>: <code>1</code></li>
2093
+
2094
+ <li><b>Min Limit (W)</b>: de obicei <code>0</code></li>
2095
+
2096
+ <li><b>Max Limit (W)</b>: puterea maxima permisa, de exemplu <code>125000</code>. Daca ramane <code>1000</code>, o comanda de <code>5000 W</code> va fi taiata la <code>1000 W</code>.</li>
2097
+
2098
+ </ul>
2099
+
2100
+ <h4>Harta registrelor A3</h4>
2101
+
2102
+ <ul>
2103
+
2104
+ <li><code>0</code>: versiune profil, read-only, valoare <code>1</code></li>
2105
+
2106
+ <li><code>1</code>: enabled, read-only, valoare <code>1</code></li>
2107
+
2108
+ <li><code>2-3</code>: nominal power in W, <code>int32</code>, big-endian word order</li>
2109
+
2110
+ <li><code>4</code>: ramp type, <code>uint16</code>: <code>0</code> none, <code>1</code> time, <code>2</code> gradient %/s, <code>3</code> gradient W/s</li>
2111
+
2112
+ <li><code>5-6</code>: ramp value, <code>int32</code>, big-endian word order</li>
2113
+
2114
+ <li><code>7</code>: sequence, read-only, creste la fiecare comanda Modbus acceptata</li>
2115
+
2116
+ <li><code>8</code>: timestamp scurt al ultimei comenzi, read-only</li>
2117
+
2118
+ </ul>
2119
+
2120
+ <p>
2121
+
2122
+ Sunt acceptate functiile Modbus <code>03</code> / <code>04</code> pentru citire si <code>06</code> /
2123
+
2124
+ <code>16</code> pentru scriere. Pentru o comanda completa se recomanda <code>FC16</code> pe registrele
2125
+
2126
+ <code>2-6</code>, astfel incat nominalul si rampa se actualizeaza impreuna.
2127
+
2128
+ </p>
2129
+
2130
+ <h4>Exemplu comanda A3</h4>
2131
+
2132
+ <p>
2133
+
2134
+ Pentru a trimite <code>5000 W</code> fara rampa, scrie cu <code>FC16</code> de la registrul <code>2</code>,
2135
+
2136
+ cantitate <code>5</code>, valorile:
2137
+
2138
+ </p>
2139
+
2140
+ <pre>reg 2 = 0
2141
+ reg 3 = 5000
2142
+ reg 4 = 0
2143
+ reg 5 = 0
2144
+ reg 6 = 0</pre>
2145
+
2146
+ <p>
2147
+
2148
+ Pentru <code>8000 W</code> cu rampa de timp <code>10 s</code>:
2149
+
2150
+ </p>
2151
+
2152
+ <pre>reg 2 = 0
2153
+ reg 3 = 8000
2154
+ reg 4 = 1
2155
+ reg 5 = 0
2156
+ reg 6 = 10</pre>
2157
+
2158
+ <p>
2159
+
2160
+ Output-ul asteptat dupa comanda:
2161
+
2162
+ </p>
2163
+
2164
+ <pre>{
2165
+ "payload": {
2166
+ "site_limit_w": 5000,
2167
+ "powerType": "active",
2168
+ "mode": "watts",
2169
+ "a3_tests": true,
2170
+ "ramp": { "type": "none", "value": 0 }
2171
+ }
2172
+ }</pre>
2173
+
2174
+ <p>
2175
+
2176
+ Campurile din editorul nodului sunt configuratie initiala si nu se actualizeaza live cand vine o
2177
+
2178
+ comanda Modbus. Pentru confirmare, citeste registrele <code>0-8</code> sau pune un nod <code>debug</code>
2179
+
2180
+ pe iesirea <code>control-export</code>.
2181
+
2182
+ </p>
2183
+
2184
+
2185
+ <h3>Limite principale</h3>
2186
+
2187
+ <ul>
2188
+
2189
+ <li><b>Min Limit (W)</b> este limita minima pe care nodul o poate emite.</li>
2190
+
2191
+ <li><b>Max Limit (W)</b> este limita maxima pe care nodul o poate emite.</li>
2192
+
2193
+ <li><b>Safe Limit on Stale (W)</b> este limita trimisa cand telemetria este prea veche.</li>
2194
+
2195
+ <li><b>Start Limit</b> alege daca nodul porneste de la limita minima sau de la ultima limita salvata.</li>
2196
+
2197
+ </ul>
2198
+
2199
+
2200
+ <p>
2201
+
2202
+ Recomandare: seteaza <code>Max Limit</code> la puterea maxima pe care instalatia are voie sa o produca,
2203
+
2204
+ nu neaparat la puterea totala instalata, daca ai restrictii de retea.
2205
+
2206
+ </p>
2207
+
2208
+
2209
+ <h3>Tuning</h3>
2210
+
2211
+ <p>
2212
+
2213
+ Parametrii de tuning controleaza cat de repede si cat de agresiv reactioneaza nodul.
2214
+
2215
+ Valorile implicite sunt un punct de pornire stabil pentru zero export.
2216
+
2217
+ </p>
2218
+
2219
+
2220
+ <ul>
2221
+
2222
+ <li><b>Deadband (W)</b> este limita maxima a zonei calme. Banda efectiva este asimetrica: spre export foloseste minimul dintre Deadband si Export Threshold, iar spre import minimul dintre Deadband si Recover Threshold.</li>
2223
+
2224
+ <li><b>Filter Tau (ms)</b> controleaza filtrarea masurarii. Valori mai mari inseamna reactie mai calma.</li>
2225
+
2226
+ <li><b>Kp</b> este castigul proportional. Valori mai mari reactioneaza mai tare la eroare.</li>
2227
+
2228
+ <li><b>Ki</b> este castigul integral. Ajuta la eliminarea erorii ramase in timp.</li>
2229
+
2230
+ <li><b>Control Period (ms)</b> este perioada la care nodul recalculeaza comanda.</li>
2231
+
2232
+ <li><b>Transient Ignore (ms)</b> intarzie numai recuperarea in sus; reducerea la export ramane imediata pentru siguranta.</li>
2233
+
2234
+ <li><b>Max Step Up (W)</b> limiteaza cat de repede poate creste limita.</li>
2235
+
2236
+ <li><b>Max Step Down (W)</b> limiteaza cat de repede poate scadea limita.</li>
2237
+
2238
+ <li><b>Adaptive Gains</b> reduce automat castigurile cand eroarea este mica, pentru stabilitate mai buna.</li>
2239
+
2240
+ </ul>
2241
+
2242
+
2243
+ <h3>Detectie export si revenire</h3>
2244
+
2245
+ <ul>
2246
+
2247
+ <li><b>Export Threshold (W)</b> este pragul peste care nodul considera ca exista export semnificativ.</li>
2248
+
2249
+ <li><b>Recover Threshold (W)</b> este pragul sub care nodul permite revenirea in sus a limitei.</li>
2250
+
2251
+ <li><b>Recovery Delay (ms)</b> este timpul minim fara export semnificativ inainte ca limita sa poata creste.</li>
2252
+
2253
+ </ul>
2254
+
2255
+
2256
+ <p>
2257
+
2258
+ Aceste campuri sunt utile pentru a evita situatia in care limita urca imediat dupa o taiere si produce
2259
+
2260
+ oscilatii repetate.
2261
+
2262
+ </p>
2263
+
2264
+ <h3>Preset Stable Grid Setpoint</h3>
2265
+ <p>
2266
+ Presetul foloseste valorile orientate spre precizie si revenire controlata: Deadband 150 W, Filter Tau
2267
+ 1500 ms, Kp 0.24, Ki 0.008, Control Period 1000 ms, Recovery Delay 2000 ms, Transient Ignore 800 ms,
2268
+ Max Step Up 1000 W, Max Step Down 1800 W si Adaptive Min Scale 0.45. Reducerea ramane mai rapida decat
2269
+ cresterea pentru protectie la export, iar componenta integrala mica elimina eroarea reziduala.
2270
+ </p>
2271
+
2272
+
2273
+ <h3>Dynamic PV Cap</h3>
2274
+
2275
+ <p>
2276
+
2277
+ <b>Dynamic PV Cap</b> limiteaza numai cresterea comenzii la puterea PV disponibila estimata plus un
2278
+ offset. Cu <b>PV Cap Growth Only</b> activ, o scadere a puterii PV masurate nu poate reduce singura
2279
+ limita deja emisa. Reducerile raman in responsabilitatea regulatorului pe baza masurarii din PCC.
2280
+
2281
+ </p>
2282
+
2283
+
2284
+ <pre>crestere permisa <= pv_available_estimate_w + pv_cap_offset_w</pre>
2285
+
2286
+
2287
+ <p>
2288
+
2289
+ Puterea PV poate veni din variabila globala configurata sau din mesaje de telemetrie PV. Estimarea
2290
+ disponibila urca imediat, pastreaza varful pentru <b>PV Availability Hold</b>, apoi coboara cel mult cu
2291
+ <b>PV Estimate Release</b>. Aceasta evita bucla limita - productie masurata - plafon - limita. Pentru mesajele
2292
+
2293
+ de telemetrie PV, nodul cauta <code>payload.power_w</code> si identifica sursa din
2294
+
2295
+ <code>payload.inverter_id</code>, <code>msg.inverter_id</code> sau <code>msg.topic</code>. Sursa trebuie
2296
+
2297
+ sa fie aceeasi cu numele configurat la PV Global Variable.
2298
+
2299
+ </p>
2300
+
2301
+ <h3>PV Feed-forward</h3>
2302
+ <p>
2303
+ Feed-forward estimeaza direct limita necesara din puterea PV masurata si eroarea PCC. Pentru conventia
2304
+ de semn selectata, tinta este aproximativ <code>pv_total_w - gain * error_w</code>. Rezultatul nu ocoleste
2305
+ limitele hard, pasii Max Step Up/Down, protectia la export sau regulatorul PI; doar aduce mai repede
2306
+ comanda in apropierea punctului corect. <b>Feed-forward Gain</b> este limitat la intervalul 0...1.5,
2307
+ iar valoarea recomandata initial este 0.85.
2308
+ </p>
2309
+
2310
+ <h3>Filtrarea masurarii PCC</h3>
2311
+ <p>
2312
+ Inainte de filtrul temporal configurat prin <b>Filter Tau</b>, nodul aplica o mediana pe ultimele trei
2313
+ masurari ale aceleiasi surse. Impulsurile izolate sunt astfel respinse fara a intarzia protectia rapida:
2314
+ reducerea imediata la export continua sa foloseasca cea mai protectiva valoare dintre brut si filtrat.
2315
+ </p>
2316
+
2317
+
2318
+ <p>
2319
+
2320
+ Plafonul PV dinamic se aplica in modurile de control pe retea. In <b>Nominal Power</b>, nodul emite
2321
+
2322
+ limita nominala configurata.
2323
+
2324
+ </p>
2325
+
2326
+
2327
+ <h3>Fail Secure</h3>
2328
+
2329
+ <p>
2330
+
2331
+ Daca <b>Fail Secure</b> este activ, nodul primeste a doua iesire. Aceasta trimite un mesaj doar cand
2332
+
2333
+ limita ramane la <code>Min Limit</code> mai mult decat timpul configurat si eroarea indica o situatie
2334
+
2335
+ care nu se recupereaza.
2336
+
2337
+ </p>
2338
+
2339
+
2340
+ <pre>{
2341
+
2342
+ "topic": "control-export/fail-secure",
2343
+
2344
+ "payload": 1,
2345
+
2346
+ "fail_secure": {
2347
+
2348
+ "active": true,
2349
+
2350
+ "duration_ms": 12000,
2351
+
2352
+ "min_limit_w": 0,
2353
+
2354
+ "site_limit_w": 0,
2355
+
2356
+ "timeout_s": 10
2357
+
2358
+ }
2359
+
2360
+ }</pre>
2361
+
2362
+
2363
+ <p>
2364
+
2365
+ Foloseste acest semnal pentru alarme, opriri suplimentare sau logica externa de siguranta.
2366
+
2367
+ </p>
2368
+
2369
+
2370
+ <h3>Output principal</h3>
2371
+
2372
+ <p>
2373
+
2374
+ Mesajul principal este trimis pe prima iesire, cu topicul configurat in <b>Output Topic</b>.
2375
+
2376
+ Campurile de baza sunt disponibile indiferent de nivelul de debug.
2377
+
2378
+ </p>
2379
+
2380
+
2381
+ <pre>{
2382
+
2383
+ "topic": "site-limit",
2384
+
2385
+ "payload": {
2386
+
2387
+ "site_limit_w": 5400,
2388
+
2389
+ "grid_reference_w": -120,
2390
+
2391
+ "grid_target_w": 0,
2392
+
2393
+ "reason": "tracking",
2394
+
2395
+ "controller_state": "tracking",
2396
+
2397
+ "stale": false,
2398
+
2399
+ "pv_total_w": 6100,
2400
+
2401
+ "pv_cap_limit_w": 6600,
2402
+
2403
+ "pv_cap_offset_w": 500,
2404
+
2405
+ "pv_cap_applied": false,
2406
+
2407
+ "pv_available_estimate_w": 6100,
2408
+
2409
+ "pv_cap_blocked_increase": false,
2410
+
2411
+ "controller_requested_limit_w": 5400,
2412
+
2413
+ "feed_forward_target_w": 6202,
2414
+
2415
+ "timestamp_ms": 1776692400000
2416
+
2417
+ }
2418
+
2419
+ }</pre>
2420
+
2421
+
2422
+ <h3>Campuri importante din output</h3>
2423
+
2424
+ <ul>
2425
+
2426
+ <li><code>site_limit_w</code> este limita totala calculata pentru sistem.</li>
2427
+
2428
+ <li><code>grid_reference_w</code> este puterea masurata in retea, dupa conventia configurata.</li>
2429
+
2430
+ <li><code>grid_target_w</code> este tinta urmarita de regulator.</li>
2431
+
2432
+ <li><code>reason</code> explica de ce a fost emisa limita curenta.</li>
2433
+
2434
+ <li><code>controller_state</code> arata starea interna principala a regulatorului.</li>
2435
+
2436
+ <li><code>stale</code> devine <code>true</code> cand se foloseste fallback din cauza telemetriei vechi.</li>
2437
+
2438
+ <li><code>pv_total_w</code> este puterea PV folosita pentru Dynamic PV Cap, daca este disponibila.</li>
2439
+
2440
+ <li><code>pv_cap_limit_w</code> este plafonul calculat din PV total plus offset.</li>
2441
+
2442
+ <li><code>pv_cap_applied</code> arata daca plafonul PV a taiat limita.</li>
2443
+
2444
+ <li><code>pv_available_estimate_w</code> este estimarea PV stabilizata cu hold si release.</li>
2445
+
2446
+ <li><code>pv_cap_blocked_increase</code> arata ca plafonul a blocat doar cresterea comenzii.</li>
2447
+
2448
+ <li><code>controller_requested_limit_w</code> este limita ceruta inainte de arbitrarea plafonului PV.</li>
2449
+
2450
+ <li><code>feed_forward_target_w</code> este tinta calculata din balanta instantanee de putere.</li>
2451
+
2452
+ <li><code>timestamp_ms</code> este timestamp-ul folosit pentru calcul.</li>
2453
+
2454
+ </ul>
2455
+
2456
+
2457
+ <h3>Debug</h3>
2458
+
2459
+ <p>
2460
+
2461
+ Cu debug activ, outputul include detalii suplimentare precum:
2462
+
2463
+ <code>raw_grid_reference_w</code>, <code>filtered_grid_reference_w</code>, <code>error_w</code>,
2464
+
2465
+ <code>correction_w</code>, <code>control_active</code>, <code>export_detected</code>,
2466
+
2467
+ <code>recovery_blocked</code>, <code>ramp_direction</code>, <code>clamped_by_min</code>,
2468
+
2469
+ <code>clamped_by_max</code> si <code>measurement_age_ms</code>.
2470
+
2471
+ </p>
2472
+
2473
+
2474
+ <p>
2475
+
2476
+ Cu debug verbose apar si campurile interne normalizate, termenii PI, castigurile active si parametrii
2477
+
2478
+ de tuning folositi la calcul. Foloseste verbose doar pentru diagnostic, deoarece mesajul devine mai mare.
2479
+
2480
+ </p>
2481
+
2482
+
2483
+ <h3>Status in editor</h3>
2484
+
2485
+ <ul>
2486
+
2487
+ <li><b>verde</b> inseamna control activ si tracking normal.</li>
2488
+
2489
+ <li><b>albastru</b> inseamna nominal, deadband, hold sau recovery hold.</li>
2490
+
2491
+ <li><b>galben</b> inseamna telemetrie stale si fallback activ.</li>
2492
+
2493
+ <li><b>rosu</b> inseamna eroare de input sau procesare.</li>
2494
+
2495
+ </ul>
2496
+
2497
+
2498
+ <h3>Recomandari practice</h3>
2499
+
2500
+ <ul>
2501
+
2502
+ <li>Verifica intai semnul meterului cu un debug node, inainte sa activezi controlul real.</li>
2503
+
2504
+ <li>Porneste cu <code>Max Limit</code> sub limita reala acceptata de instalatie si creste treptat.</li>
2505
+
2506
+ <li>Daca limita oscileaza, mareste <code>Deadband</code> sau <code>Filter Tau</code> si scade usor <code>Kp</code>.</li>
2507
+
2508
+ <li>Daca raspunsul este prea lent, scade <code>Filter Tau</code> sau mareste usor <code>Kp</code>.</li>
2509
+
2510
+ <li>Daca productia revine prea repede dupa o taiere, mareste <code>Recovery Delay</code>.</li>
2511
+
2512
+ <li>Seteaza intotdeauna <code>Stale Timeout</code> si <code>Safe Limit on Stale</code> pentru o stare sigura.</li>
2513
+
2514
+ </ul>
2515
+
2516
+ </script>
2517
+
2518
+
2519
+ <script type="text/html" data-help-name="control-export-legacy">
2520
+
2521
+
2522
+ <p>
2523
+
2524
+
2525
+ Nodul <b>control-export</b> calculează o limită totală de putere pentru un sistem fotovoltaic pe baza
2526
+
2527
+
2528
+ puterii măsurate în punctul de cuplare cu rețeaua. Rezultatul este emis ca <code>site_limit_w</code>
2529
+
2530
+
2531
+ pentru a controla producția totală a sistemului.
2532
+
2533
+
2534
+ </p>
2535
+
2536
+
2537
+
2538
+ <p>
2539
+
2540
+
2541
+ Nodul este potrivit pentru aplicații de tip:
2542
+
2543
+
2544
+ </p>
2545
+
2546
+
2547
+
2548
+ <pre>meter / grid telemetry → control-export → multi-inverter → inverter write</pre>
2549
+
2550
+
2551
+
2552
+ <h3>Ce face nodul</h3>
2553
+
2554
+
2555
+ <ul>
2556
+
2557
+
2558
+ <li>citește puterea din rețea din <code>msg.payload</code></li>
2559
+
2560
+
2561
+ <li>normalizează semnul măsurării conform convenției configurate</li>
2562
+
2563
+
2564
+ <li>filtrează măsurarea folosită de control</li>
2565
+
2566
+
2567
+ <li>rulează calculul la o perioadă internă fixă</li>
2568
+
2569
+
2570
+ <li>aplică un control PI în jurul unei ținte, cu anti-windup de bază</li>
2571
+
2572
+
2573
+ <li>folosește praguri și întârziere raportate la ținta configurată pentru revenirea în sus</li>
2574
+
2575
+
2576
+ <li>aplică limite minime și maxime</li>
2577
+
2578
+
2579
+ <li>intră în fallback dacă telemetria este veche</li>
2580
+
2581
+
2582
+ </ul>
2583
+
2584
+
2585
+
2586
+ <h3>Input acceptat</h3>
2587
+
2588
+
2589
+ <p>Nodul acceptă mai multe formate uzuale:</p>
2590
+
2591
+
2592
+
2593
+ <pre>msg.payload = 1200</pre>
2594
+
2595
+
2596
+ <p>Interpretare: valoare numerică directă pentru puterea din rețea.</p>
2597
+
2598
+
2599
+
2600
+ <pre>msg.payload = {
2601
+
2602
+
2603
+ "grid_reference_w": 1200
2604
+
2605
+
2606
+ }</pre>
2607
+
2608
+
2609
+
2610
+ <pre>msg.payload = {
2611
+
2612
+
2613
+ "pcc_power": 1200
2614
+
2615
+
2616
+ }</pre>
2617
+
2618
+
2619
+
2620
+ <pre>msg.payload = {
2621
+
2622
+
2623
+ "value": 1200
2624
+
2625
+
2626
+ }</pre>
2627
+
2628
+
2629
+
2630
+ <pre>msg.payload = {
2631
+
2632
+
2633
+ "grid_power": 1200
2634
+
2635
+
2636
+ }</pre>
2637
+
2638
+
2639
+
2640
+ <p>
2641
+
2642
+
2643
+ Pentru timestamp, nodul încearcă să folosească:
2644
+
2645
+
2646
+ <code>msg.timestamp</code>, <code>msg.ts</code>, <code>msg.time</code>,
2647
+
2648
+
2649
+ respectiv variantele echivalente din <code>msg.payload</code>.
2650
+
2651
+
2652
+ Dacă nu există timestamp, se folosește timpul curent.
2653
+
2654
+
2655
+ </p>
2656
+
2657
+
2658
+
2659
+ <h3>Moduri de lucru</h3>
2660
+
2661
+
2662
+
2663
+ <h4>Zero Feed-In</h4>
2664
+
2665
+
2666
+ <p>
2667
+
2668
+
2669
+ Nodul urmărește o țintă de <code>0W</code> în rețea.
2670
+
2671
+
2672
+ Este modul uzual pentru aplicațiile unde nu se dorește export.
2673
+
2674
+ În acest mod poți alege:
2675
+
2676
+ <b>0 relativ</b> (referință din <code>power_w</code>) sau
2677
+
2678
+ <b>0 absolut</b> (referință din faza cu cel mai mare export, dintre L1/L2/L3).
2679
+
2680
+
2681
+ </p>
2682
+
2683
+
2684
+
2685
+ <h4>Grid Set Point</h4>
2686
+
2687
+
2688
+ <p>
2689
+
2690
+
2691
+ Nodul urmărește valoarea configurată în <b>Grid Set Point (W)</b>.
2692
+
2693
+
2694
+ </p>
2695
+
2696
+
2697
+ <ul>
2698
+
2699
+
2700
+ <li><code>-100W</code> = menține un mic import</li>
2701
+
2702
+
2703
+ <li><code>0W</code> = urmărește echilibru în PCC</li>
2704
+
2705
+
2706
+ <li><code>50W</code> = permite un mic export</li>
2707
+
2708
+
2709
+ </ul>
2710
+
2711
+
2712
+
2713
+ <h4>Nominal Power</h4>
2714
+
2715
+
2716
+ <p>
2717
+
2718
+
2719
+ Nodul emite direct o limită fixă dată de <b>Nominal Power (W)</b>, tăiată între Min și Max.
2720
+
2721
+
2722
+ În acest mod nu se mai face control în funcție de rețea.
2723
+
2724
+
2725
+ </p>
2726
+
2727
+
2728
+
2729
+ <h3>Parametri</h3>
2730
+
2731
+
2732
+
2733
+ <h4>Name</h4>
2734
+
2735
+
2736
+ <p>
2737
+
2738
+
2739
+ Numele afișat în editorul Node-RED.
2740
+
2741
+
2742
+ </p>
2743
+
2744
+
2745
+
2746
+ <h4>Mode</h4>
2747
+
2748
+
2749
+ <p>
2750
+
2751
+
2752
+ Selectează modul de funcționare al nodului.
2753
+
2754
+
2755
+ </p>
2756
+
2757
+
2758
+
2759
+ <h4>Grid Set Point (W)</h4>
2760
+
2761
+
2762
+ <p>
2763
+
2764
+
2765
+ Ținta dorită în rețea trebuie introdusă în convenția fixă a nodului.
2766
+
2767
+
2768
+ În această convenție (<b>+ = import, - = export</b>), o valoare negativă înseamnă export dorit.
2769
+
2770
+
2771
+ </p>
2772
+
2773
+
2774
+
2775
+ <h4>Zero Feed-In</h4>
2776
+
2777
+
2778
+ <p>
2779
+
2780
+
2781
+ <code>0 relativ</code> folosește referința <code>power_w</code>.
2782
+
2783
+ <code>0 absolut</code> folosește faza cu cel mai mare export, ca să reducă exportul pe toate fazele.
2784
+
2785
+
2786
+ </p>
2787
+
2788
+
2789
+
2790
+ <h4>Nominal Power (W)</h4>
2791
+
2792
+
2793
+ <p>
2794
+
2795
+
2796
+ Limita fixă totală folosită în modul <b>Nominal Power</b>.
2797
+
2798
+
2799
+ </p>
2800
+
2801
+
2802
+
2803
+ <h4>Tuning Preset</h4>
2804
+
2805
+
2806
+ <p>
2807
+
2808
+
2809
+ <b>Fast</b> reacționează rapid la creșteri/scăderi de consum.
2810
+
2811
+ <b>Balanced</b> ignoră vârfuri scurte (aprox. 1-2s) și apoi corectează.
2812
+
2813
+ <b>Conservative</b> este mai lent și mai filtrat pentru stabilitate maximă.
2814
+
2815
+ În <b>Custom</b> configurezi manual toți parametrii.
2816
+
2817
+
2818
+ </p>
2819
+
2820
+
2821
+
2822
+ <h4>Semn rețea</h4>
2823
+
2824
+
2825
+ <p>
2826
+
2827
+
2828
+ În preset-urile standard (<b>Conservative / Balanced / Fast</b>) convenția este fixă: <b>+ = import, - = export</b>.
2829
+
2830
+
2831
+ În <b>Custom</b> poți alege manual convenția.
2832
+
2833
+
2834
+ </p>
2835
+
2836
+
2837
+
2838
+ <h4>Deadband (W)</h4>
2839
+
2840
+
2841
+ <p>
2842
+
2843
+
2844
+ Zonă de toleranță în jurul țintei. Dacă eroarea este în această zonă, limita rămâne neschimbată.
2845
+
2846
+
2847
+ </p>
2848
+
2849
+
2850
+
2851
+ <h4>Legacy Smoothing Alpha</h4>
2852
+
2853
+
2854
+ <p>
2855
+
2856
+
2857
+ Parametru păstrat pentru compatibilitate.
2858
+
2859
+
2860
+ Filtrarea principală folosită de control este determinată de <b>Filter Tau (ms)</b>.
2861
+
2862
+
2863
+ </p>
2864
+
2865
+
2866
+
2867
+ <h4>Filter Tau (ms)</h4>
2868
+
2869
+
2870
+ <p>
2871
+
2872
+
2873
+ Constanta de timp a filtrului aplicat măsurării din rețea.
2874
+
2875
+
2876
+ Valori mai mari produc o filtrare mai puternică.
2877
+
2878
+
2879
+ </p>
2880
+
2881
+
2882
+
2883
+ <h4>Kp</h4>
2884
+
2885
+
2886
+ <p>
2887
+
2888
+
2889
+ Câștigul proporțional al regulatorului.
2890
+
2891
+
2892
+ El stabilește cât de puternic reacționează limita la eroarea față de țintă.
2893
+
2894
+
2895
+ </p>
2896
+
2897
+
2898
+
2899
+ <h4>Ki</h4>
2900
+
2901
+
2902
+ <p>
2903
+
2904
+
2905
+ Câștigul integral al regulatorului.
2906
+
2907
+
2908
+ Ajută la eliminarea offset-ului static față de țintă, mai ales când sistemul are inerție sau rampă lentă.
2909
+
2910
+
2911
+ </p>
2912
+
2913
+
2914
+
2915
+ <h4>Control Period (ms)</h4>
2916
+
2917
+
2918
+ <p>
2919
+
2920
+
2921
+ Perioada internă la care nodul recalculează comanda.
2922
+
2923
+
2924
+ Mesajele pot veni mai des, dar limita este actualizată la acest interval.
2925
+
2926
+
2927
+ </p>
2928
+
2929
+
2930
+
2931
+ <h4>Transient Ignore (ms)</h4>
2932
+
2933
+
2934
+ <p>
2935
+
2936
+
2937
+ Durata minimă în care deficitul de export trebuie să rămână stabil înainte ca limita să crească.
2938
+
2939
+ Reducerea limitei la export nu este întârziată de acest parametru, pentru a păstra protecția rapidă.
2940
+
2941
+
2942
+ </p>
2943
+
2944
+
2945
+ <h4>Export Threshold (W)</h4>
2946
+
2947
+
2948
+ <p>
2949
+
2950
+
2951
+ Prag peste care nodul consideră că există export semnificativ.
2952
+
2953
+
2954
+ </p>
2955
+
2956
+
2957
+
2958
+ <h4>Recover Threshold (W)</h4>
2959
+
2960
+
2961
+ <p>
2962
+
2963
+
2964
+ Prag sub care nodul permite revenirea în sus a limitei.
2965
+
2966
+
2967
+ </p>
2968
+
2969
+
2970
+
2971
+ <h4>Recovery Delay (ms)</h4>
2972
+
2973
+
2974
+ <p>
2975
+
2976
+
2977
+ Timp minim fără export semnificativ înainte ca limita să poată crește.
2978
+
2979
+
2980
+ </p>
2981
+
2982
+
2983
+
2984
+ <h4>Legacy Max Step (W/ciclu)</h4>
2985
+
2986
+
2987
+ <p>
2988
+
2989
+
2990
+ Parametru păstrat pentru compatibilitate. Nu este folosit de logica principală.
2991
+
2992
+
2993
+ </p>
2994
+
2995
+
2996
+
2997
+ <h4>Stale Timeout (ms)</h4>
2998
+
2999
+
3000
+ <p>
3001
+
3002
+
3003
+ Timpul maxim acceptat pentru prospețimea telemetriei.
3004
+
3005
+
3006
+ Dacă timestamp-ul este mai vechi decât această valoare, datele sunt considerate stale.
3007
+
3008
+
3009
+ </p>
3010
+
3011
+
3012
+
3013
+ <h4>Safe Limit on Stale (W)</h4>
3014
+
3015
+
3016
+ <p>
3017
+
3018
+
3019
+ Limita totală emisă când telemetria este stale.
3020
+
3021
+
3022
+ </p>
3023
+
3024
+
3025
+
3026
+ <h4>Min Limit (W)</h4>
3027
+
3028
+
3029
+ <p>
3030
+
3031
+
3032
+ Limita minimă permisă pentru comanda totală.
3033
+
3034
+
3035
+ </p>
3036
+
3037
+
3038
+
3039
+ <h4>Max Limit (W)</h4>
3040
+
3041
+
3042
+ <p>
3043
+
3044
+
3045
+ Limita maximă permisă pentru comanda totală.
3046
+
3047
+
3048
+ </p>
3049
+
3050
+
3051
+
3052
+ <h4>Output Topic</h4>
3053
+
3054
+
3055
+ <p>
3056
+
3057
+
3058
+ Topic-ul pus în mesajul de ieșire.
3059
+
3060
+
3061
+ </p>
3062
+
3063
+
3064
+
3065
+ <h4>Start Limit</h4>
3066
+
3067
+
3068
+ <p>
3069
+
3070
+
3071
+ Alege limita folosită la pornire/deploy:
3072
+
3073
+ <code>Start from Min Limit</code> sau <code>Start from Last Limit</code>.
3074
+
3075
+
3076
+ </p>
3077
+
3078
+
3079
+
3080
+ <h4>Dynamic PV Cap (global)</h4>
3081
+
3082
+
3083
+ <p>
3084
+
3085
+
3086
+ Dacă este activ, nodul citește puterea PV totală din variabila globală setată în <b>PV Global Variable</b>
3087
+
3088
+ și plafonează limita la <code>PV + offset</code>.
3089
+
3090
+ Offset-ul pornește din <b>PV Cap Offset (W)</b> când eroarea față de setpoint este mare,
3091
+
3092
+ iar aproape de setpoint se reduce automat pentru reglaj mai fin și mai puține oscilații.
3093
+
3094
+ Acest plafon se aplică doar în modurile <b>Zero Feed-In</b> și <b>Grid Set Point</b>, nu în <b>Nominal Power</b>.
3095
+
3096
+
3097
+ </p>
3098
+
3099
+
3100
+
3101
+ <h3>Output</h3>
3102
+
3103
+
3104
+ <p>Nodul emite mesajul principal cu topicul configurat. Dacă activezi <code>Fail Secure</code>, apare și a doua ieșire: aceasta trimite <code>payload = 1</code> când limita trimisă rămâne la <code>Min Limit</code> mai mult decât timpul configurat.</p>
3105
+
3106
+
3107
+
3108
+ <pre>{
3109
+
3110
+
3111
+ "topic": "site-limit",
3112
+
3113
+
3114
+ "payload": {
3115
+
3116
+
3117
+ "site_limit_w": 5400,
3118
+
3119
+
3120
+ "grid_reference_w": 620,
3121
+
3122
+
3123
+ "filtered_grid_reference_w": 510,
3124
+
3125
+
3126
+ "grid_target_w": -100,
3127
+
3128
+
3129
+ "correction_w": 240,
3130
+
3131
+
3132
+ "error_w": 610,
3133
+
3134
+
3135
+ "control_active": true,
3136
+
3137
+
3138
+ "reason": "tracking",
3139
+
3140
+
3141
+ "stale": false,
3142
+
3143
+
3144
+ "export_detected": true,
3145
+
3146
+
3147
+ "ramp_direction": "down",
3148
+
3149
+
3150
+ "kp": 0.2,
3151
+
3152
+
3153
+ "control_period_ms": 1000,
3154
+
3155
+
3156
+ "filter_tau_ms": 2500,
3157
+
3158
+
3159
+ "measurement_age_ms": 82,
3160
+
3161
+
3162
+ "timestamp_ms": 1774692000000
3163
+
3164
+
3165
+ }
3166
+
3167
+
3168
+ }</pre>
3169
+
3170
+
3171
+
3172
+ <h3>Semnificația câmpurilor din output</h3>
3173
+
3174
+
3175
+ <ul>
3176
+
3177
+
3178
+ <li><code>site_limit_w</code> — limita totală calculată pentru site</li>
3179
+
3180
+
3181
+ <li><code>raw_grid_reference_w</code> — valoarea brută primită direct de la meter</li>
3182
+
3183
+
3184
+ <li><code>grid_reference_w</code> — măsurarea în convenția configurată în nod</li>
3185
+
3186
+
3187
+ <li><code>normalized_grid_reference_w</code> — măsurarea internă folosită de regulator</li>
3188
+
3189
+
3190
+ <li><code>filtered_grid_reference_w</code> — măsurarea filtrată în convenția configurată</li>
3191
+
3192
+
3193
+ <li><code>normalized_filtered_grid_reference_w</code> — măsurarea filtrată folosită intern de control</li>
3194
+
3195
+
3196
+ <li><code>grid_target_w</code> — ținta urmărită în convenția configurată</li>
3197
+
3198
+
3199
+ <li><code>normalized_grid_target_w</code> — ținta internă după normalizarea semnului</li>
3200
+
3201
+
3202
+ <li><code>correction_w</code> — corecția aplicată față de limita precedentă</li>
3203
+
3204
+
3205
+ <li><code>error_w</code> — eroarea față de țintă</li>
3206
+
3207
+
3208
+ <li><code>p_term_w</code> — contribuția proporțională a regulatorului</li>
3209
+
3210
+
3211
+ <li><code>i_term_w</code> — contribuția integratorului</li>
3212
+
3213
+
3214
+ <li><code>control_active</code> — indică dacă nodul a modificat activ limita</li>
3215
+
3216
+
3217
+ <li><code>reason</code> — motivul ieșirii: <code>tracking</code>, <code>deadband</code>, <code>hold_period</code>, <code>recovery_hold</code>, <code>stale</code>, <code>nominal</code></li>
3218
+
3219
+
3220
+ <li><code>controller_state</code> — stare explicită a regulatorului, utilă pentru diagnoză</li>
3221
+
3222
+
3223
+ <li><code>stale</code> — indică fallback din cauza telemetriei vechi</li>
3224
+
3225
+
3226
+ <li><code>export_detected</code> — indică depășirea pragului de export</li>
3227
+
3228
+
3229
+ <li><code>recovery_blocked</code> — indică blocarea temporară a creșterii limitei</li>
3230
+
3231
+
3232
+ <li><code>ramp_direction</code> — sensul ultimei modificări: <code>up</code>, <code>down</code>, <code>hold</code></li>
3233
+
3234
+
3235
+ <li><code>clamped_by_min</code> / <code>clamped_by_max</code> — arată dacă limita a fost tăiată de limitele configurate</li>
3236
+
3237
+
3238
+ <li><code>kp</code> — câștigul activ al regulatorului</li>
3239
+
3240
+
3241
+ <li><code>ki</code> — câștigul integral activ</li>
3242
+
3243
+
3244
+ <li><code>control_period_ms</code> — perioada internă a controlului</li>
3245
+
3246
+
3247
+ <li><code>filter_tau_ms</code> — constanta de timp a filtrului</li>
3248
+
3249
+
3250
+ <li><code>measurement_age_ms</code> — vechimea ultimei măsurători disponibile</li>
3251
+
3252
+
3253
+ <li><code>timestamp_ms</code> — timestamp-ul folosit pentru evaluare</li>
3254
+
3255
+
3256
+ </ul>
3257
+
3258
+
3259
+
3260
+ <h3>Status în editor</h3>
3261
+
3262
+
3263
+ <ul>
3264
+
3265
+
3266
+ <li><b>verde</b> — control activ și urmărire normală</li>
3267
+
3268
+
3269
+ <li><b>albastru</b> — menținere în deadband, hold period sau recovery hold</li>
3270
+
3271
+
3272
+ <li><b>galben</b> — telemetrie stale, fallback activ</li>
3273
+
3274
+
3275
+ <li><b>roșu</b> — eroare de input sau procesare</li>
3276
+
3277
+
3278
+ </ul>
3279
+
3280
+
3281
+
3282
+ <h3>Exemple de utilizare</h3>
3283
+
3284
+
3285
+
3286
+ <h4>1. Zero-export conservator</h4>
3287
+
3288
+
3289
+ <ul>
3290
+
3291
+
3292
+ <li>Mode = <code>Zero Feed-In</code></li>
3293
+
3294
+
3295
+ <li>Deadband = <code>180</code></li>
3296
+
3297
+
3298
+ <li>Filter Tau = <code>2800</code></li>
3299
+
3300
+
3301
+ <li>Kp = <code>0.22</code></li>
3302
+
3303
+
3304
+ <li>Ki = <code>0.01</code></li>
3305
+
3306
+
3307
+ <li>Control Period = <code>800</code></li>
3308
+
3309
+
3310
+ <li>Export Threshold = <code>60</code></li>
3311
+
3312
+
3313
+ <li>Recover Threshold = <code>25</code></li>
3314
+
3315
+
3316
+ <li>Recovery Delay = <code>3000</code></li>
3317
+
3318
+
3319
+ <li>Safe Limit on Stale = <code>0</code></li>
3320
+
3321
+
3322
+ </ul>
3323
+
3324
+
3325
+
3326
+ <h4>2. Grid setpoint cu mic import</h4>
3327
+
3328
+
3329
+ <ul>
3330
+
3331
+
3332
+ <li>Mode = <code>Grid Set Point</code></li>
3333
+
3334
+
3335
+ <li>Grid Set Point = <code>+100</code> pentru convenția implicită <code>+ = import</code></li>
3336
+
3337
+
3338
+ <li>Deadband = <code>80..150</code></li>
3339
+
3340
+
3341
+ <li>Filter Tau = <code>1200..2000</code></li>
3342
+
3343
+
3344
+ <li>Kp = <code>0.25..0.35</code></li>
3345
+
3346
+
3347
+ <li>Ki = <code>0.01..0.03</code></li>
3348
+
3349
+
3350
+ </ul>
3351
+
3352
+
3353
+
3354
+ <h4>3. Grid setpoint cu export controlat</h4>
3355
+
3356
+
3357
+ <ul>
3358
+
3359
+
3360
+ <li>Mode = <code>Grid Set Point</code></li>
3361
+
3362
+
3363
+ <li>Grid Set Point = <code>+500</code> sau <code>-500</code> în funcție de convenția de semn aleasă</li>
3364
+
3365
+
3366
+ <li>Deadband = <code>80..120</code></li>
3367
+
3368
+
3369
+ <li>Filter Tau = <code>1000..1500</code></li>
3370
+
3371
+
3372
+ <li>Kp = <code>0.30..0.45</code></li>
3373
+
3374
+
3375
+ <li>Ki = <code>0.02..0.05</code></li>
3376
+
3377
+
3378
+ <li>Export Threshold = <code>40..80</code></li>
3379
+
3380
+
3381
+ <li>Recover Threshold = <code>15..30</code></li>
3382
+
3383
+
3384
+ </ul>
3385
+
3386
+
3387
+
3388
+ <h4>4. Zero-export foarte stabil pentru contoare zgomotoase</h4>
3389
+
3390
+
3391
+ <ul>
3392
+
3393
+
3394
+ <li>Mode = <code>Zero Feed-In</code></li>
3395
+
3396
+
3397
+ <li>Deadband = <code>150..250</code></li>
3398
+
3399
+
3400
+ <li>Filter Tau = <code>2500..4000</code></li>
3401
+
3402
+
3403
+ <li>Kp = <code>0.15..0.25</code></li>
3404
+
3405
+
3406
+ <li>Ki = <code>0.00..0.02</code></li>
3407
+
3408
+
3409
+ <li>Control Period = <code>700..1200</code></li>
3410
+
3411
+
3412
+ <li>Recovery Delay = <code>2000..5000</code></li>
3413
+
3414
+
3415
+ </ul>
3416
+
3417
+
3418
+
3419
+ <h4>5. Zero-export mai rapid pentru sisteme reactive</h4>
3420
+
3421
+
3422
+ <ul>
3423
+
3424
+
3425
+ <li>Mode = <code>Zero Feed-In</code></li>
3426
+
3427
+
3428
+ <li>Deadband = <code>50..100</code></li>
3429
+
3430
+
3431
+ <li>Filter Tau = <code>800..1500</code></li>
3432
+
3433
+
3434
+ <li>Kp = <code>0.35..0.55</code></li>
3435
+
3436
+
3437
+ <li>Ki = <code>0.02..0.06</code></li>
3438
+
3439
+
3440
+ <li>Control Period = <code>300..700</code></li>
3441
+
3442
+
3443
+ <li>Export Threshold = <code>30..60</code></li>
3444
+
3445
+
3446
+ <li>Recover Threshold = <code>10..20</code></li>
3447
+
3448
+
3449
+ </ul>
3450
+
3451
+
3452
+
3453
+ <h4>6. Sistem cu baterie sau consum foarte variabil</h4>
3454
+
3455
+
3456
+ <ul>
3457
+
3458
+
3459
+ <li>Mode = <code>Grid Set Point</code></li>
3460
+
3461
+
3462
+ <li>Grid Set Point = <code>+100</code> la convenția <code>+ = import</code>, sau o valoare mică de bias utilă instalației</li>
3463
+
3464
+
3465
+ <li>Deadband = <code>100..180</code></li>
3466
+
3467
+
3468
+ <li>Filter Tau = <code>1500..3000</code></li>
3469
+
3470
+
3471
+ <li>Kp = <code>0.20..0.35</code></li>
3472
+
3473
+
3474
+ <li>Ki = <code>0.01..0.03</code></li>
3475
+
3476
+
3477
+ <li>Recovery Delay = <code>1500..3000</code></li>
3478
+
3479
+
3480
+ </ul>
3481
+
3482
+
3483
+
3484
+ <h4>7. Limită fixă pentru test</h4>
3485
+
3486
+
3487
+ <ul>
3488
+
3489
+
3490
+ <li>Mode = <code>Nominal Power</code></li>
3491
+
3492
+
3493
+ <li>Nominal Power = <code>8000</code></li>
3494
+
3495
+
3496
+ </ul>
3497
+
3498
+
3499
+
3500
+ <h4>8. Punct de pornire recomandat</h4>
3501
+
3502
+
3503
+ <ul>
3504
+
3505
+
3506
+ <li>Folosește valorile default actuale dacă nu ai încă măsurători de tuning</li>
3507
+
3508
+
3509
+ <li>Dacă limita oscilează prea mult: mărește <code>Deadband</code> și <code>Filter Tau</code>, sau scade <code>Kp</code> / <code>Ki</code></li>
3510
+
3511
+
3512
+ <li>Dacă nodul reacționează prea lent: scade <code>Filter Tau</code>, mărește ușor <code>Kp</code> și redu <code>Control Period</code></li>
3513
+
3514
+
3515
+ <li>Dacă urcă prea greu după o limitare: redu <code>Recovery Delay</code> sau ajustează <code>Kp</code></li>
3516
+
3517
+
3518
+ <li>Dacă taie prea agresiv producția: redu <code>Kp</code> și/sau crește <code>Deadband</code></li>
3519
+
3520
+
3521
+ </ul>
3522
+
3523
+
3524
+
3525
+ <h3>Recomandări practice</h3>
3526
+
3527
+
3528
+ <ul>
3529
+
3530
+
3531
+ <li>pentru telemetrie rapidă, folosește o perioadă internă de control mai mare decât perioada mesajelor</li>
3532
+
3533
+
3534
+ <li>pentru un comportament mai calm, mărește <code>Deadband</code> și <code>Filter Tau</code> sau scade <code>Kp</code></li>
3535
+
3536
+
3537
+ <li>pentru revenire mai lentă, mărește <code>Recovery Delay</code> sau redu <code>Kp</code></li>
3538
+
3539
+
3540
+ <li>setează întotdeauna <code>Stale Timeout</code> și <code>Safe Limit on Stale</code></li>
3541
+
3542
+
3543
+ </ul>
3544
+
3545
+
3546
+ <h3>Referinta completa parametri</h3>
3547
+
3548
+ <ul>
3549
+
3550
+ <li><code>mode</code>: selecteaza Zero Feed-In, Grid Set Point sau Nominal Power.</li>
3551
+
3552
+ <li><code>zeroFeedInMode</code>: <code>relative</code> foloseste suma fazelor, <code>absolute</code> protejeaza fiecare faza.</li>
3553
+
3554
+ <li><code>requireAllPhases</code>: cere L1/L2/L3 valide pentru modurile care folosesc faze.</li>
3555
+
3556
+ <li><code>tuningPreset</code>: aplica seturi de reglaj; <code>stable_grid</code> este recomandat cand telemetria vine foarte des si vrei mai putine oscilatii.</li>
3557
+
3558
+ <li><code>gridSetPoint</code>: tinta de retea in W pentru Grid Set Point.</li>
3559
+
3560
+ <li><code>nominalPower</code>: limita fixa in W pentru Nominal Power.</li>
3561
+
3562
+ <li><code>nominalPowerFromGlobal</code> si <code>nominalPowerGlobalVar</code>: citesc limita nominala din context global.</li>
3563
+ <li><code>maxLimitFromGlobal</code> si <code>maxLimitGlobalVar</code>: citesc limita maxima din context global.</li>
3564
+
3565
+ <li><code>a3TestsEnabled</code>: porneste serverul Modbus TCP pentru teste A3 si forteaza modul Nominal Power.</li>
3566
+
3567
+ <li><code>a3ModbusHost</code>: interfata pe care asculta serverul A3; foloseste <code>0.0.0.0</code> pentru acces din retea.</li>
3568
+
3569
+ <li><code>a3ModbusPort</code>: portul serverului A3, implicit <code>1502</code>.</li>
3570
+
3571
+ <li><code>a3ModbusUnitId</code>: Unit ID Modbus acceptat, implicit <code>1</code>.</li>
3572
+
3573
+ <li><code>a3RampType</code> si <code>a3RampValue</code>: valorile initiale de rampa pana la prima comanda Modbus.</li>
3574
+
3575
+ <li><code>signConvention</code>: adapteaza semnul import/export la sensul meterului.</li>
3576
+
3577
+ <li><code>deadband</code>: zona moarta in W.</li>
3578
+
3579
+ <li><code>minLimit</code> si <code>maxLimit</code>: limitele hard minime si maxime ale comenzii emise.</li>
3580
+
3581
+ <li><code>outputTopic</code>: topic-ul mesajului catre <code>fronius-write</code> sau <code>multi-inverter</code>.</li>
3582
+
3583
+ <li><code>smoothingAlpha</code>: filtrare simpla a comenzii vechi; valori mai mici inseamna raspuns mai lent.</li>
3584
+
3585
+ <li><code>filterTauMs</code>: filtrarea masurarii de retea.</li>
3586
+
3587
+ <li><code>kp</code> si <code>ki</code>: castigurile proportional si integral ale regulatorului.</li>
3588
+
3589
+ <li><code>controlPeriodMs</code>: perioada minima intre calcule de control.</li>
3590
+
3591
+ <li><code>exportThresholdW</code>: pragul peste care exportul este considerat critic si poate produce reducere rapida.</li>
3592
+
3593
+ <li><code>recoverThresholdW</code> si <code>recoveryDelayMs</code>: controleaza cand si cat de repede se revine dupa limitare.</li>
3594
+
3595
+ <li><code>transientIgnoreMs</code>: ignora abateri scurte pentru a reduce reactiile la zgomot.</li>
3596
+
3597
+ <li><code>maxStepUpW</code> si <code>maxStepDownW</code>: limiteaza cresterea/scaderea comenzii pe ciclu.</li>
3598
+
3599
+ <li><code>adaptiveGains</code>, <code>adaptiveErrorBandW</code>, <code>adaptiveMinScale</code>: reduc castigurile cand eroarea este mica.</li>
3600
+
3601
+ <li><code>maxStepW</code>: compatibilitate legacy pentru pas maxim simetric.</li>
3602
+
3603
+ <li><code>staleTimeoutMs</code>: vechimea maxima acceptata pentru telemetrie.</li>
3604
+
3605
+ <li><code>maxFutureTimestampMs</code>: toleranta pentru timestamp-uri venite din viitor.</li>
3606
+
3607
+ <li><code>fastSetpointMaxTelemetryAgeMs</code>: permite aplicarea unui setpoint nou cu ultima telemetrie cache, doar daca aceasta este mai noua decat pragul.</li>
3608
+
3609
+ <li><code>outputHeartbeatMs</code>: retransmite periodic ultima limita neschimbata; intre heartbeat-uri comenzile identice sunt suprimate.</li>
3610
+
3611
+ <li><code>safeLimitOnStale</code>: limita folosita cand telemetria este stale.</li>
3612
+
3613
+ <li><code>debugLevel</code>: <code>off</code>, <code>normal</code> sau <code>verbose</code>.</li>
3614
+
3615
+ <li><code>startLimitMode</code>: alege limita initiala dupa deploy/restart.</li>
3616
+
3617
+ <li><code>useGlobalPvCap</code>, <code>globalPvTotalVar</code>, <code>globalPvCapOffsetW</code>: plafoneaza cresterea limitei dupa productia PV disponibila publicata global.</li>
3618
+
3619
+ <li><code>pvCapOnlyBlocksIncrease</code>: impiedica plafonul PV sa produca singur scaderi ale limitei.</li>
3620
+
3621
+ <li><code>pvCapHoldMs</code> si <code>pvCapReleaseWPerSec</code>: stabilizeaza estimarea puterii PV disponibile.</li>
3622
+
3623
+ <li><code>feedForwardEnabled</code> si <code>feedForwardGain</code>: activeaza anticiparea bazata pe balanta PV/PCC, limitata in continuare de rampe si PI.</li>
3624
+
3625
+ <li><code>failSecureEnabled</code> si <code>failSecureTimeSec</code>: activeaza iesirea de fail secure daca nodul ramane la limita minima si exportul rezidual continua prea mult timp.</li>
3626
+
3627
+ <li><code>loadNormalCustom</code>: valoare interna legacy pentru compatibilitate cu configuratii mai vechi.</li>
3628
+
3629
+ </ul>
3630
+
3631
+
3632
+ <h3>Exemplu setpoint rapid fara oscilatii excesive</h3>
3633
+
3634
+ <pre>{
3635
+ "topic": "control-export/setpoint",
3636
+ "payload": {
3637
+ "grid_target_w": -8000
3638
+ }
3639
+ }</pre>
3640
+
3641
+ <p>Cu <code>fastSetpointMaxTelemetryAgeMs = 250</code>, nodul poate recalcula imediat daca ultima masurare de retea are sub 250 ms. Pentru stabilitate la telemetrie de 200 ms, foloseste presetul <code>stable_grid</code> si pastreaza <code>controlPeriodMs</code> mai mare decat perioada meterului.</p>
3642
+
3643
+
3644
+ </script>