dls-dodal 1.52.0__py3-none-any.whl → 1.54.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 (53) hide show
  1. {dls_dodal-1.52.0.dist-info → dls_dodal-1.54.0.dist-info}/METADATA +4 -6
  2. {dls_dodal-1.52.0.dist-info → dls_dodal-1.54.0.dist-info}/RECORD +53 -47
  3. dodal/_version.py +2 -2
  4. dodal/beamlines/__init__.py +1 -0
  5. dodal/beamlines/adsim.py +12 -11
  6. dodal/beamlines/b01_1.py +2 -2
  7. dodal/beamlines/b07.py +6 -3
  8. dodal/beamlines/b07_1.py +17 -6
  9. dodal/beamlines/b21.py +4 -4
  10. dodal/beamlines/i04.py +12 -7
  11. dodal/beamlines/i05.py +22 -0
  12. dodal/beamlines/i05_1.py +17 -0
  13. dodal/beamlines/i09.py +7 -3
  14. dodal/beamlines/i09_1.py +6 -3
  15. dodal/beamlines/i19_1.py +1 -1
  16. dodal/beamlines/i20_1.py +38 -9
  17. dodal/beamlines/p60.py +13 -3
  18. dodal/common/beamlines/beamline_parameters.py +1 -1
  19. dodal/devices/b07/__init__.py +2 -2
  20. dodal/devices/b07/enums.py +15 -0
  21. dodal/devices/b07_1/__init__.py +10 -1
  22. dodal/devices/b07_1/ccmc.py +79 -0
  23. dodal/devices/b07_1/enums.py +3 -0
  24. dodal/devices/electron_analyser/abstract/base_driver_io.py +25 -48
  25. dodal/devices/electron_analyser/abstract/base_region.py +9 -11
  26. dodal/devices/electron_analyser/abstract/types.py +12 -0
  27. dodal/devices/electron_analyser/specs/detector.py +9 -9
  28. dodal/devices/electron_analyser/specs/driver_io.py +54 -21
  29. dodal/devices/electron_analyser/specs/region.py +13 -8
  30. dodal/devices/electron_analyser/types.py +15 -6
  31. dodal/devices/electron_analyser/vgscienta/detector.py +18 -8
  32. dodal/devices/electron_analyser/vgscienta/driver_io.py +62 -24
  33. dodal/devices/electron_analyser/vgscienta/region.py +33 -16
  34. dodal/devices/i05/__init__.py +3 -0
  35. dodal/devices/i05/enums.py +8 -0
  36. dodal/devices/i09/__init__.py +2 -2
  37. dodal/devices/i09/enums.py +16 -0
  38. dodal/devices/i09_1/__init__.py +2 -2
  39. dodal/devices/i09_1/enums.py +13 -0
  40. dodal/devices/i13_1/merlin_controller.py +1 -1
  41. dodal/devices/i19/beamstop.py +2 -2
  42. dodal/devices/i24/aperture.py +1 -1
  43. dodal/devices/oav/oav_to_redis_forwarder.py +1 -1
  44. dodal/devices/oav/pin_image_recognition/__init__.py +1 -2
  45. dodal/devices/p60/__init__.py +8 -2
  46. dodal/devices/p60/enums.py +16 -0
  47. dodal/devices/robot.py +6 -3
  48. dodal/devices/tetramm.py +1 -2
  49. dodal/utils.py +3 -10
  50. {dls_dodal-1.52.0.dist-info → dls_dodal-1.54.0.dist-info}/WHEEL +0 -0
  51. {dls_dodal-1.52.0.dist-info → dls_dodal-1.54.0.dist-info}/entry_points.txt +0 -0
  52. {dls_dodal-1.52.0.dist-info → dls_dodal-1.54.0.dist-info}/licenses/LICENSE +0 -0
  53. {dls_dodal-1.52.0.dist-info → dls_dodal-1.54.0.dist-info}/top_level.txt +0 -0
