TeltoCharge write-on-change: zápis jen při změně hodnoty (EEPROM wear)

Wallbox dostával zápisy 15/19/20 každý export tick (~8x/hod: control_export
:14,:29,:44,:59 + rolling replan */15 s exportem), protože drop-unchanged
stál na fn_modbus_last_verified_map — dokud verify čtení nedoběhlo/selhalo,
mapa byla prázdná a celá trojice se psala pořád dokola. write_ev_arrival_hold
navíc psal trojici nepodmíněně při každém píchnutí kabelu (docstring lhal).

- nová ems.fn_modbus_device_state_map (R__100): nejnovější řádek journalu
  per registr, hodnota jen pro written/verified; failed/mismatch => registr
  chybí => po výpadku se konfigurace obnoví jedním zápisem
- write_ev_setpoints + write_ev_arrival_hold filtrují přes tuto mapu:
  reg 15 jen při změně plánu, watchdog 19/20 jednou po startu/po výpadku
- verify job EV chargery ověřuje už dnes (fn_modbus_written_command_ids bez
  filtru asset_type); registry 15/19/20 jsou dle oficiálního protokolu R/W
- watchdog Telto sytí jakákoli validní komunikace vč. FC3 čtení telemetrie
  (60 s << 300 s) — periodické zápisy k udržení spojení nejsou potřeba,
  failsafe 8 A nastane jen při skutečném výpadku EMS
- testy: tests/test_ev_write_on_change.py (drop, setpoints, arrival hold)
- docs: modbus-registers-teltocharge.md (sekce Zápis už není "NEimplementováno",
  R/W tabulka, watchdog sémantika), modbus-command-journal.md (sekce EV
  wallbox), CLAUDE.md (fn_modbus_device_state_map)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dusan Vojacek
2026-06-12 22:21:59 +02:00
parent a889950eba
commit 7decfebdbd
7 changed files with 362 additions and 25 deletions

View File

@@ -69,6 +69,35 @@ async def _fetch_last_verified_registers(
return {int(k): int(v) for k, v in data.items()}
async def _fetch_device_state_registers(
site_id: int,
asset_id: int,
db: asyncpg.Connection,
*,
asset_type: str,
) -> dict[int, int]:
"""
Poslední známá hodnota na zařízení podle journalu — NEJNOVĚJŠÍ řádek per
registr, hodnota jen pro status 'verified' nebo 'written' (zápis prošel,
verify ještě nemusel doběhnout). Novější failed/mismatch => registr chybí
=> volající zapíše znovu (obnova konfigurace po výpadku zařízení).
Pro write-on-change u EV wallboxů (EEPROM wear): na rozdíl od
_fetch_last_verified_registers nevyžaduje úspěšný verify, takže se zápis
neopakuje každý export tick, když verify čtení zaostává nebo selhává.
"""
raw = await db.fetchval(
"""
select ems.fn_modbus_device_state_map($1::int, $2::int, $3::text)
""",
site_id,
asset_id,
asset_type,
)
data = raw if isinstance(raw, dict) else json.loads(raw)
return {int(k): int(v) for k, v in data.items()}
async def _fetch_last_verified_inverter_registers(
site_id: int, inverter_asset_id: int, db: asyncpg.Connection
) -> dict[int, int]: