dls-dodal 1.34.1__py3-none-any.whl → 1.35.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 (71) hide show
  1. {dls_dodal-1.34.1.dist-info → dls_dodal-1.35.0.dist-info}/METADATA +2 -2
  2. {dls_dodal-1.34.1.dist-info → dls_dodal-1.35.0.dist-info}/RECORD +70 -67
  3. {dls_dodal-1.34.1.dist-info → dls_dodal-1.35.0.dist-info}/WHEEL +1 -1
  4. dodal/_version.py +2 -2
  5. dodal/beamlines/i22.py +24 -11
  6. dodal/beamlines/i24.py +4 -4
  7. dodal/beamlines/p38.py +23 -11
  8. dodal/common/beamlines/beamline_utils.py +1 -2
  9. dodal/common/crystal_metadata.py +61 -0
  10. dodal/common/signal_utils.py +10 -14
  11. dodal/devices/CTAB.py +1 -1
  12. dodal/devices/aperture.py +1 -1
  13. dodal/devices/aperturescatterguard.py +20 -8
  14. dodal/devices/apple2_undulator.py +30 -29
  15. dodal/devices/areadetector/plugins/CAM.py +3 -5
  16. dodal/devices/areadetector/plugins/MJPG.py +1 -1
  17. dodal/devices/attenuator.py +1 -1
  18. dodal/devices/backlight.py +4 -5
  19. dodal/devices/cryostream.py +3 -5
  20. dodal/devices/dcm.py +26 -2
  21. dodal/devices/detector/detector_motion.py +3 -5
  22. dodal/devices/diamond_filter.py +3 -4
  23. dodal/devices/fast_grid_scan.py +1 -1
  24. dodal/devices/fluorescence_detector_motion.py +5 -7
  25. dodal/devices/focusing_mirror.py +12 -11
  26. dodal/devices/hutch_shutter.py +4 -5
  27. dodal/devices/i10/i10_apple2.py +20 -19
  28. dodal/devices/i10/i10_setting_data.py +2 -2
  29. dodal/devices/i22/dcm.py +43 -75
  30. dodal/devices/i22/fswitch.py +5 -5
  31. dodal/devices/i24/aperture.py +3 -5
  32. dodal/devices/i24/beamstop.py +3 -5
  33. dodal/devices/i24/dcm.py +1 -1
  34. dodal/devices/i24/dual_backlight.py +4 -6
  35. dodal/devices/i24/pmac.py +35 -46
  36. dodal/devices/i24/vgonio.py +16 -0
  37. dodal/devices/ipin.py +5 -3
  38. dodal/devices/linkam3.py +7 -7
  39. dodal/devices/oav/oav_detector.py +3 -3
  40. dodal/devices/oav/oav_to_redis_forwarder.py +8 -7
  41. dodal/devices/oav/pin_image_recognition/__init__.py +9 -7
  42. dodal/devices/oav/snapshots/grid_overlay.py +16 -16
  43. dodal/devices/oav/snapshots/snapshot_with_beam_centre.py +5 -5
  44. dodal/devices/oav/snapshots/snapshot_with_grid.py +6 -6
  45. dodal/devices/oav/utils.py +2 -2
  46. dodal/devices/p99/sample_stage.py +3 -5
  47. dodal/devices/pgm.py +5 -6
  48. dodal/devices/qbpm.py +1 -1
  49. dodal/devices/robot.py +3 -3
  50. dodal/devices/smargon.py +1 -1
  51. dodal/devices/synchrotron.py +9 -4
  52. dodal/devices/tetramm.py +7 -7
  53. dodal/devices/thawer.py +13 -7
  54. dodal/devices/undulator.py +5 -5
  55. dodal/devices/util/epics_util.py +1 -1
  56. dodal/devices/watsonmarlow323_pump.py +45 -0
  57. dodal/devices/webcam.py +9 -2
  58. dodal/devices/xbpm_feedback.py +3 -5
  59. dodal/devices/xspress3/xspress3.py +8 -9
  60. dodal/devices/xspress3/xspress3_channel.py +3 -5
  61. dodal/devices/zebra.py +7 -6
  62. dodal/devices/zebra_controlled_shutter.py +5 -6
  63. dodal/devices/zocalo/__init__.py +2 -2
  64. dodal/devices/zocalo/zocalo_constants.py +3 -0
  65. dodal/devices/zocalo/zocalo_interaction.py +2 -1
  66. dodal/devices/zocalo/zocalo_results.py +92 -79
  67. dodal/utils.py +4 -0
  68. dodal/devices/i24/i24_vgonio.py +0 -17
  69. {dls_dodal-1.34.1.dist-info → dls_dodal-1.35.0.dist-info}/LICENSE +0 -0
  70. {dls_dodal-1.34.1.dist-info → dls_dodal-1.35.0.dist-info}/entry_points.txt +0 -0
  71. {dls_dodal-1.34.1.dist-info → dls_dodal-1.35.0.dist-info}/top_level.txt +0 -0
