dls-dodal 1.60.0__py3-none-any.whl → 1.61.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 (36) hide show
  1. {dls_dodal-1.60.0.dist-info → dls_dodal-1.61.0.dist-info}/METADATA +1 -1
  2. {dls_dodal-1.60.0.dist-info → dls_dodal-1.61.0.dist-info}/RECORD +36 -24
  3. dodal/_version.py +2 -2
  4. dodal/devices/apple2_undulator.py +85 -52
  5. dodal/devices/areadetector/__init__.py +0 -0
  6. dodal/devices/areadetector/plugins/__init__.py +0 -0
  7. dodal/devices/attenuator/__init__.py +0 -0
  8. dodal/devices/electron_analyser/abstract/__init__.py +2 -2
  9. dodal/devices/electron_analyser/abstract/base_detector.py +13 -26
  10. dodal/devices/electron_analyser/abstract/base_driver_io.py +5 -4
  11. dodal/devices/electron_analyser/abstract/base_region.py +28 -13
  12. dodal/devices/electron_analyser/detector.py +19 -31
  13. dodal/devices/electron_analyser/specs/driver_io.py +0 -1
  14. dodal/devices/electron_analyser/vgscienta/driver_io.py +0 -1
  15. dodal/devices/fast_grid_scan.py +14 -11
  16. dodal/devices/i04/murko_results.py +24 -12
  17. dodal/devices/i10/i10_apple2.py +15 -15
  18. dodal/devices/i10/rasor/__init__.py +0 -0
  19. dodal/devices/i11/__init__.py +0 -0
  20. dodal/devices/i15/__init__.py +0 -0
  21. dodal/devices/i15/focussing_mirror.py +4 -20
  22. dodal/devices/i15/jack.py +2 -10
  23. dodal/devices/i15/laue.py +1 -5
  24. dodal/devices/i15/multilayer_mirror.py +1 -5
  25. dodal/devices/i15/rail.py +1 -5
  26. dodal/devices/i18/__init__.py +0 -0
  27. dodal/devices/i22/__init__.py +0 -0
  28. dodal/devices/i24/commissioning_jungfrau.py +9 -1
  29. dodal/devices/mx_phase1/__init__.py +0 -0
  30. dodal/devices/oav/snapshots/__init__.py +0 -0
  31. dodal/devices/xspress3/__init__.py +0 -0
  32. dodal/parameters/__init__.py +0 -0
  33. {dls_dodal-1.60.0.dist-info → dls_dodal-1.61.0.dist-info}/WHEEL +0 -0
  34. {dls_dodal-1.60.0.dist-info → dls_dodal-1.61.0.dist-info}/entry_points.txt +0 -0
  35. {dls_dodal-1.60.0.dist-info → dls_dodal-1.61.0.dist-info}/licenses/LICENSE +0 -0
  36. {dls_dodal-1.60.0.dist-info → dls_dodal-1.61.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dls-dodal
3
- Version: 1.60.0
3
+ Version: 1.61.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
@@ -1,7 +1,7 @@
1
- dls_dodal-1.60.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1
+ dls_dodal-1.61.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=DQ2eCF3LevBLy0Ytmwazcpp3NBvIb4zB_mzF8VnCjP4,706
4
+ dodal/_version.py,sha256=R0nxjIENITg3eHoaeIFi4EMDLFAHK-E5jkMwqc20ybI,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
@@ -68,7 +68,7 @@ dodal/common/beamlines/device_helpers.py,sha256=8sasAIFRDwo6ElHqLrXnpj_v7xcEg-29
68
68
  dodal/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
69
  dodal/devices/aperture.py,sha256=S7AoIoQg_kFxaQk7HUMijbm1kYkOEi2csbdj76kp1ys,427
70
70
  dodal/devices/aperturescatterguard.py,sha256=FH2oru3DU-I6BwOJOVEgHdFplLKIIknxtEBUBAXZ_WA,13946
71
- dodal/devices/apple2_undulator.py,sha256=-IKrEFatjbyI8nbfbCsAmsYWZU-l4A0gXFDJ-WPYEpw,22144
71
+ dodal/devices/apple2_undulator.py,sha256=LjhU4ILkSrhnccHOwOrOT9HenowKmI0NFHLfpQk14lY,23035
72
72
  dodal/devices/backlight.py,sha256=y40kR6N3qSFVLRaGU8CjJJpwv_hq2QALf_85fLQqras,1415
73
73
  dodal/devices/baton.py,sha256=315I_0V73_DYYVT0PBs0luVy4CMqdPo0kLvHBi12MIU,606
74
74
  dodal/devices/bimorph_mirror.py,sha256=OGe6aCczG0gVco4OvIRLJVxn2kw5F2QG1e06uqhFLTw,4609
@@ -80,7 +80,7 @@ dodal/devices/diamond_filter.py,sha256=hySd7HnLdplpPNvBrLddLjO_3LqgD8-99Zr__Sy_G
80
80
  dodal/devices/eiger.py,sha256=ZKaRXF-YnYWuMW94BIg-gmdKW7JKmtMUvehF3S5XaLY,16670
81
81
  dodal/devices/eiger_odin.py,sha256=1JoqPppTp99IZCiFOXJZB3h62xXRYKZINxTYXdLnT8c,7480
82
82
  dodal/devices/eurotherm.py,sha256=rdLldmWYP1PZBckoya6svPy1mDxHYaa1IfMleMPGzD4,3832
83
- dodal/devices/fast_grid_scan.py,sha256=Bng0FFIw3PYqdqX2Mj-KhyGRKIbKZVYEygzrYtylorY,14530
83
+ dodal/devices/fast_grid_scan.py,sha256=4HHOVAPwhUf4u1nC_kNhCAUzCqYuEsJ15lqcnxfdrVg,14659
84
84
  dodal/devices/fluorescence_detector_motion.py,sha256=hJ1M9Zs6Dlw8DDL9APh7yVePlS4tU2hnlySd16hqfwE,346
85
85
  dodal/devices/flux.py,sha256=1CDsq9yU2-ho8MfYBl50Tl9ABZwpUBHnV486PQXKNoQ,462
86
86
  dodal/devices/focusing_mirror.py,sha256=2UWVrY6bs6_0i-h6JJ_LAUZPX_FgMzIx04JqZafdtYU,7464
@@ -111,8 +111,11 @@ dodal/devices/xbpm_feedback.py,sha256=HKLY3k52FPe2G-EAg8ir2LSdeUuGvhRD73ibDZ4mjH
111
111
  dodal/devices/aithre_lasershaping/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
112
112
  dodal/devices/aithre_lasershaping/goniometer.py,sha256=-2ewYMcdzB1DqP9sMpr4L1i4KeTCrGAJfngAt9-eFWw,1005
113
113
  dodal/devices/aithre_lasershaping/laser_robot.py,sha256=vy-I1ASYflFYIUYePhT2l3l7g2iekrIFzLxA0GLmKY8,717
114
+ dodal/devices/areadetector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
115
  dodal/devices/areadetector/plugins/CAM.py,sha256=sZzJm5Ez3eWfXZi_EB67wluhZmMQm1UyOc2bJFfzd1U,964
115
116
  dodal/devices/areadetector/plugins/MJPG.py,sha256=QTsxCoWbofNpLMGPoOR2hWoM33KyntuLepbF0YmX0KE,3031
117
+ dodal/devices/areadetector/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
118
+ dodal/devices/attenuator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
119
  dodal/devices/attenuator/attenuator.py,sha256=-rJYY_bHCDl8NVn3yYPytYIANiOPsz0f-3U1zBsT5pE,6246
117
120
  dodal/devices/attenuator/filter.py,sha256=xb_8S62ktJdqdNE03PqhsfsTsQyTsRbR9CF1ksfX06M,542
118
121
  dodal/devices/attenuator/filter_selections.py,sha256=k77EKESAlbA_ZUf1eNBdzMa8Xs8Mmmmb7XBkhipo3lE,1900
@@ -136,24 +139,24 @@ dodal/devices/detector/det_resolution.py,sha256=aQkKp24LpRGiwzPAQM3wLVa4ANw32Hdr
136
139
  dodal/devices/detector/detector.py,sha256=DqQvlgdjIdKErrZqgM9IH7drc-r6N8nhkFk7KwlgYAk,4815
137
140
  dodal/devices/detector/detector_motion.py,sha256=Ly4nz_AsB-_EDOADMC2u2J0dunpkOMbafVN_dgh3T-E,1389
138
141
  dodal/devices/electron_analyser/__init__.py,sha256=mpC48s2fK58aEKI-Fl3CiYBb4tXgcaa4Msj8_jLpH8g,956
139
- dodal/devices/electron_analyser/detector.py,sha256=nnZ5bGaZJql-G9JaDK9VqLcsbPXb7hRSSfO1103JbX0,5046
142
+ dodal/devices/electron_analyser/detector.py,sha256=FE8Qh7H01tej5GqWLXtCbiWo4n7OGm6q-QVViv-dqZ8,4749
140
143
  dodal/devices/electron_analyser/energy_sources.py,sha256=NO3oxaVgmXnm6QuvyPBaeFbTjl3KwXzXY7EsdOhjATM,3152
141
144
  dodal/devices/electron_analyser/enums.py,sha256=ruSH7LNRhtqMflv7gYxKeAu0uLzZIaoCai7sSTzpvkg,204
142
145
  dodal/devices/electron_analyser/types.py,sha256=_kmdSp4E98fsusg5Wg-Hyo25zHYA_pMe_hFW8lKe3tE,1869
143
146
  dodal/devices/electron_analyser/util.py,sha256=ZFQJxxwDPIr43859DPIS9lBuq54phUT3R2f_Rbn-FbU,1235
144
- dodal/devices/electron_analyser/abstract/__init__.py,sha256=4M9RF4ccXRnkK0zrNzt3vR-8vHgqcPiCl2CPXHSlyxg,620
145
- dodal/devices/electron_analyser/abstract/base_detector.py,sha256=_jcUSZoHJNwLVI_HLk7DD8vEjZTSN2E3APJS8-Ssjzc,2368
146
- dodal/devices/electron_analyser/abstract/base_driver_io.py,sha256=uQVsw43RigLJ_nWxS6RxtzeSjrfNgFHz5aOcbBrxIvI,9839
147
- dodal/devices/electron_analyser/abstract/base_region.py,sha256=DXJkDBJUyD7gi0ZEksXP5Wh1EZvmVbSlOevbZUHrVjU,5056
147
+ dodal/devices/electron_analyser/abstract/__init__.py,sha256=OE5iU5L_7wQudl3cddtMk6eRNbXzZxqiVIzaOAZmuxc,612
148
+ dodal/devices/electron_analyser/abstract/base_detector.py,sha256=alBDZgJxNYLAHDrUa_C5tRUSlujxwWdBebFHDQ8jOv0,2055
149
+ dodal/devices/electron_analyser/abstract/base_driver_io.py,sha256=1j_bCS1k4LqyOBvlt34ijGOLbgX3vboxOUD4j-zv-MU,9963
150
+ dodal/devices/electron_analyser/abstract/base_region.py,sha256=DY5wNs-BMYimkH5tpBEqXGq4n0YhHpk81kuHASveBpw,5702
148
151
  dodal/devices/electron_analyser/abstract/types.py,sha256=eoeO7sqlv5RE7AEnyzsxHyuBXlUzOIYdRLWp0EnLyAU,596
149
152
  dodal/devices/electron_analyser/specs/__init__.py,sha256=qN57kY6ERUGdrAWhU5m8CBrc0KaqiOGwPL6h2BNm3i0,291
150
153
  dodal/devices/electron_analyser/specs/detector.py,sha256=RaNkSmQ_Jb9AuwQqVqo3vv5Q6DcTtpoVqqRAQvgoeEs,1139
151
- dodal/devices/electron_analyser/specs/driver_io.py,sha256=vfiwK2TcDMJ5AbSPqwJDI9Xs1t0In7KzHys7PgCjNAg,4557
154
+ dodal/devices/electron_analyser/specs/driver_io.py,sha256=_6FG5zXGw8vzKc8JzyxdqqQAcU5mBoDjrF_ehfG9h8w,4500
152
155
  dodal/devices/electron_analyser/specs/enums.py,sha256=cXPcbIGEN5ZJJt1KeiNJEtZkkcjQ-fz7dBfsU283zYQ,235
153
156
  dodal/devices/electron_analyser/specs/region.py,sha256=P7rwbY72-QGKzC6TeldmP8sTmardpB_-rYx7gPVCMqo,1213
154
157
  dodal/devices/electron_analyser/vgscienta/__init__.py,sha256=5ZUJcUkJJ9ruwE0PzCFdA0HyWKrfjxaUBeGAMCC9JH4,357
155
158
  dodal/devices/electron_analyser/vgscienta/detector.py,sha256=JkO8E5N8tQjYfaHMXoDV6r3oEyzTC0oQI5IqWbOmvxw,1446
156
- dodal/devices/electron_analyser/vgscienta/driver_io.py,sha256=ISOQek03DK7UItZwZY4DxatyLwGmOlgQn131onVcmtE,3567
159
+ dodal/devices/electron_analyser/vgscienta/driver_io.py,sha256=c1v3AOSGKp6DlVmE82rvuN5aYElWsHeWPtYHPLwIM-s,3510
157
160
  dodal/devices/electron_analyser/vgscienta/enums.py,sha256=3vmX67ExATU8clueVp_mCzw3OUQx799oZMc8gyHtMJE,205
158
161
  dodal/devices/electron_analyser/vgscienta/region.py,sha256=6k6Eah6_I74Pi2_a0KfyMYFfwHwJbh1ndqy0fuq5o14,2184
159
162
  dodal/devices/i02_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -164,7 +167,7 @@ dodal/devices/i03/dcm.py,sha256=zDcgxOdMRVOQZBGDsLaIlr7o4UJIK2vehPWHxAxt6VA,2268
164
167
  dodal/devices/i03/undulator_dcm.py,sha256=L7dr1QcwJ1SiQWkHm0a4yQVJFtPiAJQyDLEXok64Ab8,2857
165
168
  dodal/devices/i04/__init__.py,sha256=Kvukapy4a5lUQ20qaCqYCJzKNaqJn2DfXP5nKZ_Pec8,118
166
169
  dodal/devices/i04/constants.py,sha256=_Dw28NeXldwRYH-h6YP6OHnyj7h0z4NQs_-RysNby5Q,281
167
- dodal/devices/i04/murko_results.py,sha256=JZNW0_G0f41ZWjp1l0TxBuYWcqjfr1XPpGRe4ObCVUk,8506
170
+ dodal/devices/i04/murko_results.py,sha256=Lool7IqYrYoUojexKpoVK8C4ztk9-sNFDg5lcPHBFSw,8949
168
171
  dodal/devices/i04/transfocator.py,sha256=sVI4Bgv-2-DH4-F1nIXMp5Aktevrm3agZnCA-WgjmW8,3780
169
172
  dodal/devices/i05/__init__.py,sha256=v0Axsf-vMyLyxcbqS8lljJc0BQ5Tw8yVBTWcO3tG_4g,67
170
173
  dodal/devices/i05/enums.py,sha256=9FHBIexkSfZSTHxUnGupravfG9sVriHcrB15jemkQOQ,191
@@ -175,13 +178,15 @@ dodal/devices/i09_1/__init__.py,sha256=dTazun87AkMDXh7J8gIZYJxJth0XUs72qahJ-VvhR
175
178
  dodal/devices/i09_1/enums.py,sha256=FwhsvKnEcm9M22U4gcAJi6FE2RaUBZH0jRjNgJUKRqc,1319
176
179
  dodal/devices/i10/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
177
180
  dodal/devices/i10/diagnostics.py,sha256=iEPScPnjhLX20dD313hBekqM2OmL_WYQN-4yKAMOFWU,5440
178
- dodal/devices/i10/i10_apple2.py,sha256=QN5y03VNE52DyMB6wI6trd7JYzQmQuXBKVod9dL2jRg,19489
181
+ dodal/devices/i10/i10_apple2.py,sha256=Rqk_xaBdIqpimjosFvM4_z5r-PkY0pPEoR2xjyqgWKI,19480
179
182
  dodal/devices/i10/i10_setting_data.py,sha256=69XWgE-YNTiW7C3t67MNcTL5JDDhOo7h-X7DCTpFE5g,164
180
183
  dodal/devices/i10/mirrors.py,sha256=Zofd0g2f3YvFi_KIHQE_w4dFZNglhmh--65tRnWLuyY,504
181
184
  dodal/devices/i10/slits.py,sha256=jI4wJPVT2vtl4bvabrU9u_v-SdGjOfDLBm0S9GMZC7w,4157
185
+ dodal/devices/i10/rasor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
182
186
  dodal/devices/i10/rasor/rasor_current_amp.py,sha256=hImaPI3veKiS5YVfYwv-qrQ1AYNBjFVxG_MwTQqtkcc,2368
183
187
  dodal/devices/i10/rasor/rasor_motors.py,sha256=yWBxsiVhbdkQzhaK1S-nUq_wVntwHgdf6T8c8f7wHUE,1327
184
188
  dodal/devices/i10/rasor/rasor_scaler_cards.py,sha256=sfWJKNx6pq342PEAlmg_Yt_Tijq7mO1XFjcCDtToQiU,467
189
+ dodal/devices/i11/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
185
190
  dodal/devices/i11/cyberstar_blower.py,sha256=YngbNzNkWVj_LUGEIqo9JRxRQ0PT7zjouj2VljdDlOk,1033
186
191
  dodal/devices/i11/diff_stages.py,sha256=3kcBRUvIwaqmoVf9MxY0CvU8LBbX_qaGhqbbYEmOri4,1713
187
192
  dodal/devices/i11/mythen.py,sha256=_fi7SAoWfJ34RyfSNXEyG0gwP0ox6_l9RUN9BF6OBUI,5221
@@ -190,14 +195,16 @@ dodal/devices/i11/spinner.py,sha256=PjumM7QRB7iFUtN4b6xCvxKItYUNX2XLLWDupy5LI_M,
190
195
  dodal/devices/i13_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
191
196
  dodal/devices/i13_1/merlin.py,sha256=mgTFSMJftRzLL-HXAUuJkOYxtyA3Rp8YX0L46JCb30Y,1019
192
197
  dodal/devices/i13_1/merlin_controller.py,sha256=myfmByOEXyMrlJZfsjOxDHeGQVwZGfsRtzrfSy2001o,1495
198
+ dodal/devices/i15/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
193
199
  dodal/devices/i15/dcm.py,sha256=QLqnKhNbkcVuCFEy9yB-2Ga4fAR0EOGZQshfSoVN8-w,3113
194
- dodal/devices/i15/focussing_mirror.py,sha256=o0foDrdqDJNFO5Mv_Cb42fVLcJ8hrMjLau3uUS7_yWE,1863
195
- dodal/devices/i15/jack.py,sha256=3FmRdVO54O1rsX3J8VBAYRvbmKOJJveAIz836SKTvoc,1024
196
- dodal/devices/i15/laue.py,sha256=I6aTJuVV93PjwtMtE_P4ZeUCsKTIvZPoG0HlOTUGGnY,527
200
+ dodal/devices/i15/focussing_mirror.py,sha256=E6T_c7M2osgHLa7u8eUfQJlXlJbZHtOp95FhP3qZ4gY,1739
201
+ dodal/devices/i15/jack.py,sha256=VafCNx-uqkIy0LxbBAhSm_tuC8_SbGCrnTbvQCExAzA,962
202
+ dodal/devices/i15/laue.py,sha256=H0nLPH8gqJejBZtZeY0lv84EaE2lqdL3CmXvT9iHhpk,496
197
203
  dodal/devices/i15/motors.py,sha256=-kGHisSWc_zuV9m2L0mGgmiSiIZ7puLsimddk28h_7s,831
198
- dodal/devices/i15/multilayer_mirror.py,sha256=KHalIhgz5QX-SPl-d8ypPwIc72iqmj004pzeIMOhytg,772
199
- dodal/devices/i15/rail.py,sha256=STXoNC8G7UbaK_wBhRv8RwnLJa2qklqE-XzaWxkD7h0,459
204
+ dodal/devices/i15/multilayer_mirror.py,sha256=qdxnn6PBAxK0MgeVUpM8Ukkq4tdX3jhjlmyU-AzzH4M,741
205
+ dodal/devices/i15/rail.py,sha256=9cRTRKy6uZBASMhK1wC8K_k_5FfLgHnoh9VblSOTkpQ,428
200
206
  dodal/devices/i18/KBMirror.py,sha256=W4R3TeulSjosUqAFIIznyWzje_Y2AoEf9f8N-NkisYM,710
207
+ dodal/devices/i18/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
208
  dodal/devices/i18/diode.py,sha256=nk5kvn4LsbhczRpCwHOO0_jJTYOz7MP9qm_uvBWuv7c,1468
202
209
  dodal/devices/i19/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
203
210
  dodal/devices/i19/backlight.py,sha256=c3F8RTessUJmJHlCWATSj-W25qlkAttb-c95eS28lKs,662
@@ -209,6 +216,7 @@ dodal/devices/i19/shutter.py,sha256=B4KP0Ruc8Ex47OfCskbjYSSu81jz4tK9GC_7pxMIkgI,
209
216
  dodal/devices/i20_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
210
217
  dodal/devices/i21/__init__.py,sha256=1H0Ov9s8K7nu6e20WtQDH39wgSKWz2ChRVAUzytIyzQ,67
211
218
  dodal/devices/i21/enums.py,sha256=asy90S84fBBydYwz89jNeocz04JXpbHn8ynaPzW353c,141
219
+ dodal/devices/i22/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
212
220
  dodal/devices/i22/dcm.py,sha256=s3RU6NxsQxOc_YgMBwR8lhboStI1gCDKbWc5pk22O0M,4489
213
221
  dodal/devices/i22/fswitch.py,sha256=kpgegs4Wv_weBSzbrlXLXqXOOZdzTn3X9k5PlEN5F6c,2881
214
222
  dodal/devices/i22/nxsas.py,sha256=M6hhiPmHO4WDBsFoSOhKAG_yPF6vTpLRPbB2UW3-aMs,6122
@@ -216,12 +224,13 @@ dodal/devices/i24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
216
224
  dodal/devices/i24/aperture.py,sha256=dlH_g7OPTBc5QRMd-ADU3_GDTKh668kkMHo4k_JxUcY,770
217
225
  dodal/devices/i24/beam_center.py,sha256=nQyOif6JtlU_qP7kZP-8wN1ldW81MIbFUykPC1bEm70,446
218
226
  dodal/devices/i24/beamstop.py,sha256=6tbiQLlcTlp4PCPhHJ_mlHtkv0kz5ArQ99zg9rwTnrw,1133
219
- dodal/devices/i24/commissioning_jungfrau.py,sha256=kVt19MS0d9kZhBCgQnWAsC5kxMY9ckTEIprggf-gveY,4217
227
+ dodal/devices/i24/commissioning_jungfrau.py,sha256=i-B6Sy2braKSGYF2G7QrluPkThXHY-YiULUJRqClFok,4532
220
228
  dodal/devices/i24/dcm.py,sha256=l7qbJh2JKL-5ANlMYXDeU5EBYY6mDiLxn7kp_Z9KNaM,1206
221
229
  dodal/devices/i24/dual_backlight.py,sha256=N0R7M1mHPRmQ4fks5lGU1wrXjOvcW_ZKIXaRoC8aLDE,2084
222
230
  dodal/devices/i24/focus_mirrors.py,sha256=DYiYLpDw8FJ1LYHxLOxE_om5qGfUo2itzskgqhmQZlg,1763
223
231
  dodal/devices/i24/pmac.py,sha256=-HYf2HPzaqWvszp4T8TXohdp40-xmKqQq4V0mLvVri8,7028
224
232
  dodal/devices/i24/vgonio.py,sha256=sxSmcYZayVJPJz_D_91j9PmNor7Tbl1RGQFRrdtESlw,533
233
+ dodal/devices/mx_phase1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
225
234
  dodal/devices/mx_phase1/beamstop.py,sha256=GeHLqHVf3XwJSszUHuxZ2JBwQlcDiFs6jVpdHuw7snM,3127
226
235
  dodal/devices/oav/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
227
236
  dodal/devices/oav/oav_calculations.py,sha256=qoaNz4MAdbpu0XHAkWTQ3rebFwsWfa8JA1eerUgISjA,2367
@@ -232,6 +241,7 @@ dodal/devices/oav/utils.py,sha256=T-xg7J38ritzXwRrKjsWZ7daC_qR7aNhlbkXx75nJ8Q,31
232
241
  dodal/devices/oav/pin_image_recognition/__init__.py,sha256=ZDqvAk-Nvw_WM00efxfx2aDEoK1T5QydIciqqCCpp7Q,6302
233
242
  dodal/devices/oav/pin_image_recognition/manual_test.py,sha256=h1Rto6ZDCB3jWhjSy9N8ECxRN583iYDJr9LxrTJ8kfE,903
234
243
  dodal/devices/oav/pin_image_recognition/utils.py,sha256=L9ypluYqeOFoS7gQuws-vTNc8LqaKl2ZIDNeQ2JaNpg,8592
244
+ dodal/devices/oav/snapshots/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
235
245
  dodal/devices/oav/snapshots/grid_overlay.py,sha256=CdvCdTKMCiwMwxm2lV28KpcIUSXlscZmWxb73_KKmiI,3694
236
246
  dodal/devices/oav/snapshots/snapshot.py,sha256=VDHYxko97sATMKvD5wClgvN7WQUtUAjcXXSyoQAL8q8,499
237
247
  dodal/devices/oav/snapshots/snapshot_image_processing.py,sha256=wDxH9WPmB0nsotr49PUpw2Ke4l4RFxirrbpPzOUF9s4,2318
@@ -250,6 +260,7 @@ dodal/devices/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
250
260
  dodal/devices/util/adjuster_plans.py,sha256=c40PFZpXFw0YmJLh9jU4VIb8vRxHyafZlmvprTKAOhM,824
251
261
  dodal/devices/util/epics_util.py,sha256=4useFL8ngsVF08fhOn48BlnO4oh0T4sEKqjdS6mjvG0,4687
252
262
  dodal/devices/util/lookup_tables.py,sha256=jH9f_D8JbTSqzL-RKHUWOORLt8lEoNQL3o9HpXE98TY,3476
263
+ dodal/devices/xspress3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
253
264
  dodal/devices/xspress3/xspress3.py,sha256=OerapEy-IuK7EFz13B5z0BzBmESVl6pYUlqAWHIwJck,4555
254
265
  dodal/devices/xspress3/xspress3_channel.py,sha256=w8tAx2lz5kJ_LeJ_eb_4o--Dtt8MRijsYNgDG6oEIVg,1626
255
266
  dodal/devices/zebra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -260,6 +271,7 @@ dodal/devices/zocalo/__init__.py,sha256=W3R09eHAmybP4BcM_gHy6gVAW5_gVSnFq2o3z2iT
260
271
  dodal/devices/zocalo/zocalo_constants.py,sha256=vu7Xjz7UNEpBUWEEBxDvP4bVFkZIN6NLGfQDpWbCjH8,98
261
272
  dodal/devices/zocalo/zocalo_interaction.py,sha256=GFukU9xqagQtVSDg5BrL23jxl1w8wjs4b4NLLqdFfpk,3584
262
273
  dodal/devices/zocalo/zocalo_results.py,sha256=8ZYkNB-By47Ag_p68iOq7jK8_P-LAsWPNs0m_DhnVEQ,12639
274
+ dodal/parameters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
263
275
  dodal/parameters/experiment_parameter_base.py,sha256=vONep-llSqhM0OpVj3zFUxdnzRQpu9y9mPhh2HauEJE,226
264
276
  dodal/plan_stubs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
265
277
  dodal/plan_stubs/check_topup.py,sha256=3gyLHfHNQBCgEWuAg4QE-ONx7y2Do1vVv5HP8ss0Z1I,5371
@@ -279,8 +291,8 @@ dodal/testing/__init__.py,sha256=AUYZKAvVOs7ZvxO1dVhL0pDTleRO34FQlO5MNe_cwgU,96
279
291
  dodal/testing/setup.py,sha256=8cQnrzE5MQD4Etf0eqMarmtr-opsUOMQww-k1V7DzIQ,2442
280
292
  dodal/testing/electron_analyser/__init__.py,sha256=-lc1opD2dCv0x678-J-ApOhHtvEvcslfOQ7E613U8-Y,118
281
293
  dodal/testing/electron_analyser/device_factory.py,sha256=tkMY6fW3iI02DTD1XXHi4lH6sjo8RHHZBGDHSuTdmNU,2243
282
- dls_dodal-1.60.0.dist-info/METADATA,sha256=ZayuvRHyJXBJ91LHyhhe137vN9naAJGFYm5NfbTgZ4E,16941
283
- dls_dodal-1.60.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
284
- dls_dodal-1.60.0.dist-info/entry_points.txt,sha256=bycw_EKUzup_rxfCetOwcauXV4kLln_OPpPT8jEnr-I,94
285
- dls_dodal-1.60.0.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
286
- dls_dodal-1.60.0.dist-info/RECORD,,
294
+ dls_dodal-1.61.0.dist-info/METADATA,sha256=CzbkDC58MYPwQdjxT6ArCdVqQWB9qWGQSHRUC8iWB7A,16941
295
+ dls_dodal-1.61.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
296
+ dls_dodal-1.61.0.dist-info/entry_points.txt,sha256=bycw_EKUzup_rxfCetOwcauXV4kLln_OPpPT8jEnr-I,94
297
+ dls_dodal-1.61.0.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
298
+ dls_dodal-1.61.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.60.0'
32
- __version_tuple__ = version_tuple = (1, 60, 0)
31
+ __version__ = version = '1.61.0'
32
+ __version_tuple__ = version_tuple = (1, 61, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -301,9 +301,68 @@ class UndulatorJawPhase(SafeUndulatorMover[float]):
301
301
  )
302
302
 
303
303
 
304
+ class Apple2Motors(StandardReadable, Movable):
305
+ """
306
+ Device representing the combined motor controls for an Apple2 undulator.
307
+
308
+ Attributes
309
+ ----------
310
+ gap : UndulatorGap
311
+ The undulator gap motor device.
312
+ phase : UndulatorPhaseAxes
313
+ The undulator phase axes device, consisting of four phase motors.
314
+ """
315
+
316
+ def __init__(self, id_gap: UndulatorGap, id_phase: UndulatorPhaseAxes, name=""):
317
+ """
318
+ Parameters
319
+ ----------
320
+
321
+ id_gap: UndulatorGap
322
+ An UndulatorGap device.
323
+ id_phase: UndulatorPhaseAxes
324
+ An UndulatorPhaseAxes device.
325
+ name: str
326
+ Name of the device.
327
+ """
328
+ with self.add_children_as_readables():
329
+ self.gap = id_gap
330
+ self.phase = id_phase
331
+ super().__init__(name=name)
332
+
333
+ @AsyncStatus.wrap
334
+ async def set(self, id_motor_values: Apple2Val) -> None:
335
+ """
336
+ Check ID is in a movable state and set all the demand value before moving them
337
+ all at the same time. This should be modified by the beamline specific ID
338
+ class, if the ID motors has to move in a specific order.
339
+ """
340
+
341
+ # Only need to check gap as the phase motors share both fault and gate with gap.
342
+ await self.gap.raise_if_cannot_move()
343
+ await asyncio.gather(
344
+ self.phase.top_outer.user_setpoint.set(value=id_motor_values.top_outer),
345
+ self.phase.top_inner.user_setpoint.set(value=id_motor_values.top_inner),
346
+ self.phase.btm_inner.user_setpoint.set(value=id_motor_values.btm_inner),
347
+ self.phase.btm_outer.user_setpoint.set(value=id_motor_values.btm_outer),
348
+ self.gap.user_setpoint.set(value=id_motor_values.gap),
349
+ )
350
+ timeout = np.max(
351
+ await asyncio.gather(self.gap.get_timeout(), self.phase.get_timeout())
352
+ )
353
+ LOGGER.info(
354
+ f"Moving f{self.name} apple2 motors to {id_motor_values}, timeout = {timeout}"
355
+ )
356
+ await asyncio.gather(
357
+ self.gap.set_move.set(value=1, wait=False, timeout=timeout),
358
+ self.phase.set_move.set(value=1, wait=False, timeout=timeout),
359
+ )
360
+ await wait_for_value(self.gap.gate, UndulatorGateStatus.CLOSE, timeout=timeout)
361
+
362
+
304
363
  class EnergyMotorConvertor(Protocol):
305
364
  def __call__(self, energy: float, pol: Pol) -> tuple[float, float]:
306
- """Protocol to provide energy to motor position convertion"""
365
+ """Protocol to provide energy to motor position conversion"""
307
366
  ...
308
367
 
309
368
 
@@ -318,15 +377,13 @@ class Apple2(abc.ABC, StandardReadable, Movable):
318
377
  The class is designed to manage the undulator's gap, phase motors, and polarisation settings, while
319
378
  abstracting hardware interactions and providing a high-level interface for beamline operations.
320
379
 
321
- The class is abstract and requires beamline-specific implementations for set motor
380
+ The class is abstract and requires beamline-specific implementations for _set motor
322
381
  positions based on energy and polarisation.
323
382
 
324
383
  Attributes
325
384
  ----------
326
- gap : UndulatorGap
327
- The gap control device for the undulator.
328
- phase : UndulatorPhaseAxes
329
- The phase control device, consisting of four phase motors.
385
+ apple2_motors : Apple2Motors
386
+ A collection of gap and phase motor devices.
330
387
  energy : SignalR
331
388
  A soft signal for the current energy readback.
332
389
  polarisation_setpoint : SignalR
@@ -340,7 +397,7 @@ class Apple2(abc.ABC, StandardReadable, Movable):
340
397
 
341
398
  Abstract Methods
342
399
  ----------------
343
- set(value: float) -> None
400
+ _set(value: float) -> None
344
401
  Abstract method to set motor positions for a given energy and polarisation.
345
402
 
346
403
  Methods
@@ -363,8 +420,7 @@ class Apple2(abc.ABC, StandardReadable, Movable):
363
420
 
364
421
  def __init__(
365
422
  self,
366
- id_gap: UndulatorGap,
367
- id_phase: UndulatorPhaseAxes,
423
+ apple2_motors: Apple2Motors,
368
424
  energy_motor_convertor: EnergyMotorConvertor,
369
425
  name: str = "",
370
426
  ) -> None:
@@ -378,8 +434,7 @@ class Apple2(abc.ABC, StandardReadable, Movable):
378
434
  name: Name of the device.
379
435
  """
380
436
 
381
- self.gap = id_gap
382
- self.phase = id_phase
437
+ self.motors = apple2_motors
383
438
  self.energy_to_motor = energy_motor_convertor
384
439
  with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
385
440
  # Store the set energy for readback.
@@ -398,11 +453,11 @@ class Apple2(abc.ABC, StandardReadable, Movable):
398
453
  raw_to_derived=self._read_pol,
399
454
  set_derived=self._set_pol,
400
455
  pol=self.polarisation_setpoint,
401
- top_outer=self.phase.top_outer.user_readback,
402
- top_inner=self.phase.top_inner.user_readback,
403
- btm_inner=self.phase.btm_inner.user_readback,
404
- btm_outer=self.phase.btm_outer.user_readback,
405
- gap=id_gap.user_readback,
456
+ top_outer=self.motors.phase.top_outer.user_readback,
457
+ top_inner=self.motors.phase.top_inner.user_readback,
458
+ btm_inner=self.motors.phase.btm_inner.user_readback,
459
+ btm_outer=self.motors.phase.btm_outer.user_readback,
460
+ gap=self.motors.gap.user_readback,
406
461
  )
407
462
  super().__init__(name)
408
463
 
@@ -420,24 +475,32 @@ class Apple2(abc.ABC, StandardReadable, Movable):
420
475
  self._set_pol_setpoint(value)
421
476
  await self.set(await self.energy.get_value())
422
477
 
423
- @abc.abstractmethod
424
478
  @AsyncStatus.wrap
425
479
  async def set(self, value: float) -> None:
426
480
  """
427
481
  Set should be in energy units, this will set the energy of the ID by setting the
428
482
  gap and phase motors to the correct position for the given energy
429
483
  and polarisation.
430
- This method should be implemented by the beamline specific ID class as the
431
- motor positions will be different for each beamline depending on the
432
- undulator design and the lookup table used.
433
- _set can be used to set the motor positions for the given energy and
434
- polarisation provided that all motors can be moved at the same time.
484
+
435
485
 
436
486
  Examples
437
487
  --------
438
488
  RE( id.set(888.0)) # This will set the ID to 888 eV
439
489
  RE(scan([detector], id,600,700,100)) # This will scan the ID from 600 to 700 eV in 100 steps.
440
490
  """
491
+ await self._set(value)
492
+ self._set_energy_rbv(value) # Update energy after move for readback.
493
+ LOGGER.info(f"Energy set to {value} eV successfully.")
494
+
495
+ @abc.abstractmethod
496
+ async def _set(self, value: float) -> None:
497
+ """
498
+ This method should be implemented by the beamline specific ID class as the
499
+ motor positions will be different for each beamline depending on the
500
+ undulator design and the lookup table used. The set method can be
501
+ used to set the motor positions for the given energy and polarisation
502
+ provided that all motors can be moved at the same time.
503
+ """
441
504
 
442
505
  def _read_pol(
443
506
  self,
@@ -468,36 +531,6 @@ class Apple2(abc.ABC, StandardReadable, Movable):
468
531
 
469
532
  return read_pol
470
533
 
471
- async def _set(self, value: Apple2Val, energy: float) -> None:
472
- """
473
- Check ID is in a movable state and set all the demand value before moving them
474
- all at the same time. This should be modified by the beamline specific ID class
475
- , if the ID motors has to move in a specific order.
476
- """
477
-
478
- # Only need to check gap as the phase motors share both fault and gate with gap.
479
- await self.gap.raise_if_cannot_move()
480
- await asyncio.gather(
481
- self.phase.top_outer.user_setpoint.set(value=value.top_outer),
482
- self.phase.top_inner.user_setpoint.set(value=value.top_inner),
483
- self.phase.btm_inner.user_setpoint.set(value=value.btm_inner),
484
- self.phase.btm_outer.user_setpoint.set(value=value.btm_outer),
485
- self.gap.user_setpoint.set(value=value.gap),
486
- )
487
- timeout = np.max(
488
- await asyncio.gather(self.gap.get_timeout(), self.phase.get_timeout())
489
- )
490
- LOGGER.info(
491
- f"Moving f{self.name} energy and polorisation to {energy}, {await self.polarisation.get_value()}"
492
- + f"with motor position {value}, timeout = {timeout}"
493
- )
494
- await asyncio.gather(
495
- self.gap.set_move.set(value=1, wait=False, timeout=timeout),
496
- self.phase.set_move.set(value=1, wait=False, timeout=timeout),
497
- )
498
- await wait_for_value(self.gap.gate, UndulatorGateStatus.CLOSE, timeout=timeout)
499
- self._set_energy_rbv(energy) # Update energy after move for readback.
500
-
501
534
  def determine_phase_from_hardware(
502
535
  self,
503
536
  top_outer: float,
File without changes
File without changes
File without changes
@@ -1,5 +1,5 @@
1
1
  from .base_detector import (
2
- AbstractElectronAnalyserDetector,
2
+ BaseElectronAnalyserDetector,
3
3
  )
4
4
  from .base_driver_io import AbstractAnalyserDriverIO, TAbstractAnalyserDriverIO
5
5
  from .base_region import (
@@ -19,7 +19,7 @@ __all__ = [
19
19
  "TAcquisitionMode",
20
20
  "TLensMode",
21
21
  "AbstractAnalyserDriverIO",
22
- "AbstractElectronAnalyserDetector",
22
+ "BaseElectronAnalyserDetector",
23
23
  "AbstractAnalyserDriverIO",
24
24
  "TAbstractAnalyserDriverIO",
25
25
  ]
@@ -1,4 +1,3 @@
1
- from abc import abstractmethod
2
1
  from typing import Generic
3
2
 
4
3
  from bluesky.protocols import Reading, Triggerable
@@ -9,14 +8,14 @@ from ophyd_async.core import (
9
8
  AsyncStatus,
10
9
  Device,
11
10
  )
11
+ from ophyd_async.epics.adcore import ADBaseController
12
12
 
13
- from dodal.devices.controllers import ConstantDeadTimeController
14
13
  from dodal.devices.electron_analyser.abstract.base_driver_io import (
15
14
  TAbstractAnalyserDriverIO,
16
15
  )
17
16
 
18
17
 
19
- class AbstractElectronAnalyserDetector(
18
+ class BaseElectronAnalyserDetector(
20
19
  Device,
21
20
  Triggerable,
22
21
  AsyncReadable,
@@ -34,43 +33,31 @@ class AbstractElectronAnalyserDetector(
34
33
 
35
34
  def __init__(
36
35
  self,
37
- driver: TAbstractAnalyserDriverIO,
36
+ controller: ADBaseController[TAbstractAnalyserDriverIO],
38
37
  name: str = "",
39
38
  ):
40
- self.controller = ConstantDeadTimeController(driver, 0)
39
+ self._controller = controller
41
40
  super().__init__(name)
42
41
 
43
42
  @AsyncStatus.wrap
44
43
  async def trigger(self) -> None:
45
- await self.controller.arm()
46
- await self.controller.wait_for_idle()
44
+ await self._controller.arm()
45
+ await self._controller.wait_for_idle()
47
46
 
48
47
  async def read(self) -> dict[str, Reading]:
49
- return await self.driver.read()
48
+ return await self._controller.driver.read()
50
49
 
51
50
  async def describe(self) -> dict[str, DataKey]:
52
- data = await self.driver.describe()
51
+ data = await self._controller.driver.describe()
53
52
  # Correct the shape for image
54
- prefix = self.driver.name + "-"
55
- energy_size = len(await self.driver.energy_axis.get_value())
56
- angle_size = len(await self.driver.angle_axis.get_value())
53
+ prefix = self._controller.driver.name + "-"
54
+ energy_size = len(await self._controller.driver.energy_axis.get_value())
55
+ angle_size = len(await self._controller.driver.angle_axis.get_value())
57
56
  data[prefix + "image"]["shape"] = [angle_size, energy_size]
58
57
  return data
59
58
 
60
59
  async def read_configuration(self) -> dict[str, Reading]:
61
- return await self.driver.read_configuration()
60
+ return await self._controller.driver.read_configuration()
62
61
 
63
62
  async def describe_configuration(self) -> dict[str, DataKey]:
64
- return await self.driver.describe_configuration()
65
-
66
- @property
67
- @abstractmethod
68
- def driver(self) -> TAbstractAnalyserDriverIO:
69
- """
70
- Define common property for all implementations to access the driver. Some
71
- implementations will store this as a reference so it doesn't have conflicting
72
- parents.
73
-
74
- Returns:
75
- instance of the driver.
76
- """
63
+ return await self._controller.driver.describe_configuration()
@@ -153,11 +153,12 @@ class AbstractAnalyserDriverIO(
153
153
  self.energy_source.selected_source.set(region.excitation_energy_source)
154
154
  excitation_energy = await self.energy_source.energy.get_value()
155
155
 
156
- # Copy region so doesn't alter the actual region and switch to kinetic energy
157
- ke_region = region.model_copy()
158
- ke_region.switch_energy_mode(EnergyMode.KINETIC, excitation_energy)
159
-
156
+ # Switch to kinetic energy as epics doesn't support BINDING.
157
+ ke_region = region.switch_energy_mode(EnergyMode.KINETIC, excitation_energy)
160
158
  await self._set_region(ke_region)
159
+ # Set the true energy mode from original region so binding_energy_axis can be
160
+ # calculated correctly.
161
+ await self.energy_mode.set(region.energy_mode)
161
162
 
162
163
  @abstractmethod
163
164
  async def _set_region(self, ke_region: TAbstractBaseRegion):
@@ -1,7 +1,7 @@
1
1
  import re
2
2
  from abc import ABC
3
3
  from collections.abc import Callable
4
- from typing import Generic, TypeVar
4
+ from typing import Generic, Self, TypeVar
5
5
 
6
6
  from pydantic import BaseModel, Field, model_validator
7
7
 
@@ -88,28 +88,43 @@ class AbstractBaseRegion(
88
88
  return self.energy_mode == EnergyMode.KINETIC
89
89
 
90
90
  def switch_energy_mode(
91
- self, energy_mode: EnergyMode, excitation_energy: float
92
- ) -> None:
91
+ self, energy_mode: EnergyMode, excitation_energy: float, copy: bool = True
92
+ ) -> Self:
93
93
  """
94
- Switch region to new energy mode: Kinetic or Binding. Updates the low_energy,
95
- centre_energy, high_energy, and energy_mode, only if it switches to a new one.
94
+ Switch region with to a new energy mode with a new energy mode: Kinetic or Binding.
95
+ It caculates new values for low_energy, centre_energy, high_energy, via the
96
+ excitation enerrgy. It doesn't calculate anything if the region is already of
97
+ the same energy mode.
96
98
 
97
99
  Parameters:
98
- energy_mode: mode you want to switch the region to.
99
- excitation_energy: the energy to calculate the new values of low_energy,
100
- centre_energy, and high_energy.
100
+ energy_mode: Mode you want to switch the region to.
101
+ excitation_energy: Energy conversion for low_energy, centre_energy, and
102
+ high_energy for new energy mode.
103
+ copy: Defaults to True. If true, create a copy of this region for the new
104
+ energy_mode and return it. If False, alter this region for the
105
+ energy_mode and return it self.
106
+
107
+ Returns:
108
+ Region with selected energy mode and new calculated energy values.
101
109
  """
110
+ switched_r = self.model_copy() if copy else self
102
111
  conv = (
103
112
  to_binding_energy
104
113
  if energy_mode == EnergyMode.BINDING
105
114
  else to_kinetic_energy
106
115
  )
107
- self.low_energy = conv(self.low_energy, self.energy_mode, excitation_energy)
108
- self.centre_energy = conv(
109
- self.centre_energy, self.energy_mode, excitation_energy
116
+ switched_r.low_energy = conv(
117
+ switched_r.low_energy, switched_r.energy_mode, excitation_energy
110
118
  )
111
- self.high_energy = conv(self.high_energy, self.energy_mode, excitation_energy)
112
- self.energy_mode = energy_mode
119
+ switched_r.centre_energy = conv(
120
+ switched_r.centre_energy, switched_r.energy_mode, excitation_energy
121
+ )
122
+ switched_r.high_energy = conv(
123
+ switched_r.high_energy, switched_r.energy_mode, excitation_energy
124
+ )
125
+ switched_r.energy_mode = energy_mode
126
+
127
+ return switched_r
113
128
 
114
129
  @model_validator(mode="before")
115
130
  @classmethod
@@ -1,14 +1,13 @@
1
1
  from typing import Generic, TypeVar
2
2
 
3
3
  from bluesky.protocols import Stageable
4
- from ophyd_async.core import (
5
- AsyncStatus,
6
- Reference,
7
- )
4
+ from ophyd_async.core import AsyncStatus
5
+ from ophyd_async.epics.adcore import ADBaseController
8
6
 
9
7
  from dodal.common.data_util import load_json_file_to_class
8
+ from dodal.devices.controllers import ConstantDeadTimeController
10
9
  from dodal.devices.electron_analyser.abstract.base_detector import (
11
- AbstractElectronAnalyserDetector,
10
+ BaseElectronAnalyserDetector,
12
11
  )
13
12
  from dodal.devices.electron_analyser.abstract.base_driver_io import (
14
13
  TAbstractAnalyserDriverIO,
@@ -20,35 +19,27 @@ from dodal.devices.electron_analyser.abstract.base_region import (
20
19
 
21
20
 
22
21
  class ElectronAnalyserRegionDetector(
23
- AbstractElectronAnalyserDetector[TAbstractAnalyserDriverIO],
22
+ BaseElectronAnalyserDetector[TAbstractAnalyserDriverIO],
24
23
  Generic[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
25
24
  ):
26
25
  """
27
26
  Extends electron analyser detector to configure specific region settings before data
28
- acqusition. This object must be passed in a driver and store it as a reference. It
29
- is designed to only exist inside a plan.
27
+ acquisition. It is designed to only exist inside a plan.
30
28
  """
31
29
 
32
30
  def __init__(
33
31
  self,
34
- driver: TAbstractAnalyserDriverIO,
32
+ controller: ADBaseController[TAbstractAnalyserDriverIO],
35
33
  region: TAbstractBaseRegion,
36
34
  name: str = "",
37
35
  ):
38
- self._driver_ref = Reference(driver)
39
36
  self.region = region
40
- super().__init__(driver, name)
41
-
42
- @property
43
- def driver(self) -> TAbstractAnalyserDriverIO:
44
- # Store as a reference, this implementation will be given a driver so needs to
45
- # make sure we don't get conflicting parents.
46
- return self._driver_ref()
37
+ super().__init__(controller, name)
47
38
 
48
39
  @AsyncStatus.wrap
49
40
  async def trigger(self) -> None:
50
41
  # Configure region parameters on the driver first before data collection.
51
- await self.driver.set(self.region)
42
+ await self._controller.driver.set(self.region)
52
43
  await super().trigger()
53
44
 
54
45
 
@@ -59,7 +50,7 @@ TElectronAnalyserRegionDetector = TypeVar(
59
50
 
60
51
 
61
52
  class ElectronAnalyserDetector(
62
- AbstractElectronAnalyserDetector[TAbstractAnalyserDriverIO],
53
+ BaseElectronAnalyserDetector[TAbstractAnalyserDriverIO],
63
54
  Stageable,
64
55
  Generic[
65
56
  TAbstractAnalyserDriverIO,
@@ -79,16 +70,11 @@ class ElectronAnalyserDetector(
79
70
  driver: TAbstractAnalyserDriverIO,
80
71
  name: str = "",
81
72
  ):
82
- # Pass in driver
83
- self._driver = driver
73
+ # Save driver as direct child so participates with connect()
74
+ self.driver = driver
84
75
  self._sequence_class = sequence_class
85
- super().__init__(self.driver, name)
86
-
87
- @property
88
- def driver(self) -> TAbstractAnalyserDriverIO:
89
- # This implementation creates the driver and wants this to be the parent so it
90
- # can be used with connect() method.
91
- return self._driver
76
+ controller = ConstantDeadTimeController[TAbstractAnalyserDriverIO](driver, 0)
77
+ super().__init__(controller, name)
92
78
 
93
79
  @AsyncStatus.wrap
94
80
  async def stage(self) -> None:
@@ -103,13 +89,13 @@ class ElectronAnalyserDetector(
103
89
  Raises:
104
90
  Any exceptions raised by the driver's stage or controller's disarm methods.
105
91
  """
106
- await self.controller.disarm()
92
+ await self._controller.disarm()
107
93
  await self.driver.stage()
108
94
 
109
95
  @AsyncStatus.wrap
110
96
  async def unstage(self) -> None:
111
97
  """Disarm the detector."""
112
- await self.controller.disarm()
98
+ await self._controller.disarm()
113
99
  await self.driver.unstage()
114
100
 
115
101
  def load_sequence(self, filename: str) -> TAbstractBaseSequence:
@@ -144,7 +130,9 @@ class ElectronAnalyserDetector(
144
130
  seq = self.load_sequence(filename)
145
131
  regions = seq.get_enabled_regions() if enabled_only else seq.regions
146
132
  return [
147
- ElectronAnalyserRegionDetector(self.driver, r, self.name + "_" + r.name)
133
+ ElectronAnalyserRegionDetector(
134
+ self._controller, r, self.name + "_" + r.name
135
+ )
148
136
  for r in regions
149
137
  ]
150
138
 
@@ -66,7 +66,6 @@ class SpecsAnalyserDriverIO(
66
66
  async def _set_region(self, ke_region: SpecsRegion[TLensMode, TPsuMode]):
67
67
  await asyncio.gather(
68
68
  self.region_name.set(ke_region.name),
69
- self.energy_mode.set(ke_region.energy_mode),
70
69
  self.low_energy.set(ke_region.low_energy),
71
70
  self.high_energy.set(ke_region.high_energy),
72
71
  self.slices.set(ke_region.slices),
@@ -74,7 +74,6 @@ class VGScientaAnalyserDriverIO(
74
74
  async def _set_region(self, ke_region: VGScientaRegion[TLensMode, TPassEnergyEnum]):
75
75
  await asyncio.gather(
76
76
  self.region_name.set(ke_region.name),
77
- self.energy_mode.set(ke_region.energy_mode),
78
77
  self.low_energy.set(ke_region.low_energy),
79
78
  self.centre_energy.set(ke_region.centre_energy),
80
79
  self.high_energy.set(ke_region.high_energy),
@@ -296,15 +296,15 @@ class FastGridScanThreeD(FastGridScanCommon[ParamType]):
296
296
  Subclasses must implement _create_position_counter.
297
297
  """
298
298
 
299
- def __init__(self, prefix: str, name: str = "") -> None:
300
- full_prefix = prefix + "FGS:"
299
+ def __init__(self, prefix: str, infix: str, name: str = "") -> None:
300
+ full_prefix = prefix + infix
301
301
 
302
302
  # Number of vertical steps during the second grid scan, after the rotation in omega
303
- self.z_steps = epics_signal_rw_rbv(int, f"{prefix}Z_NUM_STEPS")
304
- self.z_step_size = epics_signal_rw_rbv(float, f"{prefix}Z_STEP_SIZE")
305
- self.z2_start = epics_signal_rw_rbv(float, f"{prefix}Z2_START")
306
- self.y2_start = epics_signal_rw_rbv(float, f"{prefix}Y2_START")
307
- self.x_counter = epics_signal_r(int, f"{full_prefix}X_COUNTER")
303
+ self.z_steps = epics_signal_rw_rbv(int, f"{full_prefix}Z_NUM_STEPS")
304
+ self.z_step_size = epics_signal_rw_rbv(float, f"{full_prefix}Z_STEP_SIZE")
305
+ self.z2_start = epics_signal_rw_rbv(float, f"{full_prefix}Z2_START")
306
+ self.y2_start = epics_signal_rw_rbv(float, f"{full_prefix}Y2_START")
307
+ # panda does not have x counter
308
308
  self.y_counter = epics_signal_r(int, f"{full_prefix}Y_COUNTER")
309
309
 
310
310
  super().__init__(full_prefix, prefix, name)
@@ -343,10 +343,12 @@ class ZebraFastGridScanThreeD(FastGridScanThreeD[ZebraGridScanParamsThreeD]):
343
343
  """
344
344
 
345
345
  def __init__(self, prefix: str, name: str = "") -> None:
346
- full_prefix = prefix + "FGS:"
346
+ infix = "FGS:"
347
+ full_prefix = prefix + infix
347
348
  # Time taken to travel between X steps
348
349
  self.dwell_time_ms = epics_signal_rw_rbv(float, f"{full_prefix}DWELL_TIME")
349
- super().__init__(prefix, name)
350
+ self.x_counter = epics_signal_r(int, f"{full_prefix}X_COUNTER")
351
+ super().__init__(prefix, infix, name)
350
352
  self.movable_params["dwell_time_ms"] = self.dwell_time_ms
351
353
 
352
354
  def _create_position_counter(self, prefix: str):
@@ -363,7 +365,8 @@ class PandAFastGridScan(FastGridScanThreeD[PandAGridScanParams]):
363
365
  """
364
366
 
365
367
  def __init__(self, prefix: str, name: str = "") -> None:
366
- full_prefix = prefix + "PGS:"
368
+ infix = "PGS:"
369
+ full_prefix = prefix + infix
367
370
  self.time_between_x_steps_ms = (
368
371
  epics_signal_rw_rbv( # Used by motion controller to set goniometer velocity
369
372
  float, f"{full_prefix}TIME_BETWEEN_X_STEPS"
@@ -375,7 +378,7 @@ class PandAFastGridScan(FastGridScanThreeD[PandAGridScanParams]):
375
378
  self.run_up_distance_mm = epics_signal_rw_rbv(
376
379
  float, f"{full_prefix}RUNUP_DISTANCE"
377
380
  )
378
- super().__init__(prefix, name)
381
+ super().__init__(prefix, infix, name)
379
382
 
380
383
  self.movable_params["run_up_distance_mm"] = self.run_up_distance_mm
381
384
 
@@ -32,6 +32,7 @@ class MurkoMetadata(TypedDict):
32
32
  sample_id: str
33
33
  omega_angle: float
34
34
  uuid: str
35
+ used_for_centring: bool | None
35
36
 
36
37
 
37
38
  class Coord(Enum):
@@ -47,6 +48,7 @@ class MurkoResult:
47
48
  y_dist_mm: float
48
49
  omega: float
49
50
  uuid: str
51
+ metadata: MurkoMetadata
50
52
 
51
53
 
52
54
  class NoResultsFound(ValueError):
@@ -101,7 +103,7 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
101
103
 
102
104
  def _reset(self):
103
105
  self._last_omega = 0
104
- self.results: list[MurkoResult] = []
106
+ self._results: list[MurkoResult] = []
105
107
 
106
108
  @AsyncStatus.wrap
107
109
  async def stage(self):
@@ -126,17 +128,17 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
126
128
  continue
127
129
  await self.process_batch(message, sample_id)
128
130
 
129
- if not self.results:
131
+ if not self._results:
130
132
  raise NoResultsFound("No results retrieved from Murko")
131
133
 
132
- for result in self.results:
134
+ for result in self._results:
133
135
  LOGGER.debug(result)
134
136
 
135
- self.filter_outliers()
137
+ filtered_results = self.filter_outliers()
136
138
 
137
- x_dists_mm = [result.x_dist_mm for result in self.results]
138
- y_dists_mm = [result.y_dist_mm for result in self.results]
139
- omegas = [result.omega for result in self.results]
139
+ x_dists_mm = [result.x_dist_mm for result in filtered_results]
140
+ y_dists_mm = [result.y_dist_mm for result in filtered_results]
141
+ omegas = [result.omega for result in filtered_results]
140
142
 
141
143
  LOGGER.info(f"Using average of x beam distances: {x_dists_mm}")
142
144
  avg_x = float(np.mean(x_dists_mm))
@@ -147,6 +149,11 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
147
149
  self._y_mm_setter(-best_y)
148
150
  self._z_mm_setter(-best_z)
149
151
 
152
+ for result in self._results:
153
+ await self.redis_client.hset( # type: ignore
154
+ f"murko:{sample_id}:metadata", result.uuid, json.dumps(result.metadata)
155
+ )
156
+
150
157
  async def process_batch(self, message: dict | None, sample_id: str):
151
158
  if message and message["type"] == "message":
152
159
  batch_results: list[dict] = pickle.loads(message["data"])
@@ -186,13 +193,14 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
186
193
  centre_px[0],
187
194
  centre_px[1],
188
195
  )
189
- self.results.append(
196
+ self._results.append(
190
197
  MurkoResult(
191
198
  centre_px=centre_px,
192
199
  x_dist_mm=beam_dist_px[0] * metadata["microns_per_x_pixel"] / 1000,
193
200
  y_dist_mm=beam_dist_px[1] * metadata["microns_per_y_pixel"] / 1000,
194
201
  omega=omega,
195
202
  uuid=metadata["uuid"],
203
+ metadata=metadata,
196
204
  )
197
205
  )
198
206
  self._last_omega = omega
@@ -203,8 +211,8 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
203
211
  meaning that by keeping only a percentage of the results with the smallest X we
204
212
  remove many of the outliers.
205
213
  """
206
- LOGGER.info(f"Number of results before filtering: {len(self.results)}")
207
- sorted_results = sorted(self.results, key=lambda item: item.centre_px[0])
214
+ LOGGER.info(f"Number of results before filtering: {len(self._results)}")
215
+ sorted_results = sorted(self._results, key=lambda item: item.centre_px[0])
208
216
 
209
217
  worst_results = [
210
218
  r.uuid for r in sorted_results[-self.NUMBER_OF_WRONG_RESULTS_TO_LOG :]
@@ -214,9 +222,13 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
214
222
  f"Worst {self.NUMBER_OF_WRONG_RESULTS_TO_LOG} murko results were {worst_results}"
215
223
  )
216
224
  cutoff = max(1, int(len(sorted_results) * self.PERCENTAGE_TO_USE / 100))
225
+ for i, result in enumerate(sorted_results):
226
+ result.metadata["used_for_centring"] = i < cutoff
227
+
217
228
  smallest_x = sorted_results[:cutoff]
218
- self.results = smallest_x
219
- LOGGER.info(f"Number of results after filtering: {len(self.results)}")
229
+
230
+ LOGGER.info(f"Number of results after filtering: {len(smallest_x)}")
231
+ return smallest_x
220
232
 
221
233
 
222
234
  def get_yz_least_squares(vertical_dists: list, omegas: list) -> tuple[float, float]:
@@ -19,10 +19,9 @@ from ophyd_async.core import (
19
19
  )
20
20
  from pydantic import BaseModel, ConfigDict, RootModel
21
21
 
22
- from dodal.log import LOGGER
23
-
24
- from ..apple2_undulator import (
22
+ from dodal.devices.apple2_undulator import (
25
23
  Apple2,
24
+ Apple2Motors,
26
25
  Apple2Val,
27
26
  EnergyMotorConvertor,
28
27
  Pol,
@@ -30,6 +29,8 @@ from ..apple2_undulator import (
30
29
  UndulatorJawPhase,
31
30
  UndulatorPhaseAxes,
32
31
  )
32
+ from dodal.log import LOGGER
33
+
33
34
  from ..pgm import PGM
34
35
 
35
36
  ROW_PHASE_MOTOR_TOLERANCE = 0.004
@@ -359,14 +360,15 @@ class I10Apple2(Apple2):
359
360
 
360
361
  with self.add_children_as_readables():
361
362
  super().__init__(
362
- id_gap=UndulatorGap(name="id_gap", prefix=prefix),
363
- id_phase=UndulatorPhaseAxes(
364
- name="id_phase",
365
- prefix=prefix,
366
- top_outer="RPQ1",
367
- top_inner="RPQ2",
368
- btm_inner="RPQ3",
369
- btm_outer="RPQ4",
363
+ apple2_motors=Apple2Motors(
364
+ id_gap=UndulatorGap(prefix=prefix),
365
+ id_phase=UndulatorPhaseAxes(
366
+ prefix=prefix,
367
+ top_outer="RPQ1",
368
+ top_inner="RPQ2",
369
+ btm_inner="RPQ3",
370
+ btm_outer="RPQ4",
371
+ ),
370
372
  ),
371
373
  energy_motor_convertor=energy_motor_convertor,
372
374
  name=name,
@@ -376,8 +378,7 @@ class I10Apple2(Apple2):
376
378
  move_pv="RPQ1",
377
379
  )
378
380
 
379
- @AsyncStatus.wrap
380
- async def set(self, value: float) -> None:
381
+ async def _set(self, value: float) -> None:
381
382
  """
382
383
  Check polarisation state and use it together with the energy(value)
383
384
  to calculate the required gap and phases before setting it.
@@ -408,11 +409,10 @@ class I10Apple2(Apple2):
408
409
  )
409
410
 
410
411
  LOGGER.info(f"Setting polarisation to {pol}, with values: {id_set_val}")
411
- await self._set(value=id_set_val, energy=value)
412
+ await self.motors.set(id_motor_values=id_set_val)
412
413
  if pol != Pol.LA:
413
414
  await self.id_jaw_phase.set(0)
414
415
  await self.id_jaw_phase.set_move.set(1)
415
- LOGGER.info(f"Energy set to {value} eV successfully.")
416
416
 
417
417
 
418
418
  class EnergySetter(StandardReadable, Movable[float]):
File without changes
File without changes
File without changes
@@ -5,11 +5,7 @@ from ophyd_async.epics.motor import Motor
5
5
  class FocusingMirrorBase(StandardReadable):
6
6
  """Focusing Mirror with curve, ellip & pitch"""
7
7
 
8
- def __init__(
9
- self,
10
- prefix: str,
11
- name: str = "",
12
- ):
8
+ def __init__(self, prefix: str, name: str = ""):
13
9
  with self.add_children_as_readables():
14
10
  self.curve = Motor(prefix + "CURVE")
15
11
  self.ellipticity = Motor(prefix + "ELLIP")
@@ -21,11 +17,7 @@ class FocusingMirrorBase(StandardReadable):
21
17
  class FocusingMirrorHorizontal(FocusingMirrorBase):
22
18
  """Focusing Mirror with curve, ellip, pitch & X"""
23
19
 
24
- def __init__(
25
- self,
26
- prefix: str,
27
- name: str = "",
28
- ):
20
+ def __init__(self, prefix: str, name: str = ""):
29
21
  with self.add_children_as_readables():
30
22
  self.x = Motor(prefix + "X")
31
23
 
@@ -35,11 +27,7 @@ class FocusingMirrorHorizontal(FocusingMirrorBase):
35
27
  class FocusingMirrorVertical(FocusingMirrorBase):
36
28
  """Focusing Mirror with curve, ellip, pitch & Y"""
37
29
 
38
- def __init__(
39
- self,
40
- prefix: str,
41
- name: str = "",
42
- ):
30
+ def __init__(self, prefix: str, name: str = ""):
43
31
  with self.add_children_as_readables():
44
32
  self.y = Motor(prefix + "Y")
45
33
 
@@ -49,11 +37,7 @@ class FocusingMirrorVertical(FocusingMirrorBase):
49
37
  class FocusingMirror(FocusingMirrorBase):
50
38
  """Focusing Mirror with curve, ellip, pitch, yaw, X & Y"""
51
39
 
52
- def __init__(
53
- self,
54
- prefix: str,
55
- name: str = "",
56
- ):
40
+ def __init__(self, prefix: str, name: str = ""):
57
41
  with self.add_children_as_readables():
58
42
  self.yaw = Motor(prefix + "YAW")
59
43
  self.x = Motor(prefix + "X")
dodal/devices/i15/jack.py CHANGED
@@ -5,11 +5,7 @@ from ophyd_async.epics.motor import Motor
5
5
  class JackX(StandardReadable):
6
6
  """Focusing Mirror"""
7
7
 
8
- def __init__(
9
- self,
10
- prefix: str,
11
- name: str = "",
12
- ):
8
+ def __init__(self, prefix: str, name: str = ""):
13
9
  with self.add_children_as_readables():
14
10
  self.rotation = Motor(prefix + "Ry")
15
11
  self.transx = Motor(prefix + "X")
@@ -23,11 +19,7 @@ class JackX(StandardReadable):
23
19
  class JackY(StandardReadable):
24
20
  """Focusing Mirror"""
25
21
 
26
- def __init__(
27
- self,
28
- prefix: str,
29
- name: str = "",
30
- ):
22
+ def __init__(self, prefix: str, name: str = ""):
31
23
  with self.add_children_as_readables():
32
24
  self.j1 = Motor(prefix + "J1")
33
25
  self.j2 = Motor(prefix + "J2")
dodal/devices/i15/laue.py CHANGED
@@ -3,11 +3,7 @@ from ophyd_async.epics.motor import Motor
3
3
 
4
4
 
5
5
  class LaueMonochrometer(StandardReadable):
6
- def __init__(
7
- self,
8
- prefix: str,
9
- name: str = "",
10
- ):
6
+ def __init__(self, prefix: str, name: str = ""):
11
7
  with self.add_children_as_readables():
12
8
  self.bend = Motor(prefix + "BENDER")
13
9
  self.bragg = Motor(prefix + "PITCH")
@@ -5,11 +5,7 @@ from ophyd_async.epics.motor import Motor
5
5
  class MultiLayerMirror(StandardReadable):
6
6
  """Multilayer Mirror"""
7
7
 
8
- def __init__(
9
- self,
10
- prefix: str,
11
- name: str = "",
12
- ):
8
+ def __init__(self, prefix: str, name: str = ""):
13
9
  with self.add_children_as_readables():
14
10
  self.ds_x = Motor(prefix + "X2")
15
11
  self.ds_y = Motor(prefix + "J3")
dodal/devices/i15/rail.py CHANGED
@@ -3,11 +3,7 @@ from ophyd_async.epics.motor import Motor
3
3
 
4
4
 
5
5
  class Rail(StandardReadable):
6
- def __init__(
7
- self,
8
- prefix: str,
9
- name: str = "",
10
- ):
6
+ def __init__(self, prefix: str, name: str = ""):
11
7
  with self.add_children_as_readables():
12
8
  self.pitch = Motor(prefix + "PITCH")
13
9
  self.y = Motor(prefix + "Y")
File without changes
File without changes
@@ -5,11 +5,13 @@ from pathlib import Path
5
5
  from bluesky.protocols import StreamAsset
6
6
  from event_model import DataKey # type: ignore
7
7
  from ophyd_async.core import (
8
+ AsyncStatus,
8
9
  AutoIncrementingPathProvider,
9
10
  DetectorWriter,
10
11
  StandardDetector,
11
12
  StandardReadable,
12
13
  StaticPathProvider,
14
+ TriggerInfo,
13
15
  observe_value,
14
16
  wait_for_value,
15
17
  )
@@ -40,6 +42,7 @@ class JunfrauCommissioningWriter(DetectorWriter, StandardReadable):
40
42
  self.file_name = epics_signal_rw_rbv(str, f"{prefix}FileName")
41
43
  self.file_path = epics_signal_rw_rbv(str, f"{prefix}FilePath")
42
44
  self.writer_ready = epics_signal_r(int, f"{prefix}Ready_RBV")
45
+ self.expected_frames = epics_signal_rw(int, f"{prefix}NumCapture")
43
46
  super().__init__(name)
44
47
 
45
48
  async def open(self, name: str, exposures_per_event: int = 1) -> dict[str, DataKey]:
@@ -80,7 +83,7 @@ class JunfrauCommissioningWriter(DetectorWriter, StandardReadable):
80
83
  async def observe_indices_written(
81
84
  self, timeout: float
82
85
  ) -> AsyncGenerator[int, None]:
83
- timeout = timeout * 2 # This filewriter is slow
86
+ timeout = timeout * 4 # This filewriter is very slow
84
87
  async for num_captured in observe_value(self.frame_counter, timeout):
85
88
  yield num_captured // (self._exposures_per_event)
86
89
 
@@ -112,3 +115,8 @@ class CommissioningJungfrau(
112
115
  writer = JunfrauCommissioningWriter(writer_prefix, path_provider)
113
116
  controller = JungfrauController(self.drv)
114
117
  super().__init__(controller, writer, name=name)
118
+
119
+ @AsyncStatus.wrap
120
+ async def prepare(self, value: TriggerInfo) -> None:
121
+ await super().prepare(value)
122
+ await self._writer.expected_frames.set(value.total_number_of_exposures)
File without changes
File without changes
File without changes
File without changes