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 presets(): folder_obj = Path(__file__).parent / 'test_presets' folder = str(folder_obj) if folder_obj.exists(): shutil.rmtree(folder) bl = folder + '/beamline' user = folder + '/user' os.makedirs(bl) os.makedirs(user) setup_preset_paths(beamline=bl, 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, using ``daq_platform`` to define the ``platform`` argument if provided. The default value if ``daq_platform`` was not defined is 0. - Use ``db`` key to load devices from the ``happi`` beamline database and create a ``hutch_beampath`` object from ``lightpath`` - Use ``load`` key to bring up the user's ``beamline`` modules - 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 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 DAQ.') # 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, dict) or 'proposal' not in experiment or 'run' not in experiment): logger.error(('Invalid experiment selection %s, must be a dict ' 'with keys "proposal" and "run"'), 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({}) bec = BestEffortCallback() RE.subscribe(bec) cache(RE=RE) try: install_kicker() except RuntimeError: # Probably don't have a display if this failed, so nothing to kick pass # Collect Plans cache(plans=plan_defaults) cache(p=plan_defaults) # Daq with safe_load('daq'): daq_objs = get_daq_objs(daq_platform, RE) cache(**daq_objs) # 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) cache(**{"{}_beampath".format(hutch.lower()): bp}) # Elog 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)) # Load user files if load is not None: load_objs = get_user_objs(load) cache(**load_objs) # Auto select experiment if we need to proposal = None if experiment is None: if hutch is not None: try: # xpplp1216 expname = get_current_experiment(hutch) logger.info('Selected active experiment %s', expname) # lp12 proposal = expname[3:-2] # 16 run = expname[-2:] except Exception: err = 'Failed to select experiment automatically' logger.error(err) logger.debug(err, exc_info=True) # Experiment objects if experiment is not None: proposal = experiment['proposal'] run = experiment['run'] if proposal is not None: qs_objs = get_qs_objs(proposal, run) cache(**qs_objs) user = get_exp_objs(proposal, run) 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('EpicsMotor', 'motors', cache) default_class_namespace('Slits', 'slits', cache) 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 proposal is not None: experiment_presets = presets_dir / (proposal + str(run)) preset_paths.append(experiment_presets) for path in preset_paths: if not path.exists(): path.mkdir() path.chmod(0o777) if proposal 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 test_presets(presets, motor): logger.debug('test_presets') motor.mv(3, wait=True) motor.presets.add_beamline('zero', 0, comment='center') motor.presets.add_here_user('sample') assert motor.wm_zero() == -3 assert motor.wm_sample() == 0 # Clear paths, refresh, should still exist old_paths = motor.presets._paths setup_preset_paths() assert not hasattr(motor, 'wm_zero') setup_preset_paths(**old_paths) assert motor.wm_zero() == -3 assert motor.wm_sample() == 0 motor.mv_zero(wait=True) motor.mvr(1, wait=True) assert motor.wm_zero() == -1 assert motor.wm() == 1 # Sleep for one so we don't override old history time.sleep(1) motor.presets.positions.zero.update_pos(comment='hats') assert motor.wm_zero() == 0 assert motor.presets.positions.zero.pos == 1 assert len(motor.presets.positions.zero.history) == 2 assert len(motor.presets.positions.sample.history) == 1 repr(motor.presets.positions.zero) motor.presets.positions.zero.deactivate() with pytest.raises(AttributeError): motor.wm_zero() with pytest.raises(AttributeError): motor.presets.positions.zero motor.umv_sample() assert motor.wm() == 3 motor.presets.positions.sample.update_comment('hello there') assert len(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 = 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 motor.presets.positions.sample.pos == 3 motor.presets.positions.sample.update_pos(2) assert not hasattr(motor, 'wm_sample') motor.presets.sync() assert not hasattr(motor, 'mv_sample') proc.join() motor.presets.sync() assert hasattr(motor, 'mv_sample')