dls-dodal 1.29.3__py3-none-any.whl → 1.30.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 (88) hide show
  1. {dls_dodal-1.29.3.dist-info → dls_dodal-1.30.0.dist-info}/METADATA +28 -43
  2. dls_dodal-1.30.0.dist-info/RECORD +132 -0
  3. {dls_dodal-1.29.3.dist-info → dls_dodal-1.30.0.dist-info}/WHEEL +1 -1
  4. dls_dodal-1.30.0.dist-info/entry_points.txt +3 -0
  5. dodal/__init__.py +1 -4
  6. dodal/_version.py +2 -2
  7. dodal/beamlines/__init__.py +3 -1
  8. dodal/beamlines/i03.py +28 -23
  9. dodal/beamlines/i04.py +34 -12
  10. dodal/beamlines/i13_1.py +66 -0
  11. dodal/beamlines/i22.py +5 -5
  12. dodal/beamlines/i24.py +15 -1
  13. dodal/beamlines/p38.py +7 -7
  14. dodal/beamlines/p45.py +7 -5
  15. dodal/beamlines/p99.py +61 -0
  16. dodal/cli.py +6 -3
  17. dodal/common/beamlines/beamline_parameters.py +2 -2
  18. dodal/common/beamlines/beamline_utils.py +6 -5
  19. dodal/common/maths.py +1 -3
  20. dodal/common/types.py +2 -3
  21. dodal/common/udc_directory_provider.py +14 -3
  22. dodal/common/visit.py +2 -3
  23. dodal/devices/CTAB.py +22 -17
  24. dodal/devices/aperturescatterguard.py +114 -136
  25. dodal/devices/areadetector/adaravis.py +8 -6
  26. dodal/devices/areadetector/adsim.py +2 -3
  27. dodal/devices/areadetector/adutils.py +20 -12
  28. dodal/devices/areadetector/plugins/MJPG.py +0 -4
  29. dodal/devices/attenuator.py +4 -4
  30. dodal/devices/cryostream.py +19 -7
  31. dodal/devices/detector/__init__.py +13 -2
  32. dodal/devices/detector/det_dim_constants.py +2 -2
  33. dodal/devices/detector/det_dist_to_beam_converter.py +1 -1
  34. dodal/devices/detector/detector.py +8 -7
  35. dodal/devices/detector/detector_motion.py +38 -31
  36. dodal/devices/eiger.py +23 -23
  37. dodal/devices/eiger_odin.py +12 -13
  38. dodal/devices/fast_grid_scan.py +4 -3
  39. dodal/devices/fluorescence_detector_motion.py +13 -4
  40. dodal/devices/focusing_mirror.py +66 -66
  41. dodal/devices/hutch_shutter.py +4 -4
  42. dodal/devices/i22/dcm.py +4 -3
  43. dodal/devices/i22/fswitch.py +4 -4
  44. dodal/devices/i22/nxsas.py +23 -32
  45. dodal/devices/i24/dcm.py +42 -0
  46. dodal/devices/i24/pmac.py +47 -8
  47. dodal/devices/ipin.py +7 -4
  48. dodal/devices/linkam3.py +11 -5
  49. dodal/devices/logging_ophyd_device.py +1 -1
  50. dodal/devices/motors.py +31 -5
  51. dodal/devices/oav/grid_overlay.py +1 -0
  52. dodal/devices/oav/microns_for_zoom_levels.json +1 -1
  53. dodal/devices/oav/oav_detector.py +2 -0
  54. dodal/devices/oav/oav_parameters.py +18 -10
  55. dodal/devices/oav/oav_to_redis_forwarder.py +100 -0
  56. dodal/devices/oav/pin_image_recognition/__init__.py +19 -17
  57. dodal/devices/oav/pin_image_recognition/utils.py +5 -6
  58. dodal/devices/oav/utils.py +3 -17
  59. dodal/devices/p99/__init__.py +0 -0
  60. dodal/devices/p99/sample_stage.py +43 -0
  61. dodal/devices/robot.py +30 -18
  62. dodal/devices/scintillator.py +8 -5
  63. dodal/devices/smargon.py +3 -3
  64. dodal/devices/status.py +2 -31
  65. dodal/devices/tetramm.py +4 -4
  66. dodal/devices/thawer.py +5 -3
  67. dodal/devices/undulator_dcm.py +6 -8
  68. dodal/devices/util/adjuster_plans.py +2 -2
  69. dodal/devices/util/epics_util.py +6 -8
  70. dodal/devices/util/lookup_tables.py +2 -3
  71. dodal/devices/util/save_panda.py +87 -0
  72. dodal/devices/util/test_utils.py +17 -0
  73. dodal/devices/webcam.py +3 -8
  74. dodal/devices/xbpm_feedback.py +0 -23
  75. dodal/devices/zebra.py +10 -10
  76. dodal/devices/zebra_controlled_shutter.py +3 -3
  77. dodal/devices/zocalo/zocalo_interaction.py +10 -2
  78. dodal/devices/zocalo/zocalo_results.py +31 -18
  79. dodal/log.py +14 -5
  80. dodal/plans/data_session_metadata.py +1 -0
  81. dodal/plans/motor_util_plans.py +117 -0
  82. dodal/utils.py +74 -26
  83. dls_dodal-1.29.3.dist-info/RECORD +0 -124
  84. dls_dodal-1.29.3.dist-info/entry_points.txt +0 -2
  85. dodal/devices/qbpm1.py +0 -8
  86. {dls_dodal-1.29.3.dist-info → dls_dodal-1.30.0.dist-info}/LICENSE +0 -0
  87. {dls_dodal-1.29.3.dist-info → dls_dodal-1.30.0.dist-info}/top_level.txt +0 -0
  88. /dodal/devices/i24/{I24_detector_motion.py → i24_detector_motion.py} +0 -0
@@ -1,5 +1,6 @@
1
1
  import dataclasses
2
2
  import getpass
3
+ import os
3
4
  import socket
4
5
  from dataclasses import dataclass
5
6
 
@@ -37,6 +38,12 @@ class ZocaloStartInfo:
37
38
  message_index: int
38
39
 
39
40
 
41
+ def _get_zocalo_headers() -> tuple[str, str]:
42
+ user = os.environ.get("ZOCALO_GO_USER", getpass.getuser())
43
+ hostname = os.environ.get("ZOCALO_GO_HOSTNAME", socket.gethostname())
44
+ return user, hostname
45
+
46
+
40
47
  class ZocaloTrigger:
41
48
  """This class just sends 'run_start' and 'run_end' messages to zocalo, it is
42
49
  intended to be used in bluesky callback classes. To get results from zocalo back
@@ -55,9 +62,10 @@ class ZocaloTrigger:
55
62
  "recipes": ["mimas"],
56
63
  "parameters": parameters,
57
64
  }
65
+ user, hostname = _get_zocalo_headers()
58
66
  header = {
59
- "zocalo.go.user": getpass.getuser(),
60
- "zocalo.go.host": socket.gethostname(),
67
+ "zocalo.go.user": user,
68
+ "zocalo.go.host": hostname,
61
69
  }
62
70
  transport.send("processing_recipe", message, headers=header)
63
71
  finally:
@@ -1,8 +1,9 @@
1
1
  import asyncio
2
2
  from collections import OrderedDict
3
+ from collections.abc import Generator, Sequence
3
4
  from enum import Enum
4
5
  from queue import Empty, Queue
5
- from typing import Any, Generator, Sequence, Tuple, TypedDict
6
+ from typing import Any, TypedDict
6
7
 
7
8
  import bluesky.plan_stubs as bps
8
9
  import numpy as np
@@ -10,7 +11,7 @@ import workflows.recipe
10
11
  import workflows.transport
11
12
  from bluesky.protocols import Descriptor, Triggerable
12
13
  from numpy.typing import NDArray
13
- from ophyd_async.core import StandardReadable, soft_signal_r_and_setter
14
+ from ophyd_async.core import HintedSignal, StandardReadable, soft_signal_r_and_setter
14
15
  from ophyd_async.core.async_status import AsyncStatus
15
16
  from workflows.transport.common_transport import CommonTransport
16
17
 
@@ -79,34 +80,41 @@ class ZocaloResults(StandardReadable, Triggerable):
79
80
  self._raw_results_received: Queue = Queue()
80
81
  self.transport: CommonTransport | None = None
81
82
 
82
- self.results, _ = soft_signal_r_and_setter(list[XrcResult], name="results")
83
- self.centres_of_mass, _ = soft_signal_r_and_setter(
83
+ self.results, self._results_setter = soft_signal_r_and_setter(
84
+ list[XrcResult], name="results"
85
+ )
86
+ self.centres_of_mass, self._com_setter = soft_signal_r_and_setter(
84
87
  NDArray[np.uint64], name="centres_of_mass"
85
88
  )
86
- self.bbox_sizes, _ = soft_signal_r_and_setter(
89
+ self.bbox_sizes, self._bbox_setter = soft_signal_r_and_setter(
87
90
  NDArray[np.uint64], "bbox_sizes", self.name
88
91
  )
89
- self.ispyb_dcid, _ = soft_signal_r_and_setter(int, name="ispyb_dcid")
90
- self.ispyb_dcgid, _ = soft_signal_r_and_setter(int, name="ispyb_dcgid")
91
- self.set_readable_signals(
92
- read=[
92
+ self.ispyb_dcid, self._ispyb_dcid_setter = soft_signal_r_and_setter(
93
+ int, name="ispyb_dcid"
94
+ )
95
+ self.ispyb_dcgid, self._ispyb_dcgid_setter = soft_signal_r_and_setter(
96
+ int, name="ispyb_dcgid"
97
+ )
98
+ self.add_readables(
99
+ [
93
100
  self.results,
94
101
  self.centres_of_mass,
95
102
  self.bbox_sizes,
96
103
  self.ispyb_dcid,
97
104
  self.ispyb_dcgid,
98
- ]
105
+ ],
106
+ wrapper=HintedSignal,
99
107
  )
100
108
  super().__init__(name)
101
109
 
102
110
  async def _put_results(self, results: Sequence[XrcResult], ispyb_ids):
103
- await self.results._backend.put(list(results))
111
+ self._results_setter(list(results))
104
112
  centres_of_mass = np.array([r["centre_of_mass"] for r in results])
105
113
  bbox_sizes = np.array([bbox_size(r) for r in results])
106
- await self.centres_of_mass._backend.put(centres_of_mass)
107
- await self.bbox_sizes._backend.put(bbox_sizes)
108
- await self.ispyb_dcid._backend.put(ispyb_ids["dcid"])
109
- await self.ispyb_dcgid._backend.put(ispyb_ids["dcgid"])
114
+ self._com_setter(centres_of_mass)
115
+ self._bbox_setter(bbox_sizes)
116
+ self._ispyb_dcid_setter(ispyb_ids["dcid"])
117
+ self._ispyb_dcgid_setter(ispyb_ids["dcgid"])
110
118
 
111
119
  def _clear_old_results(self):
112
120
  LOGGER.info("Clearing queue")
@@ -120,7 +128,12 @@ class ZocaloResults(StandardReadable, Triggerable):
120
128
  before triggering processing for the experiment"""
121
129
 
122
130
  LOGGER.info("Subscribing to results queue")
123
- self._subscribe_to_results()
131
+ try:
132
+ self._subscribe_to_results()
133
+ except Exception as e:
134
+ print(f"GOT {e}")
135
+ raise
136
+
124
137
  await asyncio.sleep(CLEAR_QUEUE_WAIT_S)
125
138
  self._clear_old_results()
126
139
 
@@ -152,7 +165,7 @@ class ZocaloResults(StandardReadable, Triggerable):
152
165
  )
153
166
 
154
167
  raw_results = self._raw_results_received.get(timeout=self.timeout_s)
155
- LOGGER.info(f"Zocalo: found {len(raw_results)} crystals.")
168
+ LOGGER.info(f"Zocalo: found {len(raw_results['results'])} crystals.")
156
169
  # Sort from strongest to weakest in case of multiple crystals
157
170
  await self._put_results(
158
171
  sorted(
@@ -242,7 +255,7 @@ class ZocaloResults(StandardReadable, Triggerable):
242
255
 
243
256
  def get_processing_result(
244
257
  zocalo: ZocaloResults,
245
- ) -> Generator[Any, Any, Tuple[np.ndarray, np.ndarray] | Tuple[None, None]]:
258
+ ) -> Generator[Any, Any, tuple[np.ndarray, np.ndarray] | tuple[None, None]]:
246
259
  """A minimal plan which will extract the top ranked xray centre and crystal bounding
247
260
  box size from the zocalo results. Returns (None, None) if no crystals were found."""
248
261
 
dodal/log.py CHANGED
@@ -6,7 +6,7 @@ from logging import Logger, StreamHandler
6
6
  from logging.handlers import TimedRotatingFileHandler
7
7
  from os import environ
8
8
  from pathlib import Path
9
- from typing import Deque, Tuple, TypedDict
9
+ from typing import TypedDict
10
10
 
11
11
  from bluesky.log import logger as bluesky_logger
12
12
  from graypy import GELFTCPHandler
@@ -37,11 +37,14 @@ class CircularMemoryHandler(logging.Handler):
37
37
  that always contains the last {capacity} number of messages, this is only flushed
38
38
  when a log of specific {flushLevel} comes in. On flush this buffer is then passed to
39
39
  the {target} handler.
40
+
41
+ The CircularMemoryHandler becomes the owner of the target handler which will be closed
42
+ on close of this handler.
40
43
  """
41
44
 
42
45
  def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
43
46
  logging.Handler.__init__(self)
44
- self.buffer: Deque[logging.LogRecord] = deque(maxlen=capacity)
47
+ self.buffer: deque[logging.LogRecord] = deque(maxlen=capacity)
45
48
  self.flushLevel = flushLevel
46
49
  self.target = target
47
50
 
@@ -66,6 +69,12 @@ class CircularMemoryHandler(logging.Handler):
66
69
  self.acquire()
67
70
  try:
68
71
  self.buffer.clear()
72
+ if self.target:
73
+ self.target.acquire()
74
+ try:
75
+ self.target.close()
76
+ finally:
77
+ self.target.release()
69
78
  self.target = None
70
79
  logging.Handler.close(self)
71
80
  finally:
@@ -121,7 +130,7 @@ def set_up_graylog_handler(logger: Logger, host: str, port: int):
121
130
  def set_up_INFO_file_handler(logger, path: Path, filename: str):
122
131
  """Set up a file handler for the logger, at INFO level, which will keep 30 days
123
132
  of logs, rotating once per day. Creates the directory if necessary."""
124
- print(f"Logging to {path/filename}")
133
+ print(f"Logging to INFO file handler {path/filename}")
125
134
  path.mkdir(parents=True, exist_ok=True)
126
135
  file_handler = TimedRotatingFileHandler(
127
136
  filename=path / filename, when="MIDNIGHT", backupCount=INFO_LOG_DAYS
@@ -137,8 +146,8 @@ def set_up_DEBUG_memory_handler(
137
146
  """Set up a Memory handler which holds 200k lines, and writes them to an hourly
138
147
  log file when it sees a message of severity ERROR. Creates the directory if
139
148
  necessary"""
140
- print(f"Logging to {path/filename}")
141
149
  debug_path = path / "debug"
150
+ print(f"Logging to DEBUG handler {debug_path/filename}")
142
151
  debug_path.mkdir(parents=True, exist_ok=True)
143
152
  file_handler = TimedRotatingFileHandler(
144
153
  filename=debug_path / filename, when="H", backupCount=DEBUG_LOG_FILES_TO_KEEP
@@ -240,7 +249,7 @@ def get_logging_file_path() -> Path:
240
249
 
241
250
  def get_graylog_configuration(
242
251
  dev_mode: bool, graylog_port: int | None = None
243
- ) -> Tuple[str, int]:
252
+ ) -> tuple[str, int]:
244
253
  """Get the host and port for the graylog handler.
245
254
 
246
255
  If running in dev mode, this switches to localhost. Otherwise it publishes to the
@@ -37,6 +37,7 @@ def attach_data_session_metadata_wrapper(
37
37
  directory_info: DirectoryInfo = provider()
38
38
  # https://github.com/DiamondLightSource/dodal/issues/452
39
39
  # As part of 452, write each dataCollection into their own folder, then can use resource_dir directly
40
+ assert directory_info.prefix is not None
40
41
  data_session = directory_info.prefix.removesuffix("-")
41
42
  yield from bpp.inject_md_wrapper(plan, md={DATA_SESSION: data_session})
42
43
 
@@ -0,0 +1,117 @@
1
+ import uuid
2
+ from collections.abc import Generator
3
+ from typing import Any, TypeVar
4
+
5
+ from bluesky import plan_stubs as bps
6
+ from bluesky.preprocessors import finalize_wrapper, pchain
7
+ from bluesky.utils import Msg, make_decorator
8
+ from ophyd_async.core import Device
9
+ from ophyd_async.epics.motion import Motor
10
+
11
+ from dodal.common import MsgGenerator
12
+
13
+ AnyDevice = TypeVar("AnyDevice", bound=Device)
14
+
15
+
16
+ class MoveTooLarge(Exception):
17
+ def __init__(
18
+ self, axis: Device, maximum_move: float, position: float, *args: object
19
+ ) -> None:
20
+ self.axis = axis
21
+ self.maximum_move = maximum_move
22
+ self.position = position
23
+ super().__init__(*args)
24
+
25
+
26
+ def _check_and_cache_values(
27
+ devices_and_positions: dict[AnyDevice, float],
28
+ smallest_move: float,
29
+ maximum_move: float,
30
+ ) -> Generator[Msg, Any, dict[AnyDevice, float]]:
31
+ """Caches the positions of all Motors on specified device if they are within
32
+ smallest_move of home_position. Throws MoveTooLarge if they are outside maximum_move
33
+ of the home_position
34
+ """
35
+ positions = {}
36
+ for axis, new_position in devices_and_positions.items():
37
+ position = yield from bps.rd(axis)
38
+ if abs(position - new_position) > maximum_move:
39
+ raise MoveTooLarge(axis, maximum_move, position)
40
+ if abs(position - new_position) > smallest_move:
41
+ positions[axis] = position
42
+ return positions
43
+
44
+
45
+ def home_and_reset_wrapper(
46
+ plan: MsgGenerator,
47
+ device: Device,
48
+ smallest_move: float,
49
+ maximum_move: float,
50
+ group: str | None = None,
51
+ wait_for_all: bool = True,
52
+ ) -> MsgGenerator:
53
+ home_positions = {
54
+ axis: 0.0 for _, axis in device.children() if isinstance(axis, Motor)
55
+ }
56
+ return move_and_reset_wrapper(
57
+ plan, home_positions, smallest_move, maximum_move, group, wait_for_all
58
+ )
59
+
60
+
61
+ def move_and_reset_wrapper(
62
+ plan: MsgGenerator,
63
+ device_and_positions: dict[AnyDevice, float],
64
+ smallest_move: float,
65
+ maximum_move: float,
66
+ group: str | None = None,
67
+ wait_for_all: bool = True,
68
+ ) -> MsgGenerator:
69
+ """Wrapper that does the following:
70
+ 1. Caches the positions of all Motors on device
71
+ 2. Throws a MoveTooLarge exception if any positions are maximum_move away from home_position
72
+ 2. Moves any motor that is more than smallest_move away from the home_position to home_position
73
+ 3. Runs the specified plan
74
+ 4. Moves all motors back to their cached positions
75
+
76
+ Args:
77
+ plan (Callable[[], MsgGenerator]): The plan to move between homing and returning to the cache
78
+ device (Device): The device to move. All Motors in the device will be cached and moved
79
+ smallest_move (float): The smallest move that we care about doing the home and cache for.
80
+ Useful for not wearing out motors if you have large tolerances
81
+ maximum_move (float): If any Motor starts this far from the home an exception is raised
82
+ and no moves occur
83
+ home_position (float): The position to move every motor to after caching
84
+ group (str, optional): If set the home move will be done using the home-{group}
85
+ group and the reset to cache done using reset-{group}
86
+ wait_for_all (bool, optional): If true the home and reset to cache will be waited
87
+ on. If false it is left up to the caller to wait on
88
+ them. Defaults to True.
89
+ """
90
+ initial_positions = yield from _check_and_cache_values(
91
+ device_and_positions, smallest_move, maximum_move
92
+ )
93
+
94
+ def move_to_home():
95
+ home_group = f"home-{group if group else str(uuid.uuid4())[:6]}"
96
+ for axis, position in device_and_positions.items():
97
+ if axis in initial_positions.keys():
98
+ yield from bps.abs_set(axis, position, group=home_group)
99
+ if wait_for_all:
100
+ yield from bps.wait(home_group)
101
+
102
+ def return_to_initial_position():
103
+ reset_group = f"reset-{group if group else str(uuid.uuid4())[:6]}"
104
+ for axis, position in initial_positions.items():
105
+ yield from bps.abs_set(axis, position, group=reset_group)
106
+ if wait_for_all:
107
+ yield from bps.wait(reset_group)
108
+
109
+ return (
110
+ yield from finalize_wrapper(
111
+ pchain(move_to_home(), plan),
112
+ return_to_initial_position(),
113
+ )
114
+ )
115
+
116
+
117
+ home_and_reset_decorator = make_decorator(home_and_reset_wrapper)
dodal/utils.py CHANGED
@@ -4,6 +4,7 @@ import os
4
4
  import re
5
5
  import socket
6
6
  import string
7
+ from collections.abc import Callable, Iterable, Mapping
7
8
  from dataclasses import dataclass
8
9
  from functools import wraps
9
10
  from importlib import import_module
@@ -12,13 +13,6 @@ from os import environ
12
13
  from types import ModuleType
13
14
  from typing import (
14
15
  Any,
15
- Callable,
16
- Dict,
17
- Iterable,
18
- List,
19
- Mapping,
20
- Tuple,
21
- Type,
22
16
  TypeVar,
23
17
  )
24
18
 
@@ -46,7 +40,7 @@ import dodal.log
46
40
  try:
47
41
  from typing import TypeAlias
48
42
  except ImportError:
49
- from typing_extensions import TypeAlias
43
+ from typing import TypeAlias
50
44
 
51
45
 
52
46
  #: Protocols defining interface to hardware
@@ -108,9 +102,35 @@ def skip_device(precondition=lambda: True):
108
102
  return decorator
109
103
 
110
104
 
105
+ def make_device(
106
+ module: str | ModuleType,
107
+ device_name: str,
108
+ **kwargs,
109
+ ) -> dict[str, AnyDevice]:
110
+ """Make a single named device and its dependencies from the given beamline module.
111
+
112
+ Args:
113
+ module (str | ModuleType): The module to make devices from.
114
+ device_name: Name of the device to construct
115
+ **kwargs: Arguments passed on to every device factory
116
+
117
+ Returns:
118
+ dict[str, AnyDevice]: A dict mapping device names to the constructed devices
119
+ """
120
+ if isinstance(module, str):
121
+ module = import_module(module)
122
+
123
+ device_collector = {}
124
+ factories = collect_factories(module)
125
+ device_collector[device_name] = _make_one_device(
126
+ module, device_name, device_collector, factories, **kwargs
127
+ )
128
+ return device_collector
129
+
130
+
111
131
  def make_all_devices(
112
132
  module: str | ModuleType | None = None, include_skipped: bool = False, **kwargs
113
- ) -> Tuple[Dict[str, AnyDevice], Dict[str, Exception]]:
133
+ ) -> tuple[dict[str, AnyDevice], dict[str, Exception]]:
114
134
  """Makes all devices in the given beamline module.
115
135
 
116
136
  In cases of device interdependencies it ensures a device is created before any which
@@ -129,7 +149,7 @@ def make_all_devices(
129
149
  if isinstance(module, str) or module is None:
130
150
  module = import_module(module or __name__)
131
151
  factories = collect_factories(module, include_skipped)
132
- devices: Tuple[Dict[str, AnyDevice], Dict[str, Exception]] = invoke_factories(
152
+ devices: tuple[dict[str, AnyDevice], dict[str, Exception]] = invoke_factories(
133
153
  factories, **kwargs
134
154
  )
135
155
 
@@ -139,7 +159,7 @@ def make_all_devices(
139
159
  def invoke_factories(
140
160
  factories: Mapping[str, AnyDeviceFactory],
141
161
  **kwargs,
142
- ) -> Tuple[Dict[str, AnyDevice], Dict[str, Exception]]:
162
+ ) -> tuple[dict[str, AnyDevice], dict[str, Exception]]:
143
163
  """Call device factory functions in the correct order to resolve dependencies.
144
164
  Inspect function signatures to work out dependencies and execute functions in
145
165
  correct order.
@@ -259,15 +279,13 @@ def is_any_device_factory(func: Callable) -> bool:
259
279
  return is_v1_device_factory(func) or is_v2_device_factory(func)
260
280
 
261
281
 
262
- def is_v2_device_type(obj: Type[Any]) -> bool:
263
- return inspect.isclass(obj) and issubclass(obj, OphydV2Device)
282
+ def is_v2_device_type(obj: type[Any]) -> bool:
283
+ return inspect.isclass(obj) and isinstance(obj, OphydV2Device)
264
284
 
265
285
 
266
- def is_v1_device_type(obj: Type[Any]) -> bool:
286
+ def is_v1_device_type(obj: type[Any]) -> bool:
267
287
  is_class = inspect.isclass(obj)
268
- follows_protocols = any(
269
- (isinstance(obj, protocol) for protocol in BLUESKY_PROTOCOLS)
270
- )
288
+ follows_protocols = any(isinstance(obj, protocol) for protocol in BLUESKY_PROTOCOLS)
271
289
  return is_class and follows_protocols and not is_v2_device_type(obj)
272
290
 
273
291
 
@@ -292,13 +310,11 @@ def get_beamline_based_on_environment_variable() -> ModuleType:
292
310
  or any(c not in valid_characters for c in beamline)
293
311
  ):
294
312
  raise ValueError(
295
- "Invalid BEAMLINE variable - module name is not a permissible python module name, got '{}'".format(
296
- beamline
297
- )
313
+ f"Invalid BEAMLINE variable - module name is not a permissible python module name, got '{beamline}'"
298
314
  )
299
315
 
300
316
  try:
301
- return importlib.import_module("dodal.beamlines.{}".format(beamline))
317
+ return importlib.import_module(f"dodal.beamlines.{beamline}")
302
318
  except ImportError as e:
303
319
  raise ValueError(
304
320
  f"Failed to import beamline-specific dodal module 'dodal.beamlines.{beamline}'."
@@ -306,7 +322,7 @@ def get_beamline_based_on_environment_variable() -> ModuleType:
306
322
  ) from e
307
323
 
308
324
 
309
- def _find_next_run_number_from_files(file_names: List[str]) -> int:
325
+ def _find_next_run_number_from_files(file_names: list[str]) -> int:
310
326
  valid_numbers = []
311
327
 
312
328
  for file_name in file_names:
@@ -322,12 +338,44 @@ def _find_next_run_number_from_files(file_names: List[str]) -> int:
322
338
  return max(valid_numbers) + 1 if valid_numbers else 1
323
339
 
324
340
 
325
- def get_run_number(directory: str) -> int:
326
- """Looks at the numbers coming from all nexus files with the format "xxx_(any number}.nxs", and returns the highest number + 1,
327
- or 1 if there are no numbers found"""
328
- nexus_file_names = [file for file in os.listdir(directory) if file.endswith(".nxs")]
341
+ def get_run_number(directory: str, prefix: str = "") -> int:
342
+ """Looks at the numbers coming from all nexus files with the format
343
+ "{prefix}_(any number}.nxs", and returns the highest number + 1, or 1 if there are
344
+ no matching numbers found. If no prefix is given, considers all files in the dir."""
345
+ nexus_file_names = [
346
+ file
347
+ for file in os.listdir(directory)
348
+ if file.endswith(".nxs") and file.startswith(prefix)
349
+ ]
329
350
 
330
351
  if len(nexus_file_names) == 0:
331
352
  return 1
332
353
  else:
333
354
  return _find_next_run_number_from_files(nexus_file_names)
355
+
356
+
357
+ def _make_one_device(
358
+ module: ModuleType,
359
+ device_name: str,
360
+ devices: dict[str, AnyDevice],
361
+ factories: dict[str, AnyDeviceFactory],
362
+ **kwargs,
363
+ ) -> AnyDevice:
364
+ factory = factories.get(device_name)
365
+ if not factory:
366
+ raise ValueError(f"Unable to find factory for {device_name}")
367
+
368
+ dependencies = list(extract_dependencies(factories, device_name))
369
+ for dependency_name in dependencies:
370
+ if dependency_name not in devices:
371
+ try:
372
+ devices[dependency_name] = _make_one_device(
373
+ module, dependency_name, devices, factories, **kwargs
374
+ )
375
+ except Exception as e:
376
+ raise RuntimeError(
377
+ f"Unable to construct device {dependency_name}"
378
+ ) from e
379
+
380
+ params = {name: devices[name] for name in dependencies}
381
+ return factory(**params, **kwargs)
@@ -1,124 +0,0 @@
1
- dodal/__init__.py,sha256=y-VRpfiX-Lm5nchB9N0VfMy_6dwFqVxpSn5SiAQql9I,114
2
- dodal/__main__.py,sha256=kP2S2RPitnOWpNGokjZ1Yq-1umOtp5sNOZk2B3tBPLM,111
3
- dodal/_version.py,sha256=MFlkKJKBPRaG5o-APjK5uWe00dvrrzhYEjQcU5M7B2I,413
4
- dodal/adsim.py,sha256=OW2dcS7ciD4Yq9WFw4PN_c5Bwccrmu7R-zr-u6ZCbQM,497
5
- dodal/cli.py,sha256=z0UBESrNrq6Kq4rttp4uHcwS1fnOnRkKBRDHSriPpGY,2058
6
- dodal/log.py,sha256=dfo1rfYrGG8oIm2HkNxaa_ldVs4vJKtgWSoKe1Z_Xno,8533
7
- dodal/utils.py,sha256=aH-W94t6NFOoGHZ7awbUKY8_k7qIYDourCFs3MKIjjA,10024
8
- dodal/beamline_specific_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- dodal/beamline_specific_utils/i03.py,sha256=Ixe1anFQl-kwRJubmQx28TIW4Zw8qDxpElNNNapWQHI,396
10
- dodal/beamlines/README.md,sha256=K9MkL_GomxlsoTB7Mz-_dJA5NNSbmCfMiutchGg3C8o,404
11
- dodal/beamlines/__init__.py,sha256=U0dQYFEUloCdQOs24zyfpPTncJXOO4cDcfHSevVOAw4,2890
12
- dodal/beamlines/i03.py,sha256=J-4GawTxAhrYDQZh87n11L9ehTgObEQ1Lz2PueXCWbc,16892
13
- dodal/beamlines/i04.py,sha256=JOyNcUnC3wva4no2MHKp6b8gOKAcQXL_c4cBo7oneVs,13034
14
- dodal/beamlines/i04_1.py,sha256=KDxSUQNhIs_NFiRaLY-Jiory0DeN7Y0ErvGuoTrwCDU,4731
15
- dodal/beamlines/i20_1.py,sha256=MaPgONHqpoZuBtkiKEzYtViJnKBM2_ekeP4OdbmuXHE,1158
16
- dodal/beamlines/i22.py,sha256=3VFdA4Wc7O40-64lwUtUBIN23fH4JVNbLKJ1JLjy9as,9870
17
- dodal/beamlines/i23.py,sha256=2j5qLoqE_hg9ETHqNkOVu7LLkVB8qalgXeORnVYKN_I,1075
18
- dodal/beamlines/i24.py,sha256=dCMQGcBZ6ADZ6_rEDFcV2BPHGKBC9iVFvfxewxVts4k,6111
19
- dodal/beamlines/p38.py,sha256=TC78u4GwEnj6X0r5cnHhqNdBbbDRnh8lY8pEucBZjEU,8006
20
- dodal/beamlines/p45.py,sha256=TNIkC-SBfj0ayZtlLLXW9xCSi5CzJkO8XpAMIo8fjao,2957
21
- dodal/common/__init__.py,sha256=ZC4ICKUDB0BDxRaVy8nmqclVmDBne-dPtk6UJsoFq6I,258
22
- dodal/common/coordination.py,sha256=OxIjDiO1-9A9KESRPFtzwkvvQlavbgA5RHemlbubBPg,1168
23
- dodal/common/maths.py,sha256=JRSBhbMzwlicKp1_Bsfu9gA79JJA_Dgq9EpbExFH65M,1829
24
- dodal/common/types.py,sha256=M0gZs9F7--gREF8VYJn-Y1Mt9mIEgp1aLY3oUpUkSno,546
25
- dodal/common/udc_directory_provider.py,sha256=zNlt_VgdAlyBtVN7neTHk_0tWBbI4pPUL7q9WQOzXvo,1260
26
- dodal/common/visit.py,sha256=MhrFbLptMG0Wvd2nHubBwQ44qAzoP4Bf4_z_-wO8rh0,6063
27
- dodal/common/beamlines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- dodal/common/beamlines/beamline_parameters.py,sha256=N22dtDLw3Hlo7EUmGxe4qFGcu7OnldwCz_mU7yK2rd0,3577
29
- dodal/common/beamlines/beamline_utils.py,sha256=AgmH9wpnFQ4DHAA7_Yo0COa2piX2ksFxukFt2_600kA,4488
30
- dodal/common/beamlines/device_helpers.py,sha256=s79js7no9k8JMfG7NvdmFomSP5m5VDQ6th_Hsbx1znA,939
31
- dodal/devices/CTAB.py,sha256=_MfL_KH4uDPxq_RuHFEZ9HVXOpUnQb5be3csoz9DdAs,1630
32
- dodal/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- dodal/devices/adsim.py,sha256=dMU0TKIuiODHYFHQOH4_5UvB8iJtaJEtjqaEDGjcU-w,311
34
- dodal/devices/aperture.py,sha256=0MtTzKMDZ5DVAz0DE0kXI0M76VCp0y9vFsrMggEMpxk,586
35
- dodal/devices/aperturescatterguard.py,sha256=2JJsEPJGJHrI0ztv1cSaP7H5T6qdzDfUcN-VEQ39B8o,11012
36
- dodal/devices/attenuator.py,sha256=OD7fElTIMHWk7ZopPqEu29lionm7WwgC0-Kvl8vBIb0,2599
37
- dodal/devices/backlight.py,sha256=vsNGZB4C_mVMafllvJlOTghsfv6UqALMKUMLXu3WZ5k,1115
38
- dodal/devices/beamstop.py,sha256=8L3qhlk-3ZBOp10xK1i8qZqYTGOXX1mVF1MgXoN0dfg,215
39
- dodal/devices/cryostream.py,sha256=6MU4rXIOL33C-8F3DVfAtv0ZnwiysTtawjkeePd5IrQ,332
40
- dodal/devices/dcm.py,sha256=vfyGYDzfSwTiNqlzkfNjkrL-Q1hNVSgJddvJ5Un_lvg,1610
41
- dodal/devices/eiger.py,sha256=NE4tHdqgUZpUxJLQbd5lLUIHZcpeotppexJGlDNByzM,13868
42
- dodal/devices/eiger_odin.py,sha256=U5Byb7uNwDdNscBRp7yBYQrsjKrKXl2l5WdSpL09lAw,6980
43
- dodal/devices/fast_grid_scan.py,sha256=BEj96j78r60JPPJoOMP-XXG-_9yURFTuu-pp2LcqQmY,12452
44
- dodal/devices/fluorescence_detector_motion.py,sha256=RrXfPmJzWnAjcjp9u0AnJEfjvWPMKburVTySB2hxYbw,181
45
- dodal/devices/flux.py,sha256=RtPStHw7Mad0igVKntKWVZfuZn2clokVJqH14HLix6M,198
46
- dodal/devices/focusing_mirror.py,sha256=aRqBkE3OgaXpH6lP3v1VbSYgHsMMbSsPPXzeyAGf_Pg,6435
47
- dodal/devices/hutch_shutter.py,sha256=nZ3gRbYIVJsXLlpZMWT4UEYUFFQP1MwMe8Oy304QsqE,3360
48
- dodal/devices/ipin.py,sha256=OGMXwAE4KDDonZRPFkUmR9Vsk6X4Ox-hEvPT5drP-mQ,208
49
- dodal/devices/linkam3.py,sha256=TPhiQ1D9i_HIlKHAlfnVfX7H6aPOAeXPEJLdmvwdKWQ,3776
50
- dodal/devices/logging_ophyd_device.py,sha256=xw4lbyqq5_ehESGterVEfubJsBiJTWvBp5b9k62gSkg,666
51
- dodal/devices/motors.py,sha256=16ID2jFJ35h6ZrFp76nJG_oQg6uDrupgcbvcbmjlc7c,300
52
- dodal/devices/p45.py,sha256=jzBW2fGRhIbGzSRs5Fgupxro6aqE611n1RTcrTTG-yY,1047
53
- dodal/devices/qbpm1.py,sha256=OY7-WbdxMiLGUK8Z57ezwqSXbHxoPP-y3GvBgj9kgMA,220
54
- dodal/devices/robot.py,sha256=5WQ9kF5m8xhHhipBycsycDV0-_2IBNBkcwuSWP-9-1I,4337
55
- dodal/devices/s4_slit_gaps.py,sha256=j3kgF9WfGFaU9xdUuiAh-QqI5u_vhiAftaDVINt91SM,243
56
- dodal/devices/scatterguard.py,sha256=0qnvhoo3RjLsrxVgIoDJpryqunlgMVgaTsoyKRC2g4Y,331
57
- dodal/devices/scintillator.py,sha256=4Dej1a6HRom9GRwTDsaTKGfvloP20POUqIeHqsI8-R8,184
58
- dodal/devices/slits.py,sha256=URru9VN2N19KqeUPDZaBmyKYn0_JJiE0Vko4sZpfsl8,601
59
- dodal/devices/smargon.py,sha256=Ds8QFqK3ljbTxalqkQ6clpArj4u4hu9d4vrt97Fzdf4,4693
60
- dodal/devices/status.py,sha256=TuUGidZ4Ar-WCRc_sX0wn58DmL6brj1pMr8rNF5Z6VU,1198
61
- dodal/devices/synchrotron.py,sha256=QtTufJA_fCaBawHougSc7nxwu240oX46_y0P-4qIW8o,1960
62
- dodal/devices/tetramm.py,sha256=XriN-zBFVnHxhnTbphSPIZcxEbdWBTbw2g_ulUBl4bw,8538
63
- dodal/devices/thawer.py,sha256=hIdZOzCNloY7CtSvdE2gk4vCMMoOtaIA4dPH_k0OwFg,1527
64
- dodal/devices/turbo_slit.py,sha256=W3ZRIqDhq4iMhr5GcIiWvl2U1GaPtGanqkL7upQOZTY,1132
65
- dodal/devices/undulator.py,sha256=kn84MQpuBHtQj7H7HeBoAYKXu5buGKvTgs3tf2gdEdw,2074
66
- dodal/devices/undulator_dcm.py,sha256=TC9fO55r1YIG_88PPbGGtzfjcRJcaoC2ny51JiDOEX4,5199
67
- dodal/devices/webcam.py,sha256=FXYcxQdOOCRIMAf8jMWlDVAhSEs4ycGCnoODvHb-apM,1554
68
- dodal/devices/xbpm_feedback.py,sha256=8QHYKHo9ksZo30olbFM-tHpCHcJRFozgHKVJijv3Gck,1986
69
- dodal/devices/zebra.py,sha256=9Zkq5I3-gcP6qfDBnPEAtFU4QJ-VJyp7cHvB79ZfLHk,9186
70
- dodal/devices/zebra_controlled_shutter.py,sha256=MqX4KE6w0FliZRDBltswcLCNSsp6vQrD_iBY640IljI,1094
71
- dodal/devices/areadetector/__init__.py,sha256=8IwLxuZMW0MOJpJp_ZDdlaE20hrtsH_PXWGaKgMiYs4,240
72
- dodal/devices/areadetector/adaravis.py,sha256=pwbmmnakarjhD59XoyAIXJdakS-nqDG09Xmwq17AVw4,3787
73
- dodal/devices/areadetector/adsim.py,sha256=3U7kS93RM3Xeh-XWKjeuw5jXbIGWAbrs59LfxtvB7OU,1907
74
- dodal/devices/areadetector/adutils.py,sha256=JIx1_sYlehpLtEXcwOEuzVoMplsLdKVW7OWv5eiJqgE,2576
75
- dodal/devices/areadetector/plugins/MJPG.py,sha256=pLuEZiRGgCwJM1ONA5jdetGLo6O0OVhvmcVkkYtPeR8,4159
76
- dodal/devices/detector/__init__.py,sha256=XEwjopgTtBq93RRuFthVVVI9DT1jUvpOJzWOHantJpU,104
77
- dodal/devices/detector/det_dim_constants.py,sha256=MZ4w2nsTKzj4eN7yGsSs1pqKWIuU4vc6UzcSll02uWg,2305
78
- dodal/devices/detector/det_dist_to_beam_converter.py,sha256=f6JFp-eEB2v8NzZg27UrN0VDP5CMjRnaPU6BTA7_n_s,1937
79
- dodal/devices/detector/det_resolution.py,sha256=aQkKp24LpRGiwzPAQM3wLVa4ANw32HdrKc2kftHfKQA,3253
80
- dodal/devices/detector/detector.py,sha256=arP27DbrgOjYZhE6Ibp9kDBglfmqZpPBk53S5ItsrvE,4756
81
- dodal/devices/detector/detector_motion.py,sha256=REREva2kyPcIzOZmahN9rT0jDSuUbV0qUDl4IcBnutA,1221
82
- dodal/devices/i03/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
- dodal/devices/i04/transfocator.py,sha256=uieByXIj0JRbmvMB_om5NOAEbEJkzfkCD24bl2aEo1g,3154
84
- dodal/devices/i20_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
- dodal/devices/i22/dcm.py,sha256=Kzyd_qFg8KVhRsgfTQVOpghESE8yIOgACKa0Fv9NaZI,6270
86
- dodal/devices/i22/fswitch.py,sha256=AdYtnkCBuhivyJGZqelg_7sjB2pHN7vl1JTtlO4vHo4,3061
87
- dodal/devices/i22/nxsas.py,sha256=ky7v9UZ1UQFsm5hI0wD9OXG-fTKFLj2wJjB7wADxKpw,5655
88
- dodal/devices/i24/I24_detector_motion.py,sha256=Joqr1orgeNvRS7n01bjaO-4Yu4obb8fnKaWHQfjPX14,365
89
- dodal/devices/i24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
- dodal/devices/i24/aperture.py,sha256=kKfHli5oKp-j-qZhZoXTRK81SAUNyhpI6VRvtw0SkZA,850
91
- dodal/devices/i24/beamstop.py,sha256=28hQowTvgN5Zw38tkDh32h2ceyN-2GE8bAaGPvDOt5U,1234
92
- dodal/devices/i24/dual_backlight.py,sha256=Th-RKr28aFxE8LCT_mdN9KkRIVw0BHLGKkI0ienfRZU,2049
93
- dodal/devices/i24/i24_vgonio.py,sha256=Igqs7687z6lyhGVeJEDtDmPachYxU48MUH2BF0RpK9Q,461
94
- dodal/devices/i24/pmac.py,sha256=pN54myYvzqPl7iW0Vsp59J1EiV_gtn0xQGwbsKJpiYE,3876
95
- dodal/devices/oav/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
- dodal/devices/oav/grid_overlay.py,sha256=FRtjcFd420XY8MEQ9sWedL0i4pK-KUJOSxh2C5zM3PA,5232
97
- dodal/devices/oav/microns_for_zoom_levels.json,sha256=5PA71RzldFTp0eTUGPmov0MjxHe583mzvfor5f3thXI,1208
98
- dodal/devices/oav/oav_calculations.py,sha256=wt71vFcyQrr98FvX8oyUM2n5vmKi3K7PyOTuWp0gq5w,1665
99
- dodal/devices/oav/oav_detector.py,sha256=JtzRdFQVXUdVK4Qyd9knDhsfkK6tsXoD_rIWDpLdpD4,3654
100
- dodal/devices/oav/oav_errors.py,sha256=cc4mGnaTiAc5WIlOt_BIYOc7CRSkrCdnBaavfAJ0pXY,754
101
- dodal/devices/oav/oav_parameters.py,sha256=4XybkhKeG7IEjPRfx0PVM9KNenuyN0rAGWBZG7H3zvQ,7941
102
- dodal/devices/oav/utils.py,sha256=zbUDvNETDoCtclj5jNzxz1XBt5mQlWBbxUrhRP7pZrU,3663
103
- dodal/devices/oav/pin_image_recognition/__init__.py,sha256=qEX3BRnrcP1BLZD-f_smHiMMPLJPkWQZQbIWTbW25JA,6499
104
- dodal/devices/oav/pin_image_recognition/manual_test.py,sha256=h1Rto6ZDCB3jWhjSy9N8ECxRN583iYDJr9LxrTJ8kfE,903
105
- dodal/devices/oav/pin_image_recognition/utils.py,sha256=-7-Zs-331UVTq_AZrfdF-zwZdmMn7eitTkBSqnBrxnk,8620
106
- dodal/devices/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
- dodal/devices/util/adjuster_plans.py,sha256=2AYaywQP_LbA2KJ6Op3cok8GoRtj696utrSSDfaJtBY,875
108
- dodal/devices/util/epics_util.py,sha256=eQr-ImBnADpBL_6XWr3_q9yuMe55Lu0h3j9L1fG4Jws,4714
109
- dodal/devices/util/lookup_tables.py,sha256=Up-0BlARt79TIEM76SkDyn9LtTFLxPUcaEPZv6D6bws,2141
110
- dodal/devices/util/motor_utils.py,sha256=pNY-aUk9LxaIWeDr5rpMS6udiB9j19wcCXkNDLp1uA0,257
111
- dodal/devices/xspress3/xspress3.py,sha256=29elzI3JtceryKeMWXhcP9nWl0tlSdnTZhltCitet6A,4668
112
- dodal/devices/xspress3/xspress3_channel.py,sha256=yJRwseLmtkW2Vv6GB8sLdOFuBn3e4c9Q8fgPacMgl5w,1638
113
- dodal/devices/zocalo/__init__.py,sha256=oPhjFB39yf2NWkGD-MMcPFnnOVZ_RtdyBt2OLYn-Xa4,505
114
- dodal/devices/zocalo/zocalo_interaction.py,sha256=B6TBTDwUlluksLTwC4TiEEgfFzWLOmwgG8xM5Xd4Wik,3132
115
- dodal/devices/zocalo/zocalo_results.py,sha256=U4Vk4OF-eL8w0BR-fbw3k4jyRo6G3Ywaf8NMAkjr4Hs,9658
116
- dodal/parameters/experiment_parameter_base.py,sha256=O7JamfuJ5cYHkPf9tsHJPqn-OMHTAGouigvM1cDFehE,313
117
- dodal/plans/check_topup.py,sha256=Pj6Eu8fa6nvoW4awrMxvzE_ftpLfYz8bN0QDLRw0Yuk,2989
118
- dodal/plans/data_session_metadata.py,sha256=QNx9rb1EfGBHb21eFekIi7KjNhC0PL-SVKBCggDuNeg,1650
119
- dls_dodal-1.29.3.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
120
- dls_dodal-1.29.3.dist-info/METADATA,sha256=WmlVBYCSTsRn07vPGXJa_FF8x_djiThku9KSaCay3A8,16942
121
- dls_dodal-1.29.3.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
122
- dls_dodal-1.29.3.dist-info/entry_points.txt,sha256=wpzz9FsTiYxI8OBwLKX9V9ResLwThBSmtRMcPwII0FA,46
123
- dls_dodal-1.29.3.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
124
- dls_dodal-1.29.3.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- dodal = dodal.__main__:main
dodal/devices/qbpm1.py DELETED
@@ -1,8 +0,0 @@
1
- from ophyd import Component as Cpt
2
- from ophyd import Device, EpicsSignalRO, Kind
3
-
4
-
5
- class QBPM1(Device):
6
- """Quadrant Beam Position Monitor"""
7
-
8
- intensity = Cpt(EpicsSignalRO, "-DI-QBPM-01:INTEN", kind=Kind.normal)