dls-dodal 1.68.0__py3-none-any.whl → 1.69.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 (57) hide show
  1. {dls_dodal-1.68.0.dist-info → dls_dodal-1.69.0.dist-info}/METADATA +1 -31
  2. {dls_dodal-1.68.0.dist-info → dls_dodal-1.69.0.dist-info}/RECORD +57 -49
  3. dodal/_version.py +2 -2
  4. dodal/beamlines/adsim.py +30 -23
  5. dodal/beamlines/i02_1.py +14 -42
  6. dodal/beamlines/i02_2.py +5 -11
  7. dodal/beamlines/i03.py +4 -1
  8. dodal/beamlines/i03_supervisor.py +19 -0
  9. dodal/beamlines/i04.py +74 -179
  10. dodal/beamlines/i05.py +8 -0
  11. dodal/beamlines/i06_1.py +24 -0
  12. dodal/beamlines/i09.py +53 -9
  13. dodal/beamlines/i09_1.py +8 -0
  14. dodal/beamlines/i09_2.py +4 -4
  15. dodal/beamlines/i16.py +11 -0
  16. dodal/beamlines/i20_1.py +14 -0
  17. dodal/beamlines/i21.py +12 -4
  18. dodal/beamlines/i23.py +19 -25
  19. dodal/beamlines/i24.py +55 -105
  20. dodal/beamlines/p60.py +11 -1
  21. dodal/common/__init__.py +2 -1
  22. dodal/common/maths.py +80 -0
  23. dodal/devices/eiger.py +29 -14
  24. dodal/devices/electron_analyser/base/__init__.py +3 -3
  25. dodal/devices/electron_analyser/base/base_controller.py +19 -8
  26. dodal/devices/electron_analyser/base/base_enums.py +0 -5
  27. dodal/devices/electron_analyser/base/base_region.py +2 -1
  28. dodal/devices/electron_analyser/base/energy_sources.py +27 -26
  29. dodal/devices/electron_analyser/specs/specs_detector.py +7 -6
  30. dodal/devices/electron_analyser/vgscienta/vgscienta_detector.py +7 -6
  31. dodal/devices/fast_shutter.py +108 -25
  32. dodal/devices/i04/beam_centre.py +84 -0
  33. dodal/devices/i04/max_pixel.py +4 -17
  34. dodal/devices/i04/murko_results.py +18 -3
  35. dodal/devices/i10/i10_apple2.py +6 -6
  36. dodal/devices/i17/i17_apple2.py +6 -6
  37. dodal/devices/i24/commissioning_jungfrau.py +9 -10
  38. dodal/devices/insertion_device/__init__.py +12 -8
  39. dodal/devices/insertion_device/apple2_controller.py +380 -0
  40. dodal/devices/insertion_device/apple2_undulator.py +152 -531
  41. dodal/devices/insertion_device/energy.py +88 -0
  42. dodal/devices/insertion_device/energy_motor_lookup.py +1 -1
  43. dodal/devices/insertion_device/lookup_table_models.py +2 -2
  44. dodal/devices/insertion_device/polarisation.py +36 -0
  45. dodal/devices/oav/oav_detector.py +66 -1
  46. dodal/devices/oav/utils.py +17 -0
  47. dodal/devices/robot.py +35 -18
  48. dodal/devices/selectable_source.py +38 -0
  49. dodal/devices/zebra/zebra.py +15 -0
  50. dodal/devices/zebra/zebra_constants_mapping.py +1 -0
  51. dodal/plans/configure_arm_trigger_and_disarm_detector.py +0 -1
  52. dodal/testing/__init__.py +0 -0
  53. {dls_dodal-1.68.0.dist-info → dls_dodal-1.69.0.dist-info}/WHEEL +0 -0
  54. {dls_dodal-1.68.0.dist-info → dls_dodal-1.69.0.dist-info}/entry_points.txt +0 -0
  55. {dls_dodal-1.68.0.dist-info → dls_dodal-1.69.0.dist-info}/licenses/LICENSE +0 -0
  56. {dls_dodal-1.68.0.dist-info → dls_dodal-1.69.0.dist-info}/top_level.txt +0 -0
  57. /dodal/devices/insertion_device/{id_enum.py → enum.py} +0 -0
dodal/beamlines/i20_1.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from pathlib import Path
2
2
 
3
3
  from ophyd_async.epics.motor import Motor
