dls-dodal 1.54.0__py3-none-any.whl → 1.55.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 (35) hide show
  1. {dls_dodal-1.54.0.dist-info → dls_dodal-1.55.0.dist-info}/METADATA +1 -1
  2. {dls_dodal-1.54.0.dist-info → dls_dodal-1.55.0.dist-info}/RECORD +35 -33
  3. dodal/_version.py +16 -3
  4. dodal/beamline_specific_utils/i05_shared.py +11 -0
  5. dodal/beamlines/aithre.py +2 -3
  6. dodal/beamlines/b16.py +0 -1
  7. dodal/beamlines/b21.py +0 -2
  8. dodal/beamlines/i03.py +10 -31
  9. dodal/beamlines/i04.py +7 -38
  10. dodal/beamlines/i05.py +2 -2
  11. dodal/beamlines/i05_1.py +8 -3
  12. dodal/beamlines/i13_1.py +0 -1
  13. dodal/beamlines/i19_1.py +0 -1
  14. dodal/beamlines/i19_2.py +0 -1
  15. dodal/beamlines/i19_optics.py +1 -4
  16. dodal/beamlines/i23.py +1 -2
  17. dodal/beamlines/i24.py +0 -12
  18. dodal/common/beamlines/device_helpers.py +0 -33
  19. dodal/devices/aithre_lasershaping/__init__.py +0 -0
  20. dodal/devices/aithre_lasershaping/goniometer.py +3 -26
  21. dodal/devices/aithre_lasershaping/laser_robot.py +2 -2
  22. dodal/devices/focusing_mirror.py +1 -1
  23. dodal/devices/i03/undulator_dcm.py +8 -3
  24. dodal/devices/i10/mirrors.py +2 -6
  25. dodal/devices/motors.py +75 -1
  26. dodal/devices/robot.py +1 -1
  27. dodal/devices/webcam.py +2 -1
  28. dodal/devices/zebra/zebra.py +1 -1
  29. dodal/devices/zebra/zebra_controlled_shutter.py +1 -1
  30. dodal/devices/zocalo/__init__.py +2 -0
  31. dodal/devices/zocalo/zocalo_results.py +16 -26
  32. {dls_dodal-1.54.0.dist-info → dls_dodal-1.55.0.dist-info}/WHEEL +0 -0
  33. {dls_dodal-1.54.0.dist-info → dls_dodal-1.55.0.dist-info}/entry_points.txt +0 -0
  34. {dls_dodal-1.54.0.dist-info → dls_dodal-1.55.0.dist-info}/licenses/LICENSE +0 -0
  35. {dls_dodal-1.54.0.dist-info → dls_dodal-1.55.0.dist-info}/top_level.txt +0 -0
dodal/beamlines/i04.py CHANGED
@@ -43,6 +43,7 @@ from dodal.devices.zebra.zebra_constants_mapping import (
43
43
  )
44
44
  from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
45
45
  from dodal.devices.zocalo import ZocaloResults
46
+ from dodal.devices.zocalo.zocalo_results import ZocaloSource
46
47
  from dodal.log import set_beamline as set_log_beamline
47
48
  from dodal.utils import BeamlinePrefix, get_beamline_name
48
49
 
@@ -71,7 +72,6 @@ def smargon() -> Smargon:
71
72
  """
72
73
  return Smargon(
73
74
  f"{PREFIX.beamline_prefix}-MO-SGON-01:",
74
- "smargon",
75
75
  )
76
76
 
77
77
 
@@ -93,7 +93,6 @@ def sample_delivery_system() -> XYZStage:
93
93
  """
94
94
  return XYZStage(
95
95
  f"{PREFIX.beamline_prefix}-MO-SDE-01:",
96
- "sample_delivery_system",
97
96
  )
98
97
 
99
98
 
@@ -104,7 +103,6 @@ def ipin() -> IPin:
104
103
  """
105
104
  return IPin(
106
105
  f"{PREFIX.beamline_prefix}-EA-PIN-01:",
107
- "ipin",
108
106
  )
109
107
 
110
108
 
@@ -126,7 +124,6 @@ def sample_shutter() -> ZebraShutter:
126
124
  """
