From 89fb4f1924cd2f6e219c42a0ed0993d1a33fd9d7 Mon Sep 17 00:00:00 2001 From: Dusan Vojacek Date: Wed, 29 Apr 2026 13:09:43 +0200 Subject: [PATCH] fix idempotency gne port uctoff --- backend/services/control/exporter_monolith.py | 10 ++++++++-- .../test_drop_registers_matching_last_verified.py | 14 +++++++++++--- docs/04-modules/modbus-registers.md | 4 +++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/backend/services/control/exporter_monolith.py b/backend/services/control/exporter_monolith.py index cda47b2..e006deb 100644 --- a/backend/services/control/exporter_monolith.py +++ b/backend/services/control/exporter_monolith.py @@ -340,8 +340,14 @@ def _drop_registers_matching_last_verified( continue # reg179: porovnáváme jen bits0–1 maskou 0x0003 (masked RMW zachovává ostatní bity). if int(reg) == 179 and _deye_reg179_verify_match(int(val), int(lv)): - skipped.append(int(reg)) - continue + # GEN cutoff (BA81): chceme na zařízení dostat "clean" hodnotu 2/3. + # Pokud minulý verified stav obsahuje jiné bity (např. 0xFFFE/0xFFFF), + # maska sice sedí, ale firmware/UI nemusí cutoff aplikovat správně. + # Proto reg179 skipneme jen tehdy, když je poslední verified hodnota už + # skutečně 2 nebo 3 (tj. clean value), ne jen maskově ekvivalentní. + if int(lv) in (REG179_MI_EXPORT_DISABLE, REG179_MI_EXPORT_ENABLE): + skipped.append(int(reg)) + continue if int(lv) == int(val): skipped.append(int(reg)) continue diff --git a/backend/tests/test_drop_registers_matching_last_verified.py b/backend/tests/test_drop_registers_matching_last_verified.py index 304139c..1e868fd 100644 --- a/backend/tests/test_drop_registers_matching_last_verified.py +++ b/backend/tests/test_drop_registers_matching_last_verified.py @@ -21,9 +21,17 @@ def test_drop_registers_keeps_reg178_when_mask_differs(): assert skipped == [] -def test_drop_registers_skips_reg179_when_mask_matches(): - registers = [(179, "control_board_special_1", 2)] # bits0–1 = 2 (cutoff ON) - last_verified = {179: 0x1236} # ...0110b => bits0–1 still == 2 +def test_drop_registers_keeps_reg179_when_mask_matches_but_not_clean(): + registers = [(179, "control_board_special_1", 2)] # want cutoff ON (clean value) + last_verified = {179: 0x1236} # bits0–1 still == 2, but not a clean 2/3 value + out, skipped = _drop_registers_matching_last_verified(registers, last_verified) + assert out == registers + assert skipped == [] + + +def test_drop_registers_skips_reg179_when_clean_value_matches(): + registers = [(179, "control_board_special_1", 2)] # want cutoff ON (clean value) + last_verified = {179: 2} # already clean cutoff ON out, skipped = _drop_registers_matching_last_verified(registers, last_verified) assert out == [] assert skipped == [179] diff --git a/docs/04-modules/modbus-registers.md b/docs/04-modules/modbus-registers.md index 02a7db2..ff54c22 100644 --- a/docs/04-modules/modbus-registers.md +++ b/docs/04-modules/modbus-registers.md @@ -72,7 +72,9 @@ protože některé firmware/UI varianty nevyhodnocují jen bity 0–1 maskou, al Ověření v journalu (`verify_modbus_commands`) přesto porovnává jen bits0–1 maskou `0x0003` (odolnost vůči paralelním změnám jiných bitů / verzím FW). -**Idempotence:** pokud poslední `verified` hodnota už má správně nastavené bits0–1 (maska `0x0003`), EMS zápis reg. 179 v dalším běhu přeskočí (ostatní bity se ignorují). +**Idempotence:** EMS zápis reg. 179 přeskočí jen tehdy, když poslední `verified` hodnota je už **clean 2/3**. +Masková shoda s hodnotami typu `0xfffe` / `0xffff` se záměrně **nepovažuje** za “už zapsáno”, aby se zařízení +dostalo do stabilního stavu, který odpovídá UI i chování firmware. **Pozn.:** Flag se v solveru vůbec nevytváří ani neukládá tam, kde není povolen feature `asset_inverter.deye_gen_microinverter_cutoff_enabled` – takové lokality ho nemají ani v UI. ### Provozní režim EMS SELF_SUSTAIN