def set(self, mode, config=None): """ (Re)-initialize all devices based on given mode simulated devices <-- debug actual devices <-- dryrun, production """ if mode.lower() not in ['debug', 'dryrun', 'production']: raise ValueError(f"Unknown mode: {mode}") else: self._mode = mode global A_shutter global suspend_A_shutter global tomostage global preci, samX, ksamX, ksamZ, samY global psofly global det # re-init all tomo related devices A_shutter = get_shutter(self._mode) tomostage = get_motors(self._mode) preci = tomostage.preci samX = tomostage.samX ksamX = tomostage.ksamX ksamZ = tomostage.ksamZ samY = tomostage.samY psofly = get_fly_motor(self._mode) det = get_detector(self._mode) # some quick sanity check production mode import apstools.devices as APS_devices aps = APS_devices.ApsMachineParametersDevice(name="APS") suspend_A_shutter = SuspendFloor(A_shutter.pss_state, 1) """
def __init__(self, mode='debug'): self.RE = bluesky.RunEngine({}) self.db = databroker.Broker.named("mongodb_config") self.RE.subscribe(self.db.insert) self.RE.subscribe(BestEffortCallback()) self._mode = mode from apstools.devices import ApsMachineParametersDevice self._aps = ApsMachineParametersDevice(name="APS") self.shutter = Experiment.get_shutter(mode) self.suspend_shutter = SuspendFloor(self.shutter.pss_state, 1) # monitor APS current self.suspend_APS_current = SuspendFloor(self._aps.current, 2, resume_thresh=10) self.RE.install_suspender(self.suspend_APS_current)
def imprint_row(*args, events=1, min_mj=0.5): """ Run a single row of the imprint scan Parameters ---------- args: passed to bluesky.plans.scan events: int The number of events to allow at each stopping point in the scan min_mj, float, optional The minimum energy the scan should continue to execute scans Example ------- .. code:: # Run a scan from -1, 1 in 10 steps with 40 shots at each step RE(imprint_row(pi_x, -1, 1, 10, events=40)) # Run a scan with two motors from -1, 1 and 12, 20, in five steps with # 10 shots at each step. Pause if we have less than 1 mJ in the GDET RE(imprint_row(pi_x, -1, 1, cxi.kb2.hl, 12, 20, 5, events=10, min_mj=1)) """ # Create a new suspender suspender = SuspendFloor(beam_stats.mj_avg, min_mj) # Install the new suspender yield from bps.install_suspender(suspender) # Execute a scan first configuring the Sequencer and DAQ try: yield from sequencer_mode(daq, sequencer, events, sequence_wait=.25) yield from bp.scan([daq, sequencer], *args) # Always ensure we remove the suspender finally: yield from bps.remove_suspender(suspender)
def get_shutter(mode='debug'): """ return simulated shutter <-- dryrun, debug acutal shutter <-- production """ import apstools.devices as APS_devices aps = APS_devices.ApsMachineParametersDevice(name="APS") if mode.lower() in ['debug', 'dryrun']: A_shutter = APS_devices.SimulatedApsPssShutterWithStatus(name="A_shutter") elif mode.lower() == 'production': A_shutter = APS_devices.ApsPssShutterWithStatus( _devices['A_shutter'][0], _devices['A_shutter'][1], name="A_shutter", ) suspend_APS_current = SuspendFloor(aps.current, 2, resume_thresh=10) RE.install_suspender(suspend_APS_current) else: raise ValueError(f"🙉: invalide mode, {mode}") return A_shutter
# be placed in this file. You may instead wish to make a copy of this file in # the user's data directory, and use that as a working copy. ################################################################################ from ophyd import EpicsSignal from bluesky.suspenders import SuspendFloor, SuspendCeil if True: # Define suspenders to hold data collection if x-ray # beam is not available. ring_current = EpicsSignal('SR:OPS-BI{DCCT:1}I:Real-I') sus = SuspendFloor(ring_current, 100, resume_thresh=101) RE.install_suspender(sus) #absorber_pos = EpicsSignal( 'XF:11BMB-ES{SM:1-Ax:ArmR}Mtr.RBV') #sus_abs_low = SuspendFloor(absorber_pos, -56, resume_thresh=-55) #sus_abs_hi = SuspendCeil(absorber_pos, -54, resume_thresh=-55) #RE.install_suspender(sus_abs_low) #RE.install_suspender(sus_abs_hi) #RE.clear_suspenders() if False: # The following shortcuts can be used for unit conversions. For instance, # for a motor operating in 'mm' units, one could instead do: # sam.xr( 10*um )
print(__file__) from bluesky.suspenders import (SuspendBoolHigh, SuspendBoolLow, SuspendFloor, SuspendCeil, SuspendInBand, SuspendOutBand) fe_shut_suspender = SuspendBoolHigh(EpicsSignalRO(shutter_fe.status.pvname), sleep=10 * 60) ph_shut_suspender = SuspendBoolHigh(EpicsSignalRO(shutter_ph.status.pvname), sleep=10 * 60) # suspender for beamline current is mA beam_current_suspender = SuspendFloor(nsls_ii.beam_current, suspend_thresh=300, sleep=10 * 60) suspenders = [ fe_shut_suspender, ph_shut_suspender, beam_current_suspender, ] ''' Some help on suspenders /bluesky # how to add a suspender: # Method 1: # RE.install_suspender(fe_shut_suspender) # Method 2 (in the plan): # RE(bpp.suspend_wrapper(myplan(), [suspenders])) # general bluesky info # blue sky plans (mostly) reside here: # general plans
import bluesky.plans as bp from bluesky.suspenders import SuspendFloor, SuspendBoolLow, SuspendBoolHigh, SuspendCeil print(f'Loading {__file__}') # Count on XBPM2 suspender susp_xbpm2_sum = SuspendFloor(xbpm2.sumY, 0.3, resume_thresh=0.8) RE.install_suspender(susp_xbpm2_sum) # Ring current suspender susp_beam = SuspendFloor(ring.current, 100, resume_thresh=350) RE.install_suspender(susp_beam) # Front end shutter suspender susp_smi_shutter = SuspendFloor(smi_shutter_enable, 0.1, resume_thresh=0.9) RE.install_suspender(susp_smi_shutter) # to clear suspenders RE.clear_suspenders()
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))
# Shutter from bluesky.suspenders import SuspendFloor print("Setting up Shutter") if in_production: # define the real shutter used at 6BMA@APS # NOTE: # this requires connection to the hardware, otherwise a connection error will be raised A_shutter = APS_devices.ApsPssShutterWithStatus( "6bmb1:rShtrA:", "PA:06BM:STA_A_FES_OPEN_PL", name="A_shutter", ) A_shutter.pss_state # no scans until A_shutter is open suspend_A_shutter = SuspendFloor(A_shutter.pss_state, 1) # NOTE: # since tomo scan take dark field images with shutter colosed, the # suspender installation for A_shutter is located in the plan for # granular control. # no scans if aps.current is too low suspend_APS_current = SuspendFloor(aps.current, 2, resume_thresh=10) RE.install_suspender(suspend_APS_current) else: # for testing during dark time (no beam, shutter closed by APS) A_shutter = APS_devices.SimulatedApsPssShutterWithStatus(name="A_shutter") suspend_A_shutter = SuspendFloor(A_shutter.pss_state, 1) print("---use simulated shutter---")
from bluesky.suspenders import SuspendFloor, SuspendBoolHigh ring_suspender = SuspendFloor(ring_curr, 190, resume_thresh=200, sleep=120, post_plan=beamline_align_v2) #shutterb_suspender = SuspendBoolHigh(EpicsSignalRO(shutterb.status.pvname), sleep=5, post_plan=beamline_align_v2) # Is this the right PV??? #fe_shut_suspender = SuspendBoolHigh(EpicsSignal('XF:02ID-PPS{Sh:FE}Enbl-Sts'), sleep=20*60) ## It needs: ## RE.install_suspender(test_shutsusp) ## RE.remove_suspender(test_shutsusp) RE.install_suspender(ring_suspender) #RE.install_suspender(shutterb_suspender) print("") print( "You can safely ignore the 'SuspendOutBand' warning - this is a known issue that is fixed in a newer version." )
from bluesky.suspenders import (SuspendFloor, SuspendCeil, SuspendBoolHigh, SuspendBoolLow) import bluesky.plans as bp import bluesky.plan_stubs as bps import bluesky.preprocessors as bpp def shuttergenerator(shutter, value): return (yield from bpp.rewindable_wrapper(bps.mv(shutter, value), False)) # Ring current suspender susp_rc = SuspendFloor(ring_current, 140, resume_thresh=160, sleep=10 * 60, pre_plan=list(shuttergenerator(shut_b, 'Close')), post_plan=list(shuttergenerator(shut_b, 'Open'))) # Cryo cooler suspender susp_cryo = SuspendCeil(cryo_v19, 0.8, resume_thresh=0.2, sleep=15 * 60, pre_plan=list(shuttergenerator(shut_b, 'Close')), post_plan=list(shuttergenerator(shut_b, 'Open'))) # Shutter status suspender susp_shut_fe = SuspendBoolHigh(EpicsSignalRO(shut_fe.status.pvname), sleep=10) susp_shut_a = SuspendBoolHigh(EpicsSignalRO(shut_a.status.pvname), sleep=10) susp_shut_b = SuspendBoolHigh(EpicsSignalRO(shut_b.status.pvname), sleep=10)
class glbl(): beamline_host_name = BEAMLINE_HOST_NAME base = BASE_DIR home = HOME_DIR _export_tar_dir = _EXPORT_TAR_DIR xpdconfig = BLCONFIG_DIR import_dir = IMPORT_DIR config_base = CONFIG_BASE tiff_base = TIFF_BASE usrScript_dir = USERSCRIPT_DIR yaml_dir = YAML_DIR allfolders = ALL_FOLDERS archive_dir = USER_BACKUP_DIR dk_yaml = DARK_YAML_NAME dk_window = DARK_WINDOW frame_acq_time = FRAME_ACQUIRE_TIME auto_dark = True owner = OWNER beamline_id = BEAMLINE_ID group = GROUP _allowed_scanplan_type = ALLOWED_SCANPLAN_TYPE # logic to assign correct objects depends on simulation or real experiment if not simulation: from bluesky.run_engine import RunEngine from bluesky.register_mds import register_mds # import real object as other names to avoid possible self-referencing later from bluesky import Msg as msg from bluesky.plans import Count as count from bluesky.plans import AbsScanPlan as absScanPlan from databroker import DataBroker from databroker import get_images as getImages from databroker import get_events as getEvents from bluesky.callbacks import LiveTable as livetable from bluesky.callbacks.broker import verify_files_saved as verifyFiles from ophyd import EpicsSignalRO, EpicsSignal from bluesky.suspenders import SuspendFloor ring_current = EpicsSignalRO('SR:OPS-BI{DCCT:1}I:Real-I', name='ring_current') xpdRE = RunEngine() xpdRE.md['owner'] = owner xpdRE.md['beamline_id'] = beamline_id xpdRE.md['group'] = group register_mds(xpdRE) beamdump_sus = SuspendFloor(ring_current, ring_current.get() * 0.9, resume_thresh=ring_current.get() * 0.9, sleep=1200) #xpdRE.install_suspender(beamdump_sus) # don't enable it untill beam is back # real imports Msg = msg Count = count db = DataBroker LiveTable = livetable get_events = getEvents get_images = getImages AbsScanPlan = absScanPlan verify_files_saved = verifyFiles # real collection objects area_det = None temp_controller = None shutter = None else: simulation = True ARCHIVE_BASE_DIR = os.path.join(BASE_DIR, 'userSimulationArchive') # mock imports Msg = MagicMock() Count = MagicMock() AbsScanPlan = MagicMock() db = MagicMock() get_events = MagicMock() get_images = MagicMock() LiveTable = mock_livetable verify_files_saved = MagicMock() # mock collection objects xpdRE = MagicMock() temp_controller = MagicMock() shutter = mock_shutter() area_det = MagicMock() area_det.cam = MagicMock() area_det.cam.acquire_time = MagicMock() area_det.cam.acquire_time.put = MagicMock(return_value=0.1) area_det.cam.acquire_time.get = MagicMock(return_value=0.1) area_det.number_of_sets = MagicMock() area_det.number_of_sets.put = MagicMock(return_value=1) print('==== Simulation being created in current directory:{} ===='. format(BASE_DIR))
from bluesky.suspenders import SuspendFloor, SuspendBoolHigh, SuspendBoolLow from BMM import user_ns as user_ns_module user_ns = vars(user_ns_module) from BMM.user_ns.detectors import quadem1 from BMM.user_ns.metadata import ring from BMM.user_ns.instruments import bmps, idps #RE.clear_suspenders() all_BMM_suspenders = list() ## ---------------------------------------------------------------------------------- ## suspend when I0 drops below 0.1 nA (not in use) suspender_I0 = SuspendFloor(quadem1.I0, 0.1, resume_thresh=1, sleep=5) #all_BMM_suspenders.append(suspender_I0) ## ---------------------------------------------------------------------------------- ## suspend upon beam dump, resume 30 seconds after hitting 90% of fill target try: if ring.filltarget.connected is True and ring.filltarget.get() > 20: suspender_ring_current = SuspendFloor(ring.current, 10, resume_thresh=0.9 * ring.filltarget.get(), sleep=60) all_BMM_suspenders.append(suspender_ring_current) except Exception as e: print(f'failed to create ring current suspender: {e}') pass
""" suspender.py callbacks that suspend operation (beam low, ) """ from ..framework import RE from ..devices.misc_devices import I1 from ..plans import tablev_scan from bluesky.suspenders import SuspendFloor susp_tablev = SuspendFloor(I1, suspend_thresh=0.1, resume_thresh=0.5, sleep=20 tripped_message='I1 dropped below threshold', pre_plan=tablev_scan) RE.install_suspender(tablev_suspender)
from bluesky.suspenders import (SuspendBoolHigh, SuspendBoolLow, SuspendFloor, SuspendCeil, SuspendInBand, SuspendOutBand) from ophyd import EpicsSignal from .startup import RE ring_suspender = SuspendFloor(EpicsSignal('XF:23ID-SR{}I-I'), 250, sleep=3 * 60) #ring_suspender = SuspendFloor(EpicsSignal('XF:23ID-SR{}I-I'), 250, sleep=3) fe_shut_suspender = SuspendBoolHigh(EpicsSignal('XF:23ID-PPS{Sh:FE}Pos-Sts'), sleep=10 * 60) #fe_shut_suspender = SuspendBoolHigh(EpicsSignal('XF:23ID-PPS{Sh:FE}Pos-Sts'), sleep=3) ps1_shut_suspender = SuspendBoolHigh(EpicsSignal('XF:23IDA-PPS:1{PSh}Pos-Sts'), sleep=5 * 60) #ps1_shut_suspender = SuspendBoolHigh(EpicsSignal('XF:23IDA-PPS:1{PSh}Pos-Sts'),sleep=3) RE.install_suspender(ring_suspender) RE.install_suspender(fe_shut_suspender) RE.install_suspender(ps1_shut_suspender)
from bluesky.suspenders import (SuspendFloor, SuspendBoolHigh, SuspendBoolLow) # Here are some conditions that will cause scans to pause automatically: # - when the beam current goes below a certain threshold susp_current = SuspendFloor(beamline_status.beam_current, suspend_thresh=150.0, resume_thresh=240.0, tripped_message='beam current too low', ) # - when the shutter is closed susp_shutter = SuspendBoolLow(beamline_status.shutter_status, tripped_message='shutter not open', ) # - if the beamline isn't enabled susp_enabled = SuspendBoolLow(beamline_status.beamline_enabled, tripped_message='beamline is not enabled', ) susp_cryo_cooler = SuspendBoolHigh(beamline_status.cryo_filling, sleep=60*10, tripped_message='Cyrocooler is refilling') # NOTE: to enable or disable the suspenders, (un)comment the following: # Install all suspenders: #RE.install_suspender(susp_current) #RE.install_suspender(susp_shutter) #RE.install_suspender(susp_enabled) #RE.install_suspender(susp_cryo_cooler)
from bluesky.suspenders import SuspendFloor ring_suspender = SuspendFloor(ring_curr, 290, resume_thresh=300, sleep=120) # Is this the right PV??? #fe_shut_suspender = SuspendBoolHigh(EpicsSignal('XF:02ID-PPS{Sh:FE}Enbl-Sts'), sleep=20*60) ## It needs: ## RE.install_suspender(test_shutsusp) ## RE.remove_suspender(test_shutsusp) RE.install_suspender(ring_suspender) #RE.install_suspender(fe_shut_suspender) print("") print( "You can safely ignore the 'SuspendOutBand' warning - this is a known issue that is fixed in a newer version." )
def test_suspenders_stress(RE): """ Run scan with tons of inconvenient suspenders """ sig = Signal(name="dull signal") def pre_plan(*args, **kwargs): yield from null() logger.debug("starting suspender") def post_plan(*args, **kwargs): yield from null() logger.debug("releasing suspender") suspenders = [ SuspendFloor(sig, i, sleep=10, pre_plan=pre_plan, post_plan=post_plan) for i in range(10) ] for s in suspenders: RE.install_suspender(s) mot = SlowSoftPositioner(n_steps=1000, delay=0.001, position=0, name='test_mot') def sig_sequence(sig): sig.put(15) time.sleep(1) sig.put(9) logger.debug('expect suspend soon') time.sleep(1) sig.put(8) logger.debug('expect second suspend layer') time.sleep(1) sig.put(14) logger.debug('expect resume after 1 second') time.sleep(2) sig.put(2) logger.debug('expect many layered suspend') time.sleep(1) sig.put(15) logger.debug('expect resume after 1 second') time.sleep(2) sig.put(-10) logger.debug('expect to confuse the scan now') sig.put(10) sig.put(3) sig.put(5) sig.put(456) sig.put(0) sig.put(23) sig.put(0) sig.put(15) logger.debug('hopefully it suspended and is now waiting to resume') # 3 suspends and 3 resumes should add 6s to the scan @run_decorator() def dull_scan(mot, count, sig=None, sleep_time=0): if sig: thread = threading.Thread(target=sig_sequence, args=(sig, )) thread.start() for i in range(count): yield from checkpoint() try: yield from mv(mot, i) except: pass # make every step take 1s extra yield from sleep(sleep_time) yield from checkpoint() yield from create() yield from read(mot) yield from save() yield from checkpoint() out = [] coll = collector("test_mot", out) RE.subscribe('event', coll) base_start = time.time() RE(dull_scan(mot, 10, sleep_time=1)) base_elapsed = time.time() - base_start susp_start = time.time() RE(dull_scan(mot, 10, sig=sig, sleep_time=1)) susp_elapsed = time.time() - susp_start assert susp_elapsed - base_elapsed > 6
from ophyd import EpicsSignal from bluesky.suspenders import SuspendFloor from bluesky.suspenders import SuspendCeil #from bluesky.suspenders import SuspendBoolLow BEAM_RECOVER_TIME = 30 #Time in seconds BEAM_THRES = 300 beam_current = EpicsSignal('SR:OPS-BI{DCCT:1}I:Real-I') beam_current_sus = SuspendFloor(beam_current, BEAM_THRES, sleep=BEAM_RECOVER_TIME) def install_beam_suspender(): RE.install_suspender(beam_current_sus) def uninstall_beam_suspender(): RE.remove_suspender(beam_current_sus) bpm_xpos = EpicsSignal('XF:16IDB-CT{Best}:BPM0:PosX_Mean') BPM_X_THRES_HIGH = 20 BPM_X_THRES_LOW = -20 BPM_RECOVER_TIME = 5 bpm_ypos = EpicsSignal('XF:16IDB-CT{Best}:BPM0:PosY_Mean') BPM_Y_THRES_HIGH = 20 BPM_Y_THRES_LOW = -20
from bluesky.suspenders import SuspendFloor, SuspendBoolHigh, SuspendBoolLow from IPython import get_ipython user_ns = get_ipython().user_ns #RE.clear_suspenders() all_BMM_suspenders = list() ## ---------------------------------------------------------------------------------- ## suspend when I0 drops below 0.1 nA (not in use) suspender_I0 = SuspendFloor(user_ns['quadem1'].I0, 0.1, resume_thresh=1, sleep=5) #all_BMM_suspenders.append(suspender_I0) ## ---------------------------------------------------------------------------------- ## suspend upon beam dump, resume 30 seconds after hitting 90% of fill target try: if user_ns['ring'].filltarget.get() > 20: suspender_ring_current = SuspendFloor(user_ns['ring'].current, 10, resume_thresh=0.9 * user_ns['ring'].filltarget.get(), sleep=60) all_BMM_suspenders.append(suspender_ring_current) except: pass ## ---------------------------------------------------------------------------------- ## suspend if the BM photon shutter closes, resume 5 seconds after opening