Run the Pot simulation from server startup, not just once a Sud loads

PotTask.on_process() only calls Pot.process() once Pot.is_configured()
- which required plant params (M/C/L/Td) that, by design, only ever
came from a Sud's own doc via SudTask.apply_plant_params(). So with no
Sud loaded (or the temp controller disabled), the simulated Pot just
sat inert: manually driving the heater never moved its temperature at
all.

EMPTY_SUD's pot_mass/pot_material defaulted to 0/None, which would
have made a zero-mass plant (a ZeroDivisionError in Pot.process()) -
they're actually the physical kettle's own properties, not really
per-recipe (every sude/*.json so far uses the same pot), so default
them to that real kettle's values instead. server/brewpi.py now seeds
the Pot with derive_plant_params() from the not-yet-loaded Sud right
away, same as ambient temperature already was - a real Sud's own doc
still overrides these the moment one is loaded.
This commit is contained in:
2026-06-23 20:01:59 +02:00
parent a8ae6f2a65
commit 63fa093a8a
2 changed files with 18 additions and 10 deletions
+11 -2
View File
@@ -59,8 +59,17 @@ def _build_step(number, defaults, raw_step):
EMPTY_SUD = { EMPTY_SUD = {
'Name': '', 'Name': '',
'Description': '', 'Description': '',
'pot_mass': 0, # pot_mass/pot_material are the physical kettle's own properties, not
'pot_material': None, # really per-recipe (every sude/*.json so far uses this exact pot) -
# defaulting to 0/None here used to make derive_plant_params() come out
# with a zero-mass plant (a ZeroDivisionError in Pot.process() waiting
# to happen) for as long as no Sud had ever been loaded yet. Matching
# the real kettle here instead lets server/brewpi.py seed the Pot with
# a physically sane default at startup, so its simulation actually
# runs - and a real Sud's own doc still overrides these the moment one
# is loaded (see SudTask.apply_plant_params()).
'pot_mass': 5.96,
'pot_material': "Edelstahl 18/10",
# Pot energy loss coefficient [W/(kg*K)] and transport propagation # Pot energy loss coefficient [W/(kg*K)] and transport propagation
# delay [s] - see derive_plant_params(). Defaults match the generic # delay [s] - see derive_plant_params(). Defaults match the generic
# values brewpi.py used to hardcode for every brew alike. # values brewpi.py used to hardcode for every brew alike.
+7 -8
View File
@@ -69,16 +69,15 @@ if __name__ == '__main__':
sensor_task = TempSensorTask(sensor, DT_TASK, dispatcher.msgio_get("Sensor")) sensor_task = TempSensorTask(sensor, DT_TASK, dispatcher.msgio_get("Sensor"))
taskmgr.add(sensor_task) taskmgr.add(sensor_task)
# Plant - deliberately left without plant params (M/C/L/Td) here: a # Plant - seeded with the not-yet-loaded sud's own (now physically
# Sud's own doc is the only source for those now (see # sane, see Sud.EMPTY_SUD) plant params right away, same as ambient
# tasks/sud.py's SudTask.apply_plant_params(), called immediately on # below, so the simulated Pot already runs - manual heater
# Load), so the plant stays inert (Pot.is_configured() is False, # power/Sud disabled or not - instead of sitting inert until a real
# PotTask skips process() - see there) until one actually is. Ambient # Sud loads. A real Sud's own doc still overrides these the moment
# is independent of any Sud (a global setting, changeable live via # one is loaded (see tasks/sud.py's SudTask.apply_plant_params()).
# the System channel - see on_system_recv() below), so it's set
# right away regardless.
pot = Pot(DT) pot = Pot(DT)
pot.set_ambient_temperature(theta_amb) pot.set_ambient_temperature(theta_amb)
pot.set_plant_params(sud.derive_plant_params(sud.grain_mass, sud.water_mass))
taskmgr.add(PotTask(pot, DT_TASK, dispatcher.msgio_get("Pot"))) taskmgr.add(PotTask(pot, DT_TASK, dispatcher.msgio_get("Pot")))
# Heater # Heater