dls-dodal 1.36.1a0__py3-none-any.whl → 1.36.3__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 (36) hide show
  1. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/METADATA +2 -2
  2. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/RECORD +31 -33
  3. dodal/_version.py +2 -2
  4. dodal/beamlines/__init__.py +1 -0
  5. dodal/beamlines/adsim.py +75 -0
  6. dodal/beamlines/i10.py +74 -7
  7. dodal/beamlines/i24.py +17 -1
  8. dodal/devices/adsim.py +10 -10
  9. dodal/devices/aperture.py +0 -7
  10. dodal/devices/aperturescatterguard.py +79 -195
  11. dodal/devices/apple2_undulator.py +9 -9
  12. dodal/devices/attenuator.py +15 -5
  13. dodal/devices/focusing_mirror.py +12 -3
  14. dodal/devices/i10/i10_setting_data.py +3 -3
  15. dodal/devices/i10/mirrors.py +24 -0
  16. dodal/devices/i10/slits.py +37 -0
  17. dodal/devices/i24/dual_backlight.py +1 -0
  18. dodal/devices/i24/focus_mirrors.py +12 -12
  19. dodal/devices/linkam3.py +2 -2
  20. dodal/devices/oav/pin_image_recognition/__init__.py +2 -4
  21. dodal/devices/p99/sample_stage.py +15 -15
  22. dodal/devices/slits.py +29 -7
  23. dodal/devices/tetramm.py +16 -16
  24. dodal/devices/undulator_dcm.py +4 -0
  25. dodal/devices/util/test_utils.py +2 -2
  26. dodal/devices/xspress3/xspress3.py +3 -3
  27. dodal/devices/zebra.py +13 -13
  28. dodal/adsim.py +0 -17
  29. dodal/devices/areadetector/__init__.py +0 -10
  30. dodal/devices/areadetector/adaravis.py +0 -101
  31. dodal/devices/areadetector/adsim.py +0 -47
  32. dodal/devices/areadetector/adutils.py +0 -81
  33. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/LICENSE +0 -0
  34. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/WHEEL +0 -0
  35. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/entry_points.txt +0 -0
  36. {dls_dodal-1.36.1a0.dist-info → dls_dodal-1.36.3.dist-info}/top_level.txt +0 -0
dodal/devices/slits.py CHANGED
@@ -2,16 +2,38 @@ from ophyd_async.core import StandardReadable
2
2
  from ophyd_async.epics.motor import Motor
3
3
 
4
4
 
5
- class Slits(StandardReadable):
5
+ class MinimalSlits(StandardReadable):
6
+ """Gap only X Y slits."""
7
+
8
+ def __init__(
9
+ self,
10
+ prefix: str,
11
+ x_gap: str = "X:SIZE",
12
+ y_gap: str = "Y:SIZE",
13
+ name: str = "",
14
+ ) -> None:
15
+ with self.add_children_as_readables():
16
+ self.x_gap = Motor(prefix + x_gap)
17
+ self.y_gap = Motor(prefix + y_gap)
18
+ super().__init__(name=name)
19
+
20
+
21
+ class Slits(MinimalSlits):
6
22
  """
7
23
  Representation of a 4-blade set of slits. Allows control/readout of the gap
8
24
  between each pair of blades.
9
25
  """
10
26
 
11
- def __init__(self, prefix: str, name: str = "") -> None:
27
+ def __init__(
28
+ self,
29
+ prefix: str,
30
+ x_gap: str = "X:SIZE",
31
+ y_gap: str = "Y:SIZE",
32
+ x_centre: str = "X:CENTRE",
33
+ y_centre: str = "Y:CENTRE",
34
+ name: str = "",
35
+ ) -> None:
12
36
  with self.add_children_as_readables():
