dls-dodal 1.36.3__py3-none-any.whl → 1.38.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.36.3.dist-info → dls_dodal-1.38.0.dist-info}/METADATA +4 -3
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/RECORD +58 -38
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/i02_1.py +37 -0
- dodal/beamlines/i03.py +34 -5
- dodal/beamlines/i04.py +16 -5
- dodal/beamlines/i10.py +105 -0
- dodal/beamlines/i13_1.py +20 -2
- dodal/beamlines/i22.py +15 -0
- dodal/beamlines/i24.py +14 -2
- dodal/beamlines/p99.py +6 -2
- dodal/beamlines/training_rig.py +10 -1
- dodal/common/crystal_metadata.py +3 -3
- dodal/common/udc_directory_provider.py +3 -1
- dodal/devices/aperturescatterguard.py +3 -0
- dodal/devices/{attenuator.py → attenuator/attenuator.py} +29 -1
- dodal/devices/attenuator/filter.py +11 -0
- dodal/devices/attenuator/filter_selections.py +72 -0
- dodal/devices/bimorph_mirror.py +151 -0
- dodal/devices/current_amplifiers/__init__.py +34 -0
- dodal/devices/current_amplifiers/current_amplifier.py +103 -0
- dodal/devices/current_amplifiers/current_amplifier_detector.py +109 -0
- dodal/devices/current_amplifiers/femto.py +143 -0
- dodal/devices/current_amplifiers/sr570.py +214 -0
- dodal/devices/current_amplifiers/struck_scaler_counter.py +79 -0
- dodal/devices/detector/det_dim_constants.py +15 -0
- dodal/devices/eiger_odin.py +3 -3
- dodal/devices/fast_grid_scan.py +8 -3
- dodal/devices/flux.py +10 -3
- dodal/devices/i03/beamstop.py +85 -0
- dodal/devices/i04/transfocator.py +67 -53
- dodal/devices/i10/rasor/rasor_current_amp.py +72 -0
- dodal/devices/i10/rasor/rasor_motors.py +62 -0
- dodal/devices/i10/rasor/rasor_scaler_cards.py +12 -0
- dodal/devices/i13_1/__init__.py +0 -0
- dodal/devices/i13_1/merlin.py +33 -0
- dodal/devices/i13_1/merlin_controller.py +52 -0
- dodal/devices/i13_1/merlin_io.py +17 -0
- dodal/devices/i24/beam_center.py +1 -1
- dodal/devices/p45.py +31 -20
- dodal/devices/p99/sample_stage.py +2 -28
- dodal/devices/robot.py +2 -2
- dodal/devices/s4_slit_gaps.py +8 -4
- dodal/devices/undulator_dcm.py +9 -11
- dodal/devices/util/lookup_tables.py +14 -10
- dodal/devices/zebra/__init__.py +0 -0
- dodal/devices/{zebra.py → zebra/zebra.py} +9 -33
- dodal/devices/zebra/zebra_constants_mapping.py +96 -0
- dodal/devices/zocalo/zocalo_interaction.py +2 -1
- dodal/devices/zocalo/zocalo_results.py +22 -2
- dodal/log.py +2 -2
- dodal/plans/wrapped.py +3 -3
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/LICENSE +0 -0
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.36.3.dist-info → dls_dodal-1.38.0.dist-info}/top_level.txt +0 -0
- /dodal/devices/{zebra_controlled_shutter.py → zebra/zebra_controlled_shutter.py} +0 -0
- /dodal/{devices/util → plans}/save_panda.py +0 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
from ophyd_async.core import (
|
|
5
|
+
AsyncStatus,
|
|
6
|
+
StandardReadableFormat,
|
|
7
|
+
StrictEnum,
|
|
8
|
+
)
|
|
9
|
+
from ophyd_async.epics.core import epics_signal_rw
|
|
10
|
+
|
|
11
|
+
from dodal.devices.current_amplifiers import CurrentAmp
|
|
12
|
+
from dodal.log import LOGGER
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Femto3xxGainTable(StrictEnum):
|
|
16
|
+
"""These are the sensitivity setting for Femto 3xx current amplifier"""
|
|
17
|
+
|
|
18
|
+
SEN_1 = "10^4"
|
|
19
|
+
SEN_2 = "10^5"
|
|
20
|
+
SEN_3 = "10^6"
|
|
21
|
+
SEN_4 = "10^7"
|
|
22
|
+
SEN_5 = "10^8"
|
|
23
|
+
SEN_6 = "10^9"
|
|
24
|
+
SEN_7 = "10^10"
|
|
25
|
+
SEN_8 = "10^11"
|
|
26
|
+
SEN_9 = "10^12"
|
|
27
|
+
SEN_10 = "10^13"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Femto3xxGainToCurrentTable(float, Enum):
|
|
31
|
+
"""These are the voltage to current setting for Femto 3xx current amplifier"""
|
|
32
|
+
|
|
33
|
+
SEN_1 = 1e4
|
|
34
|
+
SEN_2 = 1e5
|
|
35
|
+
SEN_3 = 1e6
|
|
36
|
+
SEN_4 = 1e7
|
|
37
|
+
SEN_5 = 1e8
|
|
38
|
+
SEN_6 = 1e9
|
|
39
|
+
SEN_7 = 1e10
|
|
40
|
+
SEN_8 = 1e11
|
|
41
|
+
SEN_9 = 1e12
|
|
42
|
+
SEN_10 = 1e13
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Femto3xxRaiseTime(float, Enum):
|
|
46
|
+
"""These are the gain dependent raise time(s) for Femto 3xx current amplifier"""
|
|
47
|
+
|
|
48
|
+
SEN_1 = 0.8e-3
|
|
49
|
+
SEN_2 = 0.8e-3
|
|
50
|
+
SEN_3 = 0.8e-3
|
|
51
|
+
SEN_4 = 0.8e-3
|
|
52
|
+
SEN_5 = 2.3e-3
|
|
53
|
+
SEN_6 = 2.3e-3
|
|
54
|
+
SEN_7 = 17e-3
|
|
55
|
+
SEN_8 = 17e-3
|
|
56
|
+
SEN_9 = 350e-3
|
|
57
|
+
SEN_10 = 350e-3
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class FemtoDDPCA(CurrentAmp):
|
|
61
|
+
"""
|
|
62
|
+
Femto current amplifier device, this class should cover all DDPCA Femto current
|
|
63
|
+
amplifiers, As the main different between all the DDPCA Femto is their gain table
|
|
64
|
+
and response time table.
|
|
65
|
+
Attributes:
|
|
66
|
+
gain (SignalRW): This is the epic signal that control current amplifier gain.
|
|
67
|
+
gain_table (strictEnum): The table epic use to set gain.
|
|
68
|
+
upperlimit (float): upperlimit of the current amplifier
|
|
69
|
+
lowerlimit (float): lowerlimit of the current amplifier
|
|
70
|
+
timeout (float): Maximum waiting time in second for setting gain.
|
|
71
|
+
raise_timetable (Enum): Table contain the minimum amount of time to wait after
|
|
72
|
+
changing gain.
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
prefix: str,
|
|
79
|
+
suffix: str,
|
|
80
|
+
gain_table: type[StrictEnum],
|
|
81
|
+
gain_to_current_table: type[Enum],
|
|
82
|
+
raise_timetable: type[Enum],
|
|
83
|
+
upperlimit: float = 8.8,
|
|
84
|
+
lowerlimit: float = 0.68,
|
|
85
|
+
timeout: float = 1,
|
|
86
|
+
name: str = "",
|
|
87
|
+
) -> None:
|
|
88
|
+
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
89
|
+
self.gain = epics_signal_rw(gain_table, prefix + suffix)
|
|
90
|
+
|
|
91
|
+
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
92
|
+
self.gain_table = gain_table
|
|
93
|
+
self.timeout = timeout
|
|
94
|
+
self.raise_timetable = raise_timetable
|
|
95
|
+
self.upperlimit = upperlimit
|
|
96
|
+
self.lowerlimit = lowerlimit
|
|
97
|
+
super().__init__(name=name, gain_conversion_table=gain_to_current_table)
|
|
98
|
+
|
|
99
|
+
@AsyncStatus.wrap
|
|
100
|
+
async def set(self, value) -> None:
|
|
101
|
+
if value not in [item.value for item in self.gain_conversion_table]:
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"Gain value {value} is not within {self.name} range."
|
|
104
|
+
+ "\n Available gain:"
|
|
105
|
+
+ f" {[f'{c.value:.0e}' for c in self.gain_conversion_table]}"
|
|
106
|
+
)
|
|
107
|
+
SEN_setting = self.gain_conversion_table(value).name
|
|
108
|
+
LOGGER.info(f"{self.name} gain change to {SEN_setting}:{value}")
|
|
109
|
+
|
|
110
|
+
await self.gain.set(
|
|
111
|
+
value=self.gain_table[SEN_setting].value,
|
|
112
|
+
timeout=self.timeout,
|
|
113
|
+
)
|
|
114
|
+
# wait for current amplifier's bandpass filter to settle.
|
|
115
|
+
await asyncio.sleep(self.raise_timetable[SEN_setting].value)
|
|
116
|
+
|
|
117
|
+
@AsyncStatus.wrap
|
|
118
|
+
async def increase_gain(self, value: int = 1) -> None:
|
|
119
|
+
current_gain = int((await self.get_gain()).name.split("_")[-1])
|
|
120
|
+
current_gain += value
|
|
121
|
+
if current_gain > len(self.gain_table):
|
|
122
|
+
raise ValueError("Gain at max value")
|
|
123
|
+
await self.set(self.gain_conversion_table[f"SEN_{current_gain}"])
|
|
124
|
+
|
|
125
|
+
@AsyncStatus.wrap
|
|
126
|
+
async def decrease_gain(self, value: int = 1) -> None:
|
|
127
|
+
current_gain = int((await self.get_gain()).name.split("_")[-1])
|
|
128
|
+
current_gain -= value
|
|
129
|
+
if current_gain < 1:
|
|
130
|
+
raise ValueError("Gain at min value")
|
|
131
|
+
await self.set(self.gain_conversion_table[f"SEN_{current_gain}"])
|
|
132
|
+
|
|
133
|
+
@AsyncStatus.wrap
|
|
134
|
+
async def get_gain(self) -> Enum:
|
|
135
|
+
return self.gain_conversion_table[(await self.gain.get_value()).name]
|
|
136
|
+
|
|
137
|
+
@AsyncStatus.wrap
|
|
138
|
+
async def get_upperlimit(self) -> float:
|
|
139
|
+
return self.upperlimit
|
|
140
|
+
|
|
141
|
+
@AsyncStatus.wrap
|
|
142
|
+
async def get_lowerlimit(self) -> float:
|
|
143
|
+
return self.lowerlimit
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
from ophyd_async.core import (
|
|
5
|
+
AsyncStatus,
|
|
6
|
+
StandardReadableFormat,
|
|
7
|
+
StrictEnum,
|
|
8
|
+
)
|
|
9
|
+
from ophyd_async.epics.core import epics_signal_rw
|
|
10
|
+
|
|
11
|
+
from dodal.devices.current_amplifiers.current_amplifier import CurrentAmp
|
|
12
|
+
from dodal.log import LOGGER
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SR570GainTable(StrictEnum):
|
|
16
|
+
"""Coarse/unit sensitivity setting for SR570 current amplifier"""
|
|
17
|
+
|
|
18
|
+
SEN_1 = "mA/V"
|
|
19
|
+
SEN_2 = "uA/V"
|
|
20
|
+
SEN_3 = "nA/V"
|
|
21
|
+
SEN_4 = "pA/V"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SR570FineGainTable(StrictEnum):
|
|
25
|
+
"""Fine sensitivity setting for SR570 current amplifier"""
|
|
26
|
+
|
|
27
|
+
SEN_1 = "1"
|
|
28
|
+
SEN_2 = "2"
|
|
29
|
+
SEN_3 = "5"
|
|
30
|
+
SEN_4 = "10"
|
|
31
|
+
SEN_5 = "20"
|
|
32
|
+
SEN_6 = "50"
|
|
33
|
+
SEN_7 = "100"
|
|
34
|
+
SEN_8 = "200"
|
|
35
|
+
SEN_9 = "500"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SR570RaiseTimeTable(float, Enum):
|
|
39
|
+
"""These are the gain dependent raise time(s) for SR570 current amplifier"""
|
|
40
|
+
|
|
41
|
+
SEN_1 = 1e-4
|
|
42
|
+
SEN_2 = 1e-2
|
|
43
|
+
SEN_3 = 0.15
|
|
44
|
+
SEN_4 = 0.2
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SR570FullGainTable(Enum):
|
|
48
|
+
"""Combined gain table, as each gain step is a combination of both coarse gain and
|
|
49
|
+
fine gain setting"""
|
|
50
|
+
|
|
51
|
+
SEN_1 = [SR570GainTable.SEN_1, SR570FineGainTable.SEN_1]
|
|
52
|
+
SEN_2 = [SR570GainTable.SEN_2, SR570FineGainTable.SEN_9]
|
|
53
|
+
SEN_3 = [SR570GainTable.SEN_2, SR570FineGainTable.SEN_8]
|
|
54
|
+
SEN_4 = [SR570GainTable.SEN_2, SR570FineGainTable.SEN_7]
|
|
55
|
+
SEN_5 = [SR570GainTable.SEN_2, SR570FineGainTable.SEN_6]
|
|
56
|
+
SEN_6 = [SR570GainTable.SEN_2, SR570FineGainTable.SEN_5]
|
|
57
|
+
SEN_7 = [SR570GainTable.SEN_2, SR570FineGainTable.SEN_4]
|
|
58
|
+
SEN_8 = [SR570GainTable.SEN_2, SR570FineGainTable.SEN_3]
|
|
59
|
+
SEN_9 = [SR570GainTable.SEN_2, SR570FineGainTable.SEN_2]
|
|
60
|
+
SEN_10 = [SR570GainTable.SEN_2, SR570FineGainTable.SEN_1]
|
|
61
|
+
SEN_11 = [SR570GainTable.SEN_3, SR570FineGainTable.SEN_9]
|
|
62
|
+
SEN_12 = [SR570GainTable.SEN_3, SR570FineGainTable.SEN_8]
|
|
63
|
+
SEN_13 = [SR570GainTable.SEN_3, SR570FineGainTable.SEN_7]
|
|
64
|
+
SEN_14 = [SR570GainTable.SEN_3, SR570FineGainTable.SEN_6]
|
|
65
|
+
SEN_15 = [SR570GainTable.SEN_3, SR570FineGainTable.SEN_5]
|
|
66
|
+
SEN_16 = [SR570GainTable.SEN_3, SR570FineGainTable.SEN_4]
|
|
67
|
+
SEN_17 = [SR570GainTable.SEN_3, SR570FineGainTable.SEN_3]
|
|
68
|
+
SEN_18 = [SR570GainTable.SEN_3, SR570FineGainTable.SEN_2]
|
|
69
|
+
SEN_19 = [SR570GainTable.SEN_3, SR570FineGainTable.SEN_1]
|
|
70
|
+
SEN_20 = [SR570GainTable.SEN_4, SR570FineGainTable.SEN_9]
|
|
71
|
+
SEN_21 = [SR570GainTable.SEN_4, SR570FineGainTable.SEN_8]
|
|
72
|
+
SEN_22 = [SR570GainTable.SEN_4, SR570FineGainTable.SEN_7]
|
|
73
|
+
SEN_23 = [SR570GainTable.SEN_4, SR570FineGainTable.SEN_6]
|
|
74
|
+
SEN_24 = [SR570GainTable.SEN_4, SR570FineGainTable.SEN_5]
|
|
75
|
+
SEN_25 = [SR570GainTable.SEN_4, SR570FineGainTable.SEN_4]
|
|
76
|
+
SEN_26 = [SR570GainTable.SEN_4, SR570FineGainTable.SEN_3]
|
|
77
|
+
SEN_27 = [SR570GainTable.SEN_4, SR570FineGainTable.SEN_2]
|
|
78
|
+
SEN_28 = [SR570GainTable.SEN_4, SR570FineGainTable.SEN_1]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class SR570GainToCurrentTable(float, Enum):
|
|
82
|
+
"""Conversion table for gain(sen) to current"""
|
|
83
|
+
|
|
84
|
+
SEN_1 = 1e3
|
|
85
|
+
SEN_2 = 2e3
|
|
86
|
+
SEN_3 = 5e3
|
|
87
|
+
SEN_4 = 1e4
|
|
88
|
+
SEN_5 = 2e4
|
|
89
|
+
SEN_6 = 5e4
|
|
90
|
+
SEN_7 = 1e5
|
|
91
|
+
SEN_8 = 2e5
|
|
92
|
+
SEN_9 = 5e5
|
|
93
|
+
SEN_10 = 1e6
|
|
94
|
+
SEN_11 = 2e6
|
|
95
|
+
SEN_12 = 5e6
|
|
96
|
+
SEN_13 = 1e7
|
|
97
|
+
SEN_14 = 2e7
|
|
98
|
+
SEN_15 = 5e7
|
|
99
|
+
SEN_16 = 1e8
|
|
100
|
+
SEN_17 = 2e8
|
|
101
|
+
SEN_18 = 5e8
|
|
102
|
+
SEN_19 = 1e9
|
|
103
|
+
SEN_20 = 2e9
|
|
104
|
+
SEN_21 = 5e9
|
|
105
|
+
SEN_22 = 1e10
|
|
106
|
+
SEN_23 = 2e10
|
|
107
|
+
SEN_24 = 5e10
|
|
108
|
+
SEN_25 = 1e11
|
|
109
|
+
SEN_26 = 2e11
|
|
110
|
+
SEN_27 = 5e11
|
|
111
|
+
SEN_28 = 1e12
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class SR570(CurrentAmp):
|
|
115
|
+
"""
|
|
116
|
+
SR570 current amplifier device. This is similar to Femto with the only different
|
|
117
|
+
is SR570 has two gain setting fine and coarse, therefore it requires extra
|
|
118
|
+
gain tables.
|
|
119
|
+
Attributes:
|
|
120
|
+
fine_gain (SignalRW): This is the epic signal that control SR570 fine gain.
|
|
121
|
+
coarse_gain (SignalRW): This is the epic signal that control SR570 coarse gain.
|
|
122
|
+
fine_gain_table (strictEnum): The table that fine_gain use to set gain.
|
|
123
|
+
coarse_gain_table (strictEnum): The table that coarse_gain use to set gain.
|
|
124
|
+
timeout (float): Maximum waiting time in second for setting gain.
|
|
125
|
+
raise_timetable (Enum): Table contain the amount of time to wait after
|
|
126
|
+
setting gain.
|
|
127
|
+
combined_table (Enum): Table that combine fine and coarse table into one.
|
|
128
|
+
gain (SignalRW([str]): Soft signal to store the member name of the current gain
|
|
129
|
+
setting in gain_conversion_table.
|
|
130
|
+
upperlimit (float): upperlimit of the current amplifier
|
|
131
|
+
lowerlimit (float): lowerlimit of the current amplifier
|
|
132
|
+
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
def __init__(
|
|
136
|
+
self,
|
|
137
|
+
prefix: str,
|
|
138
|
+
suffix: str,
|
|
139
|
+
fine_gain_table: type[StrictEnum],
|
|
140
|
+
coarse_gain_table: type[StrictEnum],
|
|
141
|
+
combined_table: type[Enum],
|
|
142
|
+
gain_to_current_table: type[Enum],
|
|
143
|
+
raise_timetable: type[Enum],
|
|
144
|
+
upperlimit: float = 4.8,
|
|
145
|
+
lowerlimit: float = 0.4,
|
|
146
|
+
timeout: float = 1,
|
|
147
|
+
name: str = "",
|
|
148
|
+
) -> None:
|
|
149
|
+
super().__init__(name=name, gain_conversion_table=gain_to_current_table)
|
|
150
|
+
|
|
151
|
+
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
152
|
+
self.fine_gain = epics_signal_rw(fine_gain_table, prefix + suffix + "1")
|
|
153
|
+
self.coarse_gain = epics_signal_rw(coarse_gain_table, prefix + suffix + "2")
|
|
154
|
+
|
|
155
|
+
self.fine_gain_table = fine_gain_table
|
|
156
|
+
self.coarse_gain_table = coarse_gain_table
|
|
157
|
+
self.timeout = timeout
|
|
158
|
+
self.raise_timetable = raise_timetable
|
|
159
|
+
self.combined_table = combined_table
|
|
160
|
+
self.upperlimit = upperlimit
|
|
161
|
+
self.lowerlimit = lowerlimit
|
|
162
|
+
|
|
163
|
+
@AsyncStatus.wrap
|
|
164
|
+
async def set(self, value) -> None:
|
|
165
|
+
if value not in [item.value for item in self.gain_conversion_table]:
|
|
166
|
+
raise ValueError(
|
|
167
|
+
f"Gain value {value} is not within {self.name} range."
|
|
168
|
+
+ "\n Available gain:"
|
|
169
|
+
+ f" {[f'{c.value:.0e}' for c in self.gain_conversion_table]}"
|
|
170
|
+
)
|
|
171
|
+
SEN_setting = self.gain_conversion_table(value).name
|
|
172
|
+
LOGGER.info(f"{self.name} gain change to {value}")
|
|
173
|
+
|
|
174
|
+
coarse_gain, fine_gain = self.combined_table[SEN_setting].value
|
|
175
|
+
await asyncio.gather(
|
|
176
|
+
self.fine_gain.set(value=fine_gain, timeout=self.timeout),
|
|
177
|
+
self.coarse_gain.set(value=coarse_gain, timeout=self.timeout),
|
|
178
|
+
)
|
|
179
|
+
await asyncio.sleep(self.raise_timetable[coarse_gain.name].value)
|
|
180
|
+
|
|
181
|
+
@AsyncStatus.wrap
|
|
182
|
+
async def increase_gain(self, value=3) -> None:
|
|
183
|
+
current_gain = int((await self.get_gain()).name.split("_")[-1])
|
|
184
|
+
current_gain += value
|
|
185
|
+
if current_gain > len(self.combined_table):
|
|
186
|
+
await self.set(
|
|
187
|
+
self.gain_conversion_table[f"SEN_{len(self.combined_table)}"]
|
|
188
|
+
)
|
|
189
|
+
raise ValueError("Gain at max value")
|
|
190
|
+
await self.set(self.gain_conversion_table[f"SEN_{current_gain}"])
|
|
191
|
+
|
|
192
|
+
@AsyncStatus.wrap
|
|
193
|
+
async def decrease_gain(self, value=3) -> None:
|
|
194
|
+
current_gain = int((await self.get_gain()).name.split("_")[-1])
|
|
195
|
+
current_gain -= value
|
|
196
|
+
if current_gain < 1:
|
|
197
|
+
await self.set(self.gain_conversion_table["SEN_1"])
|
|
198
|
+
raise ValueError("Gain at min value")
|
|
199
|
+
await self.set(self.gain_conversion_table[f"SEN_{current_gain}"])
|
|
200
|
+
|
|
201
|
+
@AsyncStatus.wrap
|
|
202
|
+
async def get_gain(self) -> Enum:
|
|
203
|
+
result = await asyncio.gather(
|
|
204
|
+
self.coarse_gain.get_value(), self.fine_gain.get_value()
|
|
205
|
+
)
|
|
206
|
+
return self.gain_conversion_table[self.combined_table(result).name]
|
|
207
|
+
|
|
208
|
+
@AsyncStatus.wrap
|
|
209
|
+
async def get_upperlimit(self) -> float:
|
|
210
|
+
return self.upperlimit
|
|
211
|
+
|
|
212
|
+
@AsyncStatus.wrap
|
|
213
|
+
async def get_lowerlimit(self) -> float:
|
|
214
|
+
return self.lowerlimit
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from ophyd_async.core import (
|
|
2
|
+
AsyncStatus,
|
|
3
|
+
StandardReadableFormat,
|
|
4
|
+
StrictEnum,
|
|
5
|
+
set_and_wait_for_other_value,
|
|
6
|
+
)
|
|
7
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
8
|
+
|
|
9
|
+
from dodal.devices.current_amplifiers import (
|
|
10
|
+
CurrentAmpCounter,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CountMode(StrictEnum):
|
|
15
|
+
AUTO = "AutoCount"
|
|
16
|
+
ONE_SHOT = "OneShot"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CountState(StrictEnum):
|
|
20
|
+
DONE = "Done"
|
|
21
|
+
COUNT = "Count" # type: ignore
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
COUNT_PER_VOLTAGE = 100000
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class StruckScaler(CurrentAmpCounter):
|
|
28
|
+
"""
|
|
29
|
+
StruckScaler is a counting card that record the output signal from a wide
|
|
30
|
+
range of detectors. This class contains the basic control to run the struckscaler
|
|
31
|
+
card together with a current amplifier. It has functions that provide conversion
|
|
32
|
+
between count and voltage.
|
|
33
|
+
Attributes:
|
|
34
|
+
readout(SignalR): Scaler card output.
|
|
35
|
+
count_mode (SignalR[CountMode]): Counting card setting.
|
|
36
|
+
count_time (SignalRW([float]): Count time.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self, prefix: str, suffix: str, count_per_volt=COUNT_PER_VOLTAGE, name: str = ""
|
|
41
|
+
):
|
|
42
|
+
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
43
|
+
self.readout = epics_signal_r(float, prefix + suffix)
|
|
44
|
+
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
45
|
+
self.count_mode = epics_signal_rw(CountMode, prefix + ":AutoCount")
|
|
46
|
+
self.count_time = epics_signal_rw(float, prefix + ".TP")
|
|
47
|
+
self.trigger_start = epics_signal_rw(CountState, prefix + ".CNT")
|
|
48
|
+
super().__init__(count_per_volt=count_per_volt, name=name)
|
|
49
|
+
|
|
50
|
+
@AsyncStatus.wrap
|
|
51
|
+
async def stage(self) -> None:
|
|
52
|
+
await self.count_mode.set(CountMode.ONE_SHOT)
|
|
53
|
+
|
|
54
|
+
@AsyncStatus.wrap
|
|
55
|
+
async def unstage(self) -> None:
|
|
56
|
+
await self.count_mode.set(CountMode.AUTO)
|
|
57
|
+
|
|
58
|
+
@AsyncStatus.wrap
|
|
59
|
+
async def trigger(self) -> None:
|
|
60
|
+
await set_and_wait_for_other_value(
|
|
61
|
+
set_signal=self.trigger_start,
|
|
62
|
+
set_value=CountState.COUNT,
|
|
63
|
+
match_signal=self.trigger_start,
|
|
64
|
+
match_value=CountState.COUNT,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@AsyncStatus.wrap
|
|
68
|
+
async def prepare(self, value) -> None:
|
|
69
|
+
await self.count_time.set(value)
|
|
70
|
+
|
|
71
|
+
async def get_count(self) -> float:
|
|
72
|
+
await self.trigger()
|
|
73
|
+
return await self.readout.get_value()
|
|
74
|
+
|
|
75
|
+
async def get_count_per_sec(self) -> float:
|
|
76
|
+
return await self.get_count() / await self.count_time.get_value()
|
|
77
|
+
|
|
78
|
+
async def get_voltage_per_sec(self) -> float:
|
|
79
|
+
return await self.get_count_per_sec() / self.count_per_volt
|
|
@@ -26,6 +26,21 @@ class DetectorSizeConstants:
|
|
|
26
26
|
ALL_DETECTORS[self.det_type_string] = self
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
PILATUS_TYPE_6M = "PILATUS_6M"
|
|
30
|
+
PILATUS_6M_DIMENSION_X = 423.636
|
|
31
|
+
PILATUS_6M_DIMENSION_Y = 434.644
|
|
32
|
+
PILATUS_6M_DIMENSION = DetectorSize(PILATUS_6M_DIMENSION_X, PILATUS_6M_DIMENSION_Y)
|
|
33
|
+
PIXELS_X_PILATUS_6M = 2463
|
|
34
|
+
PIXELS_Y_PILATUS_6M = 2527
|
|
35
|
+
PIXELS_PILATUS_6M = DetectorSize(PIXELS_X_PILATUS_6M, PIXELS_Y_PILATUS_6M)
|
|
36
|
+
PILATUS_6M_SIZE = DetectorSizeConstants(
|
|
37
|
+
PILATUS_TYPE_6M,
|
|
38
|
+
PILATUS_6M_DIMENSION,
|
|
39
|
+
PIXELS_PILATUS_6M,
|
|
40
|
+
PILATUS_6M_DIMENSION,
|
|
41
|
+
PIXELS_PILATUS_6M,
|
|
42
|
+
)
|
|
43
|
+
|
|
29
44
|
EIGER_TYPE_EIGER2_X_4M = "EIGER2_X_4M"
|
|
30
45
|
EIGER2_X_4M_DIMENSION_X = 155.1
|
|
31
46
|
EIGER2_X_4M_DIMENSION_Y = 162.15
|
dodal/devices/eiger_odin.py
CHANGED
|
@@ -91,10 +91,10 @@ class OdinNodesStatus(Device):
|
|
|
91
91
|
def wait_for_no_errors(self, timeout) -> dict[SubscriptionStatus, str]:
|
|
92
92
|
errors = {}
|
|
93
93
|
for node_number, node_pv in enumerate(self.nodes):
|
|
94
|
-
errors[
|
|
95
|
-
|
|
96
|
-
] = f"Filewriter {node_number} is in an error state with error message\
|
|
94
|
+
errors[await_value(node_pv.error_status, False, timeout)] = (
|
|
95
|
+
f"Filewriter {node_number} is in an error state with error message\
|
|
97
96
|
- {node_pv.error_message.get()}"
|
|
97
|
+
)
|
|
98
98
|
|
|
99
99
|
return errors
|
|
100
100
|
|
dodal/devices/fast_grid_scan.py
CHANGED
|
@@ -97,9 +97,14 @@ class GridScanParamsCommon(AbstractExperimentWithBeamParams):
|
|
|
97
97
|
"""Converts a grid position, given as steps in the x, y, z grid,
|
|
98
98
|
to a real motor position.
|
|
99
99
|
|
|
100
|
-
:
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
Args:
|
|
101
|
+
grid_position: The x, y, z position in grid steps. The origin is at the
|
|
102
|
+
centre of the first grid box
|
|
103
|
+
Returns:
|
|
104
|
+
The motor position this corresponds to.
|
|
105
|
+
Raises:
|
|
106
|
+
IndexError if the desired position is outside the grid.
|
|
107
|
+
"""
|
|
103
108
|
for position, axis in zip(
|
|
104
109
|
grid_position, [self.x_axis, self.y_axis, self.z_axis], strict=False
|
|
105
110
|
):
|
dodal/devices/flux.py
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
from
|
|
1
|
+
from ophyd_async.core import (
|
|
2
|
+
StandardReadable,
|
|
3
|
+
StandardReadableFormat,
|
|
4
|
+
)
|
|
5
|
+
from ophyd_async.epics.core import epics_signal_r
|
|
2
6
|
|
|
3
7
|
|
|
4
|
-
class Flux(
|
|
8
|
+
class Flux(StandardReadable):
|
|
5
9
|
"""Simple device to get the flux reading"""
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
def __init__(self, prefix: str, name="") -> None:
|
|
12
|
+
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
13
|
+
self.flux_reading = epics_signal_r(float, prefix + "SAMP")
|
|
14
|
+
super().__init__(name=name)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from asyncio import gather
|
|
2
|
+
from math import isclose
|
|
3
|
+
|
|
4
|
+
from ophyd_async.core import StandardReadable, StrictEnum
|
|
5
|
+
from ophyd_async.epics.motor import Motor
|
|
6
|
+
|
|
7
|
+
from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters
|
|
8
|
+
from dodal.common.signal_utils import create_hardware_backed_soft_signal
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BeamstopPositions(StrictEnum):
|
|
12
|
+
"""
|
|
13
|
+
Beamstop positions.
|
|
14
|
+
GDA supports Standard/High/Low resolution positions, as well as parked and
|
|
15
|
+
robot load however all 3 resolution positions are the same. We also
|
|
16
|
+
do not use the robot load position in Hyperion.
|
|
17
|
+
|
|
18
|
+
Until we support moving the beamstop it is only necessary to check whether the
|
|
19
|
+
beamstop is in beam or not.
|
|
20
|
+
|
|
21
|
+
See Also:
|
|
22
|
+
https://github.com/DiamondLightSource/mx-bluesky/issues/484
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
DATA_COLLECTION: The beamstop is in beam ready for data collection
|
|
26
|
+
UNKNOWN: The beamstop is in some other position, check the device motor
|
|
27
|
+
positions to determine it.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
DATA_COLLECTION = "Data Collection"
|
|
31
|
+
UNKNOWN = "Unknown"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Beamstop(StandardReadable):
|
|
35
|
+
"""
|
|
36
|
+
Beamstop for I03.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
x: beamstop x position in mm
|
|
40
|
+
y: beamstop y position in mm
|
|
41
|
+
z: beamstop z position in mm
|
|
42
|
+
selected_pos: Get the current position of the beamstop as an enum. Currently this
|
|
43
|
+
is read-only.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
prefix: str,
|
|
49
|
+
beamline_parameters: GDABeamlineParameters,
|
|
50
|
+
name: str = "",
|
|
51
|
+
):
|
|
52
|
+
with self.add_children_as_readables():
|
|
53
|
+
self.x_mm = Motor(prefix + "X")
|
|
54
|
+
self.y_mm = Motor(prefix + "Y")
|
|
55
|
+
self.z_mm = Motor(prefix + "Z")
|
|
56
|
+
self.selected_pos = create_hardware_backed_soft_signal(
|
|
57
|
+
BeamstopPositions, self._get_selected_position
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
self._in_beam_xyz_mm = [
|
|
61
|
+
float(beamline_parameters[f"in_beam_{axis}_STANDARD"])
|
|
62
|
+
for axis in ("x", "y", "z")
|
|
63
|
+
]
|
|
64
|
+
self._xyz_tolerance_mm = [
|
|
65
|
+
float(beamline_parameters[f"bs_{axis}_tolerance"])
|
|
66
|
+
for axis in ("x", "y", "z")
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
super().__init__(name)
|
|
70
|
+
|
|
71
|
+
async def _get_selected_position(self) -> BeamstopPositions:
|
|
72
|
+
current_pos = await gather(
|
|
73
|
+
self.x_mm.user_readback.get_value(),
|
|
74
|
+
self.y_mm.user_readback.get_value(),
|
|
75
|
+
self.z_mm.user_readback.get_value(),
|
|
76
|
+
)
|
|
77
|
+
if all(
|
|
78
|
+
isclose(axis_pos, axis_in_beam, abs_tol=axis_tolerance)
|
|
79
|
+
for axis_pos, axis_in_beam, axis_tolerance in zip(
|
|
80
|
+
current_pos, self._in_beam_xyz_mm, self._xyz_tolerance_mm, strict=False
|
|
81
|
+
)
|
|
82
|
+
):
|
|
83
|
+
return BeamstopPositions.DATA_COLLECTION
|
|
84
|
+
else:
|
|
85
|
+
return BeamstopPositions.UNKNOWN
|