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.
Files changed (103) hide show
  1. {dls_dodal-1.29.4.dist-info → dls_dodal-1.31.0.dist-info}/METADATA +29 -44
  2. dls_dodal-1.31.0.dist-info/RECORD +134 -0
  3. {dls_dodal-1.29.4.dist-info → dls_dodal-1.31.0.dist-info}/WHEEL +1 -1
  4. dls_dodal-1.31.0.dist-info/entry_points.txt +3 -0
  5. dodal/__init__.py +1 -4
  6. dodal/_version.py +2 -2
  7. dodal/beamline_specific_utils/i03.py +1 -4
  8. dodal/beamlines/__init__.py +7 -1
  9. dodal/beamlines/i03.py +34 -29
  10. dodal/beamlines/i04.py +39 -16
  11. dodal/beamlines/i13_1.py +66 -0
  12. dodal/beamlines/i22.py +22 -22
  13. dodal/beamlines/i24.py +1 -1
  14. dodal/beamlines/p38.py +21 -21
  15. dodal/beamlines/p45.py +18 -16
  16. dodal/beamlines/p99.py +61 -0
  17. dodal/beamlines/training_rig.py +64 -0
  18. dodal/cli.py +6 -3
  19. dodal/common/beamlines/beamline_parameters.py +7 -6
  20. dodal/common/beamlines/beamline_utils.py +15 -14
  21. dodal/common/maths.py +1 -3
  22. dodal/common/types.py +6 -5
  23. dodal/common/udc_directory_provider.py +39 -21
  24. dodal/common/visit.py +60 -62
  25. dodal/devices/CTAB.py +22 -17
  26. dodal/devices/aperture.py +1 -1
  27. dodal/devices/aperturescatterguard.py +139 -209
  28. dodal/devices/areadetector/adaravis.py +8 -6
  29. dodal/devices/areadetector/adsim.py +2 -3
  30. dodal/devices/areadetector/adutils.py +20 -12
  31. dodal/devices/areadetector/plugins/MJPG.py +2 -1
  32. dodal/devices/backlight.py +12 -1
  33. dodal/devices/cryostream.py +19 -7
  34. dodal/devices/dcm.py +1 -1
  35. dodal/devices/detector/__init__.py +13 -2
  36. dodal/devices/detector/det_dim_constants.py +2 -2
  37. dodal/devices/detector/det_dist_to_beam_converter.py +1 -1
  38. dodal/devices/detector/detector.py +33 -32
  39. dodal/devices/detector/detector_motion.py +38 -31
  40. dodal/devices/eiger.py +11 -15
  41. dodal/devices/eiger_odin.py +9 -10
  42. dodal/devices/fast_grid_scan.py +18 -27
  43. dodal/devices/fluorescence_detector_motion.py +13 -4
  44. dodal/devices/focusing_mirror.py +6 -6
  45. dodal/devices/hutch_shutter.py +4 -4
  46. dodal/devices/i22/dcm.py +5 -4
  47. dodal/devices/i22/fswitch.py +10 -6
  48. dodal/devices/i22/nxsas.py +55 -43
  49. dodal/devices/i24/aperture.py +1 -1
  50. dodal/devices/i24/beamstop.py +1 -1
  51. dodal/devices/i24/dcm.py +1 -1
  52. dodal/devices/i24/{I24_detector_motion.py → i24_detector_motion.py} +1 -1
  53. dodal/devices/i24/pmac.py +67 -12
  54. dodal/devices/ipin.py +7 -4
  55. dodal/devices/linkam3.py +12 -6
  56. dodal/devices/logging_ophyd_device.py +1 -1
  57. dodal/devices/motors.py +32 -6
  58. dodal/devices/oav/grid_overlay.py +1 -0
  59. dodal/devices/oav/microns_for_zoom_levels.json +1 -1
  60. dodal/devices/oav/oav_detector.py +2 -1
  61. dodal/devices/oav/oav_parameters.py +18 -10
  62. dodal/devices/oav/oav_to_redis_forwarder.py +129 -0
  63. dodal/devices/oav/pin_image_recognition/__init__.py +6 -6
  64. dodal/devices/oav/pin_image_recognition/utils.py +5 -6
  65. dodal/devices/oav/utils.py +2 -2
  66. dodal/devices/p99/__init__.py +0 -0
  67. dodal/devices/p99/sample_stage.py +43 -0
  68. dodal/devices/robot.py +31 -20
  69. dodal/devices/scatterguard.py +1 -1
  70. dodal/devices/scintillator.py +8 -5
  71. dodal/devices/slits.py +1 -1
  72. dodal/devices/smargon.py +4 -4
  73. dodal/devices/status.py +2 -31
  74. dodal/devices/tetramm.py +23 -19
  75. dodal/devices/thawer.py +5 -3
  76. dodal/devices/training_rig/__init__.py +0 -0
  77. dodal/devices/training_rig/sample_stage.py +10 -0
  78. dodal/devices/turbo_slit.py +1 -1
  79. dodal/devices/undulator.py +1 -1
  80. dodal/devices/undulator_dcm.py +6 -8
  81. dodal/devices/util/adjuster_plans.py +3 -3
  82. dodal/devices/util/epics_util.py +5 -7
  83. dodal/devices/util/lookup_tables.py +2 -3
  84. dodal/devices/util/save_panda.py +87 -0
  85. dodal/devices/util/test_utils.py +17 -0
  86. dodal/devices/webcam.py +3 -3
  87. dodal/devices/xbpm_feedback.py +1 -25
  88. dodal/devices/xspress3/xspress3.py +1 -1
  89. dodal/devices/zebra.py +15 -10
  90. dodal/devices/zebra_controlled_shutter.py +26 -11
  91. dodal/devices/zocalo/zocalo_interaction.py +10 -2
  92. dodal/devices/zocalo/zocalo_results.py +36 -19
  93. dodal/log.py +46 -15
  94. dodal/plans/check_topup.py +65 -10
  95. dodal/plans/data_session_metadata.py +8 -9
  96. dodal/plans/motor_util_plans.py +117 -0
  97. dodal/utils.py +65 -22
  98. dls_dodal-1.29.4.dist-info/RECORD +0 -125
  99. dls_dodal-1.29.4.dist-info/entry_points.txt +0 -2
  100. dodal/devices/beamstop.py +0 -8
  101. dodal/devices/qbpm1.py +0 -8
  102. {dls_dodal-1.29.4.dist-info → dls_dodal-1.31.0.dist-info}/LICENSE +0 -0
  103. {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 typing import Callable, Dict, Final, List, Optional, TypeVar, cast
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 UpdatingDirectoryProvider
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: Dict[str, AnyDevice] = {}
16
+ ACTIVE_DEVICES: dict[str, AnyDevice] = {}
16
17
  BL = ""
17
- DIRECTORY_PROVIDER: UpdatingDirectoryProvider | None = None
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() -> List[str]:
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".format(device.__class__.__name__)
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: Optional[Callable[[T], None]] = None,
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 set_directory_provider(provider: UpdatingDirectoryProvider):
128
- global DIRECTORY_PROVIDER
128
+ def set_path_provider(provider: UpdatingPathProvider):
129
+ global PATH_PROVIDER
129
130
 
130
- DIRECTORY_PROVIDER = provider
131
+ PATH_PROVIDER = provider
131
132
 
132
133
 
133
- def get_directory_provider() -> UpdatingDirectoryProvider:
134
- if DIRECTORY_PROVIDER is None:
134
+ def get_path_provider() -> UpdatingPathProvider:
135
+ if PATH_PROVIDER is None:
135
136
  raise ValueError(
136
- "DirectoryProvider has not been set! Ophyd-async StandardDetectors will not be able to write!"
137
+ "PathProvider has not been set! Ophyd-async StandardDetectors will not be able to write!"
137
138
  )
138
- return DIRECTORY_PROVIDER
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) -> Tuple[float, float, int]:
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 DirectoryProvider
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 UpdatingDirectoryProvider(DirectoryProvider, ABC):
18
+ class UpdatingPathProvider(PathProvider, ABC):
20
19
  @abstractmethod
