Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(api, shared-data): raise error when using gripper in ot2 protocols #13208

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion api/src/opentrons/protocol_engine/commands/move_labware.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add like "action": "gripper-move-labware" or something to details?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between that and putting "Labware movement using a gripper is not supported on the OT2" in message?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing :) it's just nice to have both

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
):
Expand Down
2 changes: 2 additions & 0 deletions api/src/opentrons/protocol_engine/errors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
LabwareMovementNotAllowedError,
LocationIsOccupiedError,
InvalidAxisForRobotType,
NotSupportedOnRobotType,
)

from .error_occurrence import ErrorOccurrence, ProtocolCommandFailedError
Expand Down Expand Up @@ -114,6 +115,7 @@
"LabwareMovementNotAllowedError",
"LocationIsOccupiedError",
"InvalidAxisForRobotType",
"NotSupportedOnRobotType",
# error occurrence models
"ErrorOccurrence",
]
19 changes: 18 additions & 1 deletion api/src/opentrons/protocol_engine/errors/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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."""
Comment on lines +796 to +797
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between HardwareNotSupportedError and NotSupportedOnRobotType?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HardwareNotSupportedError is raised after checking the specific hardware API type and will only be able to be checked when running on an actual robot (or emulator), while NotSupportedOnRobotType checks only the robot type specified for the particular protocol/ engine. Subtle difference but an analysis saying 'not supported on hardware' sounds kinda misleading.


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
)
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -15,6 +15,7 @@
LabwareMovementStrategy,
LabwareOffsetVector,
LabwareMovementOffsetData,
DeckType,
)
from opentrons.protocol_engine.state import StateView
from opentrons.protocol_engine.commands.move_labware import (
Expand Down Expand Up @@ -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)
4 changes: 4 additions & 0 deletions shared-data/errors/definitions/1/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@
"4002": {
"detail": "API Removed",
"category": "generalError"
},
"4003": {
"detail": "Not supported on this robot type",
"category": "generalError"
}
}
}
1 change: 1 addition & 0 deletions shared-data/python/opentrons_shared_data/errors/codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down