diff --git a/api/src/opentrons/protocol_engine/commands/move_labware.py b/api/src/opentrons/protocol_engine/commands/move_labware.py index 31d16feb11b..025ba86dbf1 100644 --- a/api/src/opentrons/protocol_engine/commands/move_labware.py +++ b/api/src/opentrons/protocol_engine/commands/move_labware.py @@ -12,7 +12,7 @@ LabwareOffsetVector, LabwareMovementOffsetData, ) -from ..errors import LabwareMovementNotAllowedError +from ..errors import LabwareMovementNotAllowedError, NotSupportedOnRobotType from ..resources import labware_validation from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate @@ -120,6 +120,11 @@ async def execute(self, params: MoveLabwareParams) -> MoveLabwareResult: ) if params.strategy == LabwareMovementStrategy.USING_GRIPPER: + if self._state_view.config.robot_type == "OT-2 Standard": + raise NotSupportedOnRobotType( + message="Labware movement using a gripper is not supported on the OT-2", + details={"strategy": params.strategy}, + ) if labware_validation.validate_definition_is_adapter( current_labware_definition ): diff --git a/api/src/opentrons/protocol_engine/errors/__init__.py b/api/src/opentrons/protocol_engine/errors/__init__.py index 3fb722a4837..3b3338bb261 100644 --- a/api/src/opentrons/protocol_engine/errors/__init__.py +++ b/api/src/opentrons/protocol_engine/errors/__init__.py @@ -54,6 +54,7 @@ LabwareMovementNotAllowedError, LocationIsOccupiedError, InvalidAxisForRobotType, + NotSupportedOnRobotType, ) from .error_occurrence import ErrorOccurrence, ProtocolCommandFailedError @@ -114,6 +115,7 @@ "LabwareMovementNotAllowedError", "LocationIsOccupiedError", "InvalidAxisForRobotType", + "NotSupportedOnRobotType", # error occurrence models "ErrorOccurrence", ] diff --git a/api/src/opentrons/protocol_engine/errors/exceptions.py b/api/src/opentrons/protocol_engine/errors/exceptions.py index b5f8125c842..2a6871ef813 100644 --- a/api/src/opentrons/protocol_engine/errors/exceptions.py +++ b/api/src/opentrons/protocol_engine/errors/exceptions.py @@ -684,7 +684,9 @@ def __init__( wrapping: Optional[Sequence[EnumeratedError]] = None, ) -> None: """Build a HardwareNotSupportedError.""" - super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping) + super().__init__( + ErrorCodes.NOT_SUPPORTED_ON_ROBOT_TYPE, message, details, wrapping + ) class GripperNotAttachedError(ProtocolEngineError): @@ -789,3 +791,18 @@ def __init__( ) -> None: """Build an EStopActivatedError.""" super().__init__(ErrorCodes.E_STOP_ACTIVATED, message, details, wrapping) + + +class NotSupportedOnRobotType(ProtocolEngineError): + """Raised when attempting to perform an action that is not supported for the given robot type.""" + + def __init__( + self, + message: Optional[str] = None, + details: Optional[Dict[str, Any]] = None, + wrapping: Optional[Sequence[EnumeratedError]] = None, + ) -> None: + """Build a NotSupportedOnRobotType exception.""" + super().__init__( + ErrorCodes.NOT_SUPPORTED_ON_ROBOT_TYPE, message, details, wrapping + ) diff --git a/api/tests/opentrons/protocol_engine/commands/test_move_labware.py b/api/tests/opentrons/protocol_engine/commands/test_move_labware.py index a0ab7f9d7f4..6e444d9cf17 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_move_labware.py +++ b/api/tests/opentrons/protocol_engine/commands/test_move_labware.py @@ -5,7 +5,7 @@ from opentrons.types import DeckSlotName from opentrons.protocols.models import LabwareDefinition -from opentrons.protocol_engine import errors +from opentrons.protocol_engine import errors, Config from opentrons.protocol_engine.resources import labware_validation from opentrons.protocol_engine.types import ( DeckSlotLocation, @@ -15,6 +15,7 @@ LabwareMovementStrategy, LabwareOffsetVector, LabwareMovementOffsetData, + DeckType, ) from opentrons.protocol_engine.state import StateView from opentrons.protocol_engine.commands.move_labware import ( @@ -424,3 +425,44 @@ async def test_move_labware_raises_when_moving_adapter_with_gripper( with pytest.raises(errors.LabwareMovementNotAllowedError, match="gripper"): await subject.execute(data) + + +async def test_move_labware_with_gripper_raises_on_ot2( + decoy: Decoy, + equipment: EquipmentHandler, + labware_movement: LabwareMovementHandler, + state_view: StateView, + run_control: RunControlHandler, +) -> None: + """It should raise an error when using a gripper with robot type of OT2.""" + subject = MoveLabwareImplementation( + state_view=state_view, + equipment=equipment, + labware_movement=labware_movement, + run_control=run_control, + ) + data = MoveLabwareParams( + labwareId="my-cool-labware-id", + newLocation=DeckSlotLocation(slotName=DeckSlotName.SLOT_4), + strategy=LabwareMovementStrategy.USING_GRIPPER, + ) + decoy.when(state_view.labware.get(labware_id="my-cool-labware-id")).then_return( + LoadedLabware( + id="my-cool-labware-id", + loadName="load-name", + definitionUri="opentrons-test/load-name/1", + location=DeckSlotLocation(slotName=DeckSlotName.SLOT_4), + offsetId=None, + ) + ) + decoy.when( + state_view.labware.get_definition(labware_id="my-cool-labware-id") + ).then_return( + LabwareDefinition.construct(namespace="spacename") # type: ignore[call-arg] + ) + + decoy.when(state_view.config).then_return( + Config(robot_type="OT-2 Standard", deck_type=DeckType.OT2_STANDARD) + ) + with pytest.raises(errors.NotSupportedOnRobotType): + await subject.execute(data) diff --git a/shared-data/errors/definitions/1/errors.json b/shared-data/errors/definitions/1/errors.json index 8c749ae3383..fa3b99be8be 100644 --- a/shared-data/errors/definitions/1/errors.json +++ b/shared-data/errors/definitions/1/errors.json @@ -157,6 +157,10 @@ "4002": { "detail": "API Removed", "category": "generalError" + }, + "4003": { + "detail": "Not supported on this robot type", + "category": "generalError" } } } diff --git a/shared-data/python/opentrons_shared_data/errors/codes.py b/shared-data/python/opentrons_shared_data/errors/codes.py index d8327508029..fe7ea40c89c 100644 --- a/shared-data/python/opentrons_shared_data/errors/codes.py +++ b/shared-data/python/opentrons_shared_data/errors/codes.py @@ -69,6 +69,7 @@ class ErrorCodes(Enum): GENERAL_ERROR = _code_from_dict_entry("4000") ROBOT_IN_USE = _code_from_dict_entry("4001") API_REMOVED = _code_from_dict_entry("4002") + NOT_SUPPORTED_ON_ROBOT_TYPE = _code_from_dict_entry("4003") @classmethod @lru_cache(25)