MAINT-21: comments only - trim restated notes, add DECISIONS pointers
No code change: tools/maint21_same_ast.py and tools/maint20_same_code.py HEAD~ both print OK, and the -OO code objects are the same. - Trims 56 comment and docstring lines (2088 -> 2032) in app/main.py and the new files that only restated their MAIN-orchestration.md bullet, each cut to its first sentence and a pointer. What the cut comments said and the bullet did not is now in the bullet: why a SAFETY-02 latch is restored, why the SAFETY-30 site check is not armed at one inverter, the SAFETY-18 re-derive, the MAINT-05 one-config rule, the OPT-09 sizing rules, the "no plan" row, the 5 400 s age bound, and the guard being the only latch repair. - Adds the missing DECISIONS pointers on the SMA-10, SAFETY-137 and SMA-2 comments (FALLBACK_BUDGET_S, write_setpoint, _await_inflight, _release_last, the retry wait, the failed-release branch, release_zero_sent, the in-flight write, the stop_event hand-back, the SMA-2 read and driver). - DECISIONS/SAFETY-32: the stale `main.py:932-936` now names Controller.pack_floor_soc. - main.py's docstring and import block say where the Controller's parts are. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
27a867f34b
commit
902da3f723
@@ -15,12 +15,8 @@ from . import shadow
|
||||
_LOG = logging.getLogger("goodwe")
|
||||
|
||||
|
||||
# DEV-02: the per-write budget: every write is dispatched concurrently, each
|
||||
# bounded by this, so one hung `hass.call()` cannot eat a sibling's layer-1
|
||||
# budget. DERIVED: `heartbeat_s` max 25 s + 4 <
|
||||
# `validate.FIRMWARE_INGEST_VETO_S` (30 s), graded by test_inverter.py; below
|
||||
# hass.py's 10 s HTTP timeout on purpose (abandon, re-issue next tick;
|
||||
# `last_write` moves only on an ACCEPTED write).
|
||||
# DEV-02: the per-write budget, DERIVED: `heartbeat_s` max 25 s + 4 <
|
||||
# `validate.FIRMWARE_INGEST_VETO_S` (30 s), graded by test_inverter.py.
|
||||
# DECISIONS/MAIN-orchestration.md.
|
||||
WRITE_BUDGET_S = 4.0
|
||||
|
||||
@@ -33,6 +29,7 @@ RELEASE_BUDGET_S = WRITE_BUDGET_S
|
||||
|
||||
# SMA-10: the one fallback write after a failed release (default 0 W). Worst
|
||||
# case RELEASE_BUDGET_S + FALLBACK_BUDGET_S = 6 s, inside the Supervisor's 10 s.
|
||||
# DECISIONS/SMA-10-two-idle-states.md.
|
||||
FALLBACK_BUDGET_S = 2.0
|
||||
|
||||
|
||||
@@ -345,7 +342,8 @@ class WritesMixin:
|
||||
"""Put `value` on ONE inverter, or with `unit=None` on all. `unit=None`
|
||||
is for a number that is not a share (stop button, `shutdown()`,
|
||||
`maint_abort()`: 0 W means the same to every inverter). Writes are
|
||||
concurrent, each under its own budget (DEV-02). `last`: SAFETY-137."""
|
||||
concurrent, each under its own budget (DEV-02). `last`:
|
||||
DECISIONS/SAFETY-137-last-write-is-zero.md."""
|
||||
if unit is None:
|
||||
targets = [i for i in self.inverters]
|
||||
if len(targets) > 1 and is_commandable(value) and float(value) != 0.0:
|
||||
@@ -375,14 +373,16 @@ class WritesMixin:
|
||||
|
||||
async def _await_inflight(self, inv, budget_s: float) -> None:
|
||||
"""Wait, at most `budget_s`, for writes already on their way to `inv`; one
|
||||
still running is left to its own WRITE_BUDGET_S. SAFETY-137."""
|
||||
still running is left to its own WRITE_BUDGET_S.
|
||||
DECISIONS/SAFETY-137-last-write-is-zero.md."""
|
||||
pending = [t for t in inv.inflight if not t.done()]
|
||||
if pending:
|
||||
await asyncio.wait(pending, timeout=budget_s)
|
||||
|
||||
async def _release_last(self, inv) -> bool:
|
||||
"""`release()` once any write in flight to `inv` has finished (at most
|
||||
SETTLE_BUDGET_S): the release is the LAST thing the unit hears."""
|
||||
SETTLE_BUDGET_S): the release is the LAST thing the unit hears.
|
||||
DECISIONS/SMA-10-two-idle-states.md."""
|
||||
await self._await_inflight(inv, SETTLE_BUDGET_S)
|
||||
return await self._driver(inv).release()
|
||||
|
||||
@@ -397,7 +397,7 @@ class WritesMixin:
|
||||
if not inv.driver.configured():
|
||||
return False
|
||||
# After a failure the next try waits one heartbeat; Stop and shutdown
|
||||
# (`force`) always try.
|
||||
# (`force`) always try. DECISIONS/SMA-10-two-idle-states.md.
|
||||
now_m = time.monotonic()
|
||||
if not force and now_m < inv.release_retry_at:
|
||||
return False
|
||||
@@ -421,6 +421,7 @@ class WritesMixin:
|
||||
self.log_event(f"{inv.name}: handed back",
|
||||
hint=messages.hint("handed_back", unit=inv.name))
|
||||
return True
|
||||
# SMA-10: a failed hand-back is tried again, and sent 0 W once below.
|
||||
inv.released = False
|
||||
inv.release_fails += 1
|
||||
inv.release_retry_at = now_m + float(self.o.get("heartbeat_s", 10))
|
||||
@@ -433,7 +434,7 @@ class WritesMixin:
|
||||
hint=messages.hint("hand_back_failed", unit=inv.name))
|
||||
if not inv.release_zero_sent:
|
||||
# ⚠️ ONCE: a 0 W re-sent on every try would reset the inverter's own
|
||||
# fallback timer for ever.
|
||||
# fallback timer for ever. DECISIONS/SMA-10-two-idle-states.md.
|
||||
inv.release_zero_sent = True
|
||||
try:
|
||||
if await _bounded(self._driver(inv).release_fallback(),
|
||||
@@ -539,7 +540,8 @@ class WritesMixin:
|
||||
# ACCEPTED write).
|
||||
try:
|
||||
_w = self._driver(inv).write(value)
|
||||
# SMA-10 / SAFETY-137: kept so a release or last 0 W can wait for it.
|
||||
# SMA-10 / SAFETY-137: kept so a release or last 0 W can wait for
|
||||
# it. DECISIONS/SAFETY-137-last-write-is-zero.md.
|
||||
_w = asyncio.ensure_future(_w)
|
||||
inv.inflight.add(_w)
|
||||
_w.add_done_callback(inv.inflight.discard)
|
||||
|
||||
Reference in New Issue
Block a user