dls-dodal 1.36.1a0__py3-none-any.whl → 1.36.2__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.1
2
2
  Name: dls-dodal
3
- Version: 1.36.1a0
3
+ Version: 1.36.2
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
@@ -1,14 +1,14 @@
1
1
  dodal/__init__.py,sha256=Ksms_WJF8LTkbm38gEpm1jBpGqcQ8NGvmb2ZJlOE1j8,198
2
2
  dodal/__main__.py,sha256=kP2S2RPitnOWpNGokjZ1Yq-1umOtp5sNOZk2B3tBPLM,111
3
- dodal/_version.py,sha256=ktA0kNTYPVSWYOxui27Mnyui4C-Y9QFQo0LpESSVdeo,415
4
- dodal/adsim.py,sha256=OW2dcS7ciD4Yq9WFw4PN_c5Bwccrmu7R-zr-u6ZCbQM,497
3
+ dodal/_version.py,sha256=eqat9FvXyrftwP2dajRS3LWZiDD9Pay4_MD5eY1_jxU,413
5
4
  dodal/cli.py,sha256=NieWNUgLUxyck1rHoFAPJjX1xXLzHNdQ-s4wvxYFfps,3757
6
5
  dodal/log.py,sha256=0to7CRsbzbgVfAAfKRAMhsaUuKqF2-7CGdQc-z8Uhno,9499
7
6
  dodal/utils.py,sha256=h2sNmTlsaznfxusV1Xj_mXtNjzsWjWAgmps6I0YNA3U,18097
8
7
  dodal/beamline_specific_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
8
  dodal/beamline_specific_utils/i03.py,sha256=P6Ls4FoVtcacH0RJM3v6ZwwGx27oMppcBdW0la-ohTY,377
10
9
  dodal/beamlines/README.md,sha256=K9MkL_GomxlsoTB7Mz-_dJA5NNSbmCfMiutchGg3C8o,404
11
- dodal/beamlines/__init__.py,sha256=WWXqkXkufRUNFSC_b0PoHSkyKo7Tn8wNmnQCr9klh2o,3054
10
+ dodal/beamlines/__init__.py,sha256=FsS1hMz9nqwTP12UtCJsfVn712mFElcBq2kKKpscp9k,3074
11
+ dodal/beamlines/adsim.py,sha256=DUAFS1ueoZ6DK2cmZkiEm3NElnaro1mUvyodv14pSmU,1839
12
12
  dodal/beamlines/b01_1.py,sha256=0gLjg0O9ttMjHzszSyJ_GT3fnoAB6u4aJ4MdAfjJbHA,1788
13
13
  dodal/beamlines/i03.py,sha256=WOMkGTcrrKqoBNeRObumMA8Nlo404uIr2ccfn8gK-zs,18063
14
14
  dodal/beamlines/i04.py,sha256=z8LUbhyfUDU08oSp85hg7hFE8FJkAyLsqPUQScs5SUA,14567
@@ -17,7 +17,7 @@ dodal/beamlines/i13_1.py,sha256=csXHrdwUh4sXTmb4X6ZiiSS_XxRkNShsVoBMxYI6rG0,1833
17
17
  dodal/beamlines/i20_1.py,sha256=MaPgONHqpoZuBtkiKEzYtViJnKBM2_ekeP4OdbmuXHE,1158
18
18
  dodal/beamlines/i22.py,sha256=tzx8w86uvJHo5TuwPoIUErB7C5mGhqTzlv45qrppld0,7228
19
19
  dodal/beamlines/i23.py,sha256=2j5qLoqE_hg9ETHqNkOVu7LLkVB8qalgXeORnVYKN_I,1075
20
- dodal/beamlines/i24.py,sha256=V0MMr9bS-4c3fzSQL2gK6zCOT0ft1AV6VWBrnLAn5fk,8011
20
+ dodal/beamlines/i24.py,sha256=w9xkufc7CluVj5nvQww1B7k8VN5RzNQ6AsX8bfyAi9k,8544
21
21
  dodal/beamlines/p38.py,sha256=JJbclLYoRdIxcpzpW4oTj77YJ001CdEAM0bKRk7seYI,8735
22
22
  dodal/beamlines/p45.py,sha256=N4SDTIFok3uMqb37higZHMr3xRjxItsT4ib_KacKKAE,2935
23
23
  dodal/beamlines/p99.py,sha256=I6c_3NbvEPOpy2z1uwLRqwCkd83Sf15-OXEykLkn0-c,910
@@ -36,11 +36,11 @@ dodal/common/beamlines/beamline_utils.py,sha256=LCUt58Y2wRd15PdMIYSeHiKhWVYgt6UC
36
36
  dodal/common/beamlines/device_helpers.py,sha256=aFzYa4YOPcbrvwoW1k3OrN6NVLYYmMPR8YkCpHHe-98,962
37
37
  dodal/devices/CTAB.py,sha256=5_261Ox6NG2cJIzzwnjWz289BG0nZoE0wKOaI5V5jqM,1998
38
38
  dodal/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- dodal/devices/adsim.py,sha256=dMU0TKIuiODHYFHQOH4_5UvB8iJtaJEtjqaEDGjcU-w,311
40
- dodal/devices/aperture.py,sha256=VefMpkOXJ36vv2F9uNxFwIEyDMcXiImArcK1zA_ctmU,774
41
- dodal/devices/aperturescatterguard.py,sha256=DoSH7TFJt7rXA17eChlGnv9_GsWz1R6s_EigGayPEOM,13007
39
+ dodal/devices/adsim.py,sha256=vCexraF4zLssHdjfPod-XuQGJE_sWoCttFdx__HDS8w,488
40
+ dodal/devices/aperture.py,sha256=yyw2ei3gM_lmZWDQ6VTbydB58RCDTen_nqBZyoTP2IM,583
41
+ dodal/devices/aperturescatterguard.py,sha256=PRNnGbxYKFWoa1m70Mz-6Ta4NtFG0ShUprBvv2viUeI,8993
42
42
  dodal/devices/apple2_undulator.py,sha256=HtjHiDX80n_nzabuFcUioH-gWH1a6VjZzw_xStq7S4w,22470
43
- dodal/devices/attenuator.py,sha256=6IAPr8XmLxUOHC8g7Zt8MR1trMEDOxO5JkLphd6LbRg,2582
43
+ dodal/devices/attenuator.py,sha256=SEDnnNeUpwBArJbPcwHyr5BwkHO1Jvgsrw1bpFz5u-o,2894
44
44
  dodal/devices/backlight.py,sha256=nQIr3J-I-OXnOUoWmr3ruy3nhq_q2US1KXC4NrGG_2U,1634
