ophyd-async 0.9.0a2__py3-none-any.whl → 0.10.0a2__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 +97 -62
- ophyd_async/core/_derived_signal.py +271 -0
- ophyd_async/core/_derived_signal_backend.py +300 -0
- ophyd_async/core/_detector.py +106 -125
- ophyd_async/core/_device.py +69 -63
- ophyd_async/core/_device_filler.py +65 -1
- ophyd_async/core/_flyer.py +14 -5
- 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 +44 -35
- ophyd_async/core/_settings.py +36 -27
- ophyd_async/core/_signal.py +262 -170
- ophyd_async/core/_signal_backend.py +56 -13
- ophyd_async/core/_soft_signal_backend.py +16 -11
- ophyd_async/core/_status.py +72 -24
- ophyd_async/core/_table.py +41 -11
- ophyd_async/core/_utils.py +96 -49
- ophyd_async/core/_yaml_settings.py +2 -0
- ophyd_async/epics/__init__.py +1 -0
- ophyd_async/epics/adandor/_andor.py +2 -2
- ophyd_async/epics/adandor/_andor_controller.py +4 -2
- ophyd_async/epics/adandor/_andor_io.py +2 -4
- ophyd_async/epics/adaravis/__init__.py +5 -0
- ophyd_async/epics/adaravis/_aravis.py +4 -8
- ophyd_async/epics/adaravis/_aravis_controller.py +20 -43
- ophyd_async/epics/adaravis/_aravis_io.py +13 -28
- ophyd_async/epics/adcore/__init__.py +23 -8
- ophyd_async/epics/adcore/_core_detector.py +42 -2
- ophyd_async/epics/adcore/_core_io.py +124 -99
- ophyd_async/epics/adcore/_core_logic.py +106 -27
- ophyd_async/epics/adcore/_core_writer.py +12 -8
- ophyd_async/epics/adcore/_hdf_writer.py +21 -38
- ophyd_async/epics/adcore/_single_trigger.py +2 -2
- ophyd_async/epics/adcore/_utils.py +2 -2
- ophyd_async/epics/adkinetix/__init__.py +2 -1
- ophyd_async/epics/adkinetix/_kinetix.py +3 -3
- ophyd_async/epics/adkinetix/_kinetix_controller.py +4 -2
- ophyd_async/epics/adkinetix/_kinetix_io.py +12 -13
- ophyd_async/epics/adpilatus/__init__.py +5 -0
- ophyd_async/epics/adpilatus/_pilatus.py +1 -1
- ophyd_async/epics/adpilatus/_pilatus_controller.py +5 -24
- ophyd_async/epics/adpilatus/_pilatus_io.py +11 -9
- ophyd_async/epics/adsimdetector/__init__.py +8 -1
- ophyd_async/epics/adsimdetector/_sim.py +4 -14
- ophyd_async/epics/adsimdetector/_sim_controller.py +17 -0
- ophyd_async/epics/adsimdetector/_sim_io.py +10 -0
- ophyd_async/epics/advimba/__init__.py +10 -1
- ophyd_async/epics/advimba/_vimba.py +3 -2
- ophyd_async/epics/advimba/_vimba_controller.py +4 -2
- ophyd_async/epics/advimba/_vimba_io.py +23 -28
- ophyd_async/epics/core/_aioca.py +35 -16
- ophyd_async/epics/core/_epics_connector.py +4 -0
- ophyd_async/epics/core/_epics_device.py +2 -0
- ophyd_async/epics/core/_p4p.py +10 -2
- ophyd_async/epics/core/_pvi_connector.py +65 -8
- ophyd_async/epics/core/_signal.py +51 -51
- ophyd_async/epics/core/_util.py +4 -4
- ophyd_async/epics/demo/__init__.py +16 -0
- 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/{sim/mover.db → demo/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 +65 -28
- ophyd_async/epics/signal.py +4 -1
- ophyd_async/epics/testing/_example_ioc.py +21 -9
- ophyd_async/epics/testing/_utils.py +3 -0
- ophyd_async/epics/testing/test_records.db +8 -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 -6
- ophyd_async/fastcs/panda/_block.py +29 -9
- ophyd_async/fastcs/panda/_control.py +5 -0
- ophyd_async/fastcs/panda/_hdf_panda.py +2 -0
- ophyd_async/fastcs/panda/_table.py +9 -6
- ophyd_async/fastcs/panda/_trigger.py +23 -9
- ophyd_async/fastcs/panda/_writer.py +27 -30
- ophyd_async/plan_stubs/__init__.py +2 -0
- ophyd_async/plan_stubs/_ensure_connected.py +1 -0
- ophyd_async/plan_stubs/_fly.py +2 -4
- ophyd_async/plan_stubs/_nd_attributes.py +2 -0
- ophyd_async/plan_stubs/_panda.py +1 -0
- ophyd_async/plan_stubs/_settings.py +43 -16
- ophyd_async/plan_stubs/_utils.py +3 -0
- ophyd_async/plan_stubs/_wait_for_awaitable.py +1 -1
- ophyd_async/sim/__init__.py +24 -14
- 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 +18 -32
- ophyd_async/tango/core/_tango_readable.py +2 -19
- ophyd_async/tango/core/_tango_transport.py +136 -60
- ophyd_async/tango/core/_utils.py +47 -0
- ophyd_async/tango/{sim → demo}/_counter.py +2 -0
- ophyd_async/tango/{sim → demo}/_detector.py +2 -0
- ophyd_async/tango/{sim → demo}/_mover.py +5 -4
- ophyd_async/tango/{sim → 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 +29 -7
- ophyd_async/testing/_assert.py +145 -83
- ophyd_async/testing/_mock_signal_utils.py +56 -70
- ophyd_async/testing/_one_of_everything.py +41 -21
- ophyd_async/testing/_single_derived.py +89 -0
- ophyd_async/testing/_utils.py +3 -0
- {ophyd_async-0.9.0a2.dist-info → ophyd_async-0.10.0a2.dist-info}/METADATA +25 -26
- ophyd_async-0.10.0a2.dist-info/RECORD +149 -0
- {ophyd_async-0.9.0a2.dist-info → ophyd_async-0.10.0a2.dist-info}/WHEEL +1 -1
- ophyd_async/epics/sim/__init__.py +0 -54
- ophyd_async/epics/sim/_ioc.py +0 -29
- ophyd_async/epics/sim/_mover.py +0 -101
- ophyd_async/epics/sim/_sensor.py +0 -37
- ophyd_async/epics/sim/sensor.db +0 -19
- ophyd_async/sim/_pattern_detector/__init__.py +0 -13
- ophyd_async/sim/_pattern_detector/_pattern_detector.py +0 -42
- ophyd_async/sim/_pattern_detector/_pattern_detector_controller.py +0 -69
- ophyd_async/sim/_pattern_detector/_pattern_detector_writer.py +0 -41
- ophyd_async/sim/_pattern_detector/_pattern_generator.py +0 -214
- ophyd_async/sim/_sim_motor.py +0 -107
- ophyd_async-0.9.0a2.dist-info/RECORD +0 -129
- /ophyd_async/tango/{sim → demo}/__init__.py +0 -0
- /ophyd_async/tango/{sim → demo}/_tango/__init__.py +0 -0
- {ophyd_async-0.9.0a2.dist-info → ophyd_async-0.10.0a2.dist-info/licenses}/LICENSE +0 -0
- {ophyd_async-0.9.0a2.dist-info → ophyd_async-0.10.0a2.dist-info}/top_level.txt +0 -0
|
@@ -3,17 +3,15 @@ from collections.abc import AsyncIterator
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from xml.etree import ElementTree as ET
|
|
5
5
|
|
|
6
|
-
from bluesky.protocols import
|
|
6
|
+
from bluesky.protocols import StreamAsset
|
|
7
7
|
from event_model import DataKey
|
|
8
8
|
|
|
9
9
|
from ophyd_async.core import (
|
|
10
|
-
DEFAULT_TIMEOUT,
|
|
11
10
|
DatasetDescriber,
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
HDFDatasetDescription,
|
|
12
|
+
HDFDocumentComposer,
|
|
14
13
|
NameProvider,
|
|
15
14
|
PathProvider,
|
|
16
|
-
wait_for_value,
|
|
17
15
|
)
|
|
18
16
|
|
|
19
17
|
from ._core_io import NDFileHDFIO, NDPluginBaseIO
|
|
@@ -25,6 +23,8 @@ from ._utils import (
|
|
|
25
23
|
|
|
26
24
|
|
|
27
25
|
class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
26
|
+
"""Allow `NDFileHDFIO` to be used within `StandardDetector`."""
|
|
27
|
+
|
|
28
28
|
default_suffix: str = "HDF1:"
|
|
29
29
|
|
|
30
30
|
def __init__(
|
|
@@ -44,12 +44,12 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
|
44
44
|
file_extension=".h5",
|
|
45
45
|
mimetype="application/x-hdf5",
|
|
46
46
|
)
|
|
47
|
-
self._datasets: list[
|
|
48
|
-
self.
|
|
49
|
-
self.
|
|
47
|
+
self._datasets: list[HDFDatasetDescription] = []
|
|
48
|
+
self._composer: HDFDocumentComposer | None = None
|
|
49
|
+
self._filename_template = "%s%s"
|
|
50
50
|
|
|
51
51
|
async def open(self, multiplier: int = 1) -> dict[str, DataKey]:
|
|
52
|
-
self.
|
|
52
|
+
self._composer = None
|
|
53
53
|
|
|
54
54
|
# Setting HDF writer specific signals
|
|
55
55
|
|
|
@@ -63,11 +63,6 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
|
63
63
|
self.fileio.xml_file_name.set(""),
|
|
64
64
|
)
|
|
65
65
|
|
|
66
|
-
# By default, don't add file number to filename
|
|
67
|
-
self._filename_template = "%s%s"
|
|
68
|
-
if self._include_file_number:
|
|
69
|
-
self._filename_template += "_%6.6d"
|
|
70
|
-
|
|
71
66
|
# Set common AD file plugin params, begin capturing
|
|
72
67
|
await self.begin_capture()
|
|
73
68
|
|
|
@@ -82,7 +77,7 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
|
82
77
|
|
|
83
78
|
# Add the main data
|
|
84
79
|
self._datasets = [
|
|
85
|
-
|
|
80
|
+
HDFDatasetDescription(
|
|
86
81
|
data_key=name,
|
|
87
82
|
dataset="/entry/data/data",
|
|
88
83
|
shape=detector_shape,
|
|
@@ -99,7 +94,7 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
|
99
94
|
if "<Attributes>" in maybe_xml:
|
|
100
95
|
root = ET.fromstring(maybe_xml)
|
|
101
96
|
for child in root:
|
|
102
|
-
|
|
97
|
+
data_key = child.attrib["name"]
|
|
103
98
|
if child.attrib.get("type", "EPICS_PV") == "EPICS_PV":
|
|
104
99
|
np_datatype = convert_pv_dtype_to_np(
|
|
105
100
|
child.attrib.get("dbrtype", "DBR_NATIVE")
|
|
@@ -109,15 +104,15 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
|
109
104
|
child.attrib.get("datatype", "INT")
|
|
110
105
|
)
|
|
111
106
|
self._datasets.append(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
f"/entry/instrument/NDAttributes/{
|
|
115
|
-
(),
|
|
116
|
-
np_datatype,
|
|
117
|
-
multiplier,
|
|
107
|
+
HDFDatasetDescription(
|
|
108
|
+
data_key=data_key,
|
|
109
|
+
dataset=f"/entry/instrument/NDAttributes/{data_key}",
|
|
110
|
+
shape=(),
|
|
111
|
+
dtype_numpy=np_datatype,
|
|
118
112
|
# NDAttributes appear to always be configured with
|
|
119
113
|
# this chunk size
|
|
120
114
|
chunk_shape=(16384,),
|
|
115
|
+
multiplier=multiplier,
|
|
121
116
|
)
|
|
122
117
|
)
|
|
123
118
|
|
|
@@ -139,9 +134,9 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
|
139
134
|
# TODO: fail if we get dropped frames
|
|
140
135
|
await self.fileio.flush_now.set(True)
|
|
141
136
|
if indices_written:
|
|
142
|
-
if not self.
|
|
137
|
+
if not self._composer:
|
|
143
138
|
path = Path(await self.fileio.full_file_name.get_value())
|
|
144
|
-
self.
|
|
139
|
+
self._composer = HDFDocumentComposer(
|
|
145
140
|
# See https://github.com/bluesky/ophyd-async/issues/122
|
|
146
141
|
path,
|
|
147
142
|
self._datasets,
|
|
@@ -150,19 +145,7 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
|
150
145
|
# stream datum says "here are N frames in that stream resource",
|
|
151
146
|
# you get one stream resource and many stream datums per scan
|
|
152
147
|
|
|
153
|
-
for doc in self.
|
|
148
|
+
for doc in self._composer.stream_resources():
|
|
154
149
|
yield "stream_resource", doc
|
|
155
|
-
for doc in self.
|
|
150
|
+
for doc in self._composer.stream_data(indices_written):
|
|
156
151
|
yield "stream_datum", doc
|
|
157
|
-
|
|
158
|
-
async def close(self):
|
|
159
|
-
# Already done a caput callback in _capture_status, so can't do one here
|
|
160
|
-
await self.fileio.capture.set(False, wait=False)
|
|
161
|
-
await wait_for_value(self.fileio.capture, False, DEFAULT_TIMEOUT)
|
|
162
|
-
if self._capture_status:
|
|
163
|
-
# We kicked off an open, so wait for it to return
|
|
164
|
-
await self._capture_status
|
|
165
|
-
|
|
166
|
-
@property
|
|
167
|
-
def hints(self) -> Hints:
|
|
168
|
-
return {"fields": [self._name_provider()]}
|
|
@@ -7,7 +7,7 @@ from ophyd_async.core import AsyncStatus, SignalR, StandardReadable
|
|
|
7
7
|
from ophyd_async.core import StandardReadableFormat as Format
|
|
8
8
|
|
|
9
9
|
from ._core_io import ADBaseIO, NDPluginBaseIO
|
|
10
|
-
from ._utils import
|
|
10
|
+
from ._utils import ADImageMode
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class SingleTriggerDetector(StandardReadable, Triggerable):
|
|
@@ -35,7 +35,7 @@ class SingleTriggerDetector(StandardReadable, Triggerable):
|
|
|
35
35
|
@AsyncStatus.wrap
|
|
36
36
|
async def stage(self) -> None:
|
|
37
37
|
await asyncio.gather(
|
|
38
|
-
self.drv.image_mode.set(
|
|
38
|
+
self.drv.image_mode.set(ADImageMode.SINGLE),
|
|
39
39
|
self.drv.wait_for_plugins.set(True),
|
|
40
40
|
)
|
|
41
41
|
await super().stage()
|
|
@@ -79,13 +79,13 @@ def convert_param_dtype_to_np(datatype: str) -> str:
|
|
|
79
79
|
return np_datatype
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
class
|
|
82
|
+
class ADFileWriteMode(StrictEnum):
|
|
83
83
|
SINGLE = "Single"
|
|
84
84
|
CAPTURE = "Capture"
|
|
85
85
|
STREAM = "Stream"
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
class
|
|
88
|
+
class ADImageMode(SubsetEnum):
|
|
89
89
|
SINGLE = "Single"
|
|
90
90
|
MULTIPLE = "Multiple"
|
|
91
91
|
CONTINUOUS = "Continuous"
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from ._kinetix import KinetixDetector
|
|
2
2
|
from ._kinetix_controller import KinetixController
|
|
3
|
-
from ._kinetix_io import KinetixDriverIO, KinetixTriggerMode
|
|
3
|
+
from ._kinetix_io import KinetixDriverIO, KinetixReadoutMode, KinetixTriggerMode
|
|
4
4
|
|
|
5
5
|
__all__ = [
|
|
6
6
|
"KinetixDetector",
|
|
7
7
|
"KinetixController",
|
|
8
8
|
"KinetixDriverIO",
|
|
9
9
|
"KinetixTriggerMode",
|
|
10
|
+
"KinetixReadoutMode",
|
|
10
11
|
]
|
|
@@ -13,9 +13,9 @@ from ._kinetix_io import KinetixDriverIO
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class KinetixDetector(AreaDetector[KinetixController]):
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
https://github.com/NSLS-II/ADKinetix
|
|
16
|
+
"""Ophyd-async implementation of an ADKinetix Detector.
|
|
17
|
+
|
|
18
|
+
https://github.com/NSLS-II/ADKinetix.
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
def __init__(
|
|
@@ -17,10 +17,12 @@ KINETIX_TRIGGER_MODE_MAP = {
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class KinetixController(adcore.ADBaseController[KinetixDriverIO]):
|
|
20
|
+
"""Controller for adkinetix detector."""
|
|
21
|
+
|
|
20
22
|
def __init__(
|
|
21
23
|
self,
|
|
22
24
|
driver: KinetixDriverIO,
|
|
23
|
-
good_states: frozenset[adcore.
|
|
25
|
+
good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
|
|
24
26
|
) -> None:
|
|
25
27
|
super().__init__(driver, good_states=good_states)
|
|
26
28
|
|
|
@@ -33,7 +35,7 @@ class KinetixController(adcore.ADBaseController[KinetixDriverIO]):
|
|
|
33
35
|
KINETIX_TRIGGER_MODE_MAP[trigger_info.trigger]
|
|
34
36
|
),
|
|
35
37
|
self.driver.num_images.set(trigger_info.total_number_of_triggers),
|
|
36
|
-
self.driver.image_mode.set(adcore.
|
|
38
|
+
self.driver.image_mode.set(adcore.ADImageMode.MULTIPLE),
|
|
37
39
|
)
|
|
38
40
|
if trigger_info.livetime is not None and trigger_info.trigger not in [
|
|
39
41
|
DetectorTrigger.VARIABLE_GATE,
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Annotated as A
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import SignalRW, StrictEnum
|
|
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 KinetixTriggerMode(StrictEnum):
|
|
9
|
+
"""Trigger mode for ADKinetix detector."""
|
|
10
|
+
|
|
7
11
|
INTERNAL = "Internal"
|
|
8
12
|
EDGE = "Rising Edge"
|
|
9
13
|
GATE = "Exp. Gate"
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
class KinetixReadoutMode(StrictEnum):
|
|
17
|
+
"""Readout mode for ADKinetix detector."""
|
|
18
|
+
|
|
13
19
|
SENSITIVITY = 1
|
|
14
20
|
SPEED = 2
|
|
15
21
|
DYNAMIC_RANGE = 3
|
|
@@ -17,14 +23,7 @@ class KinetixReadoutMode(StrictEnum):
|
|
|
17
23
|
|
|
18
24
|
|
|
19
25
|
class KinetixDriverIO(adcore.ADBaseIO):
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
self.trigger_mode = epics_signal_rw_rbv(
|
|
25
|
-
KinetixTriggerMode, prefix + "TriggerMode"
|
|
26
|
-
)
|
|
27
|
-
self.readout_port_idx = epics_signal_rw_rbv(
|
|
28
|
-
KinetixReadoutMode, prefix + "ReadoutPortIdx"
|
|
29
|
-
)
|
|
30
|
-
super().__init__(prefix, name)
|
|
26
|
+
"""Mirrors the interface provided by ADKinetix/db/ADKinetix.template."""
|
|
27
|
+
|
|
28
|
+
trigger_mode: A[SignalRW[KinetixTriggerMode], PvSuffix("TriggerMode")]
|
|
29
|
+
readout_port_idx: A[SignalRW[KinetixReadoutMode], PvSuffix("ReadoutPortIdx")]
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"""Support for the ADPilatus areaDetector driver.
|
|
2
|
+
|
|
3
|
+
https://github.com/areaDetector/ADPilatus
|
|
4
|
+
"""
|
|
5
|
+
|
|
1
6
|
from ._pilatus import PilatusDetector
|
|
2
7
|
from ._pilatus_controller import PilatusController, PilatusReadoutTime
|
|
3
8
|
from ._pilatus_io import PilatusDriverIO, PilatusTriggerMode
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import TypeVar, get_args
|
|
4
3
|
|
|
5
4
|
from ophyd_async.core import (
|
|
6
5
|
DEFAULT_TIMEOUT,
|
|
@@ -13,12 +12,8 @@ from ophyd_async.epics import adcore
|
|
|
13
12
|
from ._pilatus_io import PilatusDriverIO, PilatusTriggerMode
|
|
14
13
|
|
|
15
14
|
|
|
16
|
-
#: Cite: https://media.dectris.com/User_Manual-PILATUS2-V1_4.pdf
|
|
17
|
-
#: The required minimum time difference between ExpPeriod and ExpTime
|
|
18
|
-
#: (readout time) is 2.28 ms
|
|
19
|
-
#: We provide an option to override for newer Pilatus models
|
|
20
15
|
class PilatusReadoutTime(float, Enum):
|
|
21
|
-
"""Pilatus readout time per model in ms"""
|
|
16
|
+
"""Pilatus readout time per model in ms."""
|
|
22
17
|
|
|
23
18
|
# Cite: https://media.dectris.com/User_Manual-PILATUS2-V1_4.pdf
|
|
24
19
|
PILATUS2 = 2.28e-3
|
|
@@ -27,10 +22,9 @@ class PilatusReadoutTime(float, Enum):
|
|
|
27
22
|
PILATUS3 = 0.95e-3
|
|
28
23
|
|
|
29
24
|
|
|
30
|
-
PilatusControllerT = TypeVar("PilatusControllerT", bound="PilatusController")
|
|
31
|
-
|
|
32
|
-
|
|
33
25
|
class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
|
|
26
|
+
"""`DetectorController` for a `PilatusDriverIO`."""
|
|
27
|
+
|
|
34
28
|
_supported_trigger_types = {
|
|
35
29
|
DetectorTrigger.INTERNAL: PilatusTriggerMode.INTERNAL,
|
|
36
30
|
DetectorTrigger.CONSTANT_GATE: PilatusTriggerMode.EXT_ENABLE,
|
|
@@ -40,25 +34,12 @@ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
|
|
|
40
34
|
def __init__(
|
|
41
35
|
self,
|
|
42
36
|
driver: PilatusDriverIO,
|
|
43
|
-
good_states: frozenset[adcore.
|
|
37
|
+
good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
|
|
44
38
|
readout_time: float = PilatusReadoutTime.PILATUS3,
|
|
45
39
|
) -> None:
|
|
46
40
|
super().__init__(driver, good_states=good_states)
|
|
47
41
|
self._readout_time = readout_time
|
|
48
42
|
|
|
49
|
-
@classmethod
|
|
50
|
-
def controller_and_drv(
|
|
51
|
-
cls: type[PilatusControllerT],
|
|
52
|
-
prefix: str,
|
|
53
|
-
good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
|
|
54
|
-
name: str = "",
|
|
55
|
-
readout_time: float = PilatusReadoutTime.PILATUS3,
|
|
56
|
-
) -> tuple[PilatusControllerT, PilatusDriverIO]:
|
|
57
|
-
driver_cls = get_args(cls.__orig_bases__[0])[0] # type: ignore
|
|
58
|
-
driver = driver_cls(prefix, name=name)
|
|
59
|
-
controller = cls(driver, good_states=good_states, readout_time=readout_time)
|
|
60
|
-
return controller, driver
|
|
61
|
-
|
|
62
43
|
def get_deadtime(self, exposure: float | None) -> float:
|
|
63
44
|
return self._readout_time
|
|
64
45
|
|
|
@@ -74,7 +55,7 @@ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
|
|
|
74
55
|
if trigger_info.total_number_of_triggers == 0
|
|
75
56
|
else trigger_info.total_number_of_triggers
|
|
76
57
|
),
|
|
77
|
-
self.driver.image_mode.set(adcore.
|
|
58
|
+
self.driver.image_mode.set(adcore.ADImageMode.MULTIPLE),
|
|
78
59
|
)
|
|
79
60
|
|
|
80
61
|
async def arm(self):
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Annotated as A
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import SignalR, SignalRW, StrictEnum
|
|
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 PilatusTriggerMode(StrictEnum):
|
|
9
|
+
"""Trigger modes for ADPilatus detector."""
|
|
10
|
+
|
|
7
11
|
INTERNAL = "Internal"
|
|
8
12
|
EXT_ENABLE = "Ext. Enable"
|
|
9
13
|
EXT_TRIGGER = "Ext. Trigger"
|
|
@@ -12,11 +16,9 @@ class PilatusTriggerMode(StrictEnum):
|
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
class PilatusDriverIO(adcore.ADBaseIO):
|
|
15
|
-
"""
|
|
19
|
+
"""Driver for the Pilatus pixel array detectors."""
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
self.armed = epics_signal_r(bool, prefix + "Armed")
|
|
22
|
-
super().__init__(prefix, name)
|
|
21
|
+
"""This mirrors the interface provided by ADPilatus/db/pilatus.template."""
|
|
22
|
+
"""See HTML docs at https://areadetector.github.io/areaDetector/ADPilatus/pilatusDoc.html"""
|
|
23
|
+
trigger_mode: A[SignalRW[PilatusTriggerMode], PvSuffix.rbv("TriggerMode")]
|
|
24
|
+
armed: A[SignalR[bool], PvSuffix.rbv("Armed_RBV")]
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
"""Support for the ADAravis areaDetector driver.
|
|
2
|
+
|
|
3
|
+
https://github.com/areaDetector/ADSimDetector
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ._sim import SimDetector
|
|
7
|
+
from ._sim_controller import SimController
|
|
8
|
+
from ._sim_io import SimDriverIO
|
|
2
9
|
|
|
3
10
|
__all__ = [
|
|
4
11
|
"SimDriverIO",
|
|
@@ -3,23 +3,13 @@ from collections.abc import Sequence
|
|
|
3
3
|
from ophyd_async.core import PathProvider, SignalR
|
|
4
4
|
from ophyd_async.epics import adcore
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class SimController(adcore.ADBaseController[SimDriverIO]):
|
|
11
|
-
def __init__(
|
|
12
|
-
self,
|
|
13
|
-
driver: SimDriverIO,
|
|
14
|
-
good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
|
|
15
|
-
) -> None:
|
|
16
|
-
super().__init__(driver, good_states=good_states)
|
|
17
|
-
|
|
18
|
-
def get_deadtime(self, exposure: float | None) -> float:
|
|
19
|
-
return 0.001
|
|
6
|
+
from ._sim_controller import SimController
|
|
7
|
+
from ._sim_io import SimDriverIO
|
|
20
8
|
|
|
21
9
|
|
|
22
10
|
class SimDetector(adcore.AreaDetector[SimController]):
|
|
11
|
+
"""Detector for simulated Areadetector."""
|
|
12
|
+
|
|
23
13
|
def __init__(
|
|
24
14
|
self,
|
|
25
15
|
prefix: str,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from ophyd_async.epics import adcore
|
|
2
|
+
|
|
3
|
+
from ._sim_io import SimDriverIO
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SimController(adcore.ADBaseController[SimDriverIO]):
|
|
7
|
+
"""Controller for simulated Areadetector."""
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
driver: SimDriverIO,
|
|
12
|
+
good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
|
|
13
|
+
) -> None:
|
|
14
|
+
super().__init__(driver, good_states=good_states)
|
|
15
|
+
|
|
16
|
+
def get_deadtime(self, exposure: float | None) -> float:
|
|
17
|
+
return 0.001
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
from ._vimba import VimbaDetector
|
|
2
2
|
from ._vimba_controller import VimbaController
|
|
3
|
-
from ._vimba_io import
|
|
3
|
+
from ._vimba_io import (
|
|
4
|
+
VimbaConvertFormat,
|
|
5
|
+
VimbaDriverIO,
|
|
6
|
+
VimbaExposeOutMode,
|
|
7
|
+
VimbaOnOff,
|
|
8
|
+
VimbaOverlap,
|
|
9
|
+
VimbaTriggerSource,
|
|
10
|
+
)
|
|
4
11
|
|
|
5
12
|
__all__ = [
|
|
6
13
|
"VimbaDetector",
|
|
@@ -9,4 +16,6 @@ __all__ = [
|
|
|
9
16
|
"VimbaExposeOutMode",
|
|
10
17
|
"VimbaOnOff",
|
|
11
18
|
"VimbaTriggerSource",
|
|
19
|
+
"VimbaOverlap",
|
|
20
|
+
"VimbaConvertFormat",
|
|
12
21
|
]
|
|
@@ -8,8 +8,9 @@ from ._vimba_io import VimbaDriverIO
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class VimbaDetector(adcore.AreaDetector[VimbaController]):
|
|
11
|
-
"""
|
|
12
|
-
|
|
11
|
+
"""Ophyd-async implementation of an ADVimba Detector.
|
|
12
|
+
|
|
13
|
+
https://github.com/areaDetector/ADVimba
|
|
13
14
|
"""
|
|
14
15
|
|
|
15
16
|
def __init__(
|
|
@@ -24,10 +24,12 @@ EXPOSE_OUT_MODE = {
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class VimbaController(adcore.ADBaseController[VimbaDriverIO]):
|
|
27
|
+
"""Controller for the Vimba detector."""
|
|
28
|
+
|
|
27
29
|
def __init__(
|
|
28
30
|
self,
|
|
29
31
|
driver: VimbaDriverIO,
|
|
30
|
-
good_states: frozenset[adcore.
|
|
32
|
+
good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
|
|
31
33
|
) -> None:
|
|
32
34
|
super().__init__(driver, good_states=good_states)
|
|
33
35
|
|
|
@@ -39,7 +41,7 @@ class VimbaController(adcore.ADBaseController[VimbaDriverIO]):
|
|
|
39
41
|
self.driver.trigger_mode.set(TRIGGER_MODE[trigger_info.trigger]),
|
|
40
42
|
self.driver.exposure_mode.set(EXPOSE_OUT_MODE[trigger_info.trigger]),
|
|
41
43
|
self.driver.num_images.set(trigger_info.total_number_of_triggers),
|
|
42
|
-
self.driver.image_mode.set(adcore.
|
|
44
|
+
self.driver.image_mode.set(adcore.ADImageMode.MULTIPLE),
|
|
43
45
|
)
|
|
44
46
|
if trigger_info.livetime is not None and trigger_info.trigger not in [
|
|
45
47
|
DetectorTrigger.VARIABLE_GATE,
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
from
|
|
2
|
-
from ophyd_async.epics import adcore
|
|
3
|
-
from ophyd_async.epics.core import epics_signal_rw_rbv
|
|
4
|
-
|
|
1
|
+
from typing import Annotated as A
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
EXT_TRIGGER = "Ext. Trigger"
|
|
10
|
-
MULT_TRIGGER = "Mult. Trigger"
|
|
11
|
-
ALIGNMENT = "Alignment"
|
|
3
|
+
from ophyd_async.core import SignalRW, StrictEnum
|
|
4
|
+
from ophyd_async.epics import adcore
|
|
5
|
+
from ophyd_async.epics.core import PvSuffix
|
|
12
6
|
|
|
13
7
|
|
|
14
8
|
class VimbaConvertFormat(StrictEnum):
|
|
9
|
+
"""Convert pixel format for the Vimba detector."""
|
|
10
|
+
|
|
15
11
|
NONE = "None"
|
|
16
12
|
MONO8 = "Mono8"
|
|
17
13
|
MONO16 = "Mono16"
|
|
@@ -20,6 +16,8 @@ class VimbaConvertFormat(StrictEnum):
|
|
|
20
16
|
|
|
21
17
|
|
|
22
18
|
class VimbaTriggerSource(StrictEnum):
|
|
19
|
+
"""Mode for the source of triggers on the Vimbda."""
|
|
20
|
+
|
|
23
21
|
FREERUN = "Freerun"
|
|
24
22
|
LINE1 = "Line1"
|
|
25
23
|
LINE2 = "Line2"
|
|
@@ -30,36 +28,33 @@ class VimbaTriggerSource(StrictEnum):
|
|
|
30
28
|
|
|
31
29
|
|
|
32
30
|
class VimbaOverlap(StrictEnum):
|
|
31
|
+
"""Overlap modes for the Vimba detector."""
|
|
32
|
+
|
|
33
33
|
OFF = "Off"
|
|
34
34
|
PREV_FRAME = "PreviousFrame"
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class VimbaOnOff(StrictEnum):
|
|
38
|
+
"""On/Off modes on the Vimba detector."""
|
|
39
|
+
|
|
38
40
|
ON = "On"
|
|
39
41
|
OFF = "Off"
|
|
40
42
|
|
|
41
43
|
|
|
42
44
|
class VimbaExposeOutMode(StrictEnum):
|
|
45
|
+
"""Exposure control modes for Vimba detectors."""
|
|
46
|
+
|
|
43
47
|
TIMED = "Timed" # Use ExposureTime PV
|
|
44
48
|
TRIGGER_WIDTH = "TriggerWidth" # Expose for length of high signal
|
|
45
49
|
|
|
46
50
|
|
|
47
51
|
class VimbaDriverIO(adcore.ADBaseIO):
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
)
|
|
58
|
-
self.trigger_mode = epics_signal_rw_rbv(VimbaOnOff, prefix + "TriggerMode")
|
|
59
|
-
self.trigger_overlap = epics_signal_rw_rbv(
|
|
60
|
-
VimbaOverlap, prefix + "TriggerOverlap"
|
|
61
|
-
)
|
|
62
|
-
self.exposure_mode = epics_signal_rw_rbv(
|
|
63
|
-
VimbaExposeOutMode, prefix + "ExposureMode"
|
|
64
|
-
)
|
|
65
|
-
super().__init__(prefix, name)
|
|
52
|
+
"""Mirrors the interface provided by ADVimba/db/vimba.template."""
|
|
53
|
+
|
|
54
|
+
convert_pixel_format: A[
|
|
55
|
+
SignalRW[VimbaConvertFormat], PvSuffix("ConvertPixelFormat")
|
|
56
|
+
]
|
|
57
|
+
trigger_source: A[SignalRW[VimbaTriggerSource], PvSuffix("TriggerSource")]
|
|
58
|
+
trigger_mode: A[SignalRW[VimbaOnOff], PvSuffix("TriggerMode")]
|
|
59
|
+
trigger_overlap: A[SignalRW[VimbaOverlap], PvSuffix("TriggerOverlap")]
|
|
60
|
+
exposure_mode: A[SignalRW[VimbaExposeOutMode], PvSuffix("ExposureMode")]
|