dls-dodal 1.51.0__py3-none-any.whl → 1.53.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.51.0.dist-info → dls_dodal-1.53.0.dist-info}/METADATA +3 -2
- {dls_dodal-1.51.0.dist-info → dls_dodal-1.53.0.dist-info}/RECORD +46 -42
- dodal/_version.py +2 -2
- dodal/beamlines/adsim.py +12 -11
- dodal/beamlines/b07.py +7 -6
- dodal/beamlines/b07_1.py +7 -6
- dodal/beamlines/i03.py +4 -1
- dodal/beamlines/i04.py +2 -2
- dodal/beamlines/i09.py +7 -5
- dodal/beamlines/i09_1.py +6 -3
- dodal/beamlines/i09_2.py +2 -2
- dodal/beamlines/i22.py +11 -0
- dodal/beamlines/i24.py +2 -2
- dodal/beamlines/p60.py +6 -4
- dodal/beamlines/p99.py +14 -0
- dodal/common/device_utils.py +45 -0
- dodal/devices/attenuator/attenuator.py +5 -3
- dodal/devices/b07/__init__.py +2 -2
- dodal/devices/b07/enums.py +24 -0
- dodal/devices/b07_1/__init__.py +2 -2
- dodal/devices/b07_1/enums.py +18 -0
- dodal/devices/electron_analyser/abstract/__init__.py +4 -0
- dodal/devices/electron_analyser/abstract/base_driver_io.py +21 -4
- dodal/devices/electron_analyser/abstract/base_region.py +20 -7
- dodal/devices/electron_analyser/detector.py +1 -1
- dodal/devices/electron_analyser/specs/detector.py +18 -4
- dodal/devices/electron_analyser/specs/driver_io.py +17 -5
- dodal/devices/electron_analyser/specs/region.py +9 -5
- dodal/devices/electron_analyser/types.py +21 -5
- dodal/devices/electron_analyser/vgscienta/detector.py +16 -7
- dodal/devices/electron_analyser/vgscienta/driver_io.py +13 -4
- dodal/devices/electron_analyser/vgscienta/region.py +11 -5
- dodal/devices/i09/__init__.py +2 -2
- dodal/devices/i09/enums.py +15 -0
- dodal/devices/i09_1/__init__.py +3 -0
- dodal/devices/i09_1/enums.py +19 -0
- dodal/devices/linkam3.py +25 -81
- dodal/devices/oav/pin_image_recognition/__init__.py +11 -14
- dodal/devices/p60/__init__.py +3 -2
- dodal/devices/p60/enums.py +10 -0
- dodal/devices/tetramm.py +134 -150
- dodal/devices/xbpm_feedback.py +6 -3
- dodal/devices/b07/grating.py +0 -9
- dodal/devices/b07_1/grating.py +0 -10
- dodal/devices/i09/grating.py +0 -7
- {dls_dodal-1.51.0.dist-info → dls_dodal-1.53.0.dist-info}/WHEEL +0 -0
- {dls_dodal-1.51.0.dist-info → dls_dodal-1.53.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.51.0.dist-info → dls_dodal-1.53.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.51.0.dist-info → dls_dodal-1.53.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from ophyd_async.core import StrictEnum, SupersetEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Grating(StrictEnum):
|
|
5
|
+
AU_400 = "400 l/mm Au"
|
|
6
|
+
AU_600 = "600 l/mm Au"
|
|
7
|
+
PT_600 = "600 l/mm Pt"
|
|
8
|
+
AU_1200 = "1200 l/mm Au"
|
|
9
|
+
ML_1200 = "1200 l/mm ML"
|
|
10
|
+
NO_GRATING = "No Grating"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LensMode(SupersetEnum):
|
|
14
|
+
SMALL_AREA = "SmallArea"
|
|
15
|
+
ANGLE_RESOLVED_MODE_22 = "AngleResolvedMode22"
|
|
16
|
+
ANGLE_RESOLVED_MODE_30 = "AngleResolvedMode30"
|
|
17
|
+
LARGE_AREA = "LargeArea"
|
|
18
|
+
NOT_CONNECTED = "Not connected"
|
|
@@ -8,6 +8,8 @@ from .base_region import (
|
|
|
8
8
|
AbstractBaseSequence,
|
|
9
9
|
TAbstractBaseRegion,
|
|
10
10
|
TAbstractBaseSequence,
|
|
11
|
+
TAcquisitionMode,
|
|
12
|
+
TLensMode,
|
|
11
13
|
)
|
|
12
14
|
|
|
13
15
|
__all__ = [
|
|
@@ -15,6 +17,8 @@ __all__ = [
|
|
|
15
17
|
"AbstractBaseSequence",
|
|
16
18
|
"TAbstractBaseRegion",
|
|
17
19
|
"TAbstractBaseSequence",
|
|
20
|
+
"TAcquisitionMode",
|
|
21
|
+
"TLensMode",
|
|
18
22
|
"AbstractAnalyserDriverIO",
|
|
19
23
|
"AbstractElectronAnalyserDetector",
|
|
20
24
|
"AbstractAnalyserDriverIO",
|
|
@@ -11,7 +11,6 @@ from ophyd_async.core import (
|
|
|
11
11
|
SignalR,
|
|
12
12
|
StandardReadable,
|
|
13
13
|
StandardReadableFormat,
|
|
14
|
-
StrictEnum,
|
|
15
14
|
derived_signal_r,
|
|
16
15
|
soft_signal_rw,
|
|
17
16
|
)
|
|
@@ -20,6 +19,8 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
|
20
19
|
|
|
21
20
|
from dodal.devices.electron_analyser.abstract.base_region import (
|
|
22
21
|
TAbstractBaseRegion,
|
|
22
|
+
TAcquisitionMode,
|
|
23
|
+
TLensMode,
|
|
23
24
|
)
|
|
24
25
|
from dodal.devices.electron_analyser.enums import EnergyMode
|
|
25
26
|
from dodal.devices.electron_analyser.util import to_binding_energy, to_kinetic_energy
|
|
@@ -30,7 +31,7 @@ class AbstractAnalyserDriverIO(
|
|
|
30
31
|
StandardReadable,
|
|
31
32
|
ADBaseIO,
|
|
32
33
|
Movable[TAbstractBaseRegion],
|
|
33
|
-
Generic[TAbstractBaseRegion],
|
|
34
|
+
Generic[TAbstractBaseRegion, TAcquisitionMode, TLensMode],
|
|
34
35
|
):
|
|
35
36
|
"""
|
|
36
37
|
Generic device to configure electron analyser with new region settings.
|
|
@@ -40,11 +41,27 @@ class AbstractAnalyserDriverIO(
|
|
|
40
41
|
def __init__(
|
|
41
42
|
self,
|
|
42
43
|
prefix: str,
|
|
43
|
-
acquisition_mode_type: type[
|
|
44
|
+
acquisition_mode_type: type[TAcquisitionMode],
|
|
45
|
+
lens_mode_type: type[TLensMode],
|
|
44
46
|
energy_sources: Mapping[str, SignalR[float]],
|
|
45
47
|
name: str = "",
|
|
46
48
|
) -> None:
|
|
49
|
+
"""
|
|
50
|
+
Constructor method for setting up electron analyser.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
prefix: Base PV to connect to EPICS for this device.
|
|
54
|
+
acquisition_mode_type: Enum that determines the available acquisition modes
|
|
55
|
+
for this device.
|
|
56
|
+
lens_mode_type: Enum that determines the available lens mode for this
|
|
57
|
+
device.
|
|
58
|
+
energy_sources: Map that pairs a source name to an energy value signal
|
|
59
|
+
(in eV).
|
|
60
|
+
name: Name of the device.
|
|
61
|
+
"""
|
|
47
62
|
self.energy_sources = energy_sources
|
|
63
|
+
self.acquisition_mode_type = acquisition_mode_type
|
|
64
|
+
self.lens_mode_type = lens_mode_type
|
|
48
65
|
|
|
49
66
|
with self.add_children_as_readables():
|
|
50
67
|
self.image = epics_signal_r(Array1D[np.float64], prefix + "IMAGE")
|
|
@@ -63,7 +80,7 @@ class AbstractAnalyserDriverIO(
|
|
|
63
80
|
self.low_energy = epics_signal_rw(float, prefix + "LOW_ENERGY")
|
|
64
81
|
self.high_energy = epics_signal_rw(float, prefix + "HIGH_ENERGY")
|
|
65
82
|
self.slices = epics_signal_rw(int, prefix + "SLICES")
|
|
66
|
-
self.lens_mode = epics_signal_rw(
|
|
83
|
+
self.lens_mode = epics_signal_rw(lens_mode_type, prefix + "LENS_MODE")
|
|
67
84
|
self.pass_energy = epics_signal_rw(
|
|
68
85
|
self.pass_energy_type, prefix + "PASS_ENERGY"
|
|
69
86
|
)
|
|
@@ -3,13 +3,11 @@ from abc import ABC
|
|
|
3
3
|
from collections.abc import Callable
|
|
4
4
|
from typing import Generic, TypeVar
|
|
5
5
|
|
|
6
|
-
from ophyd_async.core import StrictEnum
|
|
6
|
+
from ophyd_async.core import StrictEnum, SupersetEnum
|
|
7
7
|
from pydantic import BaseModel, Field, model_validator
|
|
8
8
|
|
|
9
9
|
from dodal.devices.electron_analyser.enums import EnergyMode
|
|
10
10
|
|
|
11
|
-
TStrictEnum = TypeVar("TStrictEnum", bound=StrictEnum)
|
|
12
|
-
|
|
13
11
|
|
|
14
12
|
def java_to_python_case(java_str: str) -> str:
|
|
15
13
|
"""
|
|
@@ -46,7 +44,18 @@ def energy_mode_validation(data: dict) -> dict:
|
|
|
46
44
|
return data
|
|
47
45
|
|
|
48
46
|
|
|
49
|
-
|
|
47
|
+
TAcquisitionMode = TypeVar("TAcquisitionMode", bound=StrictEnum)
|
|
48
|
+
# Allow SupersetEnum. Specs analysers can connect to Lens mode separately to the
|
|
49
|
+
# analyser which leaves the enum to either be "Not connected" OR the available enums
|
|
50
|
+
# when connected.
|
|
51
|
+
TLensMode = TypeVar("TLensMode", bound=SupersetEnum | StrictEnum)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class AbstractBaseRegion(
|
|
55
|
+
ABC,
|
|
56
|
+
JavaToPythonModel,
|
|
57
|
+
Generic[TAcquisitionMode, TLensMode],
|
|
58
|
+
):
|
|
50
59
|
"""
|
|
51
60
|
Generic region model that holds the data. Specialised region models should inherit
|
|
52
61
|
this to extend functionality. All energy units are assumed to be in eV.
|
|
@@ -58,9 +67,9 @@ class AbstractBaseRegion(ABC, JavaToPythonModel, Generic[TStrictEnum]):
|
|
|
58
67
|
iterations: int = 1
|
|
59
68
|
excitation_energy_source: str = "source1"
|
|
60
69
|
# These ones we need subclasses to provide default values
|
|
61
|
-
lens_mode:
|
|
70
|
+
lens_mode: TLensMode
|
|
62
71
|
pass_energy: int
|
|
63
|
-
acquisition_mode:
|
|
72
|
+
acquisition_mode: TAcquisitionMode
|
|
64
73
|
low_energy: float
|
|
65
74
|
high_energy: float
|
|
66
75
|
step_time: float
|
|
@@ -83,7 +92,11 @@ class AbstractBaseRegion(ABC, JavaToPythonModel, Generic[TStrictEnum]):
|
|
|
83
92
|
TAbstractBaseRegion = TypeVar("TAbstractBaseRegion", bound=AbstractBaseRegion)
|
|
84
93
|
|
|
85
94
|
|
|
86
|
-
class AbstractBaseSequence(
|
|
95
|
+
class AbstractBaseSequence(
|
|
96
|
+
ABC,
|
|
97
|
+
JavaToPythonModel,
|
|
98
|
+
Generic[TAbstractBaseRegion, TLensMode],
|
|
99
|
+
):
|
|
87
100
|
"""
|
|
88
101
|
Generic sequence model that holds the list of region data. Specialised sequence
|
|
89
102
|
models should inherit this to extend functionality and define type of region to
|
|
@@ -48,7 +48,7 @@ class ElectronAnalyserRegionDetector(
|
|
|
48
48
|
async def trigger(self) -> None:
|
|
49
49
|
# Configure region parameters on the driver first before data collection.
|
|
50
50
|
await self.driver.set(self.region)
|
|
51
|
-
super().trigger()
|
|
51
|
+
await super().trigger()
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
TElectronAnalyserRegionDetector = TypeVar(
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from collections.abc import Mapping
|
|
2
|
+
from typing import Generic
|
|
2
3
|
|
|
3
4
|
from ophyd_async.core import SignalR
|
|
4
5
|
|
|
6
|
+
from dodal.devices.electron_analyser.abstract.base_driver_io import TLensMode
|
|
5
7
|
from dodal.devices.electron_analyser.detector import (
|
|
6
8
|
ElectronAnalyserDetector,
|
|
7
9
|
)
|
|
@@ -10,10 +12,22 @@ from dodal.devices.electron_analyser.specs.region import SpecsRegion, SpecsSeque
|
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class SpecsDetector(
|
|
13
|
-
ElectronAnalyserDetector[
|
|
15
|
+
ElectronAnalyserDetector[
|
|
16
|
+
SpecsAnalyserDriverIO[TLensMode],
|
|
17
|
+
SpecsSequence[TLensMode],
|
|
18
|
+
SpecsRegion[TLensMode],
|
|
19
|
+
],
|
|
20
|
+
Generic[TLensMode],
|
|
14
21
|
):
|
|
15
22
|
def __init__(
|
|
16
|
-
self,
|
|
23
|
+
self,
|
|
24
|
+
prefix: str,
|
|
25
|
+
lens_mode_type: type[TLensMode],
|
|
26
|
+
energy_sources: Mapping[str, SignalR[float]],
|
|
27
|
+
name: str = "",
|
|
17
28
|
):
|
|
18
|
-
driver = SpecsAnalyserDriverIO(
|
|
19
|
-
|
|
29
|
+
driver = SpecsAnalyserDriverIO[TLensMode](
|
|
30
|
+
prefix, lens_mode_type, energy_sources
|
|
31
|
+
)
|
|
32
|
+
seq = SpecsSequence[lens_mode_type]
|
|
33
|
+
super().__init__(seq, driver, name)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from collections.abc import Mapping
|
|
3
|
+
from typing import Generic
|
|
3
4
|
|
|
4
5
|
import numpy as np
|
|
5
6
|
from ophyd_async.core import (
|
|
@@ -14,13 +15,21 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
|
14
15
|
from dodal.devices.electron_analyser.abstract.base_driver_io import (
|
|
15
16
|
AbstractAnalyserDriverIO,
|
|
16
17
|
)
|
|
18
|
+
from dodal.devices.electron_analyser.abstract.base_region import TLensMode
|
|
17
19
|
from dodal.devices.electron_analyser.specs.enums import AcquisitionMode
|
|
18
20
|
from dodal.devices.electron_analyser.specs.region import SpecsRegion
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
class SpecsAnalyserDriverIO(
|
|
23
|
+
class SpecsAnalyserDriverIO(
|
|
24
|
+
AbstractAnalyserDriverIO[SpecsRegion, AcquisitionMode, TLensMode],
|
|
25
|
+
Generic[TLensMode],
|
|
26
|
+
):
|
|
22
27
|
def __init__(
|
|
23
|
-
self,
|
|
28
|
+
self,
|
|
29
|
+
prefix: str,
|
|
30
|
+
lens_mode_type: type[TLensMode],
|
|
31
|
+
energy_sources: Mapping[str, SignalR[float]],
|
|
32
|
+
name: str = "",
|
|
24
33
|
) -> None:
|
|
25
34
|
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
26
35
|
# Used for setting up region data acquisition.
|
|
@@ -31,11 +40,14 @@ class SpecsAnalyserDriverIO(AbstractAnalyserDriverIO[SpecsRegion]):
|
|
|
31
40
|
# Used to read detector data after acqusition.
|
|
32
41
|
self.min_angle_axis = epics_signal_r(float, prefix + "Y_MIN_RBV")
|
|
33
42
|
self.max_angle_axis = epics_signal_r(float, prefix + "Y_MAX_RBV")
|
|
43
|
+
self.energy_channels = epics_signal_r(
|
|
44
|
+
int, prefix + "TOTAL_POINTS_ITERATION_RBV"
|
|
45
|
+
)
|
|
34
46
|
|
|
35
|
-
super().__init__(prefix, AcquisitionMode, energy_sources, name)
|
|
47
|
+
super().__init__(prefix, AcquisitionMode, lens_mode_type, energy_sources, name)
|
|
36
48
|
|
|
37
49
|
@AsyncStatus.wrap
|
|
38
|
-
async def set(self, region: SpecsRegion):
|
|
50
|
+
async def set(self, region: SpecsRegion[TLensMode]):
|
|
39
51
|
await super().set(region)
|
|
40
52
|
|
|
41
53
|
await asyncio.gather(
|
|
@@ -73,7 +85,7 @@ class SpecsAnalyserDriverIO(AbstractAnalyserDriverIO[SpecsRegion]):
|
|
|
73
85
|
"eV",
|
|
74
86
|
min_energy=self.low_energy,
|
|
75
87
|
max_energy=self.high_energy,
|
|
76
|
-
total_points_iterations=self.
|
|
88
|
+
total_points_iterations=self.energy_channels,
|
|
77
89
|
)
|
|
78
90
|
return energy_axis
|
|
79
91
|
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
from typing import Generic
|
|
2
|
+
|
|
1
3
|
from pydantic import Field
|
|
2
4
|
|
|
3
5
|
from dodal.devices.electron_analyser.abstract.base_region import (
|
|
4
6
|
AbstractBaseRegion,
|
|
5
7
|
AbstractBaseSequence,
|
|
8
|
+
TLensMode,
|
|
6
9
|
)
|
|
7
10
|
from dodal.devices.electron_analyser.specs.enums import AcquisitionMode
|
|
8
11
|
|
|
9
12
|
|
|
10
|
-
class SpecsRegion(AbstractBaseRegion[AcquisitionMode]):
|
|
13
|
+
class SpecsRegion(AbstractBaseRegion[AcquisitionMode, TLensMode], Generic[TLensMode]):
|
|
11
14
|
# Override base class with defaults
|
|
12
|
-
lens_mode:
|
|
15
|
+
lens_mode: TLensMode
|
|
13
16
|
pass_energy: int = 5
|
|
14
17
|
acquisition_mode: AcquisitionMode = AcquisitionMode.FIXED_TRANSMISSION
|
|
15
18
|
low_energy: float = Field(default=800, alias="start_energy")
|
|
@@ -19,9 +22,10 @@ class SpecsRegion(AbstractBaseRegion[AcquisitionMode]):
|
|
|
19
22
|
# Specific to this class
|
|
20
23
|
values: int = 1
|
|
21
24
|
centre_energy: float = 0
|
|
22
|
-
|
|
25
|
+
# ToDo - Update to an enum https://github.com/DiamondLightSource/dodal/issues/1328
|
|
26
|
+
psu_mode: str = "1.5kV"
|
|
23
27
|
estimated_time_in_ms: float = 0
|
|
24
28
|
|
|
25
29
|
|
|
26
|
-
class SpecsSequence(AbstractBaseSequence[SpecsRegion]):
|
|
27
|
-
regions: list[SpecsRegion] = Field(default_factory=lambda: [])
|
|
30
|
+
class SpecsSequence(AbstractBaseSequence[SpecsRegion, TLensMode], Generic[TLensMode]):
|
|
31
|
+
regions: list[SpecsRegion[TLensMode]] = Field(default_factory=lambda: [])
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from ophyd_async.core import StrictEnum, SupersetEnum
|
|
2
|
+
|
|
1
3
|
from dodal.devices.electron_analyser.abstract.base_driver_io import (
|
|
2
4
|
AbstractAnalyserDriverIO,
|
|
3
5
|
)
|
|
@@ -18,15 +20,29 @@ from dodal.devices.electron_analyser.vgscienta.detector import (
|
|
|
18
20
|
VGScientaDetector,
|
|
19
21
|
)
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
AnyAcqMode = StrictEnum
|
|
24
|
+
AnyLensMode = SupersetEnum | StrictEnum
|
|
25
|
+
|
|
26
|
+
# Electron analyser types that encompasses all implementations, useful for tests and
|
|
27
|
+
# plans
|
|
28
|
+
ElectronAnalyserDetectorImpl = (
|
|
29
|
+
VGScientaDetector[AnyLensMode] | SpecsDetector[AnyLensMode]
|
|
30
|
+
)
|
|
31
|
+
ElectronAnalyserDriverImpl = (
|
|
32
|
+
VGScientaAnalyserDriverIO[AnyLensMode] | SpecsAnalyserDriverIO[AnyLensMode]
|
|
33
|
+
)
|
|
23
34
|
|
|
35
|
+
# Short hand the type so less verbose
|
|
36
|
+
AbstractBaseRegion = AbstractBaseRegion[AnyAcqMode, AnyLensMode]
|
|
37
|
+
|
|
38
|
+
# Generic electron analyser types that supports full typing with the abstract classes.
|
|
24
39
|
GenericElectronAnalyserDetector = ElectronAnalyserDetector[
|
|
25
|
-
AbstractAnalyserDriverIO[AbstractBaseRegion],
|
|
26
|
-
AbstractBaseSequence,
|
|
40
|
+
AbstractAnalyserDriverIO[AbstractBaseRegion, AnyAcqMode, AnyLensMode],
|
|
41
|
+
AbstractBaseSequence[AbstractBaseRegion, AnyLensMode],
|
|
27
42
|
AbstractBaseRegion,
|
|
28
43
|
]
|
|
29
44
|
|
|
30
45
|
GenericElectronAnalyserRegionDetector = ElectronAnalyserRegionDetector[
|
|
31
|
-
AbstractAnalyserDriverIO[AbstractBaseRegion],
|
|
46
|
+
AbstractAnalyserDriverIO[AbstractBaseRegion, AnyAcqMode, AnyLensMode],
|
|
47
|
+
AbstractBaseRegion,
|
|
32
48
|
]
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from collections.abc import Mapping
|
|
2
|
+
from typing import Generic
|
|
2
3
|
|
|
3
4
|
from ophyd_async.core import SignalR
|
|
4
5
|
|
|
6
|
+
from dodal.devices.electron_analyser.abstract.base_region import TLensMode
|
|
5
7
|
from dodal.devices.electron_analyser.detector import (
|
|
6
8
|
ElectronAnalyserDetector,
|
|
7
9
|
)
|
|
@@ -16,13 +18,20 @@ from dodal.devices.electron_analyser.vgscienta.region import (
|
|
|
16
18
|
|
|
17
19
|
class VGScientaDetector(
|
|
18
20
|
ElectronAnalyserDetector[
|
|
19
|
-
VGScientaAnalyserDriverIO,
|
|
20
|
-
VGScientaSequence,
|
|
21
|
-
VGScientaRegion,
|
|
22
|
-
]
|
|
21
|
+
VGScientaAnalyserDriverIO[TLensMode],
|
|
22
|
+
VGScientaSequence[TLensMode],
|
|
23
|
+
VGScientaRegion[TLensMode],
|
|
24
|
+
],
|
|
25
|
+
Generic[TLensMode],
|
|
23
26
|
):
|
|
24
27
|
def __init__(
|
|
25
|
-
self,
|
|
28
|
+
self,
|
|
29
|
+
prefix: str,
|
|
30
|
+
lens_mode_type: type[TLensMode],
|
|
31
|
+
energy_sources: Mapping[str, SignalR[float]],
|
|
32
|
+
name: str = "",
|
|
26
33
|
):
|
|
27
|
-
driver = VGScientaAnalyserDriverIO(
|
|
28
|
-
|
|
34
|
+
driver = VGScientaAnalyserDriverIO[TLensMode](
|
|
35
|
+
prefix, lens_mode_type, energy_sources
|
|
36
|
+
)
|
|
37
|
+
super().__init__(VGScientaSequence[lens_mode_type], driver, name)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from collections.abc import Mapping
|
|
3
|
+
from typing import Generic
|
|
3
4
|
|
|
4
5
|
import numpy as np
|
|
5
6
|
from ophyd_async.core import (
|
|
@@ -14,6 +15,7 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
|
14
15
|
from dodal.devices.electron_analyser.abstract.base_driver_io import (
|
|
15
16
|
AbstractAnalyserDriverIO,
|
|
16
17
|
)
|
|
18
|
+
from dodal.devices.electron_analyser.abstract.base_region import TLensMode
|
|
17
19
|
from dodal.devices.electron_analyser.util import to_kinetic_energy
|
|
18
20
|
from dodal.devices.electron_analyser.vgscienta.enums import AcquisitionMode
|
|
19
21
|
from dodal.devices.electron_analyser.vgscienta.region import (
|
|
@@ -22,9 +24,16 @@ from dodal.devices.electron_analyser.vgscienta.region import (
|
|
|
22
24
|
)
|
|
23
25
|
|
|
24
26
|
|
|
25
|
-
class VGScientaAnalyserDriverIO(
|
|
27
|
+
class VGScientaAnalyserDriverIO(
|
|
28
|
+
AbstractAnalyserDriverIO[VGScientaRegion, AcquisitionMode, TLensMode],
|
|
29
|
+
Generic[TLensMode],
|
|
30
|
+
):
|
|
26
31
|
def __init__(
|
|
27
|
-
self,
|
|
32
|
+
self,
|
|
33
|
+
prefix: str,
|
|
34
|
+
lens_mode_type: type[TLensMode],
|
|
35
|
+
energy_sources: Mapping[str, SignalR[float]],
|
|
36
|
+
name: str = "",
|
|
28
37
|
) -> None:
|
|
29
38
|
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
30
39
|
# Used for setting up region data acquisition.
|
|
@@ -35,10 +44,10 @@ class VGScientaAnalyserDriverIO(AbstractAnalyserDriverIO[VGScientaRegion]):
|
|
|
35
44
|
self.y_channel_size = epics_signal_rw(int, prefix + "SizeY")
|
|
36
45
|
self.detector_mode = epics_signal_rw(DetectorMode, prefix + "DETECTOR_MODE")
|
|
37
46
|
|
|
38
|
-
super().__init__(prefix, AcquisitionMode, energy_sources, name)
|
|
47
|
+
super().__init__(prefix, AcquisitionMode, lens_mode_type, energy_sources, name)
|
|
39
48
|
|
|
40
49
|
@AsyncStatus.wrap
|
|
41
|
-
async def set(self, region: VGScientaRegion):
|
|
50
|
+
async def set(self, region: VGScientaRegion[TLensMode]):
|
|
42
51
|
await super().set(region)
|
|
43
52
|
|
|
44
53
|
excitation_energy = await self.excitation_energy.get_value()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import uuid
|
|
2
|
+
from typing import Generic
|
|
2
3
|
|
|
3
4
|
from pydantic import Field
|
|
4
5
|
|
|
@@ -6,6 +7,7 @@ from dodal.devices.electron_analyser.abstract.base_region import (
|
|
|
6
7
|
AbstractBaseRegion,
|
|
7
8
|
AbstractBaseSequence,
|
|
8
9
|
JavaToPythonModel,
|
|
10
|
+
TLensMode,
|
|
9
11
|
)
|
|
10
12
|
from dodal.devices.electron_analyser.vgscienta.enums import (
|
|
11
13
|
AcquisitionMode,
|
|
@@ -14,9 +16,11 @@ from dodal.devices.electron_analyser.vgscienta.enums import (
|
|
|
14
16
|
)
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
class VGScientaRegion(
|
|
19
|
+
class VGScientaRegion(
|
|
20
|
+
AbstractBaseRegion[AcquisitionMode, TLensMode], Generic[TLensMode]
|
|
21
|
+
):
|
|
18
22
|
# Override defaults of base region class
|
|
19
|
-
lens_mode:
|
|
23
|
+
lens_mode: TLensMode
|
|
20
24
|
pass_energy: int = 5
|
|
21
25
|
acquisition_mode: AcquisitionMode = AcquisitionMode.SWEPT
|
|
22
26
|
low_energy: float = 8.0
|
|
@@ -48,15 +52,17 @@ class VGScientaExcitationEnergySource(JavaToPythonModel):
|
|
|
48
52
|
value: float = 0
|
|
49
53
|
|
|
50
54
|
|
|
51
|
-
class VGScientaSequence(
|
|
55
|
+
class VGScientaSequence(
|
|
56
|
+
AbstractBaseSequence[VGScientaRegion, TLensMode], Generic[TLensMode]
|
|
57
|
+
):
|
|
52
58
|
element_set: str = Field(default="Unknown")
|
|
53
59
|
excitation_energy_sources: list[VGScientaExcitationEnergySource] = Field(
|
|
54
60
|
default_factory=lambda: []
|
|
55
61
|
)
|
|
56
|
-
regions: list[VGScientaRegion] = Field(default_factory=lambda: [])
|
|
62
|
+
regions: list[VGScientaRegion[TLensMode]] = Field(default_factory=lambda: [])
|
|
57
63
|
|
|
58
64
|
def get_excitation_energy_source_by_region(
|
|
59
|
-
self, region: VGScientaRegion
|
|
65
|
+
self, region: VGScientaRegion[TLensMode]
|
|
60
66
|
) -> VGScientaExcitationEnergySource:
|
|
61
67
|
value = next(
|
|
62
68
|
(
|
dodal/devices/i09/__init__.py
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from ophyd_async.core import StrictEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Grating(StrictEnum):
|
|
5
|
+
G_300 = "300 lines/mm"
|
|
6
|
+
G_400 = "400 lines/mm"
|
|
7
|
+
G_800 = "800 lines/mm"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LensMode(StrictEnum):
|
|
11
|
+
TRANSMISSION = "Transmission"
|
|
12
|
+
ANGULAR45 = "Angular45"
|
|
13
|
+
ANGULAR60 = "Angular60"
|
|
14
|
+
ANGULAR56 = "Angular56"
|
|
15
|
+
ANGULAR45VUV = "Angular45VUV"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ophyd_async.core import SupersetEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LensMode(SupersetEnum):
|
|
5
|
+
LARGE_AREA = "LargeArea"
|
|
6
|
+
MEDIUM_ANGULAR_DISPERSION = "MediumAngularDispersion"
|
|
7
|
+
MEDIUM_AREA = "MediumArea"
|
|
8
|
+
SMALL_AREA = "SmallArea"
|
|
9
|
+
HIGH_MAGNIFICATION = "HighMagnification"
|
|
10
|
+
LOW_ANGULAR_DISPERSION = "LowAngularDispersion"
|
|
11
|
+
LOW_ANGULAR_DISPERSION2 = "LowAngularDispersion2"
|
|
12
|
+
HIGH_ANGULAR_DISPERSION = "HighAngularDispersion"
|
|
13
|
+
WIDE_ANGLE_MODE = "WideAngleMode"
|
|
14
|
+
MEDIUM_ANGLE_MODE = "MediumAngleMode"
|
|
15
|
+
MEDIUM_MAGNIFICATION = "MediumMagnification"
|
|
16
|
+
LOW_MAGNIFICATION = "LowMagnification"
|
|
17
|
+
HIGH_MAGNIFICATION2 = "HighMagnification2"
|
|
18
|
+
RAMP_MODE = "RampMode"
|
|
19
|
+
NOT_CONNECTED = "Not connected"
|
dodal/devices/linkam3.py
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import time
|
|
3
|
-
|
|
4
|
-
from bluesky.protocols import Location
|
|
5
1
|
from ophyd_async.core import (
|
|
6
2
|
StandardReadable,
|
|
7
3
|
StandardReadableFormat,
|
|
8
4
|
StrictEnum,
|
|
9
|
-
WatchableAsyncStatus,
|
|
10
|
-
WatcherUpdate,
|
|
11
|
-
observe_value,
|
|
12
5
|
)
|
|
13
6
|
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
14
7
|
|
|
@@ -18,6 +11,8 @@ class PumpControl(StrictEnum):
|
|
|
18
11
|
AUTO = "Auto"
|
|
19
12
|
|
|
20
13
|
|
|
14
|
+
# TODO: Make use of Status PV:
|
|
15
|
+
# https://github.com/DiamondLightSource/dodal/issues/338
|
|
21
16
|
class Linkam3(StandardReadable):
|
|
22
17
|
"""Device to represent a Linkam3 temperature controller
|
|
23
18
|
|
|
@@ -34,81 +29,30 @@ class Linkam3(StandardReadable):
|
|
|
34
29
|
settle_time: int = 0
|
|
35
30
|
|
|
36
31
|
def __init__(self, prefix: str, name: str = ""):
|
|
37
|
-
self.
|
|
38
|
-
|
|
39
|
-
self.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
self.chamber_vac = epics_signal_r(float, prefix + "VAC_CHAMBER:")
|
|
57
|
-
self.sensor_vac = epics_signal_r(float, prefix + "VAC_DATA1:")
|
|
58
|
-
|
|
59
|
-
self.error = epics_signal_r(str, prefix + "CTRLLR:ERR:")
|
|
60
|
-
|
|
61
|
-
# status is a bitfield stored in a double?
|
|
62
|
-
self.status = epics_signal_r(float, prefix + "STATUS:")
|
|
63
|
-
|
|
64
|
-
self.add_readables((self.temp,), format=StandardReadableFormat.HINTED_SIGNAL)
|
|
65
|
-
self.add_readables(
|
|
66
|
-
(self.ramp_rate, self.speed, self.set_point),
|
|
67
|
-
format=StandardReadableFormat.CONFIG_SIGNAL,
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
super().__init__(name=name)
|
|
71
|
-
|
|
72
|
-
@WatchableAsyncStatus.wrap
|
|
73
|
-
async def set(self, new_position: float, timeout: float | None = None):
|
|
74
|
-
# time.monotonic won't go backwards in case of NTP corrections
|
|
75
|
-
start = time.monotonic()
|
|
76
|
-
old_position = await self.set_point.get_value()
|
|
77
|
-
await self.set_point.set(new_position, wait=True)
|
|
78
|
-
async for current_position in observe_value(self.temp):
|
|
79
|
-
yield WatcherUpdate(
|
|
80
|
-
name=self.name,
|
|
81
|
-
current=current_position,
|
|
82
|
-
initial=old_position,
|
|
83
|
-
target=new_position,
|
|
84
|
-
time_elapsed=time.monotonic() - start,
|
|
32
|
+
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
33
|
+
self.temp = epics_signal_r(float, prefix + "TEMP")
|
|
34
|
+
with self.add_children_as_readables():
|
|
35
|
+
self.dsc = epics_signal_r(float, prefix + "DSC")
|
|
36
|
+
self.start_heat = epics_signal_rw(bool, prefix + "STARTHEAT")
|
|
37
|
+
|
|
38
|
+
self.ramp_rate = epics_signal_rw(
|
|
39
|
+
float, prefix + "RAMPRATE", prefix + "RAMPRATE:SET"
|
|
40
|
+
)
|
|
41
|
+
self.ramp_time = epics_signal_r(float, prefix + "RAMPTIME")
|
|
42
|
+
self.set_point = epics_signal_rw(
|
|
43
|
+
float, prefix + "SETPOINT", prefix + "SETPOINT:SET"
|
|
44
|
+
)
|
|
45
|
+
self.pump_control = epics_signal_r(
|
|
46
|
+
PumpControl,
|
|
47
|
+
prefix + "LNP_MODE:SET",
|
|
48
|
+
)
|
|
49
|
+
self.speed = epics_signal_rw(
|
|
50
|
+
float, prefix + "LNP_SPEED", prefix + "LNP_SPEED:SET"
|
|
85
51
|
)
|
|
86
|
-
if abs(current_position - new_position) < self.tolerance:
|
|
87
|
-
await asyncio.sleep(self.settle_time)
|
|
88
|
-
break
|
|
89
|
-
|
|
90
|
-
# TODO: Make use of values in Status.
|
|
91
|
-
# https://github.com/DiamondLightSource/dodal/issues/338
|
|
92
|
-
async def _is_nth_bit_set(self, n: int) -> bool:
|
|
93
|
-
return bool(int(await self.status.get_value()) & 1 << n)
|
|
94
|
-
|
|
95
|
-
async def in_error(self) -> bool:
|
|
96
|
-
return await self._is_nth_bit_set(0)
|
|
97
|
-
|
|
98
|
-
async def at_setpoint(self) -> bool:
|
|
99
|
-
return await self._is_nth_bit_set(1)
|
|
100
|
-
|
|
101
|
-
async def heater_on(self) -> bool:
|
|
102
|
-
return await self._is_nth_bit_set(2)
|
|
103
52
|
|
|
104
|
-
|
|
105
|
-
|
|
53
|
+
self.chamber_vac = epics_signal_r(float, prefix + "VAC_CHAMBER")
|
|
54
|
+
self.sensor_vac = epics_signal_r(float, prefix + "VAC_DATA1")
|
|
106
55
|
|
|
107
|
-
|
|
108
|
-
return await self._is_nth_bit_set(4)
|
|
56
|
+
self.error = epics_signal_r(str, prefix + "CTRLLR:ERR")
|
|
109
57
|
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
"readback": await self.temp.get_value(),
|
|
113
|
-
"setpoint": await self.set_point.get_value(),
|
|
114
|
-
}
|
|
58
|
+
super().__init__(name=name)
|