ophyd-async 0.2.0__py3-none-any.whl → 0.3a2__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.
Files changed (62) hide show
  1. ophyd_async/__init__.py +1 -4
  2. ophyd_async/_version.py +2 -2
  3. ophyd_async/core/__init__.py +16 -10
  4. ophyd_async/core/_providers.py +38 -5
  5. ophyd_async/core/async_status.py +3 -3
  6. ophyd_async/core/detector.py +211 -62
  7. ophyd_async/core/device.py +45 -38
  8. ophyd_async/core/device_save_loader.py +96 -23
  9. ophyd_async/core/flyer.py +30 -244
  10. ophyd_async/core/signal.py +47 -21
  11. ophyd_async/core/signal_backend.py +7 -4
  12. ophyd_async/core/sim_signal_backend.py +30 -18
  13. ophyd_async/core/standard_readable.py +4 -2
  14. ophyd_async/core/utils.py +93 -30
  15. ophyd_async/epics/_backend/_aioca.py +30 -36
  16. ophyd_async/epics/_backend/_p4p.py +75 -41
  17. ophyd_async/epics/_backend/common.py +25 -0
  18. ophyd_async/epics/areadetector/__init__.py +4 -0
  19. ophyd_async/epics/areadetector/aravis.py +69 -0
  20. ophyd_async/epics/areadetector/controllers/ad_sim_controller.py +1 -1
  21. ophyd_async/epics/areadetector/controllers/aravis_controller.py +73 -0
  22. ophyd_async/epics/areadetector/controllers/pilatus_controller.py +37 -25
  23. ophyd_async/epics/areadetector/drivers/aravis_driver.py +154 -0
  24. ophyd_async/epics/areadetector/drivers/pilatus_driver.py +4 -4
  25. ophyd_async/epics/areadetector/pilatus.py +50 -0
  26. ophyd_async/epics/areadetector/writers/_hdffile.py +21 -7
  27. ophyd_async/epics/areadetector/writers/hdf_writer.py +26 -15
  28. ophyd_async/epics/demo/__init__.py +33 -3
  29. ophyd_async/epics/motion/motor.py +20 -14
  30. ophyd_async/epics/pvi/__init__.py +3 -0
  31. ophyd_async/epics/pvi/pvi.py +318 -0
  32. ophyd_async/epics/signal/__init__.py +0 -2
  33. ophyd_async/epics/signal/signal.py +26 -9
  34. ophyd_async/panda/__init__.py +19 -5
  35. ophyd_async/panda/_common_blocks.py +49 -0
  36. ophyd_async/panda/_hdf_panda.py +48 -0
  37. ophyd_async/panda/_panda_controller.py +37 -0
  38. ophyd_async/panda/_trigger.py +39 -0
  39. ophyd_async/panda/_utils.py +15 -0
  40. ophyd_async/panda/writers/__init__.py +3 -0
  41. ophyd_async/panda/writers/_hdf_writer.py +220 -0
  42. ophyd_async/panda/writers/_panda_hdf_file.py +58 -0
  43. ophyd_async/planstubs/__init__.py +5 -0
  44. ophyd_async/planstubs/prepare_trigger_and_dets.py +57 -0
  45. ophyd_async/protocols.py +73 -0
  46. ophyd_async/sim/__init__.py +11 -0
  47. ophyd_async/sim/demo/__init__.py +3 -0
  48. ophyd_async/sim/demo/sim_motor.py +116 -0
  49. ophyd_async/sim/pattern_generator.py +318 -0
  50. ophyd_async/sim/sim_pattern_detector_control.py +55 -0
  51. ophyd_async/sim/sim_pattern_detector_writer.py +34 -0
  52. ophyd_async/sim/sim_pattern_generator.py +37 -0
  53. {ophyd_async-0.2.0.dist-info → ophyd_async-0.3a2.dist-info}/METADATA +20 -76
  54. ophyd_async-0.3a2.dist-info/RECORD +76 -0
  55. {ophyd_async-0.2.0.dist-info → ophyd_async-0.3a2.dist-info}/WHEEL +1 -1
  56. ophyd_async/epics/signal/pvi_get.py +0 -22
  57. ophyd_async/panda/panda.py +0 -294
  58. ophyd_async-0.2.0.dist-info/RECORD +0 -53
  59. /ophyd_async/panda/{table.py → _table.py} +0 -0
  60. {ophyd_async-0.2.0.dist-info → ophyd_async-0.3a2.dist-info}/LICENSE +0 -0
  61. {ophyd_async-0.2.0.dist-info → ophyd_async-0.3a2.dist-info}/entry_points.txt +0 -0
  62. {ophyd_async-0.2.0.dist-info → ophyd_async-0.3a2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,318 @@
1
+ from dataclasses import dataclass
2
+ from pathlib import Path
3
+ from typing import (
4
+ Any,
5
+ AsyncGenerator,
6
+ AsyncIterator,
7
+ Dict,
8
+ Iterator,
9
+ List,
10
+ Optional,
11
+ Sequence,
12
+ )
13
+
14
+ import h5py
15
+ import numpy as np
16
+ from bluesky.protocols import Descriptor, StreamAsset
17
+ from event_model import (
18
+ ComposeStreamResource,
19
+ ComposeStreamResourceBundle,
20
+ StreamDatum,
21
+ StreamRange,
22
+ StreamResource,
23
+ )
24
+
25
+ from ophyd_async.core import DirectoryInfo, DirectoryProvider
26
+ from ophyd_async.core.signal import SignalR, observe_value
27
+ from ophyd_async.core.sim_signal_backend import SimSignalBackend
28
+ from ophyd_async.core.utils import DEFAULT_TIMEOUT
29
+
30
+ # raw data path
31
+ DATA_PATH = "/entry/data/data"
32
+
33
+ # pixel sum path
34
+ SUM_PATH = "/entry/sum"
35
+
36
+ MAX_UINT8_VALUE = np.iinfo(np.uint8).max
37
+
38
+ SLICE_NAME = "AD_HDF5_SWMR_SLICE"
39
+
40
+
41
+ @dataclass
42
+ class DatasetConfig:
43
+ name: str
44
+ shape: Sequence[int]
45
+ maxshape: tuple[Any, ...] = (None,)
46
+ path: Optional[str] = None
47
+ multiplier: Optional[int] = 1
48
+ dtype: Optional[Any] = None
49
+ fillvalue: Optional[int] = None
50
+
51
+
52
+ def get_full_file_description(
53
+ datasets: List[DatasetConfig], outer_shape: tuple[int, ...]
54
+ ):
55
+ full_file_description: Dict[str, Descriptor] = {}
56
+ for d in datasets:
57
+ source = f"soft://{d.name}"
58
+ shape = outer_shape + tuple(d.shape)
59
+ dtype = "number" if d.shape == [1] else "array"
60
+ descriptor = Descriptor(
61
+ source=source, shape=shape, dtype=dtype, external="STREAM:"
62
+ )
63
+ key = d.name.replace("/", "_")
64
+ full_file_description[key] = descriptor
65
+ return full_file_description
66
+
67
+
68
+ def generate_gaussian_blob(height: int, width: int) -> np.ndarray:
69
+ """Make a Gaussian Blob with float values in range 0..1"""
70
+ x, y = np.meshgrid(np.linspace(-1, 1, width), np.linspace(-1, 1, height))
71
+ d = np.sqrt(x * x + y * y)
72
+ blob = np.exp(-(d**2))
73
+ return blob
74
+
75
+
76
+ def generate_interesting_pattern(x: float, y: float) -> float:
77
+ """This function is interesting in x and y in range -10..10, returning
78
+ a float value in range 0..1
79
+ """
80
+ z = 0.5 + (np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)) / 2
81
+ return z
82
+
83
+
84
+ class HdfStreamProvider:
85
+ def __init__(
86
+ self,
87
+ directory_info: DirectoryInfo,
88
+ full_file_name: Path,
89
+ datasets: List[DatasetConfig],
90
+ ) -> None:
91
+ self._last_emitted = 0
92
+ self._bundles: List[ComposeStreamResourceBundle] = self._compose_bundles(
93
+ directory_info, full_file_name, datasets
94
+ )
95
+
96
+ def _compose_bundles(
97
+ self,
98
+ directory_info: DirectoryInfo,
99
+ full_file_name: Path,
100
+ datasets: List[DatasetConfig],
101
+ ) -> List[StreamAsset]:
102
+ path = str(full_file_name.relative_to(directory_info.root))
103
+ root = str(directory_info.root)
104
+ bundler_composer = ComposeStreamResource()
105
+
106
+ bundles: List[ComposeStreamResourceBundle] = []
107
+
108
+ bundles = [
109
+ bundler_composer(
110
+ spec=SLICE_NAME,
111
+ root=root,
112
+ resource_path=path,
113
+ data_key=d.name.replace("/", "_"),
114
+ resource_kwargs={
115
+ "path": d.path,
116
+ "multiplier": d.multiplier,
117
+ "timestamps": "/entry/instrument/NDAttributes/NDArrayTimeStamp",
118
+ },
119
+ )
120
+ for d in datasets
121
+ ]
122
+ return bundles
123
+
124
+ def stream_resources(self) -> Iterator[StreamResource]:
125
+ for bundle in self._bundles:
126
+ yield bundle.stream_resource_doc
127
+
128
+ def stream_data(self, indices_written: int) -> Iterator[StreamDatum]:
129
+ # Indices are relative to resource
130
+ if indices_written > self._last_emitted:
131
+ updated_stream_range = StreamRange(
132
+ start=self._last_emitted,
133
+ stop=indices_written,
134
+ )
135
+ self._last_emitted = indices_written
136
+ for bundle in self._bundles:
137
+ yield bundle.compose_stream_datum(indices=updated_stream_range)
138
+ return None
139
+
140
+ def close(self) -> None:
141
+ for bundle in self._bundles:
142
+ bundle.close()
143
+
144
+
145
+ class PatternGenerator:
146
+ def __init__(
147
+ self,
148
+ saturation_exposure_time: float = 1,
149
+ detector_width: int = 320,
150
+ detector_height: int = 240,
151
+ ) -> None:
152
+ self.saturation_exposure_time = saturation_exposure_time
153
+ self.exposure = saturation_exposure_time
154
+ self.x = 0.0
155
+ self.y = 0.0
156
+ self.height = detector_height
157
+ self.width = detector_width
158
+ self.written_images_counter: int = 0
159
+
160
+ # it automatically initializes to 0
161
+ self.signal_backend = SimSignalBackend(int)
162
+ self.sim_signal = SignalR(self.signal_backend)
163
+ blob = np.array(
164
+ generate_gaussian_blob(width=detector_width, height=detector_height)
165
+ * MAX_UINT8_VALUE
166
+ )
167
+ self.STARTING_BLOB = blob
168
+ self._hdf_stream_provider: Optional[HdfStreamProvider] = None
169
+ self._handle_for_h5_file: Optional[h5py.File] = None
170
+ self.target_path: Optional[Path] = None
171
+
172
+ async def write_image_to_file(self) -> None:
173
+ assert self._handle_for_h5_file, "no file has been opened!"
174
+ # prepare - resize the fixed hdf5 data structure
175
+ # so that the new image can be written
176
+ new_layer = self.written_images_counter + 1
177
+ target_dimensions = (new_layer, self.height, self.width)
178
+
179
+ # generate the simulated data
180
+ intensity: float = generate_interesting_pattern(self.x, self.y)
181
+ detector_data: np.uint8 = np.uint8(
182
+ self.STARTING_BLOB
183
+ * intensity
184
+ * self.exposure
185
+ / self.saturation_exposure_time
186
+ )
187
+
188
+ self._handle_for_h5_file[DATA_PATH].resize(target_dimensions)
189
+
190
+ print(f"writing image {new_layer}")
191
+ assert self._handle_for_h5_file, "no file has been opened!"
192
+ self._handle_for_h5_file[DATA_PATH].resize(target_dimensions)
193
+
194
+ self._handle_for_h5_file[SUM_PATH].resize((new_layer,))
195
+
196
+ # write data to disc (intermediate step)
197
+ self._handle_for_h5_file[DATA_PATH][self.written_images_counter] = detector_data
198
+ self._handle_for_h5_file[SUM_PATH][self.written_images_counter] = np.sum(
199
+ detector_data
200
+ )
201
+
202
+ # save metadata - so that it's discoverable
203
+ self._handle_for_h5_file[DATA_PATH].flush()
204
+ self._handle_for_h5_file[SUM_PATH].flush()
205
+
206
+ # counter increment is last
207
+ # as only at this point the new data is visible from the outside
208
+ self.written_images_counter += 1
209
+ await self.signal_backend.put(self.written_images_counter)
210
+
211
+ def set_exposure(self, value: float) -> None:
212
+ self.exposure = value
213
+
214
+ def set_x(self, value: float) -> None:
215
+ self.x = value
216
+
217
+ def set_y(self, value: float) -> None:
218
+ self.y = value
219
+
220
+ async def open_file(
221
+ self, directory: DirectoryProvider, multiplier: int = 1
222
+ ) -> Dict[str, Descriptor]:
223
+ await self.sim_signal.connect()
224
+
225
+ self.target_path = self._get_new_path(directory)
226
+
227
+ self._handle_for_h5_file = h5py.File(self.target_path, "w", libver="latest")
228
+
229
+ assert self._handle_for_h5_file, "not loaded the file right"
230
+
231
+ datasets = self._get_datasets()
232
+ for d in datasets:
233
+ self._handle_for_h5_file.create_dataset(
234
+ name=d.name,
235
+ shape=d.shape,
236
+ dtype=d.dtype,
237
+ maxshape=d.maxshape,
238
+ )
239
+
240
+ # once datasets written, can switch the model to single writer multiple reader
241
+ self._handle_for_h5_file.swmr_mode = True
242
+
243
+ outer_shape = (multiplier,) if multiplier > 1 else ()
244
+ full_file_description = get_full_file_description(datasets, outer_shape)
245
+
246
+ # cache state to self
247
+ self._datasets = datasets
248
+ self.multiplier = multiplier
249
+ self._directory_provider = directory
250
+ return full_file_description
251
+
252
+ def _get_new_path(self, directory: DirectoryProvider) -> Path:
253
+ info = directory()
254
+ filename = f"{info.prefix}pattern{info.suffix}.h5"
255
+ new_path: Path = info.root / info.resource_dir / filename
256
+ return new_path
257
+
258
+ def _get_datasets(self) -> List[DatasetConfig]:
259
+ raw_dataset = DatasetConfig(
260
+ # name=data_name,
261
+ name=DATA_PATH,
262
+ dtype=np.uint8,
263
+ shape=(1, self.height, self.width),
264
+ maxshape=(None, self.height, self.width),
265
+ )
266
+
267
+ sum_dataset = DatasetConfig(
268
+ name=SUM_PATH,
269
+ dtype=np.float64,
270
+ shape=(1,),
271
+ maxshape=(None,),
272
+ fillvalue=-1,
273
+ )
274
+
275
+ datasets: List[DatasetConfig] = [raw_dataset, sum_dataset]
276
+ return datasets
277
+
278
+ async def collect_stream_docs(
279
+ self, indices_written: int
280
+ ) -> AsyncIterator[StreamAsset]:
281
+ """
282
+ stream resource says "here is a dataset",
283
+ stream datum says "here are N frames in that stream resource",
284
+ you get one stream resource and many stream datums per scan
285
+ """
286
+ if self._handle_for_h5_file:
287
+ self._handle_for_h5_file.flush()
288
+ # when already something was written to the file
289
+ if indices_written:
290
+ # if no frames arrived yet, there's no file to speak of
291
+ # cannot get the full filename the HDF writer will write
292
+ # until the first frame comes in
293
+ if not self._hdf_stream_provider:
294
+ assert self.target_path, "open file has not been called"
295
+ datasets = self._get_datasets()
296
+ self._datasets = datasets
297
+ self._hdf_stream_provider = HdfStreamProvider(
298
+ self._directory_provider(),
299
+ self.target_path,
300
+ self._datasets,
301
+ )
302
+ for doc in self._hdf_stream_provider.stream_resources():
303
+ yield "stream_resource", doc
304
+ if self._hdf_stream_provider:
305
+ for doc in self._hdf_stream_provider.stream_data(indices_written):
306
+ yield "stream_datum", doc
307
+
308
+ def close(self) -> None:
309
+ if self._handle_for_h5_file:
310
+ self._handle_for_h5_file.close()
311
+ print("file closed")
312
+ self._handle_for_h5_file = None
313
+
314
+ async def observe_indices_written(
315
+ self, timeout=DEFAULT_TIMEOUT
316
+ ) -> AsyncGenerator[int, None]:
317
+ async for num_captured in observe_value(self.sim_signal, timeout=timeout):
318
+ yield num_captured // self.multiplier
@@ -0,0 +1,55 @@
1
+ import asyncio
2
+ from typing import Optional
3
+
4
+ from ophyd_async.core import DirectoryProvider
5
+ from ophyd_async.core.async_status import AsyncStatus
6
+ from ophyd_async.core.detector import DetectorControl, DetectorTrigger
7
+ from ophyd_async.sim.pattern_generator import PatternGenerator
8
+
9
+
10
+ class SimPatternDetectorControl(DetectorControl):
11
+ def __init__(
12
+ self,
13
+ pattern_generator: PatternGenerator,
14
+ directory_provider: DirectoryProvider,
15
+ exposure: float = 0.1,
16
+ ) -> None:
17
+ self.pattern_generator: PatternGenerator = pattern_generator
18
+ self.pattern_generator.set_exposure(exposure)
19
+ self.directory_provider: DirectoryProvider = directory_provider
20
+ self.task: Optional[asyncio.Task] = None
21
+ super().__init__()
22
+
23
+ async def arm(
24
+ self,
25
+ num: int,
26
+ trigger: DetectorTrigger = DetectorTrigger.internal,
27
+ exposure: Optional[float] = 0.01,
28
+ ) -> AsyncStatus:
29
+ assert exposure is not None
30
+ period: float = exposure + self.get_deadtime(exposure)
31
+ task = asyncio.create_task(
32
+ self._coroutine_for_image_writing(exposure, period, num)
33
+ )
34
+ self.task = task
35
+ return AsyncStatus(task)
36
+
37
+ async def disarm(self):
38
+ if self.task:
39
+ self.task.cancel()
40
+ try:
41
+ await self.task
42
+ except asyncio.CancelledError:
43
+ pass
44
+ self.task = None
45
+
46
+ def get_deadtime(self, exposure: float) -> float:
47
+ return 0.001
48
+
49
+ async def _coroutine_for_image_writing(
50
+ self, exposure: float, period: float, frames_number: int
51
+ ):
52
+ for _ in range(frames_number):
53
+ self.pattern_generator.set_exposure(exposure)
54
+ await asyncio.sleep(period)
55
+ await self.pattern_generator.write_image_to_file()
@@ -0,0 +1,34 @@
1
+ from typing import AsyncGenerator, AsyncIterator, Dict
2
+
3
+ from bluesky.protocols import Descriptor
4
+
5
+ from ophyd_async.core import DirectoryProvider
6
+ from ophyd_async.core.detector import DetectorWriter
7
+ from ophyd_async.sim.pattern_generator import PatternGenerator
8
+
9
+
10
+ class SimPatternDetectorWriter(DetectorWriter):
11
+ pattern_generator: PatternGenerator
12
+
13
+ def __init__(
14
+ self, pattern_generator: PatternGenerator, directoryProvider: DirectoryProvider
15
+ ) -> None:
16
+ self.pattern_generator = pattern_generator
17
+ self.directory_provider = directoryProvider
18
+
19
+ async def open(self, multiplier: int = 1) -> Dict[str, Descriptor]:
20
+ return await self.pattern_generator.open_file(
21
+ self.directory_provider, multiplier
22
+ )
23
+
24
+ async def close(self) -> None:
25
+ self.pattern_generator.close()
26
+
27
+ def collect_stream_docs(self, indices_written: int) -> AsyncIterator:
28
+ return self.pattern_generator.collect_stream_docs(indices_written)
29
+
30
+ def observe_indices_written(self, timeout=...) -> AsyncGenerator[int, None]:
31
+ return self.pattern_generator.observe_indices_written()
32
+
33
+ async def get_indices_written(self) -> int:
34
+ return self.pattern_generator.written_images_counter
@@ -0,0 +1,37 @@
1
+ from pathlib import Path
2
+ from typing import Sequence
3
+
4
+ from ophyd_async.core import DirectoryProvider, StaticDirectoryProvider
5
+ from ophyd_async.core.detector import StandardDetector
6
+ from ophyd_async.core.signal import SignalR
7
+ from ophyd_async.sim.pattern_generator import PatternGenerator
8
+
9
+ from .sim_pattern_detector_control import SimPatternDetectorControl
10
+ from .sim_pattern_detector_writer import SimPatternDetectorWriter
11
+
12
+
13
+ class SimPatternDetector(StandardDetector):
14
+ def __init__(
15
+ self,
16
+ path: Path,
17
+ config_sigs: Sequence[SignalR] = [],
18
+ name: str = "sim_pattern_detector",
19
+ writer_timeout: float = 1,
20
+ ) -> None:
21
+ self.directory_provider: DirectoryProvider = StaticDirectoryProvider(path)
22
+ self.pattern_generator = PatternGenerator()
23
+ writer = SimPatternDetectorWriter(
24
+ pattern_generator=self.pattern_generator,
25
+ directoryProvider=self.directory_provider,
26
+ )
27
+ controller = SimPatternDetectorControl(
28
+ pattern_generator=self.pattern_generator,
29
+ directory_provider=self.directory_provider,
30
+ )
31
+ super().__init__(
32
+ controller=controller,
33
+ writer=writer,
34
+ config_sigs=config_sigs,
35
+ name=name,
36
+ writer_timeout=writer_timeout,
37
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ophyd-async
3
- Version: 0.2.0
3
+ Version: 0.3a2
4
4
  Summary: Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango
5
5
  Author-email: Tom Cobb <tom.cobb@diamond.ac.uk>
6
6
  License: BSD 3-Clause License
@@ -35,21 +35,19 @@ License: BSD 3-Clause License
35
35
  Project-URL: GitHub, https://github.com/bluesky/ophyd-async
36
36
  Classifier: Development Status :: 3 - Alpha
37
37
  Classifier: License :: OSI Approved :: BSD License
38
- Classifier: Programming Language :: Python :: 3.9
39
38
  Classifier: Programming Language :: Python :: 3.10
40
39
  Classifier: Programming Language :: Python :: 3.11
41
- Requires-Python: >=3.9
42
- Description-Content-Type: text/x-rst
40
+ Requires-Python: >=3.10
41
+ Description-Content-Type: text/markdown
43
42
  License-File: LICENSE
44
43
  Requires-Dist: networkx >=2.0
45
44
  Requires-Dist: numpy
46
45
  Requires-Dist: packaging
47
46
  Requires-Dist: pint
48
- Requires-Dist: bluesky
49
- Requires-Dist: event-model
47
+ Requires-Dist: bluesky >=1.13.0a3
48
+ Requires-Dist: event-model <1.21.0
50
49
  Requires-Dist: p4p
51
50
  Requires-Dist: pyyaml
52
- Requires-Dist: typing-extensions ; python_version < "3.8"
53
51
  Provides-Extra: ca
54
52
  Requires-Dist: aioca >=1.6 ; extra == 'ca'
55
53
  Provides-Extra: dev
@@ -64,7 +62,6 @@ Requires-Dist: inflection ; extra == 'dev'
64
62
  Requires-Dist: ipython ; extra == 'dev'
65
63
  Requires-Dist: ipywidgets ; extra == 'dev'
66
64
  Requires-Dist: matplotlib ; extra == 'dev'
67
- Requires-Dist: mypy ; extra == 'dev'
68
65
  Requires-Dist: myst-parser ; extra == 'dev'
69
66
  Requires-Dist: numpydoc ; extra == 'dev'
70
67
  Requires-Dist: ophyd ; extra == 'dev'
@@ -73,13 +70,14 @@ Requires-Dist: pipdeptree ; extra == 'dev'
73
70
  Requires-Dist: pre-commit ; extra == 'dev'
74
71
  Requires-Dist: pydata-sphinx-theme >=0.12 ; extra == 'dev'
75
72
  Requires-Dist: pyepics >=3.4.2 ; extra == 'dev'
76
- Requires-Dist: pyside6 ; extra == 'dev'
73
+ Requires-Dist: pyside6 ==6.6.2 ; extra == 'dev'
77
74
  Requires-Dist: pytest ; extra == 'dev'
78
75
  Requires-Dist: pytest-asyncio ; extra == 'dev'
79
76
  Requires-Dist: pytest-cov ; extra == 'dev'
80
77
  Requires-Dist: pytest-faulthandler ; extra == 'dev'
81
78
  Requires-Dist: pytest-rerunfailures ; extra == 'dev'
82
79
  Requires-Dist: pytest-timeout ; extra == 'dev'
80
+ Requires-Dist: ruff ; extra == 'dev'
83
81
  Requires-Dist: sphinx-autobuild ; extra == 'dev'
84
82
  Requires-Dist: sphinx-copybutton ; extra == 'dev'
85
83
  Requires-Dist: sphinx-design ; extra == 'dev'
@@ -89,75 +87,21 @@ Requires-Dist: types-pyyaml ; extra == 'dev'
89
87
  Provides-Extra: pva
90
88
  Requires-Dist: p4p ; extra == 'pva'
91
89
 
92
- Ophyd Async
93
- ===========
90
+ [![CI](https://github.com/bluesky/ophyd-async/actions/workflows/ci.yml/badge.svg)](https://github.com/bluesky/ophyd-async/actions/workflows/ci.yml)
91
+ [![Coverage](https://codecov.io/gh/bluesky/ophyd-async/branch/main/graph/badge.svg)](https://codecov.io/gh/bluesky/ophyd-async)
92
+ [![PyPI](https://img.shields.io/pypi/v/ophyd-async.svg)](https://pypi.org/project/ophyd-async)
93
+ [![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
94
94
 
95
- |code_ci| |docs_ci| |coverage| |pypi_version| |license|
95
+ # ophyd_async
96
96
 
97
- Asynchronous device abstraction framework, building on `Ophyd`_.
97
+ Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango
98
98
 
99
- ============== ==============================================================
100
- PyPI ``pip install ophyd-async``
101
- Source code https://github.com/bluesky/ophyd-async
102
- Documentation https://blueskyproject.io/ophyd-async
103
- ============== ==============================================================
99
+ | Source | <https://github.com/bluesky/ophyd-async> |
100
+ | :-----------: | :-----------------------------------------------: |
101
+ | PyPI | `pip install ophyd-async` |
102
+ | Documentation | <https://bluesky.github.io/ophyd-async> |
103
+ | Releases | <https://github.com/bluesky/ophyd-async/releases> |
104
104
 
105
- Python library for asynchronously interfacing with hardware, intended to
106
- be used as an abstraction layer that enables experiment orchestration and data
107
- acquisition code to operate above the specifics of particular devices and control
108
- systems.
105
+ <!-- README only content. Anything below this line won't be included in index.md -->
109
106
 
110
- Both ophyd and ophyd-async are typically used with the `Bluesky Run Engine`_ for
111
- experiment orchestration and data acquisition. However, these libraries are
112
- able to be used in a stand-alone fashion. For an example of how a facility defines
113
- and uses ophyd-async devices, see `dls-dodal`_, which is currently using a
114
- mixture of ophyd and ophyd-async devices.
115
-
116
- While `EPICS`_ is the most common control system layer that ophyd-async can
117
- interface with, other control systems like `Tango`_ are used by some facilities
118
- also. In addition to the abstractions provided by ophyd, ophyd-async allows:
119
-
120
- * Asynchronous signal access, opening the possibility for hardware-triggered
121
- scanning (also known as fly-scanning)
122
- * Simpler instantiation of devices (groupings of signals) with less reliance
123
- upon complex class hierarchies
124
-
125
- NOTE: ophyd-async is included on a provisional basis until the v1.0 release.
126
-
127
- See the tutorials for usage examples.
128
-
129
- .. |code_ci| image:: https://github.com/bluesky/ophyd-async/actions/workflows/code.yml/badge.svg?branch=main
130
- :target: https://github.com/bluesky/ophyd-async/actions/workflows/code.yml
131
- :alt: Code CI
132
-
133
- .. |docs_ci| image:: https://github.com/bluesky/ophyd-async/actions/workflows/docs.yml/badge.svg?branch=main
134
- :target: https://github.com/bluesky/ophyd-async/actions/workflows/docs.yml
135
- :alt: Docs CI
136
-
137
- .. |coverage| image:: https://codecov.io/gh/bluesky/ophyd-async/branch/master/graph/badge.svg
138
- :target: https://codecov.io/gh/bluesky/ophyd-async
139
- :alt: Test Coverage
140
-
141
- .. |pypi_version| image:: https://img.shields.io/pypi/v/ophyd-async.svg
142
- :target: https://pypi.org/project/ophyd-async
143
- :alt: Latest PyPI version
144
-
145
- .. |license| image:: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg
146
- :target: https://opensource.org/licenses/BSD-3-Clause
147
- :alt: BSD 3-Clause License
148
-
149
- .. _Bluesky Run Engine: http://blueskyproject.io/bluesky
150
-
151
- .. _Ophyd: http://blueskyproject.io/ophyd
152
-
153
- .. _dls-dodal: https://github.com/DiamondLightSource/dodal
154
-
155
- .. _EPICS: http://www.aps.anl.gov/epics/
156
-
157
- .. _Tango: https://www.tango-controls.org/
158
-
159
- ..
160
- Anything below this line is used when viewing README.rst and will be replaced
161
- when included in index.rst
162
-
163
- See https://blueskyproject.io/ophyd-async for more detailed documentation.
107
+ See https://bluesky.github.io/ophyd-async for more detailed documentation.
@@ -0,0 +1,76 @@
1
+ ophyd_async/__init__.py,sha256=v-rRiDOgZ3sQSMQKq0vgUQZvpeOkoHFXissAx6Ktg84,61
2
+ ophyd_async/__main__.py,sha256=G-Zcv_G9zK7Nhx6o5L5w-wyhMxdl_WgyMELu8IMFqAE,328
3
+ ophyd_async/_version.py,sha256=qiI6_8XDVi-QTRXglHkCWQmocyDKhwu0_7zIjyy3JmQ,408
4
+ ophyd_async/protocols.py,sha256=8kQms6exvlMRqK0DESv4LyhhweUf7bskUj0ODWBLra8,2283
5
+ ophyd_async/core/__init__.py,sha256=pzR-cIfjKTOoL8BFkvhwwflMIYzCOpeXU1xkppC1V7c,2252
6
+ ophyd_async/core/_providers.py,sha256=LrlTMPHKXWOPVkpAOw-pqBq0kip-c3C9ZZPoFfiaV4M,2212
7
+ ophyd_async/core/async_status.py,sha256=-sfIf7VhwAP25kSVwKZjAIYOTROpfnh2jgkDw5_afSU,2801
8
+ ophyd_async/core/detector.py,sha256=bc_9rXDaRiD2tciSZtP57h64r8wOCG4ZcWnmnJORRlI,11220
9
+ ophyd_async/core/device.py,sha256=Bm8u1wEN6PuJgCgis_msVRvfrO1_RumxAIMPag8k-cI,5813
10
+ ophyd_async/core/device_save_loader.py,sha256=RXA3dPUPihAR2ZGDStlGiA-TAsr_xqL0snsCjMsMnfA,9138
11
+ ophyd_async/core/flyer.py,sha256=rTAcFIyfGJwzwP-oI-V6nPNQiZazKDHn278wC9-sZgQ,2305
12
+ ophyd_async/core/signal.py,sha256=ZNHP9yh81l3kOpyQvnXuASVBlU8SJiKEQczKvJXXCxI,12549
13
+ ophyd_async/core/signal_backend.py,sha256=z0bwzM9IYp-hOZGlxy0u8JHfMGlULP17p14l2eVuk6A,1528
14
+ ophyd_async/core/sim_signal_backend.py,sha256=HK6-_jAvH3Uc8ijnVgLtjl1SsGWyiEH7ptkyUQPv_E4,6071
15
+ ophyd_async/core/standard_readable.py,sha256=ZoArn8sk31Kz389_CDe_Z70L9L6qjT5uVqYi5ywj2oM,2614
16
+ ophyd_async/core/utils.py,sha256=AVF5e42CVG_GaLoHJSI82iC4KAO60fb9fEJMISHBCNM,5043
17
+ ophyd_async/epics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ ophyd_async/epics/_backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ ophyd_async/epics/_backend/_aioca.py,sha256=TZiV5r7CvEbM40t3TrTG4mVJBZGI_NDIaQlhfUHm0us,8789
20
+ ophyd_async/epics/_backend/_p4p.py,sha256=NtGncTdiou9HPt0fLPhPjRDcvUsUJ_6em9bhcQew-C8,12061
21
+ ophyd_async/epics/_backend/common.py,sha256=o0heeQEM7aP-QYCicwOEtNHs5c5OD6raR-5cTs9yRX0,840
22
+ ophyd_async/epics/areadetector/__init__.py,sha256=T5coX2Osb59uzQFlpHykp_spV-SBRQZYWdAIpDDsYQc,442
23
+ ophyd_async/epics/areadetector/aravis.py,sha256=cCugTlUs0b7mtzKTyxtWCRXO27tYA7zycPVPMhgkMyU,2335
24
+ ophyd_async/epics/areadetector/pilatus.py,sha256=gEJVUphr5gj8TwSYcc07ZakWk9tRIKKZRnPva5LZVBE,1537
25
+ ophyd_async/epics/areadetector/single_trigger_det.py,sha256=q5mG-OUVagIjvXLb28lsrGj4eUSoH2pNW2rT4rQR8fA,1206
26
+ ophyd_async/epics/areadetector/utils.py,sha256=dez54oElIkGMnhSM9qghToUB1opSqjdWTV2vhHCgRMA,3133
27
+ ophyd_async/epics/areadetector/controllers/__init__.py,sha256=UG2-M5d2ykp2T8isQJCbAsGZF1aH0BtC_OPlzzPTjnA,149
28
+ ophyd_async/epics/areadetector/controllers/ad_sim_controller.py,sha256=mthZ6WxajMEgUKptq3bnkIctbLhjzTagV66i1auB8cg,1587
29
+ ophyd_async/epics/areadetector/controllers/aravis_controller.py,sha256=vRiSexnFzijG8eEwowMAFIe730xFNMmgjjbXQVi4zrc,2394
30
+ ophyd_async/epics/areadetector/controllers/pilatus_controller.py,sha256=cd1CKkaXlwkpQ0I1VL7nN0U8R4VweTsa08WhvHYI4nY,2243
31
+ ophyd_async/epics/areadetector/drivers/__init__.py,sha256=AOpIEYfoBhG9Nc4-SId99v4PpyEh4_RBXfNaqiXlwUI,315
32
+ ophyd_async/epics/areadetector/drivers/ad_base.py,sha256=ikfyNcZwJa5ah52DckjrBzkMMT_eDY1smM4XWfb6A6E,3689
33
+ ophyd_async/epics/areadetector/drivers/aravis_driver.py,sha256=Z96xXKfGJbLYehyy0wmP3mCW5lVRmdypmX-9U8cYbD8,5607
34
+ ophyd_async/epics/areadetector/drivers/pilatus_driver.py,sha256=fc3vNHqop9oLg-fvaU-diQNEV5U1qzA9vX2T8Hwy_E8,478
35
+ ophyd_async/epics/areadetector/writers/__init__.py,sha256=tpPcrYd1hs8WS7C0gmCnR2EBwjE5RzCljI7WwZ2V_LM,191
36
+ ophyd_async/epics/areadetector/writers/_hdfdataset.py,sha256=E0C9VgsPyY35h7k0mvcIhjsIVNavApLxizqNWlM388w,167
37
+ ophyd_async/epics/areadetector/writers/_hdffile.py,sha256=YtUgOKX53m0TaFEGBW671qXqNuuEKxEyLV5Ein1fjvo,1799
38
+ ophyd_async/epics/areadetector/writers/hdf_writer.py,sha256=oons06X8OAw3fh3IggvvQazjIgWpZAQ9jo_PBdtYewM,5259
39
+ ophyd_async/epics/areadetector/writers/nd_file_hdf.py,sha256=rutCstILCGGwhP5pH_2lWM2QUcZ88-uxx5dTZIJUMWQ,1562
40
+ ophyd_async/epics/areadetector/writers/nd_plugin.py,sha256=l0yBBEazviyFsWJv_4_sfGn_YM_Iyd0_SlMdAmUlXDU,871
41
+ ophyd_async/epics/demo/__init__.py,sha256=XSoP6xZVuQtxcDe6ZBIUy76iEgOywJgR4Z-ASEyyc90,6237
42
+ ophyd_async/epics/demo/demo_ad_sim_detector.py,sha256=06y65yvaqXvL2rDocjYyLz9kTVzuwV-LeuPhEfExdOA,944
43
+ ophyd_async/epics/demo/mover.db,sha256=RFz0rxZue689Wh1sWTZwWeFMUrH04ttPq2u5xJH_Fp4,998
44
+ ophyd_async/epics/demo/sensor.db,sha256=AVtiydrdtwAz2EFurO2Ult9SSRtre3r0akOBbL98LT0,554
45
+ ophyd_async/epics/motion/__init__.py,sha256=tnmVRIwKa9PdN_xonJdAUD04UpEceh-hoD7XI62yDB0,46
46
+ ophyd_async/epics/motion/motor.py,sha256=PBnkPuoCU1u1HD8IGEOM9GF2d1kwd0Hi7V9tr2rahZY,3786
47
+ ophyd_async/epics/pvi/__init__.py,sha256=TbOQNY4enQWgtr1T7x129vpo2p7FIFlr8cyZqqv5Lk4,158
48
+ ophyd_async/epics/pvi/pvi.py,sha256=hpn7GeO0xk_-UDoy42LIsi0adER9IZO3HPP2iu6Nmu4,11320
49
+ ophyd_async/epics/signal/__init__.py,sha256=wb93RTqvSbGKVFQj8OHykbVLGLmwKHU72oi5xYu2UaY,188
50
+ ophyd_async/epics/signal/_epics_transport.py,sha256=DEIL0iYUAWssysVEgWGu1fHSM1l-ATV2kjUgPtDN9LY,858
51
+ ophyd_async/epics/signal/signal.py,sha256=J3Kush7lK0lGj9dZG6qfNPFGNDsv4s-Ggv__1O9qaqA,3165
52
+ ophyd_async/panda/__init__.py,sha256=ZaD1nRgGKAGFGdpP1WWF-FnX3wcGuYqqq0QRZbaSBYQ,692
53
+ ophyd_async/panda/_common_blocks.py,sha256=n0PPc1rar43oDSIA-yNubTc8fR5YCW1tyjQU58whsg0,1038
54
+ ophyd_async/panda/_hdf_panda.py,sha256=zZxIdNIXHwjlZA-HTezQNfcCh98P2Pst6Pcld92rPLM,1414
55
+ ophyd_async/panda/_panda_controller.py,sha256=dIqcjmaIHVrki8UXSoDx46kk6I2Lhpe2o3sXNg5f-RQ,1238
56
+ ophyd_async/panda/_table.py,sha256=dLoRP4zYNOkD_s0Vkp2wVYAwkjVG8nNdf8-FaXOTfPo,5655
57
+ ophyd_async/panda/_trigger.py,sha256=tBH8uq_4o1ASG9yofVxq3tjf5v8LPzniDTRL4yjramI,1195
58
+ ophyd_async/panda/_utils.py,sha256=VHW5kPVISyEkmse_qQcyisBkkEwMO6GG2Ago-CH1AFA,487
59
+ ophyd_async/panda/writers/__init__.py,sha256=xy7BguVQG4HNIDBfKPjMj0KQo1tptC9LbCpEuMcVGaM,70
60
+ ophyd_async/panda/writers/_hdf_writer.py,sha256=tmIMWKQhURZu3PnmJyEn0xd94CWijpErXo_oJTSfefA,7626
61
+ ophyd_async/panda/writers/_panda_hdf_file.py,sha256=42iHaTax4JjOBpNC7d4nkNL9SM14OTnFPTIcXv2jg-4,1759
62
+ ophyd_async/planstubs/__init__.py,sha256=G9B80_d87lnOThUsGbAYPqzMw9xDelq2TbS7dkB692o,188
63
+ ophyd_async/planstubs/prepare_trigger_and_dets.py,sha256=0c4XDAxVkSanyDKtaMda0VgPEbk2jM0geVzAx707DhI,1772
64
+ ophyd_async/sim/__init__.py,sha256=ScjH1g7FMo5yPACfJRZE6xGBWCHU4bKDzNQk1tqObnA,366
65
+ ophyd_async/sim/pattern_generator.py,sha256=R9iC4ofkcM6KI1cLFDdgyggBccXrGmb5_MNIIlPVgQc,10646
66
+ ophyd_async/sim/sim_pattern_detector_control.py,sha256=Ypz8IuRYAY2J243IhVbNyGr_Z-XtpJZ1qxma6NR3TgM,1838
67
+ ophyd_async/sim/sim_pattern_detector_writer.py,sha256=X7AUYKL19Z0CLAI_wzQvJMo5CUYlebT_7-q7CgZ9FsA,1243
68
+ ophyd_async/sim/sim_pattern_generator.py,sha256=w5oc5QaFpAxzM0S5irvcOqsyASgN0rZQRzMjbZ5r8cE,1326
69
+ ophyd_async/sim/demo/__init__.py,sha256=9mxKpslrL89cfSj4g3og8Br3O--pMj3hhWZS-Xu6kyA,56
70
+ ophyd_async/sim/demo/sim_motor.py,sha256=Nt8mD_RY4pl80wQOQ3g0KIfNep4_9teJh4ioYgIsLj8,4036
71
+ ophyd_async-0.3a2.dist-info/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
72
+ ophyd_async-0.3a2.dist-info/METADATA,sha256=_ZQVR2K-Bjx7gL7Z6kmlFbOjeEGG8ttArDfMshOri9g,5249
73
+ ophyd_async-0.3a2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
74
+ ophyd_async-0.3a2.dist-info/entry_points.txt,sha256=O0YNJTEufO0w9BozXi-JurTy2U1_o0ypeCgJLQ727Jk,58
75
+ ophyd_async-0.3a2.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
76
+ ophyd_async-0.3a2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.3)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5