Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c18bb25
Make generic read hardware plans
olliesilvester Feb 4, 2025
aa44564
Merge branch 'main' into generic_read_hardware
olliesilvester Feb 4, 2025
e439bc6
Small adjustments to parameter model and config server
olliesilvester Feb 4, 2025
f67d191
Merge remote-tracking branch 'origin/main' into modify_parameters_for…
olliesilvester Feb 11, 2025
b3a68ec
Fix type checking maybe
olliesilvester Feb 11, 2025
84dc795
Fix tests and typing
olliesilvester Feb 11, 2025
819d952
Move small bits of code out of Hyperion
olliesilvester Feb 11, 2025
9d8eb86
Merge branch 'main' into modify_parameters_for_common_XRC
olliesilvester Feb 11, 2025
4d46b43
Merge branch 'main' into generic_read_hardware
olliesilvester Feb 12, 2025
af60df8
Remove horrible function
olliesilvester Feb 13, 2025
b9f3b50
Address review comments
olliesilvester Feb 14, 2025
c31bdf6
Merge branch 'main' into generic_read_hardware
olliesilvester Feb 14, 2025
bd5f497
Fix test
olliesilvester Feb 14, 2025
ab9a3d4
Merge branch 'modify_parameters_for_common_XRC' into common_flyscan_x…
olliesilvester Feb 14, 2025
4db068a
Merge branch 'generic_read_hardware' into common_flyscan_xray_centre
olliesilvester Feb 14, 2025
960b2df
WIP tests broken
olliesilvester Feb 17, 2025
0fa1257
Fix tests
olliesilvester Feb 17, 2025
dae6fd1
Fix typing
olliesilvester Feb 17, 2025
3c09901
Merge remote-tracking branch 'origin/main' into common_flyscan_xray_c…
olliesilvester Feb 24, 2025
73ce426
Merge remote-tracking branch 'origin/main' into common_flyscan_xray_c…
olliesilvester Feb 24, 2025
5ac5e40
Add todo comments (wip)
olliesilvester Feb 25, 2025
3179d7f
Move undulator check to separate preprocessor
olliesilvester Mar 10, 2025
fccff8c
Use updated dodal
olliesilvester Mar 10, 2025
fbb17e4
Merge remote-tracking branch 'origin/main' into common_flyscan_xray_c…
olliesilvester Mar 10, 2025
ee79cf0
Merge remote-tracking branch 'origin/891_undulator_check_preprocessor…
olliesilvester Mar 10, 2025
7065030
Update to use new preprocessors
olliesilvester Mar 10, 2025
cd0dc6f
Improve comments and variable names
olliesilvester Mar 11, 2025
b1f22e1
Merge remote-tracking branch 'origin/main' into common_flyscan_xray_c…
olliesilvester Mar 11, 2025
1cc43f0
Merge remote-tracking branch 'origin/main' into common_flyscan_xray_c…
olliesilvester Mar 17, 2025
e7485c5
Move undulator check out of common
olliesilvester Mar 17, 2025
db4d23e
Create vmxm entry point for XRC scans
olliesilvester Mar 17, 2025
711b5f7
Fix linting
olliesilvester Mar 19, 2025
7b9e94c
Merge remote-tracking branch 'origin/main' into common_flyscan_xray_c…
olliesilvester Mar 19, 2025
4eb1a96
Merge remote-tracking branch 'origin/common_flyscan_xray_centre' into…
olliesilvester Mar 19, 2025
d8a439a
entry plan explicitly returns MsgGenerator
olliesilvester Mar 19, 2025
d6907c3
Merge remote-tracking branch 'origin/main' into vmxm_fast_grid_scans
olliesilvester Jun 12, 2025
c138b50
Rename smargon in FGS plan
olliesilvester Jun 16, 2025
01c91e1
Merge remote-tracking branch 'origin/main' into vmxm_fast_grid_scans
olliesilvester Aug 13, 2025
412da76
Minor updates
olliesilvester Aug 13, 2025
3d3e31b
Merge remote-tracking branch 'origin/main' into vmxm_fast_grid_scans
olliesilvester Aug 28, 2025
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
5 changes: 5 additions & 0 deletions src/mx_bluesky/beamlines/i02_1/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from mx_bluesky.beamlines.i02_1.i02_1_flyscan_xray_centre_plan import (
i02_1_flyscan_xray_centre,
)

