HARD LIMIT exportu jako tvrdé pravidlo §4.19 + test
All checks were successful
CI and deploy / migration-check (push) Successful in 18s
CI and deploy / deploy (push) Successful in 1m2s

Překročení rezervovaného exportu na fakturačním elektroměru (home-01
13.5 kW) = pokuta v řádu desítek tisíc Kč/kW. Invariant: reg 143
(svorky) <= max_export_power_w (ulice) VŽDY; feed-forward navyšování
o měřenou spotřebu mezi střídačem a CT ZAKÁZÁNO (výpadek spotřeby =
přestřelení ulice). Návrh feed-forwardu z 2026-06-12 večer zavržen
před implementací na pokyn uživatele.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dusan Vojacek
2026-06-12 20:40:11 +02:00
parent 287353b082
commit 406b6a7f8f
2 changed files with 38 additions and 1 deletions

View File

@@ -112,7 +112,9 @@ Projekt je **SQL-first**: doménová logika, agregace, joiny mezi tabulkami a st
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žim Deye** (`PASSIVE` / `CHARGE` / `SELL`): **výhradně** `get_deye_mode` v `exporter_monolith.py` (bez wattových prahů: **SELL** při `battery_w` < 0 a `grid_setpoint_w` < 0; **CHARGE** při obou > 0; jinak **PASSIVE**). **PASSIVE (ZERO, AUTO):** **`export_mode=PV_SURPLUS`** → **108=0**, **109=max**, **142**=`deye_zero_export_mode` (ne selling first); jinak **108/109** dle `deye_battery_charge_discharge_amps` / `_deye_zero_export_amps_for_passive` (import bez vybíjení → **109=0**); **TOU SOC** (reg 166+): **PASSIVE** = **`min_soc_percent`**, **CHARGE** = **`max_soc_percent`** (clamp 10100 z DB), **SELL** = **`reserve_soc_percent`** (`_deye_passive_tou_battery_soc_pct`, `_deye_tou_params`). **SELL:** reg **108** EMS **nezapisuje** (selling first = **142**), **109**=max, **178**=32 (peak shaving off), **143** omezeno podle `|grid_setpoint_w|`; **142/145/TOU** jako v `write_inverter_setpoints`. **Reg 340** (*max solar power*, W): jen pokud `ems.fn_site_has_active_green_bonus_pv(site_id)` **a** `ems.fn_inverter_pv_a_max_w(inverter_id) > 0` (strop z `deye_reg340_max_solar_w`, typ. 32k home-01 / 65k jinde, ne součet Wp; min `deye_reg340_min_solar_w`, home-01 400); hodnota z plánu / curtailu (AUTO). **Není** v `DEYE_CRITICAL_REGS_SELF_SUSTAIN` — verify mismatch nečeká přepnutí do SELF_SUSTAIN. **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 **`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).
19. **HARD LIMIT exportu na fakturačním elektroměru — NIKDY nepřekročit.** Překročení rezervovaného exportního výkonu (home-01: 13.5 kW) byť o desetiny kW = smluvní pokuta v řádu desítek tisíc Kč za kW. Jediný bezpečný invariant: **reg 143 (limit na svorkách střídače) <= max_export_power_w (limit ulice) VŽDY** — v nejhorším případě (spotřeba mezi střídačem a CT odpadne) je ulice rovna svorkám. **ZAKÁZÁNO** jakékoli feed-forward navyšování terminálového limitu o měřenou spotřebu (výpadek spotřeby = přestřelení ulice). Vyšší vytěžení smí přinést jedině interní regulace střídače proti CT (firmware smyčka), nikdy náš software s 1min telemetrií a 15min ticky.
20. **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).
---

View File

@@ -0,0 +1,35 @@
"""HARD LIMIT exportu (CLAUDE.md §4.19): reg 143 nikdy nad limit ulice.
Pokuta v řádu desítek tisíc Kč za každou kW překročení rezervovaného
exportního výkonu na fakturačním elektroměru. Terminálový limit (reg 143)
nesmí přesáhnout max_export_power_w za žádných okolností — žádný
feed-forward o měřenou spotřebu mezi střídačem a CT.
"""
from __future__ import annotations
import unittest
from services.control.setpoints import _deye_reg143_export_w
class ExportHardLimitTests(unittest.TestCase):
def test_reg143_never_exceeds_street_limit(self) -> None:
street_limit = 13_500
self.assertLessEqual(
_deye_reg143_export_w(False, street_limit), street_limit
)
def test_no_export_is_zero(self) -> None:
self.assertEqual(_deye_reg143_export_w(True, 13_500), 0)
def test_plan_export_limit_caps_not_raises(self) -> None:
# vzor z write_inverter_setpoints: export_lim = min(hw, plan) — plán
# smí limit jen SNÍŽIT, nikdy zvýšit
hw = _deye_reg143_export_w(False, 13_500)
plan_limit = 20_000
self.assertLessEqual(min(hw, plan_limit), 13_500)
if __name__ == "__main__":
unittest.main()