dls-dodal 1.33.0__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 (89) hide show
  1. {dls_dodal-1.33.0.dist-info → dls_dodal-1.35.0.dist-info}/METADATA +3 -3
  2. dls_dodal-1.35.0.dist-info/RECORD +147 -0
  3. {dls_dodal-1.33.0.dist-info → dls_dodal-1.35.0.dist-info}/WHEEL +1 -1
  4. dodal/__init__.py +8 -0
  5. dodal/_version.py +2 -2
  6. dodal/beamline_specific_utils/i03.py +6 -2
  7. dodal/beamlines/__init__.py +2 -3
  8. dodal/beamlines/i03.py +41 -9
  9. dodal/beamlines/i04.py +26 -4
  10. dodal/beamlines/i10.py +257 -0
  11. dodal/beamlines/i22.py +25 -13
  12. dodal/beamlines/i24.py +11 -11
  13. dodal/beamlines/p38.py +24 -13
  14. dodal/common/beamlines/beamline_utils.py +1 -2
  15. dodal/common/crystal_metadata.py +61 -0
  16. dodal/common/signal_utils.py +10 -14
  17. dodal/common/types.py +2 -7
  18. dodal/devices/CTAB.py +1 -1
  19. dodal/devices/aperture.py +1 -1
  20. dodal/devices/aperturescatterguard.py +20 -8
  21. dodal/devices/apple2_undulator.py +603 -0
  22. dodal/devices/areadetector/plugins/CAM.py +29 -0
  23. dodal/devices/areadetector/plugins/MJPG.py +51 -106
  24. dodal/devices/attenuator.py +1 -1
  25. dodal/devices/backlight.py +11 -11
  26. dodal/devices/cryostream.py +3 -5
  27. dodal/devices/dcm.py +26 -2
  28. dodal/devices/detector/detector_motion.py +3 -5
  29. dodal/devices/diamond_filter.py +46 -0
  30. dodal/devices/eiger.py +6 -2
  31. dodal/devices/eiger_odin.py +48 -39
  32. dodal/devices/fast_grid_scan.py +1 -1
  33. dodal/devices/fluorescence_detector_motion.py +5 -7
  34. dodal/devices/focusing_mirror.py +26 -19
  35. dodal/devices/hutch_shutter.py +4 -5
  36. dodal/devices/i10/i10_apple2.py +399 -0
  37. dodal/devices/i10/i10_setting_data.py +7 -0
  38. dodal/devices/i22/dcm.py +50 -83
  39. dodal/devices/i22/fswitch.py +5 -5
  40. dodal/devices/i24/aperture.py +3 -5
  41. dodal/devices/i24/beamstop.py +3 -5
  42. dodal/devices/i24/dcm.py +1 -1
  43. dodal/devices/i24/dual_backlight.py +9 -11
  44. dodal/devices/i24/pmac.py +35 -46
  45. dodal/devices/i24/vgonio.py +16 -0
  46. dodal/devices/ipin.py +5 -3
  47. dodal/devices/linkam3.py +7 -7
  48. dodal/devices/oav/oav_calculations.py +22 -0
  49. dodal/devices/oav/oav_detector.py +118 -83
  50. dodal/devices/oav/oav_parameters.py +50 -104
  51. dodal/devices/oav/oav_to_redis_forwarder.py +77 -35
  52. dodal/devices/oav/pin_image_recognition/__init__.py +9 -7
  53. dodal/devices/oav/{grid_overlay.py → snapshots/grid_overlay.py} +16 -59
  54. dodal/devices/oav/snapshots/snapshot_with_beam_centre.py +64 -0
  55. dodal/devices/oav/snapshots/snapshot_with_grid.py +57 -0
  56. dodal/devices/oav/utils.py +28 -27
  57. dodal/devices/p99/sample_stage.py +3 -5
  58. dodal/devices/pgm.py +40 -0
  59. dodal/devices/qbpm.py +18 -0
  60. dodal/devices/robot.py +5 -5
  61. dodal/devices/smargon.py +3 -3
  62. dodal/devices/synchrotron.py +9 -4
  63. dodal/devices/tetramm.py +9 -9
  64. dodal/devices/thawer.py +13 -7
  65. dodal/devices/undulator.py +7 -6
  66. dodal/devices/util/adjuster_plans.py +1 -1
  67. dodal/devices/util/epics_util.py +1 -1
  68. dodal/devices/util/lookup_tables.py +4 -5
  69. dodal/devices/watsonmarlow323_pump.py +45 -0
  70. dodal/devices/webcam.py +9 -2
  71. dodal/devices/xbpm_feedback.py +3 -5
  72. dodal/devices/xspress3/xspress3.py +8 -9
  73. dodal/devices/xspress3/xspress3_channel.py +3 -5
  74. dodal/devices/zebra.py +12 -8
  75. dodal/devices/zebra_controlled_shutter.py +5 -6
  76. dodal/devices/zocalo/__init__.py +2 -2
  77. dodal/devices/zocalo/zocalo_constants.py +3 -0
  78. dodal/devices/zocalo/zocalo_interaction.py +2 -1
  79. dodal/devices/zocalo/zocalo_results.py +105 -89
  80. dodal/plans/data_session_metadata.py +2 -2
  81. dodal/plans/motor_util_plans.py +11 -9
  82. dodal/utils.py +11 -0
  83. dls_dodal-1.33.0.dist-info/RECORD +0 -136
  84. dodal/beamlines/i04_1.py +0 -140
  85. dodal/devices/i24/i24_vgonio.py +0 -17
  86. dodal/devices/oav/oav_errors.py +0 -35
  87. {dls_dodal-1.33.0.dist-info → dls_dodal-1.35.0.dist-info}/LICENSE +0 -0
  88. {dls_dodal-1.33.0.dist-info → dls_dodal-1.35.0.dist-info}/entry_points.txt +0 -0
  89. {dls_dodal-1.33.0.dist-info → dls_dodal-1.35.0.dist-info}/top_level.txt +0 -0
