dls-dodal 1.36.1a0__py3-none-any.whl → 1.36.3__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 (36) hide show
  1. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/METADATA +2 -2
  2. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/RECORD +31 -33
  3. dodal/_version.py +2 -2
  4. dodal/beamlines/__init__.py +1 -0
  5. dodal/beamlines/adsim.py +75 -0
  6. dodal/beamlines/i10.py +74 -7
  7. dodal/beamlines/i24.py +17 -1
  8. dodal/devices/adsim.py +10 -10
  9. dodal/devices/aperture.py +0 -7
  10. dodal/devices/aperturescatterguard.py +79 -195
  11. dodal/devices/apple2_undulator.py +9 -9
  12. dodal/devices/attenuator.py +15 -5
  13. dodal/devices/focusing_mirror.py +12 -3
  14. dodal/devices/i10/i10_setting_data.py +3 -3
  15. dodal/devices/i10/mirrors.py +24 -0
  16. dodal/devices/i10/slits.py +37 -0
  17. dodal/devices/i24/dual_backlight.py +1 -0
  18. dodal/devices/i24/focus_mirrors.py +12 -12
  19. dodal/devices/linkam3.py +2 -2
  20. dodal/devices/oav/pin_image_recognition/__init__.py +2 -4
  21. dodal/devices/p99/sample_stage.py +15 -15
  22. dodal/devices/slits.py +29 -7
  23. dodal/devices/tetramm.py +16 -16
  24. dodal/devices/undulator_dcm.py +4 -0
  25. dodal/devices/util/test_utils.py +2 -2
  26. dodal/devices/xspress3/xspress3.py +3 -3
  27. dodal/devices/zebra.py +13 -13
  28. dodal/adsim.py +0 -17
  29. dodal/devices/areadetector/__init__.py +0 -10
  30. dodal/devices/areadetector/adaravis.py +0 -101
  31. dodal/devices/areadetector/adsim.py +0 -47
  32. dodal/devices/areadetector/adutils.py +0 -81
  33. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/LICENSE +0 -0
  34. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/WHEEL +0 -0
  35. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/entry_points.txt +0 -0
  36. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/top_level.txt +0 -0
@@ -1,18 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- from collections.abc import Callable, Coroutine
5
- from typing import Any
6
4
 
7
- from bluesky.protocols import Movable, Triggerable
5
+ from bluesky.protocols import Movable
8
6
  from ophyd_async.core import (
9
7
  AsyncStatus,
10
- Reference,
11
8
  StandardReadable,
12
9
  StandardReadableFormat,
13
10
  StrictEnum,
14
11
  )
15
- from ophyd_async.epics.motor import Motor
16
12
  from pydantic import BaseModel, Field
17
13
 
18
14
  from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters
@@ -111,157 +107,7 @@ def load_positions_from_beamline_parameters(
111
107
  }
112
108
 
113
109
 