45
45
  dodal/devices/cryostream.py,sha256=K-ldpredpeDTzNt4qtQMg99nKJNjBYoXBbK0WJGexzw,656
46
46
  dodal/devices/dcm.py,sha256=JbyxLnrS68nnnv39l9XEWgJgXUBqxX6aFo19MZnL36E,2574
@@ -50,7 +50,7 @@ dodal/devices/eiger_odin.py,sha256=oZl16K-Qb2yL6tK1fyDQvqbbhhvYMSVcf_e2CjlqMa4,7
50
50
  dodal/devices/fast_grid_scan.py,sha256=dKF2-8CNmm-LaMXBkqkO31pTcAAwiAXznEeB6tg0f6E,11149
51
51
  dodal/devices/fluorescence_detector_motion.py,sha256=-1qCSvW0PdT0m6BcoLxrtc0OJ5UDIBsEe11EOLr-gFw,501
52
52
  dodal/devices/flux.py,sha256=RtPStHw7Mad0igVKntKWVZfuZn2clokVJqH14HLix6M,198
53
- dodal/devices/focusing_mirror.py,sha256=yz0MIeqLtg-Ga5M-AF1S3fsHgaHn_9b1bv9zA5OqsEk,6262
53
+ dodal/devices/focusing_mirror.py,sha256=vdUPkwyCAZBSR3LQ-EojDOoxVy1ZmOaD_nevETbj7BA,6592
54
54
  dodal/devices/hutch_shutter.py,sha256=WXY9JwqAa5prbf72IP7_MTKndPDtAltCpPJlNbq-F_0,3313
55
55
  dodal/devices/ipin.py,sha256=eq5jlKw7WGQi8VLrAWpaAIsZmfiVf-5Q0td_B22H6A4,473
56
56
  dodal/devices/linkam3.py,sha256=O1ufCb5ce6nfGXVwiO3I1WEnZ_Jp0azWxBcrZ7PWQm4,3869
@@ -72,16 +72,12 @@ dodal/devices/tetramm.py,sha256=460luDYab-u1QB0CPe7lPihtm9nZxdCDGtLPnXI-XGo,8447
72
72
  dodal/devices/thawer.py,sha256=4t4yF4VDIrT_tQ8RwjmXe_hDMwVjR8A-4rDkPx19b28,1672
73
73
  dodal/devices/turbo_slit.py,sha256=B6SPXqviMnG-U4PnUF1BdTO0LBKmTuwAUKRbxMiNJXo,1125
74
74
  dodal/devices/undulator.py,sha256=rQjDhrvgf4uXUEO17CiLopNDEagWOgkmpa02BarozDE,5295
75
- dodal/devices/undulator_dcm.py,sha256=OJBxUe2LlQkGdydWIa1kzR07UnEz8QcHQjE-gB3kUWE,2238
75
+ dodal/devices/undulator_dcm.py,sha256=4Y1ZgKatBenQgg4DuZnMtKwLDOH2YvUMo1QkFE0aaXs,2432
76
76
  dodal/devices/watsonmarlow323_pump.py,sha256=rwU94YE6esgGLYdh-pe8nBo_3tvgp6brrrbPDrqp5_M,1406
77
77
  dodal/devices/webcam.py,sha256=mef075ynDbzZ4pNAjfxR_9tdTTqF_rM7hAOVEEOV-Do,2408
78
78
  dodal/devices/xbpm_feedback.py,sha256=j8MHhhE0feoe6R54zPKqS5EbQ0bEDR-nOpLHzHhnHHQ,1156
79
79
  dodal/devices/zebra.py,sha256=p191eIGfmwM2EE1YJn6rRgChim2kqXl2KIYbMPUSOvg,9474
80
80
  dodal/devices/zebra_controlled_shutter.py,sha256=5-SH5HoXp_6P-xAtfDFJKQq6mBDwreubuCULSz78fgw,1852
81
- dodal/devices/areadetector/__init__.py,sha256=8IwLxuZMW0MOJpJp_ZDdlaE20hrtsH_PXWGaKgMiYs4,240
82
- dodal/devices/areadetector/adaravis.py,sha256=Cqw_Mzrp_zODFxQ2LZBJzHp_DsZ6_dAITkZz8gYz_0w,3797
83
- dodal/devices/areadetector/adsim.py,sha256=cIc9PRbKnftBk7Ut8d8CU_TVrin8EwcKHObP2n9VxWM,1876
84
- dodal/devices/areadetector/adutils.py,sha256=4axFR3wtn-K-sjMVJyfTcu-8g35odf2cY8mTKv1gS-o,3093
85
81
  dodal/devices/areadetector/plugins/CAM.py,sha256=sZzJm5Ez3eWfXZi_EB67wluhZmMQm1UyOc2bJFfzd1U,964
86
82
  dodal/devices/areadetector/plugins/MJPG.py,sha256=QTsxCoWbofNpLMGPoOR2hWoM33KyntuLepbF0YmX0KE,3031
87
83
  dodal/devices/detector/__init__.py,sha256=-RdACL3tzc3lLArWOoGNje48UUlv2fElOmGOz9yOuO0,317
@@ -116,7 +112,7 @@ dodal/devices/oav/oav_detector.py,sha256=lUzdkAgviBtiZkN92fA_kleQRmmpfx_wTA06rq-
116
112
  dodal/devices/oav/oav_parameters.py,sha256=gGN73TQGUiRzlIO5YKiqCRkjpTKsV6LFGo7Eu4Vs82g,6074
117
113
  dodal/devices/oav/oav_to_redis_forwarder.py,sha256=pfhaW6Uo_1wDNfywyPkS5UTrY8yhkerhjgJfRMqrJRA,6259
118
114
  dodal/devices/oav/utils.py,sha256=3IvSTw6Ygkaz4Hzoz0eU2l6mljpq0NO57M15e-K4jOE,3182
119
- dodal/devices/oav/pin_image_recognition/__init__.py,sha256=lkfEd7NdnoCR7yX03wsp-rsL0BqpzZVYh-24QO4fYio,6562
115
+ dodal/devices/oav/pin_image_recognition/__init__.py,sha256=_eCq-rEtGNXVfrpahfKMLu53lEr49q5rtVqg0GhiODQ,6534
120
116
  dodal/devices/oav/pin_image_recognition/manual_test.py,sha256=h1Rto6ZDCB3jWhjSy9N8ECxRN583iYDJr9LxrTJ8kfE,903
