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
@@ -30,8 +30,7 @@ class InvalidPinError(Exception):
30
30
 
31
31
 
32
32
  class PinTipDetection(StandardReadable):
33
- """
34
- A device which will read from an on-axis view and calculate the location of the
33
+ """A device which will read from an on-axis view and calculate the location of the
35
34
  pin-tip (in pixels) of that frame.
36
35
 
37
36
  Used for pin tip centring workflow.
@@ -42,7 +41,8 @@ class PinTipDetection(StandardReadable):
42
41
  If no tip is found it will return {INVALID_POSITION}. However, it will also
43
42
  occasionally give incorrect data. Therefore, it is recommended that you trigger
44
43
  this device, which will attempt to find a pin within {validity_timeout} seconds if
45
- no tip is found after this time it will not error but instead return {INVALID_POSITION}.
44
+ no tip is found after this time it will not error but instead return
45
+ {INVALID_POSITION}.
46
46
  """
47
47
 
48
48
  INVALID_POSITION = np.array([np.iinfo(np.int32).min, np.iinfo(np.int32).min])
@@ -98,9 +98,7 @@ class PinTipDetection(StandardReadable):
98
98
  self._bottom_edge_setter(results.edge_bottom)
99
99
 
100
100
  async def _get_tip_and_edge_data(self, array_data: np.ndarray) -> SampleLocation:
101
- """
102
- Gets the location of the pin tip and the top and bottom edges.
103
- """
101
+ """Gets the location of the pin tip and the top and bottom edges."""
104
102
  preprocess_key = await self.preprocess_operation.get_value()
105
103
  preprocess_iter = await self.preprocess_iterations.get_value()
106
104
  preprocess_ksize = await self.preprocess_ksize.get_value()
@@ -1,5 +1,4 @@
1
- """
2
- Note: this file exists exclusively to make it easier to manually run
1
+ """Note: this file exists exclusively to make it easier to manually run
3
2
  image recognition on a beamline without setting up all of the
4
3
  bluesky infrastructure.
5
4
 
@@ -100,9 +100,7 @@ NONE_VALUE: Final[int] = -1
100
100
 
101
101
  @dataclass
102
102
  class SampleLocation:
103
- """
104
- Holder type for results from sample detection.
105
- """
103
+ """Holder type for results from sample detection."""
106
104
 
107
105
  tip_x: int | None
108
106
  tip_y: int | None
@@ -111,6 +109,28 @@ class SampleLocation:
111
109
 
112
110
 
113
111
  class MxSampleDetect:
112
+ """Configures sample detection parameters.
113
+
114
+ Args:
115
+ preprocess (Callable): A preprocessing function applied to the array after
116
+ conversion to grayscale. See implementations of common functions above
117
+ for predefined conversions. Defaults to a no-op (i.e. no preprocessing).
118
+
119
+ for open and close operations, please read:
120
+ https://docs.opencv.org/4.x/d9/d61/tutorial_py_morphological_ops.html
121
+
122
+ open_ksize (int, optional): Kernel size for "open" operation, if set to zero
123
+ then ignore "open" operation.
124
+ open_iterations (int, optional): Number of iterations for "open" operation.
125
+ canny_upper (int, optional): Upper threshold for canny edge detection.
126
+ canny_lower (int, optional): Lower threshold for canny edge detection.
127
+ close_ksize (int, optional): Kernel size for "close" operation.
128
+ close_iterations (int, optional): Number of iterations for "close" operation.
129
+ scan_direction (ScanDirections, optional): ScanDirections.FORWARD for
130
+ left-to-right, ScanDirections.REVERSE for right-to-left.
131
+ min_tip_height (int, optional): Minimum height of pin tip.
132
+ """
133
+
114
134
  def __init__(
115
135
  self,
116
136
  *,
@@ -124,27 +144,6 @@ class MxSampleDetect:
124
144
  scan_direction: ScanDirections = ScanDirections.FORWARD,
125
145
  min_tip_height: int = 5,
126
146
  ):
127
- """
128
- Configures sample detection parameters.
129
-
130
- Args:
131
- preprocess: A preprocessing function applied to the array after conversion to grayscale.
132
- See implementations of common functions above for predefined conversions
133
- Defaults to a no-op (i.e. no preprocessing)
134
-
135
- for open and close operations, please read:
136
- https://docs.opencv.org/4.x/d9/d61/tutorial_py_morphological_ops.html
137
-
138
- open_ksize: kernel size for "open" operation, if set to zero then ignore "open" operation
139
- open_iterations: number of iterations for "open" operation
140
- canny_upper: upper threshold for canny edge detection
141
- canny_lower: lower threshold for canny edge detection
142
- close_ksize: kernel size for "close" operation
143
- close_iterations: number of iterations for "close" operation
144
- scan_direction: ScanDirections.FORWARD for left-to-right, ScanDirections.REVERSE for right-to-left
145
- min_tip_height: minimum height of pin tip
146
- """
147
-
148
147
  self.preprocess = preprocess
149
148
  self.open_ksize = open_ksize
150
149
  self.open_iterations = open_iterations
@@ -185,17 +184,16 @@ class MxSampleDetect:
185
184
  def _first_and_last_nonzero_by_columns(
186
185
  arr: np.ndarray,
187
186
  ) -> tuple[np.ndarray, np.ndarray]:
188
- """
189
- Finds the indexes of the first & last non-zero values by column in a 2d array.
187
+ """Finds the indexes of the first & last non-zero values by column in a 2d array.
190
188
 
191
189
  Outputs will contain NONE_VALUE if no non-zero values exist in a column.
192
190
 
193
- i.e. for input:
194
- [
195
- [0, 0, 0, 1],
196
- [1, 1, 0, 0],
197
- [0, 1, 0, 1],
198
- ]
191
+ i.e. for input::
192
+ [
193
+ [0, 0, 0, 1],
194
+ [1, 1, 0, 0],
195
+ [0, 1, 0, 1],
196
+ ]
199
197
 
200
198
  first_nonzero will be: [1, 1, NONE_VALUE, 0]
201
199
  last_nonzero will be [1, 2, NONE_VALUE, 2]
@@ -19,12 +19,12 @@ def _add_parallel_lines_to_image(
19
19
  orientation=Orientation.horizontal,
20
20
  ):
21
21
  """Draws horizontal or vertical parallel lines on a given image.
