Files
goodwe-addon/goodwe_controller/DECISIONS/SAFETY-81-load-too-high.md
T
adminandClaude Opus 5.5 f967073cfe
checks / unit (pull_request) Successful in 20m31s
infra-pin / fresh (pull_request) Successful in 2s
SAFETY-81 review: 10 s hold, ERROR line per episode, more scenarios
- The alarm must hold for peakguard.ALARM_HOLD_S (10 s) on the projection's
  clock. A load that tops cap + battery for ~5 s every 30 s alarmed at every
  quarter start while the quarter landed under the aim (sine4500_fast).
- A cycle with no battery reading leaves the hold alone.
- Notified once per quarter, but the ERROR line is written per episode.
- New sim scenarios (sine4500_fast, twice7000, step6500_gap) with 5a4cdc2
  digests; control still byte-identical. Unit checks for export and the
  hold across a missing reading. test_peakguard's saturated fixture moves
  the hold start back instead of waiting it out.
- DECISIONS and DOCS updated: costs of the hold, tolerance reasoning, the
  double count in control left to a follow-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:58:37 +02:00

3.4 KiB
Raw Blame History

SAFETY-81 — no false "Load too high for battery" alarms

Status: 2026-09-29, owner go for the card. Review kept the 10 W tolerance as a constant and added the 10 s hold.

Defect

The SAFETY-79 test (SAFETY-79-test-report.md, F1/F2) saw 6 false alarms in 7. The alarm fired whenever decide() clamped its floor to the rating (saturated). Three things set that while the quarter landed on target:

  • Double count. decide() sizes from the held meter reading plus the battery reading. When the meter sample predates the battery's last increase, the increase is counted twice. It happens under noise, and at each quarter start, where the battery is back while the held sample is still the spike.
  • The aim, not the cap. saturated is graded against cap − margin, so at margin 50 noise needs 50 W less to trip it (F2).
  • Last seconds. The gain 900 / remaining turns a few W over the aim into thousands of W wanted, with the battery far from its rating.

Decision

The alarm needs saturated and peakguard.load_too_high(): with the battery at full power from now on, the quarter still ends more than ALARM_MISS_W (10 W) over the real limit (ceiling_w). That is the miss itself, without the gain. The battery reading it uses is the lower of this cycle's and the last one's. It must hold for ALARM_HOLD_S (10 s). At most one notification per quarter, because near the threshold the test flickers with the meter; the ERROR log line is written for every episode.

Why 10 W: at margin 0 the guard itself lands up to +5.7 W over (SAFETY-79 test, check 5), and those misses are real but tiny; the alarm would fire in the last second of most guarded quarters. The margin (default 50) is the fix for them. Cost: a real miss of 10 W or less is not alarmed; at a capacity tariff of the order of EUR 50 per kW a year, 10 W in one month is cents. Review measured +13 W on a 10 kW two-unit site at margin 0, not saturated, so no alarm: the tolerance only matters when the battery is at its rating. A constant, not an option: peak_margin_w is the owner's knob.

Why the hold (review): the test assumes the load now lasts the rest of the quarter. A load over cap + battery for ~5 s every 30 s (sine4500_fast, a cycling hob or heater) alarmed at each quarter start while the quarter landed under the aim. The hold stops that.

Cost: a real alarm comes 10-12 s later than on 5a4cdc2 (step6500 at 1211 s, not 1201 s); a slow ramp 20 s later (ramp6500), as it waits for the real cap. While the battery refuses or its reading is lost, the alarm waits until the quarter is lost even at full battery (27-60 s later in review runs); those faults have their own alarms. A load over cap + battery for more than 10 s still alarms even if the quarter later lands under. ALARM_HOLD_S = 0 removes the hold.

Not changed: the 1 s spike itself

The cycle after a quarter boundary and before the new quarter's first meter sample still sees 0 s left, files no claim, and an Off battery drops to 0 W for one cycle. That is the same edge tick as OPT-15 F1, so it is left to OPT-15. Control is unchanged here: every command in tools/safety81_sim.py is byte-identical to 5a4cdc2 (test_safety81.py part 1).

The double count also moves control, not only the alarm: on a late sample the guard asks for the full rating for one cycle (edge4600 at 902 s: 2000 W where 1150 W holds the aim). Left to a follow-up card.