diff --git a/api/src/opentrons/protocol_reader/file_format_validator.py b/api/src/opentrons/protocol_reader/file_format_validator.py index f13d1339041..df119ac3ffa 100644 --- a/api/src/opentrons/protocol_reader/file_format_validator.py +++ b/api/src/opentrons/protocol_reader/file_format_validator.py @@ -1,5 +1,5 @@ """File format validation interface.""" - +from __future__ import annotations from typing import Iterable @@ -29,6 +29,16 @@ class FileFormatValidationError(ProtocolFilesInvalidError): """Raised when a file does not conform to the format it's supposed to.""" + @classmethod + def _generic_json_failure( + cls, info: IdentifiedJsonMain, exc: Exception + ) -> FileFormatValidationError: + return cls( + message=f"{info.original_file.name} could not be read as a JSON protocol.", + detail={"kind": "bad-json-protocol"}, + wrapping=[PythonException(exc)], + ) + class FileFormatValidator: """File format validation interface.""" @@ -61,22 +71,80 @@ def validate_sync() -> None: await anyio.to_thread.run_sync(validate_sync) +def _handle_v8_json_protocol_validation_error( + info: IdentifiedJsonMain, pve: PydanticValidationError +) -> None: + for error in pve.errors(): + if error["loc"] == ("commandSchemaId",) and error["type"] == "type_error.enum": + # type_error.enum is for "this entry is not in this enum" and happens if you constrain a field by + # annotating it with Enum, as we now do for command schema IDs + raise FileFormatValidationError( + message=( + f"{info.original_file.name} could not be read as a JSON protocol, in part because its command schema " + "id is unknown. This protocol may have been exported from a future version of authorship software. " + "Updating your Opentrons software may help." + ), + detail={ + "kind": "bad-command-schema-id", + "command-schema-id": info.unvalidated_json.get( + "commandSchemaId", "" + ), + }, + wrapping=[PythonException(pve)], + ) from pve + if ( + error["loc"] == ("labwareDefinitionSchemaId",) + and error["type"] == "value_error.const" + ): + # value_error.const is for "this entry is not one of these const values", which is different from type_error.enum + # for I'm sure a very good reason, and happens if you constrain a field by annotating it with a Literal + raise FileFormatValidationError( + message=( + f"{info.original_file.name} could not be read as a JSON protocol, in part because its labware schema " + "id is unknown. This protocol may have been exported from a future version of authorship software. " + "Updating your Opentrons software may help." + ), + detail={ + "kind": "bad-labware-schema-id", + "labware-schema-id": info.unvalidated_json.get( + "labwareDefinitionSchemaId", "" + ), + }, + ) + if error["loc"] == ("liquidSchemaId",) and error["type"] == "value_error.const": + raise FileFormatValidationError( + message=( + f"{info.original_file.name} could not be read as a JSON protocol, in part because its liquid schema " + "id is unknown. This protocol may have been exported from a future version of authorship software. " + "Updating your Opentrons software may help." + ), + detail={ + "kind": "bad-liquid-schema-id", + "liquid-schema-id": info.unvalidated_json.get( + "liquidSchemaId", "" + ), + }, + ) + else: + raise FileFormatValidationError._generic_json_failure(info, pve) from pve + + async def _validate_json_protocol(info: IdentifiedJsonMain) -> None: def validate_sync() -> None: - try: - if info.schema_version == 8: + if info.schema_version == 8: + try: JsonProtocolV8.parse_obj(info.unvalidated_json) - elif info.schema_version == 7: - JsonProtocolV7.parse_obj(info.unvalidated_json) - elif info.schema_version == 6: - JsonProtocolV6.parse_obj(info.unvalidated_json) - else: - JsonProtocolUpToV5.parse_obj(info.unvalidated_json) - except PydanticValidationError as e: - raise FileFormatValidationError( - message=f"{info.original_file.name} could not be read as a JSON protocol.", - detail={"kind": "bad-json-protocol"}, - wrapping=[PythonException(e)], - ) from e + except PydanticValidationError as pve: + _handle_v8_json_protocol_validation_error(info, pve) + else: + try: + if info.schema_version == 7: + JsonProtocolV7.parse_obj(info.unvalidated_json) + elif info.schema_version == 6: + JsonProtocolV6.parse_obj(info.unvalidated_json) + else: + JsonProtocolUpToV5.parse_obj(info.unvalidated_json) + except PydanticValidationError as e: + raise FileFormatValidationError._generic_json_failure(info, e) from e await anyio.to_thread.run_sync(validate_sync) diff --git a/api/src/opentrons/protocol_runner/json_translator.py b/api/src/opentrons/protocol_runner/json_translator.py index 65410662e77..6c670baf97a 100644 --- a/api/src/opentrons/protocol_runner/json_translator.py +++ b/api/src/opentrons/protocol_runner/json_translator.py @@ -1,6 +1,6 @@ """Translation of JSON protocol commands into ProtocolEngine commands.""" -from typing import cast, List, Union -from pydantic import parse_obj_as +from typing import cast, List, Union, Iterator +from pydantic import parse_obj_as, ValidationError as PydanticValidationError from opentrons_shared_data.pipette.types import PipetteNameType from opentrons_shared_data.protocol.models import ( @@ -12,6 +12,7 @@ protocol_schema_v8, ) from opentrons_shared_data import command as command_schema +from opentrons_shared_data.errors.exceptions import InvalidProtocolData, PythonException from opentrons.types import MountType from opentrons.protocol_engine import ( @@ -196,7 +197,7 @@ class JsonTranslator: """Class that translates commands/liquids from PD/JSON to ProtocolEngine.""" def translate_liquids( - self, protocol: Union[ProtocolSchemaV6, ProtocolSchemaV7] + self, protocol: Union[ProtocolSchemaV6, ProtocolSchemaV7, ProtocolSchemaV8] ) -> List[Liquid]: """Takes json protocol v6 and translates liquids->protocol engine liquids.""" protocol_liquids = protocol.liquids or {} @@ -258,7 +259,8 @@ def _translate_v8_commands( self, protocol: ProtocolSchemaV8 ) -> List[pe_commands.CommandCreate]: """Translate commands in json protocol schema v8, which might be of different command schemas.""" - command_schema_ref = protocol.commandSchemaId + command_schema_ref = protocol.commandSchemaId.value + # these calls will raise if the command schema version is invalid or unknown command_schema_version = command_schema.schema_version_from_ref( command_schema_ref @@ -267,4 +269,18 @@ def _translate_v8_commands( command_schema_version ) - return [_translate_simple_command(command) for command in protocol.commands] + def translate_all_commands() -> Iterator[pe_commands.CommandCreate]: + for command in protocol.commands: + try: + yield _translate_simple_command(command) + except PydanticValidationError as pve: + raise InvalidProtocolData( + message=( + "The protocol is invalid because it contains an unknown or malformed command, " + f'"{command.commandType}".' + ), + detail={"kind": "invalid-command"}, + wrapping=[PythonException(pve)], + ) + + return list(translate_all_commands()) diff --git a/api/tests/opentrons/protocol_runner/test_json_translator.py b/api/tests/opentrons/protocol_runner/test_json_translator.py index 62181880dbc..a583fcbf1c4 100644 --- a/api/tests/opentrons/protocol_runner/test_json_translator.py +++ b/api/tests/opentrons/protocol_runner/test_json_translator.py @@ -85,17 +85,17 @@ protocol_schema_v8.Command( commandType="aspirate", key=None, - params=protocol_schema_v8.Params( - pipetteId="pipette-id-1", - labwareId="labware-id-2", - volume=1.23, - flowRate=4.56, - wellName="A1", - wellLocation=SD_WellLocation( - origin="bottom", - offset=OffsetVector(x=0, y=0, z=7.89), - ), - ), + params={ + "pipetteId": "pipette-id-1", + "labwareId": "labware-id-2", + "volume": 1.23, + "flowRate": 4.56, + "wellName": "A1", + "wellLocation": { + "origin": "bottom", + "offset": {"x": 0, "y": 0, "z": 7.89}, + }, + }, ), pe_commands.AspirateCreate( key=None, @@ -147,17 +147,17 @@ protocol_schema_v8.Command( commandType="dispense", key="dispense-key", - params=protocol_schema_v8.Params( - pipetteId="pipette-id-1", - labwareId="labware-id-2", - volume=1.23, - flowRate=4.56, - wellName="A1", - wellLocation=SD_WellLocation( - origin="bottom", - offset=OffsetVector(x=0, y=0, z=7.89), - ), - ), + params={ + "pipetteId": "pipette-id-1", + "labwareId": "labware-id-2", + "volume": 1.23, + "flowRate": 4.56, + "wellName": "A1", + "wellLocation": { + "origin": "bottom", + "offset": {"x": 0, "y": 0, "z": 7.89}, + }, + }, ), pe_commands.DispenseCreate( key="dispense-key", @@ -193,11 +193,11 @@ ), protocol_schema_v8.Command( commandType="dropTip", - params=protocol_schema_v8.Params( - pipetteId="pipette-id-1", - labwareId="labware-id-2", - wellName="A1", - ), + params={ + "pipetteId": "pipette-id-1", + "labwareId": "labware-id-2", + "wellName": "A1", + }, ), pe_commands.DropTipCreate( params=pe_commands.DropTipParams( @@ -230,11 +230,11 @@ ), protocol_schema_v8.Command( commandType="pickUpTip", - params=protocol_schema_v8.Params( - pipetteId="pipette-id-1", - labwareId="labware-id-2", - wellName="A1", - ), + params={ + "pipetteId": "pipette-id-1", + "labwareId": "labware-id-2", + "wellName": "A1", + }, ), pe_commands.PickUpTipCreate( params=pe_commands.PickUpTipParams( @@ -272,15 +272,15 @@ ), protocol_schema_v8.Command( commandType="touchTip", - params=protocol_schema_v8.Params( - pipetteId="pipette-id-1", - labwareId="labware-id-2", - wellName="A1", - wellLocation=SD_WellLocation( - origin="bottom", - offset=OffsetVector(x=0, y=0, z=-1.23), - ), - ), + params={ + "pipetteId": "pipette-id-1", + "labwareId": "labware-id-2", + "wellName": "A1", + "wellLocation": { + "origin": "bottom", + "offset": {"x": 0, "y": 0, "z": -1.23}, + }, + }, ), pe_commands.TouchTipCreate( params=pe_commands.TouchTipParams( @@ -307,9 +307,11 @@ ), protocol_schema_v8.Command( commandType="loadPipette", - params=protocol_schema_v8.Params( - pipetteId="pipette-id-1", mount="left", pipetteName="p10_single" - ), + params={ + "pipetteId": "pipette-id-1", + "mount": "left", + "pipetteName": "p10_single", + }, ), pe_commands.LoadPipetteCreate( params=pe_commands.LoadPipetteParams( @@ -337,11 +339,11 @@ ), protocol_schema_v8.Command( commandType="loadModule", - params=protocol_schema_v8.Params( - moduleId="module-id-1", - model="magneticModuleV2", - location=Location(slotName="3"), - ), + params={ + "moduleId": "module-id-1", + "model": "magneticModuleV2", + "location": {"slotName": "3"}, + }, ), pe_commands.LoadModuleCreate( params=pe_commands.LoadModuleParams( @@ -372,14 +374,14 @@ ), protocol_schema_v8.Command( commandType="loadLabware", - params=protocol_schema_v8.Params( - labwareId="labware-id-2", - version=1, - namespace="example", - loadName="foo_8_plate_33ul", - location=Location(moduleId="temperatureModuleId"), - displayName="Trash", - ), + params={ + "labwareId": "labware-id-2", + "version": 1, + "namespace": "example", + "loadName": "foo_8_plate_33ul", + "location": {"moduleId": "temperatureModuleId"}, + "displayName": "Trash", + }, ), pe_commands.LoadLabwareCreate( params=pe_commands.LoadLabwareParams( @@ -421,16 +423,16 @@ ), protocol_schema_v8.Command( commandType="blowout", - params=protocol_schema_v8.Params( - pipetteId="pipette-id-1", - labwareId="labware-id-2", - wellName="A1", - wellLocation=SD_WellLocation( - origin="bottom", - offset=OffsetVector(x=0, y=0, z=7.89), - ), - flowRate=1.23, - ), + params={ + "pipetteId": "pipette-id-1", + "labwareId": "labware-id-2", + "wellName": "A1", + "wellLocation": { + "origin": "bottom", + "offset": {"x": 0, "y": 0, "z": 7.89}, + }, + "flowRate": 1.23, + }, ), pe_commands.BlowOutCreate( params=pe_commands.BlowOutParams( @@ -456,7 +458,7 @@ ), protocol_schema_v8.Command( commandType="delay", - params=protocol_schema_v8.Params(waitForResume=True, message="hello world"), + params={"waitForResume": True, "message": "hello world"}, ), pe_commands.WaitForResumeCreate( params=pe_commands.WaitForResumeParams(message="hello world") @@ -473,7 +475,7 @@ ), protocol_schema_v8.Command( commandType="delay", - params=protocol_schema_v8.Params(seconds=12.34, message="hello world"), + params={"seconds": 12.34, "message": "hello world"}, ), pe_commands.WaitForDurationCreate( params=pe_commands.WaitForDurationParams( @@ -493,7 +495,7 @@ ), protocol_schema_v8.Command( commandType="waitForResume", - params=protocol_schema_v8.Params(message="hello world"), + params={"message": "hello world"}, ), pe_commands.WaitForResumeCreate( params=pe_commands.WaitForResumeParams(message="hello world") @@ -510,7 +512,7 @@ ), protocol_schema_v8.Command( commandType="waitForDuration", - params=protocol_schema_v8.Params(seconds=12.34, message="hello world"), + params={"seconds": 12.34, "message": "hello world"}, ), pe_commands.WaitForDurationCreate( params=pe_commands.WaitForDurationParams( @@ -540,12 +542,12 @@ ), protocol_schema_v8.Command( commandType="moveToCoordinates", - params=protocol_schema_v8.Params( - pipetteId="pipette-id-1", - coordinates=OffsetVector(x=1.1, y=2.2, z=3.3), - minimumZHeight=123.4, - forceDirect=True, - ), + params={ + "pipetteId": "pipette-id-1", + "coordinates": {"x": 1.1, "y": 2.2, "z": 3.3}, + "minimumZHeight": 123.4, + "forceDirect": True, + }, ), pe_commands.MoveToCoordinatesCreate( params=pe_commands.MoveToCoordinatesParams( @@ -593,20 +595,20 @@ ), protocol_schema_v8.Command( commandType="thermocycler/runProfile", - params=protocol_schema_v8.Params( - moduleId="module-id-2", - blockMaxVolumeUl=1.11, - profile=[ - ProfileStep( - celsius=2.22, - holdSeconds=3.33, - ), - ProfileStep( - celsius=4.44, - holdSeconds=5.55, - ), + params={ + "moduleId": "module-id-2", + "blockMaxVolumeUl": 1.11, + "profile": [ + { + "celsius": 2.22, + "holdSeconds": 3.33, + }, + { + "celsius": 4.44, + "holdSeconds": 5.55, + }, ], - ), + }, ), pe_commands.thermocycler.RunProfileCreate( params=pe_commands.thermocycler.RunProfileParams( @@ -645,11 +647,11 @@ protocol_schema_v8.Command( commandType="loadLiquid", key=None, - params=protocol_schema_v8.Params( - labwareId="labware-id-2", - liquidId="liquid-id-555", - volumeByWell={"A1": 32, "B2": 50}, - ), + params={ + "labwareId": "labware-id-2", + "liquidId": "liquid-id-555", + "volumeByWell": {"A1": 32, "B2": 50}, + }, ), pe_commands.LoadLiquidCreate( key=None, @@ -816,7 +818,7 @@ def _make_v8_json_protocol( ), labwareDefinitions=labware_definitions, labwareDefinitionSchemaId="opentronsLabwareSchemaV2", - commandSchemaId="opentronsCommandSchemaV8", + commandSchemaId=protocol_schema_v8.CommandSchemaId("opentronsCommandSchemaV8"), commands=commands, liquidSchemaId="opentronsLiquidSchemaV1", liquids=liquids, diff --git a/shared-data/js/protocols.ts b/shared-data/js/protocols.ts index e8effe36237..147c0b3125d 100644 --- a/shared-data/js/protocols.ts +++ b/shared-data/js/protocols.ts @@ -4,6 +4,7 @@ import Ajv from 'ajv' +import commandSchema10 from '../command/schemas/10.json' import commandSchema9 from '../command/schemas/9.json' import commandSchema8 from '../command/schemas/8.json' import commandSchema7 from '../command/schemas/7.json' @@ -30,6 +31,9 @@ const validateCommands8 = ( new Promise((resolve, reject) => { const requestedSchema = toValidate.commandSchemaId switch (requestedSchema) { + case 'opentronsCommandSchemaV10': + resolve(commandSchema10) + break case 'opentronsCommandSchemaV9': resolve(commandSchema9) break @@ -43,7 +47,13 @@ const validateCommands8 = ( keyword: 'Invalid command schema requested', dataPath: requestedSchema, schemaPath: '#/properties/commandSchemaId', - params: { allowedValues: ['opentronsCommandSchemaV8'] }, + params: { + allowedValues: [ + 'opentronsCommandSchemaV8', + 'opentronsCommandSchemaV9', + 'opentronsCommandSchemaV10', + ], + }, }, ]) break diff --git a/shared-data/protocol/fixtures/8/simpleFlexV10CommandsV8.json b/shared-data/protocol/fixtures/8/simpleFlexV10CommandsV8.json new file mode 100644 index 00000000000..fbd97d37b50 --- /dev/null +++ b/shared-data/protocol/fixtures/8/simpleFlexV10CommandsV8.json @@ -0,0 +1,1475 @@ +{ + "$otSharedSchema": "#/protocol/schemas/8", + "schemaVersion": 8, + "metadata": { + "protocolName": "Simple test protocol", + "author": "engineering ", + "description": "A short test protocol", + "created": 1223131231, + "tags": ["unitTest"] + }, + "robot": { + "model": "OT-3 Standard", + "deckId": "ot3_standard" + }, + "liquidSchemaId": "opentronsLiquidSchemaV1", + "liquids": { + "waterId": { + "displayName": "Water", + "description": "Liquid H2O", + "displayColor": "#7332a8" + } + }, + "labwareDefinitionSchemaId": "opentronsLabwareSchemaV2", + "labwareDefinitions": { + "opentrons/opentrons_1_trash_1100ml_fixed/1": { + "ordering": [["A1"]], + "metadata": { + "displayCategory": "trash", + "displayVolumeUnits": "mL", + "displayName": "Opentrons Fixed Trash", + "tags": [] + }, + "schemaVersion": 2, + "version": 1, + "namespace": "opentrons", + "dimensions": { + "xDimension": 172.86, + "yDimension": 165.86, + "zDimension": 82 + }, + "parameters": { + "format": "trash", + "isTiprack": false, + "loadName": "opentrons_1_trash_1100ml_fixed", + "isMagneticModuleCompatible": false, + "quirks": ["fixedTrash", "centerMultichannelOnWells"] + }, + "wells": { + "A1": { + "shape": "rectangular", + "yDimension": 165.67, + "xDimension": 107.11, + "totalLiquidVolume": 1100000, + "depth": 77, + "x": 82.84, + "y": 53.56, + "z": 5 + } + }, + "brand": { + "brand": "Opentrons" + }, + "groups": [ + { + "wells": ["A1"], + "metadata": {} + } + ], + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "opentrons/opentrons_96_tiprack_10ul/1": { + "ordering": [ + ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"], + ["A2", "B2", "C2", "D2", "E2", "F2", "G2", "H2"], + ["A3", "B3", "C3", "D3", "E3", "F3", "G3", "H3"], + ["A4", "B4", "C4", "D4", "E4", "F4", "G4", "H4"], + ["A5", "B5", "C5", "D5", "E5", "F5", "G5", "H5"], + ["A6", "B6", "C6", "D6", "E6", "F6", "G6", "H6"], + ["A7", "B7", "C7", "D7", "E7", "F7", "G7", "H7"], + ["A8", "B8", "C8", "D8", "E8", "F8", "G8", "H8"], + ["A9", "B9", "C9", "D9", "E9", "F9", "G9", "H9"], + ["A10", "B10", "C10", "D10", "E10", "F10", "G10", "H10"], + ["A11", "B11", "C11", "D11", "E11", "F11", "G11", "H11"], + ["A12", "B12", "C12", "D12", "E12", "F12", "G12", "H12"] + ], + "brand": { + "brand": "Opentrons", + "brandId": [], + "links": [ + "https://shop.opentrons.com/collections/opentrons-tips/products/opentrons-10ul-tips" + ] + }, + "metadata": { + "displayName": "Opentrons 96 Tip Rack 10 µL", + "displayCategory": "tipRack", + "displayVolumeUnits": "µL", + "tags": [] + }, + "dimensions": { + "xDimension": 127.76, + "yDimension": 85.48, + "zDimension": 64.69 + }, + "wells": { + "A1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 74.24, + "z": 25.49 + }, + "B1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 65.24, + "z": 25.49 + }, + "C1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 56.24, + "z": 25.49 + }, + "D1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 47.24, + "z": 25.49 + }, + "E1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 38.24, + "z": 25.49 + }, + "F1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 29.24, + "z": 25.49 + }, + "G1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 20.24, + "z": 25.49 + }, + "H1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 11.24, + "z": 25.49 + }, + "A2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 74.24, + "z": 25.49 + }, + "B2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 65.24, + "z": 25.49 + }, + "C2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 56.24, + "z": 25.49 + }, + "D2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 47.24, + "z": 25.49 + }, + "E2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 38.24, + "z": 25.49 + }, + "F2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 29.24, + "z": 25.49 + }, + "G2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 20.24, + "z": 25.49 + }, + "H2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 11.24, + "z": 25.49 + }, + "A3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 74.24, + "z": 25.49 + }, + "B3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 65.24, + "z": 25.49 + }, + "C3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 56.24, + "z": 25.49 + }, + "D3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 47.24, + "z": 25.49 + }, + "E3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 38.24, + "z": 25.49 + }, + "F3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 29.24, + "z": 25.49 + }, + "G3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 20.24, + "z": 25.49 + }, + "H3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 11.24, + "z": 25.49 + }, + "A4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 74.24, + "z": 25.49 + }, + "B4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 65.24, + "z": 25.49 + }, + "C4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 56.24, + "z": 25.49 + }, + "D4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 47.24, + "z": 25.49 + }, + "E4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 38.24, + "z": 25.49 + }, + "F4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 29.24, + "z": 25.49 + }, + "G4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 20.24, + "z": 25.49 + }, + "H4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 11.24, + "z": 25.49 + }, + "A5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 74.24, + "z": 25.49 + }, + "B5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 65.24, + "z": 25.49 + }, + "C5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 56.24, + "z": 25.49 + }, + "D5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 47.24, + "z": 25.49 + }, + "E5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 38.24, + "z": 25.49 + }, + "F5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 29.24, + "z": 25.49 + }, + "G5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 20.24, + "z": 25.49 + }, + "H5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 11.24, + "z": 25.49 + }, + "A6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 74.24, + "z": 25.49 + }, + "B6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 65.24, + "z": 25.49 + }, + "C6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 56.24, + "z": 25.49 + }, + "D6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 47.24, + "z": 25.49 + }, + "E6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 38.24, + "z": 25.49 + }, + "F6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 29.24, + "z": 25.49 + }, + "G6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 20.24, + "z": 25.49 + }, + "H6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 11.24, + "z": 25.49 + }, + "A7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 74.24, + "z": 25.49 + }, + "B7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 65.24, + "z": 25.49 + }, + "C7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 56.24, + "z": 25.49 + }, + "D7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 47.24, + "z": 25.49 + }, + "E7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 38.24, + "z": 25.49 + }, + "F7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 29.24, + "z": 25.49 + }, + "G7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 20.24, + "z": 25.49 + }, + "H7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 11.24, + "z": 25.49 + }, + "A8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 74.24, + "z": 25.49 + }, + "B8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 65.24, + "z": 25.49 + }, + "C8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 56.24, + "z": 25.49 + }, + "D8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 47.24, + "z": 25.49 + }, + "E8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 38.24, + "z": 25.49 + }, + "F8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 29.24, + "z": 25.49 + }, + "G8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 20.24, + "z": 25.49 + }, + "H8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 11.24, + "z": 25.49 + }, + "A9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 74.24, + "z": 25.49 + }, + "B9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 65.24, + "z": 25.49 + }, + "C9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 56.24, + "z": 25.49 + }, + "D9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 47.24, + "z": 25.49 + }, + "E9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 38.24, + "z": 25.49 + }, + "F9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 29.24, + "z": 25.49 + }, + "G9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 20.24, + "z": 25.49 + }, + "H9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 11.24, + "z": 25.49 + }, + "A10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 74.24, + "z": 25.49 + }, + "B10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 65.24, + "z": 25.49 + }, + "C10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 56.24, + "z": 25.49 + }, + "D10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 47.24, + "z": 25.49 + }, + "E10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 38.24, + "z": 25.49 + }, + "F10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 29.24, + "z": 25.49 + }, + "G10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 20.24, + "z": 25.49 + }, + "H10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 11.24, + "z": 25.49 + }, + "A11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 74.24, + "z": 25.49 + }, + "B11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 65.24, + "z": 25.49 + }, + "C11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 56.24, + "z": 25.49 + }, + "D11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 47.24, + "z": 25.49 + }, + "E11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 38.24, + "z": 25.49 + }, + "F11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 29.24, + "z": 25.49 + }, + "G11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 20.24, + "z": 25.49 + }, + "H11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 11.24, + "z": 25.49 + }, + "A12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 74.24, + "z": 25.49 + }, + "B12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 65.24, + "z": 25.49 + }, + "C12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 56.24, + "z": 25.49 + }, + "D12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 47.24, + "z": 25.49 + }, + "E12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 38.24, + "z": 25.49 + }, + "F12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 29.24, + "z": 25.49 + }, + "G12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 20.24, + "z": 25.49 + }, + "H12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 11.24, + "z": 25.49 + } + }, + "groups": [ + { + "metadata": {}, + "wells": [ + "A1", + "B1", + "C1", + "D1", + "E1", + "F1", + "G1", + "H1", + "A2", + "B2", + "C2", + "D2", + "E2", + "F2", + "G2", + "H2", + "A3", + "B3", + "C3", + "D3", + "E3", + "F3", + "G3", + "H3", + "A4", + "B4", + "C4", + "D4", + "E4", + "F4", + "G4", + "H4", + "A5", + "B5", + "C5", + "D5", + "E5", + "F5", + "G5", + "H5", + "A6", + "B6", + "C6", + "D6", + "E6", + "F6", + "G6", + "H6", + "A7", + "B7", + "C7", + "D7", + "E7", + "F7", + "G7", + "H7", + "A8", + "B8", + "C8", + "D8", + "E8", + "F8", + "G8", + "H8", + "A9", + "B9", + "C9", + "D9", + "E9", + "F9", + "G9", + "H9", + "A10", + "B10", + "C10", + "D10", + "E10", + "F10", + "G10", + "H10", + "A11", + "B11", + "C11", + "D11", + "E11", + "F11", + "G11", + "H11", + "A12", + "B12", + "C12", + "D12", + "E12", + "F12", + "G12", + "H12" + ] + } + ], + "parameters": { + "format": "96Standard", + "isTiprack": true, + "tipLength": 39.2, + "tipOverlap": 3.29, + "isMagneticModuleCompatible": false, + "loadName": "opentrons_96_tiprack_10ul" + }, + "namespace": "opentrons", + "version": 1, + "schemaVersion": 2, + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "example/plate/1": { + "ordering": [ + ["A1", "B1", "C1", "D1"], + ["A2", "B2", "C2", "D2"] + ], + "brand": { + "brand": "foo", + "brandId": [] + }, + "metadata": { + "displayName": "Foo 8 Well Plate 33uL", + "displayCategory": "wellPlate", + "displayVolumeUnits": "µL" + }, + "dimensions": { + "xDimension": 127.75, + "yDimension": 85.5, + "zDimension": 100 + }, + "wells": { + "A1": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 18.21, + "y": 75.43, + "z": 75 + }, + "B1": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 18.21, + "y": 56.15, + "z": 75 + }, + "C1": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 18.21, + "y": 36.87, + "z": 75 + }, + "D1": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 18.21, + "y": 17.59, + "z": 75 + }, + "A2": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 38.1, + "y": 75.43, + "z": 75 + }, + "B2": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 38.1, + "y": 56.15, + "z": 75 + }, + "C2": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 38.1, + "y": 36.87, + "z": 75 + }, + "D2": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 38.1, + "y": 17.59, + "z": 75 + } + }, + "groups": [ + { + "metadata": {}, + "wells": ["A1", "B1", "C1", "A2", "B2", "C2"] + } + ], + "parameters": { + "format": "irregular", + "quirks": [], + "isTiprack": false, + "isMagneticModuleCompatible": false, + "loadName": "foo_8_plate_33ul" + }, + "namespace": "example", + "version": 1, + "schemaVersion": 2, + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": 0 + } + } + }, + "commandSchemaId": "opentronsCommandSchemaV10", + "commands": [ + { + "commandType": "loadPipette", + "params": { + "pipetteId": "pipetteId", + "pipetteName": "p1000_96", + "mount": "left" + } + }, + { + "commandType": "loadModule", + "params": { + "moduleId": "magneticBlockId", + "model": "magneticBlockV1", + "location": { "slotName": "3" } + } + }, + { + "commandType": "loadModule", + "params": { + "moduleId": "temperatureModuleId", + "model": "temperatureModuleV2", + "location": { "slotName": "1" } + } + }, + { + "commandType": "loadLabware", + "params": { + "labwareId": "sourcePlateId", + "loadName": "armadillo_96_wellplate_200ul_pcr_full_skirt", + "namespace": "opentrons", + "version": 1, + "location": { + "moduleId": "temperatureModuleId" + }, + "displayName": "Source Plate" + } + }, + { + "commandType": "loadLabware", + "params": { + "labwareId": "destPlateId", + "loadName": "armadillo_96_wellplate_200ul_pcr_full_skirt", + "namespace": "opentrons", + "version": 1, + "location": { + "moduleId": "magneticBlockId" + }, + "displayName": "Sample Collection Plate" + } + }, + { + "commandType": "loadLabware", + "params": { + "labwareId": "tipRackId", + "location": { "slotName": "8" }, + "loadName": "opentrons_96_tiprack_1000ul", + "namespace": "opentrons", + "version": 1, + "displayName": "Opentrons 96 Tip Rack 1000 µL" + } + }, + { + "commandType": "loadLabware", + "params": { + "labwareId": "fixedTrash", + "location": { + "slotName": "12" + }, + "loadName": "opentrons_1_trash_1100ml_fixed", + "namespace": "opentrons", + "version": 1, + "displayName": "Trash" + } + }, + { + "commandType": "loadLiquid", + "params": { + "liquidId": "waterId", + "labwareId": "sourcePlateId", + "volumeByWell": { + "A1": 100, + "B1": 100 + } + } + }, + { + "commandType": "home", + "params": {} + }, + { + "commandType": "pickUpTip", + "params": { + "pipetteId": "pipetteId", + "labwareId": "tipRackId", + "wellName": "B1" + } + }, + { + "commandType": "aspirate", + "params": { + "pipetteId": "pipetteId", + "labwareId": "sourcePlateId", + "wellName": "A1", + "volume": 5, + "flowRate": 3, + "wellLocation": { + "origin": "bottom", + "offset": { "x": 0, "y": 0, "z": 2 } + } + } + }, + { + "commandType": "waitForDuration", + "params": { + "seconds": 42 + } + }, + { + "commandType": "dispense", + "params": { + "pipetteId": "pipetteId", + "labwareId": "destPlateId", + "wellName": "B1", + "volume": 4.5, + "flowRate": 2.5, + "wellLocation": { + "origin": "bottom", + "offset": { "x": 0, "y": 0, "z": 1 } + } + } + }, + { + "commandType": "touchTip", + "params": { + "pipetteId": "pipetteId", + "labwareId": "destPlateId", + "wellName": "B1", + "speed": 42.0, + "wellLocation": { + "origin": "bottom", + "offset": { "x": 0, "y": 0, "z": 11 } + } + } + }, + { + "commandType": "blowout", + "params": { + "pipetteId": "pipetteId", + "labwareId": "destPlateId", + "wellName": "B1", + "flowRate": 2, + "wellLocation": { + "origin": "bottom", + "offset": { "x": 0, "y": 0, "z": 12 } + } + } + }, + { + "commandType": "moveToCoordinates", + "params": { + "pipetteId": "pipetteId", + "coordinates": { "x": 100, "y": 100, "z": 100 } + } + }, + { + "commandType": "moveToWell", + "params": { + "pipetteId": "pipetteId", + "labwareId": "destPlateId", + "wellName": "B2", + "speed": 12.3 + } + }, + { + "commandType": "moveToWell", + "params": { + "pipetteId": "pipetteId", + "labwareId": "destPlateId", + "wellName": "B2", + "wellLocation": { + "origin": "bottom", + "offset": { "x": 2, "y": 3, "z": 10 } + }, + "minimumZHeight": 35, + "forceDirect": true + } + }, + { + "commandType": "dropTip", + "params": { + "pipetteId": "pipetteId", + "labwareId": "fixedTrash", + "wellName": "A1" + } + }, + { + "commandType": "waitForResume", + "params": { + "message": "pause command" + } + }, + { + "commandType": "moveToCoordinates", + "params": { + "pipetteId": "pipetteId", + "coordinates": { "x": 0, "y": 0, "z": 0 }, + "minimumZHeight": 35, + "forceDirect": true + } + }, + { + "commandType": "moveRelative", + "params": { + "pipetteId": "pipetteId", + "axis": "x", + "distance": 1 + } + }, + { + "commandType": "moveRelative", + "params": { + "pipetteId": "pipetteId", + "axis": "y", + "distance": 0.1 + } + }, + { + "commandType": "savePosition", + "params": { + "pipetteId": "pipetteId" + } + }, + { + "commandType": "moveRelative", + "params": { + "pipetteId": "pipetteId", + "axis": "z", + "distance": 10 + } + }, + { + "commandType": "savePosition", + "params": { + "pipetteId": "pipetteId", + "positionId": "positionId" + } + } + ], + "commandAnnotationSchemaId": "opentronsCommandAnnotationSchemaV1", + "commandAnnotations": [ + { + "commandKeys": [ + "1abc123", + "2abc123", + "3abc123", + "4abc123", + "5abc123", + "6abc123", + "7abc123" + ], + "annotationType": "custom" + } + ] +} diff --git a/shared-data/protocol/fixtures/8/simpleV10CommandsV8.json b/shared-data/protocol/fixtures/8/simpleV10CommandsV8.json new file mode 100644 index 00000000000..936e830a113 --- /dev/null +++ b/shared-data/protocol/fixtures/8/simpleV10CommandsV8.json @@ -0,0 +1,1473 @@ +{ + "$otSharedSchema": "#/protocol/schemas/8", + "schemaVersion": 8, + "metadata": { + "protocolName": "Simple test protocol", + "author": "engineering ", + "description": "A short test protocol", + "created": 1223131231, + "tags": ["unitTest"] + }, + "robot": { + "model": "OT-2 Standard", + "deckId": "ot2_standard" + }, + "liquidSchemaId": "opentronsLiquidSchemaV1", + "liquids": { + "waterId": { + "displayName": "Water", + "description": "Liquid H2O", + "displayColor": "#7332a8" + } + }, + "labwareDefinitionSchemaId": "opentronsLabwareSchemaV2", + "labwareDefinitions": { + "opentrons/opentrons_1_trash_1100ml_fixed/1": { + "ordering": [["A1"]], + "metadata": { + "displayCategory": "trash", + "displayVolumeUnits": "mL", + "displayName": "Opentrons Fixed Trash", + "tags": [] + }, + "schemaVersion": 2, + "version": 1, + "namespace": "opentrons", + "dimensions": { + "xDimension": 172.86, + "yDimension": 165.86, + "zDimension": 82 + }, + "parameters": { + "format": "trash", + "isTiprack": false, + "loadName": "opentrons_1_trash_1100ml_fixed", + "isMagneticModuleCompatible": false, + "quirks": ["fixedTrash", "centerMultichannelOnWells"] + }, + "wells": { + "A1": { + "shape": "rectangular", + "yDimension": 165.67, + "xDimension": 107.11, + "totalLiquidVolume": 1100000, + "depth": 77, + "x": 82.84, + "y": 53.56, + "z": 5 + } + }, + "brand": { + "brand": "Opentrons" + }, + "groups": [ + { + "wells": ["A1"], + "metadata": {} + } + ], + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "opentrons/opentrons_96_tiprack_10ul/1": { + "ordering": [ + ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"], + ["A2", "B2", "C2", "D2", "E2", "F2", "G2", "H2"], + ["A3", "B3", "C3", "D3", "E3", "F3", "G3", "H3"], + ["A4", "B4", "C4", "D4", "E4", "F4", "G4", "H4"], + ["A5", "B5", "C5", "D5", "E5", "F5", "G5", "H5"], + ["A6", "B6", "C6", "D6", "E6", "F6", "G6", "H6"], + ["A7", "B7", "C7", "D7", "E7", "F7", "G7", "H7"], + ["A8", "B8", "C8", "D8", "E8", "F8", "G8", "H8"], + ["A9", "B9", "C9", "D9", "E9", "F9", "G9", "H9"], + ["A10", "B10", "C10", "D10", "E10", "F10", "G10", "H10"], + ["A11", "B11", "C11", "D11", "E11", "F11", "G11", "H11"], + ["A12", "B12", "C12", "D12", "E12", "F12", "G12", "H12"] + ], + "brand": { + "brand": "Opentrons", + "brandId": [], + "links": [ + "https://shop.opentrons.com/collections/opentrons-tips/products/opentrons-10ul-tips" + ] + }, + "metadata": { + "displayName": "Opentrons 96 Tip Rack 10 µL", + "displayCategory": "tipRack", + "displayVolumeUnits": "µL", + "tags": [] + }, + "dimensions": { + "xDimension": 127.76, + "yDimension": 85.48, + "zDimension": 64.69 + }, + "wells": { + "A1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 74.24, + "z": 25.49 + }, + "B1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 65.24, + "z": 25.49 + }, + "C1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 56.24, + "z": 25.49 + }, + "D1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 47.24, + "z": 25.49 + }, + "E1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 38.24, + "z": 25.49 + }, + "F1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 29.24, + "z": 25.49 + }, + "G1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 20.24, + "z": 25.49 + }, + "H1": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 14.38, + "y": 11.24, + "z": 25.49 + }, + "A2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 74.24, + "z": 25.49 + }, + "B2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 65.24, + "z": 25.49 + }, + "C2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 56.24, + "z": 25.49 + }, + "D2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 47.24, + "z": 25.49 + }, + "E2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 38.24, + "z": 25.49 + }, + "F2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 29.24, + "z": 25.49 + }, + "G2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 20.24, + "z": 25.49 + }, + "H2": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 23.38, + "y": 11.24, + "z": 25.49 + }, + "A3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 74.24, + "z": 25.49 + }, + "B3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 65.24, + "z": 25.49 + }, + "C3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 56.24, + "z": 25.49 + }, + "D3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 47.24, + "z": 25.49 + }, + "E3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 38.24, + "z": 25.49 + }, + "F3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 29.24, + "z": 25.49 + }, + "G3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 20.24, + "z": 25.49 + }, + "H3": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 32.38, + "y": 11.24, + "z": 25.49 + }, + "A4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 74.24, + "z": 25.49 + }, + "B4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 65.24, + "z": 25.49 + }, + "C4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 56.24, + "z": 25.49 + }, + "D4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 47.24, + "z": 25.49 + }, + "E4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 38.24, + "z": 25.49 + }, + "F4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 29.24, + "z": 25.49 + }, + "G4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 20.24, + "z": 25.49 + }, + "H4": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 41.38, + "y": 11.24, + "z": 25.49 + }, + "A5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 74.24, + "z": 25.49 + }, + "B5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 65.24, + "z": 25.49 + }, + "C5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 56.24, + "z": 25.49 + }, + "D5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 47.24, + "z": 25.49 + }, + "E5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 38.24, + "z": 25.49 + }, + "F5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 29.24, + "z": 25.49 + }, + "G5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 20.24, + "z": 25.49 + }, + "H5": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 50.38, + "y": 11.24, + "z": 25.49 + }, + "A6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 74.24, + "z": 25.49 + }, + "B6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 65.24, + "z": 25.49 + }, + "C6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 56.24, + "z": 25.49 + }, + "D6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 47.24, + "z": 25.49 + }, + "E6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 38.24, + "z": 25.49 + }, + "F6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 29.24, + "z": 25.49 + }, + "G6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 20.24, + "z": 25.49 + }, + "H6": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 59.38, + "y": 11.24, + "z": 25.49 + }, + "A7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 74.24, + "z": 25.49 + }, + "B7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 65.24, + "z": 25.49 + }, + "C7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 56.24, + "z": 25.49 + }, + "D7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 47.24, + "z": 25.49 + }, + "E7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 38.24, + "z": 25.49 + }, + "F7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 29.24, + "z": 25.49 + }, + "G7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 20.24, + "z": 25.49 + }, + "H7": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 68.38, + "y": 11.24, + "z": 25.49 + }, + "A8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 74.24, + "z": 25.49 + }, + "B8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 65.24, + "z": 25.49 + }, + "C8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 56.24, + "z": 25.49 + }, + "D8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 47.24, + "z": 25.49 + }, + "E8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 38.24, + "z": 25.49 + }, + "F8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 29.24, + "z": 25.49 + }, + "G8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 20.24, + "z": 25.49 + }, + "H8": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 77.38, + "y": 11.24, + "z": 25.49 + }, + "A9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 74.24, + "z": 25.49 + }, + "B9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 65.24, + "z": 25.49 + }, + "C9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 56.24, + "z": 25.49 + }, + "D9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 47.24, + "z": 25.49 + }, + "E9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 38.24, + "z": 25.49 + }, + "F9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 29.24, + "z": 25.49 + }, + "G9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 20.24, + "z": 25.49 + }, + "H9": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 86.38, + "y": 11.24, + "z": 25.49 + }, + "A10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 74.24, + "z": 25.49 + }, + "B10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 65.24, + "z": 25.49 + }, + "C10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 56.24, + "z": 25.49 + }, + "D10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 47.24, + "z": 25.49 + }, + "E10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 38.24, + "z": 25.49 + }, + "F10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 29.24, + "z": 25.49 + }, + "G10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 20.24, + "z": 25.49 + }, + "H10": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 95.38, + "y": 11.24, + "z": 25.49 + }, + "A11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 74.24, + "z": 25.49 + }, + "B11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 65.24, + "z": 25.49 + }, + "C11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 56.24, + "z": 25.49 + }, + "D11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 47.24, + "z": 25.49 + }, + "E11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 38.24, + "z": 25.49 + }, + "F11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 29.24, + "z": 25.49 + }, + "G11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 20.24, + "z": 25.49 + }, + "H11": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 104.38, + "y": 11.24, + "z": 25.49 + }, + "A12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 74.24, + "z": 25.49 + }, + "B12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 65.24, + "z": 25.49 + }, + "C12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 56.24, + "z": 25.49 + }, + "D12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 47.24, + "z": 25.49 + }, + "E12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 38.24, + "z": 25.49 + }, + "F12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 29.24, + "z": 25.49 + }, + "G12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 20.24, + "z": 25.49 + }, + "H12": { + "depth": 39.2, + "shape": "circular", + "diameter": 3.27, + "totalLiquidVolume": 10, + "x": 113.38, + "y": 11.24, + "z": 25.49 + } + }, + "groups": [ + { + "metadata": {}, + "wells": [ + "A1", + "B1", + "C1", + "D1", + "E1", + "F1", + "G1", + "H1", + "A2", + "B2", + "C2", + "D2", + "E2", + "F2", + "G2", + "H2", + "A3", + "B3", + "C3", + "D3", + "E3", + "F3", + "G3", + "H3", + "A4", + "B4", + "C4", + "D4", + "E4", + "F4", + "G4", + "H4", + "A5", + "B5", + "C5", + "D5", + "E5", + "F5", + "G5", + "H5", + "A6", + "B6", + "C6", + "D6", + "E6", + "F6", + "G6", + "H6", + "A7", + "B7", + "C7", + "D7", + "E7", + "F7", + "G7", + "H7", + "A8", + "B8", + "C8", + "D8", + "E8", + "F8", + "G8", + "H8", + "A9", + "B9", + "C9", + "D9", + "E9", + "F9", + "G9", + "H9", + "A10", + "B10", + "C10", + "D10", + "E10", + "F10", + "G10", + "H10", + "A11", + "B11", + "C11", + "D11", + "E11", + "F11", + "G11", + "H11", + "A12", + "B12", + "C12", + "D12", + "E12", + "F12", + "G12", + "H12" + ] + } + ], + "parameters": { + "format": "96Standard", + "isTiprack": true, + "tipLength": 39.2, + "tipOverlap": 3.29, + "isMagneticModuleCompatible": false, + "loadName": "opentrons_96_tiprack_10ul" + }, + "namespace": "opentrons", + "version": 1, + "schemaVersion": 2, + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "example/plate/1": { + "ordering": [ + ["A1", "B1", "C1", "D1"], + ["A2", "B2", "C2", "D2"] + ], + "brand": { + "brand": "foo", + "brandId": [] + }, + "metadata": { + "displayName": "Foo 8 Well Plate 33uL", + "displayCategory": "wellPlate", + "displayVolumeUnits": "µL" + }, + "dimensions": { + "xDimension": 127.75, + "yDimension": 85.5, + "zDimension": 100 + }, + "wells": { + "A1": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 18.21, + "y": 75.43, + "z": 75 + }, + "B1": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 18.21, + "y": 56.15, + "z": 75 + }, + "C1": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 18.21, + "y": 36.87, + "z": 75 + }, + "D1": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 18.21, + "y": 17.59, + "z": 75 + }, + "A2": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 38.1, + "y": 75.43, + "z": 75 + }, + "B2": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 38.1, + "y": 56.15, + "z": 75 + }, + "C2": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 38.1, + "y": 36.87, + "z": 75 + }, + "D2": { + "depth": 25, + "totalLiquidVolume": 33, + "shape": "circular", + "diameter": 10, + "x": 38.1, + "y": 17.59, + "z": 75 + } + }, + "groups": [ + { + "metadata": {}, + "wells": ["A1", "B1", "C1", "A2", "B2", "C2"] + } + ], + "parameters": { + "format": "irregular", + "quirks": [], + "isTiprack": false, + "isMagneticModuleCompatible": false, + "loadName": "foo_8_plate_33ul" + }, + "namespace": "example", + "version": 1, + "schemaVersion": 2, + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": 0 + } + } + }, + "commandSchemaId": "opentronsCommandSchemaV10", + "commands": [ + { + "commandType": "loadPipette", + "params": { + "pipetteId": "pipetteId", + "pipetteName": "p10_single", + "mount": "left" + } + }, + { + "commandType": "loadModule", + "params": { + "moduleId": "magneticModuleId", + "model": "magneticModuleV2", + "location": { "slotName": "3" } + } + }, + { + "commandType": "loadModule", + "params": { + "moduleId": "temperatureModuleId", + "model": "temperatureModuleV2", + "location": { "slotName": "1" } + } + }, + { + "commandType": "loadLabware", + "params": { + "labwareId": "sourcePlateId", + "loadName": "armadillo_96_wellplate_200ul_pcr_full_skirt", + "namespace": "opentrons", + "version": 1, + "location": { + "moduleId": "temperatureModuleId" + }, + "displayName": "Source Plate" + } + }, + { + "commandType": "loadLabware", + "params": { + "labwareId": "destPlateId", + "loadName": "armadillo_96_wellplate_200ul_pcr_full_skirt", + "namespace": "opentrons", + "version": 1, + "location": { + "moduleId": "magneticModuleId" + }, + "displayName": "Sample Collection Plate" + } + }, + { + "commandType": "loadLabware", + "params": { + "labwareId": "tipRackId", + "location": { "slotName": "8" }, + "loadName": "opentrons_96_tiprack_10ul", + "namespace": "opentrons", + "version": 1, + "displayName": "Opentrons 96 Tip Rack 10 µL" + } + }, + { + "commandType": "loadLabware", + "params": { + "labwareId": "fixedTrash", + "location": { + "slotName": "12" + }, + "loadName": "opentrons_1_trash_1100ml_fixed", + "namespace": "opentrons", + "version": 1, + "displayName": "Trash" + } + }, + { + "commandType": "loadLiquid", + "params": { + "liquidId": "waterId", + "labwareId": "sourcePlateId", + "volumeByWell": { + "A1": 100, + "B1": 100 + } + } + }, + { + "commandType": "home", + "params": {} + }, + { + "commandType": "pickUpTip", + "params": { + "pipetteId": "pipetteId", + "labwareId": "tipRackId", + "wellName": "B1" + } + }, + { + "commandType": "aspirate", + "params": { + "pipetteId": "pipetteId", + "labwareId": "sourcePlateId", + "wellName": "A1", + "volume": 5, + "flowRate": 3, + "wellLocation": { + "origin": "bottom", + "offset": { "x": 0, "y": 0, "z": 2 } + } + } + }, + { + "commandType": "waitForDuration", + "params": { + "seconds": 42 + } + }, + { + "commandType": "dispense", + "params": { + "pipetteId": "pipetteId", + "labwareId": "destPlateId", + "wellName": "B1", + "volume": 4.5, + "flowRate": 2.5, + "wellLocation": { + "origin": "bottom", + "offset": { "x": 0, "y": 0, "z": 1 } + } + } + }, + { + "commandType": "touchTip", + "params": { + "pipetteId": "pipetteId", + "labwareId": "destPlateId", + "wellName": "B1", + "wellLocation": { + "origin": "bottom", + "offset": { "x": 0, "y": 0, "z": 11 } + } + } + }, + { + "commandType": "blowout", + "params": { + "pipetteId": "pipetteId", + "labwareId": "destPlateId", + "wellName": "B1", + "flowRate": 2, + "wellLocation": { + "origin": "bottom", + "offset": { "x": 0, "y": 0, "z": 12 } + } + } + }, + { + "commandType": "moveToCoordinates", + "params": { + "pipetteId": "pipetteId", + "coordinates": { "x": 100, "y": 100, "z": 100 } + } + }, + { + "commandType": "moveToWell", + "params": { + "pipetteId": "pipetteId", + "labwareId": "destPlateId", + "wellName": "B2" + } + }, + { + "commandType": "moveToWell", + "params": { + "pipetteId": "pipetteId", + "labwareId": "destPlateId", + "wellName": "B2", + "wellLocation": { + "origin": "bottom", + "offset": { "x": 2, "y": 3, "z": 10 } + }, + "minimumZHeight": 35, + "forceDirect": true + } + }, + { + "commandType": "dropTip", + "params": { + "pipetteId": "pipetteId", + "labwareId": "fixedTrash", + "wellName": "A1" + } + }, + { + "commandType": "waitForResume", + "params": { + "message": "pause command" + } + }, + { + "commandType": "moveToCoordinates", + "params": { + "pipetteId": "pipetteId", + "coordinates": { "x": 0, "y": 0, "z": 0 }, + "minimumZHeight": 35, + "forceDirect": true + } + }, + { + "commandType": "moveRelative", + "params": { + "pipetteId": "pipetteId", + "axis": "x", + "distance": 1 + } + }, + { + "commandType": "moveRelative", + "params": { + "pipetteId": "pipetteId", + "axis": "y", + "distance": 0.1 + } + }, + { + "commandType": "savePosition", + "params": { + "pipetteId": "pipetteId" + } + }, + { + "commandType": "moveRelative", + "params": { + "pipetteId": "pipetteId", + "axis": "z", + "distance": 10 + } + }, + { + "commandType": "savePosition", + "params": { + "pipetteId": "pipetteId", + "positionId": "positionId" + } + } + ], + "commandAnnotationSchemaId": "opentronsCommandAnnotationSchemaV1", + "commandAnnotations": [ + { + "commandKeys": [ + "1abc123", + "2abc123", + "3abc123", + "4abc123", + "5abc123", + "6abc123", + "7abc123" + ], + "annotationType": "custom" + } + ] +} diff --git a/shared-data/python/opentrons_shared_data/command/__init__.py b/shared-data/python/opentrons_shared_data/command/__init__.py index f1b58056638..a0d75a8aaa6 100644 --- a/shared-data/python/opentrons_shared_data/command/__init__.py +++ b/shared-data/python/opentrons_shared_data/command/__init__.py @@ -1,27 +1,37 @@ """opentrons_shared_data.command: functions command schemas.""" from pathlib import Path +from functools import cache import json import os import re +from logging import getLogger -from opentrons_shared_data.errors.exceptions import InvalidProtocolData, PythonException +from opentrons_shared_data.errors.exceptions import ( + InvalidProtocolData, + PythonException, + InvalidStoredData, +) from ..load import load_shared_data, get_shared_data_root +LOG = getLogger(__name__) + SCHEMA_REF_VERSION_RE = re.compile(r"opentronsCommandSchemaV(\d+)") def get_newest_schema_version() -> str: """Get the version string of the most modern command schema currently in shared-data.""" - command_schemas_dir = get_shared_data_root() / "command" / "schemas" - command_schemas = os.listdir(command_schemas_dir) - all_schema_versions = [] - for schema_file_name in command_schemas: - schema_version_match = re.match(r"(\d+).json", schema_file_name) - if schema_version_match is not None: - all_schema_versions.append(int(schema_version_match.group(1))) + schema_ids = known_schema_ids() + max_version = 0 + for schema_id in schema_ids: + ref_version_match = SCHEMA_REF_VERSION_RE.match(schema_id) + if not ref_version_match: + continue + ref_version = int(ref_version_match.group(1)) + if ref_version > max_version: + max_version = ref_version - return str(max(all_schema_versions)) + return str(max_version) def load_schema_string(version: str) -> str: @@ -50,3 +60,52 @@ def schema_version_from_ref(ref: str) -> str: detail={"ref": ref, "type": "bad-schema-ref", "schema-kind": "command"}, ) return version.group(1) + + +def _schema_id_from_file(schema_file_name: str) -> str: + schema_version_match = re.match(r"^.*(\d+).json$", schema_file_name) + if schema_version_match is None: + raise InvalidStoredData( + message=f"Command schema {schema_file_name} is not named appropriately for its version", + detail={"type": "bad-schema-name", "schema-kind": "command"}, + ) + try: + schema_content = json.load(open(schema_file_name)) + except json.JSONDecodeError as jde: + raise InvalidStoredData( + message=f"Command schema {schema_file_name} is not valid json", + detail={"type": "bad-schema-json", "schema-kind": "command"}, + wrapping=[PythonException(jde)], + ) from jde + + try: + schema_id: str = schema_content["$id"] + except KeyError as ke: + raise InvalidStoredData( + message=f"Command schema {schema_file_name} has no $id", + detail={"type": "bad-schema-json", "schema-kind": "command"}, + wrapping=[PythonException(ke)], + ) from ke + if not SCHEMA_REF_VERSION_RE.match(schema_id): + raise InvalidStoredData( + message=f"Command schema {schema_file_name} has an invalid id {schema_id} that does not match opentronsCommandSchema#" + ) + return schema_id + + +@cache +def known_schema_ids() -> list[str]: + """Get a list of all known command schema IDs.""" + command_schemas_dir = get_shared_data_root() / "command" / "schemas" + command_schemas = os.listdir(command_schemas_dir) + all_schema_ids = [] + for schema_file_name in command_schemas: + try: + all_schema_ids.append( + _schema_id_from_file(str(command_schemas_dir / schema_file_name)) + ) + except Exception: + LOG.exception( + f"Could not load command schema from {str(command_schemas_dir/schema_file_name)}, skipping" + ) + return all_schema_ids diff --git a/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v8.py b/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v8.py index ce5b814d73f..11beddb96c0 100644 --- a/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v8.py +++ b/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v8.py @@ -1,80 +1,23 @@ -from pydantic import BaseModel, Field, Extra -from typing import Any, List, Optional, Dict, Union +from enum import Enum +from typing import Any, List, Optional, Dict from typing_extensions import Literal +from pydantic import BaseModel, Field, Extra + from opentrons_shared_data.labware.labware_definition import LabwareDefinition +from opentrons_shared_data.command import known_schema_ids from .shared_models import ( Liquid, - Location, - ProfileStep, - WellLocation, - OffsetVector, Metadata, DesignerApplication, Robot, - NozzleConfigurationParams, ) -# TODO (tamar 3/15/22): split apart all the command payloads when we tackle #9583 -class Params(BaseModel): - slotName: Optional[str] - axes: Optional[List[str]] - pipetteId: Optional[str] - mount: Optional[str] - moduleId: Optional[str] - location: Optional[Union[Location, Literal["offDeck"]]] - labwareId: Optional[str] - displayName: Optional[str] - liquidId: Optional[str] - volumeByWell: Optional[Dict[str, Any]] - wellName: Optional[str] - volume: Optional[float] - flowRate: Optional[float] - wellLocation: Optional[WellLocation] - waitForResume: Optional[Literal[True]] - seconds: Optional[float] - minimumZHeight: Optional[float] - forceDirect: Optional[bool] - speed: Optional[float] - message: Optional[str] - coordinates: Optional[OffsetVector] - axis: Optional[str] - distance: Optional[float] - positionId: Optional[str] - temperature: Optional[float] - celsius: Optional[float] - blockMaxVolumeUl: Optional[float] - rpm: Optional[float] - height: Optional[float] - offset: Optional[OffsetVector] - profile: Optional[List[ProfileStep]] - radius: Optional[float] - newLocation: Optional[Union[Location, Literal["offDeck"]]] - strategy: Optional[str] - # schema v7 add-ons - homeAfter: Optional[bool] - alternateDropLocation: Optional[bool] - holdTimeSeconds: Optional[float] - maintenancePosition: Optional[str] - pipetteName: Optional[str] - model: Optional[str] - loadName: Optional[str] - namespace: Optional[str] - version: Optional[int] - pushOut: Optional[float] - pickUpOffset: Optional[OffsetVector] - dropOffset: Optional[OffsetVector] - # schema v8 add-ons - addressableAreaName: Optional[str] - configurationParams: Optional[NozzleConfigurationParams] - stayAtHighestPossibleZ: Optional[bool] - - class Command(BaseModel): commandType: str - params: Params + params: Dict[str, Any] key: Optional[str] @@ -86,6 +29,11 @@ class Config: extra = Extra.allow +CommandSchemaId = Enum( # type: ignore[misc] + "CommandSchemaId", ((schema_id, schema_id) for schema_id in known_schema_ids()) +) + + class ProtocolSchemaV8(BaseModel): otSharedSchema: Literal["#/protocol/schemas/8"] = Field( ..., @@ -100,7 +48,7 @@ class ProtocolSchemaV8(BaseModel): liquids: Dict[str, Liquid] labwareDefinitionSchemaId: Literal["opentronsLabwareSchemaV2"] labwareDefinitions: Dict[str, LabwareDefinition] - commandSchemaId: Literal["opentronsCommandSchemaV8", "opentronsCommandSchemaV9"] + commandSchemaId: CommandSchemaId commands: List[Command] commandAnnotationSchemaId: Literal["opentronsCommandAnnotationSchemaV1"] commandAnnotations: List[CommandAnnotation] diff --git a/shared-data/python/tests/command/test_known_schema_ids.py b/shared-data/python/tests/command/test_known_schema_ids.py new file mode 100644 index 00000000000..6ec282428ac --- /dev/null +++ b/shared-data/python/tests/command/test_known_schema_ids.py @@ -0,0 +1,9 @@ +from opentrons_shared_data.load import get_shared_data_root +from opentrons_shared_data.command import known_schema_ids + + +def test_known_schema_ids() -> None: + schema_paths = get_shared_data_root() / "command" / "schemas" + names = [filename.stem for filename in schema_paths.iterdir()] + ids = [f"opentronsCommandSchemaV{version}" for version in names] + assert sorted(ids) == sorted(known_schema_ids()) diff --git a/shared-data/python/tests/protocol/test_protocol_schema_v8.py b/shared-data/python/tests/protocol/test_protocol_schema_v8.py index 0928441d328..63d00db571d 100644 --- a/shared-data/python/tests/protocol/test_protocol_schema_v8.py +++ b/shared-data/python/tests/protocol/test_protocol_schema_v8.py @@ -11,10 +11,12 @@ def test_v8_types(defpath): def_data = load_shared_data(defpath) def_model = protocol_schema_v8.ProtocolSchemaV8.parse_raw(def_data) - def_dict_from_model = def_model.dict( - exclude_unset=True, - # 'schemaVersion' in python is '$schemaVersion' in JSON - by_alias=True, + def_dict_from_model = json.loads( + def_model.json( + exclude_unset=True, + # 'schemaVersion' in python is '$schemaVersion' in JSON + by_alias=True, + ) ) expected_def_dict = json.loads(def_data) assert def_dict_from_model == expected_def_dict