@@ -5,8 +5,11 @@ import bluesky.plan_stubs as bps
5
5
  import numpy as np
6
6
  from bluesky.utils import Msg
7
7
 
8
- from dodal.devices.oav.oav_calculations import camera_coordinates_to_xyz
9
- from dodal.devices.oav.oav_detector import OAVConfigParams
8
+ from dodal.devices.oav.oav_calculations import (
9
+ calculate_beam_distance,
10
+ camera_coordinates_to_xyz,
11
+ )
12
+ from dodal.devices.oav.oav_detector import OAV
10
13
  from dodal.devices.oav.pin_image_recognition import PinTipDetection
11
14
  from dodal.devices.smargon import Smargon
12
15
 
@@ -36,21 +39,6 @@ def bottom_right_from_top_left(
36
39
  )
37
40
 
38
41
 
39
- class ColorMode(IntEnum):
40
- """
41
- Enum to store the various color modes of the camera. We use RGB1.
42
- """
43
-
44
- MONO = 0
45
- BAYER = 1
46
- RGB1 = 2
47
- RGB2 = 3
48
- RGB3 = 4
49
- YUV444 = 5
50
- YUV422 = 6
51
- YUV421 = 7
52
-
53
-
54
42
  class EdgeOutputArrayImageType(IntEnum):
