Helios panel: refresh about every 0.65 s, and yield the port to the operator
Three things were making the panel feel slow, all of them waiting rather than working. The trailing quiet window is waited out once per query, so it set the pace of the whole sweep: at 50 ms that was 400 ms of a 590 ms poll spent listening to silence. A reply streams at the baud rate — ~1 ms between bytes, no measurable gap between its lines — and the deadline restarts on every line read, so 20 ms outlasts the gap it exists for twenty times over. A tail that still arrives late is caught by _discard_input(), which is what actually protects the next query. Replayed against the rig transcript, a sweep goes from 590 ms to 356 ms. The poll interval was 1 s on top of that, so a value could be 1.6 s stale. At 0.3 s the panel comes round about every 0.65 s. And a poll held the port for its whole sweep, so a button pressed during one waited for all eight queries. _work_pending() on the worker base lets a poll drop what is left as soon as the operator queues something: a click now waits ~135 ms for the register read in progress instead of the full sweep, and the rest is picked up next time round. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+15
-2
@@ -343,7 +343,9 @@ class HeliosWorker(PollingQueueWorker):
|
||||
qswitch_temp_updated = pyqtSignal(float)
|
||||
status_registers_updated = pyqtSignal(object, object, object) # LER, LCE, CCE (int|None)
|
||||
|
||||
POLL_INTERVAL_S = 1.0
|
||||
# A full sweep is eight queries, ~360 ms of port time. The interval is
|
||||
# the gap between sweeps, so the panel refreshes about every 0.65 s.
|
||||
POLL_INTERVAL_S = 0.3
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(poll_interval_s=self.POLL_INTERVAL_S)
|
||||
@@ -416,17 +418,26 @@ class HeliosWorker(PollingQueueWorker):
|
||||
|
||||
def _poll_once(self):
|
||||
"""Read the full status set. Individual reads are allowed to fail
|
||||
(a timed-out register shouldn't suppress the rest of the panel)."""
|
||||
(a timed-out register shouldn't suppress the rest of the panel).
|
||||
|
||||
Abandoned as soon as the operator queues something: the rest of the
|
||||
sweep is worth less than a button that responds now, and the next
|
||||
poll will pick it up.
|
||||
"""
|
||||
if not self._laser or not self._laser.is_connected:
|
||||
return
|
||||
try:
|
||||
self.status_registers_updated.emit(*self._laser.get_status_registers())
|
||||
except Exception:
|
||||
pass
|
||||
if self._work_pending():
|
||||
return
|
||||
try:
|
||||
self.enabled_updated.emit(self._laser.is_laser_enabled())
|
||||
except Exception:
|
||||
pass
|
||||
if self._work_pending():
|
||||
return
|
||||
# A failed LDS read must not leave the last good value on screen
|
||||
# looking live: that is indistinguishable from a setpoint that
|
||||
# refuses to move, which is the failure this panel exists to show.
|
||||
@@ -444,6 +455,8 @@ class HeliosWorker(PollingQueueWorker):
|
||||
(self._laser.get_power_stage_temp_c, self.pstage_temp_updated),
|
||||
(self._laser.get_qswitch_temp_c, self.qswitch_temp_updated),
|
||||
):
|
||||
if self._work_pending():
|
||||
return
|
||||
try:
|
||||
value = getter()
|
||||
if value is not None:
|
||||
|
||||
Reference in New Issue
Block a user