diff --git a/superxas_bec/devices/falcon.py b/superxas_bec/devices/falcon.py index 1c12169..8bdd51e 100644 --- a/superxas_bec/devices/falcon.py +++ b/superxas_bec/devices/falcon.py @@ -8,6 +8,7 @@ from bec_lib.logger import bec_logger from ophyd import Component as Cpt from ophyd import DeviceStatus, Kind, Signal, StatusBase from ophyd.status import SubscriptionStatus +from ophyd_devices import CompareStatus, StatusBase, TransitionStatus from ophyd_devices.devices.dxp import EpicsDXPFalcon, EpicsMCARecord, Falcon from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase @@ -141,7 +142,7 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): Default values for signals should be set here. """ - def on_stage(self) -> DeviceStatus | StatusBase | None: + def on_stage(self) -> CompareStatus: """ Called while staging the device. @@ -149,26 +150,16 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): """ self.collect_mode.set(0).wait() self.preset_real_time.set(0).wait() + status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE, timeout=self._pv_timeout) self.stop_all.put(1) - if ( - self.wait_for_condition( - lambda: self.acquiring.get() == FalconAcquiringStatus.DONE, timeout=self._pv_timeout - ) - is False - ): - raise TimeoutError("Timeout on Falcon stage") + return status - def on_unstage(self) -> DeviceStatus | StatusBase | None: + def on_unstage(self) -> CompareStatus: """Called while unstaging the device.""" self.stop_all.put(1) self.erase_all.put(1) - if ( - self.wait_for_condition( - lambda: self.acquiring.get() == FalconAcquiringStatus.DONE, timeout=self._pv_timeout - ) - is False - ): - raise TimeoutError("Timeout on Falcon unstage") + status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE, timeout=self._pv_timeout) + return status def on_pre_scan(self) -> DeviceStatus | StatusBase | None: """Called right before the scan starts on all devices automatically.""" @@ -186,19 +177,18 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): """Called when the device is stopped.""" self.stop_all.put(1) - def _stop_erase_and_wait_for_acquiring(self) -> DeviceStatus: + def _stop_erase_and_wait_for_acquiring(self) -> TransitionStatus: """Method called from the Trigger card to reset counts on the Falcon""" if self.acquiring.get() != FalconAcquiringStatus.DONE: self.stop_all.put(1) - def _check_acquiriting(*, old_value, value, **kwargs): - if old_value == FalconAcquiringStatus.DONE and value == FalconAcquiringStatus.ACQUIRING: - return True - return False - - status = SubscriptionStatus(self.acquiring, _check_acquiriting) - - logger.info("Triggering Falcon") + # TODO in case this fails, it could be that DONE is not emitted as it is only visible + # for a short time, below the update frequency of the EPICS acquiring PV (100ms). In this case, + # add here a CompareStatus, waiting for Done to appear before sending the erase command. + status = TransitionStatus( + self.acquiring, + transitions=[FalconAcquiringStatus.DONE, FalconAcquiringStatus.ACQUIRING], + ) self.erase_start.put(1) return status diff --git a/superxas_bec/devices/timepix/timepix_fly_client/timepix_fly_client.py b/superxas_bec/devices/timepix/timepix_fly_client/timepix_fly_client.py index e45c4aa..371c957 100644 --- a/superxas_bec/devices/timepix/timepix_fly_client/timepix_fly_client.py +++ b/superxas_bec/devices/timepix/timepix_fly_client/timepix_fly_client.py @@ -260,7 +260,7 @@ class TimepixFlyClient: or exception, respectively and removed from the list of callbacks. """ status = self._status - logger.warning(f"Running status callbacks for status: {status.value}") + logger.debug(f"Running status callbacks for status: {status.value}") callback_ids = list(self._status_callbacks.keys()) for cb_id in callback_ids: dev_status, success, error = self._status_callbacks[cb_id] diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index 21e7c06..29aede6 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -1,16 +1,16 @@ """SuperXAS Trigger Device""" -from bec_lib.logger import bec_logger -from ophyd import Component as Cpt -from ophyd import Device, DeviceStatus, EpicsSignal, EpicsSignalRO, Kind, StatusBase - -logger = bec_logger.logger - import enum from bec_lib.devicemanager import ScanInfo +from bec_lib.logger import bec_logger +from ophyd import Component as Cpt +from ophyd import Device, DeviceStatus, EpicsSignal, EpicsSignalRO, Kind, StatusBase +from ophyd_devices import TransitionStatus from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase +logger = bec_logger.logger + class ContinuousSamplingMode(int, enum.Enum): """Options for start_csmpl signal""" @@ -107,20 +107,10 @@ class Trigger(PSIDeviceBase, TriggerControl): status = falcon._stop_erase_and_wait_for_acquiring() status.wait() - started = False - - def _sampling_done(): - nonlocal started - if not started and self.smpl_done.get() == SamplingDone.RUNNING: - started = True - return False - if started and self.smpl_done.get() == SamplingDone.DONE: - return True - - return self.smpl_done.get() == SamplingDone.DONE - + status = TransitionStatus( + self.smpl_done, transitions=[SamplingDone.DONE, SamplingDone.RUNNING, SamplingDone.DONE] + ) self.smpl.put(1) - status = self.task_handler.submit_task(_sampling_done, run=True) return status def on_complete(self) -> DeviceStatus | StatusBase | None: diff --git a/tests/tests_devices/test_devices_falcon.py b/tests/tests_devices/test_devices_falcon.py index edea7f0..e86015e 100644 --- a/tests/tests_devices/test_devices_falcon.py +++ b/tests/tests_devices/test_devices_falcon.py @@ -5,6 +5,7 @@ from unittest import mock import ophyd import pytest +from ophyd.utils.errors import StatusTimeoutError from ophyd_devices.tests.utils import MockPV, patch_dual_pvs from superxas_bec.devices.falcon import FalconAcquiringStatus, FalconSuperXAS @@ -50,8 +51,9 @@ def test_devices_falcon_stage(falcon): # Should timeout falcon.acquiring.put(FalconAcquiringStatus.ACQUIRING) falcon._pv_timeout = 0.1 - with pytest.raises(TimeoutError): - falcon.on_stage() + with pytest.raises(StatusTimeoutError): + status = falcon.on_stage() + status.wait(timeout=2) def test_devices_falcon_unstage(falcon): @@ -67,8 +69,9 @@ def test_devices_falcon_unstage(falcon): # Should timeout falcon.acquiring.put(FalconAcquiringStatus.ACQUIRING) falcon._pv_timeout = 0.1 - with pytest.raises(TimeoutError): - falcon.on_unstage() + with pytest.raises(StatusTimeoutError): + status = falcon.on_unstage() + status.wait(timeout=2) def test_devices_falcon_stop(falcon): diff --git a/tests/tests_devices/test_devices_trigger.py b/tests/tests_devices/test_devices_trigger.py index 6da34ea..cfd27de 100644 --- a/tests/tests_devices/test_devices_trigger.py +++ b/tests/tests_devices/test_devices_trigger.py @@ -9,7 +9,7 @@ from bec_server.device_server.tests.utils import DMMock from ophyd import DeviceStatus from ophyd_devices.tests.utils import MockPV, patch_dual_pvs -from superxas_bec.devices.trigger import ContinuousSamplingMode, Trigger +from superxas_bec.devices.trigger import ContinuousSamplingMode, SamplingDone, Trigger # pylint: disable=protected-access # pylint: disable=redefined-outer-name @@ -94,13 +94,19 @@ def test_devices_trigger_trigger(trigger): trigger_status = DeviceStatus(device=trigger) trigger_status.set_finished() - with mock.patch.object( - trigger.task_handler, "submit_task", return_value=trigger_status - ) as mock_submit: - status = trigger.trigger() - assert falcon._stop_erase_and_wait_for_acquiring.call_count == 1 - assert trigger.smpl.get() == 1 # smpl called with 1 - # TODO check that the task_handler is called with the correct function - # This is currently not easily testable - assert mock_submit.call_count == 1 - assert trigger_status == status + trigger.smpl_done._read_pv.mock_data = SamplingDone.DONE + status = trigger.trigger() + assert falcon._stop_erase_and_wait_for_acquiring.call_count == 1 + assert trigger.smpl.get() == 1 # smpl called with 1 + assert status.done is False + trigger.smpl_done._read_pv.mock_data = SamplingDone.RUNNING + assert status.done is False + trigger.smpl_done._read_pv.mock_data = SamplingDone.DONE + status.wait(timeout=2) + assert status.done is True + assert status.success is True + + # # TODO check that the task_handler is called with the correct function + # # This is currently not easily testable + # assert mock_submit.call_count == 1 + # assert trigger_status == status