sude/sud_0010.json moved from a list of combined ramp+hold+wait
"Rasten" with global stirrer constants to a flat "schedule" list of
explicit {"type": "heat"|"hold", ...} steps, each carrying its own
stirrer_speed/stirrer_time_on/stirrer_time_off and an optional
user_wait_for_continue (+ user_message).
- components/sud.py: Sud now tracks the current `step` instead of
`rast`; "heat" steps enter RAMPING (advanced externally via
temp_reached()), "hold" steps enter HOLDING and count down
`duration` minutes (0 if omitted). Either step type can pause in
WAIT_USER via user_wait_for_continue, surfaced through the new
user_message attribute.
- tasks/sud.py: SudTask only pushes theta_soll/heatrate_soll on
"heat" steps, converts each step's stirrer_time_on/off into a
duty_cycle/cycle_time on every step change (replacing the old
state-based RAMPING/HOLDING stirrer switching), and broadcasts
user_message changes. Stirring is now fully schedule-driven instead
of implicitly stopped on WAIT_USER/DONE (DONE still stops it, since
there's no step left to read settings from).
- scripts/demos/sud/demo_sud.py: mirrors the same step-driven wiring.
- README's Mash schedules section rewritten for the new schema.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
73 lines
1.5 KiB
Python
73 lines
1.5 KiB
Python
import json
|
|
from enum import Enum
|
|
from utils.value import AttributeChange
|
|
|
|
|
|
class SudState(Enum):
|
|
IDLE = 0
|
|
RAMPING = 1
|
|
HOLDING = 2
|
|
WAIT_USER = 3
|
|
DONE = 4
|
|
|
|
|
|
class Sud(AttributeChange):
|
|
def __init__(self, path):
|
|
AttributeChange.__init__(self)
|
|
with open(path) as f:
|
|
data = json.load(f)
|
|
|
|
self.name = data.get('Name', '')
|
|
self.description = data.get('Description', '')
|
|
self.schedule = data['schedule']
|
|
|
|
self.index = -1
|
|
self.hold_remaining = 0.0
|
|
self.state = SudState.IDLE
|
|
self.step = None
|
|
self.user_message = None
|
|
|
|
def start(self):
|
|
if self.state != SudState.IDLE:
|
|
return
|
|
self.index = -1
|
|
self._advance()
|
|
|
|
def confirm(self):
|
|
if self.state == SudState.WAIT_USER:
|
|
self._advance()
|
|
|
|
def temp_reached(self):
|
|
if self.state == SudState.RAMPING:
|
|
self._finish_step()
|
|
|
|
def tick(self, dt):
|
|
if self.state == SudState.HOLDING:
|
|
self.hold_remaining -= dt
|
|
if self.hold_remaining <= 0:
|
|
self._finish_step()
|
|
|
|
def _advance(self):
|
|
self.index += 1
|
|
if self.index >= len(self.schedule):
|
|
self.state = SudState.DONE
|
|
self.user_message = None
|
|
self.step = None
|
|
return
|
|
|
|
next_step = self.schedule[self.index]
|
|
if next_step['type'] == 'heat':
|
|
self.state = SudState.RAMPING
|
|
else:
|
|
self.hold_remaining = next_step.get('duration', 0) * 60.0
|
|
self.state = SudState.HOLDING
|
|
|
|
self.user_message = next_step.get('user_message')
|
|
self.step = next_step
|
|
|
|
def _finish_step(self):
|
|
if self.step.get('user_wait_for_continue', False):
|
|
self.state = SudState.WAIT_USER
|
|
else:
|
|
self._advance()
|