HOLD's time check moves into Maintenance._hold(), used by step() and
_no_soc(). A hold that loses its SoC now ends at hold_min, writes
last_completed and logs the same COMPLETE line. The 15 min HOLD line says
the hold still ends at its normal time. Behaviour with a reading is
unchanged (differential trace against db9e231).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
69 lines
3.7 KiB
Markdown
69 lines
3.7 KiB
Markdown
# MAINT-14 - no SoC reading in a maintenance cycle
|
|
|
|
**Status: for review.** Tests: `test_maint14.py`. No option added; the one new
|
|
number is a constant in `app/maintenance.py`.
|
|
|
|
**The fault.** `Maintenance.tick()` returned early when the SoC was `None`,
|
|
before any timeout check. (1) A charge on a pack whose SoC reading was lost
|
|
never ended: no timeout, no back-off, no notice, and the 7-day backstop of
|
|
MAINT-13 sat behind the same return. (2) A drain kept commanding `discharge_w`
|
|
with an unknown level, which breaks the TEL-08 rule that an unknown level never
|
|
allows discharge.
|
|
|
|
## Rules
|
|
|
|
1. **Charge, no SoC.** The charge timeout (`CHARGE_TIMEOUT_MIN`, gate-open
|
|
minutes) and the 7-day wall backstop still run. When one fires: if the last
|
|
SoC seen was >= target - 3 the charge counts as done (the MAINT-13 "close
|
|
enough to full" rule, HOLD follows); otherwise it fails: MAINT-13 back-off
|
|
and its one notification. If no SoC was ever seen, it fails.
|
|
2. **Drain, no SoC.** DRAIN holds at 0 W and owns the setpoint, exactly as HOLD
|
|
does. The 12 h drain timeout still runs and fails as before.
|
|
3. **SoC comes back.** Nothing is recorded; the drain continues at the drain
|
|
rate, the charge carries on, and the stall wait of MAINT-13 restarts from
|
|
what it was: it paused while the reading was missing, so a gap never counts
|
|
as "no more charge".
|
|
4. **HOLD** is unchanged (0 W).
|
|
5. **Log.** One line when a drain starts holding for this reason ("the battery
|
|
level is unknown, so the drain holds at 0 W until it is back"), and one line
|
|
when the level has been missing for `NOSOC_NOTE_MIN` = 15 min. Both are said
|
|
once per episode; the old line that repeated every tick is gone.
|
|
|
|
## Why
|
|
|
|
- **"Last seen", not "best seen".** A pack that was at 99 and then dropped to 50
|
|
before the reading went is not close to full. Only the last reading counts.
|
|
It is saved with the charge record (`charge_track.last`), and saved at once
|
|
when the reading first goes missing, so a restart mid-gap still knows it
|
|
(within the usual 5 min save interval otherwise).
|
|
- **Failure when nothing was seen.** With no level we cannot call a pack full.
|
|
The failure costs a 7-day wait and one notice; calling it done costs a
|
|
missed balance and a month-long false "complete". The first is the honest one.
|
|
- **Timeouts count the same minutes as with a reading.** Gate-open charging
|
|
minutes only; the wall backstop for a gate that never opens.
|
|
- **15 min.** A judgement. A poll glitch of a few minutes should not write a
|
|
line; a reading lost for a quarter of an hour is worth one. Raise it if the
|
|
action log shows noise.
|
|
- **0 W, not "stop the phase".** Aborting a drain on a short gap would throw
|
|
away the part already drained. Holding at 0 W loses nothing and the 12 h
|
|
timeout is the limit.
|
|
|
|
## Cost
|
|
|
|
- A drain that loses its reading stops draining, and the 12 h clock keeps
|
|
running. A gap of many hours can therefore fail a drain that was working. The
|
|
back-off then applies (7 days).
|
|
- A charge whose reading is lost early (last SoC below target - 3) fails at the
|
|
timeout, even if the pack was in fact filling. The same 7-day wait applies.
|
|
|
|
## What this does not cover
|
|
|
|
- **HOLD with no SoC**: closed by MAINT-15 (`MAINT-15-hold-ends-without-soc.md`).
|
|
- It does not tell the operator why the reading is missing (sensor, register,
|
|
link). It does not reuse the stale-input gate; it is the cycle's own rule.
|
|
- The charge itself is still driven by the control loop, which has its own
|
|
handling of a missing SoC (TEL-08 for the loop is a separate card).
|
|
- A restart loses at most 5 min of accounting, as in MAINT-13. The "unknown for
|
|
long" line may be said again after a restart: the episode start is in memory.
|
|
- Text added is two log lines, English only, like the other log lines.
|