Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add do fgs_plan #140

Merged
merged 27 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ce4ab7b
old commit
olliesilvester Aug 5, 2024
c8ef9a0
Create general do_fgs plan stub with basic tests
olliesilvester Aug 5, 2024
9b91030
Correct comment
olliesilvester Aug 5, 2024
6aa5dae
Move plan into a plans stubs folder
olliesilvester Aug 5, 2024
b439f20
Move Hyperion constants and read_hardware_for_zocalo plan, and remove…
olliesilvester Aug 20, 2024
161ebec
Merge remote-tracking branch 'origin/main' into 80_add_do_fgs_plan
olliesilvester Oct 14, 2024
431a5d8
move read_hardware_for_zocalo outside of hyperion
olliesilvester Oct 14, 2024
4c6b31e
Move some constants out of hyperion
olliesilvester Oct 14, 2024
812b737
Adjust tests
olliesilvester Oct 14, 2024
addaae8
make sure plan is called with run open
olliesilvester Oct 14, 2024
361103c
Restructure location of things
olliesilvester Oct 15, 2024
54d3894
improve comments and tests
olliesilvester Oct 15, 2024
3215aca
add read hardware for zocalo test
olliesilvester Oct 15, 2024
3680210
Move common parameters to correct location
olliesilvester Oct 15, 2024
2f2fe98
Merge remote-tracking branch 'origin/main' into 80_add_do_fgs_plan
olliesilvester Oct 23, 2024
acbfb73
Move out larger section of FGS plan
olliesilvester Oct 23, 2024
c828ef5
Improve test, move tracing to common
olliesilvester Oct 23, 2024
124bfb4
Wait for zocalo queue to be empty before zocalo is triggered
olliesilvester Oct 23, 2024
8f0596e
Improved comments
olliesilvester Oct 23, 2024
268a036
FGS plan configures zocalo device metadata
olliesilvester Oct 23, 2024
20662ff
Remove plan name as a parameter
olliesilvester Oct 24, 2024
8fb7cc1
zocalo env as parameter, plan_stub is now a regular plan
olliesilvester Oct 24, 2024
73f5987
Move log to correct line
olliesilvester Oct 24, 2024
9c3512c
Add a few more tests
olliesilvester Oct 25, 2024
bcc1398
fix typing check
olliesilvester Oct 25, 2024
9de7297
Merge branch 'main' into 80_add_do_fgs_plan
olliesilvester Oct 29, 2024
72677d5
Review response
olliesilvester Oct 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import bluesky.plan_stubs as bps
from dodal.devices.eiger import EigerDetector

from mx_bluesky.common.parameters.constants import MxConstants


def read_hardware_for_zocalo(detector: EigerDetector):
olliesilvester marked this conversation as resolved.
Show resolved Hide resolved
""" "
If the RunEngine is subscribed to the ZocaloCallback, this plan will also trigger zocalo.
A bluesky run must be open to use this plan
"""
yield from bps.create(name=MxConstants.DESCRIPTORS.ZOCALO_HW_READ)
yield from bps.read(detector.odin.file_writer.id) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
yield from bps.save()
35 changes: 35 additions & 0 deletions src/mx_bluesky/common/parameters/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import os

from pydantic.dataclasses import dataclass

TEST_MODE = os.environ.get("HYPERION_TEST_MODE") # Environment name will be updated in
# https://github.com/DiamondLightSource/mx-bluesky/issues/214


@dataclass(frozen=True)
class DocDescriptorNames:
# Robot load event descriptor
ROBOT_LOAD = "robot_load"
# For callbacks to use
OAV_ROTATION_SNAPSHOT_TRIGGERED = "rotation_snapshot_triggered"
OAV_GRID_SNAPSHOT_TRIGGERED = "snapshot_to_ispyb"
HARDWARE_READ_PRE = "read_hardware_for_callbacks_pre_collection"
HARDWARE_READ_DURING = "read_hardware_for_callbacks_during_collection"
ZOCALO_HW_READ = "zocalo_read_hardware_plan"


@dataclass(frozen=True)
class PlanNameConstants:
DO_FGS = "do_fgs"


@dataclass(frozen=True)
class TriggerConstants:
ZOCALO = "trigger_zocalo_on"


