dls-dodal 1.29.3__py3-none-any.whl → 1.30.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.
Files changed (88) hide show
  1. {dls_dodal-1.29.3.dist-info → dls_dodal-1.30.0.dist-info}/METADATA +28 -43
  2. dls_dodal-1.30.0.dist-info/RECORD +132 -0
  3. {dls_dodal-1.29.3.dist-info → dls_dodal-1.30.0.dist-info}/WHEEL +1 -1
  4. dls_dodal-1.30.0.dist-info/entry_points.txt +3 -0
  5. dodal/__init__.py +1 -4
  6. dodal/_version.py +2 -2
  7. dodal/beamlines/__init__.py +3 -1
  8. dodal/beamlines/i03.py +28 -23
  9. dodal/beamlines/i04.py +34 -12
  10. dodal/beamlines/i13_1.py +66 -0
  11. dodal/beamlines/i22.py +5 -5
  12. dodal/beamlines/i24.py +15 -1
  13. dodal/beamlines/p38.py +7 -7
  14. dodal/beamlines/p45.py +7 -5
  15. dodal/beamlines/p99.py +61 -0
  16. dodal/cli.py +6 -3
  17. dodal/common/beamlines/beamline_parameters.py +2 -2
  18. dodal/common/beamlines/beamline_utils.py +6 -5
  19. dodal/common/maths.py +1 -3
  20. dodal/common/types.py +2 -3
  21. dodal/common/udc_directory_provider.py +14 -3
  22. dodal/common/visit.py +2 -3
  23. dodal/devices/CTAB.py +22 -17
  24. dodal/devices/aperturescatterguard.py +114 -136
  25. dodal/devices/areadetector/adaravis.py +8 -6
  26. dodal/devices/areadetector/adsim.py +2 -3
  27. dodal/devices/areadetector/adutils.py +20 -12
  28. dodal/devices/areadetector/plugins/MJPG.py +0 -4
  29. dodal/devices/attenuator.py +4 -4
  30. dodal/devices/cryostream.py +19 -7
  31. dodal/devices/detector/__init__.py +13 -2
  32. dodal/devices/detector/det_dim_constants.py +2 -2
  33. dodal/devices/detector/det_dist_to_beam_converter.py +1 -1
  34. dodal/devices/detector/detector.py +8 -7
  35. dodal/devices/detector/detector_motion.py +38 -31
  36. dodal/devices/eiger.py +23 -23
  37. dodal/devices/eiger_odin.py +12 -13
  38. dodal/devices/fast_grid_scan.py +4 -3
  39. dodal/devices/fluorescence_detector_motion.py +13 -4
  40. dodal/devices/focusing_mirror.py +66 -66
  41. dodal/devices/hutch_shutter.py +4 -4
  42. dodal/devices/i22/dcm.py +4 -3
  43. dodal/devices/i22/fswitch.py +4 -4
  44. dodal/devices/i22/nxsas.py +23 -32
  45. dodal/devices/i24/dcm.py +42 -0
  46. dodal/devices/i24/pmac.py +47 -8
  47. dodal/devices/ipin.py +7 -4
  48. dodal/devices/linkam3.py +11 -5
  49. dodal/devices/logging_ophyd_device.py +1 -1
  50. dodal/devices/motors.py +31 -5
  51. dodal/devices/oav/grid_overlay.py +1 -0
  52. dodal/devices/oav/microns_for_zoom_levels.json +1 -1
  53. dodal/devices/oav/oav_detector.py +2 -0
  54. dodal/devices/oav/oav_parameters.py +18 -10
  55. dodal/devices/oav/oav_to_redis_forwarder.py +100 -0
  56. dodal/devices/oav/pin_image_recognition/__init__.py +19 -17
  57. dodal/devices/oav/pin_image_recognition/utils.py +5 -6
  58. dodal/devices/oav/utils.py +3 -17
  59. dodal/devices/p99/__init__.py +0 -0
  60. dodal/devices/p99/sample_stage.py +43 -0
  61. dodal/devices/robot.py +30 -18
  62. dodal/devices/scintillator.py +8 -5
  63. dodal/devices/smargon.py +3 -3
  64. dodal/devices/status.py +2 -31
  65. dodal/devices/tetramm.py +4 -4
  66. dodal/devices/thawer.py +5 -3
  67. dodal/devices/undulator_dcm.py +6 -8
  68. dodal/devices/util/adjuster_plans.py +2 -2
  69. dodal/devices/util/epics_util.py +6 -8
  70. dodal/devices/util/lookup_tables.py +2 -3
  71. dodal/devices/util/save_panda.py +87 -0
  72. dodal/devices/util/test_utils.py +17 -0
  73. dodal/devices/webcam.py +3 -8
  74. dodal/devices/xbpm_feedback.py +0 -23
  75. dodal/devices/zebra.py +10 -10
  76. dodal/devices/zebra_controlled_shutter.py +3 -3
  77. dodal/devices/zocalo/zocalo_interaction.py +10 -2
  78. dodal/devices/zocalo/zocalo_results.py +31 -18
  79. dodal/log.py +14 -5
  80. dodal/plans/data_session_metadata.py +1 -0
  81. dodal/plans/motor_util_plans.py +117 -0
  82. dodal/utils.py +74 -26
  83. dls_dodal-1.29.3.dist-info/RECORD +0 -124
  84. dls_dodal-1.29.3.dist-info/entry_points.txt +0 -2
  85. dodal/devices/qbpm1.py +0 -8
  86. {dls_dodal-1.29.3.dist-info → dls_dodal-1.30.0.dist-info}/LICENSE +0 -0
  87. {dls_dodal-1.29.3.dist-info → dls_dodal-1.30.0.dist-info}/top_level.txt +0 -0
  88. /dodal/devices/i24/{I24_detector_motion.py → i24_detector_motion.py} +0 -0
