dls-dodal 1.46.0__py3-none-any.whl → 1.47.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 (67) hide show
  1. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/METADATA +1 -1
  2. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/RECORD +62 -51
  3. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/WHEEL +1 -1
  4. dodal/_version.py +2 -2
  5. dodal/beamlines/__init__.py +0 -1
  6. dodal/beamlines/b07.py +2 -6
  7. dodal/beamlines/b07_1.py +1 -3
  8. dodal/beamlines/i03.py +12 -15
  9. dodal/beamlines/i04.py +48 -16
  10. dodal/beamlines/i09.py +1 -3
  11. dodal/beamlines/i09_1.py +1 -3
  12. dodal/beamlines/i23.py +17 -1
  13. dodal/beamlines/p38.py +1 -1
  14. dodal/beamlines/p60.py +2 -6
  15. dodal/beamlines/p99.py +48 -4
  16. dodal/common/beamlines/beamline_parameters.py +1 -2
  17. dodal/common/data_util.py +4 -0
  18. dodal/devices/aperturescatterguard.py +47 -47
  19. dodal/devices/current_amplifiers/struck_scaler_counter.py +1 -1
  20. dodal/devices/diamond_filter.py +5 -17
  21. dodal/devices/eiger.py +1 -1
  22. dodal/devices/electron_analyser/__init__.py +8 -0
  23. dodal/devices/electron_analyser/abstract/__init__.py +28 -0
  24. dodal/devices/electron_analyser/abstract/base_detector.py +210 -0
  25. dodal/devices/electron_analyser/abstract/base_driver_io.py +121 -0
  26. dodal/devices/electron_analyser/{abstract_region.py → abstract/base_region.py} +2 -9
  27. dodal/devices/electron_analyser/specs/__init__.py +11 -0
  28. dodal/devices/electron_analyser/specs/detector.py +29 -0
  29. dodal/devices/electron_analyser/specs/driver_io.py +64 -0
  30. dodal/devices/electron_analyser/{specs_region.py → specs/region.py} +1 -1
  31. dodal/devices/electron_analyser/types.py +6 -0
  32. dodal/devices/electron_analyser/util.py +13 -0
  33. dodal/devices/electron_analyser/vgscienta/__init__.py +12 -0
  34. dodal/devices/electron_analyser/vgscienta/detector.py +36 -0
  35. dodal/devices/electron_analyser/vgscienta/driver_io.py +39 -0
  36. dodal/devices/electron_analyser/{vgscienta_region.py → vgscienta/region.py} +1 -1
  37. dodal/devices/fast_grid_scan.py +7 -9
  38. dodal/devices/i03/__init__.py +3 -0
  39. dodal/devices/i04/__init__.py +3 -0
  40. dodal/devices/i04/constants.py +9 -0
  41. dodal/devices/i04/murko_results.py +195 -0
  42. dodal/devices/i10/diagnostics.py +9 -61
  43. dodal/devices/i24/focus_mirrors.py +9 -13
  44. dodal/devices/i24/pilatus_metadata.py +9 -9
  45. dodal/devices/i24/pmac.py +19 -14
  46. dodal/devices/{i03 → mx_phase1}/beamstop.py +6 -12
  47. dodal/devices/oav/oav_calculations.py +2 -2
  48. dodal/devices/oav/oav_detector.py +32 -22
  49. dodal/devices/oav/utils.py +2 -2
  50. dodal/devices/p99/andor2_point.py +41 -0
  51. dodal/devices/positioner.py +49 -0
  52. dodal/devices/tetramm.py +5 -2
  53. dodal/devices/util/adjuster_plans.py +1 -1
  54. dodal/devices/zebra/zebra_constants_mapping.py +1 -1
  55. dodal/devices/zocalo/__init__.py +0 -3
  56. dodal/devices/zocalo/zocalo_results.py +6 -32
  57. dodal/log.py +14 -14
  58. dodal/plan_stubs/electron_analyser/__init__.py +3 -0
  59. dodal/plan_stubs/electron_analyser/{configure_controller.py → configure_driver.py} +30 -18
  60. dodal/common/signal_utils.py +0 -88
  61. dodal/devices/electron_analyser/abstract_analyser_io.py +0 -47
  62. dodal/devices/electron_analyser/specs_analyser_io.py +0 -19
  63. dodal/devices/electron_analyser/vgscienta_analyser_io.py +0 -26
  64. dodal/devices/logging_ophyd_device.py +0 -17
  65. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/entry_points.txt +0 -0
  66. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/licenses/LICENSE +0 -0
  67. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