121
117
  dodal/devices/oav/pin_image_recognition/utils.py,sha256=L9ypluYqeOFoS7gQuws-vTNc8LqaKl2ZIDNeQ2JaNpg,8592
122
118
  dodal/devices/oav/snapshots/grid_overlay.py,sha256=CdvCdTKMCiwMwxm2lV28KpcIUSXlscZmWxb73_KKmiI,3694
@@ -148,9 +144,9 @@ dodal/plan_stubs/wrapped.py,sha256=nriHKX4BF010CmrhdoUhY3-txClW5W8TPLz64kE_AXU,4
148
144
  dodal/plans/__init__.py,sha256=nH1jNxw3DzDMg9O8Uda0kqKIalRVEWBrq07OLY6Ey38,93
149
145
  dodal/plans/scanspec.py,sha256=Q0AcvTKRT401iGMRDSqK-D523UX5_ofiVMZ_rNXKOx8,2074
150
146
  dodal/plans/wrapped.py,sha256=Cr2iOpQCuk2ORKo5CZOh-zbQXAjoTfaLrfm7r1--GhU,2098
151
- dls_dodal-1.36.1a0.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
152
- dls_dodal-1.36.1a0.dist-info/METADATA,sha256=Du63G1abjqQlRwGNTbqO-QoFfAcTJ9ovv6VNkdaQVlc,16657
153
- dls_dodal-1.36.1a0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
154
- dls_dodal-1.36.1a0.dist-info/entry_points.txt,sha256=bycw_EKUzup_rxfCetOwcauXV4kLln_OPpPT8jEnr-I,94
155
- dls_dodal-1.36.1a0.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
156
- dls_dodal-1.36.1a0.dist-info/RECORD,,
147
+ dls_dodal-1.36.2.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
148
+ dls_dodal-1.36.2.dist-info/METADATA,sha256=CWBsjskEIuoboOXY_f3RJ4xuoDWdR5c9RhDjsuqNL3w,16655
149
+ dls_dodal-1.36.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
150
+ dls_dodal-1.36.2.dist-info/entry_points.txt,sha256=bycw_EKUzup_rxfCetOwcauXV4kLln_OPpPT8jEnr-I,94
151
+ dls_dodal-1.36.2.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
152
+ dls_dodal-1.36.2.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.36.1a0'
16
- __version_tuple__ = version_tuple = (1, 36, 1)
15
+ __version__ = version = '1.36.2'
16
+ __version_tuple__ = version_tuple = (1, 36, 2)
@@ -16,6 +16,7 @@ _BEAMLINE_NAME_OVERRIDES = {
16
16
  "p47": "training_rig",
17
17
  "p48": "training_rig",
18
18
  "p49": "training_rig",
19
+ "t01": "adsim",
19
20
  }
20
21
 
21
22
 
@@ -0,0 +1,75 @@
1
+ from pathlib import Path
2
+
3
+ from ophyd_async.epics.adsimdetector import SimDetector
4
+
5
+ from dodal.common.beamlines.beamline_utils import (
6
+ device_factory,
7
+ get_path_provider,
8
+ set_path_provider,
9
+ )
10
+ from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
11
+ from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider
12
+ from dodal.devices.adsim import SimStage
13
+ from dodal.log import set_beamline as set_log_beamline
14
+ from dodal.utils import BeamlinePrefix
15
+
16
+ BL = "adsim"
17
+ PREFIX = BeamlinePrefix("t01")
18
+ set_log_beamline(BL)
19
+ set_utils_beamline(BL)
20
+
21
+ set_path_provider(
22
+ StaticVisitPathProvider(
23
+ BL,
24
+ Path("/tmp"),
25
+ client=LocalDirectoryServiceClient(),
26
+ )
27
+ )
28
+
29
+ """
30
+ Beamline module for use with the simulated AreaDetector and motors.
31
+ These devices are simulated at the EPICS level, enabling testing of
32
+ dodal and ophyd-async against what appear to be "real" signals.
33
+
34
+ Usage Example
35
+ -------------
36
+
37
+ Start the simulated beamline by following the epics-containers tutorial at
38
+ https://epics-containers.github.io/main/tutorials/launch_example.html
39
+ And ensure that the signals are visible:
40
+
41
+ ```sh
42
+ export EPICS_CA_ADDR_LIST=127.0.0.1
43
+ ```
44
+
45
+ How to use the devices in a plan:
46
+ In an ipython terminal run:
47
+
48
+ ```python
49
+ from bluesky.run_engine import RunEngine
50
+
51
+ from dodal.beamlines.adsim import det, stage
52
+ from dodal.plans import count
53
+
54
+ RE = RunEngine()
55
+ d = det(connect_immediately=True)
56
+ s = stage(connect_immediately=True)
57
+ RE(count([d], num=10))
58
+ ```
59
+
60
+ """
61
+
62
+
63
+ @device_factory()
64
+ def stage() -> SimStage:
65
+ return SimStage(f"{PREFIX.beamline_prefix}-MO-SIMC-01:")
66
+
67
+
68
+ @device_factory()
69
+ def det() -> SimDetector:
70
+ return SimDetector(
71
+ f"{PREFIX.beamline_prefix}-DI-CAM-01:",
72
+ path_provider=get_path_provider(),
73
+ drv_suffix="DET:",
74
+ hdf_suffix="HDF:",
75
+ )
dodal/beamlines/i24.py CHANGED
@@ -1,5 +1,6 @@
1
1
  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
+ from dodal.devices.attenuator import ReadOnlyAttenuator
3
4
  from dodal.devices.detector import DetectorParams
4
5
  from dodal.devices.eiger import EigerDetector
5
6
  from dodal.devices.hutch_shutter import HutchShutter
@@ -29,6 +30,21 @@ set_log_beamline(BL)
29
30
  set_utils_beamline(BL)
30
31
 
31
32
 
33
+ def attenuator(
34
+ wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
35
+ ) -> ReadOnlyAttenuator:
36
+ """Get a read-only attenuator device for i24, instantiate it if it hasn't already
37
+ been. If this is called when already instantiated in i24, it will return the
38
+ existing object."""
39
+ return device_instantiation(
40
+ ReadOnlyAttenuator,
41
+ "attenuator",
42
+ "-OP-ATTN-01:",
43
+ wait_for_connection,
44
+ fake_with_ophyd_sim,
45
+ )
46
+
47
+
32
48
  def aperture(
33
49
  wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
34
50
  ) -> Aperture:
dodal/devices/adsim.py CHANGED
@@ -1,13 +1,13 @@
1
- from ophyd import Component, EpicsMotor, MotorBundle
1
+ from ophyd_async.core import StandardReadable
2
+ from ophyd_async.epics.motor import Motor
2
3
 
3
4
 
4
- class SimStage(MotorBundle):
5
- """
6
- ADSIM EPICS motors
7
- """
5
+ class SimStage(StandardReadable):
6
+ """Simulated Sample Stage for use with the containerised simulated beamline
7
+ https://github.com/epics-containers/example-services"""
8
8
 
9
- x = Component(EpicsMotor, "M1")
10
- y = Component(EpicsMotor, "M2")
11
- z = Component(EpicsMotor, "M3")
12
- theta = Component(EpicsMotor, "M4")
13
- load = Component(EpicsMotor, "M5")
9
+ def __init__(self, prefix: str, name: str = "sim"):
10
+ with self.add_children_as_readables():
11
+ self.x = Motor(prefix + "M1")
12
+ self.y = Motor(prefix + "M2")
13
+ super().__init__(name=name)
dodal/devices/aperture.py CHANGED
@@ -12,10 +12,3 @@ class Aperture(StandardReadable):
12
12
  self.medium = epics_signal_r(float, prefix + "Y:MEDIUM_CALC")
13
13
  self.large = epics_signal_r(float, prefix + "Y:LARGE_CALC")
14
14
  super().__init__(name)
15
-
16
- async def in_position(self):
17
- return (
18
- await self.small.get_value()
19
- or await self.medium.get_value()
20
- or await self.large.get_value()
21
- )
@@ -1,18 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- from collections.abc import Callable, Coroutine
5
- from typing import Any
6
4
 
7
- from bluesky.protocols import Movable, Triggerable
5
+ from bluesky.protocols import Movable
8
6
  from ophyd_async.core import (
9
7
  AsyncStatus,
10
- Reference,
11
8
  StandardReadable,
12
9
  StandardReadableFormat,
13
10
  StrictEnum,
14
11
  )
15
- from ophyd_async.epics.motor import Motor
16
12
  from pydantic import BaseModel, Field
17
13
 
18
14
  from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters
@@ -111,157 +107,7 @@ def load_positions_from_beamline_parameters(
111
107
  }
112
108
 
113
109
 
114
- async def _safe_move_whilst_in_beam(
115
- aperture: Aperture,
116
- scatterguard: Scatterguard,
117
- position: AperturePosition,
118
- aperture_z_tolerance: float,
119
- ):
120
- """
121
- Move the aperture and scatterguard combo safely to a new position.
122
- See https://github.com/DiamondLightSource/hyperion/wiki/Aperture-Scatterguard-Collisions
123
- for why this is required. TLDR is that we have a collision at the top of y so we need
124
- to make sure we move the assembly down before we move the scatterguard up.
125
-
126
- We also check that the assembly has been moved into the correct z position
127
- previously. If we try and move whilst in the incorrect Z position we will collide
128
- with the table.
129
- """
130
- ap_z_in_position = await aperture.z.motor_done_move.get_value()
131
- if not ap_z_in_position:
132
- raise InvalidApertureMove(
133
- "ApertureScatterguard z is still moving. Wait for it to finish "
134
- "before triggering another move."
135
- )
136
-
137
- current_ap_z = await aperture.z.user_readback.get_value()
138
- diff_on_z = abs(current_ap_z - position.aperture_z)
139
- if diff_on_z > aperture_z_tolerance:
140
- raise InvalidApertureMove(
141
- f"Current aperture z ({current_ap_z}), outside of tolerance ({aperture_z_tolerance}) from target ({position.aperture_z})."
142
- )
143
-
144
- current_ap_y = await aperture.y.user_readback.get_value()
145
-
146
- aperture_x, aperture_y, aperture_z, scatterguard_x, scatterguard_y = position.values
147
-
148
- if aperture_y > current_ap_y:
149
- # Assembly needs to move up so move the scatterguard down first
150
- await asyncio.gather(
151
- scatterguard.x.set(scatterguard_x),
152
- scatterguard.y.set(scatterguard_y),
153
- )
154
- await asyncio.gather(
155
- aperture.x.set(aperture_x),
156
- aperture.y.set(aperture_y),
157
- aperture.z.set(aperture_z),
158
- )
159
- else:
160
- await asyncio.gather(
161
- aperture.x.set(aperture_x),
162
- aperture.y.set(aperture_y),
163
- aperture.z.set(aperture_z),
164
- )
165
-
166
- await asyncio.gather(
167
- scatterguard.x.set(scatterguard_x),
168
- scatterguard.y.set(scatterguard_y),
169
- )
170
-
171
-
172
- class ApertureSelector(StandardReadable, Movable):
173
- """Allows for moving all axes other than Y into the correct position, this means
174
- that we can set up the aperture while it is out of the beam then move it in later."""
175
-
176
- def __init__(
177
- self,
178
- aperture: Aperture,
179
- scatterguard: Scatterguard,
180
- out_of_beam: Callable[[], Coroutine[Any, Any, bool]],
181
- loaded_positions: dict[ApertureValue, AperturePosition],
182
- aperture_z_tolerance: float,
183
- ):
184
- self.aperture = Reference(aperture)
185
- self.scatterguard = Reference(scatterguard)
186
- self.loaded_positions = loaded_positions
187
- self.get_is_out_of_beam = out_of_beam
188
- self.aperture_z_tolerance = aperture_z_tolerance
189
- super().__init__()
190
-
191
- @AsyncStatus.wrap
192
- async def set(self, value: ApertureValue):
193
- """Moves the assembly to the position for the specified aperture, whilst keeping
194
- it out of the beam if it already is so.
195
-
196
- Moving the assembly whilst out of the beam has no collision risk so we can just
197
- move all the motors together.
198
- """
199
- if await self.get_is_out_of_beam():
200
- aperture_x, _, aperture_z, scatterguard_x, scatterguard_y = (
201
- self.loaded_positions[value].values
202
- )
203
-
204
- await asyncio.gather(
205
- self.aperture().x.set(aperture_x),
206
- self.aperture().z.set(aperture_z),
207
- self.scatterguard().x.set(scatterguard_x),
208
- self.scatterguard().y.set(scatterguard_y),
209
- )
210
- else:
211
- await _safe_move_whilst_in_beam(
212
- self.aperture(),
213
- self.scatterguard(),
214
- self.loaded_positions[value],
215
- self.aperture_z_tolerance,
216
- )
217
-
218
-
219
- class OutTrigger(StandardReadable, Triggerable):
220
- """Allows for moving just the Y stage of the assembly out of the beam."""
221
-
222
- def __init__(
223
- self,
224
- aperture_y: Motor,
225
- out_y: float,
226
- ):
227
- self.aperture_y = Reference(aperture_y)
228
- self.out_y = out_y
229
- super().__init__()
230
-
231
- @AsyncStatus.wrap
232
- async def trigger(self):
233
- """Moves the assembly out of the beam."""
234
- await self.aperture_y().set(self.out_y)
235
-
236
-
237
110
  class ApertureScatterguard(StandardReadable, Movable):
