id3c.devices.interlocked_motor#

EpicsMotor with a Bluesky-session interlock.

Provides:

  • MotionInterlock – exception raised when a move is blocked.

  • InterlockedEpicsMotorophyd.EpicsMotor subclass that consults a caller-supplied interlock() callable both before starting a motion (pre-flight) and during the motion (mid-flight, via subscriptions on caller-supplied signals).

Scope and limitations#

This interlock lives entirely in the running Bluesky/Python session. It does not write to any EPICS protection field (no DISP, no SPMG Stop, no sequencer record). If this Python process crashes, exits, or is bypassed (MEDM jog, caput, a different Bluesky session, SPEC, etc.), the underlying EPICS motor is unaffected by anything in this module.

For session-independent, hardware-grade protection (e.g. preventing collisions regardless of which client commands the move), implement the interlock in the IOC: a CALC/SCALC record, a state-notation sequencer, or a soft record driving the motor’s DISP field.

Wiring pattern#

InterlockedEpicsMotor does not know about any particular other device. The interlock condition is supplied late, typically in startup.py after all devices have been created, e.g.:

omega = sample_stage.omega
omega.interlock = lambda: laser_optics.is_out
omega.interlock_description = "laser_optics OUT"
omega.interlock_watch = (
    laser_optics.us.user_readback,
    laser_optics.ds.user_readback,
)

This keeps the class reusable and avoids import cycles between mutually-interlocked devices.

Attributes#

Exceptions#

MotionInterlock

Raised when an InterlockedEpicsMotor move is blocked.

Classes#

InterlockedEpicsMotor

EpicsMotor that consults a callable interlock before and during moves.

Module Contents#

id3c.devices.interlocked_motor.logger[source]#
exception id3c.devices.interlocked_motor.MotionInterlock[source]#

Bases: RuntimeError

Raised when an InterlockedEpicsMotor move is blocked.

The exception message is intended to be self-diagnostic so that the final line of a (typically long) Bluesky traceback identifies both the affected motor and the interlock that blocked it.

Initialize self. See help(type(self)) for accurate signature.

class id3c.devices.interlocked_motor.InterlockedEpicsMotor(*args, interlock_description: str = '', **kwargs)[source]#

Bases: ophyd.EpicsMotor

EpicsMotor that consults a callable interlock before and during moves.

Parameters:

interlock_description (str, optional) – Short human-readable description of the interlock condition, used in MotionInterlock messages. May be supplied via YAML (it is popped from kwargs before super().__init__).

Notes

The interlock callable and interlock_watch signals are assigned as plain attributes (not Components) and are expected to be wired after construction; see the module docstring.

If interlock is None (the default), this class behaves identically to a plain EpicsMotor.

Initialize; pop interlock_description before EpicsMotor.

interlock and interlock_watch are initialized to inert defaults and are expected to be assigned post-construction (see the module docstring).

interlock_description: str = ''[source]#
interlock: Callable[[], bool] | None = None[source]#
interlock_watch: Iterable = ()[source]#
move(position, wait=True, **kwargs)[source]#

Pre-flight interlock check, then EpicsMotor.move.

Raises:

MotionInterlock – If self.interlock is wired and returns False at the time of the call. No EPICS write is performed in that case.