dls-dodal 1.55.1__py3-none-any.whl → 1.57.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 (104) hide show
  1. {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/METADATA +3 -3
  2. {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/RECORD +101 -87
  3. dodal/_version.py +16 -3
  4. dodal/beamlines/b01_1.py +6 -1
  5. dodal/beamlines/b07.py +2 -1
  6. dodal/beamlines/b07_1.py +2 -1
  7. dodal/beamlines/b21.py +4 -24
  8. dodal/beamlines/i03.py +53 -53
  9. dodal/beamlines/i04.py +16 -38
  10. dodal/beamlines/i09.py +3 -2
  11. dodal/beamlines/i09_1.py +2 -1
  12. dodal/beamlines/i11.py +143 -0
  13. dodal/beamlines/i17.py +37 -0
  14. dodal/beamlines/i19_1.py +1 -0
  15. dodal/beamlines/i19_2.py +7 -0
  16. dodal/beamlines/i22.py +5 -5
  17. dodal/beamlines/i23.py +3 -3
  18. dodal/beamlines/i24.py +6 -33
  19. dodal/beamlines/p38.py +1 -0
  20. dodal/beamlines/p60.py +3 -2
  21. dodal/cli.py +11 -1
  22. dodal/common/__init__.py +4 -0
  23. dodal/common/beamlines/beamline_parameters.py +1 -1
  24. dodal/common/beamlines/beamline_utils.py +5 -1
  25. dodal/common/enums.py +19 -0
  26. dodal/common/watcher_utils.py +83 -0
  27. dodal/devices/aithre_lasershaping/laser_robot.py +4 -9
  28. dodal/devices/aperturescatterguard.py +52 -12
  29. dodal/devices/apple2_undulator.py +0 -1
  30. dodal/devices/b16/detector.py +1 -10
  31. dodal/devices/backlight.py +8 -20
  32. dodal/devices/bimorph_mirror.py +4 -7
  33. dodal/devices/collimation_table.py +36 -0
  34. dodal/devices/controllers.py +21 -0
  35. dodal/devices/cryostream.py +97 -7
  36. dodal/devices/current_amplifiers/femto.py +1 -1
  37. dodal/devices/detector/detector_motion.py +1 -7
  38. dodal/devices/eiger.py +22 -8
  39. dodal/devices/eiger_odin.py +2 -0
  40. dodal/devices/electron_analyser/__init__.py +2 -1
  41. dodal/devices/electron_analyser/abstract/__init__.py +0 -1
  42. dodal/devices/electron_analyser/abstract/base_detector.py +3 -25
  43. dodal/devices/electron_analyser/abstract/base_driver_io.py +18 -9
  44. dodal/devices/electron_analyser/abstract/base_region.py +34 -3
  45. dodal/devices/electron_analyser/detector.py +24 -0
  46. dodal/devices/electron_analyser/enums.py +5 -0
  47. dodal/devices/electron_analyser/specs/detector.py +2 -1
  48. dodal/devices/electron_analyser/specs/driver_io.py +21 -26
  49. dodal/devices/electron_analyser/specs/region.py +1 -1
  50. dodal/devices/electron_analyser/util.py +20 -0
  51. dodal/devices/electron_analyser/vgscienta/__init__.py +3 -3
  52. dodal/devices/electron_analyser/vgscienta/detector.py +2 -1
  53. dodal/devices/electron_analyser/vgscienta/driver_io.py +24 -32
  54. dodal/devices/electron_analyser/vgscienta/enums.py +0 -8
  55. dodal/devices/electron_analyser/vgscienta/region.py +2 -31
  56. dodal/devices/eurotherm.py +126 -0
  57. dodal/devices/fluorescence_detector_motion.py +3 -10
  58. dodal/devices/focusing_mirror.py +1 -1
  59. dodal/devices/i03/undulator_dcm.py +0 -1
  60. dodal/devices/i09/enums.py +8 -8
  61. dodal/devices/i10/diagnostics.py +4 -4
  62. dodal/devices/i10/i10_apple2.py +3 -6
  63. dodal/devices/i11/cyberstar_blower.py +34 -0
  64. dodal/devices/i11/diff_stages.py +55 -0
  65. dodal/devices/i11/mythen.py +165 -0
  66. dodal/devices/i11/nx100robot.py +153 -0
  67. dodal/devices/i11/spinner.py +30 -0
  68. dodal/devices/i13_1/merlin_controller.py +4 -4
  69. dodal/devices/i19/diffractometer.py +34 -0
  70. dodal/devices/i19/shutter.py +11 -1
  71. dodal/devices/i22/dcm.py +1 -1
  72. dodal/devices/i22/fswitch.py +3 -12
  73. dodal/devices/i24/aperture.py +3 -3
  74. dodal/devices/i24/beam_center.py +1 -2
  75. dodal/devices/i24/dcm.py +11 -15
  76. dodal/devices/i24/dual_backlight.py +11 -12
  77. dodal/devices/i24/pmac.py +8 -7
  78. dodal/devices/mx_phase1/beamstop.py +10 -11
  79. dodal/devices/oav/pin_image_recognition/__init__.py +0 -3
  80. dodal/devices/p60/enums.py +8 -8
  81. dodal/devices/p60/lab_xray_source.py +3 -2
  82. dodal/devices/pressure_jump_cell.py +77 -123
  83. dodal/devices/scintillator.py +76 -4
  84. dodal/devices/smargon.py +35 -18
  85. dodal/devices/synchrotron.py +1 -2
  86. dodal/devices/thawer.py +22 -15
  87. dodal/devices/undulator.py +3 -8
  88. dodal/devices/util/epics_util.py +1 -1
  89. dodal/devices/watsonmarlow323_pump.py +7 -7
  90. dodal/devices/webcam.py +1 -0
  91. dodal/devices/xbpm_feedback.py +4 -6
  92. dodal/devices/xspress3/xspress3.py +0 -5
  93. dodal/devices/zocalo/zocalo_results.py +1 -3
  94. dodal/testing/__init__.py +3 -0
  95. dodal/testing/electron_analyser/__init__.py +6 -0
  96. dodal/testing/electron_analyser/device_factory.py +59 -0
  97. dodal/testing/setup.py +67 -0
  98. dodal/devices/CTAB.py +0 -41
  99. dodal/devices/i24/pilatus_metadata.py +0 -44
  100. dodal/devices/util/test_utils.py +0 -37
  101. {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/WHEEL +0 -0
  102. {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/entry_points.txt +0 -0
  103. {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/licenses/LICENSE +0 -0
  104. {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/top_level.txt +0 -0
@@ -7,12 +7,12 @@ from ophyd_async.core import (
7
7
  AsyncStatus,
8
8
  StandardReadable,
9
9
  StandardReadableFormat,
10
- StrictEnum,
11
10
  soft_signal_r_and_setter,
12
11
  )
13
12
  from ophyd_async.epics.core import epics_signal_r
14
13
  from ophyd_async.epics.motor import Motor
15
14
 
15
+ from dodal.common.enums import EnabledDisabledUpper
16
16
  from dodal.log import LOGGER
17
17
 
18
18
  from .util.lookup_tables import energy_distance_table
@@ -33,11 +33,6 @@ UNDULATOR_DISCREPANCY_THRESHOLD_MM = 2e-3
33
33
  STATUS_TIMEOUT_S: float = 10.0
34
34
 
35
35
 
36
- class UndulatorGapAccess(StrictEnum):
37
- ENABLED = "ENABLED"
38
- DISABLED = "DISABLED"
39
-
40
-
41
36
  def _get_gap_for_energy(
42
37
  dcm_energy_ev: float, energy_to_distance_table: ndarray
43
38
  ) -> float:
@@ -73,7 +68,7 @@ class Undulator(StandardReadable, Movable[float]):
73
68
  with self.add_children_as_readables():
74
69
  self.gap_motor = Motor(prefix + "BLGAPMTR")
75
70
  self.current_gap = epics_signal_r(float, prefix + "CURRGAPD")
76
- self.gap_access = epics_signal_r(UndulatorGapAccess, prefix + "IDBLENA")
71
+ self.gap_access = epics_signal_r(EnabledDisabledUpper, prefix + "IDBLENA")
77
72
 
78
73
  with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
79
74
  self.gap_discrepancy_tolerance_mm, _ = soft_signal_r_and_setter(
@@ -110,7 +105,7 @@ class Undulator(StandardReadable, Movable[float]):
110
105
 
111
106
  async def raise_if_not_enabled(self):
112
107
  access_level = await self.gap_access.get_value()
113
- if access_level is UndulatorGapAccess.DISABLED and not TEST_MODE:
108
+ if access_level is EnabledDisabledUpper.DISABLED and not TEST_MODE:
114
109
  raise AccessError("Undulator gap access is disabled. Contact Control Room")
115
110
 
116
111
  async def _set_undulator_gap(self, energy_kev: float) -> None:
@@ -117,7 +117,7 @@ def call_func(func: Callable[[], StatusBase]) -> StatusBase:
117
117
  class SetWhenEnabled(OphydAsyncDevice, Movable[int]):
118
118
  """A device that sets the proc field of a PV when it becomes enabled."""
119
119
 
120
- def __init__(self, name: str = "", prefix: str = ""):
120
+ def __init__(self, prefix: str, name: str = ""):
121
121
  self.proc = epics_signal_rw(int, prefix + ".PROC")
122
122
  self.disp = epics_signal_r(int, prefix + ".DISP")
123
123
  super().__init__(name)
@@ -1,12 +1,12 @@
1
- from ophyd_async.core import StandardReadable, StandardReadableFormat, StrictEnum
1
+ from ophyd_async.core import (
2
+ EnabledDisabled,
3
+ StandardReadable,
4
+ StandardReadableFormat,
5
+ StrictEnum,
6
+ )
2
7
  from ophyd_async.epics.core import epics_signal_rw
3
8
 
4
9
 
5
- class WatsonMarlow323PumpEnable(StrictEnum):
6
- DISABLED = "Disabled"
7
- ENABLED = "Enabled"
8
-
9
-
10
10
  class WatsonMarlow323PumpDirection(StrictEnum):
11
11
  CLOCKWISE = "CW"
12
12
  COUNTER_CLOCKWISE = "CCW"
@@ -38,7 +38,7 @@ class WatsonMarlow323Pump(StandardReadable):
38
38
 
39
39
  with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
40
40
  self.enabled = epics_signal_rw(
41
- WatsonMarlow323PumpEnable,
41
+ EnabledDisabled,
42
42
  prefix + "DISABLE",
43
43
  )
44
44
 
dodal/devices/webcam.py CHANGED
@@ -51,6 +51,7 @@ class Webcam(StandardReadable, Triggerable):
51
51
  )
52
52
  try:
53
53
  data = await response.read()
54
+ Image.open(BytesIO(data)).verify()
54
55
  LOGGER.info(f"Saving webcam image from {self.url} to {file_path}")
55
56
  except Exception as e:
56
57
  LOGGER.warning(
@@ -14,12 +14,10 @@ class XBPMFeedback(Device, Triggerable):
14
14
  """The XBPM feedback device is an IOC that moves the DCM, HFM and VFM to automatically
15
15
  hold the beam into place, as measured by the XBPM sensor."""
16
16
 
17
- def __init__(self, prefix: str = "", name: str = "xbpm_feedback") -> None:
18
- self.pos_ok = epics_signal_r(float, prefix + "-EA-FDBK-01:XBPM2POSITION_OK")
19
- self.pos_stable = epics_signal_r(float, prefix + "-EA-FDBK-01:XBPM2_STABLE")
20
- self.pause_feedback = epics_signal_rw(Pause, prefix + "-EA-FDBK-01:FB_PAUSE")
21
- self.x = epics_signal_r(float, prefix + "-EA-XBPM-02:PosX:MeanValue_RBV")
22
- self.y = epics_signal_r(float, prefix + "-EA-XBPM-02:PosY:MeanValue_RBV")
17
+ def __init__(self, prefix: str, name: str = "") -> None:
18
+ self.pos_ok = epics_signal_r(float, prefix + "XBPM2POSITION_OK")
19
+ self.pos_stable = epics_signal_r(float, prefix + "XBPM2_STABLE")
20
+ self.pause_feedback = epics_signal_rw(Pause, prefix + "FB_PAUSE")
23
21
  super().__init__(name=name)
24
22
 
25
23
  @AsyncStatus.wrap
@@ -34,11 +34,6 @@ class TriggerMode(StrictEnum):
34
34
  LVDS_BOTH = "LVDS Both"
35
35
 
36
36
 
37
- class UpdateRBV(StrictEnum):
38
- DISABLED = "Disabled"
39
- ENABLED = "Enabled"
40
-
41
-
42
37
  class AcquireRBVState(StrictEnum):
43
38
  DONE = "Done"
44
39
  ACQUIRE = "Acquiring"
@@ -125,19 +125,17 @@ class ZocaloResults(StandardReadable, Triggerable):
125
125
 
126
126
  def __init__(
127
127
  self,
128
- name: str = "zocalo",
128
+ name: str = "",
129
129
  zocalo_environment: str = ZOCALO_ENV,
130
130
  channel: str = "xrc.i03",
131
131
  sort_key: str = DEFAULT_SORT_KEY.value,
132
132
  timeout_s: float = DEFAULT_TIMEOUT,
133
- prefix: str = "",
134
133
  results_source: ZocaloSource = ZocaloSource.CPU,
135
134
  ) -> None:
136
135
  self.zocalo_environment = zocalo_environment
137
136
  self.sort_key = SortKeys[sort_key]
138
137
  self.channel = channel
139
138
  self.timeout_s = timeout_s
140
- self._prefix = prefix
141
139
  self._raw_results_received: Queue = Queue()
142
140
  self.transport: CommonTransport | None = None
143
141
  self.results_source = results_source
@@ -0,0 +1,3 @@
1
+ from .setup import patch_all_motors, patch_motor
2
+
3
+ __all__ = ["patch_motor", "patch_all_motors"]
@@ -0,0 +1,6 @@
1
+ from .device_factory import create_detector, create_driver
2
+
3
+ __all__ = [
4
+ "create_detector",
5
+ "create_driver",
6
+ ]
@@ -0,0 +1,59 @@
1
+ from typing import Any, get_args, get_origin
2
+
3
+ from dodal.devices.electron_analyser.abstract import (
4
+ TAbstractAnalyserDriverIO,
5
+ )
6
+ from dodal.devices.electron_analyser.detector import TElectronAnalyserDetector
7
+ from dodal.devices.electron_analyser.vgscienta import (
8
+ VGScientaAnalyserDriverIO,
9
+ VGScientaDetector,
10
+ )
11
+
12
+
13
+ async def create_driver(
14
+ driver_class: type[TAbstractAnalyserDriverIO],
15
+ **kwargs: Any,
16
+ ) -> TAbstractAnalyserDriverIO:
17
+ """
18
+ Helper function that helps to reduce the code to setup an analyser driver. The
19
+ parameters used for the enum types are taken directly from the subscripts of the
20
+ class so the user only needs to provide it in one place.
21
+
22
+ Args:
23
+ driver_class: The class for the driver which must include the enums in the
24
+ subscript, for example MyDriverClass[MyLensMode, ...]
25
+ kwargs: Additional key worded arguments that the driver needs for initalisation.
26
+ """
27
+ parameters = {
28
+ "lens_mode_type": get_args(driver_class)[0],
29
+ "psu_mode_type": get_args(driver_class)[1],
30
+ }
31
+ if get_origin(driver_class) is VGScientaAnalyserDriverIO:
32
+ parameters["pass_energy_type"] = get_args(driver_class)[2]
33
+
34
+ return driver_class(**(parameters | kwargs))
35
+
36
+
37
+ async def create_detector(
38
+ detector_class: type[TElectronAnalyserDetector],
39
+ **kwargs: Any,
40
+ ) -> TElectronAnalyserDetector:
41
+ """
42
+ Helper function that helps to reduce the code to setup an analyser detector. The
43
+ parameters used for the enum types are taken directly from the subscripts of the
44
+ class so the user only needs to provide it in one place.
45
+
46
+ Args:
47
+ detector_class: The class for the detector which must include the enums in the
48
+ subscript, for example MyDetectorClass[MyLensMode, ...]
49
+ kwargs: Additional key worded arguments that the detector needs for
50
+ initalisation.
51
+ """
52
+ parameters = {
53
+ "lens_mode_type": get_args(detector_class)[0],
54
+ "psu_mode_type": get_args(detector_class)[1],
55
+ }
56
+ if get_origin(detector_class) is VGScientaDetector:
57
+ parameters["pass_energy_type"] = get_args(detector_class)[2]
58
+
59
+ return detector_class(**(parameters | kwargs))
dodal/testing/setup.py ADDED
@@ -0,0 +1,67 @@
1
+ from contextlib import ExitStack
2
+
3
+ from ophyd_async.core import Device
4
+ from ophyd_async.epics.motor import Motor
5
+ from ophyd_async.testing import (
6
+ callback_on_mock_put,
7
+ set_mock_value,
8
+ )
9
+
10
+
11
+ def patch_motor(
12
+ motor: Motor,
13
+ initial_position: float = 0,
14
+ deadband: float = 0.001,
15
+ velocity: float = 3,
16
+ max_velocity: float = 5,
17
+ low_limit_travel: float = float("-inf"),
18
+ high_limit_travel: float = float("inf"),
19
+ ):
20
+ """
21
+ Patch a mock motor with sensible default values so that it can still be used in
22
+ tests and plans without running into errors as default values are zero.
23
+
24
+ Parameters:
25
+ motor: The mock motor to set mock values with.
26
+ initial_position: The default initial position of the motor to be set.
27
+ deadband: The tolerance between readback value and demand setpoint which the
28
+ motor is considered at position.
29
+ velocity: Requested move speed when the mock motor moves.
30
+ max_velocity: The maximum allowable velocity that can be set for the motor.
31
+ low_limit_travel: The lower limit that the motor can move to.
32
+ high_limit_travel: The higher limit that the motor can move to.
33
+ """
34
+ set_mock_value(motor.user_setpoint, initial_position)
35
+ set_mock_value(motor.user_readback, initial_position)
36
+ set_mock_value(motor.deadband, deadband)
37
+ set_mock_value(motor.motor_done_move, 1)
38
+ set_mock_value(motor.velocity, velocity)
39
+ set_mock_value(motor.max_velocity, max_velocity)
40
+ set_mock_value(motor.low_limit_travel, low_limit_travel)
41
+ set_mock_value(motor.high_limit_travel, high_limit_travel)
42
+ return callback_on_mock_put(
43
+ motor.user_setpoint,
44
+ lambda pos, *args, **kwargs: set_mock_value(motor.user_readback, pos),
45
+ )
46
+
47
+
48
+ def patch_all_motors(parent_device: Device):
49
+ """
50
+ Check all children of a device and patch any motors with mock values.
51
+
52
+ Parameters:
53
+ parent_device: The device that hold motor(s) as children.
54
+ """
55
+ motors = []
56
+
57
+ def recursively_find_motors(device: Device):
58
+ for _, child_device in device.children():
59
+ if isinstance(child_device, Motor):
60
+ motors.append(child_device)
61
+ recursively_find_motors(child_device)
62
+
63
+ recursively_find_motors(parent_device)
64
+ motor_patch_stack = ExitStack()
65
+ for motor in motors:
66
+ motor_patch_stack.enter_context(patch_motor(motor))
67
+ return motor_patch_stack
dodal/devices/CTAB.py DELETED
@@ -1,41 +0,0 @@
1
- from ophyd_async.core import StandardReadable
2
- from ophyd_async.epics.core import epics_signal_r
3
- from ophyd_async.epics.motor import Motor
4
-
5
-
6
- class CTAB(StandardReadable):
7
- """Basic collimantion table (CTAB) device for motion plus the motion disable signal
8
- when laser curtain triggered and hutch not locked.
9
-
10
- CTAB has 3 physical vertical motors, the jacks. 1 upstream and 2 downstream.
11
- The two downstream jacks are labelled as outboard (away from the ring) and
12
- inboard (towards the ring).
13
- Together these 3 jacks provide compound motion for vertical motion and pitch/roll.
14
- There are 2 physical horizontal motors 1 upstream, 1 downstream. These provide yaw.
15
-
16
- CTAB motion is disabled by an object being within the laser curtain area and can be
17
- overriden by use of the dead man's handle device or locking the hutch. The effect of
18
- these disabling systems is to cut power to the motors - signal for this is crate_power
19
- """
20
-
21
- def __init__(self, prefix: str, name: str = ""):
22
- with self.add_children_as_readables():
23
- self.inboard_y = Motor(prefix + "-MO-TABLE-01:INBOARDY")
24
- self.outboard_y = Motor(prefix + "-MO-TABLE-01:OUTBOARDY")
25
- self.upstream_y = Motor(prefix + "-MO-TABLE-01:UPSTREAMY")
26
- self.combined_downstream_y = Motor(prefix + "-MO-TABLE-01:DOWNSTREAMY")
27
- self.combined_all_y = Motor(prefix + "-MO-TABLE-01:Y")
28
-
29
- self.downstream_x = Motor(prefix + "-MO-TABLE-01:DOWNSTREAMX")
30
- self.upstream_x = Motor(prefix + "-MO-TABLE-01:UPSTREAMX")
31
- self.combined_all_x = Motor(prefix + "-MO-TABLE-01:X")
32
-
33
- self.pitch = Motor(prefix + "-MO-TABLE-01:PITCH")
34
- self.roll = Motor(prefix + "-MO-TABLE-01:ROLL")
35
- self.yaw = Motor(prefix + "-MO-TABLE-01:YAW")
36
-
37
- self.crate_power = epics_signal_r(
38
- int, prefix + "-MO-PMAC-02:CRATE2_HEALTHY"
39
- ) # returns 0 if no power
40
-
41
- super().__init__(name)
@@ -1,44 +0,0 @@
1
- """A small temporary device to set and read the filename template from the pilatus"""
2
-
3
- import re
4
-
5
- from ophyd_async.core import StandardReadable, derived_signal_r
6
- from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
7
-
8
-
9
- class PilatusMetadata(StandardReadable):
10
- def __init__(self, prefix: str, name: str = "") -> None:
11
- self.filename = epics_signal_rw(str, prefix + "cam1:FileName")
12
- self.template = epics_signal_r(str, prefix + "cam1:FileTemplate_RBV")
13
- self.filenumber = epics_signal_r(int, prefix + "cam1:FileNumber_RBV")
14
- with self.add_children_as_readables():
15
- self.filename_template = derived_signal_r(
16
- self._get_full_filename_template,
17
- filename=self.filename,
18
- filename_template=self.template,
19
- file_number=self.filenumber,
20
- )
21
- super().__init__(name)
22
-
23
- def _get_full_filename_template(
24
- self, filename: str, filename_template: str, file_number: int
25
- ) -> str:
26
- """
27
- Get the template file path by querying the detector PVs.
28
- Mirror the construction that the PPU does.
29
-
30
- Returns: A template string, with the image numbers replaced with '#'
31
- """
32
- # Exploit fact that passing negative numbers will put the - before the 0's
33
- expected_filename = str(
34
- filename_template % (filename, f"{file_number:05d}_", -9)
35
- )
36
- # Now, find the -09 part of this
37
- numberpart = re.search(r"(-0+9)", expected_filename)
38
- assert numberpart is not None
39
- template_fill = "#" * len(numberpart.group(0))
40
- return (
41
- expected_filename[: numberpart.start()]
42
- + template_fill
43
- + expected_filename[numberpart.end() :]
44
- )
@@ -1,37 +0,0 @@
1
- from contextlib import ExitStack
2
-
3
- from ophyd_async.core import Device
4
- from ophyd_async.epics.motor import Motor
5
- from ophyd_async.testing import (
6
- callback_on_mock_put,
7
- set_mock_value,
8
- )
9
-
10
-
11
- def patch_motor(motor: Motor, initial_position=0):
12
- set_mock_value(motor.user_setpoint, initial_position)
13
- set_mock_value(motor.user_readback, initial_position)
14
- set_mock_value(motor.deadband, 0.001)
15
- set_mock_value(motor.motor_done_move, 1)
16
- set_mock_value(motor.velocity, 3)
17
- set_mock_value(motor.max_velocity, 5)
18
- return callback_on_mock_put(
19
- motor.user_setpoint,
20
- lambda pos, *args, **kwargs: set_mock_value(motor.user_readback, pos),
21
- )
22
-
23
-
24
- def patch_all_motors(parent_device: Device):
25
- motors = []
26
-
27
- def recursively_find_motors(device: Device):
28
- for _, child_device in device.children():
29
- if isinstance(child_device, Motor):
30
- motors.append(child_device)
31
- recursively_find_motors(child_device)
32
-
33
- recursively_find_motors(parent_device)
34
- motor_patch_stack = ExitStack()
35
- for motor in motors:
36
- motor_patch_stack.enter_context(patch_motor(motor))
37
- return motor_patch_stack