1
- from bluesky.protocols import Movable
2
1
  from ophyd_async.core import (
3
- AsyncStatus,
4
2
  Device,
5
3
  StandardReadable,
6
4
  StrictEnum,
@@ -13,7 +11,6 @@ from ophyd_async.epics.core import (
13
11
  epics_signal_r,
14
12
  epics_signal_rw,
15
13
  )
16
- from ophyd_async.epics.motor import Motor
17
14
 
18
15
  from dodal.devices.current_amplifiers import (
19
16
  CurrentAmpDet,
@@ -23,6 +20,7 @@ from dodal.devices.current_amplifiers import (
23
20
  FemtoDDPCA,
24
21
  StruckScaler,
25
22
  )
23
+ from dodal.devices.positioner import create_positioner
26
24
 
27
25
 
28
26
  class D3Position(StrictEnum):
@@ -70,36 +68,6 @@ class InOutReadBackTable(StrictEnum):
70
68
  OUT_OF_BEAM = "Out of Beam"
71
69
 
72
70
 
73
- class Positioner(StandardReadable, Movable):
74
- """1D stage with a enum table to select positions."""
75
-
76
- def __init__(
77
- self,
78
- prefix: str,
79
- positioner_enum: type[StrictEnum],
80
- positioner_suffix: str = "",
81
- Positioner_pv_suffix: str = ":MP:SELECT",
82
- name: str = "",
83
- ) -> None:
84
- self._stage_motion = Motor(prefix=prefix + positioner_suffix)
85
- with self.add_children_as_readables(Format.CONFIG_SIGNAL):
86
- self.stage_position = epics_signal_rw(
87
- positioner_enum,
88
- read_pv=prefix + positioner_suffix + Positioner_pv_suffix,
89
- )
90
- super().__init__(name=name)
91
- self.positioner_enum = positioner_enum
92
-
93
- @AsyncStatus.wrap
94
- async def set(self, value: StrictEnum) -> None:
95
- if value in self.positioner_enum:
96
- await self.stage_position.set(value=value)
97
- else:
98
- raise ValueError(
99
- f"{value} is not an allow position. Position must be: {self.positioner_enum}"
100
- )
101
-
102
-
103
71
  class I10PneumaticStage(StandardReadable):
104
72
  """Pneumatic stage only has two real positions in or out.
105
73
  Use for fluorescent screen which can be insert into the x-ray beam.
@@ -154,16 +122,13 @@ class FullDiagnostic(Device):
154
122
  self,
155
123
  prefix: str,
156
124
  positioner_enum: type[StrictEnum],
157
- positioner_suffix: str = "",
158
- Positioner_pv_suffix: str = ":MP:SELECT",
125
+ positioner_suffix: str,
159
126
  cam_infix: str = "DCAM:",
160
127
  name: str = "",
161
128
  ) -> None:
162
- self.positioner = Positioner(
163
- prefix=prefix,
164
- positioner_enum=positioner_enum,
165
- positioner_suffix=positioner_suffix,
166
- Positioner_pv_suffix=Positioner_pv_suffix,
129
+ self.positioner = create_positioner(
130
+ positioner_enum,
131
+ prefix + positioner_suffix,
167
132
  )
168
133
  self.screen = ScreenCam(
169
134
  prefix,
@@ -185,28 +150,11 @@ class I10Diagnostic(Device):
185
150
  positioner_suffix="DET:X",
186
151
  )
187
152
  self.d4 = ScreenCam(prefix=prefix + "PHDGN-04:")
188
- self.d5 = Positioner(
189
- prefix=prefix + "IONC-01:",
190
- positioner_enum=D5Position,
191
- positioner_suffix="Y",
192
- )
193
-
194
- self.d5A = Positioner(
195
- prefix=prefix + "PHDGN-06:",
196
- positioner_enum=D5APosition,
197
- positioner_suffix="DET:X",
198
- )
153
+ self.d5 = create_positioner(D5Position, f"{prefix}IONC-01:Y")
154
+ self.d5A = create_positioner(D5APosition, f"{prefix}PHDGN-06:DET:X")
155
+ self.d6 = FullDiagnostic(f"{prefix}PHDGN-05:", D6Position, "DET:X")
156
+ self.d7 = create_positioner(D7Position, f"{prefix}PHDGN-07:Y")
199
157
 
200
- self.d6 = FullDiagnostic(
201
- prefix=prefix + "PHDGN-05:",
202
- positioner_enum=D6Position,
203
- positioner_suffix="DET:X",
204
- )
205
- self.d7 = Positioner(
206
- prefix=prefix + "PHDGN-07:",
207
- positioner_enum=D7Position,
208
- positioner_suffix="Y",
209
- )
210
158
  super().__init__(name)
211
159
 
212
160
 
@@ -1,8 +1,6 @@
1
- from ophyd_async.core import StandardReadable, StrictEnum
1
+ from ophyd_async.core import StandardReadable, StrictEnum, derived_signal_r
2
2
  from ophyd_async.epics.core import epics_signal_rw
3
3
 
4
- from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
5
-
6
4
 
7
5
  class HFocusMode(StrictEnum):
8
6
  FOCUS_10 = "HMFMfocus10"
@@ -40,21 +38,19 @@ class FocusMirrorsMode(StandardReadable):
40
38
  self.vertical = epics_signal_rw(VFocusMode, prefix + "G0:TARGETAPPLY")
41
39
 
42
40
  with self.add_children_as_readables():
43
- self.beam_size_x = create_r_hardware_backed_soft_signal(
44
- int, self._get_beam_size_x, units="um"
41
+ self.beam_size_x = derived_signal_r(
42
+ self._get_beam_size_x, horizontal=self.horizontal, derived_units="um"
45
43
  )
46
- self.beam_size_y = create_r_hardware_backed_soft_signal(
47
- int, self._get_beam_size_y, units="um"
44
+ self.beam_size_y = derived_signal_r(
45
+ self._get_beam_size_y, vertical=self.vertical, derived_units="um"
48
46
  )
49
47
 
50
48
  super().__init__(name)
51
49
 
52
- async def _get_beam_size_x(self) -> int:
53
- h_mode = await self.horizontal.get_value()
54
- beam_x = BEAM_SIZES[h_mode.removeprefix("HMFM")][0]
50
+ def _get_beam_size_x(self, horizontal: HFocusMode) -> int:
51
+ beam_x = BEAM_SIZES[horizontal.removeprefix("HMFM")][0]
55
52
  return beam_x
56
53
 
57
- async def _get_beam_size_y(self) -> int:
58
- v_mode = await self.vertical.get_value()
59
- beam_y = BEAM_SIZES[v_mode.removeprefix("VMFM")][1]
54
+ def _get_beam_size_y(self, vertical: VFocusMode) -> int:
55
+ beam_y = BEAM_SIZES[vertical.removeprefix("VMFM")][1]
60
56
  return beam_y
@@ -2,11 +2,9 @@
2
2
 
3
3
  import re
4
4
 
5
- from ophyd_async.core import StandardReadable
5
+ from ophyd_async.core import StandardReadable, derived_signal_r
6
6
  from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
7
7
 
8
- from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
9
-
10
8
 
11
9
  class PilatusMetadata(StandardReadable):
12
10
  def __init__(self, prefix: str, name: str = "") -> None:
@@ -14,21 +12,23 @@ class PilatusMetadata(StandardReadable):
14
12
  self.template = epics_signal_r(str, prefix + "cam1:FileTemplate_RBV")
15
13
  self.filenumber = epics_signal_r(int, prefix + "cam1:FileNumber_RBV")
16
14
  with self.add_children_as_readables():
17
- self.filename_template = create_r_hardware_backed_soft_signal(
18
- str, self._get_full_filename_template
15
+ self.filename_template = derived_signal_r(
16
+ self._get_full_filename_template,
17
+ filename=self.filename,
18
+ filename_template=self.template,
19
+ file_number=self.filenumber,
19
20
  )
20
21
  super().__init__(name)
21
22
 
22
- async def _get_full_filename_template(self) -> str:
23
+ def _get_full_filename_template(
24
+ self, filename: str, filename_template: str, file_number: int
25
+ ) -> str:
23
26
  """
24
27
  Get the template file path by querying the detector PVs.
25
28
  Mirror the construction that the PPU does.
26
29
 
27
30
  Returns: A template string, with the image numbers replaced with '#'
28
31
  """
29
- filename = await self.filename.get_value()
30
- filename_template = await self.template.get_value()
31
- file_number = await self.filenumber.get_value()
32
32
  # Exploit fact that passing negative numbers will put the - before the 0's
33
33
  expected_filename = str(
34
34
  filename_template % (filename, f"{file_number:05d}_", -9)
dodal/devices/i24/pmac.py CHANGED
@@ -10,6 +10,7 @@ from ophyd_async.core import (
10
10
  SignalR,
11
11
  SignalRW,
12
12
  StandardReadable,
13
+ observe_signals_value,
13
14
  soft_signal_rw,
14
15
  wait_for_value,
15
16
  )
@@ -120,15 +121,16 @@ class ProgramRunner(Device, Flyable):
120
121
  self,
121
122
  pmac_str_sig: SignalRW,
122
123
  status_sig: SignalR,
124
+ counter_sig: SignalR,
123
125
  prog_num_sig: SignalRW,
124
- collection_time_sig: SignalRW,
126
+ counter_time_sig: SignalRW,
125
127
  name: str = "",
126
128
  ) -> None:
127
129
  self._signal_ref = Reference(pmac_str_sig)
128
130
  self._status_ref = Reference(status_sig)
131
+ self._counter_ref = Reference(counter_sig)
129
132
  self._prog_num_ref = Reference(prog_num_sig)
130
-
131
- self._collection_time_ref = Reference(collection_time_sig)
133
+ self._counter_time_ref = Reference(counter_time_sig)
132
134
 
133
135
  super().__init__(name)
134
136
 
@@ -151,16 +153,18 @@ class ProgramRunner(Device, Flyable):
151
153
 
152
154
  @AsyncStatus.wrap
153
155
  async def complete(self):
154
- """Stop collecting when the scan status PV goes to 0.
155
-
156
- Args:
157
- complete_time (float): total time required by the collection to \
158
- finish correctly.
156
+ """Stop collecting when the scan status PV goes to 0 or when counter PV hasn't \
157
+ updated for 30 seconds.
159
158
  """
160
- scan_complete_time = await self._collection_time_ref().get_value()
161
- await wait_for_value(
162
- self._status_ref(), ScanState.DONE, timeout=scan_complete_time
163
- )
159
+ counter_time = await self._counter_time_ref().get_value()
160
+ async for signal, value in observe_signals_value(
161
+ self._status_ref(),
162
+ self._counter_ref(),
163
+ timeout=counter_time,
164
+ ):
165
+ if signal is self._status_ref():
166
+ if value == ScanState.DONE:
167
+ break
164
168
 
165
169
 
166
170
  class ProgramAbort(Triggerable):
@@ -217,13 +221,14 @@ class PMAC(StandardReadable):
217
221
  # A couple of soft signals for running a collection: program number to send to
218
222
  # the PMAC_STRING and expected collection time.
219
223
  self.program_number = soft_signal_rw(int)
220
- self.collection_time = soft_signal_rw(float, initial_value=600.0, units="s")
224
+ self.counter_time = soft_signal_rw(float, initial_value=30.0, units="s")
221
225
 
222
226
  self.run_program = ProgramRunner(
223
227
  self.pmac_string,
224
228
  self.scanstatus,
229
+ self.counter,
225
230
  self.program_number,
226
- self.collection_time,
231
+ self.counter_time,
227
232
  )
228
233
  self.abort_program = ProgramAbort(self.pmac_string, self.scanstatus)
229
234
 
@@ -1,11 +1,9 @@
1
- from asyncio import gather
2
1
  from math import isclose
3
2
 
4
- from ophyd_async.core import StandardReadable, StrictEnum
3
+ from ophyd_async.core import StandardReadable, StrictEnum, derived_signal_r
5
4
  from ophyd_async.epics.motor import Motor
6
5
 
7
6
  from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters
8
- from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
9
7
 
10
8
 
11
9
  class BeamstopPositions(StrictEnum):
@@ -33,7 +31,7 @@ class BeamstopPositions(StrictEnum):
33
31
 
34
32
  class Beamstop(StandardReadable):
35
33
  """
36
- Beamstop for I03.
34
+ Beamstop for I03 and I04.
37
35
 
38
36
  Attributes:
39
37
  x: beamstop x position in mm
@@ -53,8 +51,8 @@ class Beamstop(StandardReadable):
53
51
  self.x_mm = Motor(prefix + "X")
54
52
  self.y_mm = Motor(prefix + "Y")
55
53
  self.z_mm = Motor(prefix + "Z")
56
- self.selected_pos = create_r_hardware_backed_soft_signal(
57
- BeamstopPositions, self._get_selected_position
54
+ self.selected_pos = derived_signal_r(
55
+ self._get_selected_position, x=self.x_mm, y=self.y_mm, z=self.z_mm
58
56
  )
59
57
 
60
58
  self._in_beam_xyz_mm = [
@@ -68,12 +66,8 @@ class Beamstop(StandardReadable):
68
66
 
69
67
  super().__init__(name)
70
68
 
71
- async def _get_selected_position(self) -> BeamstopPositions:
72
- current_pos = await gather(
73
- self.x_mm.user_readback.get_value(),
74
- self.y_mm.user_readback.get_value(),
75
- self.z_mm.user_readback.get_value(),
76
- )
69
+ def _get_selected_position(self, x: float, y: float, z: float) -> BeamstopPositions:
70
+ current_pos = [x, y, z]
77
71
  if all(
78
72
  isclose(axis_pos, axis_in_beam, abs_tol=axis_tolerance)
79
73
  for axis_pos, axis_in_beam, axis_tolerance in zip(
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
 
3
3
 
4
- def camera_coordinates_to_xyz(
4
+ def camera_coordinates_to_xyz_mm(
5
5
  horizontal: float,
6
6
  vertical: float,
7
7
  omega: float,
@@ -9,7 +9,7 @@ def camera_coordinates_to_xyz(
9
9
  microns_per_j_pixel: float,
10
10
  ) -> np.ndarray:
11
11
  """
12
- Converts from (horizontal,vertical) pixel measurements from the OAV camera into to (x, y, z) motor coordinates in millmeters.
12
+ Converts from (horizontal,vertical) pixel measurements from the OAV camera into to (x, y, z) motor coordinates in millimetres.
13
13
  For an overview of the coordinate system for I03 see https://github.com/DiamondLightSource/hyperion/wiki/Gridscan-Coordinate-System.
14
14
 
15
15
  Args:
@@ -6,10 +6,11 @@ from ophyd_async.core import (
6
6
  AsyncStatus,
7
7
  LazyMock,
8
8
  StandardReadable,
9
+ derived_signal_r,
10
+ soft_signal_rw,
9
11
  )
10
12
  from ophyd_async.epics.core import epics_signal_rw
11
13
 
12
- from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
13
14
  from dodal.devices.areadetector.plugins.CAM import Cam
14
15
  from dodal.devices.oav.oav_parameters import DEFAULT_OAV_WINDOW, OAVConfig
15
16
  from dodal.devices.oav.snapshots.snapshot import Snapshot
@@ -60,49 +61,58 @@ class OAV(StandardReadable):
60
61
  self.zoom_controller = ZoomController(f"{_bl_prefix}-EA-OAV-01:FZOOM:", name)
61
62
 
62
63
  self.cam = Cam(f"{prefix}CAM:", name=name)
63
-
64
64
  with self.add_children_as_readables():
65
65
  self.grid_snapshot = SnapshotWithGrid(f"{prefix}MJPG:", name)
66
- self.microns_per_pixel_x = create_r_hardware_backed_soft_signal(
67
- float,
68
- lambda: self._get_microns_per_pixel(Coords.X),
66
+
67
+ self.sizes = [self.grid_snapshot.x_size, self.grid_snapshot.y_size]
68
+
69
+ with self.add_children_as_readables():
70
+ self.microns_per_pixel_x = derived_signal_r(
71
+ self._get_microns_per_pixel,
72
+ zoom_level=self.zoom_controller.level,
73
+ size=self.sizes[Coords.X],
74
+ coord=soft_signal_rw(datatype=int, initial_value=Coords.X.value),
69
75
  )
70
- self.microns_per_pixel_y = create_r_hardware_backed_soft_signal(
71
- float,
72
- lambda: self._get_microns_per_pixel(Coords.Y),
76
+ self.microns_per_pixel_y = derived_signal_r(
77
+ self._get_microns_per_pixel,
78
+ zoom_level=self.zoom_controller.level,
79
+ size=self.sizes[Coords.Y],
80
+ coord=soft_signal_rw(datatype=int, initial_value=Coords.Y.value),
73
81
  )
74
- self.beam_centre_i = create_r_hardware_backed_soft_signal(
75
- int, lambda: self._get_beam_position(Coords.X)
82
+ self.beam_centre_i = derived_signal_r(
83
+ self._get_beam_position,
84
+ zoom_level=self.zoom_controller.level,
85
+ size=self.sizes[Coords.X],
86
+ coord=soft_signal_rw(datatype=int, initial_value=Coords.X.value),
76
87
  )
77
- self.beam_centre_j = create_r_hardware_backed_soft_signal(
78
- int, lambda: self._get_beam_position(Coords.Y)
88
+ self.beam_centre_j = derived_signal_r(
89
+ self._get_beam_position,
90
+ zoom_level=self.zoom_controller.level,
91
+ size=self.sizes[Coords.Y],
92
+ coord=soft_signal_rw(datatype=int, initial_value=Coords.Y.value),
79
93
  )
80
94
  self.snapshot = Snapshot(
81
95
  f"{self._prefix}MJPG:",
82
96
  self._name,
83
97
  )
84
98
 
85
- self.sizes = [self.grid_snapshot.x_size, self.grid_snapshot.y_size]
86
-
87
99
  super().__init__(name)
88
100
 
89
- async def _read_current_zoom(self) -> str:
90
- _zoom = await self.zoom_controller.level.get_value()
101
+ def _read_current_zoom(self, _zoom: str) -> str:
91
102
  return _get_correct_zoom_string(_zoom)
92
103
 
93
- async def _get_microns_per_pixel(self, coord: int) -> float:
104
+ def _get_microns_per_pixel(self, zoom_level: str, size: int, coord: int) -> float:
94
105
  """Extracts the microns per x pixel and y pixel for a given zoom level."""
95
- _zoom = await self._read_current_zoom()
106
+ _zoom = self._read_current_zoom(zoom_level)
96
107
  value = self.parameters[_zoom].microns_per_pixel[coord]
97
- size = await self.sizes[coord].get_value()
98
108
  return value * DEFAULT_OAV_WINDOW[coord] / size
99
109
 
100
- async def _get_beam_position(self, coord: int) -> int:
110
+ def _get_beam_position(self, zoom_level: str, size: int, coord: int) -> int:
101
111
  """Extracts the beam location in pixels `xCentre` `yCentre`, for a requested \
102
112
  zoom level. """
103
- _zoom = await self._read_current_zoom()
113
+ _zoom = self._read_current_zoom(zoom_level)
104
114
  value = self.parameters[_zoom].crosshair[coord]
105
- size = await self.sizes[coord].get_value()
115
+
106
116
  return int(value * size / DEFAULT_OAV_WINDOW[coord])
107
117
 
108
118
  async def connect(
@@ -7,7 +7,7 @@ from bluesky.utils import Msg
7
7
 
8
8
  from dodal.devices.oav.oav_calculations import (
9
9
  calculate_beam_distance,
10
- camera_coordinates_to_xyz,
10
+ camera_coordinates_to_xyz_mm,
11
11
  )
12
12
  from dodal.devices.oav.oav_detector import OAV
13
13
  from dodal.devices.oav.pin_image_recognition import PinTipDetection
@@ -90,7 +90,7 @@ def calculate_x_y_z_of_pixel(
90
90
  """Get the x, y, z position of a pixel in mm"""
91
91
  beam_distance_px: Pixel = calculate_beam_distance(beam_centre, *pixel)
92
92
 
93
- return current_x_y_z + camera_coordinates_to_xyz(
93
+ return current_x_y_z + camera_coordinates_to_xyz_mm(
94
94
  beam_distance_px[0],
95
95
  beam_distance_px[1],
96
96
  current_omega,
@@ -0,0 +1,41 @@
1
+ from ophyd_async.core import (
2
+ StandardReadableFormat,
3
+ )
4
+ from ophyd_async.epics.adandor import Andor2DriverIO
5
+ from ophyd_async.epics.adcore import NDPluginBaseIO, SingleTriggerDetector
6
+ from ophyd_async.epics.core import epics_signal_r
7
+
8
+
9
+ class Andor2Point(SingleTriggerDetector):
10
+ """Using the andor2 as if it is a massive point detector, read the read uncached
11
+ value after a picture is taken."""
12
+
13
+ def __init__(
14
+ self,
15
+ prefix: str,
16
+ drv_suffix: str,
17
+ read_uncached: dict[str, str],
18
+ name: str = "",
19
+ plugins: dict[str, NDPluginBaseIO] | None = None,
20
+ ) -> None:
21
+ """
22
+ Parameters
23
+ ----------
24
+ prefix: str,
25
+ Beamline camera pv
26
+ drv_suffix : str,
27
+ Camera pv suffix
28
+ read_uncached: dict[str,str]
29
+ A dictionary contains the name and the pv suffix for the statistic plugin.
30
+ name: str:
31
+ Name of the device.
32
+ plugins:: Optional[dict[str, NDPluginBaseIO] | None
33
+ Dictionary containing plugin that are forward to the base class.
34
+ """
35
+ with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
36
+ for k, v in read_uncached.items():
37
+ setattr(self, k, epics_signal_r(float, prefix + v))
38
+
39
+ super().__init__(
40
+ drv=Andor2DriverIO(prefix + drv_suffix), name=name, plugins=plugins
41
+ )
@@ -0,0 +1,49 @@
1
+ from typing import TypeVar
2
+
3
+ from bluesky.protocols import Movable
4
+ from ophyd_async.core import (
5
+ AsyncStatus,
6
+ StandardReadable,
7
+ StrictEnum,
8
+ )
9
+ from ophyd_async.epics.core import (
10
+ epics_signal_rw,
11
+ )
12
+ from ophyd_async.epics.motor import Motor
13
+
14
+ T = TypeVar("T", bound=StrictEnum)
15
+
16
+
17
+ class Positioner1D(StandardReadable, Movable[T]):
18
+ """1D stage with a enum table to select positions.
19
+
20
+ Use this when writing a device with an EPICS positioner on a single axis.
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ prefix: str,
26
+ datatype: type[StrictEnum],
27
+ positioner_pv_suffix: str = ":MP:SELECT",
28
+ name: str = "",
29
+ ) -> None:
30
+ self._stage_motion = Motor(prefix=prefix)
31
+ with self.add_children_as_readables():
32
+ self.stage_position = epics_signal_rw(
33
+ datatype,
34
+ read_pv=prefix + positioner_pv_suffix,
35
+ )
36
+ super().__init__(name=name)
37
+
38
+ @AsyncStatus.wrap
39
+ async def set(self, value: T) -> None:
40
+ await self.stage_position.set(value=value)
41
+
42
+
43
+ def create_positioner(
44
+ datatype: type[T],
45
+ prefix,
46
+ positioner_pv_suffix: str = ":MP:SELECT",
47
+ name: str = "",
48
+ ) -> Positioner1D[T]:
49
+ return Positioner1D[datatype](prefix, datatype, positioner_pv_suffix, name)
dodal/devices/tetramm.py CHANGED
@@ -131,11 +131,14 @@ class TetrammController(DetectorController):
131
131
  )
132
132
 
133
133
  async def arm(self):
134
- self._arm_status = await set_and_wait_for_value(self._drv.acquire, True)
134
+ self._arm_status = await set_and_wait_for_value(
135
+ self._drv.acquire, True, wait_for_set_completion=False
136
+ )
135
137
 
136
138
  async def wait_for_idle(self):
137
- if self._arm_status:
139
+ if self._arm_status and not self._arm_status.done:
138
140
  await self._arm_status
141
+ self._arm_status = None
139
142
 
140
143
  def _validate_trigger(self, trigger: DetectorTrigger) -> None:
141
144
  supported_trigger_types = {
@@ -20,6 +20,6 @@ def lookup_table_adjuster(
20
20
  def adjust(group=None) -> Generator[Msg, None, None]:
21
21
  setpoint = lookup_table(input)
22
22
  LOGGER.info(f"lookup_table_adjuster setting {output_device.name} to {setpoint}")
23
- yield from bps.abs_set(output_device, setpoint, group=group) # type: ignore # See: https://github.com/DiamondLightSource/dodal/issues/827
23
+ yield from bps.abs_set(output_device, setpoint, group=group)
24
24
 
25
25
  return adjust
@@ -89,7 +89,7 @@ class ZebraMapping(ZebraMappingValidations):
89
89
  # Which of the Zebra's four AND gates is used to control the automatic shutter, if it's being used.
90
90
  # After defining, the correct GateControl device can be accessed with, eg,
91
91
  # zebra.logic_gates.and_gates[zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER]. Set to -1 if not being used.
92
- AND_GATE_FOR_AUTO_SHUTTER: int = Field(default=-1, ge=-1, le=4)
92
+ AND_GATE_FOR_AUTO_SHUTTER: int = Field(default=2, ge=-1, le=4)
93
93
 
94
94
 
95
95
  class UnmappedZebraException(Exception):
@@ -12,10 +12,7 @@ __all__ = [
12
12
  "XrcResult",
13
13
  "ZocaloTrigger",
14
14
  "get_full_processing_results",
15
- "ZOCALO_READING_PLAN_NAME",
16
15
  "NoResultsFromZocalo",
17
16
  "NoZocaloSubscription",
18
17
  "ZocaloStartInfo",
19
18
  ]
20
-
21
- ZOCALO_READING_PLAN_NAME = "zocalo reading"
@@ -1,7 +1,6 @@
1
1
  import asyncio
2
2
  from collections.abc import Generator, Sequence
3
3
  from enum import Enum
4
- from inspect import get_annotations
5
4
  from queue import Empty, Queue
6
5
  from typing import Any, TypedDict
7
6
 
@@ -47,7 +46,6 @@ class ZocaloSource(str, Enum):
47
46
 
48
47
  DEFAULT_TIMEOUT = 180
49
48
  DEFAULT_SORT_KEY = SortKeys.max_count
50
- ZOCALO_READING_PLAN_NAME = "zocalo reading"
51
49
  CLEAR_QUEUE_WAIT_S = 2.0
52
50
  ZOCALO_STAGE_GROUP = "clear zocalo queue"
53
51
 
@@ -389,12 +387,12 @@ def get_full_processing_results(
389
387
  """A plan that will return the raw zocalo results, ranked in descending order according to the sort key.
390
388
  Returns empty list in the event no results found."""
391
389
  LOGGER.info("Retrieving raw zocalo processing results")
392
- com = yield from bps.rd(zocalo.centre_of_mass, default_value=[]) # type: ignore
393
- max_voxel = yield from bps.rd(zocalo.max_voxel, default_value=[]) # type: ignore
394
- max_count = yield from bps.rd(zocalo.max_count, default_value=[]) # type: ignore
395
- n_voxels = yield from bps.rd(zocalo.n_voxels, default_value=[]) # type: ignore
396
- total_count = yield from bps.rd(zocalo.total_count, default_value=[]) # type: ignore
397
- bounding_box = yield from bps.rd(zocalo.bounding_box, default_value=[]) # type: ignore
390
+ com = yield from bps.rd(zocalo.centre_of_mass, default_value=[])
391
+ max_voxel = yield from bps.rd(zocalo.max_voxel, default_value=[])
392
+ max_count = yield from bps.rd(zocalo.max_count, default_value=[])
393
+ n_voxels = yield from bps.rd(zocalo.n_voxels, default_value=[])
394
+ total_count = yield from bps.rd(zocalo.total_count, default_value=[])
395
+ bounding_box = yield from bps.rd(zocalo.bounding_box, default_value=[])
398
396
  return [
399
397
  _corrected_xrc_result(
400
398
  XrcResult(
@@ -410,27 +408,3 @@ def get_full_processing_results(
410
408
  com, max_voxel, max_count, n_voxels, total_count, bounding_box, strict=True
411
409
  )
412
410
  ]
413
-
414
-
415
- def get_processing_results_from_event(
416
- device_name: str, doc: dict
417
- ) -> Sequence[XrcResult]:
418
- """
419
- Decode an event document into the corresponding x-ray centring results
420
-
421
- Args:
422
- doc A bluesky event document containing the signals read from the ZocaloResults
423
- device_name The device name prefix to prepend to the document keys
424
-
425
- Returns:
426
- The list of XrcResults decoded from the event document
427
- """
428
- results_keys = get_annotations(XrcResult).keys()
429
- results_dict = {k: doc["data"][f"{device_name}-{k}"] for k in results_keys}
430
- results_values = [results_dict[k].tolist() for k in results_keys]
431
-
432
- def create_result(*argv):
433
- kwargs = dict(zip(results_keys, argv, strict=False))
434
- return XrcResult(**kwargs)
435
-
436
- return list(map(create_result, *results_values))