dls-dodal 1.56.0__py3-none-any.whl → 1.58.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dls-dodal
3
- Version: 1.56.0
3
+ Version: 1.58.0
4
4
  Summary: Ophyd devices and other utils that could be used across DLS beamlines
5
5
  Author-email: Dominic Oram <dominic.oram@diamond.ac.uk>, Joseph Ware <joseph.ware@diamond.ac.uk>, Oliver Silvester <Oliver.Silvester@diamond.ac.uk>, Noemi Frisina <noemi.frisina@diamond.ac.uk>
6
6
  License: Apache License
@@ -215,7 +215,7 @@ Description-Content-Type: text/markdown
215
215
  License-File: LICENSE
216
216
  Requires-Dist: click
217
217
  Requires-Dist: ophyd
218
- Requires-Dist: ophyd-async[ca,pva]>=0.13.0
218
+ Requires-Dist: ophyd-async[ca,pva]>=0.13.2
219
219
  Requires-Dist: bluesky==1.14.2
220
220
  Requires-Dist: pyepics
221
221
  Requires-Dist: dataclasses-json
@@ -1,7 +1,7 @@
1
- dls_dodal-1.56.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1
+ dls_dodal-1.58.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
2
2
  dodal/__init__.py,sha256=Ksms_WJF8LTkbm38gEpm1jBpGqcQ8NGvmb2ZJlOE1j8,198
3
3
  dodal/__main__.py,sha256=kP2S2RPitnOWpNGokjZ1Yq-1umOtp5sNOZk2B3tBPLM,111
4
- dodal/_version.py,sha256=-Q-x6fbTDcv8A0qE6FFdxYCjVSPznhUJwbWxT9so9xg,706
4
+ dodal/_version.py,sha256=0S_WOya-t7UDFMpZKDOCqr088G6Obb9i_ReErRJRu3o,706
5
5
  dodal/cli.py,sha256=yi8dXOp0hqzlg4ZZXCRGU-LpDa_ydaropDjyREWbZ5Y,4152
6
6
  dodal/log.py,sha256=Rt5O3hFZfMnJvQueZvgagQuXnPqHrFxhponOvVkpfrk,9871
7
7
  dodal/utils.py,sha256=abGitd4FLpLnmckF7lUqOKYUL88r5Ex_NGSVgO4gOf4,19305
@@ -19,8 +19,8 @@ dodal/beamlines/b16.py,sha256=rK00hEj8KLGNkLZr9omAuoMz_DyLN9rK4eUQssETR9E,1703
19
19
  dodal/beamlines/b18.py,sha256=ryxrGtcCdwoFgZ8ljWYgr1g9gKvoA7nxkARVxl1IE78,1189
20
20
  dodal/beamlines/b21.py,sha256=mN21vnOBmnwzG695HuV7P5DkuTtU8joflJI-evOpgwc,3333
21
21
  dodal/beamlines/i02_1.py,sha256=d2IyqFMgeaSEyZYm7GMSjTKr7_02SakyC_oARx-XwnY,1204
22
- dodal/beamlines/i03.py,sha256=4ZDmWoqHBz1fu0sgMOinQsxBDnH70oeGs78XIrgVMEs,16132
23
- dodal/beamlines/i04.py,sha256=MhKVuMdbw58YSYbLKaRmkoUHjOykqS10jEWp35d8H3Y,13228
22
+ dodal/beamlines/i03.py,sha256=wYeOyVn7UKhj7NhoGSeiFzK3TduL6YHMIHg37tVmE18,16170
23
+ dodal/beamlines/i04.py,sha256=mRzHnKSKHVmJzs6fwpfBi0QSiry72ixYqa-bs96-0gU,13717
24
24
  dodal/beamlines/i05.py,sha256=v4QKd8-neh4Og205oovm6NDRnAU6Oktu1WrxalXsI40,656
25
25
  dodal/beamlines/i05_1.py,sha256=R6JFFg8Bj-Izw355mx3mOd4IDvJb5ipB4p7_S0I_4Z0,670
26
26
  dodal/beamlines/i09.py,sha256=OrqOcVanJU4F7erRZZDkjLwV8VnLlUzz3LUduaCBgUc,1629
@@ -29,14 +29,15 @@ dodal/beamlines/i09_2.py,sha256=lyYO1rOaIsXNuVOL39Psh-8jQjkhQBXEQMpbXa-Zcw0,713
29
29
  dodal/beamlines/i10.py,sha256=0rSWOlIihkDtq7ZZaJ5O2jkMBrteRYjl4Hhqe8EP1Yc,5697
30
30
  dodal/beamlines/i11.py,sha256=rjb5iARfbToNzeP-gewYljTu0f__V7bYj3JkJ5bWkGE,4349
31
31
  dodal/beamlines/i13_1.py,sha256=VYVqMN8-njy7YSI08gskRccT-K2paRC9edAx0ah-Cwo,1602
32
+ dodal/beamlines/i17.py,sha256=Nickt8CKQ9JcQ1D_ulNICUT4jjLF1Aib7D9jblSnzA4,987
32
33
  dodal/beamlines/i18.py,sha256=FuU8G-q1piu6BRou-Shj3BQEbNtsF7CUsSIqqkvCKZc,3615
33
34
  dodal/beamlines/i19_1.py,sha256=tg3eALJTn9p686VFV7GjfUvChGS1mrh8-98uzjdLj_g,2934
34
- dodal/beamlines/i19_2.py,sha256=xSMS2Vhk_6V5WYyefVo1SWXbC4UUyaqru7JVK8VTg00,2554
35
+ dodal/beamlines/i19_2.py,sha256=qotmAulSL2zTLaoxG1Mf6UabeJV-X4iekL0cotTtTww,2929
35
36
  dodal/beamlines/i19_optics.py,sha256=8hdlDAAMgFrhcXrp5xCPZtLUlrDUEC9VwKnnuUAMbbU,1150
36
37
  dodal/beamlines/i20_1.py,sha256=Zsr1lsH7ySbOgK7RhMVMWzNWZAV-fuYW0iAjSEJZicY,2625
37
- dodal/beamlines/i22.py,sha256=VU5gz0q_K1D4zBHb88xnd1nAISAIBZOF6o8_YrGPGck,8103
38
+ dodal/beamlines/i22.py,sha256=009Tk5cYyHfmm1DfIwR562WzomXRvyv_5chiBbPgQUs,8100
38
39
  dodal/beamlines/i23.py,sha256=ZXvPEiMA4mPbRTXOxvL1NcoVWDg4Deyl8k57cveDg90,3060