238
- """Move the aperture and scatterguard assembly in a safe way. There are two ways to
239
- interact with the device depending on if you want simplicity or move flexibility.
240
-
241
- The simple interface is using:
242
-
243
- await aperture_scatterguard.set(ApertureValue.LARGE)
244
-
245
- This will move the assembly so that the large aperture is in the beam, regardless
246
- of where the assembly currently is.
247
-
248
- However, the aperture Y axis is faster than the others. In some cases we may want to
249
- move the assembly out of the beam with this axis without moving others:
250
-
251
- await aperture_scatterguard.move_out.trigger()
252
-
253
- We may then want to keep the assembly out of the beam whilst asynchronously preparing
254
- the other axes for the aperture that's to follow:
255
-
256
- await aperture_scatterguard.aperture_outside_beam.set(ApertureValue.LARGE)
257
-
258
- Then, at a later time, move back into the beam:
259
-
260
- await aperture_scatterguard.set(ApertureValue.LARGE)
261
-
262
- This move will now be faster as only the y is left to move.
263
- """
264
-
265
111
  def __init__(
266
112
  self,
267
113
  loaded_positions: dict[ApertureValue, AperturePosition],
@@ -269,8 +115,8 @@ class ApertureScatterguard(StandardReadable, Movable):
269
115
  prefix: str = "",
270
116
  name: str = "",
271
117
  ) -> None:
272
- self._aperture = Aperture(prefix + "-MO-MAPT-01:")
273
- self._scatterguard = Scatterguard(prefix + "-MO-SCAT-01:")
118
+ self.aperture = Aperture(prefix + "-MO-MAPT-01:")
119
+ self.scatterguard = Scatterguard(prefix + "-MO-SCAT-01:")
274
120
  self.radius = create_hardware_backed_soft_signal(
275
121
  float, self._get_current_radius, units="µm"
276
122
  )
@@ -278,43 +124,30 @@ class ApertureScatterguard(StandardReadable, Movable):
278
124
  self._tolerances = tolerances
279
125
  self.add_readables(
280
126
  [
281
- self._aperture.x.user_readback,
282
- self._aperture.y.user_readback,
283
- self._aperture.z.user_readback,
284
- self._scatterguard.x.user_readback,
285
- self._scatterguard.y.user_readback,
127
+ self.aperture.x.user_readback,
128
+ self.aperture.y.user_readback,
129
+ self.aperture.z.user_readback,
130
+ self.scatterguard.x.user_readback,
131
+ self.scatterguard.y.user_readback,
286
132
  self.radius,
287
133
  ],
288
134
  )
289
-
290
135
  with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
291
136
  self.selected_aperture = create_hardware_backed_soft_signal(
292
137
  ApertureValue, self._get_current_aperture_position
293
138
  )
294
139
 
295
- # Setting this will select the aperture but not move it into beam
296
- self.aperture_outside_beam = ApertureSelector(
297
- self._aperture,
298
- self._scatterguard,
299
- self._is_out_of_beam,
300
- self._loaded_positions,
301
- self._tolerances.aperture_z,
302
- )
303
-
304
- # Setting this will just move the assembly out of the beam
305
- self.move_out = OutTrigger(
306
- self._aperture.y, loaded_positions[ApertureValue.ROBOT_LOAD].aperture_y
307
- )
308
-
309
140
  super().__init__(name)
310
141
 
142
+ def get_position_from_gda_aperture_name(
143
+ self, gda_aperture_name: str
144
+ ) -> ApertureValue:
145
+ return ApertureValue(gda_aperture_name)
146
+
311
147
  @AsyncStatus.wrap
312
148
  async def set(self, value: ApertureValue):
313
- """This set will move the aperture into the beam or move to robot load"""
314
149
  position = self._loaded_positions[value]
315
- await _safe_move_whilst_in_beam(
316
- self._aperture, self._scatterguard, position, self._tolerances.aperture_z
317
- )
150
+ await self._safe_move_within_datacollection_range(position, value)
318
151
 
319
152
  @AsyncStatus.wrap
320
153
  async def _set_raw_unsafe(self, position: AperturePosition):
@@ -324,18 +157,13 @@ class ApertureScatterguard(StandardReadable, Movable):
324
157
  )
325
158
 
326
159
  await asyncio.gather(
327
- self._aperture.x.set(aperture_x),
328
- self._aperture.y.set(aperture_y),
329
- self._aperture.z.set(aperture_z),
330
- self._scatterguard.x.set(scatterguard_x),
331
- self._scatterguard.y.set(scatterguard_y),
160
+ self.aperture.x.set(aperture_x),
161
+ self.aperture.y.set(aperture_y),
162
+ self.aperture.z.set(aperture_z),
163
+ self.scatterguard.x.set(scatterguard_x),
164
+ self.scatterguard.y.set(scatterguard_y),
332
165
  )
333
166
 
334
- async def _is_out_of_beam(self) -> bool:
335
- current_ap_y = await self._aperture.y.user_readback.get_value()
336
- robot_load_ap_y = self._loaded_positions[ApertureValue.ROBOT_LOAD].aperture_y
337
- return current_ap_y <= robot_load_ap_y + self._tolerances.aperture_y
338
-
339
167
  async def _get_current_aperture_position(self) -> ApertureValue:
340
168
  """
341
169
  Returns the current aperture position using readback values
@@ -343,13 +171,15 @@ class ApertureScatterguard(StandardReadable, Movable):
343
171
  mini aperture y <= ROBOT_LOAD.location.aperture_y + tolerance.
344
172
  If no position is found then raises InvalidApertureMove.
345
173
  """
346
- if await self._aperture.large.get_value(cached=False) == 1:
174
+ current_ap_y = await self.aperture.y.user_readback.get_value(cached=False)
175
+ robot_load_ap_y = self._loaded_positions[ApertureValue.ROBOT_LOAD].aperture_y
176
+ if await self.aperture.large.get_value(cached=False) == 1:
347
177
  return ApertureValue.LARGE
