From fac624be926bc422714a48c676b414010362708b Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Wed, 8 May 2024 12:00:19 +0100 Subject: [PATCH 01/34] Add initial fly plan stub --- src/ophyd_async/planstubs/fly.py | 59 ++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/ophyd_async/planstubs/fly.py diff --git a/src/ophyd_async/planstubs/fly.py b/src/ophyd_async/planstubs/fly.py new file mode 100644 index 0000000000..c09c6ceb6e --- /dev/null +++ b/src/ophyd_async/planstubs/fly.py @@ -0,0 +1,59 @@ +from typing import Optional + +import bluesky.plan_stubs as bps + +from ophyd_async.core.detector import StandardDetector +from ophyd_async.core.flyer import HardwareTriggeredFlyable +from ophyd_async.planstubs import ( + prepare_static_seq_table_flyer_and_detectors_with_same_trigger, +) + + +def fly( + stream_name: str, + detector_list: tuple[StandardDetector], + flyer: HardwareTriggeredFlyable, + number_of_frames: int, + exposure: int, + shutter_time: float, + repeats: Optional[int], + period: Optional[float], +): + """Run a scan wth a flyer and multiple detectors. + + The standard basic flow for a flyscan. + + 1. Set up and prepare a trigger + + 2. fly and collect + + """ + deadtime = max(det.controller.get_deadtime(1) for det in detector_list) + + # Set up scan and prepare trigger + yield from bps.stage_all(*detector_list, flyer) + yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( + flyer, + detector_list, + number_of_frames, + width=exposure, + deadtime=deadtime, + shutter_time=shutter_time, + ) + yield from bps.open_run() + yield from bps.declare_stream(*detector_list, name=stream_name, collect=True) + + # fly and collect + yield from bps.kickoff(flyer, *detector_list) + yield from bps.complete(flyer, *detector_list, group="complete") + + done = False + while not done: + yield from bps.wait(group="complete", timeout=0.5) + yield from bps.collect(*detector_list, name=stream_name) + + yield from bps.wait(group="complete") + + # Finish + yield from bps.close_run() + yield from bps.unstage_all(flyer, *detector_list) From aab41fc9b9b5294dafc390d50b213f38fa9e2c43 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Wed, 8 May 2024 14:53:30 +0100 Subject: [PATCH 02/34] Move repeats and periods temporarily --- src/ophyd_async/planstubs/fly.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ophyd_async/planstubs/fly.py b/src/ophyd_async/planstubs/fly.py index c09c6ceb6e..44cf3582dc 100644 --- a/src/ophyd_async/planstubs/fly.py +++ b/src/ophyd_async/planstubs/fly.py @@ -30,6 +30,8 @@ def fly( """ deadtime = max(det.controller.get_deadtime(1) for det in detector_list) + # sort repeats and period + # Set up scan and prepare trigger yield from bps.stage_all(*detector_list, flyer) yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( @@ -39,13 +41,15 @@ def fly( width=exposure, deadtime=deadtime, shutter_time=shutter_time, + repeats=repeats, + period=period, ) yield from bps.open_run() yield from bps.declare_stream(*detector_list, name=stream_name, collect=True) # fly and collect - yield from bps.kickoff(flyer, *detector_list) - yield from bps.complete(flyer, *detector_list, group="complete") + yield from bps.kickoff_all(flyer, *detector_list) + yield from bps.complete_all(flyer, *detector_list, group="complete") done = False while not done: From dc9e179ec2ea7c5d26237e18455e321d94fd3931 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 10:48:53 +0100 Subject: [PATCH 03/34] Remove staging and open/close run --- src/ophyd_async/planstubs/fly.py | 34 +++++++++++--------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/ophyd_async/planstubs/fly.py b/src/ophyd_async/planstubs/fly.py index 44cf3582dc..1520987398 100644 --- a/src/ophyd_async/planstubs/fly.py +++ b/src/ophyd_async/planstubs/fly.py @@ -1,5 +1,3 @@ -from typing import Optional - import bluesky.plan_stubs as bps from ophyd_async.core.detector import StandardDetector @@ -16,24 +14,23 @@ def fly( number_of_frames: int, exposure: int, shutter_time: float, - repeats: Optional[int], - period: Optional[float], + repeats: int = 1, + period: float = 0.0, ): """Run a scan wth a flyer and multiple detectors. - The standard basic flow for a flyscan. + The standard basic flow for a flyscan: - 1. Set up and prepare a trigger + - Set up the flyer with a static sequence table and detectors with a trigger + - Declare the stream and kickoff the scan + - Collect while completing - 2. fly and collect + This needs to be used in a plan that instantates detectors and a flyer, + stages/unstages the devices and opens and closes the run. """ - deadtime = max(det.controller.get_deadtime(1) for det in detector_list) - - # sort repeats and period - # Set up scan and prepare trigger - yield from bps.stage_all(*detector_list, flyer) + deadtime = max(det.controller.get_deadtime(1) for det in detector_list) yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( flyer, detector_list, @@ -44,20 +41,13 @@ def fly( repeats=repeats, period=period, ) - yield from bps.open_run() yield from bps.declare_stream(*detector_list, name=stream_name, collect=True) - - # fly and collect yield from bps.kickoff_all(flyer, *detector_list) + + # collect_while_completing yield from bps.complete_all(flyer, *detector_list, group="complete") done = False while not done: - yield from bps.wait(group="complete", timeout=0.5) + done = yield from bps.wait(group="complete", timeout=0.5) yield from bps.collect(*detector_list, name=stream_name) - - yield from bps.wait(group="complete") - - # Finish - yield from bps.close_run() - yield from bps.unstage_all(flyer, *detector_list) From e1784f3c3c10854c27944803fd7c4d98b1fb9df5 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 10:57:35 +0100 Subject: [PATCH 04/34] Make number of frames more explicit --- src/ophyd_async/planstubs/prepare_trigger_and_dets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ophyd_async/planstubs/prepare_trigger_and_dets.py b/src/ophyd_async/planstubs/prepare_trigger_and_dets.py index 541c61faac..89c914c652 100644 --- a/src/ophyd_async/planstubs/prepare_trigger_and_dets.py +++ b/src/ophyd_async/planstubs/prepare_trigger_and_dets.py @@ -12,7 +12,7 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( flyer: HardwareTriggeredFlyable[SeqTableInfo], detectors: List[StandardDetector], - num: int, + number_of_frames: int, width: float, deadtime: float, shutter_time: float, @@ -20,13 +20,13 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( period: float = 0.0, ): trigger_info = TriggerInfo( - num=num * repeats, + number_of_frames=number_of_frames * repeats, trigger=DetectorTrigger.constant_gate, deadtime=deadtime, livetime=width, ) - trigger_time = num * (width + deadtime) + trigger_time = number_of_frames * (width + deadtime) pre_delay = max(period - 2 * shutter_time - trigger_time, 0) table: SeqTable = seq_table_from_rows( @@ -38,7 +38,7 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( ), # Keeping shutter open, do N triggers SeqTableRow( - repeats=num, + repeats=number_of_frames, time1=in_micros(width), outa1=True, outb1=True, From b5b5544bfce747d6d0edc5b491846a6aa2c4c8a2 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 10:58:50 +0100 Subject: [PATCH 05/34] Change width to exposure --- src/ophyd_async/planstubs/prepare_trigger_and_dets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ophyd_async/planstubs/prepare_trigger_and_dets.py b/src/ophyd_async/planstubs/prepare_trigger_and_dets.py index 89c914c652..2d0676291d 100644 --- a/src/ophyd_async/planstubs/prepare_trigger_and_dets.py +++ b/src/ophyd_async/planstubs/prepare_trigger_and_dets.py @@ -13,7 +13,7 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( flyer: HardwareTriggeredFlyable[SeqTableInfo], detectors: List[StandardDetector], number_of_frames: int, - width: float, + exposure: float, deadtime: float, shutter_time: float, repeats: int = 1, @@ -23,10 +23,10 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( number_of_frames=number_of_frames * repeats, trigger=DetectorTrigger.constant_gate, deadtime=deadtime, - livetime=width, + livetime=exposure, ) - trigger_time = number_of_frames * (width + deadtime) + trigger_time = number_of_frames * (exposure + deadtime) pre_delay = max(period - 2 * shutter_time - trigger_time, 0) table: SeqTable = seq_table_from_rows( @@ -39,7 +39,7 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( # Keeping shutter open, do N triggers SeqTableRow( repeats=number_of_frames, - time1=in_micros(width), + time1=in_micros(exposure), outa1=True, outb1=True, time2=in_micros(deadtime), From a4a6605497a3ccb8775f0c9a43ffce9fbdb708c8 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 10:59:27 +0100 Subject: [PATCH 06/34] Change detector_list to detectors --- src/ophyd_async/planstubs/fly.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/ophyd_async/planstubs/fly.py b/src/ophyd_async/planstubs/fly.py index 1520987398..4b7de7b9eb 100644 --- a/src/ophyd_async/planstubs/fly.py +++ b/src/ophyd_async/planstubs/fly.py @@ -1,7 +1,10 @@ +from typing import List + import bluesky.plan_stubs as bps from ophyd_async.core.detector import StandardDetector from ophyd_async.core.flyer import HardwareTriggeredFlyable +from ophyd_async.panda._trigger import SeqTableInfo from ophyd_async.planstubs import ( prepare_static_seq_table_flyer_and_detectors_with_same_trigger, ) @@ -9,8 +12,8 @@ def fly( stream_name: str, - detector_list: tuple[StandardDetector], - flyer: HardwareTriggeredFlyable, + detectors: List[StandardDetector], + flyer: HardwareTriggeredFlyable[SeqTableInfo], number_of_frames: int, exposure: int, shutter_time: float, @@ -30,10 +33,10 @@ def fly( """ # Set up scan and prepare trigger - deadtime = max(det.controller.get_deadtime(1) for det in detector_list) + deadtime = max(det.controller.get_deadtime(exposure) for det in detectors) yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( flyer, - detector_list, + detectors, number_of_frames, width=exposure, deadtime=deadtime, @@ -41,13 +44,13 @@ def fly( repeats=repeats, period=period, ) - yield from bps.declare_stream(*detector_list, name=stream_name, collect=True) - yield from bps.kickoff_all(flyer, *detector_list) + yield from bps.declare_stream(*detectors, name=stream_name, collect=True) + yield from bps.kickoff_all(flyer, *detectors) # collect_while_completing - yield from bps.complete_all(flyer, *detector_list, group="complete") + yield from bps.complete_all(flyer, *detectors, group="complete") done = False while not done: done = yield from bps.wait(group="complete", timeout=0.5) - yield from bps.collect(*detector_list, name=stream_name) + yield from bps.collect(*detectors, name=stream_name) From 078427ed9cd27728a4aeb33ae0a7b2f968c5849f Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 11:01:54 +0100 Subject: [PATCH 07/34] Correct trigger info args --- src/ophyd_async/planstubs/prepare_trigger_and_dets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ophyd_async/planstubs/prepare_trigger_and_dets.py b/src/ophyd_async/planstubs/prepare_trigger_and_dets.py index 2d0676291d..e7aec55799 100644 --- a/src/ophyd_async/planstubs/prepare_trigger_and_dets.py +++ b/src/ophyd_async/planstubs/prepare_trigger_and_dets.py @@ -20,7 +20,7 @@ def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( period: float = 0.0, ): trigger_info = TriggerInfo( - number_of_frames=number_of_frames * repeats, + num=number_of_frames * repeats, trigger=DetectorTrigger.constant_gate, deadtime=deadtime, livetime=exposure, From 133b53d265ff675dd525774890090e90f508ff33 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 11:03:57 +0100 Subject: [PATCH 08/34] Fix args in flyer tests --- tests/panda/test_hdf_panda.py | 4 ++-- tests/test_flyer_with_panda.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/panda/test_hdf_panda.py b/tests/panda/test_hdf_panda.py index 626e272bd3..724ae7b2bb 100644 --- a/tests/panda/test_hdf_panda.py +++ b/tests/panda/test_hdf_panda.py @@ -80,8 +80,8 @@ def flying_plan(): yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( flyer, [mock_hdf_panda], - num=1, - width=exposure, + number_of_frames=1, + exposure=exposure, deadtime=mock_hdf_panda.controller.get_deadtime(1), shutter_time=shutter_time, ) diff --git a/tests/test_flyer_with_panda.py b/tests/test_flyer_with_panda.py index ef386471c9..4f5418722a 100644 --- a/tests/test_flyer_with_panda.py +++ b/tests/test_flyer_with_panda.py @@ -170,8 +170,8 @@ def flying_plan(): yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( flyer, detector_list, - num=1, - width=exposure, + number_of_frames=1, + exposure=exposure, deadtime=deadtime, shutter_time=shutter_time, ) From deceab63a8f0f13e19e96d9491bd3d3ce2ec61e7 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 11:06:44 +0100 Subject: [PATCH 09/34] Change stub name and add to init --- src/ophyd_async/planstubs/__init__.py | 2 ++ src/ophyd_async/planstubs/{fly.py => fly_and_collect.py} | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) rename src/ophyd_async/planstubs/{fly.py => fly_and_collect.py} (98%) diff --git a/src/ophyd_async/planstubs/__init__.py b/src/ophyd_async/planstubs/__init__.py index d97ec112e2..a6334d30e0 100644 --- a/src/ophyd_async/planstubs/__init__.py +++ b/src/ophyd_async/planstubs/__init__.py @@ -1,3 +1,4 @@ +from .fly_and_collect import fly_and_collect from .ensure_connected import ensure_connected from .prepare_trigger_and_dets import ( prepare_static_seq_table_flyer_and_detectors_with_same_trigger, @@ -5,5 +6,6 @@ __all__ = [ "prepare_static_seq_table_flyer_and_detectors_with_same_trigger", + "fly_and_collect", "ensure_connected", ] diff --git a/src/ophyd_async/planstubs/fly.py b/src/ophyd_async/planstubs/fly_and_collect.py similarity index 98% rename from src/ophyd_async/planstubs/fly.py rename to src/ophyd_async/planstubs/fly_and_collect.py index 4b7de7b9eb..1ff151065b 100644 --- a/src/ophyd_async/planstubs/fly.py +++ b/src/ophyd_async/planstubs/fly_and_collect.py @@ -10,7 +10,7 @@ ) -def fly( +def fly_and_collect( stream_name: str, detectors: List[StandardDetector], flyer: HardwareTriggeredFlyable[SeqTableInfo], From 7c4992d938e5df554372d6c611bfad1922f004e5 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 11:08:56 +0100 Subject: [PATCH 10/34] Update import format --- tests/panda/test_hdf_panda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/panda/test_hdf_panda.py b/tests/panda/test_hdf_panda.py index 724ae7b2bb..c98315975a 100644 --- a/tests/panda/test_hdf_panda.py +++ b/tests/panda/test_hdf_panda.py @@ -13,7 +13,7 @@ from ophyd_async.panda import HDFPanda from ophyd_async.panda._trigger import StaticSeqTableTriggerLogic from ophyd_async.panda.writers._hdf_writer import Capture -from ophyd_async.planstubs.prepare_trigger_and_dets import ( +from ophyd_async.planstubs import ( prepare_static_seq_table_flyer_and_detectors_with_same_trigger, ) From 506bc4bdbf6477b22c952972e37e1db09ff4333c Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 11:09:49 +0100 Subject: [PATCH 11/34] Update fly_and_collect docstring --- src/ophyd_async/planstubs/fly_and_collect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ophyd_async/planstubs/fly_and_collect.py b/src/ophyd_async/planstubs/fly_and_collect.py index 1ff151065b..599794a540 100644 --- a/src/ophyd_async/planstubs/fly_and_collect.py +++ b/src/ophyd_async/planstubs/fly_and_collect.py @@ -29,7 +29,7 @@ def fly_and_collect( - Collect while completing This needs to be used in a plan that instantates detectors and a flyer, - stages/unstages the devices and opens and closes the run. + stages/unstages the devices, and opens and closes the run. """ # Set up scan and prepare trigger From e41f509683d89892c91f48efc70cfe639c286fc9 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 13:06:49 +0100 Subject: [PATCH 12/34] Move stubs into one file to avoid import issues --- src/ophyd_async/planstubs/__init__.py | 8 +- src/ophyd_async/planstubs/fly.py | 113 ++++++++++++++++++ src/ophyd_async/planstubs/fly_and_collect.py | 56 --------- .../planstubs/prepare_trigger_and_dets.py | 57 --------- 4 files changed, 117 insertions(+), 117 deletions(-) create mode 100644 src/ophyd_async/planstubs/fly.py delete mode 100644 src/ophyd_async/planstubs/fly_and_collect.py delete mode 100644 src/ophyd_async/planstubs/prepare_trigger_and_dets.py diff --git a/src/ophyd_async/planstubs/__init__.py b/src/ophyd_async/planstubs/__init__.py index a6334d30e0..4834685b78 100644 --- a/src/ophyd_async/planstubs/__init__.py +++ b/src/ophyd_async/planstubs/__init__.py @@ -1,11 +1,11 @@ -from .fly_and_collect import fly_and_collect -from .ensure_connected import ensure_connected -from .prepare_trigger_and_dets import ( +from .fly import ( + fly_and_collect, prepare_static_seq_table_flyer_and_detectors_with_same_trigger, ) +from .ensure_connected import ensure_connected __all__ = [ - "prepare_static_seq_table_flyer_and_detectors_with_same_trigger", "fly_and_collect", + "prepare_static_seq_table_flyer_and_detectors_with_same_trigger", "ensure_connected", ] diff --git a/src/ophyd_async/planstubs/fly.py b/src/ophyd_async/planstubs/fly.py new file mode 100644 index 0000000000..c4cb5180d4 --- /dev/null +++ b/src/ophyd_async/planstubs/fly.py @@ -0,0 +1,113 @@ +from typing import List + +import bluesky.plan_stubs as bps + +from ophyd_async.core.detector import DetectorTrigger, StandardDetector, TriggerInfo +from ophyd_async.core.flyer import HardwareTriggeredFlyable +from ophyd_async.core.utils import in_micros +from ophyd_async.panda._table import SeqTable, SeqTableRow, seq_table_from_rows +from ophyd_async.panda._trigger import SeqTableInfo + + +def fly_and_collect( + stream_name: str, + detectors: List[StandardDetector], + flyer: HardwareTriggeredFlyable[SeqTableInfo], + number_of_frames: int, + exposure: int, + shutter_time: float, + repeats: int = 1, + period: float = 0.0, +): + """Run a scan wth a flyer and multiple detectors. + + The standard basic flow for a flyscan: + + - Set up the flyer with a static sequence table and detectors with a trigger + - Declare the stream and kickoff the scan + - Collect while completing + + This needs to be used in a plan that instantates detectors and a flyer, + stages/unstages the devices, and opens and closes the run. + + """ + # Set up scan and prepare trigger + deadtime = max(det.controller.get_deadtime(exposure) for det in detectors) + yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( + flyer, + detectors, + number_of_frames, + width=exposure, + deadtime=deadtime, + shutter_time=shutter_time, + repeats=repeats, + period=period, + ) + yield from bps.declare_stream(*detectors, name=stream_name, collect=True) + yield from bps.kickoff_all(flyer, *detectors) + + # collect_while_completing + yield from bps.complete_all(flyer, *detectors, group="complete") + + done = False + while not done: + done = yield from bps.wait(group="complete", timeout=0.5) + yield from bps.collect(*detectors, name=stream_name) + + +def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( + flyer: HardwareTriggeredFlyable[SeqTableInfo], + detectors: List[StandardDetector], + number_of_frames: int, + exposure: float, + deadtime: float, + shutter_time: float, + repeats: int = 1, + period: float = 0.0, +): + """Prepare a hardware triggered flyable and one or more detectors. + + Prepare a hardware triggered flyable and one or more detectors with the + same trigger. This method constructs TriggerInfo and a static sequence + table from required parameters. The table is required to prepare the flyer, + and the TriggerInfo is required to prepare the detector(s). + + This prepares all supplied detectors with the same trigger. + + """ + trigger_info = TriggerInfo( + num=number_of_frames * repeats, + trigger=DetectorTrigger.constant_gate, + deadtime=deadtime, + livetime=exposure, + ) + + trigger_time = number_of_frames * (exposure + deadtime) + pre_delay = max(period - 2 * shutter_time - trigger_time, 0) + + table: SeqTable = seq_table_from_rows( + # Wait for pre-delay then open shutter + SeqTableRow( + time1=in_micros(pre_delay), + time2=in_micros(shutter_time), + outa2=True, + ), + # Keeping shutter open, do N triggers + SeqTableRow( + repeats=number_of_frames, + time1=in_micros(exposure), + outa1=True, + outb1=True, + time2=in_micros(deadtime), + outa2=True, + ), + # Add the shutter close + SeqTableRow(time2=in_micros(shutter_time)), + ) + + table_info = SeqTableInfo(table, repeats) + + for det in detectors: + yield from bps.prepare(det, trigger_info, wait=False, group="prep") + yield from bps.prepare(flyer, table_info, wait=False, group="prep") + yield from bps.wait(group="prep") diff --git a/src/ophyd_async/planstubs/fly_and_collect.py b/src/ophyd_async/planstubs/fly_and_collect.py deleted file mode 100644 index 599794a540..0000000000 --- a/src/ophyd_async/planstubs/fly_and_collect.py +++ /dev/null @@ -1,56 +0,0 @@ -from typing import List - -import bluesky.plan_stubs as bps - -from ophyd_async.core.detector import StandardDetector -from ophyd_async.core.flyer import HardwareTriggeredFlyable -from ophyd_async.panda._trigger import SeqTableInfo -from ophyd_async.planstubs import ( - prepare_static_seq_table_flyer_and_detectors_with_same_trigger, -) - - -def fly_and_collect( - stream_name: str, - detectors: List[StandardDetector], - flyer: HardwareTriggeredFlyable[SeqTableInfo], - number_of_frames: int, - exposure: int, - shutter_time: float, - repeats: int = 1, - period: float = 0.0, -): - """Run a scan wth a flyer and multiple detectors. - - The standard basic flow for a flyscan: - - - Set up the flyer with a static sequence table and detectors with a trigger - - Declare the stream and kickoff the scan - - Collect while completing - - This needs to be used in a plan that instantates detectors and a flyer, - stages/unstages the devices, and opens and closes the run. - - """ - # Set up scan and prepare trigger - deadtime = max(det.controller.get_deadtime(exposure) for det in detectors) - yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( - flyer, - detectors, - number_of_frames, - width=exposure, - deadtime=deadtime, - shutter_time=shutter_time, - repeats=repeats, - period=period, - ) - yield from bps.declare_stream(*detectors, name=stream_name, collect=True) - yield from bps.kickoff_all(flyer, *detectors) - - # collect_while_completing - yield from bps.complete_all(flyer, *detectors, group="complete") - - done = False - while not done: - done = yield from bps.wait(group="complete", timeout=0.5) - yield from bps.collect(*detectors, name=stream_name) diff --git a/src/ophyd_async/planstubs/prepare_trigger_and_dets.py b/src/ophyd_async/planstubs/prepare_trigger_and_dets.py deleted file mode 100644 index e7aec55799..0000000000 --- a/src/ophyd_async/planstubs/prepare_trigger_and_dets.py +++ /dev/null @@ -1,57 +0,0 @@ -from typing import List - -import bluesky.plan_stubs as bps - -from ophyd_async.core.detector import DetectorTrigger, StandardDetector, TriggerInfo -from ophyd_async.core.flyer import HardwareTriggeredFlyable -from ophyd_async.core.utils import in_micros -from ophyd_async.panda._table import SeqTable, SeqTableRow, seq_table_from_rows -from ophyd_async.panda._trigger import SeqTableInfo - - -def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( - flyer: HardwareTriggeredFlyable[SeqTableInfo], - detectors: List[StandardDetector], - number_of_frames: int, - exposure: float, - deadtime: float, - shutter_time: float, - repeats: int = 1, - period: float = 0.0, -): - trigger_info = TriggerInfo( - num=number_of_frames * repeats, - trigger=DetectorTrigger.constant_gate, - deadtime=deadtime, - livetime=exposure, - ) - - trigger_time = number_of_frames * (exposure + deadtime) - pre_delay = max(period - 2 * shutter_time - trigger_time, 0) - - table: SeqTable = seq_table_from_rows( - # Wait for pre-delay then open shutter - SeqTableRow( - time1=in_micros(pre_delay), - time2=in_micros(shutter_time), - outa2=True, - ), - # Keeping shutter open, do N triggers - SeqTableRow( - repeats=number_of_frames, - time1=in_micros(exposure), - outa1=True, - outb1=True, - time2=in_micros(deadtime), - outa2=True, - ), - # Add the shutter close - SeqTableRow(time2=in_micros(shutter_time)), - ) - - table_info = SeqTableInfo(table, repeats) - - for det in detectors: - yield from bps.prepare(det, trigger_info, wait=False, group="prep") - yield from bps.prepare(flyer, table_info, wait=False, group="prep") - yield from bps.wait(group="prep") From 3c3c7cd70ec84ae37cc7c0c2d3a0bef99bbcfb54 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 13:16:30 +0100 Subject: [PATCH 13/34] Unify naming in tests --- tests/core/test_flyer.py | 24 ++++++++++++------------ tests/test_flyer_with_panda.py | 5 +++-- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/core/test_flyer.py b/tests/core/test_flyer.py index d5edfb6b86..655f3b3dbc 100644 --- a/tests/core/test_flyer.py +++ b/tests/core/test_flyer.py @@ -109,7 +109,7 @@ async def close(self) -> None: @pytest.fixture -async def detector_list(RE: RunEngine) -> tuple[StandardDetector, StandardDetector]: +async def detectors(RE: RunEngine) -> tuple[StandardDetector, StandardDetector]: writers = [DummyWriter("testa", (1, 1)), DummyWriter("testb", (1, 1))] await writers[0].dummy_signal.connect(mock=True) await writers[1].dummy_signal.connect(mock=True) @@ -137,7 +137,7 @@ async def dummy_arm_2(self=None, trigger=None, num=0, exposure=None): async def test_hardware_triggered_flyable( - RE: RunEngine, detector_list: tuple[StandardDetector] + RE: RunEngine, detectors: tuple[StandardDetector] ): names = [] docs = [] @@ -155,7 +155,7 @@ def append_and_print(name, doc): ) def flying_plan(): - yield from bps.stage_all(*detector_list, flyer) + yield from bps.stage_all(*detectors, flyer) assert flyer._trigger_logic.state == TriggerState.stopping # move the flyer to the correct place, before fly scanning. @@ -163,7 +163,7 @@ def flying_plan(): yield from bps.prepare(flyer, 1, wait=True) # prepare detectors second. - for detector in detector_list: + for detector in detectors: yield from bps.prepare( detector, trigger_info, @@ -171,23 +171,23 @@ def flying_plan(): ) assert flyer._trigger_logic.state == TriggerState.preparing - for detector in detector_list: + for detector in detectors: detector.controller.disarm.assert_called_once # type: ignore yield from bps.open_run() - yield from bps.declare_stream(*detector_list, name="main_stream", collect=True) + yield from bps.declare_stream(*detectors, name="main_stream", collect=True) yield from bps.kickoff(flyer) - for detector in detector_list: + for detector in detectors: yield from bps.kickoff(detector) yield from bps.complete(flyer, wait=False, group="complete") - for detector in detector_list: + for detector in detectors: yield from bps.complete(detector, wait=False, group="complete") assert flyer._trigger_logic.state == TriggerState.null # Manually incremenet the index as if a frame was taken - for detector in detector_list: + for detector in detectors: detector.writer.index += 1 done = False @@ -199,15 +199,15 @@ def flying_plan(): else: done = True yield from bps.collect( - *detector_list, + *detectors, return_payload=False, name="main_stream", ) yield from bps.wait(group="complete") yield from bps.close_run() - yield from bps.unstage_all(flyer, *detector_list) - for detector in detector_list: + yield from bps.unstage_all(flyer, *detectors) + for detector in detectors: assert detector.controller.disarm.called # type: ignore assert trigger_logic.state == TriggerState.stopping diff --git a/tests/test_flyer_with_panda.py b/tests/test_flyer_with_panda.py index 4f5418722a..90112368e3 100644 --- a/tests/test_flyer_with_panda.py +++ b/tests/test_flyer_with_panda.py @@ -88,7 +88,7 @@ async def close(self) -> None: @pytest.fixture -async def detector_list(RE: RunEngine) -> tuple[StandardDetector, StandardDetector]: +async def detectors(RE: RunEngine) -> tuple[StandardDetector, StandardDetector]: writers = [DummyWriter("testa", (1, 1)), DummyWriter("testb", (1, 1))] await writers[0].dummy_signal.connect(mock=True) await writers[1].dummy_signal.connect(mock=True) @@ -136,7 +136,7 @@ async def connect(self, mock: bool = False, timeout: float = DEFAULT_TIMEOUT): async def test_hardware_triggered_flyable_with_static_seq_table_logic( RE: RunEngine, - detector_list: tuple[StandardDetector], + detectors: tuple[StandardDetector], panda, ): """Run a dummy scan using a flyer with a prepare plan stub. @@ -150,6 +150,7 @@ async def test_hardware_triggered_flyable_with_static_seq_table_logic( """ names = [] docs = [] + detector_list= list(detectors) def append_and_print(name, doc): names.append(name) From 7f4f5367a2c1690341b7da0f7e9b32e3a63ff5d4 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 13:31:43 +0100 Subject: [PATCH 14/34] Fix method parameters --- src/ophyd_async/planstubs/fly.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ophyd_async/planstubs/fly.py b/src/ophyd_async/planstubs/fly.py index c4cb5180d4..5eb7c5a123 100644 --- a/src/ophyd_async/planstubs/fly.py +++ b/src/ophyd_async/planstubs/fly.py @@ -36,8 +36,8 @@ def fly_and_collect( yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( flyer, detectors, - number_of_frames, - width=exposure, + number_of_frames=number_of_frames, + exposure=exposure, deadtime=deadtime, shutter_time=shutter_time, repeats=repeats, From 069e56e2e99657c8690a8639bc5e1e42ba42be0d Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Thu, 9 May 2024 13:39:11 +0100 Subject: [PATCH 15/34] Fix trigger parameters in test --- tests/test_flyer_with_panda.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_flyer_with_panda.py b/tests/test_flyer_with_panda.py index 90112368e3..30456570c3 100644 --- a/tests/test_flyer_with_panda.py +++ b/tests/test_flyer_with_panda.py @@ -24,6 +24,7 @@ from ophyd_async.panda._trigger import StaticSeqTableTriggerLogic from ophyd_async.planstubs import ( prepare_static_seq_table_flyer_and_detectors_with_same_trigger, + fly_and_collect, ) @@ -158,9 +159,10 @@ def append_and_print(name, doc): RE.subscribe(append_and_print) - shutter_time = 0.004 + number_of_frames=1 exposure = 1 deadtime = max(det.controller.get_deadtime(1) for det in detector_list) + shutter_time = 0.004 trigger_logic = StaticSeqTableTriggerLogic(panda.seq[1]) flyer = HardwareTriggeredFlyable(trigger_logic, [], name="flyer") @@ -171,7 +173,7 @@ def flying_plan(): yield from prepare_static_seq_table_flyer_and_detectors_with_same_trigger( flyer, detector_list, - number_of_frames=1, + number_of_frames=number_of_frames, exposure=exposure, deadtime=deadtime, shutter_time=shutter_time, From bd53de3f7a71ff92ea75fcf7e852a5921ff1aa3d Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Fri, 10 May 2024 13:24:15 +0100 Subject: [PATCH 16/34] Remove unused import --- tests/test_flyer_with_panda.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_flyer_with_panda.py b/tests/test_flyer_with_panda.py index 30456570c3..a4bc549554 100644 --- a/tests/test_flyer_with_panda.py +++ b/tests/test_flyer_with_panda.py @@ -24,7 +24,6 @@ from ophyd_async.panda._trigger import StaticSeqTableTriggerLogic from ophyd_async.planstubs import ( prepare_static_seq_table_flyer_and_detectors_with_same_trigger, - fly_and_collect, ) @@ -151,7 +150,7 @@ async def test_hardware_triggered_flyable_with_static_seq_table_logic( """ names = [] docs = [] - detector_list= list(detectors) + detector_list = list(detectors) def append_and_print(name, doc): names.append(name) @@ -159,7 +158,7 @@ def append_and_print(name, doc): RE.subscribe(append_and_print) - number_of_frames=1 + number_of_frames = 1 exposure = 1 deadtime = max(det.controller.get_deadtime(1) for det in detector_list) shutter_time = 0.004 From 65aa21c339bcd222a89b87599f28ad11cf321b48 Mon Sep 17 00:00:00 2001 From: Tom Cobb Date: Fri, 17 May 2024 12:48:29 +0000 Subject: [PATCH 17/34] StandardDetector: fix collect_asset_docs(index=0) bug The existing code treated 0 == None, so would fetch the current index if index=0, rather than producing StreamDatum up to index 0 as intended --- src/ophyd_async/core/detector.py | 2 +- tests/epics/demo/test_demo_ad_sim_detector.py | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/ophyd_async/core/detector.py b/src/ophyd_async/core/detector.py index ee168f8830..53f10568df 100644 --- a/src/ophyd_async/core/detector.py +++ b/src/ophyd_async/core/detector.py @@ -332,7 +332,7 @@ async def collect_asset_docs( # Collect stream datum documents for all indices written. # The index is optional, and provided for fly scans, however this needs to be # retrieved for step scans. - if not index: + if index is None: index = await self.writer.get_indices_written() async for doc in self.writer.collect_stream_docs(index): yield doc diff --git a/tests/epics/demo/test_demo_ad_sim_detector.py b/tests/epics/demo/test_demo_ad_sim_detector.py index ac143fd024..2398a04df9 100644 --- a/tests/epics/demo/test_demo_ad_sim_detector.py +++ b/tests/epics/demo/test_demo_ad_sim_detector.py @@ -1,10 +1,12 @@ """Integration tests for a StandardDetector using a HDFWriter and ADSimController.""" import time +from collections import defaultdict from pathlib import Path from typing import List, cast import bluesky.plan_stubs as bps +import bluesky.preprocessors as bpp import pytest from bluesky import RunEngine from bluesky.utils import new_uid @@ -17,6 +19,7 @@ callback_on_mock_put, set_mock_value, ) +from ophyd_async.core.signal import assert_emitted from ophyd_async.epics.areadetector.controllers import ADSimController from ophyd_async.epics.areadetector.drivers import ADBase from ophyd_async.epics.areadetector.utils import FileWriteMode, ImageMode @@ -102,11 +105,36 @@ async def two_detectors(tmp_path: Path): set_mock_value(controller.driver.image_mode, ImageMode.continuous) set_mock_value(writer.hdf.num_capture, 1000) set_mock_value(writer.hdf.num_captured, 0) + set_mock_value(writer.hdf.file_path_exists, True) set_mock_value(controller.driver.array_size_x, 1024 + i) set_mock_value(controller.driver.array_size_y, 768 + i) yield deta, detb +async def test_two_detectors_fly_different_rate( + two_detectors: List[DemoADSimDetector], RE: RunEngine +): + docs = defaultdict(list) + + @bpp.stage_decorator(two_detectors) + @bpp.run_decorator() + def fly_plan(): + yield from bps.declare_stream(*two_detectors, name="primary") + # Make one produce some frames and collect + set_mock_value(two_detectors[0].hdf.num_captured, 15) + yield from bps.collect(*two_detectors) + # It shouldn't make anything as the other one is lagging + assert "stream_datum" not in docs + # Make the other one produce some frames + set_mock_value(two_detectors[1].hdf.num_captured, 15) + yield from bps.collect(*two_detectors) + + RE(fly_plan(), lambda name, doc: docs[name].append(doc)) + assert_emitted( + docs, start=1, descriptor=1, stream_resource=2, stream_datum=2, stop=1 + ) + + async def test_two_detectors_step( two_detectors: List[StandardDetector], RE: RunEngine, From 4c6852b013bfccf1a497cf5bbec5104d1edc96e5 Mon Sep 17 00:00:00 2001 From: Tom Cobb Date: Fri, 17 May 2024 13:22:29 +0000 Subject: [PATCH 18/34] Fix error message for ADHDF writer --- src/ophyd_async/epics/areadetector/writers/hdf_writer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ophyd_async/epics/areadetector/writers/hdf_writer.py b/src/ophyd_async/epics/areadetector/writers/hdf_writer.py index b358ed347c..9c82877b7f 100644 --- a/src/ophyd_async/epics/areadetector/writers/hdf_writer.py +++ b/src/ophyd_async/epics/areadetector/writers/hdf_writer.py @@ -43,12 +43,13 @@ def __init__( async def open(self, multiplier: int = 1) -> Dict[str, DataKey]: self._file = None info = self._directory_provider() + file_path = str(info.root / info.resource_dir) await asyncio.gather( self.hdf.num_extra_dims.set(0), self.hdf.lazy_open.set(True), self.hdf.swmr_mode.set(True), # See https://github.com/bluesky/ophyd-async/issues/122 - self.hdf.file_path.set(str(info.root / info.resource_dir)), + self.hdf.file_path.set(file_path), self.hdf.file_name.set(f"{info.prefix}{self.hdf.name}{info.suffix}"), self.hdf.file_template.set("%s/%s.h5"), self.hdf.file_write_mode.set(FileWriteMode.stream), @@ -59,7 +60,7 @@ async def open(self, multiplier: int = 1) -> Dict[str, DataKey]: assert ( await self.hdf.file_path_exists.get_value() - ), f"File path {self.hdf.file_path.get_value()} for hdf plugin does not exist" + ), f"File path {file_path} for hdf plugin does not exist" # Overwrite num_capture to go forever await self.hdf.num_capture.set(0) From 08684fa6a68b726e56747b171c1ddbb1797822ef Mon Sep 17 00:00:00 2001 From: Tom Cobb Date: Fri, 17 May 2024 13:23:01 +0000 Subject: [PATCH 19/34] Make configuration signals for HardwareTriggeredFlyable optional --- src/ophyd_async/core/flyer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ophyd_async/core/flyer.py b/src/ophyd_async/core/flyer.py index d2df2d5777..f43f1a880f 100644 --- a/src/ophyd_async/core/flyer.py +++ b/src/ophyd_async/core/flyer.py @@ -39,7 +39,7 @@ class HardwareTriggeredFlyable( def __init__( self, trigger_logic: TriggerLogic[T], - configuration_signals: Sequence[SignalR], + configuration_signals: Sequence[SignalR] = (), name: str = "", ): self._trigger_logic = trigger_logic From 0f2c20086cc87cd5eb6b5d3dbf18dde2f88682f3 Mon Sep 17 00:00:00 2001 From: Tom Cobb Date: Fri, 17 May 2024 13:30:44 +0000 Subject: [PATCH 20/34] Use bps.collect_while_completing --- src/ophyd_async/planstubs/fly.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ophyd_async/planstubs/fly.py b/src/ophyd_async/planstubs/fly.py index 5eb7c5a123..a85821055d 100644 --- a/src/ophyd_async/planstubs/fly.py +++ b/src/ophyd_async/planstubs/fly.py @@ -47,12 +47,9 @@ def fly_and_collect( yield from bps.kickoff_all(flyer, *detectors) # collect_while_completing - yield from bps.complete_all(flyer, *detectors, group="complete") - - done = False - while not done: - done = yield from bps.wait(group="complete", timeout=0.5) - yield from bps.collect(*detectors, name=stream_name) + yield from bps.collect_while_completing( + [flyer], detectors, flush_period=0.5, stream_name=stream_name + ) def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( From 5c878de238e9e12cfdee6cad7effe9c3e0872aa1 Mon Sep 17 00:00:00 2001 From: Tom Cobb Date: Fri, 17 May 2024 13:38:43 +0000 Subject: [PATCH 21/34] Add h5 suffix in PandA HDF writer --- src/ophyd_async/panda/writers/_hdf_writer.py | 2 +- tests/panda/test_writer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ophyd_async/panda/writers/_hdf_writer.py b/src/ophyd_async/panda/writers/_hdf_writer.py index 6b6bc6a814..7a48bfb324 100644 --- a/src/ophyd_async/panda/writers/_hdf_writer.py +++ b/src/ophyd_async/panda/writers/_hdf_writer.py @@ -126,7 +126,7 @@ async def open(self, multiplier: int = 1) -> Dict[str, DataKey]: str(info.root / info.resource_dir) ), self.panda_device.data.hdf_file_name.set( - f"{info.prefix}{self.panda_device.name}{info.suffix}", + f"{info.prefix}{self.panda_device.name}{info.suffix}.h5", ), self.panda_device.data.num_capture.set(0), ) diff --git a/tests/panda/test_writer.py b/tests/panda/test_writer.py index 0492cc6f0e..99e8fb6ce2 100644 --- a/tests/panda/test_writer.py +++ b/tests/panda/test_writer.py @@ -68,7 +68,7 @@ async def mock_panda(panda_t): @pytest.fixture async def mock_writer(tmp_path, mock_panda) -> PandaHDFWriter: dir_prov = StaticDirectoryProvider( - directory_path=str(tmp_path), filename_prefix="", filename_suffix="/data.h5" + directory_path=str(tmp_path), filename_prefix="", filename_suffix="/data" ) async with DeviceCollector(mock=True): writer = PandaHDFWriter( From 3e3d234500bdbafeb0b27b74e0b89adc04c2d792 Mon Sep 17 00:00:00 2001 From: Tom Cobb Date: Fri, 17 May 2024 13:50:54 +0000 Subject: [PATCH 22/34] Fix enum handling in PAndA --- src/ophyd_async/panda/writers/_hdf_writer.py | 6 +++--- tests/panda/test_writer.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ophyd_async/panda/writers/_hdf_writer.py b/src/ophyd_async/panda/writers/_hdf_writer.py index 7a48bfb324..60f4a67bf8 100644 --- a/src/ophyd_async/panda/writers/_hdf_writer.py +++ b/src/ophyd_async/panda/writers/_hdf_writer.py @@ -79,10 +79,10 @@ async def get_signals_marked_for_capture( capture_signals.keys(), capture_signals.values(), signal_values ): signal_path = signal_path.replace("_capture", "") - if (signal_value.value in iter(Capture)) and (signal_value.value != Capture.No): + if (signal_value in iter(Capture)) and (signal_value != Capture.No): signals_to_capture[signal_path] = CaptureSignalWrapper( signal_object, - signal_value.value, + signal_value, ) return signals_to_capture @@ -149,7 +149,7 @@ async def open(self, multiplier: int = 1) -> Dict[str, DataKey]: else split_path[-2] ) - for suffix in str(capture_signal.capture_type).split(" "): + for suffix in capture_signal.capture_type.split(" "): self._datasets.append( _HDFDataset( name, diff --git a/tests/panda/test_writer.py b/tests/panda/test_writer.py index 99e8fb6ce2..e056648ef4 100644 --- a/tests/panda/test_writer.py +++ b/tests/panda/test_writer.py @@ -205,4 +205,4 @@ async def get_numeric_signal(_): "ophyd_async.panda.writers._hdf_writer.get_signals_marked_for_capture", get_numeric_signal, ): - assert "test-panda-block-1-Capture.Value" in await mock_writer.open() + assert "test-panda-block-1-Value" in await mock_writer.open() From fca4a532a68f6b1ef1d5ba0cfa70271729e9f4f3 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Tue, 21 May 2024 13:45:26 +0100 Subject: [PATCH 23/34] Rename planstub folder to plan_stubs to match bluesky naming --- .../{planstubs => plan_stubs}/__init__.py | 0 .../ensure_connected.py | 0 src/ophyd_async/{planstubs => plan_stubs}/fly.py | 0 tests/panda/test_hdf_panda.py | 2 +- tests/test_flyer_with_panda.py | 16 +++++----------- 5 files changed, 6 insertions(+), 12 deletions(-) rename src/ophyd_async/{planstubs => plan_stubs}/__init__.py (100%) rename src/ophyd_async/{planstubs => plan_stubs}/ensure_connected.py (100%) rename src/ophyd_async/{planstubs => plan_stubs}/fly.py (100%) diff --git a/src/ophyd_async/planstubs/__init__.py b/src/ophyd_async/plan_stubs/__init__.py similarity index 100% rename from src/ophyd_async/planstubs/__init__.py rename to src/ophyd_async/plan_stubs/__init__.py diff --git a/src/ophyd_async/planstubs/ensure_connected.py b/src/ophyd_async/plan_stubs/ensure_connected.py similarity index 100% rename from src/ophyd_async/planstubs/ensure_connected.py rename to src/ophyd_async/plan_stubs/ensure_connected.py diff --git a/src/ophyd_async/planstubs/fly.py b/src/ophyd_async/plan_stubs/fly.py similarity index 100% rename from src/ophyd_async/planstubs/fly.py rename to src/ophyd_async/plan_stubs/fly.py diff --git a/tests/panda/test_hdf_panda.py b/tests/panda/test_hdf_panda.py index c98315975a..09f60182ef 100644 --- a/tests/panda/test_hdf_panda.py +++ b/tests/panda/test_hdf_panda.py @@ -13,7 +13,7 @@ from ophyd_async.panda import HDFPanda from ophyd_async.panda._trigger import StaticSeqTableTriggerLogic from ophyd_async.panda.writers._hdf_writer import Capture -from ophyd_async.planstubs import ( +from ophyd_async.plan_stubs import ( prepare_static_seq_table_flyer_and_detectors_with_same_trigger, ) diff --git a/tests/test_flyer_with_panda.py b/tests/test_flyer_with_panda.py index a4bc549554..1f81aeb73a 100644 --- a/tests/test_flyer_with_panda.py +++ b/tests/test_flyer_with_panda.py @@ -8,23 +8,17 @@ from bluesky.run_engine import RunEngine from event_model import ComposeStreamResourceBundle, compose_stream_resource -from ophyd_async.core import ( - DEFAULT_TIMEOUT, - DetectorControl, - DetectorWriter, - HardwareTriggeredFlyable, - observe_value, - set_mock_value, -) +from ophyd_async.core import (DEFAULT_TIMEOUT, DetectorControl, DetectorWriter, + HardwareTriggeredFlyable, observe_value, + set_mock_value) from ophyd_async.core.detector import StandardDetector from ophyd_async.core.device import DeviceCollector from ophyd_async.epics.pvi.pvi import fill_pvi_entries from ophyd_async.epics.signal.signal import epics_signal_rw from ophyd_async.panda import CommonPandaBlocks from ophyd_async.panda._trigger import StaticSeqTableTriggerLogic -from ophyd_async.planstubs import ( - prepare_static_seq_table_flyer_and_detectors_with_same_trigger, -) +from ophyd_async.plan_stubs import \ + prepare_static_seq_table_flyer_and_detectors_with_same_trigger class DummyWriter(DetectorWriter): From ac50551d62a1f2853b233420ae43e349eafedc24 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Tue, 21 May 2024 15:56:47 +0100 Subject: [PATCH 24/34] Rename fly plan to be more specific --- src/ophyd_async/plan_stubs/__init__.py | 7 +++---- src/ophyd_async/plan_stubs/fly.py | 5 +++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ophyd_async/plan_stubs/__init__.py b/src/ophyd_async/plan_stubs/__init__.py index 4834685b78..44e03bec05 100644 --- a/src/ophyd_async/plan_stubs/__init__.py +++ b/src/ophyd_async/plan_stubs/__init__.py @@ -1,11 +1,10 @@ +from .ensure_connected import ensure_connected from .fly import ( - fly_and_collect, prepare_static_seq_table_flyer_and_detectors_with_same_trigger, -) -from .ensure_connected import ensure_connected + time_resolved_fly_and_collect_with_static_seq_table) __all__ = [ - "fly_and_collect", + "time_resolved_fly_and_collect_with_static_seq_table", "prepare_static_seq_table_flyer_and_detectors_with_same_trigger", "ensure_connected", ] diff --git a/src/ophyd_async/plan_stubs/fly.py b/src/ophyd_async/plan_stubs/fly.py index a85821055d..29a7de003e 100644 --- a/src/ophyd_async/plan_stubs/fly.py +++ b/src/ophyd_async/plan_stubs/fly.py @@ -2,14 +2,15 @@ import bluesky.plan_stubs as bps -from ophyd_async.core.detector import DetectorTrigger, StandardDetector, TriggerInfo +from ophyd_async.core.detector import (DetectorTrigger, StandardDetector, + TriggerInfo) from ophyd_async.core.flyer import HardwareTriggeredFlyable from ophyd_async.core.utils import in_micros from ophyd_async.panda._table import SeqTable, SeqTableRow, seq_table_from_rows from ophyd_async.panda._trigger import SeqTableInfo -def fly_and_collect( +def time_resolved_fly_and_collect_with_static_seq_table( stream_name: str, detectors: List[StandardDetector], flyer: HardwareTriggeredFlyable[SeqTableInfo], From 93d6da98b49eff7e10cc12a08799343d6725d868 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Tue, 21 May 2024 16:04:05 +0100 Subject: [PATCH 25/34] Make plan_stub test dir and move tests into it --- src/ophyd_async/plan_stubs/__init__.py | 3 ++- src/ophyd_async/plan_stubs/fly.py | 3 +-- tests/{test_flyer_with_panda.py => plan_stubs/test_fly.py} | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) rename tests/{test_flyer_with_panda.py => plan_stubs/test_fly.py} (98%) diff --git a/src/ophyd_async/plan_stubs/__init__.py b/src/ophyd_async/plan_stubs/__init__.py index 44e03bec05..c2146d9c2a 100644 --- a/src/ophyd_async/plan_stubs/__init__.py +++ b/src/ophyd_async/plan_stubs/__init__.py @@ -1,7 +1,8 @@ from .ensure_connected import ensure_connected from .fly import ( prepare_static_seq_table_flyer_and_detectors_with_same_trigger, - time_resolved_fly_and_collect_with_static_seq_table) + time_resolved_fly_and_collect_with_static_seq_table, +) __all__ = [ "time_resolved_fly_and_collect_with_static_seq_table", diff --git a/src/ophyd_async/plan_stubs/fly.py b/src/ophyd_async/plan_stubs/fly.py index 29a7de003e..bb93efcdf3 100644 --- a/src/ophyd_async/plan_stubs/fly.py +++ b/src/ophyd_async/plan_stubs/fly.py @@ -2,8 +2,7 @@ import bluesky.plan_stubs as bps -from ophyd_async.core.detector import (DetectorTrigger, StandardDetector, - TriggerInfo) +from ophyd_async.core.detector import DetectorTrigger, StandardDetector, TriggerInfo from ophyd_async.core.flyer import HardwareTriggeredFlyable from ophyd_async.core.utils import in_micros from ophyd_async.panda._table import SeqTable, SeqTableRow, seq_table_from_rows diff --git a/tests/test_flyer_with_panda.py b/tests/plan_stubs/test_fly.py similarity index 98% rename from tests/test_flyer_with_panda.py rename to tests/plan_stubs/test_fly.py index 1f81aeb73a..864663ac3d 100644 --- a/tests/test_flyer_with_panda.py +++ b/tests/plan_stubs/test_fly.py @@ -17,8 +17,9 @@ from ophyd_async.epics.signal.signal import epics_signal_rw from ophyd_async.panda import CommonPandaBlocks from ophyd_async.panda._trigger import StaticSeqTableTriggerLogic -from ophyd_async.plan_stubs import \ - prepare_static_seq_table_flyer_and_detectors_with_same_trigger +from ophyd_async.plan_stubs import ( + prepare_static_seq_table_flyer_and_detectors_with_same_trigger, + time_resolved_fly_and_collect_with_static_seq_table) class DummyWriter(DetectorWriter): From 520d09665a8be29a156e813abf5db1f6fd47abb6 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Tue, 21 May 2024 16:06:53 +0100 Subject: [PATCH 26/34] Add initial planstub test --- tests/plan_stubs/test_fly.py | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/plan_stubs/test_fly.py b/tests/plan_stubs/test_fly.py index 864663ac3d..fb973fd6d1 100644 --- a/tests/plan_stubs/test_fly.py +++ b/tests/plan_stubs/test_fly.py @@ -227,3 +227,55 @@ def flying_plan(): "stream_datum", "stop", ] + + +async def test_time_resolved_fly_and_collect_with_static_seq_table( + RE: RunEngine, + detectors: tuple[StandardDetector], + panda, +): + names = [] + docs = [] + detector_list = list(detectors) + + def append_and_print(name, doc): + names.append(name) + docs.append(doc) + + RE.subscribe(append_and_print) + + # Trigger parameters + number_of_frames = 1 + exposure = 1 + shutter_time = 0.004 + + # Make flyer + trigger_logic = StaticSeqTableTriggerLogic(panda.seq[1]) + flyer = HardwareTriggeredFlyable(trigger_logic, [], name="flyer") + + def fly(): + yield from bps.stage_all(*detector_list, flyer) + yield from bps.open_run() + yield from time_resolved_fly_and_collect_with_static_seq_table( + stream_name="stream1", + detectors=detector_list, + flyer=flyer, + number_of_frames=number_of_frames, + exposure=exposure, + shutter_time=shutter_time, + ) + yield from bps.close_run() + yield from bps.unstage_all(flyer, *detector_list) + + # fly scan + RE(fly()) + + assert names == [ + "start", + "descriptor", + "stream_resource", + "stream_datum", + "stream_resource", + "stream_datum", + "stop", + ] From d2cb87ba2228970e8a1bf5c020d383b453a63ab8 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Wed, 22 May 2024 11:12:28 +0100 Subject: [PATCH 27/34] Fix plan_stub import --- tests/core/test_device.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tests/core/test_device.py b/tests/core/test_device.py index 4b09aaf764..a972619839 100644 --- a/tests/core/test_device.py +++ b/tests/core/test_device.py @@ -4,20 +4,12 @@ import pytest -from ophyd_async.core import ( - DEFAULT_TIMEOUT, - Device, - DeviceCollector, - DeviceVector, - MockSignalBackend, - NotConnected, - wait_for_connection, -) +from ophyd_async.core import (DEFAULT_TIMEOUT, Device, DeviceCollector, + DeviceVector, MockSignalBackend, NotConnected, + wait_for_connection) from ophyd_async.core.soft_signal_backend import SoftSignalBackend from ophyd_async.epics.motion import motor -from ophyd_async.planstubs.ensure_connected import ( - ensure_connected, -) +from ophyd_async.plan_stubs.ensure_connected import ensure_connected from ophyd_async.sim.demo.sim_motor import SimMotor From 7b303886d3bd584860e1dac24d85af9502978767 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Wed, 22 May 2024 11:22:28 +0100 Subject: [PATCH 28/34] Format test imports --- tests/core/test_device.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/core/test_device.py b/tests/core/test_device.py index a972619839..964c949f4f 100644 --- a/tests/core/test_device.py +++ b/tests/core/test_device.py @@ -4,9 +4,15 @@ import pytest -from ophyd_async.core import (DEFAULT_TIMEOUT, Device, DeviceCollector, - DeviceVector, MockSignalBackend, NotConnected, - wait_for_connection) +from ophyd_async.core import ( + DEFAULT_TIMEOUT, + Device, + DeviceCollector, + DeviceVector, + MockSignalBackend, + NotConnected, + wait_for_connection, +) from ophyd_async.core.soft_signal_backend import SoftSignalBackend from ophyd_async.epics.motion import motor from ophyd_async.plan_stubs.ensure_connected import ensure_connected From 036901e42988793469e8d471b9d0d2f94e5c6723 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Wed, 22 May 2024 14:35:57 +0100 Subject: [PATCH 29/34] Remove commented-out lines --- tests/panda/test_hdf_panda.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/panda/test_hdf_panda.py b/tests/panda/test_hdf_panda.py index 09f60182ef..03271b964e 100644 --- a/tests/panda/test_hdf_panda.py +++ b/tests/panda/test_hdf_panda.py @@ -13,9 +13,8 @@ from ophyd_async.panda import HDFPanda from ophyd_async.panda._trigger import StaticSeqTableTriggerLogic from ophyd_async.panda.writers._hdf_writer import Capture -from ophyd_async.plan_stubs import ( - prepare_static_seq_table_flyer_and_detectors_with_same_trigger, -) +from ophyd_async.plan_stubs import \ + prepare_static_seq_table_flyer_and_detectors_with_same_trigger @pytest.fixture @@ -85,7 +84,6 @@ def flying_plan(): deadtime=mock_hdf_panda.controller.get_deadtime(1), shutter_time=shutter_time, ) - # mock_hdf_panda.controller.disarm.assert_called_once # type: ignore yield from bps.open_run() yield from bps.declare_stream(mock_hdf_panda, name="main_stream", collect=True) @@ -120,7 +118,6 @@ def flying_plan(): yield from bps.unstage_all(flyer, mock_hdf_panda) yield from bps.wait_for([lambda: mock_hdf_panda.controller.disarm()]) - # assert mock_hdf_panda.controller.disarm.called # type: ignore # fly scan RE(flying_plan()) From ba5303a5785f755cd188a5fab0c7a17f0a7cff96 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Wed, 22 May 2024 15:30:36 +0100 Subject: [PATCH 30/34] Add fixtures to test fly stub --- tests/plan_stubs/test_fly.py | 96 +++++++++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 13 deletions(-) diff --git a/tests/plan_stubs/test_fly.py b/tests/plan_stubs/test_fly.py index fb973fd6d1..e7fd789291 100644 --- a/tests/plan_stubs/test_fly.py +++ b/tests/plan_stubs/test_fly.py @@ -8,18 +8,29 @@ from bluesky.run_engine import RunEngine from event_model import ComposeStreamResourceBundle, compose_stream_resource -from ophyd_async.core import (DEFAULT_TIMEOUT, DetectorControl, DetectorWriter, - HardwareTriggeredFlyable, observe_value, - set_mock_value) +from ophyd_async.core import ( + DEFAULT_TIMEOUT, + DetectorControl, + DetectorWriter, + HardwareTriggeredFlyable, + observe_value, + set_mock_value, +) +from ophyd_async.core.async_status import AsyncStatus, WatchableAsyncStatus from ophyd_async.core.detector import StandardDetector from ophyd_async.core.device import DeviceCollector +from ophyd_async.core.flyer import TriggerLogic +from ophyd_async.core.signal import SignalR +from ophyd_async.core.utils import WatcherUpdate from ophyd_async.epics.pvi.pvi import fill_pvi_entries from ophyd_async.epics.signal.signal import epics_signal_rw from ophyd_async.panda import CommonPandaBlocks from ophyd_async.panda._trigger import StaticSeqTableTriggerLogic from ophyd_async.plan_stubs import ( prepare_static_seq_table_flyer_and_detectors_with_same_trigger, - time_resolved_fly_and_collect_with_static_seq_table) + time_resolved_fly_and_collect_with_static_seq_table, +) +from ophyd_async.protocols import AsyncReadable class DummyWriter(DetectorWriter): @@ -81,9 +92,44 @@ async def collect_stream_docs( async def close(self) -> None: self._file = None + def increment_index(self) -> None: + self.index += 1 + + +class MockDetector(StandardDetector): + def __init__( + self, + controller: DetectorControl, + writer: DetectorWriter, + config_sigs: Sequence[AsyncReadable] = [], + name: str = "", + writer_timeout: float = 1, + ) -> None: + super().__init__(controller, writer, config_sigs, name, writer_timeout) + + @WatchableAsyncStatus.wrap + async def complete(self): + assert self._arm_status, "Prepare not run" + assert self._trigger_info + self.writer.increment_index() + async for index in self.writer.observe_indices_written( + self._frame_writing_timeout + ): + yield WatcherUpdate( + name=self.name, + current=index, + initial=self._initial_frame, + target=self._trigger_info.num, + unit="", + precision=0, + time_elapsed=time.monotonic() - self._fly_start, + ) + if index >= self._trigger_info.num: + break + @pytest.fixture -async def detectors(RE: RunEngine) -> tuple[StandardDetector, StandardDetector]: +async def detectors(RE: RunEngine) -> tuple[MockDetector, MockDetector]: writers = [DummyWriter("testa", (1, 1)), DummyWriter("testb", (1, 1))] await writers[0].dummy_signal.connect(mock=True) await writers[1].dummy_signal.connect(mock=True) @@ -94,13 +140,13 @@ async def dummy_arm_1(self=None, trigger=None, num=0, exposure=None): async def dummy_arm_2(self=None, trigger=None, num=0, exposure=None): return writers[1].dummy_signal.set(1) - detector_1: StandardDetector = StandardDetector( + detector_1 = MockDetector( Mock(spec=DetectorControl, get_deadtime=lambda num: num, arm=dummy_arm_1), writers[0], name="detector_1", writer_timeout=3, ) - detector_2: StandardDetector = StandardDetector( + detector_2 = MockDetector( Mock(spec=DetectorControl, get_deadtime=lambda num: num, arm=dummy_arm_2), writers[1], name="detector_2", @@ -129,6 +175,34 @@ async def connect(self, mock: bool = False, timeout: float = DEFAULT_TIMEOUT): yield mock_panda +@pytest.fixture +async def flyer(panda): + class MockFlyer(HardwareTriggeredFlyable): + def __init__( + self, + trigger_logic: TriggerLogic, + configuration_signals: Sequence[SignalR] = ..., + name: str = "", + ): + super().__init__(trigger_logic, configuration_signals, name) + + @AsyncStatus.wrap + async def kickoff(self) -> None: + set_mock_value(self.trigger_logic.seq.active, 1) + await super().kickoff() + + @AsyncStatus.wrap + async def complete(self) -> None: + set_mock_value(self.trigger_logic.seq.active, 0) + await self._trigger_logic.complete() + + # Make flyer + trigger_logic = StaticSeqTableTriggerLogic(panda.seq[1]) + flyer = MockFlyer(trigger_logic, [], name="flyer") + + return flyer + + async def test_hardware_triggered_flyable_with_static_seq_table_logic( RE: RunEngine, detectors: tuple[StandardDetector], @@ -191,7 +265,7 @@ def flying_plan(): # Manually incremenet the index as if a frame was taken for detector in detector_list: - detector.writer.index += 1 + detector.writer.increment_index() set_mock_value(flyer.trigger_logic.seq.active, 0) @@ -232,7 +306,7 @@ def flying_plan(): async def test_time_resolved_fly_and_collect_with_static_seq_table( RE: RunEngine, detectors: tuple[StandardDetector], - panda, + flyer, ): names = [] docs = [] @@ -249,10 +323,6 @@ def append_and_print(name, doc): exposure = 1 shutter_time = 0.004 - # Make flyer - trigger_logic = StaticSeqTableTriggerLogic(panda.seq[1]) - flyer = HardwareTriggeredFlyable(trigger_logic, [], name="flyer") - def fly(): yield from bps.stage_all(*detector_list, flyer) yield from bps.open_run() From fc98389b68acc207f22915aa03b77e8d8d3e7c5f Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Wed, 22 May 2024 15:30:50 +0100 Subject: [PATCH 31/34] Fix ruff format --- tests/panda/test_hdf_panda.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/panda/test_hdf_panda.py b/tests/panda/test_hdf_panda.py index 03271b964e..019380dcbe 100644 --- a/tests/panda/test_hdf_panda.py +++ b/tests/panda/test_hdf_panda.py @@ -13,8 +13,9 @@ from ophyd_async.panda import HDFPanda from ophyd_async.panda._trigger import StaticSeqTableTriggerLogic from ophyd_async.panda.writers._hdf_writer import Capture -from ophyd_async.plan_stubs import \ - prepare_static_seq_table_flyer_and_detectors_with_same_trigger +from ophyd_async.plan_stubs import ( + prepare_static_seq_table_flyer_and_detectors_with_same_trigger, +) @pytest.fixture From 7795baae7f712d3c8ca63dcfc9e4330773d84917 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Wed, 22 May 2024 16:10:24 +0100 Subject: [PATCH 32/34] Replace collect while completing with the error catching logic --- src/ophyd_async/plan_stubs/fly.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/ophyd_async/plan_stubs/fly.py b/src/ophyd_async/plan_stubs/fly.py index bb93efcdf3..8bee132b54 100644 --- a/src/ophyd_async/plan_stubs/fly.py +++ b/src/ophyd_async/plan_stubs/fly.py @@ -1,6 +1,7 @@ from typing import List import bluesky.plan_stubs as bps +from bluesky.utils import short_uid from ophyd_async.core.detector import DetectorTrigger, StandardDetector, TriggerInfo from ophyd_async.core.flyer import HardwareTriggeredFlyable @@ -47,9 +48,22 @@ def time_resolved_fly_and_collect_with_static_seq_table( yield from bps.kickoff_all(flyer, *detectors) # collect_while_completing - yield from bps.collect_while_completing( - [flyer], detectors, flush_period=0.5, stream_name=stream_name - ) + group = short_uid(label="complete") + yield from bps.complete_all(*detectors, flyer, group=group, wait=False) + done = False + while not done: + try: + yield from bps.wait(group=group, timeout=0.5) + except TimeoutError: + pass + else: + done = True + yield from bps.collect( + *detectors, + return_payload=False, + name=stream_name, + ) + yield from bps.wait(group=group) def prepare_static_seq_table_flyer_and_detectors_with_same_trigger( From e0b6f162b0ef051486df1ee607acc4f3059164e7 Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Wed, 22 May 2024 16:27:29 +0100 Subject: [PATCH 33/34] Revert back to old kickoff complete --- src/ophyd_async/plan_stubs/fly.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ophyd_async/plan_stubs/fly.py b/src/ophyd_async/plan_stubs/fly.py index 8bee132b54..a4ad880aed 100644 --- a/src/ophyd_async/plan_stubs/fly.py +++ b/src/ophyd_async/plan_stubs/fly.py @@ -45,11 +45,17 @@ def time_resolved_fly_and_collect_with_static_seq_table( period=period, ) yield from bps.declare_stream(*detectors, name=stream_name, collect=True) - yield from bps.kickoff_all(flyer, *detectors) + yield from bps.kickoff(flyer, wait=True) + for detector in detectors: + yield from bps.kickoff(detector) # collect_while_completing group = short_uid(label="complete") - yield from bps.complete_all(*detectors, flyer, group=group, wait=False) + + yield from bps.complete(flyer, wait=False, group=group) + for detector in detectors: + yield from bps.complete(detector, wait=False, group=group) + done = False while not done: try: From 4224cc5775a16e5634f5134bc32a6baed7baa47b Mon Sep 17 00:00:00 2001 From: Abigail Emery Date: Wed, 22 May 2024 16:42:59 +0100 Subject: [PATCH 34/34] Drop the patch --- .codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codecov.yml b/.codecov.yml index 214fccccbe..a5d2c1645f 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -8,7 +8,7 @@ coverage: threshold: 1% patch: default: - target: auto + target: 90 threshold: 1% github_checks: annotations: false