Skip to content

Commit

Permalink
Restructure ophyd-async (#467)
Browse files Browse the repository at this point in the history
This is a repository wide restructure based on the ADR defined in #26.
  • Loading branch information
abbiemery committed Jul 23, 2024
1 parent af9c780 commit 9c99913
Show file tree
Hide file tree
Showing 144 changed files with 1,863 additions and 1,802 deletions.
40 changes: 19 additions & 21 deletions docs/examples/foo_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,20 @@

from bluesky.protocols import HasHints, Hints

from ophyd_async.core import DirectoryProvider
from ophyd_async.core.async_status import AsyncStatus
from ophyd_async.core.detector import DetectorControl, DetectorTrigger, StandardDetector
from ophyd_async.epics.areadetector.drivers.ad_base import (
ADBase,
ADBaseShapeProvider,
start_acquiring_driver_and_ensure_status,
from ophyd_async.core import (
AsyncStatus,
DetectorControl,
DetectorTrigger,
PathProvider,
StandardDetector,
)
from ophyd_async.epics.areadetector.utils import ImageMode, ad_rw, stop_busy_record
from ophyd_async.epics.areadetector.writers.hdf_writer import HDFWriter
from ophyd_async.epics.areadetector.writers.nd_file_hdf import NDFileHDF
from ophyd_async.epics import adcore
from ophyd_async.epics.signal import epics_signal_rw_rbv


class FooDriver(ADBase):
class FooDriver(adcore.ADBaseIO):
def __init__(self, prefix: str, name: str = "") -> None:
self.trigger_mode = ad_rw(str, prefix + "TriggerMode")
self.trigger_mode = epics_signal_rw_rbv(str, prefix + "TriggerMode")
super().__init__(prefix, name)


Expand All @@ -38,40 +36,40 @@ async def arm(
) -> AsyncStatus:
await asyncio.gather(
self._drv.num_images.set(num),
self._drv.image_mode.set(ImageMode.multiple),
self._drv.image_mode.set(adcore.ImageMode.multiple),
self._drv.trigger_mode.set(f"FOO{trigger}"),
)
if exposure is not None:
await self._drv.acquire_time.set(exposure)
return await start_acquiring_driver_and_ensure_status(self._drv)
return await adcore.start_acquiring_driver_and_ensure_status(self._drv)

async def disarm(self):
await stop_busy_record(self._drv.acquire, False, timeout=1)
await adcore.stop_busy_record(self._drv.acquire, False, timeout=1)


class FooDetector(StandardDetector, HasHints):
_controller: FooController
_writer: HDFWriter
_writer: adcore.ADHDFWriter

def __init__(
self,
prefix: str,
directory_provider: DirectoryProvider,
path_provider: PathProvider,
drv_suffix="cam1:",
hdf_suffix="HDF1:",
name="",
):
# Must be children to pick up connect
self.drv = FooDriver(prefix + drv_suffix)
self.hdf = NDFileHDF(prefix + hdf_suffix)
self.hdf = adcore.NDFileHDFIO(prefix + hdf_suffix)

super().__init__(
FooController(self.drv),
HDFWriter(
adcore.ADHDFWriter(
self.hdf,
directory_provider,
path_provider,
lambda: self.name,
ADBaseShapeProvider(self.drv),
adcore.ADBaseShapeProvider(self.drv),
),
config_sigs=(self.drv.acquire_time,),
name=name,
Expand Down
4 changes: 2 additions & 2 deletions docs/how-to/compound-devices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Assembly

Compound assemblies can be used to group Devices into larger logical Devices:

.. literalinclude:: ../../src/ophyd_async/epics/demo/__init__.py
.. literalinclude:: ../../src/ophyd_async/epics/demo/_mover.py
:pyobject: SampleStage

This applies prefixes on construction:
Expand All @@ -35,7 +35,7 @@ Grouping by Index

Sometimes, it makes sense to group devices by number, say an array of sensors:

.. literalinclude:: ../../src/ophyd_async/epics/demo/__init__.py
.. literalinclude:: ../../src/ophyd_async/epics/demo/_sensor.py
:pyobject: SensorGroup

:class:`~ophyd-async.core.DeviceVector` allows writing maintainable, arbitrary-length device groups instead of fixed classes for each possible grouping. A :class:`~ophyd-async.core.DeviceVector` can be accessed via indices, for example: ``my_sensor_group.sensors[2]``. Here ``sensors`` is a dictionary with integer indices rather than a list so that the most semantically sensible indices may be used, the sensor group above may be 1-indexed, for example, because the sensors' datasheet calls them "sensor 1", "sensor 2" etc.
Expand Down
6 changes: 3 additions & 3 deletions docs/how-to/make-a-simple-device.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ For a simple :external+bluesky:py:class:`bluesky.protocols.Readable` object like
define some signals, then tell the superclass which signals should contribute to
``read()`` and ``read_configuration()``:

.. literalinclude:: ../../src/ophyd_async/epics/demo/__init__.py
.. literalinclude:: ../../src/ophyd_async/epics/demo/_sensor.py
:pyobject: Sensor

First some Signals are constructed and stored on the Device. Each one is passed
Expand Down Expand Up @@ -54,7 +54,7 @@ Movable
For a more complicated device like a `Mover`, you can still use `StandardReadable`
and implement some addition protocols:

.. literalinclude:: ../../src/ophyd_async/epics/demo/__init__.py
.. literalinclude:: ../../src/ophyd_async/epics/demo/_mover.py
:pyobject: Mover

The ``set()`` method implements :external+bluesky:py:class:`bluesky.protocols.Movable`. This
Expand All @@ -71,7 +71,7 @@ Assembly

Compound assemblies can be used to group Devices into larger logical Devices:

.. literalinclude:: ../../src/ophyd_async/epics/demo/__init__.py
.. literalinclude:: ../../src/ophyd_async/epics/demo/_mover.py
:pyobject: SampleStage

This applies prefixes on construction:
Expand Down
14 changes: 7 additions & 7 deletions docs/how-to/make-a-standard-detector.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ Make a StandardDetector
`StandardDetector` is an abstract class to assist in creating Device classes for hardware that writes its own data e.g. an AreaDetector implementation, or a PandA writing motor encoder positions to file.
The `StandardDetector` is a simple compound device, with 2 standard components:

- `DetectorWriter` to handle data persistence, i/o and pass information about data to the RunEngine (usually an instance of :py:class:`HDFWriter`)
- `DetectorWriter` to handle data persistence, i/o and pass information about data to the RunEngine (usually an instance of :py:class:`ADHDFWriter`)
- `DetectorControl` with logic for arming and disarming the detector. This will be unique to the StandardDetector implementation.

Writing an AreaDetector StandardDetector
----------------------------------------

For an AreaDetector implementation of the StandardDetector, two entity objects which are subdevices of the `StandardDetector` are used to map to AreaDetector plugins:

- An NDPluginFile instance (for :py:class:`HDFWriter` an instance of :py:class:`NDFileHDF`)
- An :py:class:`ADBase` instance mapping to NDArray for the "driver" of the detector implementation
- An NDPluginFile instance (for :py:class:`ADHDFWriter` an instance of :py:class:`NDFileHDFIO`)
- An :py:class:`ADBaseIO` instance mapping to NDArray for the "driver" of the detector implementation


Define a :py:class:`FooDriver` if the NDArray requires fields in addition to those on :py:class:`ADBase` to be exposed. It should extend :py:class:`ADBase`.
Define a :py:class:`FooDriver` if the NDArray requires fields in addition to those on :py:class:`ADBaseIO` to be exposed. It should extend :py:class:`ADBaseIO`.
Enumeration fields should be named to prevent namespace collision, i.e. for a Signal named "TriggerSource" use the enum "FooTriggerSource"

.. literalinclude:: ../examples/foo_detector.py
Expand Down Expand Up @@ -50,15 +50,15 @@ Writing a non-AreaDetector StandardDetector
A non-AreaDetector `StandardDetector` should implement `DetectorControl` and `DetectorWriter` directly.
Here we construct a `DetectorControl` that co-ordinates signals on a PandA PositionCapture block - a child device "pcap" of the `StandardDetector` implementation, analogous to the :py:class:`FooDriver`.

.. literalinclude:: ../../src/ophyd_async/panda/_panda_controller.py
.. literalinclude:: ../../src/ophyd_async/fastcs/panda/_panda_controller.py
:pyobject: PandaPcapController

The PandA may write a number of fields, and the :py:class:`PandaHDFWriter` co-ordinates those, configures the filewriter and describes the data for the RunEngine.

.. literalinclude:: ../../src/ophyd_async/panda/writers/_hdf_writer.py
.. literalinclude:: ../../src/ophyd_async/fastcs/panda/_hdf_writer.py
:pyobject: PandaHDFWriter

The PandA StandardDetector implementation simply ties the component parts and its child devices together.

.. literalinclude:: ../../src/ophyd_async/panda/_hdf_panda.py
.. literalinclude:: ../../src/ophyd_async/fastcs/panda/_hdf_panda.py
:pyobject: HDFPanda
2 changes: 1 addition & 1 deletion docs/reference/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ This is the internal API reference for ophyd_async

core
epics
panda
fastcs
Loading

0 comments on commit 9c99913

Please sign in to comment.