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,89 +1,71 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from collections.abc import
|
|
2
|
+
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
|
-
AsyncStatus,
|
|
12
10
|
DatasetDescriber,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
HDFFile,
|
|
11
|
+
HDFDatasetDescription,
|
|
12
|
+
HDFDocumentComposer,
|
|
16
13
|
NameProvider,
|
|
17
14
|
PathProvider,
|
|
18
|
-
observe_value,
|
|
19
|
-
set_and_wait_for_value,
|
|
20
|
-
wait_for_value,
|
|
21
15
|
)
|
|
22
16
|
|
|
23
|
-
from ._core_io import
|
|
17
|
+
from ._core_io import NDFileHDFIO, NDPluginBaseIO
|
|
18
|
+
from ._core_writer import ADWriter
|
|
24
19
|
from ._utils import (
|
|
25
|
-
FileWriteMode,
|
|
26
20
|
convert_param_dtype_to_np,
|
|
27
21
|
convert_pv_dtype_to_np,
|
|
28
22
|
)
|
|
29
23
|
|
|
30
24
|
|
|
31
|
-
class ADHDFWriter(
|
|
25
|
+
class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
26
|
+
"""Allow `NDFileHDFIO` to be used within `StandardDetector`."""
|
|
27
|
+
|
|
28
|
+
default_suffix: str = "HDF1:"
|
|
29
|
+
|
|
32
30
|
def __init__(
|
|
33
31
|
self,
|
|
34
|
-
|
|
32
|
+
fileio: NDFileHDFIO,
|
|
35
33
|
path_provider: PathProvider,
|
|
36
34
|
name_provider: NameProvider,
|
|
37
35
|
dataset_describer: DatasetDescriber,
|
|
38
|
-
|
|
36
|
+
plugins: dict[str, NDPluginBaseIO] | None = None,
|
|
39
37
|
) -> None:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
self.
|
|
38
|
+
super().__init__(
|
|
39
|
+
fileio,
|
|
40
|
+
path_provider,
|
|
41
|
+
name_provider,
|
|
42
|
+
dataset_describer,
|
|
43
|
+
plugins=plugins,
|
|
44
|
+
file_extension=".h5",
|
|
45
|
+
mimetype="application/x-hdf5",
|
|
46
|
+
)
|
|
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.
|
|
53
|
-
info = self._path_provider(device_name=self._name_provider())
|
|
52
|
+
self._composer = None
|
|
54
53
|
|
|
55
|
-
#
|
|
56
|
-
# when directory path PV is processed.
|
|
57
|
-
await self.hdf.create_directory.set(info.create_dir_depth)
|
|
54
|
+
# Setting HDF writer specific signals
|
|
58
55
|
|
|
59
56
|
# Make sure we are using chunk auto-sizing
|
|
60
|
-
await asyncio.gather(self.
|
|
57
|
+
await asyncio.gather(self.fileio.chunk_size_auto.set(True))
|
|
61
58
|
|
|
62
59
|
await asyncio.gather(
|
|
63
|
-
self.
|
|
64
|
-
self.
|
|
65
|
-
self.
|
|
66
|
-
|
|
67
|
-
self.hdf.file_path.set(str(info.directory_path)),
|
|
68
|
-
self.hdf.file_name.set(info.filename),
|
|
69
|
-
self.hdf.file_template.set("%s/%s.h5"),
|
|
70
|
-
self.hdf.file_write_mode.set(FileWriteMode.STREAM),
|
|
71
|
-
# Never use custom xml layout file but use the one defined
|
|
72
|
-
# in the source code file NDFileHDF5LayoutXML.cpp
|
|
73
|
-
self.hdf.xml_file_name.set(""),
|
|
74
|
-
self.hdf.enable_callbacks.set(Callback.ENABLE),
|
|
60
|
+
self.fileio.num_extra_dims.set(0),
|
|
61
|
+
self.fileio.lazy_open.set(True),
|
|
62
|
+
self.fileio.swmr_mode.set(True),
|
|
63
|
+
self.fileio.xml_file_name.set(""),
|
|
75
64
|
)
|
|
76
65
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
), f"File path {info.directory_path} for hdf plugin does not exist"
|
|
66
|
+
# Set common AD file plugin params, begin capturing
|
|
67
|
+
await self.begin_capture()
|
|
80
68
|
|
|
81
|
-
# Overwrite num_capture to go forever
|
|
82
|
-
await self.hdf.num_capture.set(0)
|
|
83
|
-
# Wait for it to start, stashing the status that tells us when it finishes
|
|
84
|
-
self._capture_status = await set_and_wait_for_value(
|
|
85
|
-
self.hdf.capture, True, wait_for_set_completion=False
|
|
86
|
-
)
|
|
87
69
|
name = self._name_provider()
|
|
88
70
|
detector_shape = await self._dataset_describer.shape()
|
|
89
71
|
np_dtype = await self._dataset_describer.np_datatype()
|
|
@@ -91,11 +73,11 @@ class ADHDFWriter(DetectorWriter):
|
|
|
91
73
|
outer_shape = (multiplier,) if multiplier > 1 else ()
|
|
92
74
|
|
|
93
75
|
# Determine number of frames that will be saved per HDF chunk
|
|
94
|
-
frames_per_chunk = await self.
|
|
76
|
+
frames_per_chunk = await self.fileio.num_frames_chunks.get_value()
|
|
95
77
|
|
|
96
78
|
# Add the main data
|
|
97
79
|
self._datasets = [
|
|
98
|
-
|
|
80
|
+
HDFDatasetDescription(
|
|
99
81
|
data_key=name,
|
|
100
82
|
dataset="/entry/data/data",
|
|
101
83
|
shape=detector_shape,
|
|
@@ -105,14 +87,14 @@ class ADHDFWriter(DetectorWriter):
|
|
|
105
87
|
)
|
|
106
88
|
]
|
|
107
89
|
# And all the scalar datasets
|
|
108
|
-
for plugin in self._plugins:
|
|
90
|
+
for plugin in self._plugins.values():
|
|
109
91
|
maybe_xml = await plugin.nd_attributes_file.get_value()
|
|
110
92
|
# This is the check that ADCore does to see if it is an XML string
|
|
111
93
|
# rather than a filename to parse
|
|
112
94
|
if "<Attributes>" in maybe_xml:
|
|
113
95
|
root = ET.fromstring(maybe_xml)
|
|
114
96
|
for child in root:
|
|
115
|
-
|
|
97
|
+
data_key = child.attrib["name"]
|
|
116
98
|
if child.attrib.get("type", "EPICS_PV") == "EPICS_PV":
|
|
117
99
|
np_datatype = convert_pv_dtype_to_np(
|
|
118
100
|
child.attrib.get("dbrtype", "DBR_NATIVE")
|
|
@@ -122,21 +104,21 @@ class ADHDFWriter(DetectorWriter):
|
|
|
122
104
|
child.attrib.get("datatype", "INT")
|
|
123
105
|
)
|
|
124
106
|
self._datasets.append(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
f"/entry/instrument/NDAttributes/{
|
|
128
|
-
(),
|
|
129
|
-
np_datatype,
|
|
130
|
-
multiplier,
|
|
107
|
+
HDFDatasetDescription(
|
|
108
|
+
data_key=data_key,
|
|
109
|
+
dataset=f"/entry/instrument/NDAttributes/{data_key}",
|
|
110
|
+
shape=(),
|
|
111
|
+
dtype_numpy=np_datatype,
|
|
131
112
|
# NDAttributes appear to always be configured with
|
|
132
113
|
# this chunk size
|
|
133
114
|
chunk_shape=(16384,),
|
|
115
|
+
multiplier=multiplier,
|
|
134
116
|
)
|
|
135
117
|
)
|
|
136
118
|
|
|
137
119
|
describe = {
|
|
138
120
|
ds.data_key: DataKey(
|
|
139
|
-
source=self.
|
|
121
|
+
source=self.fileio.full_file_name.source,
|
|
140
122
|
shape=list(outer_shape + tuple(ds.shape)),
|
|
141
123
|
dtype="array" if ds.shape else "number",
|
|
142
124
|
dtype_numpy=ds.dtype_numpy,
|
|
@@ -146,26 +128,15 @@ class ADHDFWriter(DetectorWriter):
|
|
|
146
128
|
}
|
|
147
129
|
return describe
|
|
148
130
|
|
|
149
|
-
async def observe_indices_written(
|
|
150
|
-
self, timeout=DEFAULT_TIMEOUT
|
|
151
|
-
) -> AsyncGenerator[int, None]:
|
|
152
|
-
"""Wait until a specific index is ready to be collected"""
|
|
153
|
-
async for num_captured in observe_value(self.hdf.num_captured, timeout):
|
|
154
|
-
yield num_captured // self._multiplier
|
|
155
|
-
|
|
156
|
-
async def get_indices_written(self) -> int:
|
|
157
|
-
num_captured = await self.hdf.num_captured.get_value()
|
|
158
|
-
return num_captured // self._multiplier
|
|
159
|
-
|
|
160
131
|
async def collect_stream_docs(
|
|
161
132
|
self, indices_written: int
|
|
162
133
|
) -> AsyncIterator[StreamAsset]:
|
|
163
134
|
# TODO: fail if we get dropped frames
|
|
164
|
-
await self.
|
|
135
|
+
await self.fileio.flush_now.set(True)
|
|
165
136
|
if indices_written:
|
|
166
|
-
if not self.
|
|
167
|
-
path = Path(await self.
|
|
168
|
-
self.
|
|
137
|
+
if not self._composer:
|
|
138
|
+
path = Path(await self.fileio.full_file_name.get_value())
|
|
139
|
+
self._composer = HDFDocumentComposer(
|
|
169
140
|
# See https://github.com/bluesky/ophyd-async/issues/122
|
|
170
141
|
path,
|
|
171
142
|
self._datasets,
|
|
@@ -174,19 +145,7 @@ class ADHDFWriter(DetectorWriter):
|
|
|
174
145
|
# stream datum says "here are N frames in that stream resource",
|
|
175
146
|
# you get one stream resource and many stream datums per scan
|
|
176
147
|
|
|
177
|
-
for doc in self.
|
|
148
|
+
for doc in self._composer.stream_resources():
|
|
178
149
|
yield "stream_resource", doc
|
|
179
|
-
for doc in self.
|
|
150
|
+
for doc in self._composer.stream_data(indices_written):
|
|
180
151
|
yield "stream_datum", doc
|
|
181
|
-
|
|
182
|
-
async def close(self):
|
|
183
|
-
# Already done a caput callback in _capture_status, so can't do one here
|
|
184
|
-
await self.hdf.capture.set(False, wait=False)
|
|
185
|
-
await wait_for_value(self.hdf.capture, False, DEFAULT_TIMEOUT)
|
|
186
|
-
if self._capture_status:
|
|
187
|
-
# We kicked off an open, so wait for it to return
|
|
188
|
-
await self._capture_status
|
|
189
|
-
|
|
190
|
-
@property
|
|
191
|
-
def hints(self) -> Hints:
|
|
192
|
-
return {"fields": [self._name_provider()]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from ophyd_async.core import DatasetDescriber, NameProvider, PathProvider
|
|
2
|
+
|
|
3
|
+
from ._core_io import NDFileIO, NDPluginBaseIO
|
|
4
|
+
from ._core_writer import ADWriter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ADJPEGWriter(ADWriter[NDFileIO]):
|
|
8
|
+
default_suffix: str = "JPEG1:"
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
fileio: NDFileIO,
|
|
13
|
+
path_provider: PathProvider,
|
|
14
|
+
name_provider: NameProvider,
|
|
15
|
+
dataset_describer: DatasetDescriber,
|
|
16
|
+
plugins: dict[str, NDPluginBaseIO] | None = None,
|
|
17
|
+
) -> None:
|
|
18
|
+
super().__init__(
|
|
19
|
+
fileio,
|
|
20
|
+
path_provider,
|
|
21
|
+
name_provider,
|
|
22
|
+
dataset_describer,
|
|
23
|
+
plugins=plugins,
|
|
24
|
+
file_extension=".jpg",
|
|
25
|
+
mimetype="multipart/related;type=image/jpeg",
|
|
26
|
+
)
|
|
@@ -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):
|
|
@@ -16,11 +16,12 @@ class SingleTriggerDetector(StandardReadable, Triggerable):
|
|
|
16
16
|
drv: ADBaseIO,
|
|
17
17
|
read_uncached: Sequence[SignalR] = (),
|
|
18
18
|
name="",
|
|
19
|
-
|
|
19
|
+
plugins: dict[str, NDPluginBaseIO] | None = None,
|
|
20
20
|
) -> None:
|
|
21
21
|
self.drv = drv
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
if plugins is not None:
|
|
23
|
+
for k, v in plugins.items():
|
|
24
|
+
setattr(self, k, v)
|
|
24
25
|
|
|
25
26
|
self.add_readables(
|
|
26
27
|
[self.drv.array_counter, *read_uncached],
|
|
@@ -34,7 +35,7 @@ class SingleTriggerDetector(StandardReadable, Triggerable):
|
|
|
34
35
|
@AsyncStatus.wrap
|
|
35
36
|
async def stage(self) -> None:
|
|
36
37
|
await asyncio.gather(
|
|
37
|
-
self.drv.image_mode.set(
|
|
38
|
+
self.drv.image_mode.set(ADImageMode.SINGLE),
|
|
38
39
|
self.drv.wait_for_plugins.set(True),
|
|
39
40
|
)
|
|
40
41
|
await super().stage()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from ophyd_async.core import DatasetDescriber, NameProvider, PathProvider
|
|
2
|
+
|
|
3
|
+
from ._core_io import NDFileIO, NDPluginBaseIO
|
|
4
|
+
from ._core_writer import ADWriter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ADTIFFWriter(ADWriter[NDFileIO]):
|
|
8
|
+
default_suffix: str = "TIFF1:"
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
fileio: NDFileIO,
|
|
13
|
+
path_provider: PathProvider,
|
|
14
|
+
name_provider: NameProvider,
|
|
15
|
+
dataset_describer: DatasetDescriber,
|
|
16
|
+
plugins: dict[str, NDPluginBaseIO] | None = None,
|
|
17
|
+
) -> None:
|
|
18
|
+
super().__init__(
|
|
19
|
+
fileio,
|
|
20
|
+
path_provider,
|
|
21
|
+
name_provider,
|
|
22
|
+
dataset_describer,
|
|
23
|
+
plugins=plugins,
|
|
24
|
+
file_extension=".tiff",
|
|
25
|
+
mimetype="multipart/related;type=image/tiff",
|
|
26
|
+
)
|
|
@@ -6,6 +6,7 @@ from ophyd_async.core import (
|
|
|
6
6
|
SignalR,
|
|
7
7
|
SignalRW,
|
|
8
8
|
StrictEnum,
|
|
9
|
+
SubsetEnum,
|
|
9
10
|
wait_for_value,
|
|
10
11
|
)
|
|
11
12
|
|
|
@@ -78,13 +79,13 @@ def convert_param_dtype_to_np(datatype: str) -> str:
|
|
|
78
79
|
return np_datatype
|
|
79
80
|
|
|
80
81
|
|
|
81
|
-
class
|
|
82
|
+
class ADFileWriteMode(StrictEnum):
|
|
82
83
|
SINGLE = "Single"
|
|
83
84
|
CAPTURE = "Capture"
|
|
84
85
|
STREAM = "Stream"
|
|
85
86
|
|
|
86
87
|
|
|
87
|
-
class
|
|
88
|
+
class ADImageMode(SubsetEnum):
|
|
88
89
|
SINGLE = "Single"
|
|
89
90
|
MULTIPLE = "Multiple"
|
|
90
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
|
]
|
|
@@ -1,44 +1,49 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Sequence
|
|
2
2
|
|
|
3
|
-
from ophyd_async.core import PathProvider,
|
|
4
|
-
from ophyd_async.epics import
|
|
3
|
+
from ophyd_async.core import PathProvider, SignalR
|
|
4
|
+
from ophyd_async.epics.adcore import (
|
|
5
|
+
ADHDFWriter,
|
|
6
|
+
ADWriter,
|
|
7
|
+
AreaDetector,
|
|
8
|
+
NDPluginBaseIO,
|
|
9
|
+
)
|
|
5
10
|
|
|
6
11
|
from ._kinetix_controller import KinetixController
|
|
7
12
|
from ._kinetix_io import KinetixDriverIO
|
|
8
13
|
|
|
9
14
|
|
|
10
|
-
class KinetixDetector(
|
|
11
|
-
"""
|
|
12
|
-
Ophyd-async implementation of an ADKinetix Detector.
|
|
13
|
-
https://github.com/NSLS-II/ADKinetix
|
|
14
|
-
"""
|
|
15
|
+
class KinetixDetector(AreaDetector[KinetixController]):
|
|
16
|
+
"""Ophyd-async implementation of an ADKinetix Detector.
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
https://github.com/NSLS-II/ADKinetix.
|
|
19
|
+
"""
|
|
18
20
|
|
|
19
21
|
def __init__(
|
|
20
22
|
self,
|
|
21
23
|
prefix: str,
|
|
22
24
|
path_provider: PathProvider,
|
|
23
|
-
drv_suffix="cam1:",
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
drv_suffix: str = "cam1:",
|
|
26
|
+
writer_cls: type[ADWriter] = ADHDFWriter,
|
|
27
|
+
fileio_suffix: str | None = None,
|
|
28
|
+
name: str = "",
|
|
29
|
+
plugins: dict[str, NDPluginBaseIO] | None = None,
|
|
30
|
+
config_sigs: Sequence[SignalR] = (),
|
|
26
31
|
):
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
driver = KinetixDriverIO(prefix + drv_suffix)
|
|
33
|
+
controller = KinetixController(driver)
|
|
34
|
+
|
|
35
|
+
writer = writer_cls.with_io(
|
|
36
|
+
prefix,
|
|
37
|
+
path_provider,
|
|
38
|
+
dataset_source=driver,
|
|
39
|
+
fileio_suffix=fileio_suffix,
|
|
40
|
+
plugins=plugins,
|
|
41
|
+
)
|
|
29
42
|
|
|
30
43
|
super().__init__(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
path_provider,
|
|
35
|
-
lambda: self.name,
|
|
36
|
-
adcore.ADBaseDatasetDescriber(self.drv),
|
|
37
|
-
),
|
|
38
|
-
config_sigs=(self.drv.acquire_time,),
|
|
44
|
+
controller=controller,
|
|
45
|
+
writer=writer,
|
|
46
|
+
plugins=plugins,
|
|
39
47
|
name=name,
|
|
48
|
+
config_sigs=config_sigs,
|
|
40
49
|
)
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def hints(self) -> Hints:
|
|
44
|
-
return self._writer.hints
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
|
|
3
3
|
from ophyd_async.core import (
|
|
4
|
-
AsyncStatus,
|
|
5
|
-
DetectorController,
|
|
6
4
|
DetectorTrigger,
|
|
7
5
|
TriggerInfo,
|
|
8
6
|
)
|
|
@@ -18,37 +16,29 @@ KINETIX_TRIGGER_MODE_MAP = {
|
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
|
|
21
|
-
class KinetixController(
|
|
19
|
+
class KinetixController(adcore.ADBaseController[KinetixDriverIO]):
|
|
20
|
+
"""Controller for adkinetix detector."""
|
|
21
|
+
|
|
22
22
|
def __init__(
|
|
23
23
|
self,
|
|
24
24
|
driver: KinetixDriverIO,
|
|
25
|
+
good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
|
|
25
26
|
) -> None:
|
|
26
|
-
|
|
27
|
-
self._arm_status: AsyncStatus | None = None
|
|
27
|
+
super().__init__(driver, good_states=good_states)
|
|
28
28
|
|
|
29
29
|
def get_deadtime(self, exposure: float | None) -> float:
|
|
30
30
|
return 0.001
|
|
31
31
|
|
|
32
32
|
async def prepare(self, trigger_info: TriggerInfo):
|
|
33
33
|
await asyncio.gather(
|
|
34
|
-
self.
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
self.driver.trigger_mode.set(
|
|
35
|
+
KINETIX_TRIGGER_MODE_MAP[trigger_info.trigger]
|
|
36
|
+
),
|
|
37
|
+
self.driver.num_images.set(trigger_info.total_number_of_triggers),
|
|
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,
|
|
40
42
|
DetectorTrigger.CONSTANT_GATE,
|
|
41
43
|
]:
|
|
42
|
-
await self.
|
|
43
|
-
|
|
44
|
-
async def arm(self):
|
|
45
|
-
self._arm_status = await adcore.start_acquiring_driver_and_ensure_status(
|
|
46
|
-
self._drv
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
async def wait_for_idle(self):
|
|
50
|
-
if self._arm_status:
|
|
51
|
-
await self._arm_status
|
|
52
|
-
|
|
53
|
-
async def disarm(self):
|
|
54
|
-
await adcore.stop_busy_record(self._drv.acquire, False, timeout=1)
|
|
44
|
+
await self.driver.acquire_time.set(trigger_info.livetime)
|
|
@@ -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,5 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"""Support for the ADPilatus areaDetector driver.
|
|
2
|
+
|
|
3
|
+
https://github.com/areaDetector/ADPilatus
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ._pilatus import PilatusDetector
|
|
7
|
+
from ._pilatus_controller import PilatusController, PilatusReadoutTime
|
|
3
8
|
from ._pilatus_io import PilatusDriverIO, PilatusTriggerMode
|
|
4
9
|
|
|
5
10
|
__all__ = [
|
|
@@ -1,33 +1,18 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Sequence
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from ophyd_async.core import PathProvider
|
|
4
|
+
from ophyd_async.core._signal import SignalR
|
|
5
|
+
from ophyd_async.epics.adcore._core_detector import AreaDetector
|
|
6
|
+
from ophyd_async.epics.adcore._core_io import NDPluginBaseIO
|
|
7
|
+
from ophyd_async.epics.adcore._core_writer import ADWriter
|
|
8
|
+
from ophyd_async.epics.adcore._hdf_writer import ADHDFWriter
|
|
4
9
|
|
|
5
|
-
from
|
|
6
|
-
from ophyd_async.epics import adcore
|
|
7
|
-
|
|
8
|
-
from ._pilatus_controller import PilatusController
|
|
10
|
+
from ._pilatus_controller import PilatusController, PilatusReadoutTime
|
|
9
11
|
from ._pilatus_io import PilatusDriverIO
|
|
10
12
|
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
#: (readout time) is 2.28 ms
|
|
15
|
-
#: We provide an option to override for newer Pilatus models
|
|
16
|
-
class PilatusReadoutTime(float, Enum):
|
|
17
|
-
"""Pilatus readout time per model in ms"""
|
|
18
|
-
|
|
19
|
-
# Cite: https://media.dectris.com/User_Manual-PILATUS2-V1_4.pdf
|
|
20
|
-
PILATUS2 = 2.28e-3
|
|
21
|
-
|
|
22
|
-
# Cite: https://media.dectris.com/user-manual-pilatus3-2020.pdf
|
|
23
|
-
PILATUS3 = 0.95e-3
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class PilatusDetector(StandardDetector):
|
|
27
|
-
"""A Pilatus StandardDetector writing HDF files"""
|
|
28
|
-
|
|
29
|
-
_controller: PilatusController
|
|
30
|
-
_writer: adcore.ADHDFWriter
|
|
14
|
+
class PilatusDetector(AreaDetector[PilatusController]):
|
|
15
|
+
"""A Pilatus StandardDetector writing HDF files."""
|
|
31
16
|
|
|
32
17
|
def __init__(
|
|
33
18
|
self,
|
|
@@ -35,24 +20,27 @@ class PilatusDetector(StandardDetector):
|
|
|
35
20
|
path_provider: PathProvider,
|
|
36
21
|
readout_time: PilatusReadoutTime = PilatusReadoutTime.PILATUS3,
|
|
37
22
|
drv_suffix: str = "cam1:",
|
|
38
|
-
|
|
23
|
+
writer_cls: type[ADWriter] = ADHDFWriter,
|
|
24
|
+
fileio_suffix: str | None = None,
|
|
39
25
|
name: str = "",
|
|
26
|
+
plugins: dict[str, NDPluginBaseIO] | None = None,
|
|
27
|
+
config_sigs: Sequence[SignalR] = (),
|
|
40
28
|
):
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
driver = PilatusDriverIO(prefix + drv_suffix)
|
|
30
|
+
controller = PilatusController(driver)
|
|
31
|
+
|
|
32
|
+
writer = writer_cls.with_io(
|
|
33
|
+
prefix,
|
|
34
|
+
path_provider,
|
|
35
|
+
dataset_source=driver,
|
|
36
|
+
fileio_suffix=fileio_suffix,
|
|
37
|
+
plugins=plugins,
|
|
38
|
+
)
|
|
43
39
|
|
|
44
40
|
super().__init__(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
path_provider,
|
|
49
|
-
lambda: self.name,
|
|
50
|
-
adcore.ADBaseDatasetDescriber(self.drv),
|
|
51
|
-
),
|
|
52
|
-
config_sigs=(self.drv.acquire_time,),
|
|
41
|
+
controller=controller,
|
|
42
|
+
writer=writer,
|
|
43
|
+
plugins=plugins,
|
|
53
44
|
name=name,
|
|
45
|
+
config_sigs=config_sigs,
|
|
54
46
|
)
|
|
55
|
-
|
|
56
|
-
@property
|
|
57
|
-
def hints(self) -> Hints:
|
|
58
|
-
return self._writer.hints
|