__all__ = ["i02_1_flyscan_xray_centre"]
7 changes: 7 additions & 0 deletions src/mx_bluesky/beamlines/i02_1/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from pydantic.dataclasses import dataclass


@dataclass(frozen=True)
class I02_1_Constants:
GRAYLOG_PORT = 12232
LOG_FILE_NAME = "i02_1_bluesky.log"
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import bluesky.plan_stubs as bps
from dodal.devices.zebra.zebra import Zebra

ZEBRA_STATUS_TIMEOUT = 30


# Control Eiger from motion controller
def setup_zebra_for_xrc_flyscan(zebra: Zebra, group="setup_zebra_for_xrc", wait=True):
yield from bps.abs_set(
zebra.output.out_pvs[zebra.mapping.outputs.TTL_EIGER],
zebra.mapping.sources.IN1_TTL,
)
if wait:
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)


# State of zebra expected by GDA
def tidy_up_zebra_after_gridscan(
zebra: Zebra, group="tidy_up_vmxm_zebra_after_gridscan", wait=False
):
yield from bps.abs_set(
zebra.output.out_pvs[zebra.mapping.outputs.TTL_EIGER],
zebra.mapping.sources.OR1,
group=group,
)

if wait:
yield from bps.wait(group)
122 changes: 122 additions & 0 deletions src/mx_bluesky/beamlines/i02_1/i02_1_flyscan_xray_centre_plan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from functools import partial

import bluesky.plan_stubs as bps
import bluesky.preprocessors as bpp
import pydantic
from bluesky.utils import MsgGenerator
from dodal.beamlines.i02_1 import TwoDFastGridScan
from dodal.common import inject
from dodal.devices.eiger import EigerDetector
from dodal.devices.fast_grid_scan import (
set_fast_grid_scan_params as set_flyscan_params_plan,
)
from dodal.devices.i02_1.sample_motors import SampleMotors
from dodal.devices.synchrotron import Synchrotron
from dodal.devices.zebra.zebra import Zebra
from dodal.devices.zocalo.zocalo_results import (
ZocaloResults,
)

from mx_bluesky.beamlines.i02_1.constants import I02_1_Constants
from mx_bluesky.beamlines.i02_1.device_setup_plans.setup_zebra import (
setup_zebra_for_xrc_flyscan,
tidy_up_zebra_after_gridscan,
)
from mx_bluesky.common.experiment_plans.common_flyscan_xray_centre_plan import (
CALLBACKS_FOR_SUBS_DECORATOR,
BeamlineSpecificFGSFeatures,
FlyScanEssentialDevices,
common_flyscan_xray_centre,
construct_beamline_specific_FGS_features,
)
from mx_bluesky.common.parameters.gridscan import SpecifiedThreeDGridScan
from mx_bluesky.common.utils.log import LOGGER, do_default_logging_setup


@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
class FlyScanXRayCentreComposite(FlyScanEssentialDevices):
"""All devices which are directly or indirectly required by this plan"""

# todo add fast grid scan device to essentials
zebra_fast_grid_scan: TwoDFastGridScan
zebra: Zebra
sample_stages: SampleMotors


def construct_i02_1_specific_features(
fgs_composite: FlyScanXRayCentreComposite,
parameters: SpecifiedThreeDGridScan,
) -> BeamlineSpecificFGSFeatures:
signals_to_read_pre_flyscan = [
fgs_composite.synchrotron.synchrotron_mode,
fgs_composite.sample_stages,
]
signals_to_read_during_collection = [
fgs_composite.eiger.bit_depth,
]

return construct_beamline_specific_FGS_features(
_zebra_triggering_setup,
partial(_tidy_plan, group="flyscan_zebra_tidy", wait=True),
partial(
set_flyscan_params_plan,
fgs_composite.zebra_fast_grid_scan,
parameters.FGS_params,
),
fgs_composite.zebra_fast_grid_scan,
signals_to_read_pre_flyscan,
signals_to_read_during_collection, # type: ignore # See : https://github.com/bluesky/bluesky/issues/1809
)


def _zebra_triggering_setup(
fgs_composite: FlyScanXRayCentreComposite,
):
yield from setup_zebra_for_xrc_flyscan(fgs_composite.zebra)


def _tidy_plan(
fgs_composite: FlyScanXRayCentreComposite, group, wait=True
) -> MsgGenerator:
LOGGER.info("Tidying up Zebra")
yield from tidy_up_zebra_after_gridscan(fgs_composite.zebra)
LOGGER.info("Tidying up Zocalo")
# make sure we don't consume any other results
yield from bps.unstage(fgs_composite.zocalo, group=group, wait=wait)