dodal/devices/i22/dcm.py CHANGED
@@ -1,32 +1,24 @@
1
1
  import time
2
- from collections.abc import Sequence
3
- from dataclasses import dataclass
4
- from typing import Literal
5
2
 
3
+ import numpy as np
6
4
  from bluesky.protocols import Reading
7
5
  from event_model.documents.event_descriptor import DataKey
8
- from ophyd_async.core import ConfigSignal, StandardReadable, soft_signal_r_and_setter
6
+ from ophyd_async.core import (
7
+ Array1D,
8
+ StandardReadable,
9
+ StandardReadableFormat,
10
+ soft_signal_r_and_setter,
11
+ )
12
+ from ophyd_async.epics.core import epics_signal_r
9
13
  from ophyd_async.epics.motor import Motor
10
- from ophyd_async.epics.signal import epics_signal_r
14
+
15
+ from dodal.common.crystal_metadata import CrystalMetadata
11
16
 
12
17
  # Conversion constant for energy and wavelength, taken from the X-Ray data booklet
13
18
  # Converts between energy in KeV and wavelength in angstrom
14
19
  _CONVERSION_CONSTANT = 12.3984
15
20
 
16
21
 
17
- @dataclass(frozen=True, unsafe_hash=True)
18
- class CrystalMetadata:
19
- """
20
- Metadata used in the NeXus format,
21
- see https://manual.nexusformat.org/classes/base_classes/NXcrystal.html
22
- """
23
-
24
- usage: Literal["Bragg", "Laue"] | None = None
25
- type: str | None = None
26
- reflection: tuple[int, int, int] | None = None
27
- d_spacing: tuple[float, str] | None = None
28
-
29
-
30
22
  class DoubleCrystalMonochromator(StandardReadable):
