dls-dodal 1.43.0__py3-none-any.whl → 1.45.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dls_dodal-1.43.0.dist-info → dls_dodal-1.45.0.dist-info}/METADATA +4 -3
- {dls_dodal-1.43.0.dist-info → dls_dodal-1.45.0.dist-info}/RECORD +66 -49
- {dls_dodal-1.43.0.dist-info → dls_dodal-1.45.0.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/__init__.py +2 -0
- dodal/beamlines/b01_1.py +8 -0
- dodal/beamlines/b07.py +27 -0
- dodal/beamlines/b07_1.py +25 -0
- dodal/beamlines/i03.py +11 -0
- dodal/beamlines/i09.py +25 -0
- dodal/beamlines/i09_1.py +25 -0
- dodal/beamlines/i10.py +19 -35
- dodal/beamlines/i13_1.py +22 -48
- dodal/beamlines/i19_1.py +17 -5
- dodal/beamlines/i19_2.py +13 -3
- dodal/beamlines/i19_optics.py +4 -2
- dodal/beamlines/i20_1.py +2 -1
- dodal/beamlines/i23.py +10 -0
- dodal/beamlines/p60.py +21 -0
- dodal/common/data_util.py +20 -0
- dodal/common/signal_utils.py +43 -4
- dodal/common/visit.py +1 -41
- dodal/devices/aperturescatterguard.py +3 -3
- dodal/devices/baton.py +17 -0
- dodal/devices/current_amplifiers/current_amplifier.py +1 -6
- dodal/devices/current_amplifiers/current_amplifier_detector.py +2 -2
- dodal/devices/current_amplifiers/femto.py +0 -5
- dodal/devices/current_amplifiers/sr570.py +0 -5
- dodal/devices/detector/det_dist_to_beam_converter.py +16 -23
- dodal/devices/detector/detector.py +2 -1
- dodal/devices/electron_analyser/__init__.py +0 -0
- dodal/devices/electron_analyser/abstract_analyser_io.py +47 -0
- dodal/devices/electron_analyser/abstract_region.py +112 -0
- dodal/devices/electron_analyser/specs_analyser_io.py +19 -0
- dodal/devices/electron_analyser/specs_region.py +26 -0
- dodal/devices/electron_analyser/vgscienta_analyser_io.py +26 -0
- dodal/devices/electron_analyser/vgscienta_region.py +90 -0
- dodal/devices/fast_grid_scan.py +2 -2
- dodal/devices/i03/beamstop.py +2 -2
- dodal/devices/i10/diagnostics.py +239 -0
- dodal/devices/i10/slits.py +93 -6
- dodal/devices/i13_1/merlin.py +1 -2
- dodal/devices/i13_1/merlin_controller.py +12 -8
- dodal/devices/i19/beamstop.py +30 -0
- dodal/devices/i19/blueapi_device.py +102 -0
- dodal/devices/i19/hutch_access.py +2 -0
- dodal/devices/i19/shutter.py +24 -40
- dodal/devices/i22/nxsas.py +1 -3
- dodal/devices/i24/focus_mirrors.py +3 -3
- dodal/devices/i24/pilatus_metadata.py +2 -2
- dodal/devices/motors.py +21 -0
- dodal/devices/oav/oav_detector.py +7 -9
- dodal/devices/oav/snapshots/snapshot.py +21 -0
- dodal/devices/oav/snapshots/snapshot_image_processing.py +74 -0
- dodal/devices/turbo_slit.py +8 -2
- dodal/devices/undulator.py +9 -7
- dodal/devices/util/adjuster_plans.py +1 -2
- dodal/devices/util/lookup_tables.py +38 -0
- dodal/devices/util/test_utils.py +1 -0
- dodal/plan_stubs/electron_analyser/__init__.py +0 -0
- dodal/plan_stubs/electron_analyser/configure_controller.py +80 -0
- dodal/plan_stubs/motor_utils.py +10 -12
- dodal/utils.py +0 -7
- dodal/devices/i13_1/merlin_io.py +0 -17
- dodal/devices/oav/microns_for_zoom_levels.json +0 -55
- dodal/devices/oav/snapshots/snapshot_with_beam_centre.py +0 -64
- dodal/devices/util/motor_utils.py +0 -6
- {dls_dodal-1.43.0.dist-info → dls_dodal-1.45.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.43.0.dist-info → dls_dodal-1.45.0.dist-info/licenses}/LICENSE +0 -0
- {dls_dodal-1.43.0.dist-info → dls_dodal-1.45.0.dist-info}/top_level.txt +0 -0
dodal/beamlines/i13_1.py
CHANGED
|
@@ -3,19 +3,20 @@ from pathlib import Path
|
|
|
3
3
|
from ophyd_async.epics.adaravis import AravisDetector
|
|
4
4
|
|
|
5
5
|
from dodal.common.beamlines.beamline_utils import (
|
|
6
|
-
|
|
6
|
+
device_factory,
|
|
7
7
|
get_path_provider,
|
|
8
8
|
set_path_provider,
|
|
9
9
|
)
|
|
10
10
|
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
|
|
11
|
-
from dodal.common.beamlines.device_helpers import CAM_SUFFIX, HDF5_SUFFIX
|
|
12
11
|
from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider
|
|
13
12
|
from dodal.devices.i13_1.merlin import Merlin
|
|
14
13
|
from dodal.devices.motors import XYZPositioner
|
|
15
14
|
from dodal.log import set_beamline as set_log_beamline
|
|
16
|
-
from dodal.utils import get_beamline_name
|
|
15
|
+
from dodal.utils import BeamlinePrefix, get_beamline_name
|
|
17
16
|
|
|
18
17
|
BL = get_beamline_name("i13-1")
|
|
18
|
+
PREFIX_BL13I = BeamlinePrefix(BL) # Can't use this yet as returns BL13I
|
|
19
|
+
PREFIX = "BL13J"
|
|
19
20
|
set_log_beamline(BL)
|
|
20
21
|
set_utils_beamline(BL)
|
|
21
22
|
set_path_provider(
|
|
@@ -27,59 +28,32 @@ set_path_provider(
|
|
|
27
28
|
)
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return device_instantiation(
|
|
34
|
-
XYZPositioner,
|
|
35
|
-
prefix="BL13J-MO-PI-02:",
|
|
36
|
-
name="sample_xyz_stage",
|
|
37
|
-
wait=wait_for_connection,
|
|
38
|
-
fake=fake_with_ophyd_sim,
|
|
39
|
-
bl_prefix=False,
|
|
40
|
-
)
|
|
31
|
+
@device_factory()
|
|
32
|
+
def sample_xyz_stage() -> XYZPositioner:
|
|
33
|
+
return XYZPositioner(prefix=f"{PREFIX}-MO-PI-02:")
|
|
41
34
|
|
|
42
35
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return device_instantiation(
|
|
47
|
-
XYZPositioner,
|
|
48
|
-
prefix="BL13J-MO-PI-02:FIXANG:",
|
|
49
|
-
name="sample_xyz_lab_fa_stage",
|
|
50
|
-
wait=wait_for_connection,
|
|
51
|
-
fake=fake_with_ophyd_sim,
|
|
52
|
-
bl_prefix=False,
|
|
53
|
-
)
|
|
36
|
+
@device_factory()
|
|
37
|
+
def sample_xyz_lab_fa_stage() -> XYZPositioner:
|
|
38
|
+
return XYZPositioner(prefix=f"{PREFIX}-MO-PI-02:FIXANG:")
|
|
54
39
|
|
|
55
40
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
name="side_camera",
|
|
63
|
-
bl_prefix=False,
|
|
64
|
-
drv_suffix=CAM_SUFFIX,
|
|
65
|
-
fileio_suffix=HDF5_SUFFIX,
|
|
41
|
+
@device_factory()
|
|
42
|
+
def side_camera() -> AravisDetector:
|
|
43
|
+
return AravisDetector(
|
|
44
|
+
prefix=f"{PREFIX}-OP-FLOAT-03:",
|
|
45
|
+
drv_suffix="CAM:",
|
|
46
|
+
fileio_suffix="HDF5:",
|
|
66
47
|
path_provider=get_path_provider(),
|
|
67
|
-
wait=wait_for_connection,
|
|
68
|
-
fake=fake_with_ophyd_sim,
|
|
69
48
|
)
|
|
70
49
|
|
|
71
50
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Merlin,
|
|
77
|
-
prefix="BL13J-EA-DET-04:",
|
|
51
|
+
@device_factory()
|
|
52
|
+
def merlin() -> Merlin:
|
|
53
|
+
return Merlin(
|
|
54
|
+
prefix=f"{PREFIX}-EA-DET-04:",
|
|
78
55
|
name="merlin",
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
fileio_suffix=HDF5_SUFFIX,
|
|
56
|
+
drv_suffix="CAM:",
|
|
57
|
+
fileio_suffix="HDF5:",
|
|
82
58
|
path_provider=get_path_provider(),
|
|
83
|
-
wait=wait_for_connection,
|
|
84
|
-
fake=fake_with_ophyd_sim,
|
|
85
59
|
)
|
dodal/beamlines/i19_1.py
CHANGED
|
@@ -4,7 +4,9 @@ from dodal.common.beamlines.beamline_utils import (
|
|
|
4
4
|
from dodal.common.beamlines.beamline_utils import (
|
|
5
5
|
set_beamline as set_utils_beamline,
|
|
6
6
|
)
|
|
7
|
-
from dodal.devices.i19.
|
|
7
|
+
from dodal.devices.i19.beamstop import BeamStop
|
|
8
|
+
from dodal.devices.i19.blueapi_device import HutchState
|
|
9
|
+
from dodal.devices.i19.shutter import AccessControlledShutter
|
|
8
10
|
from dodal.devices.oav.oav_detector import OAV
|
|
9
11
|
from dodal.devices.oav.oav_parameters import OAVConfig
|
|
10
12
|
from dodal.devices.synchrotron import Synchrotron
|
|
@@ -35,10 +37,20 @@ ZOOM_PARAMS_FILE = (
|
|
|
35
37
|
DISPLAY_CONFIG = "/dls_sw/i19-1/software/daq_configuration/domain/display.configuration"
|
|
36
38
|
|
|
37
39
|
|
|
40
|
+
# Needs to wait until enum is fixed on the beamline
|
|
41
|
+
# See https://github.com/DiamondLightSource/dodal/issues/1150
|
|
42
|
+
@device_factory(skip=True)
|
|
43
|
+
def beamstop() -> BeamStop:
|
|
44
|
+
"""Get the i19-1 beamstop device, instantiate it if it hasn't already been.
|
|
45
|
+
If this is called when already instantiated in i19-1, it will return the existing object.
|
|
46
|
+
"""
|
|
47
|
+
return BeamStop(prefix=f"{PREFIX.beamline_prefix}-RS-ABSB-01:")
|
|
48
|
+
|
|
49
|
+
|
|
38
50
|
@device_factory()
|
|
39
51
|
def oav() -> OAV:
|
|
40
52
|
return OAV(
|
|
41
|
-
prefix=f"{PREFIX.beamline_prefix}-
|
|
53
|
+
prefix=f"{PREFIX.beamline_prefix}-EA-OAV-01:",
|
|
42
54
|
config=OAVConfig(ZOOM_PARAMS_FILE, DISPLAY_CONFIG),
|
|
43
55
|
)
|
|
44
56
|
|
|
@@ -56,11 +68,11 @@ def zebra() -> Zebra:
|
|
|
56
68
|
|
|
57
69
|
|
|
58
70
|
@device_factory()
|
|
59
|
-
def shutter() ->
|
|
60
|
-
"""Get the i19-
|
|
71
|
+
def shutter() -> AccessControlledShutter:
|
|
72
|
+
"""Get the i19-1 hutch shutter device, instantiate it if it hasn't already been.
|
|
61
73
|
If this is called when already instantiated, it will return the existing object.
|
|
62
74
|
"""
|
|
63
|
-
return
|
|
75
|
+
return AccessControlledShutter(
|
|
64
76
|
prefix=f"{PREFIX.beamline_prefix}-PS-SHTR-01:",
|
|
65
77
|
hutch=HutchState.EH1,
|
|
66
78
|
)
|
dodal/beamlines/i19_2.py
CHANGED
|
@@ -4,7 +4,9 @@ from dodal.common.beamlines.beamline_utils import (
|
|
|
4
4
|
from dodal.common.beamlines.beamline_utils import (
|
|
5
5
|
set_beamline as set_utils_beamline,
|
|
6
6
|
)
|
|
7
|
-
from dodal.devices.i19.
|
|
7
|
+
from dodal.devices.i19.beamstop import BeamStop
|
|
8
|
+
from dodal.devices.i19.blueapi_device import HutchState
|
|
9
|
+
from dodal.devices.i19.shutter import AccessControlledShutter
|
|
8
10
|
from dodal.devices.synchrotron import Synchrotron
|
|
9
11
|
from dodal.devices.zebra.zebra import Zebra
|
|
10
12
|
from dodal.devices.zebra.zebra_constants_mapping import (
|
|
@@ -30,6 +32,14 @@ I19_2_ZEBRA_MAPPING = ZebraMapping(
|
|
|
30
32
|
)
|
|
31
33
|
|
|
32
34
|
|
|
35
|
+
@device_factory()
|
|
36
|
+
def beamstop() -> BeamStop:
|
|
37
|
+
"""Get the i19-2 beamstop device, instantiate it if it hasn't already been.
|
|
38
|
+
If this is called when already instantiated in i19-2, it will return the existing object.
|
|
39
|
+
"""
|
|
40
|
+
return BeamStop(prefix=f"{PREFIX.beamline_prefix}-OP-ABSB-02:")
|
|
41
|
+
|
|
42
|
+
|
|
33
43
|
@device_factory()
|
|
34
44
|
def zebra() -> Zebra:
|
|
35
45
|
"""Get the i19-2 zebra device, instantiate it if it hasn't already been.
|
|
@@ -43,11 +53,11 @@ def zebra() -> Zebra:
|
|
|
43
53
|
|
|
44
54
|
|
|
45
55
|
@device_factory()
|
|
46
|
-
def shutter() ->
|
|
56
|
+
def shutter() -> AccessControlledShutter:
|
|
47
57
|
"""Get the i19-2 hutch shutter device, instantiate it if it hasn't already been.
|
|
48
58
|
If this is called when already instantiated, it will return the existing object.
|
|
49
59
|
"""
|
|
50
|
-
return
|
|
60
|
+
return AccessControlledShutter(
|
|
51
61
|
prefix=f"{PREFIX.beamline_prefix}-PS-SHTR-01:",
|
|
52
62
|
hutch=HutchState.EH2,
|
|
53
63
|
)
|
dodal/beamlines/i19_optics.py
CHANGED
|
@@ -5,7 +5,7 @@ from dodal.common.beamlines.beamline_utils import (
|
|
|
5
5
|
set_beamline as set_utils_beamline,
|
|
6
6
|
)
|
|
7
7
|
from dodal.devices.hutch_shutter import HutchShutter
|
|
8
|
-
from dodal.devices.i19.hutch_access import HutchAccessControl
|
|
8
|
+
from dodal.devices.i19.hutch_access import ACCESS_DEVICE_NAME, HutchAccessControl
|
|
9
9
|
from dodal.log import set_beamline as set_log_beamline
|
|
10
10
|
from dodal.utils import BeamlinePrefix
|
|
11
11
|
|
|
@@ -31,4 +31,6 @@ def access_control() -> HutchAccessControl:
|
|
|
31
31
|
"""Get a device that checks the active hutch for i19, instantiate it if it hasn't already been.
|
|
32
32
|
If this is called when already instantiated, it will return the existing object.
|
|
33
33
|
"""
|
|
34
|
-
return HutchAccessControl(
|
|
34
|
+
return HutchAccessControl(
|
|
35
|
+
f"{PREFIX.beamline_prefix}-OP-STAT-01:", ACCESS_DEVICE_NAME
|
|
36
|
+
)
|
dodal/beamlines/i20_1.py
CHANGED
|
@@ -32,7 +32,8 @@ set_path_provider(
|
|
|
32
32
|
)
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
# NOTE this is mock as we cannot move items on the beamline until we get sign-off to do so
|
|
36
|
+
@device_factory(mock=True)
|
|
36
37
|
def turbo_slit() -> TurboSlit:
|
|
37
38
|
"""
|
|
38
39
|
turboslit for selecting energy from the polychromator
|
dodal/beamlines/i23.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from dodal.common.beamlines.beamline_utils import device_factory
|
|
2
2
|
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
|
|
3
|
+
from dodal.devices.motors import SixAxisGonio
|
|
3
4
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
4
5
|
from dodal.log import set_beamline as set_log_beamline
|
|
5
6
|
from dodal.utils import BeamlinePrefix, get_beamline_name, get_hostname
|
|
@@ -28,3 +29,12 @@ def oav_pin_tip_detection() -> PinTipDetection:
|
|
|
28
29
|
f"{PREFIX.beamline_prefix}-DI-OAV-01:",
|
|
29
30
|
"pin_tip_detection",
|
|
30
31
|
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@device_factory()
|
|
35
|
+
def gonio() -> SixAxisGonio:
|
|
36
|
+
"""Get the i23 goniometer"""
|
|
37
|
+
|
|
38
|
+
return SixAxisGonio(
|
|
39
|
+
f"{PREFIX.beamline_prefix}-MO-GONIO-01:",
|
|
40
|
+
)
|
dodal/beamlines/p60.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dodal.common.beamlines.beamline_utils import (
|
|
2
|
+
device_factory,
|
|
3
|
+
)
|
|
4
|
+
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
|
|
5
|
+
from dodal.devices.electron_analyser.vgscienta_analyser_io import (
|
|
6
|
+
VGScientaAnalyserDriverIO,
|
|
7
|
+
)
|
|
8
|
+
from dodal.log import set_beamline as set_log_beamline
|
|
9
|
+
from dodal.utils import BeamlinePrefix, get_beamline_name
|
|
10
|
+
|
|
11
|
+
BL = get_beamline_name("p60")
|
|
12
|
+
PREFIX = BeamlinePrefix(BL)
|
|
13
|
+
set_log_beamline(BL)
|
|
14
|
+
set_utils_beamline(BL)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@device_factory()
|
|
18
|
+
def analyser_driver() -> VGScientaAnalyserDriverIO:
|
|
19
|
+
return VGScientaAnalyserDriverIO(
|
|
20
|
+
name="analyser_driver", prefix=f"{PREFIX.beamline_prefix}-EA-DET-01:CAM:"
|
|
21
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from typing import TypeVar
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
TBaseModel = TypeVar("TBaseModel", bound=BaseModel)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def load_json_file_to_class(
|
|
9
|
+
t: type[TBaseModel],
|
|
10
|
+
file: str,
|
|
11
|
+
) -> TBaseModel:
|
|
12
|
+
with open(file) as f:
|
|
13
|
+
json_obj = f.read()
|
|
14
|
+
cls = t.model_validate_json(json_obj)
|
|
15
|
+
return cls
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def save_class_to_json_file(model: BaseModel, file: str) -> None:
|
|
19
|
+
with open(file, "w") as f:
|
|
20
|
+
f.write(model.model_dump_json())
|
dodal/common/signal_utils.py
CHANGED
|
@@ -2,22 +2,26 @@ from collections.abc import Callable, Coroutine
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from bluesky.protocols import Reading
|
|
5
|
-
from ophyd_async.core import SignalDatatypeT, SignalR, SoftSignalBackend
|
|
5
|
+
from ophyd_async.core import SignalDatatypeT, SignalR, SignalRW, SoftSignalBackend
|
|
6
|
+
|
|
7
|
+
SetHardwareType = Callable[[SignalDatatypeT], Coroutine[Any, Any, None]]
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class HardwareBackedSoftSignalBackend(SoftSignalBackend[SignalDatatypeT]):
|
|
9
11
|
def __init__(
|
|
10
12
|
self,
|
|
11
13
|
get_from_hardware_func: Callable[[], Coroutine[Any, Any, SignalDatatypeT]],
|
|
14
|
+
set_to_hardware_func: SetHardwareType | None = None,
|
|
12
15
|
*args,
|
|
13
16
|
**kwargs,
|
|
14
17
|
) -> None:
|
|
15
18
|
self.get_from_hardware_func = get_from_hardware_func
|
|
19
|
+
self.set_to_hardware_func = set_to_hardware_func
|
|
16
20
|
super().__init__(*args, **kwargs)
|
|
17
21
|
|
|
18
22
|
async def _update_value(self):
|
|
19
23
|
new_value = await self.get_from_hardware_func()
|
|
20
|
-
|
|
24
|
+
self.set_value(new_value)
|
|
21
25
|
|
|
22
26
|
async def get_reading(self) -> Reading:
|
|
23
27
|
await self._update_value()
|
|
@@ -27,8 +31,39 @@ class HardwareBackedSoftSignalBackend(SoftSignalBackend[SignalDatatypeT]):
|
|
|
27
31
|
await self._update_value()
|
|
28
32
|
return await super().get_value()
|
|
29
33
|
|
|
34
|
+
async def put(self, value: SignalDatatypeT | None, wait: bool) -> None:
|
|
35
|
+
if self.set_to_hardware_func:
|
|
36
|
+
write_value = self.initial_value if value is None else value
|
|
37
|
+
await self.set_to_hardware_func(write_value)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def create_rw_hardware_backed_soft_signal(
|
|
41
|
+
datatype: type[SignalDatatypeT],
|
|
42
|
+
get_from_hardware_func: Callable[[], Coroutine[Any, Any, SignalDatatypeT]],
|
|
43
|
+
set_to_hardware_func: SetHardwareType,
|
|
44
|
+
units: str | None = None,
|
|
45
|
+
precision: int | None = None,
|
|
46
|
+
):
|
|
47
|
+
"""Creates a soft signal that, when read will call the function passed into
|
|
48
|
+
`get_from_hardware_func` and return this. When set it will call `set_to_hardware_func`
|
|
49
|
+
and send something to the hardware.
|
|
50
|
+
|
|
51
|
+
This will allow you to make soft signals derived from arbitrary hardware signals.
|
|
52
|
+
However, calling subscribe on this signal does not give you a sensible value. See https://github.com/bluesky/ophyd-async/issues/525
|
|
53
|
+
for a more full solution.
|
|
54
|
+
"""
|
|
55
|
+
return SignalRW(
|
|
56
|
+
backend=HardwareBackedSoftSignalBackend(
|
|
57
|
+
get_from_hardware_func,
|
|
58
|
+
set_to_hardware_func,
|
|
59
|
+
datatype,
|
|
60
|
+
units=units,
|
|
61
|
+
precision=precision,
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
|
|
30
65
|
|
|
31
|
-
def
|
|
66
|
+
def create_r_hardware_backed_soft_signal(
|
|
32
67
|
datatype: type[SignalDatatypeT],
|
|
33
68
|
get_from_hardware_func: Callable[[], Coroutine[Any, Any, SignalDatatypeT]],
|
|
34
69
|
units: str | None = None,
|
|
@@ -44,6 +79,10 @@ def create_hardware_backed_soft_signal(
|
|
|
44
79
|
"""
|
|
45
80
|
return SignalR(
|
|
46
81
|
backend=HardwareBackedSoftSignalBackend(
|
|
47
|
-
get_from_hardware_func,
|
|
82
|
+
get_from_hardware_func,
|
|
83
|
+
None,
|
|
84
|
+
datatype,
|
|
85
|
+
units=units,
|
|
86
|
+
precision=precision,
|
|
48
87
|
)
|
|
49
88
|
)
|
dodal/common/visit.py
CHANGED
|
@@ -3,8 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
from typing import Literal
|
|
4
4
|
|
|
5
5
|
from aiohttp import ClientSession
|
|
6
|
-
from
|
|
7
|
-
from ophyd_async.core import FilenameProvider, PathInfo, PathProvider
|
|
6
|
+
from ophyd_async.core import FilenameProvider, PathInfo
|
|
8
7
|
from pydantic import BaseModel
|
|
9
8
|
|
|
10
9
|
from dodal.common.types import UpdatingPathProvider
|
|
@@ -151,42 +150,3 @@ class StaticVisitPathProvider(UpdatingPathProvider):
|
|
|
151
150
|
return PathInfo(
|
|
152
151
|
directory_path=self._root, filename=self._filename_provider(device_name)
|
|
153
152
|
)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
DEFAULT_TEMPLATE = "{device_name}-{instrument}-{scan_id}"
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
class StartDocumentPathProvider(PathProvider):
|
|
160
|
-
"""A PathProvider that sources from metadata in a RunStart document.
|
|
161
|
-
|
|
162
|
-
This uses metadata from a RunStart document to determine file names and data session
|
|
163
|
-
directories. The file naming defaults to "{device_name}-{instrument}-{scan_id}", so
|
|
164
|
-
the file name is incremented by scan number. A template can be included in the
|
|
165
|
-
StartDocument to allow for custom naming conventions.
|
|
166
|
-
|
|
167
|
-
"""
|
|
168
|
-
|
|
169
|
-
def __init__(self) -> None:
|
|
170
|
-
self._doc = {}
|
|
171
|
-
|
|
172
|
-
def update_run(self, name: str, start_doc: RunStart) -> None:
|
|
173
|
-
"""Cache a start document.
|
|
174
|
-
|
|
175
|
-
This can be plugged into the run engine's subscribe method.
|
|
176
|
-
"""
|
|
177
|
-
if name == "start":
|
|
178
|
-
self._doc = start_doc
|
|
179
|
-
|
|
180
|
-
def __call__(self, device_name: str | None = None) -> PathInfo:
|
|
181
|
-
"""Returns the directory path and filename for a given data_session.
|
|
182
|
-
|
|
183
|
-
The default template for file naming is: "{device_name}-{instrument}-{scan_id}"
|
|
184
|
-
however, this can be changed by providing a template in the start document. For
|
|
185
|
-
example: "template": "custom-{device_name}--{scan_id}".
|
|
186
|
-
|
|
187
|
-
If you do not provide a data_session_directory it will default to "/tmp".
|
|
188
|
-
"""
|
|
189
|
-
template = self._doc.get("template", DEFAULT_TEMPLATE)
|
|
190
|
-
sub_path = template.format_map(self._doc | {"device_name": device_name})
|
|
191
|
-
data_session_directory = Path(self._doc.get("data_session_directory", "/tmp"))
|
|
192
|
-
return PathInfo(directory_path=data_session_directory, filename=sub_path)
|
|
@@ -12,7 +12,7 @@ from ophyd_async.core import (
|
|
|
12
12
|
from pydantic import BaseModel, Field
|
|
13
13
|
|
|
14
14
|
from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters
|
|
15
|
-
from dodal.common.signal_utils import
|
|
15
|
+
from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
|
|
16
16
|
from dodal.devices.aperture import Aperture
|
|
17
17
|
from dodal.devices.scatterguard import Scatterguard
|
|
18
18
|
|
|
@@ -164,7 +164,7 @@ class ApertureScatterguard(StandardReadable, Movable[ApertureValue], Preparable)
|
|
|
164
164
|
) -> None:
|
|
165
165
|
self.aperture = Aperture(prefix + "-MO-MAPT-01:")
|
|
166
166
|
self.scatterguard = Scatterguard(prefix + "-MO-SCAT-01:")
|
|
167
|
-
self.radius =
|
|
167
|
+
self.radius = create_r_hardware_backed_soft_signal(
|
|
168
168
|
float, self._get_current_radius, units="µm"
|
|
169
169
|
)
|
|
170
170
|
self._loaded_positions = loaded_positions
|
|
@@ -181,7 +181,7 @@ class ApertureScatterguard(StandardReadable, Movable[ApertureValue], Preparable)
|
|
|
181
181
|
)
|
|
182
182
|
|
|
183
183
|
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
184
|
-
self.selected_aperture =
|
|
184
|
+
self.selected_aperture = create_r_hardware_backed_soft_signal(
|
|
185
185
|
ApertureValue, self._get_current_aperture_position
|
|
186
186
|
)
|
|
187
187
|
|
dodal/devices/baton.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Annotated as A
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import (
|
|
4
|
+
SignalRW,
|
|
5
|
+
StandardReadable,
|
|
6
|
+
)
|
|
7
|
+
from ophyd_async.core import StandardReadableFormat as Format
|
|
8
|
+
from ophyd_async.epics.core import EpicsDevice, PvSuffix
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Baton(StandardReadable, EpicsDevice):
|
|
12
|
+
requested_user: A[
|
|
13
|
+
SignalRW[str], PvSuffix("REQUESTED_USER"), Format.HINTED_UNCACHED_SIGNAL
|
|
14
|
+
]
|
|
15
|
+
current_user: A[
|
|
16
|
+
SignalRW[str], PvSuffix("CURRENT_USER"), Format.HINTED_UNCACHED_SIGNAL
|
|
17
|
+
]
|
|
@@ -23,7 +23,6 @@ class CurrentAmp(ABC, StandardReadable, Movable):
|
|
|
23
23
|
super().__init__(name)
|
|
24
24
|
|
|
25
25
|
@abstractmethod
|
|
26
|
-
@AsyncStatus.wrap
|
|
27
26
|
async def increase_gain(self, value: int = 1) -> None:
|
|
28
27
|
"""Increase gain, increment by 1 by default.
|
|
29
28
|
|
|
@@ -31,7 +30,6 @@ class CurrentAmp(ABC, StandardReadable, Movable):
|
|
|
31
30
|
bool: True if success.
|
|
32
31
|
"""
|
|
33
32
|
|
|
34
|
-
@AsyncStatus.wrap
|
|
35
33
|
@abstractmethod
|
|
36
34
|
async def decrease_gain(self, value: int = 1) -> None:
|
|
37
35
|
"""Decrease gain, decrement by 1 by default.
|
|
@@ -40,21 +38,18 @@ class CurrentAmp(ABC, StandardReadable, Movable):
|
|
|
40
38
|
bool: True if success.
|
|
41
39
|
"""
|
|
42
40
|
|
|
43
|
-
@AsyncStatus.wrap
|
|
44
41
|
@abstractmethod
|
|
45
|
-
async def get_gain(self) ->
|
|
42
|
+
async def get_gain(self) -> Enum:
|
|
46
43
|
"""Get the current gain setting
|
|
47
44
|
|
|
48
45
|
Returns:
|
|
49
46
|
Enum: The member name of the current gain setting in gain_conversion_table.
|
|
50
47
|
"""
|
|
51
48
|
|
|
52
|
-
@AsyncStatus.wrap
|
|
53
49
|
@abstractmethod
|
|
54
50
|
async def get_upperlimit(self) -> float:
|
|
55
51
|
"""Get the upper limit of the current amplifier"""
|
|
56
52
|
|
|
57
|
-
@AsyncStatus.wrap
|
|
58
53
|
@abstractmethod
|
|
59
54
|
async def get_lowerlimit(self) -> float:
|
|
60
55
|
"""Get the lower limit of the current amplifier"""
|
|
@@ -92,8 +92,8 @@ class CurrentAmpDet(StandardReadable, Preparable):
|
|
|
92
92
|
self.current_amp().get_gain(),
|
|
93
93
|
self.counter().get_voltage_per_sec(),
|
|
94
94
|
)
|
|
95
|
-
|
|
96
|
-
corrected_current = voltage_per_sec /
|
|
95
|
+
assert isinstance(current_gain.value, float)
|
|
96
|
+
corrected_current = voltage_per_sec / current_gain.value
|
|
97
97
|
return corrected_current
|
|
98
98
|
|
|
99
99
|
@AsyncStatus.wrap
|
|
@@ -114,7 +114,6 @@ class FemtoDDPCA(CurrentAmp):
|
|
|
114
114
|
# wait for current amplifier's bandpass filter to settle.
|
|
115
115
|
await asyncio.sleep(self.raise_timetable[SEN_setting].value)
|
|
116
116
|
|
|
117
|
-
@AsyncStatus.wrap
|
|
118
117
|
async def increase_gain(self, value: int = 1) -> None:
|
|
119
118
|
current_gain = int((await self.get_gain()).name.split("_")[-1])
|
|
120
119
|
current_gain += value
|
|
@@ -122,7 +121,6 @@ class FemtoDDPCA(CurrentAmp):
|
|
|
122
121
|
raise ValueError("Gain at max value")
|
|
123
122
|
await self.set(self.gain_conversion_table[f"SEN_{current_gain}"])
|
|
124
123
|
|
|
125
|
-
@AsyncStatus.wrap
|
|
126
124
|
async def decrease_gain(self, value: int = 1) -> None:
|
|
127
125
|
current_gain = int((await self.get_gain()).name.split("_")[-1])
|
|
128
126
|
current_gain -= value
|
|
@@ -130,14 +128,11 @@ class FemtoDDPCA(CurrentAmp):
|
|
|
130
128
|
raise ValueError("Gain at min value")
|
|
131
129
|
await self.set(self.gain_conversion_table[f"SEN_{current_gain}"])
|
|
132
130
|
|
|
133
|
-
@AsyncStatus.wrap
|
|
134
131
|
async def get_gain(self) -> Enum:
|
|
135
132
|
return self.gain_conversion_table[(await self.gain.get_value()).name]
|
|
136
133
|
|
|
137
|
-
@AsyncStatus.wrap
|
|
138
134
|
async def get_upperlimit(self) -> float:
|
|
139
135
|
return self.upperlimit
|
|
140
136
|
|
|
141
|
-
@AsyncStatus.wrap
|
|
142
137
|
async def get_lowerlimit(self) -> float:
|
|
143
138
|
return self.lowerlimit
|
|
@@ -178,7 +178,6 @@ class SR570(CurrentAmp):
|
|
|
178
178
|
)
|
|
179
179
|
await asyncio.sleep(self.raise_timetable[coarse_gain.name].value)
|
|
180
180
|
|
|
181
|
-
@AsyncStatus.wrap
|
|
182
181
|
async def increase_gain(self, value=3) -> None:
|
|
183
182
|
current_gain = int((await self.get_gain()).name.split("_")[-1])
|
|
184
183
|
current_gain += value
|
|
@@ -189,7 +188,6 @@ class SR570(CurrentAmp):
|
|
|
189
188
|
raise ValueError("Gain at max value")
|
|
190
189
|
await self.set(self.gain_conversion_table[f"SEN_{current_gain}"])
|
|
191
190
|
|
|
192
|
-
@AsyncStatus.wrap
|
|
193
191
|
async def decrease_gain(self, value=3) -> None:
|
|
194
192
|
current_gain = int((await self.get_gain()).name.split("_")[-1])
|
|
195
193
|
current_gain -= value
|
|
@@ -198,17 +196,14 @@ class SR570(CurrentAmp):
|
|
|
198
196
|
raise ValueError("Gain at min value")
|
|
199
197
|
await self.set(self.gain_conversion_table[f"SEN_{current_gain}"])
|
|
200
198
|
|
|
201
|
-
@AsyncStatus.wrap
|
|
202
199
|
async def get_gain(self) -> Enum:
|
|
203
200
|
result = await asyncio.gather(
|
|
204
201
|
self.coarse_gain.get_value(), self.fine_gain.get_value()
|
|
205
202
|
)
|
|
206
203
|
return self.gain_conversion_table[self.combined_table(result).name]
|
|
207
204
|
|
|
208
|
-
@AsyncStatus.wrap
|
|
209
205
|
async def get_upperlimit(self) -> float:
|
|
210
206
|
return self.upperlimit
|
|
211
207
|
|
|
212
|
-
@AsyncStatus.wrap
|
|
213
208
|
async def get_lowerlimit(self) -> float:
|
|
214
209
|
return self.lowerlimit
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from dodal.devices.util.lookup_tables import (
|
|
4
|
+
linear_extrapolation_lut,
|
|
5
|
+
parse_lookup_table,
|
|
6
|
+
)
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
class Axis(Enum):
|
|
@@ -11,12 +14,20 @@ class Axis(Enum):
|
|
|
11
14
|
class DetectorDistanceToBeamXYConverter:
|
|
12
15
|
def __init__(self, lookup_file: str):
|
|
13
16
|
self.lookup_file: str = lookup_file
|
|
14
|
-
|
|
17
|
+
lookup_table_columns: list = parse_lookup_table(self.lookup_file)
|
|
18
|
+
self._d_to_x = linear_extrapolation_lut(
|
|
19
|
+
lookup_table_columns[0], lookup_table_columns[1]
|
|
20
|
+
)
|
|
21
|
+
self._d_to_y = linear_extrapolation_lut(
|
|
22
|
+
lookup_table_columns[0], lookup_table_columns[2]
|
|
23
|
+
)
|
|
15
24
|
|
|
16
25
|
def get_beam_xy_from_det_dist(self, det_dist_mm: float, beam_axis: Axis) -> float:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
return (
|
|
27
|
+
self._d_to_x(det_dist_mm)
|
|
28
|
+
if beam_axis == Axis.X_AXIS
|
|
29
|
+
else self._d_to_y(det_dist_mm)
|
|
30
|
+
)
|
|
20
31
|
|
|
21
32
|
def get_beam_axis_pixels(
|
|
22
33
|
self,
|
|
@@ -41,21 +52,3 @@ class DetectorDistanceToBeamXYConverter:
|
|
|
41
52
|
return self.get_beam_axis_pixels(
|
|
42
53
|
det_distance, image_size_pixels, det_dim, Axis.X_AXIS
|
|
43
54
|
)
|
|
44
|
-
|
|
45
|
-
def reload_lookup_table(self):
|
|
46
|
-
self.lookup_table_values = self.parse_table()
|
|
47
|
-
|
|
48
|
-
def parse_table(self) -> list:
|
|
49
|
-
rows = loadtxt(self.lookup_file, delimiter=" ", comments=["#", "Units"])
|
|
50
|
-
columns = list(zip(*rows, strict=False))
|
|
51
|
-
|
|
52
|
-
return columns
|
|
53
|
-
|
|
54
|
-
def __eq__(self, other):
|
|
55
|
-
if not isinstance(other, DetectorDistanceToBeamXYConverter):
|
|
56
|
-
return NotImplemented
|
|
57
|
-
if self.lookup_file != other.lookup_file:
|
|
58
|
-
return False
|
|
59
|
-
if self.lookup_table_values != other.lookup_table_values:
|
|
60
|
-
return False
|
|
61
|
-
return True
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from enum import Enum, auto
|
|
2
|
+
from functools import cached_property
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
from pydantic import BaseModel, Field, field_serializer, field_validator
|
|
@@ -49,7 +50,7 @@ class DetectorParams(BaseModel):
|
|
|
49
50
|
False # Remove in https://github.com/DiamondLightSource/hyperion/issues/1395
|
|
50
51
|
)
|
|
51
52
|
|
|
52
|
-
@
|
|
53
|
+
@cached_property
|
|
53
54
|
def beam_xy_converter(self) -> DetectorDistanceToBeamXYConverter:
|
|
54
55
|
return DetectorDistanceToBeamXYConverter(self.det_dist_to_beam_converter_path)
|
|
55
56
|
|
|
File without changes
|