ophyd-async 0.1.0__py3-none-any.whl → 0.3.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/__init__.py +1 -4
- ophyd_async/_version.py +2 -2
- ophyd_async/core/__init__.py +91 -19
- ophyd_async/core/_providers.py +68 -0
- ophyd_async/core/async_status.py +90 -42
- ophyd_async/core/detector.py +341 -0
- ophyd_async/core/device.py +226 -0
- ophyd_async/core/device_save_loader.py +286 -0
- ophyd_async/core/flyer.py +85 -0
- ophyd_async/core/mock_signal_backend.py +82 -0
- ophyd_async/core/mock_signal_utils.py +145 -0
- ophyd_async/core/{_device/_signal/signal.py → signal.py} +249 -61
- ophyd_async/core/{_device/_backend/signal_backend.py → signal_backend.py} +12 -5
- ophyd_async/core/{_device/_backend/sim_signal_backend.py → soft_signal_backend.py} +54 -48
- ophyd_async/core/standard_readable.py +261 -0
- ophyd_async/core/utils.py +127 -30
- ophyd_async/epics/_backend/_aioca.py +62 -43
- ophyd_async/epics/_backend/_p4p.py +100 -52
- ophyd_async/epics/_backend/common.py +25 -0
- ophyd_async/epics/areadetector/__init__.py +16 -15
- ophyd_async/epics/areadetector/aravis.py +63 -0
- ophyd_async/epics/areadetector/controllers/__init__.py +5 -0
- ophyd_async/epics/areadetector/controllers/ad_sim_controller.py +52 -0
- ophyd_async/epics/areadetector/controllers/aravis_controller.py +78 -0
- ophyd_async/epics/areadetector/controllers/kinetix_controller.py +49 -0
- ophyd_async/epics/areadetector/controllers/pilatus_controller.py +61 -0
- ophyd_async/epics/areadetector/controllers/vimba_controller.py +66 -0
- ophyd_async/epics/areadetector/drivers/__init__.py +21 -0
- ophyd_async/epics/areadetector/drivers/ad_base.py +107 -0
- ophyd_async/epics/areadetector/drivers/aravis_driver.py +38 -0
- ophyd_async/epics/areadetector/drivers/kinetix_driver.py +27 -0
- ophyd_async/epics/areadetector/drivers/pilatus_driver.py +21 -0
- ophyd_async/epics/areadetector/drivers/vimba_driver.py +63 -0
- ophyd_async/epics/areadetector/kinetix.py +46 -0
- ophyd_async/epics/areadetector/pilatus.py +45 -0
- ophyd_async/epics/areadetector/single_trigger_det.py +18 -10
- ophyd_async/epics/areadetector/utils.py +91 -13
- ophyd_async/epics/areadetector/vimba.py +43 -0
- ophyd_async/epics/areadetector/writers/__init__.py +5 -0
- ophyd_async/epics/areadetector/writers/_hdfdataset.py +10 -0
- ophyd_async/epics/areadetector/writers/_hdffile.py +54 -0
- ophyd_async/epics/areadetector/writers/hdf_writer.py +142 -0
- ophyd_async/epics/areadetector/writers/nd_file_hdf.py +40 -0
- ophyd_async/epics/areadetector/writers/nd_plugin.py +38 -0
- ophyd_async/epics/demo/__init__.py +78 -51
- ophyd_async/epics/demo/demo_ad_sim_detector.py +35 -0
- ophyd_async/epics/motion/motor.py +67 -52
- ophyd_async/epics/pvi/__init__.py +3 -0
- ophyd_async/epics/pvi/pvi.py +318 -0
- ophyd_async/epics/signal/__init__.py +8 -3
- ophyd_async/epics/signal/signal.py +27 -10
- ophyd_async/log.py +130 -0
- ophyd_async/panda/__init__.py +24 -7
- ophyd_async/panda/_common_blocks.py +49 -0
- ophyd_async/panda/_hdf_panda.py +48 -0
- ophyd_async/panda/_panda_controller.py +37 -0
- ophyd_async/panda/_table.py +158 -0
- ophyd_async/panda/_trigger.py +39 -0
- ophyd_async/panda/_utils.py +15 -0
- ophyd_async/panda/writers/__init__.py +3 -0
- ophyd_async/panda/writers/_hdf_writer.py +220 -0
- ophyd_async/panda/writers/_panda_hdf_file.py +58 -0
- ophyd_async/plan_stubs/__init__.py +13 -0
- ophyd_async/plan_stubs/ensure_connected.py +22 -0
- ophyd_async/plan_stubs/fly.py +149 -0
- ophyd_async/protocols.py +126 -0
- ophyd_async/sim/__init__.py +11 -0
- ophyd_async/sim/demo/__init__.py +3 -0
- ophyd_async/sim/demo/sim_motor.py +103 -0
- ophyd_async/sim/pattern_generator.py +318 -0
- ophyd_async/sim/sim_pattern_detector_control.py +55 -0
- ophyd_async/sim/sim_pattern_detector_writer.py +34 -0
- ophyd_async/sim/sim_pattern_generator.py +37 -0
- {ophyd_async-0.1.0.dist-info → ophyd_async-0.3.0.dist-info}/METADATA +35 -67
- ophyd_async-0.3.0.dist-info/RECORD +86 -0
- {ophyd_async-0.1.0.dist-info → ophyd_async-0.3.0.dist-info}/WHEEL +1 -1
- ophyd_async/core/_device/__init__.py +0 -0
- ophyd_async/core/_device/_backend/__init__.py +0 -0
- ophyd_async/core/_device/_signal/__init__.py +0 -0
- ophyd_async/core/_device/device.py +0 -60
- ophyd_async/core/_device/device_collector.py +0 -121
- ophyd_async/core/_device/device_vector.py +0 -14
- ophyd_async/core/_device/standard_readable.py +0 -72
- ophyd_async/epics/areadetector/ad_driver.py +0 -18
- ophyd_async/epics/areadetector/directory_provider.py +0 -18
- ophyd_async/epics/areadetector/hdf_streamer_det.py +0 -167
- ophyd_async/epics/areadetector/nd_file_hdf.py +0 -22
- ophyd_async/epics/areadetector/nd_plugin.py +0 -13
- ophyd_async/epics/signal/pvi_get.py +0 -22
- ophyd_async/panda/panda.py +0 -332
- ophyd_async-0.1.0.dist-info/RECORD +0 -45
- {ophyd_async-0.1.0.dist-info → ophyd_async-0.3.0.dist-info}/LICENSE +0 -0
- {ophyd_async-0.1.0.dist-info → ophyd_async-0.3.0.dist-info}/entry_points.txt +0 -0
- {ophyd_async-0.1.0.dist-info → ophyd_async-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from ophyd_async.core import AsyncStatus, DetectorControl, DetectorTrigger
|
|
5
|
+
from ophyd_async.epics.areadetector.drivers.ad_base import (
|
|
6
|
+
start_acquiring_driver_and_ensure_status,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from ..drivers.vimba_driver import (
|
|
10
|
+
VimbaDriver,
|
|
11
|
+
VimbaExposeOutMode,
|
|
12
|
+
VimbaOnOff,
|
|
13
|
+
VimbaTriggerSource,
|
|
14
|
+
)
|
|
15
|
+
from ..utils import ImageMode, stop_busy_record
|
|
16
|
+
|
|
17
|
+
TRIGGER_MODE = {
|
|
18
|
+
DetectorTrigger.internal: VimbaOnOff.off,
|
|
19
|
+
DetectorTrigger.constant_gate: VimbaOnOff.on,
|
|
20
|
+
DetectorTrigger.variable_gate: VimbaOnOff.on,
|
|
21
|
+
DetectorTrigger.edge_trigger: VimbaOnOff.on,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
EXPOSE_OUT_MODE = {
|
|
25
|
+
DetectorTrigger.internal: VimbaExposeOutMode.timed,
|
|
26
|
+
DetectorTrigger.constant_gate: VimbaExposeOutMode.trigger_width,
|
|
27
|
+
DetectorTrigger.variable_gate: VimbaExposeOutMode.trigger_width,
|
|
28
|
+
DetectorTrigger.edge_trigger: VimbaExposeOutMode.timed,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class VimbaController(DetectorControl):
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
driver: VimbaDriver,
|
|
36
|
+
) -> None:
|
|
37
|
+
self._drv = driver
|
|
38
|
+
|
|
39
|
+
def get_deadtime(self, exposure: float) -> float:
|
|
40
|
+
return 0.001
|
|
41
|
+
|
|
42
|
+
async def arm(
|
|
43
|
+
self,
|
|
44
|
+
num: int,
|
|
45
|
+
trigger: DetectorTrigger = DetectorTrigger.internal,
|
|
46
|
+
exposure: Optional[float] = None,
|
|
47
|
+
) -> AsyncStatus:
|
|
48
|
+
await asyncio.gather(
|
|
49
|
+
self._drv.trigger_mode.set(TRIGGER_MODE[trigger]),
|
|
50
|
+
self._drv.expose_mode.set(EXPOSE_OUT_MODE[trigger]),
|
|
51
|
+
self._drv.num_images.set(num),
|
|
52
|
+
self._drv.image_mode.set(ImageMode.multiple),
|
|
53
|
+
)
|
|
54
|
+
if exposure is not None and trigger not in [
|
|
55
|
+
DetectorTrigger.variable_gate,
|
|
56
|
+
DetectorTrigger.constant_gate,
|
|
57
|
+
]:
|
|
58
|
+
await self._drv.acquire_time.set(exposure)
|
|
59
|
+
if trigger != DetectorTrigger.internal:
|
|
60
|
+
self._drv.trig_source.set(VimbaTriggerSource.line1)
|
|
61
|
+
else:
|
|
62
|
+
self._drv.trig_source.set(VimbaTriggerSource.freerun)
|
|
63
|
+
return await start_acquiring_driver_and_ensure_status(self._drv)
|
|
64
|
+
|
|
65
|
+
async def disarm(self):
|
|
66
|
+
await stop_busy_record(self._drv.acquire, False, timeout=1)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .ad_base import (
|
|
2
|
+
ADBase,
|
|
3
|
+
ADBaseShapeProvider,
|
|
4
|
+
DetectorState,
|
|
5
|
+
start_acquiring_driver_and_ensure_status,
|
|
6
|
+
)
|
|
7
|
+
from .aravis_driver import AravisDriver
|
|
8
|
+
from .kinetix_driver import KinetixDriver
|
|
9
|
+
from .pilatus_driver import PilatusDriver
|
|
10
|
+
from .vimba_driver import VimbaDriver
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ADBase",
|
|
14
|
+
"ADBaseShapeProvider",
|
|
15
|
+
"PilatusDriver",
|
|
16
|
+
"AravisDriver",
|
|
17
|
+
"KinetixDriver",
|
|
18
|
+
"VimbaDriver",
|
|
19
|
+
"start_acquiring_driver_and_ensure_status",
|
|
20
|
+
"DetectorState",
|
|
21
|
+
]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import FrozenSet, Sequence, Set
|
|
4
|
+
|
|
5
|
+
from ophyd_async.core import (
|
|
6
|
+
DEFAULT_TIMEOUT,
|
|
7
|
+
AsyncStatus,
|
|
8
|
+
ShapeProvider,
|
|
9
|
+
set_and_wait_for_value,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from ...signal.signal import epics_signal_r, epics_signal_rw_rbv
|
|
13
|
+
from ..utils import ImageMode
|
|
14
|
+
from ..writers.nd_plugin import NDArrayBase
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DetectorState(str, Enum):
|
|
18
|
+
"""
|
|
19
|
+
Default set of states of an AreaDetector driver.
|
|
20
|
+
See definition in ADApp/ADSrc/ADDriver.h in https://github.com/areaDetector/ADCore
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
Idle = "Idle"
|
|
24
|
+
Acquire = "Acquire"
|
|
25
|
+
Readout = "Readout"
|
|
26
|
+
Correct = "Correct"
|
|
27
|
+
Saving = "Saving"
|
|
28
|
+
Aborting = "Aborting"
|
|
29
|
+
Error = "Error"
|
|
30
|
+
Waiting = "Waiting"
|
|
31
|
+
Initializing = "Initializing"
|
|
32
|
+
Disconnected = "Disconnected"
|
|
33
|
+
Aborted = "Aborted"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
#: Default set of states that we should consider "good" i.e. the acquisition
|
|
37
|
+
# is complete and went well
|
|
38
|
+
DEFAULT_GOOD_STATES: FrozenSet[DetectorState] = frozenset(
|
|
39
|
+
[DetectorState.Idle, DetectorState.Aborted]
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ADBase(NDArrayBase):
|
|
44
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
45
|
+
# Define some signals
|
|
46
|
+
self.acquire_time = epics_signal_rw_rbv(float, prefix + "AcquireTime")
|
|
47
|
+
self.num_images = epics_signal_rw_rbv(int, prefix + "NumImages")
|
|
48
|
+
self.image_mode = epics_signal_rw_rbv(ImageMode, prefix + "ImageMode")
|
|
49
|
+
self.detector_state = epics_signal_r(
|
|
50
|
+
DetectorState, prefix + "DetectorState_RBV"
|
|
51
|
+
)
|
|
52
|
+
super().__init__(prefix, name=name)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def start_acquiring_driver_and_ensure_status(
|
|
56
|
+
driver: ADBase,
|
|
57
|
+
good_states: Set[DetectorState] = set(DEFAULT_GOOD_STATES),
|
|
58
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
59
|
+
) -> AsyncStatus:
|
|
60
|
+
"""
|
|
61
|
+
Start acquiring driver, raising ValueError if the detector is in a bad state.
|
|
62
|
+
|
|
63
|
+
This sets driver.acquire to True, and waits for it to be True up to a timeout.
|
|
64
|
+
Then, it checks that the DetectorState PV is in DEFAULT_GOOD_STATES, and otherwise
|
|
65
|
+
raises a ValueError.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
driver:
|
|
70
|
+
The driver to start acquiring. Must subclass ADBase.
|
|
71
|
+
good_states:
|
|
72
|
+
set of states defined in DetectorState enum which are considered good states.
|
|
73
|
+
timeout:
|
|
74
|
+
How long to wait for driver.acquire to readback True (i.e. acquiring).
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
AsyncStatus:
|
|
79
|
+
An AsyncStatus that can be awaited to set driver.acquire to True and perform
|
|
80
|
+
subsequent raising (if applicable) due to detector state.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
status = await set_and_wait_for_value(driver.acquire, True, timeout=timeout)
|
|
84
|
+
|
|
85
|
+
async def complete_acquisition() -> None:
|
|
86
|
+
"""NOTE: possible race condition here between the callback from
|
|
87
|
+
set_and_wait_for_value and the detector state updating."""
|
|
88
|
+
await status
|
|
89
|
+
state = await driver.detector_state.get_value()
|
|
90
|
+
if state not in good_states:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"Final detector state {state} not in valid end states: {good_states}"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return AsyncStatus(complete_acquisition())
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class ADBaseShapeProvider(ShapeProvider):
|
|
99
|
+
def __init__(self, driver: ADBase) -> None:
|
|
100
|
+
self._driver = driver
|
|
101
|
+
|
|
102
|
+
async def __call__(self) -> Sequence[int]:
|
|
103
|
+
shape = await asyncio.gather(
|
|
104
|
+
self._driver.array_size_y.get_value(),
|
|
105
|
+
self._driver.array_size_x.get_value(),
|
|
106
|
+
)
|
|
107
|
+
return shape
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from ophyd_async.epics.areadetector.drivers import ADBase
|
|
5
|
+
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AravisTriggerMode(str, Enum):
|
|
9
|
+
"""GigEVision GenICAM standard: on=externally triggered"""
|
|
10
|
+
|
|
11
|
+
on = "On"
|
|
12
|
+
off = "Off"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
"""A minimal set of TriggerSources that must be supported by the underlying record.
|
|
16
|
+
To enable hardware triggered scanning, line_N must support each N in GPIO_NUMBER.
|
|
17
|
+
To enable software triggered scanning, freerun must be supported.
|
|
18
|
+
Other enumerated values may or may not be preset.
|
|
19
|
+
To prevent requiring one Enum class per possible configuration, we set as this Enum
|
|
20
|
+
but read from the underlying signal as a str.
|
|
21
|
+
"""
|
|
22
|
+
AravisTriggerSource = Literal["Freerun", "Line1", "Line2", "Line3", "Line4"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AravisDriver(ADBase):
|
|
26
|
+
# If instantiating a new instance, ensure it is supported in the _deadtimes dict
|
|
27
|
+
"""Generic Driver supporting the Manta and Mako drivers.
|
|
28
|
+
Fetches deadtime prior to use in a Streaming scan.
|
|
29
|
+
Requires driver firmware up to date:
|
|
30
|
+
- Model_RBV must be of the form "^(Mako|Manta) (model)$"
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
34
|
+
self.trigger_mode = epics_signal_rw_rbv(
|
|
35
|
+
AravisTriggerMode, prefix + "TriggerMode"
|
|
36
|
+
)
|
|
37
|
+
self.trigger_source = epics_signal_rw_rbv(str, prefix + "TriggerSource")
|
|
38
|
+
super().__init__(prefix, name=name)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
|
|
4
|
+
|
|
5
|
+
from .ad_base import ADBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class KinetixTriggerMode(str, Enum):
|
|
9
|
+
internal = "Internal"
|
|
10
|
+
edge = "Rising Edge"
|
|
11
|
+
gate = "Exp. Gate"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class KinetixReadoutMode(str, Enum):
|
|
15
|
+
sensitivity = 1
|
|
16
|
+
speed = 2
|
|
17
|
+
dynamic_range = 3
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class KinetixDriver(ADBase):
|
|
21
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
22
|
+
# self.pixel_format = epics_signal_rw_rbv(PixelFormat, prefix + "PixelFormat")
|
|
23
|
+
self.trigger_mode = epics_signal_rw_rbv(
|
|
24
|
+
KinetixTriggerMode, prefix + "TriggerMode"
|
|
25
|
+
)
|
|
26
|
+
self.mode = epics_signal_rw_rbv(KinetixReadoutMode, prefix + "ReadoutPortIdx")
|
|
27
|
+
super().__init__(prefix, name)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
|
|
4
|
+
|
|
5
|
+
from .ad_base import ADBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PilatusTriggerMode(str, Enum):
|
|
9
|
+
internal = "Internal"
|
|
10
|
+
ext_enable = "Ext. Enable"
|
|
11
|
+
ext_trigger = "Ext. Trigger"
|
|
12
|
+
mult_trigger = "Mult. Trigger"
|
|
13
|
+
alignment = "Alignment"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PilatusDriver(ADBase):
|
|
17
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
18
|
+
self.trigger_mode = epics_signal_rw_rbv(
|
|
19
|
+
PilatusTriggerMode, prefix + "TriggerMode"
|
|
20
|
+
)
|
|
21
|
+
super().__init__(prefix, name)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
|
|
4
|
+
|
|
5
|
+
from .ad_base import ADBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class VimbaPixelFormat(str, Enum):
|
|
9
|
+
internal = "Mono8"
|
|
10
|
+
ext_enable = "Mono12"
|
|
11
|
+
ext_trigger = "Ext. Trigger"
|
|
12
|
+
mult_trigger = "Mult. Trigger"
|
|
13
|
+
alignment = "Alignment"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class VimbaConvertFormat(str, Enum):
|
|
17
|
+
none = "None"
|
|
18
|
+
mono8 = "Mono8"
|
|
19
|
+
mono16 = "Mono16"
|
|
20
|
+
rgb8 = "RGB8"
|
|
21
|
+
rgb16 = "RGB16"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class VimbaTriggerSource(str, Enum):
|
|
25
|
+
freerun = "Freerun"
|
|
26
|
+
line1 = "Line1"
|
|
27
|
+
line2 = "Line2"
|
|
28
|
+
fixed_rate = "FixedRate"
|
|
29
|
+
software = "Software"
|
|
30
|
+
action0 = "Action0"
|
|
31
|
+
action1 = "Action1"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class VimbaOverlap(str, Enum):
|
|
35
|
+
off = "Off"
|
|
36
|
+
prev_frame = "PreviousFrame"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class VimbaOnOff(str, Enum):
|
|
40
|
+
on = "On"
|
|
41
|
+
off = "Off"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class VimbaExposeOutMode(str, Enum):
|
|
45
|
+
timed = "Timed" # Use ExposureTime PV
|
|
46
|
+
trigger_width = "TriggerWidth" # Expose for length of high signal
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class VimbaDriver(ADBase):
|
|
50
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
51
|
+
# self.pixel_format = epics_signal_rw_rbv(PixelFormat, prefix + "PixelFormat")
|
|
52
|
+
self.convert_format = epics_signal_rw_rbv(
|
|
53
|
+
VimbaConvertFormat, prefix + "ConvertPixelFormat"
|
|
54
|
+
) # Pixel format of data outputted to AD
|
|
55
|
+
self.trig_source = epics_signal_rw_rbv(
|
|
56
|
+
VimbaTriggerSource, prefix + "TriggerSource"
|
|
57
|
+
)
|
|
58
|
+
self.trigger_mode = epics_signal_rw_rbv(VimbaOnOff, prefix + "TriggerMode")
|
|
59
|
+
self.overlap = epics_signal_rw_rbv(VimbaOverlap, prefix + "TriggerOverlap")
|
|
60
|
+
self.expose_mode = epics_signal_rw_rbv(
|
|
61
|
+
VimbaExposeOutMode, prefix + "ExposureMode"
|
|
62
|
+
)
|
|
63
|
+
super().__init__(prefix, name)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from bluesky.protocols import HasHints, Hints
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import DirectoryProvider, StandardDetector
|
|
4
|
+
from ophyd_async.epics.areadetector.controllers.kinetix_controller import (
|
|
5
|
+
KinetixController,
|
|
6
|
+
)
|
|
7
|
+
from ophyd_async.epics.areadetector.drivers import ADBaseShapeProvider
|
|
8
|
+
from ophyd_async.epics.areadetector.drivers.kinetix_driver import KinetixDriver
|
|
9
|
+
from ophyd_async.epics.areadetector.writers import HDFWriter, NDFileHDF
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class KinetixDetector(StandardDetector, HasHints):
|
|
13
|
+
"""
|
|
14
|
+
Ophyd-async implementation of an ADKinetix Detector.
|
|
15
|
+
https://github.com/NSLS-II/ADKinetix
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
_controller: KinetixController
|
|
19
|
+
_writer: HDFWriter
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
prefix: str,
|
|
24
|
+
directory_provider: DirectoryProvider,
|
|
25
|
+
drv_suffix="cam1:",
|
|
26
|
+
hdf_suffix="HDF1:",
|
|
27
|
+
name="",
|
|
28
|
+
):
|
|
29
|
+
self.drv = KinetixDriver(prefix + drv_suffix)
|
|
30
|
+
self.hdf = NDFileHDF(prefix + hdf_suffix)
|
|
31
|
+
|
|
32
|
+
super().__init__(
|
|
33
|
+
KinetixController(self.drv),
|
|
34
|
+
HDFWriter(
|
|
35
|
+
self.hdf,
|
|
36
|
+
directory_provider,
|
|
37
|
+
lambda: self.name,
|
|
38
|
+
ADBaseShapeProvider(self.drv),
|
|
39
|
+
),
|
|
40
|
+
config_sigs=(self.drv.acquire_time,),
|
|
41
|
+
name=name,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def hints(self) -> Hints:
|
|
46
|
+
return self._writer.hints
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from bluesky.protocols import Hints
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import DirectoryProvider
|
|
4
|
+
from ophyd_async.core.detector import StandardDetector
|
|
5
|
+
from ophyd_async.epics.areadetector.controllers.pilatus_controller import (
|
|
6
|
+
PilatusController,
|
|
7
|
+
)
|
|
8
|
+
from ophyd_async.epics.areadetector.drivers.ad_base import ADBaseShapeProvider
|
|
9
|
+
from ophyd_async.epics.areadetector.drivers.pilatus_driver import PilatusDriver
|
|
10
|
+
from ophyd_async.epics.areadetector.writers.hdf_writer import HDFWriter
|
|
11
|
+
from ophyd_async.epics.areadetector.writers.nd_file_hdf import NDFileHDF
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PilatusDetector(StandardDetector):
|
|
15
|
+
"""A Pilatus StandardDetector writing HDF files"""
|
|
16
|
+
|
|
17
|
+
_controller: PilatusController
|
|
18
|
+
_writer: HDFWriter
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
prefix: str,
|
|
23
|
+
directory_provider: DirectoryProvider,
|
|
24
|
+
drv_suffix="cam1:",
|
|
25
|
+
hdf_suffix="HDF1:",
|
|
26
|
+
name="",
|
|
27
|
+
):
|
|
28
|
+
self.drv = PilatusDriver(prefix + drv_suffix)
|
|
29
|
+
self.hdf = NDFileHDF(prefix + hdf_suffix)
|
|
30
|
+
|
|
31
|
+
super().__init__(
|
|
32
|
+
PilatusController(self.drv),
|
|
33
|
+
HDFWriter(
|
|
34
|
+
self.hdf,
|
|
35
|
+
directory_provider,
|
|
36
|
+
lambda: self.name,
|
|
37
|
+
ADBaseShapeProvider(self.drv),
|
|
38
|
+
),
|
|
39
|
+
config_sigs=(self.drv.acquire_time,),
|
|
40
|
+
name=name,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def hints(self) -> Hints:
|
|
45
|
+
return self._writer.hints
|
|
@@ -3,29 +3,37 @@ from typing import Sequence
|
|
|
3
3
|
|
|
4
4
|
from bluesky.protocols import Triggerable
|
|
5
5
|
|
|
6
|
-
from ophyd_async.core import
|
|
6
|
+
from ophyd_async.core import (
|
|
7
|
+
AsyncStatus,
|
|
8
|
+
ConfigSignal,
|
|
9
|
+
HintedSignal,
|
|
10
|
+
SignalR,
|
|
11
|
+
StandardReadable,
|
|
12
|
+
)
|
|
7
13
|
|
|
8
|
-
from .
|
|
9
|
-
from .nd_plugin import NDPlugin
|
|
14
|
+
from .drivers.ad_base import ADBase
|
|
10
15
|
from .utils import ImageMode
|
|
16
|
+
from .writers.nd_plugin import NDPluginBase
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
class SingleTriggerDet(StandardReadable, Triggerable):
|
|
14
20
|
def __init__(
|
|
15
21
|
self,
|
|
16
|
-
drv:
|
|
22
|
+
drv: ADBase,
|
|
17
23
|
read_uncached: Sequence[SignalR] = (),
|
|
18
24
|
name="",
|
|
19
|
-
**plugins:
|
|
25
|
+
**plugins: NDPluginBase,
|
|
20
26
|
) -> None:
|
|
21
27
|
self.drv = drv
|
|
22
28
|
self.__dict__.update(plugins)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
config=[self.drv.acquire_time],
|
|
29
|
+
|
|
30
|
+
self.add_readables(
|
|
31
|
+
[self.drv.array_counter, *read_uncached],
|
|
32
|
+
wrapper=HintedSignal.uncached,
|
|
28
33
|
)
|
|
34
|
+
|
|
35
|
+
self.add_readables([self.drv.acquire_time], wrapper=ConfigSignal)
|
|
36
|
+
|
|
29
37
|
super().__init__(name=name)
|
|
30
38
|
|
|
31
39
|
@AsyncStatus.wrap
|
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from xml.etree import cElementTree as ET
|
|
3
4
|
|
|
4
|
-
from ophyd_async.core import
|
|
5
|
-
|
|
6
|
-
from ..signal.signal import epics_signal_r, epics_signal_rw
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def ad_rw(datatype: Type[T], prefix: str) -> SignalRW[T]:
|
|
10
|
-
return epics_signal_rw(datatype, prefix + "_RBV", prefix)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def ad_r(datatype: Type[T], prefix: str) -> SignalR[T]:
|
|
14
|
-
return epics_signal_r(datatype, prefix + "_RBV")
|
|
5
|
+
from ophyd_async.core import DEFAULT_TIMEOUT, SignalRW, T, wait_for_value
|
|
15
6
|
|
|
16
7
|
|
|
17
8
|
class FileWriteMode(str, Enum):
|
|
@@ -20,7 +11,94 @@ class FileWriteMode(str, Enum):
|
|
|
20
11
|
stream = "Stream"
|
|
21
12
|
|
|
22
13
|
|
|
23
|
-
class ImageMode(Enum):
|
|
14
|
+
class ImageMode(str, Enum):
|
|
24
15
|
single = "Single"
|
|
25
16
|
multiple = "Multiple"
|
|
26
17
|
continuous = "Continuous"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NDAttributeDataType(str, Enum):
|
|
21
|
+
INT = "INT"
|
|
22
|
+
DOUBLE = "DOUBLE"
|
|
23
|
+
STRING = "STRING"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class NDAttributesXML:
|
|
27
|
+
"""Helper to make NDAttributesFile XML for areaDetector"""
|
|
28
|
+
|
|
29
|
+
_dbr_types = {
|
|
30
|
+
None: "DBR_NATIVE",
|
|
31
|
+
NDAttributeDataType.INT: "DBR_LONG",
|
|
32
|
+
NDAttributeDataType.DOUBLE: "DBR_DOUBLE",
|
|
33
|
+
NDAttributeDataType.STRING: "DBR_STRING",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def __init__(self):
|
|
37
|
+
self._root = ET.Element("Attributes")
|
|
38
|
+
|
|
39
|
+
def add_epics_pv(
|
|
40
|
+
self,
|
|
41
|
+
name: str,
|
|
42
|
+
pv: str,
|
|
43
|
+
datatype: Optional[NDAttributeDataType] = None,
|
|
44
|
+
description: str = "",
|
|
45
|
+
):
|
|
46
|
+
"""Add a PV to the attribute list
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
name: The attribute name
|
|
50
|
+
pv: The pv to get from
|
|
51
|
+
datatype: An override datatype, otherwise will use native EPICS type
|
|
52
|
+
description: A description that appears in the HDF file as an attribute
|
|
53
|
+
"""
|
|
54
|
+
ET.SubElement(
|
|
55
|
+
self._root,
|
|
56
|
+
"Attribute",
|
|
57
|
+
name=name,
|
|
58
|
+
type="EPICS_PV",
|
|
59
|
+
source=pv,
|
|
60
|
+
datatype=self._dbr_types[datatype],
|
|
61
|
+
description=description,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def add_param(
|
|
65
|
+
self,
|
|
66
|
+
name: str,
|
|
67
|
+
param: str,
|
|
68
|
+
datatype: NDAttributeDataType,
|
|
69
|
+
addr: int = 0,
|
|
70
|
+
description: str = "",
|
|
71
|
+
):
|
|
72
|
+
"""Add a driver or plugin parameter to the attribute list
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
name: The attribute name
|
|
76
|
+
param: The parameter string as seen in the INP link of the record
|
|
77
|
+
datatype: The datatype of the parameter
|
|
78
|
+
description: A description that appears in the HDF file as an attribute
|
|
79
|
+
"""
|
|
80
|
+
ET.SubElement(
|
|
81
|
+
self._root,
|
|
82
|
+
"Attribute",
|
|
83
|
+
name=name,
|
|
84
|
+
type="PARAM",
|
|
85
|
+
source=param,
|
|
86
|
+
addr=str(addr),
|
|
87
|
+
datatype=datatype.value,
|
|
88
|
+
description=description,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def __str__(self) -> str:
|
|
92
|
+
"""Output the XML pretty printed"""
|
|
93
|
+
ET.indent(self._root, space=" ", level=0)
|
|
94
|
+
return ET.tostring(self._root, xml_declaration=True, encoding="utf-8").decode()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
async def stop_busy_record(
|
|
98
|
+
signal: SignalRW[T],
|
|
99
|
+
value: T,
|
|
100
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
101
|
+
status_timeout: Optional[float] = None,
|
|
102
|
+
) -> None:
|
|
103
|
+
await signal.set(value, wait=False, timeout=status_timeout)
|
|
104
|
+
await wait_for_value(signal, value, timeout=timeout)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from bluesky.protocols import HasHints, Hints
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import DirectoryProvider, StandardDetector
|
|
4
|
+
from ophyd_async.epics.areadetector.controllers.vimba_controller import VimbaController
|
|
5
|
+
from ophyd_async.epics.areadetector.drivers import ADBaseShapeProvider
|
|
6
|
+
from ophyd_async.epics.areadetector.drivers.vimba_driver import VimbaDriver
|
|
7
|
+
from ophyd_async.epics.areadetector.writers import HDFWriter, NDFileHDF
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class VimbaDetector(StandardDetector, HasHints):
|
|
11
|
+
"""
|
|
12
|
+
Ophyd-async implementation of an ADVimba Detector.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
_controller: VimbaController
|
|
16
|
+
_writer: HDFWriter
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
prefix: str,
|
|
21
|
+
directory_provider: DirectoryProvider,
|
|
22
|
+
drv_suffix="cam1:",
|
|
23
|
+
hdf_suffix="HDF1:",
|
|
24
|
+
name="",
|
|
25
|
+
):
|
|
26
|
+
self.drv = VimbaDriver(prefix + drv_suffix)
|
|
27
|
+
self.hdf = NDFileHDF(prefix + hdf_suffix)
|
|
28
|
+
|
|
29
|
+
super().__init__(
|
|
30
|
+
VimbaController(self.drv),
|
|
31
|
+
HDFWriter(
|
|
32
|
+
self.hdf,
|
|
33
|
+
directory_provider,
|
|
34
|
+
lambda: self.name,
|
|
35
|
+
ADBaseShapeProvider(self.drv),
|
|
36
|
+
),
|
|
37
|
+
config_sigs=(self.drv.acquire_time,),
|
|
38
|
+
name=name,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def hints(self) -> Hints:
|
|
43
|
+
return self._writer.hints
|