31
23
  """
32
24
  A double crystal monochromator (DCM), used to select the energy of the beam.
@@ -40,8 +32,8 @@ class DoubleCrystalMonochromator(StandardReadable):
40
32
  def __init__(
41
33
  self,
42
34
  temperature_prefix: str,
43
- crystal_1_metadata: CrystalMetadata | None = None,
44
- crystal_2_metadata: CrystalMetadata | None = None,
35
+ crystal_1_metadata: CrystalMetadata,
36
+ crystal_2_metadata: CrystalMetadata,
45
37
  prefix: str = "",
46
38
  name: str = "",
47
39
  ) -> None:
@@ -69,61 +61,37 @@ class DoubleCrystalMonochromator(StandardReadable):
69
61
 
70
62
  # Soft metadata
71
63
  # If supplied include crystal details in output of read_configuration
72
- crystal_1_metadata = crystal_1_metadata or CrystalMetadata()
73
- crystal_2_metadata = crystal_2_metadata or CrystalMetadata()
74
- with self.add_children_as_readables(ConfigSignal):
75
- if crystal_1_metadata.usage is not None:
76
- self.crystal_1_usage, _ = soft_signal_r_and_setter(
77
- str, initial_value=crystal_1_metadata.usage
78
- )
79
- else:
80
- self.crystal_1_usage = None
81
- if crystal_1_metadata.type is not None:
82
- self.crystal_1_type, _ = soft_signal_r_and_setter(
83
- str, initial_value=crystal_1_metadata.type
84
- )
85
- else:
86
- self.crystal_1_type = None
87
- if crystal_1_metadata.reflection is not None:
88
- self.crystal_1_reflection, _ = soft_signal_r_and_setter(
89
- Sequence[int], initial_value=list(crystal_1_metadata.reflection)
90
- )
91
- else:
92
- self.crystal_1_reflection = None
93
- if crystal_1_metadata.d_spacing is not None:
94
- self.crystal_1_d_spacing, _ = soft_signal_r_and_setter(
95
- float,
96
- initial_value=crystal_1_metadata.d_spacing[0],
97
- units=crystal_1_metadata.d_spacing[1],
98
- )
99
- else:
100
- self.crystal_1_d_spacing = None
101
- if crystal_2_metadata.usage is not None:
102
- self.crystal_2_usage, _ = soft_signal_r_and_setter(
103
- str, initial_value=crystal_2_metadata.usage
104
- )
105
- else:
106
- self.crystal_2_usage = None
107
- if crystal_2_metadata.type is not None:
108
- self.crystal_2_type, _ = soft_signal_r_and_setter(
109
- str, initial_value=crystal_2_metadata.type
110
- )
111
- else:
112
- self.crystal_2_type = None
113
- if crystal_2_metadata.reflection is not None:
114
- self.crystal_2_reflection, _ = soft_signal_r_and_setter(
115
- Sequence[int], initial_value=list(crystal_2_metadata.reflection)
116
- )
117
- else:
118
- self.crystal_2_reflection = None
119
- if crystal_2_metadata.d_spacing is not None:
120
- self.crystal_2_d_spacing, _ = soft_signal_r_and_setter(
121
- float,
122
- initial_value=crystal_2_metadata.d_spacing[0],
123
- units=crystal_2_metadata.d_spacing[1],
124
- )
125
- else:
126
- self.crystal_2_d_spacing = None
64
+ with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
65
+ self.crystal_1_usage, _ = soft_signal_r_and_setter(
66
+ str, initial_value=crystal_1_metadata.usage
67
+ )
68
+ self.crystal_1_type, _ = soft_signal_r_and_setter(
69
+ str, initial_value=crystal_1_metadata.type
70
+ )
71
+ self.crystal_1_reflection, _ = soft_signal_r_and_setter(
72
+ Array1D[np.int32],
73
+ initial_value=np.array(crystal_1_metadata.reflection),
74
+ )
75
+ self.crystal_1_d_spacing, _ = soft_signal_r_and_setter(
76
+ float,
77
+ initial_value=crystal_1_metadata.d_spacing[0],
78
+ units=crystal_1_metadata.d_spacing[1],
79
+ )
80
+ self.crystal_2_usage, _ = soft_signal_r_and_setter(
81
+ str, initial_value=crystal_2_metadata.usage
82
+ )
83
+ self.crystal_2_type, _ = soft_signal_r_and_setter(
84
+ str, initial_value=crystal_2_metadata.type
85
+ )
86
+ self.crystal_2_reflection, _ = soft_signal_r_and_setter(
87
+ Array1D[np.int32],
88
+ initial_value=np.array(crystal_2_metadata.reflection),
89
+ )
90
+ self.crystal_2_d_spacing, _ = soft_signal_r_and_setter(
91
+ float,
92
+ initial_value=crystal_2_metadata.d_spacing[0],
93
+ units=crystal_2_metadata.d_spacing[1],
94
+ )
127
95
 
128
96
  super().__init__(name)
129
97
 
@@ -1,19 +1,19 @@
1
1
  import asyncio
2
2
  import time
3
- from enum import Enum
4
3
 
5
4
  from bluesky.protocols import Reading
6
5
  from event_model import DataKey
7
6
  from ophyd_async.core import (
8
- ConfigSignal,
9
7
  DeviceVector,
10
8
  StandardReadable,
9
+ StandardReadableFormat,
10
+ StrictEnum,
11
11
  soft_signal_r_and_setter,
12
12
  )
13
- from ophyd_async.epics.signal import epics_signal_r
13
+ from ophyd_async.epics.core import epics_signal_r
14
14
 
15
15
 
16
- class FilterState(str, Enum):
16
+ class FilterState(StrictEnum):
17
17
  """
18
18
  Note that the in/out here refers to the internal rocker
19
19
  position so a PV value of IN implies a filter OUT of beam
@@ -54,7 +54,7 @@ class FSwitch(StandardReadable):
54
54
  for i in range(FSwitch.NUM_FILTERS)