21
- def update(self, **kwargs) -> None: ...
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 DirectoryInfo
3
+ from ophyd_async.core import FilenameProvider, PathInfo
4
4
 
5
- from dodal.common.types import UpdatingDirectoryProvider
5
+ from dodal.common.types import UpdatingPathProvider
6
6
  from dodal.log import LOGGER
7
7
 
8
8
 
9
- class PandASubdirectoryProvider(UpdatingDirectoryProvider):
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, which must exist before attempting to arm the PCAP block"""
19
+ directory path provided"""
12
20
 
13
21
  resource_dir = Path("panda")
14
22
 
15
- def __init__(self, directory: Path | None = None):
16
- if directory is None:
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 update(self, directory: Path):
27
- self._directory_info = DirectoryInfo(
28
- root=directory, resource_dir=self.resource_dir
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 Optional
3
+ from typing import Literal
4
4
 
5
5
  from aiohttp import ClientSession
6
- from ophyd_async.core import DirectoryInfo
6
+ from ophyd_async.core import FilenameProvider, PathInfo
7
7
  from pydantic import BaseModel
8
8
 
9
- from dodal.common.types import UpdatingDirectoryProvider
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 DirectoryServiceClientBase(ABC):
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 DirectoryServiceClient(DirectoryServiceClientBase):
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
- async with ClientSession() as session:
53
- async with session.post(f"{self._url}/numtracker") as response:
54
- response.raise_for_status()
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
- async with ClientSession() as session:
62
- async with session.get(f"{self._url}/numtracker") as response:
63
- response.raise_for_status()
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
- class LocalDirectoryServiceClient(DirectoryServiceClientBase):
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 StaticVisitDirectoryProvider(UpdatingDirectoryProvider):
100
+ class StaticVisitPathProvider(UpdatingPathProvider):
89
101
  """
90
- Static (single visit) implementation of DirectoryProvider whilst awaiting auth infrastructure to generate necessary information per-scan.
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, assuming all detectors are mounted with /data -> /dls/ixx/data, root=/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: Optional[DirectoryServiceClientBase] = None,
114
+ client: DirectoryServiceClient | None = None,
109
115
  ):
110
116
  self._beamline = beamline
111
- self._client = client or DirectoryServiceClient(f"{beamline}-control:8088/api")
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._current_collection = None
114
- self._session = None
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
- collection_id_info = await self._client.create_new_collection()
126
- self._current_collection = self._generate_directory_info(collection_id_info)
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._current_collection = None
141
+ self._collection_id_info = None
132
142
  raise
133
143
 
134
- def _generate_directory_info(
135
- self,
136
- collection_id_info: DataCollectionIdentifier,
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) -> DirectoryInfo:
149
- if self._current_collection is not None:
150
- return self._current_collection
151
- else:
152
- raise ValueError(
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 ophyd import Component as Cpt
2
- from ophyd import Device, EpicsMotor, EpicsSignalRO
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(Device):
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
- inboard_y = Cpt(EpicsMotor, "-MO-TABLE-01:INBOARDY")
21
- outboard_y = Cpt(EpicsMotor, "-MO-TABLE-01:OUTBOARDY")
22
- upstream_y = Cpt(EpicsMotor, "-MO-TABLE-01:UPSTREAMY")
23
- combined_downstream_y = Cpt(EpicsMotor, "-MO-TABLE-01:DOWNSTREAMY")
24
- combined_all_y = Cpt(EpicsMotor, "-MO-TABLE-01:Y")
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
- downstream_x = Cpt(EpicsMotor, "-MO-TABLE-01:DOWNSTREAMX")
27
- upstream_x = Cpt(EpicsMotor, "-MO-TABLE-01:UPSTREAMX")
28
- combined_all_x = Cpt(EpicsMotor, "-MO-TABLE-01:X")
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
- pitch = Cpt(EpicsMotor, "-MO-TABLE-01:PITCH")
31
- roll = Cpt(EpicsMotor, "-MO-TABLE-01:ROLL")
32
- yaw = Cpt(EpicsMotor, "-MO-TABLE-01:YAW")
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
- crate_power = Cpt(
35
- EpicsSignalRO, "-MO-PMAC-02:CRATE2_HEALTHY"
36
- ) # returns 0 if no power
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
@@ -1,5 +1,5 @@
1
1
  from ophyd_async.core import StandardReadable
2
- from ophyd_async.epics.motion import Motor
2
+ from ophyd_async.epics.motor import Motor
3
3
  from ophyd_async.epics.signal import epics_signal_r
4
4
 
5
5