diff --git a/mx_bec/devices/beam_steering.py b/mx_bec/devices/beam_steering.py index f154a29..fd997be 100644 --- a/mx_bec/devices/beam_steering.py +++ b/mx_bec/devices/beam_steering.py @@ -2,14 +2,14 @@ from __future__ import annotations +import threading from typing import Literal, cast from bec_lib.logger import bec_logger from ophyd import Component as Cpt -from ophyd import Kind, Signal +from ophyd import DeviceStatus, Kind, Signal from ophyd_devices import EpicsMotorEC from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase -from ophyd_devices.utils.psi_device_base_utils import Status from mx_bec.devices.beam_profile import BeamProfile @@ -20,6 +20,21 @@ class BeamSteerer(PSIDeviceBase): """Based on the beam profile, step towards the centre by moving the focussing mirrors. Assumes everything is done at max zoom.""" + def __init__( + self, + *, + name: str, + prefix: str = "", + scan_info: ScanInfo | None = None, + device_manager: DeviceManagerBase | None = None, + **kwargs, + ): + super().__init__( + name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs + ) + self._busy = threading.Lock() + self._abort = threading.Event() + # Config hfm_step_per_x_px = Cpt(Signal, name="hfm_step_per_x_px", kind=Kind.config) vfm_step_per_y_px = Cpt(Signal, name="hfm_step_per_y_px", kind=Kind.config) @@ -93,8 +108,37 @@ class BeamSteerer(PSIDeviceBase): return x_status if y_status is not None: return y_status - return Status(done=True) + return None + + def _iterate_steps(self, status): + logger.info("Running beam steering...") + step_count = 1 + try: + while (_step_status := self.step_towards_centre()) is not None: + logger.info(f"Beam steering iteration: {step_count}") + step_count += 1 + if self._abort.is_set(): + raise RuntimeError(f"{self.name}: aborted") + _step_status.wait() + except Exception as exc: + status.set_exception(exc) + else: + status.set_finished() + finally: + self._busy.release() def trigger(self): - """External interface for 'step_towards_centre'""" - return self.step_towards_centre() + """External interface for to iterate stepping towards the centre until finished""" + + if not self._busy.acquire(blocking=False): + raise RuntimeError(f"{self.name} is still busy") + self._abort.clear() + status = DeviceStatus(self, timeout=60, settle_time=0.0) + threading.Thread( + target=self._iterate_steps, args=(status,), daemon=True, name=f"{self.name}_trigger" + ).start() + return status + + def stop(self, *, success=False): + self._abort.set() + super().stop(success=success)