Skip to content

Commit 7e11d47

Browse files
committed
Address review comments
1 parent 3bd44f4 commit 7e11d47

File tree

6 files changed

+137
-87
lines changed

6 files changed

+137
-87
lines changed

src/dodal/beamlines/i22.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ def waxs(
3939
)
4040

4141

42-
def panda(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> PandA:
42+
def panda_01(
43+
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
44+
) -> PandA:
4345
return device_instantiation(
4446
PandA, "panda-01", "-EA-PANDA-01:", wait_for_connection, fake_with_ophyd_sim
4547
)
@@ -53,12 +55,12 @@ def linkam(
5355
)
5456

5557

56-
def tetramm1(
58+
def incident_beam(
5759
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
5860
) -> TetrammDetector:
5961
return device_instantiation(
6062
TetrammDetector,
61-
"i0",
63+
"incident_beam",
6264
"-EA-XBPM-02:",
6365
wait_for_connection,
6466
fake_with_ophyd_sim,
@@ -67,12 +69,12 @@ def tetramm1(
6769
)
6870

6971

70-
def tetramm2(
72+
def transmitted_beam(
7173
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
7274
) -> TetrammDetector:
7375
return device_instantiation(
7476
TetrammDetector,
75-
"it",
77+
"transmitted_beam",
7678
"-EA-TTRM-02:",
7779
wait_for_connection,
7880
fake_with_ophyd_sim,

src/dodal/beamlines/p38.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ def d12(
3939
)
4040

4141

42-
def linkam3(
42+
def linkam(
4343
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
4444
) -> Linkam3:
4545
return device_instantiation(
46-
Linkam3, "linkam3", "-EA-LINKM-02:", wait_for_connection, fake_with_ophyd_sim
46+
Linkam3, "linkam", "-EA-LINKM-02:", wait_for_connection, fake_with_ophyd_sim
4747
)
4848

4949

@@ -60,7 +60,9 @@ def tetramm(
6060
)
6161

6262

63-
def panda(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> PandA:
63+
def panda_01(
64+
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
65+
) -> PandA:
6466
return device_instantiation(
6567
PandA, "panda", "-EA-PANDA-01:", wait_for_connection, fake_with_ophyd_sim
6668
)

src/dodal/devices/areadetector/adaravis.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,10 @@ def __init__(self, driver: AdAravisMakoDriver, gpio_number: int) -> None:
154154
}
155155

156156
def get_deadtime(self, exposure: float) -> float:
157-
return 0.002
157+
# Not found in technical specifications
158+
# May need to be discovered experimentally
159+
# Returning 0.001 as a safe non-zero default
160+
return 0.001
158161

159162
async def arm(
160163
self,

src/dodal/devices/linkam3.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,19 @@ class PumpControl(str, Enum):
1414

1515

1616
class Linkam3(StandardReadable):
17-
tolerance: float = 0.5
18-
"""
19-
The deadband around the setpoint within which the position is assumed to
20-
have been reached
17+
"""Device to represent a Linkam3 temperature controller
18+
19+
Attributes:
20+
tolerance (float): Deadband around the setpoint within which the position is assumed to have been reached
21+
settle_time (int): The delay between reaching the setpoint and the move being considered complete
22+
23+
Args:
24+
prefix (str): PV prefix for this device
25+
name (str): unique name for this device
2126
"""
2227

28+
tolerance: float = 0.5
2329
settle_time: int = 0
24-
"""The delay between reaching the setpoint and the move being considered complete"""
2530

2631
def __init__(self, prefix: str, name: str):
2732
self.temp = epics_signal_r(float, prefix + "TEMP:")
@@ -80,21 +85,25 @@ def set(self, new_position: float, timeout: Optional[float] = None) -> AsyncStat
8085
coro = asyncio.wait_for(self._move(new_position, watchers), timeout=timeout)
8186
return AsyncStatus(coro, watchers)
8287

83-
# TODO: Check bitshift order
88+
# TODO: Make use of values in Status.
89+
# https://github.com/DiamondLightSource/dodal/issues/338
90+
async def _is_nth_bit_set(self, n: int):
91+
return bool(int(await self.status.get_value()) & 1 << n)
92+
8493
async def in_error(self) -> bool:
85-
return bool(int(await self.status.get_value()) & 1)
94+
return self._is_nth_bit_set(0)
8695

8796
async def at_setpoint(self) -> bool:
88-
return bool(int(await self.status.get_value()) & 1 << 1)
97+
return self._is_nth_bit_set(1)
8998

9099
async def heater_on(self) -> bool:
91-
return bool(int(await self.status.get_value()) & 1 << 2)
100+
return self._is_nth_bit_set(2)
92101

93102
async def pump_on(self) -> bool:
94-
return bool(int(await self.status.get_value()) & 1 << 3)
103+
return self._is_nth_bit_set(3)
95104

96105
async def pump_auto(self) -> bool:
97-
return bool(int(await self.status.get_value()) & 1 << 4)
106+
return self._is_nth_bit_set(4)
98107

99108
async def locate(self) -> Location:
100109
return {

src/dodal/devices/tetramm.py

Lines changed: 72 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import asyncio
22
from enum import Enum
33
from math import floor
4-
from typing import Generator, Optional, Sequence
4+
from typing import Generator, Sequence
55

66
import bluesky.plan_stubs as bps
77
from bluesky.protocols import Hints
@@ -91,50 +91,60 @@ def __init__(
9191

9292

9393
class TetrammController(DetectorControl):
94+
"""Controller for a TetrAMM current monitor
95+
96+
Attributes:
97+
base_sample_rate (int): Fixed in hardware
98+
99+
Args:
100+
drv (TetrammDriver): A configured driver for the device
101+
maximum_readings_per_frame (int): Maximum number of readings per frame: actual readings may be lower if higher frame rate is required
102+
minimum_values_per_reading (int): Lower bound on the values that will be averaged to create a single reading
103+
readings_per_frame (int): Actual number of readings per frame.
104+
105+
"""
106+
107+
base_sample_rate: int = 100_000
108+
94109
def __init__(
95110
self,
96111
drv: TetrammDriver,
97-
minimum_values_per_reading=5,
98-
maximum_readings_per_frame=1_000,
99-
base_sample_rate=100_000,
100-
readings_per_frame=1_000,
112+
minimum_values_per_reading: int = 5,
113+
maximum_readings_per_frame: int = 1_000,
114+
readings_per_frame: int = 1_000,
101115
):
116+
# TODO: Are any of these also fixed by hardware constraints?
102117
self._drv = drv
103-
104-
self.base_sample_rate = base_sample_rate
105-
"""base rate - fixed in hardware"""
106-
107118
self.maximum_readings_per_frame = maximum_readings_per_frame
108-
"""
109-
Maximum number of readings per frame
110-
Actual readings may be lower if higher frame rate is required
111-
"""
112-
113119
self.minimum_values_per_reading = minimum_values_per_reading
114-
"""A lower bound on the values that will be averaged to create a single reading"""
115-
116120
self.readings_per_frame = readings_per_frame
117-
"""The number of readings per frame"""
118121

119-
def get_deadtime(self, _exposure: float) -> float:
120-
return 0.001 # Picked from a hat
122+
def get_deadtime(self, exposure: float) -> float:
123+
# Not found in technical specifications
124+
# May need to be discovered experimentally
125+
# Returning 0.001 as a safe non-zero default
126+
return 0.001
121127

122128
async def arm(
123129
self,
124-
trigger: DetectorTrigger = DetectorTrigger.internal,
125-
num: int = 0,
126-
exposure: Optional[float] = None,
130+
trigger: DetectorTrigger,
131+
num: int,
132+
exposure: float,
127133
) -> AsyncStatus:
128-
"""Arms the tetramm.
134+
"""Arms the TetrAMM
135+
136+
Args:
137+
trigger (DetectorTrigger): Trigger type: supports edge_trigger, constant_gate
138+
num (int): ignored
139+
exposure (float): Exposure time in seconds
129140
130-
Note that num is meaningless in this context, and is ignored.
141+
Raises:
142+
ValueError: If DetectorTrigger is not supported
131143
"""
132-
if exposure is None:
133-
raise ValueError("Exposure time is required")
134144
if trigger not in {DetectorTrigger.edge_trigger, DetectorTrigger.constant_gate}:
135145
raise ValueError("Only edge triggers are supported")
136146

137-
# trigger mode must be set first and on it's own!
147+
# trigger mode must be set first and on its own!
138148
await self._drv.trigger_mode.set(TetrammTrigger.ExtTrigger)
139149

140150
await asyncio.gather(
@@ -148,17 +158,31 @@ async def arm(
148158
async def disarm(self):
149159
await stop_busy_record(self._drv.acquire, 0, timeout=1)
150160

151-
async def set_frame_time(self, seconds):
152-
# It may not always be possible to set the exact collection time if the
153-
# exposure is not a multiple of the base sample rate. In this case it
154-
# will always be the closest collection time *below* the requested time
155-
# to ensure that triggers are not missed.
156-
values_per_reading = int(
157-
floor(seconds * self.base_sample_rate / self.readings_per_frame)
161+
async def set_frame_time(self, frame_time: float):
162+
"""Tries to set the exposure time of a single frame.
163+
164+
As during the exposure time, the device must collect an integer number
165+
of readings, in the case where the frame_time is not a multiple of the base
166+
sample rate, it will be lowered to the prior multiple ot ensure triggers
167+
are not missed.
168+
169+
Args:
170+
frame_time (float): The time for a single frame in seconds
171+
172+
Raises:
173+
ValueError: If frame_time is too low to collect the required number
174+
of readings per frame.
175+
"""
176+
177+
values_per_reading: int = int(
178+
frame_time * self.base_sample_rate / self.readings_per_frame
158179
)
180+
159181
if values_per_reading < self.minimum_values_per_reading:
160182
raise ValueError(
161-
f"Exposure ({seconds}) too short to collect required number of readings {self.readings_per_frame}. Values per reading is {values_per_reading}, seconds is : {seconds}"
183+
f"frame_time {frame_time} is too low to collect at least \
184+
{self.minimum_values_per_reading} values per reading, at \
185+
{self.readings_per_frame} readings per frame."
162186
)
163187
await self._drv.values_per_reading.set(values_per_reading)
164188

@@ -184,9 +208,6 @@ def minimum_frame_time(self, frame: float):
184208
)
185209

186210

187-
# TODO: need to change this name.
188-
MAX_CHANNELS = 11
189-
190211
IDLE_TETRAMM = {
191212
"drv.acquire": 0,
192213
}
@@ -230,13 +251,16 @@ def free_tetramm(dev: TetrammDriver) -> Generator[Msg, None, None]:
230251

231252

232253
class TetrammShapeProvider(ShapeProvider):
254+
max_channels = 11
255+
233256
def __init__(self, controller: TetrammController) -> None:
234257
self.controller = controller
235258

236259
async def __call__(self) -> Sequence[int]:
237-
return [MAX_CHANNELS, self.controller.readings_per_frame]
260+
return [self.max_channels, self.controller.readings_per_frame]
238261

239262

263+
# TODO: Support MeanValue signals https://github.com/DiamondLightSource/dodal/issues/337
240264
class TetrammDetector(StandardDetector):
241265
def __init__(
242266
self,
@@ -245,31 +269,23 @@ def __init__(
245269
name: str,
246270
**scalar_sigs: str,
247271
) -> None:
248-
drv = TetrammDriver(prefix + "DRV:")
249-
hdf = NDFileHDF(prefix + "HDF5:")
250-
251-
self.drv = drv
252-
self.hdf = hdf
253-
254-
# TODO: how to make the below, readable signals?
255-
# self.current_1 = ad_r(float, prefix + ":Cur1:MeanValue")
256-
# self.current_2 = ad_r(float, prefix + ":Cur2:MeanValue")
257-
# self.current_3 = ad_r(float, prefix + ":Cur3:MeanValue")
258-
# self.current_4 = ad_r(float, prefix + ":Cur4:MeanValue")
259-
260-
# self.position_x = ad_r(float, prefix + ":PosX:MeanValue")
261-
# self.position_y = ad_r(float, prefix + ":PosY:MeanValue")
262-
controller = TetrammController(drv)
272+
self.drv = TetrammDriver(prefix + "DRV:")
273+
self.hdf = NDFileHDF(prefix + "HDF5:")
274+
controller = TetrammController(self.drv)
263275
super().__init__(
264276
controller,
265277
HDFWriter(
266-
hdf,
278+
self.hdf,
267279
directory_provider,
268280
lambda: self.name,
269281
TetrammShapeProvider(controller),
270282
**scalar_sigs,
271283
),
272-
[drv.values_per_reading, drv.averaging_time, drv.sample_time],
284+
[
285+
self.drv.values_per_reading,
286+
self.drv.averaging_time,
287+
self.drv.sample_time,
288+
],
273289
name,
274290
)
275291

0 commit comments

Comments
 (0)