114
- async def _safe_move_whilst_in_beam(
115
- aperture: Aperture,
116
- scatterguard: Scatterguard,
117
- position: AperturePosition,
118
- aperture_z_tolerance: float,
119
- ):
120
- """
121
- Move the aperture and scatterguard combo safely to a new position.
122
- See https://github.com/DiamondLightSource/hyperion/wiki/Aperture-Scatterguard-Collisions
123
- for why this is required. TLDR is that we have a collision at the top of y so we need
124
- to make sure we move the assembly down before we move the scatterguard up.
125
-
126
- We also check that the assembly has been moved into the correct z position
127
- previously. If we try and move whilst in the incorrect Z position we will collide
128
- with the table.
129
- """
130
- ap_z_in_position = await aperture.z.motor_done_move.get_value()
131
- if not ap_z_in_position:
132
- raise InvalidApertureMove(
133
- "ApertureScatterguard z is still moving. Wait for it to finish "
134
- "before triggering another move."
135
- )
136
-
137
- current_ap_z = await aperture.z.user_readback.get_value()
138
- diff_on_z = abs(current_ap_z - position.aperture_z)
139
- if diff_on_z > aperture_z_tolerance:
140
- raise InvalidApertureMove(
141
- f"Current aperture z ({current_ap_z}), outside of tolerance ({aperture_z_tolerance}) from target ({position.aperture_z})."
142
- )
143
-
144
- current_ap_y = await aperture.y.user_readback.get_value()
145
-
146
- aperture_x, aperture_y, aperture_z, scatterguard_x, scatterguard_y = position.values
147
-
148
- if aperture_y > current_ap_y:
149
- # Assembly needs to move up so move the scatterguard down first
150
- await asyncio.gather(
151
- scatterguard.x.set(scatterguard_x),
152
- scatterguard.y.set(scatterguard_y),
153
- )
154
- await asyncio.gather(
155
- aperture.x.set(aperture_x),
156
- aperture.y.set(aperture_y),
157
- aperture.z.set(aperture_z),
158
- )
159
- else:
160
- await asyncio.gather(
161
- aperture.x.set(aperture_x),
162
- aperture.y.set(aperture_y),
163
- aperture.z.set(aperture_z),
164
- )
165
-
166
- await asyncio.gather(
167
- scatterguard.x.set(scatterguard_x),
168
- scatterguard.y.set(scatterguard_y),
169
- )
170
-
171
-
172
- class ApertureSelector(StandardReadable, Movable):
173
- """Allows for moving all axes other than Y into the correct position, this means
174
- that we can set up the aperture while it is out of the beam then move it in later."""
175
-
176
- def __init__(
177
- self,
178
- aperture: Aperture,
179
- scatterguard: Scatterguard,
180
- out_of_beam: Callable[[], Coroutine[Any, Any, bool]],
181
- loaded_positions: dict[ApertureValue, AperturePosition],
182
- aperture_z_tolerance: float,
183
- ):
184
- self.aperture = Reference(aperture)
185
- self.scatterguard = Reference(scatterguard)
186
- self.loaded_positions = loaded_positions
187
- self.get_is_out_of_beam = out_of_beam
188
- self.aperture_z_tolerance = aperture_z_tolerance
189
- super().__init__()
190
-
191
- @AsyncStatus.wrap
192
- async def set(self, value: ApertureValue):
193
- """Moves the assembly to the position for the specified aperture, whilst keeping
194
- it out of the beam if it already is so.
195
-
196
- Moving the assembly whilst out of the beam has no collision risk so we can just
197
- move all the motors together.
198
- """
199
- if await self.get_is_out_of_beam():
200
- aperture_x, _, aperture_z, scatterguard_x, scatterguard_y = (
201
- self.loaded_positions[value].values
202
- )
203
-
204
- await asyncio.gather(
205
- self.aperture().x.set(aperture_x),
206
- self.aperture().z.set(aperture_z),
207
- self.scatterguard().x.set(scatterguard_x),
208
- self.scatterguard().y.set(scatterguard_y),
209
- )
210
- else:
211
- await _safe_move_whilst_in_beam(
212
- self.aperture(),
213
- self.scatterguard(),
214
- self.loaded_positions[value],
215
- self.aperture_z_tolerance,
216
- )
217
-
218
-
219
- class OutTrigger(StandardReadable, Triggerable):
220
- """Allows for moving just the Y stage of the assembly out of the beam."""
221
-
222
- def __init__(
223
- self,
224
- aperture_y: Motor,
225
- out_y: float,
226
- ):
227
- self.aperture_y = Reference(aperture_y)
228
- self.out_y = out_y
229
- super().__init__()
230
-
231
- @AsyncStatus.wrap
232
- async def trigger(self):
233
- """Moves the assembly out of the beam."""
234
- await self.aperture_y().set(self.out_y)
235
-
236
-
237
110
  class ApertureScatterguard(StandardReadable, Movable):
