dls-dodal 1.37.0__py3-none-any.whl → 1.38.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.2
2
2
  Name: dls-dodal
3
- Version: 1.37.0
3
+ Version: 1.38.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>
6
6
  License: Apache License
@@ -237,6 +237,7 @@ Requires-Dist: scanspec>=0.7.3
237
237
  Provides-Extra: dev
238
238
  Requires-Dist: black; extra == "dev"
239
239
  Requires-Dist: diff-cover; extra == "dev"
240
+ Requires-Dist: import-linter; extra == "dev"
240
241
  Requires-Dist: mypy; extra == "dev"
241
242
  Requires-Dist: myst-parser; extra == "dev"
242
243
  Requires-Dist: ophyd_async[sim]; extra == "dev"
@@ -1,6 +1,6 @@
1
1
  dodal/__init__.py,sha256=Ksms_WJF8LTkbm38gEpm1jBpGqcQ8NGvmb2ZJlOE1j8,198
2
2
  dodal/__main__.py,sha256=kP2S2RPitnOWpNGokjZ1Yq-1umOtp5sNOZk2B3tBPLM,111
3
- dodal/_version.py,sha256=G8h3RZ-rmov-shRSPQyyHBDC9-SRuF1mCqQtZVgfOsc,413
3
+ dodal/_version.py,sha256=j09VNHKdlg-z35f-tcp_3JKPr5SFOeDelj6hrHQ_smI,413
4
4
  dodal/cli.py,sha256=NieWNUgLUxyck1rHoFAPJjX1xXLzHNdQ-s4wvxYFfps,3757
5
5
  dodal/log.py,sha256=ry8WMq1S4WMIAPqtqGeKuegMRN7Jy3qdVTJlkpKXkL8,9503
6
6
  dodal/utils.py,sha256=h2sNmTlsaznfxusV1Xj_mXtNjzsWjWAgmps6I0YNA3U,18097
@@ -11,18 +11,18 @@ dodal/beamlines/__init__.py,sha256=FsS1hMz9nqwTP12UtCJsfVn712mFElcBq2kKKpscp9k,3
11
11
  dodal/beamlines/adsim.py,sha256=DUAFS1ueoZ6DK2cmZkiEm3NElnaro1mUvyodv14pSmU,1839
12
12
  dodal/beamlines/b01_1.py,sha256=0gLjg0O9ttMjHzszSyJ_GT3fnoAB6u4aJ4MdAfjJbHA,1788
13
13
  dodal/beamlines/i02_1.py,sha256=d2IyqFMgeaSEyZYm7GMSjTKr7_02SakyC_oARx-XwnY,1204
14
- dodal/beamlines/i03.py,sha256=YMbZFpTWEYO3o0qrIbMqQEO8rDTaXqClIdrvdi45oS4,18651
15
- dodal/beamlines/i04.py,sha256=pZvMEm4sHNQjGPEJowGNJn0NRzh6K2clGcj24BBpgVM,14614
14
+ dodal/beamlines/i03.py,sha256=kAyStqsODSzg0kr27X63jm_nikWMCserMjJEOzRH2F4,19002
15
+ dodal/beamlines/i04.py,sha256=3_cMBAnRuLkzC9ELf0-a-Rq2c76YPNTIJomHqIjys5U,14926
16
16
  dodal/beamlines/i10.py,sha256=lkn_xg0pt-vFuWkUGyl62A0xT-Rzs71JztJ1EeQkMi0,11487
17
- dodal/beamlines/i13_1.py,sha256=csXHrdwUh4sXTmb4X6ZiiSS_XxRkNShsVoBMxYI6rG0,1833
17
+ dodal/beamlines/i13_1.py,sha256=MYl2TLomSXvAdEAEMWbNfEIcX46elaugbKtkehLvB8Q,2371
18
18
  dodal/beamlines/i20_1.py,sha256=MaPgONHqpoZuBtkiKEzYtViJnKBM2_ekeP4OdbmuXHE,1158
19
19
  dodal/beamlines/i22.py,sha256=ED6TtjwZzGn0Hot1WSgUhdOQ4amLDjkse5hBqEBD6Zg,7619
20
20
  dodal/beamlines/i23.py,sha256=2j5qLoqE_hg9ETHqNkOVu7LLkVB8qalgXeORnVYKN_I,1075
21
- dodal/beamlines/i24.py,sha256=IXujBTX_O0zEL0sBVk0DDerHBZN84dYP0aMEEtbH9RA,8550
21
+ dodal/beamlines/i24.py,sha256=tao1YIZS0amhS8EHYOQ7-22twmPujC8fAvcGncDsm0A,8851
22
22
  dodal/beamlines/p38.py,sha256=JJbclLYoRdIxcpzpW4oTj77YJ001CdEAM0bKRk7seYI,8735
23
23
  dodal/beamlines/p45.py,sha256=N4SDTIFok3uMqb37higZHMr3xRjxItsT4ib_KacKKAE,2935
24
24
  dodal/beamlines/p99.py,sha256=k24QhYpoOHBd0188Fu3wvmpT6dsu8okiIVqVVckdBkw,1063
25
- dodal/beamlines/training_rig.py,sha256=qAsUV4X1hxQFIjuQG92UMwYtE0nu1_hL4cP1zSiCe3Y,1698
25
+ dodal/beamlines/training_rig.py,sha256=74lrNa_qNksA1eOsSZnhhcwAXcMJdw4fBc0a-w4IV5M,1897
26
26
  dodal/common/__init__.py,sha256=ZC4ICKUDB0BDxRaVy8nmqclVmDBne-dPtk6UJsoFq6I,258
27
27
  dodal/common/coordination.py,sha256=OxIjDiO1-9A9KESRPFtzwkvvQlavbgA5RHemlbubBPg,1168
28
28
  dodal/common/crystal_metadata.py,sha256=XGr-X81G9SZvPx5b4nBCH4FOnywyX_zYVy6zwDxIMVM,1926
