Surface Sud's Load rejection as a server-driven error, not GUI guesswork

Sud.load() already refuses a new schedule while a run is in progress
(state not IDLE/DONE) - but tasks/sud.py's SudTask.recv() silently
dropped that refusal, and an earlier attempt to handle it client-side
by pre-emptively greying out the File menu based on the GUI's own
(replicated, laggy) view of sud_state was reverted: the intelligence
belongs on the server, which already knows definitively whether it's
running, not on the client guessing from a mirrored state that can
lag or miss the message that changed it.

tasks/sud.py: when Sud.load() returns False, send {'Sud': {'Error':
...}} explaining why, instead of doing nothing.

client/brewpi_gui.py: New/Save/Load Sud stay enabled unconditionally;
on_sud_changed() shows whatever 'Error' the server sends via
QMessageBox.warning(). on_action_sud_new()'s comment updated to match
(it goes through the same Load path, so gets the same rejection/error
for free).

Verified live: triggering Load while a run is in progress on an
isolated test server round-trips the Error over the wire and pops the
expected dialog ("Cannot load a new schedule while a run is in
progress - stop it first.").

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhiQe64F74uHV8jzuoSa5K
This commit is contained in:
2026-06-22 13:01:01 +02:00
co-authored by Claude Sonnet 4.6
parent de92559e31
commit 1af8c39926
2 changed files with 14 additions and 2 deletions
+7 -2
View File
@@ -659,8 +659,11 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
self.msg_sud.send({'Confirm': True})
def on_action_sud_new(self):
# Replaces the running Sud's schedule with an empty one, same as
# Load but with a built-in empty document instead of a file dialog.
# Sends an empty schedule via the same Load path Load Sud uses, with
# a built-in empty document instead of a file dialog - the server
# itself refuses this (like any Load) while a run is in progress,
# surfacing an error (see on_sud_changed()'s 'Error' handling)
# rather than the GUI trying to guess that here.
self.msg_sud.send({'Load': {'Name': '', 'Description': '', 'pot_mass': 0, 'pot_material': None, 'steps': []}})
def on_action_sud_save(self):
@@ -876,6 +879,8 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
self.server_remaining_forecast = (anchor_min, forecast['T'], forecast['Theta'])
elif "Forecast" in key:
self.on_sud_forecast_received(msg['Forecast'])
elif "Error" in key:
QtWidgets.QMessageBox.warning(self, "Sud", msg['Error'])
self.update_sud_actions()
def on_sud_forecast_received(self, forecast):
+7
View File
@@ -196,6 +196,13 @@ class SudTask(ATask):
await self.send({'Name': self.sud.name, 'Description': self.sud.description})
await self.send({'Json': pair[1]})
await self.send_forecast(pair[1])
else:
# Sud.load() refuses while a run is in progress (state
# not IDLE/DONE) - tell the client why instead of
# silently dropping the request. The client has no
# business pre-emptively guessing this itself from its
# own (replicated, laggy) view of the state.
await self.send({'Error': 'Cannot load a new schedule while a run is in progress - stop it first.'})
async def send(self, data):
await self.msg_handler.send(data)