238
- """Move the aperture and scatterguard assembly in a safe way. There are two ways to
239
- interact with the device depending on if you want simplicity or move flexibility.
240
-
241
- The simple interface is using:
242
-
243
- await aperture_scatterguard.set(ApertureValue.LARGE)
244
-
245
- This will move the assembly so that the large aperture is in the beam, regardless
246
- of where the assembly currently is.
247
-
248
- However, the aperture Y axis is faster than the others. In some cases we may want to
249
- move the assembly out of the beam with this axis without moving others:
250
-
251
- await aperture_scatterguard.move_out.trigger()
252
-
253
- We may then want to keep the assembly out of the beam whilst asynchronously preparing
254
- the other axes for the aperture that's to follow:
255
-
256
- await aperture_scatterguard.aperture_outside_beam.set(ApertureValue.LARGE)
257
-
258
- Then, at a later time, move back into the beam:
259
-
260
- await aperture_scatterguard.set(ApertureValue.LARGE)
261
-
262
- This move will now be faster as only the y is left to move.
263
- """
264
-
265
111
  def __init__(
266
112
  self,
267
113
  loaded_positions: dict[ApertureValue, AperturePosition],
@@ -269,8 +115,8 @@ class ApertureScatterguard(StandardReadable, Movable):
269
115
  prefix: str = "",
270
116
  name: str = "",
271
117
  ) -> None:
272
- self._aperture = Aperture(prefix + "-MO-MAPT-01:")
273
- self._scatterguard = Scatterguard(prefix + "-MO-SCAT-01:")
118
+ self.aperture = Aperture(prefix + "-MO-MAPT-01:")
119
+ self.scatterguard = Scatterguard(prefix + "-MO-SCAT-01:")
274
120
  self.radius = create_hardware_backed_soft_signal(
275
121
  float, self._get_current_radius, units="µm"
276
122
  )
@@ -278,43 +124,30 @@ class ApertureScatterguard(StandardReadable, Movable):
278
124
  self._tolerances = tolerances
279
125
  self.add_readables(
280
126
  [
281
- self._aperture.x.user_readback,
282
- self._aperture.y.user_readback,
283
- self._aperture.z.user_readback,
284
- self._scatterguard.x.user_readback,
285
- self._scatterguard.y.user_readback,
127
+ self.aperture.x.user_readback,
128
+ self.aperture.y.user_readback,
129
+ self.aperture.z.user_readback,
130
+ self.scatterguard.x.user_readback,
131
+ self.scatterguard.y.user_readback,
286
132
  self.radius,
287
133
  ],
288
134
  )
289
-
290
135
  with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
291
136
  self.selected_aperture = create_hardware_backed_soft_signal(
292
137
  ApertureValue, self._get_current_aperture_position
293
138
  )
294
139
 
295
- # Setting this will select the aperture but not move it into beam
296
- self.aperture_outside_beam = ApertureSelector(
297
- self._aperture,
298
- self._scatterguard,
299
- self._is_out_of_beam,
300
- self._loaded_positions,
301
- self._tolerances.aperture_z,
302
- )
303
-
304
- # Setting this will just move the assembly out of the beam
305
- self.move_out = OutTrigger(
306
- self._aperture.y, loaded_positions[ApertureValue.ROBOT_LOAD].aperture_y
307
- )
308
-
309
140
  super().__init__(name)
310
141
 
142
+ def get_position_from_gda_aperture_name(
143
+ self, gda_aperture_name: str
144
+ ) -> ApertureValue:
145
+ return ApertureValue(gda_aperture_name)
146
+
311
147
  @AsyncStatus.wrap
312
148
  async def set(self, value: ApertureValue):
313
- """This set will move the aperture into the beam or move to robot load"""
314
149
  position = self._loaded_positions[value]
315
- await _safe_move_whilst_in_beam(
316
- self._aperture, self._scatterguard, position, self._tolerances.aperture_z
317
- )
150
+ await self._safe_move_within_datacollection_range(position, value)
318
151
 
319
152
  @AsyncStatus.wrap
320
153
  async def _set_raw_unsafe(self, position: AperturePosition):
@@ -324,18 +157,13 @@ class ApertureScatterguard(StandardReadable, Movable):
324
157
  )
325
158
 
326
159
  await asyncio.gather(
327
- self._aperture.x.set(aperture_x),
328
- self._aperture.y.set(aperture_y),
329
- self._aperture.z.set(aperture_z),
330
- self._scatterguard.x.set(scatterguard_x),
331
- self._scatterguard.y.set(scatterguard_y),
160
+ self.aperture.x.set(aperture_x),
161
+ self.aperture.y.set(aperture_y),
162
+ self.aperture.z.set(aperture_z),
163
+ self.scatterguard.x.set(scatterguard_x),
164
+ self.scatterguard.y.set(scatterguard_y),
332
165
  )
