From d6b0a66ab66e2c44612ecd208d372c9abe6e258c Mon Sep 17 00:00:00 2001 From: CaseyBatten Date: Wed, 30 Oct 2024 10:50:27 -0400 Subject: [PATCH 1/6] allow labware lids to be dropped in the trash bin --- api/src/opentrons/legacy_commands/helpers.py | 10 +++++-- .../protocol_api/core/engine/labware.py | 4 +++ .../protocol_api/core/engine/protocol.py | 5 ++++ .../opentrons/protocol_api/core/labware.py | 4 +++ .../core/legacy/legacy_labware_core.py | 5 ++++ .../core/legacy/legacy_protocol_core.py | 1 + .../opentrons/protocol_api/core/protocol.py | 1 + .../protocol_api/protocol_context.py | 10 ++++++- .../protocol_engine/commands/move_labware.py | 28 +++++++++++++++++-- .../resources/fixture_validation.py | 5 +++- .../protocol_engine/state/labware.py | 9 +++--- 11 files changed, 71 insertions(+), 11 deletions(-) diff --git a/api/src/opentrons/legacy_commands/helpers.py b/api/src/opentrons/legacy_commands/helpers.py index b3de03de4bc..5b08bb1e436 100644 --- a/api/src/opentrons/legacy_commands/helpers.py +++ b/api/src/opentrons/legacy_commands/helpers.py @@ -49,7 +49,9 @@ def stringify_disposal_location(location: Union[TrashBin, WasteChute]) -> str: def _stringify_labware_movement_location( - location: Union[DeckLocation, OffDeckType, Labware, ModuleContext, WasteChute] + location: Union[ + DeckLocation, OffDeckType, Labware, ModuleContext, WasteChute, TrashBin + ] ) -> str: if isinstance(location, (int, str)): return f"slot {location}" @@ -61,11 +63,15 @@ def _stringify_labware_movement_location( return str(location) elif isinstance(location, WasteChute): return "Waste Chute" + elif isinstance(location, TrashBin): + return "Trash Bin " + location.location.name def stringify_labware_movement_command( source_labware: Labware, - destination: Union[DeckLocation, OffDeckType, Labware, ModuleContext, WasteChute], + destination: Union[ + DeckLocation, OffDeckType, Labware, ModuleContext, WasteChute, TrashBin + ], use_gripper: bool, ) -> str: source_labware_text = _stringify_labware_movement_location(source_labware) diff --git a/api/src/opentrons/protocol_api/core/engine/labware.py b/api/src/opentrons/protocol_api/core/engine/labware.py index f09a51ef181..9648805c563 100644 --- a/api/src/opentrons/protocol_api/core/engine/labware.py +++ b/api/src/opentrons/protocol_api/core/engine/labware.py @@ -139,6 +139,10 @@ def is_adapter(self) -> bool: """Whether the labware is an adapter.""" return LabwareRole.adapter in self._definition.allowedRoles + def is_lid(self) -> bool: + """Whether the labware is a lid.""" + return LabwareRole.lid in self._definition.allowedRoles + def is_fixed_trash(self) -> bool: """Whether the labware is a fixed trash.""" return self._engine_client.state.labware.is_fixed_trash( diff --git a/api/src/opentrons/protocol_api/core/engine/protocol.py b/api/src/opentrons/protocol_api/core/engine/protocol.py index 0ed5270320a..44e05b3fd74 100644 --- a/api/src/opentrons/protocol_api/core/engine/protocol.py +++ b/api/src/opentrons/protocol_api/core/engine/protocol.py @@ -329,6 +329,7 @@ def move_labware( NonConnectedModuleCore, OffDeckType, WasteChute, + TrashBin, ], use_gripper: bool, pause_for_manual_move: bool, @@ -802,6 +803,7 @@ def _convert_labware_location( NonConnectedModuleCore, OffDeckType, WasteChute, + TrashBin, ], ) -> LabwareLocation: if isinstance(location, LabwareCore): @@ -818,6 +820,7 @@ def _get_non_stacked_location( NonConnectedModuleCore, OffDeckType, WasteChute, + TrashBin, ] ) -> NonStackedLocation: if isinstance(location, (ModuleCore, NonConnectedModuleCore)): @@ -831,3 +834,5 @@ def _get_non_stacked_location( elif isinstance(location, WasteChute): # TODO(mm, 2023-12-06) This will need to determine the appropriate Waste Chute to return, but only move_labware uses this for now return AddressableAreaLocation(addressableAreaName="gripperWasteChute") + elif isinstance(location, TrashBin): + return AddressableAreaLocation(addressableAreaName=location.area_name) diff --git a/api/src/opentrons/protocol_api/core/labware.py b/api/src/opentrons/protocol_api/core/labware.py index 67b452cca6d..691a764e8d3 100644 --- a/api/src/opentrons/protocol_api/core/labware.py +++ b/api/src/opentrons/protocol_api/core/labware.py @@ -97,6 +97,10 @@ def is_tip_rack(self) -> bool: def is_adapter(self) -> bool: """Whether the labware is an adapter.""" + @abstractmethod + def is_lid(self) -> bool: + """Whether the labware is a lid.""" + @abstractmethod def is_fixed_trash(self) -> bool: """Whether the labware is a fixed trash.""" diff --git a/api/src/opentrons/protocol_api/core/legacy/legacy_labware_core.py b/api/src/opentrons/protocol_api/core/legacy/legacy_labware_core.py index 575fd7a8cc6..06411765d51 100644 --- a/api/src/opentrons/protocol_api/core/legacy/legacy_labware_core.py +++ b/api/src/opentrons/protocol_api/core/legacy/legacy_labware_core.py @@ -138,6 +138,11 @@ def is_tip_rack(self) -> bool: def is_adapter(self) -> bool: return False # Adapters were introduced in v2.15 and not supported in legacy protocols + def is_lid(self) -> bool: + return ( + False # Lids were introduced in v2.21 and not supported in legacy protocols + ) + def is_fixed_trash(self) -> bool: """Whether the labware is fixed trash.""" return "fixedTrash" in self.get_quirks() 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 aeef0e9d7c7..eac5a9109fa 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 @@ -277,6 +277,7 @@ def move_labware( legacy_module_core.LegacyModuleCore, OffDeckType, WasteChute, + TrashBin, ], use_gripper: bool, pause_for_manual_move: bool, diff --git a/api/src/opentrons/protocol_api/core/protocol.py b/api/src/opentrons/protocol_api/core/protocol.py index 9c3692c7e44..f79ab987157 100644 --- a/api/src/opentrons/protocol_api/core/protocol.py +++ b/api/src/opentrons/protocol_api/core/protocol.py @@ -104,6 +104,7 @@ def move_labware( ModuleCoreType, OffDeckType, WasteChute, + TrashBin, ], use_gripper: bool, pause_for_manual_move: bool, diff --git a/api/src/opentrons/protocol_api/protocol_context.py b/api/src/opentrons/protocol_api/protocol_context.py index 43c5956afd9..f9db0e18db6 100644 --- a/api/src/opentrons/protocol_api/protocol_context.py +++ b/api/src/opentrons/protocol_api/protocol_context.py @@ -668,7 +668,7 @@ def move_labware( self, labware: Labware, new_location: Union[ - DeckLocation, Labware, ModuleTypes, OffDeckType, WasteChute + DeckLocation, Labware, ModuleTypes, OffDeckType, WasteChute, TrashBin ], use_gripper: bool = False, pick_up_offset: Optional[Mapping[str, float]] = None, @@ -727,11 +727,19 @@ def move_labware( OffDeckType, DeckSlotName, StagingSlotName, + TrashBin, ] if isinstance(new_location, (Labware, ModuleContext)): location = new_location._core elif isinstance(new_location, (OffDeckType, WasteChute)): location = new_location + elif isinstance(new_location, TrashBin): + if labware._core.is_lid(): + location = new_location + else: + raise CommandPreconditionViolated( + "Can only dispose of Lid-type labware and tips in the Trash Bin. Did you mean to use a Waste Chute?" + ) else: location = validation.ensure_and_convert_deck_slot( new_location, self._api_version, self._core.robot_type diff --git a/api/src/opentrons/protocol_engine/commands/move_labware.py b/api/src/opentrons/protocol_engine/commands/move_labware.py index 0d2967e87d5..eb11f5b7373 100644 --- a/api/src/opentrons/protocol_engine/commands/move_labware.py +++ b/api/src/opentrons/protocol_engine/commands/move_labware.py @@ -130,6 +130,7 @@ async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C ) definition_uri = current_labware.definitionUri post_drop_slide_offset: Optional[Point] = None + trash_lid_drop_offset: Optional[LabwareOffsetVector] = None if self._state_view.labware.is_fixed_trash(params.labwareId): raise LabwareMovementNotAllowedError( @@ -138,9 +139,11 @@ async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C if isinstance(params.newLocation, AddressableAreaLocation): area_name = params.newLocation.addressableAreaName - if not fixture_validation.is_gripper_waste_chute( - area_name - ) and not fixture_validation.is_deck_slot(area_name): + if ( + not fixture_validation.is_gripper_waste_chute(area_name) + and not fixture_validation.is_deck_slot(area_name) + and not fixture_validation.is_trash(area_name) + ): raise LabwareMovementNotAllowedError( f"Cannot move {current_labware.loadName} to addressable area {area_name}" ) @@ -162,6 +165,22 @@ async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C y=0, z=0, ) + elif fixture_validation.is_trash(area_name): + # When dropping labware in the trash bins we want to ensure they are lids + # and enforce a y-axis drop offset to ensure they fall within the trash bin + if labware_validation.validate_definition_is_lid( + self._state_view.labware.get_definition(params.labwareId) + ): + trash_lid_drop_offset = LabwareOffsetVector( + x=0, + y=20.0, + z=0, + ) + else: + raise LabwareMovementNotAllowedError( + "Can only move labware with allowed role 'Lid' to a Trash Bin." + ) + elif isinstance(params.newLocation, DeckSlotLocation): self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration( params.newLocation.slotName.id @@ -232,6 +251,9 @@ async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C dropOffset=params.dropOffset or LabwareOffsetVector(x=0, y=0, z=0), ) + if trash_lid_drop_offset: + user_offset_data.dropOffset += trash_lid_drop_offset + try: # Skips gripper moves when using virtual gripper await self._labware_movement.move_labware_with_gripper( diff --git a/api/src/opentrons/protocol_engine/resources/fixture_validation.py b/api/src/opentrons/protocol_engine/resources/fixture_validation.py index 745df22d712..7792b1d3378 100644 --- a/api/src/opentrons/protocol_engine/resources/fixture_validation.py +++ b/api/src/opentrons/protocol_engine/resources/fixture_validation.py @@ -29,7 +29,10 @@ def is_drop_tip_waste_chute(addressable_area_name: str) -> bool: def is_trash(addressable_area_name: str) -> bool: """Check if an addressable area is a trash bin.""" - return addressable_area_name in {"movableTrash", "fixedTrash", "shortFixedTrash"} + return [ + s in addressable_area_name + for s in {"movableTrash", "fixedTrash", "shortFixedTrash"} + ] def is_staging_slot(addressable_area_name: str) -> bool: diff --git a/api/src/opentrons/protocol_engine/state/labware.py b/api/src/opentrons/protocol_engine/state/labware.py index 7cea4f9765b..0bbb8b3ab30 100644 --- a/api/src/opentrons/protocol_engine/state/labware.py +++ b/api/src/opentrons/protocol_engine/state/labware.py @@ -227,10 +227,11 @@ def _set_labware_location(self, state_update: update_types.StateUpdate) -> None: if labware_location_update.new_location: new_location = labware_location_update.new_location - if isinstance( - new_location, AddressableAreaLocation - ) and fixture_validation.is_gripper_waste_chute( - new_location.addressableAreaName + if isinstance(new_location, AddressableAreaLocation) and ( + fixture_validation.is_gripper_waste_chute( + new_location.addressableAreaName + ) + or fixture_validation.is_trash(new_location.addressableAreaName) ): # If a labware has been moved into a waste chute it's been chuted away and is now technically off deck new_location = OFF_DECK_LOCATION From cb4ab7a8ac911ebd04462e29eaf4b9d6945cf7bd Mon Sep 17 00:00:00 2001 From: CaseyBatten Date: Wed, 30 Oct 2024 11:08:59 -0400 Subject: [PATCH 2/6] linting fix for trash validation --- .../protocol_engine/resources/fixture_validation.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/api/src/opentrons/protocol_engine/resources/fixture_validation.py b/api/src/opentrons/protocol_engine/resources/fixture_validation.py index 7792b1d3378..a17bf147f85 100644 --- a/api/src/opentrons/protocol_engine/resources/fixture_validation.py +++ b/api/src/opentrons/protocol_engine/resources/fixture_validation.py @@ -29,10 +29,12 @@ def is_drop_tip_waste_chute(addressable_area_name: str) -> bool: def is_trash(addressable_area_name: str) -> bool: """Check if an addressable area is a trash bin.""" - return [ - s in addressable_area_name - for s in {"movableTrash", "fixedTrash", "shortFixedTrash"} - ] + return any( + [ + s in addressable_area_name + for s in {"movableTrash", "fixedTrash", "shortFixedTrash"} + ] + ) def is_staging_slot(addressable_area_name: str) -> bool: From 115957dc20b171ccda25cc5d39e7c767c3326639 Mon Sep 17 00:00:00 2001 From: CaseyBatten Date: Wed, 30 Oct 2024 14:32:14 -0400 Subject: [PATCH 3/6] z height drop increase and test fixtures --- .../protocol_api/protocol_context.py | 5 +- .../protocol_engine/commands/move_labware.py | 2 +- .../protocol_api/test_protocol_context.py | 76 +++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/api/src/opentrons/protocol_api/protocol_context.py b/api/src/opentrons/protocol_api/protocol_context.py index f9db0e18db6..ed7d24f4d3f 100644 --- a/api/src/opentrons/protocol_api/protocol_context.py +++ b/api/src/opentrons/protocol_api/protocol_context.py @@ -45,6 +45,7 @@ UnsupportedAPIError, ) from opentrons_shared_data.errors.exceptions import CommandPreconditionViolated +from opentrons.protocol_engine.errors import LabwareMovementNotAllowedError from ._types import OffDeckType from .core.common import ModuleCore, LabwareCore, ProtocolCore @@ -737,8 +738,8 @@ def move_labware( if labware._core.is_lid(): location = new_location else: - raise CommandPreconditionViolated( - "Can only dispose of Lid-type labware and tips in the Trash Bin. Did you mean to use a Waste Chute?" + raise LabwareMovementNotAllowedError( + "Can only dispose of tips and Lid-type labware in a Trash Bin. Did you mean to use a Waste Chute?" ) else: location = validation.ensure_and_convert_deck_slot( diff --git a/api/src/opentrons/protocol_engine/commands/move_labware.py b/api/src/opentrons/protocol_engine/commands/move_labware.py index eb11f5b7373..c27f8350837 100644 --- a/api/src/opentrons/protocol_engine/commands/move_labware.py +++ b/api/src/opentrons/protocol_engine/commands/move_labware.py @@ -174,7 +174,7 @@ async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C trash_lid_drop_offset = LabwareOffsetVector( x=0, y=20.0, - z=0, + z=50.0, ) else: raise LabwareMovementNotAllowedError( diff --git a/api/tests/opentrons/protocol_api/test_protocol_context.py b/api/tests/opentrons/protocol_api/test_protocol_context.py index 2bedbd5fb6f..7a67ac0ea7f 100644 --- a/api/tests/opentrons/protocol_api/test_protocol_context.py +++ b/api/tests/opentrons/protocol_api/test_protocol_context.py @@ -49,6 +49,8 @@ from opentrons.protocols.api_support.deck_type import ( NoTrashDefinedError, ) +from opentrons.protocol_engine.errors import LabwareMovementNotAllowedError +from opentrons.protocol_engine.clients import SyncClient as EngineClient @pytest.fixture(autouse=True) @@ -101,6 +103,12 @@ def api_version() -> APIVersion: return MAX_SUPPORTED_VERSION +@pytest.fixture +def mock_engine_client(decoy: Decoy) -> EngineClient: + """Get a mock ProtocolEngine synchronous client.""" + return decoy.mock(cls=EngineClient) + + @pytest.fixture def subject( mock_core: ProtocolCore, @@ -944,6 +952,74 @@ def test_move_labware_off_deck_raises( subject.move_labware(labware=movable_labware, new_location=OFF_DECK) +def test_move_labware_to_trash_raises( + subject: ProtocolContext, + decoy: Decoy, + mock_core: ProtocolCore, + mock_core_map: LoadedCoreMap, + mock_engine_client: EngineClient, +) -> None: + """It should raise an LabwareMovementNotAllowedError if using move_labware to move something that is not a lid to a TrashBin.""" + mock_labware_core = decoy.mock(cls=LabwareCore) + trash_location = TrashBin( + location=DeckSlotName.SLOT_D3, + addressable_area_name="moveableTrashD3", + api_version=MAX_SUPPORTED_VERSION, + engine_client=mock_engine_client, + ) + + decoy.when(mock_labware_core.get_well_columns()).then_return([]) + + movable_labware = Labware( + core=mock_labware_core, + api_version=MAX_SUPPORTED_VERSION, + protocol_core=mock_core, + core_map=mock_core_map, + ) + + with pytest.raises(LabwareMovementNotAllowedError): + subject.move_labware(labware=movable_labware, new_location=trash_location) + + +def test_move_lid_to_trash_passes( + decoy: Decoy, + mock_core: ProtocolCore, + mock_core_map: LoadedCoreMap, + subject: ProtocolContext, + mock_engine_client: EngineClient, +) -> None: + """It should move a lid labware into a trashbin successfully.""" + mock_labware_core = decoy.mock(cls=LabwareCore) + trash_location = TrashBin( + location=DeckSlotName.SLOT_D3, + addressable_area_name="moveableTrashD3", + api_version=MAX_SUPPORTED_VERSION, + engine_client=mock_engine_client, + ) + + decoy.when(mock_labware_core.get_well_columns()).then_return([]) + decoy.when(mock_labware_core.is_lid()).then_return(True) + + movable_labware = Labware( + core=mock_labware_core, + api_version=MAX_SUPPORTED_VERSION, + protocol_core=mock_core, + core_map=mock_core_map, + ) + + subject.move_labware(labware=movable_labware, new_location=trash_location) + decoy.verify( + mock_core.move_labware( + labware_core=mock_labware_core, + new_location=trash_location, + use_gripper=True, + pause_for_manual_move=True, + pick_up_offset=None, + drop_offset=None, + ) + ) + + def test_load_trash_bin( decoy: Decoy, mock_core: ProtocolCore, From 1b3340cfebc8eb3bb74acd307d9fd251089531d6 Mon Sep 17 00:00:00 2001 From: CaseyBatten Date: Wed, 30 Oct 2024 14:47:48 -0400 Subject: [PATCH 4/6] 10mm offset value adjustment --- api/src/opentrons/protocol_engine/commands/move_labware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/opentrons/protocol_engine/commands/move_labware.py b/api/src/opentrons/protocol_engine/commands/move_labware.py index c27f8350837..b95efa902b2 100644 --- a/api/src/opentrons/protocol_engine/commands/move_labware.py +++ b/api/src/opentrons/protocol_engine/commands/move_labware.py @@ -173,7 +173,7 @@ async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C ): trash_lid_drop_offset = LabwareOffsetVector( x=0, - y=20.0, + y=10.0, z=50.0, ) else: From 4a7f69470ae528c442d24f81b2e30011252fbcb7 Mon Sep 17 00:00:00 2001 From: CaseyBatten Date: Wed, 30 Oct 2024 15:15:42 -0400 Subject: [PATCH 5/6] move offsets into labware definition and further test fixture adjustments --- .../protocol_engine/commands/move_labware.py | 31 +++++++++++++++---- .../protocol_api/test_protocol_context.py | 2 +- .../1.json | 12 +++++++ 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/move_labware.py b/api/src/opentrons/protocol_engine/commands/move_labware.py index b95efa902b2..59c87f89fe4 100644 --- a/api/src/opentrons/protocol_engine/commands/move_labware.py +++ b/api/src/opentrons/protocol_engine/commands/move_labware.py @@ -22,7 +22,11 @@ LabwareOffsetVector, LabwareMovementOffsetData, ) -from ..errors import LabwareMovementNotAllowedError, NotSupportedOnRobotType +from ..errors import ( + LabwareMovementNotAllowedError, + NotSupportedOnRobotType, + LabwareOffsetDoesNotExistError, +) from ..resources import labware_validation, fixture_validation from .command import ( AbstractCommandImpl, @@ -171,11 +175,26 @@ async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C if labware_validation.validate_definition_is_lid( self._state_view.labware.get_definition(params.labwareId) ): - trash_lid_drop_offset = LabwareOffsetVector( - x=0, - y=10.0, - z=50.0, - ) + self._state_view.labware.get_labware_gripper_offsets + if ( + "lidDisposalOffsets" + in current_labware_definition.gripperOffsets.keys() + ): + trash_lid_drop_offset = LabwareOffsetVector( + x=current_labware_definition.gripperOffsets[ + "lidDisposalOffsets" + ].dropOffset.x, + y=current_labware_definition.gripperOffsets[ + "lidDisposalOffsets" + ].dropOffset.y, + z=current_labware_definition.gripperOffsets[ + "lidDisposalOffsets" + ].dropOffset.z, + ) + else: + raise LabwareOffsetDoesNotExistError( + f"Labware Definition {current_labware.loadName} does not contain required field 'lidDisposalOffsets' of 'gripperOffsets'." + ) else: raise LabwareMovementNotAllowedError( "Can only move labware with allowed role 'Lid' to a Trash Bin." diff --git a/api/tests/opentrons/protocol_api/test_protocol_context.py b/api/tests/opentrons/protocol_api/test_protocol_context.py index 7a67ac0ea7f..5e516a5b274 100644 --- a/api/tests/opentrons/protocol_api/test_protocol_context.py +++ b/api/tests/opentrons/protocol_api/test_protocol_context.py @@ -1012,7 +1012,7 @@ def test_move_lid_to_trash_passes( mock_core.move_labware( labware_core=mock_labware_core, new_location=trash_location, - use_gripper=True, + use_gripper=False, pause_for_manual_move=True, pick_up_offset=None, drop_offset=None, diff --git a/shared-data/labware/definitions/2/opentrons_tough_pcr_auto_sealing_lid/1.json b/shared-data/labware/definitions/2/opentrons_tough_pcr_auto_sealing_lid/1.json index d5d56101e7f..f872649a027 100644 --- a/shared-data/labware/definitions/2/opentrons_tough_pcr_auto_sealing_lid/1.json +++ b/shared-data/labware/definitions/2/opentrons_tough_pcr_auto_sealing_lid/1.json @@ -98,6 +98,18 @@ "y": 0, "z": -1 } + }, + "lidDisposalOffsets": { + "pickUpOffset": { + "x": 0, + "y": 0, + "z": 0 + }, + "dropOffset": { + "x": 0, + "y": 5.0, + "z": 50.0 + } } } } From 10974f8fa6a3375dd6cb7621df3aa8f58c532b23 Mon Sep 17 00:00:00 2001 From: CaseyBatten Date: Wed, 30 Oct 2024 17:15:45 -0400 Subject: [PATCH 6/6] labware movement formatting cleanup --- .../protocol_engine/commands/move_labware.py | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/move_labware.py b/api/src/opentrons/protocol_engine/commands/move_labware.py index 59c87f89fe4..eb4b101e76c 100644 --- a/api/src/opentrons/protocol_engine/commands/move_labware.py +++ b/api/src/opentrons/protocol_engine/commands/move_labware.py @@ -175,21 +175,16 @@ async def execute(self, params: MoveLabwareParams) -> _ExecuteReturn: # noqa: C if labware_validation.validate_definition_is_lid( self._state_view.labware.get_definition(params.labwareId) ): - self._state_view.labware.get_labware_gripper_offsets - if ( - "lidDisposalOffsets" - in current_labware_definition.gripperOffsets.keys() - ): + lid_disposable_offfets = ( + current_labware_definition.gripperOffsets.get( + "lidDisposalOffsets" + ) + ) + if lid_disposable_offfets is not None: trash_lid_drop_offset = LabwareOffsetVector( - x=current_labware_definition.gripperOffsets[ - "lidDisposalOffsets" - ].dropOffset.x, - y=current_labware_definition.gripperOffsets[ - "lidDisposalOffsets" - ].dropOffset.y, - z=current_labware_definition.gripperOffsets[ - "lidDisposalOffsets" - ].dropOffset.z, + x=lid_disposable_offfets.dropOffset.x, + y=lid_disposable_offfets.dropOffset.y, + z=lid_disposable_offfets.dropOffset.z, ) else: raise LabwareOffsetDoesNotExistError(