Custom Devices

A Device is made from Components, which encapsulate other Devices or Signals. See examples.

from ophyd import Device, EpicsSignal, EpicsSignalRO
from ophyd import Component as Cpt
from ophyd.utils import set_and_wait

class Robot(Device):
    sample_number = Cpt(EpicsSignal, 'ID:Tgt-SP')
    load_cmd = Cpt(EpicsSignal, 'Cmd:Load-Cmd.PROC')
    unload_cmd = Cpt(EpicsSignal, 'Cmd:Unload-Cmd.PROC')
    execute_cmd = Cpt(EpicsSignal, 'Cmd:Exec-Cmd')
    status = Cpt(EpicsSignal, 'Sts-Sts')

my_robot = Robot('pv_prefix:', name='my_robot')

In this case, my_robot.load_cmd would be an EpicsSignal that points to the PV pv_prefix:Cmd:Load-Cmd.PROC. Each of the components can be used as stage_sigs, added to the list of read_attrs or configuration_attrs, or simply as EpicsSignals on their own.

Devices and bluesky count_time

When a Device is used as a bluesky detector in a scan, a count_time component will be checked for prior to staging. For example:

from ophyd import (Device, Signal, Component as Cpt, EpicsSignal)

class DetectorWithCountTime(Device):
    count_time = Cpt(Signal)
    exposure_time = Cpt(EpicsSignal, 'ExposureTime-SP')

    def stage(self):
        if self.count_time.get() is not None:
            actual_exposure_time = (self.count_time.get() - 0.1)
            self.stage_sigs[self.exposure_time] = actual_exposure_time
        super().stage()

det = DetectorWithCountTime('prefix:', name='det')
gs.DETS.append(det)
RE(dscan(mtr, 0, 1, 5, time=5.0))
# count_time would be set to 5.0 here, prior to the scan starting

Using the approach of a soft Signal on detectors allows stage to process the value that comes directly from the user. A slightly less flexible alternative would be to define count_time just as the EpicsSignal exposure_time below it, if those values should always be the same.