333
166
 
334
- async def _is_out_of_beam(self) -> bool:
335
- current_ap_y = await self._aperture.y.user_readback.get_value()
336
- robot_load_ap_y = self._loaded_positions[ApertureValue.ROBOT_LOAD].aperture_y
337
- return current_ap_y <= robot_load_ap_y + self._tolerances.aperture_y
338
-
339
167
  async def _get_current_aperture_position(self) -> ApertureValue:
340
168
  """
341
169
  Returns the current aperture position using readback values
@@ -343,13 +171,15 @@ class ApertureScatterguard(StandardReadable, Movable):
343
171
  mini aperture y <= ROBOT_LOAD.location.aperture_y + tolerance.
344
172
  If no position is found then raises InvalidApertureMove.
345
173
  """
346
- if await self._aperture.large.get_value(cached=False) == 1:
174
+ current_ap_y = await self.aperture.y.user_readback.get_value(cached=False)
175
+ robot_load_ap_y = self._loaded_positions[ApertureValue.ROBOT_LOAD].aperture_y
176
+ if await self.aperture.large.get_value(cached=False) == 1:
347
177
  return ApertureValue.LARGE
348
- elif await self._aperture.medium.get_value(cached=False) == 1:
178
+ elif await self.aperture.medium.get_value(cached=False) == 1:
349
179
  return ApertureValue.MEDIUM
350
- elif await self._aperture.small.get_value(cached=False) == 1:
180
+ elif await self.aperture.small.get_value(cached=False) == 1:
351
181
  return ApertureValue.SMALL
352
- elif await self._is_out_of_beam():
182
+ elif current_ap_y <= robot_load_ap_y + self._tolerances.aperture_y:
353
183
  return ApertureValue.ROBOT_LOAD
354
184
 
355
185
  raise InvalidApertureMove("Current aperture/scatterguard state unrecognised")
@@ -357,3 +187,57 @@ class ApertureScatterguard(StandardReadable, Movable):
357
187
  async def _get_current_radius(self) -> float:
358
188
  current_value = await self._get_current_aperture_position()
359
189
  return self._loaded_positions[current_value].radius
190
+
191
+ async def _safe_move_within_datacollection_range(
192
+ self, position: AperturePosition, value: ApertureValue
193
+ ):
194
+ """
195
+ Move the aperture and scatterguard combo safely to a new position.
196
+ See https://github.com/DiamondLightSource/hyperion/wiki/Aperture-Scatterguard-Collisions
197
+ for why this is required.
198
+ """
199
+ assert self._loaded_positions is not None
200
+
201
+ ap_z_in_position = await self.aperture.z.motor_done_move.get_value()
202
+ if not ap_z_in_position:
203
+ raise InvalidApertureMove(
204
+ "ApertureScatterguard z is still moving. Wait for it to finish "
205
+ "before triggering another move."
206
+ )
207
+
208
+ current_ap_z = await self.aperture.z.user_readback.get_value()
209
+ diff_on_z = abs(current_ap_z - position.aperture_z)
210
+ if diff_on_z > self._tolerances.aperture_z:
211
+ raise InvalidApertureMove(
212
+ "ApertureScatterguard safe move is not yet defined for positions "
213
+ "outside of LARGE, MEDIUM, SMALL, ROBOT_LOAD. "
214
+ f"Current aperture z ({current_ap_z}), outside of tolerance ({self._tolerances.aperture_z}) from target ({position.aperture_z})."
215
+ )
216
+
217
+ current_ap_y = await self.aperture.y.user_readback.get_value()
218
+
219
+ aperture_x, aperture_y, aperture_z, scatterguard_x, scatterguard_y = (
220
+ position.values
221
+ )
222
+
223
+ if position.aperture_y > current_ap_y:
224
+ await asyncio.gather(
225
+ self.scatterguard.x.set(scatterguard_x),
226
+ self.scatterguard.y.set(scatterguard_y),
227
+ )
228
+ await asyncio.gather(
229
+ self.aperture.x.set(aperture_x),
230
+ self.aperture.y.set(aperture_y),
231
+ self.aperture.z.set(aperture_z),
232
+ )
233
+ else:
234
+ await asyncio.gather(
235
+ self.aperture.x.set(aperture_x),
236
+ self.aperture.y.set(aperture_y),
237
+ self.aperture.z.set(aperture_z),
238
+ )
239
+
240
+ await asyncio.gather(
241
+ self.scatterguard.x.set(scatterguard_x),
242
+ self.scatterguard.y.set(scatterguard_y),
243
+ )
@@ -21,8 +21,8 @@ from dodal.log import LOGGER
21
21
 
