Source code for instrument.utils.make_devices_yaml
"""
Make devices from YAML files
=============================
Construct ophyd-style devices from simple specifications in YAML files.
.. autosummary::
:nosignatures:
~make_devices
~Instrument
"""
import logging
import pathlib
import sys
import time
import guarneri
from apstools.plans import run_blocking_function
from apstools.utils import dynamic_import
from bluesky import plan_stubs as bps
from .aps_functions import host_on_aps_subnet
from .config_loaders import iconfig
from .config_loaders import load_config_yaml
from .controls_setup import oregistry # noqa: F401
logger = logging.getLogger(__name__)
logger.bsdev(__file__)
configs_path = pathlib.Path(__file__).parent.parent / "configs"
main_namespace = sys.modules["__main__"]
local_control_devices_file = iconfig["DEVICES_FILE"]
aps_control_devices_file = iconfig["APS_DEVICES_FILE"]
[docs]
def make_devices(*, pause: float = 1):
"""
(plan stub) Create the ophyd-style controls for this instrument.
Feel free to modify this plan to suit the needs of your instrument.
EXAMPLE::
RE(make_devices())
PARAMETERS
pause : float
Wait 'pause' seconds (default: 1) for slow objects to connect.
"""
logger.debug("(Re)Loading local control objects.")
yield from run_blocking_function(
_loader, configs_path / local_control_devices_file, main=True
)
if host_on_aps_subnet():
yield from run_blocking_function(
_loader, configs_path / aps_control_devices_file, main=True
)
if pause > 0:
logger.debug(
"Waiting %s seconds for slow objects to connect.",
pause,
)
yield from bps.sleep(pause)
# Configure any of the controls here, or in plan stubs
[docs]
def _loader(yaml_device_file, main=True):
"""
Load our ophyd-style controls as described in a YAML file.
PARAMETERS
yaml_device_file : str or pathlib.Path
YAML file describing ophyd-style controls to be created.
main : bool
If ``True`` add these devices to the ``__main__`` namespace.
"""
logger.debug("Devices file %r.", str(yaml_device_file))
t0 = time.time()
_instr.load(yaml_device_file)
logger.debug("Devices loaded in %.3f s.", time.time() - t0)
if main:
for label in oregistry.device_names:
# add to __main__ namespace
setattr(main_namespace, label, oregistry[label])
[docs]
class Instrument(guarneri.Instrument):
"""Custom YAML loader for guarneri."""
[docs]
def parse_yaml_file(self, config_file: pathlib.Path | str) -> list[dict]:
"""Read device configurations from YAML format file."""
if isinstance(config_file, str):
config_file = pathlib.Path(config_file)
def parser(class_name, specs):
if class_name not in self.device_classes:
self.device_classes[class_name] = dynamic_import(class_name)
entries = [
{
"device_class": class_name,
"args": (), # ALL specs are kwargs!
"kwargs": table,
}
for table in specs
]
return entries
devices = [
device
# parse the file
for k, v in load_config_yaml(config_file).items()
# each support type (class, factory, function, ...)
for device in parser(k, v)
]
return devices
_instr = Instrument({}, registry=oregistry) # singleton