55
55
  }
56
56
  )
57
- with self.add_children_as_readables(ConfigSignal):
57
+ with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
58
58
  if lens_geometry is not None:
59
59
  self.lens_geometry, _ = soft_signal_r_and_setter(
60
60
  str, initial_value=lens_geometry
@@ -1,11 +1,9 @@
1
- from enum import Enum
2
-
3
- from ophyd_async.core import StandardReadable
1
+ from ophyd_async.core import StandardReadable, StrictEnum
2
+ from ophyd_async.epics.core import epics_signal_rw
4
3
  from ophyd_async.epics.motor import Motor
5
- from ophyd_async.epics.signal import epics_signal_rw
6
4
 
7
5
 
8
- class AperturePositions(str, Enum):
6
+ class AperturePositions(StrictEnum):
9
7
  IN = "In"
10
8
  OUT = "Out"
11
9
  ROBOT = "Robot"
@@ -1,11 +1,9 @@
1
- from enum import Enum
2
-
3
- from ophyd_async.core import StandardReadable
1
+ from ophyd_async.core import StandardReadable, StrictEnum
2
+ from ophyd_async.epics.core import epics_signal_rw
4
3
  from ophyd_async.epics.motor import Motor
5
- from ophyd_async.epics.signal import epics_signal_rw
6
4
 
7
5
 
8
- class BeamstopPositions(str, Enum):
6
+ class BeamstopPositions(StrictEnum):
9
7
  CHECK_BEAM = "CheckBeam"
10
8
  DATA_COLLECTION = "Data Collection"
11
9
  DATA_COLLECTION_FAR = "Data Collection Far"
dodal/devices/i24/dcm.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from ophyd_async.core import StandardReadable
2
+ from ophyd_async.epics.core import epics_signal_r
2
3
  from ophyd_async.epics.motor import Motor
3
- from ophyd_async.epics.signal import epics_signal_r
4
4
 
5
5
 
6
6
  class DCM(StandardReadable):
@@ -1,10 +1,8 @@
1
- from enum import Enum
1
+ from ophyd_async.core import AsyncStatus, StandardReadable, StrictEnum
2
+ from ophyd_async.epics.core import epics_signal_rw
2
3
 
3
- from ophyd_async.core import AsyncStatus, StandardReadable
4
- from ophyd_async.epics.signal import epics_signal_rw
5
4
 
6
-
7
- class BacklightPositions(str, Enum):
5
+ class BacklightPositions(StrictEnum):
8
6
  OUT = "Out"
9
7
  IN = "In"
10
8
  LOAD_CHECK = "LoadCheck"
@@ -12,7 +10,7 @@ class BacklightPositions(str, Enum):
12
10
  DIODE = "Diode"
13
11
 
14
12
 
15
- class LEDStatus(str, Enum):
13
+ class LEDStatus(StrictEnum):
16
14
  OFF = "OFF"
17
15
  ON = "ON"
18
16
 
dodal/devices/i24/pmac.py CHANGED
@@ -1,21 +1,20 @@
1
1
  from asyncio import sleep
2
2
  from enum import Enum, IntEnum
3
3
 
4
- from bluesky.protocols import Flyable, Triggerable
4
+ from bluesky.protocols import Flyable, Movable, Triggerable
5
5
  from ophyd_async.core import (
6
- CALCULATE_TIMEOUT,
7
6
  DEFAULT_TIMEOUT,
8
7
  AsyncStatus,
9
- SignalBackend,
8
+ Device,
9
+ Reference,
10
10
  SignalR,
11
11
  SignalRW,
12
- SoftSignalBackend,
13
12
  StandardReadable,
14
13
  soft_signal_rw,
15
14
  wait_for_value,
16
15
  )
16
+ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
17
17
  from ophyd_async.epics.motor import Motor
18
- from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
19
18
 
20
19
  HOME_STR = r"\#1hmz\#2hmz\#3hmz" # Command to home the PMAC motors
21
20
  ZERO_STR = "!x0y0z0" # Command to blend any ongoing move into new position
@@ -64,61 +63,53 @@ class PMACStringMove(Triggerable):
64
63
  pmac_str_sig: SignalRW,
65
64
  string_to_send: str,
66
65
  ) -> None:
67
- self.signal = pmac_str_sig
66
+ self.signal_ref = Reference(pmac_str_sig)
68
67
  self.cmd_string = string_to_send
69
68
 
70
69
  @AsyncStatus.wrap
71
70
  async def trigger(self):
72
- await self.signal.set(self.cmd_string, wait=True)
71
+ await self.signal_ref().set(self.cmd_string, wait=True)
73
72
 
74
73
 
75
- class PMACStringLaser(SignalRW):
74
+ class PMACStringLaser(Device, Movable):
76
75
  """Set the pmac_string to control the laser."""
77
76
 
78
77
  def __init__(
79
78
  self,
80
79
  pmac_str_sig: SignalRW,
81
- backend: SignalBackend,
82
- timeout: float | None = DEFAULT_TIMEOUT,
83
80
  name: str = "",
84
81
  ) -> None:
85
- self.signal = pmac_str_sig
86
- super().__init__(backend, timeout, name)
82
+ self._signal_ref = Reference(pmac_str_sig)
83
+ super().__init__(name)
87
84
 
88
85
  @AsyncStatus.wrap
89
86
  async def set(
90
87
  self,
91
88
  value: LaserSettings,
92
- wait=True,
93
- timeout=CALCULATE_TIMEOUT,
94
89
  ):
95
- await self.signal.set(value.value, wait, timeout)
90
+ await self._signal_ref().set(value.value)
96
91
 
97
92
 
98
- class PMACStringEncReset(SignalRW):
93
+ class PMACStringEncReset(Device, Movable):
99
94
  """Set a pmac_string to control the encoder channels in the controller."""
100
95
 
101
96
  def __init__(
102
97
  self,
103
98
  pmac_str_sig: SignalRW,
104
- backend: SignalBackend,
105
- timeout: float | None = DEFAULT_TIMEOUT,
106
99
  name: str = "",
107
100
  ) -> None:
108
- self.signal = pmac_str_sig
109
- super().__init__(backend, timeout, name)
101
+ self._signal_ref = Reference(pmac_str_sig)
102
+ super().__init__(name)
110
103
 
111
104
  @AsyncStatus.wrap
112
105
  async def set(
113
106
  self,
114
107
  value: EncReset,
115
- wait=True,
116
- timeout=CALCULATE_TIMEOUT,
117
108
  ):
118
- await self.signal.set(value.value, wait, timeout)
109
+ await self._signal_ref().set(value.value)
119
110
 
120
111
 
121
- class ProgramRunner(SignalRW, Flyable):
112
+ class ProgramRunner(Device, Flyable):
122
113
  """Run the collection by setting the program number on the PMAC string.
123
114
 
124
115
  Once the program number has been set, wait for the collection to be complete.
@@ -131,21 +122,18 @@ class ProgramRunner(SignalRW, Flyable):
131
122
  status_sig: SignalR,
132
123
  prog_num_sig: SignalRW,
133
124
  collection_time_sig: SignalRW,
134
- backend: SignalBackend,
135
- timeout: float | None = DEFAULT_TIMEOUT,
136
125
  name: str = "",
137
126
  ) -> None:
138
- self.signal = pmac_str_sig
139
- self.status = status_sig
140
- self.prog_num = prog_num_sig
127
+ self._signal_ref = Reference(pmac_str_sig)
128
+ self._status_ref = Reference(status_sig)
129
+ self._prog_num_ref = Reference(prog_num_sig)
141
130
 
142
- self.collection_time = collection_time_sig
143
- self.KICKOFF_TIMEOUT = timeout
131
+ self._collection_time_ref = Reference(collection_time_sig)
144
132
 
145
- super().__init__(backend, timeout, name)
133
+ super().__init__(name)
146
134
 
147
135
  async def _get_prog_number_string(self) -> str:
148
- prog_num = await self.prog_num.get_value()
136
+ prog_num = await self._prog_num_ref().get_value()
149
137
  return f"&2b{prog_num}r"
150
138
 
151
139
  @AsyncStatus.wrap
@@ -154,11 +142,11 @@ class ProgramRunner(SignalRW, Flyable):
154
142
  wait for the scan status PV to go to 1.
155
143
  """