22
22
 
23
23
  class UndulatorGateStatus(StrictEnum):
24
- open = "Open"
25
- close = "Closed"
24
+ OPEN = "Open"
25
+ CLOSE = "Closed"
26
26
 
27
27
 
28
28
  @dataclass
@@ -146,7 +146,7 @@ class UndulatorGap(StandardReadable, Movable):
146
146
  timeout = await self._cal_timeout()
147
147
  LOGGER.info(f"Moving {self.name} to {value} with timeout = {timeout}")
148
148
  await self.set_move.set(value=1, timeout=timeout)
149
- await wait_for_value(self.gate, UndulatorGateStatus.close, timeout=timeout)
149
+ await wait_for_value(self.gate, UndulatorGateStatus.CLOSE, timeout=timeout)
150
150
 
151
151
  async def _cal_timeout(self) -> float:
152
152
  vel = await self.velocity.get_value()
@@ -157,7 +157,7 @@ class UndulatorGap(StandardReadable, Movable):
157
157
  async def check_id_status(self) -> None:
158
158
  if await self.fault.get_value() != 0:
159
159
  raise RuntimeError(f"{self.name} is in fault state")
160
- if await self.gate.get_value() == UndulatorGateStatus.open:
160
+ if await self.gate.get_value() == UndulatorGateStatus.OPEN:
161
161
  raise RuntimeError(f"{self.name} is already in motion.")
162
162
 
163
163
  async def get_timeout(self) -> float:
@@ -251,7 +251,7 @@ class UndulatorPhaseAxes(StandardReadable, Movable):
251
251
  )
252
252
  timeout = await self._cal_timeout()
253
253
  await self.set_move.set(value=1, timeout=timeout)
254
- await wait_for_value(self.gate, UndulatorGateStatus.close, timeout=timeout)
254
+ await wait_for_value(self.gate, UndulatorGateStatus.CLOSE, timeout=timeout)
255
255
 
256
256
  async def _cal_timeout(self) -> float:
257
257
  """
@@ -283,7 +283,7 @@ class UndulatorPhaseAxes(StandardReadable, Movable):
283
283
  async def check_id_status(self) -> None:
284
284
  if await self.fault.get_value() != 0:
285
285
  raise RuntimeError(f"{self.name} is in fault state")
286
- if await self.gate.get_value() == UndulatorGateStatus.open:
286
+ if await self.gate.get_value() == UndulatorGateStatus.OPEN:
287
287
  raise RuntimeError(f"{self.name} is already in motion.")
288
288
 
289
289
  async def get_timeout(self) -> float:
@@ -325,7 +325,7 @@ class UndulatorJawPhase(StandardReadable, Movable):
325
325
  )
326
326
  timeout = await self._cal_timeout()
327
327
  await self.set_move.set(value=1, timeout=timeout)
328
- await wait_for_value(self.gate, UndulatorGateStatus.close, timeout=timeout)
328
+ await wait_for_value(self.gate, UndulatorGateStatus.CLOSE, timeout=timeout)
329
329
 
330
330
  async def _cal_timeout(self) -> float:
331
331
  """
@@ -345,7 +345,7 @@ class UndulatorJawPhase(StandardReadable, Movable):
345
345
  async def check_id_status(self) -> None:
346
346
  if await self.fault.get_value() != 0:
347
347
  raise RuntimeError(f"{self.name} is in fault state")
