def test_add_index_file(labware_name, labware_offset_tempdir): deck = Deck() parent = deck.position_for(1) definition = labware.get_labware_definition(labware_name) lw = labware.Labware(definition, parent) labware_hash = helpers.hash_labware_def(definition) labware.save_calibration(lw, Point(0, 0, 0)) lw_uri = helpers.uri_from_definition(definition) str_parent = labware._get_parent_identifier(lw) slot = '1' if str_parent: mod_dict = {str_parent: f'{slot}-{str_parent}'} else: mod_dict = {} full_id = f'{labware_hash}{str_parent}' blob = { "uri": f'{lw_uri}', "slot": full_id, "module": mod_dict } lw_path = labware_offset_tempdir / 'index.json' info = file_operators.read_cal_file(lw_path) assert info['data'][full_id] == blob
def save_new_offsets(labware_hash, delta, definition): calibration_path = CONFIG['labware_calibration_offsets_dir_v2'] if not calibration_path.exists(): calibration_path.mkdir(parents=True, exist_ok=True) old_delta = _look_up_offsets(labware_hash, definition) uri = cal_helpers.uri_from_definition(definition) # Note that the next line looks incorrect (like it's letting the prior # value leak into the new one). That's sort of correct, but this actually # functions properly as is--the old labware system was designed to not go # back to an offset of (0,0,0) as a factory default, so once you have # calibrated once, you can't get the original back without deleting the # entire database. Instead of modifying the database to allow resetting a # single labware, we're replacing the old data representation with a new # one that does have (0,0,0) as its base offset. Once the old data is # removed from the system, it will be possible to modify this so that it # replaces the coordinates with the exact offset calibrated, instead of the # delta between the old and new offests, but for now this is necessary to # make both v1 and v2 labware work. This function only handles v2, but that # is why the delta is passed here instead of an absolute. new_delta = old_delta + Point(x=delta[0], y=delta[1], z=delta[2]) labware_offset_path = calibration_path / '{}.json'.format(labware_hash) calibration_data = modify._helper_offset_data_format( str(labware_offset_path), new_delta) modify._add_to_index_offset_file('', '', uri, labware_hash) io.save_to_file(labware_offset_path, calibration_data)
def labware_from_paths(paths: List[str]) -> Dict[str, 'LabwareDefinition']: labware_defs: Dict[str, 'LabwareDefinition'] = {} for strpath in paths: log.info(f"local labware: checking path {strpath}") purepath = pathlib.PurePath(strpath) if purepath.is_absolute(): path = pathlib.Path(purepath) else: path = pathlib.Path.cwd() / purepath if not path.is_dir(): raise RuntimeError(f'{path} is not a directory') for child in path.iterdir(): if child.is_file() and child.suffix.endswith('json'): try: defn = labware.verify_definition(child.read_bytes()) except (ValidationError, JSONDecodeError) as e: log.info(f"{child}: invalid labware, ignoring") log.debug(f"{child}: labware invalid because: {str(e)}") else: uri = helpers.uri_from_definition(defn) labware_defs[uri] = defn log.info(f'loaded labware {uri} from {child}') else: log.info(f'ignoring {child} in labware path') return labware_defs
def test_uris(): details = ('opentrons', 'opentrons_96_tiprack_300ul', '1') uri = 'opentrons/opentrons_96_tiprack_300ul/1' assert helpers.uri_from_details(*details) == uri defn = labware.get_labware_definition(details[1], details[0], details[2]) assert helpers.uri_from_definition(defn) == uri lw = labware.Labware(defn, Location(Point(0, 0, 0), 'Test Slot')) assert lw.uri == uri
def create_bundle(contents: BundleContents, into_file: BinaryIO): """ Create a bundle from assumed-good contents """ with ZipFile(into_file, mode='w') as zf: zf.writestr(MAIN_PROTOCOL_FILENAME, contents.protocol) for dataname, datafile in contents.bundled_data.items(): name = PurePath(dataname).name zf.writestr(f'{DATA_DIR}/{name}', datafile) for lwdef in contents.bundled_labware.values(): zipsafe = uri_from_definition(lwdef, '-') zf.writestr(f'{LABWARE_DIR}/{zipsafe}.json', json.dumps(lwdef)) zf.writestr('.bundle_beta', str(date.today()))
def build_and_prep(cls, name, contents, hardware, loop, broker, motion_lock, extra_labware): protocol = parse(contents, filename=name, extra_labware={ helpers.uri_from_definition(defn): defn for defn in extra_labware }) sess = cls(name, protocol, hardware, loop, broker, motion_lock) sess.prepare() return sess
def extract_bundle(bundle: ZipFile) -> BundleContents: # noqa(C901) """ Extract a bundle and verify its contents and structure. """ if not _has_files_at_root(bundle): raise RuntimeError( 'No files found in ZIP file\'s root directory. When selecting ' 'files to zip, make sure to directly select the files ' 'themselves. Do not select their parent directory, which would ' 'result in nesting all files inside that directory in the ZIP.') try: with bundle.open(MAIN_PROTOCOL_FILENAME, 'r') as protocol_file: py_protocol = protocol_file.read().decode('utf-8') except KeyError: raise RuntimeError( f'Bundled protocol should have a {MAIN_PROTOCOL_FILENAME} ' + 'file in the root directory') bundled_labware: Dict[str, 'LabwareDefinition'] = {} bundled_data = {} bundled_python = {} for zipInfo in bundle.infolist(): filepath = PurePosixPath(zipInfo.filename) rootpath = filepath.parts[0] # skip directories and weird OS-added directories # (note: the __MACOSX dir would contain '__MACOSX/foo.py' # and other files. This would break our inferences, so we need # to exclude all contents of that directory) if rootpath == '__MACOSX' or zipInfo.is_dir(): continue with bundle.open(zipInfo) as f: if rootpath == LABWARE_DIR and filepath.suffix == '.json': labware_def = json.loads(f.read().decode('utf-8')) labware_key = uri_from_definition(labware_def) if labware_key in bundled_labware: raise RuntimeError( f'Conflicting labware in bundle: {labware_key}') bundled_labware[labware_key] = labware_def elif rootpath == DATA_DIR: # note: data files are read as binary bundled_data[str(filepath.relative_to(DATA_DIR))] = f.read() elif (filepath.suffix == '.py' and str(filepath) != MAIN_PROTOCOL_FILENAME): bundled_python[str(filepath)] = f.read().decode('utf-8') if not bundled_labware: raise RuntimeError('No labware definitions found in bundle.') return BundleContents(py_protocol, bundled_labware, bundled_data, bundled_python)
def uri(self) -> str: """ A string fully identifying the labware. :returns: The uri, ``"namespace/loadname/version"`` """ return helpers.uri_from_definition(self._definition)