156
144
  prog_num_str = await self._get_prog_number_string()
157
- await self.signal.set(prog_num_str, wait=True)
145
+ await self._signal_ref().set(prog_num_str, wait=True)
158
146
  await wait_for_value(
159
- self.status,
147
+ self._status_ref(),
160
148
  ScanState.RUNNING,
161
- timeout=self.KICKOFF_TIMEOUT,
149
+ timeout=DEFAULT_TIMEOUT,
162
150
  )
163
151
 
164
152
  @AsyncStatus.wrap
@@ -169,8 +157,10 @@ class ProgramRunner(SignalRW, Flyable):
169
157
  complete_time (float): total time required by the collection to \
170
158
  finish correctly.
171
159
  """
172
- scan_complete_time = await self.collection_time.get_value()
173
- await wait_for_value(self.status, ScanState.DONE, timeout=scan_complete_time)
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
+ )
174
164
 
175
165
 
176
166
  class ProgramAbort(Triggerable):
@@ -183,16 +173,16 @@ class ProgramAbort(Triggerable):
183
173
  pmac_str_sig: SignalRW,
184
174
  status_sig: SignalR,
185
175
  ) -> None:
186
- self.signal = pmac_str_sig
187
- self.status = status_sig
176
+ self._signal_ref = Reference(pmac_str_sig)
177
+ self._status_ref = Reference(status_sig)
188
178
 
189
179
  @AsyncStatus.wrap
190
180
  async def trigger(self):
191
- await self.signal.set("A", wait=True)
181
+ await self._signal_ref().set("A", wait=True)
192
182
  await sleep(1.0) # TODO Check with scientist what this sleep is really for.
193
- await self.signal.set("P2401=0", wait=True)
183
+ await self._signal_ref().set("P2401=0", wait=True)
194
184
  await wait_for_value(
195
- self.status,
185
+ self._status_ref(),
196
186
  ScanState.DONE,
197
187
  timeout=DEFAULT_TIMEOUT,
198
188
  )
@@ -209,10 +199,10 @@ class PMAC(StandardReadable):
209
199
  )
210
200
  self.to_xyz_zero = PMACStringMove(self.pmac_string, ZERO_STR)
211
201
 
212
- self.laser = PMACStringLaser(self.pmac_string, backend=SoftSignalBackend(str))
202
+ self.laser = PMACStringLaser(self.pmac_string)
213
203
 
214
204
  self.enc_reset = PMACStringEncReset(
215
- self.pmac_string, backend=SoftSignalBackend(str)
205
+ self.pmac_string,
216
206
  )
217
207
 
218
208
  self.x = Motor(prefix + "X")
@@ -234,7 +224,6 @@ class PMAC(StandardReadable):
234
224
  self.scanstatus,
235
225
  self.program_number,
236
226
  self.collection_time,
237
- backend=SoftSignalBackend(str),
238
227
  )
239
228
  self.abort_program = ProgramAbort(self.pmac_string, self.scanstatus)
240
229
 
@@ -0,0 +1,16 @@
1
+ from ophyd_async.core import StandardReadable
2
+ from ophyd_async.epics.motor import Motor
3
+
4
+
5
+ class VerticalGoniometer(StandardReadable):
6
+ def __init__(self, prefix: str, name: str = "") -> None:
7
+ self.x = Motor(prefix + "PINX")
8
+ self.z = Motor(prefix + "PINZ")
9
+ self.yh = Motor(prefix + "PINYH")
10
+ self.omega = Motor(prefix + "OMEGA")
11
+
12
+ self.real_x = Motor(prefix + "PINXS")
13
+ self.real_z = Motor(prefix + "PINZS")
14
+ self.fast_y = Motor(prefix + "PINYS")
15
+
16
+ super().__init__(name)
dodal/devices/ipin.py CHANGED
@@ -1,11 +1,13 @@
1
- from ophyd_async.core import HintedSignal, StandardReadable
2
- from ophyd_async.epics.signal import epics_signal_r
1
+ from ophyd_async.core import StandardReadable, StandardReadableFormat
2
+ from ophyd_async.epics.core import epics_signal_r
3
3
 
4
4
 
5
5
  class IPin(StandardReadable):
6
6
  """Simple device to get the ipin reading"""
7
7
 
8
8
  def __init__(self, prefix: str, name: str = "") -> None:
9
- with self.add_children_as_readables(wrapper=HintedSignal):
9
+ with self.add_children_as_readables(
10
+ format=StandardReadableFormat.HINTED_SIGNAL
11
+ ):
10
12
  self.pin_readback = epics_signal_r(float, prefix + "I")
11
13
  super().__init__(name)
dodal/devices/linkam3.py CHANGED
@@ -1,20 +1,19 @@
1
1
  import asyncio
2
2
  import time
3
- from enum import Enum
4
3
 
5
4
  from bluesky.protocols import Location
6
5
  from ophyd_async.core import (
7
- ConfigSignal,
8
- HintedSignal,
9
6
  StandardReadable,
7
+ StandardReadableFormat,
8
+ StrictEnum,
10
9
  WatchableAsyncStatus,
11
10
  WatcherUpdate,
12
11
  observe_value,
13
12
  )
14
- from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
13
+ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
15
14
 
16
15
 
17
- class PumpControl(str, Enum):
16
+ class PumpControl(StrictEnum):
18
17
  Manual = "Manual"
19
18
  Auto = "Auto"
20
19
 
@@ -62,9 +61,10 @@ class Linkam3(StandardReadable):
62
61
  # status is a bitfield stored in a double?
63
62
  self.status = epics_signal_r(float, prefix + "STATUS:")
64
63
 
65
- self.add_readables((self.temp,), wrapper=HintedSignal)
64
+ self.add_readables((self.temp,), format=StandardReadableFormat.HINTED_SIGNAL)
66
65
  self.add_readables(
67
- (self.ramp_rate, self.speed, self.set_point), wrapper=ConfigSignal
66
+ (self.ramp_rate, self.speed, self.set_point),
67
+ format=StandardReadableFormat.CONFIG_SIGNAL,
68
68
  )
69
69
 
70
70
  super().__init__(name=name)
@@ -1,7 +1,7 @@
1
1
  from enum import IntEnum
2
2
 
3
- from ophyd_async.core import DEFAULT_TIMEOUT, AsyncStatus, StandardReadable
4
- from ophyd_async.epics.signal import epics_signal_rw
3
+ from ophyd_async.core import DEFAULT_TIMEOUT, AsyncStatus, LazyMock, StandardReadable
4
+ from ophyd_async.epics.core import epics_signal_rw
5
5
 
6
6
  from dodal.common.signal_utils import create_hardware_backed_soft_signal
7
7
  from dodal.devices.areadetector.plugins.CAM import Cam
@@ -118,7 +118,7 @@ class OAV(StandardReadable):
118
118
 
119
119
  async def connect(
120
120
  self,
121
- mock: bool = False,
121
+ mock: bool | LazyMock = False,
122
122
  timeout: float = DEFAULT_TIMEOUT,
123
123
  force_reconnect: bool = False,
124
124
  ):
@@ -14,7 +14,7 @@ from ophyd_async.core import (
14
14
  soft_signal_r_and_setter,
15
15
  soft_signal_rw,
16
16
  )
17
- from ophyd_async.epics.signal import epics_signal_r
17
+ from ophyd_async.epics.core import epics_signal_r
18
18
  from redis.asyncio import StrictRedis
19
19
 
20
20
  from dodal.log import LOGGER
@@ -42,6 +42,7 @@ class OAVSource(StandardReadable):
42
42
  ):
43
43
  self.url = epics_signal_r(str, f"{prefix}MJPG_URL_RBV")
44
44
  self.oav_name = oav_name
45
+ super().__init__()
45
46
 
46
47
 
47
48
  class OAVToRedisForwarder(StandardReadable, Flyable, Stoppable):
@@ -78,13 +79,13 @@ class OAVToRedisForwarder(StandardReadable, Flyable, Stoppable):
78
79
  """