39
- dodal/beamlines/i24.py,sha256=f6SeNbMdxviurfTAnKps8S9NNbDpU7SFzr-J1_c0yn8,6833
40
+ dodal/beamlines/i24.py,sha256=FvK7MqC_dX_alUFMEoS8L4HACThky9lQ7w7rwy0gXdA,6259
40
41
  dodal/beamlines/k11.py,sha256=sBOl-MLk7FMo10KFykuK5aLqH1zyW4FnI0XXmtQhUfg,978
41
42
  dodal/beamlines/p38.py,sha256=lxFGzmQhth0JhOFbTrZd9LEbUgvKmQdMtiHDW6UJgDs,5763
42
43
  dodal/beamlines/p45.py,sha256=tQ7EkWfr7o2okK7Prw1C7DNGYdKPU2ofqjm98wu_-G0,2158
@@ -57,13 +58,14 @@ dodal/common/watcher_utils.py,sha256=jWtXjr2a2NAcTy8A9OnvtB_hWh3GuuLvWxVWHouSvOs
57
58
  dodal/common/beamlines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
59
  dodal/common/beamlines/beamline_parameters.py,sha256=nYTy5TDYegvon6iNCxf7Uez3hbdu9GCoLUmJhRO1qhE,2665
59
60
  dodal/common/beamlines/beamline_utils.py,sha256=UnDJlRs7p8srjv-peX05jHZBb8J2d2lPSyG9118x560,5286
61
+ dodal/common/beamlines/commissioning_mode.py,sha256=KL24Ia4IOkSxi7M4CKRRQQW0T3HUDpj7K3Y5_DtALRA,1314
60
62
  dodal/common/beamlines/device_helpers.py,sha256=8sasAIFRDwo6ElHqLrXnpj_v7xcEg-29Zhey_e4r9u8,84
61
63
  dodal/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
64
  dodal/devices/aperture.py,sha256=S7AoIoQg_kFxaQk7HUMijbm1kYkOEi2csbdj76kp1ys,427
63
65
  dodal/devices/aperturescatterguard.py,sha256=FH2oru3DU-I6BwOJOVEgHdFplLKIIknxtEBUBAXZ_WA,13946
64
66
  dodal/devices/apple2_undulator.py,sha256=KbAX1GstP01XOm-QpXlyWCJewfxXKKawnoqbsfsksyY,26265
65
67
  dodal/devices/backlight.py,sha256=y40kR6N3qSFVLRaGU8CjJJpwv_hq2QALf_85fLQqras,1415
66
- dodal/devices/baton.py,sha256=BnakfZxx3cIIX6Kxj8-abxn8Z9LaQODhcPbStbD0as4,485
68
+ dodal/devices/baton.py,sha256=315I_0V73_DYYVT0PBs0luVy4CMqdPo0kLvHBi12MIU,606
67
69
  dodal/devices/bimorph_mirror.py,sha256=OGe6aCczG0gVco4OvIRLJVxn2kw5F2QG1e06uqhFLTw,4609
68
70
  dodal/devices/collimation_table.py,sha256=64HunSPJH-L2gZdfIj_RYdOlOuwRFEfMHfLHzu4BAKI,1681
69
71
  dodal/devices/common_dcm.py,sha256=8QSRE6Z10RQjfL3g4JZhyHRNI_aCKxWlzKSsDgiJHhE,3049
@@ -90,16 +92,16 @@ dodal/devices/robot.py,sha256=k4Vkjpd3R-wXWf6YbYC1225sSdjvTrZS54v5v6Qy_EU,7003
90
92
  dodal/devices/s4_slit_gaps.py,sha256=4KdarIQoRqX4ry3LUS1Km7fkjUFahA0VuTd2DvYEqQ8,446
91
93
  dodal/devices/scintillator.py,sha256=JresF8SY_-t1raibzR4f0UoMXnFi-Abh3ywGm2DjhKs,3003
92
94
  dodal/devices/slits.py,sha256=b_7ku2sHlzhMHTvWrwiRwee6ufrbxNX9JB_Z0lvk15o,1105
93
- dodal/devices/smargon.py,sha256=TC1ye35mDT1gLlrjIe3CcGeE2vEb78Cu7f0DbzUQR8M,5813
95
+ dodal/devices/smargon.py,sha256=48oOBOKlsvPhlUlK41ozXzCMUKMp3QkQx-pVqKL2Xts,6569
94
96
  dodal/devices/status.py,sha256=hVrJS1yooQo6PRumRACoIEh-SKBUKxvBlQl-MtLFUMQ,327
95
97
  dodal/devices/synchrotron.py,sha256=OHBrTrm4K39XE8BrE9b_Jn_ZfMRyDp9CHCwvmiV-KOc,1989
96
98
  dodal/devices/tetramm.py,sha256=qBu2ClXRM4RFO-y4C2pXVKsp5sX4VpOelbVHOAkkUHQ,8936
97
- dodal/devices/thawer.py,sha256=UtikbirSZZGjcbZEDWO33n5CIsd_dSvmohIS4R65xWg,1571
99
+ dodal/devices/thawer.py,sha256=HexahKZ1rUxF9jBMeULruqYt6I7fsl6OgXkvcxPQY3M,1963
98
100
  dodal/devices/turbo_slit.py,sha256=xhcnhfbdcTYSYozogw6Li4fF4ofoPsc350rEyrRdaNE,1460
99
- dodal/devices/undulator.py,sha256=3IRn1iOF1PLOuIzaMpsNLVYx-rGvAnr-PG7C3YupW-w,5266
101
+ dodal/devices/undulator.py,sha256=SsgYvIEQUNs_zZiUPAzlw0yJPaSExdmBFltlq5ypbc8,5530
100
102
  dodal/devices/watsonmarlow323_pump.py,sha256=xNwjoxW3NJIDkeDWHfb0A8Yj95_KKRXMD9AghvX-WLk,1337
101
- dodal/devices/webcam.py,sha256=QUfOwblZr_xUGVqt5cMhTwfyQAL-jhSJ2ORQRNmZBrM,2436
102
- dodal/devices/xbpm_feedback.py,sha256=Uv_jML9gkMT5OcpUrckzxDmsBmktEwV1lga6USxeRyY,1065
103
+ dodal/devices/webcam.py,sha256=UAx2KF0mKi6I-mJJUb5z56MHY-Wd89-tqyPcFbouQFg,2491
104
+ dodal/devices/xbpm_feedback.py,sha256=HKLY3k52FPe2G-EAg8ir2LSdeUuGvhRD73ibDZ4mjH8,1418
103
105
  dodal/devices/aithre_lasershaping/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
