Source code for sm_bluesky.electron_analyser.plan_stubs.analyser_per_step

from collections.abc import Iterable, Mapping, Sequence
from typing import Any, TypeVar

from bluesky.plan_stubs import move_per_step, mv, trigger_and_read
from bluesky.protocols import Movable, Readable
from bluesky.utils import (
    MsgGenerator,
    plan,
)
from dodal.devices.electron_analyser.base import ElectronAnalyserDetector
from dodal.log import LOGGER

T = TypeVar("T")


def get_first_of_type(objects: Iterable[Any], target_type: type[T]) -> T:
    for obj in objects:
        if isinstance(obj, target_type):
            return obj
    raise RuntimeError(f"Cannot find object from {objects} with type {target_type}")


[docs] @plan def analyser_nd_step( detectors: Sequence[Readable], step: Mapping[Movable, Any], pos_cache: dict[Movable, Any], *args, ) -> MsgGenerator: """ Inner loop of an N-dimensional step scan Modified default function for ``per_step`` param in ND plans. Performs an extra for loop for each ElectronAnalyserRegionDetector present so they can be collected one by one. Parameters ---------- detectors : iterable devices to read step : dict mapping motors to positions in this step pos_cache : dict mapping motors to their last-set positions """ analyser = get_first_of_type(detectors, ElectronAnalyserDetector) if analyser.sequence.data is None: raise RuntimeError( f"Electron analyser {analyser.name}.sequence is None. It must be configured" " using prepare plan stub." ) # Step provides the map of motors to single position to move to. Move motors to # required positions. yield from move_per_step(step, pos_cache) # This is to satisfy type checking. Motors are Moveable and Readable, so make # them Readable so positions can be measured. motors: list[Readable] = [s for s in step.keys() if isinstance(s, Readable)] readables = list(detectors) + motors for region in analyser.sequence.data.get_enabled_regions(): LOGGER.info(f"Scanning region {region.name}.") yield from mv(analyser, region) yield from trigger_and_read(readables, name=region.name)
@plan def analyser_shot(detectors: Sequence[Readable], *args) -> MsgGenerator: yield from analyser_nd_step(detectors, {}, {}, *args)