Skip to content

Commit

Permalink
added number_of_frames instead of iterations
Browse files Browse the repository at this point in the history
  • Loading branch information
ZohebShaikh committed Sep 17, 2024
1 parent 989beeb commit c52e34b
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 20 deletions.
46 changes: 34 additions & 12 deletions src/ophyd_async/core/_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,16 @@ class DetectorTrigger(str, Enum):
class TriggerInfo(BaseModel):
"""Minimal set of information required to setup triggering on a detector"""

#: Number of triggers that will be sent, 0 means infinite
number: int = Field(ge=0)
#: Number of triggers that will be sent, (0 means infinite) Can be:
# - A single interger or
# - A list of intergers for mulitple triggers
# Example for tomography: TriggerInfo(number=[2,3,100,3])
#: This would trigger:
#: - 2 times for dark field images
#: - 3 times for initial flat field images
#: - 1000 times for projections
#: - 3 times for final flat field images
number: int | list[int]
#: Sort of triggers that will be sent
trigger: DetectorTrigger = Field(default=DetectorTrigger.internal)
#: What is the minimum deadtime between triggers
Expand All @@ -65,10 +73,6 @@ class TriggerInfo(BaseModel):
#: e.g. if num=10 and multiplier=5 then the detector will take 10 frames,
#: but publish 2 indices, and describe() will show a shape of (5, h, w)
multiplier: int = 1
#: The number of times the detector can go through a complete cycle of kickoff and
#: complete without needing to re-arm. This is important for detectors where the
#: process of arming is expensive in terms of time
iteration: int = 1


class DetectorControl(ABC):
Expand Down Expand Up @@ -201,6 +205,8 @@ def __init__(
self._iterations_completed: int = 0
self._intial_frame: int
self._last_frame: int
self._total_frames: int

super().__init__(name)

@property
Expand Down Expand Up @@ -307,7 +313,18 @@ async def prepare(self, value: TriggerInfo) -> None:
)
self._trigger_info = value
self._initial_frame = await self.writer.get_indices_written()
self._last_frame = self._initial_frame + self._trigger_info.number
self._scan_index: int = 0
if isinstance(self._trigger_info.number, list):
assert all(
frame >= 0 and type(frame) is int for frame in self._trigger_info.number
), "Number of frames can only be greater than or equal to 0"
self._total_frames = sum(self._trigger_info.number)
else:
assert (
self._trigger_info.number >= 0
), "Number of frames can only be greater than or equal to 0"
self._total_frames = self._trigger_info.number
self._last_frame = self._initial_frame + self._total_frames
self._describe, _ = await asyncio.gather(
self.writer.open(value.multiplier), self.controller.prepare(value)
)
Expand All @@ -318,8 +335,8 @@ async def prepare(self, value: TriggerInfo) -> None:
@AsyncStatus.wrap
async def kickoff(self):
assert self._trigger_info, "Prepare must be called before kickoff!"
if self._iterations_completed >= self._trigger_info.iteration:
raise Exception(f"Kickoff called more than {self._trigger_info.iteration}")
if self._iterations_completed >= self._total_frames:
raise Exception(f"Kickoff called more than {self._total_frames}")
self._iterations_completed += 1

@WatchableAsyncStatus.wrap
Expand All @@ -342,9 +359,14 @@ async def complete(self):
precision=0,
time_elapsed=time.monotonic() - self._fly_start,
)
if index >= self._trigger_info.number:
break
if self._iterations_completed == self._trigger_info.iteration:
if isinstance(self._trigger_info.number, list):
if index >= self._trigger_info.number[self._scan_index]:
self._scan_index += 1
break
else:
if index >= self._trigger_info.number:
break
if self._iterations_completed == self._total_frames:
await self.controller.wait_for_idle()

async def describe_collect(self) -> Dict[str, DataKey]:
Expand Down
19 changes: 13 additions & 6 deletions src/ophyd_async/plan_stubs/_fly.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,12 @@ def prepare_static_pcomp_flyer_and_detectors(
def prepare_static_seq_table_flyer_and_detectors_with_same_trigger(
flyer: StandardFlyer[SeqTableInfo],
detectors: List[StandardDetector],
number_of_frames: int,
number_of_frames: int | list[int],
exposure: float,
shutter_time: float,
repeats: int = 1,
period: float = 0.0,
frame_timeout: Optional[float] = None,
iteration: int = 1,
):
"""Prepare a hardware triggered flyable and one or more detectors.
Expand All @@ -62,16 +61,24 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger(
raise ValueError("No detectors provided. There must be at least one.")

deadtime = max(det.controller.get_deadtime(exposure) for det in detectors)
if isinstance(number_of_frames, list):
assert (
[number_of_frames[0]] * len(number_of_frames) == number_of_frames
), "In fly scan number of frames should be same for each iteration"
number_of_frames = [frames * repeats for frames in number_of_frames]
single_frame = number_of_frames[0]
else:
number_of_frames = number_of_frames * repeats
single_frame = number_of_frames

trigger_info = TriggerInfo(
number=number_of_frames * repeats,
number=number_of_frames,
trigger=DetectorTrigger.constant_gate,
deadtime=deadtime,
livetime=exposure,
frame_timeout=frame_timeout,
iteration=iteration,
)
trigger_time = number_of_frames * (exposure + deadtime)
trigger_time = single_frame * (exposure + deadtime)
pre_delay = max(period - 2 * shutter_time - trigger_time, 0)

table = (
Expand All @@ -84,7 +91,7 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger(
+
# Keeping shutter open, do N triggers
SeqTable.row(
repeats=number_of_frames,
repeats=single_frame,
time1=in_micros(exposure),
outa1=True,
outb1=True,
Expand Down
3 changes: 1 addition & 2 deletions tests/fastcs/panda/test_hdf_panda.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,9 @@ def flying_plan():
yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( # noqa: E501
flyer,
[mock_hdf_panda],
number_of_frames=1,
number_of_frames=[1] * iteration,
exposure=exposure,
shutter_time=shutter_time,
iteration=iteration,
)

yield from bps.declare_stream(mock_hdf_panda, name="main_stream", collect=True)
Expand Down

0 comments on commit c52e34b

Please sign in to comment.