@@ -22,27 +22,36 @@ from dodal.devices.electron_analyser.vgscienta.detector import (
22
22
 
23
23
  AnyAcqMode = StrictEnum
24
24
  AnyLensMode = SupersetEnum | StrictEnum
25
+ AnyPsuMode = SupersetEnum | StrictEnum
26
+ AnyPassEnergy = StrictEnum | float
27
+ AnyPassEnergyEnum = StrictEnum
25
28
 
26
29
  # Electron analyser types that encompasses all implementations, useful for tests and
27
30
  # plans
28
31
  ElectronAnalyserDetectorImpl = (
29
- VGScientaDetector[AnyLensMode] | SpecsDetector[AnyLensMode]
32
+ VGScientaDetector[AnyLensMode, AnyPsuMode, AnyPassEnergyEnum]
33
+ | SpecsDetector[AnyLensMode, AnyPsuMode]
30
34
  )
31
35
  ElectronAnalyserDriverImpl = (
32
- VGScientaAnalyserDriverIO[AnyLensMode] | SpecsAnalyserDriverIO[AnyLensMode]
36
+ VGScientaAnalyserDriverIO[AnyLensMode, AnyPsuMode, AnyPassEnergyEnum]
37
+ | SpecsAnalyserDriverIO[AnyLensMode, AnyPsuMode]
33
38
  )
34
39
 
35
40
  # Short hand the type so less verbose
36
- AbstractBaseRegion = AbstractBaseRegion[AnyAcqMode, AnyLensMode]
41
+ AbstractBaseRegion = AbstractBaseRegion[AnyAcqMode, AnyLensMode, AnyPassEnergy]
37
42
 
38
43
  # Generic electron analyser types that supports full typing with the abstract classes.
39
44
  GenericElectronAnalyserDetector = ElectronAnalyserDetector[
40
- AbstractAnalyserDriverIO[AbstractBaseRegion, AnyAcqMode, AnyLensMode],
41
- AbstractBaseSequence[AbstractBaseRegion, AnyLensMode],
45
+ AbstractAnalyserDriverIO[
46
+ AbstractBaseRegion, AnyAcqMode, AnyLensMode, AnyPsuMode, AnyPassEnergy
47
+ ],
48
+ AbstractBaseSequence[AbstractBaseRegion],
42
49
  AbstractBaseRegion,
43
50
  ]
44
51
 
45
52
  GenericElectronAnalyserRegionDetector = ElectronAnalyserRegionDetector[
46
- AbstractAnalyserDriverIO[AbstractBaseRegion, AnyAcqMode, AnyLensMode],
53
+ AbstractAnalyserDriverIO[
54
+ AbstractBaseRegion, AnyAcqMode, AnyLensMode, AnyPsuMode, AnyPassEnergy
55
+ ],
47
56
  AbstractBaseRegion,
48
57
  ]
@@ -3,7 +3,11 @@ from typing import Generic
3
3
 
4
4
  from ophyd_async.core import SignalR
5
5
 
6
- from dodal.devices.electron_analyser.abstract.base_region import TLensMode
6
+ from dodal.devices.electron_analyser.abstract.types import (
7
+ TLensMode,
8
+ TPassEnergyEnum,
9
+ TPsuMode,
10
+ )
7
11
  from dodal.devices.electron_analyser.detector import (
8
12
  ElectronAnalyserDetector,
9
13
  )
@@ -18,20 +22,26 @@ from dodal.devices.electron_analyser.vgscienta.region import (
18
22
 
19
23
  class VGScientaDetector(
20
24
  ElectronAnalyserDetector[
21
- VGScientaAnalyserDriverIO[TLensMode],
22
- VGScientaSequence[TLensMode],
23
- VGScientaRegion[TLensMode],
25
+ VGScientaAnalyserDriverIO[TLensMode, TPsuMode, TPassEnergyEnum],
26
+ VGScientaSequence[TLensMode, TPsuMode, TPassEnergyEnum],
27
+ VGScientaRegion[TLensMode, TPassEnergyEnum],
24
28
  ],
25
- Generic[TLensMode],
29
+ Generic[TLensMode, TPsuMode, TPassEnergyEnum],
26
30
  ):
27
31
  def __init__(
28
32
  self,
29
33
  prefix: str,
30
34
  lens_mode_type: type[TLensMode],
35
+ psu_mode_type: type[TPsuMode],
36
+ pass_energy_type: type[TPassEnergyEnum],
31
37
  energy_sources: Mapping[str, SignalR[float]],
32
38
  name: str = "",
33
39
  ):
34
- driver = VGScientaAnalyserDriverIO[TLensMode](
35
- prefix, lens_mode_type, energy_sources
40
+ driver = VGScientaAnalyserDriverIO[TLensMode, TPsuMode, TPassEnergyEnum](
41
+ prefix, lens_mode_type, psu_mode_type, pass_energy_type, energy_sources
42
+ )
43
+ super().__init__(
44
+ VGScientaSequence[lens_mode_type, psu_mode_type, pass_energy_type],
45
+ driver,
46
+ name,
36
47
  )
37
- super().__init__(VGScientaSequence[lens_mode_type], driver, name)
@@ -15,54 +15,96 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
15
15
  from dodal.devices.electron_analyser.abstract.base_driver_io import (
16
16
  AbstractAnalyserDriverIO,
17
17
  )
18
- from dodal.devices.electron_analyser.abstract.base_region import TLensMode
18
+ from dodal.devices.electron_analyser.abstract.types import (
19
+ TLensMode,
20
+ TPassEnergyEnum,
21
+ TPsuMode,
22
+ )
19
23
  from dodal.devices.electron_analyser.util import to_kinetic_energy
20
- from dodal.devices.electron_analyser.vgscienta.enums import AcquisitionMode
21
- from dodal.devices.electron_analyser.vgscienta.region import (
24
+ from dodal.devices.electron_analyser.vgscienta.enums import (
25
+ AcquisitionMode,
22
26
  DetectorMode,
27
+ )
28
+ from dodal.devices.electron_analyser.vgscienta.region import (
23
29
  VGScientaRegion,
24
30
  )
25
31
 
26
32
 
27
33
  class VGScientaAnalyserDriverIO(
28
- AbstractAnalyserDriverIO[VGScientaRegion, AcquisitionMode, TLensMode],
29
- Generic[TLensMode],
34
+ AbstractAnalyserDriverIO[
35
+ VGScientaRegion[TLensMode, TPassEnergyEnum],
36
+ AcquisitionMode,
37
+ TLensMode,
38
+ TPsuMode,
39
+ TPassEnergyEnum,
40
+ ],
41
+ Generic[TLensMode, TPsuMode, TPassEnergyEnum],
30
42
  ):
31
43
  def __init__(
32
44
  self,
33
45
  prefix: str,
34
46
  lens_mode_type: type[TLensMode],
47
+ psu_mode_type: type[TPsuMode],
48
+ pass_energy_type: type[TPassEnergyEnum],
35
49
  energy_sources: Mapping[str, SignalR[float]],
36
50
  name: str = "",
37
51
  ) -> None:
38
52
  with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
39
53
  # Used for setting up region data acquisition.
40
- self.centre_energy = epics_signal_rw(float, prefix + "CENTRE_ENERGY")
41
- self.first_x_channel = epics_signal_rw(int, prefix + "MinX")
42
- self.first_y_channel = epics_signal_rw(int, prefix + "MinY")
43
- self.x_channel_size = epics_signal_rw(int, prefix + "SizeX")
44
- self.y_channel_size = epics_signal_rw(int, prefix + "SizeY")
45
54
  self.detector_mode = epics_signal_rw(DetectorMode, prefix + "DETECTOR_MODE")
46
55
 
47
- super().__init__(prefix, AcquisitionMode, lens_mode_type, energy_sources, name)
56
+ self.region_min_x = epics_signal_rw(int, prefix + "MinX")
57
+ self.region_size_x = epics_signal_rw(int, prefix + "SizeX")
58
+ self.sensor_max_size_x = epics_signal_rw(int, prefix + "MaxSizeX")
59
+
60
+ self.region_min_y = epics_signal_rw(int, prefix + "MinY")
61
+ self.region_size_y = epics_signal_rw(int, prefix + "SizeY")
62
+ self.sensor_max_size_y = epics_signal_rw(int, prefix + "MaxSizeY")
63
+
64
+ super().__init__(
65
+ prefix,
66
+ AcquisitionMode,
67
+ lens_mode_type,
68
+ psu_mode_type,
69
+ pass_energy_type,
70
+ energy_sources,
71
+ name,
72
+ )
48
73
 
49
74
  @AsyncStatus.wrap
50
- async def set(self, region: VGScientaRegion[TLensMode]):
51
- await super().set(region)
75
+ async def set(self, region: VGScientaRegion[TLensMode, TPassEnergyEnum]):
76
+ source = self._get_energy_source(region.excitation_energy_source)
77
+ excitation_energy = await source.get_value() # eV
52
78
 
53
- excitation_energy = await self.excitation_energy.get_value()
79
+ low_energy = to_kinetic_energy(
80
+ region.low_energy, region.energy_mode, excitation_energy
81
+ )
54
82
  centre_energy = to_kinetic_energy(
55
- region.fix_energy, region.energy_mode, excitation_energy
83
+ region.centre_energy, region.energy_mode, excitation_energy
84
+ )
85
+ high_energy = to_kinetic_energy(
86
+ region.high_energy, region.energy_mode, excitation_energy
56
87
  )
57
88
  await asyncio.gather(
89
+ self.region_name.set(region.name),
90
+ self.energy_mode.set(region.energy_mode),
91
+ self.low_energy.set(low_energy),
58
92
  self.centre_energy.set(centre_energy),
93
+ self.high_energy.set(high_energy),
94
+ self.slices.set(region.slices),
95
+ self.lens_mode.set(region.lens_mode),
96
+ self.pass_energy.set(region.pass_energy),
97
+ self.iterations.set(region.iterations),
98
+ self.acquisition_mode.set(region.acquisition_mode),
99
+ self.excitation_energy.set(excitation_energy),
100
+ self.excitation_energy_source.set(source.name),
59
101
  self.energy_step.set(region.energy_step),
60
- self.first_x_channel.set(region.first_x_channel),
61
- self.first_y_channel.set(region.first_y_channel),
62
- self.x_channel_size.set(region.x_channel_size()),
63
- self.y_channel_size.set(region.y_channel_size()),
64
- self.detector_mode.set(region.detector_mode),
65
102
  self.image_mode.set(ADImageMode.SINGLE),
103
+ self.detector_mode.set(region.detector_mode),
104
+ self.region_min_x.set(region.min_x),
105
+ self.region_size_x.set(region.size_x),
106
+ self.region_min_y.set(region.min_y),
107
+ self.region_size_y.set(region.size_y),
66
108
  )
67
109
 
68
110
  def _create_energy_axis_signal(self, prefix: str) -> SignalR[Array1D[np.float64]]:
@@ -70,7 +112,3 @@ class VGScientaAnalyserDriverIO(
70
112
 
71
113
  def _create_angle_axis_signal(self, prefix: str) -> SignalR[Array1D[np.float64]]:
72
114
  return epics_signal_r(Array1D[np.float64], prefix + "Y_SCALE_RBV")
73
-
74
- @property
75
- def pass_energy_type(self) -> type:
76
- return str
@@ -1,13 +1,17 @@
1
1
  import uuid
2
2
  from typing import Generic
3
3
 
4
- from pydantic import Field
4
+ from pydantic import Field, field_validator
5
5
 
6
6
  from dodal.devices.electron_analyser.abstract.base_region import (
7
7
  AbstractBaseRegion,
8
8
  AbstractBaseSequence,
9
9
  JavaToPythonModel,
10
+ )
11
+ from dodal.devices.electron_analyser.abstract.types import (
10
12
  TLensMode,
13
+ TPassEnergyEnum,
14
+ TPsuMode,
11
15
  )
12
16
  from dodal.devices.electron_analyser.vgscienta.enums import (
13
17
  AcquisitionMode,
@@ -17,33 +21,43 @@ from dodal.devices.electron_analyser.vgscienta.enums import (
17
21
 
18
22
 
19
23
  class VGScientaRegion(
20
- AbstractBaseRegion[AcquisitionMode, TLensMode], Generic[TLensMode]
24
+ AbstractBaseRegion[AcquisitionMode, TLensMode, TPassEnergyEnum],
25
+ Generic[TLensMode, TPassEnergyEnum],
21
26
  ):
22
27
  # Override defaults of base region class
23
28
  lens_mode: TLensMode
24
- pass_energy: int = 5
29
+ pass_energy: TPassEnergyEnum
25
30
  acquisition_mode: AcquisitionMode = AcquisitionMode.SWEPT
26
31
  low_energy: float = 8.0
27
32
  high_energy: float = 10.0
28
33
  step_time: float = 1.0
29
34
  energy_step: float = Field(default=200.0)
35
+ centre_energy: float = Field(alias="fix_energy", default=9)
30
36
  # Specific to this class
31
37
  id: str = Field(default=str(uuid.uuid4()), alias="region_id")
32
- fix_energy: float = 9.0
33
38
  total_steps: float = 13.0
34
39
  total_time: float = 13.0
35
- first_x_channel: int = 1
36
- last_x_channel: int = 1000
37
- first_y_channel: int = 101
38
- last_y_channel: int = 800
40
+ min_x: int = Field(alias="first_x_channel", default=1)
41
+ sensor_max_size_x: int = Field(alias="last_x_channel", default=1000)
42
+ min_y: int = Field(alias="first_y_channel", default=101)
43
+ sensor_max_size_y: int = Field(alias="last_y_channel", default=800)
39
44
  detector_mode: DetectorMode = DetectorMode.ADC
40
45
  status: Status = Status.READY
41
46
 
42
- def x_channel_size(self) -> int:
43
- return self.last_x_channel - self.first_x_channel + 1
47
+ @property
48
+ def size_x(self) -> int:
49
+ return self.sensor_max_size_x - self.min_x + 1
50
+
51
+ @property
52
+ def size_y(self) -> int:
53
+ return self.sensor_max_size_y - self.min_y + 1
44
54
 
45
- def y_channel_size(self) -> int:
46
- return self.last_y_channel - self.first_y_channel + 1
55
+ @field_validator("pass_energy", mode="before")
56
+ @classmethod
57
+ def validate_pass_energy(cls, val):
58
+ # This is needed because if the value is a number, it can't be casted to the
59
+ # enum correctly.
60
+ return str(val)
47
61
 
48
62
 
49
63
  class VGScientaExcitationEnergySource(JavaToPythonModel):
@@ -53,16 +67,19 @@ class VGScientaExcitationEnergySource(JavaToPythonModel):
53
67
 
54
68
 
55
69
  class VGScientaSequence(
56
- AbstractBaseSequence[VGScientaRegion, TLensMode], Generic[TLensMode]
70
+ AbstractBaseSequence[VGScientaRegion[TLensMode, TPassEnergyEnum]],
71
+ Generic[TLensMode, TPsuMode, TPassEnergyEnum],
57
72
  ):
58
- element_set: str = Field(default="Unknown")
73
+ psu_mode: TPsuMode = Field(alias="element_set")
59
74
  excitation_energy_sources: list[VGScientaExcitationEnergySource] = Field(
60
75
  default_factory=lambda: []
61
76
  )
62
- regions: list[VGScientaRegion[TLensMode]] = Field(default_factory=lambda: [])
77
+ regions: list[VGScientaRegion[TLensMode, TPassEnergyEnum]] = Field(
78
+ default_factory=lambda: []
79
+ )
63
80
 
64
81
  def get_excitation_energy_source_by_region(
65
- self, region: VGScientaRegion[TLensMode]
82
+ self, region: VGScientaRegion[TLensMode, TPassEnergyEnum]
66
83
  ) -> VGScientaExcitationEnergySource:
67
84
  value = next(
68
85
  (
@@ -0,0 +1,3 @@
1
+ from dodal.devices.i05.enums import Grating
2
+
3
+ __all__ = ["Grating"]
@@ -0,0 +1,8 @@
1
+ from ophyd_async.core import StrictEnum
2
+
3
+
4
+ class Grating(StrictEnum):
5
+ PT_400 = "400 lines/mm"
6
+ C_1600 = "C 1600 lines/mm"
7
+ RH_1600 = "Rh 1600 lines/mm"
8
+ PT_800 = "B 800 lines/mm"
@@ -1,4 +1,4 @@
1
1
  from dodal.devices.i09.dcm import DCM
2
- from dodal.devices.i09.enums import Grating, LensMode
2
+ from dodal.devices.i09.enums import Grating, LensMode, PassEnergy, PsuMode
3
3
 
4
- __all__ = ["DCM", "Grating", "LensMode"]
4
+ __all__ = ["DCM", "Grating", "LensMode", "PsuMode", "PassEnergy"]
@@ -13,3 +13,19 @@ class LensMode(StrictEnum):
13
13
  ANGULAR60 = "Angular60"
14
14
  ANGULAR56 = "Angular56"
15
15
  ANGULAR45VUV = "Angular45VUV"
16
+
17
+
18
+ class PsuMode(StrictEnum):
19
+ HIGH = "High"
20
+ LOW = "Low"
21
+
22
+
23
+ class PassEnergy(StrictEnum):
24
+ E5 = 5
25
+ E10 = 10
26
+ E20 = 20
27
+ E50 = 50
28
+ E70 = 70
29
+ E100 = 100
30
+ E200 = 200
31
+ E500 = 500
@@ -1,3 +1,3 @@
1
- from .enums import LensMode
1
+ from .enums import LensMode, PsuMode
2
2
 
3
- __all__ = ["LensMode"]
3
+ __all__ = ["LensMode", "PsuMode"]
@@ -16,4 +16,17 @@ class LensMode(SupersetEnum):
16
16
  LOW_MAGNIFICATION = "LowMagnification"
17
17
  HIGH_MAGNIFICATION2 = "HighMagnification2"
18
18
  RAMP_MODE = "RampMode"
19
+ # This is connected to the device separately and will only have "Not connected" as
20
+ # option if disconnected. Once it is connected, "Not connected" is replaced with the
21
+ # options above. This is also why this must be a SupersetEnum.
22
+ NOT_CONNECTED = "Not connected"
23
+
24
+
25
+ class PsuMode(SupersetEnum):
26
+ V3500 = "3.5kV"
27
+ V1500 = "1.5kV"
28
+ V400 = "400V"
29
+ # This is connected to the device separately and will only have "Not connected" as
30
+ # option if disconnected. Once it is connected, "Not connected" is replaced with the
31
+ # options above. This is also why this must be a SupersetEnum.
19
32
  NOT_CONNECTED = "Not connected"
@@ -12,8 +12,8 @@ from ophyd_async.epics.adcore import (
12
12
  ADBaseIO,
13
13
  ADImageMode,
14
14
  ADState,
15
- stop_busy_record,
16
15
  )
16
+ from ophyd_async.epics.core import stop_busy_record
17
17
 
18
18
 
19
19
  class MerlinController(ADBaseController):
@@ -6,7 +6,7 @@ from dodal.devices.motors import XYZStage
6
6
 
7
7
  class HomeGroup(StrictEnum):
8
8
  NONE = "none"
9
- ALL = "All"
9
+ ALL = "ALL"
10
10
  X = "X"
11
11
  Y = "Y"
12
12
  Z = "Z"
@@ -23,4 +23,4 @@ class BeamStop(XYZStage):
23
23
  def __init__(self, prefix: str, name: str = "") -> None:
24
24
  self.homing = HomingControl(f"{prefix}HM", name)
25
25
 
26
- super().__init__(name)
26
+ super().__init__(prefix, name)
@@ -22,4 +22,4 @@ class Aperture(XYStage):
22
22
 
23
23
  def __init__(self, prefix: str, name: str = "") -> None:
24
24
  self.position = epics_signal_rw(AperturePositions, prefix + "MP:SELECT")
25
- super().__init__(name)
25
+ super().__init__(prefix, name)
@@ -110,11 +110,11 @@ class OAVToRedisForwarder(StandardReadable, Flyable, Stoppable):
110
110
  pickled numpy array of pixel values but raw byes are more space efficient. There
111
111
  may be better ways of doing this, see https://github.com/DiamondLightSource/mx-bluesky/issues/592"""
112
112
  jpeg_bytes = await get_next_jpeg(response)
113
- self.uuid_setter(redis_uuid)
114
113
  sample_id = await self.sample_id.get_value()
115
114
  redis_key = f"murko:{sample_id}:raw"
116
115
  await self.redis_client.hset(redis_key, redis_uuid, jpeg_bytes) # type: ignore
117
116
  await self.redis_client.expire(redis_key, timedelta(days=self.DATA_EXPIRY_DAYS))
117
+ self.uuid_setter(redis_uuid)
118
118
 
119
119
  async def _open_connection_and_do_function(
120
120
  self, function_to_do: Callable[[ClientResponse, OAVSource], Awaitable]
@@ -1,4 +1,3 @@
1
- import asyncio
2
1
  import time
3
2
 
4
3
  import numpy as np
@@ -160,7 +159,7 @@ class PinTipDetection(StandardReadable):
160
159
  )
161
160
  else:
162
161
  break
163
- except asyncio.exceptions.TimeoutError:
162
+ except TimeoutError:
164
163
  LOGGER.error(
165
164
  f"No tip found in {await self.validity_timeout.get_value()} seconds."
166
165
  )
@@ -1,4 +1,10 @@
1
- from .enums import LensMode
1
+ from .enums import LensMode, PassEnergy, PsuMode
2
2
  from .lab_xray_source import LabXraySource, LabXraySourceReadable
3
3
 
4
- __all__ = ["LensMode", "LabXraySource", "LabXraySourceReadable"]
4
+ __all__ = [
5
+ "LensMode",
6
+ "PsuMode",
7
+ "PassEnergy",
8
+ "LabXraySource",
9
+ "LabXraySourceReadable",
10
+ ]
@@ -8,3 +8,19 @@ class LensMode(StrictEnum):
8
8
  ANGULAR30 = "Angular30"
9
9
  ANGULAR30_SMALLSPOT = "Angular30_SmallSpot"
10
10
  ANGULAR14_SMALLSPOT = "Angular14_SmallSpot"
11
+
12
+
13
+ class PsuMode(StrictEnum):
14
+ HIGH = "High Pass (XPS)"
15
+ LOW = "Low Pass (UPS)"
16
+
17
+
18
+ class PassEnergy(StrictEnum):
19
+ E1 = 1
20
+ E2 = 2
21
+ E5 = 5
22
+ E10 = 10
23
+ E20 = 20
24
+ E50 = 50
25
+ E100 = 100
26
+ E200 = 200
dodal/devices/robot.py CHANGED
@@ -21,6 +21,9 @@ from ophyd_async.epics.core import (
21
21
 
22
22
  from dodal.log import LOGGER
23
23
 
24
+ WAIT_FOR_OLD_PIN_MSG = "Waiting on old pin unloaded"
25
+ WAIT_FOR_NEW_PIN_MSG = "Waiting on new pin loaded"
26
+
24
27
 
25
28
  class RobotLoadFailed(Exception):
26
29
  error_code: int
@@ -144,6 +147,7 @@ class BartRobot(StandardReadable, Movable[SampleLocation]):
144
147
  # in the current task, when it propagates to here we should cancel all pending tasks before bubbling up
145
148
  for task in tasks:
146
149
  task.cancel()
150
+
147
151
  raise
148
152
 
149
153
  async def _load_pin_and_puck(self, sample_location: SampleLocation):
@@ -164,9 +168,9 @@ class BartRobot(StandardReadable, Movable[SampleLocation]):
164
168
  )
165
169
  await self.load.trigger()
166
170
  if await self.gonio_pin_sensor.get_value() == PinMounted.PIN_MOUNTED:
167
- LOGGER.info("Waiting on old pin unloaded")
171
+ LOGGER.info(WAIT_FOR_OLD_PIN_MSG)
168
172
  await wait_for_value(self.gonio_pin_sensor, PinMounted.NO_PIN_MOUNTED, None)
169
- LOGGER.info("Waiting on new pin loaded")
173
+ LOGGER.info(WAIT_FOR_NEW_PIN_MSG)
170
174
 
171
175
  await self.pin_mounted_or_no_pin_found()
172
176
 
@@ -178,7 +182,6 @@ class BartRobot(StandardReadable, Movable[SampleLocation]):
178
182
  timeout=self.LOAD_TIMEOUT + self.NOT_BUSY_TIMEOUT,
179
183
  )
180
184
  except TimeoutError as e:
181
- # Will only need to catch asyncio.TimeoutError after https://github.com/bluesky/ophyd-async/issues/572
182
185
  await self.prog_error.raise_if_error(e)
183
186
  await self.controller_error.raise_if_error(e)
184
187
  raise RobotLoadFailed(0, "Robot timed out") from e
dodal/devices/tetramm.py CHANGED
@@ -22,9 +22,8 @@ from ophyd_async.epics.adcore import (
22
22
  NDArrayBaseIO,
23
23
  NDFileHDFIO,
24
24
  NDPluginBaseIO,
25
- stop_busy_record,
26
25
  )
27
- from ophyd_async.epics.core import PvSuffix
26
+ from ophyd_async.epics.core import PvSuffix, stop_busy_record
28
27
 
29
28
 
30
29
  class TetrammRange(StrictEnum):
dodal/utils.py CHANGED
@@ -426,16 +426,9 @@ def is_v2_device_type(obj: type[Any]) -> bool:
426
426
  # This is all very badly documented and possibly prone to change in future versions of Python
427
427
  non_parameterized_class = obj.__origin__
428
428
  if non_parameterized_class:
429
- try:
430
- return non_parameterized_class and issubclass(
431
- non_parameterized_class, OphydV2Device
432
- )
433
- except TypeError:
434
- # Python 3.10 will return inspect.isclass(t) == True but then
435
- # raise TypeError: issubclass() arg 1 must be a class
436
- # when inspecting device_factory decorator function itself
437
- # Later versions of Python seem not to be affected
438
- pass
429
+ return non_parameterized_class and issubclass(
430
+ non_parameterized_class, OphydV2Device
431
+ )
439
432
 
440
433
  return False
441
434