From a36ab5b65dee8c0d5369dad904f306b700b296d7 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 30 Apr 2026 09:53:43 +0100 Subject: [PATCH 1/9] Newest ophyd-async --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 92b5bfb..cb14d8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,9 +25,10 @@ dependencies = [ "scanspec", "deepdiff", "blueapi >= 1.0.0", - "ophyd-async[ca,pva]>=0.13.0", + "ophyd-async[ca,pva]>=v0.17a4", "bluesky >= 1.13.1", "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@main", + "pytest>=9.0.2", ] dynamic = ["version"] From 63c0bbec98cfeddeba769ba2f29602809479ace4 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 30 Apr 2026 09:53:57 +0100 Subject: [PATCH 2/9] Add snapshots plan --- .../i15_1/plans/snapshots.py | 13 +++++++++ tests/unit_tests/i15_1/conftest.py | 7 +++++ tests/unit_tests/i15_1/test_snapshots.py | 28 +++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 src/crystallography_bluesky/i15_1/plans/snapshots.py create mode 100644 tests/unit_tests/i15_1/conftest.py create mode 100644 tests/unit_tests/i15_1/test_snapshots.py diff --git a/src/crystallography_bluesky/i15_1/plans/snapshots.py b/src/crystallography_bluesky/i15_1/plans/snapshots.py new file mode 100644 index 0000000..771e2ad --- /dev/null +++ b/src/crystallography_bluesky/i15_1/plans/snapshots.py @@ -0,0 +1,13 @@ +import bluesky.plan_stubs as bps +from ophyd_async.core import TriggerInfo +from ophyd_async.epics.adcore import ContAcqDetector + + +# @bpp.run_decorator() +def take_snapshot(camera: ContAcqDetector): + yield from bps.open_run() + yield from bps.stage(camera) + yield from bps.prepare(camera, TriggerInfo(number_of_events=1), wait=True) + yield from bps.trigger_and_read([camera]) + yield from bps.unstage(camera) + yield from bps.close_run() diff --git a/tests/unit_tests/i15_1/conftest.py b/tests/unit_tests/i15_1/conftest.py new file mode 100644 index 0000000..f15120a --- /dev/null +++ b/tests/unit_tests/i15_1/conftest.py @@ -0,0 +1,7 @@ +import pytest +from bluesky import RunEngine + + +@pytest.fixture +def run_engine(): + return RunEngine() diff --git a/tests/unit_tests/i15_1/test_snapshots.py b/tests/unit_tests/i15_1/test_snapshots.py new file mode 100644 index 0000000..18e3ad8 --- /dev/null +++ b/tests/unit_tests/i15_1/test_snapshots.py @@ -0,0 +1,28 @@ +import pytest +from bluesky import RunEngine +from dodal.beamlines.i15_1 import cam_1 +from dodal.common.visit import DataCollectionIdentifier, StaticVisitPathProvider +from ophyd_async.core import init_devices +from ophyd_async.epics.adcore import ADImageMode, ContAcqDetector + +from crystallography_bluesky.i15_1.plans.snapshots import take_snapshot + + +@pytest.fixture +async def camera(tmp_path) -> ContAcqDetector: + assert tmp_path.exists() + path_provider = StaticVisitPathProvider("i15_1", tmp_path) + path_provider._filename_provider.collectionId = DataCollectionIdentifier( + collectionNumber=0 + ) + async with init_devices(mock=True): + camera = cam_1(path_provider) + + await camera.driver.image_mode.set(ADImageMode.CONTINUOUS) + await camera.driver.acquire.set(True) + + return camera + + +def test_take_snapshot_plan(run_engine: RunEngine, camera: ContAcqDetector): + run_engine(take_snapshot(camera)) From 583b8e216856264b0f1f7707bde618de7f0dad09 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 30 Apr 2026 13:26:42 +0100 Subject: [PATCH 3/9] Small fixes --- src/crystallography_bluesky/i15_1/plans/__init__.py | 3 ++- src/crystallography_bluesky/i15_1/plans/snapshots.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/crystallography_bluesky/i15_1/plans/__init__.py b/src/crystallography_bluesky/i15_1/plans/__init__.py index 6b39546..873142f 100644 --- a/src/crystallography_bluesky/i15_1/plans/__init__.py +++ b/src/crystallography_bluesky/i15_1/plans/__init__.py @@ -1,3 +1,4 @@ from .robot import robot_load, robot_unload +from .snapshots import take_snapshot -__all__ = ["robot_load", "robot_unload"] +__all__ = ["robot_load", "robot_unload", "take_snapshot"] diff --git a/src/crystallography_bluesky/i15_1/plans/snapshots.py b/src/crystallography_bluesky/i15_1/plans/snapshots.py index 771e2ad..cee145f 100644 --- a/src/crystallography_bluesky/i15_1/plans/snapshots.py +++ b/src/crystallography_bluesky/i15_1/plans/snapshots.py @@ -1,13 +1,13 @@ import bluesky.plan_stubs as bps +import bluesky.preprocessors as bpp +from bluesky.utils import MsgGenerator from ophyd_async.core import TriggerInfo from ophyd_async.epics.adcore import ContAcqDetector -# @bpp.run_decorator() -def take_snapshot(camera: ContAcqDetector): - yield from bps.open_run() +@bpp.run_decorator() +def take_snapshot(camera: ContAcqDetector) -> MsgGenerator: yield from bps.stage(camera) yield from bps.prepare(camera, TriggerInfo(number_of_events=1), wait=True) yield from bps.trigger_and_read([camera]) yield from bps.unstage(camera) - yield from bps.close_run() From fe1065410607d36870a95ee58b4c7212a93688c0 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 19 May 2026 13:27:32 +0100 Subject: [PATCH 4/9] Fix --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d2c5333..65b01de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,6 @@ dependencies = [ "bluesky >= 1.13.1", "daq-config-server>=1.3.1", "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@main", - "pytest>=9.0.2", ] dynamic = ["version"] From d6bfb25b12a38d5d6694b1e8ae211d1da5a240ab Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 19 May 2026 13:33:08 +0100 Subject: [PATCH 5/9] Fix --- src/crystallography_bluesky/i15_1/plans/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/crystallography_bluesky/i15_1/plans/__init__.py b/src/crystallography_bluesky/i15_1/plans/__init__.py index 6e0f990..acbde7e 100644 --- a/src/crystallography_bluesky/i15_1/plans/__init__.py +++ b/src/crystallography_bluesky/i15_1/plans/__init__.py @@ -1,10 +1,16 @@ -from .robot import move_hexapod_to_home_position, robot_load, robot_unload +from .robot import ( + move_hexapod_to_home_position, + prepare_beamline_for_robot_load, + robot_load, + robot_unload, +) from .snapshots import take_snapshot from .take_i0_data import take_i0_data __all__ = [ - "move_hexapod_to_home_position", "robot_load", + "move_hexapod_to_home_position", + "prepare_beamline_for_robot_load", "robot_unload", "take_i0_data", "take_snapshot", From 29c41cbe263846c72296fb2d940b58228f52c2f1 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 19 May 2026 13:36:48 +0100 Subject: [PATCH 6/9] Pin dodal to branch --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 65b01de..8e646de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "ophyd-async[ca,pva]>=v0.17a4", "bluesky >= 1.13.1", "daq-config-server>=1.3.1", - "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@main", + "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@cryst_bluesky_20_add_i15_cams", ] dynamic = ["version"] From fe60754d42241caac974d1409ae3839847257540 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 26 May 2026 16:26:54 +0100 Subject: [PATCH 7/9] Pin ophyd-async --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 65fc06f..e4b7158 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ dependencies = [ "scanspec", "deepdiff", "blueapi >= 1.0.0", - "ophyd-async[ca,pva]>=v0.17a4", + "ophyd-async[ca,pva]==v0.17a4", "bluesky >= 1.13.1", "daq-config-server>=1.3.1", "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@main", From 0e14dd503269743416e4423a5b53e5c4e00ddd0c Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 26 May 2026 16:47:37 +0100 Subject: [PATCH 8/9] Add test --- pyproject.toml | 2 +- tests/unit_tests/i15_1/test_snapshots.py | 46 +++++++++++++++++++++--- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e4b7158..cf382b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ dependencies = [ "scanspec", "deepdiff", "blueapi >= 1.0.0", - "ophyd-async[ca,pva]==v0.17a4", + "ophyd-async[ca,pva] @ git+https://github.com/bluesky/ophyd-async.git@add_cb_trigger", "bluesky >= 1.13.1", "daq-config-server>=1.3.1", "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@main", diff --git a/tests/unit_tests/i15_1/test_snapshots.py b/tests/unit_tests/i15_1/test_snapshots.py index 18e3ad8..bc9cb8e 100644 --- a/tests/unit_tests/i15_1/test_snapshots.py +++ b/tests/unit_tests/i15_1/test_snapshots.py @@ -1,6 +1,8 @@ +from unittest.mock import AsyncMock + import pytest from bluesky import RunEngine -from dodal.beamlines.i15_1 import cam_1 +from bluesky.simulators import RunEngineSimulator, assert_message_and_return_remaining from dodal.common.visit import DataCollectionIdentifier, StaticVisitPathProvider from ophyd_async.core import init_devices from ophyd_async.epics.adcore import ADImageMode, ContAcqDetector @@ -10,19 +12,55 @@ @pytest.fixture async def camera(tmp_path) -> ContAcqDetector: - assert tmp_path.exists() path_provider = StaticVisitPathProvider("i15_1", tmp_path) path_provider._filename_provider.collectionId = DataCollectionIdentifier( collectionNumber=0 ) async with init_devices(mock=True): - camera = cam_1(path_provider) + camera = ContAcqDetector("", path_provider, name="cam_1") await camera.driver.image_mode.set(ADImageMode.CONTINUOUS) await camera.driver.acquire.set(True) + camera.writer.file_path_exists.get_value = AsyncMock(return_value=True) + return camera -def test_take_snapshot_plan(run_engine: RunEngine, camera: ContAcqDetector): +def test_take_snapshot_plan_runs_without_error( + run_engine: RunEngine, camera: ContAcqDetector +): run_engine(take_snapshot(camera)) + + +def test_take_snapshot_plan_makes_expected_calls(camera: ContAcqDetector): + run_engine = RunEngineSimulator() + + msgs = run_engine.simulate_plan(take_snapshot(camera)) + + msgs = assert_message_and_return_remaining( + msgs, predicate=lambda msg: msg.command == "open_run" + ) + msgs = assert_message_and_return_remaining( + msgs, + predicate=lambda msg: msg.command == "stage" and msg.obj.name == "cam_1", + ) + msgs = assert_message_and_return_remaining( + msgs, + predicate=lambda msg: msg.command == "prepare" and msg.obj.name == "cam_1", + ) + msgs = assert_message_and_return_remaining( + msgs, + predicate=lambda msg: msg.command == "trigger" and msg.obj.name == "cam_1", + ) + msgs = assert_message_and_return_remaining( + msgs, + predicate=lambda msg: msg.command == "read" and msg.obj.name == "cam_1", + ) + msgs = assert_message_and_return_remaining( + msgs, + predicate=lambda msg: msg.command == "unstage" and msg.obj.name == "cam_1", + ) + msgs = assert_message_and_return_remaining( + msgs, predicate=lambda msg: msg.command == "close_run" + ) From 2374dc2f89cfa53f8ac8a2ec5c60043cf562adf5 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 26 May 2026 17:12:47 +0100 Subject: [PATCH 9/9] Remove test --- tests/unit_tests/i15_1/test_snapshots.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/unit_tests/i15_1/test_snapshots.py b/tests/unit_tests/i15_1/test_snapshots.py index bc9cb8e..2acdd7c 100644 --- a/tests/unit_tests/i15_1/test_snapshots.py +++ b/tests/unit_tests/i15_1/test_snapshots.py @@ -1,7 +1,6 @@ from unittest.mock import AsyncMock import pytest -from bluesky import RunEngine from bluesky.simulators import RunEngineSimulator, assert_message_and_return_remaining from dodal.common.visit import DataCollectionIdentifier, StaticVisitPathProvider from ophyd_async.core import init_devices @@ -27,12 +26,6 @@ async def camera(tmp_path) -> ContAcqDetector: return camera -def test_take_snapshot_plan_runs_without_error( - run_engine: RunEngine, camera: ContAcqDetector -): - run_engine(take_snapshot(camera)) - - def test_take_snapshot_plan_makes_expected_calls(camera: ContAcqDetector): run_engine = RunEngineSimulator()