127
125
  return ZebraShutter(
128
126
  f"{PREFIX.beamline_prefix}-EA-SHTR-01:",
129
- "sample_shutter",
130
127
  )
131
128
 
132
129
 
@@ -148,7 +145,6 @@ def transfocator() -> Transfocator:
148
145
  """
149
146
  return Transfocator(
150
147
  f"{PREFIX.beamline_prefix}-MO-FSWT-01:",
151
- "transfocator",
152
148
  )
153
149
 
154
150
 
@@ -159,22 +155,15 @@ def xbpm_feedback() -> XBPMFeedback:
159
155
  """
160
156
  return XBPMFeedback(
161
157
  PREFIX.beamline_prefix,
162
- "xbpm_feedback",
163
158
  )
164
159
 
165
160
 
166
161
  @device_factory()
167
- def flux(mock: bool = False) -> Flux:
162
+ def flux() -> Flux:
168
163
  """Get the i04 flux device, instantiate it if it hasn't already been.
169
164
  If this is called when already instantiated in i04, it will return the existing object.
170
165
  """
171
- return device_instantiation(
172
- Flux,
173
- "flux",
174
- "-MO-FLUX-01:",
175
- wait=False,
176
- fake=mock,
177
- )
166
+ return Flux(f"{PREFIX.beamline_prefix}-MO-FLUX-01:")
178
167
 
179
168
 
180
169
  @device_factory()
@@ -184,7 +173,6 @@ def dcm() -> DCM:
184
173
  """
185
174
  return DCM(
186
175
  f"{PREFIX.beamline_prefix}-MO-DCM-01:",
187
- "dcm",
188
176
  )
189
177
 
190
178
 
@@ -195,7 +183,6 @@ def backlight() -> Backlight:
195
183
  """
196
184
  return Backlight(
197
185
  PREFIX.beamline_prefix,
198
- "backlight",
199
186
  )
200
187
 
201
188
 
@@ -208,7 +195,6 @@ def aperture_scatterguard() -> ApertureScatterguard:
208
195
  params = get_beamline_parameters()
209
196
  return ApertureScatterguard(
210
197
  prefix=PREFIX.beamline_prefix,
211
- name="aperture_scatterguard",
212
198
  loaded_positions=load_positions_from_beamline_parameters(params),
213
199
  tolerances=AperturePosition.tolerances_from_gda_params(params),
214
200
  )
@@ -241,23 +227,16 @@ def zebra_fast_grid_scan() -> ZebraFastGridScan:
241
227
  If this is called when already instantiated in i04, it will return the existing object.
242
228
  """
243
229
  return ZebraFastGridScan(
244
- name="zebra_fast_grid_scan",
245
230
  prefix=f"{PREFIX.beamline_prefix}-MO-SGON-01:",
246
231
  )
247
232
 
248
233
 
249
234
  @device_factory()
250
- def s4_slit_gaps(mock: bool = False) -> S4SlitGaps:
235
+ def s4_slit_gaps() -> S4SlitGaps:
251
236
  """Get the i04 s4_slit_gaps device, instantiate it if it hasn't already been.
252
237
  If this is called when already instantiated in i04, it will return the existing object.
253
238
  """
254
- return device_instantiation(
255
- S4SlitGaps,
256
- "s4_slit_gaps",
257
- "-AL-SLITS-04:",
258
- wait=False,
259
- fake=mock,
260
- )
239
+ return S4SlitGaps(f"{PREFIX.beamline_prefix}-AL-SLITS-04:")
261
240
 
262
241
 
263
242
  @device_factory()
@@ -266,7 +245,6 @@ def undulator() -> Undulator:
266
245
  If this is called when already instantiated in i04, it will return the existing object.
267
246
  """
268
247
  return Undulator(
269
- name="undulator",
270
248
  prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:",
271
249
  id_gap_lookup_table_path="/dls_sw/i04/software/gda/config/lookupTables/BeamLine_Undulator_toGap.txt",
272
250
  )
@@ -277,10 +255,7 @@ def synchrotron() -> Synchrotron:
277
255
  """Get the i04 synchrotron device, instantiate it if it hasn't already been.
278
256
  If this is called when already instantiated in i04, it will return the existing object.
279
257
  """
