dls-dodal 1.69.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 (282) hide show
  1. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/METADATA +1 -1
  2. dls_dodal-2.0.0.dist-info/RECORD +354 -0
  3. {dls_dodal-1.69.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 +17 -17
  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 +3 -3
  14. dodal/beamlines/i02_2.py +1 -1
  15. dodal/beamlines/i03.py +4 -4
  16. dodal/beamlines/i04.py +16 -8
  17. dodal/beamlines/i05.py +7 -45
  18. dodal/beamlines/i05_1.py +4 -13
  19. dodal/beamlines/i05_shared.py +51 -0
  20. dodal/beamlines/i06_1.py +7 -5
  21. dodal/beamlines/{i06.py → i06_shared.py} +25 -14
  22. dodal/beamlines/i07.py +14 -16
  23. dodal/beamlines/i09.py +54 -51
  24. dodal/beamlines/i09_1.py +25 -64
  25. dodal/beamlines/i09_1_shared.py +61 -0
  26. dodal/beamlines/i09_2.py +6 -100
  27. dodal/beamlines/i09_2_shared.py +110 -0
  28. dodal/beamlines/i10.py +60 -54
  29. dodal/beamlines/i10_1.py +99 -10
  30. dodal/beamlines/{i10_optics.py → i10_shared.py} +80 -66
  31. dodal/beamlines/i11.py +31 -18
  32. dodal/beamlines/i13_1.py +1 -1
  33. dodal/beamlines/i15.py +6 -6
  34. dodal/beamlines/i15_1.py +6 -6
  35. dodal/beamlines/i17.py +37 -28
  36. dodal/beamlines/i18.py +3 -4
  37. dodal/beamlines/i19_1.py +95 -34
  38. dodal/beamlines/i19_2.py +68 -52
  39. dodal/beamlines/i19_optics.py +26 -13
  40. dodal/beamlines/i20_1.py +6 -14
  41. dodal/beamlines/i21.py +35 -28
  42. dodal/beamlines/i22.py +19 -4
  43. dodal/beamlines/i23.py +1 -2
  44. dodal/beamlines/i24.py +11 -10
  45. dodal/beamlines/k07.py +99 -5
  46. dodal/beamlines/p38.py +3 -3
  47. dodal/beamlines/p60.py +28 -17
  48. dodal/beamlines/p99.py +16 -15
  49. dodal/beamlines/training_rig.py +20 -12
  50. dodal/cli.py +36 -2
  51. dodal/common/beamlines/beamline_parameters.py +2 -1
  52. dodal/common/beamlines/beamline_utils.py +11 -9
  53. dodal/common/beamlines/commissioning_mode.py +6 -3
  54. dodal/common/coordination.py +12 -14
  55. dodal/common/crystal_metadata.py +5 -8
  56. dodal/common/device_utils.py +4 -3
  57. dodal/common/maths.py +28 -40
  58. dodal/common/udc_directory_provider.py +13 -8
  59. dodal/common/visit.py +18 -21
  60. dodal/common/watcher_utils.py +13 -12
  61. dodal/device_manager.py +94 -54
  62. dodal/devices/aperturescatterguard.py +26 -27
  63. dodal/devices/areadetector/plugins/cam.py +1 -3
  64. dodal/devices/areadetector/plugins/mjpg.py +6 -5
  65. dodal/devices/attenuator/attenuator.py +12 -11
  66. dodal/devices/beamlines/b07/__init__.py +3 -0
  67. dodal/devices/{b07_1 → beamlines/b07_1}/__init__.py +2 -2
  68. dodal/devices/{b07_1 → beamlines/b07_1}/ccmc.py +5 -10
  69. dodal/devices/{b16 → beamlines/b16}/detector.py +2 -3
  70. dodal/devices/{i02_1 → beamlines/i02_1}/fast_grid_scan.py +2 -3
  71. dodal/devices/{i02_1 → beamlines/i02_1}/sample_motors.py +1 -1
  72. dodal/devices/{i03 → beamlines/i03}/beamsize.py +11 -7
  73. dodal/devices/{i03 → beamlines/i03}/dcm.py +1 -2
  74. dodal/devices/{i03 → beamlines/i03}/undulator_dcm.py +4 -5
  75. dodal/devices/beamlines/i04/beam_centre.py +151 -0
  76. dodal/devices/{i04 → beamlines/i04}/beamsize.py +11 -7
  77. dodal/devices/{i04 → beamlines/i04}/murko_results.py +5 -5
  78. dodal/devices/{i04 → beamlines/i04}/transfocator.py +10 -15
  79. dodal/devices/beamlines/i05/__init__.py +3 -0
  80. dodal/devices/beamlines/i06_shared/__init__.py +3 -0
  81. dodal/devices/beamlines/i06_shared/i06_enum.py +7 -0
  82. dodal/devices/{i07 → beamlines/i07}/dcm.py +2 -3
  83. dodal/devices/{i07 → beamlines/i07}/id.py +8 -9
  84. dodal/devices/beamlines/i09/__init__.py +3 -0
  85. dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_energy.py +5 -6
  86. dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_undulator_functions.py +19 -16
  87. dodal/devices/{i10 → beamlines/i10}/diagnostics.py +4 -3
  88. dodal/devices/{i10 → beamlines/i10}/i10_apple2.py +31 -45
  89. dodal/devices/{i10 → beamlines/i10}/rasor/rasor_current_amp.py +1 -24
  90. dodal/devices/{i10 → beamlines/i10}/rasor/rasor_motors.py +2 -2
  91. dodal/devices/{i10 → beamlines/i10}/slits.py +5 -3
  92. dodal/devices/beamlines/i10_1/__init__.py +9 -0
  93. dodal/devices/beamlines/i10_1/electromagnet/magnet.py +16 -0
  94. dodal/devices/beamlines/i10_1/electromagnet/stages.py +14 -0
  95. dodal/devices/beamlines/i10_1/scaler_cards.py +13 -0
  96. dodal/devices/{i11 → beamlines/i11}/cyberstar_blower.py +1 -1
  97. dodal/devices/{i11 → beamlines/i11}/diff_stages.py +4 -6
  98. dodal/devices/{i11 → beamlines/i11}/mythen.py +3 -4
  99. dodal/devices/{i11 → beamlines/i11}/nx100robot.py +6 -6
  100. dodal/devices/{i11 → beamlines/i11}/spinner.py +1 -1
  101. dodal/devices/{i13_1 → beamlines/i13_1}/merlin.py +1 -1
  102. dodal/devices/{i15 → beamlines/i15}/dcm.py +1 -2
  103. dodal/devices/{i15 → beamlines/i15}/focussing_mirror.py +5 -5
  104. dodal/devices/{i15 → beamlines/i15}/jack.py +2 -2
  105. dodal/devices/{i15 → beamlines/i15}/multilayer_mirror.py +1 -1
  106. dodal/devices/{i17 → beamlines/i17}/i17_apple2.py +10 -16
  107. dodal/devices/{i18 → beamlines/i18}/diode.py +1 -1
  108. dodal/devices/{i19 → beamlines/i19}/access_controlled/attenuator_motor_squad.py +12 -8
  109. dodal/devices/{i19 → beamlines/i19}/access_controlled/blueapi_device.py +16 -15
  110. dodal/devices/beamlines/i19/access_controlled/piezo_control.py +72 -0
  111. dodal/devices/{i19 → beamlines/i19}/access_controlled/shutter.py +11 -9
  112. dodal/devices/{i19 → beamlines/i19}/backlight.py +3 -1
  113. dodal/devices/{i19 → beamlines/i19}/mapt_configuration.py +2 -1
  114. dodal/devices/{i19 → beamlines/i19}/pin_col_stages.py +11 -8
  115. dodal/devices/beamlines/i19/pin_tip.py +32 -0
  116. dodal/devices/beamlines/i21/__init__.py +3 -0
  117. dodal/devices/{i22 → beamlines/i22}/dcm.py +1 -2
  118. dodal/devices/{i22 → beamlines/i22}/fswitch.py +1 -3
  119. dodal/devices/{i22 → beamlines/i22}/nxsas.py +5 -4
  120. dodal/devices/{i24 → beamlines/i24}/beam_center.py +1 -1
  121. dodal/devices/{i24 → beamlines/i24}/beamstop.py +2 -2
  122. dodal/devices/{i24 → beamlines/i24}/commissioning_jungfrau.py +3 -2
  123. dodal/devices/{i24 → beamlines/i24}/dcm.py +1 -3
  124. dodal/devices/{i24 → beamlines/i24}/dual_backlight.py +3 -3
  125. dodal/devices/{i24 → beamlines/i24}/pmac.py +9 -7
  126. dodal/devices/{p60 → beamlines/p60}/lab_xray_source.py +1 -1
  127. dodal/devices/beamlines/p99/__init__.py +0 -0
  128. dodal/devices/{p99 → beamlines/p99}/andor2_point.py +11 -15
  129. dodal/devices/bimorph_mirror.py +22 -20
  130. dodal/devices/collimation_table.py +3 -2
  131. dodal/devices/common_dcm.py +30 -20
  132. dodal/devices/controllers.py +2 -2
  133. dodal/devices/cryostream.py +8 -0
  134. dodal/devices/current_amplifiers/current_amplifier.py +16 -18
  135. dodal/devices/current_amplifiers/current_amplifier_detector.py +9 -10
  136. dodal/devices/current_amplifiers/femto.py +8 -9
  137. dodal/devices/current_amplifiers/sr570.py +16 -16
  138. dodal/devices/current_amplifiers/struck_scaler_counter.py +5 -5
  139. dodal/devices/detector/det_resolution.py +9 -8
  140. dodal/devices/detector/detector.py +4 -2
  141. dodal/devices/diamond_filter.py +3 -4
  142. dodal/devices/eiger.py +3 -3
  143. dodal/devices/eiger_odin.py +1 -1
  144. dodal/devices/electron_analyser/base/base_controller.py +13 -13
  145. dodal/devices/electron_analyser/base/base_detector.py +15 -20
  146. dodal/devices/electron_analyser/base/base_driver_io.py +39 -46
  147. dodal/devices/electron_analyser/base/base_region.py +27 -30
  148. dodal/devices/electron_analyser/base/base_util.py +18 -16
  149. dodal/devices/electron_analyser/base/energy_sources.py +13 -19
  150. dodal/devices/eurotherm.py +3 -2
  151. dodal/devices/fast_grid_scan.py +31 -34
  152. dodal/devices/fast_shutter.py +24 -21
  153. dodal/devices/flux.py +1 -1
  154. dodal/devices/focusing_mirror.py +29 -11
  155. dodal/devices/hutch_shutter.py +6 -6
  156. dodal/devices/insertion_device/__init__.py +8 -0
  157. dodal/devices/insertion_device/apple2_controller.py +51 -60
  158. dodal/devices/insertion_device/apple2_undulator.py +40 -64
  159. dodal/devices/insertion_device/apple_knot_controller.py +222 -0
  160. dodal/devices/insertion_device/energy.py +100 -27
  161. dodal/devices/insertion_device/energy_motor_lookup.py +20 -27
  162. dodal/devices/insertion_device/lookup_table_models.py +45 -50
  163. dodal/devices/insertion_device/polarisation.py +1 -1
  164. dodal/devices/ipin.py +1 -1
  165. dodal/devices/linkam3.py +7 -5
  166. dodal/devices/motors.py +107 -19
  167. dodal/devices/mx_phase1/beamstop.py +2 -4
  168. dodal/devices/oav/oav_calculations.py +20 -13
  169. dodal/devices/oav/oav_detector.py +28 -23
  170. dodal/devices/oav/oav_parameters.py +4 -9
  171. dodal/devices/oav/oav_to_redis_forwarder.py +22 -18
  172. dodal/devices/oav/pin_image_recognition/__init__.py +4 -6
  173. dodal/devices/oav/pin_image_recognition/manual_test.py +1 -2
  174. dodal/devices/oav/pin_image_recognition/utils.py +30 -32
  175. dodal/devices/oav/snapshots/grid_overlay.py +10 -9
  176. dodal/devices/oav/snapshots/snapshot_image_processing.py +15 -13
  177. dodal/devices/oav/utils.py +9 -12
  178. dodal/devices/p45.py +3 -9
  179. dodal/devices/pgm.py +8 -14
  180. dodal/devices/pressure_jump_cell.py +93 -32
  181. dodal/devices/qbpm.py +1 -3
  182. dodal/devices/robot.py +12 -4
  183. dodal/devices/s4_slit_gaps.py +1 -1
  184. dodal/devices/selectable_source.py +5 -2
  185. dodal/devices/slits.py +2 -5
  186. dodal/devices/smargon.py +2 -3
  187. dodal/devices/temperture_controller/lakeshore/lakeshore.py +38 -64
  188. dodal/devices/temperture_controller/lakeshore/lakeshore_io.py +21 -35
  189. dodal/devices/tetramm.py +7 -7
  190. dodal/devices/turbo_slit.py +8 -7
  191. dodal/devices/undulator.py +42 -56
  192. dodal/devices/util/adjuster_plans.py +2 -3
  193. dodal/devices/util/epics_util.py +10 -10
  194. dodal/devices/util/lookup_tables.py +17 -18
  195. dodal/devices/v2f.py +2 -3
  196. dodal/devices/watsonmarlow323_pump.py +1 -1
  197. dodal/devices/xbpm_feedback.py +3 -2
  198. dodal/devices/xspress3/xspress3.py +8 -11
  199. dodal/devices/xspress3/xspress3_channel.py +3 -6
  200. dodal/devices/zebra/zebra.py +6 -7
  201. dodal/devices/zebra/zebra_constants_mapping.py +11 -7
  202. dodal/devices/zebra/zebra_controlled_shutter.py +2 -1
  203. dodal/devices/zocalo/zocalo_interaction.py +14 -14
  204. dodal/devices/zocalo/zocalo_results.py +33 -33
  205. dodal/log.py +23 -20
  206. dodal/plan_stubs/check_topup.py +15 -15
  207. dodal/plan_stubs/data_session.py +6 -6
  208. dodal/plan_stubs/motor_utils.py +22 -18
  209. dodal/plan_stubs/pressure_jump_cell.py +18 -0
  210. dodal/plan_stubs/wrapped.py +40 -55
  211. dodal/plans/bimorph.py +63 -52
  212. dodal/plans/device_setup_plans/__init__.py +5 -0
  213. dodal/plans/device_setup_plans/setup_pin_tip_params.py +63 -0
  214. dodal/plans/preprocessors/verify_undulator_gap.py +10 -8
  215. dodal/plans/spec_path.py +3 -5
  216. dodal/plans/verify_undulator_gap.py +1 -2
  217. dodal/plans/wrapped.py +4 -3
  218. dodal/testing/electron_analyser/device_factory.py +5 -7
  219. dodal/testing/fixtures/devices/apple2.py +38 -0
  220. dodal/testing/fixtures/run_engine.py +3 -7
  221. dodal/testing/fixtures/utils.py +1 -2
  222. dodal/utils.py +60 -58
  223. dls_dodal-1.69.0.dist-info/RECORD +0 -338
  224. dodal/beamline_specific_utils/i05_shared.py +0 -14
  225. dodal/devices/b07/__init__.py +0 -3
  226. dodal/devices/i04/beam_centre.py +0 -84
  227. dodal/devices/i05/__init__.py +0 -3
  228. dodal/devices/i09/__init__.py +0 -3
  229. dodal/devices/i21/__init__.py +0 -5
  230. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/entry_points.txt +0 -0
  231. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/licenses/LICENSE +0 -0
  232. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/top_level.txt +0 -0
  233. /dodal/{beamline_specific_utils → devices/beamlines}/__init__.py +0 -0
  234. /dodal/devices/{b07 → beamlines/b07}/enums.py +0 -0
  235. /dodal/devices/{b07_1 → beamlines/b07_1}/enums.py +0 -0
  236. /dodal/devices/{b16 → beamlines/b16}/__init__.py +0 -0
  237. /dodal/devices/{i02_1 → beamlines/i02_1}/__init__.py +0 -0
  238. /dodal/devices/{i02_2 → beamlines/i02_2}/__init__.py +0 -0
  239. /dodal/devices/{i03 → beamlines/i03}/__init__.py +0 -0
  240. /dodal/devices/{i03 → beamlines/i03}/constants.py +0 -0
  241. /dodal/devices/{i04 → beamlines/i04}/__init__.py +0 -0
  242. /dodal/devices/{i04 → beamlines/i04}/constants.py +0 -0
  243. /dodal/devices/{i04 → beamlines/i04}/max_pixel.py +0 -0
  244. /dodal/devices/{i05 → beamlines/i05}/enums.py +0 -0
  245. /dodal/devices/{i07 → beamlines/i07}/__init__.py +0 -0
  246. /dodal/devices/{i09 → beamlines/i09}/enums.py +0 -0
  247. /dodal/devices/{i09_1 → beamlines/i09_1}/__init__.py +0 -0
  248. /dodal/devices/{i09_1 → beamlines/i09_1}/enums.py +0 -0
  249. /dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/__init__.py +0 -0
  250. /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/__init__.py +0 -0
  251. /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/i09_apple2.py +0 -0
  252. /dodal/devices/{i10 → beamlines/i10}/__init__.py +0 -0
  253. /dodal/devices/{i10 → beamlines/i10}/i10_setting_data.py +0 -0
  254. /dodal/devices/{i10 → beamlines/i10}/mirrors.py +0 -0
  255. /dodal/devices/{i10 → beamlines/i10}/rasor/__init__.py +0 -0
  256. /dodal/devices/{i10 → beamlines/i10}/rasor/rasor_scaler_cards.py +0 -0
  257. /dodal/devices/{i11 → beamlines/i10_1/electromagnet}/__init__.py +0 -0
  258. /dodal/devices/{i13_1 → beamlines/i11}/__init__.py +0 -0
  259. /dodal/devices/{i15 → beamlines/i13_1}/__init__.py +0 -0
  260. /dodal/devices/{i13_1 → beamlines/i13_1}/merlin_controller.py +0 -0
  261. /dodal/devices/{i17 → beamlines/i15}/__init__.py +0 -0
  262. /dodal/devices/{i15 → beamlines/i15}/laue.py +0 -0
  263. /dodal/devices/{i15 → beamlines/i15}/motors.py +0 -0
  264. /dodal/devices/{i15 → beamlines/i15}/rail.py +0 -0
  265. /dodal/devices/{i18 → beamlines/i17}/__init__.py +0 -0
  266. /dodal/devices/{i19 → beamlines/i18}/__init__.py +0 -0
  267. /dodal/devices/{i18 → beamlines/i18}/kb_mirror.py +0 -0
  268. /dodal/devices/{i19/access_controlled → beamlines/i19}/__init__.py +0 -0
  269. /dodal/devices/{i20_1 → beamlines/i19/access_controlled}/__init__.py +0 -0
  270. /dodal/devices/{i19 → beamlines/i19}/access_controlled/hutch_access.py +0 -0
  271. /dodal/devices/{i19 → beamlines/i19}/beamstop.py +0 -0
  272. /dodal/devices/{i19 → beamlines/i19}/diffractometer.py +0 -0
  273. /dodal/devices/{i22 → beamlines/i20_1}/__init__.py +0 -0
  274. /dodal/devices/{i21 → beamlines/i21}/enums.py +0 -0
  275. /dodal/devices/{i24 → beamlines/i22}/__init__.py +0 -0
  276. /dodal/devices/{p99 → beamlines/i24}/__init__.py +0 -0
  277. /dodal/devices/{i24 → beamlines/i24}/aperture.py +0 -0
  278. /dodal/devices/{i24 → beamlines/i24}/focus_mirrors.py +0 -0
  279. /dodal/devices/{i24 → beamlines/i24}/vgonio.py +0 -0
  280. /dodal/devices/{p60 → beamlines/p60}/__init__.py +0 -0
  281. /dodal/devices/{p60 → beamlines/p60}/enums.py +0 -0
  282. /dodal/devices/{p99 → beamlines/p99}/sample_stage.py +0 -0
dodal/devices/motors.py CHANGED
@@ -8,29 +8,31 @@ from ophyd_async.epics.motor import Motor
8
8
  _X, _Y, _Z = "X", "Y", "Z"
9
9
 
10
10
  _OMEGA = "OMEGA"
11
+ _POLAR = "POLAR"
12
+ _AZIMUTH = "AZIMUTH"
13
+ _TILT = "TILT"
11
14
 
12
15
 
13
16
  class Stage(StandardReadable, ABC):
14
- """
15
- For these devices, the following co-ordinates are typical but not enforced:
17
+ """For these devices, the following co-ordinates are typical but not enforced:
16
18
  - z is horizontal & parallel to the direction of beam travel
17
19
  - y is vertical and antiparallel to the force of gravity
18
20
  - x is the cross product of y🞬z
19
21
 
20
- Parameters
21
- ----------
22
- prefix:
23
- Common part of the EPICS PV for all motors, including ":".
24
- name:
25
- Name of the stage, each child motor will be named "{name}-{field_name}"
26
- *_infix:
27
- Infix between the common prefix and the EPICS motor record fields for the field.
28
- """
22
+ Attributes:
23
+ prefix (str): Common part of the EPICS PV for all motors, including ":".
24
+ name (str, optional): Name of the stage, each child motor will be named
25
+ "{name}-{field_name}".
26
+ *_infix: Infix between the common prefix and the EPICS motor record fields for
27
+ the field.
28
+ """ # noqa D415
29
29
 
30
30
  ...
31
31
 
32
32
 
33
33
  class XThetaStage(Stage):
34
+ """Two-axis stage with an x and a theta motor."""
35
+
34
36
  def __init__(
35
37
  self, prefix: str, name: str = "", x_infix: str = _X, theta_infix: str = "A"
36
38
  ):
@@ -41,6 +43,8 @@ class XThetaStage(Stage):
41
43
 
42
44
 
43
45
  class XYStage(Stage):
46
+ """A standard two-axis stage with an x and a y motor."""
47
+
44
48
  def __init__(
45
49
  self, prefix: str, name: str = "", x_infix: str = _X, y_infix: str = _Y
46
50
  ):
@@ -51,6 +55,8 @@ class XYStage(Stage):
51
55
 
52
56
 
53
57
  class XYZStage(XYStage):
58
+ """A standard three-axis stage with an x, a y, and a z motor."""
59
+
54
60
  def __init__(
55
61
  self,
56
62
  prefix: str,
@@ -65,6 +71,8 @@ class XYZStage(XYStage):
65
71
 
66
72
 
67
73
  class XYZThetaStage(XYZStage):
74
+ """Four-axis stage with a standard xyz stage and one axis of rotation: theta."""
75
+
68
76
  def __init__(
69
77
  self,
70
78
  prefix: str,
@@ -80,6 +88,8 @@ class XYZThetaStage(XYZStage):
80
88
 
81
89
 
82
90
  class XYZOmegaStage(XYZStage):
91
+ """Four-axis stage with a standard xyz stage and one axis of rotation: omega."""
92
+
83
93
  def __init__(
84
94
  self,
85
95
  prefix: str,
@@ -94,7 +104,69 @@ class XYZOmegaStage(XYZStage):
94
104
  super().__init__(prefix, name, x_infix, y_infix, z_infix)
95
105
 
96
106
 
107
+ class XYZPolarStage(XYZStage):
108
+ """Four-axis stage with a standard xyz stage and one axis of rotation: polar."""
109
+
110
+ def __init__(
111
+ self,
112
+ prefix: str,
113
+ name: str = "",
114
+ x_infix: str = _X,
115
+ y_infix: str = _Y,
116
+ z_infix: str = _Z,
117
+ polar_infix: str = _POLAR,
118
+ ) -> None:
119
+ with self.add_children_as_readables():
120
+ self.polar = Motor(prefix + polar_infix)
121
+ super().__init__(prefix, name, x_infix, y_infix, z_infix)
122
+
123
+
124
+ class XYZPolarAzimuthStage(XYZPolarStage):
125
+ """Five-axis stage with a standard xyz stage and two axis of rotation: polar and
126
+ azimuth.
127
+ """
128
+
129
+ def __init__(
130
+ self,
131
+ prefix: str,
132
+ name: str = "",
133
+ x_infix: str = _X,
134
+ y_infix: str = _Y,
135
+ z_infix: str = _Z,
136
+ polar_infix: str = _POLAR,
137
+ azimuth_infix: str = _AZIMUTH,
138
+ ):
139
+ with self.add_children_as_readables():
140
+ self.azimuth = Motor(prefix + azimuth_infix)
141
+ super().__init__(prefix, name, x_infix, y_infix, z_infix, polar_infix)
142
+
143
+
144
+ class XYZPolarAzimuthTiltStage(XYZPolarAzimuthStage):
145
+ """Six-axis stage with a standard xyz stage and three axis of rotation: polar,
146
+ azimuth and tilt.
147
+ """
148
+
149
+ def __init__(
150
+ self,
151
+ prefix: str,
152
+ name: str = "",
153
+ x_infix: str = _X,
154
+ y_infix: str = _Y,
155
+ z_infix: str = _Z,
156
+ polar_infix: str = _POLAR,
157
+ azimuth_infix: str = _AZIMUTH,
158
+ tilt_infix: str = _TILT,
159
+ ):
160
+ with self.add_children_as_readables():
161
+ self.tilt = Motor(prefix + tilt_infix)
162
+ super().__init__(
163
+ prefix, name, x_infix, y_infix, z_infix, polar_infix, azimuth_infix
164
+ )
165
+
166
+
97
167
  class XYPhiStage(XYStage):
168
+ """Three-axis stage with a standard xy stage and one axis of rotation: phi."""
169
+
98
170
  def __init__(
99
171
  self,
100
172
  prefix: str,
@@ -109,6 +181,8 @@ class XYPhiStage(XYStage):
109
181
 
110
182
 
111
183
  class XYPitchStage(XYStage):
184
+ """Three-axis stage with a standard xy stage and one axis of rotation: pitch."""
185
+
112
186
  def __init__(
113
187
  self,
114
188
  prefix: str,
@@ -123,6 +197,8 @@ class XYPitchStage(XYStage):
123
197
 
124
198
 
125
199
  class XYRollStage(XYStage):
200
+ """Three-axis stage with a standard xy stage and one axis of rotation: roll."""
201
+
126
202
  def __init__(
127
203
  self,
128
204
  prefix: str,
@@ -137,6 +213,10 @@ class XYRollStage(XYStage):
137
213
 
138
214
 
139
215
  class XYZPitchYawStage(XYZStage):
216
+ """Five-axis stage with a standard xyz stage and two axes of rotation: pitch and
217
+ yaw.
218
+ """
219
+
140
220
  def __init__(
141
221
  self,
142
222
  prefix: str,
@@ -154,6 +234,10 @@ class XYZPitchYawStage(XYZStage):
154
234
 
155
235
 
156
236
  class XYZPitchYawRollStage(XYZStage):
237
+ """Five-axis stage with a standard xyz stage and three axes of rotation: pitch, yaw,
238
+ and roll.
239
+ """
240
+
157
241
  def __init__(
158
242
  self,
159
243
  prefix: str,
@@ -173,6 +257,10 @@ class XYZPitchYawRollStage(XYZStage):
173
257
 
174
258
 
175
259
  class SixAxisGonio(XYZOmegaStage):
260
+ """Six-axis goniometer with a standard xyz stage and three axes of rotation:
261
+ kappa, phi and omega.
262
+ """
263
+
176
264
  def __init__(
177
265
  self,
178
266
  prefix: str,
@@ -184,9 +272,6 @@ class SixAxisGonio(XYZOmegaStage):
184
272
  phi_infix: str = "PHI",
185
273
  omega_infix: str = _OMEGA,
186
274
  ):
187
- """Six-axis goniometer with a standard xyz stage and three axes of rotation:
188
- kappa, phi and omega.
189
- """
190
275
  with self.add_children_as_readables():
191
276
  self.kappa = Motor(prefix + kappa_infix)
192
277
  self.phi = Motor(prefix + phi_infix)
@@ -198,6 +283,10 @@ class SixAxisGonio(XYZOmegaStage):
198
283
 
199
284
 
200
285
  class SixAxisGonioKappaPhi(XYZStage):
286
+ """Six-axis goniometer with a standard xyz stage and two axes of rotation:
287
+ kappa and phi.
288
+ """
289
+
201
290
  def __init__(
202
291
  self,
203
292
  prefix: str,
@@ -208,9 +297,6 @@ class SixAxisGonioKappaPhi(XYZStage):
208
297
  kappa_infix: str = "KAPPA",
209
298
  phi_infix: str = "PHI",
210
299
  ):
211
- """Six-axis goniometer with a standard xyz stage and two axes of rotation:
212
- kappa and phi.
213
- """
214
300
  with self.add_children_as_readables():
215
301
  self.kappa = Motor(prefix + kappa_infix)
216
302
  self.phi = Motor(prefix + phi_infix)
@@ -218,6 +304,8 @@ class SixAxisGonioKappaPhi(XYZStage):
218
304
 
219
305
 
220
306
  class YZStage(Stage):
307
+ """Two-axis stage with an x and a z motor."""
308
+
221
309
  def __init__(
222
310
  self, prefix: str, name: str = "", y_infix: str = _Y, z_infix: str = _Z
223
311
  ) -> None:
@@ -244,9 +332,9 @@ def create_axis_perp_to_rotation(motor_theta: Motor, motor_i: Motor, motor_j: Mo
244
332
  Args:
245
333
  motor_theta (Motor): this is the rotation axis of the sample.
246
334
  motor_i (Motor): this is the axis that, when the sample is at 0 deg rotation,
247
- a move here is entirely parallel with the derived axis.
335
+ a move here is entirely parallel with the derived axis.
248
336
  motor_j (Motor): this is the axis that, when the sample is at 90 deg rotation,
249
- a move here is entirely parallel with the derived axis.
337
+ a move here is entirely parallel with the derived axis.
250
338
  """
251
339
 
252
340
  def _get(j_val: float, i_val: float, rot_value: float) -> float:
@@ -14,8 +14,7 @@ _BEAMSTOP_OUT_DELTA_Y_MM = -2
14
14
 
15
15
 
16
16
  class BeamstopPositions(StrictEnum):
17
- """
18
- Beamstop positions.
17
+ """Beamstop positions.
19
18
  GDA supports Standard/High/Low resolution positions, as well as parked and
20
19
  robot load however all 3 resolution positions are the same. We also
21
20
  do not use the robot load position in Hyperion.
@@ -35,8 +34,7 @@ class BeamstopPositions(StrictEnum):
35
34
 
36
35
 
37
36
  class Beamstop(StandardReadable):
38
- """
39
- Beamstop for I03 and I04.
37
+ """Beamstop for I03 and I04.
40
38
 
41
39
  Attributes:
42
40
  x: beamstop x position in mm
@@ -11,19 +11,24 @@ def camera_coordinates_to_xyz_mm(
11
11
  y_vertical_sign: int,
12
12
  z_vertical_sign: int,
13
13
  ) -> np.ndarray:
14
- """
15
- Converts from (horizontal,vertical) pixel measurements from the OAV camera into to (x, y, z) motor coordinates in millimetres.
16
- For an overview of the coordinate system for I03 see https://github.com/DiamondLightSource/hyperion/wiki/Gridscan-Coordinate-System.
14
+ """Converts from (horizontal,vertical) pixel measurements from the OAV camera into
15
+ to (x, y, z) motor coordinates in millimetres.
16
+ For an overview of the coordinate system for I03 see
17
+ https://github.com/DiamondLightSource/hyperion/wiki/Gridscan-Coordinate-System.
17
18
 
18
19
  Args:
19
20
  horizontal (float): A i value from the camera in pixels.
20
21
  vertical (float): A j value from the camera in pixels.
21
- omega (float): The omega angle of the smargon that the horizontal, vertical measurements were obtained at.
22
- microns_per_i_pixel (float): The number of microns per i pixel, adjusted for the zoom level horizontal was measured at.
23
- microns_per_j_pixel (float): The number of microns per j pixel, adjusted for the zoom level vertical was measured at.
24
- x_horizontal_sign (int): Direction mapping for x, positive means the oav and motor are on same direction, default from hyperion
25
- y_vertical_sign (int): Direction mapping for y
26
- z_vertical_sign (int): Direction mapping for z
22
+ omega (float): The omega angle of the smargon that the horizontal, vertical
23
+ measurements were obtained at.
24
+ microns_per_i_pixel (float): The number of microns per i pixel, adjusted for
25
+ the zoom level horizontal was measured at.
26
+ microns_per_j_pixel (float): The number of microns per j pixel, adjusted for
27
+ the zoom level vertical was measured at.
28
+ x_horizontal_sign (int): Direction mapping for x, positive means the oav and
29
+ motor are on same direction, default from hyperion.
30
+ y_vertical_sign (int): Direction mapping for y.
31
+ z_vertical_sign (int): Direction mapping for z.
27
32
  """
28
33
  # Convert the vertical and horizontal into mm.
29
34
  horizontal *= microns_per_i_pixel * 1e-3
@@ -49,15 +54,17 @@ def calculate_beam_distance(
49
54
  horizontal_pixels: int,
50
55
  vertical_pixels: int,
51
56
  ) -> tuple[int, int]:
52
- """
53
- Calculates the distance between the beam centre and the given (horizontal, vertical).
57
+ """Calculates the distance between the beam centre and the given (horizontal,
58
+ vertical).
54
59
 
55
60
  Args:
61
+ beam_centre (tuple[int, int]): The position of the beam in pixels.
56
62
  horizontal_pixels (int): The x (camera coordinates) value in pixels.
57
63
  vertical_pixels (int): The y (camera coordinates) value in pixels.
64
+
58
65
  Returns:
59
- The distance between the beam centre and the (horizontal, vertical) point in pixels as a tuple
60
- (horizontal_distance, vertical_distance).
66
+ tuple: The distance between the beam centre and the (horizontal, vertical) point
67
+ in pixels as a tuple (horizontal_distance, vertical_distance).
61
68
  """
62
69
  beam_x, beam_y = beam_centre
63
70
  return (
@@ -64,7 +64,9 @@ class NullZoomController(BaseZoomController):
64
64
  class BeamCentreForZoom(StandardReadable):
65
65
  """These PVs hold the beam centre on the OAV at each zoom level.
66
66
 
67
- When the zoom level is changed the IOC will update the OAV overlay PVs to be at these positions."""
67
+ When the zoom level is changed the IOC will update the OAV overlay PVs to be at
68
+ these positions.
69
+ """
68
70
 
69
71
  def __init__(
70
72
  self, prefix: str, level_name_pv_suffix: str, centre_value_pv_suffix: str
@@ -91,14 +93,14 @@ class InstantMovingZoom(DeviceMock["ZoomController"]):
91
93
 
92
94
  @default_mock_class(InstantMovingZoom)
93
95
  class ZoomController(BaseZoomController):
94
- """
95
- Device to control the zoom level. This should be set like
96
- o = OAV(name="oav")
96
+ """Device to control the zoom level. This should be set like::
97
+
98
+ oav = OAV(name="oav")
97
99
  oav.zoom_controller.set("1.0x")
98
100
 
99
101
  Note that changing the zoom may change the AD wiring on the associated OAV, as such
100
102
  you should wait on any zoom changes to finish before changing the OAV wiring.
101
- """
103
+ """ # noqa 415
102
104
 
103
105
  DELAY_BETWEEN_MOTORS_AND_IMAGE_UPDATING_S = 2
104
106
 
@@ -114,7 +116,7 @@ class ZoomController(BaseZoomController):
114
116
  async def set(self, value: str):
115
117
  await self.level.set(value, wait=True)
116
118
  LOGGER.info(
117
- "Waiting {self.DELAY_BETWEEN_MOTORS_AND_IMAGE_UPDATING_S} seconds for zoom to be noticeable"
119
+ f"Waiting {self.DELAY_BETWEEN_MOTORS_AND_IMAGE_UPDATING_S} seconds for zoom to be noticeable"
118
120
  )
119
121
  await asyncio.sleep(self.DELAY_BETWEEN_MOTORS_AND_IMAGE_UPDATING_S)
120
122
 
@@ -143,14 +145,15 @@ class ZoomControllerWithBeamCentres(ZoomController):
143
145
 
144
146
 
145
147
  class OAV(StandardReadable):
146
- """
147
- Class for oav device
148
-
149
- x_direction(int): Should only be 1 or -1, with 1 indicating the oav x direction is the same with motor x
150
- y_direction(int): Same with x_direction but for motor y
151
- z_direction(int): Same with x_direction but for motor z
152
- mjpg_x_size_pv(str): PV infix for x_size in mjpg
153
- mjpg_y_size_pv(str): PV infix for y_size in mjpg
148
+ """Class for oav device.
149
+
150
+ Attributes:
151
+ x_direction(int, optional): Should only be 1 or -1, with 1 indicating the oav x
152
+ direction is the same with motor x.
153
+ y_direction(int, optional): Same with x_direction but for motor y.
154
+ z_direction(int, optional): Same with x_direction but for motor z.
155
+ mjpg_x_size_pv(str, optional): PV infix for x_size in mjpg.
156
+ mjpg_y_size_pv(str, optional): PV infix for y_size in mjpg.
154
157
  """
155
158
 
156
159
  beam_centre_i: SignalR[int]
@@ -235,16 +238,17 @@ class OAV(StandardReadable):
235
238
 
236
239
 
237
240
  class OAVBeamCentreFile(OAV):
238
- """
239
- OAV device that reads its beam centre values from a file. The config parameter
241
+ """OAV device that reads its beam centre values from a file. The config parameter
240
242
  must be a OAVConfigBeamCentre object, as this contains a filepath to where the beam
241
243
  centre values are stored.
242
244
 
243
- x_direction(int): Should only be 1 or -1, with 1 indicating the oav x direction is the same with motor x
244
- y_direction(int): Same with x_direction but for motor y
245
- z_direction(int): Same with x_direction but for motor z
246
- mjpg_x_size_pv(str): PV infix for x_size in mjpg
247
- mjpg_y_size_pv(str): PV infix for y_size in mjpg
245
+ Attributes:
246
+ x_direction(int, optional): Should only be 1 or -1, with 1 indicating the oav x
247
+ direction is the same with motor x.
248
+ y_direction(int, optional): Same with x_direction but for motor y.
249
+ z_direction(int, optional): Same with x_direction but for motor z.
250
+ mjpg_x_size_pv(str, optional): PV infix for x_size in mjpg.
251
+ mjpg_y_size_pv(str, optional): PV infix for y_size in mjpg.
248
252
  """
249
253
 
250
254
  def __init__(
@@ -290,8 +294,9 @@ class OAVBeamCentreFile(OAV):
290
294
  self.set_name(self.name)
291
295
 
292
296
  def _get_beam_position(self, zoom_level: str, size: int, coord: int) -> int:
293
- """Extracts the beam location in pixels `xCentre` `yCentre`, for a requested \
294
- zoom level. """
297
+ """Extracts the beam location in pixels `xCentre` `yCentre`, for a requested
298
+ zoom level.
299
+ """
295
300
  _zoom = self._read_current_zoom(zoom_level)
296
301
  value = self.parameters[_zoom].crosshair[coord]
297
302
  return int(value * size / DEFAULT_OAV_WINDOW[coord])
@@ -25,9 +25,7 @@ def _get_element_as_float(node: Element, element_name: str) -> float:
25
25
 
26
26
 
27
27
  class OAVParameters:
28
- """
29
- The parameters to set up the OAV depending on the context.
30
- """
28
+ """The parameters to set up the OAV depending on the context."""
31
29
 
32
30
  def __init__(
33
31
  self,
@@ -45,8 +43,7 @@ class OAVParameters:
45
43
 
46
44
  @staticmethod
47
45
  def load_json(filename: str) -> tuple[dict[str, Any], dict[str, dict]]:
48
- """
49
- Loads the json from the specified file, and returns a dict with all the
46
+ """Loads the json from the specified file, and returns a dict with all the
50
47
  individual top-level k-v pairs, and one with all the subdicts.
51
48
  """
52
49
  with open(filename) as f:
@@ -101,9 +98,7 @@ class OAVParameters:
101
98
  self.max_tip_distance: float = update("max_tip_distance", float, default=300)
102
99
 
103
100
  def get_max_tip_distance_in_pixels(self, microns_per_pixel: float) -> float:
104
- """
105
- Get the maximum tip distance in pixels.
106
- """
101
+ """Get the maximum tip distance in pixels."""
107
102
  return self.max_tip_distance / microns_per_pixel
108
103
 
109
104
 
@@ -154,7 +149,7 @@ class OAVConfig(OAVConfigBase[ZoomParams]):
154
149
 
155
150
 
156
151
  class OAVConfigBeamCentre(OAVConfigBase[ZoomParamsCrosshair]):
157
- """ Read the OAV config files and return a dictionary of {'zoom_level': ZoomParams}\
152
+ """Read the OAV config files and return a dictionary of {'zoom_level': ZoomParams}
158
153
  with information about microns per pixels and crosshairs.
159
154
  """
160
155
 
@@ -45,12 +45,23 @@ class OAVSource(StandardReadable):
45
45
 
46
46
 
47
47
  class OAVToRedisForwarder(StandardReadable, Flyable, Stoppable):
48
- """Forwards OAV image data to redis. To use call:
48
+ """Forwards OAV image data to redis.
49
49
 
50
- > bps.kickoff(oav_forwarder)
51
- > bps.monitor(oav_forwarder.uuid)
52
- > bps.complete(oav_forwarder)
50
+ To use call::
53
51
 
52
+ bps.kickoff(oav_forwarder)
53
+ bps.monitor(oav_forwarder.uuid)
54
+ bps.complete(oav_forwarder)
55
+
56
+ Reads image data from the MJPEG stream on an OAV and forwards it into a
57
+ redis database. This is currently only used for murko integration.
58
+
59
+ Args:
60
+ prefix (str): The PV prefix of the OAV.
61
+ redis_host (str): The host where the redis database is running.
62
+ redis_password (str): The password for the redis database.
63
+ redis_db (int): Which redis database to connect to, defaults to 0.
64
+ name (str): The name of this device.
54
65
  """
55
66
 
56
67
  DATA_EXPIRY_DAYS = 7
@@ -68,16 +79,6 @@ class OAVToRedisForwarder(StandardReadable, Flyable, Stoppable):
68
79
  redis_db: int = 0,
69
80
  name: str = "",
70
81
  ) -> None:
71
- """Reads image data from the MJPEG stream on an OAV and forwards it into a
72
- redis database. This is currently only used for murko integration.
73
-
74
- Arguments:
75
- prefix: str the PV prefix of the OAV
76
- redis_host: str the host where the redis database is running
77
- redis_password: str the password for the redis database
78
- redis_db: int which redis database to connect to, defaults to 0
79
- name: str the name of this device
80
- """
81
82
  self.counter = epics_signal_r(int, f"{prefix}CAM:ArrayCounter_RBV")
82
83
  self.sources = DeviceVector(
83
84
  {
@@ -109,7 +110,9 @@ class OAVToRedisForwarder(StandardReadable, Flyable, Stoppable):
109
110
  ):
110
111
  """Stores the raw bytes of the jpeg image in redis. Murko ultimately wants a
111
112
  pickled numpy array of pixel values but raw byes are more space efficient. There
112
- may be better ways of doing this, see https://github.com/DiamondLightSource/mx-bluesky/issues/592"""
113
+ may be better ways of doing this, see
114
+ https://github.com/DiamondLightSource/mx-bluesky/issues/592.
115
+ """
113
116
  jpeg_bytes = await get_next_jpeg(response)
114
117
  sample_id = await self.sample_id.get_value()
115
118
  redis_key = f"murko:{sample_id}:raw"
@@ -131,12 +134,13 @@ class OAVToRedisForwarder(StandardReadable, Flyable, Stoppable):
131
134
  await function_to_do(response, source)
132
135
 
133
136
  async def _stream_to_redis(self, response: ClientResponse, source: OAVSource):
134
- """Uses the update of the frame counter as a trigger to pull an image off the OAV
135
- and into redis.
137
+ """Uses the update of the frame counter as a trigger to pull an image off the
138
+ OAV and into redis.
136
139
 
137
140
  The frame counter is continually increasing on the timescales we store data and
138
141
  so can be used as a uuid. If the OAV is updating too quickly we may drop frames
139
- but in this case a best effort on getting as many frames as possible is sufficient.
142
+ but in this case a best effort on getting as many frames as possible is
143
+ sufficient.
140
144
  """
141
145
  done_status = AsyncStatus(
142
146
  asyncio.wait_for(self._stop_flag.wait(), timeout=self.TIMEOUT)
@@ -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]