348
- elif await self._aperture.medium.get_value(cached=False) == 1:
178
+ elif await self.aperture.medium.get_value(cached=False) == 1:
349
179
  return ApertureValue.MEDIUM
350
- elif await self._aperture.small.get_value(cached=False) == 1:
180
+ elif await self.aperture.small.get_value(cached=False) == 1:
351
181
  return ApertureValue.SMALL
352
- elif await self._is_out_of_beam():
182
+ elif current_ap_y <= robot_load_ap_y + self._tolerances.aperture_y:
353
183
  return ApertureValue.ROBOT_LOAD
354
184
 
355
185
  raise InvalidApertureMove("Current aperture/scatterguard state unrecognised")
@@ -357,3 +187,57 @@ class ApertureScatterguard(StandardReadable, Movable):
357
187
  async def _get_current_radius(self) -> float:
358
188
  current_value = await self._get_current_aperture_position()
359
189
  return self._loaded_positions[current_value].radius
190
+
191
+ async def _safe_move_within_datacollection_range(
192
+ self, position: AperturePosition, value: ApertureValue
193
+ ):
194
+ """
195
+ Move the aperture and scatterguard combo safely to a new position.
196
+ See https://github.com/DiamondLightSource/hyperion/wiki/Aperture-Scatterguard-Collisions
197
+ for why this is required.
198
+ """
199
+ assert self._loaded_positions is not None
200
+
201
+ ap_z_in_position = await self.aperture.z.motor_done_move.get_value()
202
+ if not ap_z_in_position:
203
+ raise InvalidApertureMove(
204
+ "ApertureScatterguard z is still moving. Wait for it to finish "
205
+ "before triggering another move."
206
+ )
207
+
208
+ current_ap_z = await self.aperture.z.user_readback.get_value()
209
+ diff_on_z = abs(current_ap_z - position.aperture_z)
210
+ if diff_on_z > self._tolerances.aperture_z:
211
+ raise InvalidApertureMove(
212
+ "ApertureScatterguard safe move is not yet defined for positions "
213
+ "outside of LARGE, MEDIUM, SMALL, ROBOT_LOAD. "
214
+ f"Current aperture z ({current_ap_z}), outside of tolerance ({self._tolerances.aperture_z}) from target ({position.aperture_z})."
215
+ )
216
+
217
+ current_ap_y = await self.aperture.y.user_readback.get_value()
218
+
219
+ aperture_x, aperture_y, aperture_z, scatterguard_x, scatterguard_y = (
220
+ position.values
221
+ )
222
+
223
+ if position.aperture_y > current_ap_y:
224
+ await asyncio.gather(
225
+ self.scatterguard.x.set(scatterguard_x),
226
+ self.scatterguard.y.set(scatterguard_y),
227
+ )
228
+ await asyncio.gather(
229
+ self.aperture.x.set(aperture_x),
230
+ self.aperture.y.set(aperture_y),
231
+ self.aperture.z.set(aperture_z),
232
+ )
233
+ else:
234
+ await asyncio.gather(
235
+ self.aperture.x.set(aperture_x),
236
+ self.aperture.y.set(aperture_y),
237
+ self.aperture.z.set(aperture_z),
238
+ )
239
+
240
+ await asyncio.gather(
241
+ self.scatterguard.x.set(scatterguard_x),
242
+ self.scatterguard.y.set(scatterguard_y),
243
+ )
@@ -14,7 +14,20 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw, epics_signal
14
14
  from dodal.log import LOGGER
15
15
 
16
16
 
17
- class Attenuator(StandardReadable, Movable):
17
+ class ReadOnlyAttenuator(StandardReadable):
18
+ """A read-only attenuator class with a minimum set of PVs for reading.
19
+
20
+ The actual_transmission will return a fractional transmission between 0-1.
21
+ """
22
+
23
+ def __init__(self, prefix: str, name: str = "") -> None:
24
+ with self.add_children_as_readables():
25
+ self.actual_transmission = epics_signal_r(float, prefix + "MATCH")
26
+
27
+ super().__init__(name)
28
+
29
+
30
+ class Attenuator(ReadOnlyAttenuator, Movable):
18
31
  """The attenuator will insert filters into the beam to reduce its transmission.
19
32
 
20
33
  This device should be set with:
@@ -42,10 +55,7 @@ class Attenuator(StandardReadable, Movable):
42
55
  self._use_current_energy = epics_signal_x(prefix + "E2WL:USECURRENTENERGY.PROC")
43
56
  self._change = epics_signal_x(prefix + "FANOUT")
44
57
 
45
- with self.add_children_as_readables():
46
- self.actual_transmission = epics_signal_r(float, prefix + "MATCH")
47
-
48
- super().__init__(name)
58
+ super().__init__(prefix, name)
49
59
 
50
60
  @AsyncStatus.wrap
51
61
  async def set(self, value: float):
@@ -1,3 +1,5 @@
1
+ from typing import TypedDict
2
+
1
3
  from ophyd_async.core import (
2
4
  AsyncStatus,
3
5
  Device,
@@ -36,6 +38,12 @@ class MirrorStripe(StrictEnum):
36
38
  PLATINUM = "Platinum"
37
39
 
38
40
 
41
+ class MirrorStripeConfiguration(TypedDict):
42
+ stripe: MirrorStripe
43
+ yaw_mrad: float
44
+ lat_mm: float
45
+
46
+
39
47
  class MirrorVoltageDemand(StrictEnum):
40
48
  N_A = "N/A"
41
49
  OK = "OK"
@@ -173,9 +181,10 @@ class FocusingMirrorWithStripes(FocusingMirror):
173
181
 
174
182
  super().__init__(prefix, name, *args, **kwargs)
175
183
 
176
- def energy_to_stripe(self, energy_kev) -> MirrorStripe:
184
+ def energy_to_stripe(self, energy_kev) -> MirrorStripeConfiguration:
185
+ """Return the stripe, yaw angle and lateral position for the specified energy"""
177
186
  # In future, this should be configurable per-mirror
178
187
  if energy_kev < 7:
179
- return MirrorStripe.BARE
188
+ return {"stripe": MirrorStripe.BARE, "yaw_mrad": 6.2, "lat_mm": 0.0}
180
189
  else:
181
- return MirrorStripe.RHODIUM
190
+ return {"stripe": MirrorStripe.RHODIUM, "yaw_mrad": 0.0, "lat_mm": 10.0}
@@ -61,7 +61,7 @@ class PinTipDetection(StandardReadable):
61
61
  self.triggered_bottom_edge, self._bottom_edge_setter = soft_signal_r_and_setter(
62
62
  Array1D[np.int32], name="triggered_bottom_edge"
63
63
  )
64
- self.array_data = epics_signal_r(Array1D[np.uint8], f"pva://{prefix}PVA:ARRAY")
64
+ self.array_data = epics_signal_r(np.ndarray, f"pva://{prefix}PVA:ARRAY")
65
65
 
66
66
  # Soft parameters for pin-tip detection.
67
67
  self.preprocess_operation = soft_signal_rw(int, 10, name="preprocess")
@@ -99,9 +99,7 @@ class PinTipDetection(StandardReadable):
99
99
  self._top_edge_setter(results.edge_top)
100
100
  self._bottom_edge_setter(results.edge_bottom)
101
101
 
102
- async def _get_tip_and_edge_data(
103
- self, array_data: Array1D[np.uint8]
104
- ) -> SampleLocation:
102
+ async def _get_tip_and_edge_data(self, array_data: np.ndarray) -> SampleLocation:
105
103
  """