22
- Draws a line of a given length and orientation starting from a given point; then \
23
- draws a given number of parallel lines equally spaced with a given spacing. \
24
- If the line is horizontal, the start point corresponds to left end of the initial \
25
- line and the other parallel lines will be drawn below the initial line; if \
26
- vertical, the start point corresponds to the top end of the initial line and the \
27
- other parallel lines will be drawn to the right of the initial line. (0,0) is the \
22
+ Draws a line of a given length and orientation starting from a given point; then
23
+ draws a given number of parallel lines equally spaced with a given spacing.
24
+ If the line is horizontal, the start point corresponds to left end of the initial
25
+ line and the other parallel lines will be drawn below the initial line; if
26
+ vertical, the start point corresponds to the top end of the initial line and the
27
+ other parallel lines will be drawn to the right of the initial line. (0,0) is the
28
28
  top left of the image.
29
29
 
30
30
  Args:
@@ -32,11 +32,12 @@ def _add_parallel_lines_to_image(
32
32
  start_x (int): The x coordinate (in pixels) of the start of the initial line.
33
33
  start_y (int): The y coordinate (in pixels) of the start of the initial line.
34
34
  line_length (int): The length of each of the parallel lines in pixels.
35
- spacing (float): The spacing, in pixels, between each parallel line. Strictly, \
35
+ spacing (float): The spacing, in pixels, between each parallel line. Strictly,
36
36
  there are spacing-1 pixels between each line
37
37
  num_lines (int): The total number of parallel lines to draw.
38
- orientation (Orientation): The orientation (horizontal or vertical) of the \
39
- parallel lines to draw."""
38
+ orientation (Orientation): The orientation (horizontal or vertical) of the
39
+ parallel lines to draw.
40
+ """
40
41
  lines = [
41
42
  (
42
43
  (
@@ -8,12 +8,12 @@ CROSSHAIR_FILL_COLOUR = "White"
8
8
 
9
9
 
10
10
  def draw_crosshair(image: Image.Image, beam_x: int, beam_y: int):
11
- """
12
- Draw a crosshair at the beam centre coordinates specified.
11
+ """Draw a crosshair at the beam centre coordinates specified.
12
+
13
13
  Args:
14
- image: The image to draw the crosshair onto. This is mutated.
15
- beam_x: The x-coordinate of the crosshair (pixels)
16
- beam_y: The y-coordinate of the crosshair (pixels)
14
+ image (Image): The image to draw the crosshair onto. This is mutated.
15
+ beam_x (int): The x-coordinate of the crosshair (pixels).
16
+ beam_y (int): The y-coordinate of the crosshair (pixels).
17
17
  """
18
18
  draw = ImageDraw.Draw(image)
19
19
  outline_width = 1
@@ -51,16 +51,18 @@ def compute_beam_centre_pixel_xy_for_mm_position(
51
51
  beam_pos_at_origin_px: Pixel,
52
52
  microns_per_pixel: tuple[float, float],
53
53
  ) -> Pixel:
54
- """
55
- Compute the location of the beam centre in pixels on a reference image.
54
+ """Compute the location of the beam centre in pixels on a reference image.
55
+
56
56
  Args:
57
- sample_pos_mm: x, y location of the sample in mm relative to when the reference image
58
- was taken.
59
- beam_pos_at_origin_px: x, y position of the beam centre in the reference image (pixels)
60
- microns_per_pixel: x, y scaling factor relating the sample position to the position in the image.
61
- Returns:
62
- x, y location of the beam centre (pixels)
57
+ sample_pos_mm (tuple[float, float]): x, y location of the sample in mm relative
58
+ to when the reference image was taken.
59
+ beam_pos_at_origin_px (Pixel): x, y position of the beam centre in the reference
60
+ image (pixels).
61
+ microns_per_pixel (tuple[float, float]): x, y scaling factor relating the sample
62
+ position to the position in the image.
63
63
 
64
+ Returns:
65
+ Pixel: x, y location of the beam centre (pixels).
64
66
  """
65
67
 
66
68
  def centre(sample_pos, beam_pos, um_per_px) -> int:
@@ -2,6 +2,7 @@ from collections.abc import Generator
2
2
  from enum import IntEnum
3
3
 
4
4
  import bluesky.plan_stubs as bps
5
+ import cv2
5
6
  import numpy as np
6
7
  from bluesky.utils import Msg
7
8
 
@@ -40,9 +41,7 @@ def bottom_right_from_top_left(
40
41
 
41
42
 
42
43
  class EdgeOutputArrayImageType(IntEnum):
43
- """
44
- Enum to store the types of image to tweak the output array. We use Original.
45
- """
44
+ """Enum to store the types of image to tweak the output array. We use Original."""
46
45
 
47
46
  ORIGINAL = 0
48
47
  GREYSCALE = 1
@@ -56,8 +55,9 @@ def get_move_required_so_that_beam_is_at_pixel(
56
55
  pixel: Pixel,
57
56
  oav: OAV,
58
57
  ) -> Generator[Msg, None, np.ndarray]:
59
- """Calculate the required move so that the given pixel is in the centre of the beam."""
60
-
58
+ """Calculate the required move so that the given pixel is in the centre of the
59
+ beam.
60
+ """
61
61
  current_motor_xyz = np.array(
62
62
  [
63
63
  (yield from bps.rd(gonio.x)),
@@ -94,7 +94,7 @@ def calculate_x_y_z_of_pixel(
94
94
  microns_per_pixel: tuple[float, float],
95
95
  xyz_direction: tuple[int, int, int],
96
96
  ) -> np.ndarray:
97
- """Get the x, y, z position of a pixel in mm"""
97
+ """Get the x, y, z position of a pixel in mm."""
98
98
  beam_distance_px: Pixel = calculate_beam_distance(beam_centre, *pixel)
99
99
 
100
100
  return current_x_y_z + camera_coordinates_to_xyz_mm(
@@ -119,3 +119,17 @@ def wait_for_tip_to_be_found(
119
119
  raise PinNotFoundError(f"No pin found after {timeout} seconds")
120
120
 
121
121
  return Pixel((int(found_tip[0]), int(found_tip[1])))
122
+
123
+
124
+ def convert_to_gray_and_blur(data: cv2.typing.MatLike) -> cv2.typing.MatLike:
125
+ """Preprocess the image array data (convert to grayscale and apply a gaussian blur)
126
+ Image is converted to grayscale (using a weighted mean as green contributes more to
127
+ brightness) as we aren't interested in data relating to colour. A blur is then
128
+ applied to mitigate errors due to rogue hot pixels.
129
+ """
130
+ # kernel size describes how many of the neighbouring pixels are used for the blur,
131
+ # higher kernal size means more of a blur effect
132
+ kernel_size = (7, 7)
133
+
134
+ gray_arr = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
135
+ return cv2.GaussianBlur(gray_arr, kernel_size, 0)
dodal/devices/p45.py CHANGED
@@ -3,9 +3,7 @@ from ophyd_async.epics.motor import Motor
3
3
 
4
4
 
5
5
  class SampleY(StandardReadable):
6
- """
7
- Motors for controlling the sample's y position and stretch in the y axis.
8
- """
6
+ """Motors for controlling the sample's y position and stretch in the y axis."""
9
7
 
10
8
  def __init__(self, prefix: str, name: str = ""):
11
9
  with self.add_children_as_readables():
@@ -17,9 +15,7 @@ class SampleY(StandardReadable):
17
15
 
18
16
 
19
17
  class SampleTheta(StandardReadable):
20
- """
21
- Motors for controlling the sample's theta position and skew
22
- """
18
+ """Motors for controlling the sample's theta position and skew."""
23
19
 
24
20
  def __init__(self, prefix: str, name: str = ""):
25
21
  with self.add_children_as_readables():
@@ -31,9 +27,7 @@ class SampleTheta(StandardReadable):
31
27
 
32
28
 
33
29
  class TomoStageWithStretchAndSkew(StandardReadable):
34
- """
35
- Grouping of motors for the P45 tomography stage
36
- """
30
+ """Grouping of motors for the P45 tomography stage."""
37
31
 
38
32
  def __init__(self, prefix: str, name: str = ""):
39
33
  with self.add_children_as_readables():
dodal/devices/pgm.py CHANGED
@@ -8,8 +8,14 @@ from ophyd_async.epics.motor import Motor
8
8
 
9
9
 
10
10
  class PlaneGratingMonochromator(StandardReadable):
11
- """
12
- Plane grating monochromator, it is use in soft x-ray beamline to generate monochromic beam.
11
+ """Plane grating monochromator, it is use in soft x-ray beamline to generate
12
+ monochromic beam.
13
+
14
+ Args:
15
+ prefix (str): Beamline specific part of the PV.
16
+ grating (type[StrictEnum]): The Enum for the grating table.
17
+ grating_pv (str): The suffix PV part of grating PV.
18
+ name (str, optional): Name of the device.
13
19
  """
14
20
 
15
21
  def __init__(
@@ -19,18 +25,6 @@ class PlaneGratingMonochromator(StandardReadable):
19
25
  grating_pv: str = "GRATINGSELECT:SELECT",
20
26
  name: str = "",
21
27
  ) -> None:
22
- """
23
- Parameters
24
- ----------
25
- prefix:
26
- Beamline specific part of the PV
27
- grating:
28
- The Enum for the grating table.
29
- grating_pv:
30
- The suffix pv part of grating Pv
31
- name:
32
- Name of the device
33
- """
34
28
  with self.add_children_as_readables():
35
29
  self.energy = Motor(prefix + "ENERGY")
36
30
  with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
@@ -2,14 +2,21 @@ import asyncio
2
2
  from enum import IntEnum
3
3
  from typing import Generic, TypeVar
4
4
 
5
- from bluesky.protocols import Movable
5
+ from bluesky.protocols import Movable, Stoppable, Triggerable
6
6
  from ophyd_async.core import (
7
7
  AsyncStatus,
8
+ DeviceMock,
8
9
  DeviceVector,
10
+ Reference,
9
11
  SignalR,
10
12
  StandardReadable,
11
13
  StandardReadableFormat,
12
14
  StrictEnum,
15
+ callback_on_mock_put,
16
+ default_mock_class,
17
+ set_and_wait_for_other_value,
18
+ set_mock_value,
19
+ wait_for_value,
13
20
  )
14
21
  from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
15
22
 
@@ -22,11 +29,6 @@ class PumpState(StrictEnum):
22
29
  AUTO_POSITION = "Auto Position"
23
30
 
24
31
 
25
- class StopState(StrictEnum):
26
- CONTINUE = "CONTINUE"
27
- STOP = "STOP"
28
-
29
-
30
32
  class ValveControlRequest(StrictEnum):
31
33
  OPEN = "Open"
32
34
  CLOSE = "Close"
@@ -99,10 +101,8 @@ class ValveControl(
99
101
 
100
102
 
101
103
  class AllValvesControl(StandardReadable):
102
- """
103
- The default IOC for this device only controls
104
- specific valves. Other valves are under manual
105
- control.
104
+ """The default IOC for this device only controls specific valves. Other valves are
105
+ under manual control.
106
106
  """
107
107
 
108
108
  def __init__(
@@ -163,12 +163,10 @@ class Pump(StandardReadable):
163
163
 
164
164
 
165
165
  class PressureTransducer(StandardReadable):
166
- """
167
- Pressure transducer for a high pressure X-ray cell.
166
+ """Pressure transducer for a high pressure X-ray cell.
168
167
  This is the chamber and there are three of them.
169
168
  1 is the start, 3 is where the sample is.
170
169
  NOTE: the distinction between the adc prefix and the cell prefix is kept here.
171
-
172
170
  """
173
171
 
174
172
  def __init__(
@@ -201,34 +199,99 @@ class PressureTransducer(StandardReadable):
201
199
  super().__init__(name)
202
200
 
203
201
 
204
- class PressureJumpCellController(StandardReadable):
205
- """
206
- Top-level control for a fixed pressure or pressure jumps.
207
- """
202
+ class DoJump(StandardReadable, Triggerable):
203
+ def __init__(
204
+ self,
205
+ prefix: str,
206
+ busy_signal: Reference,
207
+ timeout_signal: Reference,
208
+ name: str = "",
209
+ ):
210
+ self._busy_signal = busy_signal
211
+ self._timeout_signal = timeout_signal
208
212
 
209
- def __init__(self, prefix: str, name: str = "") -> None:
210
213
  with self.add_children_as_readables():
211
- self.stop = epics_signal_rw(StopState, f"{prefix}STOP")
214
+ self.set_jump = epics_signal_rw(bool, f"{prefix}SETJUMP")
215
+
216
+ self._name = name
217
+ super().__init__(name)
218
+
219
+ @AsyncStatus.wrap
220
+ async def trigger(self):
221
+ await set_and_wait_for_other_value(
222
+ self.set_jump, True, self._busy_signal(), True
223
+ )
224
+
225
+ timeout_value = await self._timeout_signal().get_value()
226
+ await wait_for_value(self._busy_signal(), False, timeout_value)
212
227
 
213
- self.target_pressure = epics_signal_rw(float, f"{prefix}TARGET")
214
- self.timeout = epics_signal_rw(float, f"{prefix}TIMER.HIGH")
215
- self.go = epics_signal_rw(bool, f"{prefix}GO")
216
228
 
217
- self.from_pressure = epics_signal_rw(float, f"{prefix}JUMPF")
218
- self.to_pressure = epics_signal_rw(float, f"{prefix}JUMPT")
219
- self.jump_ready = epics_signal_rw(bool, f"{prefix}SETJUMP")
229
+ class PressureJumpCellController(StandardReadable, Movable, Stoppable):
230
+ """Top-level control for a fixed pressure or pressure jumps."""
220
231
 
232
+ def __init__(self, prefix: str, name: str = "") -> None:
233
+ with self.add_children_as_readables():
234
+ # Common
235
+ self.busy = epics_signal_r(bool, f"{prefix}GOTOBUSY")
221
236
  self.result = epics_signal_r(str, f"{prefix}RESULT")
237
+ self.timeout = epics_signal_rw(float, f"{prefix}TIMER.HIGH")
222
238
 
239
+ # Constant pressure
240
+ self.target_pressure = epics_signal_rw(int, f"{prefix}TARGET")
241
+ self.go = epics_signal_rw(bool, f"{prefix}GO")
242
+
243
+ # Pressure jump
244
+ self.from_pressure = epics_signal_rw(int, f"{prefix}JUMPF")
245
+ self.to_pressure = epics_signal_rw(int, f"{prefix}JUMPT")
246
+ self.do_jump = DoJump(
247
+ prefix, Reference(self.busy), Reference(self.timeout), name
248
+ )
249
+
250
+ # Internal
251
+ self._stop = epics_signal_rw(bool, f"{prefix}STOP")
223
252
  self._name = name
253
+
224
254
  super().__init__(name)
225
255
 
256
+ @AsyncStatus.wrap
257
+ async def set(self, value: int):
258
+ """Sets the desired pressure waiting for the device to complete the
259
+ operation.
260
+ """
261
+ timeout = await self.timeout.get_value()
262
+
263
+ await self.target_pressure.set(value)
264
+ await set_and_wait_for_other_value(self.go, True, self.busy, True)
265
+
266
+ await wait_for_value(self.busy, False, timeout) # Change complete
226
267
 
268
+ @AsyncStatus.wrap
269
+ async def stop(self, success=True):
270
+ await self._stop.set(True)
271
+
272
+
273
+ class BusyMock(DeviceMock["PressureJumpCell"]):
274
+ async def connect(self, device) -> None:
275
+ async def busy(*_, **__):
276
+ async def busy_idle():
277
+ await asyncio.sleep(0)
278
+ set_mock_value(device.control.busy, True)
279
+ await asyncio.sleep(0)
280
+ set_mock_value(device.control.busy, False)
281
+
282
+ asyncio.create_task(busy_idle())
283
+
284
+ callback_on_mock_put(device.control.go, busy)
285
+ callback_on_mock_put(device.control.do_jump.set_jump, busy)
286
+
287
+
288
+ @default_mock_class(BusyMock)
227
289
  class PressureJumpCell(StandardReadable):
228
- """
229
- High pressure X-ray cell, used to apply pressure or pressure jumps to a sample.
230
- prefix: str
231
- The prefix of beamline - SPECIAL - unusual that the cell prefix is computed separately
290
+ """High pressure X-ray cell, used to apply pressure or pressure jumps to a sample.
291
+
292
+ Args:
293
+ prefix (str): The prefix of beamline - SPECIAL - unusual that the cell prefix is
294
+ computed separately.
232
295
  """
233
296
 
234
297
  def __init__(
@@ -241,9 +304,7 @@ class PressureJumpCell(StandardReadable):
241
304
  self.all_valves_control = AllValvesControl(f"{prefix}{cell_prefix}", name)
242
305
  self.pump = Pump(f"{prefix}{cell_prefix}", name)
243
306
 
244
- self.controller = PressureJumpCellController(
245
- f"{prefix}{cell_prefix}CTRL:", name
246
- )
307
+ self.control = PressureJumpCellController(f"{prefix}{cell_prefix}CTRL:", name)
247
308
 
248
309
  with self.add_children_as_readables():
249
310
  self.pressure_transducers: DeviceVector[PressureTransducer] = DeviceVector(
dodal/devices/qbpm.py CHANGED
@@ -3,9 +3,7 @@ from ophyd_async.epics.core import epics_signal_r
3
3
 
4
4
 
5
5
  class QBPM(StandardReadable):
6
- """
7
- A beam position monitor that gives a position and intensity of the beam.
8
- """
6
+ """A beam position monitor that gives a position and intensity of the beam."""
9
7
 
10
8
  def __init__(
11
9
  self,