Trigger a Device with the EPICS record .PROC field#
The EPICS .PROC
field is one of the fields common to all EPICS records. It tells the IOC’s database to execute the standard actions for that record’s data. When the record is in Passive
scan mode, sending a 1
value to the .PROC
field processes the record.
EPICS record processing is similar to the ophyd trigger()
method which tells the Device to run its data acquisition method.
[1]:
from ophyd import Component, Device, EpicsSignal
Let’s make a custom ophyd Device
, limiting our attention only to the fields of interest for this howto guide.
field |
description |
---|---|
|
The a value. |
|
The calculation to produce a new A value when the record is processed. |
|
Send a |
|
The record’s scan mode selection. |
Here, we add the trigger_value
keyword argument. This is how we send a 1
to the .PROC
field when the Device is triggered.
It is worth commenting about the kind
keyword value. The kind attribute is the means to identify a signal that is relevant for handling by a callback.
kind |
usage |
---|---|
hinted |
attribute should be reported by |
config |
value is metadata, should be reported by |
omitted |
value is not reportable |
[2]:
class Transform(Device):
a = Component(EpicsSignal, ".A", kind="hinted")
calc_a = Component(EpicsSignal, ".CLCA", kind="config", string=True)
process_record = Component(EpicsSignal, ".PROC", kind="omitted", trigger_value=1)
scan_mode = Component(EpicsSignal, ".SCAN", kind="config", string=True)
Create an ophyd object and connect it with EPICS.
[3]:
xform = Transform("gp:userTran11", name="xform")
xform.wait_for_connection()
Print the summary ophyd of the Device.
[4]:
xform.summary()
data keys (* hints)
-------------------
*xform_a
read attrs
----------
a EpicsSignal ('xform_a')
config keys
-----------
xform_calc_a
xform_scan_mode
configuration attrs
-------------------
calc_a EpicsSignal ('xform_calc_a')
scan_mode EpicsSignal ('xform_scan_mode')
unused attrs
------------
process_record EpicsSignal ('xform_process_record')
Show the values as they exist now using the .read()
method.
[5]:
print(f"{xform.read()=}")
xform.read()=OrderedDict([('xform_a', {'value': 2.0, 'timestamp': 1714336346.373808})])
Tell ophyd to trigger the Device (which tells the transform record to process) using the .trigger()
method.
Note that only the a value is reported. All other attributes are either configuration or not reportable (such as process_record
).
[6]:
xform.trigger()
print(f"{xform.read()=}")
xform.read()=OrderedDict([('xform_a', {'value': 3.0, 'timestamp': 1714337169.963434})])
Reset the Device before we demonstrate.
[7]:
xform.calc_a.put("")
xform.a.put(0)
xform.scan_mode.put("Passive")
print(f"{xform.read()=}")
xform.read()=OrderedDict([('xform_a', {'value': 0.0, 'timestamp': 1714337169.970021})])
Define the calculation to run (increment a) each time the record is processed.
[8]:
xform.calc_a.put("A+1")
Print the value before triggering (processing), trigger the, then print the value afterwards. We expect the value will increment by 1
.
[9]:
print(f"{xform.read()=}")
xform.trigger()
print(f"{xform.read()=}")
xform.read()=OrderedDict([('xform_a', {'value': 0.0, 'timestamp': 1714337169.970021})])
xform.read()=OrderedDict([('xform_a', {'value': 1.0, 'timestamp': 1714337169.982637})])
Let’s do the same thing in a bluesky plan. First, we need some parts from the bluesky package. These are the most basic parts:
[10]:
from bluesky import RunEngine
from bluesky import plan_stubs as bps
RE = RunEngine()
Define the bluesky plan. Instead of calling the Device’s .trigger()
method directly, we let the RunEngine handle that task (with bps.trigger()
), in case the device takes some time to complete its trigger action(s).
[11]:
def plan():
print(f"{xform.read()=}")
yield from bps.trigger(xform)
print(f"{xform.read()=}")
Run the plan.
[12]:
RE(plan())
xform.read()=OrderedDict([('xform_a', {'value': 1.0, 'timestamp': 1714337169.982637})])
xform.read()=OrderedDict([('xform_a', {'value': 2.0, 'timestamp': 1714337170.147542})])
[12]:
()