From 7393c92cefcd97b2e9354428084f6888ec5161ec Mon Sep 17 00:00:00 2001 From: CaseyBatten Date: Fri, 17 Jan 2025 11:16:49 -0500 Subject: [PATCH] feat(api, shared-data, app): Move lid PAPI and Core implementation (#17259) Covers EXEC-1005 Introduce `move_lid()` command to PAPI, allows the movement of lids from stacks and source labware to destination locations. --- ...0][Flex_X_v2_21_tc_lids_wrong_target].json | 866 ++++++- ...c62][Flex_S_v2_21_tc_lids_happy_path].json | 2039 ++++++++++++++++- api/src/opentrons/legacy_commands/helpers.py | 15 + .../protocol_api/core/engine/deck_conflict.py | 6 +- .../protocol_api/core/engine/protocol.py | 200 ++ .../protocol_api/core/engine/stringify.py | 3 + .../core/legacy/legacy_protocol_core.py | 19 + .../opentrons/protocol_api/core/protocol.py | 19 + .../protocol_api/protocol_context.py | 112 +- api/src/opentrons/protocol_engine/__init__.py | 2 + .../commands/absorbance_reader/__init__.py | 1 - .../protocol_engine/commands/load_lid.py | 6 +- .../commands/load_lid_stack.py | 75 +- .../protocol_engine/commands/move_labware.py | 36 +- .../protocol_engine/execution/equipment.py | 16 +- .../resources/labware_validation.py | 5 + .../protocol_engine/slot_standardization.py | 2 + .../protocol_engine/state/geometry.py | 3 +- .../protocol_engine/state/labware.py | 56 +- .../protocol_engine/state/update_types.py | 25 +- api/src/opentrons/protocol_engine/types.py | 9 +- .../protocol_api/test_protocol_context.py | 4 +- .../state/test_labware_view_old.py | 3 +- .../createSnippet.ts | 6 +- .../SetupLabware/LabwareListItem.tsx | 1 + .../SetupLabware/SetupLabwareList.tsx | 1 + .../Desktop/Devices/RunPreview/index.tsx | 1 + .../hooks/useDeckMapUtils.ts | 6 +- .../MoveLabwareInterventionContent.tsx | 7 +- .../utils/getRunLabwareRenderInfo.ts | 2 +- .../hooks/getLabwareLocationCombos.ts | 15 +- .../utils/labware.ts | 17 +- .../SingleLabwareModal.tsx | 1 + .../ProtocolSetupLabware/index.tsx | 4 +- .../analysis/getLabwareOffsetLocation.ts | 8 +- .../analysis/getLabwareRenderInfo.ts | 1 + .../getLabwareSetupItemGroups.ts | 6 +- .../transformations/getLocationInfoNames.ts | 5 +- .../transformations/getNestedLabwareInfo.ts | 6 +- .../src/hardware-sim/BaseDeck/BaseDeck.tsx | 2 + .../hardware-sim/Deck/MoveLabwareOnDeck.tsx | 4 +- .../ProtocolDeck/utils/getLabwareInSlots.ts | 2 + .../utils/commandText/getLoadCommandText.ts | 5 +- .../utils/getLabwareLocation.tsx | 7 +- labware-library/src/localization/en.ts | 2 + protocol-designer/src/analytics/middleware.ts | 3 + .../src/labware-ingred/reducers/index.ts | 2 +- .../src/pages/ProtocolOverview/utils.ts | 3 + .../src/step-forms/utils/index.ts | 2 + .../formLevel/moveLabwareFormErrors.ts | 1 + shared-data/command/schemas/11.json | 33 + shared-data/command/types/setup.ts | 1 + .../helpers/getAddressableAreasInProtocol.ts | 5 + .../js/helpers/parseProtocolCommands.ts | 7 +- shared-data/js/types.ts | 1 + .../1.json | 10 +- .../3/protocol_engine_lid_stack_object/1.json | 9 +- .../src/commandCreators/atomic/moveLabware.ts | 13 +- .../forMoveLabware.ts | 2 +- .../utils/createTimelineFromRunCommands.ts | 5 +- 60 files changed, 3550 insertions(+), 178 deletions(-) diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[7d16d5dbf0][Flex_X_v2_21_tc_lids_wrong_target].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[7d16d5dbf0][Flex_X_v2_21_tc_lids_wrong_target].json index a5d33bd3325..ca0539fd2ad 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[7d16d5dbf0][Flex_X_v2_21_tc_lids_wrong_target].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[7d16d5dbf0][Flex_X_v2_21_tc_lids_wrong_target].json @@ -1906,22 +1906,809 @@ "commandType": "loadLabware", "completedAt": "TIMESTAMP", "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "48dfe520259476c2688c469837455156", + "notes": [], + "params": { + "displayName": "D2", + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + }, + "namespace": "opentrons", + "version": 1 + }, + "result": { + "definition": { + "allowedRoles": [ + "labware", + "lid" + ], + "brand": { + "brand": "Opentrons", + "brandId": [] + }, + "compatibleParentLabware": [ + "armadillo_96_wellplate_200ul_pcr_full_skirt", + "biorad_96_wellplate_200ul_pcr", + "opentrons_96_wellplate_200ul_pcr_full_skirt", + "opentrons_flex_deck_riser", + "opentrons_tough_pcr_auto_sealing_lid", + "protocol_engine_lid_stack_object" + ], + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": -0.71 + }, + "dimensions": { + "xDimension": 127.7, + "yDimension": 85.48, + "zDimension": 12.8 + }, + "gripForce": 15.0, + "gripHeightFromLabwareBottom": 7.91, + "gripperOffsets": { + "default": { + "dropOffset": { + "x": 0, + "y": 0.52, + "z": -6 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 1.5 + } + }, + "lidDisposalOffsets": { + "dropOffset": { + "x": 0, + "y": 5.0, + "z": 50.0 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "lidOffsets": { + "dropOffset": { + "x": 0.5, + "y": 0, + "z": -1 + }, + "pickUpOffset": { + "x": 0.5, + "y": 0, + "z": -5 + } + } + }, + "groups": [ + { + "metadata": {}, + "wells": [] + } + ], + "metadata": { + "displayCategory": "lid", + "displayName": "Opentrons Tough PCR Auto-Sealing Lid", + "displayVolumeUnits": "µL", + "tags": [] + }, + "namespace": "opentrons", + "ordering": [], + "parameters": { + "format": "irregular", + "isMagneticModuleCompatible": false, + "isTiprack": false, + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "quirks": [] + }, + "schemaVersion": 3, + "stackLimit": 5, + "stackingOffsetWithLabware": { + "armadillo_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "biorad_96_wellplate_200ul_pcr": { + "x": 0, + "y": 0, + "z": 8.08 + }, + "default": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_flex_deck_riser": { + "x": 0, + "y": 0, + "z": 34 + }, + "opentrons_tough_pcr_auto_sealing_lid": { + "x": 0, + "y": 0, + "z": 6.492 + }, + "protocol_engine_lid_stack_object": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "stackingOffsetWithModule": { + "thermocyclerModuleV2": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "version": 1, + "wells": {} + }, + "labwareId": "UUID" + }, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "loadLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "dbb90de7194277dcb8565653c9e086cf", + "notes": [], + "params": { + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + }, + "namespace": "opentrons", + "version": 1 + }, + "result": { + "definition": { + "allowedRoles": [ + "labware", + "lid" + ], + "brand": { + "brand": "Opentrons", + "brandId": [] + }, + "compatibleParentLabware": [ + "armadillo_96_wellplate_200ul_pcr_full_skirt", + "biorad_96_wellplate_200ul_pcr", + "opentrons_96_wellplate_200ul_pcr_full_skirt", + "opentrons_flex_deck_riser", + "opentrons_tough_pcr_auto_sealing_lid", + "protocol_engine_lid_stack_object" + ], + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": -0.71 + }, + "dimensions": { + "xDimension": 127.7, + "yDimension": 85.48, + "zDimension": 12.8 + }, + "gripForce": 15.0, + "gripHeightFromLabwareBottom": 7.91, + "gripperOffsets": { + "default": { + "dropOffset": { + "x": 0, + "y": 0.52, + "z": -6 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 1.5 + } + }, + "lidDisposalOffsets": { + "dropOffset": { + "x": 0, + "y": 5.0, + "z": 50.0 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "lidOffsets": { + "dropOffset": { + "x": 0.5, + "y": 0, + "z": -1 + }, + "pickUpOffset": { + "x": 0.5, + "y": 0, + "z": -5 + } + } + }, + "groups": [ + { + "metadata": {}, + "wells": [] + } + ], + "metadata": { + "displayCategory": "lid", + "displayName": "Opentrons Tough PCR Auto-Sealing Lid", + "displayVolumeUnits": "µL", + "tags": [] + }, + "namespace": "opentrons", + "ordering": [], + "parameters": { + "format": "irregular", + "isMagneticModuleCompatible": false, + "isTiprack": false, + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "quirks": [] + }, + "schemaVersion": 3, + "stackLimit": 5, + "stackingOffsetWithLabware": { + "armadillo_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "biorad_96_wellplate_200ul_pcr": { + "x": 0, + "y": 0, + "z": 8.08 + }, + "default": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_flex_deck_riser": { + "x": 0, + "y": 0, + "z": 34 + }, + "opentrons_tough_pcr_auto_sealing_lid": { + "x": 0, + "y": 0, + "z": 6.492 + }, + "protocol_engine_lid_stack_object": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "stackingOffsetWithModule": { + "thermocyclerModuleV2": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "version": 1, + "wells": {} + }, + "labwareId": "UUID" + }, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "loadLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "a02dfd0c2994ad8f36030bc7d6a9d9b4", + "notes": [], + "params": { + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + }, + "namespace": "opentrons", + "version": 1 + }, + "result": { + "definition": { + "allowedRoles": [ + "labware", + "lid" + ], + "brand": { + "brand": "Opentrons", + "brandId": [] + }, + "compatibleParentLabware": [ + "armadillo_96_wellplate_200ul_pcr_full_skirt", + "biorad_96_wellplate_200ul_pcr", + "opentrons_96_wellplate_200ul_pcr_full_skirt", + "opentrons_flex_deck_riser", + "opentrons_tough_pcr_auto_sealing_lid", + "protocol_engine_lid_stack_object" + ], + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": -0.71 + }, + "dimensions": { + "xDimension": 127.7, + "yDimension": 85.48, + "zDimension": 12.8 + }, + "gripForce": 15.0, + "gripHeightFromLabwareBottom": 7.91, + "gripperOffsets": { + "default": { + "dropOffset": { + "x": 0, + "y": 0.52, + "z": -6 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 1.5 + } + }, + "lidDisposalOffsets": { + "dropOffset": { + "x": 0, + "y": 5.0, + "z": 50.0 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "lidOffsets": { + "dropOffset": { + "x": 0.5, + "y": 0, + "z": -1 + }, + "pickUpOffset": { + "x": 0.5, + "y": 0, + "z": -5 + } + } + }, + "groups": [ + { + "metadata": {}, + "wells": [] + } + ], + "metadata": { + "displayCategory": "lid", + "displayName": "Opentrons Tough PCR Auto-Sealing Lid", + "displayVolumeUnits": "µL", + "tags": [] + }, + "namespace": "opentrons", + "ordering": [], + "parameters": { + "format": "irregular", + "isMagneticModuleCompatible": false, + "isTiprack": false, + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "quirks": [] + }, + "schemaVersion": 3, + "stackLimit": 5, + "stackingOffsetWithLabware": { + "armadillo_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "biorad_96_wellplate_200ul_pcr": { + "x": 0, + "y": 0, + "z": 8.08 + }, + "default": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_flex_deck_riser": { + "x": 0, + "y": 0, + "z": 34 + }, + "opentrons_tough_pcr_auto_sealing_lid": { + "x": 0, + "y": 0, + "z": 6.492 + }, + "protocol_engine_lid_stack_object": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "stackingOffsetWithModule": { + "thermocyclerModuleV2": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "version": 1, + "wells": {} + }, + "labwareId": "UUID" + }, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "loadLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "78e449de1478e61a7fcd82e5ceb3d0da", + "notes": [], + "params": { + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + }, + "namespace": "opentrons", + "version": 1 + }, + "result": { + "definition": { + "allowedRoles": [ + "labware", + "lid" + ], + "brand": { + "brand": "Opentrons", + "brandId": [] + }, + "compatibleParentLabware": [ + "armadillo_96_wellplate_200ul_pcr_full_skirt", + "biorad_96_wellplate_200ul_pcr", + "opentrons_96_wellplate_200ul_pcr_full_skirt", + "opentrons_flex_deck_riser", + "opentrons_tough_pcr_auto_sealing_lid", + "protocol_engine_lid_stack_object" + ], + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": -0.71 + }, + "dimensions": { + "xDimension": 127.7, + "yDimension": 85.48, + "zDimension": 12.8 + }, + "gripForce": 15.0, + "gripHeightFromLabwareBottom": 7.91, + "gripperOffsets": { + "default": { + "dropOffset": { + "x": 0, + "y": 0.52, + "z": -6 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 1.5 + } + }, + "lidDisposalOffsets": { + "dropOffset": { + "x": 0, + "y": 5.0, + "z": 50.0 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "lidOffsets": { + "dropOffset": { + "x": 0.5, + "y": 0, + "z": -1 + }, + "pickUpOffset": { + "x": 0.5, + "y": 0, + "z": -5 + } + } + }, + "groups": [ + { + "metadata": {}, + "wells": [] + } + ], + "metadata": { + "displayCategory": "lid", + "displayName": "Opentrons Tough PCR Auto-Sealing Lid", + "displayVolumeUnits": "µL", + "tags": [] + }, + "namespace": "opentrons", + "ordering": [], + "parameters": { + "format": "irregular", + "isMagneticModuleCompatible": false, + "isTiprack": false, + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "quirks": [] + }, + "schemaVersion": 3, + "stackLimit": 5, + "stackingOffsetWithLabware": { + "armadillo_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "biorad_96_wellplate_200ul_pcr": { + "x": 0, + "y": 0, + "z": 8.08 + }, + "default": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_flex_deck_riser": { + "x": 0, + "y": 0, + "z": 34 + }, + "opentrons_tough_pcr_auto_sealing_lid": { + "x": 0, + "y": 0, + "z": 6.492 + }, + "protocol_engine_lid_stack_object": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "stackingOffsetWithModule": { + "thermocyclerModuleV2": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "version": 1, + "wells": {} + }, + "labwareId": "UUID" + }, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "loadLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "8b34edfc427771290ae5069fb3e17178", + "notes": [], + "params": { + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + }, + "namespace": "opentrons", + "version": 1 + }, + "result": { + "definition": { + "allowedRoles": [ + "labware", + "lid" + ], + "brand": { + "brand": "Opentrons", + "brandId": [] + }, + "compatibleParentLabware": [ + "armadillo_96_wellplate_200ul_pcr_full_skirt", + "biorad_96_wellplate_200ul_pcr", + "opentrons_96_wellplate_200ul_pcr_full_skirt", + "opentrons_flex_deck_riser", + "opentrons_tough_pcr_auto_sealing_lid", + "protocol_engine_lid_stack_object" + ], + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": -0.71 + }, + "dimensions": { + "xDimension": 127.7, + "yDimension": 85.48, + "zDimension": 12.8 + }, + "gripForce": 15.0, + "gripHeightFromLabwareBottom": 7.91, + "gripperOffsets": { + "default": { + "dropOffset": { + "x": 0, + "y": 0.52, + "z": -6 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 1.5 + } + }, + "lidDisposalOffsets": { + "dropOffset": { + "x": 0, + "y": 5.0, + "z": 50.0 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "lidOffsets": { + "dropOffset": { + "x": 0.5, + "y": 0, + "z": -1 + }, + "pickUpOffset": { + "x": 0.5, + "y": 0, + "z": -5 + } + } + }, + "groups": [ + { + "metadata": {}, + "wells": [] + } + ], + "metadata": { + "displayCategory": "lid", + "displayName": "Opentrons Tough PCR Auto-Sealing Lid", + "displayVolumeUnits": "µL", + "tags": [] + }, + "namespace": "opentrons", + "ordering": [], + "parameters": { + "format": "irregular", + "isMagneticModuleCompatible": false, + "isTiprack": false, + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "quirks": [] + }, + "schemaVersion": 3, + "stackLimit": 5, + "stackingOffsetWithLabware": { + "armadillo_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "biorad_96_wellplate_200ul_pcr": { + "x": 0, + "y": 0, + "z": 8.08 + }, + "default": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_flex_deck_riser": { + "x": 0, + "y": 0, + "z": 34 + }, + "opentrons_tough_pcr_auto_sealing_lid": { + "x": 0, + "y": 0, + "z": 6.492 + }, + "protocol_engine_lid_stack_object": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "stackingOffsetWithModule": { + "thermocyclerModuleV2": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "version": 1, + "wells": {} + }, + "labwareId": "UUID" + }, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "moveLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", "error": { "createdAt": "TIMESTAMP", - "detail": "ValueError: Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.", + "detail": "Labware opentrons_tough_pcr_auto_sealing_lid cannot be loaded onto labware nest_96_wellplate_2ml_deep", "errorCode": "4000", - "errorInfo": { - "args": "('Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.',)", - "class": "ValueError", - "traceback": " File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_engine/execution/command_executor.py\", line N, in execute\n result = await command_impl.execute(\n\n File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_engine/commands/load_labware.py\", line N, in execute\n raise ValueError(\n" - }, - "errorType": "PythonException", + "errorInfo": {}, + "errorType": "LabwareCannotBeStackedError", "id": "UUID", "isDefined": false, "wrappedErrors": [] }, "id": "UUID", - "key": "48dfe520259476c2688c469837455156", + "key": "5b419658c58e3cd2794830de913fb88c", "notes": [ { "longMessage": "Handling this command failure with FAIL_RUN.", @@ -1931,13 +2718,11 @@ } ], "params": { - "displayName": "D2", - "loadName": "opentrons_tough_pcr_auto_sealing_lid", - "location": { + "labwareId": "UUID", + "newLocation": { "labwareId": "UUID" }, - "namespace": "opentrons", - "version": 1 + "strategy": "usingGripper" }, "startedAt": "TIMESTAMP", "status": "failed" @@ -1954,7 +2739,7 @@ "errors": [ { "createdAt": "TIMESTAMP", - "detail": "ProtocolCommandFailedError [line 17]: Error 4000 GENERAL_ERROR (ProtocolCommandFailedError): PythonException: ValueError: Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.", + "detail": "ProtocolCommandFailedError [line 22]: Error 4000 GENERAL_ERROR (ProtocolCommandFailedError): LabwareCannotBeStackedError: Labware opentrons_tough_pcr_auto_sealing_lid cannot be loaded onto labware nest_96_wellplate_2ml_deep", "errorCode": "4000", "errorInfo": {}, "errorType": "ExceptionInProtocolError", @@ -1963,7 +2748,7 @@ "wrappedErrors": [ { "createdAt": "TIMESTAMP", - "detail": "PythonException: ValueError: Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.", + "detail": "LabwareCannotBeStackedError: Labware opentrons_tough_pcr_auto_sealing_lid cannot be loaded onto labware nest_96_wellplate_2ml_deep", "errorCode": "4000", "errorInfo": {}, "errorType": "ProtocolCommandFailedError", @@ -1972,14 +2757,10 @@ "wrappedErrors": [ { "createdAt": "TIMESTAMP", - "detail": "ValueError: Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.", + "detail": "Labware opentrons_tough_pcr_auto_sealing_lid cannot be loaded onto labware nest_96_wellplate_2ml_deep", "errorCode": "4000", - "errorInfo": { - "args": "('Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.',)", - "class": "ValueError", - "traceback": " File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_engine/execution/command_executor.py\", line N, in execute\n result = await command_impl.execute(\n\n File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_engine/commands/load_labware.py\", line N, in execute\n raise ValueError(\n" - }, - "errorType": "PythonException", + "errorInfo": {}, + "errorType": "LabwareCannotBeStackedError", "id": "UUID", "isDefined": false, "wrappedErrors": [] @@ -2019,6 +2800,47 @@ "location": { "slotName": "B2" } + }, + { + "definitionUri": "opentrons/opentrons_tough_pcr_auto_sealing_lid/1", + "displayName": "D2", + "id": "UUID", + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + } + }, + { + "definitionUri": "opentrons/opentrons_tough_pcr_auto_sealing_lid/1", + "id": "UUID", + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + } + }, + { + "definitionUri": "opentrons/opentrons_tough_pcr_auto_sealing_lid/1", + "id": "UUID", + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + } + }, + { + "definitionUri": "opentrons/opentrons_tough_pcr_auto_sealing_lid/1", + "id": "UUID", + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + } + }, + { + "definitionUri": "opentrons/opentrons_tough_pcr_auto_sealing_lid/1", + "id": "UUID", + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + } } ], "liquidClasses": [], diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[99c15c6c62][Flex_S_v2_21_tc_lids_happy_path].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[99c15c6c62][Flex_S_v2_21_tc_lids_happy_path].json index 8644d850edb..a33f00a85fa 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[99c15c6c62][Flex_S_v2_21_tc_lids_happy_path].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[99c15c6c62][Flex_S_v2_21_tc_lids_happy_path].json @@ -84,30 +84,167 @@ "commandType": "loadLabware", "completedAt": "TIMESTAMP", "createdAt": "TIMESTAMP", - "error": { - "createdAt": "TIMESTAMP", - "detail": "ValueError: Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.", - "errorCode": "4000", - "errorInfo": { - "args": "('Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.',)", - "class": "ValueError", - "traceback": " File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_engine/execution/command_executor.py\", line N, in execute\n result = await command_impl.execute(\n\n File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_engine/commands/load_labware.py\", line N, in execute\n raise ValueError(\n" + "id": "UUID", + "key": "50de88d471ad3910c29207fb6df4502e", + "notes": [], + "params": { + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + }, + "namespace": "opentrons", + "version": 1 + }, + "result": { + "definition": { + "allowedRoles": [ + "labware", + "lid" + ], + "brand": { + "brand": "Opentrons", + "brandId": [] + }, + "compatibleParentLabware": [ + "armadillo_96_wellplate_200ul_pcr_full_skirt", + "biorad_96_wellplate_200ul_pcr", + "opentrons_96_wellplate_200ul_pcr_full_skirt", + "opentrons_flex_deck_riser", + "opentrons_tough_pcr_auto_sealing_lid", + "protocol_engine_lid_stack_object" + ], + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": -0.71 + }, + "dimensions": { + "xDimension": 127.7, + "yDimension": 85.48, + "zDimension": 12.8 + }, + "gripForce": 15.0, + "gripHeightFromLabwareBottom": 7.91, + "gripperOffsets": { + "default": { + "dropOffset": { + "x": 0, + "y": 0.52, + "z": -6 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 1.5 + } + }, + "lidDisposalOffsets": { + "dropOffset": { + "x": 0, + "y": 5.0, + "z": 50.0 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "lidOffsets": { + "dropOffset": { + "x": 0.5, + "y": 0, + "z": -1 + }, + "pickUpOffset": { + "x": 0.5, + "y": 0, + "z": -5 + } + } + }, + "groups": [ + { + "metadata": {}, + "wells": [] + } + ], + "metadata": { + "displayCategory": "lid", + "displayName": "Opentrons Tough PCR Auto-Sealing Lid", + "displayVolumeUnits": "µL", + "tags": [] + }, + "namespace": "opentrons", + "ordering": [], + "parameters": { + "format": "irregular", + "isMagneticModuleCompatible": false, + "isTiprack": false, + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "quirks": [] + }, + "schemaVersion": 3, + "stackLimit": 5, + "stackingOffsetWithLabware": { + "armadillo_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "biorad_96_wellplate_200ul_pcr": { + "x": 0, + "y": 0, + "z": 8.08 + }, + "default": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_flex_deck_riser": { + "x": 0, + "y": 0, + "z": 34 + }, + "opentrons_tough_pcr_auto_sealing_lid": { + "x": 0, + "y": 0, + "z": 6.492 + }, + "protocol_engine_lid_stack_object": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "stackingOffsetWithModule": { + "thermocyclerModuleV2": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "version": 1, + "wells": {} }, - "errorType": "PythonException", - "id": "UUID", - "isDefined": false, - "wrappedErrors": [] + "labwareId": "UUID" }, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "loadLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", "id": "UUID", - "key": "50de88d471ad3910c29207fb6df4502e", - "notes": [ - { - "longMessage": "Handling this command failure with FAIL_RUN.", - "noteKind": "debugErrorRecovery", - "shortMessage": "Handling this command failure with FAIL_RUN.", - "source": "execution" - } - ], + "key": "a83392f86baf8cd5b4f0157c89d31dbd", + "notes": [], "params": { "loadName": "opentrons_tough_pcr_auto_sealing_lid", "location": { @@ -116,8 +253,1782 @@ "namespace": "opentrons", "version": 1 }, + "result": { + "definition": { + "allowedRoles": [ + "labware", + "lid" + ], + "brand": { + "brand": "Opentrons", + "brandId": [] + }, + "compatibleParentLabware": [ + "armadillo_96_wellplate_200ul_pcr_full_skirt", + "biorad_96_wellplate_200ul_pcr", + "opentrons_96_wellplate_200ul_pcr_full_skirt", + "opentrons_flex_deck_riser", + "opentrons_tough_pcr_auto_sealing_lid", + "protocol_engine_lid_stack_object" + ], + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": -0.71 + }, + "dimensions": { + "xDimension": 127.7, + "yDimension": 85.48, + "zDimension": 12.8 + }, + "gripForce": 15.0, + "gripHeightFromLabwareBottom": 7.91, + "gripperOffsets": { + "default": { + "dropOffset": { + "x": 0, + "y": 0.52, + "z": -6 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 1.5 + } + }, + "lidDisposalOffsets": { + "dropOffset": { + "x": 0, + "y": 5.0, + "z": 50.0 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "lidOffsets": { + "dropOffset": { + "x": 0.5, + "y": 0, + "z": -1 + }, + "pickUpOffset": { + "x": 0.5, + "y": 0, + "z": -5 + } + } + }, + "groups": [ + { + "metadata": {}, + "wells": [] + } + ], + "metadata": { + "displayCategory": "lid", + "displayName": "Opentrons Tough PCR Auto-Sealing Lid", + "displayVolumeUnits": "µL", + "tags": [] + }, + "namespace": "opentrons", + "ordering": [], + "parameters": { + "format": "irregular", + "isMagneticModuleCompatible": false, + "isTiprack": false, + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "quirks": [] + }, + "schemaVersion": 3, + "stackLimit": 5, + "stackingOffsetWithLabware": { + "armadillo_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "biorad_96_wellplate_200ul_pcr": { + "x": 0, + "y": 0, + "z": 8.08 + }, + "default": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_flex_deck_riser": { + "x": 0, + "y": 0, + "z": 34 + }, + "opentrons_tough_pcr_auto_sealing_lid": { + "x": 0, + "y": 0, + "z": 6.492 + }, + "protocol_engine_lid_stack_object": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "stackingOffsetWithModule": { + "thermocyclerModuleV2": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "version": 1, + "wells": {} + }, + "labwareId": "UUID" + }, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "loadLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "596b975cce05f1835b73fd3e2e9c04b0", + "notes": [], + "params": { + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + }, + "namespace": "opentrons", + "version": 1 + }, + "result": { + "definition": { + "allowedRoles": [ + "labware", + "lid" + ], + "brand": { + "brand": "Opentrons", + "brandId": [] + }, + "compatibleParentLabware": [ + "armadillo_96_wellplate_200ul_pcr_full_skirt", + "biorad_96_wellplate_200ul_pcr", + "opentrons_96_wellplate_200ul_pcr_full_skirt", + "opentrons_flex_deck_riser", + "opentrons_tough_pcr_auto_sealing_lid", + "protocol_engine_lid_stack_object" + ], + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": -0.71 + }, + "dimensions": { + "xDimension": 127.7, + "yDimension": 85.48, + "zDimension": 12.8 + }, + "gripForce": 15.0, + "gripHeightFromLabwareBottom": 7.91, + "gripperOffsets": { + "default": { + "dropOffset": { + "x": 0, + "y": 0.52, + "z": -6 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 1.5 + } + }, + "lidDisposalOffsets": { + "dropOffset": { + "x": 0, + "y": 5.0, + "z": 50.0 + }, + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "lidOffsets": { + "dropOffset": { + "x": 0.5, + "y": 0, + "z": -1 + }, + "pickUpOffset": { + "x": 0.5, + "y": 0, + "z": -5 + } + } + }, + "groups": [ + { + "metadata": {}, + "wells": [] + } + ], + "metadata": { + "displayCategory": "lid", + "displayName": "Opentrons Tough PCR Auto-Sealing Lid", + "displayVolumeUnits": "µL", + "tags": [] + }, + "namespace": "opentrons", + "ordering": [], + "parameters": { + "format": "irregular", + "isMagneticModuleCompatible": false, + "isTiprack": false, + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "quirks": [] + }, + "schemaVersion": 3, + "stackLimit": 5, + "stackingOffsetWithLabware": { + "armadillo_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "biorad_96_wellplate_200ul_pcr": { + "x": 0, + "y": 0, + "z": 8.08 + }, + "default": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_96_wellplate_200ul_pcr_full_skirt": { + "x": 0, + "y": 0, + "z": 8.193 + }, + "opentrons_flex_deck_riser": { + "x": 0, + "y": 0, + "z": 34 + }, + "opentrons_tough_pcr_auto_sealing_lid": { + "x": 0, + "y": 0, + "z": 6.492 + }, + "protocol_engine_lid_stack_object": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "stackingOffsetWithModule": { + "thermocyclerModuleV2": { + "x": 0, + "y": 0, + "z": 0 + } + }, + "version": 1, + "wells": {} + }, + "labwareId": "UUID" + }, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "loadModule", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "f36e0b7f0c55cacfd1ab16170f19e1a5", + "notes": [], + "params": { + "location": { + "slotName": "B1" + }, + "model": "thermocyclerModuleV2" + }, + "result": { + "definition": { + "calibrationPoint": { + "x": 14.4, + "y": 64.93, + "z": 97.8 + }, + "compatibleWith": [], + "dimensions": { + "bareOverallHeight": 108.96, + "lidHeight": 61.7, + "overLabwareHeight": 0.0 + }, + "displayName": "Thermocycler Module GEN2", + "gripperOffsets": { + "default": { + "dropOffset": { + "x": 0.0, + "y": 0.0, + "z": 5.6 + }, + "pickUpOffset": { + "x": 0.0, + "y": 0.0, + "z": 4.6 + } + } + }, + "labwareOffset": { + "x": 0.0, + "y": 68.8, + "z": 108.96 + }, + "model": "thermocyclerModuleV2", + "moduleType": "thermocyclerModuleType", + "otSharedSchema": "module/schemas/2", + "quirks": [], + "slotTransforms": { + "ot3_standard": { + "B1": { + "cornerOffsetFromSlot": [ + [ + -98, + 0, + 0, + 1 + ], + [ + -20.005, + 0, + 0, + 1 + ], + [ + -0.84, + 0, + 0, + 1 + ], + [ + 0, + 0, + 0, + 1 + ] + ], + "labwareOffset": [ + [ + -98, + 0, + 0, + 1 + ], + [ + -20.005, + 0, + 0, + 1 + ], + [ + -0.84, + 0, + 0, + 1 + ], + [ + 0, + 0, + 0, + 1 + ] + ] + } + } + } + }, + "model": "thermocyclerModuleV2", + "moduleId": "UUID", + "serialNumber": "UUID" + }, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "thermocycler/openLid", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "2dcefbc1eeff361e71d07198140c8bb0", + "notes": [], + "params": { + "moduleId": "UUID" + }, + "result": {}, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "loadLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "17ad8ecd2ae3ba1129713b2904ab71f8", + "notes": [], + "params": { + "loadName": "armadillo_96_wellplate_200ul_pcr_full_skirt", + "location": { + "moduleId": "UUID" + }, + "namespace": "opentrons", + "version": 2 + }, + "result": { + "definition": { + "allowedRoles": [], + "brand": { + "brand": "Thermo Scientific", + "brandId": [ + "AB2396" + ], + "links": [ + "https://www.fishersci.com/shop/products/armadillo-96-well-pcr-plate-1/AB2396" + ] + }, + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": 0 + }, + "dimensions": { + "xDimension": 127.76, + "yDimension": 85.48, + "zDimension": 16.0 + }, + "gripForce": 15.0, + "gripHeightFromLabwareBottom": 10.0, + "gripperOffsets": {}, + "groups": [ + { + "metadata": { + "wellBottomShape": "v" + }, + "wells": [ + "A1", + "A10", + "A11", + "A12", + "A2", + "A3", + "A4", + "A5", + "A6", + "A7", + "A8", + "A9", + "B1", + "B10", + "B11", + "B12", + "B2", + "B3", + "B4", + "B5", + "B6", + "B7", + "B8", + "B9", + "C1", + "C10", + "C11", + "C12", + "C2", + "C3", + "C4", + "C5", + "C6", + "C7", + "C8", + "C9", + "D1", + "D10", + "D11", + "D12", + "D2", + "D3", + "D4", + "D5", + "D6", + "D7", + "D8", + "D9", + "E1", + "E10", + "E11", + "E12", + "E2", + "E3", + "E4", + "E5", + "E6", + "E7", + "E8", + "E9", + "F1", + "F10", + "F11", + "F12", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "G1", + "G10", + "G11", + "G12", + "G2", + "G3", + "G4", + "G5", + "G6", + "G7", + "G8", + "G9", + "H1", + "H10", + "H11", + "H12", + "H2", + "H3", + "H4", + "H5", + "H6", + "H7", + "H8", + "H9" + ] + } + ], + "metadata": { + "displayCategory": "wellPlate", + "displayName": "Armadillo 96 Well Plate 200 µL PCR Full Skirt", + "displayVolumeUnits": "µL", + "tags": [] + }, + "namespace": "opentrons", + "ordering": [ + [ + "A1", + "B1", + "C1", + "D1", + "E1", + "F1", + "G1", + "H1" + ], + [ + "A10", + "B10", + "C10", + "D10", + "E10", + "F10", + "G10", + "H10" + ], + [ + "A11", + "B11", + "C11", + "D11", + "E11", + "F11", + "G11", + "H11" + ], + [ + "A12", + "B12", + "C12", + "D12", + "E12", + "F12", + "G12", + "H12" + ], + [ + "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" + ] + ], + "parameters": { + "format": "96Standard", + "isMagneticModuleCompatible": true, + "isTiprack": false, + "loadName": "armadillo_96_wellplate_200ul_pcr_full_skirt" + }, + "schemaVersion": 2, + "stackingOffsetWithLabware": { + "opentrons_96_pcr_adapter": { + "x": 0, + "y": 0, + "z": 10.95 + }, + "opentrons_96_well_aluminum_block": { + "x": 0, + "y": 0, + "z": 11.91 + } + }, + "stackingOffsetWithModule": { + "magneticBlockV1": { + "x": 0, + "y": 0, + "z": 3.54 + }, + "thermocyclerModuleV2": { + "x": 0, + "y": 0, + "z": 10.7 + } + }, + "version": 2, + "wells": { + "A1": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 14.38, + "y": 74.24, + "z": 1.05 + }, + "A10": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 95.38, + "y": 74.24, + "z": 1.05 + }, + "A11": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 104.38, + "y": 74.24, + "z": 1.05 + }, + "A12": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 113.38, + "y": 74.24, + "z": 1.05 + }, + "A2": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 23.38, + "y": 74.24, + "z": 1.05 + }, + "A3": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 32.38, + "y": 74.24, + "z": 1.05 + }, + "A4": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 41.38, + "y": 74.24, + "z": 1.05 + }, + "A5": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 50.38, + "y": 74.24, + "z": 1.05 + }, + "A6": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 59.38, + "y": 74.24, + "z": 1.05 + }, + "A7": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 68.38, + "y": 74.24, + "z": 1.05 + }, + "A8": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 77.38, + "y": 74.24, + "z": 1.05 + }, + "A9": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 86.38, + "y": 74.24, + "z": 1.05 + }, + "B1": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 14.38, + "y": 65.24, + "z": 1.05 + }, + "B10": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 95.38, + "y": 65.24, + "z": 1.05 + }, + "B11": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 104.38, + "y": 65.24, + "z": 1.05 + }, + "B12": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 113.38, + "y": 65.24, + "z": 1.05 + }, + "B2": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 23.38, + "y": 65.24, + "z": 1.05 + }, + "B3": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 32.38, + "y": 65.24, + "z": 1.05 + }, + "B4": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 41.38, + "y": 65.24, + "z": 1.05 + }, + "B5": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 50.38, + "y": 65.24, + "z": 1.05 + }, + "B6": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 59.38, + "y": 65.24, + "z": 1.05 + }, + "B7": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 68.38, + "y": 65.24, + "z": 1.05 + }, + "B8": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 77.38, + "y": 65.24, + "z": 1.05 + }, + "B9": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 86.38, + "y": 65.24, + "z": 1.05 + }, + "C1": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 14.38, + "y": 56.24, + "z": 1.05 + }, + "C10": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 95.38, + "y": 56.24, + "z": 1.05 + }, + "C11": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 104.38, + "y": 56.24, + "z": 1.05 + }, + "C12": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 113.38, + "y": 56.24, + "z": 1.05 + }, + "C2": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 23.38, + "y": 56.24, + "z": 1.05 + }, + "C3": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 32.38, + "y": 56.24, + "z": 1.05 + }, + "C4": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 41.38, + "y": 56.24, + "z": 1.05 + }, + "C5": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 50.38, + "y": 56.24, + "z": 1.05 + }, + "C6": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 59.38, + "y": 56.24, + "z": 1.05 + }, + "C7": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 68.38, + "y": 56.24, + "z": 1.05 + }, + "C8": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 77.38, + "y": 56.24, + "z": 1.05 + }, + "C9": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 86.38, + "y": 56.24, + "z": 1.05 + }, + "D1": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 14.38, + "y": 47.24, + "z": 1.05 + }, + "D10": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 95.38, + "y": 47.24, + "z": 1.05 + }, + "D11": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 104.38, + "y": 47.24, + "z": 1.05 + }, + "D12": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 113.38, + "y": 47.24, + "z": 1.05 + }, + "D2": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 23.38, + "y": 47.24, + "z": 1.05 + }, + "D3": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 32.38, + "y": 47.24, + "z": 1.05 + }, + "D4": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 41.38, + "y": 47.24, + "z": 1.05 + }, + "D5": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 50.38, + "y": 47.24, + "z": 1.05 + }, + "D6": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 59.38, + "y": 47.24, + "z": 1.05 + }, + "D7": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 68.38, + "y": 47.24, + "z": 1.05 + }, + "D8": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 77.38, + "y": 47.24, + "z": 1.05 + }, + "D9": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 86.38, + "y": 47.24, + "z": 1.05 + }, + "E1": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 14.38, + "y": 38.24, + "z": 1.05 + }, + "E10": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 95.38, + "y": 38.24, + "z": 1.05 + }, + "E11": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 104.38, + "y": 38.24, + "z": 1.05 + }, + "E12": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 113.38, + "y": 38.24, + "z": 1.05 + }, + "E2": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 23.38, + "y": 38.24, + "z": 1.05 + }, + "E3": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 32.38, + "y": 38.24, + "z": 1.05 + }, + "E4": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 41.38, + "y": 38.24, + "z": 1.05 + }, + "E5": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 50.38, + "y": 38.24, + "z": 1.05 + }, + "E6": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 59.38, + "y": 38.24, + "z": 1.05 + }, + "E7": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 68.38, + "y": 38.24, + "z": 1.05 + }, + "E8": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 77.38, + "y": 38.24, + "z": 1.05 + }, + "E9": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 86.38, + "y": 38.24, + "z": 1.05 + }, + "F1": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 14.38, + "y": 29.24, + "z": 1.05 + }, + "F10": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 95.38, + "y": 29.24, + "z": 1.05 + }, + "F11": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 104.38, + "y": 29.24, + "z": 1.05 + }, + "F12": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 113.38, + "y": 29.24, + "z": 1.05 + }, + "F2": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 23.38, + "y": 29.24, + "z": 1.05 + }, + "F3": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 32.38, + "y": 29.24, + "z": 1.05 + }, + "F4": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 41.38, + "y": 29.24, + "z": 1.05 + }, + "F5": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 50.38, + "y": 29.24, + "z": 1.05 + }, + "F6": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 59.38, + "y": 29.24, + "z": 1.05 + }, + "F7": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 68.38, + "y": 29.24, + "z": 1.05 + }, + "F8": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 77.38, + "y": 29.24, + "z": 1.05 + }, + "F9": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 86.38, + "y": 29.24, + "z": 1.05 + }, + "G1": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 14.38, + "y": 20.24, + "z": 1.05 + }, + "G10": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 95.38, + "y": 20.24, + "z": 1.05 + }, + "G11": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 104.38, + "y": 20.24, + "z": 1.05 + }, + "G12": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 113.38, + "y": 20.24, + "z": 1.05 + }, + "G2": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 23.38, + "y": 20.24, + "z": 1.05 + }, + "G3": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 32.38, + "y": 20.24, + "z": 1.05 + }, + "G4": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 41.38, + "y": 20.24, + "z": 1.05 + }, + "G5": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 50.38, + "y": 20.24, + "z": 1.05 + }, + "G6": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 59.38, + "y": 20.24, + "z": 1.05 + }, + "G7": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 68.38, + "y": 20.24, + "z": 1.05 + }, + "G8": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 77.38, + "y": 20.24, + "z": 1.05 + }, + "G9": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 86.38, + "y": 20.24, + "z": 1.05 + }, + "H1": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 14.38, + "y": 11.24, + "z": 1.05 + }, + "H10": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 95.38, + "y": 11.24, + "z": 1.05 + }, + "H11": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 104.38, + "y": 11.24, + "z": 1.05 + }, + "H12": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 113.38, + "y": 11.24, + "z": 1.05 + }, + "H2": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 23.38, + "y": 11.24, + "z": 1.05 + }, + "H3": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 32.38, + "y": 11.24, + "z": 1.05 + }, + "H4": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 41.38, + "y": 11.24, + "z": 1.05 + }, + "H5": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 50.38, + "y": 11.24, + "z": 1.05 + }, + "H6": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 59.38, + "y": 11.24, + "z": 1.05 + }, + "H7": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 68.38, + "y": 11.24, + "z": 1.05 + }, + "H8": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 77.38, + "y": 11.24, + "z": 1.05 + }, + "H9": { + "depth": 14.95, + "diameter": 5.5, + "shape": "circular", + "totalLiquidVolume": 200, + "x": 86.38, + "y": 11.24, + "z": 1.05 + } + } + }, + "labwareId": "UUID" + }, "startedAt": "TIMESTAMP", - "status": "failed" + "status": "succeeded" + }, + { + "commandType": "moveLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "4f2b8d555e3d80c7769b6d56d34bdac2", + "notes": [], + "params": { + "dropOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "labwareId": "UUID", + "newLocation": { + "labwareId": "UUID" + }, + "pickUpOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "strategy": "usingGripper" + }, + "result": {}, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "moveLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "6a4f94683ebc77002fe4ca6c072390c4", + "notes": [], + "params": { + "dropOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "labwareId": "UUID", + "newLocation": { + "slotName": "C2" + }, + "pickUpOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "strategy": "usingGripper" + }, + "result": {}, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "moveLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "8a1f9783f24db47c079c42b86f0d4bfd", + "notes": [], + "params": { + "dropOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "labwareId": "UUID", + "newLocation": { + "labwareId": "UUID" + }, + "pickUpOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "strategy": "usingGripper" + }, + "result": {}, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "moveLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "8e182d34c608b4545a72b00825d06b8b", + "notes": [], + "params": { + "dropOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "labwareId": "UUID", + "newLocation": { + "labwareId": "UUID" + }, + "pickUpOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "strategy": "usingGripper" + }, + "result": {}, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "moveLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "8dab37f748235b00361b88351aa7306c", + "notes": [], + "params": { + "dropOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "labwareId": "UUID", + "newLocation": { + "labwareId": "UUID" + }, + "pickUpOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "strategy": "usingGripper" + }, + "result": {}, + "startedAt": "TIMESTAMP", + "status": "succeeded" + }, + { + "commandType": "moveLabware", + "completedAt": "TIMESTAMP", + "createdAt": "TIMESTAMP", + "id": "UUID", + "key": "3b0794c23d1869d861403159d414e74e", + "notes": [], + "params": { + "dropOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "labwareId": "UUID", + "newLocation": { + "labwareId": "UUID" + }, + "pickUpOffset": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "strategy": "usingGripper" + }, + "result": {}, + "startedAt": "TIMESTAMP", + "status": "succeeded" } ], "config": { @@ -128,44 +2039,7 @@ "protocolType": "python" }, "createdAt": "TIMESTAMP", - "errors": [ - { - "createdAt": "TIMESTAMP", - "detail": "ProtocolCommandFailedError [line 60]: Error 4000 GENERAL_ERROR (ProtocolCommandFailedError): PythonException: ValueError: Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.", - "errorCode": "4000", - "errorInfo": {}, - "errorType": "ExceptionInProtocolError", - "id": "UUID", - "isDefined": false, - "wrappedErrors": [ - { - "createdAt": "TIMESTAMP", - "detail": "PythonException: ValueError: Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.", - "errorCode": "4000", - "errorInfo": {}, - "errorType": "ProtocolCommandFailedError", - "id": "UUID", - "isDefined": false, - "wrappedErrors": [ - { - "createdAt": "TIMESTAMP", - "detail": "ValueError: Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.", - "errorCode": "4000", - "errorInfo": { - "args": "('Labware Lid opentrons_tough_pcr_auto_sealing_lid may not be loaded on parent labware Opentrons Flex Deck Riser.',)", - "class": "ValueError", - "traceback": " File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_engine/execution/command_executor.py\", line N, in execute\n result = await command_impl.execute(\n\n File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_engine/commands/load_labware.py\", line N, in execute\n raise ValueError(\n" - }, - "errorType": "PythonException", - "id": "UUID", - "isDefined": false, - "wrappedErrors": [] - } - ] - } - ] - } - ], + "errors": [], "files": [ { "name": "Flex_S_v2_21_tc_lids_happy_path.py", @@ -180,6 +2054,38 @@ "location": { "slotName": "B2" } + }, + { + "definitionUri": "opentrons/opentrons_tough_pcr_auto_sealing_lid/1", + "id": "UUID", + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + } + }, + { + "definitionUri": "opentrons/opentrons_tough_pcr_auto_sealing_lid/1", + "id": "UUID", + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "labwareId": "UUID" + } + }, + { + "definitionUri": "opentrons/opentrons_tough_pcr_auto_sealing_lid/1", + "id": "UUID", + "loadName": "opentrons_tough_pcr_auto_sealing_lid", + "location": { + "slotName": "C2" + } + }, + { + "definitionUri": "opentrons/armadillo_96_wellplate_200ul_pcr_full_skirt/2", + "id": "UUID", + "loadName": "armadillo_96_wellplate_200ul_pcr_full_skirt", + "location": { + "moduleId": "UUID" + } } ], "liquidClasses": [], @@ -187,9 +2093,18 @@ "metadata": { "protocolName": "Opentrons Flex Deck Riser with TC Lids Test" }, - "modules": [], + "modules": [ + { + "id": "UUID", + "location": { + "slotName": "B1" + }, + "model": "thermocyclerModuleV2", + "serialNumber": "UUID" + } + ], "pipettes": [], - "result": "not-ok", + "result": "ok", "robotType": "OT-3 Standard", "runTimeParameters": [] } diff --git a/api/src/opentrons/legacy_commands/helpers.py b/api/src/opentrons/legacy_commands/helpers.py index 5b08bb1e436..488bba91cb1 100644 --- a/api/src/opentrons/legacy_commands/helpers.py +++ b/api/src/opentrons/legacy_commands/helpers.py @@ -78,3 +78,18 @@ def stringify_labware_movement_command( destination_text = _stringify_labware_movement_location(destination) gripper_text = " with gripper" if use_gripper else "" return f"Moving {source_labware_text} to {destination_text}{gripper_text}" + + +def stringify_lid_movement_command( + source: Union[ + DeckLocation, OffDeckType, Labware, ModuleContext, WasteChute, TrashBin + ], + destination: Union[ + DeckLocation, OffDeckType, Labware, ModuleContext, WasteChute, TrashBin + ], + use_gripper: bool, +) -> str: + source_labware_text = _stringify_labware_movement_location(source) + destination_text = _stringify_labware_movement_location(destination) + gripper_text = " with gripper" if use_gripper else "" + return f"Moving lid from {source_labware_text} to {destination_text}{gripper_text}" diff --git a/api/src/opentrons/protocol_api/core/engine/deck_conflict.py b/api/src/opentrons/protocol_api/core/engine/deck_conflict.py index ee724ea5ca3..c56602a498c 100644 --- a/api/src/opentrons/protocol_api/core/engine/deck_conflict.py +++ b/api/src/opentrons/protocol_api/core/engine/deck_conflict.py @@ -25,6 +25,7 @@ OnLabwareLocation, AddressableAreaLocation, OFF_DECK_LOCATION, + SYSTEM_LOCATION, ) from opentrons.protocol_engine.errors.exceptions import LabwareNotLoadedOnModuleError from opentrons.types import DeckSlotName, StagingSlotName, Point @@ -245,7 +246,10 @@ def _map_labware( # TODO(jbl 2023-06-08) check if we need to do any logic here or if this is correct return None - elif location_from_engine == OFF_DECK_LOCATION: + elif ( + location_from_engine == OFF_DECK_LOCATION + or location_from_engine == SYSTEM_LOCATION + ): # This labware is off-deck. Exclude it from conflict checking. # todo(mm, 2023-02-23): Move this logic into wrapped_deck_conflict. return None diff --git a/api/src/opentrons/protocol_api/core/engine/protocol.py b/api/src/opentrons/protocol_api/core/engine/protocol.py index ece431b0d1e..ce8449f70eb 100644 --- a/api/src/opentrons/protocol_api/core/engine/protocol.py +++ b/api/src/opentrons/protocol_api/core/engine/protocol.py @@ -47,6 +47,7 @@ from opentrons.protocol_engine.types import ( ModuleModel as ProtocolEngineModuleModel, OFF_DECK_LOCATION, + SYSTEM_LOCATION, LabwareLocation, NonStackedLocation, ) @@ -77,6 +78,7 @@ ) from .exceptions import InvalidModuleLocationError from . import load_labware_params, deck_conflict, overlap_versions +from opentrons.protocol_engine.resources import labware_validation if TYPE_CHECKING: from ...labware import Labware @@ -442,6 +444,203 @@ def move_labware( existing_module_ids=list(self._module_cores_by_id.keys()), ) + def move_lid( # noqa: C901 + self, + source_location: Union[DeckSlotName, StagingSlotName, LabwareCore], + new_location: Union[ + DeckSlotName, + StagingSlotName, + LabwareCore, + OffDeckType, + WasteChute, + TrashBin, + ], + use_gripper: bool, + pause_for_manual_move: bool, + pick_up_offset: Optional[Tuple[float, float, float]], + drop_offset: Optional[Tuple[float, float, float]], + ) -> LabwareCore | None: + """Move the given lid to a new location.""" + if use_gripper: + strategy = LabwareMovementStrategy.USING_GRIPPER + elif pause_for_manual_move: + strategy = LabwareMovementStrategy.MANUAL_MOVE_WITH_PAUSE + else: + strategy = LabwareMovementStrategy.MANUAL_MOVE_WITHOUT_PAUSE + + if isinstance(source_location, DeckSlotName) or isinstance( + source_location, StagingSlotName + ): + # Find the source labware at the provided deck slot + labware_in_slot = self._engine_client.state.labware.get_by_slot( + source_location + ) + if labware_in_slot is None: + raise LabwareNotLoadedOnLabwareError( + "Lid cannot be loaded on non-labware position." + ) + else: + labware = LabwareCore(labware_in_slot.id, self._engine_client) + else: + labware = source_location + + # if this is a labware stack, we need to find the labware at the top of the stack + if labware_validation.is_lid_stack(labware.load_name): + lid_id = self._engine_client.state.labware.get_highest_child_labware( + labware.labware_id + ) + # if this is a labware with a lid, we just need to find its lid_id + else: + lid = self._engine_client.state.labware.get_lid_by_labware_id( + labware.labware_id + ) + if lid is not None: + lid_id = lid.id + else: + raise ValueError("Cannot move a lid off of a labware with no lid.") + + _pick_up_offset = ( + LabwareOffsetVector( + x=pick_up_offset[0], y=pick_up_offset[1], z=pick_up_offset[2] + ) + if pick_up_offset + else None + ) + _drop_offset = ( + LabwareOffsetVector(x=drop_offset[0], y=drop_offset[1], z=drop_offset[2]) + if drop_offset + else None + ) + + create_new_lid_stack = False + + if isinstance(new_location, DeckSlotName) or isinstance( + new_location, StagingSlotName + ): + # Find the destination labware at the provided deck slot + destination_labware_in_slot = self._engine_client.state.labware.get_by_slot( + new_location + ) + if destination_labware_in_slot is None: + to_location = self._convert_labware_location(location=new_location) + # absolutely must make a new lid stack + create_new_lid_stack = True + else: + highest_child_location = ( + self._engine_client.state.labware.get_highest_child_labware( + destination_labware_in_slot.id + ) + ) + if labware_validation.validate_definition_is_adapter( + self._engine_client.state.labware.get_definition( + highest_child_location + ) + ): + # absolutely must make a new lid stack + create_new_lid_stack = True + + to_location = self._convert_labware_location( + location=LabwareCore(highest_child_location, self._engine_client) + ) + elif isinstance(new_location, LabwareCore): + highest_child_location = ( + self._engine_client.state.labware.get_highest_child_labware( + new_location.labware_id + ) + ) + if labware_validation.validate_definition_is_adapter( + self._engine_client.state.labware.get_definition(highest_child_location) + ): + # absolutely must make a new lid stack + create_new_lid_stack = True + to_location = self._convert_labware_location( + location=LabwareCore(highest_child_location, self._engine_client) + ) + else: + to_location = self._convert_labware_location(location=new_location) + + output_result = None + if create_new_lid_stack: + # Make a new lid stack object that is empty + result = self._engine_client.execute_command_without_recovery( + cmd.LoadLidStackParams( + location=SYSTEM_LOCATION, + loadName="empty", + version=1, + namespace="empty", + quantity=0, + ) + ) + + # Move the lid stack object from the SYSTEM_LOCATION space to the desired deck location + self._engine_client.execute_command( + cmd.MoveLabwareParams( + labwareId=result.stackLabwareId, + newLocation=to_location, + strategy=LabwareMovementStrategy.MANUAL_MOVE_WITHOUT_PAUSE, + pickUpOffset=None, + dropOffset=None, + ) + ) + + output_result = LabwareCore( + labware_id=result.stackLabwareId, engine_client=self._engine_client + ) + destination = self._convert_labware_location(location=output_result) + else: + destination = to_location + + self._engine_client.execute_command( + cmd.MoveLabwareParams( + labwareId=lid_id, + newLocation=destination, + strategy=strategy, + pickUpOffset=_pick_up_offset, + dropOffset=_drop_offset, + ) + ) + + # Handle leftover empty lid stack if there is one + if ( + labware_validation.is_lid_stack(labware.load_name) + and self._engine_client.state.labware.get_highest_child_labware( + labware_id=labware.labware_id + ) + == labware.labware_id + ): + # The originating lid stack is now empty, so we need to move it to the SYSTEM_LOCATION + self._engine_client.execute_command( + cmd.MoveLabwareParams( + labwareId=labware.labware_id, + newLocation=SYSTEM_LOCATION, + strategy=LabwareMovementStrategy.MANUAL_MOVE_WITHOUT_PAUSE, + pickUpOffset=None, + dropOffset=None, + ) + ) + + if strategy == LabwareMovementStrategy.USING_GRIPPER: + # Clear out last location since it is not relevant to pipetting + # and we only use last location for in-place pipetting commands + self.set_last_location(location=None, mount=Mount.EXTENSION) + + # FIXME(jbl, 2024-01-04) deck conflict after execution logic issue, read notes in load_labware for more info: + deck_conflict.check( + engine_state=self._engine_client.state, + new_labware_id=lid_id, + existing_disposal_locations=self._disposal_locations, + # TODO: We can now fetch these IDs from engine too. + # See comment in self.load_labware(). + existing_labware_ids=[ + labware_id + for labware_id in self._labware_cores_by_id + if labware_id != labware_id + ], + existing_module_ids=list(self._module_cores_by_id.keys()), + ) + + return output_result + def _resolve_module_hardware( self, serial_number: str, model: ModuleModel ) -> AbstractModule: @@ -734,6 +933,7 @@ def load_lid_stack( ) # FIXME(CHB, 2024-12-04) just like load labware and load adapter we have a validating after loading the object issue + assert load_result.definition is not None validation.ensure_definition_is_lid(load_result.definition) deck_conflict.check( diff --git a/api/src/opentrons/protocol_api/core/engine/stringify.py b/api/src/opentrons/protocol_api/core/engine/stringify.py index 78de37c5c5d..c70b7ecfc1e 100644 --- a/api/src/opentrons/protocol_api/core/engine/stringify.py +++ b/api/src/opentrons/protocol_api/core/engine/stringify.py @@ -50,6 +50,9 @@ def _labware_location_string( elif location == "offDeck": return "[off-deck]" + elif location == "systemLocation": + return "[systemLocation]" + def _labware_name(engine_client: SyncClient, labware_id: str) -> str: """Return the user-specified labware label, or fall back to the display name from the def.""" diff --git a/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py b/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py index 8adadbe1ecf..866b2e7bd12 100644 --- a/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py +++ b/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py @@ -307,6 +307,25 @@ def move_labware( """Move labware to new location.""" raise APIVersionError(api_element="Labware movement") + def move_lid( + self, + source_location: Union[DeckSlotName, StagingSlotName, LegacyLabwareCore], + new_location: Union[ + DeckSlotName, + StagingSlotName, + LegacyLabwareCore, + OffDeckType, + WasteChute, + TrashBin, + ], + use_gripper: bool, + pause_for_manual_move: bool, + pick_up_offset: Optional[Tuple[float, float, float]], + drop_offset: Optional[Tuple[float, float, float]], + ) -> LegacyLabwareCore | None: + """Move lid to new location.""" + raise APIVersionError(api_element="Lid movement") + def load_module( self, model: ModuleModel, diff --git a/api/src/opentrons/protocol_api/core/protocol.py b/api/src/opentrons/protocol_api/core/protocol.py index 27d41b921b0..3a35fdd824e 100644 --- a/api/src/opentrons/protocol_api/core/protocol.py +++ b/api/src/opentrons/protocol_api/core/protocol.py @@ -131,6 +131,25 @@ def move_labware( ) -> None: ... + @abstractmethod + def move_lid( + self, + source_location: Union[DeckSlotName, StagingSlotName, LabwareCoreType], + new_location: Union[ + DeckSlotName, + StagingSlotName, + LabwareCoreType, + OffDeckType, + WasteChute, + TrashBin, + ], + use_gripper: bool, + pause_for_manual_move: bool, + pick_up_offset: Optional[Tuple[float, float, float]], + drop_offset: Optional[Tuple[float, float, float]], + ) -> LabwareCoreType | None: + ... + @abstractmethod def load_module( self, diff --git a/api/src/opentrons/protocol_api/protocol_context.py b/api/src/opentrons/protocol_api/protocol_context.py index b9f96e4d536..84b42eefdae 100644 --- a/api/src/opentrons/protocol_api/protocol_context.py +++ b/api/src/opentrons/protocol_api/protocol_context.py @@ -24,7 +24,10 @@ AbsorbanceReaderModel, ) from opentrons.legacy_commands import protocol_commands as cmds, types as cmd_types -from opentrons.legacy_commands.helpers import stringify_labware_movement_command +from opentrons.legacy_commands.helpers import ( + stringify_labware_movement_command, + stringify_lid_movement_command, +) from opentrons.legacy_commands.publisher import ( CommandPublisher, publish, @@ -1441,6 +1444,113 @@ def load_lid_stack( ) return labware + @requires_version(2, 23) + def move_lid( + self, + source_location: Union[DeckLocation, Labware], + new_location: Union[DeckLocation, Labware, OffDeckType, WasteChute, TrashBin], + use_gripper: bool = False, + pick_up_offset: Optional[Mapping[str, float]] = None, + drop_offset: Optional[Mapping[str, float]] = None, + ) -> Labware | None: + """Move a lid from a valid source to a new location. Can return a Lid Stack if one is created. + + :param source_location: Where to take the lid from. This is either: + + * A deck slot like ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`. + * A labware or adapter that's already been loaded on the deck + with :py:meth:`load_labware` or :py:meth:`load_adapter`. + * A lid stack that's already been loaded on the deck with + with :py:meth:`load_lid_stack`. + + :param new_location: Where to move the lid to. This is either: + + * A deck slot like ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`. + * A hardware module that's already been loaded on the deck + with :py:meth:`load_module`. + * A labware or adapter that's already been loaded on the deck + with :py:meth:`load_labware` or :py:meth:`load_adapter`. + * The special constant :py:obj:`OFF_DECK`. + + :param use_gripper: Whether to use the Flex Gripper for this movement. + + * If ``True``, use the gripper to perform an automatic + movement. This will raise an error in an OT-2 protocol. + * If ``False``, pause protocol execution until the user + performs the movement. Protocol execution remains paused until + the user presses **Confirm and resume**. + + Gripper-only parameters: + + :param pick_up_offset: Optional x, y, z vector offset to use when picking up a lid. + :param drop_offset: Optional x, y, z vector offset to use when dropping off a lid. + + Before moving a lid to or from a labware in a hardware module, make sure that the + labware's current and new locations are accessible, i.e., open the Thermocycler lid + or open the Heater-Shaker's labware latch. + """ + source: Union[LabwareCore, DeckSlotName, StagingSlotName] + if isinstance(source_location, Labware): + source = source_location._core + else: + source = validation.ensure_and_convert_deck_slot( + source_location, self._api_version, self._core.robot_type + ) + + destination: Union[ + ModuleCore, + LabwareCore, + WasteChute, + OffDeckType, + DeckSlotName, + StagingSlotName, + TrashBin, + ] + if isinstance(new_location, Labware): + destination = new_location._core + elif isinstance(new_location, (OffDeckType, WasteChute, TrashBin)): + destination = new_location + else: + destination = validation.ensure_and_convert_deck_slot( + new_location, self._api_version, self._core.robot_type + ) + + _pick_up_offset = ( + validation.ensure_valid_labware_offset_vector(pick_up_offset) + if pick_up_offset + else None + ) + _drop_offset = ( + validation.ensure_valid_labware_offset_vector(drop_offset) + if drop_offset + else None + ) + with publish_context( + broker=self.broker, + command=cmds.move_labware( + # This needs to be called from protocol context and not the command for import loop reasons + text=stringify_lid_movement_command( + source_location, new_location, use_gripper + ) + ), + ): + result = self._core.move_lid( + source_location=source, + new_location=destination, + use_gripper=use_gripper, + pause_for_manual_move=True, + pick_up_offset=_pick_up_offset, + drop_offset=_drop_offset, + ) + if result is not None: + return Labware( + core=result, + api_version=self._api_version, + protocol_core=self._core, + core_map=self._core_map, + ) + return None + def _create_module_context( module_core: Union[ModuleCore, NonConnectedModuleCore], diff --git a/api/src/opentrons/protocol_engine/__init__.py b/api/src/opentrons/protocol_engine/__init__.py index 7efaef7199d..4b5b2a1f3c3 100644 --- a/api/src/opentrons/protocol_engine/__init__.py +++ b/api/src/opentrons/protocol_engine/__init__.py @@ -38,6 +38,7 @@ OnLabwareLocation, AddressableAreaLocation, OFF_DECK_LOCATION, + SYSTEM_LOCATION, Dimensions, EngineStatus, LabwareLocation, @@ -105,6 +106,7 @@ "OnLabwareLocation", "AddressableAreaLocation", "OFF_DECK_LOCATION", + "SYSTEM_LOCATION", "Dimensions", "EngineStatus", "LabwareLocation", diff --git a/api/src/opentrons/protocol_engine/commands/absorbance_reader/__init__.py b/api/src/opentrons/protocol_engine/commands/absorbance_reader/__init__.py index 2ed24ae23c3..c91c4977fe5 100644 --- a/api/src/opentrons/protocol_engine/commands/absorbance_reader/__init__.py +++ b/api/src/opentrons/protocol_engine/commands/absorbance_reader/__init__.py @@ -33,7 +33,6 @@ __all__ = [ - "MoveLidResult", # absorbanace_reader/closeLid "CloseLidCommandType", "CloseLidParams", diff --git a/api/src/opentrons/protocol_engine/commands/load_lid.py b/api/src/opentrons/protocol_engine/commands/load_lid.py index 4f2e49c7447..323b1ab4271 100644 --- a/api/src/opentrons/protocol_engine/commands/load_lid.py +++ b/api/src/opentrons/protocol_engine/commands/load_lid.py @@ -99,9 +99,9 @@ async def execute(self, params: LoadLidParams) -> SuccessData[LoadLidResult]: state_update = StateUpdate() # In the case of lids being loaded on top of other labware, set the parent labware's lid - state_update.set_lid( - parent_labware_id=params.location.labwareId, - lid_id=loaded_labware.labware_id, + state_update.set_lids( + parent_labware_ids=[params.location.labwareId], + lid_ids=[loaded_labware.labware_id], ) state_update.set_loaded_labware( diff --git a/api/src/opentrons/protocol_engine/commands/load_lid_stack.py b/api/src/opentrons/protocol_engine/commands/load_lid_stack.py index 7b430dfaf45..c4aebebdf28 100644 --- a/api/src/opentrons/protocol_engine/commands/load_lid_stack.py +++ b/api/src/opentrons/protocol_engine/commands/load_lid_stack.py @@ -1,7 +1,8 @@ """Load lid stack command request, result, and implementation models.""" from __future__ import annotations from pydantic import BaseModel, Field -from typing import TYPE_CHECKING, Optional, Type, List +from typing import TYPE_CHECKING, Optional, Type, List, Any +from pydantic.json_schema import SkipJsonSchema from typing_extensions import Literal from opentrons_shared_data.labware.labware_definition import LabwareDefinition @@ -10,6 +11,7 @@ from ..resources import fixture_validation, labware_validation from ..types import ( LabwareLocation, + SYSTEM_LOCATION, OnLabwareLocation, DeckSlotLocation, AddressableAreaLocation, @@ -21,11 +23,16 @@ if TYPE_CHECKING: from ..state.state import StateView - from ..execution import EquipmentHandler + from ..execution import LoadedLabwareData, EquipmentHandler LoadLidStackCommandType = Literal["loadLidStack"] + +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + _LID_STACK_PE_LABWARE = "protocol_engine_lid_stack_object" _LID_STACK_PE_NAMESPACE = "opentrons" _LID_STACK_PE_VERSION = 1 @@ -50,6 +57,18 @@ class LoadLidStackParams(BaseModel): ..., description="The lid labware definition version.", ) + stackLabwareId: str | SkipJsonSchema[None] = Field( + None, + description="An optional ID to assign to the lid stack labware object created." + "If None, an ID will be generated.", + json_schema_extra=_remove_default, + ) + labwareIds: List[str] | SkipJsonSchema[None] = Field( + None, + description="An optional list of IDs to assign to the lids in the stack." + "If None, an ID will be generated.", + json_schema_extra=_remove_default, + ) quantity: int = Field( ..., description="The quantity of lids to load.", @@ -67,7 +86,7 @@ class LoadLidStackResult(BaseModel): ..., description="A list of lid labware IDs to reference the lids in this stack by. The first ID is the bottom of the stack.", ) - definition: LabwareDefinition = Field( + definition: LabwareDefinition | None = Field( ..., description="The full definition data for this lid labware.", ) @@ -107,6 +126,10 @@ async def execute( self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration( params.location.slotName.id ) + if params.quantity <= 0 and params.location != SYSTEM_LOCATION: + raise ProtocolEngineError( + message="Lid Stack Labware Object with quantity 0 must be loaded onto System Location." + ) verified_location = self._state_view.geometry.ensure_location_not_occupied( params.location @@ -117,8 +140,9 @@ async def execute( namespace=_LID_STACK_PE_NAMESPACE, version=_LID_STACK_PE_VERSION, location=verified_location, - labware_id=None, + labware_id=params.stackLabwareId, ) + if not labware_validation.validate_definition_is_system( lid_stack_object.definition ): @@ -126,13 +150,29 @@ async def execute( message="Lid Stack Labware Object Labware Definition does not contain required allowed role 'system'." ) - loaded_lid_labwares = await self._equipment.load_lids( - load_name=params.loadName, - namespace=params.namespace, - version=params.version, - location=OnLabwareLocation(labwareId=lid_stack_object.labware_id), - quantity=params.quantity, - ) + loaded_lid_labwares: List[LoadedLabwareData] = [] + lid_labware_definition = None + + if params.quantity > 0: + loaded_lid_labwares = await self._equipment.load_lids( + load_name=params.loadName, + namespace=params.namespace, + version=params.version, + location=OnLabwareLocation(labwareId=lid_stack_object.labware_id), + quantity=params.quantity, + labware_ids=params.labwareIds, + ) + + lid_labware_definition = loaded_lid_labwares[0].definition + + if isinstance(verified_location, OnLabwareLocation): + self._state_view.labware.raise_if_labware_cannot_be_stacked( + top_labware_definition=loaded_lid_labwares[ + params.quantity - 1 + ].definition, + bottom_labware_id=verified_location.labwareId, + ) + loaded_lid_locations_by_id = {} load_location = OnLabwareLocation(labwareId=lid_stack_object.labware_id) for loaded_lid in loaded_lid_labwares: @@ -144,24 +184,15 @@ async def execute( stack_id=lid_stack_object.labware_id, stack_object_definition=lid_stack_object.definition, stack_location=verified_location, - labware_ids=list(loaded_lid_locations_by_id.keys()), - labware_definition=loaded_lid_labwares[0].definition, locations=loaded_lid_locations_by_id, + labware_definition=lid_labware_definition, ) - if isinstance(verified_location, OnLabwareLocation): - self._state_view.labware.raise_if_labware_cannot_be_stacked( - top_labware_definition=loaded_lid_labwares[ - params.quantity - 1 - ].definition, - bottom_labware_id=verified_location.labwareId, - ) - return SuccessData( public=LoadLidStackResult( stackLabwareId=lid_stack_object.labware_id, labwareIds=list(loaded_lid_locations_by_id.keys()), - definition=loaded_lid_labwares[0].definition, + definition=lid_labware_definition, location=params.location, ), state_update=state_update, diff --git a/api/src/opentrons/protocol_engine/commands/move_labware.py b/api/src/opentrons/protocol_engine/commands/move_labware.py index 8eb93ce9217..e5db5256dba 100644 --- a/api/src/opentrons/protocol_engine/commands/move_labware.py +++ b/api/src/opentrons/protocol_engine/commands/move_labware.py @@ -1,7 +1,7 @@ """Models and implementation for the ``moveLabware`` command.""" from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Type, Any +from typing import TYPE_CHECKING, Optional, Type, Any, List from pydantic.json_schema import SkipJsonSchema from pydantic import BaseModel, Field @@ -355,6 +355,40 @@ async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C new_offset_id=new_offset_id, ) + if labware_validation.validate_definition_is_lid( + definition=self._state_view.labware.get_definition(params.labwareId) + ): + parent_updates: List[str] = [] + lid_updates: List[str | None] = [] + # when moving a lid between locations we need to: + assert isinstance(current_labware.location, OnLabwareLocation) + if ( + self._state_view.labware.get_lid_by_labware_id( + current_labware.location.labwareId + ) + is not None + ): + # if the source location was a parent labware and not a lid stack or lid, update the parent labware lid ID to None (no more lid) + parent_updates.append(current_labware.location.labwareId) + lid_updates.append(None) + + # If we're moving to a non lid object, add to the setlids list of things to do + if isinstance( + available_new_location, OnLabwareLocation + ) and not labware_validation.validate_definition_is_lid( + self._state_view.labware.get_definition( + available_new_location.labwareId + ) + ): + parent_updates.append(available_new_location.labwareId) + lid_updates.append(params.labwareId) + # Add to setlids + if len(parent_updates) > 0: + state_update.set_lids( + parent_labware_ids=parent_updates, + lid_ids=lid_updates, + ) + return SuccessData( public=MoveLabwareResult(offsetId=new_offset_id), state_update=state_update, diff --git a/api/src/opentrons/protocol_engine/execution/equipment.py b/api/src/opentrons/protocol_engine/execution/equipment.py index 8b23c3a0173..b16e26cd6ae 100644 --- a/api/src/opentrons/protocol_engine/execution/equipment.py +++ b/api/src/opentrons/protocol_engine/execution/equipment.py @@ -388,6 +388,7 @@ async def load_lids( version: int, location: LabwareLocation, quantity: int, + labware_ids: Optional[List[str]] = None, ) -> List[LoadedLabwareData]: """Load one or many lid labware by assigning an identifier and pulling required data. @@ -396,6 +397,7 @@ async def load_lids( namespace: The lid labware's namespace. version: The lid labware's version. location: The deck location at which lid(s) will be placed. + quantity: The quantity of lids to load at a location. labware_ids: An optional list of identifiers to assign the labware. If None, an identifier will be generated. @@ -438,10 +440,20 @@ async def load_lids( ) load_labware_data_list = [] - for i in range(quantity): + ids = [] + if labware_ids is not None: + if len(labware_ids) < quantity: + raise ValueError( + f"Requested quantity {quantity} is greater than the number of labware lid IDs provided for {load_name}." + ) + ids = labware_ids + else: + for i in range(quantity): + ids.append(self._model_utils.generate_id()) + for id in ids: load_labware_data_list.append( LoadedLabwareData( - labware_id=self._model_utils.generate_id(), + labware_id=id, definition=definition, offsetId=None, ) diff --git a/api/src/opentrons/protocol_engine/resources/labware_validation.py b/api/src/opentrons/protocol_engine/resources/labware_validation.py index 924d0fc0a24..17ee211cb66 100644 --- a/api/src/opentrons/protocol_engine/resources/labware_validation.py +++ b/api/src/opentrons/protocol_engine/resources/labware_validation.py @@ -16,6 +16,11 @@ def is_absorbance_reader_lid(load_name: str) -> bool: return load_name == "opentrons_flex_lid_absorbance_plate_reader_module" +def is_lid_stack(load_name: str) -> bool: + """Check if a labware object is a system lid stack object.""" + return load_name == "protocol_engine_lid_stack_object" + + def validate_definition_is_labware(definition: LabwareDefinition) -> bool: """Validate that one of the definition's allowed roles is `labware`. diff --git a/api/src/opentrons/protocol_engine/slot_standardization.py b/api/src/opentrons/protocol_engine/slot_standardization.py index d940517eebe..5943febc820 100644 --- a/api/src/opentrons/protocol_engine/slot_standardization.py +++ b/api/src/opentrons/protocol_engine/slot_standardization.py @@ -22,6 +22,7 @@ from . import commands from .types import ( OFF_DECK_LOCATION, + SYSTEM_LOCATION, DeckSlotLocation, LabwareLocation, AddressableAreaLocation, @@ -128,6 +129,7 @@ def _standardize_labware_location( original, (ModuleLocation, OnLabwareLocation, AddressableAreaLocation) ) or original == OFF_DECK_LOCATION + or original == SYSTEM_LOCATION ): return original diff --git a/api/src/opentrons/protocol_engine/state/geometry.py b/api/src/opentrons/protocol_engine/state/geometry.py index 7c725d88c62..0344ef321fc 100644 --- a/api/src/opentrons/protocol_engine/state/geometry.py +++ b/api/src/opentrons/protocol_engine/state/geometry.py @@ -26,6 +26,7 @@ from ..resources import fixture_validation, labware_validation from ..types import ( OFF_DECK_LOCATION, + SYSTEM_LOCATION, LoadedLabware, LoadedModule, WellLocation, @@ -388,7 +389,7 @@ def _get_calibrated_module_offset( elif isinstance(location, OnLabwareLocation): labware_data = self._labware.get(location.labwareId) return self._get_calibrated_module_offset(labware_data.location) - elif location == OFF_DECK_LOCATION: + elif location == OFF_DECK_LOCATION or location == SYSTEM_LOCATION: raise errors.LabwareNotOnDeckError( "Labware does not have a slot or module associated with it" " since it is no longer on the deck." diff --git a/api/src/opentrons/protocol_engine/state/labware.py b/api/src/opentrons/protocol_engine/state/labware.py index 34e69a57a3b..c4a2afcd62f 100644 --- a/api/src/opentrons/protocol_engine/state/labware.py +++ b/api/src/opentrons/protocol_engine/state/labware.py @@ -50,6 +50,7 @@ LabwareMovementOffsetData, OnDeckLabwareLocation, OFF_DECK_LOCATION, + SYSTEM_LOCATION, ) from ..actions import ( Action, @@ -248,7 +249,11 @@ def _add_loaded_lid_stack(self, state_update: update_types.StateUpdate) -> None: ) # Add the Lids on top of the stack object - for i in range(len(loaded_lid_stack_update.labware_ids)): + for labware_id in loaded_lid_stack_update.new_locations_by_id: + if loaded_lid_stack_update.definition is None: + raise ValueError( + "Lid Stack Labware Definition cannot be None when multiple lids are loaded." + ) definition_uri = uri_from_details( namespace=loaded_lid_stack_update.definition.namespace, load_name=loaded_lid_stack_update.definition.parameters.loadName, @@ -259,14 +264,10 @@ def _add_loaded_lid_stack(self, state_update: update_types.StateUpdate) -> None: definition_uri ] = loaded_lid_stack_update.definition - location = loaded_lid_stack_update.new_locations_by_id[ - loaded_lid_stack_update.labware_ids[i] - ] + location = loaded_lid_stack_update.new_locations_by_id[labware_id] - self._state.labware_by_id[ - loaded_lid_stack_update.labware_ids[i] - ] = LoadedLabware.construct( - id=loaded_lid_stack_update.labware_ids[i], + self._state.labware_by_id[labware_id] = LoadedLabware.construct( + id=labware_id, location=location, loadName=loaded_lid_stack_update.definition.parameters.loadName, definitionUri=definition_uri, @@ -277,9 +278,10 @@ def _add_loaded_lid_stack(self, state_update: update_types.StateUpdate) -> None: def _set_labware_lid(self, state_update: update_types.StateUpdate) -> None: labware_lid_update = state_update.labware_lid if labware_lid_update != update_types.NO_CHANGE: - parent_labware_id = labware_lid_update.parent_labware_id - lid_id = labware_lid_update.lid_id - self._state.labware_by_id[parent_labware_id].lid_id = lid_id + parent_labware_ids = labware_lid_update.parent_labware_ids + for i in range(len(parent_labware_ids)): + lid_id = labware_lid_update.lid_ids[i] + self._state.labware_by_id[parent_labware_ids[i]].lid_id = lid_id def _set_labware_location(self, state_update: update_types.StateUpdate) -> None: labware_location_update = state_update.labware_location @@ -462,6 +464,16 @@ def get_parent_location(self, labware_id: str) -> NonStackedLocation: return self.get_parent_location(parent.labwareId) return parent + def get_highest_child_labware(self, labware_id: str) -> str: + """Get labware's highest child labware returning the labware ID.""" + for labware in self._state.labware_by_id.values(): + if ( + isinstance(labware.location, OnLabwareLocation) + and labware.location.labwareId == labware_id + ): + return self.get_highest_child_labware(labware_id=labware.id) + return labware_id + def get_labware_stack( self, labware_stack: List[LoadedLabware] ) -> List[LoadedLabware]: @@ -472,6 +484,22 @@ def get_labware_stack( return self.get_labware_stack(labware_stack) return labware_stack + def get_lid_by_labware_id(self, labware_id: str) -> LoadedLabware | None: + """Get the Lid Labware that is currently on top of a given labware, if there is one.""" + lid_id = self._state.labware_by_id[labware_id].lid_id + if lid_id: + return self._state.labware_by_id[lid_id] + else: + return None + + def get_labware_by_lid_id(self, lid_id: str) -> LoadedLabware | None: + """Get the labware that is currently covered by a given lid, if there is one.""" + loaded_labware = list(self._state.labware_by_id.values()) + for labware in loaded_labware: + if labware.lid_id == lid_id: + return labware + return None + def get_all(self) -> List[LoadedLabware]: """Get a list of all labware entries in state.""" return list(self._state.labware_by_id.values()) @@ -871,7 +899,9 @@ def raise_if_labware_inaccessible_by_pipette(self, labware_id: str) -> None: f"Cannot move pipette to {labware.loadName}," f" labware is on staging slot {labware_location.addressableAreaName}" ) - elif labware_location == OFF_DECK_LOCATION: + elif ( + labware_location == OFF_DECK_LOCATION or labware_location == SYSTEM_LOCATION + ): raise errors.LocationNotAccessibleByPipetteError( f"Cannot move pipette to {labware.loadName}, labware is off-deck." ) @@ -939,7 +969,7 @@ def raise_if_labware_cannot_be_stacked( # noqa: C901 for lw in labware_stack: if not labware_validation.validate_definition_is_adapter( self.get_definition(lw.id) - ): + ) and not labware_validation.is_lid_stack(self.get_load_name(lw.id)): stack_without_adapters.append(lw) if len(stack_without_adapters) >= self.get_labware_stacking_maximum( top_labware_definition diff --git a/api/src/opentrons/protocol_engine/state/update_types.py b/api/src/opentrons/protocol_engine/state/update_types.py index a8075b2f883..1bd15979223 100644 --- a/api/src/opentrons/protocol_engine/state/update_types.py +++ b/api/src/opentrons/protocol_engine/state/update_types.py @@ -135,13 +135,10 @@ class LoadedLidStackUpdate: stack_location: LabwareLocation "The initial location of the Lid Stack Object." - labware_ids: typing.List[str] - """The unique IDs of the new lids.""" - new_locations_by_id: typing.Dict[str, OnLabwareLocation] """Each lid's initial location keyed by Labware ID.""" - definition: LabwareDefinition + definition: LabwareDefinition | None "The Labware Definition of the Lid Labware(s) loaded." @@ -149,10 +146,10 @@ class LoadedLidStackUpdate: class LabwareLidUpdate: """An update that identifies a lid on a given parent labware.""" - parent_labware_id: str - """The unique ID of the parent labware.""" + parent_labware_ids: typing.List[str] + """The unique IDs of the parent labwares.""" - lid_id: str + lid_ids: typing.List[str | None] """The unique IDs of the new lids.""" @@ -525,9 +522,8 @@ def set_loaded_lid_stack( stack_id: str, stack_object_definition: LabwareDefinition, stack_location: LabwareLocation, - labware_definition: LabwareDefinition, - labware_ids: typing.List[str], locations: typing.Dict[str, OnLabwareLocation], + labware_definition: LabwareDefinition | None, ) -> Self: """Add a new lid stack to state. See `LoadedLidStackUpdate`.""" self.loaded_lid_stack = LoadedLidStackUpdate( @@ -535,20 +531,19 @@ def set_loaded_lid_stack( stack_object_definition=stack_object_definition, stack_location=stack_location, definition=labware_definition, - labware_ids=labware_ids, new_locations_by_id=locations, ) return self - def set_lid( + def set_lids( self: Self, - parent_labware_id: str, - lid_id: str, + parent_labware_ids: typing.List[str], + lid_ids: typing.List[str | None], ) -> Self: """Update the labware parent of a loaded or moved lid. See `LabwareLidUpdate`.""" self.labware_lid = LabwareLidUpdate( - parent_labware_id=parent_labware_id, - lid_id=lid_id, + parent_labware_ids=parent_labware_ids, + lid_ids=lid_ids, ) return self diff --git a/api/src/opentrons/protocol_engine/types.py b/api/src/opentrons/protocol_engine/types.py index 316c701eb20..1914834fea9 100644 --- a/api/src/opentrons/protocol_engine/types.py +++ b/api/src/opentrons/protocol_engine/types.py @@ -180,13 +180,16 @@ class OnLabwareLocation(BaseModel): _OffDeckLocationType = Literal["offDeck"] +_SystemLocationType = Literal["systemLocation"] OFF_DECK_LOCATION: _OffDeckLocationType = "offDeck" +SYSTEM_LOCATION: _SystemLocationType = "systemLocation" LabwareLocation = Union[ DeckSlotLocation, ModuleLocation, OnLabwareLocation, _OffDeckLocationType, + _SystemLocationType, AddressableAreaLocation, ] """Union of all locations where it's legal to keep a labware.""" @@ -196,7 +199,11 @@ class OnLabwareLocation(BaseModel): ] NonStackedLocation = Union[ - DeckSlotLocation, AddressableAreaLocation, ModuleLocation, _OffDeckLocationType + DeckSlotLocation, + AddressableAreaLocation, + ModuleLocation, + _OffDeckLocationType, + _SystemLocationType, ] """Union of all locations where it's legal to keep a labware that can't be stacked on another labware""" diff --git a/api/tests/opentrons/protocol_api/test_protocol_context.py b/api/tests/opentrons/protocol_api/test_protocol_context.py index 80728b7820c..28ac0c014e0 100644 --- a/api/tests/opentrons/protocol_api/test_protocol_context.py +++ b/api/tests/opentrons/protocol_api/test_protocol_context.py @@ -782,7 +782,7 @@ def test_load_labware_with_lid( load_name="lowercase_lid", location=mock_labware_core, namespace="some_namespace", - version=None, + version=1337, ) ).then_return(mock_lid_core) @@ -796,7 +796,7 @@ def test_load_labware_with_lid( label="some_display_name", namespace="some_namespace", version=1337, - lid="UPPERCASE_LID", + lid="lowercase_lid", ) assert isinstance(result, Labware) diff --git a/api/tests/opentrons/protocol_engine/state/test_labware_view_old.py b/api/tests/opentrons/protocol_engine/state/test_labware_view_old.py index c657ec2a7ae..770f24f3e7f 100644 --- a/api/tests/opentrons/protocol_engine/state/test_labware_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_labware_view_old.py @@ -1389,7 +1389,8 @@ def test_raise_if_labware_cannot_be_stacked_on_labware_on_adapter() -> None: }, definitions_by_uri={ "def-uri-1": LabwareDefinition.model_construct( # type: ignore[call-arg] - allowedRoles=[LabwareRole.labware] + allowedRoles=[LabwareRole.labware], + parameters=Parameters.model_construct(loadName="test"), # type: ignore[call-arg] ), "def-uri-2": LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=[LabwareRole.adapter] diff --git a/app/src/molecules/PythonLabwareOffsetSnippet/createSnippet.ts b/app/src/molecules/PythonLabwareOffsetSnippet/createSnippet.ts index 79a5a70fad2..38ff2da4771 100644 --- a/app/src/molecules/PythonLabwareOffsetSnippet/createSnippet.ts +++ b/app/src/molecules/PythonLabwareOffsetSnippet/createSnippet.ts @@ -36,7 +36,11 @@ export function createSnippet( const { loadName } = labwareDefinitions[ loadedLabware.definitionUri ].parameters - if (command.params.location === 'offDeck') { + + if ( + command.params.location === 'offDeck' || + command.params.location === 'systemLocation' + ) { loadStatement = `labware_${labwareCount} = protocol.load_labware("${String( loadName )}", location="offDeck")` diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/LabwareListItem.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/LabwareListItem.tsx index f31a3bcf28d..2a09e740ca7 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/LabwareListItem.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/LabwareListItem.tsx @@ -191,6 +191,7 @@ export function LabwareListItem( const matchingHeaterShaker = attachedModuleInfo != null && initialLocation !== 'offDeck' && + initialLocation !== 'systemLocation' && 'moduleId' in initialLocation && attachedModuleInfo[initialLocation.moduleId] != null ? attachedModuleInfo[initialLocation.moduleId].attachedModuleMatch diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/SetupLabwareList.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/SetupLabwareList.tsx index b71c84da0f8..ba5b321380e 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/SetupLabwareList.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/SetupLabwareList.tsx @@ -57,6 +57,7 @@ export function SetupLabwareList( const labwareOnAdapter = allItems.find( item => labwareItem.initialLocation !== 'offDeck' && + labwareItem.initialLocation !== 'systemLocation' && 'labwareId' in labwareItem.initialLocation && item.labwareId === labwareItem.initialLocation.labwareId ) diff --git a/app/src/organisms/Desktop/Devices/RunPreview/index.tsx b/app/src/organisms/Desktop/Devices/RunPreview/index.tsx index e0846a9a790..525fa8ecf8f 100644 --- a/app/src/organisms/Desktop/Devices/RunPreview/index.tsx +++ b/app/src/organisms/Desktop/Devices/RunPreview/index.tsx @@ -56,6 +56,7 @@ export const RunPreviewComponent = ( ): JSX.Element | null => { const { t } = useTranslation(['run_details', 'protocol_setup']) const robotSideAnalysis = useMostRecentCompletedAnalysis(runId) + console.log(robotSideAnalysis) const runStatus = useRunStatus(runId) const { data: runRecord } = useNotifyRunQuery(runId) const isRunTerminal = diff --git a/app/src/organisms/ErrorRecoveryFlows/hooks/useDeckMapUtils.ts b/app/src/organisms/ErrorRecoveryFlows/hooks/useDeckMapUtils.ts index 923a62472bd..1c61644e482 100644 --- a/app/src/organisms/ErrorRecoveryFlows/hooks/useDeckMapUtils.ts +++ b/app/src/organisms/ErrorRecoveryFlows/hooks/useDeckMapUtils.ts @@ -415,7 +415,11 @@ export function getSlotNameAndLwLocFrom( robotType: FLEX_ROBOT_TYPE, })?.slotName ?? null - if (location == null || location === 'offDeck') { + if ( + location == null || + location === 'offDeck' || + location === 'systemLocation' + ) { return [null, null] } else if ('moduleId' in location) { if (excludeModules) { diff --git a/app/src/organisms/InterventionModal/MoveLabwareInterventionContent.tsx b/app/src/organisms/InterventionModal/MoveLabwareInterventionContent.tsx index 9220cf2b447..87f65370746 100644 --- a/app/src/organisms/InterventionModal/MoveLabwareInterventionContent.tsx +++ b/app/src/organisms/InterventionModal/MoveLabwareInterventionContent.tsx @@ -256,7 +256,7 @@ function LabwareDisplayLocation( const { t } = useTranslation('protocol_command_text') const { protocolData, location, robotType } = props let displayLocation: string = '' - if (location === 'offDeck') { + if (location === 'offDeck' || location === 'systemLocation') { // TODO(BC, 08/28/23): remove this string cast after update i18next to >23 (see https://www.i18next.com/overview/typescript#argument-of-type-defaulttfuncreturn-is-not-assignable-to-parameter-of-type-xyz) displayLocation = String(t('offdeck')) } else if ('slotName' in location) { @@ -291,7 +291,10 @@ function LabwareDisplayLocation( ) if (adapter == null) { console.warn('labware is located on an unknown adapter') - } else if (adapter.location === 'offDeck') { + } else if ( + adapter.location === 'offDeck' || + adapter.location === 'systemLocation' + ) { displayLocation = t('off_deck') } else if ('slotName' in adapter.location) { displayLocation = adapter.location.slotName diff --git a/app/src/organisms/InterventionModal/utils/getRunLabwareRenderInfo.ts b/app/src/organisms/InterventionModal/utils/getRunLabwareRenderInfo.ts index 1634e12cc2b..c8c0f173c84 100644 --- a/app/src/organisms/InterventionModal/utils/getRunLabwareRenderInfo.ts +++ b/app/src/organisms/InterventionModal/utils/getRunLabwareRenderInfo.ts @@ -38,7 +38,7 @@ export function getRunLabwareRenderInfo( return acc } - if (location !== 'offDeck') { + if (location !== 'offDeck' && location !== 'systemLocation') { const slotName = 'addressableAreaName' in location ? location.addressableAreaName diff --git a/app/src/organisms/LegacyApplyHistoricOffsets/hooks/getLabwareLocationCombos.ts b/app/src/organisms/LegacyApplyHistoricOffsets/hooks/getLabwareLocationCombos.ts index 433f7e37b2c..4dc49c5ca23 100644 --- a/app/src/organisms/LegacyApplyHistoricOffsets/hooks/getLabwareLocationCombos.ts +++ b/app/src/organisms/LegacyApplyHistoricOffsets/hooks/getLabwareLocationCombos.ts @@ -32,7 +32,10 @@ export function getLabwareLocationCombos( ) return acc const definitionUri = getLabwareDefURI(command.result.definition) - if (command.params.location === 'offDeck') { + if ( + command.params.location === 'offDeck' || + command.params.location === 'systemLocation' + ) { return acc } else if ('moduleId' in command.params.location) { const { moduleId } = command.params.location @@ -85,7 +88,10 @@ export function getLabwareLocationCombos( ) return acc } - if (command.params.newLocation === 'offDeck') { + if ( + command.params.newLocation === 'offDeck' || + command.params.newLocation === 'systemLocation' + ) { return acc } else if ('moduleId' in command.params.newLocation) { const modLocation = resolveModuleLocation( @@ -195,7 +201,10 @@ function resolveAdapterLocation( let moduleIdUnderAdapter let adapterOffsetLocation: LabwareOffsetLocation | null = null - if (labwareEntity.location === 'offDeck') { + if ( + labwareEntity.location === 'offDeck' || + labwareEntity.location === 'systemLocation' + ) { return { adapterOffsetLocation: null } // can't have adapter on top of an adapter } else if ('labwareId' in labwareEntity.location) { diff --git a/app/src/organisms/LegacyLabwarePositionCheck/utils/labware.ts b/app/src/organisms/LegacyLabwarePositionCheck/utils/labware.ts index c8dd594ccb7..ed6c0efcdc9 100644 --- a/app/src/organisms/LegacyLabwarePositionCheck/utils/labware.ts +++ b/app/src/organisms/LegacyLabwarePositionCheck/utils/labware.ts @@ -67,7 +67,10 @@ export const getTiprackIdsInOrder = ( ...tipRackLocations.map(loc => ({ definition: labwareDef, labwareId: labwareId, - slot: loc !== 'offDeck' && 'slotName' in loc ? loc.slotName : '', + slot: + loc !== 'offDeck' && loc !== 'systemLocation' && 'slotName' in loc + ? loc.slotName + : '', })), ] } @@ -125,7 +128,10 @@ export const getAllTipracksIdsThatPipetteUsesInOrder = ( ...tipRackLocations.map(loc => ({ labwareId: tipRackId, definition, - slot: loc !== 'offDeck' && 'slotName' in loc ? loc.slotName : '', + slot: + loc !== 'offDeck' && loc !== 'systemLocation' && 'slotName' in loc + ? loc.slotName + : '', })), ] }, []) @@ -164,7 +170,7 @@ export const getLabwareIdsInOrder = ( ...acc, ...labwareLocations.reduce((innerAcc, loc) => { let slot = '' - if (loc === 'offDeck') { + if (loc === 'offDeck' || loc === 'systemLocation') { slot = 'offDeck' } else if ('moduleId' in loc) { slot = getModuleInitialLoadInfo(loc.moduleId, commands).location @@ -176,7 +182,10 @@ export const getLabwareIdsInOrder = ( command.result?.labwareId === loc.labwareId ) const adapterLocation = matchingAdapter?.params.location - if (adapterLocation === 'offDeck') { + if ( + adapterLocation === 'offDeck' || + adapterLocation === 'systemLocation' + ) { slot = 'offDeck' } else if ( adapterLocation != null && diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/SingleLabwareModal.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/SingleLabwareModal.tsx index 91ccd682acf..6a19f762b24 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/SingleLabwareModal.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/SingleLabwareModal.tsx @@ -87,6 +87,7 @@ export const SingleLabwareModal = ( {selectedLabware.nickName} {selectedLabwareLocation != null && selectedLabwareLocation !== 'offDeck' && + selectedLabwareLocation !== 'systemLocation' && 'labwareId' in selectedLabwareLocation ? t('on_adapter', { adapterName: mostRecentAnalysis?.labware.find( diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/index.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/index.tsx index 19875b2336e..e98a48924e7 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/index.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/index.tsx @@ -231,6 +231,7 @@ export function ProtocolSetupLabware({ const labwareOnAdapter = onDeckItems.find( item => labware.initialLocation !== 'offDeck' && + labware.initialLocation !== 'systemLocation' && 'labwareId' in labware.initialLocation && item.labwareId === labware.initialLocation.labwareId ) @@ -451,6 +452,7 @@ function RowLabware({ const matchedModule = initialLocation !== 'offDeck' && + initialLocation !== 'systemLocation' && 'moduleId' in initialLocation && attachedProtocolModules.length > 0 ? attachedProtocolModules.find( @@ -467,7 +469,7 @@ function RowLabware({ let slotName: string = slot let location: JSX.Element = - if (initialLocation === 'offDeck') { + if (initialLocation === 'offDeck' || initialLocation === 'systemLocation') { location = ( ) diff --git a/app/src/transformations/analysis/getLabwareOffsetLocation.ts b/app/src/transformations/analysis/getLabwareOffsetLocation.ts index 9a7ccd58841..526c3f780cb 100644 --- a/app/src/transformations/analysis/getLabwareOffsetLocation.ts +++ b/app/src/transformations/analysis/getLabwareOffsetLocation.ts @@ -20,7 +20,7 @@ export const getLabwareOffsetLocation = ( ): LabwareOffsetLocation | null => { const labwareLocation = getLabwareLocation(labwareId, commands) - if (labwareLocation === 'offDeck') { + if (labwareLocation === 'offDeck' || labwareLocation === 'systemLocation') { return null } else if ('moduleId' in labwareLocation) { const module = modules.find( @@ -34,7 +34,11 @@ export const getLabwareOffsetLocation = ( return { slotName, moduleModel } } else if ('labwareId' in labwareLocation) { const adapter = labware.find(lw => lw.id === labwareLocation.labwareId) - if (adapter == null || adapter.location === 'offDeck') { + if ( + adapter == null || + adapter.location === 'offDeck' || + adapter.location === 'systemLocation' + ) { return null } else if ('slotName' in adapter.location) { return { diff --git a/app/src/transformations/analysis/getLabwareRenderInfo.ts b/app/src/transformations/analysis/getLabwareRenderInfo.ts index dc57ed379e6..3cfa0491d42 100644 --- a/app/src/transformations/analysis/getLabwareRenderInfo.ts +++ b/app/src/transformations/analysis/getLabwareRenderInfo.ts @@ -34,6 +34,7 @@ export const getLabwareRenderInfo = ( const labwareDef = command.result?.definition if ( location === 'offDeck' || + location === 'systemLocation' || 'moduleId' in location || 'labwareId' in location ) diff --git a/app/src/transformations/commands/transformations/getLabwareSetupItemGroups.ts b/app/src/transformations/commands/transformations/getLabwareSetupItemGroups.ts index 10e255470ec..39c4eb747f7 100644 --- a/app/src/transformations/commands/transformations/getLabwareSetupItemGroups.ts +++ b/app/src/transformations/commands/transformations/getLabwareSetupItemGroups.ts @@ -38,7 +38,11 @@ export function getLabwareSetupItemGroups( if (definition == null) return acc let moduleModel = null let moduleLocation = null - if (location !== 'offDeck' && 'moduleId' in location) { + if ( + location !== 'offDeck' && + location !== 'systemLocation' && + 'moduleId' in location + ) { const loadModuleCommand = commands.find( (c): c is LoadModuleRunTimeCommand => c.commandType === 'loadModule' && diff --git a/app/src/transformations/commands/transformations/getLocationInfoNames.ts b/app/src/transformations/commands/transformations/getLocationInfoNames.ts index e87f5d68ba8..009b0719061 100644 --- a/app/src/transformations/commands/transformations/getLocationInfoNames.ts +++ b/app/src/transformations/commands/transformations/getLocationInfoNames.ts @@ -53,7 +53,7 @@ export function getLocationInfoNames( loadLabwareCommands ) - if (labwareLocation === 'offDeck') { + if (labwareLocation === 'offDeck' || labwareLocation === 'systemLocation') { return { slotName: 'Off deck', labwareName, labwareQuantity } } else if ('slotName' in labwareLocation) { return { slotName: labwareLocation.slotName, labwareName, labwareQuantity } @@ -91,6 +91,7 @@ export function getLocationInfoNames( return { slotName: '', labwareName: labwareName, labwareQuantity } } else if ( loadedAdapterCommand?.params.location !== 'offDeck' && + loadedAdapterCommand?.params.location !== 'systemLocation' && 'slotName' in loadedAdapterCommand?.params.location ) { return { @@ -104,6 +105,7 @@ export function getLocationInfoNames( } } else if ( loadedAdapterCommand?.params.location !== 'offDeck' && + loadedAdapterCommand?.params.location !== 'systemLocation' && 'addressableAreaName' in loadedAdapterCommand?.params.location ) { return { @@ -117,6 +119,7 @@ export function getLocationInfoNames( } } else if ( loadedAdapterCommand?.params.location !== 'offDeck' && + loadedAdapterCommand?.params.location !== 'systemLocation' && 'moduleId' in loadedAdapterCommand?.params.location ) { const moduleId = loadedAdapterCommand?.params.location.moduleId diff --git a/app/src/transformations/commands/transformations/getNestedLabwareInfo.ts b/app/src/transformations/commands/transformations/getNestedLabwareInfo.ts index cafc814f593..3dff0e51cf8 100644 --- a/app/src/transformations/commands/transformations/getNestedLabwareInfo.ts +++ b/app/src/transformations/commands/transformations/getNestedLabwareInfo.ts @@ -21,13 +21,17 @@ export function getNestedLabwareInfo( (command): command is LoadLabwareRunTimeCommand => command.commandType === 'loadLabware' && command.params.location !== 'offDeck' && + command.params.location !== 'systemLocation' && 'labwareId' in command.params.location && command.params.location.labwareId === labwareSetupItem.labwareId ) if (nestedLabware == null) return null let sharedSlotId: string = '' - if (labwareSetupItem.initialLocation !== 'offDeck') { + if ( + labwareSetupItem.initialLocation !== 'offDeck' && + labwareSetupItem.initialLocation !== 'systemLocation' + ) { const adapterLocation = labwareSetupItem.initialLocation if ('slotName' in adapterLocation) { sharedSlotId = adapterLocation.slotName diff --git a/components/src/hardware-sim/BaseDeck/BaseDeck.tsx b/components/src/hardware-sim/BaseDeck/BaseDeck.tsx index c8f22175952..e180cccca8a 100644 --- a/components/src/hardware-sim/BaseDeck/BaseDeck.tsx +++ b/components/src/hardware-sim/BaseDeck/BaseDeck.tsx @@ -305,6 +305,7 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element { }) => { if ( labwareLocation === 'offDeck' || + labwareLocation === 'systemLocation' || !('slotName' in labwareLocation) || // for legacy protocols that list fixed trash as a labware, do not render definition.parameters.loadName === @@ -380,6 +381,7 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element { ({ labwareLocation, definition, stacked = false }) => { if ( labwareLocation === 'offDeck' || + labwareLocation === 'systemLocation' || !('slotName' in labwareLocation) || // for legacy protocols that list fixed trash as a labware, do not render definition.parameters.loadName === diff --git a/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx b/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx index ff1cf8483b3..a41e99c1036 100644 --- a/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx +++ b/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx @@ -64,7 +64,7 @@ function getLabwareCoordinates({ loadedModules: LoadedModule[] loadedLabware: LoadedLabware[] }): Coordinates | null { - if (location === 'offDeck') { + if (location === 'offDeck' || location === 'systemLocation') { return null } else if ('labwareId' in location) { const loadedAdapter = loadedLabware.find(l => l.id === location.labwareId) @@ -73,6 +73,7 @@ function getLabwareCoordinates({ if ( loadedAdapterLocation === 'offDeck' || + loadedAdapterLocation === 'systemLocation' || 'labwareId' in loadedAdapterLocation ) return null @@ -159,6 +160,7 @@ export function MoveLabwareOnDeck( const initialSlotId = initialLabwareLocation === 'offDeck' || + initialLabwareLocation === 'systemLocation' || !('slotName' in initialLabwareLocation) ? deckDef.locations.addressableAreas[1].id : initialLabwareLocation.slotName diff --git a/components/src/hardware-sim/ProtocolDeck/utils/getLabwareInSlots.ts b/components/src/hardware-sim/ProtocolDeck/utils/getLabwareInSlots.ts index 88d769409ae..c88c43a1695 100644 --- a/components/src/hardware-sim/ProtocolDeck/utils/getLabwareInSlots.ts +++ b/components/src/hardware-sim/ProtocolDeck/utils/getLabwareInSlots.ts @@ -39,6 +39,7 @@ export const getInitialAndMovedLabwareInSlots = ( if ( location === 'offDeck' || + location === 'systemLocation' || 'moduleId' in location || 'labwareId' in location ) @@ -108,6 +109,7 @@ export const getTopMostLabwareInSlots = ( const labwareDef = command.result?.definition if ( location === 'offDeck' || + location === 'systemLocation' || 'moduleId' in location || 'labwareId' in location ) diff --git a/components/src/organisms/CommandText/useCommandTextString/utils/commandText/getLoadCommandText.ts b/components/src/organisms/CommandText/useCommandTextString/utils/commandText/getLoadCommandText.ts index 355449a61c7..e875ce989cb 100644 --- a/components/src/organisms/CommandText/useCommandTextString/utils/commandText/getLoadCommandText.ts +++ b/components/src/organisms/CommandText/useCommandTextString/utils/commandText/getLoadCommandText.ts @@ -59,7 +59,10 @@ export const getLoadCommandText = ({ const labwareName = command.result?.definition.metadata.displayName // use in preposition for modules and slots, on for labware and adapters let displayLocation = t('in_location', { location }) - if (command.params.location === 'offDeck') { + if ( + command.params.location === 'offDeck' || + command.params.location === 'systemLocation' + ) { displayLocation = location } else if ('labwareId' in command.params.location) { displayLocation = t('on_location', { location }) diff --git a/components/src/organisms/CommandText/useCommandTextString/utils/getLabwareLocation.tsx b/components/src/organisms/CommandText/useCommandTextString/utils/getLabwareLocation.tsx index 50eec1883b2..a1a84a6a8f6 100644 --- a/components/src/organisms/CommandText/useCommandTextString/utils/getLabwareLocation.tsx +++ b/components/src/organisms/CommandText/useCommandTextString/utils/getLabwareLocation.tsx @@ -52,6 +52,8 @@ export function getLabwareLocation( return null } else if (location === 'offDeck') { return { slotName: 'offDeck' } + } else if (location === 'systemLocation') { + return { slotName: 'systemLocation' } } else if ('slotName' in location) { return { slotName: location.slotName } } else if ('addressableAreaName' in location) { @@ -92,7 +94,10 @@ export function getLabwareLocation( const adapterName = adapterDef != null ? getLabwareDisplayName(adapterDef) : '' - if (adapter.location === 'offDeck') { + if ( + adapter.location === 'offDeck' || + adapter.location === 'systemLocation' + ) { return { slotName: 'offDeck', adapterName } } else if ( 'slotName' in adapter.location || diff --git a/labware-library/src/localization/en.ts b/labware-library/src/localization/en.ts index 9745ed44fb2..40af070a105 100644 --- a/labware-library/src/localization/en.ts +++ b/labware-library/src/localization/en.ts @@ -11,6 +11,7 @@ export const CATEGORY_LABELS_BY_CATEGORY = { other: 'Other', adapter: 'Adapter', lid: 'Lid', + system: 'System', } export const PLURAL_CATEGORY_LABELS_BY_CATEGORY = { @@ -24,6 +25,7 @@ export const PLURAL_CATEGORY_LABELS_BY_CATEGORY = { lid: 'Lid', trash: 'Trashes', other: 'Other', + system: 'System', } export const WELL_TYPE_BY_CATEGORY = { diff --git a/protocol-designer/src/analytics/middleware.ts b/protocol-designer/src/analytics/middleware.ts index 661bb2a65ff..2f34d6582ff 100644 --- a/protocol-designer/src/analytics/middleware.ts +++ b/protocol-designer/src/analytics/middleware.ts @@ -405,6 +405,7 @@ export const reduxActionToAnalyticsEvent = ( )) || (command.commandType === 'moveLabware' && command.params.newLocation !== 'offDeck' && + command.params.newLocation !== 'systemLocation' && 'addressableAreaName' in command.params.newLocation && command.params.newLocation.addressableAreaName === 'gripperWasteChute') @@ -421,10 +422,12 @@ export const reduxActionToAnalyticsEvent = ( command => (command.commandType === 'loadLabware' && command.params.location !== 'offDeck' && + command.params.location !== 'systemLocation' && 'addressableAreaName' in command.params.location && command.params.location.addressableAreaName === location) || (command.commandType === 'moveLabware' && command.params.newLocation !== 'offDeck' && + command.params.newLocation !== 'systemLocation' && 'addressableAreaName' in command.params.newLocation && command.params.newLocation.addressableAreaName === location) ) diff --git a/protocol-designer/src/labware-ingred/reducers/index.ts b/protocol-designer/src/labware-ingred/reducers/index.ts index 3ac4d42e0ad..47cf1884284 100644 --- a/protocol-designer/src/labware-ingred/reducers/index.ts +++ b/protocol-designer/src/labware-ingred/reducers/index.ts @@ -243,7 +243,7 @@ export const savedLabware: Reducer = handleActions( const { displayName, loadName, labwareId } = command.params const location = command.params.location let slot - if (location === 'offDeck') { + if (location === 'offDeck' || location === 'systemLocation') { slot = 'offDeck' } else if ('moduleId' in location) { slot = location.moduleId diff --git a/protocol-designer/src/pages/ProtocolOverview/utils.ts b/protocol-designer/src/pages/ProtocolOverview/utils.ts index 5ca25e43070..bac77ea4341 100644 --- a/protocol-designer/src/pages/ProtocolOverview/utils.ts +++ b/protocol-designer/src/pages/ProtocolOverview/utils.ts @@ -80,10 +80,12 @@ export const getUnusedStagingAreas = ( command => (command.commandType === 'loadLabware' && command.params.location !== 'offDeck' && + command.params.location !== 'systemLocation' && 'addressableAreaName' in command.params.location && command.params.location.addressableAreaName === location) || (command.commandType === 'moveLabware' && command.params.newLocation !== 'offDeck' && + command.params.newLocation !== 'systemLocation' && 'addressableAreaName' in command.params.newLocation && command.params.newLocation.addressableAreaName === location) ) @@ -131,6 +133,7 @@ export const getUnusedTrash = ( )) || (command.commandType === 'moveLabware' && command.params.newLocation !== 'offDeck' && + command.params.newLocation !== 'systemLocation' && 'addressableAreaName' in command.params.newLocation && command.params.newLocation.addressableAreaName === 'gripperWasteChute') diff --git a/protocol-designer/src/step-forms/utils/index.ts b/protocol-designer/src/step-forms/utils/index.ts index 042a6edfbd4..c1cb8fc14fd 100644 --- a/protocol-designer/src/step-forms/utils/index.ts +++ b/protocol-designer/src/step-forms/utils/index.ts @@ -338,6 +338,7 @@ export const getUnoccupiedSlotForTrash = ( const location = command.params.location if ( location !== 'offDeck' && + location !== 'systemLocation' && location !== null && 'slotName' in location ) { @@ -369,6 +370,7 @@ export const getUnoccupiedSlotForTrash = ( const newLocation = command.params.newLocation if ( newLocation !== 'offDeck' && + newLocation !== 'systemLocation' && newLocation !== null && 'slotName' in newLocation ) { diff --git a/protocol-designer/src/steplist/formLevel/moveLabwareFormErrors.ts b/protocol-designer/src/steplist/formLevel/moveLabwareFormErrors.ts index 54a375caa0b..c7ef90bb3e3 100644 --- a/protocol-designer/src/steplist/formLevel/moveLabwareFormErrors.ts +++ b/protocol-designer/src/steplist/formLevel/moveLabwareFormErrors.ts @@ -22,6 +22,7 @@ const getMoveLabwareError = ( labware == null || newLocation == null || newLocation === 'offDeck' || + newLocation === 'systemLocation' || !getLabwareDefIsStandard(labware?.def) ) return null diff --git a/shared-data/command/schemas/11.json b/shared-data/command/schemas/11.json index 01cd2e3f4a8..b70724f23cc 100644 --- a/shared-data/command/schemas/11.json +++ b/shared-data/command/schemas/11.json @@ -2058,6 +2058,11 @@ "enum": ["offDeck"], "type": "string" }, + { + "const": "systemLocation", + "enum": ["systemLocation"], + "type": "string" + }, { "$ref": "#/$defs/AddressableAreaLocation" } @@ -2132,6 +2137,11 @@ "enum": ["offDeck"], "type": "string" }, + { + "const": "systemLocation", + "enum": ["systemLocation"], + "type": "string" + }, { "$ref": "#/$defs/AddressableAreaLocation" } @@ -2185,6 +2195,14 @@ "LoadLidStackParams": { "description": "Payload required to load a lid stack onto a location.", "properties": { + "labwareIds": { + "description": "An optional list of IDs to assign to the lids in the stack.If None, an ID will be generated.", + "items": { + "type": "string" + }, + "title": "Labwareids", + "type": "array" + }, "loadName": { "description": "Name used to reference a lid labware definition.", "title": "Loadname", @@ -2206,6 +2224,11 @@ "enum": ["offDeck"], "type": "string" }, + { + "const": "systemLocation", + "enum": ["systemLocation"], + "type": "string" + }, { "$ref": "#/$defs/AddressableAreaLocation" } @@ -2223,6 +2246,11 @@ "title": "Quantity", "type": "integer" }, + "stackLabwareId": { + "description": "An optional ID to assign to the lid stack labware object created.If None, an ID will be generated.", + "title": "Stacklabwareid", + "type": "string" + }, "version": { "description": "The lid labware definition version.", "title": "Version", @@ -2713,6 +2741,11 @@ "enum": ["offDeck"], "type": "string" }, + { + "const": "systemLocation", + "enum": ["systemLocation"], + "type": "string" + }, { "$ref": "#/$defs/AddressableAreaLocation" } diff --git a/shared-data/command/types/setup.ts b/shared-data/command/types/setup.ts index 13d29c682b4..2a29c6d7b71 100644 --- a/shared-data/command/types/setup.ts +++ b/shared-data/command/types/setup.ts @@ -101,6 +101,7 @@ export type SetupCreateCommand = export type LabwareLocation = | 'offDeck' + | 'systemLocation' | { slotName: string } | { moduleId: string } | { labwareId: string } diff --git a/shared-data/js/helpers/getAddressableAreasInProtocol.ts b/shared-data/js/helpers/getAddressableAreasInProtocol.ts index 81222777db2..9be0a547f40 100644 --- a/shared-data/js/helpers/getAddressableAreasInProtocol.ts +++ b/shared-data/js/helpers/getAddressableAreasInProtocol.ts @@ -19,6 +19,7 @@ export function getAddressableAreasInProtocol( if ( commandType === 'moveLabware' && params.newLocation !== 'offDeck' && + params.newLocation !== 'systemLocation' && 'slotName' in params.newLocation && !acc.includes(params.newLocation.slotName as AddressableAreaName) ) { @@ -35,6 +36,7 @@ export function getAddressableAreasInProtocol( } else if ( commandType === 'moveLabware' && params.newLocation !== 'offDeck' && + params.newLocation !== 'systemLocation' && 'addressableAreaName' in params.newLocation && !acc.includes(params.newLocation.addressableAreaName) ) { @@ -42,6 +44,7 @@ export function getAddressableAreasInProtocol( } else if ( commandType === 'loadLabware' && params.location !== 'offDeck' && + params.location !== 'systemLocation' && 'slotName' in params.location && !acc.includes(params.location.slotName as AddressableAreaName) ) { @@ -74,6 +77,7 @@ export function getAddressableAreasInProtocol( } else if ( commandType === 'loadLabware' && params.location !== 'offDeck' && + params.location !== 'systemLocation' && 'addressableAreaName' in params.location && !acc.includes(params.location.addressableAreaName) ) { @@ -100,6 +104,7 @@ export function getAddressableAreasInProtocol( ({ loadName, location }) => loadName === 'opentrons_1_trash_3200ml_fixed' && location !== 'offDeck' && + location !== 'systemLocation' && 'slotName' in location && location.slotName === 'A3' ) diff --git a/shared-data/js/helpers/parseProtocolCommands.ts b/shared-data/js/helpers/parseProtocolCommands.ts index 4f111e4d3e5..9bc0b99d11d 100644 --- a/shared-data/js/helpers/parseProtocolCommands.ts +++ b/shared-data/js/helpers/parseProtocolCommands.ts @@ -153,6 +153,7 @@ export function getTopLabwareInfo( command => command.commandType === 'loadLabware' && command.params.location !== 'offDeck' && + command.params.location !== 'systemLocation' && 'labwareId' in command.params.location && command.params.location.labwareId === labwareId ) @@ -208,7 +209,11 @@ export function getLabwareStackCountAndLocation( const labwareLocation = loadLabwareCommand.params.location - if (labwareLocation !== 'offDeck' && 'labwareId' in labwareLocation) { + if ( + labwareLocation !== 'offDeck' && + labwareLocation !== 'systemLocation' && + 'labwareId' in labwareLocation + ) { const lowerLabwareCommand = loadLabwareCommands?.find(command => command.result != null ? command.result?.labwareId === labwareLocation.labwareId diff --git a/shared-data/js/types.ts b/shared-data/js/types.ts index dab252a54ec..e5e0f0b050c 100644 --- a/shared-data/js/types.ts +++ b/shared-data/js/types.ts @@ -88,6 +88,7 @@ export type LabwareDisplayCategory = | 'other' | 'adapter' | 'lid' + | 'system' export type LabwareVolumeUnits = 'µL' | 'mL' | 'L' // TODO(mc, 2019-05-29): Remove this enum in favor of string + exported diff --git a/shared-data/labware/definitions/3/opentrons_tough_pcr_auto_sealing_lid/1.json b/shared-data/labware/definitions/3/opentrons_tough_pcr_auto_sealing_lid/1.json index 479fd00b772..95a55074c61 100644 --- a/shared-data/labware/definitions/3/opentrons_tough_pcr_auto_sealing_lid/1.json +++ b/shared-data/labware/definitions/3/opentrons_tough_pcr_auto_sealing_lid/1.json @@ -75,13 +75,21 @@ "x": 0, "y": 0, "z": 34 + }, + "protocol_engine_lid_stack_object": { + "x": 0, + "y": 0, + "z": 0 } }, "stackLimit": 5, "compatibleParentLabware": [ "armadillo_96_wellplate_200ul_pcr_full_skirt", "opentrons_96_wellplate_200ul_pcr_full_skirt", - "opentrons_tough_pcr_auto_sealing_lid" + "opentrons_tough_pcr_auto_sealing_lid", + "biorad_96_wellplate_200ul_pcr", + "opentrons_flex_deck_riser", + "protocol_engine_lid_stack_object" ], "gripForce": 15, "gripHeightFromLabwareBottom": 7.91, diff --git a/shared-data/labware/definitions/3/protocol_engine_lid_stack_object/1.json b/shared-data/labware/definitions/3/protocol_engine_lid_stack_object/1.json index f8aeb02350c..134b90bf13f 100644 --- a/shared-data/labware/definitions/3/protocol_engine_lid_stack_object/1.json +++ b/shared-data/labware/definitions/3/protocol_engine_lid_stack_object/1.json @@ -37,5 +37,12 @@ }, "namespace": "opentrons", "version": 1, - "schemaVersion": 3 + "schemaVersion": 3, + "stackingOffsetWithLabware": { + "opentrons_flex_deck_riser": { + "x": 0, + "y": 0, + "z": 34 + } + } } diff --git a/step-generation/src/commandCreators/atomic/moveLabware.ts b/step-generation/src/commandCreators/atomic/moveLabware.ts index c24fac9b708..4976b6d7827 100644 --- a/step-generation/src/commandCreators/atomic/moveLabware.ts +++ b/step-generation/src/commandCreators/atomic/moveLabware.ts @@ -48,6 +48,7 @@ export const moveLabware: CommandCreator = ( const newLocationInWasteChute = newLocation !== 'offDeck' && + newLocation !== 'systemLocation' && 'addressableAreaName' in newLocation && newLocation.addressableAreaName === 'gripperWasteChute' @@ -56,7 +57,9 @@ export const moveLabware: CommandCreator = ( ) const newLocationSlot = - newLocation !== 'offDeck' && 'slotName' in newLocation + newLocation !== 'offDeck' && + newLocation !== 'systemLocation' && + 'slotName' in newLocation ? newLocation.slotName : null @@ -134,12 +137,16 @@ export const moveLabware: CommandCreator = ( } } const destModuleId = - newLocation !== 'offDeck' && 'moduleId' in newLocation + newLocation !== 'offDeck' && + newLocation !== 'systemLocation' && + 'moduleId' in newLocation ? newLocation.moduleId : null const destAdapterId = - newLocation !== 'offDeck' && 'labwareId' in newLocation + newLocation !== 'offDeck' && + newLocation !== 'systemLocation' && + 'labwareId' in newLocation ? newLocation.labwareId : null diff --git a/step-generation/src/getNextRobotStateAndWarnings/forMoveLabware.ts b/step-generation/src/getNextRobotStateAndWarnings/forMoveLabware.ts index 389860d302b..e41c5e9908e 100644 --- a/step-generation/src/getNextRobotStateAndWarnings/forMoveLabware.ts +++ b/step-generation/src/getNextRobotStateAndWarnings/forMoveLabware.ts @@ -10,7 +10,7 @@ export function forMoveLabware( const { robotState } = robotStateAndWarnings let newLocationString = '' - if (newLocation === 'offDeck') { + if (newLocation === 'offDeck' || newLocation === 'systemLocation') { newLocationString = newLocation } else if ('moduleId' in newLocation) { newLocationString = newLocation.moduleId diff --git a/step-generation/src/utils/createTimelineFromRunCommands.ts b/step-generation/src/utils/createTimelineFromRunCommands.ts index 882fec107b4..94acc8a7adf 100644 --- a/step-generation/src/utils/createTimelineFromRunCommands.ts +++ b/step-generation/src/utils/createTimelineFromRunCommands.ts @@ -43,7 +43,10 @@ export function getResultingTimelineFrameFromRunCommands( (acc, command) => { if (command.commandType === 'loadLabware' && command.result != null) { let slot - if (command.params.location === 'offDeck') { + if ( + command.params.location === 'offDeck' || + command.params.location === 'systemLocation' + ) { slot = command.params.location } else if ('slotName' in command.params.location) { slot = command.params.location.slotName