348
- if await self.gate.get_value() == UndulatorGateStatus.open:
348
+ if await self.gate.get_value() == UndulatorGateStatus.OPEN:
349
349
  raise RuntimeError(f"{self.name} is already in motion.")
350
350
 
351
351
  async def get_timeout(self) -> float:
@@ -458,7 +458,7 @@ class Apple2(StandardReadable, Movable):
458
458
  self.phase().set_move.set(value=1, timeout=timeout),
459
459
  )
460
460
  await wait_for_value(
461
- self.gap().gate, UndulatorGateStatus.close, timeout=timeout
461
+ self.gap().gate, UndulatorGateStatus.CLOSE, timeout=timeout
462
462
  )
463
463
  self._energy_set(energy) # Update energy for after move for readback.
464
464
 
@@ -14,7 +14,20 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw, epics_signal
14
14
  from dodal.log import LOGGER
15
15
 
16
16
 
17
- class Attenuator(StandardReadable, Movable):
17
+ class ReadOnlyAttenuator(StandardReadable):
18
+ """A read-only attenuator class with a minimum set of PVs for reading.
19
+
20
+ The actual_transmission will return a fractional transmission between 0-1.
21
+ """
22
+
23
+ def __init__(self, prefix: str, name: str = "") -> None:
24
+ with self.add_children_as_readables():
25
+ self.actual_transmission = epics_signal_r(float, prefix + "MATCH")
26
+
27
+ super().__init__(name)
28
+
29
+
30
+ class Attenuator(ReadOnlyAttenuator, Movable):
18
31
  """The attenuator will insert filters into the beam to reduce its transmission.
19
32
 
20
33
  This device should be set with:
@@ -42,10 +55,7 @@ class Attenuator(StandardReadable, Movable):
42
55
  self._use_current_energy = epics_signal_x(prefix + "E2WL:USECURRENTENERGY.PROC")
43
56
  self._change = epics_signal_x(prefix + "FANOUT")
44
57
 
45
- with self.add_children_as_readables():
46
- self.actual_transmission = epics_signal_r(float, prefix + "MATCH")
47
-
48
- super().__init__(name)
58
+ super().__init__(prefix, name)
49
59
 
50
60
  @AsyncStatus.wrap
51
61
  async def set(self, value: float):
@@ -1,3 +1,5 @@
1
+ from typing import TypedDict
2
+
1
3
  from ophyd_async.core import (
2
4
  AsyncStatus,
3
5
  Device,
@@ -36,6 +38,12 @@ class MirrorStripe(StrictEnum):
36
38
  PLATINUM = "Platinum"
37
39
 
38
40
 
41
+ class MirrorStripeConfiguration(TypedDict):
42
+ stripe: MirrorStripe
43
+ yaw_mrad: float
44
+ lat_mm: float
45
+
46
+
39
47
  class MirrorVoltageDemand(StrictEnum):
40
48
  N_A = "N/A"
41
49
  OK = "OK"
@@ -173,9 +181,10 @@ class FocusingMirrorWithStripes(FocusingMirror):
173
181
 
174
182
  super().__init__(prefix, name, *args, **kwargs)
175
183
 
176
- def energy_to_stripe(self, energy_kev) -> MirrorStripe:
184
+ def energy_to_stripe(self, energy_kev) -> MirrorStripeConfiguration:
185
+ """Return the stripe, yaw angle and lateral position for the specified energy"""
177
186
  # In future, this should be configurable per-mirror
178
187
  if energy_kev < 7:
179
- return MirrorStripe.BARE
188
+ return {"stripe": MirrorStripe.BARE, "yaw_mrad": 6.2, "lat_mm": 0.0}
180
189
  else:
181
- return MirrorStripe.RHODIUM
190
+ return {"stripe": MirrorStripe.RHODIUM, "yaw_mrad": 0.0, "lat_mm": 10.0}
@@ -2,6 +2,6 @@ from ophyd_async.core import StrictEnum
2
2
 
3
3
 
4
4
  class I10Grating(StrictEnum):
5
- Au400 = "400 line/mm Au"
6
- Si400 = "400 line/mm Si"
7
- Au1200 = "1200 line/mm Au"
5
+ AU_400 = "400 line/mm Au"
6
+ SI_400 = "400 line/mm Si"
7
+ AU_1200 = "1200 line/mm Au"
@@ -0,0 +1,24 @@
1
+ from ophyd_async.core import StandardReadable
2
+ from ophyd_async.epics.core import epics_signal_rw
3
+ from ophyd_async.epics.motor import Motor
4
+
5
+
6
+ class PiezoMirror(StandardReadable):
7
+ def __init__(
8
+ self,
9
+ prefix: str,
10
+ name: str = "",
11
+ ):
12
+ with self.add_children_as_readables():
13
+ self.x = Motor(prefix + "X")
14
+ self.y = Motor(prefix + "Y")
15
+ self.z = Motor(prefix + "Z")
16
+ self.yaw = Motor(prefix + "YAW")
17
+ self.pitch = Motor(prefix + "PITCH")
18
+ self.roll = Motor(prefix + "ROLL")
19
+ self.fine_pitch = epics_signal_rw(
20
+ float,
21
+ read_pv=prefix + "FPITCH:RBV:AI",
22
+ write_pv=prefix + "FPITCH:DMD:AO",
23
+ )
24
+ super().__init__(name=name)
@@ -0,0 +1,37 @@
1
+ from ophyd_async.epics.motor import Motor
2
+
3
+ from dodal.devices.slits import Slits
4
+
5
+
6
+ class I10Slits(Slits):
7
+ def __init__(self, prefix: str, name: str = "") -> None:
8
+ with self.add_children_as_readables():
9
+ self.x_ring_blade = Motor(prefix + "XRING")
10
+ self.x_hall_blade = Motor(prefix + "XHALL")
11
+ self.y_top_blade = Motor(prefix + "YPLUS")
12
+ self.y_bot_blade = Motor(prefix + "YMINUS")
13
+ super().__init__(
14
+ prefix=prefix,
15
+ x_gap="XSIZE",
16
+ x_centre="XCENTRE",
17
+ y_gap="YSIZE",
18
+ y_centre="YCENTRE",
19
+ name=name,
20
+ )
21
+
22
+
23
+ class I10PrimarySlits(Slits):
24
+ def __init__(self, prefix: str, name: str = "") -> None:
25
+ with self.add_children_as_readables():
26
+ self.x_aptr_1 = Motor(prefix + "APTR1:X")
27
+ self.x_aptr_2 = Motor(prefix + "APTR2:X")
28
+ self.y_aptr_1 = Motor(prefix + "APTR1:Y")
29
+ self.y_aptr_1 = Motor(prefix + "APTR2:Y")
30
+ super().__init__(
31
+ prefix=prefix,
32
+ x_gap="XSIZE",
33
+ x_centre="XCENTRE",
34
+ y_gap="YSIZE",
35
+ y_centre="YCENTRE",
36
+ name=name,
37
+ )
@@ -8,6 +8,7 @@ class BacklightPositions(StrictEnum):
8
8
  LOAD_CHECK = "LoadCheck"
9
9
  OAV2 = "OAV2"
10
10
  DIODE = "Diode"
11
+ WHITE_IN = "White In"
11
12
 
12
13
 
13
14
  class LEDStatus(StrictEnum):
@@ -5,21 +5,21 @@ from dodal.common.signal_utils import create_hardware_backed_soft_signal
5
5
 
6
6
 
7
7
  class HFocusMode(StrictEnum):
8
- focus10 = "HMFMfocus10"
9
- focus20d = "HMFMfocus20d"
10
- focus30d = "HMFMfocus30d"
11
- focus50d = "HMFMfocus50d"
12
- focus1050d = "HMFMfocus1030d"
13
- focus3010d = "HMFMfocus3010d"
8
+ FOCUS_10 = "HMFMfocus10"
9
+ FOCUS_20D = "HMFMfocus20d"
10
+ FOCUS_30D = "HMFMfocus30d"
11
+ FOCUS_50D = "HMFMfocus50d"
12
+ FOCUS_1050D = "HMFMfocus1030d"
13
+ FOCUS_3010D = "HMFMfocus3010d"
14
14
 
15
15
 
16
16
  class VFocusMode(StrictEnum):
17
- focus10 = "VMFMfocus10"
18
- focus20d = "VMFMfocus20d"
19
- focus30d = "VMFMfocus30d"
20
- focus50d = "VMFMfocus50d"
21
- focus1030d = "VMFMfocus1030d"
22
- focus3010d = "VMFMfocus3010d"
17
+ FOCUS_10 = "VMFMfocus10"
18
+ FOCUS_20D = "VMFMfocus20d"
19
+ FOCUS_30D = "VMFMfocus30d"
20
+ FOCUS_50D = "VMFMfocus50d"
21
+ FOCUS_1030D = "VMFMfocus1030d"
22
+ FOCUS_3010D = "VMFMfocus3010d"
23
23
 
24
24
 
25
25
  BEAM_SIZES = {
dodal/devices/linkam3.py CHANGED
@@ -14,8 +14,8 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
14
14
 
15
15
 
16
16
  class PumpControl(StrictEnum):
17
- Manual = "Manual"
18
- Auto = "Auto"
17
+ MANUAL = "Manual"
18
+ AUTO = "Auto"
19
19
 
20
20
 
21
21
  class Linkam3(StandardReadable):
@@ -61,7 +61,7 @@ class PinTipDetection(StandardReadable):
61
61
  self.triggered_bottom_edge, self._bottom_edge_setter = soft_signal_r_and_setter(
62
62
  Array1D[np.int32], name="triggered_bottom_edge"
63
63
  )
64
- self.array_data = epics_signal_r(Array1D[np.uint8], f"pva://{prefix}PVA:ARRAY")
64
+ self.array_data = epics_signal_r(np.ndarray, f"pva://{prefix}PVA:ARRAY")
65
65
 
66
66
  # Soft parameters for pin-tip detection.
67
67
  self.preprocess_operation = soft_signal_rw(int, 10, name="preprocess")
@@ -99,9 +99,7 @@ class PinTipDetection(StandardReadable):
99
99
  self._top_edge_setter(results.edge_top)
100
100
  self._bottom_edge_setter(results.edge_bottom)
101
101
 
102
- async def _get_tip_and_edge_data(
103
- self, array_data: Array1D[np.uint8]
104
- ) -> SampleLocation:
102
+ async def _get_tip_and_edge_data(self, array_data: np.ndarray) -> SampleLocation:
105
103
  """
