From aeeecc4ddc4787a258949e6081dea32b95acc4bc Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Wed, 1 Jul 2026 09:18:23 +0200 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01TvgC7oy9MxaA4ZQxXqkNdS --- README.md | 54 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index d271958..06a043b 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,12 @@ components/ Pluggable building blocks behind factories: tasks/ Async tasks (one per component) that poll hardware/sim state at a fixed interval and publish changes through the message dispatcher. - sud_log.py's SudLogTask additionally records - each Sud run's measured data and forecast to - logs/*.json - see "Logging & analysis" below. + server_log.py's ServerLogTask additionally + records temp/power samples for the whole server + 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 (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" 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". 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* @@ -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 get the connection closed outright (code 1009). `SudTask.forecast_t`/ `forecast_theta` themselves stay at full simulated resolution (used for -the exact-match truncation in `_reanchor_forecast()` below, and for -`SudLogTask`'s full-fidelity `logs/forecast_*.json` - see "Logging & -analysis") - only the copy actually sent to clients is thinned, which the -GUI's few-hundred-pixel-wide plot can't show the difference from anyway. +the exact-match truncation in `_reanchor_forecast()` below) - only the copy +actually sent to clients is thinned, which the 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 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 -`tasks/sud_log.py`'s `SudLogTask` records every Sud run's measured data - -the same six signals the GUI's Automatic tab plots live (`temp_ist`/ -`temp_soll`, `rate_ist`/`rate_soll`, `power_set`/`power_eff`), each sample -with both simulated-elapsed-seconds and a real wall-clock timestamp - to -`logs/log_T