106
104
  Gets the location of the pin tip and the top and bottom edges.
107
105
  """
@@ -5,6 +5,7 @@ from ophyd_async.core import AsyncStatus, StandardReadable
5
5
 
6
6
  from dodal.common.beamlines.beamline_parameters import get_beamline_parameters
7
7
 
8
+ from ..log import LOGGER
8
9
  from .dcm import DCM
9
10
  from .undulator import Undulator
10
11
 
@@ -59,3 +60,6 @@ class UndulatorDCM(StandardReadable, Movable):
59
60
  self.dcm.energy_in_kev.set(value, timeout=ENERGY_TIMEOUT_S),
60
61
  self.undulator.set(value),
61
62
  )
63
+ # DCM Perp pitch
64
+ LOGGER.info(f"Adjusting DCM offset to {self.dcm_fixed_offset_mm} mm")
65
+ await self.dcm.offset_in_mm.set(self.dcm_fixed_offset_mm)
dodal/adsim.py DELETED
@@ -1,17 +0,0 @@
1
- import os
2
-
3
- from dodal.devices.adsim import SimStage
4
- from dodal.devices.areadetector import AdSimDetector
5
-
6
- from .utils import get_hostname
7
-
8
- # Default prefix to hostname unless overriden with export PREFIX=<prefix>
9
- PREFIX: str = os.environ.get("PREFIX", get_hostname())
10
-
11
-
12
- def stage(name: str = "sim_motors") -> SimStage:
13
- return SimStage(name=name, prefix=f"{PREFIX}-MO-SIM-01:")
14
-
15
-
16
- def det(name: str = "adsim") -> AdSimDetector:
17
- return AdSimDetector(name=name, prefix=f"{PREFIX}-AD-SIM-01:")
@@ -1,10 +0,0 @@
1
- from .adaravis import AdAravisDetector
2
- from .adsim import AdSimDetector
3
- from .adutils import Hdf5Writer, SynchronisedAdDriverBase
4
-
5
- __all__ = [
6
- "AdSimDetector",
7
- "SynchronisedAdDriverBase",
8
- "Hdf5Writer",
9
- "AdAravisDetector",
10
- ]
@@ -1,101 +0,0 @@
1
- from collections.abc import Mapping
2
- from typing import Any
3
-
4
- from ophyd import EpicsSignal, Signal
5
- from ophyd.areadetector.base import ADComponent as Cpt
6
- from ophyd.areadetector.detectors import DetectorBase
7
-
8
- from .adutils import Hdf5Writer, SingleTriggerV33, SynchronisedAdDriverBase
9
-
10
- _ACQUIRE_BUFFER_PERIOD = 0.2
11
-
12
-
13
- class AdAravisDetector(SingleTriggerV33, DetectorBase):
14
- cam = Cpt(SynchronisedAdDriverBase, suffix="DET:")
15
- hdf = Cpt(
16
- Hdf5Writer,
17
- suffix="HDF5:",
18
- root="",
19
- write_path_template="",
20
- )
21
- _priming_settings: Mapping[Signal, Any]
22
-
23
- def __init__(self, *args, **kwargs) -> None:
24
- super().__init__(*args, **kwargs)
25
- self.hdf.kind = "normal"
26
-
27
- # Values for staging
28
- self.stage_sigs = {
29
- # Get stage to wire up the plugins
30
- self.hdf.nd_array_port: self.cam.port_name.get(),
31
- # Reset array counter on stage
32
- self.cam.array_counter: 0,
33
- # Set image mode to multiple on stage so we have the option, can still
34
- # set num_images to 1
35
- self.cam.image_mode: "Multiple",
36
- # For now, this Ophyd device does not support hardware
37
- # triggered scanning, disable on stage
38
- self.cam.trigger_mode: "Off",
39
- **self.stage_sigs, # type: ignore
40
- }
41
-
42
- # Settings to apply when priming plugins during pre-stage
43
- self._priming_settings = {
44
- self.hdf.enable: 1,
45
- self.hdf.nd_array_port: self.cam.port_name.get(),
46
- self.cam.array_callbacks: 1,
47
- self.cam.image_mode: "Single",
48
- self.cam.trigger_mode: "Off",
49
- # Take the quickest possible frame
50
- self.cam.acquire_time: 6.3e-05,
51
- self.cam.acquire_period: 0.003,
52
- }
53
-
54
- # Signals that control driver and hdf writer should be put_complete to
55
- # avoid race conditions during priming
56
- for signal in set(self.stage_sigs.keys()).union(
57
- set(self._priming_settings.keys())
58
- ):
59
- if isinstance(signal, EpicsSignal):
60
- signal.put_complete = True
61
- self.cam.acquire.put_complete = True
62
-
63
- def stage(self, *args, **kwargs) -> list[object]:
64
- # We have to manually set the acquire period bcause the EPICS driver
65
- # doesn't do it for us. If acquire time is a staged signal, we use the
66
- # stage value to calculate the acquire period, otherwise we perform
67
- # a caget and use the current acquire time.
68
- if self.cam.acquire_time in self.stage_sigs:
69
- acquire_time = self.stage_sigs[self.cam.acquire_time]
70
- else:
71
- acquire_time = self.cam.acquire_time.get()
72
- self.stage_sigs[self.cam.acquire_period] = (
73
- float(acquire_time) + _ACQUIRE_BUFFER_PERIOD
74
- )
75
-
76
- # Ensure detector warmed up
77
- self._prime_hdf()
78
-
79
- # Now calling the super method should set the acquire period
80
- return super().stage(*args, **kwargs)
81
-
82
- def _prime_hdf(self) -> None:
83
- """
84
- Take a single frame and pipe it through the HDF5 writer plugin
85
- """
86
-
87
- # Backup state and ensure we are not acquiring
88
- reset_to = {signal: signal.get() for signal in self._priming_settings.keys()}
89
- self.cam.acquire.set(0).wait(timeout=10)
90
-
91
- # Apply all settings for acquisition
92
- for signal, value in self._priming_settings.items():
93
- # Ensure that .wait really will wait until the PV is set including its RBV
94
- signal.set(value).wait(timeout=10)
95
-
96
- # Acquire a frame
97
- self.cam.acquire.set(1).wait(timeout=10)
98
-
99
- # Revert settings to previous values
100
- for signal, value in reversed(reset_to.items()):
101
- signal.set(value).wait(timeout=10)
@@ -1,47 +0,0 @@
1
- from ophyd.areadetector.base import ADComponent as Cpt
2
- from ophyd.areadetector.detectors import DetectorBase
3
-
4
- from .adutils import Hdf5Writer, SingleTriggerV33, SynchronisedAdDriverBase
5
-
6
-
7
- class AdSimDetector(SingleTriggerV33, DetectorBase):
8
- cam = Cpt(SynchronisedAdDriverBase, suffix="CAM:", lazy=True)
9
- hdf = Cpt(
10
- Hdf5Writer,
11
- suffix="HDF5:",
12
- root="",
13
- write_path_template="",
14
- lazy=True,
15
- )
16
-
17
- def __init__(self, *args, **kwargs) -> None:
18
- super().__init__(*args, **kwargs)
19
- self.hdf.kind = "normal"
20
-
21
- self.stage_sigs = {
22
- # Get stage to wire up the plugins
23
- self.hdf.nd_array_port: self.cam.port_name.get(),
24
- # Reset array counter on stage
25
- self.cam.array_counter: 0,
26
- # Set image mode to multiple on stage so we have the option, can still
27
- # set num_images to 1
28
- self.cam.image_mode: "Multiple",
29
- # For now, this Ophyd device does not support hardware
30
- # triggered scanning, disable on stage
31
- self.cam.trigger_mode: "Internal",
32
- **self.stage_sigs, # type: ignore
33
- }
34
-
35
- def stage(self, *args, **kwargs) -> list[object]:
36
- # We have to manually set the acquire period bcause the EPICS driver
37
- # doesn't do it for us. If acquire time is a staged signal, we use the
38
- # stage value to calculate the acquire period, otherwise we perform
39
- # a caget and use the current acquire time.
40
- if self.cam.acquire_time in self.stage_sigs:
41
- acquire_time = self.stage_sigs[self.cam.acquire_time]
42
- else:
43
- acquire_time = self.cam.acquire_time.get()
44
- self.stage_sigs[self.cam.acquire_period] = acquire_time
45
-
46
- # Now calling the super method should set the acquire period
47
- return super().stage(*args, **kwargs)
@@ -1,81 +0,0 @@
1
- import time as ttime
2
-
3
- from ophyd import Component as Cpt
4
- from ophyd import DetectorBase, Device, EpicsSignal, EpicsSignalRO, Staged
5
- from ophyd.areadetector import ADTriggerStatus, TriggerBase
6
- from ophyd.areadetector.cam import AreaDetectorCam
7
- from ophyd.areadetector.filestore_mixins import FileStoreHDF5, FileStoreIterativeWrite
8
- from ophyd.areadetector.plugins import HDF5Plugin
9
-
10
-
11
- class SingleTriggerV33(TriggerBase):
12
- _status_type = ADTriggerStatus
13
-
14
- def __init__(self, *args, image_name=None, **kwargs):
15
- super().__init__(*args, **kwargs)
16
- if image_name is None:
17
- # Ensure that this mixin is part of valid device with name
18
- assert isinstance(self, Device)
19
- image_name = "_".join([self.name, "image"])
20
- self._image_name = image_name
21
-
22
- def trigger(self):
23
- "Trigger one acquisition."
24
- if self._staged != Staged.yes:
25
- raise RuntimeError(
26
- "This detector is not ready to trigger."
27
- "Call the stage() method before triggering."
28
- )
29
-
30
- self._status = self._status_type(self)
31
-
32
- def _acq_done(*args, **kwargs):
33
- # TODO sort out if anything useful in here
34
- self._status._finished() # noqa: SLF001
35
-
36
- self._acquisition_signal.put(1, use_complete=True, callback=_acq_done)
37
- # Ensure that this mixin is part of valid Detector with generate_datum
38
- assert isinstance(self, DetectorBase)
39
- self.generate_datum(self._image_name, ttime.time())
40
- return self._status
41
-
42
-
43
- class SynchronisedAdDriverBase(AreaDetectorCam):
44
- """
45
- Base Ophyd device to control an AreaDetector driver and
46
- syncrhonise it on other AreaDetector plugins, even non-blocking ones.
47
- """
48
-
49
- adcore_version = Cpt(EpicsSignalRO, "ADCoreVersion_RBV", string=True, kind="config")
50
- driver_version = Cpt(EpicsSignalRO, "DriverVersion_RBV", string=True, kind="config")
51
- wait_for_plugins = Cpt(EpicsSignal, "WaitForPlugins", string=True, kind="config")
52
-
53
- def stage(self, *args, **kwargs):
54
- # Makes the detector allow non-blocking AD plugins but makes Ophyd use
55
- # the AcquireBusy PV to determine when an acquisition is complete
56
- self.ensure_nonblocking()
57
- return super().stage(*args, **kwargs)
58
-
59
- def ensure_nonblocking(self):
60
- self.stage_sigs["wait_for_plugins"] = "Yes"
61
- if self.parent is not None:
62
- for c in self.parent.component_names:
63
- cpt = getattr(self.parent, c)
64
- if cpt is self:
65
- continue
66
- if hasattr(cpt, "ensure_nonblocking"):
67
- cpt.ensure_nonblocking()
68
-
69
-
70
- # ophyd code to be removed, only used for adim
71
- # https://github.com/DiamondLightSource/dodal/issues/404
72
- class Hdf5Writer(HDF5Plugin, FileStoreHDF5, FileStoreIterativeWrite): # type: ignore
73
- """ """
74
-
75
- pool_max_buffers = None
76
- file_number_sync = None
77
- file_number_write = None
78
-
79
- def get_frames_per_point(self):
80
- assert isinstance(self.parent, DetectorBase)
81
- return self.parent.cam.num_images.get()