Source code for dodal.plan_stubs.check_topup

from typing import Any

import bluesky.plan_stubs as bps

from dodal.common.beamlines.beamline_parameters import (
    get_beamline_parameters,
)
from dodal.devices.synchrotron import Synchrotron, SynchrotronMode
from dodal.log import LOGGER

ALLOWED_MODES = [SynchrotronMode.USER, SynchrotronMode.SPECIAL]
DECAY_MODE_COUNTDOWN = -1  # Value of the start_countdown PV when in decay mode
COUNTDOWN_DURING_TOPUP = 0

DEFAULT_THRESHOLD_EXPOSURE_S = 120
DEFAULT_TOPUP_GATE_DELAY_S = 1


[docs] class TopupConfig: # For planned exposures less than this value, wait for topup to finish instead of # collecting throughout topup. THRESHOLD_EXPOSURE_S = "dodal_topup_threshold_exposure_s" # Additional configurable safety margin to wait after the end of topup, as the start # and end countdowns do not have the same precision, and in addition we want to be sure # that collection does not overlap with any transients that may occur after the # nominal endpoint. TOPUP_GATE_DELAY_S = "dodal_topup_end_delay_s"
def _in_decay_mode(time_to_topup): if time_to_topup == DECAY_MODE_COUNTDOWN: LOGGER.info("Machine in decay mode, gating disabled") return True return False def _gating_permitted(machine_mode: SynchrotronMode): if machine_mode in ALLOWED_MODES: LOGGER.info("Machine in allowed mode, gating top up enabled.") return True LOGGER.info("Machine not in allowed mode, gating disabled") return False def _delay_to_avoid_topup( total_run_time_s: float, time_to_topup_s: float, topup_configuration: dict, total_exposure_time_s: float, ) -> bool: """Determine whether we should delay collection until after a topup. Generally if a topup is due to occur during the collection we will delay collection until after the topup. However for long-running collections, impact of the topup is potentially less and collection-duration may be a significant fraction of the topup-interval, therefore we may wish to collect during a topup. Args: total_run_time_s: Anticipated time until end of the collection in seconds time_to_topup_s: Time to the start of the topup as measured from the PV topup_configuration: configuration dictionary total_exposure_time_s: Total exposure time of the sample in s""" if total_run_time_s > time_to_topup_s: limit_s = topup_configuration.get( TopupConfig.THRESHOLD_EXPOSURE_S, DEFAULT_THRESHOLD_EXPOSURE_S ) gate = total_exposure_time_s < limit_s if gate: LOGGER.info(f""" Exposure time of {total_exposure_time_s}s below the threshold of {limit_s}s. Collection delayed until topup done. """) else: LOGGER.info(f""" Exposure time of {total_exposure_time_s}s meets the threshold of {limit_s}s. Collection proceeding through topup. """) return gate LOGGER.info( """ Total run time less than time to next topup. Proceeding with collection. """ ) return False
[docs] def wait_for_topup_complete(synchrotron: Synchrotron): LOGGER.info("Waiting for topup to complete") start = yield from bps.rd(synchrotron.top_up_start_countdown) while start == COUNTDOWN_DURING_TOPUP: yield from bps.sleep(0.1) start = yield from bps.rd(synchrotron.top_up_start_countdown)
[docs] def check_topup_and_wait_if_necessary( synchrotron: Synchrotron, total_exposure_time: float, ops_time: float, # Account for xray centering, rotation speed, etc ): # See https://github.com/DiamondLightSource/hyperion/issues/932 """A small plan to check if topup gating is permitted and sleep until the topup\ is over if it starts before the end of collection. Args: synchrotron (Synchrotron): Synchrotron device. total_exposure_time (float): Expected total exposure time for \ collection, in seconds. ops_time (float): Additional time to account for various operations,\ eg. x-ray centering, in seconds. Defaults to 30.0. """ machine_mode = yield from bps.rd(synchrotron.synchrotron_mode) assert isinstance(machine_mode, SynchrotronMode) time_to_topup = yield from bps.rd(synchrotron.top_up_start_countdown) if _in_decay_mode(time_to_topup) or not _gating_permitted(machine_mode): yield from bps.null() return tot_run_time = total_exposure_time + ops_time end_topup = yield from bps.rd(synchrotron.top_up_end_countdown) topup_configuration = _load_topup_configuration_from_properties_file() should_wait = _delay_to_avoid_topup( tot_run_time, time_to_topup, topup_configuration, total_exposure_time, ) topup_gate_delay = topup_configuration.get( TopupConfig.TOPUP_GATE_DELAY_S, DEFAULT_TOPUP_GATE_DELAY_S ) time_to_wait = end_topup + topup_gate_delay if should_wait else 0.0 yield from bps.sleep(time_to_wait) check_start = yield from bps.rd(synchrotron.top_up_start_countdown) if check_start == COUNTDOWN_DURING_TOPUP: yield from wait_for_topup_complete(synchrotron)
def _load_topup_configuration_from_properties_file() -> dict[str, Any]: params = get_beamline_parameters() return params.params