Source code for dodal.devices.undulator_dcm

import asyncio

from bluesky.protocols import Movable
from ophyd_async.core import AsyncStatus, StandardReadable

from dodal.common.beamlines.beamline_parameters import get_beamline_parameters

from .dcm import DCM
from .undulator import Undulator, UndulatorGapAccess

ENERGY_TIMEOUT_S: float = 30.0

# Enable to allow testing when the beamline is down, do not change in production!
TEST_MODE = False


class AccessError(Exception):
    pass


[docs] class UndulatorDCM(StandardReadable, Movable): """ Composite device to handle changing beamline energies, wraps the Undulator and the DCM. The DCM has a motor which controls the beam energy, when it moves, the Undulator gap may also have to change to enable emission at the new energy. The relationship between the two motor motor positions is provided via a lookup table. Calling unulator_dcm.set(energy) will move the DCM motor, perform a table lookup and move the Undulator gap motor if needed. So the set method can be thought of as a comprehensive way to set beam energy. """ def __init__( self, undulator: Undulator, dcm: DCM, daq_configuration_path: str, prefix: str = "", name: str = "", ): super().__init__(name) # Attributes are set after super call so they are not renamed to # <name>-undulator, etc. self.undulator = undulator self.dcm = dcm # These attributes are just used by hyperion for lookup purposes self.pitch_energy_table_path = ( daq_configuration_path + "/lookup/BeamLineEnergy_DCM_Pitch_converter.txt" ) self.roll_energy_table_path = ( daq_configuration_path + "/lookup/BeamLineEnergy_DCM_Roll_converter.txt" ) # I03 configures the DCM Perp as a side effect of applying this fixed value to the DCM Offset after an energy change # Nb this parameter is misleadingly named to confuse you self.dcm_fixed_offset_mm = get_beamline_parameters( daq_configuration_path + "/domain/beamlineParameters" )["DCM_Perp_Offset_FIXED"] @AsyncStatus.wrap async def set(self, value: float): await asyncio.gather( self._set_dcm_energy(value), self.undulator.set(value), ) async def _set_dcm_energy(self, energy_kev: float) -> None: access_level = await self.undulator.gap_access.get_value() if access_level is UndulatorGapAccess.DISABLED and not TEST_MODE: raise AccessError("Undulator gap access is disabled. Contact Control Room") await self.dcm.energy_in_kev.set( energy_kev, timeout=ENERGY_TIMEOUT_S, )