ophyd-async 0.14.0__py3-none-any.whl → 0.14.1__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/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.14.0'
32
- __version_tuple__ = version_tuple = (0, 14, 0)
31
+ __version__ = version = '0.14.1'
32
+ __version_tuple__ = version_tuple = (0, 14, 1)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -48,6 +48,7 @@ from ._protocol import AsyncConfigurable, AsyncReadable, AsyncStageable, Watcher
48
48
  from ._providers import (
49
49
  AutoIncrementFilenameProvider,
50
50
  AutoIncrementingPathProvider,
51
+ AutoMaxIncrementingPathProvider,
51
52
  DatasetDescriber,
52
53
  FilenameProvider,
53
54
  PathInfo,
@@ -222,6 +223,7 @@ __all__ = [
222
223
  "FilenameProvider",
223
224
  "StaticFilenameProvider",
224
225
  "AutoIncrementFilenameProvider",
226
+ "AutoMaxIncrementingPathProvider",
225
227
  "UUIDFilenameProvider",
226
228
  # Datatset
227
229
  "DatasetDescriber",
@@ -1,9 +1,10 @@
1
+ import re
1
2
  import uuid
2
3
  from abc import abstractmethod
3
4
  from collections.abc import Callable
4
5
  from dataclasses import dataclass
5
6
  from datetime import date
6
- from pathlib import PurePath, PureWindowsPath
7
+ from pathlib import Path, PurePath, PureWindowsPath
7
8
  from typing import Protocol
8
9
  from urllib.parse import urlunparse
9
10
 
@@ -157,6 +158,98 @@ class StaticPathProvider(PathProvider):
157
158
  )
158
159
 
159
160
 
161
+ class AutoMaxIncrementingPathProvider(PathProvider):
162
+ """Increment directory name on each call by checking existing directories.
163
+
164
+ Looks through directories in a specified base path to increment directory name.
165
+ PathInfo gives path like base_path/0001_dirname/dirname, or
166
+ base_path/yyyy-mm-dd/0001_dirname/dirname if dated is true. Here,
167
+ the '0001' is the value which gets incremented if the file exists already.
168
+
169
+ It's recommended for the base path provider to be non-incrementing.
170
+
171
+ Args:
172
+ base_path_provider: Path to create directories inside of. Note that the filename of
173
+ this provider is used as the top level directory and the filename
174
+ max_digits: Number of digits to pad onto the parent directory.
175
+ starting_value: Number to start incrementing from.
176
+ dated: Whether to create an extra directory to specify the day.
177
+
178
+ """
179
+
180
+ def __init__(
181
+ self,
182
+ base_path_provider: PathProvider,
183
+ max_digits: int = 4,
184
+ starting_value: int = 0,
185
+ dated: bool = False,
186
+ ):
187
+ self._base_path_provider = base_path_provider
188
+ self._max_digits = max_digits
189
+ self._next_value = starting_value
190
+ self._dated = dated
191
+
192
+ def _get_highest_number_from(self, path: Path) -> int:
193
+ # Look through directories in path which end in "_{number} and get highest
194
+ # number"
195
+ highest_number = 0
196
+ candidates = [
197
+ x for x in path.iterdir() if x.is_dir() and re.match(r"^\d+_", x.name)
198
+ ]
199
+ if candidates:
200
+ highest_number = max(
201
+ int(x.name.split("_", maxsplit=1)[0]) for x in candidates
202
+ )
203
+ else:
204
+ highest_number = self._next_value
205
+ return highest_number
206
+
207
+ def __call__(self, device_name: str | None = None) -> PathInfo:
208
+ base_path_info = self._base_path_provider.__call__()
209
+ base_path_dir = Path(base_path_info.directory_path)
210
+ if self._dated:
211
+ # Make sure we are the max ID of any other days to keep numbering
212
+ # consistent.
213
+ cands = [
214
+ self._get_highest_number_from(x)
215
+ for x in base_path_dir.iterdir()
216
+ if re.match(r"^\d\d\d\d-\d\d-\d\d$", x.name)
217
+ ]
218
+ if cands:
219
+ self._next_value = max(max(cands) + 1, self._next_value)
220
+
221
+ path = base_path_dir / date.today().strftime("%Y-%m-%d")
222
+ else:
223
+ path = base_path_dir
224
+
225
+ val_to_use = self._next_value
226
+
227
+ # Get the highest number using files in path, or use
228
+ # stored next value if no files are found.
229
+ if path.exists():
230
+ highest_number = self._get_highest_number_from(path)
231
+ val_to_use = (
232
+ highest_number + 1
233
+ if not highest_number == self._next_value
234
+ else highest_number
235
+ )
236
+
237
+ self._next_value = val_to_use + 1
238
+
239
+ filename = base_path_info.filename
240
+
241
+ padded_counter = f"{val_to_use:0{self._max_digits}}"
242
+ full_path = (
243
+ path / f"{padded_counter}_{filename.strip('_')}" / filename.rstrip("_")
244
+ )
245
+ return PathInfo(
246
+ directory_path=full_path.parent,
247
+ directory_uri=None,
248
+ filename=full_path.name,
249
+ create_dir_depth=0,
250
+ )
251
+
252
+
160
253
  class AutoIncrementingPathProvider(PathProvider):
161
254
  """Provides a new numerically incremented path on each call."""
162
255
 
@@ -10,6 +10,7 @@ from ophyd_async.core import (
10
10
  AsyncStatus,
11
11
  FlyerController,
12
12
  error_if_none,
13
+ gather_dict,
13
14
  observe_value,
14
15
  set_and_wait_for_value,
15
16
  wait_for_value,
@@ -202,12 +203,25 @@ class PmacTrajectoryTriggerLogic(FlyerController):
202
203
  coord = self.pmac.coord[motor_info.cs_number]
203
204
  coros = []
204
205
  await coord.defer_moves.set(True)
206
+
207
+ motor_readbacks = await gather_dict(
208
+ {motor: motor.user_readback.get_value() for motor in ramp_up_position}
209
+ )
210
+
211
+ move_times = [
212
+ abs(position - motor_readbacks[motor])
213
+ / motor_info.motor_max_velocity[motor]
214
+ for motor, position in ramp_up_position.items()
215
+ ]
216
+
217
+ longest_time = max(move_times)
218
+
205
219
  for motor, position in ramp_up_position.items():
206
220
  coros.append(
207
221
  set_and_wait_for_value(
208
222
  coord.cs_axis_setpoint[motor_info.motor_cs_index[motor]],
209
223
  position,
210
- set_timeout=10,
224
+ set_timeout=longest_time + DEFAULT_TIMEOUT,
211
225
  wait_for_set_completion=False,
212
226
  )
213
227
  )
@@ -29,7 +29,7 @@ class EigerDetectorIO(Device):
29
29
  frame_time: SignalRW[float]
30
30
  nimages: SignalRW[int]
31
31
  ntrigger: SignalRW[int]
32
- nexpi: SignalRW[int]
32
+ nexpi: SignalRW[int] | None
33
33
  trigger_mode: SignalRW[str]
34
34
  roi_mode: SignalRW[str]
35
35
  photon_energy: SignalRW[float]
@@ -35,7 +35,8 @@ class PandaPcapController(DetectorController):
35
35
  await wait_for_value(self.pcap.active, True, timeout=1)
36
36
 
37
37
  async def wait_for_idle(self):
38
- pass
38
+ if self._arm_status and not self._arm_status.done:
39
+ await self._arm_status
39
40
 
40
41
  async def disarm(self):
41
42
  await self.pcap.arm.set(False)
@@ -9,7 +9,9 @@ from ._signal import (
9
9
  tango_signal_x,
10
10
  )
11
11
  from ._tango_transport import (
12
+ AttributeInfoEx,
12
13
  AttributeProxy,
14
+ CommandInfo,
13
15
  CommandProxy,
14
16
  CommandProxyReadCharacter,
15
17
  TangoDoubleStringTable,
@@ -21,6 +23,7 @@ from ._tango_transport import (
21
23
  get_python_type,
22
24
  get_source_metadata,
23
25
  get_tango_trl,
26
+ parse_precision,
24
27
  )
25
28
  from ._utils import (
26
29
  DevStateEnum,
@@ -30,7 +33,9 @@ from ._utils import (
30
33
  )
31
34
 
32
35
  __all__ = [
36
+ "AttributeInfoEx",
33
37
  "AttributeProxy",
38
+ "CommandInfo",
34
39
  "CommandProxy",
35
40
  "CommandProxyReadCharacter",
36
41
  "DevStateEnum",
@@ -44,6 +49,7 @@ __all__ = [
44
49
  "infer_python_type",
45
50
  "infer_signal_type",
46
51
  "make_backend",
52
+ "parse_precision",
47
53
  "tango_signal_r",
48
54
  "tango_signal_rw",
49
55
  "tango_signal_w",
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import functools
3
3
  import logging
4
+ import re
4
5
  import time
5
6
  from abc import abstractmethod
6
7
  from collections.abc import Callable, Coroutine, Sequence
@@ -582,6 +583,15 @@ class CommandProxy(TangoProxy):
582
583
  pass
583
584
 
584
585
 
586
+ PRECISION_PATTERN = re.compile(r"%\d*\.(\d+)f")
587
+
588
+
589
+ def parse_precision(config: AttributeInfoEx):
590
+ if config.format and (matches := PRECISION_PATTERN.findall(config.format)):
591
+ return int(matches[0])
592
+ return None
593
+
594
+
585
595
  def get_dtype_extended(datatype) -> object | None:
586
596
  """For converting tango types to numpy datatype formats."""
587
597
  # DevState tango type does not have numpy equivalents
@@ -639,17 +649,8 @@ def get_source_metadata(
639
649
  if tr_dtype == CmdArgType.DevState:
640
650
  _choices = list(DevState.names.keys())
641
651
 
642
- _precision = None
643
- if config.format:
644
- try:
645
- _precision = int(config.format.split(".")[1].split("f")[0])
646
- except (ValueError, IndexError) as exc:
647
- # If parsing config.format fails, _precision remains None.
648
- logger.warning(
649
- "Failed to parse precision from config.format: %s. Error: %s",
650
- config.format,
651
- exc,
652
- )
652
+ _precision = parse_precision(config)
653
+
653
654
  no_limits = Limits(
654
655
  control=LimitsRange(high=None, low=None),
655
656
  warning=LimitsRange(high=None, low=None),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ophyd-async
3
- Version: 0.14.0
3
+ Version: 0.14.1
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
@@ -1,9 +1,9 @@
1
1
  ophyd_async/__init__.py,sha256=dcAA3qsj1nNIMe5l-v2tlduZ_ypwBmyuHe45Lsq4k4w,206
2
2
  ophyd_async/__main__.py,sha256=n_U4O9bgm97OuboUB_9eK7eFiwy8BZSgXJ0OzbE0DqU,481
3
3
  ophyd_async/_docs_parser.py,sha256=gPYrigfSbYCF7QoSf2UvE-cpQu4snSssl7ZWN-kKDzI,352
4
- ophyd_async/_version.py,sha256=Byw420VruQzJ1exmj6PcZ9zpcSTgfBRBp5ZU4O6rwSc,706
4
+ ophyd_async/_version.py,sha256=a3VJZDtDsD7dO22j4y92zbdkUlJwzXf_QabiVquJm1Y,706
5
5
  ophyd_async/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- ophyd_async/core/__init__.py,sha256=3YD4rtXXJy5HWBsefK0nC-j2xC5tfiFbD7KJbN3nFvw,6008
6
+ ophyd_async/core/__init__.py,sha256=GJxLNTxXD3Qlghq6CNe-mCeludgBBpHkHXkbDFojdHQ,6084
7
7
  ophyd_async/core/_derived_signal.py,sha256=TuZza_j3J1Bw4QSqBYB9Ta2FyQP5BycO3nSHVtJ890Q,13015
8
8
  ophyd_async/core/_derived_signal_backend.py,sha256=Ibce9JHghiI5Ir8w0pUYULHL2qWkobeUYc0-CDrsO2E,12615
9
9
  ophyd_async/core/_detector.py,sha256=9fYbBPmRnMGADcDTYkspDAL2uzhtNNiKCEeBUU0oKaY,14942
@@ -16,7 +16,7 @@ ophyd_async/core/_log.py,sha256=DxKR4Nz3SgTaTzKBZWqt-w48yT8WUAr_3Qr223TEWRw,3587
16
16
  ophyd_async/core/_mock_signal_backend.py,sha256=cFovZEwwqYKV2LuQDZStSr3TFFvA31IJeSRoheHnZ8Q,3401
17
17
  ophyd_async/core/_mock_signal_utils.py,sha256=ePFBDaon2lFT0vcnAaCRuoLr953QV6tYgHmY2fXQW-8,5520
18
18
  ophyd_async/core/_protocol.py,sha256=wQ_snxhTprHqEjQb1HgFwBljwolMY6A8C3xgV1PXwdU,4051
19
- ophyd_async/core/_providers.py,sha256=WBht3QCgvGc0stNcwH6z4Zr6hAz3e01-88NjsYI2w6I,9740
19
+ ophyd_async/core/_providers.py,sha256=IO6xlAVQLkbECT96ezgzoDfZ_OhbD9NrDdXvEcyWJkI,13087
20
20
  ophyd_async/core/_readable.py,sha256=iBo1YwA5bsAbzLbznvmSnzKDWUuGkLh850Br3BXsgeU,11707
21
21
  ophyd_async/core/_settings.py,sha256=_ZccbXKP7j5rG6-bMKk7aaLr8hChdRDAPY_YSR71XXM,4213
22
22
  ophyd_async/core/_signal.py,sha256=OEfjW_BIC3dVxgsWC0aVUj9rQL36KHkNTBCUVXjSRFo,28274
@@ -85,7 +85,7 @@ ophyd_async/epics/odin/__init__.py,sha256=7kRqVzwoD8PVtp7Nj9iQWlgbLeoWE_8oiq-B0k
85
85
  ophyd_async/epics/odin/_odin_io.py,sha256=YDBrS15PnEKe5SHmz397Emh--lZSQEnbR3G7p8pbShY,6533
86
86
  ophyd_async/epics/pmac/__init__.py,sha256=GqJTiJudqE9pu050ZNED09F9tKRfazn0wBsojsMH2gg,273
87
87
  ophyd_async/epics/pmac/_pmac_io.py,sha256=cbChieNrDWRzrr5Mdsqtm2Azp8sG0KHP9rGeJxmbYrA,4332
88
- ophyd_async/epics/pmac/_pmac_trajectory.py,sha256=hzAcpLNmFoNceubsjk6mjsmg6-PgSjWUu1a8Exyvi6I,7729
88
+ ophyd_async/epics/pmac/_pmac_trajectory.py,sha256=DzO6gfvbK8M3Ec6vq0WTlz0RRYGNzP8qDJIMWTWJeLo,8150
89
89
  ophyd_async/epics/pmac/_pmac_trajectory_generation.py,sha256=3IIxXa0r6-2uNnILKLGxp3xosOZx8MubKF-F_OM7uaw,27331
90
90
  ophyd_async/epics/pmac/_utils.py,sha256=MfuY6NicT7wkwVIWAZkWoCu1ZoSzy6jda1wLK9XAOLA,8614
91
91
  ophyd_async/epics/testing/__init__.py,sha256=aTIv4D2DYrpnGco5RQF8QuLG1SfFkIlTyM2uYEKXltA,522
@@ -98,7 +98,7 @@ ophyd_async/fastcs/core.py,sha256=pL_srtTrfuoBHUjDFpxES92owFq9M4Jve0Skk1oeuFA,51
98
98
  ophyd_async/fastcs/eiger/__init__.py,sha256=RxwOFjERKy5tUD_IDGCGuMh716FaZgCq7R9elPixBwo,312
99
99
  ophyd_async/fastcs/eiger/_eiger.py,sha256=jo3K5dM3Co_RDYIyO6poCVDqp2g_1z4MqnYftwnMhUk,1103
100
100
  ophyd_async/fastcs/eiger/_eiger_controller.py,sha256=Cucj-1M-1CaxSJxHZmHs3f_OXwtTIspcqUFhRNGzn_E,2361
101
- ophyd_async/fastcs/eiger/_eiger_io.py,sha256=yNozTKX4CMoqIaFvyKJEs5RXXGJ4VITP_T4TIFHWRmc,1242
101
+ ophyd_async/fastcs/eiger/_eiger_io.py,sha256=mVgDC296B4RkSObWBiA7AXtVRTQw388lJPw2roQctFs,1249
102
102
  ophyd_async/fastcs/jungfrau/__init__.py,sha256=xwTaPiqvtyyljP2acz07FSlUW79Io8EsKks9ENgbumA,765
103
103
  ophyd_async/fastcs/jungfrau/_controller.py,sha256=TvUxRuP3NJFTOcyHeMn5-1Va7HzOJswWCYSGdp1NMFI,5778
104
104
  ophyd_async/fastcs/jungfrau/_jungfrau.py,sha256=KAHCmRHMyzIh-r2JXVJcQOGLkCOOdW5Mao_KChITO2s,929
@@ -107,7 +107,7 @@ ophyd_async/fastcs/jungfrau/_utils.py,sha256=QpdWbPT_31Jwyi7INFMRq9hncSZIK_4J3l6
107
107
  ophyd_async/fastcs/odin/__init__.py,sha256=da1PTClDMl-IBkrSvq6JC1lnS-K_BASzCvxVhNxN5Ls,13
108
108
  ophyd_async/fastcs/panda/__init__.py,sha256=GbnPqH_13wvyPK1CvRHGAViamKVWHY9n-sTmfAdcnMA,1229
109
109
  ophyd_async/fastcs/panda/_block.py,sha256=Bffta9IkuSq_NSvidDvLyC1YUrfQCwMhppsu1Te7vec,2679
110
- ophyd_async/fastcs/panda/_control.py,sha256=xtW3dH_MLQoycgP-4vJtYx1M9alHjWo13iu9UFTgwzY,1306
110
+ ophyd_async/fastcs/panda/_control.py,sha256=lUogRZMkQk4eZFghTuhwSDLqxR16-DQvx9MyFpg10Qc,1387
111
111
  ophyd_async/fastcs/panda/_hdf_panda.py,sha256=tL_OWHxlMQcMZGq9sxHLSeag6hP9MRIbTPn1W0u0iNI,1237
112
112
  ophyd_async/fastcs/panda/_table.py,sha256=maKGoKypEuYqTSVWGgDO6GMEKOtlDm9Dn5YiYdBzu6c,2486
113
113
  ophyd_async/fastcs/panda/_trigger.py,sha256=iBxW4YMfRYrpg7AoQaHb7rHKCE95UbSxguRuR9FOgw8,7610
@@ -132,11 +132,11 @@ ophyd_async/sim/_pattern_generator.py,sha256=kuxvyX2gIxrywhQRhaO1g8YluBT7LBkE20I
132
132
  ophyd_async/sim/_point_detector.py,sha256=wMG_ncvm99WMCPihlFyuMEf3UknAxCpB1hpk3uKiENE,3024
133
133
  ophyd_async/sim/_stage.py,sha256=_SywbmSQwxf7JLx68qwo0RpiB3oIWlbTLmvRKxUoig0,1602
134
134
  ophyd_async/tango/__init__.py,sha256=g9xzjlzPpUAP12YI-kYwfAoLSYPAQdL1S11R2c-cius,60
135
- ophyd_async/tango/core/__init__.py,sha256=dO2tG_y61zZFQRQh5L37Ps-IqNf-DGOT77Ov5Kobfhs,1349
135
+ ophyd_async/tango/core/__init__.py,sha256=cgO5GWfEZqjH0Aj9KUeoZwxMrOQOTn9V9kaYel7j0x4,1473
136
136
  ophyd_async/tango/core/_base_device.py,sha256=X5ncxaWKOfRhhqPyT8tmTBJGc3ldGthw1ZCe_j_M2Tg,5088
137
137
  ophyd_async/tango/core/_converters.py,sha256=xI_RhMR8dY6IVORUZVVCL9LdYnEE6TA6BBPX_lTu06w,2183
138
138
  ophyd_async/tango/core/_signal.py,sha256=8mIxRVEVjhDN33LDbbKZWGMUYn9Gl5ZMEIYw6GSBTUE,5569
139
- ophyd_async/tango/core/_tango_transport.py,sha256=KxjhHqKADrOvzGi9tbOQXUWdsJ0NKGejWxHItxpUsjg,37401
139
+ ophyd_async/tango/core/_tango_transport.py,sha256=fPICZzsF9Rf53sHtM3NXlKmhF-8CNO2prra3msD74R0,37177
140
140
  ophyd_async/tango/core/_utils.py,sha256=pwT7V1DNWSyPOSzvDZ6OsDZTjaV-pAeDLDlmgtHVcNM,1673
141
141
  ophyd_async/tango/demo/__init__.py,sha256=_j-UicTnckuIBp8PnieFMOMnLFGivnaKdmo9o0hYtzc,256
142
142
  ophyd_async/tango/demo/_counter.py,sha256=m6zxOJLbHgCEBAapVc1UiOOqKj5lvrlxjA6mXWMRMjo,1200
@@ -154,8 +154,8 @@ ophyd_async/testing/_one_of_everything.py,sha256=U9ui7B-iNHDM3H3hIWUuaCb8Gc2eLlU
154
154
  ophyd_async/testing/_single_derived.py,sha256=5-HOTzgePcZ354NK_ssVpyIbJoJmKyjVQCxSwQXUC-4,2730
155
155
  ophyd_async/testing/_utils.py,sha256=zClRo5ve8RGia7wQnby41W-Zprj-slOA5da1LfYnuhw,45
156
156
  ophyd_async/testing/_wait_for_pending.py,sha256=YZAR48n-CW0GsPey3zFRzMJ4byDAr3HvMIoawjmTrHw,732
157
- ophyd_async-0.14.0.dist-info/licenses/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
158
- ophyd_async-0.14.0.dist-info/METADATA,sha256=1H2TzMmCIJfib07o1qSOHNYJVDHgqkwIUn9wPjzoYNU,5703
159
- ophyd_async-0.14.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
160
- ophyd_async-0.14.0.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
161
- ophyd_async-0.14.0.dist-info/RECORD,,
157
+ ophyd_async-0.14.1.dist-info/licenses/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
158
+ ophyd_async-0.14.1.dist-info/METADATA,sha256=I2Y_2-lFXNeZYNYDhNxPvrubLM8bd8d5OzPbPzdn0mM,5703
159
+ ophyd_async-0.14.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
160
+ ophyd_async-0.14.1.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
161
+ ophyd_async-0.14.1.dist-info/RECORD,,