def test_uid_passthrough(RE, hw): # Test that none of the preprocessors in SupplementalData break plans # returning uids (a bug that was caught on the floor). RE.msg_hook = print # preliminary sanity check def mycount1(): uid = yield from count([]) assert uid is not None assert isinstance(uid, str) RE(mycount1()) # actual test sd = SupplementalData() sd.baseline = [hw.det] sd.monitors = [hw.rand] sd.flyers = [hw.flyer1] hw.flyer1.loop = RE.loop def mycount2(): uid = yield from sd(count([])) assert uid is not None assert isinstance(uid, str) RE(mycount2())
def test_monitors(hw): det = hw.det rand = hw.rand rand2 = hw.rand2 # no-op D = SupplementalData() original = list(count([det])) processed = list(D(count([det]))) assert len(processed) == len(original) # one monitor D.monitors.append(rand) original = list(count([det])) processed = list(D(count([det]))) assert len(processed) == 2 + len(original) # two monitors D.monitors.append(rand2) processed = list(D(count([det]))) assert len(processed) == 4 + len(original) # two monitors applied a plan with consecutive runs original = list(list(count([det])) + list(count([det]))) processed = list(D(pchain(count([det]), count([det])))) assert len(processed) == 8 + len(original)
def test_mixture(hw): D = SupplementalData(baseline=[hw.det2], flyers=[hw.flyer1], monitors=[hw.rand]) original = list(count([hw.det])) processed = list(D(count([hw.det]))) assert len(processed) == 2 + 5 + 10 + len(original)
def test_pickle(): D = SupplementalData(baseline=['placeholder1'], monitors=['placeholder2'], flyers=['placeholder3']) D2 = pickle.loads(pickle.dumps(D)) assert D2.baseline == D.baseline assert D2.monitors == D.monitors assert D2.flyers == D.flyers
def test_baseline(hw): # one baseline detector D = SupplementalData(baseline=[hw.det2]) original = list(count([hw.det])) processed = list(D(count([hw.det]))) # should add 2X (trigger, wait, create, read, save) assert len(processed) == 10 + len(original) # two baseline detectors D.baseline.append(hw.det3) processed = list(D(count([hw.det]))) # should add 2X (trigger, triger, wait, create, read, read, save) assert len(processed) == 14 + len(original) # two baseline detectors applied to a plan with two consecutive runs original = list(list(count([hw.det])) + list(count([hw.det]))) processed = list(D(pchain(count([hw.det]), count([hw.det])))) assert len(processed) == 28 + len(original)
def test_flyers(hw): # one flyer D = SupplementalData(flyers=[hw.flyer1]) original = list(count([hw.det])) processed = list(D(count([hw.det]))) # should add kickoff, wait, complete, wait, collect assert len(processed) == 5 + len(original) # two flyers D.flyers.append(hw.flyer2) processed = list(D(count([hw.det]))) # should add 2 * (kickoff, complete, collect) + 2 * (wait) assert len(processed) == 8 + len(original) # two flyers applied to a plan with two consecutive runs original = list(list(count([hw.det])) + list(count([hw.det]))) processed = list(D(pchain(count([hw.det]), count([hw.det])))) assert len(processed) == 16 + len(original)
def test_order(hw): D = SupplementalData(baseline=[hw.det2], flyers=[hw.flyer1], monitors=[hw.rand]) def null_run(): yield Msg('open_run') yield Msg('null') yield Msg('close_run') actual = [msg.command for msg in D(null_run())] expected = [ 'open_run', # baseline 'trigger', 'wait', 'create', 'read', 'save', # monitors 'monitor', # flyers 'kickoff', 'wait', # plan 'null', # flyers 'complete', 'wait', 'collect', # montiors 'unmonitor', # baseline 'trigger', 'wait', 'create', 'read', 'save', 'close_run' ] assert actual == expected
RE.md = PersistentDict(md_path) if old_md is not None: logger.info('migrating RE.md storage to PersistentDict') RE.md.update(old_md) # keep track of callback subscriptions callback_db = {} # set up databroker import databroker db = databroker.Broker.named('mongoCat') callback_db['Broker'] = RE.subscribe(db.insert) # Set up SupplementalData. from bluesky import SupplementalData sd = SupplementalData() RE.preprocessors.append(sd) # Add a progress bar. from bluesky.utils import ProgressBarManager pbar_manager = ProgressBarManager() RE.waiting_hook = pbar_manager # Register bluesky IPython magics. from IPython import get_ipython from bluesky.magics import BlueskyMagics get_ipython().register_magics(BlueskyMagics) get_ipython().magic("automagic 0") # Turn off automagic # Set up the BestEffortCallback. from bluesky.callbacks.best_effort import BestEffortCallback
def configure_base(user_ns, broker_name, *, bec=True, epics_context=True, magics=True, mpl=True, ophyd_logging=True, pbar=True): """ Perform base setup and instantiation of important objects. This factory function instantiates the following and adds them to the namespace: * ``RE`` -- a RunEngine * ``db`` -- a Broker (from "databroker"), subscribe to ``RE`` * ``bec`` -- a BestEffortCallback, subscribed to ``RE`` * ``peaks`` -- an alias for ``bec.peaks`` * ``sd`` -- a SupplementalData preprocessor, added to ``RE.preprocessors`` * ``pbar_maanger`` -- a ProgressBarManager, set as the ``RE.waiting_hook`` And it performs some low-level configuration: * creates a context in ophyd's control layer (``ophyd.setup_ophyd()``) * turns out interactive plotting (``matplotlib.pyplot.ion()``) * bridges the RunEngine and Qt event loops (``bluesky.utils.install_kicker()``) * logs ERROR-level log message from ophyd to the standard out Parameters ---------- user_ns: dict a namespace --- for example, ``get_ipython().user_ns`` broker_name : Union[str, Broker] Name of databroker configuration or a Broker instance. bec : boolean, optional True by default. Set False to skip BestEffortCallback. epics_context : boolean, optional True by default. Set False to skip ``setup_ophyd()``. magics : boolean, optional True by default. Set False to skip registration of custom IPython magics. mpl : boolean, optional True by default. Set False to skip matplotlib ``ion()`` at event-loop bridging. ophyd_logging : boolean, optional True by default. Set False to skip ERROR-level log configuration for ophyd. pbar : boolean, optional True by default. Set false to skip ProgressBarManager. Returns ------- names : list list of names added to the namespace Examples -------- Configure IPython for CHX. >>>> configure_base(get_ipython().user_ns, 'chx'); """ ns = {} # We will update user_ns with this at the end. # Test if we are in Jupyter or IPython: in_jupyter = user_ns['get_ipython']().has_trait('kernel') # Set up a RunEngine and use metadata backed by a sqlite file. from bluesky import RunEngine from bluesky.utils import get_history # if RunEngine already defined grab it # useful when users make their own custom RunEngine if 'RE' in user_ns: RE = user_ns['RE'] else: RE = RunEngine(get_history()) ns['RE'] = RE # Set up SupplementalData. # (This is a no-op until devices are added to it, # so there is no need to provide a 'skip_sd' switch.) from bluesky import SupplementalData sd = SupplementalData() RE.preprocessors.append(sd) ns['sd'] = sd if isinstance(broker_name, str): # Set up a Broker. from databroker import Broker db = Broker.named(broker_name) ns['db'] = db else: db = broker_name RE.subscribe(db.insert) if pbar and not in_jupyter: # Add a progress bar. from bluesky.utils import ProgressBarManager pbar_manager = ProgressBarManager() RE.waiting_hook = pbar_manager ns['pbar_manager'] = pbar_manager if magics: # Register bluesky IPython magics. from bluesky.magics import BlueskyMagics get_ipython().register_magics(BlueskyMagics) if bec: # Set up the BestEffortCallback. from bluesky.callbacks.best_effort import BestEffortCallback _bec = BestEffortCallback() bec = _bec RE.subscribe(_bec) if in_jupyter: _bec.disable_plots() ns['bec'] = _bec ns['peaks'] = _bec.peaks # just as alias for less typing if mpl: # Import matplotlib and put it in interactive mode. import matplotlib.pyplot as plt ns['plt'] = plt plt.ion() # Commented to allow more intelligent setting of kickers (for Jupyter and IPython): ## Make plots update live while scans run. # from bluesky.utils import install_kicker # install_kicker() # Make plots update live while scans run. if in_jupyter: from bluesky.utils import install_nb_kicker install_nb_kicker() else: from bluesky.utils import install_qt_kicker install_qt_kicker() if not ophyd_logging: # Turn on error-level logging, particularly useful for knowing when # pyepics callbacks fail. import logging import ophyd.ophydobj ch = logging.StreamHandler() ch.setLevel(logging.ERROR) ophyd.ophydobj.logger.addHandler(ch) # convenience imports # some of the * imports are for 'back-compatibility' of a sort -- we have # taught BL staff to expect LiveTable and LivePlot etc. to be in their # namespace import numpy as np ns['np'] = np import bluesky.callbacks ns['bc'] = bluesky.callbacks import_star(bluesky.callbacks, ns) import bluesky.plans ns['bp'] = bluesky.plans import_star(bluesky.plans, ns) import bluesky.plan_stubs ns['bps'] = bluesky.plan_stubs import_star(bluesky.plan_stubs, ns) # special-case the commonly-used mv / mvr and its aliases mov / movr4 ns['mv'] = bluesky.plan_stubs.mv ns['mvr'] = bluesky.plan_stubs.mvr ns['mov'] = bluesky.plan_stubs.mov ns['movr'] = bluesky.plan_stubs.movr import bluesky.preprocessors ns['bpp'] = bluesky.preprocessors import bluesky.callbacks.broker import_star(bluesky.callbacks.broker, ns) import bluesky.simulators import_star(bluesky.simulators, ns) user_ns.update(ns) return list(ns)
def configure_base( user_ns, broker_name, *, bec=True, epics_context=False, magics=True, mpl=True, configure_logging=True, pbar=True, ipython_exc_logging=True, ): """ Perform base setup and instantiation of important objects. This factory function instantiates essential objects to data collection environments at NSLS-II and adds them to the current namespace. In some cases (documented below), it will check whether certain variables already exist in the user name space, and will avoid creating them if so. The following are added: * ``RE`` -- a RunEngine This is created only if an ``RE`` instance does not currently exist in the namespace. * ``db`` -- a Broker (from "databroker"), subscribe to ``RE`` * ``bec`` -- a BestEffortCallback, subscribed to ``RE`` * ``peaks`` -- an alias for ``bec.peaks`` * ``sd`` -- a SupplementalData preprocessor, added to ``RE.preprocessors`` * ``pbar_maanger`` -- a ProgressBarManager, set as the ``RE.waiting_hook`` And it performs some low-level configuration: * creates a context in ophyd's control layer (``ophyd.setup_ophyd()``) * turns on interactive plotting (``matplotlib.pyplot.ion()``) * bridges the RunEngine and Qt event loops (``bluesky.utils.install_kicker()``) * logs ERROR-level log message from ophyd to the standard out Parameters ---------- user_ns: dict a namespace --- for example, ``get_ipython().user_ns`` broker_name : Union[str, Broker] Name of databroker configuration or a Broker instance. bec : boolean, optional True by default. Set False to skip BestEffortCallback. epics_context : boolean, optional True by default. Set False to skip ``setup_ophyd()``. magics : boolean, optional True by default. Set False to skip registration of custom IPython magics. mpl : boolean, optional True by default. Set False to skip matplotlib ``ion()`` at event-loop bridging. configure_logging : boolean, optional True by default. Set False to skip INFO-level logging to /var/logs/bluesky/bluesky.log. pbar : boolean, optional True by default. Set false to skip ProgressBarManager. ipython_exc_logging : boolean, optional True by default. Exception stack traces will be written to IPython log file when IPython logging is enabled. Returns ------- names : list list of names added to the namespace Examples -------- Configure IPython for CHX. >>>> configure_base(get_ipython().user_ns, 'chx'); """ ns = {} # We will update user_ns with this at the end. # Protect against double-subscription. SENTINEL = "__nslsii_configure_base_has_been_run" if user_ns.get(SENTINEL): raise RuntimeError( "configure_base should only be called once per process.") ns[SENTINEL] = True # Set up a RunEngine and use metadata backed by files on disk. from bluesky import RunEngine, __version__ as bluesky_version if LooseVersion(bluesky_version) >= LooseVersion("1.6.0"): # current approach using PersistentDict from bluesky.utils import PersistentDict directory = os.path.expanduser("~/.config/bluesky/md") os.makedirs(directory, exist_ok=True) md = PersistentDict(directory) else: # legacy approach using HistoryDict from bluesky.utils import get_history md = get_history() # if RunEngine already defined grab it # useful when users make their own custom RunEngine if "RE" in user_ns: RE = user_ns["RE"] else: RE = RunEngine(md) ns["RE"] = RE # Set up SupplementalData. # (This is a no-op until devices are added to it, # so there is no need to provide a 'skip_sd' switch.) from bluesky import SupplementalData sd = SupplementalData() RE.preprocessors.append(sd) ns["sd"] = sd if isinstance(broker_name, str): # Set up a Broker. from databroker import Broker db = Broker.named(broker_name) ns["db"] = db else: db = broker_name RE.subscribe(db.insert) if pbar: # Add a progress bar. from bluesky.utils import ProgressBarManager pbar_manager = ProgressBarManager() RE.waiting_hook = pbar_manager ns["pbar_manager"] = pbar_manager if magics: # Register bluesky IPython magics. from bluesky.magics import BlueskyMagics get_ipython().register_magics(BlueskyMagics) if bec: # Set up the BestEffortCallback. from bluesky.callbacks.best_effort import BestEffortCallback _bec = BestEffortCallback() RE.subscribe(_bec) ns["bec"] = _bec ns["peaks"] = _bec.peaks # just as alias for less typing if mpl: # Import matplotlib and put it in interactive mode. import matplotlib.pyplot as plt ns["plt"] = plt plt.ion() # Make plots update live while scans run. if LooseVersion(bluesky_version) < LooseVersion("1.6.0"): from bluesky.utils import install_kicker install_kicker() if epics_context: # Create a context in the underlying EPICS client. from ophyd import setup_ophyd setup_ophyd() if configure_logging: configure_bluesky_logging(ipython=get_ipython()) if ipython_exc_logging: # IPython logging must be enabled separately from nslsii.common.ipynb.logutils import log_exception configure_ipython_exc_logging(exception_logger=log_exception, ipython=get_ipython()) # always configure %xmode minimal get_ipython().magic("xmode minimal") # convenience imports # some of the * imports are for 'back-compatibility' of a sort -- we have # taught BL staff to expect LiveTable and LivePlot etc. to be in their # namespace import numpy as np ns["np"] = np import bluesky.callbacks ns["bc"] = bluesky.callbacks import_star(bluesky.callbacks, ns) import bluesky.plans ns["bp"] = bluesky.plans import_star(bluesky.plans, ns) import bluesky.plan_stubs ns["bps"] = bluesky.plan_stubs import_star(bluesky.plan_stubs, ns) # special-case the commonly-used mv / mvr and its aliases mov / movr4 ns["mv"] = bluesky.plan_stubs.mv ns["mvr"] = bluesky.plan_stubs.mvr ns["mov"] = bluesky.plan_stubs.mov ns["movr"] = bluesky.plan_stubs.movr import bluesky.preprocessors ns["bpp"] = bluesky.preprocessors import bluesky.callbacks.broker import_star(bluesky.callbacks.broker, ns) import bluesky.simulators import_star(bluesky.simulators, ns) user_ns.update(ns) return list(ns)
def test_repr(): expected = "SupplementalData(baseline=[], monitors=[], flyers=[])" D = SupplementalData() actual = repr(D) assert actual == expected