ophyd-async 0.8.0a5__py3-none-any.whl → 0.9.0__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/_version.py +2 -2
- ophyd_async/core/__init__.py +17 -46
- ophyd_async/core/_detector.py +68 -44
- ophyd_async/core/_device.py +120 -79
- ophyd_async/core/_device_filler.py +17 -8
- ophyd_async/core/_flyer.py +2 -2
- ophyd_async/core/_protocol.py +0 -28
- ophyd_async/core/_readable.py +30 -23
- ophyd_async/core/_settings.py +104 -0
- ophyd_async/core/_signal.py +164 -151
- ophyd_async/core/_signal_backend.py +4 -1
- ophyd_async/core/_soft_signal_backend.py +2 -1
- ophyd_async/core/_table.py +27 -14
- ophyd_async/core/_utils.py +30 -5
- ophyd_async/core/_yaml_settings.py +64 -0
- ophyd_async/epics/adandor/__init__.py +9 -0
- ophyd_async/epics/adandor/_andor.py +45 -0
- ophyd_async/epics/adandor/_andor_controller.py +49 -0
- ophyd_async/epics/adandor/_andor_io.py +36 -0
- ophyd_async/epics/adaravis/__init__.py +3 -1
- ophyd_async/epics/adaravis/_aravis.py +23 -37
- ophyd_async/epics/adaravis/_aravis_controller.py +21 -30
- ophyd_async/epics/adaravis/_aravis_io.py +4 -4
- ophyd_async/epics/adcore/__init__.py +15 -8
- ophyd_async/epics/adcore/_core_detector.py +41 -0
- ophyd_async/epics/adcore/_core_io.py +56 -31
- ophyd_async/epics/adcore/_core_logic.py +99 -84
- ophyd_async/epics/adcore/_core_writer.py +219 -0
- ophyd_async/epics/adcore/_hdf_writer.py +33 -59
- ophyd_async/epics/adcore/_jpeg_writer.py +26 -0
- ophyd_async/epics/adcore/_single_trigger.py +5 -4
- ophyd_async/epics/adcore/_tiff_writer.py +26 -0
- ophyd_async/epics/adcore/_utils.py +37 -36
- ophyd_async/epics/adkinetix/_kinetix.py +29 -24
- ophyd_async/epics/adkinetix/_kinetix_controller.py +15 -27
- ophyd_async/epics/adkinetix/_kinetix_io.py +7 -7
- ophyd_async/epics/adpilatus/__init__.py +2 -2
- ophyd_async/epics/adpilatus/_pilatus.py +28 -40
- ophyd_async/epics/adpilatus/_pilatus_controller.py +47 -25
- ophyd_async/epics/adpilatus/_pilatus_io.py +5 -5
- ophyd_async/epics/adsimdetector/__init__.py +3 -3
- ophyd_async/epics/adsimdetector/_sim.py +33 -17
- ophyd_async/epics/advimba/_vimba.py +23 -23
- ophyd_async/epics/advimba/_vimba_controller.py +21 -35
- ophyd_async/epics/advimba/_vimba_io.py +23 -23
- ophyd_async/epics/core/_aioca.py +52 -21
- ophyd_async/epics/core/_p4p.py +59 -16
- ophyd_async/epics/core/_pvi_connector.py +4 -2
- ophyd_async/epics/core/_signal.py +9 -2
- ophyd_async/epics/core/_util.py +10 -1
- ophyd_async/epics/eiger/_eiger_controller.py +10 -5
- ophyd_async/epics/eiger/_eiger_io.py +3 -3
- ophyd_async/epics/motor.py +26 -15
- ophyd_async/epics/sim/_ioc.py +29 -0
- ophyd_async/epics/{demo → sim}/_mover.py +12 -6
- ophyd_async/epics/{demo → sim}/_sensor.py +2 -2
- ophyd_async/epics/testing/__init__.py +24 -0
- ophyd_async/epics/testing/_example_ioc.py +91 -0
- ophyd_async/epics/testing/_utils.py +50 -0
- ophyd_async/epics/testing/test_records.db +174 -0
- ophyd_async/epics/testing/test_records_pva.db +177 -0
- ophyd_async/fastcs/core.py +2 -2
- ophyd_async/fastcs/panda/__init__.py +0 -2
- ophyd_async/fastcs/panda/_block.py +9 -9
- ophyd_async/fastcs/panda/_control.py +9 -4
- ophyd_async/fastcs/panda/_hdf_panda.py +7 -2
- ophyd_async/fastcs/panda/_table.py +4 -1
- ophyd_async/fastcs/panda/_trigger.py +7 -7
- ophyd_async/plan_stubs/__init__.py +14 -0
- ophyd_async/plan_stubs/_ensure_connected.py +11 -17
- ophyd_async/plan_stubs/_fly.py +2 -2
- ophyd_async/plan_stubs/_nd_attributes.py +7 -5
- ophyd_async/plan_stubs/_panda.py +13 -0
- ophyd_async/plan_stubs/_settings.py +125 -0
- ophyd_async/plan_stubs/_wait_for_awaitable.py +13 -0
- ophyd_async/sim/__init__.py +19 -0
- ophyd_async/sim/{demo/_pattern_detector → _pattern_detector}/_pattern_detector_controller.py +9 -2
- ophyd_async/sim/{demo/_pattern_detector → _pattern_detector}/_pattern_generator.py +13 -6
- ophyd_async/sim/{demo/_sim_motor.py → _sim_motor.py} +34 -32
- ophyd_async/tango/__init__.py +0 -43
- ophyd_async/tango/{signal → core}/__init__.py +7 -2
- ophyd_async/tango/{base_devices → core}/_base_device.py +38 -64
- ophyd_async/tango/{signal → core}/_signal.py +16 -4
- ophyd_async/tango/{base_devices → core}/_tango_readable.py +3 -4
- ophyd_async/tango/{signal → core}/_tango_transport.py +13 -15
- ophyd_async/tango/{demo → sim}/_counter.py +6 -7
- ophyd_async/tango/{demo → sim}/_mover.py +13 -9
- ophyd_async/testing/__init__.py +52 -0
- ophyd_async/testing/__pytest_assert_rewrite.py +4 -0
- ophyd_async/testing/_assert.py +176 -0
- ophyd_async/{core → testing}/_mock_signal_utils.py +15 -11
- ophyd_async/testing/_one_of_everything.py +126 -0
- ophyd_async/testing/_wait_for_pending.py +22 -0
- {ophyd_async-0.8.0a5.dist-info → ophyd_async-0.9.0.dist-info}/METADATA +50 -48
- ophyd_async-0.9.0.dist-info/RECORD +129 -0
- {ophyd_async-0.8.0a5.dist-info → ophyd_async-0.9.0.dist-info}/WHEEL +1 -1
- ophyd_async/core/_device_save_loader.py +0 -274
- ophyd_async/epics/adsimdetector/_sim_controller.py +0 -51
- ophyd_async/fastcs/panda/_utils.py +0 -16
- ophyd_async/sim/demo/__init__.py +0 -19
- ophyd_async/sim/testing/__init__.py +0 -0
- ophyd_async/tango/base_devices/__init__.py +0 -4
- ophyd_async-0.8.0a5.dist-info/RECORD +0 -112
- ophyd_async-0.8.0a5.dist-info/entry_points.txt +0 -2
- /ophyd_async/epics/{demo → sim}/__init__.py +0 -0
- /ophyd_async/epics/{demo → sim}/mover.db +0 -0
- /ophyd_async/epics/{demo → sim}/sensor.db +0 -0
- /ophyd_async/sim/{demo/_pattern_detector → _pattern_detector}/__init__.py +0 -0
- /ophyd_async/sim/{demo/_pattern_detector → _pattern_detector}/_pattern_detector.py +0 -0
- /ophyd_async/sim/{demo/_pattern_detector → _pattern_detector}/_pattern_detector_writer.py +0 -0
- /ophyd_async/tango/{demo → sim}/__init__.py +0 -0
- /ophyd_async/tango/{demo → sim}/_detector.py +0 -0
- /ophyd_async/tango/{demo → sim}/_tango/__init__.py +0 -0
- /ophyd_async/tango/{demo → sim}/_tango/_servers.py +0 -0
- {ophyd_async-0.8.0a5.dist-info → ophyd_async-0.9.0.dist-info}/LICENSE +0 -0
- {ophyd_async-0.8.0a5.dist-info → ophyd_async-0.9.0.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
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
|
|
|
@@ -8,79 +8,64 @@ from event_model import DataKey
|
|
|
8
8
|
|
|
9
9
|
from ophyd_async.core import (
|
|
10
10
|
DEFAULT_TIMEOUT,
|
|
11
|
-
AsyncStatus,
|
|
12
11
|
DatasetDescriber,
|
|
13
|
-
DetectorWriter,
|
|
14
12
|
HDFDataset,
|
|
15
13
|
HDFFile,
|
|
16
14
|
NameProvider,
|
|
17
15
|
PathProvider,
|
|
18
|
-
observe_value,
|
|
19
|
-
set_and_wait_for_value,
|
|
20
16
|
wait_for_value,
|
|
21
17
|
)
|
|
22
18
|
|
|
23
|
-
from ._core_io import
|
|
19
|
+
from ._core_io import NDFileHDFIO, NDPluginBaseIO
|
|
20
|
+
from ._core_writer import ADWriter
|
|
24
21
|
from ._utils import (
|
|
25
|
-
FileWriteMode,
|
|
26
22
|
convert_param_dtype_to_np,
|
|
27
23
|
convert_pv_dtype_to_np,
|
|
28
24
|
)
|
|
29
25
|
|
|
30
26
|
|
|
31
|
-
class ADHDFWriter(
|
|
27
|
+
class ADHDFWriter(ADWriter[NDFileHDFIO]):
|
|
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
|
-
|
|
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
47
|
self._datasets: list[HDFDataset] = []
|
|
48
48
|
self._file: HDFFile | None = None
|
|
49
|
-
self.
|
|
49
|
+
self._filename_template = "%s%s"
|
|
50
50
|
|
|
51
51
|
async def open(self, multiplier: int = 1) -> dict[str, DataKey]:
|
|
52
52
|
self._file = None
|
|
53
|
-
info = self._path_provider(device_name=self._name_provider())
|
|
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(""),
|
|
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(""),
|
|
74
64
|
)
|
|
75
65
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
), 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()
|
|
79
68
|
|
|
80
|
-
# Overwrite num_capture to go forever
|
|
81
|
-
await self.hdf.num_capture.set(0)
|
|
82
|
-
# Wait for it to start, stashing the status that tells us when it finishes
|
|
83
|
-
self._capture_status = await set_and_wait_for_value(self.hdf.capture, True)
|
|
84
69
|
name = self._name_provider()
|
|
85
70
|
detector_shape = await self._dataset_describer.shape()
|
|
86
71
|
np_dtype = await self._dataset_describer.np_datatype()
|
|
@@ -88,7 +73,7 @@ class ADHDFWriter(DetectorWriter):
|
|
|
88
73
|
outer_shape = (multiplier,) if multiplier > 1 else ()
|
|
89
74
|
|
|
90
75
|
# Determine number of frames that will be saved per HDF chunk
|
|
91
|
-
frames_per_chunk = await self.
|
|
76
|
+
frames_per_chunk = await self.fileio.num_frames_chunks.get_value()
|
|
92
77
|
|
|
93
78
|
# Add the main data
|
|
94
79
|
self._datasets = [
|
|
@@ -102,7 +87,7 @@ class ADHDFWriter(DetectorWriter):
|
|
|
102
87
|
)
|
|
103
88
|
]
|
|
104
89
|
# And all the scalar datasets
|
|
105
|
-
for plugin in self._plugins:
|
|
90
|
+
for plugin in self._plugins.values():
|
|
106
91
|
maybe_xml = await plugin.nd_attributes_file.get_value()
|
|
107
92
|
# This is the check that ADCore does to see if it is an XML string
|
|
108
93
|
# rather than a filename to parse
|
|
@@ -133,7 +118,7 @@ class ADHDFWriter(DetectorWriter):
|
|
|
133
118
|
|
|
134
119
|
describe = {
|
|
135
120
|
ds.data_key: DataKey(
|
|
136
|
-
source=self.
|
|
121
|
+
source=self.fileio.full_file_name.source,
|
|
137
122
|
shape=list(outer_shape + tuple(ds.shape)),
|
|
138
123
|
dtype="array" if ds.shape else "number",
|
|
139
124
|
dtype_numpy=ds.dtype_numpy,
|
|
@@ -143,25 +128,14 @@ class ADHDFWriter(DetectorWriter):
|
|
|
143
128
|
}
|
|
144
129
|
return describe
|
|
145
130
|
|
|
146
|
-
async def observe_indices_written(
|
|
147
|
-
self, timeout=DEFAULT_TIMEOUT
|
|
148
|
-
) -> AsyncGenerator[int, None]:
|
|
149
|
-
"""Wait until a specific index is ready to be collected"""
|
|
150
|
-
async for num_captured in observe_value(self.hdf.num_captured, timeout):
|
|
151
|
-
yield num_captured // self._multiplier
|
|
152
|
-
|
|
153
|
-
async def get_indices_written(self) -> int:
|
|
154
|
-
num_captured = await self.hdf.num_captured.get_value()
|
|
155
|
-
return num_captured // self._multiplier
|
|
156
|
-
|
|
157
131
|
async def collect_stream_docs(
|
|
158
132
|
self, indices_written: int
|
|
159
133
|
) -> AsyncIterator[StreamAsset]:
|
|
160
134
|
# TODO: fail if we get dropped frames
|
|
161
|
-
await self.
|
|
135
|
+
await self.fileio.flush_now.set(True)
|
|
162
136
|
if indices_written:
|
|
163
137
|
if not self._file:
|
|
164
|
-
path = Path(await self.
|
|
138
|
+
path = Path(await self.fileio.full_file_name.get_value())
|
|
165
139
|
self._file = HDFFile(
|
|
166
140
|
# See https://github.com/bluesky/ophyd-async/issues/122
|
|
167
141
|
path,
|
|
@@ -178,8 +152,8 @@ class ADHDFWriter(DetectorWriter):
|
|
|
178
152
|
|
|
179
153
|
async def close(self):
|
|
180
154
|
# Already done a caput callback in _capture_status, so can't do one here
|
|
181
|
-
await self.
|
|
182
|
-
await wait_for_value(self.
|
|
155
|
+
await self.fileio.capture.set(False, wait=False)
|
|
156
|
+
await wait_for_value(self.fileio.capture, False, DEFAULT_TIMEOUT)
|
|
183
157
|
if self._capture_status:
|
|
184
158
|
# We kicked off an open, so wait for it to return
|
|
185
159
|
await self._capture_status
|
|
@@ -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
|
+
)
|
|
@@ -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(ImageMode.
|
|
38
|
+
self.drv.image_mode.set(ImageMode.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,47 +6,48 @@ 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
|
|
|
12
13
|
|
|
13
14
|
class ADBaseDataType(StrictEnum):
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
INT8 = "Int8"
|
|
16
|
+
UINT8 = "UInt8"
|
|
17
|
+
INT16 = "Int16"
|
|
18
|
+
UINT16 = "UInt16"
|
|
19
|
+
INT32 = "Int32"
|
|
20
|
+
UINT32 = "UInt32"
|
|
21
|
+
INT64 = "Int64"
|
|
22
|
+
UINT64 = "UInt64"
|
|
23
|
+
FLOAT32 = "Float32"
|
|
24
|
+
FLOAT64 = "Float64"
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def convert_ad_dtype_to_np(ad_dtype: ADBaseDataType) -> str:
|
|
27
28
|
ad_dtype_to_np_dtype = {
|
|
28
|
-
ADBaseDataType.
|
|
29
|
-
ADBaseDataType.
|
|
30
|
-
ADBaseDataType.
|
|
31
|
-
ADBaseDataType.
|
|
32
|
-
ADBaseDataType.
|
|
33
|
-
ADBaseDataType.
|
|
34
|
-
ADBaseDataType.
|
|
35
|
-
ADBaseDataType.
|
|
36
|
-
ADBaseDataType.
|
|
37
|
-
ADBaseDataType.
|
|
29
|
+
ADBaseDataType.INT8: "|i1",
|
|
30
|
+
ADBaseDataType.UINT8: "|u1",
|
|
31
|
+
ADBaseDataType.INT16: "<i2",
|
|
32
|
+
ADBaseDataType.UINT16: "<u2",
|
|
33
|
+
ADBaseDataType.INT32: "<i4",
|
|
34
|
+
ADBaseDataType.UINT32: "<u4",
|
|
35
|
+
ADBaseDataType.INT64: "<i8",
|
|
36
|
+
ADBaseDataType.UINT64: "<u8",
|
|
37
|
+
ADBaseDataType.FLOAT32: "<f4",
|
|
38
|
+
ADBaseDataType.FLOAT64: "<f8",
|
|
38
39
|
}
|
|
39
40
|
return ad_dtype_to_np_dtype[ad_dtype]
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
def convert_pv_dtype_to_np(datatype: str) -> str:
|
|
43
44
|
_pvattribute_to_ad_datatype = {
|
|
44
|
-
"DBR_SHORT": ADBaseDataType.
|
|
45
|
-
"DBR_ENUM": ADBaseDataType.
|
|
46
|
-
"DBR_INT": ADBaseDataType.
|
|
47
|
-
"DBR_LONG": ADBaseDataType.
|
|
48
|
-
"DBR_FLOAT": ADBaseDataType.
|
|
49
|
-
"DBR_DOUBLE": ADBaseDataType.
|
|
45
|
+
"DBR_SHORT": ADBaseDataType.INT16,
|
|
46
|
+
"DBR_ENUM": ADBaseDataType.INT16,
|
|
47
|
+
"DBR_INT": ADBaseDataType.INT32,
|
|
48
|
+
"DBR_LONG": ADBaseDataType.INT32,
|
|
49
|
+
"DBR_FLOAT": ADBaseDataType.FLOAT32,
|
|
50
|
+
"DBR_DOUBLE": ADBaseDataType.FLOAT64,
|
|
50
51
|
}
|
|
51
52
|
if datatype in ["DBR_STRING", "DBR_CHAR"]:
|
|
52
53
|
np_datatype = "s40"
|
|
@@ -62,9 +63,9 @@ def convert_pv_dtype_to_np(datatype: str) -> str:
|
|
|
62
63
|
|
|
63
64
|
def convert_param_dtype_to_np(datatype: str) -> str:
|
|
64
65
|
_paramattribute_to_ad_datatype = {
|
|
65
|
-
"INT": ADBaseDataType.
|
|
66
|
-
"INT64": ADBaseDataType.
|
|
67
|
-
"DOUBLE": ADBaseDataType.
|
|
66
|
+
"INT": ADBaseDataType.INT32,
|
|
67
|
+
"INT64": ADBaseDataType.INT64,
|
|
68
|
+
"DOUBLE": ADBaseDataType.FLOAT64,
|
|
68
69
|
}
|
|
69
70
|
if datatype in ["STRING"]:
|
|
70
71
|
np_datatype = "s40"
|
|
@@ -79,15 +80,15 @@ def convert_param_dtype_to_np(datatype: str) -> str:
|
|
|
79
80
|
|
|
80
81
|
|
|
81
82
|
class FileWriteMode(StrictEnum):
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
SINGLE = "Single"
|
|
84
|
+
CAPTURE = "Capture"
|
|
85
|
+
STREAM = "Stream"
|
|
85
86
|
|
|
86
87
|
|
|
87
|
-
class ImageMode(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
class ImageMode(SubsetEnum):
|
|
89
|
+
SINGLE = "Single"
|
|
90
|
+
MULTIPLE = "Multiple"
|
|
91
|
+
CONTINUOUS = "Continuous"
|
|
91
92
|
|
|
92
93
|
|
|
93
94
|
class NDAttributeDataType(StrictEnum):
|
|
@@ -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(
|
|
15
|
+
class KinetixDetector(AreaDetector[KinetixController]):
|
|
11
16
|
"""
|
|
12
17
|
Ophyd-async implementation of an ADKinetix Detector.
|
|
13
18
|
https://github.com/NSLS-II/ADKinetix
|
|
14
19
|
"""
|
|
15
20
|
|
|
16
|
-
_controller: KinetixController
|
|
17
|
-
_writer: adcore.ADHDFWriter
|
|
18
|
-
|
|
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
|
)
|
|
@@ -11,44 +9,34 @@ from ophyd_async.epics import adcore
|
|
|
11
9
|
from ._kinetix_io import KinetixDriverIO, KinetixTriggerMode
|
|
12
10
|
|
|
13
11
|
KINETIX_TRIGGER_MODE_MAP = {
|
|
14
|
-
DetectorTrigger.
|
|
15
|
-
DetectorTrigger.
|
|
16
|
-
DetectorTrigger.
|
|
17
|
-
DetectorTrigger.
|
|
12
|
+
DetectorTrigger.INTERNAL: KinetixTriggerMode.INTERNAL,
|
|
13
|
+
DetectorTrigger.CONSTANT_GATE: KinetixTriggerMode.GATE,
|
|
14
|
+
DetectorTrigger.VARIABLE_GATE: KinetixTriggerMode.GATE,
|
|
15
|
+
DetectorTrigger.EDGE_TRIGGER: KinetixTriggerMode.EDGE,
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
|
|
21
|
-
class KinetixController(
|
|
19
|
+
class KinetixController(adcore.ADBaseController[KinetixDriverIO]):
|
|
22
20
|
def __init__(
|
|
23
21
|
self,
|
|
24
22
|
driver: KinetixDriverIO,
|
|
23
|
+
good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
|
|
25
24
|
) -> None:
|
|
26
|
-
|
|
27
|
-
self._arm_status: AsyncStatus | None = None
|
|
25
|
+
super().__init__(driver, good_states=good_states)
|
|
28
26
|
|
|
29
27
|
def get_deadtime(self, exposure: float | None) -> float:
|
|
30
28
|
return 0.001
|
|
31
29
|
|
|
32
30
|
async def prepare(self, trigger_info: TriggerInfo):
|
|
33
31
|
await asyncio.gather(
|
|
34
|
-
self.
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
self.driver.trigger_mode.set(
|
|
33
|
+
KINETIX_TRIGGER_MODE_MAP[trigger_info.trigger]
|
|
34
|
+
),
|
|
35
|
+
self.driver.num_images.set(trigger_info.total_number_of_triggers),
|
|
36
|
+
self.driver.image_mode.set(adcore.ImageMode.MULTIPLE),
|
|
37
37
|
)
|
|
38
38
|
if trigger_info.livetime is not None and trigger_info.trigger not in [
|
|
39
|
-
DetectorTrigger.
|
|
40
|
-
DetectorTrigger.
|
|
39
|
+
DetectorTrigger.VARIABLE_GATE,
|
|
40
|
+
DetectorTrigger.CONSTANT_GATE,
|
|
41
41
|
]:
|
|
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)
|
|
42
|
+
await self.driver.acquire_time.set(trigger_info.livetime)
|
|
@@ -4,16 +4,16 @@ from ophyd_async.epics.core import epics_signal_rw_rbv
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class KinetixTriggerMode(StrictEnum):
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
INTERNAL = "Internal"
|
|
8
|
+
EDGE = "Rising Edge"
|
|
9
|
+
GATE = "Exp. Gate"
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class KinetixReadoutMode(StrictEnum):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
SENSITIVITY = 1
|
|
14
|
+
SPEED = 2
|
|
15
|
+
DYNAMIC_RANGE = 3
|
|
16
|
+
SUB_ELECTRON = 4
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class KinetixDriverIO(adcore.ADBaseIO):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from ._pilatus import PilatusDetector
|
|
2
|
-
from ._pilatus_controller import PilatusController
|
|
1
|
+
from ._pilatus import PilatusDetector
|
|
2
|
+
from ._pilatus_controller import PilatusController, PilatusReadoutTime
|
|
3
3
|
from ._pilatus_io import PilatusDriverIO, PilatusTriggerMode
|
|
4
4
|
|
|
5
5
|
__all__ = [
|
|
@@ -1,58 +1,46 @@
|
|
|
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
|
-
#: The required minimum time difference between ExpPeriod and ExpTime
|
|
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):
|
|
14
|
+
class PilatusDetector(AreaDetector[PilatusController]):
|
|
27
15
|
"""A Pilatus StandardDetector writing HDF files"""
|
|
28
16
|
|
|
29
|
-
_controller: PilatusController
|
|
30
|
-
_writer: adcore.ADHDFWriter
|
|
31
|
-
|
|
32
17
|
def __init__(
|
|
33
18
|
self,
|
|
34
19
|
prefix: str,
|
|
35
20
|
path_provider: PathProvider,
|
|
36
|
-
readout_time: PilatusReadoutTime = PilatusReadoutTime.
|
|
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
|