diff --git a/README.rst b/README.rst index 0808ea0..00bc6c5 100644 --- a/README.rst +++ b/README.rst @@ -1,13 +1,127 @@ -=============================== -guarneri -=============================== +========================== +Guarneri Instrument Maker +========================== .. image:: https://img.shields.io/pypi/v/guarneri.svg :target: https://pypi.python.org/pypi/guarneri -guarneri +A package for creating Ophyd and Ophyd-async devices from configuration +files. + +Instead of instantiating devices directly in python, Guarneri reads a +configuration file and creates/connects the devices for you. This +provides the following benefits: + +1) Beamline configuration is in a human-readable configuration file (e.g. TOML). +2) Other tools can modify the configuration file if needed. +3) Devices can be connected in parallel. +4) Missing devices are handled gracefully. +5) Devices can be simulated/mocked by changing single value in the config file. + + +Usage +----- + +Let's say you have some ophyd and ophyd-async devices defined (with +type hints) in a file called ``devices.py``. + +.. code-block:: python + :caption: devices.py + + from ophyd_async.epics import epics_signal_rw + from ophyd_async.core import AsyncDevice + from ophyd import Device, Component + + from guarneri import Instrument + + class MyDevice(Device): + description = Component(".DESC") + + + class MyAsyncDevice(AsyncDevice): + def __init__(self, prefix: str, name: str = ""): + self.description = epics_signal_rw(str, f"{prefix}.DESC") + super().__init__(name=name) + + + def area_detector_factory(hdf: bool=True): + # Create devices here using the arguments + area_detector = ... + return area_detector + + +Instead of instantiating these in a python startup script, Guarneri +will let you **create devices from a TOML configuration file**. First +we create a TOML file with the necessary parameters, these map +directly onto the arguments of the device's ``__init__()``, or the +arguments of a factory that returns a device. + + +.. code-block:: toml + :caption: instrument.toml + + [my_device.device1] + prefix = '255id:' + + [async_device.device3] + prefix = '255id:' + + [area_detector.sim_det] + hdf = true + + +Then in your beamline startup code, create a Guarneri instrument and +load the config files. + +.. code-block:: python + :caption: instrument.py + + from io import StringIO + + from devices import MyDevice, MyAsyncDevice, area_detector_factory + + # Prepare the instrument device + instrument = Instrument({ + "my_device": MyDevice, + "async_device": MyAsyncDevice, + "area_detector": area_detector_factory, + }) + + # Create the devices from the TOML configuration file + instrument.load_config_files("instrument.toml") + # Optionally connect all the devices + await instrument.connect_devices() + + # Now use the devices for science! + instrument.devices['device_1'].description.get() + + +The first argument to :py:meth:`guarneri.Instrument.__init__()` is a +mapping of TOML section names to device classes. Guarneri then +introspects the device or factory to decide which TOML keys and types +are valid. In the above example, the heading ``[my_device.device1]`` +will create an instance of ``MyDevice()`` with the name ``"device1"``. + + +What About Happi? +----------------- + +Happi has a similar goal to Guarneri, but with a different +scope. While Happi is meant for facility-level configuration (e.g. +LCLS), Guarneri is aimed at individual beamlines at a synchrotron. +Happi uses :py:class:`HappiItem` classes with :py:class:`ItemInfo` +objects to describe the devices definitions, while Guarneri uses the +device classes themselves. Happi provides a python client for adding +and modifying the devices, while Guarneri uses human-readable +configuration files. + +**Which one is better?** Depends on what you're trying to do. If you +want a **flexible and scalable** system the **shares devices across a +facility**, try Happi. If you want a way to **get devices running +quickly** on your beamline before users show up, try Guarneri. + Documentation -------------