def i02_1_flyscan_xray_centre(
parameters: SpecifiedThreeDGridScan,
eiger: EigerDetector = inject("eiger"),
zebra_fast_grid_scan: TwoDFastGridScan = inject("TwoDFastGridScan"),
synchrotron: Synchrotron = inject("synchrotron"),
zebra: Zebra = inject("zebra"),
zocalo: ZocaloResults = inject("zocalo"),
sample_motors: SampleMotors = inject("sample_motors"),
) -> MsgGenerator:
"""BlueAPI entry point for XRC grid scans"""

do_default_logging_setup(
I02_1_Constants.LOG_FILE_NAME,
I02_1_Constants.GRAYLOG_PORT,
)

# Composites have to be made this way until https://github.com/DiamondLightSource/dodal/issues/874
# is done and we can properly use composite devices in BlueAPI
composite = FlyScanXRayCentreComposite(
eiger,
synchrotron,
zocalo,
sample_motors,
zebra_fast_grid_scan,
zebra,
sample_motors,
)

beamline_specific = construct_i02_1_specific_features(composite, parameters)

@bpp.subs_decorator(CALLBACKS_FOR_SUBS_DECORATOR)
def decorated_flyscan_plan():
yield from common_flyscan_xray_centre(composite, parameters, beamline_specific)

yield from decorated_flyscan_plan()
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,32 @@
)
from dodal.devices.zocalo import ZocaloResults
from dodal.devices.zocalo.zocalo_results import (
ZOCALO_STAGE_GROUP,
XrcResult,
get_full_processing_results,
)

from mx_bluesky.common.experiment_plans.inner_plans.do_fgs import (
ZOCALO_STAGE_GROUP,
kickoff_and_complete_gridscan,
)
from mx_bluesky.common.experiment_plans.inner_plans.read_hardware import (
read_hardware_plan,
)
from mx_bluesky.common.external_interaction.callbacks.common.log_uid_tag_callback import (
LogUidTaggingCallback,
)
from mx_bluesky.common.external_interaction.callbacks.common.zocalo_callback import (
ZocaloCallback,
)
from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
GridscanISPyBCallback,
)
from mx_bluesky.common.external_interaction.callbacks.xray_centre.nexus_callback import (
GridscanNexusFileCallback,
)
from mx_bluesky.common.parameters.constants import (
DocDescriptorNames,
EnvironmentConstants,
GridscanParamConstants,
PlanGroupCheckpointConstants,
PlanNameConstants,
Expand All @@ -41,6 +54,17 @@
from mx_bluesky.common.utils.tracing import TRACER
from mx_bluesky.common.xrc_result import XRayCentreResult

# Hyperion handles its own callbacks via an external process. Other beamlines using this plan should wrap their entry point with
# @bpp.subs_decorator(CALLBACKS_FOR_SUBS_DECORATOR)
CALLBACKS_FOR_SUBS_DECORATOR = [
GridscanNexusFileCallback(param_type=SpecifiedThreeDGridScan),
GridscanISPyBCallback(
param_type=SpecifiedThreeDGridScan,
emit=ZocaloCallback(PlanNameConstants.DO_FGS, EnvironmentConstants.ZOCALO_ENV),
),
LogUidTaggingCallback(),
]


@dataclasses.dataclass
class BeamlineSpecificFGSFeatures:
Expand Down Expand Up @@ -240,7 +264,7 @@ def run_gridscan(
):
# Currently gridscan only works for omega 0, see https://github.com/DiamondLightSource/mx-bluesky/issues/410
with TRACER.start_span("moving_omega_to_0"):
yield from bps.abs_set(fgs_composite.smargon.omega, 0)
yield from bps.abs_set(fgs_composite.sample_stage.omega, 0)

with TRACER.start_span("ispyb_hardware_readings"):
yield from beamline_specific.read_pre_flyscan_plan()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from mx_bluesky.common.external_interaction.callbacks.common.zocalo_callback import (
ZocaloCallback,
)
from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
GridscanISPyBCallback,
)
from mx_bluesky.common.external_interaction.callbacks.xray_centre.nexus_callback import (
GridscanNexusFileCallback,
)
from mx_bluesky.common.parameters.constants import (
EnvironmentConstants,
PlanNameConstants,
)
from mx_bluesky.common.parameters.gridscan import SpecifiedThreeDGridScan