@dataclass(frozen=True)
class MxConstants:
DESCRIPTORS = DocDescriptorNames()
TRIGGER = TriggerConstants()
ZOCALO_ENV = "dev_artemis" if TEST_MODE else "artemis"
1 change: 1 addition & 0 deletions src/mx_bluesky/common/plans/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Common MX plans which includes open and close data collection runs. These be used in isolation or as part of a larger plan"""
121 changes: 121 additions & 0 deletions src/mx_bluesky/common/plans/do_fgs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from collections.abc import Callable
from time import time

import bluesky.plan_stubs as bps
import bluesky.preprocessors as bpp
from blueapi.core import MsgGenerator
from dodal.devices.eiger import EigerDetector
from dodal.devices.fast_grid_scan import FastGridScanCommon
from dodal.devices.synchrotron import Synchrotron
from dodal.devices.zocalo.zocalo_results import (
ZOCALO_STAGE_GROUP,
)
from dodal.log import LOGGER
from dodal.plans.check_topup import check_topup_and_wait_if_necessary
from scanspec.core import AxesPoints, Axis

from mx_bluesky.common.device_setup_plans.read_hardware_for_setup import (
read_hardware_for_zocalo,
)
from mx_bluesky.common.parameters.constants import (
MxConstants,
PlanNameConstants,
TriggerConstants,
)
from mx_bluesky.common.utils.tracing import TRACER


def _wait_for_zocalo_to_stage_then_do_fgs(
grid_scan_device: FastGridScanCommon,
detector: EigerDetector,
synchrotron: Synchrotron,
during_collection_plan: Callable[[], MsgGenerator] | None = None,
):
expected_images = yield from bps.rd(grid_scan_device.expected_images)
exposure_sec_per_image = yield from bps.rd(detector.cam.acquire_time) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
LOGGER.info("waiting for topup if necessary...")
yield from check_topup_and_wait_if_necessary(
synchrotron,
expected_images * exposure_sec_per_image,
30.0,
)

# Make sure ZocaloResults queue is clear and ready to accept our new data. Zocalo MUST
# have been staged using ZOCALO_STAGE_GROUP prior to this
LOGGER.info("Waiting for Zocalo device queue to have been cleared...")
yield from bps.wait(ZOCALO_STAGE_GROUP)

# Triggers Zocalo if RE is subscribed to ZocaloCallback
yield from read_hardware_for_zocalo(detector)
LOGGER.info("Wait for all moves with no assigned group")
yield from bps.wait()

LOGGER.info("kicking off FGS")
yield from bps.kickoff(grid_scan_device, wait=True)
gridscan_start_time = time()
if during_collection_plan:
yield from during_collection_plan()
LOGGER.info("completing FGS")
yield from bps.complete(grid_scan_device, wait=True)
# Remove this logging statement once metrics have been added
LOGGER.info(
f"Grid scan motion program took {round(time()-gridscan_start_time,2)} to complete"
)


def kickoff_and_complete_gridscan(
gridscan: FastGridScanCommon,
detector: EigerDetector, # Once Eiger inherits from StandardDetector, use that type instead
synchrotron: Synchrotron,
scan_points: list[AxesPoints[Axis]],
scan_start_indices: list[int],
plan_during_collection: Callable[[], MsgGenerator] | None = None,
zocalo_environment: str = MxConstants.ZOCALO_ENV,
):
"""Triggers a grid scan motion program and waits for completion, accounting for synchrotron topup.
If the RunEngine is subscribed to ZocaloCallback, this plan will also trigger Zocalo.

Can be used for multiple successive grid scans, see Hyperion's usage