55
43
  """
56
44
  Enum to store the types of image to tweak the output array. We use Original.
@@ -64,7 +52,7 @@ class EdgeOutputArrayImageType(IntEnum):
64
52
 
65
53
 
66
54
  def get_move_required_so_that_beam_is_at_pixel(
67
- smargon: Smargon, pixel: Pixel, oav_params: OAVConfigParams
55
+ smargon: Smargon, pixel: Pixel, oav: OAV
68
56
  ) -> Generator[Msg, None, np.ndarray]:
69
57
  """Calculate the required move so that the given pixel is in the centre of the beam."""
70
58
 
@@ -78,22 +66,35 @@ def get_move_required_so_that_beam_is_at_pixel(
78
66
  )
79
67
  current_angle = yield from bps.rd(smargon.omega)
80
68
 
81
- return calculate_x_y_z_of_pixel(current_motor_xyz, current_angle, pixel, oav_params)
69
+ beam_x = yield from bps.rd(oav.beam_centre_i)
70
+ beam_y = yield from bps.rd(oav.beam_centre_j)
71
+ microns_per_pixel_x = yield from bps.rd(oav.microns_per_pixel_x)
72
+ microns_per_pixel_y = yield from bps.rd(oav.microns_per_pixel_y)
73
+
74
+ return calculate_x_y_z_of_pixel(
75
+ current_motor_xyz,
76
+ current_angle,
77
+ pixel,
78
+ (beam_x, beam_y),
79
+ (microns_per_pixel_x, microns_per_pixel_y),
80
+ )
82
81
 
83
82
 
84
83
  def calculate_x_y_z_of_pixel(
85
- current_x_y_z, current_omega, pixel: Pixel, oav_params: OAVConfigParams
84
+ current_x_y_z,
85
+ current_omega,
86
+ pixel: Pixel,
87
+ beam_centre: tuple[int, int],
88
+ microns_per_pixel: tuple[float, float],
86
89
  ) -> np.ndarray:
87
- beam_distance_px: Pixel = oav_params.calculate_beam_distance(*pixel)
90
+ beam_distance_px: Pixel = calculate_beam_distance(beam_centre, *pixel)
88
91
 
89
- assert oav_params.micronsPerXPixel
90
- assert oav_params.micronsPerYPixel
91
92
  return current_x_y_z + camera_coordinates_to_xyz(
92
93
  beam_distance_px[0],
93
94
  beam_distance_px[1],
94
95
  current_omega,
95
- oav_params.micronsPerXPixel,
96
- oav_params.micronsPerYPixel,
96
+ microns_per_pixel[0],
97
+ microns_per_pixel[1],
97
98
  )
98
99
 
99
100
 
@@ -102,8 +103,8 @@ def wait_for_tip_to_be_found(
102
103
  ) -> Generator[Msg, None, Pixel]:
103
104
  yield from bps.trigger(ophyd_pin_tip_detection, wait=True)
104
105
  found_tip = yield from bps.rd(ophyd_pin_tip_detection.triggered_tip)
105
- if found_tip == ophyd_pin_tip_detection.INVALID_POSITION:
106
+ if all(found_tip == ophyd_pin_tip_detection.INVALID_POSITION):
106
107
  timeout = yield from bps.rd(ophyd_pin_tip_detection.validity_timeout)
107
108
  raise PinNotFoundException(f"No pin found after {timeout} seconds")
108
109
 
109
- return found_tip # type: ignore
110
+ return Pixel((int(found_tip[0]), int(found_tip[1])))
@@ -1,7 +1,5 @@
1
- from enum import Enum
2
-
3
- from ophyd_async.core import Device
4
- from ophyd_async.epics.signal import epics_signal_rw
1
+ from ophyd_async.core import Device, SubsetEnum
2
+ from ophyd_async.epics.core import epics_signal_rw
5
3
 
6
4
 
7
5
  class SampleAngleStage(Device):
@@ -18,7 +16,7 @@ class SampleAngleStage(Device):
18
16
  super().__init__(name=name)
19
17
 
20
18
 
21
- class p99StageSelections(str, Enum):
19
+ class p99StageSelections(SubsetEnum):
22
20
  Empty = "Empty"
23
21
  Mn5um = "Mn 5um"
24
22
  Fe = "Fe (empty)"
dodal/devices/pgm.py ADDED
@@ -0,0 +1,40 @@
1
+ from ophyd_async.core import (
2
+ StandardReadable,
3
+ StandardReadableFormat,
4
+ StrictEnum,
5
+ )
6
+ from ophyd_async.epics.core import epics_signal_rw
7
+ from ophyd_async.epics.motor import Motor
8
+
9
+
10
+ class PGM(StandardReadable):
11
+ """
12
+ Plane grating monochromator, it is use in soft x-ray beamline to generate monochromic beam.
13
+ """
14
+
15
+ def __init__(
16
+ self,
17
+ prefix: str,
18
+ grating: type[StrictEnum],
19
+ gratingPv: str,
20
+ name: str = "",
21
+ ) -> None:
22
+ """
23
+ Parameters
24
+ ----------
25
+ prefix:
26
+ Beamline specific part of the PV
27
+ grating:
28
+ The Enum for the grating table.
29
+ gratingPv:
30
+ The suffix pv part of grating Pv
31
+ name:
32
+ Name of the device
33
+ """
34
+ with self.add_children_as_readables():
35
+ self.energy = Motor(prefix + "ENERGY")
36
+ with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
37
+ self.grating = epics_signal_rw(grating, prefix + gratingPv)
38
+ self.cff = epics_signal_rw(float, prefix + "CFF")
39
+
40
+ super().__init__(name=name)
dodal/devices/qbpm.py ADDED
@@ -0,0 +1,18 @@
1
+ from ophyd_async.core import StandardReadable
2
+ from ophyd_async.epics.core import epics_signal_r
3
+
4
+
5
+ class QBPM(StandardReadable):
6
+ """
7
+ A beam position monitor that gives a position and intensity of the beam.
8
+ """
9
+
10
+ def __init__(
11
+ self,
12
+ prefix: str,
13
+ name: str = "",
14
+ ) -> None:
15
+ with self.add_children_as_readables():
16
+ self.intensity_uA = epics_signal_r(float, f"{prefix}INTEN")
17
+
18
+ super().__init__(name)
dodal/devices/robot.py CHANGED
@@ -1,16 +1,16 @@
1
1
  import asyncio
2
2
  from asyncio import FIRST_COMPLETED, CancelledError, Task, wait_for
3
3
  from dataclasses import dataclass
4
- from enum import Enum
5
4
 
6
5
  from bluesky.protocols import Movable
7
6
  from ophyd_async.core import (
8
7
  AsyncStatus,
9
8
  StandardReadable,
9
+ StrictEnum,
10
10
  set_and_wait_for_value,
11
11
  wait_for_value,
12
12
  )
13
- from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw_rbv, epics_signal_x
13
+ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw_rbv, epics_signal_x
14
14
 
15
15
  from dodal.log import LOGGER
16
16
 
@@ -33,7 +33,7 @@ class SampleLocation:
33
33
  pin: int
34
34
 
35
35
 
36
- class PinMounted(str, Enum):
36
+ class PinMounted(StrictEnum):
37
37
  NO_PIN_MOUNTED = "No Pin Mounted"
38
38
  PIN_MOUNTED = "Pin Mounted"
39
39
 
@@ -63,8 +63,8 @@ class BartRobot(StandardReadable, Movable):
63
63
  self.current_puck = epics_signal_r(float, prefix + "CURRENT_PUCK_RBV")
64
64
  self.current_pin = epics_signal_r(float, prefix + "CURRENT_PIN_RBV")
65
65
 
66
- self.next_sample_id = epics_signal_rw_rbv(float, prefix + "NEXT_ID")
67
- self.sample_id = epics_signal_r(float, prefix + "CURRENT_ID_RBV")
66
+ self.next_sample_id = epics_signal_rw_rbv(int, prefix + "NEXT_ID")
67
+ self.sample_id = epics_signal_r(int, prefix + "CURRENT_ID_RBV")
68
68
 
69
69
  self.load = epics_signal_x(prefix + "LOAD.PROC")
70
70
  self.program_running = epics_signal_r(bool, prefix + "PROGRAM_RUNNING")
dodal/devices/smargon.py CHANGED
@@ -7,8 +7,8 @@ from typing import cast
7
7
  from bluesky import plan_stubs as bps
8
8
  from bluesky.utils import Msg
9
9
  from ophyd_async.core import AsyncStatus, Device, StandardReadable, wait_for_value
10
+ from ophyd_async.epics.core import epics_signal_r
10
11
  from ophyd_async.epics.motor import Motor
11
- from ophyd_async.epics.signal import epics_signal_r
12
12
 
13
13
  from dodal.devices.util.epics_util import SetWhenEnabled
14
14
 
@@ -40,8 +40,8 @@ class StubOffsets(Device):
40
40
  super().__init__(name)
41
41
 
42
42
  @AsyncStatus.wrap
43
- async def set(self, pos: StubPosition):
44
- if pos == StubPosition.CURRENT_AS_CENTER:
43
+ async def set(self, value: StubPosition):
44
+ if value == StubPosition.CURRENT_AS_CENTER:
45
45
  await self.center_at_current_position.set(1)
46
46
  smargon = cast(Smargon, self.parent)
47
47
  await wait_for_value(
@@ -1,7 +1,12 @@
1
1
  from enum import Enum
2
2
 
3
- from ophyd_async.core import ConfigSignal, StandardReadable, soft_signal_r_and_setter
4
- from ophyd_async.epics.signal import epics_signal_r
3
+ from ophyd_async.core import (
4
+ StandardReadable,
5
+ StandardReadableFormat,
6
+ StrictEnum,
7
+ soft_signal_r_and_setter,
8
+ )
9
+ from ophyd_async.epics.core import epics_signal_r
5
10
 
6
11
 
7
12
  class Prefix(str, Enum):
@@ -19,7 +24,7 @@ class Suffix(str, Enum):
19
24
  END_COUNTDOWN = "ENDCOUNTDN"
20
25
 
21
26
 
22
- class SynchrotronMode(str, Enum):
27
+ class SynchrotronMode(StrictEnum):
23
28
  SHUTDOWN = "Shutdown"
24
29
  INJECTION = "Injection"
25
30
  NOBEAM = "No Beam"
@@ -44,7 +49,7 @@ class Synchrotron(StandardReadable):
44
49
  self.current = epics_signal_r(float, signal_prefix + Suffix.SIGNAL)
45
50
  self.energy = epics_signal_r(float, status_prefix + Suffix.BEAM_ENERGY)
46
51
 
47
- with self.add_children_as_readables(ConfigSignal):
52
+ with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
48
53
  self.probe, _ = soft_signal_r_and_setter(str, initial_value="x-ray")
49
54
  self.type, _ = soft_signal_r_and_setter(
50
55
  str, initial_value="Synchrotron X-ray Source"
dodal/devices/tetramm.py CHANGED
@@ -1,50 +1,50 @@
1
1
  import asyncio
2
- from enum import Enum
3
2
 
4
3
  from bluesky.protocols import Hints
5
4
  from ophyd_async.core import (
6
5
  DatasetDescriber,
7
- DetectorControl,
6
+ DetectorController,
8
7
  DetectorTrigger,
9
8
  Device,
10
9
  PathProvider,
11
10
  StandardDetector,
11
+ StrictEnum,
12
12
  TriggerInfo,
13
13
  set_and_wait_for_value,
14
14
  soft_signal_r_and_setter,
15
15
  )
16
16
  from ophyd_async.epics.adcore import ADHDFWriter, NDFileHDFIO, stop_busy_record
17
- from ophyd_async.epics.signal import (
17
+ from ophyd_async.epics.core import (
18
18
  epics_signal_r,
19
19
  epics_signal_rw,
20
20
  epics_signal_rw_rbv,
21
21
  )
22
22
 
23
23
 
24
- class TetrammRange(str, Enum):
24
+ class TetrammRange(StrictEnum):
25
25
  uA = "+- 120 uA"
26
26
  nA = "+- 120 nA"
27
27
 
28
28
 
29
- class TetrammTrigger(str, Enum):
29
+ class TetrammTrigger(StrictEnum):
30
30
  FreeRun = "Free run"
31
31
  ExtTrigger = "Ext. trig."
32
32
  ExtBulb = "Ext. bulb"
33
33
  ExtGate = "Ext. gate"
34
34
 
35
35
 
36
- class TetrammChannels(str, Enum):
36
+ class TetrammChannels(StrictEnum):
37
37
  One = "1"
38
38
  Two = "2"
39
39
  Four = "4"
40
40
 
41
41
 
42
- class TetrammResolution(str, Enum):
42
+ class TetrammResolution(StrictEnum):
43
43
  SixteenBits = "16 bits"
44
44
  TwentyFourBits = "24 bits"
45
45
 
46
46
 
47
- class TetrammGeometry(str, Enum):
47
+ class TetrammGeometry(StrictEnum):
48
48
  Diamond = "Diamond"
49
49
  Square = "Square"
50
50
 
@@ -80,7 +80,7 @@ class TetrammDriver(Device):
80
80
  super().__init__(name=name)
81
81
 
82
82
 
83
- class TetrammController(DetectorControl):
83
+ class TetrammController(DetectorController):
84
84
  """Controller for a TetrAMM current monitor
