Merge branch 'worktree-agent-a85e5077c297a63df' into dev
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## Účel
|
||||
|
||||
Každý zápis na Modbus TCP (Deye a později další aktiva) se ukládá do tabulky `ems.modbus_command` jako samostatný řádek: cílový registr, hodnota, endpoint, vazba na `site_id` a volitelně na `planning_run_id`. Po zápisu má řádek stav `written`; samostatný **verifikační job** (každé 2 minuty) nebo ruční **GET** `/api/v1/sites/{site_id}/control/verify` přečte registr zpět a nastaví `value_verified` a stav `verified` nebo `mismatch`. **Výjimka:** Deye **62–64** (systémový čas) se vždy ověřují **jako celek** jedním čtením 62–64 a **tolerančně** podle dekódovaného data/času — řádky 62–64 se **neprohánějí** striktní větví po jednom registru (jinak by zejména **64** způsoboval falešné `mismatch` a SELF_SUSTAIN). Podmnožina `written` řádků (např. jen 64) se sloučí s dotazem na všechny `written` 62–64 pro daný invertor; viz `modbus-registers.md`.
|
||||
Každý zápis na Modbus TCP (Deye `inverter` i TeltoCharge `ev_charger`) se ukládá do tabulky `ems.modbus_command` jako samostatný řádek: cílový registr, hodnota, endpoint, vazba na `site_id` a volitelně na `planning_run_id`. Po zápisu má řádek stav `written`; samostatný **verifikační job** (každé 2 minuty) nebo ruční **GET** `/api/v1/sites/{site_id}/control/verify` přečte registr zpět a nastaví `value_verified` a stav `verified` nebo `mismatch`. **Výjimka:** Deye **62–64** (systémový čas) se vždy ověřují **jako celek** jedním čtením 62–64 a **tolerančně** podle dekódovaného data/času — řádky 62–64 se **neprohánějí** striktní větví po jednom registru (jinak by zejména **64** způsoboval falešné `mismatch` a SELF_SUSTAIN). Podmnožina `written` řádků (např. jen 64) se sloučí s dotazem na všechny `written` 62–64 pro daný invertor; viz `modbus-registers.md`.
|
||||
|
||||
## Schéma `ems.modbus_command`
|
||||
|
||||
@@ -45,6 +45,30 @@ Pro diagnostiku času Deye po opravě clock logiky používej u `modbus_command`
|
||||
|
||||
Implementace: `services/control_exporter.py` — `verify_modbus_commands`, `_verify_deye_clock_written_bundle`, `_fetch_written_deye_clock_commands`, `_switch_to_self_sustain`, `DEYE_CRITICAL_REGS_SELF_SUSTAIN`, `_deye_tou_power_verify_match`; `services/notification_service.py` — `run_fn_set_mode_with_discord`, `_auto_rolling_replan_after_self_sustain_exit`, `notify_operating_mode_changed`.
|
||||
|
||||
## EV wallbox (TeltoCharge)
|
||||
|
||||
`write_ev_setpoints` (každý export tick) a `write_ev_arrival_hold` (po detekci
|
||||
příjezdu EV) zapisují registry **15** (amps to use), **19** (comm timeout 300 s)
|
||||
a **20** (failsafe 8 A) — vždy přes journal (`asset_type = 'ev_charger'`).
|
||||
|
||||
- **Verify job ověřuje všechny asset typy** — `fn_modbus_written_command_ids`
|
||||
nefiltruje podle `asset_type` a registry 15/19/20 jsou dle protokolu R/W
|
||||
(čtou se zpět standardní FC 3 větví).
|
||||
- **Write-on-change:** před zápisem se registry filtrují proti
|
||||
**`ems.fn_modbus_device_state_map`** (nejnovější řádek journalu per registr;
|
||||
hodnota jen pro stav `written`/`verified`). Shodná hodnota ⇒ zápis se
|
||||
přeskočí. Na rozdíl od `fn_modbus_last_verified_map` (Deye drop-unchanged)
|
||||
nečeká na verify — `written` stačí, takže pomalý/neúspěšný verify read
|
||||
nevede k opakovaným zápisům každý tick (EEPROM wear). Nejnovější řádek
|
||||
`failed`/`mismatch` ⇒ registr v mapě chybí ⇒ po výpadku zařízení se
|
||||
konfigurace (vč. watchdog 19/20) obnoví jedním zápisem.
|
||||
- **Mismatch po 3 pokusech NEpřepíná SELF_SUSTAIN** — fallback režim je Deye
|
||||
politika (`asset_type = 'inverter'`); u wallboxu zůstane řádek `mismatch`
|
||||
+ Discord (`notify_modbus_mismatch`).
|
||||
- Watchdog wallboxu sytí i FC 3 čtení telemetrie (60 s) — periodické zápisy
|
||||
k udržení spojení nejsou potřeba; detail
|
||||
[`modbus-registers-teltocharge.md`](modbus-registers-teltocharge.md).
|
||||
|
||||
## Střídač (Deye)
|
||||
|
||||
`write_inverter_setpoints` přidá do journalu podle potřeby **62–64** (čas — po čtení z invertoru jen při driftu / 24h intervalu; viz `modbus-registers.md`) a **time pointy 148–177** (bloky 3–6 typicky jednou denně; viz `modbus-registers.md`), dále **108**, **109**, **141**, **142**, **178**, **143**. Každý řádek daného exportního běhu má **`deye_physical_mode`** (**PASSIVE** / **SELL** / **CHARGE**). **Reg 191** EMS nezapisuje (SolarmanApp). Převod výkonu: `battery_watts_to_amps` v `modbus-registers.md`.
|
||||
@@ -83,6 +107,6 @@ Poznámka: **GEN port cut-off na BA81** se aktuálně provádí přímo přes De
|
||||
|
||||
## Související soubory
|
||||
|
||||
- Migrace: `db/migration/V023__modbus_command_journal.sql`, `V025__deye_physical_mode.sql`, `V030__deye_clock_sync_at.sql`, `V044__deye_register_max_current_a.sql`; repeatables `db/routines/R__044_fn_set_mode.sql` (`fn_expire_modes` vrací detail přepnutí pro notifikace)
|
||||
- Backend: `backend/services/control_exporter.py`, `backend/services/modbus_client.py`, `backend/services/notification_service.py`, `backend/app/main.py`
|
||||
- Registry Deye: `docs/04-modules/modbus-registers.md`
|
||||
- Migrace: `db/migration/V023__modbus_command_journal.sql`, `V025__deye_physical_mode.sql`, `V030__deye_clock_sync_at.sql`, `V044__deye_register_max_current_a.sql`; repeatables `db/routines/R__044_fn_set_mode.sql` (`fn_expire_modes` vrací detail přepnutí pro notifikace), `db/routines/R__002_fn_modbus_last_verified_map.sql`, `db/routines/R__100_fn_modbus_device_state_map.sql` (write-on-change pro EV)
|
||||
- Backend: `backend/services/control_exporter.py`, `backend/services/control/outputs.py` (EV write-on-change), `backend/services/modbus_client.py`, `backend/services/notification_service.py`, `backend/app/main.py`
|
||||
- Registry Deye: `docs/04-modules/modbus-registers.md`; TeltoCharge: `docs/04-modules/modbus-registers-teltocharge.md`
|
||||
|
||||
@@ -28,15 +28,43 @@ Mapování stavů v EMS (`TELTO_STATUS_MAP` v `telemetry_collector.py`):
|
||||
**Při selhání čtení se vzorek NEzapisuje** — fabrikovaný `available` by falešně
|
||||
ukončil session a EV výkon 0 by špinil bazál (pravidlo 15).
|
||||
|
||||
## Zápis (budoucí řízení — zatím NEimplementováno)
|
||||
## Zápis (control exporter — `write_ev_setpoints`, `write_ev_arrival_hold`)
|
||||
|
||||
| Reg | Význam | Hodnoty |
|
||||
|-----|--------|---------|
|
||||
| 15 | **Amps to use** (limit proudu) | 0 = stop, 6–32 A |
|
||||
| 16 | Start/Stop session | 0 nic · 1 stop · 2 start |
|
||||
| 19 | Communication timeout (watchdog) | 0–600 s (0 = vypnuto) |
|
||||
| 20 | Failsafe current | 0, 6–32 A |
|
||||
| Reg | R/W | Význam | Hodnoty | EMS zapisuje |
|
||||
|-----|-----|--------|---------|--------------|
|
||||
| 15 | R/W | **Amps to use** (limit proudu) | 0 = stop, 6–32 A | hodnota z plánu (`ev{1,2}_current_a`); příjezd EV → hold 0 A |
|
||||
| 16 | R/W | Start/Stop session | 0 nic · 1 stop · 2 start | ne |
|
||||
| 19 | R/W | Communication timeout (watchdog) | 0–600 s (0 = vypnuto), default 30 | `TELTO_WATCHDOG_TIMEOUT_S` = **300** |
|
||||
| 20 | R/W | Failsafe current | 0, 6–32 A, default 6 | `TELTO_WATCHDOG_FAILSAFE_A` = **8** |
|
||||
|
||||
Až se zapne řízení: zapisovat reg 15 přes journal `modbus_command`
|
||||
(pravidlo 17) a nastavit watchdog (reg 19/20) — při výpadku EMS wallbox
|
||||
spadne na failsafe proud.
|
||||
Všechny čtyři registry jsou dle oficiálního protokolu (wiki *External control
|
||||
RS485* / protokol rev 0.5) **R/W** — verify job je čte zpět standardní FC 3
|
||||
větví (žádný write-only registr v této sadě).
|
||||
|
||||
### Write-on-change — POVINNÉ (EEPROM wear)
|
||||
|
||||
Export tick běží ~8×/hod (control_export `:14,:29,:44,:59` + rolling replan
|
||||
`*/15` s exportem). Zápis do wallboxu se proto provádí **jen při skutečné
|
||||
změně hodnoty**: `write_ev_setpoints` i `write_ev_arrival_hold` filtrují
|
||||
registry přes `_drop_registers_matching_last_verified` proti
|
||||
**`ems.fn_modbus_device_state_map`** (nejnovější řádek journalu per registr
|
||||
se stavem `written` **nebo** `verified`). Důsledky:
|
||||
|
||||
- **reg 15** se zapíše jen při změně plánovaného proudu (0 ↔ 6–32 A) — to je
|
||||
legitimní zápis;
|
||||
- **reg 19/20** se zapíší jednou po nasazení / po výpadku zařízení (nejnovější
|
||||
řádek `failed`/`mismatch` ⇒ registr v mapě chybí ⇒ znovu se zapíše) a pak
|
||||
už nikdy, dokud se hodnota nezmění;
|
||||
- čekání na verify **neblokuje** skip — `written` (TCP ack) stačí, mismatch
|
||||
z verify stav mapy zneplatní a vynutí nový zápis.
|
||||
|
||||
### Watchdog — sytí ho i čtení
|
||||
|
||||
Protokol definuje timeout jako *„if no **valid communication** is present
|
||||
after a configurable time interval…"* — timer resetuje **jakákoli** validní
|
||||
Modbus komunikace s unit ID wallboxu, **včetně FC 3 čtení**. Telemetrie čte
|
||||
blok 0–40 každých **60 s**, takže watchdog 300 s je trvale sycen čtením a
|
||||
**periodické zápisy k udržení spojení nejsou potřeba**. Failsafe (omezení na
|
||||
8 A, reg 20 „max allowed current on comm timeout") nastane až po 5 min bez
|
||||
jakéhokoli pollingu = skutečný výpadek EMS; auto se pak přes noc dobije
|
||||
pomalu místo stání na 0 A.
|
||||
|
||||
Reference in New Issue
Block a user