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

feat(api): RobotContext: Add gripper commands #16752

Open
wants to merge 39 commits into
base: edge
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
74c4e0d
feat: add robot core
Laura-Danielle Sep 9, 2024
458d94a
feat: add protocol engine commands for robot movement commands
Laura-Danielle Sep 9, 2024
5690eb1
feat: add new axis types
Laura-Danielle Sep 9, 2024
d55cbd1
feat: Expose additional movement options in gantry mover and movement
Laura-Danielle Sep 17, 2024
7c69b62
feat: add robot core
Laura-Danielle Sep 17, 2024
37b8a21
command union stuff
Laura-Danielle Sep 17, 2024
155f843
add validation and shared types
Laura-Danielle Sep 17, 2024
c3a616a
some lint fixes for typing conflicts in robot_context
CaseyBatten Sep 18, 2024
cf1c63b
test: relative moves are moving in absolute
Laura-Danielle Oct 8, 2024
74d32e3
refactor: make critical point optional
Laura-Danielle Oct 8, 2024
29d1a71
use the right MotorAxis key
Laura-Danielle Oct 8, 2024
e0bf91c
fix: correctly handle Q motor moves in the function and add a test
Laura-Danielle Oct 17, 2024
20ed5b3
pass the right locations from gantry mover to the hardware controller…
Laura-Danielle Oct 17, 2024
04320d3
adding protocol engine command tests
Laura-Danielle Oct 17, 2024
c8cfbc8
add logs for motion utilities
Laura-Danielle Oct 17, 2024
2e5fd84
fix move_axes_to move, add more logs for move_axes_relative
Laura-Danielle Oct 21, 2024
2c87712
make machine to deck conversion func public hw API
Laura-Danielle Oct 21, 2024
e78c72e
fix tests, lint and formatting
Laura-Danielle Nov 1, 2024
6362d77
fixups from rebase
Laura-Danielle Nov 1, 2024
89d9c9e
fix move group runner changes
Laura-Danielle Nov 1, 2024
fd53a8e
fix failing linters and formatting
Laura-Danielle Nov 2, 2024
d0a3202
fix more linter errors
Laura-Danielle Nov 3, 2024
7ed333b
fix linter errors
Laura-Danielle Nov 3, 2024
666b705
feat: add robot core
Laura-Danielle Sep 9, 2024
3b62f19
feat: add protocol engine commands for robot movement commands
Laura-Danielle Sep 9, 2024
e070a99
feat: Expose additional movement options in gantry mover and movement
Laura-Danielle Sep 17, 2024
da0abad
feat: add robot core
Laura-Danielle Sep 17, 2024
8e81aec
some lint fixes for typing conflicts in robot_context
CaseyBatten Sep 18, 2024
f3e3006
add plunger positions to PipetteDict
Laura-Danielle Oct 29, 2024
ac1de65
add pipette plunger conversion functions
Laura-Danielle Oct 29, 2024
e3466a5
fix pip dict key error
Laura-Danielle Oct 30, 2024
b97af4b
fix pip dict related commands
Laura-Danielle Oct 30, 2024
308d1f5
add tests
Laura-Danielle Nov 1, 2024
cef72e8
actually fix plunger movement
Laura-Danielle Nov 1, 2024
6800486
fix up formatting, lint and tests
Laura-Danielle Nov 3, 2024
736370a
feat(robot-context): Add gripper commands
Laura-Danielle Nov 5, 2024
e069605
rebase fixup
Laura-Danielle Nov 21, 2024
57d1c3c
more fixups from rebase, aligning branch with edge
Laura-Danielle Nov 21, 2024
5e6bd66
add gripper commands to schema 11
Laura-Danielle Nov 21, 2024
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
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
Loading