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
@@ -4,10 +4,12 @@ from ophyd_async.core import AsyncStatus
4
4
  from pydantic import BaseModel, model_validator
5
5
  from pydantic.types import PositiveInt, StringConstraints
6
6
 
7
- from dodal.devices.i19.access_controlled.blueapi_device import (
7
+ from dodal.devices.beamlines.i19.access_controlled.blueapi_device import (
8
8
  OpticsBlueAPIDevice,
9
9
  )
10
- from dodal.devices.i19.access_controlled.hutch_access import ACCESS_DEVICE_NAME
10
+ from dodal.devices.beamlines.i19.access_controlled.hutch_access import (
11
+ ACCESS_DEVICE_NAME,
12
+ )
11
13
 
12
14
  PermittedKeyStr = Annotated[str, StringConstraints(pattern="^[A-Za-z0-9-_]*$")]
13
15
 
@@ -32,18 +34,20 @@ class AttenuatorMotorPositionDemands(BaseModel):
32
34
 
33
35
 
34
36
  class AttenuatorMotorSquad(OpticsBlueAPIDevice):
35
- """ I19-specific proxy device which requests absorber position changes in the x-ray attenuator.
37
+ """I19-specific proxy device which requests absorber position changes in the
38
+ x-ray attenuator.
36
39
 
37
40
  Sends REST call to blueapi controlling optics on the I19 cluster.
38
- The hutch in use is compared against the hutch which sent the REST call.
41
+ The hutch in use is compared against the hutch which sent the REST call.
39
42
  Only the hutch in use will be permitted to execute a plan (requesting motor moves).
40
- As the two hutches are located in series, checking the hutch in use is necessary to \
41
- avoid accidentally operating optics devices from one hutch while the other has beam time.
43
+ As the two hutches are located in series, checking the hutch in use is necessary to
44
+ avoid accidentally operating optics devices from one hutch while the other has beam
45
+ time.
42
46
 
43
- The name of the hutch that wants to operate the optics device is passed to the \
47
+ The name of the hutch that wants to operate the optics device is passed to the
44
48
  access controlled device upon instantiation of the latter.
45
49
 
46
- For details see the architecture described in \
50
+ For details see the architecture described in
47
51
  https://github.com/DiamondLightSource/i19-bluesky/issues/30.
48
52
  """
49
53
 
@@ -21,11 +21,11 @@ class HutchState(str, Enum):
21
21
 
22
22
 
23
23
  class OpticsBlueAPIDevice(StandardReadable, Movable[D]):
24
- """General device that a REST call to the blueapi instance controlling the optics \
25
- hutch running on the I19 cluster, which will evaluate the current hutch in use vs \
24
+ """General device that a REST call to the blueapi instance controlling the optics
25
+ hutch running on the I19 cluster, which will evaluate the current hutch in use vs
26
26
  the hutch sending the request and decide if the plan will be run or not.
27
27
 
28
- For details see the architecture described in \
28
+ For details see the architecture described in
29
29
  https://github.com/DiamondLightSource/i19-bluesky/issues/30.
30
30
  """
31
31
 
@@ -44,22 +44,23 @@ class OpticsBlueAPIDevice(StandardReadable, Movable[D]):
44
44
 
45
45
  @AsyncStatus.wrap
46
46
  async def set(self, value: D):
47
- """ On set send a POST request to the optics blueapi with the name and \
48
- parameters, gets the generated task_id and then sends a PUT request that runs \
47
+ """On set send a POST request to the optics blueapi with the name and
48
+ parameters, gets the generated task_id and then sends a PUT request that runs
49
49
  the plan.
50
50
 
51
51
  Args:
52
- value (dict): The value passed here should be the parameters for the POST \
53
- request, taking the form:
54
- {
55
- "name": "plan_name",
56
- "params": {
57
- "experiment_hutch": f"{hutch_name}",
58
- "access_device": "access_control",
59
- "other_params": "...",
60
- ...
52
+ value (dict): The value passed here should be the parameters for the POST
53
+ request, taking the form::
54
+
55
+ {
56
+ "name": "plan_name",
57
+ "params": {
58
+ "experiment_hutch": f"{hutch_name}",
59
+ "access_device": "access_control",
60
+ "other_params": "...",
61
+ ...
62
+ }
61
63
  }
62
- }
63
64
  """
64
65
  # Value here vould be request params dictionary.
65
66
  request_params = json.dumps(value)
@@ -0,0 +1,72 @@
1
+ from enum import StrEnum
2
+
3
+ from ophyd_async.core import AsyncStatus, StandardReadableFormat
4
+ from ophyd_async.epics.core import epics_signal_r
5
+
6
+ from dodal.devices.beamlines.i19.access_controlled.blueapi_device import (
7
+ HutchState,
8
+ OpticsBlueAPIDevice,
9
+ )
10
+ from dodal.devices.beamlines.i19.access_controlled.hutch_access import (
11
+ ACCESS_DEVICE_NAME,
12
+ )
13
+
14
+
15
+ class FocusingMirrorName(StrEnum):
16
+ VFM = "vfm"
17
+ HFM = "hfm"
18
+
19
+
20
+ PIEZO_CONTROL_PLAN_NAME = "apply_voltage_to_piezo"
21
+
22
+
23
+ # NOTE This device is only meant to control the piezo. There should be a separate device
24
+ # to control the actual focusing mirror motors, as the two operations are often done
25
+ # independently.
26
+ class AccessControlledPiezoActuator(OpticsBlueAPIDevice):
27
+ """I19-specific device to set a voltage on the focusing mirror piezoelectric
28
+ actuator.
29
+
30
+ This device will send a REST call to the blueapi instance controlling the optics
31
+ hutch running on the I19 cluster, which will evaluate the current hutch in use vs
32
+ the hutch sending the request and decide if the plan will be run or not.
33
+ As the two hutches are located in series, checking the hutch in use is necessary to
34
+ avoid accidentally operating the shutter from one hutch while the other has beamtime.
35
+
36
+ The name of the hutch that wants to operate the shutter, as well as a commissioning
37
+ directory to act as a placehlder for the instrument_session,should be passed to the
38
+ device upon instantiation.
39
+
40
+ A mirror type (vfm or hfm) also needs to be set upon instantiation so that the
41
+ correct plan can be run and the correct optics device is injected.
42
+
43
+ For details see the architecture described in
44
+ https://diamondlightsource.github.io/i19-bluesky/main/explanations/decisions/0004-optics-blueapi-architecture.html
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ prefix: str,
50
+ mirror_type: FocusingMirrorName,
51
+ hutch: HutchState,
52
+ instrument_session: str = "",
53
+ name: str = "",
54
+ ):
55
+ with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
56
+ self.readback = epics_signal_r(float, f"{prefix}AOFPITCH:RBV")
57
+ self.mirror = mirror_type
58
+ super().__init__(hutch=hutch, instrument_session=instrument_session, name=name)
59
+
60
+ @AsyncStatus.wrap
61
+ async def set(self, value: float):
62
+ request_params = {
63
+ "name": PIEZO_CONTROL_PLAN_NAME,
64
+ "params": {
65
+ "experiment_hutch": self._invoking_hutch,
66
+ "access_device": ACCESS_DEVICE_NAME,
67
+ "voltage_demand": value,
68
+ "focus_mirror": self.mirror.value,
69
+ },
70
+ "instrument_session": self.instrument_session,
71
+ }
72
+ await super().set(request_params)
@@ -1,27 +1,29 @@
1
1
  from ophyd_async.core import AsyncStatus, StandardReadableFormat
2
2
  from ophyd_async.epics.core import epics_signal_r
3
3
 
4
- from dodal.devices.hutch_shutter import ShutterDemand, ShutterState
5
- from dodal.devices.i19.access_controlled.blueapi_device import (
4
+ from dodal.devices.beamlines.i19.access_controlled.blueapi_device import (
6
5
  HutchState,
7
6
  OpticsBlueAPIDevice,
8
7
  )
9
- from dodal.devices.i19.access_controlled.hutch_access import ACCESS_DEVICE_NAME
8
+ from dodal.devices.beamlines.i19.access_controlled.hutch_access import (
9
+ ACCESS_DEVICE_NAME,
10
+ )
11
+ from dodal.devices.hutch_shutter import ShutterDemand, ShutterState
10
12
 
11
13
 
12
14
  class AccessControlledShutter(OpticsBlueAPIDevice):
13
- """ I19-specific device to operate the hutch shutter.
15
+ """I19-specific device to operate the hutch shutter.
14
16
 
15
- This device will send a REST call to the blueapi instance controlling the optics \
16
- hutch running on the I19 cluster, which will evaluate the current hutch in use vs \
17
+ This device will send a REST call to the blueapi instance controlling the optics
18
+ hutch running on the I19 cluster, which will evaluate the current hutch in use vs
17
19
  the hutch sending the request and decide if the plan will be run or not.
18
- As the two hutches are located in series, checking the hutch in use is necessary to \
20
+ As the two hutches are located in series, checking the hutch in use is necessary to
19
21
  avoid accidentally operating the shutter from one hutch while the other has beamtime.
20
22
 
21
- The name of the hutch that wants to operate the shutter should be passed to the \
23
+ The name of the hutch that wants to operate the shutter should be passed to the
22
24
  device upon instantiation.
23
25
 
24
- For details see the architecture described in \
26
+ For details see the architecture described in
25
27
  https://github.com/DiamondLightSource/i19-bluesky/issues/30.
26
28
  """
27
29
 
@@ -6,7 +6,9 @@ from dodal.common.enums import InOutUpper
6
6
 
7
7
 
8
8
  class BacklightPosition(StandardReadable, Movable[InOutUpper]):
9
- """Device moves backlight to the IN or OUT position since controls side manages switching the light on/off"""
9
+ """Device moves backlight to the IN or OUT position since controls side manages
10
+ switching the light on/off.
11
+ """
10
12
 
11
13
  def __init__(self, prefix: str, name: str = "") -> None:
12
14
  self.position = epics_signal_rw(InOutUpper, f"{prefix}AD1:choiceButton")
@@ -27,7 +27,8 @@ class MAPTConfigurationTable(StandardReadable):
27
27
  class MAPTConfigurationControl(StandardReadable):
28
28
  """A device to control the MAPT (Mini Aperture) configuration. It provides a signal
29
29
  to set the configuration PV to the requested value and a triggerable signal that
30
- will move all the motors to the correct position."""
30
+ will move all the motors to the correct position.
31
+ """
31
32
 
32
33
  def __init__(
33
34
  self, prefix: str, aperture_request: type[SubsetEnum], name: str = ""
@@ -6,7 +6,7 @@ from ophyd_async.core import AsyncStatus, StandardReadable, SubsetEnum
6
6
  from ophyd_async.epics.core import epics_signal_r
7
7
  from pydantic import BaseModel
8
8
 
9
- from dodal.devices.i19.mapt_configuration import (
9
+ from dodal.devices.beamlines.i19.mapt_configuration import (
10
10
  MAPTConfigurationControl,
11
11
  MAPTConfigurationTable,
12
12
  )
@@ -45,10 +45,10 @@ class AperturePosition(BaseModel):
45
45
  one of the available apertures.
46
46
 
47
47
  Attributes:
48
- pinhole_x: The position of the x motor on the pinhole stage
49
- pinhole_y: The position of the y motor on the pinhole stage
50
- collimator_x: The position of the x motor on the collimator stage
51
- collimator_y: The position of the y motor on the collimator stage
48
+ pinhole_x (float): The position of the x motor on the pinhole stage.
49
+ pinhole_y (float): The position of the y motor on the pinhole stage.
50
+ collimator_x (float): The position of the x motor on the collimator stage.
51
+ collimator_y (float): The position of the y motor on the collimator stage.
52
52
  """
53
53
 
54
54
  pinhole_x: float
@@ -59,7 +59,8 @@ class AperturePosition(BaseModel):
59
59
 
60
60
  class PinColConfiguration(StandardReadable):
61
61
  """Full MAPT configuration table, including out positions and selection for the
62
- Pinhole and Collimator control."""
62
+ Pinhole and Collimator control.
63
+ """
63
64
 
64
65
  def __init__(self, prefix: str, apertures: list[int], name: str = "") -> None:
65
66
  with self.add_children_as_readables():
@@ -75,7 +76,8 @@ class PinColConfiguration(StandardReadable):
75
76
 
76
77
  class PinholeCollimatorControl(StandardReadable, Movable[str]):
77
78
  """Device to control the Pinhole and Collimator stages moves on I19-2, using the
78
- MAPT configuration table to look up the positions."""
79
+ MAPT configuration table to look up the positions.
80
+ """
79
81
 
80
82
  def __init__(
81
83
  self,
@@ -130,7 +132,8 @@ class PinholeCollimatorControl(StandardReadable, Movable[str]):
130
132
  async def _safe_move_in(self, value: _PinColPosition):
131
133
  """Move the pinhole and collimator stages safely to the in position.
132
134
  In order to avoid a collision, we have to make sure that the pinhole stage is
133
- always moved in before the collimator stage."""
135
+ always moved in before the collimator stage.
136
+ """
134
137
  LOGGER.info(
135
138
  f"Moving pinhole and collimator stages to in position: {value.value}"
136
139
  )
@@ -0,0 +1,32 @@
1
+ from ophyd_async.core import StandardReadable
2
+ from ophyd_async.epics.core import epics_signal_rw
3
+
4
+
5
+ class PinTipCentreHolder(StandardReadable):
6
+ """Temporary device to hold the pin tip x,y positions for centring.
7
+ It uses the CenterX and CenterY PVs in the overlay plugin for the OAV device to
8
+ save and read the pit tip location.
9
+
10
+ Attributes:
11
+ pin_tip_i (SignalRW): x position of the pin tip, in pixels.
12
+ pin_tip_j (SignalRW): y position of the pin tip, in pixels.
13
+
14
+ This workaround is necessary because it's not yet possible to get these values back
15
+ from a plan in blueapi. It will be removed once this is completed
16
+ https://github.com/DiamondLightSource/blueapi/issues/1349
17
+ """
18
+
19
+ def __init__(
20
+ self,
21
+ prefix: str,
22
+ name: str = "",
23
+ overlay_channel: int = 1,
24
+ ):
25
+ with self.add_children_as_readables():
26
+ self.pin_tip_i = epics_signal_rw(
27
+ int, prefix + f"OVER:{overlay_channel}:CenterX"
28
+ )
29
+ self.pin_tip_j = epics_signal_rw(
30
+ int, prefix + f"OVER:{overlay_channel}:CenterY"
31
+ )
32
+ super().__init__(name)
@@ -0,0 +1,3 @@
1
+ from .enums import Grating
2
+
3
+ __all__ = ["Grating"]
@@ -24,8 +24,7 @@ _CONVERSION_CONSTANT = 12.3984
24
24
 
25
25
 
26
26
  class DCM(DoubleCrystalMonochromatorWithDSpacing[RollCrystal, PitchAndRollCrystal]):
27
- """
28
- A double crystal monochromator (DCM), used to select the energy of the beam.
27
+ """A double crystal monochromator (DCM), used to select the energy of the beam.
29
28
 
30
29
  perp describes the gap between the 2 DCM crystals which has to change as you alter
31
30
  the angle to select the requested energy.
@@ -15,8 +15,7 @@ from dodal.common.enums import InOutUpper
15
15
 
16
16
 
17
17
  class FSwitch(StandardReadable):
18
- """
19
- Device for i22's fswitch. A filter switch for manipulating
18
+ """Device for i22's fswitch. A filter switch for manipulating
20
19
  compound refractive lenses. Also referred to as a transfocator.
21
20
 
22
21
  This currently only implements the minimum
@@ -25,7 +24,6 @@ class FSwitch(StandardReadable):
25
24
  Eventually this should be combined with the transfocator device in the i04
26
25
  module but is currently incompatible as the Epics interfaces are different.
27
26
  See https://github.com/DiamondLightSource/dodal/issues/399
28
-
29
27
  """
30
28
 
31
29
  NUM_FILTERS = 128
@@ -57,8 +57,7 @@ class MetadataHolder:
57
57
 
58
58
  @dataclass
59
59
  class NXSasMetadataHolder(MetadataHolder):
60
- """
61
- Required fields for NXDetectors that are used in an NXsas application definition.
60
+ """Required fields for NXDetectors that are used in an NXsas application definition.
62
61
  All fields are Configuration and read once per run only.
63
62
  """
64
63
 
@@ -94,7 +93,8 @@ class NXSasPilatus(PilatusDetector):
94
93
  to comply with the NXsas application definition.
95
94
  Adds all values in the NXSasMetadataHolder's configuration fields
96
95
  to the configuration of the parent device.
97
- Writes hdf5 files."""
96
+ Writes hdf5 files.
97
+ """
98
98
  super().__init__(
99
99
  prefix,
100
100
  path_provider,
@@ -137,7 +137,8 @@ class NXSasOAV(AravisDetector):
137
137
  to comply with the NXsas application definition.
138
138
  Adds all values in the NXSasMetadataHolder's configuration fields
139
139
  to the configuration of the parent device.
140
- Writes hdf5 files."""
140
+ Writes hdf5 files.
141
+ """
141
142
  super().__init__(
142
143
  prefix,
143
144
  path_provider,
@@ -1,4 +1,4 @@
1
- """A small temporary device to get the beam center positions on i24"""
1
+ """A small temporary device to get the beam center positions on i24."""
2
2
 
3
3
  from ophyd_async.core import StandardReadable
4
4
  from ophyd_async.epics.core import epics_signal_rw
@@ -21,8 +21,8 @@ class Beamstop(XYZStage):
21
21
  The additional y_rotation motor is independent of the positioner and can to be moved
22
22
  on its own as needed.
23
23
 
24
- WARNING. Before moving the y_rotation motor away from 0, it is important to make sure
25
- that the backlight is in the "OUT" position to avoid a collision.
24
+ WARNING. Before moving the y_rotation motor away from 0, it is important to make
25
+ sure that the backlight is in the "OUT" position to avoid a collision.
26
26
  See also https://github.com/DiamondLightSource/dodal/issues/646.
27
27
  """
28
28
 
@@ -6,11 +6,10 @@ from bluesky.protocols import StreamAsset
6
6
  from event_model import DataKey # type: ignore
7
7
  from ophyd_async.core import (
8
8
  AsyncStatus,
9
- AutoIncrementingPathProvider,
10
9
  DetectorWriter,
10
+ PathProvider,
11
11
  StandardDetector,
12
12
  StandardReadable,
13
- StaticPathProvider,
14
13
  TriggerInfo,
15
14
  observe_value,
16
15
  wait_for_value,
@@ -22,22 +21,22 @@ from ophyd_async.fastcs.jungfrau._signals import JungfrauDriverIO
22
21
  from dodal.log import LOGGER
23
22
 
24
23
 
25
- class JunfrauCommissioningWriter(DetectorWriter, StandardReadable):
24
+ class JungfrauCommissioningWriter(DetectorWriter, StandardReadable):
26
25
  """Implementation of the temporary filewriter used for Jungfrau commissioning on i24.
27
26
 
28
27
  The PVs on this device are responsible for writing files of a specified name
29
28
  to a specified path, marking itself as "ready to write", and having a counter of
30
- frames written, which must be zero'd at the ophyd level
29
+ frames written, which must be zero'd at the ophyd level.
31
30
  """
32
31
 
33
32
  def __init__(
34
33
  self,
35
34
  prefix,
36
- path_provider: AutoIncrementingPathProvider | StaticPathProvider,
35
+ path_provider: PathProvider,
37
36
  name="",
38
37
  ) -> None:
39
38
  with self.add_children_as_readables():
40
- self._path_info = path_provider
39
+ self._path_provider = path_provider
41
40
  self.frame_counter = epics_signal_rw(int, f"{prefix}NumCaptured")
42
41
  self.file_name = epics_signal_rw_rbv(str, f"{prefix}FileName")
43
42
  self.file_path = epics_signal_rw_rbv(str, f"{prefix}FilePath")
@@ -47,9 +46,8 @@ class JunfrauCommissioningWriter(DetectorWriter, StandardReadable):
47
46
 
48
47
  async def open(self, name: str, exposures_per_event: int = 1) -> dict[str, DataKey]:
49
48
  self._exposures_per_event = exposures_per_event
50
- _path_info = self._path_info()
49
+ _path_info = self._path_provider()
51
50
 
52
- # Commissioning Jungfrau plans allow you to override path, so check to see if file exists
53
51
  requested_filepath = Path(_path_info.directory_path) / _path_info.filename
54
52
  if requested_filepath.exists():
55
53
  raise FileExistsError(
@@ -65,6 +63,7 @@ class JunfrauCommissioningWriter(DetectorWriter, StandardReadable):
65
63
  f"Jungfrau writing to folder {_path_info.directory_path} with filename {_path_info.filename}"
66
64
  )
67
65
  await wait_for_value(self.writer_ready, 1, timeout=10)
66
+ self.final_path = requested_filepath
68
67
  return await self._describe()
69
68
 
70
69
  async def _describe(self) -> dict[str, DataKey]:
@@ -99,20 +98,21 @@ class JunfrauCommissioningWriter(DetectorWriter, StandardReadable):
99
98
 
100
99
 
101
100
  class CommissioningJungfrau(
102
- StandardDetector[JungfrauController, JunfrauCommissioningWriter]
101
+ StandardDetector[JungfrauController, JungfrauCommissioningWriter]
103
102
  ):
104
103
  """Ophyd-async implementation of a Jungfrau 9M Detector, using a temporary
105
- filewriter in place of Odin"""
104
+ filewriter in place of Odin.
105
+ """
106
106
 
107
107
  def __init__(
108
108
  self,
109
109
  prefix: str,
110
110
  writer_prefix: str,
111
- path_provider: AutoIncrementingPathProvider | StaticPathProvider,
111
+ path_provider: PathProvider,
112
112
  name="",
113
113
  ):
114
114
  self.drv = JungfrauDriverIO(prefix)
115
- writer = JunfrauCommissioningWriter(writer_prefix, path_provider)
115
+ writer = JungfrauCommissioningWriter(writer_prefix, path_provider)
116
116
  controller = JungfrauController(self.drv)
117
117
  super().__init__(controller, writer, name=name)
118
118
 
@@ -8,9 +8,7 @@ from dodal.devices.common_dcm import (
8
8
 
9
9
 
10
10
  class DCM(DoubleCrystalMonochromatorWithDSpacing[RollCrystal, PitchAndRollCrystal]):
11
- """
12
- A double crystal monocromator device, used to select the beam energy.
13
- """
11
+ """A double crystal monocromator device, used to select the beam energy."""
14
12
 
15
13
  def __init__(self, prefix: str, motion_prefix: str, name: str = "") -> None:
16
14
  with self.add_children_as_readables():
@@ -27,13 +27,13 @@ class BacklightPositioner(StandardReadable):
27
27
 
28
28
 
29
29
  class DualBacklight(StandardReadable):
30
- """
31
- Device to trigger the dual backlight on I24.
30
+ """Device to trigger the dual backlight on I24.
32
31
  This device is made up by two LEDs:
33
32
  - LED1 is the "backlight", can be moved to 5 different positions.
34
33
  - LED2 is a "frontlight", it does not move, just switches on and off.
35
34
 
36
- To set the position for LED1:
35
+ To set the position for LED1::
36
+
37
37
  b = DualBacklight(name="backlight)
38
38
  b.backlight_position.set("OAV2")
39
39
 
@@ -45,9 +45,11 @@ class LaserSettings(str, Enum):
45
45
  class EncReset(str, Enum):
46
46
  """PMAC strings for position compare on encoder channels in the controller.
47
47
 
48
- For example, for ENC5:
48
+ For example, for ENC5::
49
+
49
50
  m508 sets position A to be compared with value in Channel5 in the controller.
50
51
  m509 sets position B to be compared with value in Channel5 in the controller.
52
+
51
53
  Note. These settings are usually used for initialisation.
52
54
  """
53
55
 
@@ -141,8 +143,8 @@ class ProgramRunner(Device, Flyable):
141
143
 
142
144
  @AsyncStatus.wrap
143
145
  async def kickoff(self):
144
- """Kick off the collection by sending a program number to the pmac_string and \
145
- wait for the scan status PV to go to 1.
146
+ """Kick off the collection by sending a program number to the pmac_string and
147
+ wait for the scan status PV to go to 1.
146
148
  """
147
149
  prog_num_str = await self._get_prog_number_string()
148
150
  await self._signal_ref().set(prog_num_str, wait=True)
@@ -154,8 +156,8 @@ class ProgramRunner(Device, Flyable):
154
156
 
155
157
  @AsyncStatus.wrap
156
158
  async def complete(self):
157
- """Stop collecting when the scan status PV goes to 0 or when counter PV hasn't \
158
- updated for 30 seconds.
159
+ """Stop collecting when the scan status PV goes to 0 or when counter PV hasn't
160
+ updated for 30 seconds.
159
161
  """
160
162
  counter_time = await self._counter_time_ref().get_value()
161
163
  async for signal, value in observe_signals_value(
@@ -169,8 +171,8 @@ class ProgramRunner(Device, Flyable):
169
171
 
170
172
 
171
173
  class ProgramAbort(Triggerable):
172
- """Abort a data collection by setting the PMAC string and then wait for the \
173
- status value to go back to 0.
174
+ """Abort a data collection by setting the PMAC string and then wait for the
175
+ status value to go back to 0.
174
176
  """
175
177
 
176
178
  def __init__(
@@ -12,7 +12,7 @@ class LabXraySource(float, Enum):
12
12
 
13
13
 
14
14
  class LabXraySourceReadable(StandardReadable):
15
- """Simple device to get the laboratory x-ray tube energy reading"""
15
+ """Simple device to get the laboratory x-ray tube energy reading."""
16
16
 
17
17
  def __init__(self, xraysource: LabXraySource, name: str = "") -> None:
18
18
  with self.add_children_as_readables():
File without changes
@@ -8,7 +8,17 @@ from ophyd_async.epics.core import epics_signal_r
8
8
 
9
9
  class Andor2Point(SingleTriggerDetector):
10
10
  """Using the andor2 as if it is a massive point detector, read the read uncached
11
- value after a picture is taken."""
11
+ value after a picture is taken.
12
+
13
+ Args:
14
+ prefix (str): Beamline camera PV.
15
+ drv_suffix (str): Camera PV suffix.
16
+ read_uncached (dict[str,str]): A dictionary contains the name and the PV
17
+ suffix for the statistic plugin.
18
+ name (str): Name of the device.
19
+ plugins ([dict[str, NDPluginBaseIO], optional): Dictionary containing
20
+ plugin that are forward to the base class.
21
+ """
12
22
 
13
23
  def __init__(
14
24
  self,
@@ -18,20 +28,6 @@ class Andor2Point(SingleTriggerDetector):
18
28
  name: str = "",
19
29
  plugins: dict[str, NDPluginBaseIO] | None = None,
20
30
  ) -> None:
21
- """
22
- Parameters
23
- ----------
24
- prefix: str,
25
- Beamline camera pv
26
- drv_suffix : str,
27
- Camera pv suffix
28
- read_uncached: dict[str,str]
29
- A dictionary contains the name and the pv suffix for the statistic plugin.
30
- name: str:
31
- Name of the device.
32
- plugins:: Optional[dict[str, NDPluginBaseIO] | None
33
- Dictionary containing plugin that are forward to the base class.
34
- """
35
31
  with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
36
32
  for k, v in read_uncached.items():
37
33
  setattr(self, k, epics_signal_r(float, prefix + v))