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
Beispiel #2
0
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)
Beispiel #3
0
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
Beispiel #5
0
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()))
Beispiel #6
0
 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
Beispiel #7
0
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)
Beispiel #8
0
    def uri(self) -> str:
        """ A string fully identifying the labware.

        :returns: The uri, ``"namespace/loadname/version"``
        """
        return helpers.uri_from_definition(self._definition)