106
  dodal/devices/aithre_lasershaping/goniometer.py,sha256=-2ewYMcdzB1DqP9sMpr4L1i4KeTCrGAJfngAt9-eFWw,1005
105
107
  dodal/devices/aithre_lasershaping/laser_robot.py,sha256=vy-I1ASYflFYIUYePhT2l3l7g2iekrIFzLxA0GLmKY8,717
@@ -181,6 +183,7 @@ dodal/devices/i13_1/merlin_controller.py,sha256=myfmByOEXyMrlJZfsjOxDHeGQVwZGfsR
181
183
  dodal/devices/i18/KBMirror.py,sha256=W4R3TeulSjosUqAFIIznyWzje_Y2AoEf9f8N-NkisYM,710
182
184
  dodal/devices/i18/diode.py,sha256=nk5kvn4LsbhczRpCwHOO0_jJTYOz7MP9qm_uvBWuv7c,1468
183
185
  dodal/devices/i19/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
186
+ dodal/devices/i19/backlight.py,sha256=c3F8RTessUJmJHlCWATSj-W25qlkAttb-c95eS28lKs,662
184
187
  dodal/devices/i19/beamstop.py,sha256=JkcvkEmcC3eY3GHrvYNGqv2yDwrfgdpWKVZJWSadWW8,715
185
188
  dodal/devices/i19/blueapi_device.py,sha256=Tsl4vsREz7FM2d-kKJK-9tGrYbyKq4SLxnMlEKIM-g8,3966
186
189
  dodal/devices/i19/diffractometer.py,sha256=QCEi0Gko6Ja9_ec2vfdazwMspknvX63jcz8hQ2XW1xo,1182
@@ -192,12 +195,11 @@ dodal/devices/i22/fswitch.py,sha256=kpgegs4Wv_weBSzbrlXLXqXOOZdzTn3X9k5PlEN5F6c,
192
195
  dodal/devices/i22/nxsas.py,sha256=093GCM2H2LESt28RF-JfxEljJ8QzRPkL1ByXI2XVhoA,6012
193
196
  dodal/devices/i24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
194
197
  dodal/devices/i24/aperture.py,sha256=dlH_g7OPTBc5QRMd-ADU3_GDTKh668kkMHo4k_JxUcY,770
195
- dodal/devices/i24/beam_center.py,sha256=m6LWsG9e_lhtPfZ8pc_hoLNyTYQQGGdRNav8J_2scTo,483
198
+ dodal/devices/i24/beam_center.py,sha256=nQyOif6JtlU_qP7kZP-8wN1ldW81MIbFUykPC1bEm70,446
196
199
  dodal/devices/i24/beamstop.py,sha256=6tbiQLlcTlp4PCPhHJ_mlHtkv0kz5ArQ99zg9rwTnrw,1133
197
200
  dodal/devices/i24/dcm.py,sha256=l7qbJh2JKL-5ANlMYXDeU5EBYY6mDiLxn7kp_Z9KNaM,1206
198
201
  dodal/devices/i24/dual_backlight.py,sha256=N0R7M1mHPRmQ4fks5lGU1wrXjOvcW_ZKIXaRoC8aLDE,2084
199
202
  dodal/devices/i24/focus_mirrors.py,sha256=DYiYLpDw8FJ1LYHxLOxE_om5qGfUo2itzskgqhmQZlg,1763
200
- dodal/devices/i24/pilatus_metadata.py,sha256=I-AR8vd67qf6p0vZnRPTv4aNPN5T4KpSt2Iog4_jvn0,1781
201
203
  dodal/devices/i24/pmac.py,sha256=-HYf2HPzaqWvszp4T8TXohdp40-xmKqQq4V0mLvVri8,7028
202
204
  dodal/devices/i24/vgonio.py,sha256=sxSmcYZayVJPJz_D_91j9PmNor7Tbl1RGQFRrdtESlw,533
203
205
  dodal/devices/mx_phase1/beamstop.py,sha256=GeHLqHVf3XwJSszUHuxZ2JBwQlcDiFs6jVpdHuw7snM,3127
@@ -224,7 +226,6 @@ dodal/devices/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
224
226
  dodal/devices/util/adjuster_plans.py,sha256=c40PFZpXFw0YmJLh9jU4VIb8vRxHyafZlmvprTKAOhM,824
225
227
  dodal/devices/util/epics_util.py,sha256=4useFL8ngsVF08fhOn48BlnO4oh0T4sEKqjdS6mjvG0,4687
226
228
  dodal/devices/util/lookup_tables.py,sha256=jH9f_D8JbTSqzL-RKHUWOORLt8lEoNQL3o9HpXE98TY,3476
227
- dodal/devices/util/test_utils.py,sha256=tnKKmSBaHEyx-qTQjoR3COqraRMmTdRJe9s-pWjuTPk,1176
228
229
  dodal/devices/xspress3/xspress3.py,sha256=OerapEy-IuK7EFz13B5z0BzBmESVl6pYUlqAWHIwJck,4555
229
230
  dodal/devices/xspress3/xspress3_channel.py,sha256=w8tAx2lz5kJ_LeJ_eb_4o--Dtt8MRijsYNgDG6oEIVg,1626
230
231
  dodal/devices/zebra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -250,11 +251,12 @@ dodal/plans/verify_undulator_gap.py,sha256=OcDN09-eCoMzsmhKGxvzsH5EapG2zYz0yGCqU
250
251
  dodal/plans/wrapped.py,sha256=BPMw__RcWvk9v5XnhMsi9_k4KsDEbmXogzD2n1ecbUg,2098
251
252
  dodal/plans/preprocessors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
252
253
  dodal/plans/preprocessors/verify_undulator_gap.py,sha256=cBZEGq8TW1jrXFXB00iClQVXSEaE_jP_rHMY9WTgYyY,1813
253
- dodal/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
254
+ dodal/testing/__init__.py,sha256=AUYZKAvVOs7ZvxO1dVhL0pDTleRO34FQlO5MNe_cwgU,96
255
+ dodal/testing/setup.py,sha256=8cQnrzE5MQD4Etf0eqMarmtr-opsUOMQww-k1V7DzIQ,2442
254
256
  dodal/testing/electron_analyser/__init__.py,sha256=-lc1opD2dCv0x678-J-ApOhHtvEvcslfOQ7E613U8-Y,118
