MAINT-20 part 2 r2: app/main.py comments, lines 1-500 (tariff_refresh_wanted .. __init__)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
8fc5f15b03
commit
a5fec377ad
@@ -113,14 +113,13 @@ _LOG = logging.getLogger("goodwe")
|
||||
# function CFG-04 changes.
|
||||
__all__ = ["UNIT_A", "Controller", "amain", "main"]
|
||||
|
||||
# DEV-02: the per-write budget. Every write to every inverter is dispatched
|
||||
# concurrently, each bounded by this, so one hung `hass.call()` cannot eat a
|
||||
# sibling's layer-1 budget.
|
||||
# DERIVED, NOT CHOSEN: `heartbeat_s` max is 25 s and layer 1's veto is
|
||||
# `validate.FIRMWARE_INGEST_VETO_S` (30 s), so 25 + 4 < 30. test_inverter.py
|
||||
# grades the inequality, not the literal. It is below hass.py's 10 s HTTP
|
||||
# timeout on purpose: abandon and re-issue next tick (`last_write` moves only
|
||||
# on an ACCEPTED write). DECISIONS/MAIN-orchestration.md.
|
||||
# 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).
|
||||
# DECISIONS/MAIN-orchestration.md.
|
||||
WRITE_BUDGET_S = 4.0
|
||||
|
||||
# SMA-10: the bound for handing a unit back (`driver.release()`); units are
|
||||
@@ -300,14 +299,10 @@ def why_key(winner: str, claims) -> str:
|
||||
def tariff_plan_row(plan, tick_words: str, *, age_s, horizon_h: float,
|
||||
provider: bool, max_age_s: float) -> str:
|
||||
"""The *Tariff* status row: the 30 s tick's sentence AND the provider plan
|
||||
(TARIFF-11).
|
||||
Two sources, two clocks, and the row says so: `tick_words` is regenerated
|
||||
on this I/O-free tick; `plan` is the refresh's resolution, stamped with its
|
||||
own age from `tariff_at` (not moved on a kept plan, TARIFF-12), up to
|
||||
TARIFF_MAX_AGE_S (5400 s) before `fresh_tariff_plan()` retires it. `age_s`
|
||||
is the caller's measurement: a clock read here would disagree with the one
|
||||
that judged freshness. "No plan" is said aloud on a site that configured a
|
||||
provider (a soak read 'no static profile' at all 802 samples).
|
||||
(TARIFF-11): two sources, two clocks, and the row says so. The plan is
|
||||
stamped with its own age from `tariff_at` (not moved on a kept plan,
|
||||
TARIFF-12), up to TARIFF_MAX_AGE_S (5400 s); `age_s` is the caller's
|
||||
measurement. "No plan" is said aloud on a site that configured a provider.
|
||||
DECISIONS/MAIN-orchestration.md."""
|
||||
# UX-08: plain words, every figure kept. The tick's sentence is "now"; the
|
||||
# plan carries its own age.
|
||||
@@ -373,14 +368,13 @@ class Controller:
|
||||
# UX-08: the last event in plain words, for the page. See log_event().
|
||||
self.hint = ""
|
||||
|
||||
# DEV-02: the roster, built before anything reads a rating. Every
|
||||
# per-device value lives on these objects and nowhere else
|
||||
# (app/inverter.py); a `Tuning` and an integrator are deliberately NOT
|
||||
# per device (6.2: one integrator on P1 above the split). The store
|
||||
# migration runs here, BEFORE the first `Inverter` reads its SAFETY-02
|
||||
# latch: else an existing tripped install comes up ARMED with a green
|
||||
# banner. SAFETY-02's pair is the site comparison's pair (SAFETY-30),
|
||||
# identical at N=1. DECISIONS/MAIN-orchestration.md.
|
||||
# DEV-02: the roster, built before anything reads a rating; every
|
||||
# per-device value lives on these objects (app/inverter.py), but a
|
||||
# `Tuning` and the integrator are deliberately NOT per device (6.2: one
|
||||
# integrator on P1 above the split). The store migration runs here,
|
||||
# BEFORE the first `Inverter` reads its SAFETY-02 latch, else a tripped
|
||||
# install comes up ARMED with a green banner.
|
||||
# DECISIONS/MAIN-orchestration.md.
|
||||
self.sign_cfg = sign_cfg = SignWatchdogConfig(
|
||||
min_w=float(opts.get("sign_disagree_w", 300)),
|
||||
hold_s=float(opts.get("sign_disagree_s", 180)),
|
||||
@@ -412,14 +406,11 @@ class Controller:
|
||||
hold_s=float(opts.get("delivery_hold_s",
|
||||
DELIVERY_OPTION_DEFAULTS["delivery_hold_s"])),
|
||||
)
|
||||
# SAFETY-44: hand the migration the names the machines read:
|
||||
# SAFETY-44: hand the migration the names the machines read,
|
||||
# `roster_names(opts)[0]`, never `UNIT_A` (on a declared roster the
|
||||
# flat latch landed under `unit/inverter-a/`, on no roster, and stamped
|
||||
# the marker). `roster_names()` returns `[UNIT_A]` on the flat shape,
|
||||
# so today's upgrade path is byte-identical. `misdirected` is
|
||||
# unreachable from THIS call site; the `roster` argument stays for new
|
||||
# callers and is graded in test_maintenance.py.
|
||||
# DECISIONS/MAIN-orchestration.md.
|
||||
# latch landed under `unit/inverter-a/`, on no roster, and stamped the
|
||||
# marker). `roster_names()` is `[UNIT_A]` on the flat shape, so today's
|
||||
# path is byte-identical. DECISIONS/MAIN-orchestration.md.
|
||||
_roster_names = roster_names(opts)
|
||||
self.store_migration = migrate_unit_keys(
|
||||
store, _roster_names[0], _roster_names)
|
||||
@@ -465,15 +456,12 @@ class Controller:
|
||||
)
|
||||
# CFG-03: no `opts.get(key, <literal>)` for maintenance here;
|
||||
# `MaintConfig.from_options()` reads them next to
|
||||
# `maintenance.MAINT_OPTION_DEFAULTS` (two literals once disagreed with
|
||||
# config.yaml). A new key goes in config.yaml `options:`/`schema:` and
|
||||
# that dict only.
|
||||
# MAINT-05: one `Maintenance` per roster entry. The history migration
|
||||
# runs first (else a pack believes it was never recalibrated), and
|
||||
# targets `self.inverters[0].name`, NOT `UNIT_A`: the flat keys
|
||||
# belonged to the first roster entry. The roster is passed, not a
|
||||
# count, so `migrate_maint_keys()` grades the namespace it wrote
|
||||
# (test_maintenance.py sections 9 and 11 use `["garage", "shed"]`).
|
||||
# `maintenance.MAINT_OPTION_DEFAULTS`; a new key goes in config.yaml
|
||||
# `options:`/`schema:` and that dict only. MAINT-05: one `Maintenance`
|
||||
# per roster entry; the history migration runs first (else a pack
|
||||
# believes it was never recalibrated) and targets
|
||||
# `self.inverters[0].name`, NOT `UNIT_A`; the roster is passed so
|
||||
# `migrate_maint_keys()` grades the namespace it wrote.
|
||||
# DECISIONS/MAIN-orchestration.md.
|
||||
self.maint_migration = migrate_maint_keys(
|
||||
store, self.inverters[0].name, [inv.name for inv in self.inverters])
|
||||
|
||||
Reference in New Issue
Block a user