dls-dodal 1.29.3__py3-none-any.whl → 1.29.4__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.3.dist-info → dls_dodal-1.29.4.dist-info}/METADATA +3 -3
- {dls_dodal-1.29.3.dist-info → dls_dodal-1.29.4.dist-info}/RECORD +21 -20
- {dls_dodal-1.29.3.dist-info → dls_dodal-1.29.4.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/i24.py +14 -0
- dodal/devices/areadetector/plugins/MJPG.py +0 -4
- dodal/devices/attenuator.py +4 -4
- dodal/devices/detector/detector.py +3 -2
- dodal/devices/eiger.py +12 -8
- dodal/devices/eiger_odin.py +3 -3
- dodal/devices/focusing_mirror.py +62 -62
- dodal/devices/i24/dcm.py +42 -0
- dodal/devices/oav/oav_detector.py +1 -0
- dodal/devices/oav/pin_image_recognition/__init__.py +13 -11
- dodal/devices/oav/utils.py +1 -15
- dodal/devices/util/epics_util.py +2 -2
- dodal/devices/webcam.py +1 -6
- dodal/utils.py +9 -4
- {dls_dodal-1.29.3.dist-info → dls_dodal-1.29.4.dist-info}/LICENSE +0 -0
- {dls_dodal-1.29.3.dist-info → dls_dodal-1.29.4.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.29.3.dist-info → dls_dodal-1.29.4.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.4
|
|
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
|
|
@@ -215,12 +215,12 @@ Description-Content-Type: text/x-rst
|
|
|
215
215
|
License-File: LICENSE
|
|
216
216
|
Requires-Dist: click
|
|
217
217
|
Requires-Dist: ophyd
|
|
218
|
-
Requires-Dist: ophyd-async
|
|
218
|
+
Requires-Dist: ophyd-async <0.4.0
|
|
219
219
|
Requires-Dist: bluesky
|
|
220
220
|
Requires-Dist: pyepics
|
|
221
221
|
Requires-Dist: dataclasses-json
|
|
222
222
|
Requires-Dist: pillow
|
|
223
|
-
Requires-Dist: zocalo
|
|
223
|
+
Requires-Dist: zocalo >=0.32.0
|
|
224
224
|
Requires-Dist: requests
|
|
225
225
|
Requires-Dist: graypy
|
|
226
226
|
Requires-Dist: pydantic
|
|
@@ -1,10 +1,10 @@
|
|
|
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=dGQcJRhmSkKpwrkZVgo13Sd0AVHTIBb9i0csee9mzMo,413
|
|
4
4
|
dodal/adsim.py,sha256=OW2dcS7ciD4Yq9WFw4PN_c5Bwccrmu7R-zr-u6ZCbQM,497
|
|
5
5
|
dodal/cli.py,sha256=z0UBESrNrq6Kq4rttp4uHcwS1fnOnRkKBRDHSriPpGY,2058
|
|
6
6
|
dodal/log.py,sha256=dfo1rfYrGG8oIm2HkNxaa_ldVs4vJKtgWSoKe1Z_Xno,8533
|
|
7
|
-
dodal/utils.py,sha256=
|
|
7
|
+
dodal/utils.py,sha256=SFDmUOLtxLe5OdNdVgS_EftNpX0ihxduBwq6tufw8s4,10174
|
|
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
|
|
10
10
|
dodal/beamlines/README.md,sha256=K9MkL_GomxlsoTB7Mz-_dJA5NNSbmCfMiutchGg3C8o,404
|
|
@@ -15,7 +15,7 @@ dodal/beamlines/i04_1.py,sha256=KDxSUQNhIs_NFiRaLY-Jiory0DeN7Y0ErvGuoTrwCDU,4731
|
|
|
15
15
|
dodal/beamlines/i20_1.py,sha256=MaPgONHqpoZuBtkiKEzYtViJnKBM2_ekeP4OdbmuXHE,1158
|
|
16
16
|
dodal/beamlines/i22.py,sha256=3VFdA4Wc7O40-64lwUtUBIN23fH4JVNbLKJ1JLjy9as,9870
|
|
17
17
|
dodal/beamlines/i23.py,sha256=2j5qLoqE_hg9ETHqNkOVu7LLkVB8qalgXeORnVYKN_I,1075
|
|
18
|
-
dodal/beamlines/i24.py,sha256=
|
|
18
|
+
dodal/beamlines/i24.py,sha256=tPSrWArwRd0fb59HyHFRGR1FdscM7t9_hNPk0edhh4o,6583
|
|
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
|
|
@@ -33,17 +33,17 @@ dodal/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
33
33
|
dodal/devices/adsim.py,sha256=dMU0TKIuiODHYFHQOH4_5UvB8iJtaJEtjqaEDGjcU-w,311
|
|
34
34
|
dodal/devices/aperture.py,sha256=0MtTzKMDZ5DVAz0DE0kXI0M76VCp0y9vFsrMggEMpxk,586
|
|
35
35
|
dodal/devices/aperturescatterguard.py,sha256=2JJsEPJGJHrI0ztv1cSaP7H5T6qdzDfUcN-VEQ39B8o,11012
|
|
36
|
-
dodal/devices/attenuator.py,sha256=
|
|
36
|
+
dodal/devices/attenuator.py,sha256=viK1iccNekX6ZvR_ZmSwj5JdM1j2B8pcTg8qWDdmzhQ,2584
|
|
37
37
|
dodal/devices/backlight.py,sha256=vsNGZB4C_mVMafllvJlOTghsfv6UqALMKUMLXu3WZ5k,1115
|
|
38
38
|
dodal/devices/beamstop.py,sha256=8L3qhlk-3ZBOp10xK1i8qZqYTGOXX1mVF1MgXoN0dfg,215
|
|
39
39
|
dodal/devices/cryostream.py,sha256=6MU4rXIOL33C-8F3DVfAtv0ZnwiysTtawjkeePd5IrQ,332
|
|
40
40
|
dodal/devices/dcm.py,sha256=vfyGYDzfSwTiNqlzkfNjkrL-Q1hNVSgJddvJ5Un_lvg,1610
|
|
41
|
-
dodal/devices/eiger.py,sha256=
|
|
42
|
-
dodal/devices/eiger_odin.py,sha256=
|
|
41
|
+
dodal/devices/eiger.py,sha256=HpZIZ6Y8iffo9GxmuFJmnyRCa5Tl0BKSKU2_wVccHDo,14069
|
|
42
|
+
dodal/devices/eiger_odin.py,sha256=zKNu5OaIQ9HkflfxKCi87Yr1kEG2gXfxzj0KPv0XzCk,6960
|
|
43
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
|
-
dodal/devices/focusing_mirror.py,sha256=
|
|
46
|
+
dodal/devices/focusing_mirror.py,sha256=v6TPglvmHiv9H05Lssv1LY5Nr9RBHQISLzL4SJ_QIbk,5890
|
|
47
47
|
dodal/devices/hutch_shutter.py,sha256=nZ3gRbYIVJsXLlpZMWT4UEYUFFQP1MwMe8Oy304QsqE,3360
|
|
48
48
|
dodal/devices/ipin.py,sha256=OGMXwAE4KDDonZRPFkUmR9Vsk6X4Ox-hEvPT5drP-mQ,208
|
|
49
49
|
dodal/devices/linkam3.py,sha256=TPhiQ1D9i_HIlKHAlfnVfX7H6aPOAeXPEJLdmvwdKWQ,3776
|
|
@@ -64,7 +64,7 @@ dodal/devices/thawer.py,sha256=hIdZOzCNloY7CtSvdE2gk4vCMMoOtaIA4dPH_k0OwFg,1527
|
|
|
64
64
|
dodal/devices/turbo_slit.py,sha256=W3ZRIqDhq4iMhr5GcIiWvl2U1GaPtGanqkL7upQOZTY,1132
|
|
65
65
|
dodal/devices/undulator.py,sha256=kn84MQpuBHtQj7H7HeBoAYKXu5buGKvTgs3tf2gdEdw,2074
|
|
66
66
|
dodal/devices/undulator_dcm.py,sha256=TC9fO55r1YIG_88PPbGGtzfjcRJcaoC2ny51JiDOEX4,5199
|
|
67
|
-
dodal/devices/webcam.py,sha256=
|
|
67
|
+
dodal/devices/webcam.py,sha256=dvNWJ6hHQR7BUsMRC9TH4XiCCofVhlgZ8HYfVCvd2og,1367
|
|
68
68
|
dodal/devices/xbpm_feedback.py,sha256=8QHYKHo9ksZo30olbFM-tHpCHcJRFozgHKVJijv3Gck,1986
|
|
69
69
|
dodal/devices/zebra.py,sha256=9Zkq5I3-gcP6qfDBnPEAtFU4QJ-VJyp7cHvB79ZfLHk,9186
|
|
70
70
|
dodal/devices/zebra_controlled_shutter.py,sha256=MqX4KE6w0FliZRDBltswcLCNSsp6vQrD_iBY640IljI,1094
|
|
@@ -72,12 +72,12 @@ dodal/devices/areadetector/__init__.py,sha256=8IwLxuZMW0MOJpJp_ZDdlaE20hrtsH_PXW
|
|
|
72
72
|
dodal/devices/areadetector/adaravis.py,sha256=pwbmmnakarjhD59XoyAIXJdakS-nqDG09Xmwq17AVw4,3787
|
|
73
73
|
dodal/devices/areadetector/adsim.py,sha256=3U7kS93RM3Xeh-XWKjeuw5jXbIGWAbrs59LfxtvB7OU,1907
|
|
74
74
|
dodal/devices/areadetector/adutils.py,sha256=JIx1_sYlehpLtEXcwOEuzVoMplsLdKVW7OWv5eiJqgE,2576
|
|
75
|
-
dodal/devices/areadetector/plugins/MJPG.py,sha256=
|
|
75
|
+
dodal/devices/areadetector/plugins/MJPG.py,sha256=2ISGUg9JxJYzEH640WUIsvFwKolSTywkTyPy9wz6f_k,4064
|
|
76
76
|
dodal/devices/detector/__init__.py,sha256=XEwjopgTtBq93RRuFthVVVI9DT1jUvpOJzWOHantJpU,104
|
|
77
77
|
dodal/devices/detector/det_dim_constants.py,sha256=MZ4w2nsTKzj4eN7yGsSs1pqKWIuU4vc6UzcSll02uWg,2305
|
|
78
78
|
dodal/devices/detector/det_dist_to_beam_converter.py,sha256=f6JFp-eEB2v8NzZg27UrN0VDP5CMjRnaPU6BTA7_n_s,1937
|
|
79
79
|
dodal/devices/detector/det_resolution.py,sha256=aQkKp24LpRGiwzPAQM3wLVa4ANw32HdrKc2kftHfKQA,3253
|
|
80
|
-
dodal/devices/detector/detector.py,sha256=
|
|
80
|
+
dodal/devices/detector/detector.py,sha256=7IYhndhT9CYCft0MrN1mAnopHqKYbaCRPRGLic76srw,4740
|
|
81
81
|
dodal/devices/detector/detector_motion.py,sha256=REREva2kyPcIzOZmahN9rT0jDSuUbV0qUDl4IcBnutA,1221
|
|
82
82
|
dodal/devices/i03/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
83
83
|
dodal/devices/i04/transfocator.py,sha256=uieByXIj0JRbmvMB_om5NOAEbEJkzfkCD24bl2aEo1g,3154
|
|
@@ -89,6 +89,7 @@ dodal/devices/i24/I24_detector_motion.py,sha256=Joqr1orgeNvRS7n01bjaO-4Yu4obb8fn
|
|
|
89
89
|
dodal/devices/i24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
90
90
|
dodal/devices/i24/aperture.py,sha256=kKfHli5oKp-j-qZhZoXTRK81SAUNyhpI6VRvtw0SkZA,850
|
|
91
91
|
dodal/devices/i24/beamstop.py,sha256=28hQowTvgN5Zw38tkDh32h2ceyN-2GE8bAaGPvDOt5U,1234
|
|
92
|
+
dodal/devices/i24/dcm.py,sha256=Hx5pQ-SHEQ_bLCOnpQYDm6-t-it7nNDmUvopGyiGA2w,1991
|
|
92
93
|
dodal/devices/i24/dual_backlight.py,sha256=Th-RKr28aFxE8LCT_mdN9KkRIVw0BHLGKkI0ienfRZU,2049
|
|
93
94
|
dodal/devices/i24/i24_vgonio.py,sha256=Igqs7687z6lyhGVeJEDtDmPachYxU48MUH2BF0RpK9Q,461
|
|
94
95
|
dodal/devices/i24/pmac.py,sha256=pN54myYvzqPl7iW0Vsp59J1EiV_gtn0xQGwbsKJpiYE,3876
|
|
@@ -96,16 +97,16 @@ dodal/devices/oav/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
96
97
|
dodal/devices/oav/grid_overlay.py,sha256=FRtjcFd420XY8MEQ9sWedL0i4pK-KUJOSxh2C5zM3PA,5232
|
|
97
98
|
dodal/devices/oav/microns_for_zoom_levels.json,sha256=5PA71RzldFTp0eTUGPmov0MjxHe583mzvfor5f3thXI,1208
|
|
98
99
|
dodal/devices/oav/oav_calculations.py,sha256=wt71vFcyQrr98FvX8oyUM2n5vmKi3K7PyOTuWp0gq5w,1665
|
|
99
|
-
dodal/devices/oav/oav_detector.py,sha256=
|
|
100
|
+
dodal/devices/oav/oav_detector.py,sha256=PlRIuMYSk46o6Aywel9tMZYh1WzMaBhfn3_GWqCas7w,3681
|
|
100
101
|
dodal/devices/oav/oav_errors.py,sha256=cc4mGnaTiAc5WIlOt_BIYOc7CRSkrCdnBaavfAJ0pXY,754
|
|
101
102
|
dodal/devices/oav/oav_parameters.py,sha256=4XybkhKeG7IEjPRfx0PVM9KNenuyN0rAGWBZG7H3zvQ,7941
|
|
102
|
-
dodal/devices/oav/utils.py,sha256=
|
|
103
|
-
dodal/devices/oav/pin_image_recognition/__init__.py,sha256=
|
|
103
|
+
dodal/devices/oav/utils.py,sha256=BkTk0aTqqhIHCZInhcUAYjCRPxumPOlTg9m21skncTc,3018
|
|
104
|
+
dodal/devices/oav/pin_image_recognition/__init__.py,sha256=Eve6oY9EYGOSw1x-N--xjgyIEzbZE7TTwGR-Q2NZprQ,6441
|
|
104
105
|
dodal/devices/oav/pin_image_recognition/manual_test.py,sha256=h1Rto6ZDCB3jWhjSy9N8ECxRN583iYDJr9LxrTJ8kfE,903
|
|
105
106
|
dodal/devices/oav/pin_image_recognition/utils.py,sha256=-7-Zs-331UVTq_AZrfdF-zwZdmMn7eitTkBSqnBrxnk,8620
|
|
106
107
|
dodal/devices/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
107
108
|
dodal/devices/util/adjuster_plans.py,sha256=2AYaywQP_LbA2KJ6Op3cok8GoRtj696utrSSDfaJtBY,875
|
|
108
|
-
dodal/devices/util/epics_util.py,sha256=
|
|
109
|
+
dodal/devices/util/epics_util.py,sha256=4AOGqYjF0ITzs4c7PTZZnHge81Zheg4gZbFonVW7Lko,4728
|
|
109
110
|
dodal/devices/util/lookup_tables.py,sha256=Up-0BlARt79TIEM76SkDyn9LtTFLxPUcaEPZv6D6bws,2141
|
|
110
111
|
dodal/devices/util/motor_utils.py,sha256=pNY-aUk9LxaIWeDr5rpMS6udiB9j19wcCXkNDLp1uA0,257
|
|
111
112
|
dodal/devices/xspress3/xspress3.py,sha256=29elzI3JtceryKeMWXhcP9nWl0tlSdnTZhltCitet6A,4668
|
|
@@ -116,9 +117,9 @@ dodal/devices/zocalo/zocalo_results.py,sha256=U4Vk4OF-eL8w0BR-fbw3k4jyRo6G3Ywaf8
|
|
|
116
117
|
dodal/parameters/experiment_parameter_base.py,sha256=O7JamfuJ5cYHkPf9tsHJPqn-OMHTAGouigvM1cDFehE,313
|
|
117
118
|
dodal/plans/check_topup.py,sha256=Pj6Eu8fa6nvoW4awrMxvzE_ftpLfYz8bN0QDLRw0Yuk,2989
|
|
118
119
|
dodal/plans/data_session_metadata.py,sha256=QNx9rb1EfGBHb21eFekIi7KjNhC0PL-SVKBCggDuNeg,1650
|
|
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.
|
|
124
|
-
dls_dodal-1.29.
|
|
120
|
+
dls_dodal-1.29.4.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
|
121
|
+
dls_dodal-1.29.4.dist-info/METADATA,sha256=biJK1f1BESw8LgPh4Rl0GrxC9cMJ2WCYxP5NqGPBzak,16950
|
|
122
|
+
dls_dodal-1.29.4.dist-info/WHEEL,sha256=-oYQCr74JF3a37z2nRlQays_SX2MqOANoqVjBBAP2yE,91
|
|
123
|
+
dls_dodal-1.29.4.dist-info/entry_points.txt,sha256=wpzz9FsTiYxI8OBwLKX9V9ResLwThBSmtRMcPwII0FA,46
|
|
124
|
+
dls_dodal-1.29.4.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
|
|
125
|
+
dls_dodal-1.29.4.dist-info/RECORD,,
|
dodal/_version.py
CHANGED
dodal/beamlines/i24.py
CHANGED
|
@@ -5,6 +5,7 @@ from dodal.devices.eiger import EigerDetector
|
|
|
5
5
|
from dodal.devices.hutch_shutter import HutchShutter
|
|
6
6
|
from dodal.devices.i24.aperture import Aperture
|
|
7
7
|
from dodal.devices.i24.beamstop import Beamstop
|
|
8
|
+
from dodal.devices.i24.dcm import DCM
|
|
8
9
|
from dodal.devices.i24.dual_backlight import DualBacklight
|
|
9
10
|
from dodal.devices.i24.I24_detector_motion import DetectorMotion
|
|
10
11
|
from dodal.devices.i24.i24_vgonio import VGonio
|
|
@@ -82,6 +83,19 @@ def detector_motion(
|
|
|
82
83
|
)
|
|
83
84
|
|
|
84
85
|
|
|
86
|
+
def dcm(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> DCM:
|
|
87
|
+
"""Get the i24 DCM device, instantiate it if it hasn't already been.
|
|
88
|
+
If this is called when already instantiated in i24, it will return the existing object.
|
|
89
|
+
"""
|
|
90
|
+
return device_instantiation(
|
|
91
|
+
device_factory=DCM,
|
|
92
|
+
name="dcm",
|
|
93
|
+
prefix="",
|
|
94
|
+
wait=wait_for_connection,
|
|
95
|
+
fake=fake_with_ophyd_sim,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
85
99
|
@skip_device(lambda: BL == "s24")
|
|
86
100
|
def eiger(
|
|
87
101
|
wait_for_connection: bool = True,
|
|
@@ -8,7 +8,6 @@ from ophyd import Component, Device, DeviceStatus, EpicsSignal, EpicsSignalRO, S
|
|
|
8
8
|
from PIL import Image, ImageDraw
|
|
9
9
|
|
|
10
10
|
from dodal.devices.oav.oav_parameters import OAVConfigParams
|
|
11
|
-
from dodal.devices.oav.utils import save_thumbnail
|
|
12
11
|
from dodal.log import LOGGER
|
|
13
12
|
|
|
14
13
|
|
|
@@ -50,9 +49,6 @@ class MJPG(Device, ABC):
|
|
|
50
49
|
|
|
51
50
|
LOGGER.info(f"Saving image to {path}")
|
|
52
51
|
image.save(path)
|
|
53
|
-
|
|
54
|
-
save_thumbnail(Path(path), image)
|
|
55
|
-
|
|
56
52
|
self.last_saved_path.put(path)
|
|
57
53
|
|
|
58
54
|
def trigger(self):
|
dodal/devices/attenuator.py
CHANGED
|
@@ -48,7 +48,7 @@ class Attenuator(StandardReadable, Movable):
|
|
|
48
48
|
super().__init__(name)
|
|
49
49
|
|
|
50
50
|
@AsyncStatus.wrap
|
|
51
|
-
async def set(self,
|
|
51
|
+
async def set(self, value: float):
|
|
52
52
|
"""Set the transmission to the fractional (0-1) value given.
|
|
53
53
|
|
|
54
54
|
The attenuator IOC will then insert filters to reach the desired transmission for
|
|
@@ -58,8 +58,8 @@ class Attenuator(StandardReadable, Movable):
|
|
|
58
58
|
|
|
59
59
|
LOGGER.debug("Using current energy ")
|
|
60
60
|
await self._use_current_energy.trigger()
|
|
61
|
-
LOGGER.info(f"Setting desired transmission to {
|
|
62
|
-
await self._desired_transmission.set(
|
|
61
|
+
LOGGER.info(f"Setting desired transmission to {value}")
|
|
62
|
+
await self._desired_transmission.set(value)
|
|
63
63
|
LOGGER.debug("Sending change filter command")
|
|
64
64
|
await self._change.trigger()
|
|
65
65
|
|
|
@@ -67,7 +67,7 @@ class Attenuator(StandardReadable, Movable):
|
|
|
67
67
|
*[
|
|
68
68
|
wait_for_value(
|
|
69
69
|
self._filters_in_position[i],
|
|
70
|
-
await self._calculated_filter_states[i].get_value(),
|
|
70
|
+
bool(await self._calculated_filter_states[i].get_value()),
|
|
71
71
|
None,
|
|
72
72
|
)
|
|
73
73
|
for i in range(16)
|
|
@@ -54,13 +54,14 @@ class DetectorParams(BaseModel):
|
|
|
54
54
|
DetectorSizeConstants: lambda d: d.det_type_string,
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
# should be replaced with model_validator once move to pydantic 2 is complete
|
|
58
|
+
@root_validator(pre=True)
|
|
58
59
|
def create_beamxy_and_runnumber(cls, values: dict[str, Any]) -> dict[str, Any]:
|
|
59
60
|
values["beam_xy_converter"] = DetectorDistanceToBeamXYConverter(
|
|
60
61
|
values["det_dist_to_beam_converter_path"]
|
|
61
62
|
)
|
|
62
63
|
if values.get("run_number") is None:
|
|
63
|
-
values["run_number"] = get_run_number(values["directory"])
|
|
64
|
+
values["run_number"] = get_run_number(values["directory"], values["prefix"])
|
|
64
65
|
return values
|
|
65
66
|
|
|
66
67
|
@validator("detector_size_constants", pre=True)
|
dodal/devices/eiger.py
CHANGED
|
@@ -2,7 +2,7 @@ from enum import Enum
|
|
|
2
2
|
|
|
3
3
|
from ophyd import Component, Device, EpicsSignalRO, Signal
|
|
4
4
|
from ophyd.areadetector.cam import EigerDetectorCam
|
|
5
|
-
from ophyd.status import AndStatus, Status,
|
|
5
|
+
from ophyd.status import AndStatus, Status, StatusBase
|
|
6
6
|
|
|
7
7
|
from dodal.devices.detector import DetectorParams, TriggerMode
|
|
8
8
|
from dodal.devices.eiger_odin import EigerOdin
|
|
@@ -27,6 +27,7 @@ class InternalEigerTriggerMode(Enum):
|
|
|
27
27
|
class EigerDetector(Device):
|
|
28
28
|
class ArmingSignal(Signal):
|
|
29
29
|
def set(self, value, *, timeout=None, settle_time=None, **kwargs):
|
|
30
|
+
assert isinstance(self.parent, EigerDetector)
|
|
30
31
|
return self.parent.async_stage()
|
|
31
32
|
|
|
32
33
|
do_arm = Component(ArmingSignal)
|
|
@@ -38,10 +39,12 @@ class EigerDetector(Device):
|
|
|
38
39
|
|
|
39
40
|
STALE_PARAMS_TIMEOUT = 60
|
|
40
41
|
GENERAL_STATUS_TIMEOUT = 10
|
|
42
|
+
# Long timeout for meta file to compensate for filesystem issues
|
|
43
|
+
META_FILE_READY_TIMEOUT = 30
|
|
41
44
|
ALL_FRAMES_TIMEOUT = 120
|
|
42
45
|
ARMING_TIMEOUT = 60
|
|
43
46
|
|
|
44
|
-
filewriters_finished:
|
|
47
|
+
filewriters_finished: StatusBase
|
|
45
48
|
|
|
46
49
|
detector_params: DetectorParams | None = None
|
|
47
50
|
|
|
@@ -155,7 +158,7 @@ class EigerDetector(Device):
|
|
|
155
158
|
def enable_roi_mode(self):
|
|
156
159
|
return self.change_roi_mode(True)
|
|
157
160
|
|
|
158
|
-
def change_roi_mode(self, enable: bool) ->
|
|
161
|
+
def change_roi_mode(self, enable: bool) -> StatusBase:
|
|
159
162
|
assert self.detector_params is not None
|
|
160
163
|
detector_dimensions = (
|
|
161
164
|
self.detector_params.detector_size_constants.roi_size_pixels
|
|
@@ -206,7 +209,7 @@ class EigerDetector(Device):
|
|
|
206
209
|
)
|
|
207
210
|
return status
|
|
208
211
|
|
|
209
|
-
def set_odin_pvs(self) ->
|
|
212
|
+
def set_odin_pvs(self) -> StatusBase:
|
|
210
213
|
assert self.detector_params is not None
|
|
211
214
|
file_prefix = self.detector_params.full_filename
|
|
212
215
|
status = self.odin.file_writer.file_path.set(
|
|
@@ -264,7 +267,7 @@ class EigerDetector(Device):
|
|
|
264
267
|
status.set_finished()
|
|
265
268
|
return status
|
|
266
269
|
|
|
267
|
-
def set_num_triggers_and_captures(self) ->
|
|
270
|
+
def set_num_triggers_and_captures(self) -> StatusBase:
|
|
268
271
|
"""Sets the number of triggers and the number of images for the Eiger to capture
|
|
269
272
|
during the datacollection. The number of images is the number of images per
|
|
270
273
|
trigger.
|
|
@@ -295,7 +298,7 @@ class EigerDetector(Device):
|
|
|
295
298
|
|
|
296
299
|
return status
|
|
297
300
|
|
|
298
|
-
def _wait_for_odin_status(self) ->
|
|
301
|
+
def _wait_for_odin_status(self) -> StatusBase:
|
|
299
302
|
self.forward_bit_depth_to_filewriter()
|
|
300
303
|
await_value(self.odin.meta.active, 1).wait(self.GENERAL_STATUS_TIMEOUT)
|
|
301
304
|
|
|
@@ -304,11 +307,11 @@ class EigerDetector(Device):
|
|
|
304
307
|
)
|
|
305
308
|
LOGGER.info("Eiger staging: awaiting odin metadata")
|
|
306
309
|
status &= await_value(
|
|
307
|
-
self.odin.meta.ready, 1, timeout=self.
|
|
310
|
+
self.odin.meta.ready, 1, timeout=self.META_FILE_READY_TIMEOUT
|
|
308
311
|
)
|
|
309
312
|
return status
|
|
310
313
|
|
|
311
|
-
def _wait_fan_ready(self) ->
|
|
314
|
+
def _wait_fan_ready(self) -> StatusBase:
|
|
312
315
|
self.filewriters_finished = self.odin.create_finished_status()
|
|
313
316
|
LOGGER.info("Eiger staging: awaiting odin fan ready")
|
|
314
317
|
return await_value(self.odin.fan.ready, 1, self.GENERAL_STATUS_TIMEOUT)
|
|
@@ -332,6 +335,7 @@ class EigerDetector(Device):
|
|
|
332
335
|
|
|
333
336
|
def do_arming_chain(self) -> Status:
|
|
334
337
|
functions_to_do_arm = []
|
|
338
|
+
assert self.detector_params
|
|
335
339
|
detector_params: DetectorParams = self.detector_params
|
|
336
340
|
if detector_params.use_roi_mode:
|
|
337
341
|
functions_to_do_arm.append(self.enable_roi_mode)
|
dodal/devices/eiger_odin.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import List, Tuple
|
|
|
3
3
|
from ophyd import Component, Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
|
|
4
4
|
from ophyd.areadetector.plugins import HDF5Plugin_V22
|
|
5
5
|
from ophyd.sim import NullStatus
|
|
6
|
-
from ophyd.status import
|
|
6
|
+
from ophyd.status import StatusBase
|
|
7
7
|
|
|
8
8
|
from dodal.devices.status import await_value
|
|
9
9
|
|
|
@@ -120,7 +120,7 @@ class EigerOdin(Device):
|
|
|
120
120
|
meta = Component(OdinMetaListener, "OD:META:")
|
|
121
121
|
nodes = Component(OdinNodesStatus, "")
|
|
122
122
|
|
|
123
|
-
def create_finished_status(self) ->
|
|
123
|
+
def create_finished_status(self) -> StatusBase:
|
|
124
124
|
writing_finished = await_value(self.meta.ready, 0)
|
|
125
125
|
for node_pv in self.nodes.nodes:
|
|
126
126
|
writing_finished &= await_value(node_pv.writing, 0)
|
|
@@ -157,7 +157,7 @@ class EigerOdin(Device):
|
|
|
157
157
|
|
|
158
158
|
return not errors, "\n".join(errors)
|
|
159
159
|
|
|
160
|
-
def stop(self) ->
|
|
160
|
+
def stop(self) -> StatusBase:
|
|
161
161
|
"""Stop odin manually"""
|
|
162
162
|
status = self.file_writer.capture.set(0)
|
|
163
163
|
status &= self.meta.stop_writing.set(1)
|
dodal/devices/focusing_mirror.py
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
from enum import Enum
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import (
|
|
4
|
+
AsyncStatus,
|
|
5
|
+
Device,
|
|
6
|
+
DeviceVector,
|
|
7
|
+
StandardReadable,
|
|
8
|
+
observe_value,
|
|
9
|
+
)
|
|
7
10
|
from ophyd_async.core.signal import soft_signal_r_and_setter
|
|
8
11
|
from ophyd_async.epics.motion import Motor
|
|
9
12
|
from ophyd_async.epics.signal import (
|
|
13
|
+
epics_signal_r,
|
|
10
14
|
epics_signal_rw,
|
|
11
15
|
epics_signal_x,
|
|
12
16
|
)
|
|
@@ -32,11 +36,11 @@ class MirrorStripe(str, Enum):
|
|
|
32
36
|
PLATINUM = "Platinum"
|
|
33
37
|
|
|
34
38
|
|
|
35
|
-
class MirrorVoltageDemand(
|
|
36
|
-
N_A =
|
|
37
|
-
OK =
|
|
38
|
-
FAIL =
|
|
39
|
-
SLEW =
|
|
39
|
+
class MirrorVoltageDemand(str, Enum):
|
|
40
|
+
N_A = "N/A"
|
|
41
|
+
OK = "OK"
|
|
42
|
+
FAIL = "FAIL"
|
|
43
|
+
SLEW = "SLEW"
|
|
40
44
|
|
|
41
45
|
|
|
42
46
|
class MirrorVoltageDevice(Device):
|
|
@@ -44,14 +48,16 @@ class MirrorVoltageDevice(Device):
|
|
|
44
48
|
the demanded voltage setpoint is accepted, without blocking the caller as this process can take significant time.
|
|
45
49
|
"""
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
def __init__(self, name: str = "", prefix: str = ""):
|
|
52
|
+
self._actual_v = epics_signal_r(int, prefix + "R")
|
|
53
|
+
self._setpoint_v = epics_signal_rw(int, prefix + "D")
|
|
54
|
+
self._demand_accepted = epics_signal_r(MirrorVoltageDemand, prefix + "DSEV")
|
|
55
|
+
super().__init__(name=name)
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
@AsyncStatus.wrap
|
|
58
|
+
async def set(self, value, *args, **kwargs):
|
|
52
59
|
"""Combine the following operations into a single set:
|
|
53
60
|
1. apply the value to the setpoint PV
|
|
54
|
-
2. Return to the caller with a Status future
|
|
55
61
|
3. Wait until demand is accepted
|
|
56
62
|
4. When either demand is accepted or DEFAULT_SETTLE_TIME expires, signal the result on the Status
|
|
57
63
|
"""
|
|
@@ -59,66 +65,60 @@ class MirrorVoltageDevice(Device):
|
|
|
59
65
|
setpoint_v = self._setpoint_v
|
|
60
66
|
demand_accepted = self._demand_accepted
|
|
61
67
|
|
|
62
|
-
if demand_accepted.
|
|
68
|
+
if await demand_accepted.get_value() != MirrorVoltageDemand.OK:
|
|
63
69
|
raise AssertionError(
|
|
64
70
|
f"Attempted to set {setpoint_v.name} when demand is not accepted."
|
|
65
71
|
)
|
|
66
72
|
|
|
67
|
-
if setpoint_v.
|
|
73
|
+
if await setpoint_v.get_value() == value:
|
|
68
74
|
LOGGER.debug(f"{setpoint_v.name} already at {value} - skipping set")
|
|
69
|
-
return
|
|
75
|
+
return
|
|
70
76
|
|
|
71
77
|
LOGGER.debug(f"setting {setpoint_v.name} to {value}")
|
|
72
|
-
demand_accepted_status = Status(self, DEFAULT_SETTLE_TIME_S)
|
|
73
|
-
|
|
74
|
-
subscription: dict[str, Any] = {"handle": None}
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
# Register an observer up front to ensure we don't miss events after we
|
|
80
|
+
# perform the set
|
|
81
|
+
demand_accepted_iterator = observe_value(
|
|
82
|
+
demand_accepted, timeout=DEFAULT_SETTLE_TIME_S
|
|
83
|
+
)
|
|
84
|
+
# discard the current value (OK) so we can await a subsequent change
|
|
85
|
+
await anext(demand_accepted_iterator)
|
|
86
|
+
await setpoint_v.set(value)
|
|
87
|
+
|
|
88
|
+
# The set should always change to SLEW regardless of whether we are
|
|
89
|
+
# already at the set point, then change back to OK/FAIL depending on
|
|
90
|
+
# success
|
|
91
|
+
accepted_value = await anext(demand_accepted_iterator)
|
|
92
|
+
assert accepted_value == MirrorVoltageDemand.SLEW
|
|
93
|
+
LOGGER.debug(
|
|
94
|
+
f"Demand not accepted for {setpoint_v.name}, waiting for acceptance..."
|
|
95
|
+
)
|
|
96
|
+
while MirrorVoltageDemand.SLEW == (
|
|
97
|
+
accepted_value := await anext(demand_accepted_iterator)
|
|
98
|
+
):
|
|
99
|
+
pass
|
|
87
100
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
if accepted_value != MirrorVoltageDemand.OK:
|
|
102
|
+
raise AssertionError(
|
|
103
|
+
f"Voltage slew failed for {setpoint_v.name}, new state={accepted_value}"
|
|
104
|
+
)
|
|
92
105
|
|
|
93
106
|
|
|
94
|
-
class VFMMirrorVoltages(
|
|
95
|
-
def __init__(
|
|
96
|
-
|
|
107
|
+
class VFMMirrorVoltages(StandardReadable):
|
|
108
|
+
def __init__(
|
|
109
|
+
self, name: str, prefix: str, *args, daq_configuration_path: str, **kwargs
|
|
110
|
+
):
|
|
97
111
|
self.voltage_lookup_table_path = (
|
|
98
112
|
daq_configuration_path + "/json/mirrorFocus.json"
|
|
99
113
|
)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
_channel21_voltage_device = Component(MirrorVoltageDevice, "BM:V21")
|
|
109
|
-
|
|
110
|
-
@property
|
|
111
|
-
def voltage_channels(self) -> list[MirrorVoltageDevice]:
|
|
112
|
-
return [
|
|
113
|
-
self._channel14_voltage_device,
|
|
114
|
-
self._channel15_voltage_device,
|
|
115
|
-
self._channel16_voltage_device,
|
|
116
|
-
self._channel17_voltage_device,
|
|
117
|
-
self._channel18_voltage_device,
|
|
118
|
-
self._channel19_voltage_device,
|
|
119
|
-
self._channel20_voltage_device,
|
|
120
|
-
self._channel21_voltage_device,
|
|
121
|
-
]
|
|
114
|
+
with self.add_children_as_readables():
|
|
115
|
+
self.voltage_channels = DeviceVector(
|
|
116
|
+
{
|
|
117
|
+
i - 14: MirrorVoltageDevice(prefix=f"{prefix}BM:V{i}")
|
|
118
|
+
for i in range(14, 22)
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
super().__init__(*args, name=name, **kwargs)
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
class FocusingMirror(StandardReadable):
|
dodal/devices/i24/dcm.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from ophyd_async.core import StandardReadable
|
|
2
|
+
from ophyd_async.epics.motion import Motor
|
|
3
|
+
from ophyd_async.epics.signal import epics_signal_r
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DCM(StandardReadable):
|
|
7
|
+
"""
|
|
8
|
+
A double crystal monocromator device, used to select the beam energy.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
12
|
+
with self.add_children_as_readables():
|
|
13
|
+
# Motors
|
|
14
|
+
self.bragg_in_degrees = Motor(prefix + "-MO-DCM-01:BRAGG")
|
|
15
|
+
self.x_translation_in_mm = Motor(prefix + "-MO-DCM-01:X")
|
|
16
|
+
self.offset_in_mm = Motor(prefix + "-MO-DCM-01:OFFSET")
|
|
17
|
+
self.gap_in_mm = Motor(prefix + "-MO-DCM-01:GAP")
|
|
18
|
+
self.energy_in_kev = Motor(prefix + "-MO-DCM-01:ENERGY")
|
|
19
|
+
self.xtal1_roll = Motor(prefix + "-MO-DCM-01:XTAL1:ROLL")
|
|
20
|
+
self.xtal2_roll = Motor(prefix + "-MO-DCM-01:XTAL2:ROLL")
|
|
21
|
+
self.xtal2_pitch = Motor(prefix + "-MO-DCM-01:XTAL2:PITCH")
|
|
22
|
+
|
|
23
|
+
# Wavelength is calculated in epics from the energy
|
|
24
|
+
self.wavelength_in_a = epics_signal_r(float, prefix + "-MO-DCM-01:LAMBDA")
|
|
25
|
+
|
|
26
|
+
# Temperatures
|
|
27
|
+
self.xtal1_temp = epics_signal_r(float, prefix + "-DI-DCM-01:PT100-1")
|
|
28
|
+
self.xtal1_heater_temp = epics_signal_r(
|
|
29
|
+
float, prefix + "-DI-DCM-01:PT100-2"
|
|
30
|
+
)
|
|
31
|
+
self.xtal2_temp = epics_signal_r(float, prefix + "-DI-DCM-01:PT100-4")
|
|
32
|
+
self.xtal2_heater_temp = epics_signal_r(
|
|
33
|
+
float, prefix + "-DI-DCM-01:PT100-5"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
self.roll_plate_temp = epics_signal_r(float, prefix + "-DI-DCM-01:PT100-3")
|
|
37
|
+
self.pitch_plate_temp = epics_signal_r(float, prefix + "-DI-DCM-01:PT100-6")
|
|
38
|
+
self.backplate_temp = epics_signal_r(float, prefix + "-DI-DCM-01:PT100-7")
|
|
39
|
+
self.b1_plate_temp = epics_signal_r(float, prefix + "-DI-DCM-01:PT100-7")
|
|
40
|
+
self.gap_temp = epics_signal_r(float, prefix + "-DI-DCM-01:TC-1")
|
|
41
|
+
|
|
42
|
+
super().__init__(name)
|
|
@@ -46,6 +46,7 @@ class ZoomController(Device):
|
|
|
46
46
|
sxst = Component(EpicsSignal, "MP:SELECT.SXST")
|
|
47
47
|
|
|
48
48
|
def set_flatfield_on_zoom_level_one(self, value):
|
|
49
|
+
self.parent: "OAV"
|
|
49
50
|
flat_applied = self.parent.proc.port_name.get()
|
|
50
51
|
no_flat_applied = self.parent.cam.port_name.get()
|
|
51
52
|
return self.parent.grid_snapshot.input_plugin.set(
|
|
@@ -50,11 +50,13 @@ class PinTipDetection(StandardReadable):
|
|
|
50
50
|
self._prefix: str = prefix
|
|
51
51
|
self._name = name
|
|
52
52
|
|
|
53
|
-
self.triggered_tip,
|
|
54
|
-
|
|
53
|
+
self.triggered_tip, self._tip_setter = soft_signal_r_and_setter(
|
|
54
|
+
Tip, name="triggered_tip"
|
|
55
|
+
)
|
|
56
|
+
self.triggered_top_edge, self._top_edge_setter = soft_signal_r_and_setter(
|
|
55
57
|
NDArray[np.uint32], name="triggered_top_edge"
|
|
56
58
|
)
|
|
57
|
-
self.triggered_bottom_edge,
|
|
59
|
+
self.triggered_bottom_edge, self._bottom_edge_setter = soft_signal_r_and_setter(
|
|
58
60
|
NDArray[np.uint32], name="triggered_bottom_edge"
|
|
59
61
|
)
|
|
60
62
|
self.array_data = epics_signal_r(NDArray[np.uint8], f"pva://{prefix}PVA:ARRAY")
|
|
@@ -85,14 +87,14 @@ class PinTipDetection(StandardReadable):
|
|
|
85
87
|
|
|
86
88
|
super().__init__(name=name)
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
def _set_triggered_values(self, results: SampleLocation):
|
|
89
91
|
tip = (results.tip_x, results.tip_y)
|
|
90
92
|
if tip == self.INVALID_POSITION:
|
|
91
93
|
raise InvalidPinException
|
|
92
94
|
else:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
self._tip_setter(tip)
|
|
96
|
+
self._top_edge_setter(results.edge_top)
|
|
97
|
+
self._bottom_edge_setter(results.edge_bottom)
|
|
96
98
|
|
|
97
99
|
async def _get_tip_and_edge_data(
|
|
98
100
|
self, array_data: NDArray[np.uint8]
|
|
@@ -150,7 +152,7 @@ class PinTipDetection(StandardReadable):
|
|
|
150
152
|
async for value in observe_value(self.array_data):
|
|
151
153
|
try:
|
|
152
154
|
location = await self._get_tip_and_edge_data(value)
|
|
153
|
-
|
|
155
|
+
self._set_triggered_values(location)
|
|
154
156
|
except Exception as e:
|
|
155
157
|
LOGGER.warn(
|
|
156
158
|
f"Failed to detect pin-tip location, will retry with next image: {e}"
|
|
@@ -166,6 +168,6 @@ class PinTipDetection(StandardReadable):
|
|
|
166
168
|
LOGGER.error(
|
|
167
169
|
f"No tip found in {await self.validity_timeout.get_value()} seconds."
|
|
168
170
|
)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
171
|
+
self._tip_setter(self.INVALID_POSITION)
|
|
172
|
+
self._bottom_edge_setter(np.array([]))
|
|
173
|
+
self._top_edge_setter(np.array([]))
|
dodal/devices/oav/utils.py
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
from enum import IntEnum
|
|
2
|
-
from pathlib import Path
|
|
3
2
|
from typing import Generator, Tuple
|
|
4
3
|
|
|
5
4
|
import bluesky.plan_stubs as bps
|
|
6
5
|
import numpy as np
|
|
7
6
|
from bluesky.utils import Msg
|
|
8
|
-
from PIL.Image import Image
|
|
9
7
|
|
|
10
8
|
from dodal.devices.oav.oav_calculations import camera_coordinates_to_xyz
|
|
11
|
-
from dodal.devices.oav.
|
|
9
|
+
from dodal.devices.oav.oav_detector import OAVConfigParams
|
|
12
10
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
13
11
|
from dodal.devices.smargon import Smargon
|
|
14
|
-
from dodal.log import LOGGER
|
|
15
12
|
|
|
16
13
|
Pixel = Tuple[int, int]
|
|
17
14
|
|
|
@@ -110,14 +107,3 @@ def wait_for_tip_to_be_found(
|
|
|
110
107
|
raise PinNotFoundException(f"No pin found after {timeout} seconds")
|
|
111
108
|
|
|
112
109
|
return found_tip # type: ignore
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def save_thumbnail(full_file_path: Path, full_image: Image, new_height=192):
|
|
116
|
-
"""Scales an image down to have the height specified in new_height and saves it
|
|
117
|
-
to the same location as the full image with a t appended to the filename"""
|
|
118
|
-
thumbnail_path = full_file_path.with_stem(full_file_path.stem + "t")
|
|
119
|
-
LOGGER.info(f"Saving thumbnail to {thumbnail_path}")
|
|
120
|
-
full_size = full_image.size
|
|
121
|
-
new_width = (new_height / full_size[1]) * full_size[0]
|
|
122
|
-
full_image.thumbnail((new_width, new_height))
|
|
123
|
-
full_image.save(thumbnail_path.as_posix())
|
dodal/devices/util/epics_util.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from functools import partial
|
|
2
|
-
from typing import Callable
|
|
2
|
+
from typing import Callable, Sequence
|
|
3
3
|
|
|
4
4
|
from bluesky.protocols import Movable
|
|
5
5
|
from ophyd import Component, EpicsSignal
|
|
@@ -26,7 +26,7 @@ def epics_signal_put_wait(pv_name: str, wait: float = 3.0) -> Component[EpicsSig
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def run_functions_without_blocking(
|
|
29
|
-
functions_to_chain:
|
|
29
|
+
functions_to_chain: Sequence[Callable[[], StatusBase]],
|
|
30
30
|
timeout: float = 60.0,
|
|
31
31
|
associated_obj: OphydDevice | None = None,
|
|
32
32
|
) -> Status:
|
dodal/devices/webcam.py
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import io
|
|
2
1
|
from pathlib import Path
|
|
3
2
|
|
|
4
3
|
import aiofiles
|
|
5
4
|
from aiohttp import ClientSession
|
|
6
5
|
from bluesky.protocols import Triggerable
|
|
7
6
|
from ophyd_async.core import AsyncStatus, StandardReadable, soft_signal_rw
|
|
8
|
-
from PIL import Image
|
|
9
7
|
|
|
10
|
-
from dodal.devices.oav.utils import save_thumbnail
|
|
11
8
|
from dodal.log import LOGGER
|
|
12
9
|
|
|
13
10
|
|
|
@@ -26,10 +23,8 @@ class Webcam(StandardReadable, Triggerable):
|
|
|
26
23
|
async with session.get(self.url) as response:
|
|
27
24
|
response.raise_for_status()
|
|
28
25
|
LOGGER.info(f"Saving webcam image from {self.url} to {file_path}")
|
|
29
|
-
data = await response.read()
|
|
30
26
|
async with aiofiles.open(file_path, "wb") as file:
|
|
31
|
-
await file.write(
|
|
32
|
-
save_thumbnail(Path(file_path), Image.open(io.BytesIO(data)))
|
|
27
|
+
await file.write((await response.read()))
|
|
33
28
|
|
|
34
29
|
@AsyncStatus.wrap
|
|
35
30
|
async def trigger(self) -> None:
|
dodal/utils.py
CHANGED
|
@@ -322,10 +322,15 @@ def _find_next_run_number_from_files(file_names: List[str]) -> int:
|
|
|
322
322
|
return max(valid_numbers) + 1 if valid_numbers else 1
|
|
323
323
|
|
|
324
324
|
|
|
325
|
-
def get_run_number(directory: str) -> int:
|
|
326
|
-
"""Looks at the numbers coming from all nexus files with the format
|
|
327
|
-
or 1 if there are
|
|
328
|
-
|
|
325
|
+
def get_run_number(directory: str, prefix: str = "") -> int:
|
|
326
|
+
"""Looks at the numbers coming from all nexus files with the format
|
|
327
|
+
"{prefix}_(any number}.nxs", and returns the highest number + 1, or 1 if there are
|
|
328
|
+
no matching numbers found. If no prefix is given, considers all files in the dir."""
|
|
329
|
+
nexus_file_names = [
|
|
330
|
+
file
|
|
331
|
+
for file in os.listdir(directory)
|
|
332
|
+
if file.endswith(".nxs") and file.startswith(prefix)
|
|
333
|
+
]
|
|
329
334
|
|
|
330
335
|
if len(nexus_file_names) == 0:
|
|
331
336
|
return 1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|