255
257
  dodal/testing/electron_analyser/device_factory.py,sha256=tkMY6fW3iI02DTD1XXHi4lH6sjo8RHHZBGDHSuTdmNU,2243
256
- dls_dodal-1.56.0.dist-info/METADATA,sha256=NqXV5pD4dRmGxYRSjy-i--y0scKO6E0V14rJRehyguw,16928
257
- dls_dodal-1.56.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
258
- dls_dodal-1.56.0.dist-info/entry_points.txt,sha256=bycw_EKUzup_rxfCetOwcauXV4kLln_OPpPT8jEnr-I,94
259
- dls_dodal-1.56.0.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
260
- dls_dodal-1.56.0.dist-info/RECORD,,
258
+ dls_dodal-1.58.0.dist-info/METADATA,sha256=FL_laMKISMqDK1hq8tvlBEF1M-r4XhFjVYBfT8yya5E,16928
259
+ dls_dodal-1.58.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
260
+ dls_dodal-1.58.0.dist-info/entry_points.txt,sha256=bycw_EKUzup_rxfCetOwcauXV4kLln_OPpPT8jEnr-I,94
261
+ dls_dodal-1.58.0.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
262
+ dls_dodal-1.58.0.dist-info/RECORD,,
dodal/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.56.0'
32
- __version_tuple__ = version_tuple = (1, 56, 0)
31
+ __version__ = version = '1.58.0'
32
+ __version_tuple__ = version_tuple = (1, 58, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
dodal/beamlines/i03.py CHANGED
@@ -261,6 +261,7 @@ def undulator(daq_configuration_path: str | None = None) -> Undulator:
261
261
  f"{BeamlinePrefix(BL).insertion_prefix}-MO-SERVC-01:",
262
262
  # evaluate here not as parameter default to enable post-import mocking
263
263
  id_gap_lookup_table_path=f"{daq_configuration_path or DAQ_CONFIGURATION_PATH}/lookup/BeamLine_Undulator_toGap.txt",
264
+ baton=baton(),
264
265
  )
265
266
 
266
267
 
@@ -333,7 +334,7 @@ def xbpm_feedback() -> XBPMFeedback:
333
334
  """Get the i03 XBPM feeback device, instantiate it if it hasn't already been.
334
335
  If this is called when already instantiated in i03, it will return the existing object.
335
336
  """
336
- return XBPMFeedback(f"{PREFIX.beamline_prefix}-EA-FDBK-01:")
337
+ return XBPMFeedback(f"{PREFIX.beamline_prefix}-EA-FDBK-01:", baton=baton())
337
338
 
338
339
 
339
340
  @device_factory()
dodal/beamlines/i04.py CHANGED
@@ -1,3 +1,5 @@
1
+ from ophyd_async.core import Reference
2
+
1
3
  from dodal.common.beamlines.beamline_parameters import get_beamline_parameters
