Skip to content

Commit

Permalink
feat(api): RobotContext: Add gripper commands (#16752)
Browse files Browse the repository at this point in the history
  • Loading branch information
Laura-Danielle authored Nov 26, 2024
1 parent 58903d4 commit 825e2af
Show file tree
Hide file tree
Showing 11 changed files with 359 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ async def grip(
) -> None:
...

async def home_gripper_jaw(self) -> None:
...

async def ungrip(self, force_newtons: Optional[float] = None) -> None:
"""Release gripped object.
Expand Down
8 changes: 8 additions & 0 deletions api/src/opentrons/protocol_api/core/engine/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,11 @@ def move_axes_relative(self, axis_map: AxisMapType, speed: Optional[float]) -> N
self._engine_client.execute_command(
cmd.robot.MoveAxesRelativeParams(axis_map=axis_engine_map, speed=speed)
)

def release_grip(self) -> None:
self._engine_client.execute_command(cmd.robot.openGripperJawParams())

def close_gripper(self, force: Optional[float] = None) -> None:
self._engine_client.execute_command(
cmd.robot.closeGripperJawParams(force=force)
)
8 changes: 8 additions & 0 deletions api/src/opentrons/protocol_api/core/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@ def move_axes_to(
@abstractmethod
def move_axes_relative(self, axis_map: AxisMapType, speed: Optional[float]) -> None:
...

@abstractmethod
def release_grip(self) -> None:
...

@abstractmethod
def close_gripper(self, force: Optional[float] = None) -> None:
...
8 changes: 5 additions & 3 deletions api/src/opentrons/protocol_api/robot_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,13 @@ def move_axes_relative(
)
self._core.move_axes_relative(axis_map, speed)

def close_gripper_jaw(self, force: float) -> None:
raise NotImplementedError()
def close_gripper_jaw(self, force: Optional[float] = None) -> None:
"""Command the gripper closed with some force."""
self._core.close_gripper(force)

def open_gripper_jaw(self) -> None:
raise NotImplementedError()
"""Command the gripper open."""
self._core.release_grip()

def axis_coordinates_for(
self,
Expand Down
10 changes: 10 additions & 0 deletions api/src/opentrons/protocol_engine/commands/command_unions.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,8 @@
robot.MoveTo,
robot.MoveAxesRelative,
robot.MoveAxesTo,
robot.openGripperJaw,
robot.closeGripperJaw,
],
Field(discriminator="commandType"),
]
Expand Down Expand Up @@ -499,6 +501,8 @@
robot.MoveAxesRelativeParams,
robot.MoveAxesToParams,
robot.MoveToParams,
robot.openGripperJawParams,
robot.closeGripperJawParams,
]

CommandType = Union[
Expand Down Expand Up @@ -580,6 +584,8 @@
robot.MoveAxesRelativeCommandType,
robot.MoveAxesToCommandType,
robot.MoveToCommandType,
robot.openGripperJawCommandType,
robot.closeGripperJawCommandType,
]

CommandCreate = Annotated[
Expand Down Expand Up @@ -662,6 +668,8 @@
robot.MoveAxesRelativeCreate,
robot.MoveAxesToCreate,
robot.MoveToCreate,
robot.openGripperJawCreate,
robot.closeGripperJawCreate,
],
Field(discriminator="commandType"),
]
Expand Down Expand Up @@ -745,6 +753,8 @@
robot.MoveAxesRelativeResult,
robot.MoveAxesToResult,
robot.MoveToResult,
robot.openGripperJawResult,
robot.closeGripperJawResult,
]


Expand Down
26 changes: 26 additions & 0 deletions api/src/opentrons/protocol_engine/commands/robot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@
MoveAxesRelativeResult,
MoveAxesRelativeCommandType,
)
from .open_gripper_jaw import (
openGripperJaw,
openGripperJawCreate,
openGripperJawParams,
openGripperJawResult,
openGripperJawCommandType,
)
from .close_gripper_jaw import (
closeGripperJaw,
closeGripperJawCreate,
closeGripperJawParams,
closeGripperJawResult,
closeGripperJawCommandType,
)

__all__ = [
# robot/moveTo
Expand All @@ -41,4 +55,16 @@
"MoveAxesRelativeParams",
"MoveAxesRelativeResult",
"MoveAxesRelativeCommandType",
# robot/openGripperJaw
"openGripperJaw",
"openGripperJawCreate",
"openGripperJawParams",
"openGripperJawResult",
"openGripperJawCommandType",
# robot/closeGripperJaw
"closeGripperJaw",
"closeGripperJawCreate",
"closeGripperJawParams",
"closeGripperJawResult",
"closeGripperJawCommandType",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Command models for opening a gripper jaw."""
from __future__ import annotations
from typing import Literal, Type, Optional
from opentrons.hardware_control import HardwareControlAPI
from opentrons.protocol_engine.resources import ensure_ot3_hardware

from pydantic import BaseModel, Field

from ..command import (
AbstractCommandImpl,
BaseCommand,
BaseCommandCreate,
SuccessData,
)
from opentrons.protocol_engine.errors.error_occurrence import ErrorOccurrence


closeGripperJawCommandType = Literal["robot/closeGripperJaw"]


class closeGripperJawParams(BaseModel):
"""Payload required to close a gripper."""

force: Optional[float] = Field(
default=None,
description="The force the gripper should use to hold the jaws, falls to default if none is provided.",
)


class closeGripperJawResult(BaseModel):
"""Result data from the execution of a closeGripperJaw command."""

pass


class closeGripperJawImplementation(
AbstractCommandImpl[closeGripperJawParams, SuccessData[closeGripperJawResult]]
):
"""closeGripperJaw command implementation."""

def __init__(
self,
hardware_api: HardwareControlAPI,
**kwargs: object,
) -> None:
self._hardware_api = hardware_api

async def execute(
self, params: closeGripperJawParams
) -> SuccessData[closeGripperJawResult]:
"""Release the gripper."""
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
await ot3_hardware_api.grip(force_newtons=params.force)
return SuccessData(
public=closeGripperJawResult(),
)


class closeGripperJaw(
BaseCommand[closeGripperJawParams, closeGripperJawResult, ErrorOccurrence]
):
"""closeGripperJaw command model."""

commandType: closeGripperJawCommandType = "robot/closeGripperJaw"
params: closeGripperJawParams
result: Optional[closeGripperJawResult]

_ImplementationCls: Type[
closeGripperJawImplementation
] = closeGripperJawImplementation


class closeGripperJawCreate(BaseCommandCreate[closeGripperJawParams]):
"""closeGripperJaw command request model."""

commandType: closeGripperJawCommandType = "robot/closeGripperJaw"
params: closeGripperJawParams

_CommandCls: Type[closeGripperJaw] = closeGripperJaw
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""Command models for opening a gripper jaw."""
from __future__ import annotations
from typing import Literal, Type, Optional
from opentrons.hardware_control import HardwareControlAPI
from opentrons.protocol_engine.resources import ensure_ot3_hardware

from pydantic import BaseModel

from ..command import (
AbstractCommandImpl,
BaseCommand,
BaseCommandCreate,
SuccessData,
)
from opentrons.protocol_engine.errors.error_occurrence import ErrorOccurrence


openGripperJawCommandType = Literal["robot/openGripperJaw"]


class openGripperJawParams(BaseModel):
"""Payload required to release a gripper."""

pass


class openGripperJawResult(BaseModel):
"""Result data from the execution of a openGripperJaw command."""

pass


class openGripperJawImplementation(
AbstractCommandImpl[openGripperJawParams, SuccessData[openGripperJawResult]]
):
"""openGripperJaw command implementation."""

def __init__(
self,
hardware_api: HardwareControlAPI,
**kwargs: object,
) -> None:
self._hardware_api = hardware_api

async def execute(
self, params: openGripperJawParams
) -> SuccessData[openGripperJawResult]:
"""Release the gripper."""
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)

await ot3_hardware_api.home_gripper_jaw()
return SuccessData(
public=openGripperJawResult(),
)


class openGripperJaw(
BaseCommand[openGripperJawParams, openGripperJawResult, ErrorOccurrence]
):
"""openGripperJaw command model."""

commandType: openGripperJawCommandType = "robot/openGripperJaw"
params: openGripperJawParams
result: Optional[openGripperJawResult]

_ImplementationCls: Type[
openGripperJawImplementation
] = openGripperJawImplementation


class openGripperJawCreate(BaseCommandCreate[openGripperJawParams]):
"""openGripperJaw command request model."""

commandType: openGripperJawCommandType = "robot/openGripperJaw"
params: openGripperJawParams

_CommandCls: Type[openGripperJaw] = openGripperJaw
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Test robot.open-gripper-jaw commands."""
from decoy import Decoy

from opentrons.hardware_control import OT3HardwareControlAPI

from opentrons.protocol_engine.commands.command import SuccessData
from opentrons.protocol_engine.commands.robot.close_gripper_jaw import (
closeGripperJawParams,
closeGripperJawResult,
closeGripperJawImplementation,
)


async def test_close_gripper_jaw_implementation(
decoy: Decoy,
ot3_hardware_api: OT3HardwareControlAPI,
) -> None:
"""Test the `robot.closeGripperJaw` implementation."""
subject = closeGripperJawImplementation(
hardware_api=ot3_hardware_api,
)

params = closeGripperJawParams(force=10)

result = await subject.execute(params=params)

assert result == SuccessData(public=closeGripperJawResult())
decoy.verify(await ot3_hardware_api.grip(force_newtons=10))
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Test robot.open-gripper-jaw commands."""
from decoy import Decoy

from opentrons.hardware_control import OT3HardwareControlAPI

from opentrons.protocol_engine.commands.command import SuccessData
from opentrons.protocol_engine.commands.robot.open_gripper_jaw import (
openGripperJawParams,
openGripperJawResult,
openGripperJawImplementation,
)


async def test_open_gripper_jaw_implementation(
decoy: Decoy,
ot3_hardware_api: OT3HardwareControlAPI,
) -> None:
"""Test the `robot.openGripperJaw` implementation."""
subject = openGripperJawImplementation(
hardware_api=ot3_hardware_api,
)

params = openGripperJawParams()

result = await subject.execute(params=params)

assert result == SuccessData(public=openGripperJawResult())
decoy.verify(await ot3_hardware_api.home_gripper_jaw())
Loading

0 comments on commit 825e2af

Please sign in to comment.