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
ophyd_async/__init__.py
CHANGED
ophyd_async/_version.py
CHANGED
ophyd_async/core/__init__.py
CHANGED
|
@@ -5,22 +5,34 @@ from ._providers import (
|
|
|
5
5
|
ShapeProvider,
|
|
6
6
|
StaticDirectoryProvider,
|
|
7
7
|
)
|
|
8
|
-
from .async_status import AsyncStatus
|
|
9
|
-
from .detector import
|
|
8
|
+
from .async_status import AsyncStatus, WatchableAsyncStatus
|
|
9
|
+
from .detector import (
|
|
10
|
+
DetectorControl,
|
|
11
|
+
DetectorTrigger,
|
|
12
|
+
DetectorWriter,
|
|
13
|
+
StandardDetector,
|
|
14
|
+
TriggerInfo,
|
|
15
|
+
)
|
|
10
16
|
from .device import Device, DeviceCollector, DeviceVector
|
|
11
17
|
from .device_save_loader import (
|
|
12
18
|
get_signal_values,
|
|
19
|
+
load_device,
|
|
13
20
|
load_from_yaml,
|
|
21
|
+
save_device,
|
|
14
22
|
save_to_yaml,
|
|
15
23
|
set_signal_values,
|
|
16
24
|
walk_rw_signals,
|
|
17
25
|
)
|
|
18
|
-
from .flyer import
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
from .flyer import HardwareTriggeredFlyable, TriggerLogic
|
|
27
|
+
from .mock_signal_backend import MockSignalBackend
|
|
28
|
+
from .mock_signal_utils import (
|
|
29
|
+
callback_on_mock_put,
|
|
30
|
+
get_mock_put,
|
|
31
|
+
mock_puts_blocked,
|
|
32
|
+
reset_mock_put_calls,
|
|
33
|
+
set_mock_put_proceeds,
|
|
34
|
+
set_mock_value,
|
|
35
|
+
set_mock_values,
|
|
24
36
|
)
|
|
25
37
|
from .signal import (
|
|
26
38
|
Signal,
|
|
@@ -28,18 +40,23 @@ from .signal import (
|
|
|
28
40
|
SignalRW,
|
|
29
41
|
SignalW,
|
|
30
42
|
SignalX,
|
|
43
|
+
assert_configuration,
|
|
44
|
+
assert_emitted,
|
|
45
|
+
assert_reading,
|
|
46
|
+
assert_value,
|
|
31
47
|
observe_value,
|
|
32
48
|
set_and_wait_for_value,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
set_sim_value,
|
|
49
|
+
soft_signal_r_and_setter,
|
|
50
|
+
soft_signal_rw,
|
|
36
51
|
wait_for_value,
|
|
37
52
|
)
|
|
38
53
|
from .signal_backend import SignalBackend
|
|
39
|
-
from .
|
|
40
|
-
from .standard_readable import StandardReadable
|
|
54
|
+
from .soft_signal_backend import SoftSignalBackend
|
|
55
|
+
from .standard_readable import ConfigSignal, HintedSignal, StandardReadable
|
|
41
56
|
from .utils import (
|
|
42
57
|
DEFAULT_TIMEOUT,
|
|
58
|
+
CalculatableTimeout,
|
|
59
|
+
CalculateTimeout,
|
|
43
60
|
Callback,
|
|
44
61
|
NotConnected,
|
|
45
62
|
ReadingValueCallback,
|
|
@@ -51,9 +68,15 @@ from .utils import (
|
|
|
51
68
|
)
|
|
52
69
|
|
|
53
70
|
__all__ = [
|
|
71
|
+
"get_mock_put",
|
|
72
|
+
"callback_on_mock_put",
|
|
73
|
+
"mock_puts_blocked",
|
|
74
|
+
"set_mock_values",
|
|
75
|
+
"reset_mock_put_calls",
|
|
54
76
|
"SignalBackend",
|
|
55
|
-
"
|
|
77
|
+
"SoftSignalBackend",
|
|
56
78
|
"DetectorControl",
|
|
79
|
+
"MockSignalBackend",
|
|
57
80
|
"DetectorTrigger",
|
|
58
81
|
"DetectorWriter",
|
|
59
82
|
"StandardDetector",
|
|
@@ -65,24 +88,28 @@ __all__ = [
|
|
|
65
88
|
"SignalW",
|
|
66
89
|
"SignalRW",
|
|
67
90
|
"SignalX",
|
|
91
|
+
"soft_signal_r_and_setter",
|
|
92
|
+
"soft_signal_rw",
|
|
68
93
|
"observe_value",
|
|
69
94
|
"set_and_wait_for_value",
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"set_sim_value",
|
|
95
|
+
"set_mock_put_proceeds",
|
|
96
|
+
"set_mock_value",
|
|
73
97
|
"wait_for_value",
|
|
74
98
|
"AsyncStatus",
|
|
99
|
+
"WatchableAsyncStatus",
|
|
75
100
|
"DirectoryInfo",
|
|
76
101
|
"DirectoryProvider",
|
|
77
102
|
"NameProvider",
|
|
78
103
|
"ShapeProvider",
|
|
79
104
|
"StaticDirectoryProvider",
|
|
80
105
|
"StandardReadable",
|
|
106
|
+
"ConfigSignal",
|
|
107
|
+
"HintedSignal",
|
|
81
108
|
"TriggerInfo",
|
|
82
|
-
"DetectorGroupLogic",
|
|
83
|
-
"SameTriggerDetectorGroupLogic",
|
|
84
109
|
"TriggerLogic",
|
|
85
110
|
"HardwareTriggeredFlyable",
|
|
111
|
+
"CalculateTimeout",
|
|
112
|
+
"CalculatableTimeout",
|
|
86
113
|
"DEFAULT_TIMEOUT",
|
|
87
114
|
"Callback",
|
|
88
115
|
"NotConnected",
|
|
@@ -97,4 +124,10 @@ __all__ = [
|
|
|
97
124
|
"save_to_yaml",
|
|
98
125
|
"set_signal_values",
|
|
99
126
|
"walk_rw_signals",
|
|
127
|
+
"load_device",
|
|
128
|
+
"save_device",
|
|
129
|
+
"assert_reading",
|
|
130
|
+
"assert_value",
|
|
131
|
+
"assert_configuration",
|
|
132
|
+
"assert_emitted",
|
|
100
133
|
]
|
ophyd_async/core/_providers.py
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional, Protocol, Sequence, Union
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
@dataclass
|
|
7
8
|
class DirectoryInfo:
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
"""
|
|
10
|
+
Information about where and how to write a file.
|
|
11
|
+
|
|
12
|
+
The bluesky event model splits the URI for a resource into two segments to aid in
|
|
13
|
+
different applications mounting filesystems at different mount points.
|
|
14
|
+
The portion of this path which is relevant only for the writer is the 'root',
|
|
15
|
+
while the path from an agreed upon mutual mounting is the resource_path.
|
|
16
|
+
The resource_dir is used with the filename to construct the resource_path.
|
|
17
|
+
|
|
18
|
+
:param root: Path of a root directory, relevant only for the file writer
|
|
19
|
+
:param resource_dir: Directory into which files should be written, relative to root
|
|
20
|
+
:param prefix: Optional filename prefix to add to all files
|
|
21
|
+
:param suffix: Optional filename suffix to add to all files
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
root: Path
|
|
25
|
+
resource_dir: Path
|
|
26
|
+
prefix: Optional[str] = ""
|
|
27
|
+
suffix: Optional[str] = ""
|
|
10
28
|
|
|
11
29
|
|
|
12
30
|
class DirectoryProvider(Protocol):
|
|
@@ -16,8 +34,23 @@ class DirectoryProvider(Protocol):
|
|
|
16
34
|
|
|
17
35
|
|
|
18
36
|
class StaticDirectoryProvider(DirectoryProvider):
|
|
19
|
-
def __init__(
|
|
20
|
-
self
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
directory_path: Union[str, Path],
|
|
40
|
+
filename_prefix: str = "",
|
|
41
|
+
filename_suffix: str = "",
|
|
42
|
+
resource_dir: Optional[Path] = None,
|
|
43
|
+
) -> None:
|
|
44
|
+
if resource_dir is None:
|
|
45
|
+
resource_dir = Path(".")
|
|
46
|
+
if isinstance(directory_path, str):
|
|
47
|
+
directory_path = Path(directory_path)
|
|
48
|
+
self._directory_info = DirectoryInfo(
|
|
49
|
+
root=directory_path,
|
|
50
|
+
resource_dir=resource_dir,
|
|
51
|
+
prefix=filename_prefix,
|
|
52
|
+
suffix=filename_suffix,
|
|
53
|
+
)
|
|
21
54
|
|
|
22
55
|
def __call__(self) -> DirectoryInfo:
|
|
23
56
|
return self._directory_info
|
ophyd_async/core/async_status.py
CHANGED
|
@@ -2,28 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import functools
|
|
5
|
-
|
|
5
|
+
import time
|
|
6
|
+
from dataclasses import asdict, replace
|
|
7
|
+
from typing import (
|
|
8
|
+
AsyncIterator,
|
|
9
|
+
Awaitable,
|
|
10
|
+
Callable,
|
|
11
|
+
Generic,
|
|
12
|
+
Type,
|
|
13
|
+
TypeVar,
|
|
14
|
+
cast,
|
|
15
|
+
)
|
|
6
16
|
|
|
7
17
|
from bluesky.protocols import Status
|
|
8
18
|
|
|
9
|
-
from
|
|
19
|
+
from ..protocols import Watcher
|
|
20
|
+
from .utils import Callback, P, T, WatcherUpdate
|
|
10
21
|
|
|
22
|
+
AS = TypeVar("AS", bound="AsyncStatus")
|
|
23
|
+
WAS = TypeVar("WAS", bound="WatchableAsyncStatus")
|
|
11
24
|
|
|
12
|
-
|
|
25
|
+
|
|
26
|
+
class AsyncStatusBase(Status):
|
|
13
27
|
"""Convert asyncio awaitable to bluesky Status interface"""
|
|
14
28
|
|
|
15
|
-
def __init__(
|
|
16
|
-
self,
|
|
17
|
-
awaitable: Awaitable,
|
|
18
|
-
watchers: Optional[List[Callable]] = None,
|
|
19
|
-
):
|
|
29
|
+
def __init__(self, awaitable: Awaitable):
|
|
20
30
|
if isinstance(awaitable, asyncio.Task):
|
|
21
31
|
self.task = awaitable
|
|
22
32
|
else:
|
|
23
|
-
self.task = asyncio.create_task(awaitable)
|
|
33
|
+
self.task = asyncio.create_task(awaitable)
|
|
24
34
|
self.task.add_done_callback(self._run_callbacks)
|
|
25
|
-
self._callbacks
|
|
26
|
-
self._watchers = watchers
|
|
35
|
+
self._callbacks: list[Callback[Status]] = []
|
|
27
36
|
|
|
28
37
|
def __await__(self):
|
|
29
38
|
return self.task.__await__()
|
|
@@ -35,19 +44,14 @@ class AsyncStatus(Status):
|
|
|
35
44
|
self._callbacks.append(callback)
|
|
36
45
|
|
|
37
46
|
def _run_callbacks(self, task: asyncio.Task):
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# TODO: remove ignore and bump min version when bluesky v1.12.0 is released
|
|
43
|
-
def exception( # type: ignore
|
|
44
|
-
self, timeout: Optional[float] = 0.0
|
|
45
|
-
) -> Optional[BaseException]:
|
|
47
|
+
for callback in self._callbacks:
|
|
48
|
+
callback(self)
|
|
49
|
+
|
|
50
|
+
def exception(self, timeout: float | None = 0.0) -> BaseException | None:
|
|
46
51
|
if timeout != 0.0:
|
|
47
|
-
raise
|
|
52
|
+
raise ValueError(
|
|
48
53
|
"cannot honour any timeout other than 0 in an asynchronous function"
|
|
49
54
|
)
|
|
50
|
-
|
|
51
55
|
if self.task.done():
|
|
52
56
|
try:
|
|
53
57
|
return self.task.exception()
|
|
@@ -67,30 +71,72 @@ class AsyncStatus(Status):
|
|
|
67
71
|
and self.task.exception() is None
|
|
68
72
|
)
|
|
69
73
|
|
|
70
|
-
def watch(self, watcher: Callable):
|
|
71
|
-
"""Add watcher to the list of interested parties.
|
|
72
|
-
|
|
73
|
-
Arguments as per Bluesky :external+bluesky:meth:`watch` protocol.
|
|
74
|
-
"""
|
|
75
|
-
if self._watchers is not None:
|
|
76
|
-
self._watchers.append(watcher)
|
|
77
|
-
|
|
78
|
-
@classmethod
|
|
79
|
-
def wrap(cls, f: Callable[[T], Coroutine]) -> Callable[[T], "AsyncStatus"]:
|
|
80
|
-
@functools.wraps(f)
|
|
81
|
-
def wrap_f(self) -> AsyncStatus:
|
|
82
|
-
return AsyncStatus(f(self))
|
|
83
|
-
|
|
84
|
-
return wrap_f
|
|
85
|
-
|
|
86
74
|
def __repr__(self) -> str:
|
|
87
75
|
if self.done:
|
|
88
|
-
if self.exception()
|
|
89
|
-
status = "errored"
|
|
76
|
+
if e := self.exception():
|
|
77
|
+
status = f"errored: {repr(e)}"
|
|
90
78
|
else:
|
|
91
79
|
status = "done"
|
|
92
80
|
else:
|
|
93
81
|
status = "pending"
|
|
94
|
-
return f"<{type(self).__name__} {status}>"
|
|
82
|
+
return f"<{type(self).__name__}, task: {self.task.get_coro()}, {status}>"
|
|
95
83
|
|
|
96
84
|
__str__ = __repr__
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class AsyncStatus(AsyncStatusBase):
|
|
88
|
+
@classmethod
|
|
89
|
+
def wrap(cls: Type[AS], f: Callable[P, Awaitable]) -> Callable[P, AS]:
|
|
90
|
+
"""Wrap an async function in an AsyncStatus."""
|
|
91
|
+
|
|
92
|
+
@functools.wraps(f)
|
|
93
|
+
def wrap_f(*args: P.args, **kwargs: P.kwargs) -> AS:
|
|
94
|
+
return cls(f(*args, **kwargs))
|
|
95
|
+
|
|
96
|
+
# type is actually functools._Wrapped[P, Awaitable, P, AS]
|
|
97
|
+
# but functools._Wrapped is not necessarily available
|
|
98
|
+
return cast(Callable[P, AS], wrap_f)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class WatchableAsyncStatus(AsyncStatusBase, Generic[T]):
|
|
102
|
+
"""Convert AsyncIterator of WatcherUpdates to bluesky Status interface."""
|
|
103
|
+
|
|
104
|
+
def __init__(self, iterator: AsyncIterator[WatcherUpdate[T]]):
|
|
105
|
+
self._watchers: list[Watcher] = []
|
|
106
|
+
self._start = time.monotonic()
|
|
107
|
+
self._last_update: WatcherUpdate[T] | None = None
|
|
108
|
+
super().__init__(self._notify_watchers_from(iterator))
|
|
109
|
+
|
|
110
|
+
async def _notify_watchers_from(self, iterator: AsyncIterator[WatcherUpdate[T]]):
|
|
111
|
+
async for update in iterator:
|
|
112
|
+
self._last_update = (
|
|
113
|
+
update
|
|
114
|
+
if update.time_elapsed is not None
|
|
115
|
+
else replace(update, time_elapsed=time.monotonic() - self._start)
|
|
116
|
+
)
|
|
117
|
+
for watcher in self._watchers:
|
|
118
|
+
self._update_watcher(watcher, self._last_update)
|
|
119
|
+
|
|
120
|
+
def _update_watcher(self, watcher: Watcher, update: WatcherUpdate[T]):
|
|
121
|
+
vals = asdict(
|
|
122
|
+
update, dict_factory=lambda d: {k: v for k, v in d if v is not None}
|
|
123
|
+
)
|
|
124
|
+
watcher(**vals)
|
|
125
|
+
|
|
126
|
+
def watch(self, watcher: Watcher):
|
|
127
|
+
self._watchers.append(watcher)
|
|
128
|
+
if self._last_update:
|
|
129
|
+
self._update_watcher(watcher, self._last_update)
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
def wrap(
|
|
133
|
+
cls: Type[WAS],
|
|
134
|
+
f: Callable[P, AsyncIterator[WatcherUpdate[T]]],
|
|
135
|
+
) -> Callable[P, WAS]:
|
|
136
|
+
"""Wrap an AsyncIterator in a WatchableAsyncStatus."""
|
|
137
|
+
|
|
138
|
+
@functools.wraps(f)
|
|
139
|
+
def wrap_f(*args: P.args, **kwargs: P.kwargs) -> WAS:
|
|
140
|
+
return cls(f(*args, **kwargs))
|
|
141
|
+
|
|
142
|
+
return cast(Callable[P, WAS], wrap_f)
|