79
80
  self.counter = epics_signal_r(int, f"{prefix}CAM:ArrayCounter_RBV")
80
81
 
81
- self._sources = DeviceVector(
82
+ self.sources = DeviceVector(
82
83
  {
83
84
  Source.ROI.value: OAVSource(f"{prefix}MJPG:", "roi"),
84
85
  Source.FULL_SCREEN.value: OAVSource(f"{prefix}XTAL:", "fullscreen"),
85
86
  }
86
87
  )
87
- self.selected_source = soft_signal_rw(Source)
88
+ self.selected_source = soft_signal_rw(int)
88
89
 
89
90
  self.forwarding_task = None
90
91
  self.redis_client = StrictRedis(
@@ -118,11 +119,11 @@ class OAVToRedisForwarder(StandardReadable, Flyable, Stoppable):
118
119
  async def _open_connection_and_do_function(
119
120
  self, function_to_do: Callable[[ClientResponse, OAVSource], Awaitable]
120
121
  ):
121
- source_name = await self.selected_source.get_value()
122
+ source_idx = await self.selected_source.get_value()
122
123
  LOGGER.info(
123
- f"Forwarding data from sample {await self.sample_id.get_value()} and OAV {source_name}"
124
+ f"Forwarding data from sample {await self.sample_id.get_value()} and OAV {source_idx}"
124
125
  )
125
- source = self._sources[source_name.value]
126
+ source = self.sources[source_idx]
126
127
  stream_url = await source.url.get_value()
127
128
  async with ClientSession() as session:
128
129
  async with session.get(stream_url) as response:
@@ -164,7 +165,7 @@ class OAVToRedisForwarder(StandardReadable, Flyable, Stoppable):
164
165
  async def stop(self, success=True):
165
166
  if self.forwarding_task:
166
167
  LOGGER.info(
167
- f"Stopping forwarding for {await self.selected_source.get_value()}"
168
+ f"Stopping forwarding for source id {await self.selected_source.get_value()}"
168
169
  )
169
170
  self._stop_flag.set()
170
171
  await self.forwarding_task
@@ -4,14 +4,15 @@ import time
4
4
  import numpy as np
5
5
  from numpy.typing import NDArray
6
6
  from ophyd_async.core import (
7
+ Array1D,
7
8
  AsyncStatus,
8
- HintedSignal,
9
9
  StandardReadable,
10
+ StandardReadableFormat,
10
11
  observe_value,
11
12
  soft_signal_r_and_setter,
12
13
  soft_signal_rw,
13
14
  )
14
- from ophyd_async.epics.signal import epics_signal_r
15
+ from ophyd_async.epics.core import epics_signal_r
15
16
 
16
17
  from dodal.devices.oav.pin_image_recognition.utils import (
17
18
  ARRAY_PROCESSING_FUNCTIONS_MAP,
@@ -22,7 +23,8 @@ from dodal.devices.oav.pin_image_recognition.utils import (
22
23
  )
23
24
  from dodal.log import LOGGER
24
25
 
25
- Tip = tuple[int | None, int | None]
26
+ # Tip position in x, y pixel coordinates
27
+ Tip = Array1D[np.int32]
26
28
 
27
29
 
28
30
  class InvalidPinException(Exception):
@@ -45,7 +47,7 @@ class PinTipDetection(StandardReadable):
45
47
  no tip is found after this time it will not error but instead return {INVALID_POSITION}.
46
48
  """
47
49
 
48
- INVALID_POSITION = (None, None)
50
+ INVALID_POSITION = np.array([np.iinfo(np.int32).min, np.iinfo(np.int32).min])
49
51
 
50
52
  def __init__(self, prefix: str, name: str = ""):
51
53
  self._prefix: str = prefix
@@ -84,16 +86,16 @@ class PinTipDetection(StandardReadable):
84
86
  self.triggered_top_edge,
85
87
  self.triggered_bottom_edge,
86
88
  ],
87
- wrapper=HintedSignal,
89
+ format=StandardReadableFormat.HINTED_SIGNAL,
88
90
  )
89
91
 
90
92
  super().__init__(name=name)
91
93
 
92
94
  def _set_triggered_values(self, results: SampleLocation):
93
- tip = (results.tip_x, results.tip_y)
94
- if tip == self.INVALID_POSITION:
95
+ if results.tip_x is None or results.tip_y is None:
95
96
  raise InvalidPinException
96
97
  else:
98
+ tip = np.array([results.tip_x, results.tip_y])
97
99
  self._tip_setter(tip)
98
100
  self._top_edge_setter(results.edge_top)
99
101
  self._bottom_edge_setter(results.edge_bottom)