fix(gui): wait for hw lock before auto-centering after mount
CI / lint (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Failing after 1m1s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m26s
CI / lint (pull_request) Successful in 1m35s
CI / test (3.12) (pull_request) Successful in 1m37s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m40s
CI / test-with-coverage (pull_request) Successful in 1m42s
CI / test (3.13) (pull_request) Successful in 1m45s
CI / test (3.14) (pull_request) Successful in 1m47s
CI / coverage-analysis (pull_request) Successful in 3s

The server publishes the new sample while the mount still holds the hw
lock, and /alc/center_loop takes that lock non-blocking, so the click
fired on the sample-id change was rejected as busy and never retried.
Arm on mount, fire on the first non-busy status tick instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
duan_jandClaude Opus 5.5 committed 2026-09-29 11:14:45 +02:00
1 parent 2e43e52547
commit 43054dc0e8
2 files changed
+39 -6

No files matched your search

@@ -195,6 +195,8 @@ class DataCollectionSettings(QFrame):
# startup must not trigger an auto-centering (hardware would move
# uninvited on every restart).
self._status_seen = False
# Set on mount, fired once the hw lock frees (see _track_sample).
self._center_after_mount_pending = False
self._tab_bar.currentChanged.connect(self._stack.setCurrentIndex)
self._tab_bar.currentChanged.connect(self._on_tab_changed)
@@ -242,23 +244,31 @@ class DataCollectionSettings(QFrame):
self.raster.update_daq_status(s)
self.screening.update_daq_status(s)
self.simple.update_daq_status(s)
self._track_sample(None if s.sample is None else s.sample.db_id)
self._track_sample(None if s.sample is None else s.sample.db_id, s.busy)
def _track_sample(self, db_id: int | None):
def _track_sample(self, db_id: int | None, busy: bool = False):
# Separate from update_daq_status so tests can drive mount detection
# without building a full DAQStatusModel.
if db_id is None:
# Forget on unmount so remounting the same sample counts as a
# fresh mount below.
self._sample_id = None
self._center_after_mount_pending = False
elif db_id != self._sample_id:
self._sample_id = db_id
self._tab_bar.setCurrentIndex(0)
if self._status_seen and self.auto_center_after_mount.isChecked():
# Reuse the button wiring (clicked -> daq.center_loop in
# main_window) instead of a second signal path.
self.find_tip.click()
self._center_after_mount_pending = (
self._status_seen and self.auto_center_after_mount.isChecked()
)
self._status_seen = True
# The server publishes the new sample while the mount still holds the
# hw lock, and /alc/center_loop refuses (non-blocking lock) while busy,
# so firing on the id change alone was silently rejected. Wait for idle.
if self._center_after_mount_pending and not busy:
self._center_after_mount_pending = False
# Reuse the button wiring (clicked -> daq.center_loop in
# main_window) instead of a second signal path.
self.find_tip.click()
@Slot(int)
def _on_tab_changed(self, idx: int):
@@ -353,6 +353,29 @@ def test_auto_center_fires_on_mount_only_when_armed(settings_panel):
assert len(clicks) == 3
def test_auto_center_waits_for_mount_to_release_hw_lock(settings_panel):
# Server shows the new sample while the mount still holds the hw lock;
# center_loop would be refused then, so the click must wait for idle.
clicks = []
settings_panel.find_tip.clicked.connect(lambda: clicks.append(1))
settings_panel.auto_center_after_mount.setChecked(True)
settings_panel._track_sample(1)
settings_panel._track_sample(None)
settings_panel._track_sample(2, busy=True)
settings_panel._track_sample(2, busy=True)
assert not clicks
settings_panel._track_sample(2, busy=False)
assert len(clicks) == 1
settings_panel._track_sample(2, busy=False)
assert len(clicks) == 1
# Unmount before the lock frees drops the pending centering.
settings_panel._track_sample(3, busy=True)
settings_panel._track_sample(None, busy=False)
assert len(clicks) == 1
# ---------------------------------------------------------------------------
# Pending color while typing (movestate property, committed on Enter/focus-out)
# ---------------------------------------------------------------------------