ophyd-async 0.9.0a1__py3-none-any.whl → 0.10.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ophyd_async/__init__.py +5 -8
- ophyd_async/_docs_parser.py +12 -0
- ophyd_async/_version.py +9 -4
- ophyd_async/core/__init__.py +102 -74
- ophyd_async/core/_derived_signal.py +271 -0
- ophyd_async/core/_derived_signal_backend.py +300 -0
- ophyd_async/core/_detector.py +158 -153
- ophyd_async/core/_device.py +143 -115
- ophyd_async/core/_device_filler.py +82 -9
- ophyd_async/core/_flyer.py +16 -7
- ophyd_async/core/_hdf_dataset.py +29 -22
- ophyd_async/core/_log.py +14 -23
- ophyd_async/core/_mock_signal_backend.py +11 -3
- ophyd_async/core/_protocol.py +65 -45
- ophyd_async/core/_providers.py +28 -9
- ophyd_async/core/_readable.py +74 -58
- ophyd_async/core/_settings.py +113 -0
- ophyd_async/core/_signal.py +304 -174
- ophyd_async/core/_signal_backend.py +60 -14
- ophyd_async/core/_soft_signal_backend.py +18 -12
- ophyd_async/core/_status.py +72 -24
- ophyd_async/core/_table.py +54 -17
- ophyd_async/core/_utils.py +101 -52
- ophyd_async/core/_yaml_settings.py +66 -0
- ophyd_async/epics/__init__.py +1 -0
- ophyd_async/epics/adandor/__init__.py +9 -0
- ophyd_async/epics/adandor/_andor.py +45 -0
- ophyd_async/epics/adandor/_andor_controller.py +51 -0
- ophyd_async/epics/adandor/_andor_io.py +34 -0
- ophyd_async/epics/adaravis/__init__.py +8 -1
- ophyd_async/epics/adaravis/_aravis.py +23 -41
- ophyd_async/epics/adaravis/_aravis_controller.py +23 -55
- ophyd_async/epics/adaravis/_aravis_io.py +13 -28
- ophyd_async/epics/adcore/__init__.py +36 -14
- ophyd_async/epics/adcore/_core_detector.py +81 -0
- ophyd_async/epics/adcore/_core_io.py +145 -95
- ophyd_async/epics/adcore/_core_logic.py +179 -88
- ophyd_async/epics/adcore/_core_writer.py +223 -0
- ophyd_async/epics/adcore/_hdf_writer.py +51 -92
- ophyd_async/epics/adcore/_jpeg_writer.py +26 -0
- ophyd_async/epics/adcore/_single_trigger.py +6 -5
- ophyd_async/epics/adcore/_tiff_writer.py +26 -0
- ophyd_async/epics/adcore/_utils.py +3 -2
- ophyd_async/epics/adkinetix/__init__.py +2 -1
- ophyd_async/epics/adkinetix/_kinetix.py +32 -27
- ophyd_async/epics/adkinetix/_kinetix_controller.py +11 -21
- ophyd_async/epics/adkinetix/_kinetix_io.py +12 -13
- ophyd_async/epics/adpilatus/__init__.py +7 -2
- ophyd_async/epics/adpilatus/_pilatus.py +28 -40
- ophyd_async/epics/adpilatus/_pilatus_controller.py +25 -22
- ophyd_async/epics/adpilatus/_pilatus_io.py +11 -9
- ophyd_async/epics/adsimdetector/__init__.py +8 -1
- ophyd_async/epics/adsimdetector/_sim.py +22 -16
- ophyd_async/epics/adsimdetector/_sim_controller.py +9 -43
- ophyd_async/epics/adsimdetector/_sim_io.py +10 -0
- ophyd_async/epics/advimba/__init__.py +10 -1
- ophyd_async/epics/advimba/_vimba.py +26 -25
- ophyd_async/epics/advimba/_vimba_controller.py +12 -24
- ophyd_async/epics/advimba/_vimba_io.py +23 -28
- ophyd_async/epics/core/_aioca.py +66 -30
- ophyd_async/epics/core/_epics_connector.py +4 -0
- ophyd_async/epics/core/_epics_device.py +2 -0
- ophyd_async/epics/core/_p4p.py +50 -18
- ophyd_async/epics/core/_pvi_connector.py +65 -8
- ophyd_async/epics/core/_signal.py +51 -51
- ophyd_async/epics/core/_util.py +5 -5
- ophyd_async/epics/demo/__init__.py +11 -49
- ophyd_async/epics/demo/__main__.py +31 -0
- ophyd_async/epics/demo/_ioc.py +32 -0
- ophyd_async/epics/demo/_motor.py +82 -0
- ophyd_async/epics/demo/_point_detector.py +42 -0
- ophyd_async/epics/demo/_point_detector_channel.py +22 -0
- ophyd_async/epics/demo/_stage.py +15 -0
- ophyd_async/epics/demo/{mover.db → motor.db} +2 -1
- ophyd_async/epics/demo/point_detector.db +59 -0
- ophyd_async/epics/demo/point_detector_channel.db +21 -0
- ophyd_async/epics/eiger/_eiger.py +1 -3
- ophyd_async/epics/eiger/_eiger_controller.py +11 -4
- ophyd_async/epics/eiger/_eiger_io.py +2 -0
- ophyd_async/epics/eiger/_odin_io.py +1 -2
- ophyd_async/epics/motor.py +83 -38
- ophyd_async/epics/signal.py +4 -1
- ophyd_async/epics/testing/__init__.py +14 -14
- ophyd_async/epics/testing/_example_ioc.py +68 -73
- ophyd_async/epics/testing/_utils.py +19 -44
- ophyd_async/epics/testing/test_records.db +16 -0
- ophyd_async/epics/testing/test_records_pva.db +17 -16
- ophyd_async/fastcs/__init__.py +1 -0
- ophyd_async/fastcs/core.py +6 -0
- ophyd_async/fastcs/odin/__init__.py +1 -0
- ophyd_async/fastcs/panda/__init__.py +8 -8
- ophyd_async/fastcs/panda/_block.py +29 -9
- ophyd_async/fastcs/panda/_control.py +12 -2
- ophyd_async/fastcs/panda/_hdf_panda.py +5 -1
- ophyd_async/fastcs/panda/_table.py +13 -7
- ophyd_async/fastcs/panda/_trigger.py +23 -9
- ophyd_async/fastcs/panda/_writer.py +27 -30
- ophyd_async/plan_stubs/__init__.py +16 -0
- ophyd_async/plan_stubs/_ensure_connected.py +12 -17
- ophyd_async/plan_stubs/_fly.py +3 -5
- ophyd_async/plan_stubs/_nd_attributes.py +9 -5
- ophyd_async/plan_stubs/_panda.py +14 -0
- ophyd_async/plan_stubs/_settings.py +152 -0
- ophyd_async/plan_stubs/_utils.py +3 -0
- ophyd_async/plan_stubs/_wait_for_awaitable.py +13 -0
- ophyd_async/sim/__init__.py +29 -0
- ophyd_async/sim/__main__.py +43 -0
- ophyd_async/sim/_blob_detector.py +33 -0
- ophyd_async/sim/_blob_detector_controller.py +48 -0
- ophyd_async/sim/_blob_detector_writer.py +105 -0
- ophyd_async/sim/_mirror_horizontal.py +46 -0
- ophyd_async/sim/_mirror_vertical.py +74 -0
- ophyd_async/sim/_motor.py +233 -0
- ophyd_async/sim/_pattern_generator.py +124 -0
- ophyd_async/sim/_point_detector.py +86 -0
- ophyd_async/sim/_stage.py +19 -0
- ophyd_async/tango/__init__.py +1 -0
- ophyd_async/tango/core/__init__.py +6 -1
- ophyd_async/tango/core/_base_device.py +41 -33
- ophyd_async/tango/core/_converters.py +81 -0
- ophyd_async/tango/core/_signal.py +21 -33
- ophyd_async/tango/core/_tango_readable.py +2 -19
- ophyd_async/tango/core/_tango_transport.py +148 -74
- ophyd_async/tango/core/_utils.py +47 -0
- ophyd_async/tango/demo/_counter.py +2 -0
- ophyd_async/tango/demo/_detector.py +2 -0
- ophyd_async/tango/demo/_mover.py +10 -6
- ophyd_async/tango/demo/_tango/_servers.py +4 -0
- ophyd_async/tango/testing/__init__.py +6 -0
- ophyd_async/tango/testing/_one_of_everything.py +200 -0
- ophyd_async/testing/__init__.py +48 -7
- ophyd_async/testing/__pytest_assert_rewrite.py +4 -0
- ophyd_async/testing/_assert.py +200 -96
- ophyd_async/testing/_mock_signal_utils.py +59 -73
- ophyd_async/testing/_one_of_everything.py +146 -0
- ophyd_async/testing/_single_derived.py +87 -0
- ophyd_async/testing/_utils.py +3 -0
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/METADATA +25 -26
- ophyd_async-0.10.0a1.dist-info/RECORD +149 -0
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/WHEEL +1 -1
- ophyd_async/core/_device_save_loader.py +0 -274
- ophyd_async/epics/demo/_mover.py +0 -95
- ophyd_async/epics/demo/_sensor.py +0 -37
- ophyd_async/epics/demo/sensor.db +0 -19
- ophyd_async/fastcs/panda/_utils.py +0 -16
- ophyd_async/sim/demo/__init__.py +0 -19
- ophyd_async/sim/demo/_pattern_detector/__init__.py +0 -13
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector.py +0 -42
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector_controller.py +0 -62
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector_writer.py +0 -41
- ophyd_async/sim/demo/_pattern_detector/_pattern_generator.py +0 -207
- ophyd_async/sim/demo/_sim_motor.py +0 -107
- ophyd_async/sim/testing/__init__.py +0 -0
- ophyd_async-0.9.0a1.dist-info/RECORD +0 -119
- ophyd_async-0.9.0a1.dist-info/entry_points.txt +0 -2
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info/licenses}/LICENSE +0 -0
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from enum import Enum
|
|
2
3
|
|
|
3
4
|
from ophyd_async.core import (
|
|
4
5
|
DEFAULT_TIMEOUT,
|
|
5
|
-
AsyncStatus,
|
|
6
|
-
DetectorController,
|
|
7
6
|
DetectorTrigger,
|
|
8
7
|
TriggerInfo,
|
|
9
8
|
wait_for_value,
|
|
@@ -13,7 +12,19 @@ from ophyd_async.epics import adcore
|
|
|
13
12
|
from ._pilatus_io import PilatusDriverIO, PilatusTriggerMode
|
|
14
13
|
|
|
15
14
|
|
|
16
|
-
class
|
|
15
|
+
class PilatusReadoutTime(float, Enum):
|
|
16
|
+
"""Pilatus readout time per model in ms."""
|
|
17
|
+
|
|
18
|
+
# Cite: https://media.dectris.com/User_Manual-PILATUS2-V1_4.pdf
|
|
19
|
+
PILATUS2 = 2.28e-3
|
|
20
|
+
|
|
21
|
+
# Cite: https://media.dectris.com/user-manual-pilatus3-2020.pdf
|
|
22
|
+
PILATUS3 = 0.95e-3
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
|
|
26
|
+
"""`DetectorController` for a `PilatusDriverIO`."""
|
|
27
|
+
|
|
17
28
|
_supported_trigger_types = {
|
|
18
29
|
DetectorTrigger.INTERNAL: PilatusTriggerMode.INTERNAL,
|
|
19
30
|
DetectorTrigger.CONSTANT_GATE: PilatusTriggerMode.EXT_ENABLE,
|
|
@@ -23,48 +34,43 @@ class PilatusController(DetectorController):
|
|
|
23
34
|
def __init__(
|
|
24
35
|
self,
|
|
25
36
|
driver: PilatusDriverIO,
|
|
26
|
-
|
|
37
|
+
good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
|
|
38
|
+
readout_time: float = PilatusReadoutTime.PILATUS3,
|
|
27
39
|
) -> None:
|
|
28
|
-
|
|
40
|
+
super().__init__(driver, good_states=good_states)
|
|
29
41
|
self._readout_time = readout_time
|
|
30
|
-
self._arm_status: AsyncStatus | None = None
|
|
31
42
|
|
|
32
43
|
def get_deadtime(self, exposure: float | None) -> float:
|
|
33
44
|
return self._readout_time
|
|
34
45
|
|
|
35
46
|
async def prepare(self, trigger_info: TriggerInfo):
|
|
36
47
|
if trigger_info.livetime is not None:
|
|
37
|
-
await
|
|
38
|
-
|
|
48
|
+
await self.set_exposure_time_and_acquire_period_if_supplied(
|
|
49
|
+
trigger_info.livetime
|
|
39
50
|
)
|
|
40
51
|
await asyncio.gather(
|
|
41
|
-
self.
|
|
42
|
-
self.
|
|
52
|
+
self.driver.trigger_mode.set(self._get_trigger_mode(trigger_info.trigger)),
|
|
53
|
+
self.driver.num_images.set(
|
|
43
54
|
999_999
|
|
44
55
|
if trigger_info.total_number_of_triggers == 0
|
|
45
56
|
else trigger_info.total_number_of_triggers
|
|
46
57
|
),
|
|
47
|
-
self.
|
|
58
|
+
self.driver.image_mode.set(adcore.ADImageMode.MULTIPLE),
|
|
48
59
|
)
|
|
49
60
|
|
|
50
61
|
async def arm(self):
|
|
51
62
|
# Standard arm the detector and wait for the acquire PV to be True
|
|
52
|
-
self._arm_status = await
|
|
53
|
-
|
|
54
|
-
)
|
|
63
|
+
self._arm_status = await self.start_acquiring_driver_and_ensure_status()
|
|
64
|
+
|
|
55
65
|
# The pilatus has an additional PV that goes True when the camserver
|
|
56
66
|
# is actually ready. Should wait for that too or we risk dropping
|
|
57
67
|
# a frame
|
|
58
68
|
await wait_for_value(
|
|
59
|
-
self.
|
|
69
|
+
self.driver.armed,
|
|
60
70
|
True,
|
|
61
71
|
timeout=DEFAULT_TIMEOUT,
|
|
62
72
|
)
|
|
63
73
|
|
|
64
|
-
async def wait_for_idle(self):
|
|
65
|
-
if self._arm_status:
|
|
66
|
-
await self._arm_status
|
|
67
|
-
|
|
68
74
|
@classmethod
|
|
69
75
|
def _get_trigger_mode(cls, trigger: DetectorTrigger) -> PilatusTriggerMode:
|
|
70
76
|
if trigger not in cls._supported_trigger_types.keys():
|
|
@@ -74,6 +80,3 @@ class PilatusController(DetectorController):
|
|
|
74
80
|
f"use {trigger}"
|
|
75
81
|
)
|
|
76
82
|
return cls._supported_trigger_types[trigger]
|
|
77
|
-
|
|
78
|
-
async def disarm(self):
|
|
79
|
-
await adcore.stop_busy_record(self._drv.acquire, False, timeout=1)
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Annotated as A
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import SignalR, SignalRW, StrictEnum
|
|
2
4
|
from ophyd_async.epics import adcore
|
|
3
|
-
from ophyd_async.epics.core import
|
|
5
|
+
from ophyd_async.epics.core import PvSuffix
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
class PilatusTriggerMode(StrictEnum):
|
|
9
|
+
"""Trigger modes for ADPilatus detector."""
|
|
10
|
+
|
|
7
11
|
INTERNAL = "Internal"
|
|
8
12
|
EXT_ENABLE = "Ext. Enable"
|
|
9
13
|
EXT_TRIGGER = "Ext. Trigger"
|
|
@@ -12,11 +16,9 @@ class PilatusTriggerMode(StrictEnum):
|
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
class PilatusDriverIO(adcore.ADBaseIO):
|
|
15
|
-
"""
|
|
19
|
+
"""Driver for the Pilatus pixel array detectors."""
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
self.armed = epics_signal_r(bool, prefix + "Armed")
|
|
22
|
-
super().__init__(prefix, name)
|
|
21
|
+
"""This mirrors the interface provided by ADPilatus/db/pilatus.template."""
|
|
22
|
+
"""See HTML docs at https://areadetector.github.io/areaDetector/ADPilatus/pilatusDoc.html"""
|
|
23
|
+
trigger_mode: A[SignalRW[PilatusTriggerMode], PvSuffix.rbv("TriggerMode")]
|
|
24
|
+
armed: A[SignalR[bool], PvSuffix.rbv("Armed_RBV")]
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
"""Support for the ADAravis areaDetector driver.
|
|
2
|
+
|
|
3
|
+
https://github.com/areaDetector/ADSimDetector
|
|
4
|
+
"""
|
|
5
|
+
|
|
1
6
|
from ._sim import SimDetector
|
|
2
7
|
from ._sim_controller import SimController
|
|
8
|
+
from ._sim_io import SimDriverIO
|
|
3
9
|
|
|
4
10
|
__all__ = [
|
|
5
|
-
"
|
|
11
|
+
"SimDriverIO",
|
|
6
12
|
"SimController",
|
|
13
|
+
"SimDetector",
|
|
7
14
|
]
|
|
@@ -1,35 +1,41 @@
|
|
|
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
6
|
from ._sim_controller import SimController
|
|
7
|
+
from ._sim_io import SimDriverIO
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
class SimDetector(
|
|
10
|
-
|
|
11
|
-
_writer: adcore.ADHDFWriter
|
|
10
|
+
class SimDetector(adcore.AreaDetector[SimController]):
|
|
11
|
+
"""Detector for simulated Areadetector."""
|
|
12
12
|
|
|
13
13
|
def __init__(
|
|
14
14
|
self,
|
|
15
15
|
prefix: str,
|
|
16
16
|
path_provider: PathProvider,
|
|
17
17
|
drv_suffix="cam1:",
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
writer_cls: type[adcore.ADWriter] = adcore.ADHDFWriter,
|
|
19
|
+
fileio_suffix: str | None = None,
|
|
20
|
+
name="",
|
|
20
21
|
config_sigs: Sequence[SignalR] = (),
|
|
22
|
+
plugins: dict[str, adcore.NDPluginBaseIO] | None = None,
|
|
21
23
|
):
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
driver = SimDriverIO(prefix + drv_suffix)
|
|
25
|
+
controller = SimController(driver)
|
|
26
|
+
|
|
27
|
+
writer = writer_cls.with_io(
|
|
28
|
+
prefix,
|
|
29
|
+
path_provider,
|
|
30
|
+
dataset_source=driver,
|
|
31
|
+
fileio_suffix=fileio_suffix,
|
|
32
|
+
plugins=plugins,
|
|
33
|
+
)
|
|
24
34
|
|
|
25
35
|
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),
|
|
36
|
+
controller=controller,
|
|
37
|
+
writer=writer,
|
|
38
|
+
plugins=plugins,
|
|
34
39
|
name=name,
|
|
40
|
+
config_sigs=config_sigs,
|
|
35
41
|
)
|
|
@@ -1,51 +1,17 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
|
|
3
|
-
from ophyd_async.core import (
|
|
4
|
-
DEFAULT_TIMEOUT,
|
|
5
|
-
AsyncStatus,
|
|
6
|
-
DetectorController,
|
|
7
|
-
DetectorTrigger,
|
|
8
|
-
TriggerInfo,
|
|
9
|
-
)
|
|
10
1
|
from ophyd_async.epics import adcore
|
|
11
2
|
|
|
3
|
+
from ._sim_io import SimDriverIO
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SimController(adcore.ADBaseController[SimDriverIO]):
|
|
7
|
+
"""Controller for simulated Areadetector."""
|
|
12
8
|
|
|
13
|
-
class SimController(DetectorController):
|
|
14
9
|
def __init__(
|
|
15
10
|
self,
|
|
16
|
-
driver:
|
|
17
|
-
good_states: frozenset[adcore.
|
|
11
|
+
driver: SimDriverIO,
|
|
12
|
+
good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
|
|
18
13
|
) -> None:
|
|
19
|
-
|
|
20
|
-
self.good_states = good_states
|
|
21
|
-
self.frame_timeout: float
|
|
22
|
-
self._arm_status: AsyncStatus | None = None
|
|
14
|
+
super().__init__(driver, good_states=good_states)
|
|
23
15
|
|
|
24
16
|
def get_deadtime(self, exposure: float | None) -> float:
|
|
25
|
-
return 0.
|
|
26
|
-
|
|
27
|
-
async def prepare(self, trigger_info: TriggerInfo):
|
|
28
|
-
assert (
|
|
29
|
-
trigger_info.trigger == DetectorTrigger.INTERNAL
|
|
30
|
-
), "fly scanning (i.e. external triggering) is not supported for this device"
|
|
31
|
-
self.frame_timeout = (
|
|
32
|
-
DEFAULT_TIMEOUT + await self.driver.acquire_time.get_value()
|
|
33
|
-
)
|
|
34
|
-
await asyncio.gather(
|
|
35
|
-
self.driver.num_images.set(trigger_info.total_number_of_triggers),
|
|
36
|
-
self.driver.image_mode.set(adcore.ImageMode.MULTIPLE),
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
async def arm(self):
|
|
40
|
-
self._arm_status = await adcore.start_acquiring_driver_and_ensure_status(
|
|
41
|
-
self.driver, good_states=self.good_states, timeout=self.frame_timeout
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
async def wait_for_idle(self):
|
|
45
|
-
if self._arm_status:
|
|
46
|
-
await self._arm_status
|
|
47
|
-
|
|
48
|
-
async def disarm(self):
|
|
49
|
-
# We can't use caput callback as we already used it in arm() and we can't have
|
|
50
|
-
# 2 or they will deadlock
|
|
51
|
-
await adcore.stop_busy_record(self.driver.acquire, False, timeout=1)
|
|
17
|
+
return 0.001
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
from ._vimba import VimbaDetector
|
|
2
2
|
from ._vimba_controller import VimbaController
|
|
3
|
-
from ._vimba_io import
|
|
3
|
+
from ._vimba_io import (
|
|
4
|
+
VimbaConvertFormat,
|
|
5
|
+
VimbaDriverIO,
|
|
6
|
+
VimbaExposeOutMode,
|
|
7
|
+
VimbaOnOff,
|
|
8
|
+
VimbaOverlap,
|
|
9
|
+
VimbaTriggerSource,
|
|
10
|
+
)
|
|
4
11
|
|
|
5
12
|
__all__ = [
|
|
6
13
|
"VimbaDetector",
|
|
@@ -9,4 +16,6 @@ __all__ = [
|
|
|
9
16
|
"VimbaExposeOutMode",
|
|
10
17
|
"VimbaOnOff",
|
|
11
18
|
"VimbaTriggerSource",
|
|
19
|
+
"VimbaOverlap",
|
|
20
|
+
"VimbaConvertFormat",
|
|
12
21
|
]
|
|
@@ -1,43 +1,44 @@
|
|
|
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(
|
|
11
|
-
"""
|
|
12
|
-
Ophyd-async implementation of an ADVimba Detector.
|
|
13
|
-
"""
|
|
10
|
+
class VimbaDetector(adcore.AreaDetector[VimbaController]):
|
|
11
|
+
"""Ophyd-async implementation of an ADVimba Detector.
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
https://github.com/areaDetector/ADVimba
|
|
14
|
+
"""
|
|
17
15
|
|
|
18
16
|
def __init__(
|
|
19
17
|
self,
|
|
20
18
|
prefix: str,
|
|
21
19
|
path_provider: PathProvider,
|
|
22
|
-
drv_suffix="cam1:",
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
drv_suffix: str = "cam1:",
|
|
21
|
+
writer_cls: type[adcore.ADWriter] = adcore.ADHDFWriter,
|
|
22
|
+
fileio_suffix: str | None = None,
|
|
23
|
+
name: str = "",
|
|
24
|
+
plugins: dict[str, adcore.NDPluginBaseIO] | None = None,
|
|
25
|
+
config_sigs: Sequence[SignalR] = (),
|
|
25
26
|
):
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
driver = VimbaDriverIO(prefix + drv_suffix)
|
|
28
|
+
controller = VimbaController(driver)
|
|
29
|
+
|
|
30
|
+
writer = writer_cls.with_io(
|
|
31
|
+
prefix,
|
|
32
|
+
path_provider,
|
|
33
|
+
dataset_source=driver,
|
|
34
|
+
fileio_suffix=fileio_suffix,
|
|
35
|
+
plugins=plugins,
|
|
36
|
+
)
|
|
28
37
|
|
|
29
38
|
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,),
|
|
39
|
+
controller=controller,
|
|
40
|
+
writer=writer,
|
|
41
|
+
plugins=plugins,
|
|
38
42
|
name=name,
|
|
43
|
+
config_sigs=config_sigs,
|
|
39
44
|
)
|
|
40
|
-
|
|
41
|
-
@property
|
|
42
|
-
def hints(self) -> Hints:
|
|
43
|
-
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
|
)
|
|
@@ -25,42 +23,32 @@ EXPOSE_OUT_MODE = {
|
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
|
|
28
|
-
class VimbaController(
|
|
26
|
+
class VimbaController(adcore.ADBaseController[VimbaDriverIO]):
|
|
27
|
+
"""Controller for the Vimba detector."""
|
|
28
|
+
|
|
29
29
|
def __init__(
|
|
30
30
|
self,
|
|
31
31
|
driver: VimbaDriverIO,
|
|
32
|
+
good_states: frozenset[adcore.ADState] = adcore.DEFAULT_GOOD_STATES,
|
|
32
33
|
) -> None:
|
|
33
|
-
|
|
34
|
-
self._arm_status: AsyncStatus | None = None
|
|
34
|
+
super().__init__(driver, good_states=good_states)
|
|
35
35
|
|
|
36
36
|
def get_deadtime(self, exposure: float | None) -> float:
|
|
37
37
|
return 0.001
|
|
38
38
|
|
|
39
39
|
async def prepare(self, trigger_info: TriggerInfo):
|
|
40
40
|
await asyncio.gather(
|
|
41
|
-
self.
|
|
42
|
-
self.
|
|
43
|
-
self.
|
|
44
|
-
self.
|
|
41
|
+
self.driver.trigger_mode.set(TRIGGER_MODE[trigger_info.trigger]),
|
|
42
|
+
self.driver.exposure_mode.set(EXPOSE_OUT_MODE[trigger_info.trigger]),
|
|
43
|
+
self.driver.num_images.set(trigger_info.total_number_of_triggers),
|
|
44
|
+
self.driver.image_mode.set(adcore.ADImageMode.MULTIPLE),
|
|
45
45
|
)
|
|
46
46
|
if trigger_info.livetime is not None and trigger_info.trigger not in [
|
|
47
47
|
DetectorTrigger.VARIABLE_GATE,
|
|
48
48
|
DetectorTrigger.CONSTANT_GATE,
|
|
49
49
|
]:
|
|
50
|
-
await self.
|
|
50
|
+
await self.driver.acquire_time.set(trigger_info.livetime)
|
|
51
51
|
if trigger_info.trigger != DetectorTrigger.INTERNAL:
|
|
52
|
-
self.
|
|
52
|
+
self.driver.trigger_source.set(VimbaTriggerSource.LINE1)
|
|
53
53
|
else:
|
|
54
|
-
self.
|
|
55
|
-
|
|
56
|
-
async def arm(self):
|
|
57
|
-
self._arm_status = await adcore.start_acquiring_driver_and_ensure_status(
|
|
58
|
-
self._drv
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
async def wait_for_idle(self):
|
|
62
|
-
if self._arm_status:
|
|
63
|
-
await self._arm_status
|
|
64
|
-
|
|
65
|
-
async def disarm(self):
|
|
66
|
-
await adcore.stop_busy_record(self._drv.acquire, False, timeout=1)
|
|
54
|
+
self.driver.trigger_source.set(VimbaTriggerSource.FREERUN)
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
from
|
|
2
|
-
from ophyd_async.epics import adcore
|
|
3
|
-
from ophyd_async.epics.core import epics_signal_rw_rbv
|
|
4
|
-
|
|
1
|
+
from typing import Annotated as A
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
EXT_TRIGGER = "Ext. Trigger"
|
|
10
|
-
MULT_TRIGGER = "Mult. Trigger"
|
|
11
|
-
ALIGNMENT = "Alignment"
|
|
3
|
+
from ophyd_async.core import SignalRW, StrictEnum
|
|
4
|
+
from ophyd_async.epics import adcore
|
|
5
|
+
from ophyd_async.epics.core import PvSuffix
|
|
12
6
|
|
|
13
7
|
|
|
14
8
|
class VimbaConvertFormat(StrictEnum):
|
|
9
|
+
"""Convert pixel format for the Vimba detector."""
|
|
10
|
+
|
|
15
11
|
NONE = "None"
|
|
16
12
|
MONO8 = "Mono8"
|
|
17
13
|
MONO16 = "Mono16"
|
|
@@ -20,6 +16,8 @@ class VimbaConvertFormat(StrictEnum):
|
|
|
20
16
|
|
|
21
17
|
|
|
22
18
|
class VimbaTriggerSource(StrictEnum):
|
|
19
|
+
"""Mode for the source of triggers on the Vimbda."""
|
|
20
|
+
|
|
23
21
|
FREERUN = "Freerun"
|
|
24
22
|
LINE1 = "Line1"
|
|
25
23
|
LINE2 = "Line2"
|
|
@@ -30,36 +28,33 @@ class VimbaTriggerSource(StrictEnum):
|
|
|
30
28
|
|
|
31
29
|
|
|
32
30
|
class VimbaOverlap(StrictEnum):
|
|
31
|
+
"""Overlap modes for the Vimba detector."""
|
|
32
|
+
|
|
33
33
|
OFF = "Off"
|
|
34
34
|
PREV_FRAME = "PreviousFrame"
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class VimbaOnOff(StrictEnum):
|
|
38
|
+
"""On/Off modes on the Vimba detector."""
|
|
39
|
+
|
|
38
40
|
ON = "On"
|
|
39
41
|
OFF = "Off"
|
|
40
42
|
|
|
41
43
|
|
|
42
44
|
class VimbaExposeOutMode(StrictEnum):
|
|
45
|
+
"""Exposure control modes for Vimba detectors."""
|
|
46
|
+
|
|
43
47
|
TIMED = "Timed" # Use ExposureTime PV
|
|
44
48
|
TRIGGER_WIDTH = "TriggerWidth" # Expose for length of high signal
|
|
45
49
|
|
|
46
50
|
|
|
47
51
|
class VimbaDriverIO(adcore.ADBaseIO):
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
)
|
|
58
|
-
self.trigger_mode = epics_signal_rw_rbv(VimbaOnOff, prefix + "TriggerMode")
|
|
59
|
-
self.trigger_overlap = epics_signal_rw_rbv(
|
|
60
|
-
VimbaOverlap, prefix + "TriggerOverlap"
|
|
61
|
-
)
|
|
62
|
-
self.exposure_mode = epics_signal_rw_rbv(
|
|
63
|
-
VimbaExposeOutMode, prefix + "ExposureMode"
|
|
64
|
-
)
|
|
65
|
-
super().__init__(prefix, name)
|
|
52
|
+
"""Mirrors the interface provided by ADVimba/db/vimba.template."""
|
|
53
|
+
|
|
54
|
+
convert_pixel_format: A[
|
|
55
|
+
SignalRW[VimbaConvertFormat], PvSuffix("ConvertPixelFormat")
|
|
56
|
+
]
|
|
57
|
+
trigger_source: A[SignalRW[VimbaTriggerSource], PvSuffix("TriggerSource")]
|
|
58
|
+
trigger_mode: A[SignalRW[VimbaOnOff], PvSuffix("TriggerMode")]
|
|
59
|
+
trigger_overlap: A[SignalRW[VimbaOverlap], PvSuffix("TriggerOverlap")]
|
|
60
|
+
exposure_mode: A[SignalRW[VimbaExposeOutMode], PvSuffix("ExposureMode")]
|