Source code for dodal.devices.i24.pmac
from enum import Enum
from bluesky.protocols import Triggerable
from ophyd_async.core import AsyncStatus, StandardReadable
from ophyd_async.core.signal import SignalRW
from ophyd_async.core.signal_backend import SignalBackend
from ophyd_async.core.soft_signal_backend import SoftSignalBackend
from ophyd_async.core.utils import DEFAULT_TIMEOUT
from ophyd_async.epics.motion import Motor
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
HOME_STR = r"\#1hmz\#2hmz\#3hmz" # Command to home the PMAC motors
ZERO_STR = "!x0y0z0" # Command to blend any ongoing move into new position
[docs]
class LaserSettings(str, Enum):
"""PMAC strings to switch laser on and off.
Note. On the PMAC, M-variables usually have to do with position compare
set up.
For example, for laser1:
Use M712 = 0 if triggering on falling edge.
Use M712 = 1 if on rising edge.
"""
LASER_1_ON = " M712=1 M711=1"
LASER_1_OFF = " M712=0 M711=1"
LASER_2_ON = " M812=1 M811=1"
LASER_2_OFF = " M812=0 M811=1"
[docs]
class EncReset(str, Enum):
"""PMAC strings for position compare on encoder channels in the controller.
For example, for ENC5:
m508 sets position A to be compared with value in Channel5 in the controller.
m509 sets position B to be compared with value in Channel5 in the controller.
Note. These settings are usually used for initialisation.
"""
ENC5 = "m508=100 m509=150"
ENC6 = "m608=100 m609=150"
ENC7 = "m708=100 m709=150"
ENC8 = "m808=100 m809=150"
[docs]
class PMACStringMove(Triggerable):
"""Trigger a PMAC move by setting the pmac_string."""
def __init__(
self,
pmac_str_sig: SignalRW,
string_to_send: str,
) -> None:
self.signal = pmac_str_sig
self.cmd_string = string_to_send
@AsyncStatus.wrap
async def trigger(self):
await self.signal.set(self.cmd_string, wait=True)
[docs]
class PMACStringLaser(SignalRW):
"""Set the pmac_string to control the laser."""
def __init__(
self,
pmac_str_sig: SignalRW,
backend: SignalBackend,
timeout: float | None = DEFAULT_TIMEOUT,
name: str = "",
) -> None:
self.signal = pmac_str_sig
super().__init__(backend, timeout, name)
@AsyncStatus.wrap
async def set(self, laser_setting: LaserSettings):
await self.signal.set(laser_setting.value, wait=True)
[docs]
class PMACStringEncReset(SignalRW):
""""""
def __init__(
self,
pmac_str_sig: SignalRW,
backend: SignalBackend,
timeout: float | None = DEFAULT_TIMEOUT,
name: str = "",
) -> None:
self.signal = pmac_str_sig
super().__init__(backend, timeout, name)
@AsyncStatus.wrap
async def set(self, enc_string: EncReset):
await self.signal.set(enc_string.value, wait=True)
[docs]
class PMAC(StandardReadable):
"""Device to control the chip stage on I24."""
def __init__(self, prefix: str, name: str = "") -> None:
self.pmac_string = epics_signal_rw(str, prefix + "PMAC_STRING")
self.home = PMACStringMove(
self.pmac_string,
HOME_STR,
)
self.to_xyz_zero = PMACStringMove(self.pmac_string, ZERO_STR)
self.laser = PMACStringLaser(self.pmac_string, backend=SoftSignalBackend(str))
self.enc_reset = PMACStringEncReset(
self.pmac_string, backend=SoftSignalBackend(str)
)
self.x = Motor(prefix + "X")
self.y = Motor(prefix + "Y")
self.z = Motor(prefix + "Z")
# These next signals are readback values on PVARS which are set by the motion
# program.
self.scanstatus = epics_signal_r(float, "BL24I-MO-STEP-14:signal:P2401")
self.counter = epics_signal_r(float, "BL24I-MO-STEP-14:signal:P2402")
super().__init__(name)