dls-dodal 1.67.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.
- {dls_dodal-1.67.0.dist-info → dls_dodal-1.69.0.dist-info}/METADATA +2 -32
- {dls_dodal-1.67.0.dist-info → dls_dodal-1.69.0.dist-info}/RECORD +79 -71
- dodal/_version.py +2 -2
- dodal/beamlines/adsim.py +30 -23
- dodal/beamlines/b07.py +1 -1
- dodal/beamlines/b07_1.py +1 -1
- dodal/beamlines/i02_1.py +14 -42
- dodal/beamlines/i02_2.py +5 -11
- dodal/beamlines/i03.py +4 -1
- dodal/beamlines/i03_supervisor.py +19 -0
- dodal/beamlines/i04.py +74 -179
- dodal/beamlines/i05.py +9 -1
- dodal/beamlines/i06.py +1 -1
- dodal/beamlines/i06_1.py +24 -0
- dodal/beamlines/i09.py +53 -9
- dodal/beamlines/i09_1.py +9 -1
- dodal/beamlines/i09_2.py +7 -6
- dodal/beamlines/i10_optics.py +1 -1
- dodal/beamlines/i16.py +34 -0
- dodal/beamlines/i17.py +1 -1
- dodal/beamlines/i20_1.py +14 -0
- dodal/beamlines/i21.py +71 -4
- dodal/beamlines/i23.py +19 -25
- dodal/beamlines/i24.py +55 -105
- dodal/beamlines/p60.py +12 -2
- dodal/common/__init__.py +2 -1
- dodal/common/maths.py +80 -0
- dodal/devices/eiger.py +44 -23
- dodal/devices/electron_analyser/__init__.py +0 -33
- dodal/devices/electron_analyser/base/__init__.py +58 -0
- dodal/devices/electron_analyser/base/base_controller.py +84 -0
- dodal/devices/electron_analyser/base/base_detector.py +214 -0
- dodal/devices/electron_analyser/{abstract → base}/base_driver_io.py +23 -42
- dodal/devices/electron_analyser/{enums.py → base/base_enums.py} +0 -5
- dodal/devices/electron_analyser/{abstract → base}/base_region.py +48 -11
- dodal/devices/electron_analyser/{util.py → base/base_util.py} +1 -1
- dodal/devices/electron_analyser/{energy_sources.py → base/energy_sources.py} +27 -26
- dodal/devices/electron_analyser/specs/__init__.py +4 -4
- dodal/devices/electron_analyser/specs/specs_detector.py +47 -0
- dodal/devices/electron_analyser/specs/{driver_io.py → specs_driver_io.py} +23 -26
- dodal/devices/electron_analyser/specs/{region.py → specs_region.py} +4 -3
- dodal/devices/electron_analyser/vgscienta/__init__.py +4 -4
- dodal/devices/electron_analyser/vgscienta/vgscienta_detector.py +53 -0
- dodal/devices/electron_analyser/vgscienta/{driver_io.py → vgscienta_driver_io.py} +25 -31
- dodal/devices/electron_analyser/vgscienta/{region.py → vgscienta_region.py} +6 -6
- dodal/devices/fast_shutter.py +108 -25
- dodal/devices/i04/beam_centre.py +84 -0
- dodal/devices/i04/max_pixel.py +4 -17
- dodal/devices/i04/murko_results.py +18 -3
- dodal/devices/i09_2_shared/i09_apple2.py +0 -72
- dodal/devices/i10/i10_apple2.py +7 -7
- dodal/devices/i17/i17_apple2.py +6 -6
- dodal/devices/i21/__init__.py +3 -1
- dodal/devices/i24/commissioning_jungfrau.py +9 -10
- dodal/devices/insertion_device/__init__.py +62 -0
- dodal/devices/insertion_device/apple2_controller.py +380 -0
- dodal/devices/insertion_device/apple2_undulator.py +152 -481
- dodal/devices/insertion_device/energy.py +88 -0
- dodal/devices/insertion_device/energy_motor_lookup.py +1 -1
- dodal/devices/insertion_device/enum.py +17 -0
- dodal/devices/insertion_device/lookup_table_models.py +66 -36
- dodal/devices/insertion_device/polarisation.py +36 -0
- dodal/devices/oav/oav_detector.py +66 -1
- dodal/devices/oav/utils.py +17 -0
- dodal/devices/robot.py +35 -18
- dodal/devices/selectable_source.py +38 -0
- dodal/devices/zebra/zebra.py +15 -0
- dodal/devices/zebra/zebra_constants_mapping.py +1 -0
- dodal/plans/configure_arm_trigger_and_disarm_detector.py +0 -1
- dodal/testing/__init__.py +0 -0
- dodal/testing/electron_analyser/device_factory.py +4 -4
- dodal/testing/fixtures/devices/apple2.py +1 -1
- dodal/testing/fixtures/run_engine.py +4 -0
- dodal/devices/electron_analyser/abstract/__init__.py +0 -25
- dodal/devices/electron_analyser/abstract/base_detector.py +0 -63
- dodal/devices/electron_analyser/abstract/types.py +0 -12
- dodal/devices/electron_analyser/detector.py +0 -143
- dodal/devices/electron_analyser/specs/detector.py +0 -34
- dodal/devices/electron_analyser/types.py +0 -57
- dodal/devices/electron_analyser/vgscienta/detector.py +0 -48
- {dls_dodal-1.67.0.dist-info → dls_dodal-1.69.0.dist-info}/WHEEL +0 -0
- {dls_dodal-1.67.0.dist-info → dls_dodal-1.69.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.67.0.dist-info → dls_dodal-1.69.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.67.0.dist-info → dls_dodal-1.69.0.dist-info}/top_level.txt +0 -0
- /dodal/devices/electron_analyser/specs/{enums.py → specs_enums.py} +0 -0
- /dodal/devices/electron_analyser/vgscienta/{enums.py → vgscienta_enums.py} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import Generic, TypeVar
|
|
2
|
+
from typing import Generic, TypeAlias, TypeVar
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
from bluesky.protocols import Movable
|
|
@@ -9,27 +9,29 @@ from ophyd_async.core import (
|
|
|
9
9
|
SignalR,
|
|
10
10
|
StandardReadable,
|
|
11
11
|
StandardReadableFormat,
|
|
12
|
+
StrictEnum,
|
|
13
|
+
SupersetEnum,
|
|
12
14
|
derived_signal_r,
|
|
13
15
|
soft_signal_rw,
|
|
14
16
|
)
|
|
15
|
-
from ophyd_async.epics.adcore import ADBaseIO
|
|
17
|
+
from ophyd_async.epics.adcore import ADBaseIO
|
|
16
18
|
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
17
19
|
|
|
18
|
-
from dodal.devices.electron_analyser.
|
|
20
|
+
from dodal.devices.electron_analyser.base.base_enums import EnergyMode
|
|
21
|
+
from dodal.devices.electron_analyser.base.base_region import (
|
|
22
|
+
AnyAcqMode,
|
|
23
|
+
AnyLensMode,
|
|
24
|
+
AnyPassEnergy,
|
|
25
|
+
GenericRegion,
|
|
19
26
|
TAbstractBaseRegion,
|
|
20
|
-
)
|
|
21
|
-
from dodal.devices.electron_analyser.abstract.types import (
|
|
22
27
|
TAcquisitionMode,
|
|
23
28
|
TLensMode,
|
|
24
29
|
TPassEnergy,
|
|
25
|
-
TPsuMode,
|
|
26
|
-
)
|
|
27
|
-
from dodal.devices.electron_analyser.energy_sources import (
|
|
28
|
-
DualEnergySource,
|
|
29
|
-
EnergySource,
|
|
30
30
|
)
|
|
31
|
-
from dodal.devices.electron_analyser.
|
|
32
|
-
|
|
31
|
+
from dodal.devices.electron_analyser.base.base_util import to_binding_energy
|
|
32
|
+
|
|
33
|
+
AnyPsuMode: TypeAlias = SupersetEnum | StrictEnum
|
|
34
|
+
TPsuMode = TypeVar("TPsuMode", bound=AnyPsuMode)
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
class AbstractAnalyserDriverIO(
|
|
@@ -52,7 +54,6 @@ class AbstractAnalyserDriverIO(
|
|
|
52
54
|
lens_mode_type: type[TLensMode],
|
|
53
55
|
psu_mode_type: type[TPsuMode],
|
|
54
56
|
pass_energy_type: type[TPassEnergy],
|
|
55
|
-
energy_source: EnergySource | DualEnergySource,
|
|
56
57
|
name: str = "",
|
|
57
58
|
) -> None:
|
|
58
59
|
"""
|
|
@@ -87,7 +88,6 @@ class AbstractAnalyserDriverIO(
|
|
|
87
88
|
self.total_intensity = derived_signal_r(
|
|
88
89
|
self._calculate_total_intensity, spectrum=self.spectrum
|
|
89
90
|
)
|
|
90
|
-
self.energy_source = energy_source
|
|
91
91
|
|
|
92
92
|
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
93
93
|
# Read once per scan after data acquired
|
|
@@ -96,6 +96,9 @@ class AbstractAnalyserDriverIO(
|
|
|
96
96
|
self.energy_mode = soft_signal_rw(
|
|
97
97
|
EnergyMode, initial_value=EnergyMode.KINETIC
|
|
98
98
|
)
|
|
99
|
+
self.cached_excitation_energy = soft_signal_rw(
|
|
100
|
+
float, initial_value=0, units="eV"
|
|
101
|
+
)
|
|
99
102
|
self.low_energy = epics_signal_rw(float, prefix + "LOW_ENERGY")
|
|
100
103
|
self.centre_energy = epics_signal_rw(float, prefix + "CENTRE_ENERGY")
|
|
101
104
|
self.high_energy = epics_signal_rw(float, prefix + "HIGH_ENERGY")
|
|
@@ -121,7 +124,7 @@ class AbstractAnalyserDriverIO(
|
|
|
121
124
|
self._calculate_binding_energy_axis,
|
|
122
125
|
"eV",
|
|
123
126
|
energy_axis=self.energy_axis,
|
|
124
|
-
excitation_energy=self.
|
|
127
|
+
excitation_energy=self.cached_excitation_energy,
|
|
125
128
|
energy_mode=self.energy_mode,
|
|
126
129
|
)
|
|
127
130
|
self.angle_axis = self._create_angle_axis_signal(prefix)
|
|
@@ -134,34 +137,9 @@ class AbstractAnalyserDriverIO(
|
|
|
134
137
|
iterations=self.iterations,
|
|
135
138
|
)
|
|
136
139
|
|
|
137
|
-
@AsyncStatus.wrap
|
|
138
|
-
async def stage(self) -> None:
|
|
139
|
-
await self.image_mode.set(ADImageMode.SINGLE)
|
|
140
|
-
await super().stage()
|
|
141
|
-
|
|
142
|
-
@AsyncStatus.wrap
|
|
143
|
-
async def set(self, region: TAbstractBaseRegion):
|
|
144
|
-
"""
|
|
145
|
-
Take a region object and setup the driver with it. If using a DualEnergySource,
|
|
146
|
-
set it to use the source selected by the region. It also converts the region to
|
|
147
|
-
kinetic mode before we move the driver signals to the region parameter values:
|
|
148
|
-
|
|
149
|
-
Args:
|
|
150
|
-
region: Contains the parameters to setup the driver for a scan.
|
|
151
|
-
"""
|
|
152
|
-
if isinstance(self.energy_source, DualEnergySource):
|
|
153
|
-
self.energy_source.selected_source.set(region.excitation_energy_source)
|
|
154
|
-
excitation_energy = await self.energy_source.energy.get_value()
|
|
155
|
-
|
|
156
|
-
# Switch to kinetic energy as epics doesn't support BINDING.
|
|
157
|
-
ke_region = region.switch_energy_mode(EnergyMode.KINETIC, excitation_energy)
|
|
158
|
-
await self._set_region(ke_region)
|
|
159
|
-
# Set the true energy mode from original region so binding_energy_axis can be
|
|
160
|
-
# calculated correctly.
|
|
161
|
-
await self.energy_mode.set(region.energy_mode)
|
|
162
|
-
|
|
163
140
|
@abstractmethod
|
|
164
|
-
|
|
141
|
+
@AsyncStatus.wrap
|
|
142
|
+
async def set(self, epics_region: TAbstractBaseRegion):
|
|
165
143
|
"""
|
|
166
144
|
Move a group of signals defined in a region. Each implementation of this class
|
|
167
145
|
is responsible for implementing this method correctly.
|
|
@@ -245,6 +223,9 @@ class AbstractAnalyserDriverIO(
|
|
|
245
223
|
return float(np.sum(spectrum, dtype=np.float64))
|
|
246
224
|
|
|
247
225
|
|
|
226
|
+
GenericAnalyserDriverIO = AbstractAnalyserDriverIO[
|
|
227
|
+
GenericRegion, AnyAcqMode, AnyLensMode, AnyPsuMode, AnyPassEnergy
|
|
228
|
+
]
|
|
248
229
|
TAbstractAnalyserDriverIO = TypeVar(
|
|
249
230
|
"TAbstractAnalyserDriverIO", bound=AbstractAnalyserDriverIO
|
|
250
231
|
)
|
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from abc import ABC
|
|
3
3
|
from collections.abc import Callable
|
|
4
|
-
from typing import Generic, Self, TypeVar
|
|
4
|
+
from typing import Generic, Self, TypeAlias, TypeVar
|
|
5
5
|
|
|
6
|
+
from ophyd_async.core import StrictEnum, SupersetEnum
|
|
6
7
|
from pydantic import BaseModel, Field, model_validator
|
|
7
8
|
|
|
8
|
-
from dodal.devices.electron_analyser.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
from dodal.devices.electron_analyser.base.base_enums import EnergyMode
|
|
10
|
+
from dodal.devices.electron_analyser.base.base_util import (
|
|
11
|
+
to_binding_energy,
|
|
12
|
+
to_kinetic_energy,
|
|
12
13
|
)
|
|
13
|
-
from dodal.devices.
|
|
14
|
-
|
|
14
|
+
from dodal.devices.selectable_source import SelectedSource
|
|
15
|
+
|
|
16
|
+
AnyAcqMode: TypeAlias = StrictEnum
|
|
17
|
+
AnyLensMode: TypeAlias = SupersetEnum | StrictEnum
|
|
18
|
+
AnyPassEnergy: TypeAlias = StrictEnum | float
|
|
19
|
+
AnyPsuMode: TypeAlias = SupersetEnum | StrictEnum
|
|
20
|
+
|
|
21
|
+
TAcquisitionMode = TypeVar("TAcquisitionMode", bound=AnyAcqMode)
|
|
22
|
+
# Allow SupersetEnum. Specs analysers can connect to Lens and Psu mode separately to the
|
|
23
|
+
# analyser which leaves the enum to either be "Not connected" OR the available enums
|
|
24
|
+
# when connected.
|
|
25
|
+
TLensMode = TypeVar("TLensMode", bound=AnyLensMode)
|
|
26
|
+
TPassEnergy = TypeVar("TPassEnergy", bound=AnyPassEnergy)
|
|
27
|
+
TPsuMode = TypeVar("TPsuMode", bound=AnyPsuMode)
|
|
15
28
|
|
|
16
29
|
|
|
17
30
|
def java_to_python_case(java_str: str) -> str:
|
|
@@ -88,10 +101,13 @@ class AbstractBaseRegion(
|
|
|
88
101
|
return self.energy_mode == EnergyMode.KINETIC
|
|
89
102
|
|
|
90
103
|
def switch_energy_mode(
|
|
91
|
-
self,
|
|
104
|
+
self,
|
|
105
|
+
energy_mode: EnergyMode,
|
|
106
|
+
excitation_energy: float,
|
|
107
|
+
copy: bool = True,
|
|
92
108
|
) -> Self:
|
|
93
109
|
"""
|
|
94
|
-
|
|
110
|
+
Get a region with a new energy mode: Kinetic or Binding.
|
|
95
111
|
It caculates new values for low_energy, centre_energy, high_energy, via the
|
|
96
112
|
excitation enerrgy. It doesn't calculate anything if the region is already of
|
|
97
113
|
the same energy mode.
|
|
@@ -100,8 +116,8 @@ class AbstractBaseRegion(
|
|
|
100
116
|
energy_mode: Mode you want to switch the region to.
|
|
101
117
|
excitation_energy: Energy conversion for low_energy, centre_energy, and
|
|
102
118
|
high_energy for new energy mode.
|
|
103
|
-
copy: Defaults to True. If true, create a copy of this region
|
|
104
|
-
energy_mode and return it. If False, alter this region for the
|
|
119
|
+
copy: Defaults to True. If true, create a copy of this region to alter for
|
|
120
|
+
the new energy_mode and return it. If False, alter this region for the
|
|
105
121
|
energy_mode and return it self.
|
|
106
122
|
|
|
107
123
|
Returns:
|
|
@@ -126,6 +142,25 @@ class AbstractBaseRegion(
|
|
|
126
142
|
|
|
127
143
|
return switched_r
|
|
128
144
|
|
|
145
|
+
def prepare_for_epics(self, excitation_energy: float, copy: bool = True) -> Self:
|
|
146
|
+
"""Prepares a region for epics by converting BINDING to KINETIC by calculating
|
|
147
|
+
new values for low_energy, centre_energy, and high_energy while also preserving
|
|
148
|
+
the original energy mode e.g mode BINDING will stay as BINDING.
|
|
149
|
+
|
|
150
|
+
Parameters:
|
|
151
|
+
excitation_energy: Energy conversion for low_energy, centre_energy, and
|
|
152
|
+
high_energy for new energy mode.
|
|
153
|
+
copy: Defaults to True. If true, create a copy of this region to alter to
|
|
154
|
+
calculate new energy values to return. If false, alter this region.
|
|
155
|
+
Returns:
|
|
156
|
+
Region with selected original energy mode and new calculated KINETIC energy
|
|
157
|
+
values for epics.
|
|
158
|
+
"""
|
|
159
|
+
original_energy_mode = self.energy_mode
|
|
160
|
+
r = self.switch_energy_mode(EnergyMode.KINETIC, excitation_energy, copy)
|
|
161
|
+
r.energy_mode = original_energy_mode
|
|
162
|
+
return r
|
|
163
|
+
|
|
129
164
|
@model_validator(mode="before")
|
|
130
165
|
@classmethod
|
|
131
166
|
def before_validation(cls, data: dict) -> dict:
|
|
@@ -133,6 +168,7 @@ class AbstractBaseRegion(
|
|
|
133
168
|
return energy_mode_validation(data)
|
|
134
169
|
|
|
135
170
|
|
|
171
|
+
GenericRegion = AbstractBaseRegion[AnyAcqMode, AnyLensMode, AnyPassEnergy]
|
|
136
172
|
TAbstractBaseRegion = TypeVar("TAbstractBaseRegion", bound=AbstractBaseRegion)
|
|
137
173
|
|
|
138
174
|
|
|
@@ -163,4 +199,5 @@ class AbstractBaseSequence(
|
|
|
163
199
|
return next((region for region in self.regions if region.name == name), None)
|
|
164
200
|
|
|
165
201
|
|
|
202
|
+
GenericSequence = AbstractBaseSequence[GenericRegion]
|
|
166
203
|
TAbstractBaseSequence = TypeVar("TAbstractBaseSequence", bound=AbstractBaseSequence)
|
|
@@ -3,14 +3,14 @@ from abc import abstractmethod
|
|
|
3
3
|
from ophyd_async.core import (
|
|
4
4
|
Reference,
|
|
5
5
|
SignalR,
|
|
6
|
+
SignalRW,
|
|
6
7
|
StandardReadable,
|
|
7
8
|
StandardReadableFormat,
|
|
8
9
|
derived_signal_r,
|
|
9
10
|
soft_signal_r_and_setter,
|
|
10
|
-
soft_signal_rw,
|
|
11
11
|
)
|
|
12
12
|
|
|
13
|
-
from dodal.devices.
|
|
13
|
+
from dodal.devices.selectable_source import SelectedSource, get_obj_from_selected_source
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class AbstractEnergySource(StandardReadable):
|
|
@@ -51,51 +51,52 @@ class EnergySource(AbstractEnergySource):
|
|
|
51
51
|
return self._source_ref()
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def get_float_from_selected_source(
|
|
55
|
+
selected: SelectedSource, s1: float, s2: float
|
|
56
|
+
) -> float:
|
|
57
|
+
"""Wrapper function to provide type hints for derived signal."""
|
|
58
|
+
return get_obj_from_selected_source(selected, s1, s2)
|
|
59
|
+
|
|
60
|
+
|
|
54
61
|
class DualEnergySource(AbstractEnergySource):
|
|
55
62
|
"""
|
|
56
63
|
Holds two EnergySource devices and provides a signal to read energy depending on
|
|
57
|
-
which source is selected.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
used.
|
|
64
|
+
which source is selected. The energy is the one that corrosponds to the
|
|
65
|
+
selected_source signal. For example, selected_source is source1 if selected_source
|
|
66
|
+
is at SelectedSource.SOURCE1 and vise versa for source2 and SelectedSource.SOURCE2.
|
|
61
67
|
"""
|
|
62
68
|
|
|
63
69
|
def __init__(
|
|
64
|
-
self,
|
|
70
|
+
self,
|
|
71
|
+
source1: SignalR[float],
|
|
72
|
+
source2: SignalR[float],
|
|
73
|
+
selected_source: SignalRW[SelectedSource],
|
|
74
|
+
name: str = "",
|
|
65
75
|
):
|
|
66
76
|
"""
|
|
67
77
|
Args:
|
|
68
|
-
source1:
|
|
69
|
-
source2:
|
|
70
|
-
|
|
78
|
+
source1: Energy source that corrosponds to SelectedSource.SOURCE1.
|
|
79
|
+
source2: Energy source that corrosponds to SelectedSource.SOURCE2.
|
|
80
|
+
selected_source: Signal that decides the active energy source.
|
|
81
|
+
name: Name of this device.
|
|
71
82
|
"""
|
|
72
83
|
|
|
84
|
+
self.selected_source_ref = Reference(selected_source)
|
|
73
85
|
with self.add_children_as_readables():
|
|
74
|
-
self.selected_source = soft_signal_rw(
|
|
75
|
-
SelectedSource, initial_value=SelectedSource.SOURCE1
|
|
76
|
-
)
|
|
77
86
|
self.source1 = EnergySource(source1)
|
|
78
87
|
self.source2 = EnergySource(source2)
|
|
79
88
|
|
|
80
89
|
self._selected_energy = derived_signal_r(
|
|
81
|
-
|
|
90
|
+
get_float_from_selected_source,
|
|
82
91
|
"eV",
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
selected=self.selected_source_ref(),
|
|
93
|
+
s1=self.source1.energy,
|
|
94
|
+
s2=self.source2.energy,
|
|
86
95
|
)
|
|
96
|
+
self.add_readables([selected_source])
|
|
87
97
|
|
|
88
98
|
super().__init__(name)
|
|
89
99
|
|
|
90
|
-
def _get_excitation_energy(
|
|
91
|
-
self, selected_source: SelectedSource, source1: float, source2: float
|
|
92
|
-
) -> float:
|
|
93
|
-
match selected_source:
|
|
94
|
-
case SelectedSource.SOURCE1:
|
|
95
|
-
return source1
|
|
96
|
-
case SelectedSource.SOURCE2:
|
|
97
|
-
return source2
|
|
98
|
-
|
|
99
100
|
@property
|
|
100
101
|
def energy(self) -> SignalR[float]:
|
|
101
102
|
return self._selected_energy
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from .
|
|
3
|
-
from .
|
|
4
|
-
from .
|
|
1
|
+
from .specs_detector import SpecsDetector
|
|
2
|
+
from .specs_driver_io import SpecsAnalyserDriverIO
|
|
3
|
+
from .specs_enums import AcquisitionMode
|
|
4
|
+
from .specs_region import SpecsRegion, SpecsSequence
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
7
|
"SpecsDetector",
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from typing import Generic
|
|
2
|
+
|
|
3
|
+
from dodal.devices.electron_analyser.base.base_controller import (
|
|
4
|
+
ElectronAnalyserController,
|
|
5
|
+
)
|
|
6
|
+
from dodal.devices.electron_analyser.base.base_detector import ElectronAnalyserDetector
|
|
7
|
+
from dodal.devices.electron_analyser.base.base_region import TLensMode, TPsuMode
|
|
8
|
+
from dodal.devices.electron_analyser.base.energy_sources import AbstractEnergySource
|
|
9
|
+
from dodal.devices.electron_analyser.specs.specs_driver_io import SpecsAnalyserDriverIO
|
|
10
|
+
from dodal.devices.electron_analyser.specs.specs_region import (
|
|
11
|
+
SpecsRegion,
|
|
12
|
+
SpecsSequence,
|
|
13
|
+
)
|
|
14
|
+
from dodal.devices.fast_shutter import FastShutter
|
|
15
|
+
from dodal.devices.selectable_source import SourceSelector
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SpecsDetector(
|
|
19
|
+
ElectronAnalyserDetector[
|
|
20
|
+
SpecsSequence[TLensMode, TPsuMode],
|
|
21
|
+
SpecsAnalyserDriverIO[TLensMode, TPsuMode],
|
|
22
|
+
SpecsRegion[TLensMode, TPsuMode],
|
|
23
|
+
],
|
|
24
|
+
Generic[TLensMode, TPsuMode],
|
|
25
|
+
):
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
prefix: str,
|
|
29
|
+
lens_mode_type: type[TLensMode],
|
|
30
|
+
psu_mode_type: type[TPsuMode],
|
|
31
|
+
energy_source: AbstractEnergySource,
|
|
32
|
+
shutter: FastShutter | None = None,
|
|
33
|
+
source_selector: SourceSelector | None = None,
|
|
34
|
+
name: str = "",
|
|
35
|
+
):
|
|
36
|
+
# Save to class so takes part with connect()
|
|
37
|
+
self.driver = SpecsAnalyserDriverIO[TLensMode, TPsuMode](
|
|
38
|
+
prefix, lens_mode_type, psu_mode_type
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
controller = ElectronAnalyserController[
|
|
42
|
+
SpecsAnalyserDriverIO[TLensMode, TPsuMode], SpecsRegion[TLensMode, TPsuMode]
|
|
43
|
+
](self.driver, energy_source, shutter, source_selector)
|
|
44
|
+
|
|
45
|
+
sequence_class = SpecsSequence[lens_mode_type, psu_mode_type]
|
|
46
|
+
|
|
47
|
+
super().__init__(sequence_class, controller, name)
|
|
@@ -4,22 +4,19 @@ from typing import Generic
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
from ophyd_async.core import (
|
|
6
6
|
Array1D,
|
|
7
|
+
AsyncStatus,
|
|
7
8
|
SignalR,
|
|
8
9
|
StandardReadableFormat,
|
|
9
10
|
derived_signal_r,
|
|
10
11
|
)
|
|
11
12
|
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
12
13
|
|
|
13
|
-
from dodal.devices.electron_analyser.
|
|
14
|
+
from dodal.devices.electron_analyser.base.base_driver_io import (
|
|
14
15
|
AbstractAnalyserDriverIO,
|
|
15
16
|
)
|
|
16
|
-
from dodal.devices.electron_analyser.
|
|
17
|
-
from dodal.devices.electron_analyser.
|
|
18
|
-
|
|
19
|
-
EnergySource,
|
|
20
|
-
)
|
|
21
|
-
from dodal.devices.electron_analyser.specs.enums import AcquisitionMode
|
|
22
|
-
from dodal.devices.electron_analyser.specs.region import SpecsRegion
|
|
17
|
+
from dodal.devices.electron_analyser.base.base_region import TLensMode, TPsuMode
|
|
18
|
+
from dodal.devices.electron_analyser.specs.specs_enums import AcquisitionMode
|
|
19
|
+
from dodal.devices.electron_analyser.specs.specs_region import SpecsRegion
|
|
23
20
|
|
|
24
21
|
|
|
25
22
|
class SpecsAnalyserDriverIO(
|
|
@@ -37,7 +34,6 @@ class SpecsAnalyserDriverIO(
|
|
|
37
34
|
prefix: str,
|
|
38
35
|
lens_mode_type: type[TLensMode],
|
|
39
36
|
psu_mode_type: type[TPsuMode],
|
|
40
|
-
energy_source: EnergySource | DualEnergySource,
|
|
41
37
|
name: str = "",
|
|
42
38
|
) -> None:
|
|
43
39
|
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
@@ -59,29 +55,30 @@ class SpecsAnalyserDriverIO(
|
|
|
59
55
|
lens_mode_type=lens_mode_type,
|
|
60
56
|
psu_mode_type=psu_mode_type,
|
|
61
57
|
pass_energy_type=float,
|
|
62
|
-
energy_source=energy_source,
|
|
63
58
|
name=name,
|
|
64
59
|
)
|
|
65
60
|
|
|
66
|
-
|
|
61
|
+
@AsyncStatus.wrap
|
|
62
|
+
async def set(self, epics_region: SpecsRegion[TLensMode, TPsuMode]):
|
|
67
63
|
await asyncio.gather(
|
|
68
|
-
self.region_name.set(
|
|
69
|
-
self.low_energy.set(
|
|
70
|
-
self.high_energy.set(
|
|
71
|
-
self.slices.set(
|
|
72
|
-
self.acquire_time.set(
|
|
73
|
-
self.lens_mode.set(
|
|
74
|
-
self.pass_energy.set(
|
|
75
|
-
self.iterations.set(
|
|
76
|
-
self.acquisition_mode.set(
|
|
77
|
-
self.snapshot_values.set(
|
|
78
|
-
self.psu_mode.set(
|
|
64
|
+
self.region_name.set(epics_region.name),
|
|
65
|
+
self.low_energy.set(epics_region.low_energy),
|
|
66
|
+
self.high_energy.set(epics_region.high_energy),
|
|
67
|
+
self.slices.set(epics_region.slices),
|
|
68
|
+
self.acquire_time.set(epics_region.acquire_time),
|
|
69
|
+
self.lens_mode.set(epics_region.lens_mode),
|
|
70
|
+
self.pass_energy.set(epics_region.pass_energy),
|
|
71
|
+
self.iterations.set(epics_region.iterations),
|
|
72
|
+
self.acquisition_mode.set(epics_region.acquisition_mode),
|
|
73
|
+
self.snapshot_values.set(epics_region.values),
|
|
74
|
+
self.psu_mode.set(epics_region.psu_mode),
|
|
75
|
+
self.energy_mode.set(epics_region.energy_mode),
|
|
79
76
|
)
|
|
80
|
-
if
|
|
81
|
-
await self.energy_step.set(
|
|
77
|
+
if epics_region.acquisition_mode == AcquisitionMode.FIXED_TRANSMISSION:
|
|
78
|
+
await self.energy_step.set(epics_region.energy_step)
|
|
82
79
|
|
|
83
|
-
if
|
|
84
|
-
await self.centre_energy.set(
|
|
80
|
+
if epics_region.acquisition_mode == AcquisitionMode.FIXED_ENERGY:
|
|
81
|
+
await self.centre_energy.set(epics_region.centre_energy)
|
|
85
82
|
|
|
86
83
|
def _create_angle_axis_signal(self, prefix: str) -> SignalR[Array1D[np.float64]]:
|
|
87
84
|
angle_axis = derived_signal_r(
|
|
@@ -2,12 +2,13 @@ from typing import Generic
|
|
|
2
2
|
|
|
3
3
|
from pydantic import Field
|
|
4
4
|
|
|
5
|
-
from dodal.devices.electron_analyser.
|
|
5
|
+
from dodal.devices.electron_analyser.base.base_region import (
|
|
6
6
|
AbstractBaseRegion,
|
|
7
7
|
AbstractBaseSequence,
|
|
8
|
+
TLensMode,
|
|
9
|
+
TPsuMode,
|
|
8
10
|
)
|
|
9
|
-
from dodal.devices.electron_analyser.
|
|
10
|
-
from dodal.devices.electron_analyser.specs.enums import AcquisitionMode
|
|
11
|
+
from dodal.devices.electron_analyser.specs.specs_enums import AcquisitionMode
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class SpecsRegion(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from .
|
|
3
|
-
from .
|
|
4
|
-
from .
|
|
1
|
+
from .vgscienta_detector import VGScientaDetector
|
|
2
|
+
from .vgscienta_driver_io import VGScientaAnalyserDriverIO
|
|
3
|
+
from .vgscienta_enums import AcquisitionMode, DetectorMode
|
|
4
|
+
from .vgscienta_region import VGScientaRegion, VGScientaSequence
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
7
|
"VGScientaDetector",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import Generic
|
|
2
|
+
|
|
3
|
+
from dodal.devices.electron_analyser.base.base_controller import (
|
|
4
|
+
ElectronAnalyserController,
|
|
5
|
+
)
|
|
6
|
+
from dodal.devices.electron_analyser.base.base_detector import ElectronAnalyserDetector
|
|
7
|
+
from dodal.devices.electron_analyser.base.base_region import TLensMode, TPsuMode
|
|
8
|
+
from dodal.devices.electron_analyser.base.energy_sources import AbstractEnergySource
|
|
9
|
+
from dodal.devices.electron_analyser.vgscienta.vgscienta_driver_io import (
|
|
10
|
+
VGScientaAnalyserDriverIO,
|
|
11
|
+
)
|
|
12
|
+
from dodal.devices.electron_analyser.vgscienta.vgscienta_region import (
|
|
13
|
+
TPassEnergyEnum,
|
|
14
|
+
VGScientaRegion,
|
|
15
|
+
VGScientaSequence,
|
|
16
|
+
)
|
|
17
|
+
from dodal.devices.fast_shutter import FastShutter
|
|
18
|
+
from dodal.devices.selectable_source import SourceSelector
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class VGScientaDetector(
|
|
22
|
+
ElectronAnalyserDetector[
|
|
23
|
+
VGScientaSequence[TLensMode, TPsuMode, TPassEnergyEnum],
|
|
24
|
+
VGScientaAnalyserDriverIO[TLensMode, TPsuMode, TPassEnergyEnum],
|
|
25
|
+
VGScientaRegion[TLensMode, TPassEnergyEnum],
|
|
26
|
+
],
|
|
27
|
+
Generic[TLensMode, TPsuMode, TPassEnergyEnum],
|
|
28
|
+
):
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
prefix: str,
|
|
32
|
+
lens_mode_type: type[TLensMode],
|
|
33
|
+
psu_mode_type: type[TPsuMode],
|
|
34
|
+
pass_energy_type: type[TPassEnergyEnum],
|
|
35
|
+
energy_source: AbstractEnergySource,
|
|
36
|
+
shutter: FastShutter | None = None,
|
|
37
|
+
source_selector: SourceSelector | None = None,
|
|
38
|
+
name: str = "",
|
|
39
|
+
):
|
|
40
|
+
# Save to class so takes part with connect()
|
|
41
|
+
self.driver = VGScientaAnalyserDriverIO[TLensMode, TPsuMode, TPassEnergyEnum](
|
|
42
|
+
prefix, lens_mode_type, psu_mode_type, pass_energy_type
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
controller = ElectronAnalyserController[
|
|
46
|
+
VGScientaAnalyserDriverIO[TLensMode, TPsuMode, TPassEnergyEnum],
|
|
47
|
+
VGScientaRegion[TLensMode, TPassEnergyEnum],
|
|
48
|
+
](self.driver, energy_source, shutter, source_selector)
|
|
49
|
+
|
|
50
|
+
sequence_class = VGScientaSequence[
|
|
51
|
+
lens_mode_type, psu_mode_type, pass_energy_type
|
|
52
|
+
]
|
|
53
|
+
super().__init__(sequence_class, controller, name)
|
|
@@ -4,28 +4,22 @@ from typing import Generic
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
from ophyd_async.core import (
|
|
6
6
|
Array1D,
|
|
7
|
+
AsyncStatus,
|
|
7
8
|
SignalR,
|
|
8
9
|
StandardReadableFormat,
|
|
9
10
|
)
|
|
10
11
|
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
11
12
|
|
|
12
|
-
from dodal.devices.electron_analyser.
|
|
13
|
+
from dodal.devices.electron_analyser.base.base_driver_io import (
|
|
13
14
|
AbstractAnalyserDriverIO,
|
|
14
15
|
)
|
|
15
|
-
from dodal.devices.electron_analyser.
|
|
16
|
-
|
|
17
|
-
TPassEnergyEnum,
|
|
18
|
-
TPsuMode,
|
|
19
|
-
)
|
|
20
|
-
from dodal.devices.electron_analyser.energy_sources import (
|
|
21
|
-
DualEnergySource,
|
|
22
|
-
EnergySource,
|
|
23
|
-
)
|
|
24
|
-
from dodal.devices.electron_analyser.vgscienta.enums import (
|
|
16
|
+
from dodal.devices.electron_analyser.base.base_region import TLensMode, TPsuMode
|
|
17
|
+
from dodal.devices.electron_analyser.vgscienta.vgscienta_enums import (
|
|
25
18
|
AcquisitionMode,
|
|
26
19
|
DetectorMode,
|
|
27
20
|
)
|
|
28
|
-
from dodal.devices.electron_analyser.vgscienta.
|
|
21
|
+
from dodal.devices.electron_analyser.vgscienta.vgscienta_region import (
|
|
22
|
+
TPassEnergyEnum,
|
|
29
23
|
VGScientaRegion,
|
|
30
24
|
)
|
|
31
25
|
|
|
@@ -46,7 +40,6 @@ class VGScientaAnalyserDriverIO(
|
|
|
46
40
|
lens_mode_type: type[TLensMode],
|
|
47
41
|
psu_mode_type: type[TPsuMode],
|
|
48
42
|
pass_energy_type: type[TPassEnergyEnum],
|
|
49
|
-
energy_source: EnergySource | DualEnergySource,
|
|
50
43
|
name: str = "",
|
|
51
44
|
) -> None:
|
|
52
45
|
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
@@ -67,28 +60,29 @@ class VGScientaAnalyserDriverIO(
|
|
|
67
60
|
lens_mode_type,
|
|
68
61
|
psu_mode_type,
|
|
69
62
|
pass_energy_type,
|
|
70
|
-
energy_source,
|
|
71
63
|
name,
|
|
72
64
|
)
|
|
73
65
|
|
|
74
|
-
|
|
66
|
+
@AsyncStatus.wrap
|
|
67
|
+
async def set(self, epics_region: VGScientaRegion[TLensMode, TPassEnergyEnum]):
|
|
75
68
|
await asyncio.gather(
|
|
76
|
-
self.region_name.set(
|
|
77
|
-
self.low_energy.set(
|
|
78
|
-
self.centre_energy.set(
|
|
79
|
-
self.high_energy.set(
|
|
80
|
-
self.slices.set(
|
|
81
|
-
self.lens_mode.set(
|
|
82
|
-
self.pass_energy.set(
|
|
83
|
-
self.iterations.set(
|
|
84
|
-
self.acquire_time.set(
|
|
85
|
-
self.acquisition_mode.set(
|
|
86
|
-
self.energy_step.set(
|
|
87
|
-
self.detector_mode.set(
|
|
88
|
-
self.region_min_x.set(
|
|
89
|
-
self.region_size_x.set(
|
|
90
|
-
self.region_min_y.set(
|
|
91
|
-
self.region_size_y.set(
|
|
69
|
+
self.region_name.set(epics_region.name),
|
|
70
|
+
self.low_energy.set(epics_region.low_energy),
|
|
71
|
+
self.centre_energy.set(epics_region.centre_energy),
|
|
72
|
+
self.high_energy.set(epics_region.high_energy),
|
|
73
|
+
self.slices.set(epics_region.slices),
|
|
74
|
+
self.lens_mode.set(epics_region.lens_mode),
|
|
75
|
+
self.pass_energy.set(epics_region.pass_energy),
|
|
76
|
+
self.iterations.set(epics_region.iterations),
|
|
77
|
+
self.acquire_time.set(epics_region.acquire_time),
|
|
78
|
+
self.acquisition_mode.set(epics_region.acquisition_mode),
|
|
79
|
+
self.energy_step.set(epics_region.energy_step),
|
|
80
|
+
self.detector_mode.set(epics_region.detector_mode),
|
|
81
|
+
self.region_min_x.set(epics_region.min_x),
|
|
82
|
+
self.region_size_x.set(epics_region.size_x),
|
|
83
|
+
self.region_min_y.set(epics_region.min_y),
|
|
84
|
+
self.region_size_y.set(epics_region.size_y),
|
|
85
|
+
self.energy_mode.set(epics_region.energy_mode),
|
|
92
86
|
)
|
|
93
87
|
|
|
94
88
|
def _create_energy_axis_signal(self, prefix: str) -> SignalR[Array1D[np.float64]]:
|