85
85
 
86
86
  Attributes:
dodal/devices/thawer.py CHANGED
@@ -1,36 +1,42 @@
1
1
  from asyncio import Task, create_task, sleep
2
- from enum import Enum
3
2
 
4
3
  from bluesky.protocols import Stoppable
5
- from ophyd_async.core import AsyncStatus, Device, SignalRW, StandardReadable
6
- from ophyd_async.epics.signal import epics_signal_rw
4
+ from ophyd_async.core import (
5
+ AsyncStatus,
6
+ Device,
7
+ Reference,
8
+ SignalRW,
9
+ StandardReadable,
10
+ StrictEnum,
11
+ )
12
+ from ophyd_async.epics.core import epics_signal_rw
7
13
 
8
14
 
9
15
  class ThawingException(Exception):
10
16
  pass
11
17
 
12
18
 
13
- class ThawerStates(str, Enum):
19
+ class ThawerStates(StrictEnum):
14
20
  OFF = "Off"
15
21
  ON = "On"
16
22
 
17
23
 
18
24
  class ThawingTimer(Device, Stoppable):
19
25
  def __init__(self, control_signal: SignalRW[ThawerStates]) -> None:
20
- self._control_signal = control_signal
26
+ self._control_signal_ref = Reference(control_signal)
21
27
  self._thawing_task: Task | None = None
