ophyd-async 0.2.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 +52 -19
- ophyd_async/core/_providers.py +38 -5
- ophyd_async/core/async_status.py +86 -40
- ophyd_async/core/detector.py +214 -72
- ophyd_async/core/device.py +91 -50
- ophyd_async/core/device_save_loader.py +96 -23
- ophyd_async/core/flyer.py +32 -246
- ophyd_async/core/mock_signal_backend.py +82 -0
- ophyd_async/core/mock_signal_utils.py +145 -0
- ophyd_async/core/signal.py +225 -58
- ophyd_async/core/signal_backend.py +8 -5
- ophyd_async/core/{sim_signal_backend.py → soft_signal_backend.py} +51 -49
- ophyd_async/core/standard_readable.py +212 -23
- ophyd_async/core/utils.py +123 -30
- ophyd_async/epics/_backend/_aioca.py +42 -44
- ophyd_async/epics/_backend/_p4p.py +96 -52
- ophyd_async/epics/_backend/common.py +25 -0
- ophyd_async/epics/areadetector/__init__.py +8 -4
- ophyd_async/epics/areadetector/aravis.py +63 -0
- ophyd_async/epics/areadetector/controllers/__init__.py +2 -1
- ophyd_async/epics/areadetector/controllers/ad_sim_controller.py +1 -1
- 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 +37 -25
- ophyd_async/epics/areadetector/controllers/vimba_controller.py +66 -0
- ophyd_async/epics/areadetector/drivers/__init__.py +6 -0
- ophyd_async/epics/areadetector/drivers/ad_base.py +8 -12
- 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 +8 -5
- 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 +14 -6
- ophyd_async/epics/areadetector/utils.py +2 -12
- ophyd_async/epics/areadetector/vimba.py +43 -0
- ophyd_async/epics/areadetector/writers/_hdffile.py +21 -7
- ophyd_async/epics/areadetector/writers/hdf_writer.py +32 -17
- ophyd_async/epics/areadetector/writers/nd_file_hdf.py +19 -18
- ophyd_async/epics/areadetector/writers/nd_plugin.py +15 -7
- ophyd_async/epics/demo/__init__.py +75 -49
- ophyd_async/epics/motion/motor.py +67 -53
- 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 +26 -9
- ophyd_async/log.py +130 -0
- ophyd_async/panda/__init__.py +21 -5
- 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/_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.2.0.dist-info → ophyd_async-0.3.0.dist-info}/METADATA +31 -70
- ophyd_async-0.3.0.dist-info/RECORD +86 -0
- {ophyd_async-0.2.0.dist-info → ophyd_async-0.3.0.dist-info}/WHEEL +1 -1
- ophyd_async/epics/signal/pvi_get.py +0 -22
- ophyd_async/panda/panda.py +0 -294
- ophyd_async-0.2.0.dist-info/RECORD +0 -53
- /ophyd_async/panda/{table.py → _table.py} +0 -0
- {ophyd_async-0.2.0.dist-info → ophyd_async-0.3.0.dist-info}/LICENSE +0 -0
- {ophyd_async-0.2.0.dist-info → ophyd_async-0.3.0.dist-info}/entry_points.txt +0 -0
- {ophyd_async-0.2.0.dist-info → ophyd_async-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
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.kinetix_driver import KinetixDriver, KinetixTriggerMode
|
|
10
|
+
from ..utils import ImageMode, stop_busy_record
|
|
11
|
+
|
|
12
|
+
KINETIX_TRIGGER_MODE_MAP = {
|
|
13
|
+
DetectorTrigger.internal: KinetixTriggerMode.internal,
|
|
14
|
+
DetectorTrigger.constant_gate: KinetixTriggerMode.gate,
|
|
15
|
+
DetectorTrigger.variable_gate: KinetixTriggerMode.gate,
|
|
16
|
+
DetectorTrigger.edge_trigger: KinetixTriggerMode.edge,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class KinetixController(DetectorControl):
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
driver: KinetixDriver,
|
|
24
|
+
) -> None:
|
|
25
|
+
self._drv = driver
|
|
26
|
+
|
|
27
|
+
def get_deadtime(self, exposure: float) -> float:
|
|
28
|
+
return 0.001
|
|
29
|
+
|
|
30
|
+
async def arm(
|
|
31
|
+
self,
|
|
32
|
+
num: int,
|
|
33
|
+
trigger: DetectorTrigger = DetectorTrigger.internal,
|
|
34
|
+
exposure: Optional[float] = None,
|
|
35
|
+
) -> AsyncStatus:
|
|
36
|
+
await asyncio.gather(
|
|
37
|
+
self._drv.trigger_mode.set(KINETIX_TRIGGER_MODE_MAP[trigger]),
|
|
38
|
+
self._drv.num_images.set(num),
|
|
39
|
+
self._drv.image_mode.set(ImageMode.multiple),
|
|
40
|
+
)
|
|
41
|
+
if exposure is not None and trigger not in [
|
|
42
|
+
DetectorTrigger.variable_gate,
|
|
43
|
+
DetectorTrigger.constant_gate,
|
|
44
|
+
]:
|
|
45
|
+
await self._drv.acquire_time.set(exposure)
|
|
46
|
+
return await start_acquiring_driver_and_ensure_status(self._drv)
|
|
47
|
+
|
|
48
|
+
async def disarm(self):
|
|
49
|
+
await stop_busy_record(self._drv.acquire, False, timeout=1)
|
|
@@ -1,49 +1,61 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional
|
|
3
3
|
|
|
4
|
-
from ophyd_async.core import AsyncStatus
|
|
4
|
+
from ophyd_async.core.async_status import AsyncStatus
|
|
5
|
+
from ophyd_async.core.detector import DetectorControl, DetectorTrigger
|
|
5
6
|
from ophyd_async.epics.areadetector.drivers.ad_base import (
|
|
6
|
-
DEFAULT_GOOD_STATES,
|
|
7
|
-
DetectorState,
|
|
8
7
|
start_acquiring_driver_and_ensure_status,
|
|
9
8
|
)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
DetectorTrigger.internal: TriggerMode.internal,
|
|
16
|
-
DetectorTrigger.constant_gate: TriggerMode.ext_enable,
|
|
17
|
-
DetectorTrigger.variable_gate: TriggerMode.ext_enable,
|
|
18
|
-
}
|
|
9
|
+
from ophyd_async.epics.areadetector.drivers.pilatus_driver import (
|
|
10
|
+
PilatusDriver,
|
|
11
|
+
PilatusTriggerMode,
|
|
12
|
+
)
|
|
13
|
+
from ophyd_async.epics.areadetector.utils import ImageMode, stop_busy_record
|
|
19
14
|
|
|
20
15
|
|
|
21
16
|
class PilatusController(DetectorControl):
|
|
17
|
+
_supported_trigger_types = {
|
|
18
|
+
DetectorTrigger.internal: PilatusTriggerMode.internal,
|
|
19
|
+
DetectorTrigger.constant_gate: PilatusTriggerMode.ext_enable,
|
|
20
|
+
DetectorTrigger.variable_gate: PilatusTriggerMode.ext_enable,
|
|
21
|
+
}
|
|
22
|
+
|
|
22
23
|
def __init__(
|
|
23
24
|
self,
|
|
24
25
|
driver: PilatusDriver,
|
|
25
|
-
good_states: Set[DetectorState] = set(DEFAULT_GOOD_STATES),
|
|
26
26
|
) -> None:
|
|
27
|
-
self.
|
|
28
|
-
self.good_states = good_states
|
|
27
|
+
self._drv = driver
|
|
29
28
|
|
|
30
29
|
def get_deadtime(self, exposure: float) -> float:
|
|
31
|
-
|
|
30
|
+
# Cite: https://media.dectris.com/User_Manual-PILATUS2-V1_4.pdf
|
|
31
|
+
"""The required minimum time difference between ExpPeriod and ExpTime
|
|
32
|
+
(readout time) is 2.28 ms"""
|
|
33
|
+
return 2.28e-3
|
|
32
34
|
|
|
33
35
|
async def arm(
|
|
34
36
|
self,
|
|
37
|
+
num: int,
|
|
35
38
|
trigger: DetectorTrigger = DetectorTrigger.internal,
|
|
36
|
-
num: int = 0,
|
|
37
39
|
exposure: Optional[float] = None,
|
|
38
40
|
) -> AsyncStatus:
|
|
41
|
+
if exposure is not None:
|
|
42
|
+
await self._drv.acquire_time.set(exposure)
|
|
39
43
|
await asyncio.gather(
|
|
40
|
-
self.
|
|
41
|
-
self.
|
|
42
|
-
self.
|
|
43
|
-
)
|
|
44
|
-
return await start_acquiring_driver_and_ensure_status(
|
|
45
|
-
self.driver, good_states=self.good_states
|
|
44
|
+
self._drv.trigger_mode.set(self._get_trigger_mode(trigger)),
|
|
45
|
+
self._drv.num_images.set(999_999 if num == 0 else num),
|
|
46
|
+
self._drv.image_mode.set(ImageMode.multiple),
|
|
46
47
|
)
|
|
48
|
+
return await start_acquiring_driver_and_ensure_status(self._drv)
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def _get_trigger_mode(cls, trigger: DetectorTrigger) -> PilatusTriggerMode:
|
|
52
|
+
if trigger not in cls._supported_trigger_types.keys():
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"{cls.__name__} only supports the following trigger "
|
|
55
|
+
f"types: {cls._supported_trigger_types.keys()} but was asked to "
|
|
56
|
+
f"use {trigger}"
|
|
57
|
+
)
|
|
58
|
+
return cls._supported_trigger_types[trigger]
|
|
47
59
|
|
|
48
60
|
async def disarm(self):
|
|
49
|
-
await stop_busy_record(self.
|
|
61
|
+
await stop_busy_record(self._drv.acquire, False, timeout=1)
|
|
@@ -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)
|
|
@@ -4,12 +4,18 @@ from .ad_base import (
|
|
|
4
4
|
DetectorState,
|
|
5
5
|
start_acquiring_driver_and_ensure_status,
|
|
6
6
|
)
|
|
7
|
+
from .aravis_driver import AravisDriver
|
|
8
|
+
from .kinetix_driver import KinetixDriver
|
|
7
9
|
from .pilatus_driver import PilatusDriver
|
|
10
|
+
from .vimba_driver import VimbaDriver
|
|
8
11
|
|
|
9
12
|
__all__ = [
|
|
10
13
|
"ADBase",
|
|
11
14
|
"ADBaseShapeProvider",
|
|
12
15
|
"PilatusDriver",
|
|
16
|
+
"AravisDriver",
|
|
17
|
+
"KinetixDriver",
|
|
18
|
+
"VimbaDriver",
|
|
13
19
|
"start_acquiring_driver_and_ensure_status",
|
|
14
20
|
"DetectorState",
|
|
15
21
|
]
|
|
@@ -9,8 +9,8 @@ from ophyd_async.core import (
|
|
|
9
9
|
set_and_wait_for_value,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
from ...signal.signal import
|
|
13
|
-
from ..utils import ImageMode
|
|
12
|
+
from ...signal.signal import epics_signal_r, epics_signal_rw_rbv
|
|
13
|
+
from ..utils import ImageMode
|
|
14
14
|
from ..writers.nd_plugin import NDArrayBase
|
|
15
15
|
|
|
16
16
|
|
|
@@ -43,16 +43,12 @@ DEFAULT_GOOD_STATES: FrozenSet[DetectorState] = frozenset(
|
|
|
43
43
|
class ADBase(NDArrayBase):
|
|
44
44
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
45
45
|
# Define some signals
|
|
46
|
-
self.
|
|
47
|
-
self.
|
|
48
|
-
self.
|
|
49
|
-
self.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
self.array_size_y = ad_r(int, prefix + "ArraySizeY")
|
|
53
|
-
self.detector_state = ad_r(DetectorState, prefix + "DetectorState")
|
|
54
|
-
# There is no _RBV for this one
|
|
55
|
-
self.wait_for_plugins = epics_signal_rw(bool, prefix + "WaitForPlugins")
|
|
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
|
+
)
|
|
56
52
|
super().__init__(prefix, name=name)
|
|
57
53
|
|
|
58
54
|
|
|
@@ -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)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
|
|
4
|
+
|
|
4
5
|
from .ad_base import ADBase
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
class
|
|
8
|
+
class PilatusTriggerMode(str, Enum):
|
|
8
9
|
internal = "Internal"
|
|
9
10
|
ext_enable = "Ext. Enable"
|
|
10
11
|
ext_trigger = "Ext. Trigger"
|
|
@@ -13,6 +14,8 @@ class TriggerMode(str, Enum):
|
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class PilatusDriver(ADBase):
|
|
16
|
-
def __init__(self, prefix: str) -> None:
|
|
17
|
-
self.trigger_mode =
|
|
18
|
-
|
|
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,7 +3,13 @@ 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
14
|
from .drivers.ad_base import ADBase
|
|
9
15
|
from .utils import ImageMode
|
|
@@ -20,12 +26,14 @@ class SingleTriggerDet(StandardReadable, Triggerable):
|
|
|
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,18 +1,8 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional
|
|
3
3
|
from xml.etree import cElementTree as ET
|
|
4
4
|
|
|
5
|
-
from ophyd_async.core import DEFAULT_TIMEOUT,
|
|
6
|
-
|
|
7
|
-
from ..signal.signal import epics_signal_r, epics_signal_rw
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def ad_rw(datatype: Type[T], prefix: str) -> SignalRW[T]:
|
|
11
|
-
return epics_signal_rw(datatype, prefix + "_RBV", prefix)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def ad_r(datatype: Type[T], prefix: str) -> SignalR[T]:
|
|
15
|
-
return epics_signal_r(datatype, prefix + "_RBV")
|
|
5
|
+
from ophyd_async.core import DEFAULT_TIMEOUT, SignalRW, T, wait_for_value
|
|
16
6
|
|
|
17
7
|
|
|
18
8
|
class FileWriteMode(str, Enum):
|
|
@@ -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
|
|
@@ -1,19 +1,33 @@
|
|
|
1
|
+
from pathlib import Path
|
|
1
2
|
from typing import Iterator, List
|
|
2
3
|
|
|
3
4
|
from event_model import StreamDatum, StreamResource, compose_stream_resource
|
|
4
5
|
|
|
6
|
+
from ophyd_async.core import DirectoryInfo
|
|
7
|
+
|
|
5
8
|
from ._hdfdataset import _HDFDataset
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
class _HDFFile:
|
|
9
|
-
|
|
12
|
+
"""
|
|
13
|
+
:param directory_info: Contains information about how to construct a StreamResource
|
|
14
|
+
:param full_file_name: Absolute path to the file to be written
|
|
15
|
+
:param datasets: Datasets to write into the file
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
directory_info: DirectoryInfo,
|
|
21
|
+
full_file_name: Path,
|
|
22
|
+
datasets: List[_HDFDataset],
|
|
23
|
+
) -> None:
|
|
10
24
|
self._last_emitted = 0
|
|
11
25
|
self._bundles = [
|
|
12
26
|
compose_stream_resource(
|
|
13
27
|
spec="AD_HDF5_SWMR_SLICE",
|
|
14
|
-
root=
|
|
28
|
+
root=str(directory_info.root),
|
|
15
29
|
data_key=ds.name,
|
|
16
|
-
resource_path=full_file_name,
|
|
30
|
+
resource_path=str(full_file_name.relative_to(directory_info.root)),
|
|
17
31
|
resource_kwargs={
|
|
18
32
|
"path": ds.path,
|
|
19
33
|
"multiplier": ds.multiplier,
|
|
@@ -30,10 +44,10 @@ class _HDFFile:
|
|
|
30
44
|
def stream_data(self, indices_written: int) -> Iterator[StreamDatum]:
|
|
31
45
|
# Indices are relative to resource
|
|
32
46
|
if indices_written > self._last_emitted:
|
|
33
|
-
indices =
|
|
34
|
-
start
|
|
35
|
-
stop
|
|
36
|
-
|
|
47
|
+
indices = {
|
|
48
|
+
"start": self._last_emitted,
|
|
49
|
+
"stop": indices_written,
|
|
50
|
+
}
|
|
37
51
|
self._last_emitted = indices_written
|
|
38
52
|
for bundle in self._bundles:
|
|
39
53
|
yield bundle.compose_stream_datum(indices)
|