ophyd-async 0.9.0a1__py3-none-any.whl → 0.10.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ophyd_async/__init__.py +5 -8
- ophyd_async/_docs_parser.py +12 -0
- ophyd_async/_version.py +9 -4
- ophyd_async/core/__init__.py +102 -74
- ophyd_async/core/_derived_signal.py +271 -0
- ophyd_async/core/_derived_signal_backend.py +300 -0
- ophyd_async/core/_detector.py +158 -153
- ophyd_async/core/_device.py +143 -115
- ophyd_async/core/_device_filler.py +82 -9
- ophyd_async/core/_flyer.py +16 -7
- ophyd_async/core/_hdf_dataset.py +29 -22
- ophyd_async/core/_log.py +14 -23
- ophyd_async/core/_mock_signal_backend.py +11 -3
- ophyd_async/core/_protocol.py +65 -45
- ophyd_async/core/_providers.py +28 -9
- ophyd_async/core/_readable.py +74 -58
- ophyd_async/core/_settings.py +113 -0
- ophyd_async/core/_signal.py +304 -174
- ophyd_async/core/_signal_backend.py +60 -14
- ophyd_async/core/_soft_signal_backend.py +18 -12
- ophyd_async/core/_status.py +72 -24
- ophyd_async/core/_table.py +54 -17
- ophyd_async/core/_utils.py +101 -52
- ophyd_async/core/_yaml_settings.py +66 -0
- ophyd_async/epics/__init__.py +1 -0
- ophyd_async/epics/adandor/__init__.py +9 -0
- ophyd_async/epics/adandor/_andor.py +45 -0
- ophyd_async/epics/adandor/_andor_controller.py +51 -0
- ophyd_async/epics/adandor/_andor_io.py +34 -0
- ophyd_async/epics/adaravis/__init__.py +8 -1
- ophyd_async/epics/adaravis/_aravis.py +23 -41
- ophyd_async/epics/adaravis/_aravis_controller.py +23 -55
- ophyd_async/epics/adaravis/_aravis_io.py +13 -28
- ophyd_async/epics/adcore/__init__.py +36 -14
- ophyd_async/epics/adcore/_core_detector.py +81 -0
- ophyd_async/epics/adcore/_core_io.py +145 -95
- ophyd_async/epics/adcore/_core_logic.py +179 -88
- ophyd_async/epics/adcore/_core_writer.py +223 -0
- ophyd_async/epics/adcore/_hdf_writer.py +51 -92
- ophyd_async/epics/adcore/_jpeg_writer.py +26 -0
- ophyd_async/epics/adcore/_single_trigger.py +6 -5
- ophyd_async/epics/adcore/_tiff_writer.py +26 -0
- ophyd_async/epics/adcore/_utils.py +3 -2
- ophyd_async/epics/adkinetix/__init__.py +2 -1
- ophyd_async/epics/adkinetix/_kinetix.py +32 -27
- ophyd_async/epics/adkinetix/_kinetix_controller.py +11 -21
- ophyd_async/epics/adkinetix/_kinetix_io.py +12 -13
- ophyd_async/epics/adpilatus/__init__.py +7 -2
- ophyd_async/epics/adpilatus/_pilatus.py +28 -40
- ophyd_async/epics/adpilatus/_pilatus_controller.py +25 -22
- ophyd_async/epics/adpilatus/_pilatus_io.py +11 -9
- ophyd_async/epics/adsimdetector/__init__.py +8 -1
- ophyd_async/epics/adsimdetector/_sim.py +22 -16
- ophyd_async/epics/adsimdetector/_sim_controller.py +9 -43
- ophyd_async/epics/adsimdetector/_sim_io.py +10 -0
- ophyd_async/epics/advimba/__init__.py +10 -1
- ophyd_async/epics/advimba/_vimba.py +26 -25
- ophyd_async/epics/advimba/_vimba_controller.py +12 -24
- ophyd_async/epics/advimba/_vimba_io.py +23 -28
- ophyd_async/epics/core/_aioca.py +66 -30
- ophyd_async/epics/core/_epics_connector.py +4 -0
- ophyd_async/epics/core/_epics_device.py +2 -0
- ophyd_async/epics/core/_p4p.py +50 -18
- ophyd_async/epics/core/_pvi_connector.py +65 -8
- ophyd_async/epics/core/_signal.py +51 -51
- ophyd_async/epics/core/_util.py +5 -5
- ophyd_async/epics/demo/__init__.py +11 -49
- ophyd_async/epics/demo/__main__.py +31 -0
- ophyd_async/epics/demo/_ioc.py +32 -0
- ophyd_async/epics/demo/_motor.py +82 -0
- ophyd_async/epics/demo/_point_detector.py +42 -0
- ophyd_async/epics/demo/_point_detector_channel.py +22 -0
- ophyd_async/epics/demo/_stage.py +15 -0
- ophyd_async/epics/demo/{mover.db → motor.db} +2 -1
- ophyd_async/epics/demo/point_detector.db +59 -0
- ophyd_async/epics/demo/point_detector_channel.db +21 -0
- ophyd_async/epics/eiger/_eiger.py +1 -3
- ophyd_async/epics/eiger/_eiger_controller.py +11 -4
- ophyd_async/epics/eiger/_eiger_io.py +2 -0
- ophyd_async/epics/eiger/_odin_io.py +1 -2
- ophyd_async/epics/motor.py +83 -38
- ophyd_async/epics/signal.py +4 -1
- ophyd_async/epics/testing/__init__.py +14 -14
- ophyd_async/epics/testing/_example_ioc.py +68 -73
- ophyd_async/epics/testing/_utils.py +19 -44
- ophyd_async/epics/testing/test_records.db +16 -0
- ophyd_async/epics/testing/test_records_pva.db +17 -16
- ophyd_async/fastcs/__init__.py +1 -0
- ophyd_async/fastcs/core.py +6 -0
- ophyd_async/fastcs/odin/__init__.py +1 -0
- ophyd_async/fastcs/panda/__init__.py +8 -8
- ophyd_async/fastcs/panda/_block.py +29 -9
- ophyd_async/fastcs/panda/_control.py +12 -2
- ophyd_async/fastcs/panda/_hdf_panda.py +5 -1
- ophyd_async/fastcs/panda/_table.py +13 -7
- ophyd_async/fastcs/panda/_trigger.py +23 -9
- ophyd_async/fastcs/panda/_writer.py +27 -30
- ophyd_async/plan_stubs/__init__.py +16 -0
- ophyd_async/plan_stubs/_ensure_connected.py +12 -17
- ophyd_async/plan_stubs/_fly.py +3 -5
- ophyd_async/plan_stubs/_nd_attributes.py +9 -5
- ophyd_async/plan_stubs/_panda.py +14 -0
- ophyd_async/plan_stubs/_settings.py +152 -0
- ophyd_async/plan_stubs/_utils.py +3 -0
- ophyd_async/plan_stubs/_wait_for_awaitable.py +13 -0
- ophyd_async/sim/__init__.py +29 -0
- ophyd_async/sim/__main__.py +43 -0
- ophyd_async/sim/_blob_detector.py +33 -0
- ophyd_async/sim/_blob_detector_controller.py +48 -0
- ophyd_async/sim/_blob_detector_writer.py +105 -0
- ophyd_async/sim/_mirror_horizontal.py +46 -0
- ophyd_async/sim/_mirror_vertical.py +74 -0
- ophyd_async/sim/_motor.py +233 -0
- ophyd_async/sim/_pattern_generator.py +124 -0
- ophyd_async/sim/_point_detector.py +86 -0
- ophyd_async/sim/_stage.py +19 -0
- ophyd_async/tango/__init__.py +1 -0
- ophyd_async/tango/core/__init__.py +6 -1
- ophyd_async/tango/core/_base_device.py +41 -33
- ophyd_async/tango/core/_converters.py +81 -0
- ophyd_async/tango/core/_signal.py +21 -33
- ophyd_async/tango/core/_tango_readable.py +2 -19
- ophyd_async/tango/core/_tango_transport.py +148 -74
- ophyd_async/tango/core/_utils.py +47 -0
- ophyd_async/tango/demo/_counter.py +2 -0
- ophyd_async/tango/demo/_detector.py +2 -0
- ophyd_async/tango/demo/_mover.py +10 -6
- ophyd_async/tango/demo/_tango/_servers.py +4 -0
- ophyd_async/tango/testing/__init__.py +6 -0
- ophyd_async/tango/testing/_one_of_everything.py +200 -0
- ophyd_async/testing/__init__.py +48 -7
- ophyd_async/testing/__pytest_assert_rewrite.py +4 -0
- ophyd_async/testing/_assert.py +200 -96
- ophyd_async/testing/_mock_signal_utils.py +59 -73
- ophyd_async/testing/_one_of_everything.py +146 -0
- ophyd_async/testing/_single_derived.py +87 -0
- ophyd_async/testing/_utils.py +3 -0
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/METADATA +25 -26
- ophyd_async-0.10.0a1.dist-info/RECORD +149 -0
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/WHEEL +1 -1
- ophyd_async/core/_device_save_loader.py +0 -274
- ophyd_async/epics/demo/_mover.py +0 -95
- ophyd_async/epics/demo/_sensor.py +0 -37
- ophyd_async/epics/demo/sensor.db +0 -19
- ophyd_async/fastcs/panda/_utils.py +0 -16
- ophyd_async/sim/demo/__init__.py +0 -19
- ophyd_async/sim/demo/_pattern_detector/__init__.py +0 -13
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector.py +0 -42
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector_controller.py +0 -62
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector_writer.py +0 -41
- ophyd_async/sim/demo/_pattern_detector/_pattern_generator.py +0 -207
- ophyd_async/sim/demo/_sim_motor.py +0 -107
- ophyd_async/sim/testing/__init__.py +0 -0
- ophyd_async-0.9.0a1.dist-info/RECORD +0 -119
- ophyd_async-0.9.0a1.dist-info/entry_points.txt +0 -2
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info/licenses}/LICENSE +0 -0
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/top_level.txt +0 -0
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from typing import Literal
|
|
3
2
|
|
|
4
|
-
from ophyd_async.core import
|
|
5
|
-
AsyncStatus,
|
|
6
|
-
DetectorController,
|
|
7
|
-
DetectorTrigger,
|
|
8
|
-
TriggerInfo,
|
|
9
|
-
set_and_wait_for_value,
|
|
10
|
-
)
|
|
3
|
+
from ophyd_async.core import DetectorTrigger, TriggerInfo
|
|
11
4
|
from ophyd_async.epics import adcore
|
|
12
5
|
|
|
13
6
|
from ._aravis_io import AravisDriverIO, AravisTriggerMode, AravisTriggerSource
|
|
@@ -18,60 +11,35 @@ from ._aravis_io import AravisDriverIO, AravisTriggerMode, AravisTriggerSource
|
|
|
18
11
|
_HIGHEST_POSSIBLE_DEADTIME = 1961e-6
|
|
19
12
|
|
|
20
13
|
|
|
21
|
-
class AravisController(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def __init__(self, driver: AravisDriverIO, gpio_number: GPIO_NUMBER) -> None:
|
|
25
|
-
self._drv = driver
|
|
26
|
-
self.gpio_number = gpio_number
|
|
27
|
-
self._arm_status: AsyncStatus | None = None
|
|
14
|
+
class AravisController(adcore.ADBaseController[AravisDriverIO]):
|
|
15
|
+
"""`DetectorController` for an `AravisDriverIO`."""
|
|
28
16
|
|
|
29
17
|
def get_deadtime(self, exposure: float | None) -> float:
|
|
30
18
|
return _HIGHEST_POSSIBLE_DEADTIME
|
|
31
19
|
|
|
32
|
-
async def prepare(self, trigger_info: TriggerInfo):
|
|
33
|
-
if trigger_info.total_number_of_triggers == 0:
|
|
34
|
-
image_mode = adcore.ImageMode.CONTINUOUS
|
|
35
|
-
else:
|
|
36
|
-
image_mode = adcore.ImageMode.MULTIPLE
|
|
20
|
+
async def prepare(self, trigger_info: TriggerInfo) -> None:
|
|
37
21
|
if (exposure := trigger_info.livetime) is not None:
|
|
38
|
-
await self.
|
|
39
|
-
|
|
40
|
-
trigger_mode, trigger_source = self._get_trigger_info(trigger_info.trigger)
|
|
41
|
-
# trigger mode must be set first and on it's own!
|
|
42
|
-
await self._drv.trigger_mode.set(trigger_mode)
|
|
43
|
-
|
|
44
|
-
await asyncio.gather(
|
|
45
|
-
self._drv.trigger_source.set(trigger_source),
|
|
46
|
-
self._drv.num_images.set(trigger_info.total_number_of_triggers),
|
|
47
|
-
self._drv.image_mode.set(image_mode),
|
|
48
|
-
)
|
|
22
|
+
await self.driver.acquire_time.set(exposure)
|
|
49
23
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if self._arm_status:
|
|
55
|
-
await self._arm_status
|
|
56
|
-
|
|
57
|
-
def _get_trigger_info(
|
|
58
|
-
self, trigger: DetectorTrigger
|
|
59
|
-
) -> tuple[AravisTriggerMode, AravisTriggerSource]:
|
|
60
|
-
supported_trigger_types = (
|
|
24
|
+
if trigger_info.trigger is DetectorTrigger.INTERNAL:
|
|
25
|
+
# Set trigger mode off to ignore the trigger source
|
|
26
|
+
await self.driver.trigger_mode.set(AravisTriggerMode.OFF)
|
|
27
|
+
elif trigger_info.trigger in {
|
|
61
28
|
DetectorTrigger.CONSTANT_GATE,
|
|
62
29
|
DetectorTrigger.EDGE_TRIGGER,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
f"types: {supported_trigger_types} but was asked to "
|
|
69
|
-
f"use {trigger}"
|
|
70
|
-
)
|
|
71
|
-
if trigger == DetectorTrigger.INTERNAL:
|
|
72
|
-
return AravisTriggerMode.OFF, AravisTriggerSource.FREERUN
|
|
30
|
+
}:
|
|
31
|
+
# Trigger on the rising edge of Line1
|
|
32
|
+
# trigger mode must be set first and on it's own!
|
|
33
|
+
await self.driver.trigger_mode.set(AravisTriggerMode.ON)
|
|
34
|
+
await self.driver.trigger_source.set(AravisTriggerSource.LINE1)
|
|
73
35
|
else:
|
|
74
|
-
|
|
36
|
+
raise ValueError(f"ADAravis does not support {trigger_info.trigger}")
|
|
75
37
|
|
|
76
|
-
|
|
77
|
-
|
|
38
|
+
if trigger_info.total_number_of_triggers == 0:
|
|
39
|
+
image_mode = adcore.ADImageMode.CONTINUOUS
|
|
40
|
+
else:
|
|
41
|
+
image_mode = adcore.ADImageMode.MULTIPLE
|
|
42
|
+
await asyncio.gather(
|
|
43
|
+
self.driver.num_images.set(trigger_info.total_number_of_triggers),
|
|
44
|
+
self.driver.image_mode.set(image_mode),
|
|
45
|
+
)
|
|
@@ -1,46 +1,31 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Annotated as A
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import SignalRW, StrictEnum, SubsetEnum
|
|
2
4
|
from ophyd_async.epics import adcore
|
|
3
|
-
from ophyd_async.epics.core import
|
|
5
|
+
from ophyd_async.epics.core import PvSuffix
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
class AravisTriggerMode(StrictEnum):
|
|
7
|
-
"""GigEVision GenICAM standard
|
|
9
|
+
"""GigEVision GenICAM standard TriggerMode."""
|
|
8
10
|
|
|
9
11
|
ON = "On"
|
|
10
|
-
|
|
12
|
+
"""Use TriggerSource to trigger each frame"""
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
"""
|
|
14
|
-
To enable hardware triggered scanning, line_N must support each N in GPIO_NUMBER.
|
|
15
|
-
To enable software triggered scanning, freerun must be supported.
|
|
16
|
-
Other enumerated values may or may not be preset.
|
|
17
|
-
To prevent requiring one Enum class per possible configuration, we set as this Enum
|
|
18
|
-
but read from the underlying signal as a str.
|
|
19
|
-
"""
|
|
14
|
+
OFF = "Off"
|
|
15
|
+
"""Just trigger as fast as you can"""
|
|
20
16
|
|
|
21
17
|
|
|
22
18
|
class AravisTriggerSource(SubsetEnum):
|
|
23
|
-
|
|
19
|
+
"""Which trigger source to use when TriggerMode=On."""
|
|
20
|
+
|
|
24
21
|
LINE1 = "Line1"
|
|
25
22
|
|
|
26
23
|
|
|
27
24
|
class AravisDriverIO(adcore.ADBaseIO):
|
|
28
|
-
|
|
29
|
-
"""Generic Driver supporting the Manta and Mako drivers.
|
|
30
|
-
Fetches deadtime prior to use in a Streaming scan.
|
|
31
|
-
|
|
32
|
-
Requires driver firmware up to date:
|
|
33
|
-
- Model_RBV must be of the form "^(Mako|Manta) (model)$"
|
|
25
|
+
"""Generic Driver supporting all GiGE cameras.
|
|
34
26
|
|
|
35
27
|
This mirrors the interface provided by ADAravis/db/aravisCamera.template.
|
|
36
28
|
"""
|
|
37
29
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
AravisTriggerMode, prefix + "TriggerMode"
|
|
41
|
-
)
|
|
42
|
-
self.trigger_source = epics_signal_rw_rbv(
|
|
43
|
-
AravisTriggerSource, # type: ignore
|
|
44
|
-
prefix + "TriggerSource",
|
|
45
|
-
)
|
|
46
|
-
super().__init__(prefix, name=name)
|
|
30
|
+
trigger_mode: A[SignalRW[AravisTriggerMode], PvSuffix.rbv("TriggerMode")]
|
|
31
|
+
trigger_source: A[SignalRW[AravisTriggerSource], PvSuffix.rbv("TriggerSource")]
|
|
@@ -1,22 +1,33 @@
|
|
|
1
|
+
"""Core components of the areaDetector software.
|
|
2
|
+
|
|
3
|
+
https://github.com/areaDetector/ADCore
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ._core_detector import AreaDetector, ContAcqAreaDetector
|
|
1
7
|
from ._core_io import (
|
|
8
|
+
ADBaseDatasetDescriber,
|
|
2
9
|
ADBaseIO,
|
|
3
|
-
|
|
10
|
+
ADCallbacks,
|
|
11
|
+
ADCompression,
|
|
12
|
+
ADState,
|
|
4
13
|
NDArrayBaseIO,
|
|
14
|
+
NDCBFlushOnSoftTrgMode,
|
|
5
15
|
NDFileHDFIO,
|
|
16
|
+
NDFileIO,
|
|
17
|
+
NDPluginBaseIO,
|
|
18
|
+
NDPluginCBIO,
|
|
6
19
|
NDPluginStatsIO,
|
|
7
20
|
)
|
|
8
|
-
from ._core_logic import
|
|
9
|
-
|
|
10
|
-
ADBaseDatasetDescriber,
|
|
11
|
-
set_exposure_time_and_acquire_period_if_supplied,
|
|
12
|
-
start_acquiring_driver_and_ensure_status,
|
|
13
|
-
)
|
|
21
|
+
from ._core_logic import DEFAULT_GOOD_STATES, ADBaseContAcqController, ADBaseController
|
|
22
|
+
from ._core_writer import ADWriter
|
|
14
23
|
from ._hdf_writer import ADHDFWriter
|
|
24
|
+
from ._jpeg_writer import ADJPEGWriter
|
|
15
25
|
from ._single_trigger import SingleTriggerDetector
|
|
26
|
+
from ._tiff_writer import ADTIFFWriter
|
|
16
27
|
from ._utils import (
|
|
17
28
|
ADBaseDataType,
|
|
18
|
-
|
|
19
|
-
|
|
29
|
+
ADFileWriteMode,
|
|
30
|
+
ADImageMode,
|
|
20
31
|
NDAttributeDataType,
|
|
21
32
|
NDAttributeParam,
|
|
22
33
|
NDAttributePv,
|
|
@@ -26,22 +37,33 @@ from ._utils import (
|
|
|
26
37
|
|
|
27
38
|
__all__ = [
|
|
28
39
|
"ADBaseIO",
|
|
29
|
-
"
|
|
40
|
+
"ADCallbacks",
|
|
41
|
+
"ADCompression",
|
|
42
|
+
"ADBaseContAcqController",
|
|
43
|
+
"AreaDetector",
|
|
44
|
+
"ADState",
|
|
45
|
+
"ContAcqAreaDetector",
|
|
30
46
|
"NDArrayBaseIO",
|
|
47
|
+
"NDFileIO",
|
|
31
48
|
"NDFileHDFIO",
|
|
49
|
+
"NDPluginBaseIO",
|
|
32
50
|
"NDPluginStatsIO",
|
|
33
51
|
"DEFAULT_GOOD_STATES",
|
|
34
52
|
"ADBaseDatasetDescriber",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
53
|
+
"ADBaseController",
|
|
54
|
+
"ADWriter",
|
|
37
55
|
"ADHDFWriter",
|
|
56
|
+
"ADTIFFWriter",
|
|
57
|
+
"ADJPEGWriter",
|
|
38
58
|
"SingleTriggerDetector",
|
|
39
59
|
"ADBaseDataType",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
60
|
+
"ADFileWriteMode",
|
|
61
|
+
"ADImageMode",
|
|
42
62
|
"NDAttributePv",
|
|
43
63
|
"NDAttributeParam",
|
|
44
64
|
"NDAttributeDataType",
|
|
45
65
|
"stop_busy_record",
|
|
46
66
|
"NDAttributePvDbrType",
|
|
67
|
+
"NDCBFlushOnSoftTrgMode",
|
|
68
|
+
"NDPluginCBIO",
|
|
47
69
|
]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import SignalR, StandardDetector
|
|
4
|
+
from ophyd_async.core._providers import PathProvider
|
|
5
|
+
|
|
6
|
+
from ._core_io import ADBaseIO, NDPluginBaseIO, NDPluginCBIO
|
|
7
|
+
from ._core_logic import ADBaseContAcqController, ADBaseControllerT
|
|
8
|
+
from ._core_writer import ADWriter
|
|
9
|
+
from ._hdf_writer import ADHDFWriter
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AreaDetector(StandardDetector[ADBaseControllerT, ADWriter]):
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
controller: ADBaseControllerT,
|
|
16
|
+
writer: ADWriter,
|
|
17
|
+
plugins: dict[str, NDPluginBaseIO] | None = None,
|
|
18
|
+
config_sigs: Sequence[SignalR] = (),
|
|
19
|
+
name: str = "",
|
|
20
|
+
):
|
|
21
|
+
self.driver = controller.driver
|
|
22
|
+
self.fileio = writer.fileio
|
|
23
|
+
|
|
24
|
+
if plugins is not None:
|
|
25
|
+
for name, plugin in plugins.items():
|
|
26
|
+
setattr(self, name, plugin)
|
|
27
|
+
|
|
28
|
+
super().__init__(
|
|
29
|
+
controller,
|
|
30
|
+
writer,
|
|
31
|
+
(self.driver.acquire_period, self.driver.acquire_time, *config_sigs),
|
|
32
|
+
name=name,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def get_plugin(
|
|
36
|
+
self, name: str, plugin_type: type[NDPluginBaseIO] = NDPluginBaseIO
|
|
37
|
+
) -> NDPluginBaseIO:
|
|
38
|
+
plugin = getattr(self, name, None)
|
|
39
|
+
if not isinstance(plugin, plugin_type):
|
|
40
|
+
raise TypeError(
|
|
41
|
+
f"Expected {self.name}.{name} to be a {plugin_type}, got {plugin}"
|
|
42
|
+
)
|
|
43
|
+
return plugin
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ContAcqAreaDetector(AreaDetector[ADBaseContAcqController]):
|
|
47
|
+
"""Ophyd-async implementation of a continuously acquiring AreaDetector."""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
prefix: str,
|
|
52
|
+
path_provider: PathProvider,
|
|
53
|
+
drv_cls: type[ADBaseIO] = ADBaseIO,
|
|
54
|
+
drv_suffix: str = "cam1:",
|
|
55
|
+
cb_suffix: str = "CB1:",
|
|
56
|
+
writer_cls: type[ADWriter] = ADHDFWriter,
|
|
57
|
+
fileio_suffix: str | None = None,
|
|
58
|
+
name: str = "",
|
|
59
|
+
plugins: dict[str, NDPluginBaseIO] | None = None,
|
|
60
|
+
config_sigs: Sequence[SignalR] = (),
|
|
61
|
+
):
|
|
62
|
+
self.cb_plugin = NDPluginCBIO(prefix + cb_suffix)
|
|
63
|
+
driver = drv_cls(prefix + drv_suffix)
|
|
64
|
+
controller = ADBaseContAcqController(driver, self.cb_plugin)
|
|
65
|
+
|
|
66
|
+
writer = writer_cls.with_io(
|
|
67
|
+
prefix,
|
|
68
|
+
path_provider,
|
|
69
|
+
# Since the CB plugin controls acq, use it when checking shape
|
|
70
|
+
dataset_source=self.cb_plugin,
|
|
71
|
+
fileio_suffix=fileio_suffix,
|
|
72
|
+
plugins=plugins,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
super().__init__(
|
|
76
|
+
controller=controller,
|
|
77
|
+
writer=writer,
|
|
78
|
+
plugins=plugins,
|
|
79
|
+
name=name,
|
|
80
|
+
config_sigs=config_sigs,
|
|
81
|
+
)
|
|
@@ -1,75 +1,96 @@
|
|
|
1
|
-
|
|
2
|
-
from
|
|
3
|
-
epics_signal_r,
|
|
4
|
-
epics_signal_rw,
|
|
5
|
-
epics_signal_rw_rbv,
|
|
6
|
-
)
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Annotated as A
|
|
7
3
|
|
|
8
|
-
from .
|
|
4
|
+
from ophyd_async.core import DatasetDescriber, SignalR, SignalRW, StrictEnum
|
|
5
|
+
from ophyd_async.epics.core import EpicsDevice, PvSuffix
|
|
9
6
|
|
|
7
|
+
from ._utils import ADBaseDataType, ADFileWriteMode, ADImageMode, convert_ad_dtype_to_np
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
|
|
10
|
+
class ADCallbacks(StrictEnum):
|
|
12
11
|
ENABLE = "Enable"
|
|
13
12
|
DISABLE = "Disable"
|
|
14
13
|
|
|
15
14
|
|
|
16
|
-
class NDArrayBaseIO(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
self.array_size_y = epics_signal_r(int, prefix + "ArraySizeY_RBV")
|
|
23
|
-
self.data_type = epics_signal_r(ADBaseDataType, prefix + "DataType_RBV")
|
|
24
|
-
self.array_counter = epics_signal_rw_rbv(int, prefix + "ArrayCounter")
|
|
25
|
-
# There is no _RBV for this one
|
|
26
|
-
self.wait_for_plugins = epics_signal_rw(bool, prefix + "WaitForPlugins")
|
|
27
|
-
super().__init__(name=name)
|
|
15
|
+
class NDArrayBaseIO(EpicsDevice):
|
|
16
|
+
"""Class responsible for passing detector data from drivers to pluglins.
|
|
17
|
+
|
|
18
|
+
This mirrors the interface provided by ADCore/db/NDArrayBase.template.
|
|
19
|
+
See HTML docs at https://areadetector.github.io/areaDetector/ADCore/NDArray.html
|
|
20
|
+
"""
|
|
28
21
|
|
|
22
|
+
unique_id: A[SignalR[int], PvSuffix("UniqueId_RBV")]
|
|
23
|
+
nd_attributes_file: A[SignalRW[str], PvSuffix("NDAttributesFile")]
|
|
24
|
+
acquire: A[SignalRW[bool], PvSuffix.rbv("Acquire")]
|
|
25
|
+
array_size_x: A[SignalR[int], PvSuffix("ArraySizeX_RBV")]
|
|
26
|
+
array_size_y: A[SignalR[int], PvSuffix("ArraySizeY_RBV")]
|
|
27
|
+
data_type: A[SignalR[ADBaseDataType], PvSuffix("DataType_RBV")]
|
|
28
|
+
array_counter: A[SignalRW[int], PvSuffix.rbv("ArrayCounter")]
|
|
29
|
+
# There is no _RBV for this one
|
|
30
|
+
wait_for_plugins: A[SignalRW[bool], PvSuffix("WaitForPlugins")]
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
self.
|
|
34
|
-
|
|
32
|
+
|
|
33
|
+
class ADBaseDatasetDescriber(DatasetDescriber):
|
|
34
|
+
def __init__(self, driver: NDArrayBaseIO) -> None:
|
|
35
|
+
self._driver = driver
|
|
36
|
+
|
|
37
|
+
async def np_datatype(self) -> str:
|
|
38
|
+
return convert_ad_dtype_to_np(await self._driver.data_type.get_value())
|
|
39
|
+
|
|
40
|
+
async def shape(self) -> tuple[int, int]:
|
|
41
|
+
shape = await asyncio.gather(
|
|
42
|
+
self._driver.array_size_y.get_value(),
|
|
43
|
+
self._driver.array_size_x.get_value(),
|
|
35
44
|
)
|
|
36
|
-
|
|
37
|
-
self.array_size0 = epics_signal_r(int, prefix + "ArraySize0_RBV")
|
|
38
|
-
self.array_size1 = epics_signal_r(int, prefix + "ArraySize1_RBV")
|
|
39
|
-
super().__init__(prefix, name)
|
|
45
|
+
return shape
|
|
40
46
|
|
|
41
47
|
|
|
42
|
-
class
|
|
43
|
-
"""
|
|
44
|
-
|
|
48
|
+
class NDPluginBaseIO(NDArrayBaseIO):
|
|
49
|
+
"""Base class from which plugins are derived.
|
|
50
|
+
|
|
51
|
+
This mirrors the interface provided by ADCore/db/NDPluginBase.template.
|
|
52
|
+
See HTML docs at https://areadetector.github.io/areaDetector/ADCore/NDPluginDriver.html
|
|
45
53
|
"""
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
self.cursor_y = epics_signal_rw(int, prefix + "CursorY")
|
|
61
|
-
# Array Histogram
|
|
62
|
-
self.compute_histogram = epics_signal_rw(bool, prefix + "ComputeHistogram")
|
|
63
|
-
self.hist_size = epics_signal_rw(int, prefix + "HistSize")
|
|
64
|
-
self.hist_min = epics_signal_rw(float, prefix + "HistMin")
|
|
65
|
-
self.hist_max = epics_signal_rw(float, prefix + "HistMax")
|
|
66
|
-
super().__init__(prefix, name)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
class DetectorState(StrictEnum):
|
|
55
|
+
nd_array_port: A[SignalRW[str], PvSuffix.rbv("NDArrayPort")]
|
|
56
|
+
enable_callbacks: A[SignalRW[ADCallbacks], PvSuffix.rbv("EnableCallbacks")]
|
|
57
|
+
nd_array_address: A[SignalRW[int], PvSuffix.rbv("NDArrayAddress")]
|
|
58
|
+
array_size0: A[SignalR[int], PvSuffix("ArraySize0_RBV")]
|
|
59
|
+
array_size1: A[SignalR[int], PvSuffix("ArraySize1_RBV")]
|
|
60
|
+
queue_size: A[SignalRW[int], PvSuffix.rbv("QueueSize")]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class NDPluginStatsIO(NDPluginBaseIO):
|
|
64
|
+
"""Plugin for computing statistics from an image or ROI within an image.
|
|
65
|
+
|
|
66
|
+
This mirrors the interface provided by ADCore/db/NDStats.template.
|
|
67
|
+
See HTML docs at https://areadetector.github.io/areaDetector/ADCore/NDPluginStats.html
|
|
70
68
|
"""
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
|
|
70
|
+
# Basic statistics
|
|
71
|
+
compute_statistics: A[SignalRW[bool], PvSuffix.rbv("ComputeStatistics")]
|
|
72
|
+
bgd_width: A[SignalRW[int], PvSuffix.rbv("BgdWidth")]
|
|
73
|
+
total_array: A[SignalRW[float], PvSuffix.rbv("TotalArray")]
|
|
74
|
+
# Centroid statistics
|
|
75
|
+
compute_centroid: A[SignalRW[bool], PvSuffix.rbv("ComputeCentroid")]
|
|
76
|
+
centroid_threshold: A[SignalRW[float], PvSuffix.rbv("CentroidThreshold")]
|
|
77
|
+
# X and Y Profiles
|
|
78
|
+
compute_profiles: A[SignalRW[bool], PvSuffix.rbv("ComputeProfiles")]
|
|
79
|
+
profile_size_x: A[SignalRW[int], PvSuffix.rbv("ProfileSizeX")]
|
|
80
|
+
profile_size_y: A[SignalRW[int], PvSuffix.rbv("ProfileSizeY")]
|
|
81
|
+
cursor_x: A[SignalRW[int], PvSuffix.rbv("CursorX")]
|
|
82
|
+
cursor_y: A[SignalRW[int], PvSuffix.rbv("CursorY")]
|
|
83
|
+
# Array Histogram
|
|
84
|
+
compute_histogram: A[SignalRW[bool], PvSuffix.rbv("ComputeHistogram")]
|
|
85
|
+
hist_size: A[SignalRW[int], PvSuffix.rbv("HistSize")]
|
|
86
|
+
hist_min: A[SignalRW[float], PvSuffix.rbv("HistMin")]
|
|
87
|
+
hist_max: A[SignalRW[float], PvSuffix.rbv("HistMax")]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ADState(StrictEnum):
|
|
91
|
+
"""Default set of states of an AreaDetector driver.
|
|
92
|
+
|
|
93
|
+
See definition in ADApp/ADSrc/ADDriver.h in https://github.com/areaDetector/ADCore.
|
|
73
94
|
"""
|
|
74
95
|
|
|
75
96
|
IDLE = "Idle"
|
|
@@ -86,19 +107,20 @@ class DetectorState(StrictEnum):
|
|
|
86
107
|
|
|
87
108
|
|
|
88
109
|
class ADBaseIO(NDArrayBaseIO):
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
110
|
+
"""Base class from which areaDetector drivers are derived.
|
|
111
|
+
|
|
112
|
+
This mirrors the interface provided by ADCore/db/ADBase.template.
|
|
113
|
+
See HTML docs at https://areadetector.github.io/areaDetector/ADCore/ADDriver.html
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
acquire_time: A[SignalRW[float], PvSuffix.rbv("AcquireTime")]
|
|
117
|
+
acquire_period: A[SignalRW[float], PvSuffix.rbv("AcquirePeriod")]
|
|
118
|
+
num_images: A[SignalRW[int], PvSuffix.rbv("NumImages")]
|
|
119
|
+
image_mode: A[SignalRW[ADImageMode], PvSuffix.rbv("ImageMode")]
|
|
120
|
+
detector_state: A[SignalR[ADState], PvSuffix("DetectorState_RBV")]
|
|
99
121
|
|
|
100
122
|
|
|
101
|
-
class
|
|
123
|
+
class ADCompression(StrictEnum):
|
|
102
124
|
NONE = "None"
|
|
103
125
|
NBIT = "N-bit"
|
|
104
126
|
SZIP = "szip"
|
|
@@ -109,30 +131,58 @@ class Compression(StrictEnum):
|
|
|
109
131
|
JPEG = "JPEG"
|
|
110
132
|
|
|
111
133
|
|
|
112
|
-
class
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
class NDFileIO(NDPluginBaseIO):
|
|
135
|
+
"""Base class from which file plugins are derived.
|
|
136
|
+
|
|
137
|
+
This mirrors the interface provided by ADCore/db/NDFile.template.
|
|
138
|
+
See HTML docs at https://areadetector.github.io/areaDetector/ADCore/NDPluginFile.html
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
file_path: A[SignalRW[str], PvSuffix.rbv("FilePath")]
|
|
142
|
+
file_name: A[SignalRW[str], PvSuffix.rbv("FileName")]
|
|
143
|
+
file_path_exists: A[SignalR[bool], PvSuffix("FilePathExists_RBV")]
|
|
144
|
+
file_template: A[SignalRW[str], PvSuffix.rbv("FileTemplate")]
|
|
145
|
+
full_file_name: A[SignalR[str], PvSuffix("FullFileName_RBV")]
|
|
146
|
+
file_number: A[SignalRW[int], PvSuffix("FileNumber")]
|
|
147
|
+
auto_increment: A[SignalRW[bool], PvSuffix("AutoIncrement")]
|
|
148
|
+
file_write_mode: A[SignalRW[ADFileWriteMode], PvSuffix.rbv("FileWriteMode")]
|
|
149
|
+
num_capture: A[SignalRW[int], PvSuffix.rbv("NumCapture")]
|
|
150
|
+
num_captured: A[SignalR[int], PvSuffix("NumCaptured_RBV")]
|
|
151
|
+
capture: A[SignalRW[bool], PvSuffix.rbv("Capture")]
|
|
152
|
+
array_size0: A[SignalR[int], PvSuffix("ArraySize0")]
|
|
153
|
+
array_size1: A[SignalR[int], PvSuffix("ArraySize1")]
|
|
154
|
+
create_directory: A[SignalRW[int], PvSuffix("CreateDirectory")]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class NDFileHDFIO(NDFileIO):
|
|
158
|
+
"""Plugin for storing data in HDF5 file format.
|
|
159
|
+
|
|
160
|
+
This mirrors the interface provided by ADCore/db/NDFileHDF5.template.
|
|
161
|
+
See HTML docs at https://areadetector.github.io/areaDetector/ADCore/NDFileHDF5.html
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
position_mode: A[SignalRW[bool], PvSuffix.rbv("PositionMode")]
|
|
165
|
+
compression: A[SignalRW[ADCompression], PvSuffix.rbv("Compression")]
|
|
166
|
+
num_extra_dims: A[SignalRW[int], PvSuffix.rbv("NumExtraDims")]
|
|
167
|
+
swmr_mode: A[SignalRW[bool], PvSuffix.rbv("SWMRMode")]
|
|
168
|
+
flush_now: A[SignalRW[bool], PvSuffix("FlushNow")]
|
|
169
|
+
xml_file_name: A[SignalRW[str], PvSuffix.rbv("XMLFileName")]
|
|
170
|
+
num_frames_chunks: A[SignalR[int], PvSuffix("NumFramesChunks_RBV")]
|
|
171
|
+
chunk_size_auto: A[SignalRW[bool], PvSuffix.rbv("ChunkSizeAuto")]
|
|
172
|
+
lazy_open: A[SignalRW[bool], PvSuffix.rbv("LazyOpen")]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class NDCBFlushOnSoftTrgMode(StrictEnum):
|
|
176
|
+
ON_NEW_IMAGE = "OnNewImage"
|
|
177
|
+
IMMEDIATELY = "Immediately"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class NDPluginCBIO(NDPluginBaseIO):
|
|
181
|
+
pre_count: A[SignalRW[int], PvSuffix.rbv("PreCount")]
|
|
182
|
+
post_count: A[SignalRW[int], PvSuffix.rbv("PostCount")]
|
|
183
|
+
preset_trigger_count: A[SignalRW[int], PvSuffix.rbv("PresetTriggerCount")]
|
|
184
|
+
trigger: A[SignalRW[bool], PvSuffix.rbv("Trigger")]
|
|
185
|
+
capture: A[SignalRW[bool], PvSuffix.rbv("Capture")]
|
|
186
|
+
flush_on_soft_trg: A[
|
|
187
|
+
SignalRW[NDCBFlushOnSoftTrgMode], PvSuffix.rbv("FlushOnSoftTrg")
|
|
188
|
+
]
|