22
28
  super().__init__("thaw_for_time_s")
23
29
 
24
30
  @AsyncStatus.wrap
25
31
  async def set(self, time_to_thaw_for: float):
26
- await self._control_signal.set(ThawerStates.ON)
32
+ await self._control_signal_ref().set(ThawerStates.ON)
27
33
  if self._thawing_task and not self._thawing_task.done():
28
34
  raise ThawingException("Thawing task already in progress")
29
35
  self._thawing_task = create_task(sleep(time_to_thaw_for))
30
36
  try:
31
37
  await self._thawing_task
32
38
  finally:
33
- await self._control_signal.set(ThawerStates.OFF)
39
+ await self._control_signal_ref().set(ThawerStates.OFF)
34
40
 
35
41
  @AsyncStatus.wrap
36
42
  async def stop(self, *args, **kwargs):
@@ -1,16 +1,17 @@
1
- from enum import Enum
1
+ import os
2
2
 
3
3
  import numpy as np
4
4
  from bluesky.protocols import Movable
5
5
  from numpy import argmin, ndarray
6
6
  from ophyd_async.core import (
7
7
  AsyncStatus,
8
- ConfigSignal,
9
8
  StandardReadable,
9
+ StandardReadableFormat,
10
+ StrictEnum,
10
11
  soft_signal_r_and_setter,
11
12
  )