Args:
gridscan (FastGridScanCommon): Device which can trigger a fast grid scan and wait for completion
detector (EigerDetector) Detector device
synchrotron (Synchrotron): Synchrotron device
scan_points (list[AxesPoints[Axis]]): Each element in the list contains all the grid points for that grid scan.
Two elements in this list indicates that two grid scans will be done, eg for Hyperion's 3D grid scans.
scan_start_indices (list[int]): Contains the first index of each grid scan
plan_during_collection (Optional, MsgGenerator): Generic plan called in between kickoff and completion,
eg waiting on zocalo.
zocalo_environment (Optional, str) Used for zocalo connection
"""

assert len(scan_points) == len(
scan_start_indices
), "scan_points and scan_start_indices must be lists of the same length!"

plan_name = PlanNameConstants.DO_FGS

@TRACER.start_as_current_span(plan_name)
@bpp.set_run_key_decorator(plan_name)
@bpp.run_decorator(
md={
"subplan_name": plan_name,
TriggerConstants.ZOCALO: plan_name,
"scan_points": scan_points,
"scan_start_indices": scan_start_indices,
"zocalo_environment": zocalo_environment,
}
)
@bpp.contingency_decorator(
except_plan=lambda e: (yield from bps.stop(detector)), # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
else_plan=lambda: (yield from bps.unstage(detector)), # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
)
def _decorated_do_fgs():
yield from _wait_for_zocalo_to_stage_then_do_fgs(
gridscan,
detector,
synchrotron,
during_collection_plan=plan_during_collection,
)

yield from _decorated_do_fgs()
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from opentelemetry.sdk.trace.export import BatchSpanProcessor


def setup_tracing():
resource = Resource(attributes={SERVICE_NAME: "Hyperion"})
def setup_tracing(service_name: str = "Hyperion"):
resource = Resource(attributes={SERVICE_NAME: service_name})

Check warning on line 12 in src/mx_bluesky/common/utils/tracing.py

View check run for this annotation

Codecov / codecov/patch

src/mx_bluesky/common/utils/tracing.py#L12

Added line #L12 was not covered by tests

traceProvider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(
Expand Down
2 changes: 1 addition & 1 deletion src/mx_bluesky/hyperion/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pydantic.dataclasses import dataclass

from mx_bluesky.common.utils.log import do_default_logging_setup, flush_debug_handler
from mx_bluesky.common.utils.tracing import TRACER
from mx_bluesky.hyperion.exceptions import WarningException
from mx_bluesky.hyperion.experiment_plans.experiment_registry import (
PLAN_REGISTRY,
Expand All @@ -41,7 +42,6 @@
from mx_bluesky.hyperion.parameters.cli import parse_cli_args
from mx_bluesky.hyperion.parameters.components import HyperionParameters
from mx_bluesky.hyperion.parameters.constants import CONST, Actions, Status
from mx_bluesky.hyperion.tracing import TRACER
from mx_bluesky.hyperion.utils.context import setup_context

VERBOSE_EVENT_LOGGING: bool | None = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,3 @@ def read_hardware_during_collection(
yield from bps.read(dcm.energy_in_kev)
yield from bps.read(detector.bit_depth) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
yield from bps.save()


def read_hardware_for_zocalo(detector: EigerDetector):
yield from bps.create(name=CONST.DESCRIPTORS.ZOCALO_HW_READ)
yield from bps.read(detector.odin.file_writer.id) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
yield from bps.save()
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from collections.abc import Callable
from functools import partial
from pathlib import Path
from time import time
from typing import Protocol

import bluesky.plan_stubs as bps
Expand Down Expand Up @@ -42,14 +41,13 @@
ZocaloResults,
get_processing_result,
)
from dodal.plans.check_topup import check_topup_and_wait_if_necessary
from ophyd_async.fastcs.panda import HDFPanda
from scanspec.core import AxesPoints, Axis

from mx_bluesky.common.plans.do_fgs import kickoff_and_complete_gridscan
from mx_bluesky.common.utils.tracing import TRACER
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_x_y_z
from mx_bluesky.hyperion.device_setup_plans.read_hardware_for_setup import (
read_hardware_during_collection,
read_hardware_for_zocalo,
read_hardware_pre_collection,
)
from mx_bluesky.hyperion.device_setup_plans.setup_panda import (
Expand All @@ -69,7 +67,6 @@
from mx_bluesky.hyperion.log import LOGGER
from mx_bluesky.hyperion.parameters.constants import CONST
from mx_bluesky.hyperion.parameters.gridscan import ThreeDGridScan
from mx_bluesky.hyperion.tracing import TRACER
from mx_bluesky.hyperion.utils.context import device_composite_from_context


Expand Down Expand Up @@ -143,8 +140,6 @@ def flyscan_xray_centre(
@bpp.run_decorator( # attach experiment metadata to the start document
md={
"subplan_name": CONST.PLAN.GRIDSCAN_OUTER,
CONST.TRIGGER.ZOCALO: CONST.PLAN.DO_FGS,
"zocalo_environment": parameters.zocalo_environment,
"hyperion_parameters": parameters.model_dump_json(),
"activate_callbacks": [
"GridscanNexusFileCallback",
Expand Down Expand Up @@ -296,66 +291,11 @@ def run_gridscan(
fgs_composite.synchrotron,
[parameters.scan_points_first_grid, parameters.scan_points_second_grid],
parameters.scan_indices,
do_during_run=read_during_collection,
plan_during_collection=read_during_collection,
)
yield from bps.abs_set(feature_controlled.fgs_motors.z_steps, 0, wait=False)


def kickoff_and_complete_gridscan(
gridscan: FastGridScanCommon,
eiger: EigerDetector,
synchrotron: Synchrotron,
scan_points: list[AxesPoints[Axis]],
scan_start_indices: list[int],
do_during_run: Callable[[], MsgGenerator] | None = None,
):
@TRACER.start_as_current_span(CONST.PLAN.DO_FGS)
@bpp.set_run_key_decorator(CONST.PLAN.DO_FGS)
@bpp.run_decorator(
md={
"subplan_name": CONST.PLAN.DO_FGS,
"scan_points": scan_points,
"scan_start_indices": scan_start_indices,
}
)
@bpp.contingency_decorator(
except_plan=lambda e: (yield from bps.stop(eiger)), # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
else_plan=lambda: (yield from bps.unstage(eiger)), # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
)
def do_fgs():
# Check topup gate
expected_images = yield from bps.rd(gridscan.expected_images)
exposure_sec_per_image = yield from bps.rd(eiger.cam.acquire_time) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
LOGGER.info("waiting for topup if necessary...")
yield from check_topup_and_wait_if_necessary(
synchrotron,
expected_images * exposure_sec_per_image,
30.0,
)
yield from read_hardware_for_zocalo(eiger)
LOGGER.info("Wait for all moves with no assigned group")
yield from bps.wait()
LOGGER.info("kicking off FGS")
yield from bps.kickoff(gridscan, wait=True)
gridscan_start_time = time()
LOGGER.info("Waiting for Zocalo device queue to have been cleared...")
yield from bps.wait(
ZOCALO_STAGE_GROUP
) # Make sure ZocaloResults queue is clear and ready to accept our new data
if do_during_run:
LOGGER.info(f"Running {do_during_run} during FGS")
yield from do_during_run()
LOGGER.info("completing FGS")
yield from bps.complete(gridscan, wait=True)

# Remove this logging statement once metrics have been added
LOGGER.info(
f"Gridscan motion program took {round(time()-gridscan_start_time,2)} to complete"
)

yield from do_fgs()


def wait_for_gridscan_valid(fgs_motors: FastGridScanCommon, timeout=0.5):
LOGGER.info("Waiting for valid fgs_params")
SLEEP_PER_CHECK = 0.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
from dodal.devices.zebra_controlled_shutter import ZebraShutter
from dodal.plans.check_topup import check_topup_and_wait_if_necessary

from mx_bluesky.common.device_setup_plans.read_hardware_for_setup import (
read_hardware_for_zocalo,
)
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
cleanup_sample_environment,
move_phi_chi_omega,
Expand All @@ -32,7 +35,6 @@
)
from mx_bluesky.hyperion.device_setup_plans.read_hardware_for_setup import (
read_hardware_during_collection,
read_hardware_for_zocalo,
read_hardware_pre_collection,
)
from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from bluesky import preprocessors as bpp
from dodal.devices.zocalo.zocalo_results import ZOCALO_READING_PLAN_NAME

from mx_bluesky.common.parameters.constants import PlanNameConstants
from mx_bluesky.common.utils.log import set_dcgid_tag
from mx_bluesky.hyperion.external_interaction.callbacks.common.ispyb_mapping import (
populate_data_collection_group,
Expand Down Expand Up @@ -86,7 +87,7 @@ def __init__(
self._processing_start_time: float | None = None

def activity_gated_start(self, doc: RunStart):
if doc.get("subplan_name") == CONST.PLAN.DO_FGS:
if doc.get("subplan_name") == PlanNameConstants.DO_FGS:
self._start_of_fgs_uid = doc.get("uid")
if doc.get("subplan_name") == CONST.PLAN.GRID_DETECT_AND_DO_GRIDSCAN:
self.uid_to_finalize_on = doc.get("uid")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from ispyb.strictordereddict import StrictOrderedDict
from pydantic import BaseModel

from mx_bluesky.common.utils.tracing import TRACER
from mx_bluesky.hyperion.external_interaction.ispyb.data_model import (
DataCollectionGridInfo,
DataCollectionGroupInfo,
Expand All @@ -22,7 +23,6 @@
get_session_id_from_visit,
)
from mx_bluesky.hyperion.log import ISPYB_LOGGER
from mx_bluesky.hyperion.tracing import TRACER

if TYPE_CHECKING:
pass
Expand Down
Loading
Loading