dls-dodal 1.68.0__py3-none-any.whl → 2.0.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 (292) hide show
  1. {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/METADATA +1 -31
  2. dls_dodal-2.0.0.dist-info/RECORD +354 -0
  3. {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/WHEEL +1 -1
  4. dodal/_version.py +2 -2
  5. dodal/beamlines/__init__.py +10 -17
  6. dodal/beamlines/adsim.py +40 -33
  7. dodal/beamlines/b01_1.py +11 -0
  8. dodal/beamlines/b07.py +17 -21
  9. dodal/beamlines/b07_1.py +20 -22
  10. dodal/beamlines/b07_shared.py +12 -0
  11. dodal/beamlines/b16.py +1 -1
  12. dodal/beamlines/b21.py +15 -6
  13. dodal/beamlines/i02_1.py +17 -45
  14. dodal/beamlines/i02_2.py +6 -12
  15. dodal/beamlines/i03.py +8 -5
  16. dodal/beamlines/i03_supervisor.py +19 -0
  17. dodal/beamlines/i04.py +87 -184
  18. dodal/beamlines/i05.py +9 -39
  19. dodal/beamlines/i05_1.py +4 -13
  20. dodal/beamlines/i05_shared.py +51 -0
  21. dodal/beamlines/i06_1.py +26 -0
  22. dodal/beamlines/{i06.py → i06_shared.py} +25 -14
  23. dodal/beamlines/i07.py +14 -16
  24. dodal/beamlines/i09.py +76 -29
  25. dodal/beamlines/i09_1.py +25 -56
  26. dodal/beamlines/i09_1_shared.py +61 -0
  27. dodal/beamlines/i09_2.py +6 -100
  28. dodal/beamlines/i09_2_shared.py +110 -0
  29. dodal/beamlines/i10.py +60 -54
  30. dodal/beamlines/i10_1.py +99 -10
  31. dodal/beamlines/{i10_optics.py → i10_shared.py} +80 -66
  32. dodal/beamlines/i11.py +31 -18
  33. dodal/beamlines/i13_1.py +1 -1
  34. dodal/beamlines/i15.py +6 -6
  35. dodal/beamlines/i15_1.py +6 -6
  36. dodal/beamlines/i16.py +11 -0
  37. dodal/beamlines/i17.py +37 -28
  38. dodal/beamlines/i18.py +3 -4
  39. dodal/beamlines/i19_1.py +95 -34
  40. dodal/beamlines/i19_2.py +68 -52
  41. dodal/beamlines/i19_optics.py +26 -13
  42. dodal/beamlines/i20_1.py +17 -11
  43. dodal/beamlines/i21.py +44 -29
  44. dodal/beamlines/i22.py +19 -4
  45. dodal/beamlines/i23.py +20 -27
  46. dodal/beamlines/i24.py +64 -113
  47. dodal/beamlines/k07.py +99 -5
  48. dodal/beamlines/p38.py +3 -3
  49. dodal/beamlines/p60.py +35 -14
  50. dodal/beamlines/p99.py +16 -15
  51. dodal/beamlines/training_rig.py +20 -12
  52. dodal/cli.py +36 -2
  53. dodal/common/__init__.py +2 -1
  54. dodal/common/beamlines/beamline_parameters.py +2 -1
  55. dodal/common/beamlines/beamline_utils.py +11 -9
  56. dodal/common/beamlines/commissioning_mode.py +6 -3
  57. dodal/common/coordination.py +12 -14
  58. dodal/common/crystal_metadata.py +5 -8
  59. dodal/common/device_utils.py +4 -3
  60. dodal/common/maths.py +87 -19
  61. dodal/common/udc_directory_provider.py +13 -8
  62. dodal/common/visit.py +18 -21
  63. dodal/common/watcher_utils.py +13 -12
  64. dodal/device_manager.py +94 -54
  65. dodal/devices/aperturescatterguard.py +26 -27
  66. dodal/devices/areadetector/plugins/cam.py +1 -3
  67. dodal/devices/areadetector/plugins/mjpg.py +6 -5
  68. dodal/devices/attenuator/attenuator.py +12 -11
  69. dodal/devices/beamlines/b07/__init__.py +3 -0
  70. dodal/devices/{b07_1 → beamlines/b07_1}/__init__.py +2 -2
  71. dodal/devices/{b07_1 → beamlines/b07_1}/ccmc.py +5 -10
  72. dodal/devices/{b16 → beamlines/b16}/detector.py +2 -3
  73. dodal/devices/{i02_1 → beamlines/i02_1}/fast_grid_scan.py +2 -3
  74. dodal/devices/{i02_1 → beamlines/i02_1}/sample_motors.py +1 -1
  75. dodal/devices/{i03 → beamlines/i03}/beamsize.py +11 -7
  76. dodal/devices/{i03 → beamlines/i03}/dcm.py +1 -2
  77. dodal/devices/{i03 → beamlines/i03}/undulator_dcm.py +4 -5
  78. dodal/devices/beamlines/i04/beam_centre.py +151 -0
  79. dodal/devices/{i04 → beamlines/i04}/beamsize.py +11 -7
  80. dodal/devices/beamlines/i04/max_pixel.py +25 -0
  81. dodal/devices/{i04 → beamlines/i04}/murko_results.py +23 -8
  82. dodal/devices/{i04 → beamlines/i04}/transfocator.py +10 -15
  83. dodal/devices/beamlines/i05/__init__.py +3 -0
  84. dodal/devices/beamlines/i06_shared/__init__.py +3 -0
  85. dodal/devices/beamlines/i06_shared/i06_enum.py +7 -0
  86. dodal/devices/{i07 → beamlines/i07}/dcm.py +2 -3
  87. dodal/devices/{i07 → beamlines/i07}/id.py +8 -9
  88. dodal/devices/beamlines/i09/__init__.py +3 -0
  89. dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_energy.py +5 -6
  90. dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_undulator_functions.py +19 -16
  91. dodal/devices/{i10 → beamlines/i10}/diagnostics.py +4 -3
  92. dodal/devices/{i10 → beamlines/i10}/i10_apple2.py +37 -51
  93. dodal/devices/{i10 → beamlines/i10}/rasor/rasor_current_amp.py +1 -24
  94. dodal/devices/{i10 → beamlines/i10}/rasor/rasor_motors.py +2 -2
  95. dodal/devices/{i10 → beamlines/i10}/slits.py +5 -3
  96. dodal/devices/beamlines/i10_1/__init__.py +9 -0
  97. dodal/devices/beamlines/i10_1/electromagnet/magnet.py +16 -0
  98. dodal/devices/beamlines/i10_1/electromagnet/stages.py +14 -0
  99. dodal/devices/beamlines/i10_1/scaler_cards.py +13 -0
  100. dodal/devices/{i11 → beamlines/i11}/cyberstar_blower.py +1 -1
  101. dodal/devices/{i11 → beamlines/i11}/diff_stages.py +4 -6
  102. dodal/devices/{i11 → beamlines/i11}/mythen.py +3 -4
  103. dodal/devices/{i11 → beamlines/i11}/nx100robot.py +6 -6
  104. dodal/devices/{i11 → beamlines/i11}/spinner.py +1 -1
  105. dodal/devices/{i13_1 → beamlines/i13_1}/merlin.py +1 -1
  106. dodal/devices/{i15 → beamlines/i15}/dcm.py +1 -2
  107. dodal/devices/{i15 → beamlines/i15}/focussing_mirror.py +5 -5
  108. dodal/devices/{i15 → beamlines/i15}/jack.py +2 -2
  109. dodal/devices/{i15 → beamlines/i15}/multilayer_mirror.py +1 -1
  110. dodal/devices/{i17 → beamlines/i17}/i17_apple2.py +16 -22
  111. dodal/devices/{i18 → beamlines/i18}/diode.py +1 -1
  112. dodal/devices/{i19 → beamlines/i19}/access_controlled/attenuator_motor_squad.py +12 -8
  113. dodal/devices/{i19 → beamlines/i19}/access_controlled/blueapi_device.py +16 -15
  114. dodal/devices/beamlines/i19/access_controlled/piezo_control.py +72 -0
  115. dodal/devices/{i19 → beamlines/i19}/access_controlled/shutter.py +11 -9
  116. dodal/devices/{i19 → beamlines/i19}/backlight.py +3 -1
  117. dodal/devices/{i19 → beamlines/i19}/mapt_configuration.py +2 -1
  118. dodal/devices/{i19 → beamlines/i19}/pin_col_stages.py +11 -8
  119. dodal/devices/beamlines/i19/pin_tip.py +32 -0
  120. dodal/devices/beamlines/i21/__init__.py +3 -0
  121. dodal/devices/{i22 → beamlines/i22}/dcm.py +1 -2
  122. dodal/devices/{i22 → beamlines/i22}/fswitch.py +1 -3
  123. dodal/devices/{i22 → beamlines/i22}/nxsas.py +5 -4
  124. dodal/devices/{i24 → beamlines/i24}/beam_center.py +1 -1
  125. dodal/devices/{i24 → beamlines/i24}/beamstop.py +2 -2
  126. dodal/devices/{i24 → beamlines/i24}/commissioning_jungfrau.py +12 -12
  127. dodal/devices/{i24 → beamlines/i24}/dcm.py +1 -3
  128. dodal/devices/{i24 → beamlines/i24}/dual_backlight.py +3 -3
  129. dodal/devices/{i24 → beamlines/i24}/pmac.py +9 -7
  130. dodal/devices/{p60 → beamlines/p60}/lab_xray_source.py +1 -1
  131. dodal/devices/beamlines/p99/__init__.py +0 -0
  132. dodal/devices/{p99 → beamlines/p99}/andor2_point.py +11 -15
  133. dodal/devices/bimorph_mirror.py +22 -20
  134. dodal/devices/collimation_table.py +3 -2
  135. dodal/devices/common_dcm.py +30 -20
  136. dodal/devices/controllers.py +2 -2
  137. dodal/devices/cryostream.py +8 -0
  138. dodal/devices/current_amplifiers/current_amplifier.py +16 -18
  139. dodal/devices/current_amplifiers/current_amplifier_detector.py +9 -10
  140. dodal/devices/current_amplifiers/femto.py +8 -9
  141. dodal/devices/current_amplifiers/sr570.py +16 -16
  142. dodal/devices/current_amplifiers/struck_scaler_counter.py +5 -5
  143. dodal/devices/detector/det_resolution.py +9 -8
  144. dodal/devices/detector/detector.py +4 -2
  145. dodal/devices/diamond_filter.py +3 -4
  146. dodal/devices/eiger.py +32 -17
  147. dodal/devices/eiger_odin.py +1 -1
  148. dodal/devices/electron_analyser/base/__init__.py +3 -3
  149. dodal/devices/electron_analyser/base/base_controller.py +32 -21
  150. dodal/devices/electron_analyser/base/base_detector.py +15 -20
  151. dodal/devices/electron_analyser/base/base_driver_io.py +39 -46
  152. dodal/devices/electron_analyser/base/base_enums.py +0 -5
  153. dodal/devices/electron_analyser/base/base_region.py +29 -31
  154. dodal/devices/electron_analyser/base/base_util.py +18 -16
  155. dodal/devices/electron_analyser/base/energy_sources.py +35 -40
  156. dodal/devices/electron_analyser/specs/specs_detector.py +7 -6
  157. dodal/devices/electron_analyser/vgscienta/vgscienta_detector.py +7 -6
  158. dodal/devices/eurotherm.py +3 -2
  159. dodal/devices/fast_grid_scan.py +31 -34
  160. dodal/devices/fast_shutter.py +125 -39
  161. dodal/devices/flux.py +1 -1
  162. dodal/devices/focusing_mirror.py +29 -11
  163. dodal/devices/hutch_shutter.py +6 -6
  164. dodal/devices/insertion_device/__init__.py +20 -8
  165. dodal/devices/insertion_device/apple2_controller.py +371 -0
  166. dodal/devices/insertion_device/apple2_undulator.py +184 -587
  167. dodal/devices/insertion_device/apple_knot_controller.py +222 -0
  168. dodal/devices/insertion_device/energy.py +161 -0
  169. dodal/devices/insertion_device/energy_motor_lookup.py +21 -28
  170. dodal/devices/insertion_device/lookup_table_models.py +47 -52
  171. dodal/devices/insertion_device/polarisation.py +36 -0
  172. dodal/devices/ipin.py +1 -1
  173. dodal/devices/linkam3.py +7 -5
  174. dodal/devices/motors.py +107 -19
  175. dodal/devices/mx_phase1/beamstop.py +2 -4
  176. dodal/devices/oav/oav_calculations.py +20 -13
  177. dodal/devices/oav/oav_detector.py +92 -22
  178. dodal/devices/oav/oav_parameters.py +4 -9
  179. dodal/devices/oav/oav_to_redis_forwarder.py +22 -18
  180. dodal/devices/oav/pin_image_recognition/__init__.py +4 -6
  181. dodal/devices/oav/pin_image_recognition/manual_test.py +1 -2
  182. dodal/devices/oav/pin_image_recognition/utils.py +30 -32
  183. dodal/devices/oav/snapshots/grid_overlay.py +10 -9
  184. dodal/devices/oav/snapshots/snapshot_image_processing.py +15 -13
  185. dodal/devices/oav/utils.py +20 -6
  186. dodal/devices/p45.py +3 -9
  187. dodal/devices/pgm.py +8 -14
  188. dodal/devices/pressure_jump_cell.py +93 -32
  189. dodal/devices/qbpm.py +1 -3
  190. dodal/devices/robot.py +45 -20
  191. dodal/devices/s4_slit_gaps.py +1 -1
  192. dodal/devices/selectable_source.py +41 -0
  193. dodal/devices/slits.py +2 -5
  194. dodal/devices/smargon.py +2 -3
  195. dodal/devices/temperture_controller/lakeshore/lakeshore.py +38 -64
  196. dodal/devices/temperture_controller/lakeshore/lakeshore_io.py +21 -35
  197. dodal/devices/tetramm.py +7 -7
  198. dodal/devices/turbo_slit.py +8 -7
  199. dodal/devices/undulator.py +42 -56
  200. dodal/devices/util/adjuster_plans.py +2 -3
  201. dodal/devices/util/epics_util.py +10 -10
  202. dodal/devices/util/lookup_tables.py +17 -18
  203. dodal/devices/v2f.py +2 -3
  204. dodal/devices/watsonmarlow323_pump.py +1 -1
  205. dodal/devices/xbpm_feedback.py +3 -2
  206. dodal/devices/xspress3/xspress3.py +8 -11
  207. dodal/devices/xspress3/xspress3_channel.py +3 -6
  208. dodal/devices/zebra/zebra.py +21 -7
  209. dodal/devices/zebra/zebra_constants_mapping.py +12 -7
  210. dodal/devices/zebra/zebra_controlled_shutter.py +2 -1
  211. dodal/devices/zocalo/zocalo_interaction.py +14 -14
  212. dodal/devices/zocalo/zocalo_results.py +33 -33
  213. dodal/log.py +23 -20
  214. dodal/plan_stubs/check_topup.py +15 -15
  215. dodal/plan_stubs/data_session.py +6 -6
  216. dodal/plan_stubs/motor_utils.py +22 -18
  217. dodal/plan_stubs/pressure_jump_cell.py +18 -0
  218. dodal/plan_stubs/wrapped.py +40 -55
  219. dodal/plans/bimorph.py +63 -52
  220. dodal/plans/configure_arm_trigger_and_disarm_detector.py +0 -1
  221. dodal/plans/device_setup_plans/__init__.py +5 -0
  222. dodal/plans/device_setup_plans/setup_pin_tip_params.py +63 -0
  223. dodal/plans/preprocessors/verify_undulator_gap.py +10 -8
  224. dodal/plans/spec_path.py +3 -5
  225. dodal/plans/verify_undulator_gap.py +1 -2
  226. dodal/plans/wrapped.py +4 -3
  227. dodal/testing/__init__.py +0 -0
  228. dodal/testing/electron_analyser/device_factory.py +5 -7
  229. dodal/testing/fixtures/devices/apple2.py +38 -0
  230. dodal/testing/fixtures/run_engine.py +3 -7
  231. dodal/testing/fixtures/utils.py +1 -2
  232. dodal/utils.py +60 -58
  233. dls_dodal-1.68.0.dist-info/RECORD +0 -330
  234. dodal/beamline_specific_utils/i05_shared.py +0 -14
  235. dodal/devices/b07/__init__.py +0 -3
  236. dodal/devices/i04/max_pixel.py +0 -38
  237. dodal/devices/i05/__init__.py +0 -3
  238. dodal/devices/i09/__init__.py +0 -3
  239. dodal/devices/i21/__init__.py +0 -5
  240. {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/entry_points.txt +0 -0
  241. {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/licenses/LICENSE +0 -0
  242. {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/top_level.txt +0 -0
  243. /dodal/{beamline_specific_utils → devices/beamlines}/__init__.py +0 -0
  244. /dodal/devices/{b07 → beamlines/b07}/enums.py +0 -0
  245. /dodal/devices/{b07_1 → beamlines/b07_1}/enums.py +0 -0
  246. /dodal/devices/{b16 → beamlines/b16}/__init__.py +0 -0
  247. /dodal/devices/{i02_1 → beamlines/i02_1}/__init__.py +0 -0
  248. /dodal/devices/{i02_2 → beamlines/i02_2}/__init__.py +0 -0
  249. /dodal/devices/{i03 → beamlines/i03}/__init__.py +0 -0
  250. /dodal/devices/{i03 → beamlines/i03}/constants.py +0 -0
  251. /dodal/devices/{i04 → beamlines/i04}/__init__.py +0 -0
  252. /dodal/devices/{i04 → beamlines/i04}/constants.py +0 -0
  253. /dodal/devices/{i05 → beamlines/i05}/enums.py +0 -0
  254. /dodal/devices/{i07 → beamlines/i07}/__init__.py +0 -0
  255. /dodal/devices/{i09 → beamlines/i09}/enums.py +0 -0
  256. /dodal/devices/{i09_1 → beamlines/i09_1}/__init__.py +0 -0
  257. /dodal/devices/{i09_1 → beamlines/i09_1}/enums.py +0 -0
  258. /dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/__init__.py +0 -0
  259. /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/__init__.py +0 -0
  260. /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/i09_apple2.py +0 -0
  261. /dodal/devices/{i10 → beamlines/i10}/__init__.py +0 -0
  262. /dodal/devices/{i10 → beamlines/i10}/i10_setting_data.py +0 -0
  263. /dodal/devices/{i10 → beamlines/i10}/mirrors.py +0 -0
  264. /dodal/devices/{i10 → beamlines/i10}/rasor/__init__.py +0 -0
  265. /dodal/devices/{i10 → beamlines/i10}/rasor/rasor_scaler_cards.py +0 -0
  266. /dodal/devices/{i11 → beamlines/i10_1/electromagnet}/__init__.py +0 -0
  267. /dodal/devices/{i13_1 → beamlines/i11}/__init__.py +0 -0
  268. /dodal/devices/{i15 → beamlines/i13_1}/__init__.py +0 -0
  269. /dodal/devices/{i13_1 → beamlines/i13_1}/merlin_controller.py +0 -0
  270. /dodal/devices/{i17 → beamlines/i15}/__init__.py +0 -0
  271. /dodal/devices/{i15 → beamlines/i15}/laue.py +0 -0
  272. /dodal/devices/{i15 → beamlines/i15}/motors.py +0 -0
  273. /dodal/devices/{i15 → beamlines/i15}/rail.py +0 -0
  274. /dodal/devices/{i18 → beamlines/i17}/__init__.py +0 -0
  275. /dodal/devices/{i19 → beamlines/i18}/__init__.py +0 -0
  276. /dodal/devices/{i18 → beamlines/i18}/kb_mirror.py +0 -0
  277. /dodal/devices/{i19/access_controlled → beamlines/i19}/__init__.py +0 -0
  278. /dodal/devices/{i20_1 → beamlines/i19/access_controlled}/__init__.py +0 -0
  279. /dodal/devices/{i19 → beamlines/i19}/access_controlled/hutch_access.py +0 -0
  280. /dodal/devices/{i19 → beamlines/i19}/beamstop.py +0 -0
  281. /dodal/devices/{i19 → beamlines/i19}/diffractometer.py +0 -0
  282. /dodal/devices/{i22 → beamlines/i20_1}/__init__.py +0 -0
  283. /dodal/devices/{i21 → beamlines/i21}/enums.py +0 -0
  284. /dodal/devices/{i24 → beamlines/i22}/__init__.py +0 -0
  285. /dodal/devices/{p99 → beamlines/i24}/__init__.py +0 -0
  286. /dodal/devices/{i24 → beamlines/i24}/aperture.py +0 -0
  287. /dodal/devices/{i24 → beamlines/i24}/focus_mirrors.py +0 -0
  288. /dodal/devices/{i24 → beamlines/i24}/vgonio.py +0 -0
  289. /dodal/devices/{p60 → beamlines/p60}/__init__.py +0 -0
  290. /dodal/devices/{p60 → beamlines/p60}/enums.py +0 -0
  291. /dodal/devices/{p99 → beamlines/p99}/sample_stage.py +0 -0
  292. /dodal/devices/insertion_device/{id_enum.py → enum.py} +0 -0
dodal/device_manager.py CHANGED
@@ -4,7 +4,7 @@ import typing
4
4
  from collections import UserDict
5
5
  from collections.abc import Callable, Iterable, Mapping, MutableMapping
6
6
  from functools import cached_property, wraps
7
- from inspect import Parameter
7
+ from inspect import Parameter, cleandoc
8
8
  from types import NoneType
9
9
  from typing import (
10
10
  Annotated,
@@ -17,14 +17,10 @@ from typing import (
17
17
  TypeVar,
18
18
  )
19
19
 
20
- from bluesky.run_engine import (
21
- get_bluesky_event_loop,
22
- )
20
+ from bluesky.run_engine import get_bluesky_event_loop
23
21
  from ophyd.sim import make_fake_device
24
22
 
25
- from dodal.common.beamlines.beamline_utils import (
26
- wait_for_connection,
27
- )
23
+ from dodal.common.beamlines.beamline_utils import wait_for_connection
28
24
  from dodal.utils import (
29
25
  AnyDevice,
30
26
  OphydV1Device,
@@ -33,6 +29,7 @@ from dodal.utils import (
33
29
  )
34
30
 
35
31
  DEFAULT_TIMEOUT = 30
32
+ NO_DOCS = "No documentation available."
36
33
 
37
34
  T = TypeVar("T")
38
35
  Args = ParamSpec("Args")
@@ -44,12 +41,11 @@ DeviceFactoryDecorator = Callable[[Callable[Args, V2]], "DeviceFactory[Args, V2]
44
41
  OphydInitialiser = Callable[Concatenate[V1, Args], V1 | None]
45
42
 
46
43
  _EMPTY = object()
47
- """Sentinel value to distinguish between missing values and present but null values"""
44
+ """Sentinel value to distinguish between missing values and present but null values."""
48
45
 
49
46
 
50
47
  class LazyFixtures(UserDict[str, Any]):
51
- """
52
- Wrapper around fixtures and fixture generators
48
+ """Wrapper around fixtures and fixture generators.
53
49
 
54
50
  If a fixture is provided at runtime, the generator function does not have to be called.
55
51
  """
@@ -71,11 +67,9 @@ class LazyFixtures(UserDict[str, Any]):
71
67
 
72
68
 
73
69
  class DeviceFactory(Generic[Args, V2]):
74
- """
75
- Wrapper around a device factory (any function returning a device) that holds
70
+ """Wrapper around a device factory (any function returning a device) that holds
76
71
  a reference to a device manager that can provide dependencies, along with
77
- default connection information for how the created device should be
78
- connected.
72
+ default connection information for how the created device should be connected.
79
73
  """
80
74
 
81
75
  def __init__(
@@ -103,14 +97,17 @@ class DeviceFactory(Generic[Args, V2]):
103
97
  self._manager = manager
104
98
  wraps(factory)(self)
105
99
 
100
+ return_type = inspect.get_annotations(factory).get("return")
101
+ self.__doc__ = _format_doc(self, return_type)
102
+
106
103
  @property
107
104
  def name(self) -> str:
108
- """Name of the underlying factory function"""
105
+ """Name of the underlying factory function."""
109
106
  return self.factory.__name__
110
107
 
111
108
  @cached_property
112
109
  def dependencies(self) -> set[str]:
113
- """Names of all parameters"""
110
+ """Names of all parameters."""
114
111
  sig = inspect.signature(self.factory)
115
112
  return {
116
113
  para.name
@@ -120,7 +117,7 @@ class DeviceFactory(Generic[Args, V2]):
120
117
 
121
118
  @cached_property
122
119
  def optional_dependencies(self) -> set[str]:
123
- """Names of optional dependencies"""
120
+ """Names of optional dependencies."""
124
121
  sig = inspect.signature(self.factory)
125
122
  return {
126
123
  para.name
@@ -131,9 +128,8 @@ class DeviceFactory(Generic[Args, V2]):
131
128
 
132
129
  @property
133
130
  def skip(self) -> bool:
134
- """
135
- Whether this device should be skipped as part of build_all - it will
136
- still be built if a required device depends on it
131
+ """Whether this device should be skipped as part of build_all - it will
132
+ still be built if a required device depends on it.
137
133
  """
138
134
  return self._skip() if callable(self._skip) else self._skip
139
135
 
@@ -145,7 +141,7 @@ class DeviceFactory(Generic[Args, V2]):
145
141
  timeout: float | None = None,
146
142
  **fixtures,
147
143
  ) -> V2:
148
- """Build this device, building any dependencies first"""
144
+ """Build this device, building any dependencies first."""
149
145
  devices = self._manager.build_devices(
150
146
  self,
151
147
  fixtures=fixtures,
@@ -175,8 +171,7 @@ class DeviceFactory(Generic[Args, V2]):
175
171
 
176
172
  # TODO: Remove when ophyd v1 support is no longer required - see #1718
177
173
  class V1DeviceFactory(Generic[Args, V1]):
178
- """
179
- Wrapper around an ophyd v1 device that holds a reference to a device
174
+ """Wrapper around an ophyd v1 device that holds a reference to a device
180
175
  manager that can provide dependencies, along with default connection
181
176
  information for how the created device should be connected.
182
177
  """
@@ -202,15 +197,16 @@ class V1DeviceFactory(Generic[Args, V1]):
202
197
  self.post_create = init or (lambda x: x)
203
198
  self._manager = manager
204
199
  wraps(init)(self)
200
+ self.__doc__ = _format_doc(self, factory)
205
201
 
206
202
  @property
207
203
  def name(self) -> str:
208
- """Name of the underlying factory function"""
204
+ """Name of the underlying factory function."""
209
205
  return self.post_create.__name__
210
206
 
211
207
  @cached_property
212
208
  def dependencies(self) -> set[str]:
213
- """Names of all parameters"""
209
+ """Names of all parameters."""
214
210
  sig = inspect.signature(self.post_create)
215
211
  # first parameter should be the device we've just built
216
212
  _, *params = sig.parameters.values()
@@ -218,16 +214,15 @@ class V1DeviceFactory(Generic[Args, V1]):
218
214
 
219
215
  @cached_property
220
216
  def optional_dependencies(self) -> set[str]:
221
- """Names of optional dependencies"""
217
+ """Names of optional dependencies."""
222
218
  sig = inspect.signature(self.post_create)
223
219
  _, *params = sig.parameters.values()
224
220
  return {para.name for para in params if para.default is not Parameter.empty}
225
221
 
226
222
  @property
227
223
  def skip(self) -> bool:
228
- """
229
- Whether this device should be skipped as part of build_all - it will
230
- still be built if a required device depends on it
224
+ """Whether this device should be skipped as part of build_all - it will
225
+ still be built if a required device depends on it.
231
226
  """
232
227
  return self._skip() if callable(self._skip) else self._skip
233
228
 
@@ -248,7 +243,7 @@ class V1DeviceFactory(Generic[Args, V1]):
248
243
  )
249
244
 
250
245
  def __call__(self, dev: V1, *args: Args.args, **kwargs: Args.kwargs):
251
- """Call the wrapped function to make decorator transparent"""
246
+ """Call the wrapped function to make decorator transparent."""
252
247
  return self.post_create(dev, *args, **kwargs)
253
248
 
254
249
  def create(self, *args: Args.args, **kwargs: Args.kwargs) -> V1:
@@ -259,7 +254,7 @@ class V1DeviceFactory(Generic[Args, V1]):
259
254
  return device
260
255
 
261
256
  def build(self, mock: bool = False, fixtures: dict[str, Any] | None = None) -> V1:
262
- """Build this device, building any dependencies first"""
257
+ """Build this device, building any dependencies first."""
263
258
  devices = self._manager.build_devices(
264
259
  self,
265
260
  fixtures=fixtures,
@@ -274,21 +269,21 @@ class V1DeviceFactory(Generic[Args, V1]):
274
269
 
275
270
 
276
271
  class ConnectionSpec(NamedTuple):
277
- """The options used to configure a device"""
272
+ """The options used to configure a device."""
278
273
 
279
274
  mock: bool
280
275
  timeout: float
281
276
 
282
277
 
283
278
  class ConnectionResult(NamedTuple):
284
- """Wrapper around results of building and connecting devices"""
279
+ """Wrapper around results of building and connecting devices."""
285
280
 
286
281
  devices: dict[str, AnyDevice]
287
282
  build_errors: dict[str, Exception]
288
283
  connection_errors: dict[str, Exception]
289
284
 
290
285
  def or_raise(self) -> dict[str, Any]:
291
- """Re-raise any errors from build or connect stage or return devices"""
286
+ """Re-raise any errors from build or connect stage or return devices."""
292
287
  if self.build_errors or self.connection_errors:
293
288
  all_exc = []
294
289
  for name, exc in (self.build_errors | self.connection_errors).items():
@@ -299,14 +294,14 @@ class ConnectionResult(NamedTuple):
299
294
 
300
295
 
301
296
  class DeviceBuildResult(NamedTuple):
302
- """Wrapper around the results of building devices"""
297
+ """Wrapper around the results of building devices."""
303
298
 
304
299
  devices: dict[str, AnyDevice]
305
300
  errors: dict[str, Exception]
306
301
  connection_specs: dict[str, ConnectionSpec]
307
302
 
308
303
  def connect(self, timeout: float | None = None) -> ConnectionResult:
309
- """Connect all devices that didn't fail to build"""
304
+ """Connect all devices that didn't fail to build."""
310
305
  connections = {}
311
306
  connected = {}
312
307
  loop: asyncio.EventLoop = get_bluesky_event_loop() # type: ignore
@@ -335,7 +330,7 @@ class DeviceBuildResult(NamedTuple):
335
330
  return ConnectionResult(connected, self.errors, connection_errors)
336
331
 
337
332
  def or_raise(self) -> Self:
338
- """Re-raise any build errors"""
333
+ """Re-raise any build errors."""
339
334
  if self.errors:
340
335
  for name, exc in self.errors.items():
341
336
  exc.add_note(name)
@@ -344,7 +339,7 @@ class DeviceBuildResult(NamedTuple):
344
339
 
345
340
 
346
341
  class DeviceManager:
347
- """Manager to handle building and connecting interdependent devices"""
342
+ """Manager to handle building and connecting interdependent devices."""
348
343
 
349
344
  _factories: dict[str, DeviceFactory]
350
345
  _fixtures: dict[str, Callable[[], Any]]
@@ -355,11 +350,44 @@ class DeviceManager:
355
350
  self._v1_factories = {}
356
351
  self._fixtures = {}
357
352
 
353
+ def get_all_factories(self) -> dict[str, V1DeviceFactory | DeviceFactory]:
354
+ return self._factories | self._v1_factories
355
+
358
356
  def fixture(self, func: Callable[[], T]) -> Callable[[], T]:
359
- """Add a function that can provide fixtures required by the factories"""
357
+ """Add a function that can provide fixtures required by the factories."""
360
358
  self._fixtures[func.__name__] = func
361
359
  return func
362
360
 
361
+ def include(self, other: "DeviceManager"):
362
+ """Merge an external DeviceManager into this one.
363
+
364
+ Registered devices from the included DeviceManager will be included
365
+ when all devices are built and will be available as dependencies when
366
+ building specific devices from this manager. Names of devices still
367
+ need to be unique and an error will be raised if an included device
368
+ shares a name with any existing device.
369
+
370
+ Fixtures are also included and will override any previously defined
371
+ fixtures in this manager. Fixtures defined after including the other
372
+ manager will take precedence over included ones.
373
+ """
374
+ # Bug in pyright means type checking doesn't recognise 'DeviceManager'
375
+ # as this class and fails with private member access
376
+ common = self._factories.keys() & other._factories # noqa SLF001
377
+ common |= self._v1_factories.keys() & other._v1_factories # noqa SLF001
378
+ common |= self._factories.keys() & other._v1_factories # noqa SLF001
379
+ common |= self._v1_factories.keys() & other._factories # noqa SLF001
380
+ if common:
381
+ raise ValueError(
382
+ f"Duplicate factories in included device manager: {common}"
383
+ )
384
+
385
+ self._factories.update(other._factories) # noqa SLF001
386
+ self._v1_factories.update(other._v1_factories) # noqa SLF001
387
+
388
+ # duplicate fixtures are not checked as fixtures can be overridden
389
+ self._fixtures.update(other._fixtures) # noqa SLF001
390
+
363
391
  def v1_init(
364
392
  self,
365
393
  factory: type[V1],
@@ -369,8 +397,7 @@ class DeviceManager:
369
397
  wait: bool = True,
370
398
  timeout: int = DEFAULT_TIMEOUT,
371
399
  ):
372
- """
373
- Register an ophyd v1 device
400
+ """Register an ophyd v1 device.
374
401
 
375
402
  The function this decorates is an initialiser that takes a built device
376
403
  and is not used to create the device.
@@ -429,6 +456,7 @@ class DeviceManager:
429
456
  if func.__name__ in self:
430
457
  raise ValueError(f"Duplicate factory name: {func.__name__}")
431
458
  factory = DeviceFactory(func, use_factory_name, timeout, mock, skip, self)
459
+
432
460
  self._factories[func.__name__] = factory
433
461
  return factory
434
462
 
@@ -456,7 +484,7 @@ class DeviceManager:
456
484
  return self.build_devices(
457
485
  *(
458
486
  f
459
- for f in (self._factories | self._v1_factories).values()
487
+ for f in (self.get_all_factories()).values()
460
488
  # allow overriding skip but still allow fixtures to override devices
461
489
  if (include_skipped or not f.skip)
462
490
  ),
@@ -470,11 +498,9 @@ class DeviceManager:
470
498
  fixtures: Mapping[str, Any] | None = None,
471
499
  mock: bool = False,
472
500
  ) -> DeviceBuildResult:
473
- """
474
- Build the devices from the given factories, ensuring that any
501
+ """Build the devices from the given factories, ensuring that any
475
502
  dependencies are built first and passed to later factories as required.
476
503
  """
477
-
478
504
  fixtures = LazyFixtures(provided=fixtures, factories=self._fixtures)
479
505
  if common := fixtures.keys() & {f.name for f in factories}:
480
506
  factories = tuple(f for f in factories if f.name not in common)
@@ -529,15 +555,14 @@ class DeviceManager:
529
555
  factories: Iterable[DeviceFactory[..., V2] | V1DeviceFactory[..., V1]],
530
556
  available_fixtures: Mapping[str, Any],
531
557
  ) -> set[str]:
532
- """
533
- Determine full list of devices that are required to build the given devices.
558
+ """Determine full list of devices that are required to build the given devices.
534
559
  If a dependency is available via the fixtures, a matching device factory
535
560
  will not be included unless explicitly requested allowing for devices to
536
561
  be overridden.
537
562
 
538
- Errors:
539
- If a required dependency is not available as either a device
540
- factory or a fixture, a ValueError is raised
563
+ Raises:
564
+ If a required dependency is not available as either a device factory or a
565
+ fixture, a ValueError is raised
541
566
  """
542
567
  dependencies = set()
543
568
  factories = set(factories)
@@ -561,14 +586,12 @@ class DeviceManager:
561
586
  factories: dict[str, DeviceFactory[..., V2] | V1DeviceFactory[..., V1]],
562
587
  fixtures: Mapping[str, Any],
563
588
  ) -> list[str]:
564
- """
565
- Determine the order devices in which devices should be build to ensure
566
- that dependencies are built before the things that depend on them
589
+ """Determine the order devices in which devices should be build to ensure
590
+ that dependencies are built before the things that depend on them.
567
591
 
568
592
  Assumes that all required devices and fixtures are included in the
569
593
  given factory list.
570
594
  """
571
-
572
595
  # This is not an efficient way of doing this, however, for realistic use
573
596
  # cases, it is fast enough for now
574
597
  order = []
@@ -602,3 +625,20 @@ class DeviceManager:
602
625
 
603
626
  def __repr__(self) -> str:
604
627
  return f"<DeviceManager: {len(self)} devices>"
628
+
629
+
630
+ def _format_doc(
631
+ factory: DeviceFactory | V1DeviceFactory, return_type: type[V1 | V2] | None
632
+ ) -> str | None:
633
+ """Helper function to combine the doc strings of our factory instance and the
634
+ return type of the function we wrap.
635
+ """
636
+ if not return_type:
637
+ return factory.__doc__
638
+ if existing := factory.__doc__:
639
+ return f"{existing}\n\n{_type_docs(return_type)}"
640
+ return _type_docs(return_type)
641
+
642
+
643
+ def _type_docs(target: type[V1 | V2]) -> str:
644
+ return f"{target.__name__}:\n\n{cleandoc(target.__doc__ or NO_DOCS)}"
@@ -24,7 +24,7 @@ class InvalidApertureMoveError(Exception):
24
24
 
25
25
 
26
26
  class _GDAParamApertureValue(StrictEnum):
27
- """Maps from a short usable name to the value name in the GDA Beamline parameters"""
27
+ """Maps from a short usable name to the value name in the GDA Beamline parameters."""
28
28
 
29
29
  ROBOT_LOAD = "ROBOT_LOAD"
30
30
  SMALL = "SMALL_APERTURE"
@@ -34,16 +34,16 @@ class _GDAParamApertureValue(StrictEnum):
34
34
 
35
35
 
36
36
  class AperturePosition(BaseModel):
37
- """
38
- Represents one of the available positions for the Aperture-Scatterguard.
37
+ """Represents one of the available positions for the Aperture-Scatterguard.
38
+
39
39
  Attributes:
40
- aperture_x: The x position of the aperture component in mm
41
- aperture_y: The y position of the aperture component in mm
42
- aperture_z: The z position of the aperture component in mm
43
- scatterguard_x: The x position of the scatterguard component in mm
44
- scatterguard_y: The y position of the scatterguard component in mm
45
- radius: Radius of the selected aperture. When in the Robot Load position, the
46
- radius is defined to be 0
40
+ aperture_x (float): The x position of the aperture component in mm.
41
+ aperture_y (float): The y position of the aperture component in mm.
42
+ aperture_z (float): The z position of the aperture component in mm.
43
+ scatterguard_x (float): The x position of the scatterguard component in mm.
44
+ scatterguard_y (float): The y position of the scatterguard component in mm.
45
+ diameter (float, optional): Diameter of the selected aperture. When in the Robot
46
+ Load position, the diameter is defined to be 0.
47
47
  """
48
48
 
49
49
  aperture_x: float
@@ -51,7 +51,7 @@ class AperturePosition(BaseModel):
51
51
  aperture_z: float
52
52
  scatterguard_x: float
53
53
  scatterguard_y: float
54
- radius: float = Field(json_schema_extra={"units": "µm"}, default=0.0)
54
+ diameter: float = Field(json_schema_extra={"units": "µm"}, default=0.0)
55
55
 
56
56
  @property
57
57
  def values(self) -> tuple[float, float, float, float, float]:
@@ -78,7 +78,7 @@ class AperturePosition(BaseModel):
78
78
  @staticmethod
79
79
  def from_gda_params(
80
80
  name: _GDAParamApertureValue,
81
- radius: float,
81
+ diameter: float,
82
82
  params: GDABeamlineParameters,
83
83
  ) -> AperturePosition:
84
84
  return AperturePosition(
@@ -87,7 +87,7 @@ class AperturePosition(BaseModel):
87
87
  aperture_z=params[f"miniap_z_{name.value}"],
88
88
  scatterguard_x=params[f"sg_x_{name.value}"],
89
89
  scatterguard_y=params[f"sg_y_{name.value}"],
90
- radius=radius,
90
+ diameter=diameter,
91
91
  )
92
92
 
93
93
 
@@ -95,7 +95,7 @@ class ApertureValue(StrictEnum):
95
95
  """The possible apertures that can be selected.
96
96
 
97
97
  Changing these means changing the external parameter model of Hyperion.
98
- See https://github.com/DiamondLightSource/mx-bluesky/issues/760
98
+ See https://github.com/DiamondLightSource/mx-bluesky/issues/760.
99
99
  """
100
100
 
101
101
  SMALL = "SMALL_APERTURE"
@@ -139,8 +139,8 @@ class ApertureScatterguard(StandardReadable, Preparable):
139
139
 
140
140
  await aperture_scatterguard.selected_aperture.set(ApertureValue.LARGE)
141
141
 
142
- This will move the assembly so that the large aperture is in the beam, regardless
143
- of where the assembly currently is.
142
+ This will move the assembly so that the large aperture is in the beam,
143
+ regardless of where the assembly currently is.
144
144
 
145
145
  We may also want to move the assembly out of the beam with::
146
146
 
@@ -149,8 +149,8 @@ class ApertureScatterguard(StandardReadable, Preparable):
149
149
  Note, to make sure we do this as quickly as possible, the scatterguard will stay
150
150
  in the same position relative to the aperture.
151
151
 
152
- We may then want to keep the assembly out of the beam whilst asynchronously preparing
153
- the other axes for the aperture that's to follow::
152
+ We may then want to keep the assembly out of the beam whilst asynchronously
153
+ preparing the other axes for the aperture that's to follow::
154
154
 
155
155
  await aperture_scatterguard.prepare(ApertureValue.LARGE)
156
156
 
@@ -185,8 +185,8 @@ class ApertureScatterguard(StandardReadable, Preparable):
185
185
  current_ap_z=self.aperture.z.user_readback,
186
186
  )
187
187
 
188
- self.radius = derived_signal_r(
189
- self._get_current_radius,
188
+ self.diameter = derived_signal_r(
189
+ self._get_current_diameter,
190
190
  current_aperture=self.selected_aperture,
191
191
  derived_units="µm",
192
192
  )
@@ -198,7 +198,7 @@ class ApertureScatterguard(StandardReadable, Preparable):
198
198
  self.aperture.z.user_readback,
199
199
  self.scatterguard.x.user_readback,
200
200
  self.scatterguard.y.user_readback,
201
- self.radius,
201
+ self.diameter,
202
202
  ],
203
203
  )
204
204
 
@@ -262,8 +262,8 @@ class ApertureScatterguard(StandardReadable, Preparable):
262
262
  "triggering another move."
263
263
  )
264
264
 
265
- def _get_current_radius(self, current_aperture: ApertureValue) -> float:
266
- return self._loaded_positions[current_aperture].radius
265
+ def _get_current_diameter(self, current_aperture: ApertureValue) -> float:
266
+ return self._loaded_positions[current_aperture].diameter
267
267
 
268
268
  def _is_in_position(
269
269
  self, position: ApertureValue, current_ap_y: float, current_ap_z: float
@@ -300,11 +300,10 @@ class ApertureScatterguard(StandardReadable, Preparable):
300
300
  )
301
301
 
302
302
  async def _safe_move_whilst_in_beam(self, position: AperturePosition):
303
- """
304
- Move the aperture and scatterguard combo safely to a new position.
303
+ """Move the aperture and scatterguard combo safely to a new position.
305
304
  See https://github.com/DiamondLightSource/hyperion/wiki/Aperture-Scatterguard-Collisions
306
- for why this is required. TLDR is that we have a collision at the top of y so we need
307
- to make sure we move the assembly down before we move the scatterguard up.
305
+ for why this is required. TLDR is that we have a collision at the top of y so we
306
+ need to make sure we move the assembly down before we move the scatterguard up.
308
307
  """
309
308
  current_ap_y = await self.aperture.y.user_readback.get_value()
310
309
 
@@ -3,9 +3,7 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
3
3
 
4
4
 
5
5
  class ColorMode(StrictEnum):
6
- """
7
- Enum to store the various color modes of the camera. We use RGB1.
8
- """
6
+ """Enum to store the various color modes of the camera. We use RGB1."""
9
7
 
10
8
  MONO = "Mono"
11
9
  BAYER = "Bayer"
@@ -25,7 +25,8 @@ class MJPG(StandardReadable, Triggerable, ABC):
25
25
  """The MJPG areadetector plugin creates an MJPG video stream of the camera's output.
26
26
 
27
27
  This devices uses that stream to grab images. When it is triggered it will send the
28
- latest image from the stream to the `post_processing` method for child classes to handle.
28
+ latest image from the stream to the `post_processing` method for child classes to
29
+ handle.
29
30
  """
30
31
 
31
32
  def __init__(
@@ -51,9 +52,9 @@ class MJPG(StandardReadable, Triggerable, ABC):
51
52
  super().__init__(name)
52
53
 
53
54
  async def _save_image(self, image: Image.Image):
54
- """A helper function to save a given image to the path supplied by the \
55
- directory and filename signals. The full resultant path is put on the \
56
- last_saved_path signal
55
+ """A helper function to save a given image to the path supplied by the
56
+ directory and filename signals. The full resultant path is put on the
57
+ last_saved_path signal.
57
58
  """
58
59
  filename_str = await self.filename.get_value()
59
60
  directory_str = await self.directory.get_value()
@@ -74,7 +75,7 @@ class MJPG(StandardReadable, Triggerable, ABC):
74
75
  """This takes a snapshot image from the MJPG stream and send it to the
75
76
  post_processing method, expected to be implemented by a child of this class.
76
77
 
77
- It is the responsibility of the child class to save any resulting images by \
78
+ It is the responsibility of the child class to save any resulting images by
78
79
  calling _save_image.
79
80
  """
80
81
  url_str = await self.url.get_value()
@@ -37,13 +37,15 @@ class ReadOnlyAttenuator(StandardReadable):
37
37
 
38
38
  class BinaryFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
39
39
  """The attenuator will insert filters into the beam to reduce its transmission.
40
- In this attenuator, each filter can be in one of two states: IN or OUT
40
+ In this attenuator, each filter can be in one of two states: IN or OUT.
41
41
 
42
42
  This device should be set with:
43
+
43
44
  yield from bps.set(attenuator, desired_transmission)
44
45
 
45
46
  Where desired_transmission is fraction e.g. 0-1. When the actual_transmission is
46
- read from the device it is also fractional"""
47
+ read from the device it is also fractional
48
+ """
47
49
 
48
50
  def __init__(self, prefix: str, num_filters: int, name: str = ""):
49
51
  self._calculated_filter_states: DeviceVector[SignalR[int]] = DeviceVector(
@@ -72,11 +74,10 @@ class BinaryFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
72
74
  async def set(self, value: float):
73
75
  """Set the transmission to the fractional (0-1) value given.
74
76
 
75
- The attenuator IOC will then insert filters to reach the desired transmission for
76
- the current beamline energy, the set will only complete when they have all been
77
- applied.
77
+ The attenuator IOC will then insert filters to reach the desired transmission
78
+ for the current beamline energy, the set will only complete when they have all
79
+ been applied.
78
80
  """
79
-
80
81
  LOGGER.debug("Using current energy ")
81
82
  await self._use_current_energy.trigger()
82
83
  LOGGER.info(f"Setting desired transmission to {value}")
@@ -110,7 +111,8 @@ ENUM_ATTENUATOR_SETTLE_TIME_S = 0.15
110
111
  class EnumFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
111
112
  """The attenuator will insert filters into the beam to reduce its transmission.
112
113
 
113
- This device is currently working, but feature incomplete. See https://github.com/DiamondLightSource/dodal/issues/972
114
+ This device is currently working, but feature incomplete. See
115
+ https://github.com/DiamondLightSource/dodal/issues/972
114
116
 
115
117
  In this attenuator, the state of a filter corresponds to the selected material,
116
118
  e.g Ag50, in contrast to being either 'IN' or 'OUT'; see BinaryFilterAttenuator.
@@ -141,11 +143,10 @@ class EnumFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
141
143
  async def set(self, value: float):
142
144
  """Set the transmission to the fractional (0-1) value given.
143
145
 
144
- The attenuator IOC will then insert filters to reach the desired transmission for
145
- the current beamline energy, the set will only complete when they have all been
146
- applied.
146
+ The attenuator IOC will then insert filters to reach the desired transmission
147
+ for the current beamline energy, the set will only complete when they have all
148
+ been applied.
147
149
  """
148
-
149
150
  # auto move should normally be on, but check here incase it was manually turned off
150
151
  await self._auto_move_on_desired_transmission_set.set(YesNo.YES)
151
152
 
@@ -0,0 +1,3 @@
1
+ from dodal.devices.beamlines.b07.enums import Grating, LensMode, PsuMode
2
+
3
+ __all__ = ["Grating", "LensMode", "PsuMode"]
@@ -1,8 +1,8 @@
1
- from dodal.devices.b07_1.ccmc import (
1
+ from dodal.devices.beamlines.b07_1.ccmc import (
2
2
  ChannelCutMonochromator,
3
3
  ChannelCutMonochromatorPositions,
4
4
  )
5
- from dodal.devices.b07_1.enums import Grating, LensMode
5
+ from dodal.devices.beamlines.b07_1.enums import Grating, LensMode
6
6
 
7
7
  __all__ = [
8
8
  "Grating",
@@ -25,13 +25,16 @@ error_message = "Can not get energy value in eV from ccmc position: "
25
25
  class ChannelCutMonochromator(
26
26
  StandardReadable, Movable[ChannelCutMonochromatorPositions]
27
27
  ):
28
- """
29
- Device to move the channel cut monochromator (ccmc). CCMC has three
28
+ """Device to move the channel cut monochromator (ccmc). CCMC has three
30
29
  choices of crystal (Xtal for short). Setting energy is by means of a
31
30
  multi-positioner. The positions are named after the nominal energies of the
32
31
  crystals. To change energy select one of the crystals from the list.
33
32
  This causes the Y motor to move that crystal into the beam and other
34
33
  motors have to align the angles correctly.
34
+
35
+ Args:
36
+ prefix (str): Beamline specific part of the PV.
37
+ name (str, optional): Name of the device.
35
38
  """
36
39
 
37
40
  def __init__(
@@ -39,14 +42,6 @@ class ChannelCutMonochromator(
39
42
  prefix: str,
40
43
  name: str = "",
41
44
  ) -> None:
42
- """
43
- Parameters
44
- ----------
45
- prefix:
46
- Beamline specific part of the PV
47
- name:
48
- Name of the device
49
- """
50
45
  with self.add_children_as_readables():
51
46
  # crystal motors
52
47
  self._xyz = XYZStage(prefix)