60 lines
3.9 KiB
Markdown
60 lines
3.9 KiB
Markdown
# Modbus command journal
|
||
|
||
## Úč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`.
|
||
|
||
## Schéma `ems.modbus_command`
|
||
|
||
| Sloupec | Význam |
|
||
|---------|--------|
|
||
| `asset_type` / `asset_id` / `asset_code` | Typ aktiva (`inverter`, …), FK logicky na příslušnou tabulku, čitelný kód |
|
||
| `device_*` | Host, port, Modbus unit ID |
|
||
| `register` | Číslo registru (decimal); v logu též hex |
|
||
| `register_name` | Např. `charge_limit`, `export_limit` |
|
||
| `value_to_write` / `value_written` / `value_verified` | Požadavek, potvrzený zápis, ověření čtením |
|
||
| `status` | `pending`, `written`, `verified`, `failed`, `mismatch`, `retrying` |
|
||
| `planning_run_id` | Volitelná vazba na aktivní plán |
|
||
| `deye_physical_mode` | U zápisů z `write_inverter_setpoints`: **PASSIVE** / **SELL** / **CHARGE** (stejná hodnota na všech řádcích daného běhu exportu); jinak NULL |
|
||
| `attempt_count` | Počet pokusů o zápis (pro limity retry) |
|
||
|
||
Indexy: podle `(site_id, status, created_at)` a částečný index pro `pending` / `retrying`.
|
||
|
||
## Verifikace a bezpečnost
|
||
|
||
1. Po `mismatch` se odešle **Discord** alert (`notification_service.send_discord` / `notify_modbus_mismatch`), pokud je nastaven `DISCORD_WEBHOOK_URL`.
|
||
2. **Retry** zápisu max. **3×** (počítáno přes `attempt_count` po zápisech).
|
||
3. Po třech neúspěšných cyklech: přepnutí lokality na **SELF_SUSTAIN** přes `ems.fn_set_mode` (`activated_by` = `system:mismatch`, poznámka = důvod) a **kritický** Discord alert (`notify_self_sustain_activated`).
|
||
|
||
Implementace: `services/control_exporter.py` — `verify_modbus_commands`, `_switch_to_self_sustain`.
|
||
|
||
## Střídač (Deye)
|
||
|
||
`write_inverter_setpoints` přidá do journalu mimo **62–64** (čas) a **time pointy 148–177** také řádky pro **108** (max charge A), **109** (max discharge A), **141** (energy mode, vždy 0), **142** (limit control), **178** (pevné hodnoty 32 / 48 podle fyzického režimu, bez read-modify-write), **143** (export limit W). Každý řádek daného exportního běhu má vyplněný **`deye_physical_mode`** (**PASSIVE** / **SELL** / **CHARGE**) pro audit přepínání. **Reg 191** EMS nezapisuje (SolarmanApp). Převod výkonu baterie na proud: `battery_watts_to_amps` viz `modbus-registers.md`. Všechny zápisy journalu jdou přes **`write_registers`** (FC **0x10**), ne FC 0x06. Detail režimů a registrů: `docs/04-modules/modbus-registers.md`.
|
||
|
||
## APScheduler
|
||
|
||
| Job | Frekvence | Popis |
|
||
|-----|-----------|--------|
|
||
| `verify_modbus` | každé **2 min** | Pro každou aktivní site vybere `written` příkazy s `written_at` v posledních **10 min** a zavolá `verify_modbus_commands`. |
|
||
|
||
## Ruční API
|
||
|
||
`GET /api/v1/sites/{site_id}/control/verify?minutes=10`
|
||
|
||
Vrátí počty `checked` / `verified` / `mismatch` a seznam dotčených příkazů s aktuálním stavem po verifikaci.
|
||
|
||
## `ems.cutoff_switch_log`
|
||
|
||
Tabulka pro budoucí logování **cut-off** přepínačů (mikroinvertory / GEN při záporné prodejní ceně). Záznam při **změně** stavu: `asset_code`, `new_state`, `previous_state`, `reason`, `sell_price_czk`, `triggered_by`. Zatím jen schéma; logika napojení v `control_exporter` je v TODO.
|
||
|
||
## Konfigurace
|
||
|
||
- `.env`: `DISCORD_WEBHOOK_URL` — prázdné = notifikace vypnuté (jen log).
|
||
|
||
## Související soubory
|
||
|
||
- Migrace: `db/migration/V023__modbus_command_journal.sql`, `V025__deye_physical_mode.sql`
|
||
- 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`
|