-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Standardise and add documentation for creating new StandardDetector i…
- Loading branch information
1 parent
995c126
commit 4ee3f29
Showing
10 changed files
with
183 additions
and
162 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import asyncio | ||
from typing import Optional | ||
|
||
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.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 | ||
|
||
|
||
class FooDriver(ADBase): | ||
def __init__(self, prefix: str, name: str = "") -> None: | ||
self.trigger_mode = ad_rw(str, prefix + "TriggerMode") | ||
super().__init__(prefix, name) | ||
|
||
|
||
class FooController(DetectorControl): | ||
def __init__(self, driver: FooDriver) -> None: | ||
self._drv = driver | ||
|
||
def get_deadtime(self, exposure: float) -> float: | ||
# FooDetector deadtime handling | ||
return 0.001 | ||
|
||
async def arm( | ||
self, | ||
num: int, | ||
trigger: DetectorTrigger = DetectorTrigger.internal, | ||
exposure: Optional[float] = None, | ||
) -> AsyncStatus: | ||
await asyncio.gather( | ||
self._drv.num_images.set(num), | ||
self._drv.image_mode.set(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) | ||
|
||
async def disarm(self): | ||
await stop_busy_record(self._drv.acquire, False, timeout=1) | ||
|
||
|
||
class FooDetector(StandardDetector, HasHints): | ||
_controller: FooController | ||
_writer: HDFWriter | ||
|
||
def __init__( | ||
self, | ||
prefix: str, | ||
directory_provider: DirectoryProvider, | ||
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) | ||
|
||
super().__init__( | ||
FooController(self.drv), | ||
HDFWriter( | ||
self.hdf, | ||
directory_provider, | ||
lambda: self.name, | ||
ADBaseShapeProvider(self.drv), | ||
), | ||
config_sigs=(self.drv.acquire_time,), | ||
name=name, | ||
) | ||
|
||
@property | ||
def hints(self) -> Hints: | ||
return self._writer.hints |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
.. note:: | ||
|
||
Ophyd async is included on a provisional basis until the v1.0 release and | ||
may change API on minor release numbers before then | ||
|
||
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`) | ||
- `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 | ||
|
||
|
||
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`. | ||
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 | ||
:language: python | ||
:pyobject: FooDriver | ||
|
||
Define a :py:class:`FooController` with handling for converting the standard pattern of :py:meth:`ophyd_async.core.DetectorControl.arm` and :py:meth:`ophyd_async.core.DetectorControl.disarm` to required state of :py:class:`FooDriver` e.g. setting a compatible "FooTriggerSource" for a given `DetectorTrigger`, or raising an exception if incompatible with the `DetectorTrigger`. | ||
|
||
The :py:meth:`ophyd_async.core.DetectorControl.get_deadtime` method is used when constructing sequence tables for hardware controlled scanning. Details on how to calculate the deadtime may be only available from technical manuals or otherwise complex. **If it requires fetching from signals, it is recommended to cache the value during the StandardDetector `prepare` method.** | ||
|
||
.. literalinclude:: ../examples/foo_detector.py | ||
:pyobject: FooController | ||
|
||
:py:class:`FooDetector` ties the Driver, Controller and data persistence layer together. The example :py:class:`FooDetector` writes h5 files using the standard NDPlugin. It additionally supports the :py:class:`HasHints` protocol which is optional but recommended. | ||
|
||
Its initialiser assumes the NSLS-II AreaDetector plugin EPICS address suffixes as defaults but allows overriding: **this pattern is recommended for consistency**. | ||
If the :py:class:`FooDriver` signals that should be read as configuration, they should be added to the "config_sigs" passed to the super. | ||
|
||
.. literalinclude:: ../examples/foo_detector.py | ||
:pyobject: FooDetector | ||
|
||
|
||
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 | ||
: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 | ||
:pyobject: PandaHDFWriter | ||
|
||
The PandA StandardDetector implementation simply ties the component parts and its child devices together. | ||
|
||
.. literalinclude:: ../../src/ophyd_async/panda/_hdf_panda.py | ||
:pyobject: HDFPanda |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.