def load_config_dict(pipette_id: str) -> Tuple[ 'PipetteFusedSpec', 'PipetteModel']: """ Give updated config with overrides for a pipette. This will add the default value for a mutable config before returning the modified config value. """ override = load_overrides(pipette_id) model = override['model'] config = fuse_specs(model) if 'quirks' not in override.keys(): override['quirks'] = {key: True for key in config['quirks']} for top_level_key in config.keys(): if top_level_key != 'quirks': add_default(config[top_level_key]) # type: ignore config.update(override) # type: ignore return config, model
def load( pipette_model: 'PipetteModel', pipette_id: str = None) -> PipetteConfig: """ Load pipette config data This function loads from a combination of - the pipetteModelSpecs.json file in the wheel (should never be edited) - the pipetteNameSpecs.json file in the wheel (should never be edited) - any config overrides found in ``opentrons.config.CONFIG['pipette_config_overrides_dir']`` This function reads from disk each time, so changes to the overrides will be picked up in subsequent calls. :param str pipette_model: The pipette model name (i.e. "p10_single_v1.3") for which to load configuration :param pipette_id: An (optional) unique ID for the pipette to locate config overrides. If the ID is not specified, the system assumes this is a simulated pipette and does not save settings. If the ID is specified but no overrides corresponding to the ID are found, the system creates a new overrides file for it. :type pipette_id: str or None :raises KeyError: if ``pipette_model`` is not in the top-level keys of the pipetteModelSpecs.json file (and therefore not in :py:attr:`configs`) :returns PipetteConfig: The configuration, loaded and checked """ # Load the model config and update with the name config cfg = fuse_specs(pipette_model) # Load overrides if we have a pipette id if pipette_id: try: override = load_overrides(pipette_id) if 'quirks' in override.keys(): override['quirks'] = [ qname for qname, qval in override['quirks'].items() if qval] for legacy_key in ( 'defaultAspirateFlowRate', 'defaultDispenseFlowRate', 'defaultBlowOutFlowRate'): override.pop(legacy_key, None) except FileNotFoundError: save_overrides(pipette_id, {}, pipette_model) log.info( "Save defaults for pipette model {} and id {}".format( pipette_model, pipette_id)) else: cfg.update(override) # type: ignore # the ulPerMm functions are structured in pipetteModelSpecs.json as # a list sorted from oldest to newest. That means the latest functions # are always the last element and, as of right now, the older ones are # the first element (for models that only have one function, the first # and last elements are the same, which is fine). If we add more in the # future, we’ll have to change this code to select items more # intelligently if ff.use_old_aspiration_functions(): log.debug("Using old aspiration functions") ul_per_mm = cfg['ulPerMm'][0] else: ul_per_mm = cfg['ulPerMm'][-1] smoothie_configs = cfg['smoothieConfigs'] res = PipetteConfig( top=ensure_value( cfg, 'top', MUTABLE_CONFIGS), bottom=ensure_value( cfg, 'bottom', MUTABLE_CONFIGS), blow_out=ensure_value( cfg, 'blowout', MUTABLE_CONFIGS), drop_tip=ensure_value( cfg, 'dropTip', MUTABLE_CONFIGS), pick_up_current=ensure_value(cfg, 'pickUpCurrent', MUTABLE_CONFIGS), pick_up_distance=ensure_value(cfg, 'pickUpDistance', MUTABLE_CONFIGS), pick_up_increment=ensure_value( cfg, 'pickUpIncrement', MUTABLE_CONFIGS), pick_up_presses=ensure_value(cfg, 'pickUpPresses', MUTABLE_CONFIGS), pick_up_speed=ensure_value(cfg, 'pickUpSpeed', MUTABLE_CONFIGS), aspirate_flow_rate=cfg['defaultAspirateFlowRate']['value'], dispense_flow_rate=cfg['defaultDispenseFlowRate']['value'], channels=ensure_value(cfg, 'channels', MUTABLE_CONFIGS), model_offset=ensure_value(cfg, 'modelOffset', MUTABLE_CONFIGS), plunger_current=ensure_value(cfg, 'plungerCurrent', MUTABLE_CONFIGS), drop_tip_current=ensure_value(cfg, 'dropTipCurrent', MUTABLE_CONFIGS), drop_tip_speed=ensure_value(cfg, 'dropTipSpeed', MUTABLE_CONFIGS), min_volume=ensure_value(cfg, 'minVolume', MUTABLE_CONFIGS), max_volume=ensure_value(cfg, 'maxVolume', MUTABLE_CONFIGS), ul_per_mm=ul_per_mm, quirks=validate_quirks(ensure_value(cfg, 'quirks', MUTABLE_CONFIGS)), tip_overlap=cfg['tipOverlap'], tip_length=ensure_value(cfg, 'tipLength', MUTABLE_CONFIGS), display_name=ensure_value(cfg, 'displayName', MUTABLE_CONFIGS), name=cfg['name'], back_compat_names=cfg.get('backCompatNames', []), return_tip_height=cfg.get('returnTipHeight', 0.5), blow_out_flow_rate=cfg['defaultBlowOutFlowRate']['value'], max_travel=smoothie_configs['travelDistance'], home_position=smoothie_configs['homePosition'], steps_per_mm=smoothie_configs['stepsPerMM'], idle_current=cfg.get('idleCurrent', LOW_CURRENT_DEFAULT), default_blow_out_flow_rates=cfg['defaultBlowOutFlowRate'].get( 'valuesByApiLevel', {'2.0': cfg['defaultBlowOutFlowRate']['value']}), default_dispense_flow_rates=cfg['defaultDispenseFlowRate'].get( 'valuesByApiLevel', {'2.0': cfg['defaultDispenseFlowRate']['value']}), default_aspirate_flow_rates=cfg['defaultAspirateFlowRate'].get( 'valuesByApiLevel', {'2.0': cfg['defaultAspirateFlowRate']['value']}), ) return res
def test_fuse(model, name): defdict = fuse_specs(model, name) typeguard.check_type('defdict', defdict, PipetteFusedSpec)