4
+ from ophyd_async.epics.pmac import PmacIO
4
5
  from ophyd_async.fastcs.panda import HDFPanda
5
6
 
6
7
  from dodal.common.beamlines.beamline_utils import (
@@ -58,6 +59,19 @@ def turbo_slit_x() -> Motor:
58
59
  return Motor(f"{PREFIX.beamline_prefix}-OP-PCHRO-01:TS:XFINE")
59
60
 
60
61
 
62
+ @device_factory()
63
+ def turbo_slit_pmac() -> PmacIO:
64
+ """
65
+ PMac controller using running fly scans with trajectory
66
+ """
67
+ motor = turbo_slit_x()
68
+ return PmacIO(
69
+ prefix=f"{PREFIX.beamline_prefix}-MO-STEP-06:",
70
+ raw_motors=[motor],
71
+ coord_nums=[3],
72
+ )
73
+
74
+
61
75
  @device_factory()
62
76
  def panda() -> HDFPanda:
63
77
  return HDFPanda(
dodal/beamlines/i21.py CHANGED
@@ -24,6 +24,9 @@ from dodal.devices.insertion_device.energy_motor_lookup import (
24
24
  from dodal.devices.insertion_device.lookup_table_models import LookupTableColumnConfig
25
25
  from dodal.devices.pgm import PlaneGratingMonochromator
26
26
  from dodal.devices.synchrotron import Synchrotron
27
+ from dodal.devices.temperture_controller import (
28
+ Lakeshore336,
29
+ )
27
30
  from dodal.log import set_beamline as set_log_beamline
28
31
  from dodal.utils import BeamlinePrefix, get_beamline_name
29
32
 
@@ -71,18 +74,18 @@ def id_phase() -> UndulatorPhaseAxes:
71
74
 
72
75
 
73
76
  @device_factory()
74
- def id() -> Apple2:
77
+ def id() -> Apple2[UndulatorPhaseAxes]:
75
78
  """I21 insertion device."""
76
- return Apple2(
79
+ return Apple2[UndulatorPhaseAxes](
77
80
  id_gap=id_gap(),
78
81
  id_phase=id_phase(),
79
82
  )
80
83
 
81
84
 
82
85
  @device_factory()
83
- def id_controller() -> Apple2EnforceLHMoveController:
86
+ def id_controller() -> Apple2EnforceLHMoveController[UndulatorPhaseAxes]:
84
87
  """i21 insertion device controller."""
85
- return Apple2EnforceLHMoveController(
88
+ return Apple2EnforceLHMoveController[UndulatorPhaseAxes](
86
89
  apple2=id(),
87
90
  gap_energy_motor_lut=ConfigServerEnergyMotorLookup(
88
91
  lut_config=LookupTableColumnConfig(grating=I21_GRATING_COLUMNS),
@@ -114,3 +117,8 @@ def id_polarisation() -> InsertionDevicePolarisation:
114
117
  def energy_jid() -> BeamEnergy:
115
118
  """Beam energy."""
116
119
  return BeamEnergy(id_energy=id_energy(), mono=pgm().energy)
120
+
121
+
122
+ @device_factory()
123
+ def sample_temperature_controller() -> Lakeshore336:
124
+ return Lakeshore336(prefix=f"{PREFIX.beamline_prefix}-EA-TCTRL-01:")
dodal/beamlines/i23.py CHANGED
@@ -1,16 +1,13 @@
1
+ from functools import cache
1
2
  from pathlib import Path
2
3
 
3
- from ophyd_async.core import InOut, StrictEnum
4
+ from ophyd_async.core import InOut, PathProvider, StrictEnum
4
5
  from ophyd_async.epics.adpilatus import PilatusDetector
5
6
 
6
- from dodal.common.beamlines.beamline_utils import (
7
- device_factory,
8
- get_path_provider,
9
- set_path_provider,
10
- )
11
7
  from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
12
8
  from dodal.common.beamlines.device_helpers import HDF5_SUFFIX
13
9
  from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider
10
+ from dodal.device_manager import DeviceManager
14
11
  from dodal.devices.motors import SixAxisGonio
15
12
  from dodal.devices.oav.pin_image_recognition import PinTipDetection
16
13
  from dodal.devices.positioner import Positioner1D
@@ -25,18 +22,22 @@ from dodal.log import set_beamline as set_log_beamline
25
22
  from dodal.utils import BeamlinePrefix, get_beamline_name, get_hostname
26
23
 
27
24
  BL = get_beamline_name("i23")
25
+ PREFIX = BeamlinePrefix(BL)
28
26
  set_log_beamline(BL)
29
27
  set_utils_beamline(BL)
30
28
 
31
- set_path_provider(
32
- StaticVisitPathProvider(
29
+ devices = DeviceManager()
30
+
31
+
32
+ @devices.fixture
33
+ @cache
34
+ def path_provider() -> PathProvider:
35
+ return StaticVisitPathProvider(
33
36
  BL,
34
37
  Path("/tmp"),
35
38
  client=LocalDirectoryServiceClient(),
36
39
  )
37
- )
38
40
 
39
- PREFIX = BeamlinePrefix(BL)
40
41
 
41
42
  I23_ZEBRA_MAPPING = ZebraMapping(
42
43
  outputs=ZebraTTLOutputs(TTL_DETECTOR=1, TTL_SHUTTER=4),
@@ -59,51 +60,44 @@ def _is_i23_machine():
59
60
  return hostname.startswith("i23-ws") or hostname.startswith("i23-control")
60
61
 
61
62
 
62
- @device_factory(skip=lambda: not _is_i23_machine())
63
+ @devices.factory(skip=lambda: not _is_i23_machine())
63
64
  def oav_pin_tip_detection() -> PinTipDetection:
64
- """Get the i23 OAV pin-tip detection device."""
65
-
66
65
  return PinTipDetection(
67
66
  f"{PREFIX.beamline_prefix}-DI-OAV-01:",
68
67
  "pin_tip_detection",
69
68
  )
70
69
 
71
70
 
72
- @device_factory()
71
+ @devices.factory()
73
72
  def shutter() -> ZebraShutter:
74
- """Get the i23 zebra controlled shutter."""
75
73
  return ZebraShutter(f"{PREFIX.beamline_prefix}-EA-SHTR-01:")
76
74
 
77
75
 
78
- @device_factory()
76
+ @devices.factory()
79
77
  def gonio() -> SixAxisGonio:
80
- """Get the i23 goniometer"""
81
78
  return SixAxisGonio(f"{PREFIX.beamline_prefix}-MO-GONIO-01:")
82
79
 
83
80
 
84
- @device_factory()
81
+ @devices.factory()
85
82
  def zebra() -> Zebra:
86
- """Get the i23 zebra"""
87
83
  return Zebra(
88
84
  prefix=f"{PREFIX.beamline_prefix}-EA-ZEBRA-01:ZEBRA:",
89
85
  mapping=I23_ZEBRA_MAPPING,
90
86
  )
91
87
 
92
88
 
93
- @device_factory()
94
- def pilatus() -> PilatusDetector:
95
- """Get the i23 pilatus"""
89
+ @devices.factory()
90
+ def pilatus(path_provider: PathProvider) -> PilatusDetector:
96
91
  return PilatusDetector(
97
92
  prefix=f"{PREFIX.beamline_prefix}-EA-PILAT-01:",
98
- path_provider=get_path_provider(),
93
+ path_provider=path_provider,
99
94
  drv_suffix="cam1:",
100
95
  fileio_suffix=HDF5_SUFFIX,
101
96
  )
102
97
 
103
98
 
104
- @device_factory()
99
+ @devices.factory()
105
100
  def detector_motion() -> Positioner1D[I23DetectorPositions]:
106
- """Get the i23 detector"""
107
101
  return Positioner1D[I23DetectorPositions](
108
102
  f"{PREFIX.beamline_prefix}-EA-DET-01:Z",
109
103
  datatype=I23DetectorPositions,
dodal/beamlines/i24.py CHANGED
@@ -1,12 +1,12 @@
1
- from pathlib import PurePath
1
+ from functools import cache
2
+ from pathlib import Path
2
3
 
3
- from ophyd_async.core import AutoIncrementingPathProvider, StaticFilenameProvider
4
+ from ophyd_async.core import AutoMaxIncrementingPathProvider, PathProvider
4
5
 
5
- from dodal.common.beamlines.beamline_utils import (
6
- BL,
7
- device_factory,
8
- )
6
+ from dodal.common.beamlines.beamline_utils import BL
9
7
  from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
8
+ from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider
9
+ from dodal.device_manager import DeviceManager
10
10
  from dodal.devices.attenuator.attenuator import EnumFilterAttenuator
11
11
  from dodal.devices.attenuator.filter_selections import (
12
12
  I24FilterOneSelections,
@@ -25,12 +25,14 @@ from dodal.devices.i24.vgonio import VerticalGoniometer
25
25
  from dodal.devices.motors import YZStage
26
26
  from dodal.devices.oav.oav_detector import OAVBeamCentreFile
27
27
  from dodal.devices.oav.oav_parameters import OAVConfigBeamCentre
28
+ from dodal.devices.synchrotron import Synchrotron
28
29
  from dodal.devices.zebra.zebra import Zebra
29
30
  from dodal.devices.zebra.zebra_constants_mapping import (
30
31
  ZebraMapping,
31
32
  ZebraSources,
32
33
  ZebraTTLOutputs,
33
34
  )
35
+ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
34
36
  from dodal.log import set_beamline as set_log_beamline
35
37
  from dodal.utils import BeamlinePrefix, get_beamline_name
36
38
 
@@ -45,111 +47,67 @@ set_log_beamline(BL)
45
47
  set_utils_beamline(BL)
46
48
 
47
49
  I24_ZEBRA_MAPPING = ZebraMapping(
48
- outputs=ZebraTTLOutputs(TTL_EIGER=1, TTL_PILATUS=2, TTL_FAST_SHUTTER=4),
50
+ outputs=ZebraTTLOutputs(TTL_EIGER=1, TTL_JUNGFRAU=2, TTL_FAST_SHUTTER=4),
49
51
  sources=ZebraSources(),
50
52
  )
51
53
 
52
54
  PREFIX = BeamlinePrefix(BL)
53
55
 
56
+ devices = DeviceManager()
57
+
58
+
59
+ @devices.fixture
60
+ @cache
61
+ def path_provider() -> PathProvider:
62
+ return StaticVisitPathProvider(
63
+ BL,
64
+ Path("/tmp"),
65
+ client=LocalDirectoryServiceClient(),
66
+ )
67
+
54
68
 
55
- @device_factory()
69
+ @devices.factory()
56
70
  def attenuator() -> EnumFilterAttenuator:
57
- """Get a read-only attenuator device for i24, instantiate it if it hasn't already
58
- been. If this is called when already instantiated in i24, it will return the
59
- existing object."""
60
71
  return EnumFilterAttenuator(
61
72
  f"{PREFIX.beamline_prefix}-OP-ATTN-01:",
62
73
  filter_selection=(I24FilterOneSelections, I24FilterTwoSelections),
63
74
  )
64
75
 
65
76
 
66
- @device_factory()
77
+ @devices.factory()
67
78
  def aperture() -> Aperture:
68
- """Get the i24 aperture device, instantiate it if it hasn't already been.
69
- If this is called when already instantiated in i24, it will return the existing object.
70
- """
71
- return Aperture(
72
- f"{PREFIX.beamline_prefix}-AL-APTR-01:",
73
- )
79
+ return Aperture(f"{PREFIX.beamline_prefix}-AL-APTR-01:")
74
80
 
75
81
 
76
- @device_factory()
82
+ @devices.factory()
77
83
  def beamstop() -> Beamstop:
78
- """Get the i24 beamstop device, instantiate it if it hasn't already been.
79
- If this is called when already instantiated in i24, it will return the existing object.
80
- """
81
- return Beamstop(
82
- f"{PREFIX.beamline_prefix}-MO-BS-01:",
83
- )
84
+ return Beamstop(f"{PREFIX.beamline_prefix}-MO-BS-01:")
84
85
 
85
86
 
86
- @device_factory()
87
+ @devices.factory()
87
88
  def backlight() -> DualBacklight:
88
- """Get the i24 backlight device, instantiate it if it hasn't already been.
89
- If this is called when already instantiated in i24, it will return the existing object.
90
- """
91
- return DualBacklight(
92
- prefix=PREFIX.beamline_prefix,
93
- )
89
+ return DualBacklight(prefix=PREFIX.beamline_prefix)
94
90
 
95
91
 
96
- @device_factory()
92
+ @devices.factory()
97
93
  def detector_motion() -> YZStage:
98
- """Get the i24 detector motion device, instantiate it if it hasn't already been.
99
- If this is called when already instantiated in i24, it will return the existing object.
100
- """
101
- return YZStage(
102
- prefix=f"{PREFIX.beamline_prefix}-EA-DET-01:",
103
- )
94
+ return YZStage(prefix=f"{PREFIX.beamline_prefix}-EA-DET-01:")
104
95
 
105
96
 
106
- @device_factory()
97
+ @devices.factory()
107
98
  def dcm() -> DCM:
108
- """Get the i24 DCM device, instantiate it if it hasn't already been.
109
- If this is called when already instantiated in i24, it will return the existing object.
110
- """
111
99
  return DCM(
112
100
  prefix=f"{PREFIX.beamline_prefix}-DI-DCM-01:",
113
101
  motion_prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:",
114
102
  )
115
103
 
116
104
 
117
- # TODO implement ophyd-async eiger see
118
- # https://github.com/DiamondLightSource/mx-bluesky/issues/62
119
- # @skip_device(lambda: BL == "s24")
120
- # def eiger(
121
- # wait_for_connection: bool = True,
122
- # fake_with_ophyd_sim: bool = False,
123
- # params: DetectorParams | None = None,
124
- # ) -> EigerDetector:
125
- # """Get the i24 Eiger device, instantiate it if it hasn't already been.
126
- # If this is called when already instantiated, it will return the existing object.
127
- # If called with params, will update those params to the Eiger object.
128
- # """
129
- #
130
- # def set_params(eiger: EigerDetector):
131
- # if params is not None:
132
- # eiger.set_detector_parameters(params)
133
- #
134
- # return device_instantiation(
135
- # device_factory=EigerDetector,
136
- # name="eiger",
137
- # prefix="-EA-EIGER-01:",
138
- # wait=wait_for_connection,
139
- # fake=fake_with_ophyd_sim,
140
- # post_create=set_params,
141
- # )
142
-
143
-
144
- @device_factory()
105
+ @devices.factory()
145
106
  def pmac() -> PMAC:
146
- """Get the i24 PMAC device, instantiate it if it hasn't already been.
147
- If this is called when already instantiated in i24, it will return the existing object.
148
- """
149
107
  return PMAC(PREFIX.beamline_prefix)
150
108
 
151
109
 
152
- @device_factory()
110
+ @devices.factory()
153
111
  def oav() -> OAVBeamCentreFile:
154
112
  return OAVBeamCentreFile(
155
113
  prefix=f"{PREFIX.beamline_prefix}-DI-OAV-01:",
@@ -157,62 +115,54 @@ def oav() -> OAVBeamCentreFile:
157
115
  )
158
116
 
159
117
 
160
- @device_factory()
118
+ @devices.factory()
161
119
  def vgonio() -> VerticalGoniometer:
162
- """Get the i24 vertical goniometer device, instantiate it if it hasn't already been.
163
- If this is called when already instantiated, it will return the existing object.
164
- """
165
120
  return VerticalGoniometer(f"{PREFIX.beamline_prefix}-MO-VGON-01:")
166
121
 
167
122
 
168
- @device_factory()
123
+ @devices.factory()
169
124
  def zebra() -> Zebra:
170
- """Get the i24 zebra device, instantiate it if it hasn't already been.
171
- If this is called when already instantiated in i24, it will return the existing object.
172
- """
173
125
  return Zebra(
174
126
  prefix=f"{PREFIX.beamline_prefix}-EA-ZEBRA-01:",
175
127
  mapping=I24_ZEBRA_MAPPING,
176
128
  )
177
129
 
178
130
 
179
- @device_factory()
131
+ @devices.factory()
180
132
  def shutter() -> HutchShutter:
181
- """Get the i24 hutch shutter device, instantiate it if it hasn't already been.
182
- If this is called when already instantiated, it will return the existing object.
183
- """
184
133
  return HutchShutter(f"{PREFIX.beamline_prefix}-PS-SHTR-01:")
185
134
 
186
135
 
187
- @device_factory()
136
+ @devices.factory()
188
137
  def focus_mirrors() -> FocusMirrorsMode:
189
- """Get the i24 focus mirror devise to find the beam size."""
190
138
  return FocusMirrorsMode(f"{PREFIX.beamline_prefix}-OP-MFM-01:")
191
139
 
192
140
 
193
- @device_factory()
141
+ @devices.factory()
194
142
  def eiger_beam_center() -> DetectorBeamCenter:
195
- """A device for setting/reading the beamcenter from the eiger on i24."""
196
- return DetectorBeamCenter(
197
- f"{PREFIX.beamline_prefix}-EA-EIGER-01:CAM:",
198
- "eiger_bc",
199
- )
143
+ return DetectorBeamCenter(f"{PREFIX.beamline_prefix}-EA-EIGER-01:CAM:", "eiger_bc")
200
144
 
201
145
 
202
- @device_factory()
146
+ @devices.factory()
203
147
  def commissioning_jungfrau(
204
- path_to_dir: str = "/tmp/jf", # Device factory doesn't allow for required args,
205
- filename: str = "jf_output", # but these should be manually entered when commissioning
148
+ path_provider: PathProvider,
206
149
  ) -> CommissioningJungfrau:
207
150
  """Get the commissionning Jungfrau 9M device, which uses a temporary filewriter
208
- device in place of Odin while the detector is in commissioning.
209
- Instantiates the device if it hasn't already been.
210
- If this is called when already instantiated, it will return the existing object."""
211
-
151
+ device in place of Odin while the detector is in commissioning."""
212
152
  return CommissioningJungfrau(
213
153
  f"{PREFIX.beamline_prefix}-EA-JFRAU-01:",
214
154
  f"{PREFIX.beamline_prefix}-JUNGFRAU-META:FD:",
215
- AutoIncrementingPathProvider(
216
- StaticFilenameProvider(filename), PurePath(path_to_dir)
217
- ),
155
+ AutoMaxIncrementingPathProvider(path_provider),
156
+ )
157
+
158
+
159
+ @devices.factory()
160
+ def synchrotron() -> Synchrotron:
161
+ return Synchrotron()
162
+
163
+
164
+ @devices.factory()
165
+ def sample_shutter() -> ZebraShutter:
166
+ return ZebraShutter(
167
+ f"{PREFIX.beamline_prefix}-EA-SHTR-01:",
218
168
  )
dodal/beamlines/p60.py CHANGED
@@ -11,6 +11,7 @@ from dodal.devices.p60 import (
11
11
  PassEnergy,
12
12
  PsuMode,
13
13
  )
14
+ from dodal.devices.selectable_source import SourceSelector
14
15
  from dodal.log import set_beamline as set_log_beamline
15
16
  from dodal.utils import BeamlinePrefix, get_beamline_name
16
17
 
@@ -20,6 +21,11 @@ set_log_beamline(BL)
20
21
  set_utils_beamline(BL)
21
22
 
22
23
 
24
+ @device_factory()
25
+ def source_selector() -> SourceSelector:
26
+ return SourceSelector()
27
+
28
+
23
29
  @device_factory()
24
30
  def al_kalpha_source() -> LabXraySourceReadable:
25
31
  return LabXraySourceReadable(LabXraySource.AL_KALPHA)
@@ -32,7 +38,11 @@ def mg_kalpha_source() -> LabXraySourceReadable:
32
38
 
33
39
  @device_factory()
34
40
  def energy_source() -> DualEnergySource:
35
- return DualEnergySource(al_kalpha_source().energy_ev, mg_kalpha_source().energy_ev)
41
+ return DualEnergySource(
42
+ al_kalpha_source().energy_ev,
43
+ mg_kalpha_source().energy_ev,
44
+ source_selector().selected_source,
45
+ )
36
46
 
37
47
 
38
48
  # Connect will work again after this work completed
dodal/common/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from .coordination import group_uuid, inject
2
2
  from .enums import EnabledDisabledUpper, InOutUpper, OnOffUpper
3
- from .maths import in_micros, step_to_num
3
+ from .maths import Rectangle2D, in_micros, step_to_num
4
4
  from .types import MsgGenerator, PlanGenerator
5
5
 
6
6
  __all__ = [
@@ -13,4 +13,5 @@ __all__ = [
13
13
  "MsgGenerator",
14
14
  "PlanGenerator",
15
15
  "step_to_num",
16
+ "Rectangle2D",
16
17
  ]
dodal/common/maths.py CHANGED
@@ -48,3 +48,83 @@ def in_micros(t: float) -> int:
48
48
  if t < 0:
49
49
  raise ValueError(f"Expected a positive time in seconds, got {t!r}")
50
50
  return int(np.ceil(t * 1e6))
51
+
52
+
53
+ class Rectangle2D:
54
+ """
55
+ A 2D rectangle defined by two opposite corners.
56
+
57
+ This class represents a rectangle in 2D space using two points: (x1, y1) and (x2, y2).
58
+ It provides methods to query rectangle properties and check point containment.
59
+
60
+ Attributes:
61
+ x1 (float): The x-coordinate of the first corner.
62
+ y1 (float): The y-coordinate of the first corner.
63
+ x2 (float): The x-coordinate of the second corner.
64
+ y2 (float): The y-coordinate of the second corner.
65
+ """
66
+
67
+ def __init__(self, x1: float, y1: float, x2: float, y2: float):
68
+ """
69
+ Initialize a Rectangle2D with two corner points.
70
+
71
+ Args:
72
+ x1 (float): The x-coordinate of the first corner.
73
+ y1 (float): The y-coordinate of the first corner.
74
+ x2 (float): The x-coordinate of the second corner.
75
+ y2 (float): The y-coordinate of the second corner.
76
+ """
77
+ self.x1, self.y1 = x1, y1
78
+ self.x2, self.y2 = x2, y2
79
+
80
+ def get_max_x(self) -> float:
81
+ """
82
+ Get the maximum x-coordinate of the rectangle.
83
+
84
+ Returns:
85
+ float: The larger of the two x-coordinates (x1, x2).
86
+ """
87
+ return max(self.x1, self.x2)
88
+
89
+ def get_min_x(self) -> float:
90
+ """
91
+ Get the minimum x-coordinate of the rectangle.
92
+
93
+ Returns:
94
+ float: The smaller of the two x-coordinates (x1, x2).
95
+ """
96
+ return min(self.x1, self.x2)
97
+
98
+ def get_max_y(self) -> float:
99
+ """
100
+ Get the maximum y-coordinate of the rectangle.
101
+
102
+ Returns:
103
+ float: The larger of the two y-coordinates (y1, y2).
104
+ """
105
+ return max(self.y1, self.y2)
106
+
107
+ def get_min_y(self) -> float:
108
+ """
109
+ Get the minimum y-coordinate of the rectangle.
110
+
111
+ Returns:
112
+ float: The smaller of the two y-coordinates (y1, y2).
113
+ """
114
+ return min(self.y1, self.y2)
115
+
116
+ def contains(self, x: float, y: float) -> bool:
117
+ """
118
+ Check if a point is contained within the rectangle.
119
+
120
+ Args:
121
+ x (float): The x-coordinate of the point.
122
+ y (float): The y-coordinate of the point.
123
+
124
+ Returns:
125
+ bool: True if the point is within the rectangle bounds, False otherwise.
126
+ """
127
+ return (
128
+ self.get_min_x() <= x <= self.get_max_x()
129
+ and self.get_min_y() <= y <= self.get_max_y()
130
+ )
dodal/devices/eiger.py CHANGED
@@ -4,6 +4,7 @@ from enum import Enum
4
4
  from bluesky.protocols import Stageable
5
5
  from ophyd import Component, Device, EpicsSignalRO, Signal
6
6
  from ophyd.areadetector.cam import EigerDetectorCam
7
+ from ophyd.signal import AttributeSignal
7
8
  from ophyd.status import AndStatus, Status, StatusBase, SubscriptionStatus
8
9
 
9
10
  from dodal.devices.detector import DetectorParams, TriggerMode
@@ -55,7 +56,6 @@ class EigerDetector(Device, Stageable):
55
56
 
56
57
  stale_params = Component(EpicsSignalRO, "CAM:StaleParameters_RBV")
57
58
  bit_depth = Component(EpicsSignalRO, "CAM:BitDepthImage_RBV")
58
-
59
59
  filewriters_finished: StatusBase
60
60
 
61
61
  detector_params: DetectorParams | None = None
@@ -63,9 +63,24 @@ class EigerDetector(Device, Stageable):
63
63
  arming_status = Status()
64
64
  arming_status.set_finished()
65
65
 
66
- def __init__(self, beamline: str = "i03", *args, **kwargs):
66
+ def __init__(
67
+ self,
68
+ beamline: str = "i03",
69
+ ispyb_detector_id: int | None = None,
70
+ *args,
71
+ **kwargs,
72
+ ):
67
73
  super().__init__(*args, **kwargs)
68
74
  self.beamline = beamline
75
+
76
+ self.detector_id = ispyb_detector_id
77
+ self.ispyb_detector_id = AttributeSignal(
78
+ attr="detector_id",
79
+ parent=self,
80
+ name="eiger-ispyb_detector_id",
81
+ write_access=False,
82
+ )
83
+
69
84
  # using i03 timeouts as default
70
85
  self.timeouts = AVAILABLE_TIMEOUTS.get(beamline, AVAILABLE_TIMEOUTS["i03"])
71
86
  self.disarming_status = None
@@ -74,10 +89,11 @@ class EigerDetector(Device, Stageable):
74
89
  def with_params(
75
90
  cls,
76
91
  params: DetectorParams,
77
- name: str = "EigerDetector",
78
92
  beamline: str = "i03",
93
+ ispyb_detector_id: int | None = None,
94
+ name: str = "EigerDetector",
79
95
  ):
80
- det = cls(name=name, beamline=beamline)
96
+ det = cls(name=name, beamline=beamline, ispyb_detector_id=ispyb_detector_id)
81
97
  det.set_detector_parameters(params)
82
98
  return det
83
99
 
@@ -286,25 +302,24 @@ class EigerDetector(Device, Stageable):
286
302
  beam_x_pixels, beam_y_pixels = self.detector_params.get_beam_position_pixels(
287
303
  self.detector_params.detector_distance
288
304
  )
289
- status = self.cam.beam_center_x.set(
305
+ self.cam.beam_center_x.set(
290
306
  beam_x_pixels, timeout=self.timeouts.general_status_timeout
291
- )
292
- status &= self.cam.beam_center_y.set(
307
+ ).wait(timeout=self.timeouts.general_status_timeout)
308
+ self.cam.beam_center_y.set(
293
309
  beam_y_pixels, timeout=self.timeouts.general_status_timeout
294
- )
295
- status &= self.cam.det_distance.set(
310
+ ).wait(timeout=self.timeouts.general_status_timeout)
311
+ self.cam.det_distance.set(
296
312
  self.detector_params.detector_distance,
297
313
  timeout=self.timeouts.general_status_timeout,
298
- )
299
- status &= self.cam.omega_start.set(
314
+ ).wait(timeout=self.timeouts.general_status_timeout)
315
+ self.cam.omega_start.set(
300
316
  self.detector_params.omega_start,
301
317
  timeout=self.timeouts.general_status_timeout,
302
- )
303
- status &= self.cam.omega_incr.set(
318
+ ).wait(timeout=self.timeouts.general_status_timeout)
319
+ status = self.cam.omega_incr.set(
304
320
  self.detector_params.omega_increment,
305
321
  timeout=self.timeouts.general_status_timeout,
306
322
  )
307
-
308
323
  return status
309
324
 
310
325
  def set_detector_threshold(self, energy: float, tolerance: float = 0.1) -> Status:
@@ -15,7 +15,7 @@ from .base_driver_io import (
15
15
  GenericAnalyserDriverIO,
16
16
  TAbstractAnalyserDriverIO,
17
17
  )
18
- from .base_enums import EnergyMode, SelectedSource
18
+ from .base_enums import EnergyMode
19
19
  from .base_region import (
20
20
  AbstractBaseRegion,
21
21
  AbstractBaseSequence,
@@ -27,7 +27,7 @@ from .base_region import (
27
27
  TLensMode,
28
28
  )
29
29
  from .base_util import to_binding_energy, to_kinetic_energy
30
- from .energy_sources import DualEnergySource, EnergySource
30
+ from .energy_sources import AbstractEnergySource, DualEnergySource, EnergySource
31
31
 
32
32
  __all__ = [
33
33
  "ElectronAnalyserController",
@@ -42,7 +42,6 @@ __all__ = [
42
42
  "GenericAnalyserDriverIO",
43
43
  "TAbstractAnalyserDriverIO",
44
44
  "EnergyMode",
45
- "SelectedSource",
46
45
  "AbstractBaseRegion",
47
46
  "AbstractBaseSequence",
48
47
  "GenericRegion",
@@ -53,6 +52,7 @@ __all__ = [
53
52
  "TLensMode",
54
53
  "to_binding_energy",
55
54
  "to_kinetic_energy",
55
+ "AbstractEnergySource",
56
56
  "DualEnergySource",
57
57
  "EnergySource",
58
58
  ]