Skip to content

Commit

Permalink
Merge branch 'edge' into app_system-language-modal
Browse files Browse the repository at this point in the history
  • Loading branch information
brenthagen committed Oct 17, 2024
2 parents e90b5f7 + 61e886d commit ccb395d
Show file tree
Hide file tree
Showing 210 changed files with 5,081 additions and 1,099 deletions.
2 changes: 0 additions & 2 deletions api/src/opentrons/protocol_engine/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,6 @@
ConfigureNozzleLayoutCreate,
ConfigureNozzleLayoutParams,
ConfigureNozzleLayoutResult,
ConfigureNozzleLayoutPrivateResult,
ConfigureNozzleLayoutCommandType,
)

Expand Down Expand Up @@ -569,7 +568,6 @@
"ConfigureNozzleLayoutParams",
"ConfigureNozzleLayoutResult",
"ConfigureNozzleLayoutCommandType",
"ConfigureNozzleLayoutPrivateResult",
# get pipette tip presence bundle
"GetTipPresence",
"GetTipPresenceCreate",
Expand Down
4 changes: 2 additions & 2 deletions api/src/opentrons/protocol_engine/commands/command_unions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .pipetting_common import (
OverpressureError,
LiquidNotFoundError,
TipPhysicallyAttachedError,
)

from . import absorbance_reader
Expand Down Expand Up @@ -288,7 +289,6 @@
ConfigureNozzleLayoutParams,
ConfigureNozzleLayoutResult,
ConfigureNozzleLayoutCommandType,
ConfigureNozzleLayoutPrivateResult,
)

from .verify_tip_presence import (
Expand Down Expand Up @@ -709,12 +709,12 @@
None,
LoadPipettePrivateResult,
ConfigureForVolumePrivateResult,
ConfigureNozzleLayoutPrivateResult,
]

# All `DefinedErrorData`s that implementations will actually return in practice.
CommandDefinedErrorData = Union[
DefinedErrorData[TipPhysicallyMissingError],
DefinedErrorData[TipPhysicallyAttachedError],
DefinedErrorData[OverpressureError],
DefinedErrorData[LiquidNotFoundError],
DefinedErrorData[GripperMovementError],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
)
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
from ..errors.error_occurrence import ErrorOccurrence
from .configuring_common import (
PipetteNozzleLayoutResultMixin,
)
from ..types import (
AllNozzleLayoutConfiguration,
SingleNozzleLayoutConfiguration,
Expand Down Expand Up @@ -40,12 +37,6 @@ class ConfigureNozzleLayoutParams(PipetteIdMixin):
]


class ConfigureNozzleLayoutPrivateResult(PipetteNozzleLayoutResultMixin):
"""Result sent to the store but not serialized."""

pass


class ConfigureNozzleLayoutResult(BaseModel):
"""Result data from execution of an configureNozzleLayout command."""