106
104
  Gets the location of the pin tip and the top and bottom edges.
107
105
  """
@@ -12,22 +12,22 @@ class SampleAngleStage(StandardReadable):
12
12
 
13
13
 
14
14
  class p99StageSelections(SubsetEnum):
15
- Empty = "Empty"
16
- Mn5um = "Mn 5um"
17
- Fe = "Fe (empty)"
18
- Co5um = "Co 5um"
19
- Ni5um = "Ni 5um"
20
- Cu5um = "Cu 5um"
21
- Zn5um = "Zn 5um"
22
- Zr = "Zr (empty)"
23
- Mo = "Mo (empty)"
24
- Rh = "Rh (empty)"
25
- Pd = "Pd (empty)"
26
- Ag = "Ag (empty)"
27
- Cd25um = "Cd 25um"
15
+ EMPTY = "Empty"
16
+ MN5UM = "Mn 5um"
17
+ FE = "Fe (empty)"
18
+ CO5UM = "Co 5um"
19
+ NI5UM = "Ni 5um"
20
+ CU5UM = "Cu 5um"
21
+ ZN5UM = "Zn 5um"
22
+ ZR = "Zr (empty)"
23
+ MO = "Mo (empty)"
24
+ RH = "Rh (empty)"
25
+ PD = "Pd (empty)"
26
+ AG = "Ag (empty)"
27
+ CD25UM = "Cd 25um"
28
28
  W = "W (empty)"
29
- Pt = "Pt (empty)"
30
- User = "User"
29
+ PT = "Pt (empty)"
30
+ USER = "User"
31
31
 
32
32
 
33
33
  class FilterMotor(StandardReadable):