13
- self.x_gap = Motor(prefix + "X:SIZE")
14
- self.y_gap = Motor(prefix + "Y:SIZE")
15
- self.x_centre = Motor(prefix + "X:CENTRE")
16
- self.y_centre = Motor(prefix + "Y:CENTRE")
17
- super().__init__(name)
37
+ self.x_centre = Motor(prefix + x_centre)
38
+ self.y_centre = Motor(prefix + y_centre)
39
+ super().__init__(prefix=prefix, x_gap=x_gap, y_gap=y_gap, name=name)
dodal/devices/tetramm.py CHANGED
@@ -22,31 +22,31 @@ from ophyd_async.epics.core import (
22
22
 
23
23
 
24
24
  class TetrammRange(StrictEnum):
25
- uA = "+- 120 uA"
26
- nA = "+- 120 nA"
25
+ UA = "+- 120 uA"
26
+ NA = "+- 120 nA"
27
27
 
28
28
 
29
29
  class TetrammTrigger(StrictEnum):
30
- FreeRun = "Free run"
31
- ExtTrigger = "Ext. trig."
32
- ExtBulb = "Ext. bulb"
33
- ExtGate = "Ext. gate"
30
+ FREE_RUN = "Free run"
31
+ EXT_TRIGGER = "Ext. trig."
32
+ EXT_BULB = "Ext. bulb"
33
+ EXT_GATE = "Ext. gate"
34
34
 
35
35
 
36
36
  class TetrammChannels(StrictEnum):
37
- One = "1"
38
- Two = "2"
39
- Four = "4"
37
+ ONE = "1"
38
+ TWO = "2"
39
+ FOUR = "4"
40
40
 
41
41
 
42
42
  class TetrammResolution(StrictEnum):
43
- SixteenBits = "16 bits"
44
- TwentyFourBits = "24 bits"
43
+ SIXTEEN_BITS = "16 bits"
44
+ TWENTY_FOUR_BITS = "24 bits"
45
45
 
46
46
 
47
47
  class TetrammGeometry(StrictEnum):
48
- Diamond = "Diamond"
49
- Square = "Square"
48
+ DIAMOND = "Diamond"
49
+ SQUARE = "Square"
50
50
 
51
51
 
52
52
  class TetrammDriver(Device):
@@ -118,7 +118,7 @@ class TetrammController(DetectorController):
118
118
  assert trigger_info.livetime is not None
119
119
 
120
120
  # trigger mode must be set first and on its own!
121
- await self._drv.trigger_mode.set(TetrammTrigger.ExtTrigger)
121
+ await self._drv.trigger_mode.set(TetrammTrigger.EXT_TRIGGER)
122
122
 
123
123
  await asyncio.gather(
124
124
  self._drv.averaging_time.set(trigger_info.livetime),
@@ -134,8 +134,8 @@ class TetrammController(DetectorController):
134
134
 
135
135
  def _validate_trigger(self, trigger: DetectorTrigger) -> None:
136
136
  supported_trigger_types = {
137
- DetectorTrigger.edge_trigger,
138
- DetectorTrigger.constant_gate,
137
+ DetectorTrigger.EDGE_TRIGGER,
138
+ DetectorTrigger.CONSTANT_GATE,
139
139
  }
140
140
 
141
141
  if trigger not in supported_trigger_types:
@@ -5,6 +5,7 @@ from ophyd_async.core import AsyncStatus, StandardReadable
5
5
 
6
6
  from dodal.common.beamlines.beamline_parameters import get_beamline_parameters
7
7
 
8
+ from ..log import LOGGER
8
9
  from .dcm import DCM
9
10
  from .undulator import Undulator
10
11
 
@@ -59,3 +60,6 @@ class UndulatorDCM(StandardReadable, Movable):
59
60
  self.dcm.energy_in_kev.set(value, timeout=ENERGY_TIMEOUT_S),
60
61
  self.undulator.set(value),
61
62
  )
63
+ # DCM Perp pitch
64
+ LOGGER.info(f"Adjusting DCM offset to {self.dcm_fixed_offset_mm} mm")
65
+ await self.dcm.offset_in_mm.set(self.dcm_fixed_offset_mm)
@@ -1,8 +1,8 @@
1
- from ophyd_async.core import (
1
+ from ophyd_async.epics.motor import Motor
2
+ from ophyd_async.testing import (
2
3
  callback_on_mock_put,
3
4
  set_mock_value,
4
5
  )
5
- from ophyd_async.epics.motor import Motor
6
6
 
7
7
 
8
8
  def patch_motor(motor: Motor, initial_position=0):
@@ -26,12 +26,12 @@ class TriggerMode(StrictEnum):
26
26
  SOFTWARE = "Software"
27
27
  HARDWARE = "Hardware"
28
28
  BURST = "Burst"
29
- TTL_Veto_Only = "TTL Veto Only"
29
+ TTL_VETO_ONLY = "TTL Veto Only"
30
30
  IDC = "IDC"
31
31
  SOTWARE_START_STOP = "Software Start/Stop"
32
32
  TTL_BOTH = "TTL Both"
33
33
  LVDS_VETO_ONLY = "LVDS Veto Only"
34
- LVDS_both = "LVDS Both"
34
+ LVDS_BOTH = "LVDS Both"
35
35
 
36
36
 
37
37
  class UpdateRBV(StrictEnum):
@@ -49,7 +49,7 @@ class DetectorState(StrictEnum):
49
49
  ACQUIRE = "Acquire"
50
50
  READOUT = "Readout"
51
51
  CORRECT = "Correct"
52
- Saving = "Saving"
52
+ SAVING = "Saving"
53
53
  ABORTING = "Aborting"
54
54
  ERROR = "Error"
55
55
  WAITING = "Waiting"
dodal/devices/zebra.py CHANGED
@@ -59,25 +59,25 @@ class TrigSource(StrictEnum):
59
59
 
60
60
 
61
61
  class EncEnum(StrictEnum):
62
- Enc1 = "Enc1"
63
- Enc2 = "Enc2"
64
- Enc3 = "Enc3"
65
- Enc4 = "Enc4"
66
- Enc1_4Av = "Enc1-4Av"
62
+ ENC1 = "Enc1"
63
+ ENC2 = "Enc2"
64
+ ENC3 = "Enc3"
65
+ ENC4 = "Enc4"
66
+ ENC1_4AV = "Enc1-4Av"
67
67
 
68
68
 
69
69
  class I03Axes:
70
- SMARGON_X1 = EncEnum.Enc1
71
- SMARGON_Y = EncEnum.Enc2
72
- SMARGON_Z = EncEnum.Enc3
73
- OMEGA = EncEnum.Enc4
70
+ SMARGON_X1 = EncEnum.ENC1
71
+ SMARGON_Y = EncEnum.ENC2
72
+ SMARGON_Z = EncEnum.ENC3
73
+ OMEGA = EncEnum.ENC4
74
74
 
75
75
 
76
76
  class I24Axes:
77
- VGON_Z = EncEnum.Enc1
78
- OMEGA = EncEnum.Enc2
79
- VGON_X = EncEnum.Enc3
80
- VGON_YH = EncEnum.Enc4
77
+ VGON_Z = EncEnum.ENC1
78
+ OMEGA = EncEnum.ENC2
79
+ VGON_X = EncEnum.ENC3
80
+ VGON_YH = EncEnum.ENC4
81
81
 
82
82
 
83
83
  class RotationDirection(StrictEnum):
dodal/adsim.py DELETED
@@ -1,17 +0,0 @@
1
- import os
2
-
3
- from dodal.devices.adsim import SimStage
4
- from dodal.devices.areadetector import AdSimDetector
5
-
6
- from .utils import get_hostname
7
-
8
- # Default prefix to hostname unless overriden with export PREFIX=<prefix>
9
- PREFIX: str = os.environ.get("PREFIX", get_hostname())
10
-
11
-
12
- def stage(name: str = "sim_motors") -> SimStage:
13
- return SimStage(name=name, prefix=f"{PREFIX}-MO-SIM-01:")
14
-
15
-
16
- def det(name: str = "adsim") -> AdSimDetector:
17
- return AdSimDetector(name=name, prefix=f"{PREFIX}-AD-SIM-01:")
@@ -1,10 +0,0 @@
1
- from .adaravis import AdAravisDetector
2
- from .adsim import AdSimDetector
3
- from .adutils import Hdf5Writer, SynchronisedAdDriverBase
4
-
5
- __all__ = [
6
- "AdSimDetector",
7
- "SynchronisedAdDriverBase",
8
- "Hdf5Writer",
9
- "AdAravisDetector",
10
- ]
@@ -1,101 +0,0 @@
1
- from collections.abc import Mapping
2
- from typing import Any
3
-
4
- from ophyd import EpicsSignal, Signal
5
- from ophyd.areadetector.base import ADComponent as Cpt
6
- from ophyd.areadetector.detectors import DetectorBase
7
-
8
- from .adutils import Hdf5Writer, SingleTriggerV33, SynchronisedAdDriverBase
9
-
10
- _ACQUIRE_BUFFER_PERIOD = 0.2
11
-
12
-
13
- class AdAravisDetector(SingleTriggerV33, DetectorBase):
14
- cam = Cpt(SynchronisedAdDriverBase, suffix="DET:")
15
- hdf = Cpt(
16
- Hdf5Writer,
17
- suffix="HDF5:",
18
- root="",
19
- write_path_template="",
20
- )
21
- _priming_settings: Mapping[Signal, Any]
22
-
23
- def __init__(self, *args, **kwargs) -> None:
24
- super().__init__(*args, **kwargs)
25
- self.hdf.kind = "normal"
26
-
27
- # Values for staging
28
- self.stage_sigs = {
29
- # Get stage to wire up the plugins
30
- self.hdf.nd_array_port: self.cam.port_name.get(),
31
- # Reset array counter on stage
32
- self.cam.array_counter: 0,
33
- # Set image mode to multiple on stage so we have the option, can still
34
- # set num_images to 1
35
- self.cam.image_mode: "Multiple",
36
- # For now, this Ophyd device does not support hardware
37
- # triggered scanning, disable on stage
38
- self.cam.trigger_mode: "Off",
39
- **self.stage_sigs, # type: ignore
40
- }
41
-
42
- # Settings to apply when priming plugins during pre-stage
43
- self._priming_settings = {
44
- self.hdf.enable: 1,
45
- self.hdf.nd_array_port: self.cam.port_name.get(),
46
- self.cam.array_callbacks: 1,
47
- self.cam.image_mode: "Single",
48
- self.cam.trigger_mode: "Off",
49
- # Take the quickest possible frame
50
- self.cam.acquire_time: 6.3e-05,
51
- self.cam.acquire_period: 0.003,
52
- }
53
-
54
- # Signals that control driver and hdf writer should be put_complete to
55
- # avoid race conditions during priming
56
- for signal in set(self.stage_sigs.keys()).union(
57
- set(self._priming_settings.keys())
58
- ):
59
- if isinstance(signal, EpicsSignal):
60
- signal.put_complete = True
61
- self.cam.acquire.put_complete = True
62
-
63
- def stage(self, *args, **kwargs) -> list[object]:
64
- # We have to manually set the acquire period bcause the EPICS driver
65
- # doesn't do it for us. If acquire time is a staged signal, we use the
66
- # stage value to calculate the acquire period, otherwise we perform
67
- # a caget and use the current acquire time.
68
- if self.cam.acquire_time in self.stage_sigs:
69
- acquire_time = self.stage_sigs[self.cam.acquire_time]
70
- else:
71
- acquire_time = self.cam.acquire_time.get()
72
- self.stage_sigs[self.cam.acquire_period] = (
73
- float(acquire_time) + _ACQUIRE_BUFFER_PERIOD
74
- )
75
-
76
- # Ensure detector warmed up
77
- self._prime_hdf()
78
-
79
- # Now calling the super method should set the acquire period
80
- return super().stage(*args, **kwargs)
81
-
82
- def _prime_hdf(self) -> None:
83
- """
84
- Take a single frame and pipe it through the HDF5 writer plugin
85
- """
86
-
87
- # Backup state and ensure we are not acquiring
88
- reset_to = {signal: signal.get() for signal in self._priming_settings.keys()}
89
- self.cam.acquire.set(0).wait(timeout=10)
90
-
91
- # Apply all settings for acquisition
92
- for signal, value in self._priming_settings.items():
93
- # Ensure that .wait really will wait until the PV is set including its RBV
94
- signal.set(value).wait(timeout=10)
95
-
96
- # Acquire a frame
97
- self.cam.acquire.set(1).wait(timeout=10)
98
-
99
- # Revert settings to previous values
100
- for signal, value in reversed(reset_to.items()):
101
- signal.set(value).wait(timeout=10)
@@ -1,47 +0,0 @@
1
- from ophyd.areadetector.base import ADComponent as Cpt
2
- from ophyd.areadetector.detectors import DetectorBase
3
-
4
- from .adutils import Hdf5Writer, SingleTriggerV33, SynchronisedAdDriverBase
5
-
6
-
7
- class AdSimDetector(SingleTriggerV33, DetectorBase):
8
- cam = Cpt(SynchronisedAdDriverBase, suffix="CAM:", lazy=True)
9
- hdf = Cpt(
10
- Hdf5Writer,
11
- suffix="HDF5:",
12
- root="",
13
- write_path_template="",
14
- lazy=True,
15
- )
16
-
17
- def __init__(self, *args, **kwargs) -> None:
18
- super().__init__(*args, **kwargs)
19
- self.hdf.kind = "normal"
20
-
21
- self.stage_sigs = {
22
- # Get stage to wire up the plugins
23
- self.hdf.nd_array_port: self.cam.port_name.get(),
24
- # Reset array counter on stage
25
- self.cam.array_counter: 0,
26
- # Set image mode to multiple on stage so we have the option, can still
27
- # set num_images to 1
28
- self.cam.image_mode: "Multiple",
29
- # For now, this Ophyd device does not support hardware
30
- # triggered scanning, disable on stage
31
- self.cam.trigger_mode: "Internal",
32
- **self.stage_sigs, # type: ignore
33
- }
34
-
35
- def stage(self, *args, **kwargs) -> list[object]:
36
- # We have to manually set the acquire period bcause the EPICS driver
37
- # doesn't do it for us. If acquire time is a staged signal, we use the
38
- # stage value to calculate the acquire period, otherwise we perform
39
- # a caget and use the current acquire time.
40
- if self.cam.acquire_time in self.stage_sigs:
41
- acquire_time = self.stage_sigs[self.cam.acquire_time]
42
- else:
43
- acquire_time = self.cam.acquire_time.get()
44
- self.stage_sigs[self.cam.acquire_period] = acquire_time
45
-
46
- # Now calling the super method should set the acquire period
47
- return super().stage(*args, **kwargs)
@@ -1,81 +0,0 @@
1
- import time as ttime
2
-
3
- from ophyd import Component as Cpt
4
- from ophyd import DetectorBase, Device, EpicsSignal, EpicsSignalRO, Staged
5
- from ophyd.areadetector import ADTriggerStatus, TriggerBase
6
- from ophyd.areadetector.cam import AreaDetectorCam
7
- from ophyd.areadetector.filestore_mixins import FileStoreHDF5, FileStoreIterativeWrite
8
- from ophyd.areadetector.plugins import HDF5Plugin
9
-
10
-
11
- class SingleTriggerV33(TriggerBase):
12
- _status_type = ADTriggerStatus
13
-
14
- def __init__(self, *args, image_name=None, **kwargs):
15
- super().__init__(*args, **kwargs)
16
- if image_name is None:
17
- # Ensure that this mixin is part of valid device with name
18
- assert isinstance(self, Device)
19
- image_name = "_".join([self.name, "image"])
20
- self._image_name = image_name
21
-
22
- def trigger(self):
23
- "Trigger one acquisition."
24
- if self._staged != Staged.yes:
25
- raise RuntimeError(
26
- "This detector is not ready to trigger."
27
- "Call the stage() method before triggering."
28
- )
29
-
30
- self._status = self._status_type(self)
31
-
32
- def _acq_done(*args, **kwargs):
33
- # TODO sort out if anything useful in here
34
- self._status._finished() # noqa: SLF001
35
-
36
- self._acquisition_signal.put(1, use_complete=True, callback=_acq_done)
37
- # Ensure that this mixin is part of valid Detector with generate_datum
38
- assert isinstance(self, DetectorBase)
39
- self.generate_datum(self._image_name, ttime.time())
40
- return self._status
41
-
42
-
43
- class SynchronisedAdDriverBase(AreaDetectorCam):
44
- """
45
- Base Ophyd device to control an AreaDetector driver and
46
- syncrhonise it on other AreaDetector plugins, even non-blocking ones.
47
- """
48
-
49
- adcore_version = Cpt(EpicsSignalRO, "ADCoreVersion_RBV", string=True, kind="config")
50
- driver_version = Cpt(EpicsSignalRO, "DriverVersion_RBV", string=True, kind="config")
51
- wait_for_plugins = Cpt(EpicsSignal, "WaitForPlugins", string=True, kind="config")
52
-
53
- def stage(self, *args, **kwargs):
54
- # Makes the detector allow non-blocking AD plugins but makes Ophyd use
55
- # the AcquireBusy PV to determine when an acquisition is complete
56
- self.ensure_nonblocking()
57
- return super().stage(*args, **kwargs)
58
-
59
- def ensure_nonblocking(self):
60
- self.stage_sigs["wait_for_plugins"] = "Yes"
61
- if self.parent is not None:
62
- for c in self.parent.component_names:
63
- cpt = getattr(self.parent, c)
64
- if cpt is self:
65
- continue
66
- if hasattr(cpt, "ensure_nonblocking"):
67
- cpt.ensure_nonblocking()
68
-
69
-
70
- # ophyd code to be removed, only used for adim
71
- # https://github.com/DiamondLightSource/dodal/issues/404
72
- class Hdf5Writer(HDF5Plugin, FileStoreHDF5, FileStoreIterativeWrite): # type: ignore
73
- """ """
74
-
75
- pool_max_buffers = None
76
- file_number_sync = None
77
- file_number_write = None
78
-
79
- def get_frames_per_point(self):
80
- assert isinstance(self.parent, DetectorBase)
81
- return self.parent.cam.num_images.get()