dls-dodal 1.46.0__py3-none-any.whl → 1.47.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 (67) hide show
  1. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/METADATA +1 -1
  2. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/RECORD +62 -51
  3. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/WHEEL +1 -1
  4. dodal/_version.py +2 -2
  5. dodal/beamlines/__init__.py +0 -1
  6. dodal/beamlines/b07.py +2 -6
  7. dodal/beamlines/b07_1.py +1 -3
  8. dodal/beamlines/i03.py +12 -15
  9. dodal/beamlines/i04.py +48 -16
  10. dodal/beamlines/i09.py +1 -3
  11. dodal/beamlines/i09_1.py +1 -3
  12. dodal/beamlines/i23.py +17 -1
  13. dodal/beamlines/p38.py +1 -1
  14. dodal/beamlines/p60.py +2 -6
  15. dodal/beamlines/p99.py +48 -4
  16. dodal/common/beamlines/beamline_parameters.py +1 -2
  17. dodal/common/data_util.py +4 -0
  18. dodal/devices/aperturescatterguard.py +47 -47
  19. dodal/devices/current_amplifiers/struck_scaler_counter.py +1 -1
  20. dodal/devices/diamond_filter.py +5 -17
  21. dodal/devices/eiger.py +1 -1
  22. dodal/devices/electron_analyser/__init__.py +8 -0
  23. dodal/devices/electron_analyser/abstract/__init__.py +28 -0
  24. dodal/devices/electron_analyser/abstract/base_detector.py +210 -0
  25. dodal/devices/electron_analyser/abstract/base_driver_io.py +121 -0
  26. dodal/devices/electron_analyser/{abstract_region.py → abstract/base_region.py} +2 -9
  27. dodal/devices/electron_analyser/specs/__init__.py +11 -0
  28. dodal/devices/electron_analyser/specs/detector.py +29 -0
  29. dodal/devices/electron_analyser/specs/driver_io.py +64 -0
  30. dodal/devices/electron_analyser/{specs_region.py → specs/region.py} +1 -1
  31. dodal/devices/electron_analyser/types.py +6 -0
  32. dodal/devices/electron_analyser/util.py +13 -0
  33. dodal/devices/electron_analyser/vgscienta/__init__.py +12 -0
  34. dodal/devices/electron_analyser/vgscienta/detector.py +36 -0
  35. dodal/devices/electron_analyser/vgscienta/driver_io.py +39 -0
  36. dodal/devices/electron_analyser/{vgscienta_region.py → vgscienta/region.py} +1 -1
  37. dodal/devices/fast_grid_scan.py +7 -9
  38. dodal/devices/i03/__init__.py +3 -0
  39. dodal/devices/i04/__init__.py +3 -0
  40. dodal/devices/i04/constants.py +9 -0
  41. dodal/devices/i04/murko_results.py +195 -0
  42. dodal/devices/i10/diagnostics.py +9 -61
  43. dodal/devices/i24/focus_mirrors.py +9 -13
  44. dodal/devices/i24/pilatus_metadata.py +9 -9
  45. dodal/devices/i24/pmac.py +19 -14
  46. dodal/devices/{i03 → mx_phase1}/beamstop.py +6 -12
  47. dodal/devices/oav/oav_calculations.py +2 -2
  48. dodal/devices/oav/oav_detector.py +32 -22
  49. dodal/devices/oav/utils.py +2 -2
  50. dodal/devices/p99/andor2_point.py +41 -0
  51. dodal/devices/positioner.py +49 -0
  52. dodal/devices/tetramm.py +5 -2
  53. dodal/devices/util/adjuster_plans.py +1 -1
  54. dodal/devices/zebra/zebra_constants_mapping.py +1 -1
  55. dodal/devices/zocalo/__init__.py +0 -3
  56. dodal/devices/zocalo/zocalo_results.py +6 -32
  57. dodal/log.py +14 -14
  58. dodal/plan_stubs/electron_analyser/__init__.py +3 -0
  59. dodal/plan_stubs/electron_analyser/{configure_controller.py → configure_driver.py} +30 -18
  60. dodal/common/signal_utils.py +0 -88
  61. dodal/devices/electron_analyser/abstract_analyser_io.py +0 -47
  62. dodal/devices/electron_analyser/specs_analyser_io.py +0 -19
  63. dodal/devices/electron_analyser/vgscienta_analyser_io.py +0 -26
  64. dodal/devices/logging_ophyd_device.py +0 -17
  65. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/entry_points.txt +0 -0
  66. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/licenses/LICENSE +0 -0
  67. {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/top_level.txt +0 -0
dodal/log.py CHANGED
@@ -201,6 +201,7 @@ def set_up_all_logging_handlers(
201
201
  dev_mode: bool,
202
202
  error_log_buffer_lines: int,
203
203
  graylog_port: int | None = None,
204
+ debug_logging_path: Path | None = None,
204
205
  ) -> DodalLogHandlers:
205
206
  """Set up the default logging environment.
206
207
  Args:
@@ -213,10 +214,10 @@ def set_up_all_logging_handlers(
213
214
  buffer and write to file when encountering an error message.
214
215
  graylog_port: The port to send graylog messages to, if None uses the
215
216
  default dodal port
217
+ debug_logging_path: The location to store debug log files, if None uses `logging_path`
216
218
  Returns:
217
219
  A DodaLogHandlers TypedDict with the created handlers.
218
220
  """
219
-
220
221
  handlers: DodalLogHandlers = {
221
222
  "stream_handler": set_up_stream_handler(logger),
222
223
  "graylog_handler": set_up_graylog_handler(
@@ -224,7 +225,7 @@ def set_up_all_logging_handlers(
224
225
  ),
225
226
  "info_file_handler": set_up_INFO_file_handler(logger, logging_path, filename),
226
227
  "debug_memory_handler": set_up_DEBUG_memory_handler(
227
- logger, logging_path, filename, error_log_buffer_lines
228
+ logger, debug_logging_path or logging_path, filename, error_log_buffer_lines
228
229
  ),
229
230
  }
230
231
 
@@ -238,35 +239,39 @@ def integrate_bluesky_and_ophyd_logging(parent_logger: logging.Logger):
238
239
 
239
240
 
240
241
  def do_default_logging_setup(dev_mode=False, graylog_port: int | None = None):
242
+ logging_path, debug_logging_path = get_logging_file_paths()
241
243
  set_up_all_logging_handlers(
242
244
  LOGGER,
243
- get_logging_file_path(),
245
+ logging_path,
244
246
  "dodal.log",
245
247
  dev_mode,
246
248
  ERROR_LOG_BUFFER_LINES,
247
249
  graylog_port,
250
+ debug_logging_path,
248
251
  )
249
252
  integrate_bluesky_and_ophyd_logging(LOGGER)
250
253
 
251
254
 
252
- def get_logging_file_path() -> Path:
253
- """Get the directory to write log files to.
255
+ def get_logging_file_paths() -> tuple[Path, Path]:
256
+ """Get the directories to write log files to.
254
257
 
255
258
  If on a beamline, this will return '/dls_sw/$BEAMLINE/logs/bluesky' based on the
256
- BEAMLINE envrionment variable. If no envrionment variable is found it will default
259
+ BEAMLINE environment variable. If no environment variable is found it will default
257
260
  to the tmp/dev directory.
258
261
 
259
262
  Returns:
260
- logging_path (Path): Path to the log directory for the file handlers to write to.
263
+ tuple[Path, Path]: Paths to the standard log file and to the debug log file,
264
+ for the file handlers to write to
261
265
  """
262
266
  beamline: str | None = environ.get("BEAMLINE")
263
- logging_path: Path
264
267
 
265
268
  if beamline:
266
269
  logging_path = Path("/dls_sw/" + beamline + "/logs/bluesky/")
270
+ debug_logging_path = Path("/dls/tmp/" + beamline + "/logs/bluesky/")
267
271
  else:
268
272
  logging_path = Path("./tmp/dev/")
269
- return logging_path
273
+ debug_logging_path = Path("./tmp/dev/")
274
+ return logging_path, debug_logging_path
270
275
 
271
276
 
272
277
  def get_graylog_configuration(
@@ -284,8 +289,3 @@ def get_graylog_configuration(
284
289
  return "localhost", 5555
285
290
  else:
286
291
  return "graylog-log-target.diamond.ac.uk", graylog_port or DEFAULT_GRAYLOG_PORT
287
-
288
-
289
- class _NoOpFileHandler:
290
- def write(*args, **kwargs):
291
- pass
@@ -0,0 +1,3 @@
1
+ from .configure_driver import configure_analyser, configure_specs, configure_vgscienta
2
+
3
+ __all__ = ["configure_analyser", "configure_specs", "configure_vgscienta"]
@@ -1,37 +1,44 @@
1
1
  from bluesky import plan_stubs as bps
2
+ from bluesky.utils import MsgGenerator, plan
3
+ from ophyd_async.epics.adcore import ADImageMode
2
4
 
3
- from dodal.devices.electron_analyser.abstract_analyser_io import (
5
+ from dodal.common.types import MsgGenerator
6
+ from dodal.devices.electron_analyser.abstract import (
4
7
  AbstractAnalyserDriverIO,
5
- )
6
- from dodal.devices.electron_analyser.abstract_region import (
7
8
  AbstractBaseRegion,
8
9
  )
9
- from dodal.devices.electron_analyser.specs_analyser_io import (
10
- SpecsAnalyserDriverIO,
11
- )
12
- from dodal.devices.electron_analyser.specs_region import SpecsRegion
13
- from dodal.devices.electron_analyser.vgscienta_analyser_io import (
10
+ from dodal.devices.electron_analyser.specs import SpecsAnalyserDriverIO, SpecsRegion
11
+ from dodal.devices.electron_analyser.util import to_kinetic_energy
12
+ from dodal.devices.electron_analyser.vgscienta import (
14
13
  VGScientaAnalyserDriverIO,
15
- )
16
- from dodal.devices.electron_analyser.vgscienta_region import (
17
14
  VGScientaRegion,
18
15
  )
19
16
  from dodal.log import LOGGER
20
17
 
21
18
 
19
+ @plan
22
20
  def configure_analyser(
23
21
  analyser: AbstractAnalyserDriverIO,
24
22
  region: AbstractBaseRegion,
25
23
  excitation_energy: float,
26
- ):
24
+ ) -> MsgGenerator:
27
25
  LOGGER.info(f'Configuring analyser with region "{region.name}"')
28
- low_energy = region.to_kinetic_energy(region.low_energy, excitation_energy)
29
- high_energy = region.to_kinetic_energy(region.high_energy, excitation_energy)
26
+
27
+ low_energy = to_kinetic_energy(
28
+ region.low_energy, region.energy_mode, excitation_energy
29
+ )
30
+ high_energy = to_kinetic_energy(
31
+ region.high_energy, region.energy_mode, excitation_energy
32
+ )
30
33
  pass_energy_type = analyser.pass_energy_type
31
34
  pass_energy = pass_energy_type(region.pass_energy)
35
+
32
36
  # Set detector settings, wait for them all to have completed
33
37
  # fmt: off
34
38
  yield from bps.mv(
39
+ analyser.region_name, region.name,
40
+ analyser.energy_mode, region.energy_mode,
41
+ analyser.excitation_energy, excitation_energy,
35
42
  analyser.low_energy, low_energy,
36
43
  analyser.high_energy, high_energy,
37
44
  analyser.slices, region.slices,
@@ -43,13 +50,14 @@ def configure_analyser(
43
50
  # fmt: on
44
51
 
45
52
 
53
+ @plan
46
54
  def configure_specs(
47
55
  analyser: SpecsAnalyserDriverIO, region: SpecsRegion, excitation_energy: float
48
- ):
56
+ ) -> MsgGenerator:
49
57
  yield from configure_analyser(analyser, region, excitation_energy)
50
58
  # fmt: off
51
59
  yield from bps.mv(
52
- analyser.values, region.values,
60
+ analyser.snapshot_values, region.values,
53
61
  analyser.psu_mode, region.psu_mode,
54
62
  )
55
63
  # fmt: on
@@ -60,11 +68,14 @@ def configure_specs(
60
68
  yield from bps.mv(analyser.energy_step, region.energy_step)
61
69
 
62
70
 
71
+ @plan
63
72
  def configure_vgscienta(
64
73
  analyser: VGScientaAnalyserDriverIO, region: VGScientaRegion, excitation_energy
65
- ):
74
+ ) -> MsgGenerator:
66
75
  yield from configure_analyser(analyser, region, excitation_energy)
67
- centre_energy = region.to_kinetic_energy(region.fix_energy, excitation_energy)
76
+ centre_energy = to_kinetic_energy(
77
+ region.fix_energy, region.energy_mode, excitation_energy
78
+ )
68
79
 
69
80
  # fmt: off
70
81
  yield from bps.mv(
@@ -75,6 +86,7 @@ def configure_vgscienta(
75
86
  analyser.x_channel_size, region.x_channel_size(),
76
87
  analyser.y_channel_size, region.y_channel_size(),
77
88
  analyser.detector_mode, region.detector_mode,
78
- analyser.image_mode, "Single",
89
+ analyser.excitation_energy_source, region.excitation_energy_source,
90
+ analyser.image_mode, ADImageMode.SINGLE,
79
91
  )
80
92
  # fmt: on
@@ -1,88 +0,0 @@
1
- from collections.abc import Callable, Coroutine
2
- from typing import Any
3
-
4
- from bluesky.protocols import Reading
5
- from ophyd_async.core import SignalDatatypeT, SignalR, SignalRW, SoftSignalBackend
6
-
7
- SetHardwareType = Callable[[SignalDatatypeT], Coroutine[Any, Any, None]]
8
-
9
-
10
- class HardwareBackedSoftSignalBackend(SoftSignalBackend[SignalDatatypeT]):
11
- def __init__(
12
- self,
13
- get_from_hardware_func: Callable[[], Coroutine[Any, Any, SignalDatatypeT]],
14
- set_to_hardware_func: SetHardwareType | None = None,
15
- *args,
16
- **kwargs,
17
- ) -> None:
18
- self.get_from_hardware_func = get_from_hardware_func
19
- self.set_to_hardware_func = set_to_hardware_func
20
- super().__init__(*args, **kwargs)
21
-
22
- async def _update_value(self):
23
- new_value = await self.get_from_hardware_func()
24
- self.set_value(new_value)
25
-
26
- async def get_reading(self) -> Reading:
27
- await self._update_value()
28
- return await super().get_reading()
29
-
30
- async def get_value(self) -> SignalDatatypeT:
31
- await self._update_value()
32
- return await super().get_value()
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
-
65
-
66
- def create_r_hardware_backed_soft_signal(
67
- datatype: type[SignalDatatypeT],
68
- get_from_hardware_func: Callable[[], Coroutine[Any, Any, SignalDatatypeT]],
69
- units: str | None = None,
70
- precision: int | None = None,
71
- ):
72
- """Creates a soft signal that, when read will call the function passed into
73
- `get_from_hardware_func` and return this.
74
-
75
- This will allow you to make soft signals derived from arbitrary hardware signals.
76
- However, calling subscribe on this signal does not give you a sensible value and
77
- the signal is currently read only. See https://github.com/bluesky/ophyd-async/issues/525
78
- for a more full solution.
79
- """
80
- return SignalR(
81
- backend=HardwareBackedSoftSignalBackend(
82
- get_from_hardware_func,
83
- None,
84
- datatype,
85
- units=units,
86
- precision=precision,
87
- )
88
- )
@@ -1,47 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from typing import TypeVar
3
-
4
- from ophyd_async.core import StandardReadable
5
- from ophyd_async.epics.core import epics_signal_rw
6
-
7
- from dodal.devices.electron_analyser.abstract_region import EnergyMode
8
-
9
-
10
- class AbstractAnalyserDriverIO(ABC, StandardReadable):
11
- """
12
- Generic device to configure electron analyser with new region settings.
13
- Electron analysers should inherit from this class for further specialisation.
14
- """
15
-
16
- def __init__(self, prefix: str, name: str = "") -> None:
17
- with self.add_children_as_readables():
18
- self.low_energy = epics_signal_rw(float, prefix + "LOW_ENERGY")
19
- self.high_energy = epics_signal_rw(float, prefix + "HIGH_ENERGY")
20
- self.slices = epics_signal_rw(int, prefix + "SLICES")
21
- self.lens_mode = epics_signal_rw(str, prefix + "LENS_MODE")
22
- self.pass_energy = epics_signal_rw(
23
- self.pass_energy_type, prefix + "PASS_ENERGY"
24
- )
25
- self.energy_step = epics_signal_rw(float, prefix + "STEP_SIZE")
26
- self.iterations = epics_signal_rw(int, prefix + "NumExposures")
27
- self.acquisition_mode = epics_signal_rw(str, prefix + "ACQ_MODE")
28
-
29
- super().__init__(name)
30
-
31
- def to_kinetic_energy(
32
- self, value: float, excitation_energy: float, mode: EnergyMode
33
- ) -> float:
34
- return excitation_energy - value if mode == EnergyMode.BINDING else value
35
-
36
- @property
37
- @abstractmethod
38
- def pass_energy_type(self) -> type:
39
- """
40
- Return the type the pass_energy should be. Each one is unfortunately different
41
- for the underlying analyser software and cannot be changed on epics side.
42
- """
43
-
44
-
45
- TAbstractAnalyserDriverIO = TypeVar(
46
- "TAbstractAnalyserDriverIO", bound=AbstractAnalyserDriverIO
47
- )
@@ -1,19 +0,0 @@
1
- from ophyd_async.epics.core import epics_signal_rw
2
-
3
- from dodal.devices.electron_analyser.abstract_analyser_io import (
4
- AbstractAnalyserDriverIO,
5
- )
6
-
7
-
8
- class SpecsAnalyserDriverIO(AbstractAnalyserDriverIO):
9
- def __init__(self, prefix: str, name: str = "") -> None:
10
- with self.add_children_as_readables():
11
- self.psu_mode = epics_signal_rw(str, prefix + "SCAN_RANGE")
12
- self.values = epics_signal_rw(int, prefix + "VALUES")
13
- self.centre_energy = epics_signal_rw(float, prefix + "KINETIC_ENERGY")
14
-
15
- super().__init__(prefix, name)
16
-
17
- @property
18
- def pass_energy_type(self) -> type:
19
- return float
@@ -1,26 +0,0 @@
1
- from ophyd_async.epics.core import epics_signal_rw
2
-
3
- from dodal.devices.electron_analyser.abstract_analyser_io import (
4
- AbstractAnalyserDriverIO,
5
- )
6
- from dodal.devices.electron_analyser.vgscienta_region import (
7
- DetectorMode,
8
- )
9
-
10
-
11
- class VGScientaAnalyserDriverIO(AbstractAnalyserDriverIO):
12
- def __init__(self, prefix: str, name: str = "") -> None:
13
- with self.add_children_as_readables():
14
- self.centre_energy = epics_signal_rw(float, prefix + "CENTRE_ENERGY")
15
- self.first_x_channel = epics_signal_rw(int, prefix + "MinX")
16
- self.first_y_channel = epics_signal_rw(int, prefix + "MinY")
17
- self.x_channel_size = epics_signal_rw(int, prefix + "SizeX")
18
- self.y_channel_size = epics_signal_rw(int, prefix + "SizeY")
19
- self.detector_mode = epics_signal_rw(DetectorMode, prefix + "DETECTOR_MODE")
20
- self.image_mode = epics_signal_rw(str, prefix + "ImageMode")
21
-
22
- super().__init__(prefix, name)
23
-
24
- @property
25
- def pass_energy_type(self) -> type:
26
- return str
@@ -1,17 +0,0 @@
1
- from ophyd import Device
2
- from ophyd.log import logger as ophyd_logger
3
-
4
-
5
- class InfoLoggingDevice(Device):
6
- def wait_for_connection(self, all_signals=False, timeout=2.0):
7
- class_name = self.__class__.__name__
8
- ophyd_logger.info(
9
- f"{class_name} waiting for connection, {'not' if all_signals else ''} waiting for all signals, timeout = {timeout}s.",
10
- )
11
- try:
12
- super().wait_for_connection(all_signals, timeout)
13
- except TimeoutError as e:
14
- ophyd_logger.error(f"{class_name} failed to connect.", exc_info=True)
15
- raise e
16
- else:
17
- ophyd_logger.info(f"{class_name} connected.")