2
4
  from dodal.common.beamlines.beamline_utils import (
3
5
  device_factory,
@@ -30,6 +32,7 @@ from dodal.devices.oav.oav_to_redis_forwarder import OAVToRedisForwarder
30
32
  from dodal.devices.oav.pin_image_recognition import PinTipDetection
31
33
  from dodal.devices.robot import BartRobot
32
34
  from dodal.devices.s4_slit_gaps import S4SlitGaps
35
+ from dodal.devices.scintillator import Scintillator
33
36
  from dodal.devices.smargon import Smargon
34
37
  from dodal.devices.synchrotron import Synchrotron
35
38
  from dodal.devices.thawer import Thawer
@@ -357,3 +360,15 @@ def pin_tip_detection() -> PinTipDetection:
357
360
  If this is called when already instantiated in i04, it will return the existing object.
358
361
  """
359
362
  return PinTipDetection(f"{PREFIX.beamline_prefix}-DI-OAV-01:")
363
+
364
+
365
+ @device_factory()
366
+ def scintillator() -> Scintillator:
367
+ """Get the i04 scintillator device, instantiate it if it hasn't already been.
368
+ If this is called when already instantiated in i04, it will return the existing object.
369
+ """
370
+ return Scintillator(
371
+ f"{PREFIX.beamline_prefix}-MO-SCIN-01:",
372
+ Reference(aperture_scatterguard()),
373
+ get_beamline_parameters(),
374
+ )
dodal/beamlines/i17.py ADDED
@@ -0,0 +1,37 @@
1
+ """The I17 hardware doesn't exist yet, but this configuration file is useful for
2
+ creating plans in sm-bluesky as devices build up."""
3
+
4
+ from ophyd_async.core import StrictEnum
5
+
6
+ from dodal.common.beamlines.beamline_utils import (
7
+ device_factory,
8
+ )
9
+ from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
10
+ from dodal.devices.pgm import PGM
11
+ from dodal.devices.synchrotron import Synchrotron
12
+ from dodal.log import set_beamline as set_log_beamline
13
+ from dodal.utils import BeamlinePrefix, get_beamline_name
14
+
15
+ BL = get_beamline_name("i17")
16
+ PREFIX = BeamlinePrefix(BL)
17
+ set_log_beamline(BL)
18
+ set_utils_beamline(BL)
19
+
20
+
21
+ class I17Grating(StrictEnum):
22
+ AU_400 = "400 line/mm Au"
23
+ SI_400 = "400 line/mm Si"
24
+
25
+
26
+ @device_factory()
27
+ def synchrotron() -> Synchrotron:
28
+ return Synchrotron()
29
+
30
+
31
+ @device_factory(skip=True)
32
+ def pgm() -> PGM:
33
+ return PGM(
34
+ prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:",
35
+ grating=I17Grating,
36
+ gratingPv="NLINES2",
37
+ )
dodal/beamlines/i19_2.py CHANGED
@@ -4,6 +4,7 @@ from dodal.common.beamlines.beamline_utils import (
4
4
  from dodal.common.beamlines.beamline_utils import (
5
5
  set_beamline as set_utils_beamline,
6
6
  )
7
+ from dodal.devices.i19.backlight import BacklightPosition
7
8
  from dodal.devices.i19.beamstop import BeamStop
8
9
  from dodal.devices.i19.blueapi_device import HutchState
9
10
  from dodal.devices.i19.diffractometer import FourCircleDiffractometer
@@ -75,3 +76,11 @@ def synchrotron() -> Synchrotron:
75
76
  If this is called when already instantiated in i19-2, it will return the existing object.
76
77
  """
77
78
  return Synchrotron()
79
+
80
+
81
+ @device_factory()
82
+ def backlight() -> BacklightPosition:
83
+ """Get the i19-2 backlight device, instantiate it if it hasn't already been.
84
+ If this is called when already instantiated in i19-2, it will return the existing object.
85
+ """
86
+ return BacklightPosition(prefix=f"{PREFIX.beamline_prefix}-EA-IOC-12:")
dodal/beamlines/i22.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from pathlib import Path
2
2
 
3
3
  from ophyd_async.epics.adaravis import AravisDetector
4
- from ophyd_async.epics.adcore import NDPluginStatsIO
4
+ from ophyd_async.epics.adcore import NDPluginBaseIO
5
5
  from ophyd_async.epics.adpilatus import PilatusDetector
6
6
  from ophyd_async.fastcs.panda import HDFPanda
7
7
 
@@ -103,7 +103,7 @@ def i0() -> TetrammDetector:
103
103
  path_provider=get_path_provider(),
104
104
  type="Cividec Diamond XBPM",
105
105
  plugins={
106
- "stats": NDPluginStatsIO(
106
+ "stats": NDPluginBaseIO(
107
107
  prefix=f"{PREFIX.beamline_prefix}-EA-XBPM-02:SumAll:"
108
108
  )
109
109
  },
@@ -117,7 +117,7 @@ def it() -> TetrammDetector:
117
117
  path_provider=get_path_provider(),
118
118
  type="PIN Diode",
119
119
  plugins={
120
- "stats": NDPluginStatsIO(
120
+ "stats": NDPluginBaseIO(
121
121
  prefix=f"{PREFIX.beamline_prefix}-EA-TTRM-02:SumAll:"
122
122
  )
123
123
  },
dodal/beamlines/i24.py CHANGED
@@ -11,7 +11,6 @@ from dodal.devices.i24.beamstop import Beamstop
11
11
  from dodal.devices.i24.dcm import DCM
12
12
  from dodal.devices.i24.dual_backlight import DualBacklight
13
13
  from dodal.devices.i24.focus_mirrors import FocusMirrorsMode
14
- from dodal.devices.i24.pilatus_metadata import PilatusMetadata
15
14
  from dodal.devices.i24.pmac import PMAC
16
15
  from dodal.devices.i24.vgonio import VerticalGoniometer
17
16
  from dodal.devices.motors import YZStage
@@ -100,8 +99,8 @@ def dcm() -> DCM:
100
99
  If this is called when already instantiated in i24, it will return the existing object.
101
100
  """
102
101
  return DCM(
103
- prefix=f"{PREFIX.beamline_prefix}-DI-DCM-01",
104
- motion_prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01",
102
+ prefix=f"{PREFIX.beamline_prefix}-DI-DCM-01:",
103
+ motion_prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:",
105
104
  )
106
105
 
107
106
 
@@ -188,21 +187,3 @@ def eiger_beam_center() -> DetectorBeamCenter:
188
187
  f"{PREFIX.beamline_prefix}-EA-EIGER-01:CAM:",
189
188
  "eiger_bc",
190
189
  )
191
-
192
-
193
- @device_factory()
194
- def pilatus_beam_center() -> DetectorBeamCenter:
195
- """A device for setting/reading the beamcenter from the pilatus on i24."""
196
- return DetectorBeamCenter(
197
- f"{PREFIX.beamline_prefix}-EA-PILAT-01:cam1:",
198
- "pilatus_bc",
199
- )
200
-
201
-
202
- @device_factory()
203
- def pilatus_metadata() -> PilatusMetadata:
204
- """A small pilatus driver device for figuring out the filename template."""
205
- return PilatusMetadata(
206
- f"{PREFIX.beamline_prefix}-EA-PILAT-01:",
207
- "pilatus_meta",
208
- )
@@ -0,0 +1,33 @@
1
+ """Functions relating to commissioning mode.
2
+
3
+ Commissioning Mode can be enabled for a production beamline when there is no
4
+ beam. The intent is that when it is enabled, bluesky plans may be run without beam
5
+ and plans and devices will as far as is possible behave normally.
6
+ """
7
+
8
+ import bluesky.plan_stubs as bps
9
+ from bluesky.utils import MsgGenerator
10
+ from ophyd_async.core import SignalR
11
+
12
+ _commissioning_signal: SignalR | None = None
13
+
14
+
15
+ def read_commissioning_mode() -> MsgGenerator[bool]:
16
+ """Utility method for reading the commissioning mode state from the context
17
+ of a bluesky plan, where a baton may or may not be present, or
18
+ commissioning mode is provided by some other mechanism."""
19
+ if _commissioning_signal:
20
+ return (yield from bps.rd(_commissioning_signal))
21
+ else:
22
+ return False
23
+
24
+
25
+ def set_commissioning_signal(signal: SignalR[bool] | None):
26
+ """Commissioning mode is enabled by a PV which when set enables commissioning mode.
27
+ This allows beamline staff to ensure that commissioning mode is disabled prior
28
+ to production use, via their own 'good morning' startup scripts.
29
+ Args:
30
+ signal: The signal which will be read in order to determine whether
31
+ commissioning mode is enabled."""
32
+ global _commissioning_signal
33
+ _commissioning_signal = signal
dodal/devices/baton.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from typing import Annotated as A
2
2
 
3
3
  from ophyd_async.core import (
4
+ SignalR,
4
5
  SignalRW,
5
6
  StandardReadable,
6
7
  )
@@ -15,3 +16,6 @@ class Baton(StandardReadable, EpicsDevice):
15
16
  current_user: A[
16
17
  SignalRW[str], PvSuffix("CURRENT_USER"), Format.HINTED_UNCACHED_SIGNAL
17
18
  ]
19
+ commissioning: A[
20
+ SignalR[bool], PvSuffix("COMMISSIONING"), Format.HINTED_UNCACHED_SIGNAL
21
+ ]
@@ -0,0 +1,17 @@
1
+ from bluesky.protocols import Movable
2
+ from ophyd_async.core import AsyncStatus, StandardReadable
3
+ from ophyd_async.epics.core import epics_signal_rw
4
+
5
+ from dodal.common.enums import InOutUpper
6
+
7
+
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"""
10
+
11
+ def __init__(self, prefix: str, name: str = "") -> None:
12
+ self.position = epics_signal_rw(InOutUpper, f"{prefix}AD1:choiceButton")
13
+ super().__init__(name)
14
+
15
+ @AsyncStatus.wrap
16
+ async def set(self, value: InOutUpper):
17
+ await self.position.set(value, wait=True)
@@ -1,5 +1,4 @@
1
- """A small temporary device to get the beam center positions from \
2
- eiger or pilatus detector on i24"""
1
+ """A small temporary device to get the beam center positions on i24"""
3
2
 
4
3
  from ophyd_async.core import StandardReadable
5
4
  from ophyd_async.epics.core import epics_signal_rw
dodal/devices/smargon.py CHANGED
@@ -3,7 +3,7 @@ from collections.abc import Collection, Generator
3
3
  from dataclasses import dataclass
4
4
  from enum import Enum
5
5
  from math import isclose
6
- from typing import NotRequired, TypedDict, cast
6
+ from typing import TypedDict, cast
7
7
 
8
8
  from bluesky import plan_stubs as bps
9
9
  from bluesky.protocols import Movable
@@ -12,6 +12,7 @@ from ophyd_async.core import (
12
12
  AsyncStatus,
13
13
  Device,
14
14
  StrictEnum,
15
+ set_and_wait_for_value,
15
16
  wait_for_value,
16
17
  )
17
18
  from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
@@ -104,15 +105,15 @@ class DeferMoves(StrictEnum):
104
105
  OFF = "Defer Off"
105
106
 
106
107
 
107
- class CombinedMove(TypedDict):
108
+ class CombinedMove(TypedDict, total=False):
108
109
  """A move on multiple axes at once using a deferred move"""
109
110
 
110
- x: NotRequired[float | None]
111
- y: NotRequired[float | None]
112
- z: NotRequired[float | None]
113
- omega: NotRequired[float | None]
114
- phi: NotRequired[float | None]
115
- chi: NotRequired[float | None]
111
+ x: float | None
112
+ y: float | None
113
+ z: float | None
114
+ omega: float | None
115
+ phi: float | None
116
+ chi: float | None
116
117
 
117
118
 
118
119
  class Smargon(XYZStage, Movable):
@@ -123,6 +124,8 @@ class Smargon(XYZStage, Movable):
123
124
  Robot loading can nudge these and lead to errors.
124
125
  """
125
126
 
127
+ DEFERRED_MOVE_SET_TIMEOUT = 5
128
+
126
129
  def __init__(self, prefix: str, name: str = ""):
127
130
  with self.add_children_as_readables():
128
131
  self.chi = Motor(prefix + "CHI")
@@ -161,15 +164,29 @@ class Smargon(XYZStage, Movable):
161
164
 
162
165
  @AsyncStatus.wrap
163
166
  async def set(self, value: CombinedMove):
167
+ """This will move all motion together in a deferred move.
168
+
169
+ Once defer_move is on, sets to any axis do not immediately move the axis. Instead
170
+ the setpoint will go to that value. Then, when defer_move is switched off all
171
+ axes will move at the same time. The put callbacks on the axes themselves will
172
+ only come back after the motion on that axis finished.
173
+ """
164
174
  await self.defer_move.set(DeferMoves.ON)
165
175
  try:
166
- tasks = []
167
- for k, v in value.items():
168
- if v is not None:
169
- tasks.append(getattr(self, k).set(v))
176
+ finished_moving = []
177
+ for motor_name, new_setpoint in value.items():
178
+ if new_setpoint is not None and isinstance(new_setpoint, int | float):
179
+ axis: Motor = getattr(self, motor_name)
180
+ await axis.check_motor_limit(
181
+ await axis.user_setpoint.get_value(), new_setpoint
182
+ )
183
+ put_completion = await set_and_wait_for_value(
184
+ axis.user_setpoint,
185
+ new_setpoint,
186
+ timeout=self.DEFERRED_MOVE_SET_TIMEOUT,
187
+ wait_for_set_completion=False,
188
+ )
189
+ finished_moving.append(put_completion)
170
190
  finally:
171
191
  await self.defer_move.set(DeferMoves.OFF)
172
- # The set() coroutines will not complete until after defer moves has been
173
- # switched back off so we cannot wait for them until this point.
174
- # see https://github.com/DiamondLightSource/dodal/issues/1315
175
- await asyncio.gather(*tasks)
192
+ await asyncio.gather(*finished_moving)
dodal/devices/thawer.py CHANGED
@@ -1,4 +1,4 @@
1
- from asyncio import Task, create_task, sleep
1
+ from asyncio import CancelledError, Task, create_task, sleep
2
2
 
3
3
  from bluesky.protocols import Movable, Stoppable
4
4
  from ophyd_async.core import (
@@ -11,6 +11,8 @@ from ophyd_async.core import (
11
11
  )
12
12
  from ophyd_async.epics.core import epics_signal_rw
13
13
 
14
+ from dodal.log import LOGGER
15
+
14
16
 
15
17
  class ThawingException(Exception):
16
18
  pass
@@ -24,19 +26,29 @@ class ThawingTimer(Device, Stoppable, Movable[float]):
24
26
 
25
27
  @AsyncStatus.wrap
26
28
  async def set(self, value: float):
27
- await self._control_signal_ref().set(OnOff.ON)
28
- if self._thawing_task and not self._thawing_task.done():
29
- raise ThawingException("Thawing task already in progress")
29
+ if self._thawing_task:
30
+ LOGGER.info("Thawing task already in progress, resetting timer")
31
+ self._thawing_task.cancel()
32
+ else:
33
+ LOGGER.info("Thawing started")
34
+ await self._control_signal_ref().set(OnOff.ON)
30
35
  self._thawing_task = create_task(sleep(value))
31
36
  try:
32
37
  await self._thawing_task
33
- finally:
38
+ except CancelledError:
39
+ LOGGER.info("Timer task cancelled.")
40
+ raise
41
+ else:
42
+ LOGGER.info("Thawing completed")
34
43
  await self._control_signal_ref().set(OnOff.OFF)
35
44
 
36
45
  @AsyncStatus.wrap
37
46
  async def stop(self, *args, **kwargs):
38
47
  if self._thawing_task:
39
48
  self._thawing_task.cancel()
49
+ self._thawing_task = None
50
+ LOGGER.info("Thawer stopped.")
51
+ await self._control_signal_ref().set(OnOff.OFF)
40
52
 
41
53
 
42
54
  class Thawer(StandardReadable, Stoppable):
@@ -5,6 +5,7 @@ from bluesky.protocols import Movable
5
5
  from numpy import ndarray
6
6
  from ophyd_async.core import (
7
7
  AsyncStatus,
8
+ Reference,
8
9
  StandardReadable,
9
10
  StandardReadableFormat,
10
11
  soft_signal_r_and_setter,
@@ -15,6 +16,7 @@ from ophyd_async.epics.motor import Motor
15
16
  from dodal.common.enums import EnabledDisabledUpper
16
17
  from dodal.log import LOGGER
17
18
 
19
+ from .baton import Baton
18
20
  from .util.lookup_tables import energy_distance_table
19
21
 
20
22
 
@@ -22,11 +24,6 @@ class AccessError(Exception):
22
24
  pass
23
25
 
24
26
 
25
- # Enable to allow testing when the beamline is down, do not change in production!
26
- TEST_MODE = False
27
- # will be made more generic in https://github.com/DiamondLightSource/dodal/issues/754
28
-
29
-
30
27
  # The acceptable difference, in mm, between the undulator gap and the DCM
31
28
  # energy, when the latter is converted to mm using lookup tables
32
29
  UNDULATOR_DISCREPANCY_THRESHOLD_MM = 2e-3
@@ -54,6 +51,7 @@ class Undulator(StandardReadable, Movable[float]):
54
51
  name: str = "",
55
52
  poles: int | None = None,
56
53
  length: float | None = None,
54
+ baton: Baton | None = None,
57
55
  ) -> None:
58
56
  """Constructor
59
57
 
@@ -64,6 +62,7 @@ class Undulator(StandardReadable, Movable[float]):
64
62
  name (str, optional): Name for device. Defaults to "".
65
63
  """
66
64
 
65
+ self.baton_ref = Reference(baton) if baton else None
67
66
  self.id_gap_lookup_table_path = id_gap_lookup_table_path
68
67
  with self.add_children_as_readables():
69
68
  self.gap_motor = Motor(prefix + "BLGAPMTR")
@@ -105,7 +104,8 @@ class Undulator(StandardReadable, Movable[float]):
105
104
 
106
105
  async def raise_if_not_enabled(self):
107
106
  access_level = await self.gap_access.get_value()
108
- if access_level is EnabledDisabledUpper.DISABLED and not TEST_MODE:
107
+ commissioning_mode = await self._is_commissioning_mode_enabled()
108
+ if access_level is EnabledDisabledUpper.DISABLED and not commissioning_mode:
109
109
  raise AccessError("Undulator gap access is disabled. Contact Control Room")
110
110
 
111
111
  async def _set_undulator_gap(self, energy_kev: float) -> None:
@@ -124,21 +124,25 @@ class Undulator(StandardReadable, Movable[float]):
124
124
  f"Undulator gap mismatch. {difference:.3f}mm is outside tolerance.\
125
125
  Moving gap to nominal value, {target_gap:.3f}mm"
126
126
  )
127
- if not TEST_MODE:
127
+ commissioning_mode = await self._is_commissioning_mode_enabled()
128
+ if not commissioning_mode:
128
129
  # Only move if the gap is sufficiently different to the value from the
129
- # DCM lookup table AND we're not in TEST_MODE
130
+ # DCM lookup table AND we're not in commissioning mode
130
131
  await self.gap_motor.set(
131
132
  target_gap,
132
133
  timeout=STATUS_TIMEOUT_S,
133
134
  )
134
135
  else:
135
- LOGGER.debug("In test mode, not moving ID gap")
136
+ LOGGER.warning("In test mode, not moving ID gap")
136
137
  else:
137
138
  LOGGER.debug(
138
139
  "Gap is already in the correct place for the new energy value "
139
140
  f"{energy_kev}, no need to ask it to move"
140
141
  )
141
142
 
143
+ async def _is_commissioning_mode_enabled(self):
144
+ return self.baton_ref and await self.baton_ref().commissioning.get_value()
145
+
142
146
  async def _get_gap_to_match_energy(self, energy_kev: float) -> float:
143
147
  """
144
148
  get a 2d np.array from lookup table that
dodal/devices/webcam.py CHANGED
@@ -51,6 +51,7 @@ class Webcam(StandardReadable, Triggerable):
51
51
  )
52
52
  try:
53
53
  data = await response.read()
54
+ Image.open(BytesIO(data)).verify()
54
55
  LOGGER.info(f"Saving webcam image from {self.url} to {file_path}")
55
56
  except Exception as e:
56
57
  LOGGER.warning(
@@ -1,8 +1,10 @@
1
1
  from bluesky.protocols import Triggerable
2
- from ophyd_async.core import AsyncStatus, Device, StrictEnum, observe_value
2
+ from ophyd_async.core import AsyncStatus, Device, Reference, StrictEnum, observe_value
3
3
  from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
4
4
 
5
5
  from dodal.common.device_utils import periodic_reminder
6
+ from dodal.devices.baton import Baton
7
+ from dodal.log import LOGGER
6
8
 
7
9
 
8
10
  class Pause(StrictEnum):
@@ -14,15 +16,19 @@ class XBPMFeedback(Device, Triggerable):
14
16
  """The XBPM feedback device is an IOC that moves the DCM, HFM and VFM to automatically
15
17
  hold the beam into place, as measured by the XBPM sensor."""
16
18
 
17
- def __init__(self, prefix: str, name: str = "") -> None:
19
+ def __init__(self, prefix: str, name: str = "", baton: Baton | None = None) -> None:
18
20
  self.pos_ok = epics_signal_r(float, prefix + "XBPM2POSITION_OK")
19
21
  self.pos_stable = epics_signal_r(float, prefix + "XBPM2_STABLE")
20
22
  self.pause_feedback = epics_signal_rw(Pause, prefix + "FB_PAUSE")
23
+ self.baton_ref = Reference(baton) if baton else None
21
24
  super().__init__(name=name)
22
25
 
23
26
  @AsyncStatus.wrap
24
27
  async def trigger(self):
25
- async with periodic_reminder("Waiting for XBPM"):
26
- async for value in observe_value(self.pos_stable):
27
- if value:
28
- return
28
+ if self.baton_ref and await self.baton_ref().commissioning.get_value():
29
+ LOGGER.warning("Commissioning mode enabled, ignoring feedback")
30
+ else:
31
+ async with periodic_reminder("Waiting for XBPM"):
32
+ async for value in observe_value(self.pos_stable):
33
+ if value:
34
+ return
dodal/testing/__init__.py CHANGED
@@ -0,0 +1,3 @@
1
+ from .setup import patch_all_motors, patch_motor
2
+
3
+ __all__ = ["patch_motor", "patch_all_motors"]
dodal/testing/setup.py ADDED
@@ -0,0 +1,67 @@
1
+ from contextlib import ExitStack
2
+
3
+ from ophyd_async.core import Device
4
+ from ophyd_async.epics.motor import Motor
5
+ from ophyd_async.testing import (
6
+ callback_on_mock_put,
7
+ set_mock_value,
8
+ )
9
+
10
+
11
+ def patch_motor(
12
+ motor: Motor,
13
+ initial_position: float = 0,
14
+ deadband: float = 0.001,
15
+ velocity: float = 3,
16
+ max_velocity: float = 5,
17
+ low_limit_travel: float = float("-inf"),
18
+ high_limit_travel: float = float("inf"),
19
+ ):
20
+ """
21
+ Patch a mock motor with sensible default values so that it can still be used in
22
+ tests and plans without running into errors as default values are zero.
23
+
24
+ Parameters:
25
+ motor: The mock motor to set mock values with.
26
+ initial_position: The default initial position of the motor to be set.
27
+ deadband: The tolerance between readback value and demand setpoint which the
28
+ motor is considered at position.
29
+ velocity: Requested move speed when the mock motor moves.
30
+ max_velocity: The maximum allowable velocity that can be set for the motor.
31
+ low_limit_travel: The lower limit that the motor can move to.
32
+ high_limit_travel: The higher limit that the motor can move to.
33
+ """
34
+ set_mock_value(motor.user_setpoint, initial_position)
35
+ set_mock_value(motor.user_readback, initial_position)
36
+ set_mock_value(motor.deadband, deadband)
37
+ set_mock_value(motor.motor_done_move, 1)
38
+ set_mock_value(motor.velocity, velocity)
39
+ set_mock_value(motor.max_velocity, max_velocity)
40
+ set_mock_value(motor.low_limit_travel, low_limit_travel)
41
+ set_mock_value(motor.high_limit_travel, high_limit_travel)
42
+ return callback_on_mock_put(
43
+ motor.user_setpoint,
44
+ lambda pos, *args, **kwargs: set_mock_value(motor.user_readback, pos),
45
+ )
46
+
47
+
48
+ def patch_all_motors(parent_device: Device):
49
+ """
50
+ Check all children of a device and patch any motors with mock values.
51
+
52
+ Parameters:
53
+ parent_device: The device that hold motor(s) as children.
54
+ """
55
+ motors = []
56
+
57
+ def recursively_find_motors(device: Device):
58
+ for _, child_device in device.children():
59
+ if isinstance(child_device, Motor):
60
+ motors.append(child_device)
61
+ recursively_find_motors(child_device)
62
+
63
+ recursively_find_motors(parent_device)
64
+ motor_patch_stack = ExitStack()
65
+ for motor in motors:
66
+ motor_patch_stack.enter_context(patch_motor(motor))
67
+ return motor_patch_stack
@@ -1,44 +0,0 @@
1
- """A small temporary device to set and read the filename template from the pilatus"""
2
-
3
- import re
4
-
5
- from ophyd_async.core import StandardReadable, derived_signal_r
6
- from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
7
-
8
-
9
- class PilatusMetadata(StandardReadable):
10
- def __init__(self, prefix: str, name: str = "") -> None:
11
- self.filename = epics_signal_rw(str, prefix + "cam1:FileName")
12
- self.template = epics_signal_r(str, prefix + "cam1:FileTemplate_RBV")
13
- self.filenumber = epics_signal_r(int, prefix + "cam1:FileNumber_RBV")
14
- with self.add_children_as_readables():
15
- self.filename_template = derived_signal_r(
16
- self._get_full_filename_template,
17
- filename=self.filename,
18
- filename_template=self.template,
19
- file_number=self.filenumber,
20
- )
21
- super().__init__(name)
22
-
23
- def _get_full_filename_template(
24
- self, filename: str, filename_template: str, file_number: int
25
- ) -> str:
26
- """
27
- Get the template file path by querying the detector PVs.
28
- Mirror the construction that the PPU does.
29
-
30
- Returns: A template string, with the image numbers replaced with '#'
31
- """
32
- # Exploit fact that passing negative numbers will put the - before the 0's
33
- expected_filename = str(
34
- filename_template % (filename, f"{file_number:05d}_", -9)
35
- )
36
- # Now, find the -09 part of this
37
- numberpart = re.search(r"(-0+9)", expected_filename)
38
- assert numberpart is not None
39
- template_fill = "#" * len(numberpart.group(0))
40
- return (
41
- expected_filename[: numberpart.start()]
42
- + template_fill
43
- + expected_filename[numberpart.end() :]
44
- )
@@ -1,37 +0,0 @@
1
- from contextlib import ExitStack
2
-
3
- from ophyd_async.core import Device
4
- from ophyd_async.epics.motor import Motor
5
- from ophyd_async.testing import (
6
- callback_on_mock_put,
7
- set_mock_value,
8
- )
9
-
10
-
11
- def patch_motor(motor: Motor, initial_position=0):
12
- set_mock_value(motor.user_setpoint, initial_position)
13
- set_mock_value(motor.user_readback, initial_position)
14
- set_mock_value(motor.deadband, 0.001)
15
- set_mock_value(motor.motor_done_move, 1)
16
- set_mock_value(motor.velocity, 3)
17
- set_mock_value(motor.max_velocity, 5)
18
- return callback_on_mock_put(
19
- motor.user_setpoint,
20
- lambda pos, *args, **kwargs: set_mock_value(motor.user_readback, pos),
21
- )
22
-
23
-
24
- def patch_all_motors(parent_device: Device):
25
- motors = []
26
-
27
- def recursively_find_motors(device: Device):
28
- for _, child_device in device.children():
29
- if isinstance(child_device, Motor):
30
- motors.append(child_device)
31
- recursively_find_motors(child_device)
32
-
33
- recursively_find_motors(parent_device)
34
- motor_patch_stack = ExitStack()
35
- for motor in motors:
36
- motor_patch_stack.enter_context(patch_motor(motor))
37
- return motor_patch_stack