280
- return Synchrotron(
281
- "",
282
- "synchrotron",
283
- )
258
+ return Synchrotron()
284
259
 
285
260
 
286
261
  @device_factory()
@@ -289,7 +264,6 @@ def zebra() -> Zebra:
289
264
  If this is called when already instantiated in i04, it will return the existing object.
290
265
  """
291
266
  return Zebra(
292
- name="zebra",
293
267
  prefix=f"{PREFIX.beamline_prefix}-EA-ZEBRA-01:",
294
268
  mapping=I04_ZEBRA_MAPPING,
295
269
  )
@@ -330,7 +304,6 @@ def detector_motion() -> DetectorMotion:
330
304
  If this is called when already instantiated in i04, it will return the existing object.
331
305
  """
332
306
  return DetectorMotion(
333
- name="detector_motion",
334
307
  prefix=PREFIX.beamline_prefix,
335
308
  )
336
309
 
@@ -342,7 +315,6 @@ def thawer() -> Thawer:
342
315
  """
343
316
  return Thawer(
344
317
  f"{PREFIX.beamline_prefix}-EA-THAW-01",
345
- "thawer",
346
318
  )
347
319
 
348
320
 
@@ -352,7 +324,6 @@ def robot() -> BartRobot:
352
324
  If this is called when already instantiated in i04, it will return the existing object.
353
325
  """
354
326
  return BartRobot(
355
- "robot",
356
327
  f"{PREFIX.beamline_prefix}-MO-ROBOT-01:",
357
328
  )
358
329
 
@@ -364,7 +335,6 @@ def oav_to_redis_forwarder() -> OAVToRedisForwarder:
364
335
  """
365
336
  return OAVToRedisForwarder(
366
337
  f"{PREFIX.beamline_prefix}-DI-OAV-01:",
367
- name="oav_to_redis_forwarder",
368
338
  redis_host=RedisConstants.REDIS_HOST,
369
339
  redis_password=RedisConstants.REDIS_PASSWORD,
370
340
  redis_db=RedisConstants.MURKO_REDIS_DB,
@@ -377,7 +347,6 @@ def murko_results() -> MurkoResultsDevice:
377
347
  If this is called when already instantiated in i04, it will return the existing object.
378
348
  """