def create_gridscan_callbacks() -> tuple[
GridscanNexusFileCallback, GridscanISPyBCallback
]:
return (
GridscanNexusFileCallback(param_type=SpecifiedThreeDGridScan),
GridscanISPyBCallback(
param_type=SpecifiedThreeDGridScan,
emit=ZocaloCallback(
PlanNameConstants.DO_FGS, EnvironmentConstants.ZOCALO_ENV
),
),
)
12 changes: 11 additions & 1 deletion src/mx_bluesky/common/parameters/device_composites.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Protocol

import pydantic
from dodal.devices.aperturescatterguard import (
ApertureScatterguard,
Expand All @@ -23,14 +25,21 @@
from dodal.devices.zebra.zebra import Zebra
from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
from dodal.devices.zocalo import ZocaloResults
from ophyd_async.epics.motor import Motor


# FGS plan only uses the gonio to set omega to 0, no need to constrain to a more complex device
class SampleStageWithOmega(Protocol):
omega: Motor


@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
class FlyScanEssentialDevices:
eiger: EigerDetector
synchrotron: Synchrotron
zocalo: ZocaloResults
smargon: Smargon
sample_stage: SampleStageWithOmega
# TODO add fgs device


@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
Expand Down Expand Up @@ -63,3 +72,4 @@ class GridDetectThenXRayCentreComposite(FlyScanEssentialDevices):
zebra: Zebra
robot: BartRobot
sample_shutter: ZebraShutter
smargon: Smargon
2 changes: 2 additions & 0 deletions src/mx_bluesky/hyperion/parameters/device_composites.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from dodal.devices.flux import Flux
from dodal.devices.robot import BartRobot
from dodal.devices.s4_slit_gaps import S4SlitGaps
from dodal.devices.smargon import Smargon
from dodal.devices.synchrotron import Synchrotron
from dodal.devices.undulator import Undulator
from dodal.devices.xbpm_feedback import XBPMFeedback
Expand Down Expand Up @@ -52,6 +53,7 @@ class HyperionFlyScanXRayCentreComposite(FlyScanEssentialDevices):
backlight: Backlight
xbpm_feedback: XBPMFeedback
zebra_fast_grid_scan: ZebraFastGridScan
smargon: Smargon


@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
Expand Down
51 changes: 46 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,11 @@ def fake_create_rotation_devices(
)


@pytest.fixture
def oav_parameters_for_rotation(test_config_files) -> OAVParameters:
return OAVParameters(oav_config_json=test_config_files["oav_config_json"])


@pytest.fixture
def zocalo(done_status, RE: RunEngine):
zoc = i03.zocalo(connect_immediately=True, mock=True)
Expand Down Expand Up @@ -925,11 +930,6 @@ async def create_mock_signals(devices_and_signals: dict[Device, dict[str, Any]])
return panda


@pytest.fixture
def oav_parameters_for_rotation(test_config_files) -> OAVParameters:
return OAVParameters(oav_config_json=test_config_files["oav_config_json"])


async def async_status_done():
await asyncio.sleep(0)

Expand Down Expand Up @@ -1671,6 +1671,47 @@ class TestData(OavGridSnapshotTestEvents):
}
]

test_result_large = [
{
"centre_of_mass": [1, 2, 3],
"max_voxel": [1, 2, 3],
"max_count": 105062,
"n_voxels": 35,
"total_count": 2387574,
"bounding_box": [[2, 2, 2], [8, 8, 7]],
}
]
test_result_medium = [
{
"centre_of_mass": [1, 2, 3],
"max_voxel": [2, 4, 5],
"max_count": 50000,
"n_voxels": 35,
"total_count": 100000,
"bounding_box": [[1, 2, 3], [3, 4, 4]],
}
]
test_result_small = [
{
"centre_of_mass": [1, 2, 3],
"max_voxel": [1, 2, 3],
"max_count": 1000,
"n_voxels": 35,
"total_count": 1000,
"bounding_box": [[2, 2, 2], [3, 3, 3]],
}
]
test_result_below_threshold = [
{
"centre_of_mass": [2, 3, 4],
"max_voxel": [2, 3, 4],
"max_count": 2,
"n_voxels": 1,
"total_count": 2,
"bounding_box": [[1, 2, 3], [2, 3, 4]],
}
]


def _mock_ispyb_conn(base_ispyb_conn, position_id, dcgid, dcids, giids):
def upsert_data_collection(values):
Expand Down
Loading