Expand All @@ -55,7 +46,7 @@ class ConfigureNozzleLayoutResult(BaseModel):
class ConfigureNozzleLayoutImplementation(
AbstractCommandImpl[
ConfigureNozzleLayoutParams,
SuccessData[ConfigureNozzleLayoutResult, ConfigureNozzleLayoutPrivateResult],
SuccessData[ConfigureNozzleLayoutResult, None],
]
):
"""Configure nozzle layout command implementation."""
Expand All @@ -68,7 +59,7 @@ def __init__(

async def execute(
self, params: ConfigureNozzleLayoutParams
) -> SuccessData[ConfigureNozzleLayoutResult, ConfigureNozzleLayoutPrivateResult]:
) -> SuccessData[ConfigureNozzleLayoutResult, None]:
"""Check that requested pipette can support the requested nozzle layout."""
primary_nozzle = params.configurationParams.dict().get("primaryNozzle")
front_right_nozzle = params.configurationParams.dict().get("frontRightNozzle")
Expand All @@ -93,10 +84,7 @@ async def execute(

return SuccessData(
public=ConfigureNozzleLayoutResult(),
private=ConfigureNozzleLayoutPrivateResult(
pipette_id=params.pipetteId,
nozzle_map=nozzle_map,
),
private=None,
state_update=update_state,
)

Expand Down
13 changes: 0 additions & 13 deletions api/src/opentrons/protocol_engine/commands/configuring_common.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
"""Common configuration command base models."""

from dataclasses import dataclass
from opentrons.hardware_control.nozzle_manager import (
NozzleMap,
)
from ..resources import pipette_data_provider


Expand All @@ -14,13 +11,3 @@ class PipetteConfigUpdateResultMixin:
pipette_id: str
serial_number: str
config: pipette_data_provider.LoadedStaticPipetteData


@dataclass
class PipetteNozzleLayoutResultMixin:
"""A nozzle layout result for updating the pipette state."""

pipette_id: str

nozzle_map: NozzleMap
"""A dataclass object holding information about the current nozzle configuration."""
67 changes: 50 additions & 17 deletions api/src/opentrons/protocol_engine/commands/drop_tip.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,23 @@
from typing import TYPE_CHECKING, Optional, Type
from typing_extensions import Literal

from opentrons.protocol_engine.errors.exceptions import TipAttachedError
from opentrons.protocol_engine.resources.model_utils import ModelUtils

from ..state import update_types
from ..types import DropTipWellLocation, DeckPoint
from .pipetting_common import PipetteIdMixin, DestinationPositionResult
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
from .pipetting_common import (
PipetteIdMixin,
DestinationPositionResult,
TipPhysicallyAttachedError,
)
from .command import (
AbstractCommandImpl,
BaseCommand,
BaseCommandCreate,
DefinedErrorData,
SuccessData,
)
from ..errors.error_occurrence import ErrorOccurrence

if TYPE_CHECKING:
Expand Down Expand Up @@ -54,23 +67,28 @@ class DropTipResult(DestinationPositionResult):
pass


class DropTipImplementation(
AbstractCommandImpl[DropTipParams, SuccessData[DropTipResult, None]]
):
_ExecuteReturn = (
SuccessData[DropTipResult, None] | DefinedErrorData[TipPhysicallyAttachedError]
)


class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
"""Drop tip command implementation."""

def __init__(
self,
state_view: StateView,
tip_handler: TipHandler,
movement: MovementHandler,
model_utils: ModelUtils,
**kwargs: object,
) -> None:
self._state_view = state_view
self._tip_handler = tip_handler
self._movement_handler = movement
self._model_utils = model_utils

async def execute(self, params: DropTipParams) -> SuccessData[DropTipResult, None]:
async def execute(self, params: DropTipParams) -> _ExecuteReturn:
"""Move to and drop a tip using the requested pipette."""
pipette_id = params.pipetteId
labware_id = params.labwareId
Expand Down Expand Up @@ -112,17 +130,32 @@ async def execute(self, params: DropTipParams) -> SuccessData[DropTipResult, Non
new_deck_point=deck_point,
)

await self._tip_handler.drop_tip(pipette_id=pipette_id, home_after=home_after)

state_update.update_pipette_tip_state(
pipette_id=params.pipetteId, tip_geometry=None
)

return SuccessData(
public=DropTipResult(position=deck_point),
private=None,
state_update=state_update,
)
try:
await self._tip_handler.drop_tip(
pipette_id=pipette_id, home_after=home_after
)
except TipAttachedError as exception:
error = TipPhysicallyAttachedError(
id=self._model_utils.generate_id(),
createdAt=self._model_utils.get_timestamp(),
wrappedErrors=[
ErrorOccurrence.from_failed(
id=self._model_utils.generate_id(),
createdAt=self._model_utils.get_timestamp(),
error=exception,
)
],
)
return DefinedErrorData(public=error, state_update=state_update)
else:
state_update.update_pipette_tip_state(
pipette_id=params.pipetteId, tip_geometry=None
)
return SuccessData(
public=DropTipResult(position=deck_point),
private=None,
state_update=state_update,
)


class DropTip(BaseCommand[DropTipParams, DropTipResult, ErrorOccurrence]):
Expand Down
63 changes: 45 additions & 18 deletions api/src/opentrons/protocol_engine/commands/drop_tip_in_place.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
"""Drop tip in place command request, result, and implementation models."""
from __future__ import annotations
from opentrons.protocol_engine.state import update_types
from pydantic import Field, BaseModel
from typing import TYPE_CHECKING, Optional, Type
from typing_extensions import Literal

from .pipetting_common import PipetteIdMixin
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
from .command import (
AbstractCommandImpl,
BaseCommand,
BaseCommandCreate,
DefinedErrorData,
SuccessData,
)
from .pipetting_common import PipetteIdMixin, TipPhysicallyAttachedError
from ..errors.exceptions import TipAttachedError
from ..errors.error_occurrence import ErrorOccurrence
from ..resources.model_utils import ModelUtils
from ..state import update_types

if TYPE_CHECKING:
from ..execution import TipHandler
Expand Down Expand Up @@ -35,35 +43,54 @@ class DropTipInPlaceResult(BaseModel):
pass


_ExecuteReturn = (
SuccessData[DropTipInPlaceResult, None]
| DefinedErrorData[TipPhysicallyAttachedError]
)


class DropTipInPlaceImplementation(
AbstractCommandImpl[DropTipInPlaceParams, SuccessData[DropTipInPlaceResult, None]]
AbstractCommandImpl[DropTipInPlaceParams, _ExecuteReturn]
):
"""Drop tip in place command implementation."""

def __init__(
self,
tip_handler: TipHandler,
model_utils: ModelUtils,
**kwargs: object,
) -> None:
self._tip_handler = tip_handler
self._model_utils = model_utils

async def execute(
self, params: DropTipInPlaceParams
) -> SuccessData[DropTipInPlaceResult, None]:
async def execute(self, params: DropTipInPlaceParams) -> _ExecuteReturn:
"""Drop a tip using the requested pipette."""
await self._tip_handler.drop_tip(
pipette_id=params.pipetteId, home_after=params.homeAfter
)

state_update = update_types.StateUpdate()

state_update.update_pipette_tip_state(
pipette_id=params.pipetteId, tip_geometry=None
)

return SuccessData(
public=DropTipInPlaceResult(), private=None, state_update=state_update
)
try:
await self._tip_handler.drop_tip(
pipette_id=params.pipetteId, home_after=params.homeAfter
)
except TipAttachedError as exception:
error = TipPhysicallyAttachedError(
id=self._model_utils.generate_id(),
createdAt=self._model_utils.get_timestamp(),
wrappedErrors=[
ErrorOccurrence.from_failed(
id=self._model_utils.generate_id(),
createdAt=self._model_utils.get_timestamp(),
error=exception,
)
],
)
return DefinedErrorData(public=error, state_update=state_update)
else:
state_update.update_pipette_tip_state(
pipette_id=params.pipetteId, tip_geometry=None
)
return SuccessData(
public=DropTipInPlaceResult(), private=None, state_update=state_update
)


class DropTipInPlace(
Expand Down
16 changes: 16 additions & 0 deletions api/src/opentrons/protocol_engine/commands/pipetting_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,19 @@ class LiquidNotFoundError(ErrorOccurrence):

errorCode: str = ErrorCodes.PIPETTE_LIQUID_NOT_FOUND.value.code
detail: str = ErrorCodes.PIPETTE_LIQUID_NOT_FOUND.value.detail


class TipPhysicallyAttachedError(ErrorOccurrence):
"""Returned when sensors determine that a tip remains on the pipette after a drop attempt.
The pipette will act as if the tip was not dropped. So, you won't be able to pick
up a new tip without dropping the current one, and movement commands will assume
there is a tip hanging off the bottom of the pipette.
"""

isDefined: bool = True

errorType: Literal["tipPhysicallyAttached"] = "tipPhysicallyAttached"

errorCode: str = ErrorCodes.TIP_DROP_FAILED.value.code
detail: str = ErrorCodes.TIP_DROP_FAILED.value.detail
Loading

0 comments on commit ccb395d

Please sign in to comment.