def presets(): folder_obj = Path(__file__).parent / 'test_presets' folder = str(folder_obj) if folder_obj.exists(): shutil.rmtree(folder) hutch = folder + '/hutch' user = folder + '/user' os.makedirs(hutch) os.makedirs(user) setup_preset_paths(hutch=hutch, user=user) yield setup_preset_paths() shutil.rmtree(folder)
def load_conf(conf, hutch_dir=None): """ Step through the object loading procedure, given a configuration. The procedure is: - Check the configuration for errors - Display the banner by calling `hutch_banner` - Use ``hutch`` key to create ``hutch.db`` importable namespace to stash the objects. This will be literally ``hutch.db`` if hutch is not provided, or the hutch name e.g. ``mfx.db``. - Create a ``RunEngine``, ``RE`` - Import ``plan_defaults`` and include as ``p``, ``plans`` - Create a ``daq`` object with ``RE`` registered. - Create a ``scan_pvs`` object, and leave it ``disabled``. - Use ``hutch`` and ``daq_platform`` keys to create the ``elog`` object and configure it to match the correct experiment. - Use ``db`` key to load devices from the ``happi`` beamline database and create a ``hutch_beampath`` object from ``lightpath`` - Use ``hutch`` key to load detector objects from the ``camviewer`` configuration file. - Use ``experiment`` key to select the current experiment - If ``experiment`` was missing, autoselect experiment using ``hutch`` key - Use current experiment to load experiment objects from questionnaire - Use ``load`` key to bring up the user's ``beamline`` modules - Use current experiment to load experiment file If a conf key is missing, we'll note it in a ``logger.info`` message. If an extra conf entry is found, we'll note it in a ``logger.warning`` message. If an automatically selected file is missing, we'll note it in a ``logger.info`` message. All other errors will be noted in a logger.error message. Parameters ---------- conf: ``dict`` ``dict`` interpretation of the original yaml file hutch_dir: ``Path`` or ``str``, optional ``Path`` object that points to the hutch's launch directory. This is the directory that includes the ``experiments`` directory and a hutchname directory e.g. ``mfx`` If this is missing, we'll be unable to write the ``db.txt`` file, do relative filepath database selection for ``happi``, or establish a preset positions directory. Returns ------ objs: ``dict{str: object}`` See the return value of `load` """ # Warn user about excess config entries for key in conf: if key not in VALID_KEYS: txt = ('Found %s in configuration, but this is not a valid key. ' 'The valid keys are %s') logger.warning(txt, key, VALID_KEYS) # Grab configurations from dict, set defaults, show missing try: hutch = conf['hutch'] if isinstance(hutch, str): hutch = hutch.lower() else: logger.error('Invalid hutch conf %s, must be string.', hutch) hutch = None except KeyError: hutch = None logger.info(('Missing hutch from conf. Will skip elog ' 'and cameras.')) # Display the banner if hutch is None: hutch_banner() else: hutch_banner(hutch) try: db = conf['db'] if isinstance(db, str): if db[0] == '/': db = Path(db) else: db = Path(hutch_dir) / db else: logger.error('Invalid db conf %s, must be string.', db) db = None except KeyError: db = None logger.info(('Missing db from conf. Will skip loading from shared ' 'database.')) try: load = conf['load'] if not isinstance(load, (str, list)): logger.error('Invalid load conf %s, must be string or list', load) load = None except KeyError: load = None logger.info('Missing load from conf. Will skip loading hutch files.') try: experiment = conf['experiment'] if not isinstance(experiment, str): logger.error( 'Invalid experiment selection %s, must be a string ' 'matching the elog experiment name.', experiment) experiment = None except KeyError: experiment = None if hutch is None: logger.info(('Missing hutch and experiment from conf. Will not ' 'load objects from questionnaire or experiment ' 'file.')) try: # This is an internal variable here for note-keeping. The ELog uses # this to determine if we are in the secondary or primary DAQ mode default_platform = True platform_info = conf['daq_platform'] hostname = gethostname() try: daq_platform = platform_info[hostname] logger.info('Selected %s daq platform: %s', hostname, daq_platform) default_platform = False except KeyError: daq_platform = platform_info['default'] logger.info('Selected default %s daq platform: %s', hutch, daq_platform) except KeyError: daq_platform = 0 logger.info('Selected default hutch-python daq platform: 0') # Make cache namespace cache = LoadCache((hutch or 'hutch') + '.db', hutch_dir=hutch_dir) # Make RunEngine RE = RunEngine({}) initialize_qt_teleporter() bec = BestEffortCallback() RE.subscribe(bec) cache(RE=RE) # Collect Plans cache(bp=plan_defaults.plans) cache(bps=plan_defaults.plan_stubs) cache(bpp=plan_defaults.preprocessors) # Daq with safe_load('daq'): cache(daq=Daq(RE=RE, hutch_name=hutch)) # Scan PVs if hutch is not None: with safe_load('scan_pvs'): scan_pvs = ScanVars('{}:SCAN'.format(hutch.upper()), name='scan_pvs', RE=RE) scan_pvs.enable() cache(scan_pvs=scan_pvs) # Elog if hutch is not None: with safe_load('elog'): # Use the fact if we we used the default_platform or not to decide # whether we are in a specialty station or not if default_platform: logger.debug("Using primary experiment ELog") kwargs = dict() else: logger.info("Configuring ELog to post to secondary experiment") kwargs = {'station': '1'} cache(elog=HutchELog.from_conf(hutch.upper(), **kwargs)) # Shared global devices for LCLS with safe_load('lcls PVs'): cache(**global_devices()) # Happi db and Lightpath if db is not None: with safe_load('database'): happi_objs = get_happi_objs(db, hutch) cache(**happi_objs) bp = get_lightpath(db, hutch) if bp.devices: cache(**{"{}_beampath".format(hutch.lower()): bp}) # ArchApp with safe_load('archapp'): cache(archive=EpicsArchive()) # Camviewer if hutch is not None: with safe_load('camviewer config'): objs = read_camviewer_cfg(CAMVIEWER_CFG.format(hutch)) cache(camviewer=SimpleNamespace(**objs)) # Simulated hardware with safe_load('simulated hardware'): cache(sim=sim.get_hw()) # Auto select experiment if we need to if experiment is None: if hutch is not None: try: # xpplp1216 experiment = get_current_experiment(hutch) logger.info('Selected active experiment %s', experiment) except Exception: err = 'Failed to select experiment automatically' logger.error(err) logger.debug(err, exc_info=True) # Process experiment name a bit if experiment is not None: if hutch in experiment: full_expname = experiment raw_expname = experiment.replace(hutch, '', 1) else: full_expname = hutch + experiment raw_expname = experiment # Load questionnaire if experiment is not None: qs_objs = get_qs_objs(full_expname) cache(**qs_objs) # Load user/beamline files if load is not None: load_objs = get_user_objs(load) cache(**load_objs) # Load experiment file if experiment is not None: user = get_exp_objs(raw_expname) for name, obj in qs_objs.items(): setattr(user, name, obj) cache(x=user, user=user) # Default namespaces with safe_load('default groups'): default_class_namespace('ophyd.PositionerBase', 'motors', cache) default_class_namespace('Slits', 'slits', cache) default_class_namespace('pcdsdaq.ami.AmiDet', 'detectors', cache) # Hotfix/disabled until we fix issues here # Tree namespace can cause havoc and break top-level devices # # if hutch is not None: # tree = tree_namespace(scope='hutch_python.db') # # Prune meta, remove branches with only one object # for name, space in tree.__dict__.items(): # if count_ns_leaves(space) > 1: # cache(**{name: space}) all_objs = copy(cache.objs) cache(a=all_objs, all_objects=all_objs) # Install Presets if hutch_dir is not None: with safe_load('position presets'): presets_dir = Path(hutch_dir) / 'presets' beamline_presets = presets_dir / 'beamline' preset_paths = [presets_dir, beamline_presets] if experiment is not None: experiment_presets = presets_dir / raw_expname preset_paths.append(experiment_presets) for path in preset_paths: if not path.exists(): path.mkdir() path.chmod(0o777) if experiment is None: setup_preset_paths(hutch=beamline_presets) else: setup_preset_paths(hutch=beamline_presets, exp=experiment_presets) # Write db.txt info file to the user's module try: cache.write_file() except OSError: logger.warning('No permissions to write db.txt file') return cache.objs.__dict__
def setup_test_presets(): presets = Path(__file__).resolve().parent / 'test_presets' setup_preset_paths(hutch=presets)
def test_presets(presets, fast_motor): logger.debug('test_presets') fast_motor.mv(4, wait=True) fast_motor.presets.add_hutch('four', comment='four!') fast_motor.mv(3, wait=True) fast_motor.presets.add_hutch('zero', 0, comment='center') fast_motor.presets.add_here_user('sample') print(fast_motor.presets.positions) assert fast_motor.wm_zero() == -3 assert fast_motor.wm_sample() == 0 assert fast_motor.wm_four() == 1 # Clear paths, refresh, should still exist old_paths = fast_motor.presets._paths setup_preset_paths() assert not hasattr(fast_motor, 'wm_zero') setup_preset_paths(**old_paths) assert fast_motor.wm_zero() == -3 assert fast_motor.wm_sample() == 0 fast_motor.mv_zero(wait=True) fast_motor.mvr(1, wait=True) assert fast_motor.wm_zero() == -1 assert fast_motor.wm() == 1 # Sleep for one so we don't override old history time.sleep(1) fast_motor.presets.positions.zero.update_pos(comment='hats') assert fast_motor.wm_zero() == 0 assert fast_motor.presets.positions.zero.pos == 1 assert len(fast_motor.presets.positions.zero.history) == 2 assert len(fast_motor.presets.positions.sample.history) == 1 repr(fast_motor.presets.positions.zero) fast_motor.presets.positions.zero.deactivate() with pytest.raises(AttributeError): fast_motor.wm_zero() with pytest.raises(AttributeError): fast_motor.presets.positions.zero fast_motor.umv_sample() assert fast_motor.wm() == 3 fast_motor.presets.positions.sample.update_comment('hello there') assert len(fast_motor.presets.positions.sample.history) == 2 def block_file(path, lock): with open(path, 'r+') as f: fcntl.flock(f, fcntl.LOCK_EX) lock.acquire() fcntl.flock(f, fcntl.LOCK_UN) path = fast_motor.presets.positions.sample.path lock = mp.Lock() with lock: proc = mp.Process(target=block_file, args=(path, lock)) proc.start() time.sleep(0.2) assert fast_motor.presets.positions.sample.pos == 3 fast_motor.presets.positions.sample.update_pos(2) assert not hasattr(fast_motor, 'wm_sample') fast_motor.presets.sync() assert not hasattr(fast_motor, 'mv_sample') proc.join() fast_motor.presets.sync() assert hasattr(fast_motor, 'mv_sample')