fix: embed dt and startup plant params in server log; open-loop replay_sim
server/brewpi.py: pass physics DT to ServerLogTask (not wall-clock DT_TASK) so logs carry the correct integration timestep; log startup plant params as the first PlantParams event so replay_sim can reconstruct them even when no Sud is ever loaded (previously fell back to hardcoded defaults, causing ~3°C temperature offset in replay). tasks/server_log.py: accept and embed dt in log JSON; switch log_plant_params() to wall-clock monotonic time (same base as sample t), since the elapsed argument was simulated time and not comparable. utils/replay_sim.py: switch to open-loop replay (plant driven by logged power_eff, not sim controller output); use PidFactory so Smith vs Normal controller type is honoured; set Td=0 in replay plant for Smith mode (logged temp_ist is already Smith-corrected); feed Smith predictor's internal model with real power; apply embedded PlantParams mid-run; prefer embedded dt over sample-based fallback; clamp sim power ≥ 0; replace meaningless sim-power trace in power subplot with power_set vs power_eff from the log. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+5
-2
@@ -16,20 +16,21 @@ class ServerLogTask(ATask):
|
||||
Sud state — useful for verifying controller and heater behaviour
|
||||
outside of a scheduled brew."""
|
||||
|
||||
def __init__(self, tc: APid, heater: AHeater, heater_task, interval, path='./logs', config=None):
|
||||
def __init__(self, tc: APid, heater: AHeater, heater_task, interval, path='./logs', config=None, dt=None):
|
||||
ATask.__init__(self, interval)
|
||||
self.tc = tc
|
||||
self.heater = heater
|
||||
self.heater_task = heater_task
|
||||
self.path = path
|
||||
self.config = config
|
||||
self.dt = dt
|
||||
self._samples = []
|
||||
self._param_events = []
|
||||
self._t0 = time.monotonic()
|
||||
self._run_id = time.strftime("%Y%m%dT%H%M%S", time.localtime())
|
||||
|
||||
def log_plant_params(self, elapsed, params):
|
||||
self._param_events.append({'t': elapsed, 'params': params})
|
||||
self._param_events.append({'t': time.monotonic() - self._t0, 'params': params})
|
||||
|
||||
async def on_process(self):
|
||||
while True:
|
||||
@@ -53,6 +54,8 @@ class ServerLogTask(ATask):
|
||||
filename = 'log_{}.json'.format(self._run_id)
|
||||
path = os.path.join(self.path, filename)
|
||||
log_data = {'Name': ''}
|
||||
if self.dt is not None:
|
||||
log_data['dt'] = self.dt
|
||||
if self.config is not None:
|
||||
log_data['Config'] = self.config
|
||||
if self._param_events:
|
||||
|
||||
Reference in New Issue
Block a user