@@ -50,19 +50,19 @@ dodal/devices/eiger.py,sha256=VGGgkSz8AzSOP3rpehs2GwPHnOGz0OKsRP6U_FYbBA4,15337
50
50
  dodal/devices/eiger_odin.py,sha256=ytUH_18YuM1nJDhplS6OTdtADloYvHpO6ppENjVd4jU,7411
51
51
  dodal/devices/fast_grid_scan.py,sha256=SNwbLuSJ6YnZ-Qx6t6veLZtStcrPNslRGrw8nouFx9g,11259
52
52
  dodal/devices/fluorescence_detector_motion.py,sha256=-1qCSvW0PdT0m6BcoLxrtc0OJ5UDIBsEe11EOLr-gFw,501
53
- dodal/devices/flux.py,sha256=RtPStHw7Mad0igVKntKWVZfuZn2clokVJqH14HLix6M,198
53
+ dodal/devices/flux.py,sha256=1CDsq9yU2-ho8MfYBl50Tl9ABZwpUBHnV486PQXKNoQ,462
54
54
  dodal/devices/focusing_mirror.py,sha256=vdUPkwyCAZBSR3LQ-EojDOoxVy1ZmOaD_nevETbj7BA,6592
55
55
  dodal/devices/hutch_shutter.py,sha256=WXY9JwqAa5prbf72IP7_MTKndPDtAltCpPJlNbq-F_0,3313
56
56
  dodal/devices/ipin.py,sha256=eq5jlKw7WGQi8VLrAWpaAIsZmfiVf-5Q0td_B22H6A4,473
57
57
  dodal/devices/linkam3.py,sha256=2sf-_heIsDg4qmqae-w9C2Py8pG8bPB3mT0TFPQIzd0,3869
58
58
  dodal/devices/logging_ophyd_device.py,sha256=dUVE-XhWA56WUXez0mrc4sf322CXY3MVLreTycO5j_A,668
59
59
  dodal/devices/motors.py,sha256=mduFm9vTZfu9rhwL93AMZpDzGd2TASdqalWzRaMoqec,1114
60
- dodal/devices/p45.py,sha256=jzBW2fGRhIbGzSRs5Fgupxro6aqE611n1RTcrTTG-yY,1047
60
+ dodal/devices/p45.py,sha256=hNCfWb8xubuHO8uJ5MpHPurp-r1ss7JMgbZQwHVJg-w,1743
61
61
  dodal/devices/pgm.py,sha256=am-AST9iTqma1PkGOKLozqAokZWbJUbM3TNcqXzB-6A,1132
62
62
  dodal/devices/pressure_jump_cell.py,sha256=ONvSHpnS-kA8hjOgyIks-J2n02pP6LhLm2SY1zC-i_8,9437
63
63
  dodal/devices/qbpm.py,sha256=FfrWWAHHtYv3fGRT1qljyPpAwoHJYfbooT9CfKg-oXI,465
64
64
  dodal/devices/robot.py,sha256=Pw6k8rFGr2-hkM2TVkQw2Y_dYIdXdXN7QtqjSglkp3g,5289
65
- dodal/devices/s4_slit_gaps.py,sha256=j3kgF9WfGFaU9xdUuiAh-QqI5u_vhiAftaDVINt91SM,243
65
+ dodal/devices/s4_slit_gaps.py,sha256=4KdarIQoRqX4ry3LUS1Km7fkjUFahA0VuTd2DvYEqQ8,446
66
66
  dodal/devices/scatterguard.py,sha256=jx03in9QgaThWxD4t1S8_Llent2kWrn_hThJ9KkUWTk,330
67
67
  dodal/devices/scintillator.py,sha256=PlD6cnJ39PTB_e7QrRspPliLYE4kL_K7ziJURzuxgdA,365
68
68
  dodal/devices/slits.py,sha256=b_7ku2sHlzhMHTvWrwiRwee6ufrbxNX9JB_Z0lvk15o,1105
@@ -77,8 +77,6 @@ dodal/devices/undulator_dcm.py,sha256=zulVn1wvu4-WEpf0BgLiDet58IsaKV7KW6qruAdRR_
77
77
  dodal/devices/watsonmarlow323_pump.py,sha256=rwU94YE6esgGLYdh-pe8nBo_3tvgp6brrrbPDrqp5_M,1406
78
78
  dodal/devices/webcam.py,sha256=mef075ynDbzZ4pNAjfxR_9tdTTqF_rM7hAOVEEOV-Do,2408
79
79
  dodal/devices/xbpm_feedback.py,sha256=j8MHhhE0feoe6R54zPKqS5EbQ0bEDR-nOpLHzHhnHHQ,1156
80
- dodal/devices/zebra.py,sha256=sPMAKaZOrBHQy5V9ue4-TKE9r29OG8nTZCzQQC7OMFQ,9637
81
- dodal/devices/zebra_controlled_shutter.py,sha256=5-SH5HoXp_6P-xAtfDFJKQq6mBDwreubuCULSz78fgw,1852
82
80
  dodal/devices/areadetector/plugins/CAM.py,sha256=sZzJm5Ez3eWfXZi_EB67wluhZmMQm1UyOc2bJFfzd1U,964
83
81
  dodal/devices/areadetector/plugins/MJPG.py,sha256=QTsxCoWbofNpLMGPoOR2hWoM33KyntuLepbF0YmX0KE,3031
84
82
  dodal/devices/attenuator/attenuator.py,sha256=eHJpM-E4XQfzy1mxGRk754y3n9q6i3youkCqQg_6SUM,3986
@@ -106,13 +104,17 @@ dodal/devices/i10/slits.py,sha256=4X50bGiJhTIHxhsOrv-8DATBkQPwQgEaFMNa2OsPrFY,12
106
104
  dodal/devices/i10/rasor/rasor_current_amp.py,sha256=hImaPI3veKiS5YVfYwv-qrQ1AYNBjFVxG_MwTQqtkcc,2368
