ophyd-async 0.8.0a6__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 +15 -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 +91 -151
- ophyd_async/core/_signal_backend.py +4 -1
- ophyd_async/core/_soft_signal_backend.py +2 -1
- ophyd_async/core/_table.py +18 -10
- 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 -86
- 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 +4 -4
- 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 +14 -14
- ophyd_async/epics/testing/_example_ioc.py +53 -67
- ophyd_async/epics/testing/_utils.py +17 -45
- ophyd_async/epics/testing/test_records.db +22 -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/core/_signal.py +3 -1
- ophyd_async/tango/core/_tango_transport.py +13 -15
- ophyd_async/tango/{demo → sim}/_mover.py +5 -2
- 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.0a6.dist-info → ophyd_async-0.9.0.dist-info}/METADATA +4 -2
- ophyd_async-0.9.0.dist-info/RECORD +129 -0
- {ophyd_async-0.8.0a6.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-0.8.0a6.dist-info/RECORD +0 -116
- ophyd_async-0.8.0a6.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}/_counter.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.0a6.dist-info → ophyd_async-0.9.0.dist-info}/LICENSE +0 -0
- {ophyd_async-0.8.0a6.dist-info → ophyd_async-0.9.0.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import TypeVar, get_args
|
|
2
4
|
|
|
3
5
|
from ophyd_async.core import (
|
|
4
6
|
DEFAULT_TIMEOUT,
|
|
5
|
-
AsyncStatus,
|
|
6
|
-
DetectorController,
|
|
7
7
|
DetectorTrigger,
|
|
8
8
|
TriggerInfo,
|
|
9
9
|
wait_for_value,
|
|
@@ -13,58 +13,83 @@ from ophyd_async.epics import adcore
|
|
|
13
13
|
from ._pilatus_io import PilatusDriverIO, PilatusTriggerMode
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
|
|
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
|
+
class PilatusReadoutTime(float, Enum):
|
|
21
|
+
"""Pilatus readout time per model in ms"""
|
|
22
|
+
|
|
23
|
+
# Cite: https://media.dectris.com/User_Manual-PILATUS2-V1_4.pdf
|
|
24
|
+
PILATUS2 = 2.28e-3
|
|
25
|
+
|
|
26
|
+
# Cite: https://media.dectris.com/user-manual-pilatus3-2020.pdf
|
|
27
|
+
PILATUS3 = 0.95e-3
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
PilatusControllerT = TypeVar("PilatusControllerT", bound="PilatusController")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
|
|
17
34
|
_supported_trigger_types = {
|
|
18
|
-
DetectorTrigger.
|
|
19
|
-
DetectorTrigger.
|
|
20
|
-
DetectorTrigger.
|
|
35
|
+
DetectorTrigger.INTERNAL: PilatusTriggerMode.INTERNAL,
|
|
36
|
+
DetectorTrigger.CONSTANT_GATE: PilatusTriggerMode.EXT_ENABLE,
|
|
37
|
+
DetectorTrigger.VARIABLE_GATE: PilatusTriggerMode.EXT_ENABLE,
|
|
21
38
|
}
|
|
22
39
|
|
|
23
40
|
def __init__(
|
|
24
41
|
self,
|
|
25
42
|
driver: PilatusDriverIO,
|
|
26
|
-
|
|
43
|
+
good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
|
|
44
|
+
readout_time: float = PilatusReadoutTime.PILATUS3,
|
|
27
45
|
) -> None:
|
|
28
|
-
|
|
46
|
+
super().__init__(driver, good_states=good_states)
|
|
29
47
|
self._readout_time = readout_time
|
|
30
|
-
|
|
48
|
+
|
|
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
|
|
31
61
|
|
|
32
62
|
def get_deadtime(self, exposure: float | None) -> float:
|
|
33
63
|
return self._readout_time
|
|
34
64
|
|
|
35
65
|
async def prepare(self, trigger_info: TriggerInfo):
|
|
36
66
|
if trigger_info.livetime is not None:
|
|
37
|
-
await
|
|
38
|
-
|
|
67
|
+
await self.set_exposure_time_and_acquire_period_if_supplied(
|
|
68
|
+
trigger_info.livetime
|
|
39
69
|
)
|
|
40
70
|
await asyncio.gather(
|
|
41
|
-
self.
|
|
42
|
-
self.
|
|
71
|
+
self.driver.trigger_mode.set(self._get_trigger_mode(trigger_info.trigger)),
|
|
72
|
+
self.driver.num_images.set(
|
|
43
73
|
999_999
|
|
44
74
|
if trigger_info.total_number_of_triggers == 0
|
|
45
75
|
else trigger_info.total_number_of_triggers
|
|
46
76
|
),
|
|
47
|
-
self.
|
|
77
|
+
self.driver.image_mode.set(adcore.ImageMode.MULTIPLE),
|
|
48
78
|
)
|
|
49
79
|
|
|
50
80
|
async def arm(self):
|
|
51
81
|
# Standard arm the detector and wait for the acquire PV to be True
|
|
52
|
-
self._arm_status = await
|
|
53
|
-
|
|
54
|
-
)
|
|
82
|
+
self._arm_status = await self.start_acquiring_driver_and_ensure_status()
|
|
83
|
+
|
|
55
84
|
# The pilatus has an additional PV that goes True when the camserver
|
|
56
85
|
# is actually ready. Should wait for that too or we risk dropping
|
|
57
86
|
# a frame
|
|
58
87
|
await wait_for_value(
|
|
59
|
-
self.
|
|
88
|
+
self.driver.armed,
|
|
60
89
|
True,
|
|
61
90
|
timeout=DEFAULT_TIMEOUT,
|
|
62
91
|
)
|
|
63
92
|
|
|
64
|
-
async def wait_for_idle(self):
|
|
65
|
-
if self._arm_status:
|
|
66
|
-
await self._arm_status
|
|
67
|
-
|
|
68
93
|
@classmethod
|
|
69
94
|
def _get_trigger_mode(cls, trigger: DetectorTrigger) -> PilatusTriggerMode:
|
|
70
95
|
if trigger not in cls._supported_trigger_types.keys():
|
|
@@ -74,6 +99,3 @@ class PilatusController(DetectorController):
|
|
|
74
99
|
f"use {trigger}"
|
|
75
100
|
)
|
|
76
101
|
return cls._supported_trigger_types[trigger]
|
|
77
|
-
|
|
78
|
-
async def disarm(self):
|
|
79
|
-
await adcore.stop_busy_record(self._drv.acquire, False, timeout=1)
|
|
@@ -4,11 +4,11 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw_rbv
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class PilatusTriggerMode(StrictEnum):
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
INTERNAL = "Internal"
|
|
8
|
+
EXT_ENABLE = "Ext. Enable"
|
|
9
|
+
EXT_TRIGGER = "Ext. Trigger"
|
|
10
|
+
MULT_TRIGGER = "Mult. Trigger"
|
|
11
|
+
ALIGNMENT = "Alignment"
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class PilatusDriverIO(adcore.ADBaseIO):
|
|
@@ -1,35 +1,51 @@
|
|
|
1
1
|
from collections.abc import Sequence
|
|
2
2
|
|
|
3
|
-
from ophyd_async.core import PathProvider, SignalR
|
|
3
|
+
from ophyd_async.core import PathProvider, SignalR
|
|
4
4
|
from ophyd_async.epics import adcore
|
|
5
5
|
|
|
6
|
-
from ._sim_controller import SimController
|
|
7
6
|
|
|
7
|
+
class SimDriverIO(adcore.ADBaseIO): ...
|
|
8
8
|
|
|
9
|
-
class SimDetector(StandardDetector):
|
|
10
|
-
_controller: SimController
|
|
11
|
-
_writer: adcore.ADHDFWriter
|
|
12
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
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SimDetector(adcore.AreaDetector[SimController]):
|
|
13
23
|
def __init__(
|
|
14
24
|
self,
|
|
15
25
|
prefix: str,
|
|
16
26
|
path_provider: PathProvider,
|
|
17
27
|
drv_suffix="cam1:",
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
writer_cls: type[adcore.ADWriter] = adcore.ADHDFWriter,
|
|
29
|
+
fileio_suffix: str | None = None,
|
|
30
|
+
name="",
|
|
20
31
|
config_sigs: Sequence[SignalR] = (),
|
|
32
|
+
plugins: dict[str, adcore.NDPluginBaseIO] | None = None,
|
|
21
33
|
):
|
|
22
|
-
|
|
23
|
-
|
|
34
|
+
driver = SimDriverIO(prefix + drv_suffix)
|
|
35
|
+
controller = SimController(driver)
|
|
36
|
+
|
|
37
|
+
writer = writer_cls.with_io(
|
|
38
|
+
prefix,
|
|
39
|
+
path_provider,
|
|
40
|
+
dataset_source=driver,
|
|
41
|
+
fileio_suffix=fileio_suffix,
|
|
42
|
+
plugins=plugins,
|
|
43
|
+
)
|
|
24
44
|
|
|
25
45
|
super().__init__(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
path_provider,
|
|
30
|
-
lambda: self.name,
|
|
31
|
-
adcore.ADBaseDatasetDescriber(self.drv),
|
|
32
|
-
),
|
|
33
|
-
config_sigs=(self.drv.acquire_period, self.drv.acquire_time, *config_sigs),
|
|
46
|
+
controller=controller,
|
|
47
|
+
writer=writer,
|
|
48
|
+
plugins=plugins,
|
|
34
49
|
name=name,
|
|
50
|
+
config_sigs=config_sigs,
|
|
35
51
|
)
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Sequence
|
|
2
2
|
|
|
3
|
-
from ophyd_async.core import PathProvider,
|
|
3
|
+
from ophyd_async.core import PathProvider, SignalR
|
|
4
4
|
from ophyd_async.epics import adcore
|
|
5
5
|
|
|
6
6
|
from ._vimba_controller import VimbaController
|
|
7
7
|
from ._vimba_io import VimbaDriverIO
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class VimbaDetector(
|
|
10
|
+
class VimbaDetector(adcore.AreaDetector[VimbaController]):
|
|
11
11
|
"""
|
|
12
12
|
Ophyd-async implementation of an ADVimba Detector.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
_controller: VimbaController
|
|
16
|
-
_writer: adcore.ADHDFWriter
|
|
17
|
-
|
|
18
15
|
def __init__(
|
|
19
16
|
self,
|
|
20
17
|
prefix: str,
|
|
21
18
|
path_provider: PathProvider,
|
|
22
|
-
drv_suffix="cam1:",
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
drv_suffix: str = "cam1:",
|
|
20
|
+
writer_cls: type[adcore.ADWriter] = adcore.ADHDFWriter,
|
|
21
|
+
fileio_suffix: str | None = None,
|
|
22
|
+
name: str = "",
|
|
23
|
+
plugins: dict[str, adcore.NDPluginBaseIO] | None = None,
|
|
24
|
+
config_sigs: Sequence[SignalR] = (),
|
|
25
25
|
):
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
driver = VimbaDriverIO(prefix + drv_suffix)
|
|
27
|
+
controller = VimbaController(driver)
|
|
28
|
+
|
|
29
|
+
writer = writer_cls.with_io(
|
|
30
|
+
prefix,
|
|
31
|
+
path_provider,
|
|
32
|
+
dataset_source=driver,
|
|
33
|
+
fileio_suffix=fileio_suffix,
|
|
34
|
+
plugins=plugins,
|
|
35
|
+
)
|
|
28
36
|
|
|
29
37
|
super().__init__(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
path_provider,
|
|
34
|
-
lambda: self.name,
|
|
35
|
-
adcore.ADBaseDatasetDescriber(self.drv),
|
|
36
|
-
),
|
|
37
|
-
config_sigs=(self.drv.acquire_time,),
|
|
38
|
+
controller=controller,
|
|
39
|
+
writer=writer,
|
|
40
|
+
plugins=plugins,
|
|
38
41
|
name=name,
|
|
42
|
+
config_sigs=config_sigs,
|
|
39
43
|
)
|
|
40
|
-
|
|
41
|
-
@property
|
|
42
|
-
def hints(self) -> Hints:
|
|
43
|
-
return self._writer.hints
|