Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into 770_correct_upper_left
Browse files Browse the repository at this point in the history
  • Loading branch information
DominicOram committed Jul 17, 2023
2 parents 584aa01 + d7b0ebe commit 8db9d78
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 124 deletions.
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ install_requires =
xarray
doct
databroker
dodal @ git+https://github.com/DiamondLightSource/python-dodal.git@b78af4a9927e3b525cbb15cabaae0271cbc06fa0
dodal @ git+https://github.com/DiamondLightSource/python-dodal.git@ea49bf7453a0959a48904698182b7c920a0d5fbd
pydantic<2.0 # See https://github.com/DiamondLightSource/python-artemis/issues/774


Expand Down
4 changes: 3 additions & 1 deletion src/artemis/device_setup_plans/setup_oav.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,15 @@ def pre_centring_setup_oav(oav: OAV, parameters: OAVParameters):
parameters.minimum_height,
)

# Connect MXSC output to MJPG input
yield from start_mxsc(
oav,
parameters.min_callback_time,
parameters.detection_script_filename,
)

# Connect MXSC output to MJPG input for debugging
yield from bps.abs_set(oav.snapshot.input_plugin, "OAV.MXSC")

zoom_level_str = f"{float(parameters.zoom)}x"
if zoom_level_str not in oav.zoom_controller.allowed_zoom_levels:
raise OAVError_ZoomLevelNotFound(
Expand Down
23 changes: 10 additions & 13 deletions src/artemis/experiment_plans/fast_grid_scan_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,24 +224,21 @@ def run_gridscan(

@bpp.set_run_key_decorator("do_fgs")
@bpp.run_decorator(md={"subplan_name": "do_fgs"})
@bpp.contingency_decorator(
except_plan=lambda e: (yield from bps.stop(fgs_composite.eiger)),
else_plan=lambda: (yield from bps.unstage(fgs_composite.eiger)),
)
def do_fgs():
try:
yield from bps.wait() # Wait for all moves to complete
yield from bps.kickoff(fgs_motors)
yield from bps.complete(fgs_motors, wait=True)
finally:
yield from bps.unstage(fgs_composite.eiger)

# Wait for arming to finish
artemis.log.LOGGER.info("Waiting for arming...")
yield from bps.wait("arming")
artemis.log.LOGGER.info("Arming finished")
yield from bps.wait() # Wait for all moves to complete
yield from bps.kickoff(fgs_motors)
yield from bps.complete(fgs_motors, wait=True)

yield from bps.stage(fgs_composite.eiger)

with TRACER.start_span("do_fgs"):
yield from do_fgs()

with TRACER.start_span("move_to_z_0"):
yield from bps.abs_set(fgs_motors.z_steps, 0, wait=False)
yield from bps.abs_set(fgs_motors.z_steps, 0, wait=False)


@bpp.set_run_key_decorator("run_gridscan_and_move")
Expand Down
46 changes: 28 additions & 18 deletions src/artemis/experiment_plans/full_grid_scan.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from __future__ import annotations

import json
from typing import TYPE_CHECKING, Callable

import numpy as np
from bluesky import plan_stubs as bps
from bluesky import preprocessors as bpp
from dodal.beamlines import i03
Expand All @@ -19,15 +21,15 @@
create_devices as oav_create_devices,
)
from artemis.experiment_plans.oav_grid_detection_plan import grid_detection_plan
from artemis.external_interaction.callbacks.fgs.fgs_callback_collection import (
FGSCallbackCollection,
)
from artemis.external_interaction.callbacks.oav_snapshot_callback import (
OavSnapshotCallback,
)
from artemis.log import LOGGER
from artemis.parameters.beamline_parameters import get_beamline_parameters
from artemis.parameters.plan_specific.fgs_internal_params import GridScanParams
from artemis.parameters.plan_specific.fgs_internal_params import (
FGSInternalParameters,
GridScanParams,
)

if TYPE_CHECKING:
from artemis.parameters.plan_specific.grid_scan_with_edge_detect_params import (
Expand Down Expand Up @@ -65,6 +67,17 @@ def wait_for_det_to_finish_moving(detector: DetectorMotion, timeout=120):
raise TimeoutError("Detector not finished moving")


def create_parameters_for_fast_grid_scan(
grid_scan_with_edge_params: GridScanWithEdgeDetectInternalParameters,
grid_parameters: GridScanParams,
) -> FGSInternalParameters:
params_json = json.loads(grid_scan_with_edge_params.json())
params_json["experiment_params"] = json.loads(grid_parameters.json())
fast_grid_scan_parameters = FGSInternalParameters(**params_json)
LOGGER.info(f"Parameters for FGS: {fast_grid_scan_parameters}")
return fast_grid_scan_parameters


def start_arming_then_do_grid(
parameters: GridScanWithEdgeDetectInternalParameters,
backlight: Backlight,
Expand All @@ -76,15 +89,15 @@ def start_arming_then_do_grid(
# Start stage with asynchronous arming here
yield from bps.abs_set(eiger.do_arm, 1, group="arming")

yield from bpp.finalize_wrapper(
yield from bpp.contingency_wrapper(
detect_grid_and_do_gridscan(
parameters,
backlight,
aperture_scatterguard,
detector_motion,
oav_params,
),
bps.unstage(eiger),
except_plan=lambda e: (yield from bps.stop(eiger)),
)


Expand All @@ -96,7 +109,7 @@ def detect_grid_and_do_gridscan(
oav_params: OAVParameters,
):
experiment_params: GridScanWithEdgeDetectParams = parameters.experiment_params
fgs_params = GridScanParams(dwell_time=experiment_params.exposure_time * 1000)
grid_params = GridScanParams(dwell_time=experiment_params.exposure_time * 1000)

detector_params = parameters.artemis_params.detector_params
snapshot_template = (
Expand All @@ -121,15 +134,15 @@ def run_grid_detection_plan(

yield from run_grid_detection_plan(
oav_params,
fgs_params,
grid_params,
snapshot_template,
experiment_params.snapshot_dir,
)

# Hack because GDA only passes 3 values to ispyb
out_upper_left = oav_callback.out_upper_left[0] + [
oav_callback.out_upper_left[1][1]
]
out_upper_left = np.array(
oav_callback.out_upper_left[0] + [oav_callback.out_upper_left[1][1]]
)

# Hack because the callback returns the list in inverted order
parameters.artemis_params.ispyb_params.xtal_snapshots_omega_start = (
Expand All @@ -140,12 +153,9 @@ def run_grid_detection_plan(
)
parameters.artemis_params.ispyb_params.upper_left = out_upper_left

parameters.experiment_params = fgs_params

parameters.artemis_params.detector_params.num_triggers = fgs_params.get_num_images()

LOGGER.info(f"Parameters for FGS: {parameters}")
subscriptions = FGSCallbackCollection.from_params(parameters)
fast_grid_scan_parameters = create_parameters_for_fast_grid_scan(
parameters, grid_params
)

yield from bps.abs_set(backlight.pos, Backlight.OUT)
LOGGER.info(
Expand All @@ -156,7 +166,7 @@ def run_grid_detection_plan(
)
yield from wait_for_det_to_finish_moving(detector_motion)

yield from fgs_get_plan(parameters, subscriptions)
yield from fgs_get_plan(fast_grid_scan_parameters)


def get_plan(
Expand Down
15 changes: 11 additions & 4 deletions src/artemis/experiment_plans/oav_grid_detection_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
import numpy as np
from bluesky.preprocessors import finalize_wrapper
from dodal.beamlines import i03
from dodal.devices.areadetector.plugins.MXSC import PinTipDetect
from dodal.devices.fast_grid_scan import GridScanParams
from dodal.devices.oav.oav_calculations import camera_coordinates_to_xyz
from dodal.devices.oav.oav_detector import OAV
from dodal.devices.oav.oav_detector import MXSC, OAV
from dodal.devices.smargon import Smargon

from artemis.device_setup_plans.setup_oav import pre_centring_setup_oav
Expand Down Expand Up @@ -49,10 +48,16 @@ def grid_detection_plan(
)


def wait_for_tip_to_be_found(pin_tip: PinTipDetect):
def wait_for_tip_to_be_found(mxsc: MXSC):
pin_tip = mxsc.pin_tip
yield from bps.trigger(pin_tip, wait=True)
found_tip = yield from bps.rd(pin_tip)
if found_tip == pin_tip.INVALID_POSITION:
top_edge = yield from bps.rd(mxsc.top)
bottom_edge = yield from bps.rd(mxsc.bottom)
LOGGER.info(
f"No tip found with top/bottom of {list(top_edge), list(bottom_edge)}"
)
raise WarningException(
f"No pin found after {pin_tip.validity_timeout.get()} seconds"
)
Expand Down Expand Up @@ -104,7 +109,7 @@ def grid_detection_main_plan(
# See #673 for improvements
yield from bps.sleep(0.3)

tip_x_px, tip_y_px = yield from wait_for_tip_to_be_found(oav.mxsc.pin_tip)
tip_x_px, tip_y_px = yield from wait_for_tip_to_be_found(oav.mxsc)

LOGGER.info(f"Tip is at x,y: {tip_x_px},{tip_y_px}")

Expand Down Expand Up @@ -212,5 +217,7 @@ def grid_detection_main_plan(


def reset_oav():
"""Changes the MJPG stream to look at the camera without the edge detection and turns off the edge detcetion plugin."""
oav = i03.oav()
yield from bps.abs_set(oav.snapshot.input_plugin, "OAV.CAM")
yield from bps.abs_set(oav.mxsc.enable_callbacks, 0)
7 changes: 5 additions & 2 deletions src/artemis/experiment_plans/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from bluesky.run_engine import RunEngine
from dodal.beamlines import i03
from dodal.devices.aperturescatterguard import AperturePositions
from dodal.devices.smargon import Smargon
from ophyd.status import Status

from artemis.experiment_plans.fast_grid_scan_plan import FGSComposite
Expand Down Expand Up @@ -45,7 +46,7 @@ def eiger():


@pytest.fixture
def smargon():
def smargon() -> Smargon:
smargon = i03.smargon(fake_with_ophyd_sim=True)
smargon.x.user_setpoint._use_limits = False
smargon.y.user_setpoint._use_limits = False
Expand Down Expand Up @@ -111,7 +112,7 @@ def test_full_grid_scan_params():


@pytest.fixture
def fake_fgs_composite(test_fgs_params: InternalParameters):
def fake_fgs_composite(smargon: Smargon, test_fgs_params: InternalParameters):
fake_composite = FGSComposite(
aperture_positions=AperturePositions(
LARGE=(1, 2, 3, 4, 5),
Expand All @@ -135,6 +136,8 @@ def fake_fgs_composite(test_fgs_params: InternalParameters):
fake_composite.fast_grid_scan.scan_invalid.sim_put(False)
fake_composite.fast_grid_scan.position_counter.sim_put(0)

fake_composite.sample_motors = smargon

return fake_composite


Expand Down
85 changes: 33 additions & 52 deletions src/artemis/experiment_plans/tests/test_fast_grid_scan_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,71 +337,52 @@ def test_when_grid_scan_ran_then_eiger_disarmed_before_zocalo_end(
mock_parent.assert_has_calls([call.disarm(), call.run_end(0), call.run_end(0)])


@patch("artemis.experiment_plans.fast_grid_scan_plan.bps.abs_set")
@patch("artemis.experiment_plans.fast_grid_scan_plan.bps.kickoff")
@patch("artemis.experiment_plans.fast_grid_scan_plan.bps.wait")
@patch("artemis.experiment_plans.fast_grid_scan_plan.bps.complete")
@patch("artemis.experiment_plans.fast_grid_scan_plan.bps.mv")
@patch("artemis.experiment_plans.fast_grid_scan_plan.wait_for_fgs_valid")
def test_when_exception_occurs_during_running_then_eiger_disarmed(
wait_for_valid,
mock_mv,
def test_fgs_arms_eiger_without_grid_detect(
mock_complete,
mock_kickoff,
mock_abs_set,
mock_wait,
fake_fgs_composite: FGSComposite,
test_fgs_params: FGSInternalParameters,
mock_subscriptions: FGSCallbackCollection,
RE: RunEngine,
):
fake_fgs_composite.eiger.disarm_detector = MagicMock()
fake_fgs_composite.eiger.stage = MagicMock()
fake_fgs_composite.eiger.unstage = MagicMock()

fake_fgs_composite.eiger.filewriters_finished = Status()
fake_fgs_composite.eiger.filewriters_finished.set_finished()
fake_fgs_composite.eiger.odin.check_odin_state = MagicMock(return_value=True)
fake_fgs_composite.eiger.odin.file_writer.num_captured.sim_put(1200)
fake_fgs_composite.eiger.filewriters_finished = Status()
fake_fgs_composite.eiger.filewriters_finished.set_finished()
RE(run_gridscan(fake_fgs_composite, test_fgs_params))
fake_fgs_composite.eiger.stage.assert_called_once()
fake_fgs_composite.eiger.unstage.assert_called_once()

fake_fgs_composite.eiger.stage = MagicMock(
return_value=Status(None, None, 0, True, True)
)

mock_complete.side_effect = Exception()

with pytest.raises(Exception):
RE(
run_gridscan_and_move(
fake_fgs_composite,
test_fgs_params,
mock_subscriptions,
)
)

fake_fgs_composite.eiger.disarm_detector.assert_called_once()


# Eiger is armed if eiger.armed_status is complete and fan ready is called. This test is very slow - could mocking more functions could speed it up
def test_fgs_arms_eiger_without_grid_detect(
@patch("artemis.experiment_plans.fast_grid_scan_plan.bps.wait")
@patch("artemis.experiment_plans.fast_grid_scan_plan.bps.complete")
def test_when_grid_scan_fails_then_detector_disarmed_and_correct_exception_returned(
mock_complete,
mock_wait,
fake_fgs_composite: FGSComposite,
test_fgs_params: FGSInternalParameters,
mock_subscriptions: FGSCallbackCollection,
RE: RunEngine,
):
def get_good_status():
status = Status()
status.set_finished()
return status
class CompleteException(Exception):
pass

fake_fgs_composite.eiger.odin.check_odin_state = MagicMock(return_value=True)
fake_fgs_composite.eiger.odin.check_odin_initialised = MagicMock(
return_value=[True, True]
)
fake_fgs_composite.eiger.set_odin_pvs = MagicMock(return_value=get_good_status())
fake_fgs_composite.eiger.stale_params.sim_put(0)
fake_fgs_composite.eiger._wait_fan_ready = MagicMock(return_value=get_good_status())
fake_fgs_composite.eiger._wait_for_odin_status = MagicMock(
return_value=get_good_status()
mock_complete.side_effect = CompleteException()

fake_fgs_composite.eiger.stage = MagicMock(
return_value=Status(None, None, 0, True, True)
)

RE(bps.stage(fake_fgs_composite.eiger))
fake_fgs_composite.eiger._wait_fan_ready.assert_called_once()
fake_fgs_composite.eiger.odin.check_odin_state = MagicMock()

fake_fgs_composite.eiger.disarm_detector = MagicMock()
fake_fgs_composite.eiger.disable_roi_mode = MagicMock()

# Without the complete finishing we will not get all the images
fake_fgs_composite.eiger.ALL_FRAMES_TIMEOUT = 0.1

# Want to get the underlying completion error, not the one raised from unstage
with pytest.raises(CompleteException):
RE(run_gridscan(fake_fgs_composite, test_fgs_params))

fake_fgs_composite.eiger.disable_roi_mode.assert_called()
fake_fgs_composite.eiger.disarm_detector.assert_called()
Loading

0 comments on commit 8db9d78

Please sign in to comment.