Scale the Sud forecast progress by the server's actual warp factor

SudTask now computes warp_factor = dt/interval (the same simulated-vs-
wall-clock split Pot/TempController/Stirrer already use internally)
and sends it once at startup as 'WarpFactor'. The GUI uses it to scale
real elapsed time into the forecast's simulated-schedule time axis,
so the progress line lines up with the actual sim speed instead of
assuming real time.

Also fixes SudTask.tick() to decrement hold_remaining by dt (simulated
seconds) instead of interval (wall-clock seconds) - it was the only
place in the sim using the wall-clock interval for something meant to
track simulated time, so hold steps were taking warp_factor times
longer in real time than their declared duration intends. Without
this fix the GUI's new warp-scaled progress line would race ahead of
the real server during holds.
This commit is contained in:
2026-06-21 00:27:14 +02:00
parent e1d0deb8d2
commit 189d1e24d2
3 changed files with 27 additions and 5 deletions
+15 -3
View File
@@ -11,12 +11,20 @@ TEMP_REACHED_TOLERANCE = 0.2
class SudTask(ATask):
def __init__(self, sud: Sud, tc: APid, stirrer: AStirrer, pot: APlant, interval, msg_handler: MsgIo):
def __init__(self, sud: Sud, tc: APid, stirrer: AStirrer, pot: APlant, dt, interval, msg_handler: MsgIo):
ATask.__init__(self, interval)
self.sud = sud
self.tc = tc
self.stirrer = stirrer
self.pot = pot
# Simulated seconds per tick, vs. interval's wall-clock seconds per
# tick - same dt/interval split Pot/TempController/Stirrer already
# use internally. Their warp-induced speedup falls out naturally
# from ticking real physics at simulated dt; Sud has no physics of
# its own, so hold_remaining must be ticked by dt explicitly to get
# the same speedup instead of running in real time.
self.dt = dt
self.warp_factor = dt / interval
self.msg_handler = msg_handler
msg_handler.set_recv_handler(self.recv)
@@ -104,7 +112,11 @@ class SudTask(ATask):
self.sud.set_on_changed('user_message', self.on_user_message_changed)
self.sud.set_on_changed('hold_remaining', ChangedFloat(self.on_hold_remaining_changed, prec=0).set)
asyncio.create_task(self.send({'Name': self.sud.name, 'Description': self.sud.description}))
asyncio.create_task(self.send({
'Name': self.sud.name,
'Description': self.sud.description,
'WarpFactor': self.warp_factor,
}))
while True:
if self.sud.state == SudState.RAMPING:
@@ -113,5 +125,5 @@ class SudTask(ATask):
# the previous step for one tick after a new target is pushed.
if abs(self.tc.get_theta_ist() - self.tc.get_theta_soll_set()) < TEMP_REACHED_TOLERANCE:
self.sud.temp_reached()
self.sud.tick(self.interval)
self.sud.tick(self.dt)
await asyncio.sleep(self.interval)