107
105
  dodal/devices/i10/rasor/rasor_motors.py,sha256=0w31rKDuzRL-9tGbLDj0JZljaDjfXvKHQyzMs6fc3sw,1653
108
106
  dodal/devices/i10/rasor/rasor_scaler_cards.py,sha256=wchXaIAWKLg_K14Zez4ZUmYLLk5pqL_QXegYLQO_6AY,463
107
+ dodal/devices/i13_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
+ dodal/devices/i13_1/merlin.py,sha256=Pi-nEqk6OOv3oOBYVb5lemQJiM03KsFbbR7pBAFg5Wg,982
109
+ dodal/devices/i13_1/merlin_controller.py,sha256=8uf9r-2RwpFy7DPT_rmv6AjfHPF_QyfHhIS0dqX73UM,1673
110
+ dodal/devices/i13_1/merlin_io.py,sha256=0To4dzeg1GNRTlsdKL7RbF2aXFyLlACGh9ZhEvIOhUc,531
109
111
  dodal/devices/i20_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
112
  dodal/devices/i22/dcm.py,sha256=SQDh-Sj1OvplHZ9yTWblJwv8PJrUqxseDPupZOWmcLo,4701
111
113
  dodal/devices/i22/fswitch.py,sha256=LSMoo9aDkH0SLcojbUh2NxTWIpUXHZxauTqThc3XtSk,3073
112
114
  dodal/devices/i22/nxsas.py,sha256=a8oJXl5uhkh9zcb9rSZmVooU-SplzzO3EsAaHJZRfrQ,6086
113
115
  dodal/devices/i24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
116
  dodal/devices/i24/aperture.py,sha256=XlnOyQsvdTom1dJHVUg8CUSthq3jlBlZFOUaa9b1eZ4,837
115
- dodal/devices/i24/beam_center.py,sha256=c5NRCBjhg9hMjznNMDipIq0M8-T8yM78IzhcRdpNtGw,470
117
+ dodal/devices/i24/beam_center.py,sha256=m6LWsG9e_lhtPfZ8pc_hoLNyTYQQGGdRNav8J_2scTo,483
116
118
  dodal/devices/i24/beamstop.py,sha256=89ncXUisia1rn1Faf5iWg3k0QW6Rm99j1vq9A8l9Xv8,1221
117
119
  dodal/devices/i24/dcm.py,sha256=Q3qqlgsiLJga2cgI8L4jczjyUgQixJh6QWg7shrFpTQ,1988
118
120
  dodal/devices/i24/dual_backlight.py,sha256=CbQ9mYUNhhozVdNXqR5ac73tEIAWT2RnEpRwXB3EFog,2049
@@ -141,12 +143,15 @@ dodal/devices/training_rig/sample_stage.py,sha256=jktTp837ij8wor5LidE3AajCk95L7D
141
143
  dodal/devices/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
144
  dodal/devices/util/adjuster_plans.py,sha256=XpJ1YJKoOCFzj2sonkZYJAdewi3jRaok_2gOmNsfeS0,956
143
145
  dodal/devices/util/epics_util.py,sha256=A8iNL79PnxntuxPTKA9oLGaDRKhnXaaeAVJwS6FNAWA,4687
144
- dodal/devices/util/lookup_tables.py,sha256=Bk-wZk7JO5_j_8gbot-8reirVqxvFUg9_SIqEIOVZu0,2025
146
+ dodal/devices/util/lookup_tables.py,sha256=3gU9cOBhHBu1S6XqXHWNjCNkV7F4Z-eDlDfesvKbMg4,2185
145
147
  dodal/devices/util/motor_utils.py,sha256=pNY-aUk9LxaIWeDr5rpMS6udiB9j19wcCXkNDLp1uA0,257
146
- dodal/devices/util/save_panda.py,sha256=PHFlkerBhkkU0-o5dNEdi2P95_jD9Lk8yYgaqn9R97o,2538
147
148
  dodal/devices/util/test_utils.py,sha256=x0QVKVeST4T-wpsVSSm-169MyNRXlmybVWnPTefv1as,565
148
149
  dodal/devices/xspress3/xspress3.py,sha256=75RdPuHpES4Xi-Lcywz0XUhaN2G3vZSoc-dzgcxfNvs,4636
149
150
  dodal/devices/xspress3/xspress3_channel.py,sha256=w8tAx2lz5kJ_LeJ_eb_4o--Dtt8MRijsYNgDG6oEIVg,1626
151
+ dodal/devices/zebra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
152
+ dodal/devices/zebra/zebra.py,sha256=_OA34T9CcgwXxyajVKai2jz4nHVDaEvMkb5eG5eAYKw,9192
153
+ dodal/devices/zebra/zebra_constants_mapping.py,sha256=DCWMvBFvmN90rBpZ4aOVF_hbKm4K6QWubsG811R-MK8,4170
154
+ dodal/devices/zebra/zebra_controlled_shutter.py,sha256=5-SH5HoXp_6P-xAtfDFJKQq6mBDwreubuCULSz78fgw,1852
150
155
  dodal/devices/zocalo/__init__.py,sha256=dRAZ9o7B9TACqyE7aanT3yzvqWtt019YgV5ZJY7Ylso,517
151
156
  dodal/devices/zocalo/zocalo_constants.py,sha256=vu7Xjz7UNEpBUWEEBxDvP4bVFkZIN6NLGfQDpWbCjH8,98
152
157
  dodal/devices/zocalo/zocalo_interaction.py,sha256=GFukU9xqagQtVSDg5BrL23jxl1w8wjs4b4NLLqdFfpk,3584
@@ -158,11 +163,12 @@ dodal/plan_stubs/data_session.py,sha256=33wPwbs0mtMnle0H76mH_RNTc5omld7gNSJ9BvRd
158
163
  dodal/plan_stubs/motor_utils.py,sha256=4c93U_WgjfmX12uNiztVW2oKxGVWa_SKQdJYCUNmsGU,4653
