prekopani SELL
Some checks failed
CI and deploy / migration-check (push) Failing after 15s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-04-19 22:48:51 +02:00
parent ee4355f17f
commit d8221e3169
7 changed files with 120 additions and 35 deletions

View File

@@ -92,7 +92,7 @@ Projekt je **SQL-first**: doménová logika, agregace, joiny mezi tabulkami a st
17. **Modbus zápis = journal.** Každý zápis do zařízení přes control exporter se loguje do `ems.modbus_command`. **Verifikační job** běží každé **2 minuty** a ověřuje nedávno zápis (`written` → čtení registru). Při **mismatch** po max. **3** pokusech o zápis → u běžných registrů přepnutí na **SELF_SUSTAIN** (`run_fn_set_mode_with_discord``fn_set_mode`, `activated_by` = `system:mismatch`) + **Discord** při skutečné změně režimu. **Výjimka:** souvislý blok Deye **6264** (čas) → po 3 neúspěšných ověřeních **bez** změny režimu, kritický **Discord** (`notify_modbus_clock_verify_exhausted`). **Obecně:** při jakékoli změně `mode_code` z Pythonu (`POST /api/v1/sites/{id}/mode`, mismatch → SELF_SUSTAIN, `fn_expire_modes`) lze Discord zapnout přes `DISCORD_WEBHOOK_URL`. Detail: `docs/04-modules/modbus-command-journal.md`.
18. **Deye zápis registrů 60499:** pouze **FC 0x10** (`write_registers`), **nikdy** FC 0x06 pro tento rozsah; **`execute_modbus_commands`** slučuje souvislé adresy do jednoho FC 0x10. **Fyzické režimy střídače jsou tři:** **PASSIVE**, **SELL**, **CHARGE** (mapování z plánu / politik EMS v `control_exporter.get_deye_mode`). Reg **108** (charge A) se řídí záměrem solveru: **max** při nabíjení (`bat_w > 0`), **0** jinak (pass-through, self-consumption). Reg **109** (discharge A) vždy **max z DB** (**výjimka PRESERVE:** `lock_battery=True`**0 / 0**). **Řízení:** time points blok **1** = začátek **aktuálního** 15min slotu + plán pro tento slot, blok **2** = začátek **následujícího** slotu + plán pro něj (`current_slot_hhmm` / `next_slot_hhmm`); bloky **36** neaktivní **2355** (ne 23:59 kvůli firmware), zápis **nejednou častěji než 1× denně** (Europe/Prague) + při změně podpisu (`deye_tou_inactive_signature`: `HHMM|min_soc|reserve_soc|tp_discharge_w`, V028 meta + V029 komentář); **reg 166+** u TP: **SELL** = `reserve_soc_percent`, **PASSIVE** / řádky **36** = `min_soc_percent`. **108** / **109** / **141** (0) / **142** (`deye_zero_export_mode` z DB: 1 = to load / 2 = to CT v non-SELL; 0 = selling first ve **SELL**) / **178** (pevně **32** ve **SELL**, **48** v **PASSIVE** a **CHARGE** bez read-modify-write) / **143** (export limit W z DB) / **145** (solar sell, vždy **1** = enabled) z **aktuálního** setpointu. **Reg 191** EMS **nezapisuje**. **Čas 6264:** před zařazením do fronty **čtení** 6264; zápis jen při driftu **> 60 s**, nebo **NULL** `deye_last_system_time_sync_at`, nebo uplynulých **24 h** od posledního syncu; `deye_last_system_time_sync_at` / `deye_last_system_time_sync_minute` po **úspěšném zápisu** 6264 a znovu po **úspěšné toleranční verifikaci**; při chybě čtení se čas zapisuje; reg **64** se zapisuje s **sekundami 0**; verify **vždy** čte 6264 najednou — **reg 64 nesmí** do striktní větve; toleranční odchylka až **120 s**; po 3 neúspěších u hodin **bez** SELF_SUSTAIN (jen Discord). **SELL:** `battery_w` < 500 a `grid_setpoint_w` < 200 (aktivní vybíjení baterie pro export). **CHARGE:** `battery_w` > 500 a `grid_setpoint_w` > 200. **PASSIVE:** ostatní (včetně pass-through s reg 108=0, self-consumption, `battery_w=None` u SELF_SUSTAIN). **Čtyři typy slotů:** Charge (108=max), Pass-through (108=0, PV→síť), Discharge-export (SELL, 142=0), Self-consumption (108=0, noc). Reg 109 vždy max kromě PRESERVE. Detail: `docs/04-modules/modbus-registers.md`, režimy: `docs/04-modules/operating-modes.md`.
18. **Deye zápis registrů 60499:** pouze **FC 0x10** (`write_registers`), **nikdy** FC 0x06 pro tento rozsah; **`execute_modbus_commands`** slučuje souvislé adresy do jednoho FC 0x10. **Režimy:** `get_deye_mode`**SELL** jen při **\|battery_w\| ≥ \|grid_setpoint_w\|** a obou záporných (záměr výdeje baterie do sítě); **CHARGE** při `battery_w` > 500 a `grid_setpoint_w` > 200; jinak **PASSIVE**. **PASSIVE:** reg. **108/109** škálované podle `battery_w` z plánu (ne vždy max); **SELL:** 108=0, 109=max, **143** omezeno podle `|grid_setpoint_w|`; **142/178/145/TOU** jako v `write_inverter_setpoints`. **PRESERVE:** `lock_battery` → 108/109=0. **Čas 6264**, bloky TOU **12** vs **36**, verify, Discord: beze změny oproti dřívějšímu chování — plný popis **`docs/04-modules/modbus-registers.md`** a princip *Keep it simple* u režimů **`docs/04-modules/operating-modes.md`**.
19. **Baterie export v LP:** V `solve_dispatch` binárka `z_export[t]`: pokud `grid_export` v daném slotu **≥ 1** W, platí koncové `soc[t] ≥ arb_base_wh` (ekonomická rezerva z DB, ne časová řada `arb_floor_series`). Bez exportu může plán jít k `min_soc_percent` (provozní podlaha; u paralelních packů často 1112 %, migrace V029 + komentář sloupce).