13
+ from ophyd_async.epics.core import epics_signal_r
12
14
  from ophyd_async.epics.motor import Motor
13
- from ophyd_async.epics.signal import epics_signal_r
14
15
 
15
16
  from dodal.log import LOGGER
16
17
 
@@ -32,7 +33,7 @@ UNDULATOR_DISCREPANCY_THRESHOLD_MM = 2e-3
32
33
  STATUS_TIMEOUT_S: float = 10.0
33
34
 
34
35
 
35
- class UndulatorGapAccess(str, Enum):
36
+ class UndulatorGapAccess(StrictEnum):
36
37
  ENABLED = "ENABLED"
37
38
  DISABLED = "DISABLED"
38
39
 
@@ -54,7 +55,7 @@ class Undulator(StandardReadable, Movable):
54
55
  def __init__(
55
56
  self,
56
57
  prefix: str,
57
- id_gap_lookup_table_path: str,
58
+ id_gap_lookup_table_path: str = os.devnull,
58
59
  name: str = "",
59
60
  poles: int | None = None,
60
61
  length: float | None = None,
@@ -74,7 +75,7 @@ class Undulator(StandardReadable, Movable):
74
75
  self.current_gap = epics_signal_r(float, prefix + "CURRGAPD")
75
76
  self.gap_access = epics_signal_r(UndulatorGapAccess, prefix + "IDBLENA")
76
77
 
77
- with self.add_children_as_readables(ConfigSignal):
78
+ with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
78
79
  self.gap_discrepancy_tolerance_mm, _ = soft_signal_r_and_setter(
79
80
  float,
80
81
  initial_value=UNDULATOR_DISCREPANCY_THRESHOLD_MM,
@@ -21,6 +21,6 @@ def lookup_table_adjuster(
21
21
  def adjust(group=None) -> Generator[Msg, None, None]:
22
22
  setpoint = lookup_table(input)
23
23
  LOGGER.info(f"lookup_table_adjuster setting {output_device.name} to {setpoint}")
24
- yield from bps.abs_set(output_device, setpoint, group=group)
24
+ yield from bps.abs_set(output_device, setpoint, group=group) # type: ignore # See: https://github.com/DiamondLightSource/dodal/issues/827
25
25
 
26
26
  return adjust
@@ -7,7 +7,7 @@ from ophyd import Device as OphydDevice
7
7
  from ophyd.status import Status, StatusBase
8
8
  from ophyd_async.core import AsyncStatus, wait_for_value
9
9
  from ophyd_async.core import Device as OphydAsyncDevice
10
- from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
10
+ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
11
11
 
12
12
  from dodal.log import LOGGER
13
13
 
@@ -33,7 +33,10 @@ async def energy_distance_table(lookup_table_path: str) -> np.ndarray:
33
33
 
34
34
 
35
35
  def linear_interpolation_lut(filename: str) -> Callable[[float], float]:
36
- """Returns a callable that converts values by linear interpolation of lookup table values"""
36
+ """Returns a callable that converts values by linear interpolation of lookup table
37
+ values.
38
+
39
+ If the value falls outside the lookup table then the closest value will be used."""
37
40
  LOGGER.info(f"Using lookup table {filename}")
38
41
  s_and_t_vals = zip(*loadtxt(filename, comments=["#", "Units"]), strict=False)
39
42
 
@@ -54,10 +57,6 @@ def linear_interpolation_lut(filename: str) -> Callable[[float], float]:
54
57
  )
55
58
 
56
59
  def s_to_t2(s: float) -> float:
57
- if s < s_values[0] or s > s_values[len(s_values) - 1]:
58
- raise ValueError(
59
- f"Lookup table does not support extrapolation from file {filename}, s={s}"
60
- )
61
60
  return float(interp(s, s_values, t_values))
62
61
 
63
62
  return s_to_t2
@@ -0,0 +1,45 @@
1
+ from ophyd_async.core import StandardReadable, StandardReadableFormat, StrictEnum
2
+ from ophyd_async.epics.core import epics_signal_rw
3
+
4
+
5
+ class WatsonMarlow323PumpEnable(StrictEnum):
6
+ DISABLED = "Disabled"
7
+ ENABLED = "Enabled"
8
+
9
+
10
+ class WatsonMarlow323PumpDirection(StrictEnum):
11
+ CLOCKWISE = "CW"
12
+ COUNTER_CLOCKWISE = "CCW"
13
+
14
+
15
+ class WatsonMarlow323PumpState(StrictEnum):
16
+ STOPPED = "STOP"
17
+ STARTED = "START"
18
+
19
+
20
+ class WatsonMarlow323Pump(StandardReadable):
21
+ """Watson Marlow 323 Peristaltic Pump device"""
22
+
23
+ def __init__(self, prefix: str, name: str = "") -> None:
24
+ with self.add_children_as_readables():
25
+ self.direction = epics_signal_rw(
26
+ WatsonMarlow323PumpDirection,
27
+ read_pv=prefix + "INFO:DIR",
28
+ write_pv=prefix + "SET:DIR",
29
+ )
30
+ self.state = epics_signal_rw(
31
+ WatsonMarlow323PumpState,
32
+ read_pv=prefix + "INFO:RUN",
33
+ write_pv=prefix + "SET:RUN",
34
+ )
35
+ self.speed = epics_signal_rw(
36
+ float, read_pv=prefix + "INFO:SPD", write_pv=prefix + "SET:SPD"
37
+ )
38
+
39
+ with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
40
+ self.enabled = epics_signal_rw(
41
+ WatsonMarlow323PumpEnable,
42
+ prefix + "DISABLE",
43
+ )
44
+
45
+ super().__init__(name=name)
dodal/devices/webcam.py CHANGED
@@ -5,7 +5,12 @@ from pathlib import Path
5
5
  import aiofiles
6
6
  from aiohttp import ClientSession
7
7
  from bluesky.protocols import Triggerable
8
- from ophyd_async.core import AsyncStatus, HintedSignal, StandardReadable, soft_signal_rw
8
+ from ophyd_async.core import (
9
+ AsyncStatus,
10
+ StandardReadable,
11
+ StandardReadableFormat,
12
+ soft_signal_rw,
13
+ )
9
14
  from PIL import Image
10
15
 
11
16
  from dodal.log import LOGGER
@@ -27,7 +32,9 @@ class Webcam(StandardReadable, Triggerable):
27
32
  self.directory = soft_signal_rw(str, name="directory")
28
33
  self.last_saved_path = soft_signal_rw(str, name="last_saved_path")
29
34
 
30
- self.add_readables([self.last_saved_path], wrapper=HintedSignal)
35
+ self.add_readables(
36
+ [self.last_saved_path], format=StandardReadableFormat.HINTED_SIGNAL
37
+ )
31
38
  super().__init__(name=name)
32
39
 
33
40
  async def _write_image(self, file_path: str, image: ByteString):
@@ -1,11 +1,9 @@
1
- from enum import Enum
2
-
3
1
  from bluesky.protocols import Triggerable
4
- from ophyd_async.core import AsyncStatus, Device, observe_value
5
- from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
2
+ from ophyd_async.core import AsyncStatus, Device, StrictEnum, observe_value
3
+ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
6
4
 
7
5
 
8
- class Pause(str, Enum):
6
+ class Pause(StrictEnum):
9
7
  PAUSE = "Paused" # 0
10
8
  RUN = "Ok to Run" # 1
11
9
 
@@ -1,15 +1,14 @@
1
- from enum import Enum
2
-
3
1
  from bluesky.protocols import Stageable
4
2
  from numpy import float64
5
- from numpy.typing import NDArray
6
3
  from ophyd_async.core import (
4
+ Array1D,
7
5
  AsyncStatus,
8
6
  Device,
9
7
  DeviceVector,
8
+ StrictEnum,
10
9
  wait_for_value,
11
10
  )
12
- from ophyd_async.epics.signal import (
11
+ from ophyd_async.epics.core import (
13
12
  epics_signal_r,
14
13
  epics_signal_rw,
15
14
  epics_signal_rw_rbv,
@@ -23,7 +22,7 @@ from dodal.devices.xspress3.xspress3_channel import (
23
22
  from dodal.log import LOGGER
24
23
 
25
24
 
26
- class TriggerMode(str, Enum):
25
+ class TriggerMode(StrictEnum):
27
26
  SOFTWARE = "Software"
28
27
  HARDWARE = "Hardware"
29
28
  BURST = "Burst"
@@ -35,17 +34,17 @@ class TriggerMode(str, Enum):
35
34
  LVDS_both = "LVDS Both"
36
35
 
37
36
 
38
- class UpdateRBV(str, Enum):
37
+ class UpdateRBV(StrictEnum):
39
38
  DISABLED = "Disabled"
40
39
  ENABLED = "Enabled"
41
40
 
42
41
 
43
- class AcquireRBVState(str, Enum):
42
+ class AcquireRBVState(StrictEnum):
44
43
  DONE = "Done"
45
44
  ACQUIRE = "Acquiring"
46
45
 
47
46
 
48
- class DetectorState(str, Enum):
47
+ class DetectorState(StrictEnum):
49
48
  IDLE = "Idle"
50
49
  ACQUIRE = "Acquire"
51
50
  READOUT = "Readout"
@@ -101,7 +100,7 @@ class Xspress3(Device, Stageable):
101
100
  """signal for the corrected MCA spectrum (1d array)"""
102
101
  self.dt_corrected_latest_mca = DeviceVector(
103
102
  {
104
- i: epics_signal_r(NDArray[float64], f"{prefix}ARR{i}:ArrayData")
103
+ i: epics_signal_r(Array1D[float64], f"{prefix}ARR{i}:ArrayData")
105
104
  for i in range(1, num_channels + 1)
106
105
  }
107
106
  )
@@ -1,10 +1,8 @@
1
- from enum import Enum
1
+ from ophyd_async.core import Device, StrictEnum
2
+ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
2
3
 
3
- from ophyd_async.core import Device
4
- from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
5
4
 
6
-
7
- class AcquireState(str, Enum):
5
+ class AcquireState(StrictEnum):
8
6
  DONE = "Done"
9
7
  ACQUIRE = "Acquire"
10
8