dls-dodal 1.29.4__py3-none-any.whl → 1.31.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.29.4.dist-info → dls_dodal-1.31.0.dist-info}/METADATA +29 -44
- dls_dodal-1.31.0.dist-info/RECORD +134 -0
- {dls_dodal-1.29.4.dist-info → dls_dodal-1.31.0.dist-info}/WHEEL +1 -1
- dls_dodal-1.31.0.dist-info/entry_points.txt +3 -0
- dodal/__init__.py +1 -4
- dodal/_version.py +2 -2
- dodal/beamline_specific_utils/i03.py +1 -4
- dodal/beamlines/__init__.py +7 -1
- dodal/beamlines/i03.py +34 -29
- dodal/beamlines/i04.py +39 -16
- dodal/beamlines/i13_1.py +66 -0
- dodal/beamlines/i22.py +22 -22
- dodal/beamlines/i24.py +1 -1
- dodal/beamlines/p38.py +21 -21
- dodal/beamlines/p45.py +18 -16
- dodal/beamlines/p99.py +61 -0
- dodal/beamlines/training_rig.py +64 -0
- dodal/cli.py +6 -3
- dodal/common/beamlines/beamline_parameters.py +7 -6
- dodal/common/beamlines/beamline_utils.py +15 -14
- dodal/common/maths.py +1 -3
- dodal/common/types.py +6 -5
- dodal/common/udc_directory_provider.py +39 -21
- dodal/common/visit.py +60 -62
- dodal/devices/CTAB.py +22 -17
- dodal/devices/aperture.py +1 -1
- dodal/devices/aperturescatterguard.py +139 -209
- dodal/devices/areadetector/adaravis.py +8 -6
- dodal/devices/areadetector/adsim.py +2 -3
- dodal/devices/areadetector/adutils.py +20 -12
- dodal/devices/areadetector/plugins/MJPG.py +2 -1
- dodal/devices/backlight.py +12 -1
- dodal/devices/cryostream.py +19 -7
- dodal/devices/dcm.py +1 -1
- dodal/devices/detector/__init__.py +13 -2
- dodal/devices/detector/det_dim_constants.py +2 -2
- dodal/devices/detector/det_dist_to_beam_converter.py +1 -1
- dodal/devices/detector/detector.py +33 -32
- dodal/devices/detector/detector_motion.py +38 -31
- dodal/devices/eiger.py +11 -15
- dodal/devices/eiger_odin.py +9 -10
- dodal/devices/fast_grid_scan.py +18 -27
- dodal/devices/fluorescence_detector_motion.py +13 -4
- dodal/devices/focusing_mirror.py +6 -6
- dodal/devices/hutch_shutter.py +4 -4
- dodal/devices/i22/dcm.py +5 -4
- dodal/devices/i22/fswitch.py +10 -6
- dodal/devices/i22/nxsas.py +55 -43
- dodal/devices/i24/aperture.py +1 -1
- dodal/devices/i24/beamstop.py +1 -1
- dodal/devices/i24/dcm.py +1 -1
- dodal/devices/i24/{I24_detector_motion.py → i24_detector_motion.py} +1 -1
- dodal/devices/i24/pmac.py +67 -12
- dodal/devices/ipin.py +7 -4
- dodal/devices/linkam3.py +12 -6
- dodal/devices/logging_ophyd_device.py +1 -1
- dodal/devices/motors.py +32 -6
- dodal/devices/oav/grid_overlay.py +1 -0
- dodal/devices/oav/microns_for_zoom_levels.json +1 -1
- dodal/devices/oav/oav_detector.py +2 -1
- dodal/devices/oav/oav_parameters.py +18 -10
- dodal/devices/oav/oav_to_redis_forwarder.py +129 -0
- dodal/devices/oav/pin_image_recognition/__init__.py +6 -6
- dodal/devices/oav/pin_image_recognition/utils.py +5 -6
- dodal/devices/oav/utils.py +2 -2
- dodal/devices/p99/__init__.py +0 -0
- dodal/devices/p99/sample_stage.py +43 -0
- dodal/devices/robot.py +31 -20
- dodal/devices/scatterguard.py +1 -1
- dodal/devices/scintillator.py +8 -5
- dodal/devices/slits.py +1 -1
- dodal/devices/smargon.py +4 -4
- dodal/devices/status.py +2 -31
- dodal/devices/tetramm.py +23 -19
- dodal/devices/thawer.py +5 -3
- dodal/devices/training_rig/__init__.py +0 -0
- dodal/devices/training_rig/sample_stage.py +10 -0
- dodal/devices/turbo_slit.py +1 -1
- dodal/devices/undulator.py +1 -1
- dodal/devices/undulator_dcm.py +6 -8
- dodal/devices/util/adjuster_plans.py +3 -3
- dodal/devices/util/epics_util.py +5 -7
- dodal/devices/util/lookup_tables.py +2 -3
- dodal/devices/util/save_panda.py +87 -0
- dodal/devices/util/test_utils.py +17 -0
- dodal/devices/webcam.py +3 -3
- dodal/devices/xbpm_feedback.py +1 -25
- dodal/devices/xspress3/xspress3.py +1 -1
- dodal/devices/zebra.py +15 -10
- dodal/devices/zebra_controlled_shutter.py +26 -11
- dodal/devices/zocalo/zocalo_interaction.py +10 -2
- dodal/devices/zocalo/zocalo_results.py +36 -19
- dodal/log.py +46 -15
- dodal/plans/check_topup.py +65 -10
- dodal/plans/data_session_metadata.py +8 -9
- dodal/plans/motor_util_plans.py +117 -0
- dodal/utils.py +65 -22
- dls_dodal-1.29.4.dist-info/RECORD +0 -125
- dls_dodal-1.29.4.dist-info/entry_points.txt +0 -2
- dodal/devices/beamstop.py +0 -8
- dodal/devices/qbpm1.py +0 -8
- {dls_dodal-1.29.4.dist-info → dls_dodal-1.31.0.dist-info}/LICENSE +0 -0
- {dls_dodal-1.29.4.dist-info → dls_dodal-1.31.0.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import inspect
|
|
2
|
-
from
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Final, TypeVar, cast
|
|
3
4
|
|
|
4
5
|
from bluesky.run_engine import call_in_bluesky_event_loop
|
|
5
6
|
from ophyd import Device as OphydV1Device
|
|
@@ -7,14 +8,14 @@ from ophyd.sim import make_fake_device
|
|
|
7
8
|
from ophyd_async.core import Device as OphydV2Device
|
|
8
9
|
from ophyd_async.core import wait_for_connection as v2_device_wait_for_connection
|
|
9
10
|
|
|
10
|
-
from dodal.common.types import
|
|
11
|
+
from dodal.common.types import UpdatingPathProvider
|
|
11
12
|
from dodal.utils import AnyDevice, BeamlinePrefix, skip_device
|
|
12
13
|
|
|
13
14
|
DEFAULT_CONNECTION_TIMEOUT: Final[float] = 5.0
|
|
14
15
|
|
|
15
|
-
ACTIVE_DEVICES:
|
|
16
|
+
ACTIVE_DEVICES: dict[str, AnyDevice] = {}
|
|
16
17
|
BL = ""
|
|
17
|
-
|
|
18
|
+
PATH_PROVIDER: UpdatingPathProvider | None = None
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def set_beamline(beamline: str):
|
|
@@ -33,7 +34,7 @@ def clear_device(name: str):
|
|
|
33
34
|
del ACTIVE_DEVICES[name]
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
def list_active_devices() ->
|
|
37
|
+
def list_active_devices() -> list[str]:
|
|
37
38
|
global ACTIVE_DEVICES
|
|
38
39
|
return list(ACTIVE_DEVICES.keys())
|
|
39
40
|
|
|
@@ -59,7 +60,7 @@ def wait_for_connection(
|
|
|
59
60
|
)
|
|
60
61
|
else:
|
|
61
62
|
raise TypeError(
|
|
62
|
-
"Invalid type {} in _wait_for_connection"
|
|
63
|
+
f"Invalid type {device.__class__.__name__} in _wait_for_connection"
|
|
63
64
|
)
|
|
64
65
|
|
|
65
66
|
|
|
@@ -73,7 +74,7 @@ def device_instantiation(
|
|
|
73
74
|
prefix: str,
|
|
74
75
|
wait: bool,
|
|
75
76
|
fake: bool,
|
|
76
|
-
post_create:
|
|
77
|
+
post_create: Callable[[T], None] | None = None,
|
|
77
78
|
bl_prefix: bool = True,
|
|
78
79
|
**kwargs,
|
|
79
80
|
) -> T:
|
|
@@ -124,15 +125,15 @@ def device_instantiation(
|
|
|
124
125
|
return device_instance
|
|
125
126
|
|
|
126
127
|
|
|
127
|
-
def
|
|
128
|
-
global
|
|
128
|
+
def set_path_provider(provider: UpdatingPathProvider):
|
|
129
|
+
global PATH_PROVIDER
|
|
129
130
|
|
|
130
|
-
|
|
131
|
+
PATH_PROVIDER = provider
|
|
131
132
|
|
|
132
133
|
|
|
133
|
-
def
|
|
134
|
-
if
|
|
134
|
+
def get_path_provider() -> UpdatingPathProvider:
|
|
135
|
+
if PATH_PROVIDER is None:
|
|
135
136
|
raise ValueError(
|
|
136
|
-
"
|
|
137
|
+
"PathProvider has not been set! Ophyd-async StandardDetectors will not be able to write!"
|
|
137
138
|
)
|
|
138
|
-
return
|
|
139
|
+
return PATH_PROVIDER
|
dodal/common/maths.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
from typing import Tuple
|
|
2
|
-
|
|
3
1
|
import numpy as np
|
|
4
2
|
|
|
5
3
|
|
|
6
|
-
def step_to_num(start: float, stop: float, step: float) ->
|
|
4
|
+
def step_to_num(start: float, stop: float, step: float) -> tuple[float, float, int]:
|
|
7
5
|
"""
|
|
8
6
|
Standard handling for converting from start, stop, step to start, stop, num
|
|
9
7
|
Forces step to be same direction as length
|
dodal/common/types.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
+
from collections.abc import Callable, Generator
|
|
2
3
|
from typing import (
|
|
3
4
|
Any,
|
|
4
|
-
Callable,
|
|
5
|
-
Generator,
|
|
6
5
|
)
|
|
7
6
|
|
|
8
7
|
from bluesky.utils import Msg
|
|
9
|
-
from ophyd_async.core import
|
|
8
|
+
from ophyd_async.core import PathProvider
|
|
10
9
|
|
|
11
10
|
# String identifier used by 'wait' or stubs that await
|
|
12
11
|
Group = str
|
|
@@ -16,6 +15,8 @@ MsgGenerator = Generator[Msg, Any, None]
|
|
|
16
15
|
PlanGenerator = Callable[..., MsgGenerator]
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
class
|
|
18
|
+
class UpdatingPathProvider(PathProvider, ABC):
|
|
20
19
|
@abstractmethod
|
|
21
|
-
def
|
|
20
|
+
async def data_session(self) -> str: ...
|
|
21
|
+
@abstractmethod
|
|
22
|
+
async def update(self, **kwargs) -> None: ...
|
|
@@ -1,36 +1,54 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
|
-
from ophyd_async.core import
|
|
3
|
+
from ophyd_async.core import FilenameProvider, PathInfo
|
|
4
4
|
|
|
5
|
-
from dodal.common.types import
|
|
5
|
+
from dodal.common.types import UpdatingPathProvider
|
|
6
6
|
from dodal.log import LOGGER
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class PandAFilenameProvider(FilenameProvider):
|
|
10
|
+
def __init__(self, suffix: str | None = None):
|
|
11
|
+
self.suffix = suffix
|
|
12
|
+
|
|
13
|
+
def __call__(self, device_name: str | None = None):
|
|
14
|
+
return f"{device_name}-{self.suffix}"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PandASubpathProvider(UpdatingPathProvider):
|
|
10
18
|
"""Directory provider for the HDFPanda. Points to a panda subdirectory within the
|
|
11
|
-
directory path provided
|
|
19
|
+
directory path provided"""
|
|
12
20
|
|
|
13
21
|
resource_dir = Path("panda")
|
|
14
22
|
|
|
15
|
-
def __init__(self,
|
|
16
|
-
|
|
23
|
+
def __init__(self, root_directory: Path | None = None, suffix: str = ""):
|
|
24
|
+
self._output_directory: Path | None = (
|
|
25
|
+
root_directory / self.resource_dir if root_directory else None
|
|
26
|
+
)
|
|
27
|
+
self._filename_provider = PandAFilenameProvider(suffix=suffix)
|
|
28
|
+
if self._output_directory is None:
|
|
17
29
|
LOGGER.debug(
|
|
18
30
|
f"{self.__class__.__name__} instantiated with no root path, update() must be called before writing data!"
|
|
19
31
|
)
|
|
20
|
-
self._directory_info = (
|
|
21
|
-
DirectoryInfo(root=directory, resource_dir=self.resource_dir)
|
|
22
|
-
if directory
|
|
23
|
-
else None
|
|
24
|
-
)
|
|
25
32
|
|
|
26
|
-
def
|
|
27
|
-
self.
|
|
28
|
-
|
|
33
|
+
async def data_session(self) -> str:
|
|
34
|
+
return self._filename_provider.suffix or ""
|
|
35
|
+
|
|
36
|
+
async def update(self, *, directory: Path, suffix: str = "", **kwargs):
|
|
37
|
+
"""Update the root directory into which panda pcap files are written. This will result in the panda
|
|
38
|
+
subdirectory being created if it does not already exist.
|
|
39
|
+
Args:
|
|
40
|
+
directory: Path instance that identifies the root folder. This folder must exist. The panda will
|
|
41
|
+
attempt to write into the "panda" subdirectory which will be created if not already present.
|
|
42
|
+
suffix: Optional str that will be appended to the panda device name along with the file
|
|
43
|
+
type extension to construct the output filename
|
|
44
|
+
"""
|
|
45
|
+
self._output_directory = directory / self.resource_dir
|
|
46
|
+
self._filename_provider.suffix = suffix
|
|
47
|
+
|
|
48
|
+
def __call__(self, device_name: str | None = None) -> PathInfo:
|
|
49
|
+
assert self._output_directory, "Directory unknown for PandA to write into, update() needs to be called at least once"
|
|
50
|
+
return PathInfo(
|
|
51
|
+
directory_path=self._output_directory,
|
|
52
|
+
filename=self._filename_provider(device_name),
|
|
53
|
+
create_dir_depth=-1, # allows PandA HDFWriter to make any number of dirs
|
|
29
54
|
)
|
|
30
|
-
|
|
31
|
-
def __call__(self) -> DirectoryInfo:
|
|
32
|
-
if self._directory_info is None:
|
|
33
|
-
raise ValueError(
|
|
34
|
-
"Directory unknown for PandA to write into, update() needs to be called at least once"
|
|
35
|
-
)
|
|
36
|
-
return self._directory_info
|
dodal/common/visit.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Literal
|
|
4
4
|
|
|
5
5
|
from aiohttp import ClientSession
|
|
6
|
-
from ophyd_async.core import
|
|
6
|
+
from ophyd_async.core import FilenameProvider, PathInfo
|
|
7
7
|
from pydantic import BaseModel
|
|
8
8
|
|
|
9
|
-
from dodal.common.types import
|
|
9
|
+
from dodal.common.types import UpdatingPathProvider
|
|
10
10
|
from dodal.log import LOGGER
|
|
11
11
|
|
|
12
12
|
"""
|
|
@@ -23,7 +23,7 @@ class DataCollectionIdentifier(BaseModel):
|
|
|
23
23
|
collectionNumber: int
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
class
|
|
26
|
+
class DirectoryServiceClient(ABC):
|
|
27
27
|
"""
|
|
28
28
|
Object responsible for I/O in determining collection number
|
|
29
29
|
"""
|
|
@@ -37,41 +37,53 @@ class DirectoryServiceClientBase(ABC):
|
|
|
37
37
|
"""Get current collection"""
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
class
|
|
40
|
+
class DiamondFilenameProvider(FilenameProvider):
|
|
41
|
+
def __init__(self, beamline: str, client: DirectoryServiceClient):
|
|
42
|
+
self._beamline = beamline
|
|
43
|
+
self._client = client
|
|
44
|
+
self.collectionId: DataCollectionIdentifier | None = None
|
|
45
|
+
|
|
46
|
+
def __call__(self, device_name: str | None = None):
|
|
47
|
+
assert device_name, "Diamond filename requires device_name to be passed"
|
|
48
|
+
assert self.collectionId is not None
|
|
49
|
+
return f"{self._beamline}-{self.collectionId.collectionNumber}-{device_name}"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class RemoteDirectoryServiceClient(DirectoryServiceClient):
|
|
41
53
|
"""Client for the VisitService REST API
|
|
42
54
|
Currently exposed by the GDA Server to co-ordinate unique filenames.
|
|
43
55
|
While VisitService is embedded in GDA, url is likely to be `ixx-control:8088/api`
|
|
44
56
|
"""
|
|
45
57
|
|
|
46
|
-
_url: str
|
|
47
|
-
|
|
48
58
|
def __init__(self, url: str) -> None:
|
|
49
59
|
self._url = url
|
|
50
60
|
|
|
51
61
|
async def create_new_collection(self) -> DataCollectionIdentifier:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
json = await response.json()
|
|
56
|
-
new_collection = DataCollectionIdentifier.parse_obj(json)
|
|
57
|
-
LOGGER.debug("New DataCollection: %s", new_collection)
|
|
58
|
-
return new_collection
|
|
62
|
+
new_collection = await self._identifier_from_response("POST")
|
|
63
|
+
LOGGER.debug("New DataCollection: %s", new_collection)
|
|
64
|
+
return new_collection
|
|
59
65
|
|
|
60
66
|
async def get_current_collection(self) -> DataCollectionIdentifier:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
json = await response.json()
|
|
65
|
-
current_collection = DataCollectionIdentifier.parse_obj(json)
|
|
66
|
-
LOGGER.debug("Current DataCollection: %s", current_collection)
|
|
67
|
-
return current_collection
|
|
68
|
-
|
|
67
|
+
current_collection = await self._identifier_from_response("GET")
|
|
68
|
+
LOGGER.debug("Current DataCollection: %s", current_collection)
|
|
69
|
+
return current_collection
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
async def _identifier_from_response(
|
|
72
|
+
self,
|
|
73
|
+
method: Literal["GET", "POST"],
|
|
74
|
+
) -> DataCollectionIdentifier:
|
|
75
|
+
async with (
|
|
76
|
+
ClientSession() as session,
|
|
77
|
+
session.request(method, f"{self._url}/numtracker") as response,
|
|
78
|
+
):
|
|
79
|
+
response.raise_for_status()
|
|
80
|
+
json = await response.json()
|
|
81
|
+
return DataCollectionIdentifier.model_validate_json(json)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class LocalDirectoryServiceClient(DirectoryServiceClient):
|
|
71
85
|
"""Local or dummy impl of VisitService client to co-ordinate unique filenames."""
|
|
72
86
|
|
|
73
|
-
_count: int
|
|
74
|
-
|
|
75
87
|
def __init__(self) -> None:
|
|
76
88
|
self._count = 0
|
|
77
89
|
|
|
@@ -85,35 +97,32 @@ class LocalDirectoryServiceClient(DirectoryServiceClientBase):
|
|
|
85
97
|
return DataCollectionIdentifier(collectionNumber=self._count)
|
|
86
98
|
|
|
87
99
|
|
|
88
|
-
class
|
|
100
|
+
class StaticVisitPathProvider(UpdatingPathProvider):
|
|
89
101
|
"""
|
|
90
|
-
Static (single visit) implementation of
|
|
102
|
+
Static (single visit) implementation of PathProvider whilst awaiting auth infrastructure to generate necessary information per-scan.
|
|
91
103
|
Allows setting a singular visit into which all run files will be saved.
|
|
92
104
|
update() queries a visit service to get the next DataCollectionIdentifier to increment the suffix of all file writers' next files.
|
|
93
105
|
Requires that all detectors are running with a mutual view on the filesystem.
|
|
94
106
|
Supports a single Visit which should be passed as a Path relative to the root of the Detector IOC mounting.
|
|
95
|
-
i.e. to write to visit /dls/ixx/data/YYYY/cm12345-1
|
|
107
|
+
i.e. to write to visit /dls/ixx/data/YYYY/cm12345-1
|
|
96
108
|
"""
|
|
97
109
|
|
|
98
|
-
_beamline: str
|
|
99
|
-
_root: Path
|
|
100
|
-
_client: DirectoryServiceClientBase
|
|
101
|
-
_current_collection: DirectoryInfo | None
|
|
102
|
-
_session: ClientSession | None
|
|
103
|
-
|
|
104
110
|
def __init__(
|
|
105
111
|
self,
|
|
106
112
|
beamline: str,
|
|
107
113
|
root: Path,
|
|
108
|
-
client:
|
|
114
|
+
client: DirectoryServiceClient | None = None,
|
|
109
115
|
):
|
|
110
116
|
self._beamline = beamline
|
|
111
|
-
self._client = client or
|
|
117
|
+
self._client = client or RemoteDirectoryServiceClient(
|
|
118
|
+
f"{beamline}-control:8088/api"
|
|
119
|
+
)
|
|
120
|
+
self._filename_provider = DiamondFilenameProvider(self._beamline, self._client)
|
|
112
121
|
self._root = root
|
|
113
|
-
self.
|
|
114
|
-
self._session
|
|
122
|
+
self.current_collection: PathInfo | None
|
|
123
|
+
self._session: ClientSession | None
|
|
115
124
|
|
|
116
|
-
async def update(self) -> None:
|
|
125
|
+
async def update(self, **kwargs) -> None:
|
|
117
126
|
"""
|
|
118
127
|
Creates a new data collection in the current visit.
|
|
119
128
|
"""
|
|
@@ -122,33 +131,22 @@ class StaticVisitDirectoryProvider(UpdatingDirectoryProvider):
|
|
|
122
131
|
# TODO: DAQ-4827: Pass AuthN information as part of request
|
|
123
132
|
|
|
124
133
|
try:
|
|
125
|
-
|
|
126
|
-
|
|
134
|
+
self._filename_provider.collectionId = (
|
|
135
|
+
await self._client.create_new_collection()
|
|
136
|
+
)
|
|
127
137
|
except Exception:
|
|
128
138
|
LOGGER.error(
|
|
129
139
|
"Exception while updating data collection, preventing overwriting data by setting current_collection to None"
|
|
130
140
|
)
|
|
131
|
-
self.
|
|
141
|
+
self._collection_id_info = None
|
|
132
142
|
raise
|
|
133
143
|
|
|
134
|
-
def
|
|
135
|
-
self
|
|
136
|
-
|
|
137
|
-
) -> DirectoryInfo:
|
|
138
|
-
return DirectoryInfo(
|
|
139
|
-
# See DocString of DirectoryInfo. At DLS, root = visit directory, resource_dir is relative to it.
|
|
140
|
-
root=self._root,
|
|
141
|
-
# https://github.com/DiamondLightSource/dodal/issues/452
|
|
142
|
-
# Currently all h5 files written to visit/ directory, as no guarantee that visit/dataCollection/ directory will have been produced. If it is as part of #452, append the resource_dir
|
|
143
|
-
resource_dir=Path("."),
|
|
144
|
-
# Diamond standard file naming
|
|
145
|
-
prefix=f"{self._beamline}-{collection_id_info.collectionNumber}-",
|
|
146
|
-
)
|
|
144
|
+
async def data_session(self) -> str:
|
|
145
|
+
collection = await self._client.get_current_collection()
|
|
146
|
+
return f"{self._beamline}-{collection.collectionNumber}"
|
|
147
147
|
|
|
148
|
-
def __call__(self) ->
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"No current collection, update() needs to be called at least once"
|
|
154
|
-
)
|
|
148
|
+
def __call__(self, device_name: str | None = None) -> PathInfo:
|
|
149
|
+
assert device_name, "Must call PathProvider with device_name"
|
|
150
|
+
return PathInfo(
|
|
151
|
+
directory_path=self._root, filename=self._filename_provider(device_name)
|
|
152
|
+
)
|
dodal/devices/CTAB.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
1
|
+
from ophyd_async.core import StandardReadable
|
|
2
|
+
from ophyd_async.epics.motor import Motor
|
|
3
|
+
from ophyd_async.epics.signal import epics_signal_r
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
class CTAB(
|
|
6
|
+
class CTAB(StandardReadable):
|
|
6
7
|
"""Basic collimantion table (CTAB) device for motion plus the motion disable signal
|
|
7
8
|
when laser curtain triggered and hutch not locked.
|
|
8
9
|
|
|
@@ -17,20 +18,24 @@ class CTAB(Device):
|
|
|
17
18
|
these disabling systems is to cut power to the motors - signal for this is crate_power
|
|
18
19
|
"""
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
def __init__(self, prefix: str, name: str = ""):
|
|
22
|
+
with self.add_children_as_readables():
|
|
23
|
+
self.inboard_y = Motor(prefix + "-MO-TABLE-01:INBOARDY")
|
|
24
|
+
self.outboard_y = Motor(prefix + "-MO-TABLE-01:OUTBOARDY")
|
|
25
|
+
self.upstream_y = Motor(prefix + "-MO-TABLE-01:UPSTREAMY")
|
|
26
|
+
self.combined_downstream_y = Motor(prefix + "-MO-TABLE-01:DOWNSTREAMY")
|
|
27
|
+
self.combined_all_y = Motor(prefix + "-MO-TABLE-01:Y")
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
self.downstream_x = Motor(prefix + "-MO-TABLE-01:DOWNSTREAMX")
|
|
30
|
+
self.upstream_x = Motor(prefix + "-MO-TABLE-01:UPSTREAMX")
|
|
31
|
+
self.combined_all_x = Motor(prefix + "-MO-TABLE-01:X")
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
self.pitch = Motor(prefix + "-MO-TABLE-01:PITCH")
|
|
34
|
+
self.roll = Motor(prefix + "-MO-TABLE-01:ROLL")
|
|
35
|
+
self.yaw = Motor(prefix + "-MO-TABLE-01:YAW")
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
self.crate_power = epics_signal_r(
|
|
38
|
+
int, prefix + "-MO-PMAC-02:CRATE2_HEALTHY"
|
|
39
|
+
) # returns 0 if no power
|
|
40
|
+
|
|
41
|
+
super().__init__(name)
|
dodal/devices/aperture.py
CHANGED