From b5755f8a52bb29952c74cae4ecbeb7e371aea0bd Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Mon, 22 Jun 2026 22:19:08 +0200 Subject: [PATCH] GUI: seed the actual-trace history with the real t=0 point on a fresh start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The actual trace's first sample otherwise only lands at on_plot_timer()'s next 1-second tick - under a real sim_warp_factor that's already a noticeable way into the run, leaving a small visible gap between the forecast's exact t=0 anchor and where the actual trace first appears. Seeds forecast_history with (elapsed, plot_temp_ist) the moment a fresh Elapsed push arrives, gated on elapsed still being small (FRESH_RUN_ELAPSED_THRESHOLD_S) so reconnecting mid-brew - where the current reading has nothing to do with that run's actual t=0 - doesn't get a bogus point plotted at x=0. Verified live: without this, the actual trace's first point landed at (0.5min, 20.6°) against a forecast anchored at (0min, 19.92°) - a visible gap. With it, the seeded point (0.017min, 19.9°) lines up with the anchor almost exactly, and the following natural samples continue smoothly from there. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01DkkuG48uHFCGKe6dPSERFk --- client/brewpi_gui.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/client/brewpi_gui.py b/client/brewpi_gui.py index 3d37a9a..188ebea 100755 --- a/client/brewpi_gui.py +++ b/client/brewpi_gui.py @@ -24,6 +24,16 @@ SUD_RUNNING_STATES = {"SudState.RAMPING", "SudState.HOLDING", "SudState.WAIT_USE SUD_WAIT_USER_STATE = "SudState.WAIT_USER" SUD_PAUSED_STATE = "SudState.PAUSED" +# Below this (simulated seconds), an 'Elapsed' push is treated as "this run +# just started" rather than "reconnecting mid-brew" - see on_sud_changed()'s +# Elapsed handling. Generous relative to typical dt (0.1-1s/tick): a fresh +# start's first push always lands well under it, while reconnecting to an +# established run (anything past its first handful of ticks) always lands +# well over it - and seeding the history a few seconds "late" within a +# genuinely fresh run is harmless anyway, since the temperature there is +# still essentially the t=0 anchor. +FRESH_RUN_ELAPSED_THRESHOLD_S = 5.0 + def _style_axis(ax): ax.set_facecolor('white') @@ -827,6 +837,19 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): # a run starting. if self.sud_elapsed_seconds is not None: self.sud_elapsed_seconds = msg['Elapsed'] + # Seed the actual-trace history with this run's real + # t=0 point - without it, the first sample only lands + # at on_plot_timer()'s next 1-second tick, which under + # a real sim_warp_factor can already be a noticeable + # way into the run, leaving a small visible gap between + # the forecast's exact t=0 anchor and where the actual + # trace first appears. Gated on elapsed still being + # small so reconnecting mid-brew (large elapsed) - where + # this temperature reading has nothing to do with that + # run's actual t=0 - doesn't get a bogus point plotted + # at x=0. + if not self.forecast_history and self.plot_temp_ist and msg['Elapsed'] < FRESH_RUN_ELAPSED_THRESHOLD_S: + self.forecast_history.append((msg['Elapsed'] / 60.0, self.plot_temp_ist)) elif "Forecast" in key: self.on_sud_forecast_received(msg['Forecast']) elif "Error" in key: