dls-dodal 1.45.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 (81) hide show
  1. {dls_dodal-1.45.0.dist-info → dls_dodal-1.47.0.dist-info}/METADATA +2 -2
  2. {dls_dodal-1.45.0.dist-info → dls_dodal-1.47.0.dist-info}/RECORD +76 -64
  3. {dls_dodal-1.45.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 +16 -19
  9. dodal/beamlines/i04.py +49 -17
  10. dodal/beamlines/i09.py +1 -3
  11. dodal/beamlines/i09_1.py +1 -3
  12. dodal/beamlines/i18.py +7 -4
  13. dodal/beamlines/i22.py +3 -3
  14. dodal/beamlines/i23.py +75 -4
  15. dodal/beamlines/p38.py +4 -4
  16. dodal/beamlines/p60.py +2 -6
  17. dodal/beamlines/p99.py +48 -4
  18. dodal/common/beamlines/beamline_parameters.py +1 -2
  19. dodal/common/beamlines/beamline_utils.py +5 -0
  20. dodal/common/data_util.py +4 -0
  21. dodal/devices/aperturescatterguard.py +47 -47
  22. dodal/devices/common_dcm.py +77 -0
  23. dodal/devices/current_amplifiers/struck_scaler_counter.py +1 -1
  24. dodal/devices/diamond_filter.py +5 -17
  25. dodal/devices/eiger.py +1 -1
  26. dodal/devices/electron_analyser/__init__.py +8 -0
  27. dodal/devices/electron_analyser/abstract/__init__.py +28 -0
  28. dodal/devices/electron_analyser/abstract/base_detector.py +210 -0
  29. dodal/devices/electron_analyser/abstract/base_driver_io.py +121 -0
  30. dodal/devices/electron_analyser/{abstract_region.py → abstract/base_region.py} +2 -9
  31. dodal/devices/electron_analyser/specs/__init__.py +11 -0
  32. dodal/devices/electron_analyser/specs/detector.py +29 -0
  33. dodal/devices/electron_analyser/specs/driver_io.py +64 -0
  34. dodal/devices/electron_analyser/{specs_region.py → specs/region.py} +1 -1
  35. dodal/devices/electron_analyser/types.py +6 -0
  36. dodal/devices/electron_analyser/util.py +13 -0
  37. dodal/devices/electron_analyser/vgscienta/__init__.py +12 -0
  38. dodal/devices/electron_analyser/vgscienta/detector.py +36 -0
  39. dodal/devices/electron_analyser/vgscienta/driver_io.py +39 -0
  40. dodal/devices/electron_analyser/{vgscienta_region.py → vgscienta/region.py} +1 -1
  41. dodal/devices/fast_grid_scan.py +7 -9
  42. dodal/devices/i03/__init__.py +3 -0
  43. dodal/devices/{dcm.py → i03/dcm.py} +8 -12
  44. dodal/devices/{undulator_dcm.py → i03/undulator_dcm.py} +6 -4
  45. dodal/devices/i04/__init__.py +3 -0
  46. dodal/devices/i04/constants.py +9 -0
  47. dodal/devices/i04/murko_results.py +195 -0
  48. dodal/devices/i10/diagnostics.py +9 -61
  49. dodal/devices/i13_1/merlin.py +3 -4
  50. dodal/devices/i13_1/merlin_controller.py +1 -1
  51. dodal/devices/i22/dcm.py +10 -12
  52. dodal/devices/i24/dcm.py +8 -17
  53. dodal/devices/i24/focus_mirrors.py +9 -13
  54. dodal/devices/i24/pilatus_metadata.py +9 -9
  55. dodal/devices/i24/pmac.py +19 -14
  56. dodal/devices/{i03 → mx_phase1}/beamstop.py +6 -12
  57. dodal/devices/oav/oav_calculations.py +2 -2
  58. dodal/devices/oav/oav_detector.py +32 -22
  59. dodal/devices/oav/utils.py +2 -2
  60. dodal/devices/p99/andor2_point.py +41 -0
  61. dodal/devices/positioner.py +49 -0
  62. dodal/devices/tetramm.py +8 -6
  63. dodal/devices/turbo_slit.py +2 -2
  64. dodal/devices/util/adjuster_plans.py +1 -1
  65. dodal/devices/zebra/zebra.py +4 -0
  66. dodal/devices/zebra/zebra_constants_mapping.py +1 -1
  67. dodal/devices/zocalo/__init__.py +0 -3
  68. dodal/devices/zocalo/zocalo_results.py +6 -32
  69. dodal/log.py +14 -14
  70. dodal/plan_stubs/data_session.py +10 -1
  71. dodal/plan_stubs/electron_analyser/__init__.py +3 -0
  72. dodal/plan_stubs/electron_analyser/{configure_controller.py → configure_driver.py} +30 -18
  73. dodal/plans/verify_undulator_gap.py +2 -2
  74. dodal/common/signal_utils.py +0 -88
  75. dodal/devices/electron_analyser/abstract_analyser_io.py +0 -47
  76. dodal/devices/electron_analyser/specs_analyser_io.py +0 -19
  77. dodal/devices/electron_analyser/vgscienta_analyser_io.py +0 -26
  78. dodal/devices/logging_ophyd_device.py +0 -17
  79. {dls_dodal-1.45.0.dist-info → dls_dodal-1.47.0.dist-info}/entry_points.txt +0 -0
  80. {dls_dodal-1.45.0.dist-info → dls_dodal-1.47.0.dist-info}/licenses/LICENSE +0 -0
  81. {dls_dodal-1.45.0.dist-info → dls_dodal-1.47.0.dist-info}/top_level.txt +0 -0
dodal/devices/i24/pmac.py CHANGED
@@ -10,6 +10,7 @@ from ophyd_async.core import (
10
10
  SignalR,
11
11
  SignalRW,
12
12
  StandardReadable,
13
+ observe_signals_value,
13
14
  soft_signal_rw,
14
15
  wait_for_value,
15
16
  )
@@ -120,15 +121,16 @@ class ProgramRunner(Device, Flyable):
120
121
  self,
121
122
  pmac_str_sig: SignalRW,
122
123
  status_sig: SignalR,
124
+ counter_sig: SignalR,
123
125
  prog_num_sig: SignalRW,
124
- collection_time_sig: SignalRW,
126
+ counter_time_sig: SignalRW,
125
127
  name: str = "",
126
128
  ) -> None:
127
129
  self._signal_ref = Reference(pmac_str_sig)
128
130
  self._status_ref = Reference(status_sig)
131
+ self._counter_ref = Reference(counter_sig)
129
132
  self._prog_num_ref = Reference(prog_num_sig)
130
-
131
- self._collection_time_ref = Reference(collection_time_sig)
133
+ self._counter_time_ref = Reference(counter_time_sig)
132
134
 
133
135
  super().__init__(name)
134
136
 
@@ -151,16 +153,18 @@ class ProgramRunner(Device, Flyable):
151
153
 
152
154
  @AsyncStatus.wrap
153
155
  async def complete(self):
154
- """Stop collecting when the scan status PV goes to 0.
155
-
156
- Args:
157
- complete_time (float): total time required by the collection to \
158
- finish correctly.
156
+ """Stop collecting when the scan status PV goes to 0 or when counter PV hasn't \
157
+ updated for 30 seconds.
159
158
  """
160
- scan_complete_time = await self._collection_time_ref().get_value()
161
- await wait_for_value(
162
- self._status_ref(), ScanState.DONE, timeout=scan_complete_time
163
- )
159
+ counter_time = await self._counter_time_ref().get_value()
160
+ async for signal, value in observe_signals_value(
161
+ self._status_ref(),
162
+ self._counter_ref(),
163
+ timeout=counter_time,
164
+ ):
165
+ if signal is self._status_ref():
166
+ if value == ScanState.DONE:
167
+ break
164
168
 
165
169
 
166
170
  class ProgramAbort(Triggerable):
@@ -217,13 +221,14 @@ class PMAC(StandardReadable):
217
221
  # A couple of soft signals for running a collection: program number to send to
218
222
  # the PMAC_STRING and expected collection time.
219
223
  self.program_number = soft_signal_rw(int)
220
- self.collection_time = soft_signal_rw(float, initial_value=600.0, units="s")
224
+ self.counter_time = soft_signal_rw(float, initial_value=30.0, units="s")
221
225
 
222
226
  self.run_program = ProgramRunner(
223
227
  self.pmac_string,
224
228
  self.scanstatus,
229
+ self.counter,
225
230
  self.program_number,
226
- self.collection_time,
231
+ self.counter_time,
227
232
  )
228
233
  self.abort_program = ProgramAbort(self.pmac_string, self.scanstatus)
229
234
 
@@ -1,11 +1,9 @@
1
- from asyncio import gather
2
1
  from math import isclose
3
2
 
4
- from ophyd_async.core import StandardReadable, StrictEnum
3
+ from ophyd_async.core import StandardReadable, StrictEnum, derived_signal_r
5
4
  from ophyd_async.epics.motor import Motor
6
5
 
7
6
  from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters
8
- from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
9
7
 
10
8
 
11
9
  class BeamstopPositions(StrictEnum):
@@ -33,7 +31,7 @@ class BeamstopPositions(StrictEnum):
33
31
 
34
32
  class Beamstop(StandardReadable):
35
33
  """
36
- Beamstop for I03.
34
+ Beamstop for I03 and I04.
37
35
 
38
36
  Attributes:
39
37
  x: beamstop x position in mm
@@ -53,8 +51,8 @@ class Beamstop(StandardReadable):
53
51
  self.x_mm = Motor(prefix + "X")
54
52
  self.y_mm = Motor(prefix + "Y")
55
53
  self.z_mm = Motor(prefix + "Z")
56
- self.selected_pos = create_r_hardware_backed_soft_signal(
57
- BeamstopPositions, self._get_selected_position
54
+ self.selected_pos = derived_signal_r(
55
+ self._get_selected_position, x=self.x_mm, y=self.y_mm, z=self.z_mm
58
56
  )
59
57
 
60
58
  self._in_beam_xyz_mm = [
@@ -68,12 +66,8 @@ class Beamstop(StandardReadable):
68
66
 
69
67
  super().__init__(name)
70
68
 
71
- async def _get_selected_position(self) -> BeamstopPositions:
72
- current_pos = await gather(
73
- self.x_mm.user_readback.get_value(),
74
- self.y_mm.user_readback.get_value(),
75
- self.z_mm.user_readback.get_value(),
76
- )
69
+ def _get_selected_position(self, x: float, y: float, z: float) -> BeamstopPositions:
70
+ current_pos = [x, y, z]
77
71
  if all(
78
72
  isclose(axis_pos, axis_in_beam, abs_tol=axis_tolerance)
79
73
  for axis_pos, axis_in_beam, axis_tolerance in zip(
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
 
3
3
 
4
- def camera_coordinates_to_xyz(
4
+ def camera_coordinates_to_xyz_mm(
5
5
  horizontal: float,
6
6
  vertical: float,
7
7
  omega: float,
@@ -9,7 +9,7 @@ def camera_coordinates_to_xyz(
9
9
  microns_per_j_pixel: float,
10
10
  ) -> np.ndarray:
11
11
  """
12
- Converts from (horizontal,vertical) pixel measurements from the OAV camera into to (x, y, z) motor coordinates in millmeters.
12
+ Converts from (horizontal,vertical) pixel measurements from the OAV camera into to (x, y, z) motor coordinates in millimetres.
13
13
  For an overview of the coordinate system for I03 see https://github.com/DiamondLightSource/hyperion/wiki/Gridscan-Coordinate-System.
14
14
 
15
15
  Args:
@@ -6,10 +6,11 @@ from ophyd_async.core import (
6
6
  AsyncStatus,
7
7
  LazyMock,
8
8
  StandardReadable,
9
+ derived_signal_r,
10
+ soft_signal_rw,
9
11
  )
10
12
  from ophyd_async.epics.core import epics_signal_rw
11
13
 
12
- from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
13
14
  from dodal.devices.areadetector.plugins.CAM import Cam
14
15
  from dodal.devices.oav.oav_parameters import DEFAULT_OAV_WINDOW, OAVConfig
15
16
  from dodal.devices.oav.snapshots.snapshot import Snapshot
@@ -60,49 +61,58 @@ class OAV(StandardReadable):
60
61
  self.zoom_controller = ZoomController(f"{_bl_prefix}-EA-OAV-01:FZOOM:", name)
61
62
 
62
63
  self.cam = Cam(f"{prefix}CAM:", name=name)
63
-
64
64
  with self.add_children_as_readables():
65
65
  self.grid_snapshot = SnapshotWithGrid(f"{prefix}MJPG:", name)
66
- self.microns_per_pixel_x = create_r_hardware_backed_soft_signal(
67
- float,
68
- lambda: self._get_microns_per_pixel(Coords.X),
66
+
67
+ self.sizes = [self.grid_snapshot.x_size, self.grid_snapshot.y_size]
68
+
69
+ with self.add_children_as_readables():
70
+ self.microns_per_pixel_x = derived_signal_r(
71
+ self._get_microns_per_pixel,
72
+ zoom_level=self.zoom_controller.level,
73
+ size=self.sizes[Coords.X],
74
+ coord=soft_signal_rw(datatype=int, initial_value=Coords.X.value),
69
75
  )
70
- self.microns_per_pixel_y = create_r_hardware_backed_soft_signal(
71
- float,
72
- lambda: self._get_microns_per_pixel(Coords.Y),
76
+ self.microns_per_pixel_y = derived_signal_r(
77
+ self._get_microns_per_pixel,
78
+ zoom_level=self.zoom_controller.level,
79
+ size=self.sizes[Coords.Y],
80
+ coord=soft_signal_rw(datatype=int, initial_value=Coords.Y.value),
73
81
  )
74
- self.beam_centre_i = create_r_hardware_backed_soft_signal(
75
- int, lambda: self._get_beam_position(Coords.X)
82
+ self.beam_centre_i = derived_signal_r(
83
+ self._get_beam_position,
84
+ zoom_level=self.zoom_controller.level,
85
+ size=self.sizes[Coords.X],
86
+ coord=soft_signal_rw(datatype=int, initial_value=Coords.X.value),
76
87
  )
77
- self.beam_centre_j = create_r_hardware_backed_soft_signal(
78
- int, lambda: self._get_beam_position(Coords.Y)
88
+ self.beam_centre_j = derived_signal_r(
89
+ self._get_beam_position,
90
+ zoom_level=self.zoom_controller.level,
91
+ size=self.sizes[Coords.Y],
92
+ coord=soft_signal_rw(datatype=int, initial_value=Coords.Y.value),
79
93
  )
80
94
  self.snapshot = Snapshot(
81
95
  f"{self._prefix}MJPG:",
82
96
  self._name,
83
97
  )
84
98
 
85
- self.sizes = [self.grid_snapshot.x_size, self.grid_snapshot.y_size]
86
-
87
99
  super().__init__(name)
88
100
 
89
- async def _read_current_zoom(self) -> str:
90
- _zoom = await self.zoom_controller.level.get_value()
101
+ def _read_current_zoom(self, _zoom: str) -> str:
91
102
  return _get_correct_zoom_string(_zoom)
92
103
 
93
- async def _get_microns_per_pixel(self, coord: int) -> float:
104
+ def _get_microns_per_pixel(self, zoom_level: str, size: int, coord: int) -> float:
94
105
  """Extracts the microns per x pixel and y pixel for a given zoom level."""
95
- _zoom = await self._read_current_zoom()
106
+ _zoom = self._read_current_zoom(zoom_level)
96
107
  value = self.parameters[_zoom].microns_per_pixel[coord]
97
- size = await self.sizes[coord].get_value()
98
108
  return value * DEFAULT_OAV_WINDOW[coord] / size
99
109
 
100
- async def _get_beam_position(self, coord: int) -> int:
110
+ def _get_beam_position(self, zoom_level: str, size: int, coord: int) -> int:
101
111
  """Extracts the beam location in pixels `xCentre` `yCentre`, for a requested \
102
112
  zoom level. """
103
- _zoom = await self._read_current_zoom()
113
+ _zoom = self._read_current_zoom(zoom_level)
104
114
  value = self.parameters[_zoom].crosshair[coord]
105
- size = await self.sizes[coord].get_value()
115
+
106
116
  return int(value * size / DEFAULT_OAV_WINDOW[coord])
107
117
 
108
118
  async def connect(
@@ -7,7 +7,7 @@ from bluesky.utils import Msg
7
7
 
8
8
  from dodal.devices.oav.oav_calculations import (
9
9
  calculate_beam_distance,
10
- camera_coordinates_to_xyz,
10
+ camera_coordinates_to_xyz_mm,
11
11
  )
12
12
  from dodal.devices.oav.oav_detector import OAV
13
13
  from dodal.devices.oav.pin_image_recognition import PinTipDetection
@@ -90,7 +90,7 @@ def calculate_x_y_z_of_pixel(
90
90
  """Get the x, y, z position of a pixel in mm"""
91
91
  beam_distance_px: Pixel = calculate_beam_distance(beam_centre, *pixel)
92
92
 
93
- return current_x_y_z + camera_coordinates_to_xyz(
93
+ return current_x_y_z + camera_coordinates_to_xyz_mm(
94
94
  beam_distance_px[0],
95
95
  beam_distance_px[1],
96
96
  current_omega,
@@ -0,0 +1,41 @@
1
+ from ophyd_async.core import (
2
+ StandardReadableFormat,
3
+ )
4
+ from ophyd_async.epics.adandor import Andor2DriverIO
5
+ from ophyd_async.epics.adcore import NDPluginBaseIO, SingleTriggerDetector
6
+ from ophyd_async.epics.core import epics_signal_r
7
+
8
+
9
+ class Andor2Point(SingleTriggerDetector):
10
+ """Using the andor2 as if it is a massive point detector, read the read uncached
11
+ value after a picture is taken."""
12
+
13
+ def __init__(
14
+ self,
15
+ prefix: str,
16
+ drv_suffix: str,
17
+ read_uncached: dict[str, str],
18
+ name: str = "",
19
+ plugins: dict[str, NDPluginBaseIO] | None = None,
20
+ ) -> None:
21
+ """
22
+ Parameters
23
+ ----------
24
+ prefix: str,
25
+ Beamline camera pv
26
+ drv_suffix : str,
27
+ Camera pv suffix
28
+ read_uncached: dict[str,str]
29
+ A dictionary contains the name and the pv suffix for the statistic plugin.
30
+ name: str:
31
+ Name of the device.
32
+ plugins:: Optional[dict[str, NDPluginBaseIO] | None
33
+ Dictionary containing plugin that are forward to the base class.
34
+ """
35
+ with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
36
+ for k, v in read_uncached.items():
37
+ setattr(self, k, epics_signal_r(float, prefix + v))
38
+
39
+ super().__init__(
40
+ drv=Andor2DriverIO(prefix + drv_suffix), name=name, plugins=plugins
41
+ )
@@ -0,0 +1,49 @@
1
+ from typing import TypeVar
2
+
3
+ from bluesky.protocols import Movable
4
+ from ophyd_async.core import (
5
+ AsyncStatus,
6
+ StandardReadable,
7
+ StrictEnum,
8
+ )
9
+ from ophyd_async.epics.core import (
10
+ epics_signal_rw,
11
+ )
12
+ from ophyd_async.epics.motor import Motor
13
+
14
+ T = TypeVar("T", bound=StrictEnum)
15
+
16
+
17
+ class Positioner1D(StandardReadable, Movable[T]):
18
+ """1D stage with a enum table to select positions.
19
+
20
+ Use this when writing a device with an EPICS positioner on a single axis.
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ prefix: str,
26
+ datatype: type[StrictEnum],
27
+ positioner_pv_suffix: str = ":MP:SELECT",
28
+ name: str = "",
29
+ ) -> None:
30
+ self._stage_motion = Motor(prefix=prefix)
31
+ with self.add_children_as_readables():
32
+ self.stage_position = epics_signal_rw(
33
+ datatype,
34
+ read_pv=prefix + positioner_pv_suffix,
35
+ )
36
+ super().__init__(name=name)
37
+
38
+ @AsyncStatus.wrap
39
+ async def set(self, value: T) -> None:
40
+ await self.stage_position.set(value=value)
41
+
42
+
43
+ def create_positioner(
44
+ datatype: type[T],
45
+ prefix,
46
+ positioner_pv_suffix: str = ":MP:SELECT",
47
+ name: str = "",
48
+ ) -> Positioner1D[T]:
49
+ return Positioner1D[datatype](prefix, datatype, positioner_pv_suffix, name)
dodal/devices/tetramm.py CHANGED
@@ -131,11 +131,14 @@ class TetrammController(DetectorController):
131
131
  )
132
132
 
133
133
  async def arm(self):
134
- self._arm_status = await set_and_wait_for_value(self._drv.acquire, True)
134
+ self._arm_status = await set_and_wait_for_value(
135
+ self._drv.acquire, True, wait_for_set_completion=False
136
+ )
135
137
 
136
138
  async def wait_for_idle(self):
137
- if self._arm_status:
139
+ if self._arm_status and not self._arm_status.done:
138
140
  await self._arm_status
141
+ self._arm_status = None
139
142
 
140
143
  def _validate_trigger(self, trigger: DetectorTrigger) -> None:
141
144
  supported_trigger_types = {
@@ -244,10 +247,9 @@ class TetrammDetector(StandardDetector):
244
247
  super().__init__(
245
248
  controller,
246
249
  ADHDFWriter(
247
- self.hdf,
248
- path_provider,
249
- lambda: self.name,
250
- TetrammDatasetDescriber(controller),
250
+ fileio=self.hdf,
251
+ path_provider=path_provider,
252
+ dataset_describer=TetrammDatasetDescriber(controller),
251
253
  plugins=plugins,
252
254
  ),
253
255
  config_signals,
@@ -13,9 +13,9 @@ class TurboSlit(StandardReadable, Movable[float]):
13
13
  The xfine motor can move the slit in x direction at high frequencies for different scans.
14
14
  These slits can be scanned continously or in step mode.
15
15
  The relationship between the three motors is as follows:
16
- - arc - position of the middle of the gap (coarse/ macro) extension
16
+ - arc - position of the middle of the gap in degrees (coarse/ macro) extension
17
17
  - gap - width in mm, provides energy resolution
18
- - xfine selects the energy as part of the high frequency scan
18
+ - xfine - main scanning axis in mm, selects the energy as part of the high frequency scan
19
19
  """
20
20
 
21
21
  def __init__(self, prefix: str, name: str = ""):
@@ -20,6 +20,6 @@ def lookup_table_adjuster(
20
20
  def adjust(group=None) -> Generator[Msg, None, None]:
21
21
  setpoint = lookup_table(input)
22
22
  LOGGER.info(f"lookup_table_adjuster setting {output_device.name} to {setpoint}")
23
- yield from bps.abs_set(output_device, setpoint, group=group) # type: ignore # See: https://github.com/DiamondLightSource/dodal/issues/827
23
+ yield from bps.abs_set(output_device, setpoint, group=group)
24
24
 
25
25
  return adjust
@@ -51,6 +51,10 @@ class I24Axes:
51
51
  VGON_YH = EncEnum.ENC4
52
52
 
53
53
 
54
+ class I23Axes:
55
+ OMEGA = EncEnum.ENC1
56
+
57
+
54
58
  class RotationDirection(StrictEnum):
55
59
  """
56
60
  Defines for a swept angle whether the scan width (sweep) is to be added or subtracted from
@@ -89,7 +89,7 @@ class ZebraMapping(ZebraMappingValidations):
89
89
  # Which of the Zebra's four AND gates is used to control the automatic shutter, if it's being used.
90
90
  # After defining, the correct GateControl device can be accessed with, eg,
91
91
  # zebra.logic_gates.and_gates[zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER]. Set to -1 if not being used.
92
- AND_GATE_FOR_AUTO_SHUTTER: int = Field(default=-1, ge=-1, le=4)
92
+ AND_GATE_FOR_AUTO_SHUTTER: int = Field(default=2, ge=-1, le=4)
93
93
 
94
94
 
95
95
  class UnmappedZebraException(Exception):
@@ -12,10 +12,7 @@ __all__ = [
12
12
  "XrcResult",
13
13
  "ZocaloTrigger",
14
14
  "get_full_processing_results",
15
- "ZOCALO_READING_PLAN_NAME",
16
15
  "NoResultsFromZocalo",
17
16
  "NoZocaloSubscription",
18
17
  "ZocaloStartInfo",
19
18
  ]
20
-
21
- ZOCALO_READING_PLAN_NAME = "zocalo reading"
@@ -1,7 +1,6 @@
1
1
  import asyncio
2
2
  from collections.abc import Generator, Sequence
3
3
  from enum import Enum
4
- from inspect import get_annotations
5
4
  from queue import Empty, Queue
6
5
  from typing import Any, TypedDict
7
6
 
@@ -47,7 +46,6 @@ class ZocaloSource(str, Enum):
47
46
 
48
47
  DEFAULT_TIMEOUT = 180
49
48
  DEFAULT_SORT_KEY = SortKeys.max_count
50
- ZOCALO_READING_PLAN_NAME = "zocalo reading"
51
49
  CLEAR_QUEUE_WAIT_S = 2.0
52
50
  ZOCALO_STAGE_GROUP = "clear zocalo queue"
53
51
 
@@ -389,12 +387,12 @@ def get_full_processing_results(
389
387
  """A plan that will return the raw zocalo results, ranked in descending order according to the sort key.
390
388
  Returns empty list in the event no results found."""
391
389
  LOGGER.info("Retrieving raw zocalo processing results")
392
- com = yield from bps.rd(zocalo.centre_of_mass, default_value=[]) # type: ignore
393
- max_voxel = yield from bps.rd(zocalo.max_voxel, default_value=[]) # type: ignore
394
- max_count = yield from bps.rd(zocalo.max_count, default_value=[]) # type: ignore
395
- n_voxels = yield from bps.rd(zocalo.n_voxels, default_value=[]) # type: ignore
396
- total_count = yield from bps.rd(zocalo.total_count, default_value=[]) # type: ignore
397
- bounding_box = yield from bps.rd(zocalo.bounding_box, default_value=[]) # type: ignore
390
+ com = yield from bps.rd(zocalo.centre_of_mass, default_value=[])
391
+ max_voxel = yield from bps.rd(zocalo.max_voxel, default_value=[])
392
+ max_count = yield from bps.rd(zocalo.max_count, default_value=[])
393
+ n_voxels = yield from bps.rd(zocalo.n_voxels, default_value=[])
394
+ total_count = yield from bps.rd(zocalo.total_count, default_value=[])
395
+ bounding_box = yield from bps.rd(zocalo.bounding_box, default_value=[])
398
396
  return [
399
397
  _corrected_xrc_result(
400
398
  XrcResult(
@@ -410,27 +408,3 @@ def get_full_processing_results(
410
408
  com, max_voxel, max_count, n_voxels, total_count, bounding_box, strict=True
411
409
  )
412
410
  ]
413
-
414
-
415
- def get_processing_results_from_event(
416
- device_name: str, doc: dict
417
- ) -> Sequence[XrcResult]:
418
- """
419
- Decode an event document into the corresponding x-ray centring results
420
-
421
- Args:
422
- doc A bluesky event document containing the signals read from the ZocaloResults
423
- device_name The device name prefix to prepend to the document keys
424
-
425
- Returns:
426
- The list of XrcResults decoded from the event document
427
- """
428
- results_keys = get_annotations(XrcResult).keys()
429
- results_dict = {k: doc["data"][f"{device_name}-{k}"] for k in results_keys}
430
- results_values = [results_dict[k].tolist() for k in results_keys]
431
-
432
- def create_result(*argv):
433
- kwargs = dict(zip(results_keys, argv, strict=False))
434
- return XrcResult(**kwargs)
435
-
436
- return list(map(create_result, *results_values))
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
@@ -33,7 +33,11 @@ def attach_data_session_metadata_wrapper(
33
33
  Yields:
34
34
  Iterator[Msg]: Plan messages
35
35
  """
36
- provider = provider or get_path_provider()
36
+ try:
37
+ provider = provider or get_path_provider()
38
+ except NameError:
39
+ provider = None
40
+
37
41
  if isinstance(provider, UpdatingPathProvider):
38
42
  yield from bps.wait_for([provider.update])
39
43
  ress = yield from bps.wait_for([provider.data_session])
@@ -41,6 +45,11 @@ def attach_data_session_metadata_wrapper(
41
45
  # https://github.com/DiamondLightSource/dodal/issues/452
42
46
  # As part of 452, write each dataCollection into their own folder, then can use resource_dir directly
43
47
  yield from bpp.inject_md_wrapper(plan, md={DATA_SESSION: data_session})
48
+ elif provider is None:
49
+ logging.warning(
50
+ f"There is no PathProvider set, {attach_data_session_metadata_wrapper.__name__} will have no effect"
51
+ )
52
+ yield from plan
44
53
  else:
45
54
  logging.warning(
46
55
  f"{provider} is not an UpdatingPathProvider, {attach_data_session_metadata_wrapper.__name__} will have no effect"
@@ -0,0 +1,3 @@
1
+ from .configure_driver import configure_analyser, configure_specs, configure_vgscienta
2
+
3
+ __all__ = ["configure_analyser", "configure_specs", "configure_vgscienta"]