dodal/beamlines/i24.py CHANGED
@@ -5,8 +5,9 @@ from dodal.devices.eiger import EigerDetector
5
5
  from dodal.devices.hutch_shutter import HutchShutter
6
6
  from dodal.devices.i24.aperture import Aperture
7
7
  from dodal.devices.i24.beamstop import Beamstop
8
+ from dodal.devices.i24.dcm import DCM
8
9
  from dodal.devices.i24.dual_backlight import DualBacklight
9
- from dodal.devices.i24.I24_detector_motion import DetectorMotion
10
+ from dodal.devices.i24.i24_detector_motion import DetectorMotion
10
11
  from dodal.devices.i24.i24_vgonio import VGonio
11
12
  from dodal.devices.i24.pmac import PMAC
12
13
  from dodal.devices.oav.oav_detector import OAV
@@ -82,6 +83,19 @@ def detector_motion(
82
83
  )
83
84
 
84
85
 
86
+ def dcm(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> DCM:
87
+ """Get the i24 DCM device, instantiate it if it hasn't already been.
88
+ If this is called when already instantiated in i24, it will return the existing object.
89
+ """
90
+ return device_instantiation(
91
+ device_factory=DCM,
92
+ name="dcm",
93
+ prefix="",
94
+ wait=wait_for_connection,
95
+ fake=fake_with_ophyd_sim,
96
+ )
97
+
98
+
85
99
  @skip_device(lambda: BL == "s24")
86
100
  def eiger(
87
101
  wait_for_connection: bool = True,
dodal/beamlines/p38.py CHANGED
@@ -55,7 +55,7 @@ def d3(
55
55
 
56
56
 
57
57
  # Disconnected
58
- @skip_device
58
+ @skip_device()
59
59
  def d11(
60
60
  wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
61
61
  ) -> AravisDetector:
@@ -232,13 +232,13 @@ def dcm(
232
232
  usage="Bragg",
233
233
  type="silicon",
234
234
  reflection=(1, 1, 1),
235
- d_spacing=3.13475,
235
+ d_spacing=(3.13475, "nm"),
236
236
  ),
237
237
  crystal_2_metadata=CrystalMetadata(
238
238
  usage="Bragg",
239
239
  type="silicon",
240
240
  reflection=(1, 1, 1),
241
- d_spacing=3.13475,
241
+ d_spacing=(3.13475, "nm"),
242
242
  ),
243
243
  )
244
244
 
@@ -262,7 +262,7 @@ def undulator(
262
262
  # Must find which PandA IOC(s) are compatible
263
263
  # Must document what PandAs are physically connected to
264
264
  # See: https://github.com/bluesky/ophyd-async/issues/284
265
- @skip_device
265
+ @skip_device()
266
266
  def panda1(
267
267
  wait_for_connection: bool = True,
268
268
  fake_with_ophyd_sim: bool = False,
@@ -277,7 +277,7 @@ def panda1(
277
277
  )
278
278
 
279
279
 
280
- @skip_device
280
+ @skip_device()
281
281
  def panda2(
282
282
  wait_for_connection: bool = True,
283
283
  fake_with_ophyd_sim: bool = False,
@@ -292,7 +292,7 @@ def panda2(
292
292
  )
293
293
 
294
294
 
295
- @skip_device
295
+ @skip_device()
296
296
  def panda3(
297
297
  wait_for_connection: bool = True,
298
298
  fake_with_ophyd_sim: bool = False,
@@ -307,7 +307,7 @@ def panda3(
307
307
  )
308
308
 
309
309
 
310
- @skip_device
310
+ @skip_device()
311
311
  def linkam(
312
312
  wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
313
313
  ) -> Linkam3:
dodal/beamlines/p45.py CHANGED
@@ -1,3 +1,5 @@
1
+ from pathlib import Path
2
+
1
3
  from ophyd_async.epics.areadetector import AravisDetector
2
4
  from ophyd_async.panda import HDFPanda
3
5
 
@@ -18,7 +20,7 @@ set_utils_beamline(BL)
18
20
  set_directory_provider(
19
21
  StaticVisitDirectoryProvider(
20
22
  BL,
21
- "/data/2024/cm37283-2/", # latest commissioning visit
23
+ Path("/data/2024/cm37283-2/"), # latest commissioning visit
22
24
  )
23
25
  )
24
26
 
@@ -48,7 +50,7 @@ def choppers(
48
50
 
49
51
 
50
52
  # Disconnected
51
- @skip_device
53
+ @skip_device()
52
54
  def det(
53
55
  wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
54
56
  ) -> AravisDetector:
@@ -65,7 +67,7 @@ def det(
65
67
 
66
68
 
67
69
  # Disconnected
68
- @skip_device
70
+ @skip_device()
69
71
  def diff(
70
72
  wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
71
73
  ) -> AravisDetector:
@@ -84,7 +86,7 @@ def diff(
84
86
  # Must find which PandA IOC(s) are compatible
85
87
  # Must document what PandAs are physically connected to
86
88
  # See: https://github.com/bluesky/ophyd-async/issues/284
87
- @skip_device
89
+ @skip_device()
88
90
  def panda1(
89
91
  wait_for_connection: bool = True,
90
92
  fake_with_ophyd_sim: bool = False,
@@ -99,7 +101,7 @@ def panda1(
99
101
  )
100
102
 
101
103
 
102
- @skip_device
104
+ @skip_device()
103
105
  def panda2(
104
106
  wait_for_connection: bool = True,
105
107
  fake_with_ophyd_sim: bool = False,
dodal/beamlines/p99.py ADDED
@@ -0,0 +1,61 @@
1
+ from dodal.common.beamlines.beamline_utils import device_instantiation, set_beamline
2
+ from dodal.devices.motors import XYZPositioner
3
+ from dodal.devices.p99.sample_stage import FilterMotor, SampleAngleStage
4
+ from dodal.log import set_beamline as set_log_beamline
5
+ from dodal.utils import get_beamline_name
6
+
7
+ BL = get_beamline_name("BL99P")
8
+ set_log_beamline(BL)
9
+ set_beamline(BL)
10
+
11
+
12
+ def sample_angle_stage(
13
+ wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
14
+ ) -> SampleAngleStage:
15
+ """Sample stage for p99"""
16
+
17
+ return device_instantiation(
18
+ SampleAngleStage,
19
+ prefix="-MO-STAGE-01:",
20
+ name="sample_angle_stage",
21
+ wait=wait_for_connection,
22
+ fake=fake_with_ophyd_sim,
23
+ )
24
+
25
+
26
+ def sample_stage_filer(
27
+ wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
28
+ ) -> FilterMotor:
29
+ """Sample stage for p99"""
30
+
31
+ return device_instantiation(
32
+ FilterMotor,
33
+ prefix="-MO-STAGE-02:MP:SELECT",
34
+ name="sample_stage_filer",
35
+ wait=wait_for_connection,
36
+ fake=fake_with_ophyd_sim,
37
+ )
38
+
39
+
40
+ def sample_xyz_stage(
41
+ wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
42
+ ) -> XYZPositioner:
43
+ return device_instantiation(
44
+ FilterMotor,
45
+ prefix="-MO-STAGE-02:",
46
+ name="sample_xyz_stage",
47
+ wait=wait_for_connection,
48
+ fake=fake_with_ophyd_sim,
49
+ )
50
+
51
+
52
+ def sample_lab_xyz_stage(
53
+ wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
54
+ ) -> XYZPositioner:
55
+ return device_instantiation(
56
+ FilterMotor,
57
+ prefix="-MO-STAGE-02:LAB:",
58
+ name="sample_lab_xyz_stage",
59
+ wait=wait_for_connection,
60
+ fake=fake_with_ophyd_sim,
61
+ )
dodal/cli.py CHANGED
@@ -58,10 +58,13 @@ def connect(beamline: str, all: bool, sim_backend: bool) -> None:
58
58
  include_skipped=all,
59
59
  fake_with_ophyd_sim=sim_backend,
60
60
  )
61
- sim_statement = "(sim mode)" if sim_backend else ""
61
+ sim_statement = " (sim mode)" if sim_backend else ""
62
62
 
63
- print(f"{len(devices)} devices connected {sim_statement}:")
64
- print("\n".join([f"\t{device_name}" for device_name in devices.keys()]))
63
+ print(f"{len(devices)} devices connected{sim_statement}:")
64
+ connected_devices = "\n".join(
65
+ sorted([f"\t{device_name}" for device_name in devices.keys()])
66
+ )
67
+ print(connected_devices)
65
68
 
66
69
  # If exceptions have occurred, this will print details of the relevant PVs
67
70
  if len(exceptions) > 0:
@@ -1,4 +1,4 @@
1
- from typing import Any, Tuple, cast
1
+ from typing import Any, cast
2
2
 
3
3
  from dodal.log import LOGGER
4
4
  from dodal.utils import get_beamline_name
@@ -31,7 +31,7 @@ class GDABeamlineParameters:
31
31
  for line in config_lines_nocomments
32
32
  ]
33
33
  config_pairs: list[tuple[str, Any]] = [
34
- cast(Tuple[str, Any], param)
34
+ cast(tuple[str, Any], param)
35
35
  for param in config_lines_sep_key_and_value
36
36
  if len(param) == 2
37
37
  ]
@@ -1,5 +1,6 @@
1
1
  import inspect
2
- from typing import Callable, Dict, Final, List, Optional, TypeVar, cast
2
+ from collections.abc import Callable
3
+ from typing import Final, TypeVar, cast
3
4
 
4
5
  from bluesky.run_engine import call_in_bluesky_event_loop
5
6
  from ophyd import Device as OphydV1Device
@@ -12,7 +13,7 @@ from dodal.utils import AnyDevice, BeamlinePrefix, skip_device
12
13
 
13
14
  DEFAULT_CONNECTION_TIMEOUT: Final[float] = 5.0
14
15
 
15
- ACTIVE_DEVICES: Dict[str, AnyDevice] = {}
16
+ ACTIVE_DEVICES: dict[str, AnyDevice] = {}
16
17
  BL = ""
17
18
  DIRECTORY_PROVIDER: UpdatingDirectoryProvider | None = None
18
19
 
@@ -33,7 +34,7 @@ def clear_device(name: str):
33
34
  del ACTIVE_DEVICES[name]
34
35
 
35
36
 
36
- def list_active_devices() -> List[str]:
37
+ def list_active_devices() -> list[str]:
37
38
  global ACTIVE_DEVICES
38
39
  return list(ACTIVE_DEVICES.keys())
39
40
 
@@ -59,7 +60,7 @@ def wait_for_connection(
59
60
  )
60
61
  else:
61
62
  raise TypeError(
62
- "Invalid type {} in _wait_for_connection".format(device.__class__.__name__)
63
+ f"Invalid type {device.__class__.__name__} in _wait_for_connection"
63
64
  )
64
65
 
65
66
 
@@ -73,7 +74,7 @@ def device_instantiation(
73
74
  prefix: str,
74
75
  wait: bool,
75
76
  fake: bool,
76
- post_create: Optional[Callable[[T], None]] = None,
77
+ post_create: Callable[[T], None] | None = None,
77
78
  bl_prefix: bool = True,
78
79
  **kwargs,
79
80
  ) -> T:
dodal/common/maths.py CHANGED
@@ -1,9 +1,7 @@
1
- from typing import Tuple
2
-
3
1
  import numpy as np
4
2
 
5
3
 
6
- def step_to_num(start: float, stop: float, step: float) -> Tuple[float, float, int]:
4
+ def step_to_num(start: float, stop: float, step: float) -> tuple[float, float, int]:
7
5
  """
8
6
  Standard handling for converting from start, stop, step to start, stop, num
9
7
  Forces step to be same direction as length
dodal/common/types.py CHANGED
@@ -1,8 +1,7 @@
1
1
  from abc import ABC, abstractmethod
2
+ from collections.abc import Callable, Generator
2
3
  from typing import (
3
4
  Any,
4
- Callable,
5
- Generator,
6
5
  )
7
6
 
8
7
  from bluesky.utils import Msg
@@ -18,4 +17,4 @@ PlanGenerator = Callable[..., MsgGenerator]
18
17
 
19
18
  class UpdatingDirectoryProvider(DirectoryProvider, ABC):
20
19
  @abstractmethod
21
- def update(self, **kwargs) -> None: ...
20
+ async def update(self, **kwargs) -> None: ...
@@ -8,7 +8,7 @@ from dodal.log import LOGGER
8
8
 
9
9
  class PandASubdirectoryProvider(UpdatingDirectoryProvider):
10
10
  """Directory provider for the HDFPanda. Points to a panda subdirectory within the
11
- directory path provided, which must exist before attempting to arm the PCAP block"""
11
+ directory path provided"""
12
12
 
13
13
  resource_dir = Path("panda")
14
14
 
@@ -23,9 +23,20 @@ class PandASubdirectoryProvider(UpdatingDirectoryProvider):
23
23
  else None
24
24
  )
25
25
 
26
- def update(self, directory: Path):
26
+ async def update(self, *, directory: Path, suffix: str = "", **kwargs):
27
+ """Update the root directory into which panda pcap files are written. This will result in the panda
28
+ subdirectory being created if it does not already exist.
29
+ Args:
30
+ directory: Path instance that identifies the root folder. This folder must exist. The panda will
31
+ attempt to write into the "panda" subdirectory which will be created if not already present.
32
+ suffix: Optional str that will be appended to the panda device name along with the file
33
+ type extension to construct the output filename
34
+ """
35
+ output_directory = directory / self.resource_dir
36
+ output_directory.mkdir(exist_ok=True)
37
+
27
38
  self._directory_info = DirectoryInfo(
28
- root=directory, resource_dir=self.resource_dir
39
+ root=directory, resource_dir=self.resource_dir, suffix=suffix
29
40
  )
30
41
 
31
42
  def __call__(self) -> DirectoryInfo:
dodal/common/visit.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from pathlib import Path
3
- from typing import Optional
4
3
 
5
4
  from aiohttp import ClientSession
6
5
  from ophyd_async.core import DirectoryInfo
@@ -105,7 +104,7 @@ class StaticVisitDirectoryProvider(UpdatingDirectoryProvider):
105
104
  self,
106
105
  beamline: str,
107
106
  root: Path,
108
- client: Optional[DirectoryServiceClientBase] = None,
107
+ client: DirectoryServiceClientBase | None = None,
109
108
  ):
110
109
  self._beamline = beamline
111
110
  self._client = client or DirectoryServiceClient(f"{beamline}-control:8088/api")
@@ -113,7 +112,7 @@ class StaticVisitDirectoryProvider(UpdatingDirectoryProvider):
113
112
  self._current_collection = None
114
113
  self._session = None
115
114
 
116
- async def update(self) -> None:
115
+ async def update(self, **kwargs) -> None:
117
116
  """
118
117
  Creates a new data collection in the current visit.
119
118
  """
dodal/devices/CTAB.py CHANGED
@@ -1,8 +1,9 @@
1
- from ophyd import Component as Cpt
2
- from ophyd import Device, EpicsMotor, EpicsSignalRO
1
+ from ophyd_async.core import StandardReadable
2
+ from ophyd_async.epics.motion import Motor
3
+ from ophyd_async.epics.signal import epics_signal_r
3
4
 
4
5
 
5
- class CTAB(Device):
6
+ class CTAB(StandardReadable):
6
7
  """Basic collimantion table (CTAB) device for motion plus the motion disable signal
7
8
  when laser curtain triggered and hutch not locked.
8
9
 
@@ -17,20 +18,24 @@ class CTAB(Device):
17
18
  these disabling systems is to cut power to the motors - signal for this is crate_power
18
19
  """
19
20
 
20
- inboard_y = Cpt(EpicsMotor, "-MO-TABLE-01:INBOARDY")
21
- outboard_y = Cpt(EpicsMotor, "-MO-TABLE-01:OUTBOARDY")
22
- upstream_y = Cpt(EpicsMotor, "-MO-TABLE-01:UPSTREAMY")
23
- combined_downstream_y = Cpt(EpicsMotor, "-MO-TABLE-01:DOWNSTREAMY")
24
- combined_all_y = Cpt(EpicsMotor, "-MO-TABLE-01:Y")
21
+ def __init__(self, prefix: str, name: str = ""):
22
+ with self.add_children_as_readables():
23
+ self.inboard_y = Motor(prefix + "-MO-TABLE-01:INBOARDY")
24
+ self.outboard_y = Motor(prefix + "-MO-TABLE-01:OUTBOARDY")
25
+ self.upstream_y = Motor(prefix + "-MO-TABLE-01:UPSTREAMY")
26
+ self.combined_downstream_y = Motor(prefix + "-MO-TABLE-01:DOWNSTREAMY")
27
+ self.combined_all_y = Motor(prefix + "-MO-TABLE-01:Y")
25
28
 
26
- downstream_x = Cpt(EpicsMotor, "-MO-TABLE-01:DOWNSTREAMX")
27
- upstream_x = Cpt(EpicsMotor, "-MO-TABLE-01:UPSTREAMX")
28
- combined_all_x = Cpt(EpicsMotor, "-MO-TABLE-01:X")
29
+ self.downstream_x = Motor(prefix + "-MO-TABLE-01:DOWNSTREAMX")
30
+ self.upstream_x = Motor(prefix + "-MO-TABLE-01:UPSTREAMX")
31
+ self.combined_all_x = Motor(prefix + "-MO-TABLE-01:X")
29
32
 
30
- pitch = Cpt(EpicsMotor, "-MO-TABLE-01:PITCH")
31
- roll = Cpt(EpicsMotor, "-MO-TABLE-01:ROLL")
32
- yaw = Cpt(EpicsMotor, "-MO-TABLE-01:YAW")
33
+ self.pitch = Motor(prefix + "-MO-TABLE-01:PITCH")
34
+ self.roll = Motor(prefix + "-MO-TABLE-01:ROLL")
35
+ self.yaw = Motor(prefix + "-MO-TABLE-01:YAW")
33
36
 
34
- crate_power = Cpt(
35
- EpicsSignalRO, "-MO-PMAC-02:CRATE2_HEALTHY"
36
- ) # returns 0 if no power
37
+ self.crate_power = epics_signal_r(
38
+ int, prefix + "-MO-PMAC-02:CRATE2_HEALTHY"
39
+ ) # returns 0 if no power
40
+
41
+ super().__init__(name)