From 0866f1007cbf2b1584f70f866cda23e066d9c061 Mon Sep 17 00:00:00 2001 From: x12sa Date: Tue, 13 Oct 2026 13:54:47 +0200 Subject: [PATCH] fix(lamni): arm ddg1 for external triggering in lamni_fermat_scan Switch ddg1 to EXT_RISING_EDGE in pre_scan and back to SINGLE_SHOT in post_scan and on_exception, as flomni_fermat_scan already does. Without this the RT controller triggers never reached the detectors. Co-Authored-By: Claude Opus 5.5 --- csaxs_bec/scans/lamni_fermat_scan.py | 5 +++++ tests/tests_scans/test_lamni_fermat_scan.py | 25 +++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/csaxs_bec/scans/lamni_fermat_scan.py b/csaxs_bec/scans/lamni_fermat_scan.py index b95969ca..743d8f39 100644 --- a/csaxs_bec/scans/lamni_fermat_scan.py +++ b/csaxs_bec/scans/lamni_fermat_scan.py @@ -26,6 +26,7 @@ from bec_server.scan_server.errors import ScanAbortion from bec_server.scan_server.scans.scan_base import ScanBase, ScanType from bec_server.scan_server.scans.scan_modifier import scan_hook +from csaxs_bec.devices.epics.delay_generator_csaxs.delay_generator_csaxs import TRIGGERSOURCE from csaxs_bec.scans.fermat_spiral_utils import compute_fermat_spiral_n_max from csaxs_bec.scans.lamni_components import LamNIComponents @@ -206,6 +207,8 @@ class LamniFermatScan(ScanBase): devices, e.g. devices that have a short timeout. The pre-scan logic is typically implemented on the device itself. """ + # Prepare DDG1 to be triggered externally by the RT controller + self.dev.ddg1.set_trigger(TRIGGERSOURCE.EXT_RISING_EDGE.value) self.actions.pre_scan_all_devices() @scan_hook @@ -236,6 +239,7 @@ class LamniFermatScan(ScanBase): Post-scan steps to be executed after the main scan logic. """ self.actions.complete_all_devices() + self.dev.ddg1.set_trigger(TRIGGERSOURCE.SINGLE_SHOT.value) @scan_hook def unstage(self): @@ -257,6 +261,7 @@ class LamniFermatScan(ScanBase): This is a good place to implement any cleanup logic that needs to be executed in case of an exception, such as returning the devices to a safe state or moving the motors back to their starting position. """ + self.dev.ddg1.set_trigger(TRIGGERSOURCE.SINGLE_SHOT.value) ####################################################### ######### Helper methods for the scan logic ########### diff --git a/tests/tests_scans/test_lamni_fermat_scan.py b/tests/tests_scans/test_lamni_fermat_scan.py index f2da0c09..cf4fd4c0 100644 --- a/tests/tests_scans/test_lamni_fermat_scan.py +++ b/tests/tests_scans/test_lamni_fermat_scan.py @@ -95,3 +95,28 @@ def test_shift_and_center_are_mm_and_positions_are_um(): np.testing.assert_allclose(shifted_x - base, [[10.0, 0.0]] * len(base), atol=1e-9) np.testing.assert_allclose(shifted_y - base, [[0.0, 5.0]] * len(base), atol=1e-9) np.testing.assert_allclose(centered_x - base, [[10.0, 0.0]] * len(base), atol=1e-9) + + +def test_ddg1_armed_in_pre_scan_and_reset_after_scan(): + """ddg1 must be switched to external triggering before the devices are pre-scanned, + and back to single shot after the scan and on exceptions.""" + from csaxs_bec.devices.epics.delay_generator_csaxs.delay_generator_csaxs import TRIGGERSOURCE + + scan = mock.MagicMock() + manager = mock.MagicMock() + manager.attach_mock(scan.dev.ddg1.set_trigger, "set_trigger") + manager.attach_mock(scan.actions.pre_scan_all_devices, "pre_scan_all_devices") + + LamniFermatScan.pre_scan(scan) + assert manager.mock_calls == [ + mock.call.set_trigger(TRIGGERSOURCE.EXT_RISING_EDGE.value), + mock.call.pre_scan_all_devices(), + ] + + scan.dev.ddg1.set_trigger.reset_mock() + LamniFermatScan.post_scan(scan) + scan.dev.ddg1.set_trigger.assert_called_once_with(TRIGGERSOURCE.SINGLE_SHOT.value) + + scan.dev.ddg1.set_trigger.reset_mock() + LamniFermatScan.on_exception(scan, RuntimeError("boom")) + scan.dev.ddg1.set_trigger.assert_called_once_with(TRIGGERSOURCE.SINGLE_SHOT.value)