def take_dark(): """a plan for taking a single dark frame""" print("INFO: closing shutter...") yield from bps.abs_set(xpd_configuration.get("shutter"), XPD_SHUTTER_CONF["close"], wait=True) print("INFO: taking dark frame....") # upto this stage, area_det has been configured to so exposure time is # correct area_det = xpd_configuration["area_det"] acq_time = area_det.cam.acquire_time.get() num_frame = area_det.images_per_set.get() computed_exposure = acq_time * num_frame # update md _md = { "sp_time_per_frame": acq_time, "sp_num_frames": num_frame, "sp_computed_exposure": computed_exposure, "sp_type": "ct", "sp_plan_name": "dark_{}".format(computed_exposure), "dark_frame": True, } c = bp.count([area_det], md=_md) yield from bpp.subs_wrapper(c, {"stop": [_update_dark_dict_list]}) print("opening shutter...")
def insert_take_dark(msg): now = time.time() nonlocal need_dark qualified_dark_uid = _validate_dark(expire_time=glbl["dk_window"]) area_det = xpd_configuration["area_det"] if (not need_dark) and (not qualified_dark_uid): need_dark = True if (need_dark and (not qualified_dark_uid) and msg.command == "open_run" and ("dark_frame" not in msg.kwargs)): # We are about to start a new 'run' (e.g., a count or a scan). # Insert a dark frame run first. need_dark = False # Annoying detail: the detector was probably already staged. # Unstage it (if it wasn't staged, nothing will happen) and # then take_dark() and then re-stage it. return ( bpp.pchain( bps.unstage(area_det), take_dark(), bps.stage(area_det), bpp.single_gen(msg), bps.abs_set( xpd_configuration.get("shutter"), XPD_SHUTTER_CONF["open"], wait=True, ), bps.sleep(glbl['shutter_sleep']), ), None, ) elif msg.command == "open_run" and "dark_frame" not in msg.kwargs: return ( bpp.pchain( bpp.single_gen(msg), bps.abs_set( xpd_configuration.get("shutter"), XPD_SHUTTER_CONF["open"], wait=True, ), bps.sleep(glbl['shutter_sleep'])), None, ) else: # do nothing if (not need_dark) return None, None
def set_beamdump_suspender(xrun, suspend_thres=None, resume_thres=None, wait_time=None, clear=True): """helper function to set suspender based on ring_current Parameters ---------- xrun : instance of RunEngine the run engine instance suspender will be installed suspend_thres : float, optional suspend if ring current value falls below this threshold. ring current value is read out from ring current signal when set_beamdump_suspender function is executed. default is the larger value between 50 mA or 50% of ring current resume_thres : float, optional resume if ring current value falls below this threshold. ring current value is read out from ring current signal when set_beamdump_suspender function is executed. default is the larger value among 50 mA or 80% of current ring current wait_time : float, optional wait time in seconds after the resume condition is met. default is 1200s (20 mins) clear : bool, optional option on whether to clear all the existing suspender(s). default is True (only newly added suspender will be applied) """ signal = xpd_configuration.get("ring_current", None) if signal is None: # edge case, attribute is accidentally removed raise RuntimeError("no ring current signal is found in " "current configuration, please reach out to " "local contact for more help.") signal_val = signal.get() default_suspend_thres = 50 default_resume_thres = 50 if suspend_thres is None: suspend_thres = max(default_suspend_thres, 0.5 * signal_val) if resume_thres is None: resume_thres = max(default_resume_thres, 0.8 * signal_val) if wait_time is None: wait_time = 1200 if suspend_thres <= 50: warnings.warn( "suspender set when beam current is low.\n" "For the best operation, run:\n" ">>> {}\n" "when beam current is at its full value." "To interrogate suspenders have" " been installed, please run :\n" ">>> {}\n".format("set_suspender(xrun)", "xrun.suspenders"), UserWarning, ) sus = SuspendFloor(signal, suspend_thres, resume_thresh=resume_thres, sleep=wait_time) if clear: xrun.clear_suspenders() xrun.install_suspender(sus) print("INFO: suspender on signal {}, with suspend threshold {} and " "resume threshold={}, wait time ={}s has been installed.\n".format( signal.name, suspend_thres, resume_thres, wait_time))
import bluesky.plans as bp import bluesky.plan_stubs as bps import bluesky.preprocessors as bpp from bluesky import RunEngine from bluesky.suspenders import SuspendFloor from bluesky.utils import normalize_subs_input, single_gen, Msg from bluesky.callbacks.broker import verify_files_saved from bluesky.preprocessors import pchain from xpdacq.glbl import glbl from xpdacq.tools import xpdAcqException from xpdacq.beamtime import ScanPlan, _summarize from xpdacq.xpdacq_conf import xpd_configuration, XPDACQ_MD_VERSION from xpdconf.conf import XPD_SHUTTER_CONF XPD_shutter = xpd_configuration.get("shutter") def _update_dark_dict_list(name, doc): """ generate dark frame reference This function should be subscribed to 'stop' documents from dark frame runs. """ # always grab from glbl state dark_dict_list = list(glbl["_dark_dict_list"]) # obtain light count time that is already set to area_det area_det = xpd_configuration["area_det"] acq_time = area_det.cam.acquire_time.get() num_frame = area_det.images_per_set.get() light_cnt_time = acq_time * num_frame