159
164
  dodal/plan_stubs/wrapped.py,sha256=nriHKX4BF010CmrhdoUhY3-txClW5W8TPLz64kE_AXU,4533
160
165
  dodal/plans/__init__.py,sha256=nH1jNxw3DzDMg9O8Uda0kqKIalRVEWBrq07OLY6Ey38,93
166
+ dodal/plans/save_panda.py,sha256=PHFlkerBhkkU0-o5dNEdi2P95_jD9Lk8yYgaqn9R97o,2538
161
167
  dodal/plans/scanspec.py,sha256=Q0AcvTKRT401iGMRDSqK-D523UX5_ofiVMZ_rNXKOx8,2074
162
168
  dodal/plans/wrapped.py,sha256=BPMw__RcWvk9v5XnhMsi9_k4KsDEbmXogzD2n1ecbUg,2098
163
- dls_dodal-1.37.0.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
164
- dls_dodal-1.37.0.dist-info/METADATA,sha256=HmPwH3laIqDE6a89MPQeRI_EeC-Oun96q4gGK8vV-AE,16651
165
- dls_dodal-1.37.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
166
- dls_dodal-1.37.0.dist-info/entry_points.txt,sha256=bycw_EKUzup_rxfCetOwcauXV4kLln_OPpPT8jEnr-I,94
167
- dls_dodal-1.37.0.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
168
- dls_dodal-1.37.0.dist-info/RECORD,,
169
+ dls_dodal-1.38.0.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
170
+ dls_dodal-1.38.0.dist-info/METADATA,sha256=H2_nchEL12FW1s-A-pmX4-m05TcWsK3F6dkJjsnueqA,16696
171
+ dls_dodal-1.38.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
172
+ dls_dodal-1.38.0.dist-info/entry_points.txt,sha256=bycw_EKUzup_rxfCetOwcauXV4kLln_OPpPT8jEnr-I,94
173
+ dls_dodal-1.38.0.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
174
+ dls_dodal-1.38.0.dist-info/RECORD,,
dodal/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.37.0'
16
- __version_tuple__ = version_tuple = (1, 37, 0)
15
+ __version__ = version = '1.38.0'
16
+ __version_tuple__ = version_tuple = (1, 38, 0)
dodal/beamlines/i03.py CHANGED
@@ -40,8 +40,13 @@ from dodal.devices.undulator_dcm import UndulatorDCM
40
40
  from dodal.devices.webcam import Webcam
41
41
  from dodal.devices.xbpm_feedback import XBPMFeedback
42
42
  from dodal.devices.xspress3.xspress3 import Xspress3
43
- from dodal.devices.zebra import Zebra
44
- from dodal.devices.zebra_controlled_shutter import ZebraShutter
43
+ from dodal.devices.zebra.zebra import Zebra
44
+ from dodal.devices.zebra.zebra_constants_mapping import (
45
+ ZebraMapping,
46
+ ZebraSources,
47
+ ZebraTTLOutputs,
48
+ )
49
+ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
45
50
  from dodal.devices.zocalo import ZocaloResults
46
51
  from dodal.log import set_beamline as set_log_beamline
47
52
  from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device
@@ -58,6 +63,12 @@ set_utils_beamline(BL)
58
63
 
59
64
  set_path_provider(PandASubpathProvider())
60
65
 
66
+ I03_ZEBRA_MAPPING = ZebraMapping(
67
+ outputs=ZebraTTLOutputs(TTL_DETECTOR=1, TTL_SHUTTER=2, TTL_XSPRESS3=3, TTL_PANDA=4),
68
+ sources=ZebraSources(),
69
+ AND_GATE_FOR_AUTO_SHUTTER=2,
70
+ )
71
+
61
72
 
