dls-dodal 1.29.0__py3-none-any.whl → 1.29.3__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.
- {dls_dodal-1.29.0.dist-info → dls_dodal-1.29.3.dist-info}/METADATA +4 -2
- {dls_dodal-1.29.0.dist-info → dls_dodal-1.29.3.dist-info}/RECORD +24 -23
- {dls_dodal-1.29.0.dist-info → dls_dodal-1.29.3.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/i22.py +1 -0
- dodal/beamlines/i23.py +0 -12
- dodal/beamlines/i24.py +47 -1
- dodal/devices/fast_grid_scan.py +0 -30
- dodal/devices/hutch_shutter.py +93 -0
- dodal/devices/i24/I24_detector_motion.py +8 -5
- dodal/devices/i24/aperture.py +29 -0
- dodal/devices/i24/beamstop.py +38 -0
- dodal/devices/i24/dual_backlight.py +39 -21
- dodal/devices/i24/pmac.py +2 -2
- dodal/devices/motors.py +0 -48
- dodal/devices/robot.py +2 -1
- dodal/devices/smargon.py +102 -48
- dodal/devices/thawer.py +35 -2
- dodal/devices/util/epics_util.py +16 -13
- dodal/devices/zebra.py +4 -0
- dodal/log.py +13 -2
- dodal/devices/i23/__init__.py +0 -0
- dodal/devices/i23/gonio.py +0 -29
- {dls_dodal-1.29.0.dist-info → dls_dodal-1.29.3.dist-info}/LICENSE +0 -0
- {dls_dodal-1.29.0.dist-info → dls_dodal-1.29.3.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.29.0.dist-info → dls_dodal-1.29.3.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dls-dodal
|
|
3
|
-
Version: 1.29.
|
|
3
|
+
Version: 1.29.3
|
|
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
|
|
@@ -242,9 +242,11 @@ Requires-Dist: pytest-asyncio ; extra == 'dev'
|
|
|
242
242
|
Requires-Dist: pytest-cov ; extra == 'dev'
|
|
243
243
|
Requires-Dist: pytest-random-order ; extra == 'dev'
|
|
244
244
|
Requires-Dist: ruff ; extra == 'dev'
|
|
245
|
-
Requires-Dist: sphinx-autobuild
|
|
245
|
+
Requires-Dist: sphinx-autobuild ; extra == 'dev'
|
|
246
246
|
Requires-Dist: sphinx-copybutton ; extra == 'dev'
|
|
247
|
+
Requires-Dist: sphinxcontrib-mermaid ; extra == 'dev'
|
|
247
248
|
Requires-Dist: sphinx-design ; extra == 'dev'
|
|
249
|
+
Requires-Dist: sphinx-autodoc-typehints ; extra == 'dev'
|
|
248
250
|
Requires-Dist: tox-direct ; extra == 'dev'
|
|
249
251
|
Requires-Dist: types-requests ; extra == 'dev'
|
|
250
252
|
Requires-Dist: types-mock ; extra == 'dev'
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
dodal/__init__.py,sha256=y-VRpfiX-Lm5nchB9N0VfMy_6dwFqVxpSn5SiAQql9I,114
|
|
2
2
|
dodal/__main__.py,sha256=kP2S2RPitnOWpNGokjZ1Yq-1umOtp5sNOZk2B3tBPLM,111
|
|
3
|
-
dodal/_version.py,sha256=
|
|
3
|
+
dodal/_version.py,sha256=MFlkKJKBPRaG5o-APjK5uWe00dvrrzhYEjQcU5M7B2I,413
|
|
4
4
|
dodal/adsim.py,sha256=OW2dcS7ciD4Yq9WFw4PN_c5Bwccrmu7R-zr-u6ZCbQM,497
|
|
5
5
|
dodal/cli.py,sha256=z0UBESrNrq6Kq4rttp4uHcwS1fnOnRkKBRDHSriPpGY,2058
|
|
6
|
-
dodal/log.py,sha256=
|
|
6
|
+
dodal/log.py,sha256=dfo1rfYrGG8oIm2HkNxaa_ldVs4vJKtgWSoKe1Z_Xno,8533
|
|
7
7
|
dodal/utils.py,sha256=aH-W94t6NFOoGHZ7awbUKY8_k7qIYDourCFs3MKIjjA,10024
|
|
8
8
|
dodal/beamline_specific_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
dodal/beamline_specific_utils/i03.py,sha256=Ixe1anFQl-kwRJubmQx28TIW4Zw8qDxpElNNNapWQHI,396
|
|
@@ -13,9 +13,9 @@ dodal/beamlines/i03.py,sha256=J-4GawTxAhrYDQZh87n11L9ehTgObEQ1Lz2PueXCWbc,16892
|
|
|
13
13
|
dodal/beamlines/i04.py,sha256=JOyNcUnC3wva4no2MHKp6b8gOKAcQXL_c4cBo7oneVs,13034
|
|
14
14
|
dodal/beamlines/i04_1.py,sha256=KDxSUQNhIs_NFiRaLY-Jiory0DeN7Y0ErvGuoTrwCDU,4731
|
|
15
15
|
dodal/beamlines/i20_1.py,sha256=MaPgONHqpoZuBtkiKEzYtViJnKBM2_ekeP4OdbmuXHE,1158
|
|
16
|
-
dodal/beamlines/i22.py,sha256=
|
|
17
|
-
dodal/beamlines/i23.py,sha256=
|
|
18
|
-
dodal/beamlines/i24.py,sha256=
|
|
16
|
+
dodal/beamlines/i22.py,sha256=3VFdA4Wc7O40-64lwUtUBIN23fH4JVNbLKJ1JLjy9as,9870
|
|
17
|
+
dodal/beamlines/i23.py,sha256=2j5qLoqE_hg9ETHqNkOVu7LLkVB8qalgXeORnVYKN_I,1075
|
|
18
|
+
dodal/beamlines/i24.py,sha256=dCMQGcBZ6ADZ6_rEDFcV2BPHGKBC9iVFvfxewxVts4k,6111
|
|
19
19
|
dodal/beamlines/p38.py,sha256=TC78u4GwEnj6X0r5cnHhqNdBbbDRnh8lY8pEucBZjEU,8006
|
|
20
20
|
dodal/beamlines/p45.py,sha256=TNIkC-SBfj0ayZtlLLXW9xCSi5CzJkO8XpAMIo8fjao,2957
|
|
21
21
|
dodal/common/__init__.py,sha256=ZC4ICKUDB0BDxRaVy8nmqclVmDBne-dPtk6UJsoFq6I,258
|
|
@@ -40,32 +40,33 @@ dodal/devices/cryostream.py,sha256=6MU4rXIOL33C-8F3DVfAtv0ZnwiysTtawjkeePd5IrQ,3
|
|
|
40
40
|
dodal/devices/dcm.py,sha256=vfyGYDzfSwTiNqlzkfNjkrL-Q1hNVSgJddvJ5Un_lvg,1610
|
|
41
41
|
dodal/devices/eiger.py,sha256=NE4tHdqgUZpUxJLQbd5lLUIHZcpeotppexJGlDNByzM,13868
|
|
42
42
|
dodal/devices/eiger_odin.py,sha256=U5Byb7uNwDdNscBRp7yBYQrsjKrKXl2l5WdSpL09lAw,6980
|
|
43
|
-
dodal/devices/fast_grid_scan.py,sha256=
|
|
43
|
+
dodal/devices/fast_grid_scan.py,sha256=BEj96j78r60JPPJoOMP-XXG-_9yURFTuu-pp2LcqQmY,12452
|
|
44
44
|
dodal/devices/fluorescence_detector_motion.py,sha256=RrXfPmJzWnAjcjp9u0AnJEfjvWPMKburVTySB2hxYbw,181
|
|
45
45
|
dodal/devices/flux.py,sha256=RtPStHw7Mad0igVKntKWVZfuZn2clokVJqH14HLix6M,198
|
|
46
46
|
dodal/devices/focusing_mirror.py,sha256=aRqBkE3OgaXpH6lP3v1VbSYgHsMMbSsPPXzeyAGf_Pg,6435
|
|
47
|
+
dodal/devices/hutch_shutter.py,sha256=nZ3gRbYIVJsXLlpZMWT4UEYUFFQP1MwMe8Oy304QsqE,3360
|
|
47
48
|
dodal/devices/ipin.py,sha256=OGMXwAE4KDDonZRPFkUmR9Vsk6X4Ox-hEvPT5drP-mQ,208
|
|
48
49
|
dodal/devices/linkam3.py,sha256=TPhiQ1D9i_HIlKHAlfnVfX7H6aPOAeXPEJLdmvwdKWQ,3776
|
|
49
50
|
dodal/devices/logging_ophyd_device.py,sha256=xw4lbyqq5_ehESGterVEfubJsBiJTWvBp5b9k62gSkg,666
|
|
50
|
-
dodal/devices/motors.py,sha256=
|
|
51
|
+
dodal/devices/motors.py,sha256=16ID2jFJ35h6ZrFp76nJG_oQg6uDrupgcbvcbmjlc7c,300
|
|
51
52
|
dodal/devices/p45.py,sha256=jzBW2fGRhIbGzSRs5Fgupxro6aqE611n1RTcrTTG-yY,1047
|
|
52
53
|
dodal/devices/qbpm1.py,sha256=OY7-WbdxMiLGUK8Z57ezwqSXbHxoPP-y3GvBgj9kgMA,220
|
|
53
|
-
dodal/devices/robot.py,sha256=
|
|
54
|
+
dodal/devices/robot.py,sha256=5WQ9kF5m8xhHhipBycsycDV0-_2IBNBkcwuSWP-9-1I,4337
|
|
54
55
|
dodal/devices/s4_slit_gaps.py,sha256=j3kgF9WfGFaU9xdUuiAh-QqI5u_vhiAftaDVINt91SM,243
|
|
55
56
|
dodal/devices/scatterguard.py,sha256=0qnvhoo3RjLsrxVgIoDJpryqunlgMVgaTsoyKRC2g4Y,331
|
|
56
57
|
dodal/devices/scintillator.py,sha256=4Dej1a6HRom9GRwTDsaTKGfvloP20POUqIeHqsI8-R8,184
|
|
57
58
|
dodal/devices/slits.py,sha256=URru9VN2N19KqeUPDZaBmyKYn0_JJiE0Vko4sZpfsl8,601
|
|
58
|
-
dodal/devices/smargon.py,sha256=
|
|
59
|
+
dodal/devices/smargon.py,sha256=Ds8QFqK3ljbTxalqkQ6clpArj4u4hu9d4vrt97Fzdf4,4693
|
|
59
60
|
dodal/devices/status.py,sha256=TuUGidZ4Ar-WCRc_sX0wn58DmL6brj1pMr8rNF5Z6VU,1198
|
|
60
61
|
dodal/devices/synchrotron.py,sha256=QtTufJA_fCaBawHougSc7nxwu240oX46_y0P-4qIW8o,1960
|
|
61
62
|
dodal/devices/tetramm.py,sha256=XriN-zBFVnHxhnTbphSPIZcxEbdWBTbw2g_ulUBl4bw,8538
|
|
62
|
-
dodal/devices/thawer.py,sha256=
|
|
63
|
+
dodal/devices/thawer.py,sha256=hIdZOzCNloY7CtSvdE2gk4vCMMoOtaIA4dPH_k0OwFg,1527
|
|
63
64
|
dodal/devices/turbo_slit.py,sha256=W3ZRIqDhq4iMhr5GcIiWvl2U1GaPtGanqkL7upQOZTY,1132
|
|
64
65
|
dodal/devices/undulator.py,sha256=kn84MQpuBHtQj7H7HeBoAYKXu5buGKvTgs3tf2gdEdw,2074
|
|
65
66
|
dodal/devices/undulator_dcm.py,sha256=TC9fO55r1YIG_88PPbGGtzfjcRJcaoC2ny51JiDOEX4,5199
|
|
66
67
|
dodal/devices/webcam.py,sha256=FXYcxQdOOCRIMAf8jMWlDVAhSEs4ycGCnoODvHb-apM,1554
|
|
67
68
|
dodal/devices/xbpm_feedback.py,sha256=8QHYKHo9ksZo30olbFM-tHpCHcJRFozgHKVJijv3Gck,1986
|
|
68
|
-
dodal/devices/zebra.py,sha256=
|
|
69
|
+
dodal/devices/zebra.py,sha256=9Zkq5I3-gcP6qfDBnPEAtFU4QJ-VJyp7cHvB79ZfLHk,9186
|
|
69
70
|
dodal/devices/zebra_controlled_shutter.py,sha256=MqX4KE6w0FliZRDBltswcLCNSsp6vQrD_iBY640IljI,1094
|
|
70
71
|
dodal/devices/areadetector/__init__.py,sha256=8IwLxuZMW0MOJpJp_ZDdlaE20hrtsH_PXWGaKgMiYs4,240
|
|
71
72
|
dodal/devices/areadetector/adaravis.py,sha256=pwbmmnakarjhD59XoyAIXJdakS-nqDG09Xmwq17AVw4,3787
|
|
@@ -84,13 +85,13 @@ dodal/devices/i20_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
84
85
|
dodal/devices/i22/dcm.py,sha256=Kzyd_qFg8KVhRsgfTQVOpghESE8yIOgACKa0Fv9NaZI,6270
|
|
85
86
|
dodal/devices/i22/fswitch.py,sha256=AdYtnkCBuhivyJGZqelg_7sjB2pHN7vl1JTtlO4vHo4,3061
|
|
86
87
|
dodal/devices/i22/nxsas.py,sha256=ky7v9UZ1UQFsm5hI0wD9OXG-fTKFLj2wJjB7wADxKpw,5655
|
|
87
|
-
dodal/devices/
|
|
88
|
-
dodal/devices/i23/gonio.py,sha256=cxqD2Kd578c4-K4h2sHdJjoap2gPGC46Qr4SPudHLTs,864
|
|
89
|
-
dodal/devices/i24/I24_detector_motion.py,sha256=bKbb44Qs24oguwJ780N4e5XGNtka_3ZZCGGq6BQu99Y,229
|
|
88
|
+
dodal/devices/i24/I24_detector_motion.py,sha256=Joqr1orgeNvRS7n01bjaO-4Yu4obb8fnKaWHQfjPX14,365
|
|
90
89
|
dodal/devices/i24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
91
|
-
dodal/devices/i24/
|
|
90
|
+
dodal/devices/i24/aperture.py,sha256=kKfHli5oKp-j-qZhZoXTRK81SAUNyhpI6VRvtw0SkZA,850
|
|
91
|
+
dodal/devices/i24/beamstop.py,sha256=28hQowTvgN5Zw38tkDh32h2ceyN-2GE8bAaGPvDOt5U,1234
|
|
92
|
+
dodal/devices/i24/dual_backlight.py,sha256=Th-RKr28aFxE8LCT_mdN9KkRIVw0BHLGKkI0ienfRZU,2049
|
|
92
93
|
dodal/devices/i24/i24_vgonio.py,sha256=Igqs7687z6lyhGVeJEDtDmPachYxU48MUH2BF0RpK9Q,461
|
|
93
|
-
dodal/devices/i24/pmac.py,sha256=
|
|
94
|
+
dodal/devices/i24/pmac.py,sha256=pN54myYvzqPl7iW0Vsp59J1EiV_gtn0xQGwbsKJpiYE,3876
|
|
94
95
|
dodal/devices/oav/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
95
96
|
dodal/devices/oav/grid_overlay.py,sha256=FRtjcFd420XY8MEQ9sWedL0i4pK-KUJOSxh2C5zM3PA,5232
|
|
96
97
|
dodal/devices/oav/microns_for_zoom_levels.json,sha256=5PA71RzldFTp0eTUGPmov0MjxHe583mzvfor5f3thXI,1208
|
|
@@ -104,7 +105,7 @@ dodal/devices/oav/pin_image_recognition/manual_test.py,sha256=h1Rto6ZDCB3jWhjSy9
|
|
|
104
105
|
dodal/devices/oav/pin_image_recognition/utils.py,sha256=-7-Zs-331UVTq_AZrfdF-zwZdmMn7eitTkBSqnBrxnk,8620
|
|
105
106
|
dodal/devices/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
106
107
|
dodal/devices/util/adjuster_plans.py,sha256=2AYaywQP_LbA2KJ6Op3cok8GoRtj696utrSSDfaJtBY,875
|
|
107
|
-
dodal/devices/util/epics_util.py,sha256=
|
|
108
|
+
dodal/devices/util/epics_util.py,sha256=eQr-ImBnADpBL_6XWr3_q9yuMe55Lu0h3j9L1fG4Jws,4714
|
|
108
109
|
dodal/devices/util/lookup_tables.py,sha256=Up-0BlARt79TIEM76SkDyn9LtTFLxPUcaEPZv6D6bws,2141
|
|
109
110
|
dodal/devices/util/motor_utils.py,sha256=pNY-aUk9LxaIWeDr5rpMS6udiB9j19wcCXkNDLp1uA0,257
|
|
110
111
|
dodal/devices/xspress3/xspress3.py,sha256=29elzI3JtceryKeMWXhcP9nWl0tlSdnTZhltCitet6A,4668
|
|
@@ -115,9 +116,9 @@ dodal/devices/zocalo/zocalo_results.py,sha256=U4Vk4OF-eL8w0BR-fbw3k4jyRo6G3Ywaf8
|
|
|
115
116
|
dodal/parameters/experiment_parameter_base.py,sha256=O7JamfuJ5cYHkPf9tsHJPqn-OMHTAGouigvM1cDFehE,313
|
|
116
117
|
dodal/plans/check_topup.py,sha256=Pj6Eu8fa6nvoW4awrMxvzE_ftpLfYz8bN0QDLRw0Yuk,2989
|
|
117
118
|
dodal/plans/data_session_metadata.py,sha256=QNx9rb1EfGBHb21eFekIi7KjNhC0PL-SVKBCggDuNeg,1650
|
|
118
|
-
dls_dodal-1.29.
|
|
119
|
-
dls_dodal-1.29.
|
|
120
|
-
dls_dodal-1.29.
|
|
121
|
-
dls_dodal-1.29.
|
|
122
|
-
dls_dodal-1.29.
|
|
123
|
-
dls_dodal-1.29.
|
|
119
|
+
dls_dodal-1.29.3.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
|
120
|
+
dls_dodal-1.29.3.dist-info/METADATA,sha256=WmlVBYCSTsRn07vPGXJa_FF8x_djiThku9KSaCay3A8,16942
|
|
121
|
+
dls_dodal-1.29.3.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
|
122
|
+
dls_dodal-1.29.3.dist-info/entry_points.txt,sha256=wpzz9FsTiYxI8OBwLKX9V9ResLwThBSmtRMcPwII0FA,46
|
|
123
|
+
dls_dodal-1.29.3.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
|
|
124
|
+
dls_dodal-1.29.3.dist-info/RECORD,,
|
dodal/_version.py
CHANGED
dodal/beamlines/i22.py
CHANGED
dodal/beamlines/i23.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from dodal.common.beamlines.beamline_utils import device_instantiation
|
|
2
2
|
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
|
|
3
|
-
from dodal.devices.i23.gonio import Gonio
|
|
4
3
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
5
4
|
from dodal.log import set_beamline as set_log_beamline
|
|
6
5
|
from dodal.utils import get_beamline_name, get_hostname, skip_device
|
|
@@ -19,17 +18,6 @@ def _is_i23_machine():
|
|
|
19
18
|
return hostname.startswith("i23-ws") or hostname.startswith("i23-control")
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
def gonio(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> Gonio:
|
|
23
|
-
"""Get the i23 goniometer device"""
|
|
24
|
-
return device_instantiation(
|
|
25
|
-
Gonio,
|
|
26
|
-
"Gonio",
|
|
27
|
-
"-MO-GONIO-01:",
|
|
28
|
-
wait_for_connection,
|
|
29
|
-
fake_with_ophyd_sim,
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
|
|
33
21
|
@skip_device(lambda: not _is_i23_machine())
|
|
34
22
|
def oav_pin_tip_detection(
|
|
35
23
|
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
dodal/beamlines/i24.py
CHANGED
|
@@ -2,11 +2,15 @@ from dodal.common.beamlines.beamline_utils import BL, device_instantiation
|
|
|
2
2
|
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
|
|
3
3
|
from dodal.devices.detector import DetectorParams
|
|
4
4
|
from dodal.devices.eiger import EigerDetector
|
|
5
|
+
from dodal.devices.hutch_shutter import HutchShutter
|
|
6
|
+
from dodal.devices.i24.aperture import Aperture
|
|
7
|
+
from dodal.devices.i24.beamstop import Beamstop
|
|
5
8
|
from dodal.devices.i24.dual_backlight import DualBacklight
|
|
6
9
|
from dodal.devices.i24.I24_detector_motion import DetectorMotion
|
|
7
10
|
from dodal.devices.i24.i24_vgonio import VGonio
|
|
8
11
|
from dodal.devices.i24.pmac import PMAC
|
|
9
|
-
from dodal.devices.oav.oav_detector import OAV
|
|
12
|
+
from dodal.devices.oav.oav_detector import OAV
|
|
13
|
+
from dodal.devices.oav.oav_parameters import OAVConfigParams
|
|
10
14
|
from dodal.devices.zebra import Zebra
|
|
11
15
|
from dodal.log import set_beamline as set_log_beamline
|
|
12
16
|
from dodal.utils import get_beamline_name, skip_device
|
|
@@ -21,6 +25,32 @@ set_log_beamline(BL)
|
|
|
21
25
|
set_utils_beamline(BL)
|
|
22
26
|
|
|
23
27
|
|
|
28
|
+
def aperture(
|
|
29
|
+
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
|
30
|
+
) -> Aperture:
|
|
31
|
+
"""Get the i24 aperture device, instantiate it if it hasn't already been.
|
|
32
|
+
If this is called when already instantiated in i24, it will return the existing object.
|
|
33
|
+
"""
|
|
34
|
+
return device_instantiation(
|
|
35
|
+
Aperture, "aperture", "-AL-APTR-01:", wait_for_connection, fake_with_ophyd_sim
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def beamstop(
|
|
40
|
+
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
|
41
|
+
) -> Beamstop:
|
|
42
|
+
"""Get the i24 beamstop device, instantiate it if it hasn't already been.
|
|
43
|
+
If this is called when already instantiated in i24, it will return the existing object.
|
|
44
|
+
"""
|
|
45
|
+
return device_instantiation(
|
|
46
|
+
Beamstop,
|
|
47
|
+
"beamstop",
|
|
48
|
+
"-MO-BS-01:",
|
|
49
|
+
wait_for_connection,
|
|
50
|
+
fake_with_ophyd_sim,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
24
54
|
def backlight(
|
|
25
55
|
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
|
26
56
|
) -> DualBacklight:
|
|
@@ -134,3 +164,19 @@ def zebra(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -
|
|
|
134
164
|
wait_for_connection,
|
|
135
165
|
fake_with_ophyd_sim,
|
|
136
166
|
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@skip_device(lambda: BL == "s24")
|
|
170
|
+
def shutter(
|
|
171
|
+
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
|
|
172
|
+
) -> HutchShutter:
|
|
173
|
+
"""Get the i24 hutch shutter device, instantiate it if it hasn't already been.
|
|
174
|
+
If this is called when already instantiated, it will return the existing object.
|
|
175
|
+
"""
|
|
176
|
+
return device_instantiation(
|
|
177
|
+
HutchShutter,
|
|
178
|
+
"shutter",
|
|
179
|
+
"-PS-SHTR-01:",
|
|
180
|
+
wait_for_connection,
|
|
181
|
+
fake_with_ophyd_sim,
|
|
182
|
+
)
|
dodal/devices/fast_grid_scan.py
CHANGED
|
@@ -23,7 +23,6 @@ from ophyd_async.epics.signal import (
|
|
|
23
23
|
from pydantic import validator
|
|
24
24
|
from pydantic.dataclasses import dataclass
|
|
25
25
|
|
|
26
|
-
from dodal.devices.motors import XYZLimitBundle
|
|
27
26
|
from dodal.log import LOGGER
|
|
28
27
|
from dodal.parameters.experiment_parameter_base import AbstractExperimentWithBeamParams
|
|
29
28
|
|
|
@@ -112,35 +111,6 @@ class GridScanParamsCommon(AbstractExperimentWithBeamParams):
|
|
|
112
111
|
def _get_z_axis(cls, z_axis: GridAxis, values: dict[str, Any]) -> GridAxis:
|
|
113
112
|
return GridAxis(values["z2_start"], values["z_step_size"], values["z_steps"])
|
|
114
113
|
|
|
115
|
-
def is_valid(self, limits: XYZLimitBundle) -> bool:
|
|
116
|
-
"""
|
|
117
|
-
Validates scan parameters
|
|
118
|
-
|
|
119
|
-
:param limits: The motor limits against which to validate
|
|
120
|
-
the parameters
|
|
121
|
-
:return: True if the scan is valid
|
|
122
|
-
"""
|
|
123
|
-
x_in_limits = limits.x.is_within(self.x_axis.start) and limits.x.is_within(
|
|
124
|
-
self.x_axis.end
|
|
125
|
-
)
|
|
126
|
-
y_in_limits = limits.y.is_within(self.y_axis.start) and limits.y.is_within(
|
|
127
|
-
self.y_axis.end
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
first_grid_in_limits = (
|
|
131
|
-
x_in_limits and y_in_limits and limits.z.is_within(self.z1_start)
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
z_in_limits = limits.z.is_within(self.z_axis.start) and limits.z.is_within(
|
|
135
|
-
self.z_axis.end
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
second_grid_in_limits = (
|
|
139
|
-
x_in_limits and z_in_limits and limits.y.is_within(self.y2_start)
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
return first_grid_in_limits and second_grid_in_limits
|
|
143
|
-
|
|
144
114
|
def get_num_images(self):
|
|
145
115
|
return self.x_steps * self.y_steps + self.x_steps * self.z_steps
|
|
146
116
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from bluesky.protocols import Movable
|
|
4
|
+
from ophyd_async.core import (
|
|
5
|
+
DEFAULT_TIMEOUT,
|
|
6
|
+
AsyncStatus,
|
|
7
|
+
StandardReadable,
|
|
8
|
+
wait_for_value,
|
|
9
|
+
)
|
|
10
|
+
from ophyd_async.epics.signal import epics_signal_r, epics_signal_w
|
|
11
|
+
|
|
12
|
+
HUTCH_SAFE_FOR_OPERATIONS = 0 # Hutch is locked and can't be entered
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ShutterNotSafeToOperateError(Exception):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ShutterDemand(str, Enum):
|
|
20
|
+
OPEN = "Open"
|
|
21
|
+
CLOSE = "Close"
|
|
22
|
+
RESET = "Reset"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ShutterState(str, Enum):
|
|
26
|
+
FAULT = "Fault"
|
|
27
|
+
OPEN = "Open"
|
|
28
|
+
OPENING = "Opening"
|
|
29
|
+
CLOSED = "Closed"
|
|
30
|
+
CLOSING = "Closing"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class HutchInterlock(StandardReadable):
|
|
34
|
+
"""Device to check the interlock status of the hutch."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, bl_prefix: str, name: str = "") -> None:
|
|
37
|
+
self.status = epics_signal_r(float, bl_prefix + "-PS-IOC-01:M14:LOP")
|
|
38
|
+
super().__init__(name)
|
|
39
|
+
|
|
40
|
+
# TODO replace with read
|
|
41
|
+
# See https://github.com/DiamondLightSource/dodal/issues/651
|
|
42
|
+
async def shutter_safe_to_operate(self) -> bool:
|
|
43
|
+
"""If the status value is 0, hutch has been searched and locked and it is safe \
|
|
44
|
+
to operate the shutter.
|
|
45
|
+
If the status value is not 0 (usually set to 7), the hutch is open and the \
|
|
46
|
+
shutter should not be in use.
|
|
47
|
+
"""
|
|
48
|
+
interlock_state = await self.status.get_value()
|
|
49
|
+
return interlock_state == HUTCH_SAFE_FOR_OPERATIONS
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class HutchShutter(StandardReadable, Movable):
|
|
53
|
+
"""Device to operate the hutch shutter.
|
|
54
|
+
|
|
55
|
+
When a demand is sent, the device should first check the hutch status \
|
|
56
|
+
and raise an error if it's not interlocked (searched and locked), meaning it's not \
|
|
57
|
+
safe to operate the shutter.
|
|
58
|
+
|
|
59
|
+
If the requested shutter position is "Open", the shutter control PV should first \
|
|
60
|
+
go to "Reset" and then move to "Open". This is because before opening the hutch \
|
|
61
|
+
shutter, the interlock status PV (`-PS-SHTR-01:ILKSTA`) will show as `failed` until \
|
|
62
|
+
the hutch shutter is reset. This will set the interlock status to `OK`, allowing \
|
|
63
|
+
for shutter operations. Until this step is done, the hutch shutter can't be opened.
|
|
64
|
+
The reset is not needed for closing the shutter.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
68
|
+
self.control = epics_signal_w(ShutterDemand, prefix + "CON")
|
|
69
|
+
self.status = epics_signal_r(ShutterState, prefix + "STA")
|
|
70
|
+
|
|
71
|
+
bl_prefix = prefix.split("-")[0]
|
|
72
|
+
self.interlock = HutchInterlock(bl_prefix)
|
|
73
|
+
|
|
74
|
+
super().__init__(name)
|
|
75
|
+
|
|
76
|
+
@AsyncStatus.wrap
|
|
77
|
+
async def set(self, position_demand: ShutterDemand):
|
|
78
|
+
interlock_state = await self.interlock.shutter_safe_to_operate()
|
|
79
|
+
if not interlock_state:
|
|
80
|
+
raise ShutterNotSafeToOperateError(
|
|
81
|
+
"The hutch has not been locked, not operating shutter."
|
|
82
|
+
)
|
|
83
|
+
if position_demand == ShutterDemand.OPEN:
|
|
84
|
+
await self.control.set(ShutterDemand.RESET, wait=True)
|
|
85
|
+
await self.control.set(position_demand, wait=True)
|
|
86
|
+
return await wait_for_value(
|
|
87
|
+
self.status, match=ShutterState.OPEN, timeout=DEFAULT_TIMEOUT
|
|
88
|
+
)
|
|
89
|
+
else:
|
|
90
|
+
await self.control.set(position_demand, wait=True)
|
|
91
|
+
return await wait_for_value(
|
|
92
|
+
self.status, match=ShutterState.CLOSED, timeout=DEFAULT_TIMEOUT
|
|
93
|
+
)
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
1
|
+
from ophyd_async.core import StandardReadable
|
|
2
|
+
from ophyd_async.epics.motion import Motor
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class DetectorMotion(
|
|
5
|
+
class DetectorMotion(StandardReadable):
|
|
6
6
|
"""Physical motion for detector travel"""
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
9
|
+
self.y = Motor(prefix + "Y") # Vertical
|
|
10
|
+
self.z = Motor(prefix + "Z") # Beam
|
|
11
|
+
|
|
12
|
+
super().__init__(name)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import StandardReadable
|
|
4
|
+
from ophyd_async.epics.motion import Motor
|
|
5
|
+
from ophyd_async.epics.signal import epics_signal_rw
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AperturePositions(str, Enum):
|
|
9
|
+
IN = "In"
|
|
10
|
+
OUT = "Out"
|
|
11
|
+
ROBOT = "Robot"
|
|
12
|
+
MANUAL = "Manual Mounting"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Aperture(StandardReadable):
|
|
16
|
+
"""Device to trigger the aperture motor move on I24.
|
|
17
|
+
|
|
18
|
+
The aperture positioner has 4 possible positions: In, Out, Robot and Manual.
|
|
19
|
+
|
|
20
|
+
When a position is selected, the x motor is moved.
|
|
21
|
+
The position of the y motor is calibrated at start up and is not changed.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
25
|
+
self.x = Motor(prefix + "X")
|
|
26
|
+
self.y = Motor(prefix + "Y")
|
|
27
|
+
|
|
28
|
+
self.position = epics_signal_rw(AperturePositions, prefix + "MP:SELECT")
|
|
29
|
+
super().__init__(name)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import StandardReadable
|
|
4
|
+
from ophyd_async.epics.motion import Motor
|
|
5
|
+
from ophyd_async.epics.signal import epics_signal_rw
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BeamstopPositions(str, Enum):
|
|
9
|
+
CHECK_BEAM = "CheckBeam"
|
|
10
|
+
DATA_COLLECTION = "Data Collection"
|
|
11
|
+
DATA_COLLECTION_FAR = "Data Collection Far"
|
|
12
|
+
TRAY_MOUNT = "Tray Mount"
|
|
13
|
+
ROTATABLE = "Rotatable"
|
|
14
|
+
ROBOT = "Robot"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Beamstop(StandardReadable):
|
|
18
|
+
"""Device to move the beamstop.
|
|
19
|
+
|
|
20
|
+
The positioner moves the x,y,z motors when a position is selected.
|
|
21
|
+
The additional y_rotation motor is independent of the positioner and can to be moved
|
|
22
|
+
on its own as needed.
|
|
23
|
+
|
|
24
|
+
WARNING. Before moving the y_rotation motor away from 0, it is important to make sure
|
|
25
|
+
that the backlight is in the "OUT" position to avoid a collision.
|
|
26
|
+
See also https://github.com/DiamondLightSource/dodal/issues/646.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
30
|
+
self.x = Motor(prefix + "X")
|
|
31
|
+
self.y = Motor(prefix + "Y")
|
|
32
|
+
self.z = Motor(prefix + "Z")
|
|
33
|
+
|
|
34
|
+
self.y_rotation = Motor(prefix + "ROTY")
|
|
35
|
+
|
|
36
|
+
self.pos_select = epics_signal_rw(BeamstopPositions, prefix + "MP:SELECT")
|
|
37
|
+
|
|
38
|
+
super().__init__(name)
|
|
@@ -1,14 +1,36 @@
|
|
|
1
|
-
from
|
|
1
|
+
from enum import Enum
|
|
2
2
|
|
|
3
|
+
from ophyd_async.core import AsyncStatus, StandardReadable
|
|
4
|
+
from ophyd_async.epics.signal import epics_signal_rw
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
|
|
7
|
+
class BacklightPositions(str, Enum):
|
|
8
|
+
OUT = "Out"
|
|
9
|
+
IN = "In"
|
|
10
|
+
LOAD_CHECK = "LoadCheck"
|
|
11
|
+
OAV2 = "OAV2"
|
|
12
|
+
DIODE = "Diode"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LEDStatus(str, Enum):
|
|
16
|
+
OFF = "OFF"
|
|
17
|
+
ON = "ON"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BacklightPositioner(StandardReadable):
|
|
5
21
|
"""Device to control the backlight position."""
|
|
6
22
|
|
|
7
|
-
|
|
8
|
-
|
|
23
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
24
|
+
# Enum description of the backlight position e.g. "In", "OAV2"
|
|
25
|
+
self.pos_level = epics_signal_rw(BacklightPositions, prefix + "MP:SELECT")
|
|
26
|
+
super().__init__(name)
|
|
27
|
+
|
|
28
|
+
@AsyncStatus.wrap
|
|
29
|
+
async def set(self, position: BacklightPositions):
|
|
30
|
+
await self.pos_level.set(position, wait=True)
|
|
9
31
|
|
|
10
32
|
|
|
11
|
-
class DualBacklight(
|
|
33
|
+
class DualBacklight(StandardReadable):
|
|
12
34
|
"""
|
|
13
35
|
Device to trigger the dual backlight on I24.
|
|
14
36
|
This device is made up by two LEDs:
|
|
@@ -17,27 +39,23 @@ class DualBacklight(Device):
|
|
|
17
39
|
|
|
18
40
|
To set the position for LED1:
|
|
19
41
|
b = DualBacklight(name="backlight)
|
|
20
|
-
b.
|
|
21
|
-
|
|
22
|
-
To see get the available position values for LED1:
|
|
23
|
-
b.pos1.alowed_backlight_positions
|
|
42
|
+
b.backlight_position.set("OAV2")
|
|
24
43
|
|
|
25
44
|
Note that the two LED are independently switched on and off. When LED1 is
|
|
26
45
|
in "Out" position (switched off), LED2 might still be on.
|
|
27
46
|
"""
|
|
28
47
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
led1 = Component(EpicsSignal, "-DI-LED-01:TOGGLE")
|
|
33
|
-
pos1 = Component(BacklightPositioner, "-MO-BL-01:")
|
|
48
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
49
|
+
self.backlight_state = epics_signal_rw(LEDStatus, prefix + "-DI-LED-01:TOGGLE")
|
|
50
|
+
self.backlight_position = BacklightPositioner(prefix + "-MO-BL-01:", name)
|
|
34
51
|
|
|
35
|
-
|
|
52
|
+
self.frontlight_state = epics_signal_rw(LEDStatus, prefix + "-DI-LED-02:TOGGLE")
|
|
53
|
+
super().__init__(name)
|
|
36
54
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
@AsyncStatus.wrap
|
|
56
|
+
async def set(self, position: BacklightPositions):
|
|
57
|
+
await self.backlight_position.set(position)
|
|
58
|
+
if position == BacklightPositions.OUT:
|
|
59
|
+
await self.backlight_state.set(LEDStatus.OFF, wait=True)
|
|
41
60
|
else:
|
|
42
|
-
|
|
43
|
-
return status
|
|
61
|
+
await self.backlight_state.set(LEDStatus.ON, wait=True)
|
dodal/devices/i24/pmac.py
CHANGED
|
@@ -118,7 +118,7 @@ class PMAC(StandardReadable):
|
|
|
118
118
|
|
|
119
119
|
# These next signals are readback values on PVARS which are set by the motion
|
|
120
120
|
# program.
|
|
121
|
-
self.scanstatus = epics_signal_r(
|
|
122
|
-
self.counter = epics_signal_r(
|
|
121
|
+
self.scanstatus = epics_signal_r(float, "BL24I-MO-STEP-14:signal:P2401")
|
|
122
|
+
self.counter = epics_signal_r(float, "BL24I-MO-STEP-14:signal:P2402")
|
|
123
123
|
|
|
124
124
|
super().__init__(name)
|
dodal/devices/motors.py
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import List, Tuple
|
|
3
|
-
|
|
4
|
-
import numpy as np
|
|
5
|
-
from ophyd import EpicsMotor
|
|
6
1
|
from ophyd_async.core import Device
|
|
7
2
|
from ophyd_async.epics.motion import Motor
|
|
8
3
|
|
|
@@ -13,46 +8,3 @@ class XYZPositioner(Device):
|
|
|
13
8
|
self.y = Motor(prefix + "Y")
|
|
14
9
|
self.z = Motor(prefix + "Z")
|
|
15
10
|
super().__init__(name)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@dataclass
|
|
19
|
-
class MotorLimitHelper:
|
|
20
|
-
"""
|
|
21
|
-
Represents motor limit(s)
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
motor: EpicsMotor
|
|
25
|
-
|
|
26
|
-
def is_within(self, position: float) -> bool:
|
|
27
|
-
"""Checks position against limits
|
|
28
|
-
|
|
29
|
-
:param position: The position to check
|
|
30
|
-
:return: True if position is within the limits
|
|
31
|
-
"""
|
|
32
|
-
low = float(self.motor.low_limit_travel.get())
|
|
33
|
-
high = float(self.motor.high_limit_travel.get())
|
|
34
|
-
return low <= position <= high
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
@dataclass
|
|
38
|
-
class XYZLimitBundle:
|
|
39
|
-
"""
|
|
40
|
-
Holder for limits reflecting an x, y, z bundle
|
|
41
|
-
"""
|
|
42
|
-
|
|
43
|
-
x: MotorLimitHelper
|
|
44
|
-
y: MotorLimitHelper
|
|
45
|
-
z: MotorLimitHelper
|
|
46
|
-
|
|
47
|
-
def position_valid(
|
|
48
|
-
self, position: np.ndarray | List[float] | Tuple[float, float, float]
|
|
49
|
-
):
|
|
50
|
-
if len(position) != 3:
|
|
51
|
-
raise ValueError(
|
|
52
|
-
f"Position valid expects a 3-vector, got {position} instead"
|
|
53
|
-
)
|
|
54
|
-
return (
|
|
55
|
-
self.x.is_within(position[0])
|
|
56
|
-
& self.y.is_within(position[1])
|
|
57
|
-
& self.z.is_within(position[2])
|
|
58
|
-
)
|
dodal/devices/robot.py
CHANGED
|
@@ -60,7 +60,8 @@ class BartRobot(StandardReadable, Movable):
|
|
|
60
60
|
self.program_running = epics_signal_r(bool, prefix + "PROGRAM_RUNNING")
|
|
61
61
|
self.program_name = epics_signal_r(str, prefix + "PROGRAM_NAME")
|
|
62
62
|
self.error_str = epics_signal_r(str, prefix + "PRG_ERR_MSG")
|
|
63
|
-
|
|
63
|
+
# Change error_code to int type when https://github.com/bluesky/ophyd-async/issues/280 released
|
|
64
|
+
self.error_code = epics_signal_r(float, prefix + "PRG_ERR_CODE")
|
|
64
65
|
super().__init__(name=name)
|
|
65
66
|
|
|
66
67
|
async def pin_mounted_or_no_pin_found(self):
|
dodal/devices/smargon.py
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
from collections.abc import Generator
|
|
2
|
+
from dataclasses import dataclass
|
|
1
3
|
from enum import Enum
|
|
4
|
+
from math import isclose
|
|
5
|
+
from typing import Collection, cast
|
|
2
6
|
|
|
3
|
-
from
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
+
from bluesky import plan_stubs as bps
|
|
8
|
+
from bluesky.utils import Msg
|
|
9
|
+
from ophyd_async.core import AsyncStatus, Device, StandardReadable, wait_for_value
|
|
10
|
+
from ophyd_async.epics.motion import Motor
|
|
11
|
+
from ophyd_async.epics.signal import epics_signal_r
|
|
7
12
|
|
|
8
|
-
from dodal.devices.motors import MotorLimitHelper, XYZLimitBundle
|
|
9
|
-
from dodal.devices.status import await_approx_value
|
|
10
13
|
from dodal.devices.util.epics_util import SetWhenEnabled
|
|
11
|
-
from dodal.devices.util.motor_utils import ExtendedEpicsMotor
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
class StubPosition(Enum):
|
|
@@ -16,6 +18,13 @@ class StubPosition(Enum):
|
|
|
16
18
|
RESET_TO_ROBOT_LOAD = 1
|
|
17
19
|
|
|
18
20
|
|
|
21
|
+
def approx_equal_to(target, deadband: float = 1e-9):
|
|
22
|
+
def approx_equal_to_target(value):
|
|
23
|
+
return isclose(target, value, rel_tol=0, abs_tol=deadband)
|
|
24
|
+
|
|
25
|
+
return approx_equal_to_target
|
|
26
|
+
|
|
27
|
+
|
|
19
28
|
class StubOffsets(Device):
|
|
20
29
|
"""Stub offsets are used to change the internal co-ordinate system of the smargon by
|
|
21
30
|
adding an offset to x, y, z.
|
|
@@ -25,23 +34,64 @@ class StubOffsets(Device):
|
|
|
25
34
|
set them so that the current position is zero or to pre-defined positions.
|
|
26
35
|
"""
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
def __init__(self, name: str = "", prefix: str = ""):
|
|
38
|
+
self.center_at_current_position = SetWhenEnabled(prefix=prefix + "CENTER_CS")
|
|
39
|
+
self.to_robot_load = SetWhenEnabled(prefix=prefix + "SET_STUBS_TO_RL")
|
|
40
|
+
super().__init__(name)
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def set(self, pos: StubPosition) -> StatusBase:
|
|
42
|
+
@AsyncStatus.wrap
|
|
43
|
+
async def set(self, pos: StubPosition):
|
|
34
44
|
if pos == StubPosition.CURRENT_AS_CENTER:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
await self.center_at_current_position.set(1)
|
|
46
|
+
smargon = cast(Smargon, self.parent)
|
|
47
|
+
await wait_for_value(
|
|
48
|
+
smargon.x.user_readback, approx_equal_to(0.0, 0.1), None
|
|
49
|
+
)
|
|
50
|
+
await wait_for_value(
|
|
51
|
+
smargon.y.user_readback, approx_equal_to(0.0, 0.1), None
|
|
52
|
+
)
|
|
53
|
+
await wait_for_value(
|
|
54
|
+
smargon.z.user_readback, approx_equal_to(0.0, 0.1), None
|
|
55
|
+
)
|
|
40
56
|
else:
|
|
41
|
-
|
|
57
|
+
await self.to_robot_load.set(1)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class AxisLimit:
|
|
62
|
+
"""Represents the minimum and maximum allowable values on an axis"""
|
|
63
|
+
|
|
64
|
+
min_value: float
|
|
65
|
+
max_value: float
|
|
66
|
+
|
|
67
|
+
def contains(self, pos: float):
|
|
68
|
+
"""Determine if the specified value is within limits.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
pos: the value to check
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
True if the value does not exceed the limits
|
|
75
|
+
"""
|
|
76
|
+
return self.min_value <= pos <= self.max_value
|
|
42
77
|
|
|
43
78
|
|
|
44
|
-
|
|
79
|
+
@dataclass
|
|
80
|
+
class XYZLimits:
|
|
81
|
+
"""The limits of the smargon x, y, z axes."""
|
|
82
|
+
|
|
83
|
+
x: AxisLimit
|
|
84
|
+
y: AxisLimit
|
|
85
|
+
z: AxisLimit
|
|
86
|
+
|
|
87
|
+
def position_valid(self, pos: Collection[float]) -> bool:
|
|
88
|
+
return all(
|
|
89
|
+
axis_limits.contains(value)
|
|
90
|
+
for axis_limits, value in zip([self.x, self.y, self.z], pos)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class Smargon(StandardReadable):
|
|
45
95
|
"""
|
|
46
96
|
Real motors added to allow stops following pin load (e.g. real_x1.stop() )
|
|
47
97
|
X1 and X2 real motors provide compound chi motion as well as the compound X travel,
|
|
@@ -49,35 +99,39 @@ class Smargon(MotorBundle):
|
|
|
49
99
|
Robot loading can nudge these and lead to errors.
|
|
50
100
|
"""
|
|
51
101
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
102
|
+
def __init__(self, prefix: str = "", name: str = ""):
|
|
103
|
+
with self.add_children_as_readables():
|
|
104
|
+
self.x = Motor(prefix + "X")
|
|
105
|
+
self.y = Motor(prefix + "Y")
|
|
106
|
+
self.z = Motor(prefix + "Z")
|
|
107
|
+
self.chi = Motor(prefix + "CHI")
|
|
108
|
+
self.phi = Motor(prefix + "PHI")
|
|
109
|
+
self.omega = Motor(prefix + "OMEGA")
|
|
110
|
+
self.real_x1 = Motor(prefix + "MOTOR_3")
|
|
111
|
+
self.real_x2 = Motor(prefix + "MOTOR_4")
|
|
112
|
+
self.real_y = Motor(prefix + "MOTOR_1")
|
|
113
|
+
self.real_z = Motor(prefix + "MOTOR_2")
|
|
114
|
+
self.real_phi = Motor(prefix + "MOTOR_5")
|
|
115
|
+
self.real_chi = Motor(prefix + "MOTOR_6")
|
|
116
|
+
self.stub_offsets = StubOffsets(prefix=prefix)
|
|
117
|
+
self.disabled = epics_signal_r(int, prefix + "DISABLED")
|
|
118
|
+
|
|
119
|
+
super().__init__(name)
|
|
120
|
+
|
|
121
|
+
def get_xyz_limits(self) -> Generator[Msg, None, XYZLimits]:
|
|
122
|
+
"""Obtain a plan stub that returns the smargon XYZ axis limits
|
|
123
|
+
|
|
124
|
+
Yields:
|
|
125
|
+
Bluesky messages
|
|
75
126
|
|
|
76
127
|
Returns:
|
|
77
|
-
|
|
128
|
+
the axis limits
|
|
78
129
|
"""
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
130
|
+
limits = {}
|
|
131
|
+
for name, pv in [
|
|
132
|
+
(attr_name, getattr(self, attr_name)) for attr_name in ["x", "y", "z"]
|
|
133
|
+
]:
|
|
134
|
+
min_value = yield from bps.rd(pv.low_limit_travel)
|
|
135
|
+
max_value = yield from bps.rd(pv.high_limit_travel)
|
|
136
|
+
limits[name] = AxisLimit(min_value, max_value)
|
|
137
|
+
return XYZLimits(**limits)
|
dodal/devices/thawer.py
CHANGED
|
@@ -1,15 +1,48 @@
|
|
|
1
|
+
from asyncio import Task, create_task, sleep
|
|
1
2
|
from enum import Enum
|
|
2
3
|
|
|
3
|
-
from
|
|
4
|
+
from bluesky.protocols import Stoppable
|
|
5
|
+
from ophyd_async.core import AsyncStatus, Device, SignalRW, StandardReadable
|
|
4
6
|
from ophyd_async.epics.signal import epics_signal_rw
|
|
5
7
|
|
|
6
8
|
|
|
9
|
+
class ThawingException(Exception):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
7
13
|
class ThawerStates(str, Enum):
|
|
8
14
|
OFF = "Off"
|
|
9
15
|
ON = "On"
|
|
10
16
|
|
|
11
17
|
|
|
12
|
-
class
|
|
18
|
+
class ThawingTimer(Device):
|
|
19
|
+
def __init__(self, control_signal: SignalRW[ThawerStates]) -> None:
|
|
20
|
+
self._control_signal = control_signal
|
|
21
|
+
self._thawing_task: Task | None = None
|
|
22
|
+
super().__init__("thaw_for_time_s")
|
|
23
|
+
|
|
24
|
+
@AsyncStatus.wrap
|
|
25
|
+
async def set(self, time_to_thaw_for: float):
|
|
26
|
+
await self._control_signal.set(ThawerStates.ON)
|
|
27
|
+
if self._thawing_task and not self._thawing_task.done():
|
|
28
|
+
raise ThawingException("Thawing task already in progress")
|
|
29
|
+
self._thawing_task = create_task(sleep(time_to_thaw_for))
|
|
30
|
+
try:
|
|
31
|
+
await self._thawing_task
|
|
32
|
+
finally:
|
|
33
|
+
await self._control_signal.set(ThawerStates.OFF)
|
|
34
|
+
|
|
35
|
+
async def stop(self):
|
|
36
|
+
if self._thawing_task:
|
|
37
|
+
self._thawing_task.cancel()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Thawer(StandardReadable, Stoppable):
|
|
13
41
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
14
42
|
self.control = epics_signal_rw(ThawerStates, prefix + ":CTRL")
|
|
43
|
+
self.thaw_for_time_s = ThawingTimer(self.control)
|
|
15
44
|
super().__init__(name)
|
|
45
|
+
|
|
46
|
+
async def stop(self):
|
|
47
|
+
await self.thaw_for_time_s.stop()
|
|
48
|
+
await self.control.set(ThawerStates.OFF)
|
dodal/devices/util/epics_util.py
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
from functools import partial
|
|
2
2
|
from typing import Callable
|
|
3
3
|
|
|
4
|
-
from
|
|
4
|
+
from bluesky.protocols import Movable
|
|
5
|
+
from ophyd import Component, EpicsSignal
|
|
6
|
+
from ophyd import Device as OphydDevice
|
|
5
7
|
from ophyd.status import Status, StatusBase
|
|
8
|
+
from ophyd_async.core import AsyncStatus, wait_for_value
|
|
9
|
+
from ophyd_async.core import Device as OphydAsyncDevice
|
|
10
|
+
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
6
11
|
|
|
7
|
-
from dodal.devices.status import await_value
|
|
8
12
|
from dodal.log import LOGGER
|
|
9
13
|
|
|
10
14
|
|
|
@@ -24,7 +28,7 @@ def epics_signal_put_wait(pv_name: str, wait: float = 3.0) -> Component[EpicsSig
|
|
|
24
28
|
def run_functions_without_blocking(
|
|
25
29
|
functions_to_chain: list[Callable[[], StatusBase]],
|
|
26
30
|
timeout: float = 60.0,
|
|
27
|
-
associated_obj:
|
|
31
|
+
associated_obj: OphydDevice | None = None,
|
|
28
32
|
) -> Status:
|
|
29
33
|
"""Creates and initiates an asynchronous chaining of functions which return a status
|
|
30
34
|
|
|
@@ -112,16 +116,15 @@ def call_func(func: Callable[[], StatusBase]) -> StatusBase:
|
|
|
112
116
|
return func()
|
|
113
117
|
|
|
114
118
|
|
|
115
|
-
class SetWhenEnabled(
|
|
119
|
+
class SetWhenEnabled(OphydAsyncDevice, Movable):
|
|
116
120
|
"""A device that sets the proc field of a PV when it becomes enabled."""
|
|
117
121
|
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
def __init__(self, name: str = "", prefix: str = ""):
|
|
123
|
+
self.proc = epics_signal_rw(int, prefix + ".PROC")
|
|
124
|
+
self.disp = epics_signal_r(int, prefix + ".DISP")
|
|
125
|
+
super().__init__(name)
|
|
120
126
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
lambda: self.proc.set(proc),
|
|
126
|
-
]
|
|
127
|
-
)
|
|
127
|
+
@AsyncStatus.wrap
|
|
128
|
+
async def set(self, value: int):
|
|
129
|
+
await wait_for_value(self.disp, 0, None)
|
|
130
|
+
await self.proc.set(value)
|
dodal/devices/zebra.py
CHANGED
dodal/log.py
CHANGED
|
@@ -11,13 +11,19 @@ from typing import Deque, Tuple, TypedDict
|
|
|
11
11
|
from bluesky.log import logger as bluesky_logger
|
|
12
12
|
from graypy import GELFTCPHandler
|
|
13
13
|
from ophyd.log import logger as ophyd_logger
|
|
14
|
+
from ophyd_async.log import (
|
|
15
|
+
DEFAULT_DATE_FORMAT,
|
|
16
|
+
DEFAULT_FORMAT,
|
|
17
|
+
DEFAULT_LOG_COLORS,
|
|
18
|
+
ColoredFormatterWithDeviceName,
|
|
19
|
+
)
|
|
14
20
|
from ophyd_async.log import logger as ophyd_async_logger
|
|
15
21
|
|
|
16
22
|
LOGGER = logging.getLogger("Dodal")
|
|
17
23
|
LOGGER.setLevel(logging.DEBUG)
|
|
18
24
|
|
|
19
|
-
DEFAULT_FORMATTER =
|
|
20
|
-
|
|
25
|
+
DEFAULT_FORMATTER = ColoredFormatterWithDeviceName(
|
|
26
|
+
fmt=DEFAULT_FORMAT, datefmt=DEFAULT_DATE_FORMAT, log_colors=DEFAULT_LOG_COLORS
|
|
21
27
|
)
|
|
22
28
|
ERROR_LOG_BUFFER_LINES = 20000
|
|
23
29
|
INFO_LOG_DAYS = 30
|
|
@@ -247,3 +253,8 @@ def get_graylog_configuration(
|
|
|
247
253
|
return "localhost", 5555
|
|
248
254
|
else:
|
|
249
255
|
return "graylog-log-target.diamond.ac.uk", graylog_port or DEFAULT_GRAYLOG_PORT
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class _NoOpFileHandler:
|
|
259
|
+
def write(*args, **kwargs):
|
|
260
|
+
pass
|
dodal/devices/i23/__init__.py
DELETED
|
File without changes
|
dodal/devices/i23/gonio.py
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
from ophyd import Component as Cpt
|
|
2
|
-
from ophyd import EpicsMotor
|
|
3
|
-
from ophyd.epics_motor import MotorBundle
|
|
4
|
-
|
|
5
|
-
from dodal.devices.motors import MotorLimitHelper, XYZLimitBundle
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Gonio(MotorBundle):
|
|
9
|
-
x = Cpt(EpicsMotor, "X")
|
|
10
|
-
y = Cpt(EpicsMotor, "Y")
|
|
11
|
-
z = Cpt(EpicsMotor, "Z")
|
|
12
|
-
kappa = Cpt(EpicsMotor, "KAPPA")
|
|
13
|
-
phi = Cpt(EpicsMotor, "PHI")
|
|
14
|
-
omega = Cpt(EpicsMotor, "OMEGA")
|
|
15
|
-
|
|
16
|
-
def get_xyz_limits(self) -> XYZLimitBundle:
|
|
17
|
-
"""Get the limits for the x, y and z axes.
|
|
18
|
-
|
|
19
|
-
Note that these limits may not yet be valid until wait_for_connection is called
|
|
20
|
-
on this MotorBundle.
|
|
21
|
-
|
|
22
|
-
Returns:
|
|
23
|
-
XYZLimitBundle: The limits for the underlying motors.
|
|
24
|
-
"""
|
|
25
|
-
return XYZLimitBundle(
|
|
26
|
-
MotorLimitHelper(self.x),
|
|
27
|
-
MotorLimitHelper(self.y),
|
|
28
|
-
MotorLimitHelper(self.z),
|
|
29
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|