idempotence zapisu 178 a 179 grid peak shaveing a grid cuttoff
This commit is contained in:
@@ -332,7 +332,16 @@ def _drop_registers_matching_last_verified(
|
||||
skipped: list[int] = []
|
||||
for reg, meta, val in registers:
|
||||
lv = last_verified.get(int(reg))
|
||||
if lv is not None and lv == int(val):
|
||||
if lv is not None:
|
||||
# reg178: porovnáváme jen masku bitů 4–5 (Deye si v dalších bitech drží vlastní stav).
|
||||
if int(reg) == 178 and _deye_reg178_verify_match(int(val), int(lv)):
|
||||
skipped.append(int(reg))
|
||||
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
|
||||
if int(lv) == int(val):
|
||||
skipped.append(int(reg))
|
||||
continue
|
||||
out.append((reg, meta, val))
|
||||
|
||||
38
backend/tests/test_drop_registers_matching_last_verified.py
Normal file
38
backend/tests/test_drop_registers_matching_last_verified.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from services.control.exporter_monolith import (
|
||||
REG178_PASSIVE,
|
||||
_drop_registers_matching_last_verified,
|
||||
)
|
||||
|
||||
|
||||
def test_drop_registers_skips_reg178_when_mask_matches():
|
||||
# last_verified contains extra bits beyond 0x0030; we still want to skip if bits 4–5 match.
|
||||
registers = [(178, "grid_peak_shaving_switch", REG178_PASSIVE)]
|
||||
last_verified = {178: 12030} # real-world example from home-01 (bits4-5 still == 0b11)
|
||||
out, skipped = _drop_registers_matching_last_verified(registers, last_verified)
|
||||
assert out == []
|
||||
assert skipped == [178]
|
||||
|
||||
|
||||
def test_drop_registers_keeps_reg178_when_mask_differs():
|
||||
registers = [(178, "grid_peak_shaving_switch", REG178_PASSIVE)]
|
||||
last_verified = {178: 32} # SELL mask 0b10
|
||||
out, skipped = _drop_registers_matching_last_verified(registers, last_verified)
|
||||
assert out == registers
|
||||
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
|
||||
out, skipped = _drop_registers_matching_last_verified(registers, last_verified)
|
||||
assert out == []
|
||||
assert skipped == [179]
|
||||
|
||||
|
||||
def test_drop_registers_keeps_reg179_when_mask_differs():
|
||||
registers = [(179, "control_board_special_1", 2)] # want cutoff ON
|
||||
last_verified = {179: 0x1237} # ...0111b => bits0–1 == 3 (cutoff OFF)
|
||||
out, skipped = _drop_registers_matching_last_verified(registers, last_verified)
|
||||
assert out == registers
|
||||
assert skipped == []
|
||||
|
||||
@@ -32,7 +32,7 @@ EMS zapisuje řídící hodnoty přes journal (`modbus_command`) a **`write_regi
|
||||
- **EMS NEZAPISUJE** – nastavit **manuálně v SolarmanApp**.
|
||||
- Hodnota určuje výkon peak shavingu v **W** (typicky 0–16 000).
|
||||
|
||||
### Reg 178 – hodnoty podle fyzického režimu
|
||||
### Reg 178 – hodnoty podle fyzického režimu + idempotence
|
||||
|
||||
- **SELL:** **32** – bit4–5 = **10**, grid peak shaving **disable** (export do sítě).
|
||||
- **PASSIVE** a **CHARGE:** **48** – bit4–5 = **11**, grid peak shaving **enable**.
|
||||
@@ -41,6 +41,8 @@ EMS **nezapisuje** read-modify-write (paralelní čtení jinými klienty může
|
||||
|
||||
**Ověření v journalu (`modbus_command`):** u zápisu **178** se při verify porovnávají jen **bity 4–5** maskou **`0x0030`** s očekávanou hodnotou (32/48); `value_verified` zůstává plný readback. Při nesouladu masky následuje **druhé FC3 čtení** reg. 178 (mitigace RS485 glitchů). U **TOU výkonu W (154–159)** verify akceptuje i readback **`max_charge_a × 51.2`** nebo **`max_discharge_a × 51.2`**, pokud firmware hodnotu přepíše na interní maximum (skutečný výkon je stejně omezen reg. 108/109). Detail: `modbus-command-journal.md`.
|
||||
|
||||
**Idempotence (proti spamu zápisů):** pokud poslední `verified` hodnota už má správně nastavené bity 4–5 (maska `0x0030`), EMS zápis reg. 178 v dalším běhu přeskočí (i když `value_verified` obsahuje jiné bity).
|
||||
|
||||
## Klíčové registry podle fyzického režimu Deye
|
||||
|
||||
Provozní režimy EMS (AUTO, SELF_SUSTAIN, SELL, …) se mapují na **tři fyzické režimy** střídače: **PASSIVE**, **SELL**, **CHARGE**. Solver navíc rozlišuje **čtyři typy slotů** – každý typ určuje specifickou kombinaci registrů.
|
||||
@@ -62,7 +64,12 @@ Režim **CHARGE_CHEAP** nastaví oba setpointy na stejný kladný výkon (min. 1
|
||||
### BA81: GEN port cut-off (reg 179) z plánu
|
||||
|
||||
Pro instalace s AC coupling na GEN portu (mikroinvertory) může solver uložit do `planning_interval` flag **`deye_gen_cutoff_enabled`**.\n
|
||||
- `true` → exporter nastaví reg **179** bits0–1 na **2** (`10b`, disable = cut-off ON)\n+- `false` → exporter nastaví bits0–1 na **3** (`11b`, enable = cut-off OFF)\n+\n+Zápis je **masked read-modify-write** (zachová ostatní bity reg. 179). Ověření v journalu (`verify_modbus_commands`) porovnává jen bits0–1 maskou `0x0003`.\n+
|
||||
- `true` → exporter nastaví reg **179** bits0–1 na **2** (`10b`, disable = cut-off ON)
|
||||
- `false` → exporter nastaví bits0–1 na **3** (`11b`, enable = cut-off OFF)
|
||||
|
||||
Zápis je **masked read-modify-write** (zachová ostatní bity reg. 179). Ověření v journalu (`verify_modbus_commands`) porovnává jen bits0–1 maskou `0x0003`.
|
||||
|
||||
**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í).
|
||||
**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
|
||||
|
||||
Reference in New Issue
Block a user