fix(gui): fire mount auto-centering on the first idle status tick

The "apply after mounting" checkbox clicked Auto Centering as soon as the
status poll showed a new sample id. /sample/mount still holds the hw lock
at that point, /alc/center_loop takes the same lock non-blocking, and the
resulting BeamlineBusyException is a quiet error the GUI swallows. So the
automatic click never did anything; only a later manual click worked.

Arm on the sample-id change and click on the first status tick with
busy=False, which is exactly what the user did by hand. Skip arming while
queue automation runs: it mounts and centers on its own and frees the lock
between /scan/auto calls, so a GUI centering in that gap would steal the
lock from the next scan. Pause during a scan's mount phase can still arm
that one sample, but Pause ends in an unmount, so the window is a ms-wide
lock gap sampled at 2 Hz; accepted, noted in the code.

Supersedes the three-commit approach on
fix/auto-center-after-mount-wait-idle (PR 243).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-29 16:51:07 +02:00
committed by duan_j
co-authored by Claude Fable 5.1
parent ab34da2595
commit 3409d01efd
3 changed files with 69 additions and 6 deletions
@@ -353,6 +353,39 @@ def test_auto_center_fires_on_mount_only_when_armed(settings_panel):
assert len(clicks) == 3
def test_auto_center_waits_for_hw_lock_after_mount(settings_panel):
clicks = []
settings_panel.find_tip.clicked.connect(lambda: clicks.append(1))
settings_panel.auto_center_after_mount.setChecked(True)
settings_panel._track_sample(None)
# The mount still holds the hw lock when the sample id first shows up:
# /alc/center_loop would be refused, so no click yet.
settings_panel._track_sample(1, busy=True)
settings_panel._track_sample(1, busy=True)
assert not clicks
# First idle tick fires it, exactly once.
settings_panel._track_sample(1, busy=False)
settings_panel._track_sample(1, busy=False)
assert len(clicks) == 1
# Unmount while still pending drops the request.
settings_panel._track_sample(None)
settings_panel._track_sample(2, busy=True)
settings_panel._track_sample(None, busy=False)
assert len(clicks) == 1
# Queue automation mounts and centers on its own: never fire in its gaps.
settings_panel.set_automation_running(True)
settings_panel._track_sample(3, busy=True)
settings_panel._track_sample(3, busy=False)
assert len(clicks) == 1
settings_panel.set_automation_running(False)
settings_panel._track_sample(None)
settings_panel._track_sample(4, busy=False)
assert len(clicks) == 2
# ---------------------------------------------------------------------------
# Pending color while typing (movestate property, committed on Enter/focus-out)
# ---------------------------------------------------------------------------