docs: refresh README for sud/server log split and reconnect pump task
The Logging & analysis section and a couple of cross-references still described the old SudLogTask (forecast_*.json pairing) that predated the ServerLogTask refactor and the just-reintroduced per-run SudLogTask. Also documents the state_pump() task's role in reconnect behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TvgC7oy9MxaA4ZQxXqkNdS
This commit is contained in:
@@ -72,9 +72,12 @@ components/ Pluggable building blocks behind factories:
|
|||||||
tasks/ Async tasks (one per component) that poll
|
tasks/ Async tasks (one per component) that poll
|
||||||
hardware/sim state at a fixed interval and
|
hardware/sim state at a fixed interval and
|
||||||
publish changes through the message dispatcher.
|
publish changes through the message dispatcher.
|
||||||
sud_log.py's SudLogTask additionally records
|
server_log.py's ServerLogTask additionally
|
||||||
each Sud run's measured data and forecast to
|
records temp/power samples for the whole server
|
||||||
logs/*.json - see "Logging & analysis" below.
|
session, and sud_log.py's SudLogTask (a thin
|
||||||
|
subclass reusing the same sample format) does
|
||||||
|
the same but only while a Sud run is active -
|
||||||
|
see "Logging & analysis" below.
|
||||||
|
|
||||||
ws/ Minimal WebSocket pub/sub layer: server
|
ws/ Minimal WebSocket pub/sub layer: server
|
||||||
(single- and multi-user), client, and a
|
(single- and multi-user), client, and a
|
||||||
@@ -184,6 +187,14 @@ currently-known state for that channel in one message, via `User.send()`'s
|
|||||||
same replay a brand-new one would — there's no "first connect"
|
same replay a brand-new one would — there's no "first connect"
|
||||||
special-casing anywhere in this path.
|
special-casing anywhere in this path.
|
||||||
|
|
||||||
|
A single `state_pump()` task (started once, independent of any connection)
|
||||||
|
drains the dispatcher's queue and folds every message into `global_state`
|
||||||
|
regardless of how many clients are connected. This matters for a client
|
||||||
|
that silently drops without a close frame (e.g. an iPad going to sleep):
|
||||||
|
nothing queues up waiting for it, so on reconnect it gets the one
|
||||||
|
`global_state` replay above, not a burst of every message broadcast while
|
||||||
|
it was gone.
|
||||||
|
|
||||||
One corollary, easy to miss: this mechanism has no concept of "transient".
|
One corollary, easy to miss: this mechanism has no concept of "transient".
|
||||||
Anything sent through it is implicitly durable state that will be replayed
|
Anything sent through it is implicitly durable state that will be replayed
|
||||||
to whoever connects next, even long after the fact. A one-shot *event*
|
to whoever connects next, even long after the fact. A one-shot *event*
|
||||||
@@ -432,10 +443,9 @@ can otherwise produce a single `Forecast` message of several MB, large
|
|||||||
enough to exceed the `websockets` library's default 1 MiB `max_size` and
|
enough to exceed the `websockets` library's default 1 MiB `max_size` and
|
||||||
get the connection closed outright (code 1009). `SudTask.forecast_t`/
|
get the connection closed outright (code 1009). `SudTask.forecast_t`/
|
||||||
`forecast_theta` themselves stay at full simulated resolution (used for
|
`forecast_theta` themselves stay at full simulated resolution (used for
|
||||||
the exact-match truncation in `_reanchor_forecast()` below, and for
|
the exact-match truncation in `_reanchor_forecast()` below) - only the copy
|
||||||
`SudLogTask`'s full-fidelity `logs/forecast_*.json` - see "Logging &
|
actually sent to clients is thinned, which the GUI's few-hundred-pixel-wide
|
||||||
analysis") - only the copy actually sent to clients is thinned, which the
|
plot can't show the difference from anyway.
|
||||||
GUI's few-hundred-pixel-wide plot can't show the difference from anyway.
|
|
||||||
|
|
||||||
The one wrinkle is a step with `user_wait_for_continue`: a human's response
|
The one wrinkle is a step with `user_wait_for_continue`: a human's response
|
||||||
time genuinely can't be forecast. Rather than stall the whole estimate
|
time genuinely can't be forecast. Rather than stall the whole estimate
|
||||||
@@ -675,18 +685,24 @@ running" if this is ever reachable off a trusted LAN.
|
|||||||
|
|
||||||
## Logging & analysis
|
## Logging & analysis
|
||||||
|
|
||||||
`tasks/sud_log.py`'s `SudLogTask` records every Sud run's measured data -
|
Both loggers record the same six signals the GUI's Automatic tab plots live
|
||||||
the same six signals the GUI's Automatic tab plots live (`temp_ist`/
|
(`temp_ist`/`temp_soll`, `rate_ist`/`rate_soll`, `power_set`/`power_eff`),
|
||||||
`temp_soll`, `rate_ist`/`rate_soll`, `power_set`/`power_eff`), each sample
|
each sample with both simulated-elapsed-seconds and a real wall-clock
|
||||||
with both simulated-elapsed-seconds and a real wall-clock timestamp - to
|
timestamp, plus any plant-param changes along the way (see
|
||||||
`logs/log_<date>T<time>_<sud-name>.json`, plus the forecast it was being
|
`apply_plant_params()`) - written as JSON via `write()`, whole and only
|
||||||
compared against (its *final*, most-corrected version - see "Forecast vs.
|
once, never partially mid-run.
|
||||||
actual duration" above) to a paired `logs/forecast_<date>T<time>_
|
|
||||||
<sud-name>.json`, sharing the same date-time token. A fresh pair starts the
|
`tasks/server_log.py`'s `ServerLogTask` records continuously for the whole
|
||||||
moment a run actually starts (`Sud.state` leaves `IDLE`/`DONE`) and is
|
server session, regardless of Sud state - useful for verifying controller
|
||||||
written out whole the moment it ends (`DONE`, or aborted via `Stop`) -
|
and heater behaviour outside of a scheduled brew. It starts on construction
|
||||||
never partially mid-run, since a half-written file is of no use before
|
and is written to `logs/log_<date>T<time>.json` on shutdown.
|
||||||
the run is over anyway.
|
|
||||||
|
`tasks/sud_log.py`'s `SudLogTask` subclasses `ServerLogTask` to reuse the
|
||||||
|
same sample format, but only records while a Sud run is actually active. A
|
||||||
|
fresh run starts recording on a genuine `Start` from `IDLE`/`DONE` (a
|
||||||
|
`Pause`→resume `Start` is a no-op - it's still the same run) and is written
|
||||||
|
out on `Stop` or natural completion (`DONE`), to
|
||||||
|
`logs/log_<date>T<time>_<sud-name>.log`.
|
||||||
|
|
||||||
`server/brewpi.py` also mirrors everything it prints (every component's
|
`server/brewpi.py` also mirrors everything it prints (every component's
|
||||||
`print()`-based status/debug output) to a plain text log,
|
`print()`-based status/debug output) to a plain text log,
|
||||||
|
|||||||
Reference in New Issue
Block a user