379
349
  return MurkoResultsDevice(
380
- name="murko_results",
381
350
  redis_host=RedisConstants.REDIS_HOST,
382
351
  redis_password=RedisConstants.REDIS_PASSWORD,
383
352
  redis_db=RedisConstants.MURKO_REDIS_DB,
@@ -399,7 +368,7 @@ def zocalo() -> ZocaloResults:
399
368
  """Get the i04 ZocaloResults device, instantiate it if it hasn't already been.
400
369
  If this is called when already instantiated in i04, it will return the existing object.
401
370
  """
402
- return ZocaloResults(channel="xrc.i04")
371
+ return ZocaloResults(channel="xrc.i04", results_source=ZocaloSource.CPU)
403
372
 
404
373
 
405
374
  @device_factory()
dodal/beamlines/i05.py CHANGED
@@ -1,6 +1,6 @@
1
+ from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm
1
2
  from dodal.common.beamlines.beamline_utils import device_factory
2
3
  from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
3
- from dodal.devices.i05 import Grating
4
4
  from dodal.devices.pgm import PGM
5
5
  from dodal.devices.synchrotron import Synchrotron
6
6
  from dodal.log import set_beamline as set_log_beamline
@@ -19,4 +19,4 @@ def synchrotron() -> Synchrotron:
19
19
 
20
20
  @device_factory()
21
21
  def pgm() -> PGM:
22
- return PGM(prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:", grating=Grating)
22
+ return i05_pgm()
dodal/beamlines/i05_1.py CHANGED
@@ -1,7 +1,7 @@
1
- from dodal.common.beamlines.beamline_utils import (
2
- device_factory,
3
- )
1
+ from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm
2
+ from dodal.common.beamlines.beamline_utils import device_factory
4
3
  from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
4
+ from dodal.devices.pgm import PGM
5
5
  from dodal.devices.synchrotron import Synchrotron
6
6
  from dodal.log import set_beamline as set_log_beamline
7
7
  from dodal.utils import BeamlinePrefix, get_beamline_name
@@ -12,6 +12,11 @@ set_log_beamline(BL)
12
12
  set_utils_beamline(BL)
13
13
 
14
14
 
15
+ @device_factory()
16
+ def pgm() -> PGM:
17
+ return i05_pgm()
18
+
19
+
15
20
  @device_factory()
16
21
  def synchrotron() -> Synchrotron:
17
22
  return Synchrotron()
dodal/beamlines/i13_1.py CHANGED
@@ -52,7 +52,6 @@ def side_camera() -> AravisDetector:
52
52
  def merlin() -> Merlin:
53
53
  return Merlin(
54
54
  prefix=f"{PREFIX}-EA-DET-04:",
55
- name="merlin",
56
55
  drv_suffix="CAM:",
57
56
  fileio_suffix="HDF5:",
58
57
  path_provider=get_path_provider(),
dodal/beamlines/i19_1.py CHANGED
@@ -65,7 +65,6 @@ def zebra() -> Zebra:
65
65
  """
66
66
  return Zebra(
67
67
  mapping=I19_1_ZEBRA_MAPPING,
68
- name="zebra",
69
68
  prefix=f"{PREFIX.beamline_prefix}-EA-ZEBRA-03:",
70
69
  )
71
70
 
dodal/beamlines/i19_2.py CHANGED
@@ -47,7 +47,6 @@ def zebra() -> Zebra:
47
47
  """
48
48
  return Zebra(
49
49
  mapping=I19_2_ZEBRA_MAPPING,
50
- name="zebra",
51
50
  prefix=f"{PREFIX.beamline_prefix}-EA-ZEBRA-01:",
52
51
  )
53
52
 
@@ -20,10 +20,7 @@ def shutter() -> HutchShutter:
20
20
  """Get the i19 hutch shutter device, instantiate it if it hasn't already been.
21
21
  If this is called when already instantiated, it will return the existing object.
22
22
  """
23
- return HutchShutter(
24
- f"{PREFIX.beamline_prefix}-PS-SHTR-01:",
25
- "shutter",
26
- )
23
+ return HutchShutter(f"{PREFIX.beamline_prefix}-PS-SHTR-01:")
27
24
 
28
25
 
29
26
  @device_factory()
dodal/beamlines/i23.py CHANGED
@@ -72,7 +72,7 @@ def oav_pin_tip_detection() -> PinTipDetection:
72
72
  @device_factory()
73
73
  def shutter() -> ZebraShutter:
74
74
  """Get the i23 zebra controlled shutter."""
75
- return ZebraShutter(f"{PREFIX.beamline_prefix}-EA-SHTR-01:", "shutter")
75
+ return ZebraShutter(f"{PREFIX.beamline_prefix}-EA-SHTR-01:")
76
76
 
77
77
 
78
78
  @device_factory()
@@ -85,7 +85,6 @@ def gonio() -> SixAxisGonio:
85
85
  def zebra() -> Zebra:
86
86
  """Get the i23 zebra"""
87
87
  return Zebra(
88
- name="zebra",
89
88
  prefix=f"{PREFIX.beamline_prefix}-EA-ZEBRA-01:ZEBRA:",
90
89
  mapping=I23_ZEBRA_MAPPING,
91
90
  )
dodal/beamlines/i24.py CHANGED
@@ -51,7 +51,6 @@ def attenuator() -> ReadOnlyAttenuator:
51
51
  existing object."""
52
52
  return ReadOnlyAttenuator(
53
53
  f"{PREFIX.beamline_prefix}-OP-ATTN-01:",
54
- "attenuator",
55
54
  )
56
55
 
57
56
 
@@ -62,7 +61,6 @@ def aperture() -> Aperture:
62
61
  """
63
62
  return Aperture(
64
63
  f"{PREFIX.beamline_prefix}-AL-APTR-01:",
65
- "aperture",
66
64
  )
67
65
 
68
66
 
@@ -73,7 +71,6 @@ def beamstop() -> Beamstop:
73
71
  """
74
72
  return Beamstop(
75
73
  f"{PREFIX.beamline_prefix}-MO-BS-01:",
76
- "beamstop",
77
74
  )
78
75
 
79
76
 
@@ -84,7 +81,6 @@ def backlight() -> DualBacklight:
84
81
  """
85
82
  return DualBacklight(
86
83
  prefix=PREFIX.beamline_prefix,
87
- name="backlight",
88
84
  )
89
85
 
90
86
 
@@ -94,7 +90,6 @@ def detector_motion() -> YZStage:
94
90
  If this is called when already instantiated in i24, it will return the existing object.
95
91
  """
96
92
  return YZStage(
97
- name="detector_motion",
98
93
  prefix=f"{PREFIX.beamline_prefix}-EA-DET-01:",
99
94
  )
100
95
 
@@ -105,7 +100,6 @@ def dcm() -> DCM:
105
100
  If this is called when already instantiated in i24, it will return the existing object.
106
101
  """
107
102
  return DCM(
108
- name="dcm",
109
103
  prefix=PREFIX.beamline_prefix,
110
104
  )
111
105
 
@@ -145,7 +139,6 @@ def pmac() -> PMAC:
145
139
  # prefix not BL but ME14E
146
140
  return PMAC(
147
141
  "ME14E-MO-CHIP-01:",
148
- "pmac",
149
142
  )
150
143
 
151
144
 
@@ -153,7 +146,6 @@ def pmac() -> PMAC:
153
146
  def oav() -> OAVBeamCentreFile:
154
147
  return OAVBeamCentreFile(
155
148
  prefix=f"{PREFIX.beamline_prefix}-DI-OAV-01:",
156
- name="oav",
157
149
  config=OAVConfigBeamCentre(ZOOM_PARAMS_FILE, DISPLAY_CONFIG),
158
150
  )
159
151
 
@@ -165,7 +157,6 @@ def vgonio() -> VerticalGoniometer:
165
157
  """
166
158
  return VerticalGoniometer(
167
159
  f"{PREFIX.beamline_prefix}-MO-VGON-01:",
168
- "vgonio",
169
160
  )
170
161
 
171
162
 
@@ -175,7 +166,6 @@ def zebra() -> Zebra:
175
166
  If this is called when already instantiated in i24, it will return the existing object.
176
167
  """
177
168
  return Zebra(
178
- name="zebra",
179
169
  prefix=f"{PREFIX.beamline_prefix}-EA-ZEBRA-01:",
180
170
  mapping=I24_ZEBRA_MAPPING,
181
171
  )
@@ -188,7 +178,6 @@ def shutter() -> HutchShutter:
188
178
  """
189
179
  return HutchShutter(
190
180
  f"{PREFIX.beamline_prefix}-PS-SHTR-01:",
191
- "shutter",
192
181
  )
193
182
 
194
183
 
@@ -197,7 +186,6 @@ def focus_mirrors() -> FocusMirrorsMode:
197
186
  """Get the i24 focus mirror devise to find the beam size."""
198
187
  return FocusMirrorsMode(
199
188
  f"{PREFIX.beamline_prefix}-OP-MFM-01:",
200
- "focus_mirrors",
201
189
  )
202
190
 
203
191
 
@@ -1,37 +1,4 @@
1
- from dodal.common.beamlines.beamline_utils import device_instantiation
2
- from dodal.devices.slits import Slits
3
- from dodal.utils import skip_device
4
-
5
1
  HDF5_SUFFIX = "HDF5:"
6
2
  TIFF_SUFFIX = "TIFF:"
7
3
  CAM_SUFFIX = "CAM:"
8
4
  DET_SUFFIX = "DET:"
9
-
10
-
11
- @skip_device()
12
- def numbered_slits(
13
- slit_number: int = 1,
14
- wait_for_connection: bool = True,
15
- fake_with_ophyd_sim: bool = False,
16
- ) -> Slits:
17
- """
18
- Create a slits object following the {beamline}-AL-SLITS-{slit_number} PV
19
- convention
20
-
21
- Args:
22
- slit_number: The number assigned to the slits in the control system, usually
23
- its position in the assembly. Defaults to 1.
24
- wait_for_connection: Require connection on instantiation. Defaults to True.
25
- fake_with_ophyd_sim: Make a fake device. Defaults to False.
26
-
27
- Returns:
28
- Slits: A new slits object
29
- """
30
-
31
- return device_instantiation(
32
- Slits,
33
- f"slits_{slit_number}",
34
- f"-AL-SLITS-{slit_number:02}:",
35
- wait_for_connection,
36
- fake_with_ophyd_sim,
37
- )
File without changes
@@ -1,10 +1,6 @@
1
- import asyncio
2
- import math
3
-
4
- from ophyd_async.core import derived_signal_rw
5
1
  from ophyd_async.epics.motor import Motor
6
2
 
7
- from dodal.devices.motors import XYZStage
3
+ from dodal.devices.motors import XYZStage, create_axis_perp_to_rotation
8
4
 
9
5
 
10
6
  class Goniometer(XYZStage):
@@ -23,26 +19,7 @@ class Goniometer(XYZStage):
23
19
  self.sampy = Motor(prefix + "SAMPY")
24
20
  self.sampz = Motor(prefix + "SAMPZ")
25
21
  self.omega = Motor(prefix + "OMEGA")
26
- self.vertical_position = derived_signal_rw(
27
- self._get,
28
- self._set,
29
- sampy=self.sampy,
30
- sampz=self.sampz,
31
- omega=self.omega,
22
+ self.vertical_position = create_axis_perp_to_rotation(
23
+ self.omega, self.sampz, self.sampy
32
24
  )
33
25
  super().__init__(name)
34
-
35
- def _get(self, sampz: float, sampy: float, omega: float) -> float:
36
- z_component = sampz * math.cos(math.radians(omega))
37
- y_component = sampy * math.sin(math.radians(omega))
38
- return z_component + y_component
39
-
40
- async def _set(self, value: float) -> None:
41
- omega = await self.omega.user_readback.get_value()
42
- z_component = value * math.cos(math.radians(omega))
43
- y_component = value * math.sin(math.radians(omega))
44
- await asyncio.gather(
45
- self.sampy.set(y_component),
46
- self.sampz.set(z_component),
47
- self.omega.set(omega),
48
- )
@@ -16,7 +16,7 @@ class LidHeatEnable(StrictEnum):
16
16
 
17
17
 
18
18
  class LaserRobot(BartRobot):
19
- def __init__(self, name: str, prefix: str) -> None:
19
+ def __init__(self, prefix: str, name: str = "") -> None:
20
20
  self.dewar_lid_heater = epics_signal_rw(
21
21
  LidHeatEnable, prefix + "DW_1_ENABLED", prefix + "DW_1_CTRL"
22
22
  )
@@ -24,4 +24,4 @@ class LaserRobot(BartRobot):
24
24
  self.set_beamline_safe = epics_signal_rw(
25
25
  ForceBit, prefix + "IP_16_FORCE_OPTION"
26
26
  )
27
- super().__init__(name=name, prefix=prefix)
27
+ super().__init__(prefix, name)
@@ -113,7 +113,7 @@ class SingleMirrorVoltage(Device):
113
113
 
114
114
  class MirrorVoltages(StandardReadable):
115
115
  def __init__(
116
- self, name: str, prefix: str, *args, daq_configuration_path: str, **kwargs
116
+ self, prefix: str, name: str = "", *args, daq_configuration_path: str, **kwargs
117
117
  ):
118
118
  self.voltage_lookup_table_path = (
119
119
  daq_configuration_path + "/json/mirrorFocus.json"
@@ -27,6 +27,8 @@ class UndulatorDCM(StandardReadable, Movable[float]):
27
27
  instead. See https://github.com/DiamondLightSource/dodal/issues/1092
28
28
  """
29
29
 
30
+ DCM_PERP_TOLERANCE = 0.01
31
+
30
32
  def __init__(
31
33
  self,
32
34
  undulator: Undulator,
@@ -60,6 +62,9 @@ class UndulatorDCM(StandardReadable, Movable[float]):
60
62
  self.dcm_ref().energy_in_kev.set(value, timeout=ENERGY_TIMEOUT_S),
61
63
  self.undulator_ref().set(value),
62
64
  )
63
- # DCM Perp pitch
64
- LOGGER.info(f"Adjusting DCM offset to {self.dcm_fixed_offset_mm} mm")
65
- await self.dcm_ref().offset_in_mm.set(self.dcm_fixed_offset_mm)
65
+
66
+ # The DCM perp is under vacuum so for heat management it's best to only change it when required
67
+ current_offset = await self.dcm_ref().offset_in_mm.user_readback.get_value()
68
+ if abs(current_offset - self.dcm_fixed_offset_mm) > self.DCM_PERP_TOLERANCE:
69
+ LOGGER.info(f"Adjusting DCM offset to {self.dcm_fixed_offset_mm} mm")
70
+ await self.dcm_ref().offset_in_mm.set(self.dcm_fixed_offset_mm)
@@ -1,19 +1,15 @@
1
1
  from ophyd_async.epics.core import epics_signal_rw
2
- from ophyd_async.epics.motor import Motor
3
2
 
4
- from dodal.devices.motors import XYZStage
3
+ from dodal.devices.motors import XYZPitchYawRollStage
5
4
 
6
5
 
7
- class PiezoMirror(XYZStage):
6
+ class PiezoMirror(XYZPitchYawRollStage):
8
7
  def __init__(
9
8
  self,
10
9
  prefix: str,
11
10
  name: str = "",
12
11
  ):
13
12
  with self.add_children_as_readables():
14
- self.yaw = Motor(prefix + "YAW")
15
- self.pitch = Motor(prefix + "PITCH")
16
- self.roll = Motor(prefix + "ROLL")
17
13
  self.fine_pitch = epics_signal_rw(
18
14
  float,
19
15
  read_pv=prefix + "FPITCH:RBV:AI",
dodal/devices/motors.py CHANGED
@@ -1,6 +1,8 @@
1
+ import asyncio
2
+ import math
1
3
  from abc import ABC
2
4
 
3
- from ophyd_async.core import StandardReadable
5
+ from ophyd_async.core import StandardReadable, derived_signal_rw
4
6
  from ophyd_async.epics.motor import Motor
5
7
 
6
8
  _X, _Y, _Z = "X", "Y", "Z"
@@ -89,6 +91,25 @@ class XYPitchStage(XYStage):
89
91
  super().__init__(prefix, name, x_infix, y_infix)
90
92
 
91
93
 
94
+ class XYZPitchYawRollStage(XYZStage):
95
+ def __init__(
96
+ self,
97
+ prefix: str,
98
+ name: str = "",
99
+ x_infix: str = _X,
100
+ y_infix: str = _Y,
101
+ z_infix: str = _Z,
102
+ pitch_infix: str = "PITCH",
103
+ yaw_infix: str = "YAW",
104
+ roll_infix: str = "ROLL",
105
+ ):
106
+ with self.add_children_as_readables():
107
+ self.pitch = Motor(prefix + pitch_infix)
108
+ self.yaw = Motor(prefix + yaw_infix)
109
+ self.roll = Motor(prefix + roll_infix)
110
+ super().__init__(prefix, name, x_infix, y_infix, z_infix)
111
+
112
+
92
113
  class SixAxisGonio(XYZStage):
93
114
  def __init__(
94
115
  self,
@@ -101,12 +122,19 @@ class SixAxisGonio(XYZStage):
101
122
  phi_infix: str = "PHI",
102
123
  omega_infix: str = "OMEGA",
103
124
  ):
125
+ """Six-axis goniometer with a standard xyz stage and three axes of rotation:
126
+ kappa, phi and omega.
127
+ """
104
128
  with self.add_children_as_readables():
105
129
  self.kappa = Motor(prefix + kappa_infix)
106
130
  self.phi = Motor(prefix + phi_infix)
107
131
  self.omega = Motor(prefix + omega_infix)
108
132
  super().__init__(prefix, name, x_infix, y_infix, z_infix)
109
133
 
134
+ self.vertical_in_lab_space = create_axis_perp_to_rotation(
135
+ self.omega, self.y, self.z
136
+ )
137
+
110
138
 
111
139
  class YZStage(Stage):
112
140
  def __init__(
@@ -116,3 +144,49 @@ class YZStage(Stage):
116
144
  self.y = Motor(prefix + y_infix)
117
145
  self.z = Motor(prefix + z_infix)
118
146
  super().__init__(name)
147
+
148
+
149
+ def create_axis_perp_to_rotation(motor_theta: Motor, motor_i: Motor, motor_j: Motor):
150
+ """Given a signal that controls a motor in a rotation axis and two other
151
+ signals controlling motors on a pair of orthogonal axes, these axes being in the
152
+ rotating frame of reference created by the first axis, create a derived signal
153
+ that is a projection of the two axes in the non-rotating frame of reference.
154
+
155
+ The projection is onto the axis defined by i when the rotation angle is 0 and
156
+ defined by j when the angle is at 90.
157
+
158
+ The usual use case for this is translating from sample space to lab space. For
159
+ example, if you have a sample that is mounted on a goniometer to the right hand side
160
+ of an OAV view this can provide an axis that will move the sample up/down in that
161
+ view regardless of the omega orientation of the sample.
162
+
163
+ Args:
164
+ motor_theta (Motor): this is the rotation axis of the sample.
165
+ motor_i (Motor): this is the axis that, when the sample is at 0 deg rotation,
166
+ a move here is entirely parallel with the derived axis.
167
+ motor_j (Motor): this is the axis that, when the sample is at 90 deg rotation,
168
+ a move here is entirely parallel with the derived axis.
169
+ """
170
+
171
+ def _get(j_val: float, i_val: float, rot_value: float) -> float:
172
+ i_component = i_val * math.cos(math.radians(rot_value))
173
+ j_component = j_val * math.sin(math.radians(rot_value))
174
+ return i_component + j_component
175
+
176
+ async def _set(vertical_value: float) -> None:
177
+ rot_value = await motor_theta.user_readback.get_value()
178
+ i_component = vertical_value * math.cos(math.radians(rot_value))
179
+ j_component = vertical_value * math.sin(math.radians(rot_value))
180
+ await asyncio.gather(
181
+ motor_i.set(i_component),
182
+ motor_j.set(j_component),
183
+ motor_theta.set(rot_value),
184
+ )
185
+
186
+ return derived_signal_rw(
187
+ _get,
188
+ _set,
189
+ i_val=motor_i,
190
+ j_val=motor_j,
191
+ rot_value=motor_theta,
192
+ )
dodal/devices/robot.py CHANGED
@@ -77,7 +77,7 @@ class BartRobot(StandardReadable, Movable[SampleLocation]):
77
77
  # How far the gonio position can be out before loading will fail
78
78
  LOAD_TOLERANCE_MM = 0.02
79
79
 
80
- def __init__(self, name: str, prefix: str) -> None:
80
+ def __init__(self, prefix: str, name: str = "") -> None:
81
81
  with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
82
82
  self.barcode = epics_signal_r(str, prefix + "BARCODE")
83
83
  self.gonio_pin_sensor = epics_signal_r(PinMounted, prefix + "PIN_MOUNTED")
dodal/devices/webcam.py CHANGED
@@ -12,6 +12,7 @@ from ophyd_async.core import (
12
12
  soft_signal_rw,
13
13
  )
14
14
  from PIL import Image
15
+ from yarl import URL
15
16
 
16
17
  from dodal.log import LOGGER
17
18
 
@@ -26,7 +27,7 @@ def create_placeholder_image() -> ByteString:
26
27
 
27
28
 
28
29
  class Webcam(StandardReadable, Triggerable):
29
- def __init__(self, name, prefix, url):
30
+ def __init__(self, url: URL, name: str = ""):
30
31
  self.url = url
31
32
  self.filename = soft_signal_rw(str, name="filename")
32
33
  self.directory = soft_signal_rw(str, name="directory")
@@ -286,7 +286,7 @@ class SoftInputs(StandardReadable):
286
286
  class Zebra(StandardReadable):
287
287
  """The Zebra device."""
288
288
 
289
- def __init__(self, mapping: ZebraMapping, name: str, prefix: str) -> None:
289
+ def __init__(self, mapping: ZebraMapping, prefix: str, name: str = "") -> None:
290
290
  self.mapping = mapping
291
291
  self.pc = PositionCompare(prefix, name)
292
292
  self.output = ZebraOutputPanel(prefix, name)