instrument.devices
#
See the advice below to declare your instrument’s ophyd-style in YAML files:
Device factories |
The instrument.startup module calls RE(make_devices())
to
make the devices as described.
Declare all devices#
All ophyd-style devices are declared in one of the two YAML files listed above:
when code (classes, factory functions) is provided by a package:
refer to the package class (or function) directly in the YAML file
when local customization is needed (new support or modify a packaged class):
create local custom code that defines the class or factory
refer to local custom code in the YAML file
Any device can be created with python code that is compatible with this API signature:
1creator(*, prefix="", name="", labels=[], **kwargs)
Quick example
An ophyd object for an EPICS motor PV gp:m1
is created in Python code where
ophyd.EpicsMotor
is the creator, "gp:m1"
is the prefix, and the
other kwargs are name
and labels
.
1import ophyd
2m1 = ophyd.EpicsMotor("ioc:m1", name="m1", labels=["motors"])
This YAML replaces all the Python code above to create the m1
object:
1ophyd.EpicsMotor:
2- name: "m1"
3 prefix: "ioc:m1"
4 labels: ["motors"]
Tip
The devices are (re)created each time RE(make_devices())
is run.
If you edit either of these YAML files after starting your session, you can
run RE(make_devices())
again (without restarting your session) to
(re)create the devices. You only need to restart your session if you edit
the Python code.
The make_devices()
plan stub
imports the ‘creator’ (Python code) and creates any devices listed
below it. In YAML:
Each ‘creator’ can only be listed once.
All devices that are created with a ‘creator’ are listed below it.
Each device starts with a - and then the kwargs, as shown.
Indentation is important. Follow the examples.
Tip
These YAML representations are functionally equivalent:
See yaml.org for more information and YAML examples.
1ophyd.EpicsMotor:
2- name: m1
3 prefix: ioc:m1
4 labels:
5 - motor
6
7ophyd.EpicsMotor:
8- {name: m1, prefix: ioc:m1, labels: ["motors"]}
9
10ophyd.EpicsMotor: [{name: m1, prefix: ioc:m1, labels: ["motors"]}]
11
12instrument.devices.factories.motors:
13- {prefix: ioc:m, names: m, first: 1, last: 1, labels: ["motors"]}
Examples#
Examples are provided to show how to define your ophyd-style devices.
Packaged support#
Packaged support exists for many structures (motors, scalers, area detectors, slits, shutters, …).
Motors#
When support is used for more than one device, create a YAML list. Each item in the list can be a dictionary with appropriate keyword arguments. This YAML code describes five motors, using a one-line format for each dictionary.
1ophyd.EpicsMotor:
2- {name: m1, prefix: ioc:m1, labels: ["motors"]}
3- {name: m2, prefix: ioc:m2, labels: ["motors"]}
4- {name: m3, prefix: ioc:m3, labels: ["motors"]}
5- {name: dx, prefix: vme:m58:c0:m1, labels: ["motors"]}
6- {name: dy, prefix: vme:m58:c0:m2, labels: ["motors"]}
Using a factory to define some of these motors that fit a numerical pattern:
1instrument.devices.factories.motors:
2- {prefix: ioc:m, names: m, first: 1, last: 3, labels: ["motors"]}
3
4ophyd.EpicsMotor:
5- {name: dx, prefix: vme:m58:c0:m1, labels: ["motors"]}
6- {name: dy, prefix: vme:m58:c0:m2, labels: ["motors"]}
Scalers#
This example creates a single scaler named scaler. Two keyword arguments are provided.
1ophyd.scaler.ScalerCH:
2- name: scaler
3 prefix: ioc:scaler1
4 labels: ["scalers", "detectors"]
Area detectors#
An area detector factory (from the apstools
package) can be used to
declare one or more area detector devices. Here’s an instance of
ADSimDetector with various plugins.
1apstools.devices.ad_creator:
2- name: adsimdet
3 prefix: "ad:"
4 labels: ["area_detector", "detectors"]
5 plugins:
6 - cam:
7 class: apstools.devices.SimDetectorCam_V34
8 - image
9 - pva
10 - hdf1:
11 class: apstools.devices.AD_EpicsFileNameHDF5Plugin
12 read_path_template: "/mnt/iocad/tmp/"
13 write_path_template: "/tmp/"
14 - roi1
15 - stats1
Local custom devices#
Sometimes, a package provides support that requires some local customization.
diffractometers#
While the hklpy
package provides a 6-circle diffractometer, it does
not provide a class with name substitutions for the motor axes. We need those
substitutions to describe our diffractometer’s motor assignments.
(That’s a DIY feature for improvement in a future version of hklpy
.) We’ll have
to make some local code that provides motor name substitutions as keyword
arguments.
Here’s the local support code (in new file
src/instrument/devices/diffractometers.py
):
1"""Diffractometers"""
2
3import hkl
4from ophyd import Component
5from ophyd import EpicsMotor
6from ophyd import EpicsSignalRO
7from ophyd import FormattedComponent as FCpt
8
9class SixCircle(hkl.SimMixin, hkl.E6C):
10 """
11 Our 6-circle. Eulerian.
12
13 Energy obtained (RO) from monochromator.
14 """
15
16 # the reciprocal axes are defined by SimMixin
17
18 mu = FCpt(EpicsMotor, "{prefix}{m_mu}", kind="hinted", labels=["motors"])
19 omega = FCpt(EpicsMotor, "{prefix}{m_omega}", kind="hinted", labels=["motors"])
20 chi = FCpt(EpicsMotor, "{prefix}{m_chi}", kind="hinted", labels=["motors"])
21 phi = FCpt(EpicsMotor, "{prefix}{m_phi}", kind="hinted", labels=["motors"])
22 gamma = FCpt(EpicsMotor, "{prefix}{m_gamma}", kind="hinted", labels=["motors"])
23 delta = FCpt(EpicsMotor, "{prefix}{m_delta}", kind="hinted", labels=["motors"])
24
25 energy = Component(EpicsSignalRO, "BraggERdbkAO", kind="hinted", labels=["energy"])
26 energy_units = Component(EpicsSignalRO, "BraggERdbkAO.EGU", kind="config")
27
28 def __init__( # noqa D107
29 self,
30 prefix,
31 *,
32 m_mu="",
33 m_omega="",
34 m_chi="",
35 m_phi="",
36 m_gamma="",
37 m_delta="",
38 **kwargs,
39 ):
40 self.m_mu = m_mu
41 self.m_omega = m_omega
42 self.m_chi = m_chi
43 self.m_phi = m_phi
44 self.m_gamma = m_gamma
45 self.m_delta = m_delta
46 super().__init__(prefix, **kwargs)
The YAML description of our 6-circle diffractometer uses our local
custom SixCircle
support with the assigned motors and other kwargs:
1instrument.devices.diffractometers.SixCircle:
2- name: sixc
3 prefix: "gp:"
4 labels: ["diffractometer"]
5 m_mu: m23
6 m_omega: m24
7 m_chi: m25
8 m_phi: m26
9 m_gamma: m27
10 m_delta: m28
Using the devices#
The instrument.utils.make_devices_yaml.make_devices()
plan stub adds all
devices to the command line level (the __main__
namespace, as Python calls
it). Plans or other code can obtain a reference to any of these devices through
use of the oregistry
. The default
instrument provides a shutter
device. This setup_shutter
plan stub
configures the shutter to wait a finite time every time it opens or closes.
1def setup_shutter(delay=0.05):
2 """
3 Setup the shutter.
4
5 Simulate a shutter that needs a finite recovery time after moving.
6 """
7 yield from bps.null() # makes it a plan (generator function)
8
9 shutter = oregistry["shutter"]
10 shutter.wait_for_connection()
11 shutter.delay_s = delay
With this YAML content:
1apstools.synApps.UserCalcsDevice: [{name: user_calcs, prefix: "gp:"}]
you might have a plan stub that needs two of the userCalcs. The oregistry
can provide them to your plan stub:
1dither_x = oregistry["user_calcs.calc9"]
2dither_y = oregistry["user_calcs.calc10"]
Device factories#
Device factories are used to:
Create several similar ophyd-style devices (such as
ophyd.Device
orophyd.Signal
) that fit a pattern.Import a device which is pre-defined in a module, such as the ophyd simulators in
ophyd.sim
.
|
Make one or more objects using 'creator'. |
|
Make one or more ' |
|
Provide a predefined device such as from the 'ophyd.sim' module. |
- instrument.devices.factories.factory_base(*, prefix=None, names='object{}', first=0, last=0, creator='ophyd.Signal', **kwargs)[source]#
Make one or more objects using ‘creator’.
PARAMETERS
- prefixstr
Prefix pattern for the EPICS PVs (default:
None
).- namesstr
Name pattern for the objects. The default pattern is
"object{}"
which produces devices namedobject1, object2, ..., `
. If a formatting specification ({}
) is not provided, it is appended. Each object will be named using this code:names.format(number)
, such as:In [23]: "object{}".format(22) Out[23]: 'object22'
- firstint
The first object number in the continuous series from ‘first’ through ‘last’ (inclusive).
- lastint
The first object number in the continuous series from ‘first’ through ‘last’ (inclusive).
- creatorstr
Name of the creator code that will be used to construct each device. (default:
"ophyd.Signal"
)- kwargsdict
Dictionary of additional keyword arguments. This is included when creating each object.
- instrument.devices.factories.motors(*, prefix=None, names='m{}', first=0, last=0, **kwargs)[source]#
Make one or more ‘
ophyd.EpicsMotor
’ objects.Example entry in devices.yml file:
1instrument.devices.factories.motors: 2 - {prefix: "ioc:m", first: 1, last: 4, labels: ["motor"]} 3 # skip m5 & m6 4 - {prefix: "ioc:m", first: 7, last: 22, labels: ["motor"]}
Uses this pattern:
1ophyd.EpicsMotor( 2 prefix=prefix.format(i), 3 name=names.format(i), 4 **kwargs, 5 )
where
i
iterates from ‘first’ through ‘last’ (inclusive).PARAMETERS
- prefixstr
Name pattern for the EPICS PVs. There is no default pattern. If a formatting specification (
{}
) is not provided, it is appended (as with other ophyd devices). Each motor will be configured with this prefix:prefix.format(number)
, such as:In [23]: "ioc:m{}".format(22) Out[23]: 'ioc:m22'
- namesstr
Name pattern for the motors. The default pattern is
"m{}"
which produces motors namedm1, m2, ..., m22, m23, ...`
. If a formatting specification ({}
) is not provided, it is appended. Each motor will be named using this code:names.format(number)
, such as:In [23]: "m{}".format(22) Out[23]: 'm22'
- firstint
The first motor number in the continuous series from ‘first’ through ‘last’ (inclusive).
- lastint
The first motor number in the continuous series from ‘first’ through ‘last’ (inclusive).
- kwargsdict
Dictionary of additional keyword arguments. This is included with each EpicsMotor object.
- instrument.devices.factories.predefined_device(*, name='', creator='')[source]#
Provide a predefined device such as from the ‘ophyd.sim’ module.
PARAMETERS
- creatorstr
Name of the predefined device to be used
- namestr
Simulator will be assigned this name. (default: use existing name)
Example entry in devices.yml file:
1instrument.devices.factories.predefined_device: 2 - {creator: ophyd.sim.motor, name: sim_motor} 3 - {creator: ophyd.sim.noisy_det, name: sim_det}