Source code for scanplans.beamtimehelper

"""A class to print sample information and generate bluesky plan to target samples."""
from pprint import pprint
from typing import Union, Tuple, Generator

from bluesky.plan_stubs import mv, null
from bluesky.simulators import summarize_plan
from xpdacq.beamtime import Beamtime, ScanPlan
from xpdacq.xpdacq_conf import xpd_configuration

__all__ = [
    "BeamtimeHelper"
]
POS_KEYS = (
    "sample_x",
    "sample_y"
)


[docs]class BeamtimeHelper: """ A class helping to tackle with tasks related to samples on a rack during the beam time. Attributes ---------- _bt The instance storing meta data of the sample and plan _pos_key The key for the position of samples. Default is the global variable POS_KEYS """ def __init__(self, bt: Beamtime, pos_key: Tuple[str, str] = POS_KEYS): """ Initiate the class instance. Parameters ---------- bt The instance storing meta data of the sample and plan pos_key (Optional) The keys of the horizontal position and vertical position fields. Default POS_KEY """ self._bt = bt self._pos_key = pos_key
[docs] def get_sample(self, sample: Union[int, str]) -> dict: """ Get metadata of a sample. Parameters ---------- sample The sample index or sample name key Returns ------- sample_meta the meta data of a sample """ if isinstance(sample, str): sample_cls = self._bt.samples[sample] elif isinstance(sample, int): sample_cls = list(self._bt.samples.values())[sample] else: raise ValueError(f"{sample} is not int or str. It is {type(sample)}.") sample_meta = dict(sample_cls.items()) return sample_meta
[docs] def print_sample(self, *samples: Union[int, str]): """ Print the sample information. Parameters ---------- samples The sample index or sample name key """ for sample in samples: sample_meta = self.get_sample(sample) pprint(sample_meta)
[docs] def get_plan(self, plan: Union[int, str]) -> Generator: """ Get the plan (message generator). Parameters ---------- plan The plan index or plan name key Returns ------- plan_gen The plan message generator. """ if isinstance(plan, str): plan_cls = self._bt.scanplans[plan] # type: ScanPlan elif isinstance(plan, int): plan_cls = list(self._bt.scanplans.values())[plan] # type: ScanPlan else: raise ValueError(f"{plan} is not int or str. It is {type(plan)}.") plan_gen = plan_cls.factory() return plan_gen
[docs] def print_plan(self, *plans: Union[int, str]): """ Print the plan information. Parameters ---------- plans The plan index or plan name key """ for plan in plans: plan_gen = self.get_plan(plan) summarize_plan(plan_gen)
[docs] def aim_at_sample(self, sample): """ A generator of message: move the sample to the beam spot according to sample position metadata. Parameters ---------- sample The sample index or sample name key Examples -------- Initiate a BeamtimeHelper. >>> bthelper = BeamtimeHelper(bt) Check the motors. >>> xpd_configuration["x_controller"] >>> xpd_configuration["y_controller"] Aim at the sample of index 0 in bt. >>> RE(bthelper.aim_at_sample(0)) Aim at the sample "Ni" in bt. >>> RE(bthelper.aim_at_sample("Ni")) """ posx_controller = xpd_configuration["x_controller"] posy_controller = xpd_configuration["y_controller"] sample_meta = self.get_sample(sample) name = sample_meta.get("sample_name") print(f"INFO: Target sample {name}") pos_x_key, pos_y_key = self._pos_key pos_x = sample_meta.get(pos_x_key) pos_y = sample_meta.get(pos_y_key) if pos_x is None: print(f"Warning: No {pos_x_key} in sample {sample} -> Do nothing") else: print(f"INFO: Move to x = {pos_x}") yield from mv(posx_controller, float(pos_x)) if pos_y is None: print(f"Warning: No {pos_y_key} in sample {sample} -> Do nothing") else: print(f"INFO: Move to y = {pos_y}") yield from mv(posy_controller, float(pos_y)) yield from null()