62
73
  def aperture_scatterguard(
63
74
  wait_for_connection: bool = True,
@@ -368,6 +379,7 @@ def zebra(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -
368
379
  "-EA-ZEBRA-01:",
369
380
  wait_for_connection,
370
381
  fake_with_ophyd_sim,
382
+ mapping=I03_ZEBRA_MAPPING,
371
383
  )
372
384
 
373
385
 
dodal/beamlines/i04.py CHANGED
@@ -28,8 +28,13 @@ from dodal.devices.synchrotron import Synchrotron
28
28
  from dodal.devices.thawer import Thawer
29
29
  from dodal.devices.undulator import Undulator
30
30
  from dodal.devices.xbpm_feedback import XBPMFeedback
31
- from dodal.devices.zebra import Zebra
32
- from dodal.devices.zebra_controlled_shutter import ZebraShutter
31
+ from dodal.devices.zebra.zebra import Zebra
32
+ from dodal.devices.zebra.zebra_constants_mapping import (
33
+ ZebraMapping,
34
+ ZebraSources,
35
+ ZebraTTLOutputs,
36
+ )
37
+ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
33
38
  from dodal.log import set_beamline as set_log_beamline
34
39
  from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device
35
40
 
@@ -47,6 +52,11 @@ BL = get_beamline_name("s04")
47
52
  set_log_beamline(BL)
48
53
  set_utils_beamline(BL)
49
54
 
55
+ I04_ZEBRA_MAPPING = ZebraMapping(
56
+ outputs=(ZebraTTLOutputs(TTL_DETECTOR=1, TTL_FAST_SHUTTER=2, TTL_XSPRESS3=3)),
57
+ sources=ZebraSources(),
58
+ )
59
+
50
60
 
51
61
  def smargon(
52
62
  wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
@@ -341,6 +351,7 @@ def zebra(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -
341
351
  "-EA-ZEBRA-01:",
342
352
  wait_for_connection,
343
353
  fake_with_ophyd_sim,
354
+ mapping=I04_ZEBRA_MAPPING,
344
355
  )
345
356
 
346
357
 
dodal/beamlines/i13_1.py CHANGED
@@ -8,7 +8,8 @@ from dodal.common.beamlines.beamline_utils import (
8
8
  set_path_provider,
9
9
  )
10
10
  from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
11
- from dodal.common.visit import StaticVisitPathProvider
11
+ from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider
12
+ from dodal.devices.i13_1.merlin import Merlin
12
13
  from dodal.devices.motors import XYZPositioner
13
14
  from dodal.log import set_beamline as set_log_beamline
14
15
  from dodal.utils import get_beamline_name
@@ -19,7 +20,8 @@ set_utils_beamline(BL)
19
20
  set_path_provider(
20
21
  StaticVisitPathProvider(
21
22
  BL,
22
- Path("/data/2024/cm37257-4/"), # latest commissioning visit
23
+ Path("/dls/i13-1/data/2024/cm37257-5/tmp/"), # latest commissioning visit
24
+ client=LocalDirectoryServiceClient(),
23
25
  )
24
26
  )
25
27
 
@@ -64,3 +66,19 @@ def side_camera(
64
66
  wait=wait_for_connection,
65
67
  fake=fake_with_ophyd_sim,
66
68
  )
69
+
70
+
71
+ def merlin(
72
+ wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
73
+ ) -> Merlin:
74
+ return device_instantiation(
75
+ Merlin,
76
+ prefix="BL13J-EA-DET-04:",
77
+ name="merlin",
78
+ bl_prefix=False,
79
+ drv_suffix="CAM:",
80
+ hdf_suffix="HDF5:",
81
+ path_provider=get_path_provider(),
82
+ wait=wait_for_connection,
83
+ fake=fake_with_ophyd_sim,
84
+ )
dodal/beamlines/i24.py CHANGED
@@ -16,7 +16,12 @@ from dodal.devices.i24.pmac import PMAC
16
16
  from dodal.devices.i24.vgonio import VerticalGoniometer
17
17
  from dodal.devices.oav.oav_detector import OAV
18
18
  from dodal.devices.oav.oav_parameters import OAVConfig
19
- from dodal.devices.zebra import Zebra
19
+ from dodal.devices.zebra.zebra import Zebra
20
+ from dodal.devices.zebra.zebra_constants_mapping import (
21
+ ZebraMapping,
22
+ ZebraSources,
23
+ ZebraTTLOutputs,
24
+ )
20
25
  from dodal.log import set_beamline as set_log_beamline
21
26
  from dodal.utils import get_beamline_name, skip_device
22
27
 
@@ -25,10 +30,16 @@ ZOOM_PARAMS_FILE = (
25
30
  )
26
31
  DISPLAY_CONFIG = "/dls_sw/i24/software/gda_versions/var/display.configuration"
27
32
 
33
+
28
34
  BL = get_beamline_name("s24")
29
35
  set_log_beamline(BL)
30
36
  set_utils_beamline(BL)
31
37
 
38
+ I24_ZEBRA_MAPPING = ZebraMapping(
39
+ outputs=ZebraTTLOutputs(TTL_EIGER=1, TTL_PILATUS=2, TTL_FAST_SHUTTER=4),
40
+ sources=ZebraSources(),
41
+ )
42
+
32
43
 
33
44
  def attenuator(
34
45
  wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
@@ -191,6 +202,7 @@ def zebra(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -
191
202
  "-EA-ZEBRA-01:",
192
203
  wait_for_connection,
193
204
  fake_with_ophyd_sim,
205
+ mapping=I24_ZEBRA_MAPPING,
194
206
  )
195
207
 
196
208
 
@@ -1,6 +1,7 @@
1
1
  from pathlib import Path
2
2
 
3
3
  from ophyd_async.epics.adaravis import AravisDetector
4
+ from ophyd_async.fastcs.panda import HDFPanda
4
5
 
5
6
  from dodal.common.beamlines.beamline_utils import (
6
7
  device_factory,
@@ -33,7 +34,7 @@ set_utils_beamline(BL)
33
34
  set_path_provider(
34
35
  StaticVisitPathProvider(
35
36
  BL,
36
- Path("/exports/mybeamline/data"),
37
+ Path("/data"),
37
38
  client=LocalDirectoryServiceClient(),
38
39
  )
39
40
  )
@@ -52,3 +53,11 @@ def det() -> AravisDetector:
52
53
  drv_suffix="DET:",
53
54
  hdf_suffix=HDF5_PREFIX,
54
55
  )
56
+
57
+
58
+ @device_factory()
59
+ def panda() -> HDFPanda:
60
+ return HDFPanda(
61
+ prefix=f"{PREFIX.beamline_prefix}-MO-PANDA-01:",
62
+ path_provider=get_path_provider(),
63
+ )
dodal/devices/flux.py CHANGED
@@ -1,7 +1,14 @@
1
- from ophyd import Component, Device, EpicsSignalRO, Kind
1
+ from ophyd_async.core import (
2
+ StandardReadable,
3
+ StandardReadableFormat,
4
+ )
5
+ from ophyd_async.epics.core import epics_signal_r
2
6
 
3
7
 
4
- class Flux(Device):
8
+ class Flux(StandardReadable):
5
9
  """Simple device to get the flux reading"""
6
10
 
7
- flux_reading = Component(EpicsSignalRO, "SAMP", kind=Kind.hinted)
11
+ def __init__(self, prefix: str, name="") -> None:
12
+ with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
13
+ self.flux_reading = epics_signal_r(float, prefix + "SAMP")
14
+ super().__init__(name=name)
File without changes
@@ -0,0 +1,33 @@
1
+ from ophyd_async.core import PathProvider, StandardDetector
2
+ from ophyd_async.epics import adcore
3
+
4
+ from dodal.devices.i13_1.merlin_controller import MerlinController
5
+ from dodal.devices.i13_1.merlin_io import MerlinDriverIO
6
+
7
+
8
+ class Merlin(StandardDetector):
9
+ _controller: MerlinController
10
+ _writer: adcore.ADHDFWriter
11
+
12
+ def __init__(
13
+ self,
14
+ prefix: str,
15
+ path_provider: PathProvider,
16
+ drv_suffix="CAM:",
17
+ hdf_suffix="HDF:",
18
+ name: str = "",
19
+ ):
20
+ self.drv = MerlinDriverIO(prefix + drv_suffix)
21
+ self.hdf = adcore.NDFileHDFIO(prefix + hdf_suffix)
22
+
23
+ super().__init__(
24
+ MerlinController(self.drv),
25
+ adcore.ADHDFWriter(
26
+ self.hdf,
27
+ path_provider,
28
+ lambda: self.name,
29
+ adcore.ADBaseDatasetDescriber(self.drv),
30
+ ),
31
+ config_sigs=(self.drv.acquire_period, self.drv.acquire_time),
32
+ name=name,
33
+ )
@@ -0,0 +1,52 @@
1
+ import asyncio
2
+ import logging
3
+
4
+ from ophyd_async.core import (
5
+ DEFAULT_TIMEOUT,
6
+ AsyncStatus,
7
+ DetectorController,
8
+ TriggerInfo,
9
+ )
10
+ from ophyd_async.epics import adcore
11
+
12
+ from dodal.devices.i13_1.merlin_io import MerlinDriverIO, MerlinImageMode
13
+
14
+
15
+ class MerlinController(DetectorController):
16
+ def __init__(
17
+ self,
18
+ driver: MerlinDriverIO,
19
+ good_states: frozenset[adcore.DetectorState] = adcore.DEFAULT_GOOD_STATES,
20
+ ) -> None:
21
+ self.driver = driver
22
+ self.good_states = good_states
23
+ self.frame_timeout: float = 0
24
+ self._arm_status: AsyncStatus | None = None
25
+ for drv_child in self.driver.children():
26
+ logging.debug(drv_child)
27
+
28
+ def get_deadtime(self, exposure: float | None) -> float:
29
+ return 0.002
30
+
31
+ async def prepare(self, trigger_info: TriggerInfo):
32
+ self.frame_timeout = (
33
+ DEFAULT_TIMEOUT + await self.driver.acquire_time.get_value()
34
+ )
35
+ await asyncio.gather(
36
+ self.driver.num_images.set(trigger_info.total_number_of_triggers),
37
+ self.driver.image_mode.set(MerlinImageMode.MULTIPLE),
38
+ )
39
+
40
+ async def arm(self):
41
+ self._arm_status = await adcore.start_acquiring_driver_and_ensure_status(
42
+ self.driver, good_states=self.good_states, timeout=self.frame_timeout
43
+ )
44
+
45
+ async def wait_for_idle(self):
46
+ if self._arm_status:
47
+ await self._arm_status
48
+
49
+ async def disarm(self):
50
+ # We can't use caput callback as we already used it in arm() and we can't have
51
+ # 2 or they will deadlock
52
+ await adcore.stop_busy_record(self.driver.acquire, False, timeout=1)
@@ -0,0 +1,17 @@
1
+ from ophyd_async.core import StrictEnum
2
+ from ophyd_async.epics import adcore
3
+ from ophyd_async.epics.core import epics_signal_rw_rbv
4
+
5
+
6
+ class MerlinImageMode(StrictEnum):
7
+ SINGLE = "Single"
8
+ MULTIPLE = "Multiple"
9
+ CONTINUOUS = "Continuous"
10
+ THRESHOLD = "Threshold"
11
+ BACKGROUND = "Background"
12
+
13
+
14
+ class MerlinDriverIO(adcore.ADBaseIO):
15
+ def __init__(self, prefix: str, name: str = "") -> None:
16
+ super().__init__(prefix, name)
17
+ self.image_mode = epics_signal_rw_rbv(MerlinImageMode, prefix + "ImageMode")
@@ -7,6 +7,6 @@ from ophyd_async.epics.core import epics_signal_rw
7
7
 
8
8
  class DetectorBeamCenter(StandardReadable):
9
9
  def __init__(self, prefix: str, name: str = "") -> None:
10
- self.beam_x = epics_signal_rw(float, prefix + "BeamX")
10
+ self.beam_x = epics_signal_rw(float, prefix + "BeamX") # in pixels
11
11
  self.beam_y = epics_signal_rw(float, prefix + "BeamY")
12
12
  super().__init__(name)
dodal/devices/p45.py CHANGED
@@ -1,44 +1,55 @@
1
- from ophyd import Component as Cpt
2
- from ophyd import EpicsMotor, MotorBundle
3
- from ophyd.areadetector.base import ADComponent as Cpt
1
+ from ophyd_async.core import StandardReadable
2
+ from ophyd_async.epics.motor import Motor
4
3
 
5
4
 
6
- class SampleY(MotorBundle):
5
+ class SampleY(StandardReadable):
7
6
  """
8
7
  Motors for controlling the sample's y position and stretch in the y axis.
9
8
  """
10
9
 
11
- base = Cpt(EpicsMotor, "CS:Y")
12
- stretch = Cpt(EpicsMotor, "CS:Y:STRETCH")
13
- top = Cpt(EpicsMotor, "Y:TOP")
14
- bottom = Cpt(EpicsMotor, "Y:BOT")
10
+ def __init__(self, prefix: str, name="") -> None:
11
+ with self.add_children_as_readables():
12
+ self.base = Motor(prefix + "CS:Y")
13
+ self.stretch = Motor(prefix + "CS:Y:STRETCH")
14
+ self.top = Motor(prefix + "Y:TOP")
15
+ self.bottom = Motor(prefix + "Y:BOT")
16
+ super().__init__(name=name)
15
17
 
16
18
 
17
- class SampleTheta(MotorBundle):
19
+ class SampleTheta(StandardReadable):
18
20
  """
19
21
  Motors for controlling the sample's theta position and skew
20
22
  """
21
23
 
22
- base = Cpt(EpicsMotor, "THETA:POS")
23
- skew = Cpt(EpicsMotor, "THETA:SKEW")
24
- top = Cpt(EpicsMotor, "THETA:TOP")
25
- bottom = Cpt(EpicsMotor, "THETA:BOT")
24
+ def __init__(self, prefix: str, name="") -> None:
25
+ with self.add_children_as_readables():
26
+ self.base = Motor(prefix + "THETA:POS")
27
+ self.skew = Motor(prefix + "THETA:SKEW")
28
+ self.top = Motor(prefix + "THETA:TOP")
29
+ self.bottom = Motor(prefix + "THETA:BOT")
30
+ super().__init__(name=name)
26
31
 
27
32
 
28
- class TomoStageWithStretchAndSkew(MotorBundle):
33
+ class TomoStageWithStretchAndSkew(StandardReadable):
29
34
  """
30
35
  Grouping of motors for the P45 tomography stage
31
36
  """
32
37
 
33
- x = Cpt(EpicsMotor, "X")
34
- y = Cpt(SampleY, "")
35
- theta = Cpt(SampleTheta, "")
38
+ def __init__(self, prefix: str, name="") -> None:
39
+ with self.add_children_as_readables():
40
+ self.x = Motor(prefix + "X")
41
+ self.y = SampleY(prefix)
42
+ self.theta = SampleTheta(prefix)
43
+ super().__init__(name=name)
36
44
 
37
45
 
38
- class Choppers(MotorBundle):
46
+ class Choppers(StandardReadable):
39
47
  """
40
48
  Grouping for the P45 chopper motors
41
49
  """
42
50
 
43
- x = Cpt(EpicsMotor, "ENDAT")
44
- y = Cpt(EpicsMotor, "BISS")
51
+ def __init__(self, prefix: str, name="") -> None:
52
+ with self.add_children_as_readables():
53
+ self.x = Motor(prefix + "ENDAT")
54
+ self.y = Motor(prefix + "BISS")
55
+ super().__init__(name=name)
@@ -1,8 +1,12 @@
1
- from ophyd import Component, Device, EpicsMotor
1
+ from ophyd_async.core import StandardReadable
2
+ from ophyd_async.epics.motor import Motor
2
3
 
3
4
 
4
- class S4SlitGaps(Device):
5
+ class S4SlitGaps(StandardReadable):
5
6
  """Note that the S4 slits have a different PV fromat to other beamline slits"""
6
7
 
7
- xgap = Component(EpicsMotor, "XGAP")
8
- ygap = Component(EpicsMotor, "YGAP")
8
+ def __init__(self, prefix: str, name="") -> None:
9
+ with self.add_children_as_readables():
10
+ self.xgap = Motor(prefix + "XGAP")
11
+ self.ygap = Motor(prefix + "YGAP")
12
+ super().__init__(name=name)
@@ -32,28 +32,32 @@ async def energy_distance_table(lookup_table_path: str) -> np.ndarray:
32
32
  return loadtxt(StringIO(raw_table), comments=["#", "Units"])
33
33
 
34
34
 
35
- def linear_interpolation_lut(filename: str) -> Callable[[float], float]:
35
+ def parse_lookup_table(filename: str) -> list[Sequence]:
36
+ """Parse a generic lookup table with a number of columns >= 2 and return a list \
37
+ in column major order of the values in it."""
38
+ LOGGER.info(f"Parsing lookup table file {filename}")
39
+
40
+ lut_vals = zip(*loadtxt(filename, comments=["#", "Units"]), strict=False)
41
+ return list(lut_vals)
42
+
43
+
44
+ def linear_interpolation_lut(
45
+ s_values: Sequence, t_values: Sequence
46
+ ) -> Callable[[float], float]:
36
47
  """Returns a callable that converts values by linear interpolation of lookup table
37
48
  values.
38
49
 
39
50
  If the value falls outside the lookup table then the closest value will be used."""
40
- LOGGER.info(f"Using lookup table {filename}")
41
- s_and_t_vals = zip(*loadtxt(filename, comments=["#", "Units"]), strict=False)
42
-
43
- s_values: Sequence
44
- t_values: Sequence
45
- s_values, t_values = s_and_t_vals
46
-
47
51
  # numpy interp expects x-values to be increasing
48
52
  if not np.all(np.diff(s_values) > 0):
49
53
  LOGGER.info(
50
- f"Configuration file {filename} values are not ascending, trying reverse order..."
54
+ "Configuration values in the lookup table are not ascending, trying reverse order..."
51
55
  )
52
56
  s_values = list(reversed(s_values))
53
57
  t_values = list(reversed(t_values))
54
58
  if not np.all(np.diff(s_values) > 0):
55
59
  raise AssertionError(
56
- f"Configuration file {filename} lookup table does not monotonically increase or decrease."
60
+ "Configuration lookup table does not monotonically increase or decrease."
57
61
  )
58
62
 
59
63
  def s_to_t2(s: float) -> float:
File without changes
@@ -14,37 +14,7 @@ from ophyd_async.core import (
14
14
  )
15
15
  from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
16
16
 
17
- # These constants refer to I03's Zebra. See https://github.com/DiamondLightSource/dodal/issues/772
18
- # Sources
19
- DISCONNECT = 0
20
- IN1_TTL = 1
21
- IN2_TTL = 4
22
- IN3_TTL = 7
23
- IN4_TTL = 10
24
- PC_ARM = 29
25
- PC_GATE = 30
26
- PC_PULSE = 31
27
- AND3 = 34
28
- AND4 = 35
29
- OR1 = 36
30
- PULSE1 = 52
31
- PULSE2 = 53
32
- SOFT_IN1 = 60
33
- SOFT_IN2 = 61
34
- SOFT_IN3 = 62
35
-
36
- # Instrument specific
37
- TTL_DETECTOR = 1
38
- TTL_SHUTTER = 2
39
- TTL_XSPRESS3 = 3
40
- TTL_PANDA = 4
41
-
42
- # The AND gate that controls the automatic shutter
43
- AUTO_SHUTTER_GATE = 2
44
-
45
- # The first two inputs of the auto shutter gate.
46
- AUTO_SHUTTER_INPUT_1 = 1
47
- AUTO_SHUTTER_INPUT_2 = 2
17
+ from dodal.devices.zebra.zebra_constants_mapping import ZebraMapping
48
18
 
49
19
 
50
20
  class ArmSource(StrictEnum):
@@ -303,7 +273,8 @@ class SoftInputs(StandardReadable):
303
273
  class Zebra(StandardReadable):
304
274
  """The Zebra device."""
305
275
 
306
- def __init__(self, name: str, prefix: str) -> None:
276
+ def __init__(self, mapping: ZebraMapping, name: str, prefix: str) -> None:
277
+ self.mapping = mapping
307
278
  self.pc = PositionCompare(prefix, name)
308
279
  self.output = ZebraOutputPanel(prefix, name)
309
280
  self.inputs = SoftInputs(prefix, name)
@@ -0,0 +1,96 @@
1
+ from collections import Counter
2
+
3
+ from pydantic import BaseModel, Field, model_validator
4
+
5
+
6
+ class ZebraMappingValidations(BaseModel):
7
+ """Raises an exception if field set to -1 is accessed, and validate against
8
+ multiple fields mapping to the same integer"""
9
+
10
+ def __getattribute__(self, name: str):
11
+ """To protect against mismatch between the Zebra configuration that a plan expects and the Zebra which has
12
+ been instantiated, raise exception if a field which has been set to -1 is accessed."""
13
+ value = object.__getattribute__(self, name)
14
+ if not name.startswith("__"):
15
+ if value == -1:
16
+ raise UnmappedZebraException(
17
+ f"'{type(self).__name__}.{name}' was accessed but is set to -1. Please check the zebra mappings against the zebra's physical configuration"
18
+ )
19
+ return value
20
+
21
+ @model_validator(mode="after")
22
+ def ensure_no_duplicate_connections(self):
23
+ """Check that TTL outputs and sources are mapped to unique integers"""
24
+
25
+ integer_fields = {
26
+ key: value
27
+ for key, value in self.model_dump().items()
28
+ if isinstance(value, int) and value != -1
29
+ }
30
+ counted_vals = Counter(integer_fields.values())
31
+ integer_fields_with_duplicates = {
32
+ k: v for k, v in integer_fields.items() if counted_vals[v] > 1
33
+ }
34
+ if len(integer_fields_with_duplicates):
35
+ raise ValueError(
36
+ f"Each field in {type(self)} must be mapped to a unique integer. Duplicate fields: {integer_fields_with_duplicates}"
37
+ )
38
+ return self
39
+
40
+
41
+ class ZebraTTLOutputs(ZebraMappingValidations):
42
+ """Maps hardware to the Zebra TTL output (1-4) that they're physically wired to, or
43
+ None if that hardware is not connected. A value of -1 means this hardware is not connected."""
44
+
45
+ TTL_EIGER: int = Field(default=-1, ge=-1, le=4)
46
+ TTL_PILATUS: int = Field(default=-1, ge=-1, le=4)
47
+ TTL_FAST_SHUTTER: int = Field(default=-1, ge=-1, le=4)
48
+ TTL_DETECTOR: int = Field(default=-1, ge=-1, le=4)
49
+ TTL_SHUTTER: int = Field(default=-1, ge=-1, le=4)
50
+ TTL_XSPRESS3: int = Field(default=-1, ge=-1, le=4)
51
+ TTL_PANDA: int = Field(default=-1, ge=-1, le=4)
52
+
53
+
54
+ class ZebraSources(ZebraMappingValidations):
55
+ """Maps internal Zebra signal source to their integer PV value"""
56
+
57
+ DISCONNECT: int = Field(default=0, ge=0, le=63)
58
+ IN1_TTL: int = Field(default=1, ge=0, le=63)
59
+ IN2_TTL: int = Field(default=63, ge=0, le=63)
60
+ IN3_TTL: int = Field(default=7, ge=0, le=63)
61
+ IN4_TTL: int = Field(default=10, ge=0, le=63)
62
+ PC_ARM: int = Field(default=29, ge=0, le=63)
63
+ PC_GATE: int = Field(default=30, ge=0, le=63)
64
+ PC_PULSE: int = Field(default=31, ge=0, le=63)
65
+ AND3: int = Field(default=34, ge=0, le=63)
66
+ AND4: int = Field(default=35, ge=0, le=63)
67
+ OR1: int = Field(default=36, ge=0, le=63)
68
+ PULSE1: int = Field(default=52, ge=0, le=63)
69
+ PULSE2: int = Field(default=53, ge=0, le=63)
70
+ SOFT_IN1: int = Field(default=60, ge=0, le=63)
71
+ SOFT_IN2: int = Field(default=61, ge=0, le=63)
72
+ SOFT_IN3: int = Field(default=62, ge=0, le=63)
73
+
74
+
75
+ class ZebraMapping(ZebraMappingValidations):
76
+ """Mappings to locate a Zebra device's Ophyd signals based on a specific
77
+ Zebra's hardware configuration and wiring.
78
+ """
79
+
80
+ # Zebra ophyd signal for connection can be accessed
81
+ # with, eg, zebra.output.out_pvs[zebra.mapping.outputs.TTL_DETECTOR]
82
+ outputs: ZebraTTLOutputs = ZebraTTLOutputs()
83
+
84
+ # Zebra ophyd signal sources can be mapped to a zebra output by doing, eg,
85
+ # bps.abs_set(zebra.output.out_pvs[zebra.mapping.outputs.TTL_DETECTOR],
86
+ # zebra.mapping.sources.AND3)
87
+ sources: ZebraSources = ZebraSources()
88
+
89
+ # Which of the Zebra's four AND gates is used to control the automatic shutter, if it's being used.
90
+ # After defining, the correct GateControl device can be accessed with, eg,
91
+ # zebra.logic_gates.and_gates[zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER]. Set to -1 if not being used.
92
+ AND_GATE_FOR_AUTO_SHUTTER: int = Field(default=-1, ge=-1, le=4)
93
+
94
+
95
+ class UnmappedZebraException(Exception):
96
+ pass
File without changes