Refactor sud JSON pot params under a nested 'pot' key
Top-level pot_mass/pot_material/L/Td/grain_mass/water_mass and their per-step overrides are now grouped under a 'pot' sub-object, matching the structure already applied to sud_0010.json's parent level. Updated all sude/*.json files and the parsing/consuming code in components/sud.py, tasks/sud.py, components/sud_forecast.py, and scripts/demos/sud/demo_sud.py. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T52JH848ojhXXHn1bAzdC3
This commit is contained in:
+26
-22
@@ -47,8 +47,9 @@ def _build_step(number, defaults, raw_step):
|
|||||||
its own, so it's left None, telling SudTask not to push a new
|
its own, so it's left None, telling SudTask not to push a new
|
||||||
theta_soll and just keep whatever the previous step left running."""
|
theta_soll and just keep whatever the previous step left running."""
|
||||||
step = {'number': number}
|
step = {'number': number}
|
||||||
for key in ('descr', 'user_message', 'user_wait_for_continue', 'grain_mass', 'water_mass'):
|
for key in ('descr', 'user_message', 'user_wait_for_continue'):
|
||||||
step[key] = raw_step.get(key, defaults.get(key))
|
step[key] = raw_step.get(key, defaults.get(key))
|
||||||
|
step['pot'] = _merge_defaults(defaults.get('pot', {}), raw_step.get('pot', {}))
|
||||||
step['temperature'] = raw_step.get('temperature')
|
step['temperature'] = raw_step.get('temperature')
|
||||||
step['ramp'] = _merge_defaults(defaults.get('ramp', {}), raw_step.get('ramp', {}))
|
step['ramp'] = _merge_defaults(defaults.get('ramp', {}), raw_step.get('ramp', {}))
|
||||||
if 'hold' in raw_step:
|
if 'hold' in raw_step:
|
||||||
@@ -59,21 +60,23 @@ def _build_step(number, defaults, raw_step):
|
|||||||
EMPTY_SUD = {
|
EMPTY_SUD = {
|
||||||
'Name': '',
|
'Name': '',
|
||||||
'Description': '',
|
'Description': '',
|
||||||
'pot_mass': 0,
|
'pot': {
|
||||||
'pot_material': None,
|
'mass': 0,
|
||||||
# Pot energy loss coefficient [W/(kg*K)] and transport propagation
|
'material': None,
|
||||||
# delay [s] - see derive_plant_params(). Defaults match the generic
|
# Pot energy loss coefficient [W/(kg*K)] and transport propagation
|
||||||
# values brewpi.py used to hardcode for every brew alike.
|
# delay [s] - see derive_plant_params(). Defaults match the generic
|
||||||
'L': 0.2,
|
# values brewpi.py used to hardcode for every brew alike.
|
||||||
'Td': 30,
|
'L': 0.2,
|
||||||
# Initial grain_mass/water_mass - what's actually in the pot before
|
'Td': 30,
|
||||||
# the brew starts (e.g. water added but no malt yet), as opposed to
|
# Initial grain_mass/water_mass - what's actually in the pot before
|
||||||
# default.step's grain_mass/water_mass, which is just inert template
|
# the brew starts (e.g. water added but no malt yet), as opposed to
|
||||||
# filler for steps that don't override it. Lets a caller derive
|
# default.step's pot.grain_mass/pot.water_mass, which is just inert
|
||||||
# starting plant params directly (see SudTask.recv()'s Load handler)
|
# template filler for steps that don't override it. Lets a caller
|
||||||
# without having to parse the first step out of the schedule.
|
# derive starting plant params directly (see SudTask.recv()'s Load
|
||||||
'grain_mass': 0,
|
# handler) without having to parse the first step out of the schedule.
|
||||||
'water_mass': 0,
|
'grain_mass': 0,
|
||||||
|
'water_mass': 0,
|
||||||
|
},
|
||||||
'steps': [],
|
'steps': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,12 +110,13 @@ class Sud(AttributeChange):
|
|||||||
step_defaults = data.get('default', {}).get('step', {})
|
step_defaults = data.get('default', {}).get('step', {})
|
||||||
schedule = [_build_step(i + 1, step_defaults, raw) for i, raw in enumerate(data['steps'])]
|
schedule = [_build_step(i + 1, step_defaults, raw) for i, raw in enumerate(data['steps'])]
|
||||||
|
|
||||||
pot_mass = data.get('pot_mass', 0)
|
pot_data = data.get('pot', {})
|
||||||
pot_material = data.get('pot_material')
|
pot_mass = pot_data.get('mass', 0)
|
||||||
L = data.get('L', EMPTY_SUD['L'])
|
pot_material = pot_data.get('material')
|
||||||
Td = data.get('Td', EMPTY_SUD['Td'])
|
L = pot_data.get('L', EMPTY_SUD['pot']['L'])
|
||||||
grain_mass = data.get('grain_mass', EMPTY_SUD['grain_mass'])
|
Td = pot_data.get('Td', EMPTY_SUD['pot']['Td'])
|
||||||
water_mass = data.get('water_mass', EMPTY_SUD['water_mass'])
|
grain_mass = pot_data.get('grain_mass', EMPTY_SUD['pot']['grain_mass'])
|
||||||
|
water_mass = pot_data.get('water_mass', EMPTY_SUD['pot']['water_mass'])
|
||||||
|
|
||||||
return name, description, schedule, pot_mass, pot_material, L, Td, grain_mass, water_mass
|
return name, description, schedule, pot_mass, pot_material, L, Td, grain_mass, water_mass
|
||||||
|
|
||||||
|
|||||||
@@ -156,7 +156,8 @@ class SudForecastEstimator:
|
|||||||
# re-assigns self.step to retrigger it) - only the *first* call
|
# re-assigns self.step to retrigger it) - only the *first* call
|
||||||
# for a given index is its actual start.
|
# for a given index is its actual start.
|
||||||
step_starts.setdefault(sud.index, t[-1])
|
step_starts.setdefault(sud.index, t[-1])
|
||||||
params = sud.derive_plant_params(step.get('grain_mass', 0), step.get('water_mass', 0))
|
pot = step.get('pot', {})
|
||||||
|
params = sud.derive_plant_params(pot.get('grain_mass', 0), pot.get('water_mass', 0))
|
||||||
pot.set_plant_params(params)
|
pot.set_plant_params(params)
|
||||||
tc.set_model_plant_params(params)
|
tc.set_model_plant_params(params)
|
||||||
if sud.state == SudState.RAMPING and step['temperature'] is not None:
|
if sud.state == SudState.RAMPING and step['temperature'] is not None:
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ if __name__ == '__main__':
|
|||||||
sud.load(json.load(f))
|
sud.load(json.load(f))
|
||||||
|
|
||||||
first_step = sud.schedule[0]
|
first_step = sud.schedule[0]
|
||||||
plant_params = sud.derive_plant_params(first_step['grain_mass'], first_step['water_mass'])
|
first_pot = first_step.get('pot', {})
|
||||||
|
plant_params = sud.derive_plant_params(first_pot.get('grain_mass', 0), first_pot.get('water_mass', 0))
|
||||||
|
|
||||||
ctrl = TempController(dt)
|
ctrl = TempController(dt)
|
||||||
ctrl.set_params(ctrl_params)
|
ctrl.set_params(ctrl_params)
|
||||||
@@ -74,7 +75,8 @@ if __name__ == '__main__':
|
|||||||
stirrer.set_speed(speed)
|
stirrer.set_speed(speed)
|
||||||
|
|
||||||
def apply_plant_params(step):
|
def apply_plant_params(step):
|
||||||
params = sud.derive_plant_params(step['grain_mass'], step['water_mass'])
|
pot = step.get('pot', {})
|
||||||
|
params = sud.derive_plant_params(pot.get('grain_mass', 0), pot.get('water_mass', 0))
|
||||||
plant.set_plant_params(params)
|
plant.set_plant_params(params)
|
||||||
ctrl.set_model_plant_params(params)
|
ctrl.set_model_plant_params(params)
|
||||||
|
|
||||||
|
|||||||
+12
-8
@@ -1,19 +1,23 @@
|
|||||||
{
|
{
|
||||||
"Name": "GuiTest",
|
"Name": "GuiTest",
|
||||||
"Description": "Small, fast schedule for exercising the GUI (load/start/pause/stop/restart) without waiting through a real brew",
|
"Description": "Small, fast schedule for exercising the GUI (load/start/pause/stop/restart) without waiting through a real brew",
|
||||||
"pot_mass": 5.96,
|
"pot": {
|
||||||
"pot_material": "Edelstahl 18/10",
|
"mass": 5.96,
|
||||||
"L": 0.2,
|
"material": "Edelstahl 18/10",
|
||||||
"Td": 30,
|
"L": 0.2,
|
||||||
"grain_mass": 5.21,
|
"Td": 30,
|
||||||
"water_mass": 22,
|
"grain_mass": 5.21,
|
||||||
|
"water_mass": 22
|
||||||
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"step": {
|
"step": {
|
||||||
"descr": "Put description here",
|
"descr": "Put description here",
|
||||||
"user_message": "Put user message here",
|
"user_message": "Put user message here",
|
||||||
"user_wait_for_continue": false,
|
"user_wait_for_continue": false,
|
||||||
"grain_mass": 5.21,
|
"pot": {
|
||||||
"water_mass": 22,
|
"grain_mass": 5.21,
|
||||||
|
"water_mass": 22
|
||||||
|
},
|
||||||
"temperature": 0,
|
"temperature": 0,
|
||||||
"ramp": {
|
"ramp": {
|
||||||
"rate": 5.0,
|
"rate": 5.0,
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
{
|
{
|
||||||
"Name": "GuiTest - No User",
|
"Name": "GuiTest - No User",
|
||||||
"Description": "Small, fast schedule for exercising the GUI (load/start/pause/stop/restart) without waiting through a real brew",
|
"Description": "Small, fast schedule for exercising the GUI (load/start/pause/stop/restart) without waiting through a real brew",
|
||||||
"pot_mass": 5.96,
|
"pot": {
|
||||||
"pot_material": "Edelstahl 18/10",
|
"mass": 5.96,
|
||||||
"L": 0.2,
|
"material": "Edelstahl 18/10",
|
||||||
"Td": 30,
|
"L": 0.2,
|
||||||
"grain_mass": 5.21,
|
"Td": 30,
|
||||||
"water_mass": 22,
|
"grain_mass": 5.21,
|
||||||
|
"water_mass": 22
|
||||||
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"step": {
|
"step": {
|
||||||
"descr": "Put description here",
|
"descr": "Put description here",
|
||||||
"user_message": "Put user message here",
|
"user_message": "Put user message here",
|
||||||
"user_wait_for_continue": false,
|
"user_wait_for_continue": false,
|
||||||
"grain_mass": 5.21,
|
"pot": {
|
||||||
"water_mass": 22,
|
"grain_mass": 5.21,
|
||||||
|
"water_mass": 22
|
||||||
|
},
|
||||||
"temperature": 0,
|
"temperature": 0,
|
||||||
"ramp": {
|
"ramp": {
|
||||||
"rate": 5.0,
|
"rate": 5.0,
|
||||||
|
|||||||
+18
-10
@@ -1,19 +1,23 @@
|
|||||||
{
|
{
|
||||||
"Name": "Sud-0010",
|
"Name": "Sud-0010",
|
||||||
"Description": "Rotfraenkisch, Dunkles Lager",
|
"Description": "Rotfraenkisch, Dunkles Lager",
|
||||||
"pot_mass": 5.96,
|
"pot": {
|
||||||
"pot_material": "Edelstahl 18/10",
|
"mass": 5.96,
|
||||||
"L": 0.2,
|
"material": "Edelstahl 18/10",
|
||||||
"Td": 30,
|
"L": 0.2,
|
||||||
"grain_mass": 0,
|
"Td": 30,
|
||||||
"water_mass": 22,
|
"grain_mass": 0,
|
||||||
|
"water_mass": 22
|
||||||
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"step": {
|
"step": {
|
||||||
"descr": "Put description here",
|
"descr": "Put description here",
|
||||||
"user_message": "Put user message here",
|
"user_message": "Put user message here",
|
||||||
"user_wait_for_continue": false,
|
"user_wait_for_continue": false,
|
||||||
"grain_mass": 5.21,
|
"pot": {
|
||||||
"water_mass": 22,
|
"grain_mass": 5.21,
|
||||||
|
"water_mass": 22
|
||||||
|
},
|
||||||
"temperature": 0,
|
"temperature": 0,
|
||||||
"ramp": {
|
"ramp": {
|
||||||
"rate": 1.0,
|
"rate": 1.0,
|
||||||
@@ -35,7 +39,9 @@
|
|||||||
},
|
},
|
||||||
"steps": [
|
"steps": [
|
||||||
{
|
{
|
||||||
"grain_mass": 0,
|
"pot": {
|
||||||
|
"grain_mass": 0
|
||||||
|
},
|
||||||
"descr": "Heizen für Einmaischen",
|
"descr": "Heizen für Einmaischen",
|
||||||
"temperature": 57,
|
"temperature": 57,
|
||||||
"ramp": {
|
"ramp": {
|
||||||
@@ -91,7 +97,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"water_mass": 20,
|
"pot": {
|
||||||
|
"water_mass": 20
|
||||||
|
},
|
||||||
"descr": "Abmaischen",
|
"descr": "Abmaischen",
|
||||||
"user_message": "Quittieren zum Abmaischen",
|
"user_message": "Quittieren zum Abmaischen",
|
||||||
"user_wait_for_continue": true,
|
"user_wait_for_continue": true,
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
{
|
{
|
||||||
"Name": "Sud-0010",
|
"Name": "Sud-0010",
|
||||||
"Description": "Rotfraenkisch, Dunkles Lager",
|
"Description": "Rotfraenkisch, Dunkles Lager",
|
||||||
"pot_mass": 5.96,
|
"pot": {
|
||||||
"pot_material": "Edelstahl 18/10",
|
"mass": 5.96,
|
||||||
"L": 0.2,
|
"material": "Edelstahl 18/10",
|
||||||
"Td": 30,
|
"L": 0.2,
|
||||||
"grain_mass": 0,
|
"Td": 30,
|
||||||
"water_mass": 22,
|
"grain_mass": 0,
|
||||||
|
"water_mass": 22
|
||||||
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"step": {
|
"step": {
|
||||||
"descr": "Put description here",
|
"descr": "Put description here",
|
||||||
"user_message": "Put user message here",
|
"user_message": "Put user message here",
|
||||||
"user_wait_for_continue": false,
|
"user_wait_for_continue": false,
|
||||||
"grain_mass": 5.21,
|
"pot": {
|
||||||
"water_mass": 22,
|
"grain_mass": 5.21,
|
||||||
|
"water_mass": 22
|
||||||
|
},
|
||||||
"ramp": {
|
"ramp": {
|
||||||
"rate": 1.0,
|
"rate": 1.0,
|
||||||
"temp": 0,
|
"temp": 0,
|
||||||
@@ -35,8 +39,10 @@
|
|||||||
},
|
},
|
||||||
"steps": [
|
"steps": [
|
||||||
{
|
{
|
||||||
"grain_mass": 0,
|
"pot": {
|
||||||
"descr": "Heizen f\u00fcr Einmaischen",
|
"grain_mass": 0
|
||||||
|
},
|
||||||
|
"descr": "Heizen für Einmaischen",
|
||||||
"ramp": {
|
"ramp": {
|
||||||
"rate": 1.8,
|
"rate": 1.8,
|
||||||
"temp": 57,
|
"temp": 57,
|
||||||
@@ -46,8 +52,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"descr": "Einmaischen einf\u00fcllen",
|
"descr": "Einmaischen einfüllen",
|
||||||
"user_message": "Bitte Malz einf\u00fcllen und best\u00e4tigen",
|
"user_message": "Bitte Malz einfüllen und bestätigen",
|
||||||
"user_wait_for_continue": true,
|
"user_wait_for_continue": true,
|
||||||
"hold": {
|
"hold": {
|
||||||
"stirrer": {
|
"stirrer": {
|
||||||
@@ -97,7 +103,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"water_mass": 20,
|
"pot": {
|
||||||
|
"water_mass": 20
|
||||||
|
},
|
||||||
"descr": "Heizen Abmaischen",
|
"descr": "Heizen Abmaischen",
|
||||||
"ramp": {
|
"ramp": {
|
||||||
"rate": 1.0,
|
"rate": 1.0,
|
||||||
@@ -105,7 +113,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"water_mass": 20,
|
"pot": {
|
||||||
|
"water_mass": 20
|
||||||
|
},
|
||||||
"descr": "Halten Abmaischen",
|
"descr": "Halten Abmaischen",
|
||||||
"user_message": "Quittieren zum Abmaischen",
|
"user_message": "Quittieren zum Abmaischen",
|
||||||
"user_wait_for_continue": true,
|
"user_wait_for_continue": true,
|
||||||
@@ -117,4 +127,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-11
@@ -1,19 +1,23 @@
|
|||||||
{
|
{
|
||||||
"Name": "Sud-0020",
|
"Name": "Sud-0020",
|
||||||
"Description": "Bavarian, Dunkles (Todo: Pot temperature drop at malt fill in)",
|
"Description": "Bavarian, Dunkles (Todo: Pot temperature drop at malt fill in)",
|
||||||
"pot_mass": 5.96,
|
"pot": {
|
||||||
"pot_material": "Edelstahl 18/10",
|
"mass": 5.96,
|
||||||
"L": 0.2,
|
"material": "Edelstahl 18/10",
|
||||||
"Td": 30,
|
"L": 0.2,
|
||||||
"grain_mass": 0,
|
"Td": 30,
|
||||||
"water_mass": 10,
|
"grain_mass": 0,
|
||||||
|
"water_mass": 10
|
||||||
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"step": {
|
"step": {
|
||||||
"descr": "Put description here",
|
"descr": "Put description here",
|
||||||
"user_message": "Put user message here",
|
"user_message": "Put user message here",
|
||||||
"user_wait_for_continue": false,
|
"user_wait_for_continue": false,
|
||||||
"grain_mass": 2.51,
|
"pot": {
|
||||||
"water_mass": 10,
|
"grain_mass": 2.51,
|
||||||
|
"water_mass": 10
|
||||||
|
},
|
||||||
"temperature": 0,
|
"temperature": 0,
|
||||||
"ramp": {
|
"ramp": {
|
||||||
"rate": 1.0,
|
"rate": 1.0,
|
||||||
@@ -35,7 +39,9 @@
|
|||||||
},
|
},
|
||||||
"steps": [
|
"steps": [
|
||||||
{
|
{
|
||||||
"grain_mass": 0,
|
"pot": {
|
||||||
|
"grain_mass": 0
|
||||||
|
},
|
||||||
"descr": "Heizen für Einmaischen",
|
"descr": "Heizen für Einmaischen",
|
||||||
"temperature": 57,
|
"temperature": 57,
|
||||||
"ramp": {
|
"ramp": {
|
||||||
@@ -57,7 +63,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"descr": "Einmaischen Rast",
|
"descr": "Einmaischen Rast",
|
||||||
"temperature": 55 ,
|
"temperature": 55,
|
||||||
"hold": {
|
"hold": {
|
||||||
"duration": 20
|
"duration": 20
|
||||||
}
|
}
|
||||||
@@ -92,7 +98,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"water_mass": 20,
|
"pot": {
|
||||||
|
"water_mass": 20
|
||||||
|
},
|
||||||
"descr": "Abmaischen",
|
"descr": "Abmaischen",
|
||||||
"user_message": "Quittieren zum Abmaischen",
|
"user_message": "Quittieren zum Abmaischen",
|
||||||
"user_wait_for_continue": true,
|
"user_wait_for_continue": true,
|
||||||
|
|||||||
+10
-7
@@ -164,7 +164,8 @@ class SudTask(ATask):
|
|||||||
phase = ramp if ramping else hold
|
phase = ramp if ramping else hold
|
||||||
|
|
||||||
if step is not None:
|
if step is not None:
|
||||||
self.apply_plant_params(step.get('grain_mass', 0), step.get('water_mass', 0))
|
pot = step.get('pot', {})
|
||||||
|
self.apply_plant_params(pot.get('grain_mass', 0), pot.get('water_mass', 0))
|
||||||
if ramping and step['temperature'] is not None:
|
if ramping and step['temperature'] is not None:
|
||||||
self.tc.set_theta_soll(step['temperature'])
|
self.tc.set_theta_soll(step['temperature'])
|
||||||
self.tc.set_heatrate_soll(ramp['rate'])
|
self.tc.set_heatrate_soll(ramp['rate'])
|
||||||
@@ -383,12 +384,14 @@ class SudTask(ATask):
|
|||||||
doc = {
|
doc = {
|
||||||
'Name': self.sud.name,
|
'Name': self.sud.name,
|
||||||
'Description': self.sud.description,
|
'Description': self.sud.description,
|
||||||
'pot_mass': self.sud.pot_mass,
|
'pot': {
|
||||||
'pot_material': self.sud.pot_material,
|
'mass': self.sud.pot_mass,
|
||||||
'L': self.sud.L,
|
'material': self.sud.pot_material,
|
||||||
'Td': self.sud.Td,
|
'L': self.sud.L,
|
||||||
'grain_mass': self.sud.grain_mass,
|
'Td': self.sud.Td,
|
||||||
'water_mass': self.sud.water_mass,
|
'grain_mass': self.sud.grain_mass,
|
||||||
|
'water_mass': self.sud.water_mass,
|
||||||
|
},
|
||||||
'steps': schedule[index:],
|
'steps': schedule[index:],
|
||||||
}
|
}
|
||||||
start_theta = self.tc.get_theta_ist()
|
start_theta = self.tc.get_theta_ist()
|
||||||
|
|||||||
Reference in New Issue
Block a user