Move Sud step's target temperature from ramp.temp to step.temperature
A step's target temperature applies to the whole step (it's what a
ramp ramps to and what a hold then holds at), not just its ramp phase
- promote it from a nested "ramp": {"temp": ...} to a step-level
"temperature" field, defaulted like descr/grain_mass/etc. Updates all
consumers (Sud._build_step, SudTask, the GUI's forecast estimator,
demo_sud.py) and migrates sude/sud_0010.json and the README.
This commit is contained in:
@@ -130,17 +130,19 @@ environment (`$WORKON_HOME`/`$BREWPI_HOME`) on a Raspberry Pi-style deployment.
|
||||
|
||||
Files under `sude/` describe a brew's mash schedule ("Sud"): `pot_mass`,
|
||||
`pot_material` (fixed for the whole brew), and a `steps` list, each a ramp, a
|
||||
hold, or both:
|
||||
hold, or both, plus a step-level `temperature` (the target a ramp step ramps
|
||||
to, and what a hold step holds at):
|
||||
|
||||
- a ramp — `"ramp": {"rate": ..., "temp": ...}` — ramp to `temp` at `rate`
|
||||
- a ramp — `"ramp": {"rate": ...}` — ramp to `temperature` at `rate`
|
||||
(°C/min), and/or
|
||||
- a hold — `"hold": {"duration": ...}` — hold the current target for
|
||||
`duration` minutes (omit/`0` for an immediate step).
|
||||
|
||||
A step with both ramps to `temp` and then holds there for `duration` -
|
||||
useful for a mash rest ("ramp to 63°C, then hold 40 min") without needing two
|
||||
separate schedule entries. `user_wait_for_continue`/`user_message` (below)
|
||||
apply once the whole step is done, i.e. after the hold phase if there is one.
|
||||
A step with both ramps to `temperature` and then holds there for `duration`
|
||||
- useful for a mash rest ("ramp to 63°C, then hold 40 min") without needing
|
||||
two separate schedule entries. `user_wait_for_continue`/`user_message`
|
||||
(below) apply once the whole step is done, i.e. after the hold phase if
|
||||
there is one.
|
||||
|
||||
Both `ramp` and `hold` carry a `stirrer` block (`speed`, `interval_time`,
|
||||
`on_ratio`): `interval_time: 0` runs the stirrer continuously at `speed`;
|
||||
|
||||
@@ -139,7 +139,7 @@ class SudForecastPlot(FigureCanvasQTAgg):
|
||||
hold = step.get('hold')
|
||||
if ramp is not None:
|
||||
rate = ramp.get('rate', 0)
|
||||
target = ramp['temp']
|
||||
target = step['temperature']
|
||||
if rate > 0:
|
||||
t.append(t[-1] + abs(target - theta[-1]) / rate * 60.0)
|
||||
theta.append(target)
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ def _build_step(number, defaults, raw_step):
|
||||
ramp or a hold depending on which of those keys it specifies; the other
|
||||
is left out rather than synthesized from defaults."""
|
||||
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', 'grain_mass', 'water_mass', 'temperature'):
|
||||
step[key] = raw_step.get(key, defaults.get(key))
|
||||
for key in ('ramp', 'hold'):
|
||||
if key in raw_step:
|
||||
|
||||
@@ -81,15 +81,15 @@ if __name__ == '__main__':
|
||||
# A ramp step whose target is below the current temperature can
|
||||
# only be reached by passive cooling - force the heater off,
|
||||
# mirroring tasks/sud.py's SudTask.
|
||||
cooling = ramping and 'ramp' in step and step['ramp']['temp'] < ctrl.get_theta_ist() - TEMP_REACHED_TOLERANCE
|
||||
cooling = bool(ramping and 'ramp' in step and step['temperature'] < ctrl.get_theta_ist() - TEMP_REACHED_TOLERANCE)
|
||||
ctrl.set_cooling(cooling)
|
||||
if cooling:
|
||||
print(" -> cooling down to {}".format(step['ramp']['temp']))
|
||||
print(" -> cooling down to {}".format(step['temperature']))
|
||||
|
||||
print(f"Step {step['number']}: {step['descr']}")
|
||||
apply_plant_params(step)
|
||||
if ramping and 'ramp' in step:
|
||||
ctrl.set_theta_soll(step['ramp']['temp'])
|
||||
ctrl.set_theta_soll(step['temperature'])
|
||||
ctrl.set_heatrate_soll(step['ramp']['rate'])
|
||||
apply_stirrer(phase)
|
||||
|
||||
|
||||
+12
-12
@@ -10,9 +10,9 @@
|
||||
"user_wait_for_continue": false,
|
||||
"grain_mass": 5.21,
|
||||
"water_mass": 22,
|
||||
"temperature": 0,
|
||||
"ramp": {
|
||||
"rate": 1.0,
|
||||
"temp": 0,
|
||||
"stirrer": {
|
||||
"speed": 50,
|
||||
"interval_time": 0,
|
||||
@@ -32,18 +32,18 @@
|
||||
"steps": [
|
||||
{
|
||||
"grain_mass": 0,
|
||||
"descr": "Heizen f\u00fcr Einmaischen",
|
||||
"descr": "Heizen für Einmaischen",
|
||||
"temperature": 57,
|
||||
"ramp": {
|
||||
"rate": 1.8,
|
||||
"temp": 57,
|
||||
"stirrer": {
|
||||
"speed": 75
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"descr": "Einmaischen einf\u00fcllen",
|
||||
"user_message": "Bitte Malz einf\u00fcllen und best\u00e4tigen",
|
||||
"descr": "Einmaischen einfüllen",
|
||||
"user_message": "Bitte Malz einfüllen und bestätigen",
|
||||
"user_wait_for_continue": true,
|
||||
"hold": {
|
||||
"stirrer": {
|
||||
@@ -59,9 +59,9 @@
|
||||
},
|
||||
{
|
||||
"descr": "Glucose-Rast",
|
||||
"temperature": 63,
|
||||
"ramp": {
|
||||
"rate": 1.0,
|
||||
"temp": 63
|
||||
"rate": 1.0
|
||||
},
|
||||
"hold": {
|
||||
"duration": 40,
|
||||
@@ -74,9 +74,9 @@
|
||||
},
|
||||
{
|
||||
"descr": "Verzuckerungs-Rast",
|
||||
"temperature": 72,
|
||||
"ramp": {
|
||||
"rate": 1.0,
|
||||
"temp": 72
|
||||
"rate": 1.0
|
||||
},
|
||||
"hold": {
|
||||
"duration": 30,
|
||||
@@ -91,9 +91,9 @@
|
||||
"descr": "Abmaischen",
|
||||
"user_message": "Quittieren zum Abmaischen",
|
||||
"user_wait_for_continue": true,
|
||||
"temperature": 76,
|
||||
"ramp": {
|
||||
"rate": 1.0,
|
||||
"temp": 76
|
||||
"rate": 1.0
|
||||
},
|
||||
"hold": {
|
||||
"duration": 30,
|
||||
@@ -103,4 +103,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -69,13 +69,13 @@ class SudTask(ATask):
|
||||
# bool(...): get_theta_ist() can be a numpy.float64 (Pot's transport
|
||||
# delay line is a numpy array internally) - the '<' comparison would
|
||||
# then yield numpy.bool_, which json.dumps() can't serialize.
|
||||
cooling = bool(ramping and ramp is not None and ramp['temp'] < self.tc.get_theta_ist() - TEMP_REACHED_TOLERANCE)
|
||||
cooling = bool(ramping and ramp is not None and step['temperature'] < self.tc.get_theta_ist() - TEMP_REACHED_TOLERANCE)
|
||||
self.tc.set_cooling(cooling)
|
||||
|
||||
if step is not None:
|
||||
self.apply_plant_params(step)
|
||||
if ramping and ramp is not None:
|
||||
self.tc.set_theta_soll(ramp['temp'])
|
||||
self.tc.set_theta_soll(step['temperature'])
|
||||
self.tc.set_heatrate_soll(ramp['rate'])
|
||||
self.apply_stirrer(phase)
|
||||
|
||||
@@ -83,7 +83,7 @@ class SudTask(ATask):
|
||||
'Index': self.sud.index,
|
||||
'Type': 'ramp' if ramping and ramp is not None else 'hold' if hold is not None else None,
|
||||
'Descr': step.get('descr') if step else None,
|
||||
'Temp': ramp.get('temp') if ramp else None,
|
||||
'Temp': step.get('temperature') if ramp is not None else None,
|
||||
'Rate': ramp.get('rate') if ramp else None,
|
||||
'Duration': hold.get('duration') if (hold is not None and not ramping) else None,
|
||||
'WaitForUser': step.get('user_wait_for_continue', False) if step else None,
|
||||
|
||||
Reference in New Issue
Block a user