Skip to content
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
8 changes: 4 additions & 4 deletions api/src/opentrons/protocol_api/core/engine/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,7 @@ def resin_tip_seal(
)
)

def resin_tip_unseal(self, location: Location, well_core: WellCore) -> None:
def resin_tip_unseal(self, location: Location | None, well_core: WellCore) -> None:
well_name = well_core.get_name()
labware_id = well_core.labware_id

Expand Down Expand Up @@ -1190,9 +1190,9 @@ def get_next_tip(
cmd.GetNextTipParams(
pipetteId=self._pipette_id,
labwareIds=[tip_rack.labware_id for tip_rack in valid_tip_racks],
startingTipWell=starting_well.get_name()
if starting_well is not None
else None,
startingTipWell=(
starting_well.get_name() if starting_well is not None else None
),
)
)
next_tip_info = result.nextTipInfo
Expand Down
2 changes: 1 addition & 1 deletion api/src/opentrons/protocol_api/core/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def resin_tip_seal(
@abstractmethod
def resin_tip_unseal(
self,
location: types.Location,
location: types.Location | None,
well_core: WellCoreType,
) -> None:
...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ def resin_tip_seal(

def resin_tip_unseal(
self,
location: types.Location,
location: types.Location | None,
well_core: WellCore,
) -> None:
raise APIVersionError(api_element="Unsealing resin tips.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def resin_tip_seal(

def resin_tip_unseal(
self,
location: types.Location,
location: types.Location | None,
well_core: WellCore,
) -> None:
raise APIVersionError(api_element="Unsealing resin tips.")
Expand Down
20 changes: 10 additions & 10 deletions api/src/opentrons/protocol_api/instrument_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -1713,9 +1713,9 @@ def transfer_with_liquid_class(
(types.Location(types.Point(), labware=rack), rack._core)
for rack in transfer_args.tip_racks
],
starting_tip=self.starting_tip._core
if self.starting_tip is not None
else None,
starting_tip=(
self.starting_tip._core if self.starting_tip is not None else None
),
trash_location=transfer_args.trash_location,
return_tip=return_tip,
)
Expand Down Expand Up @@ -1829,9 +1829,9 @@ def distribute_with_liquid_class(
(types.Location(types.Point(), labware=rack), rack._core)
for rack in transfer_args.tip_racks
],
starting_tip=self.starting_tip._core
if self.starting_tip is not None
else None,
starting_tip=(
self.starting_tip._core if self.starting_tip is not None else None
),
trash_location=transfer_args.trash_location,
return_tip=return_tip,
)
Expand Down Expand Up @@ -1945,9 +1945,9 @@ def consolidate_with_liquid_class(
(types.Location(types.Point(), labware=rack), rack._core)
for rack in transfer_args.tip_racks
],
starting_tip=self.starting_tip._core
if self.starting_tip is not None
else None,
starting_tip=(
self.starting_tip._core if self.starting_tip is not None else None
),
trash_location=transfer_args.trash_location,
return_tip=return_tip,
)
Expand Down Expand Up @@ -2121,7 +2121,7 @@ def resin_tip_unseal(
location=well,
),
):
self._core.resin_tip_unseal(location=well.top(), well_core=well._core)
self._core.resin_tip_unseal(location=None, well_core=well._core)

return self

Expand Down
6 changes: 6 additions & 0 deletions api/src/opentrons/protocol_engine/commands/liquid_probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,12 @@ async def _execute_common( # noqa: C901
well_name=well_name,
well_location=params.wellLocation,
)
state_view.geometry.validate_probed_height(
labware_id=labware_id,
well_name=well_name,
pipette_id=pipette_id,
probed_height=z_pos,
)
except PipetteLiquidNotFoundError as exception:
move_result.state_update.set_pipette_ready_to_aspirate(
pipette_id=pipette_id, ready_to_aspirate=True
Expand Down
67 changes: 44 additions & 23 deletions api/src/opentrons/protocol_engine/commands/seal_pipette_to_tip.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
"""Seal tips to pipette command request, result, and implementation models."""

from __future__ import annotations
from pydantic import Field, BaseModel
from typing import TYPE_CHECKING, Optional, Type, Union
from opentrons.types import MountType
from opentrons.protocol_engine.types import MotorAxis

from typing_extensions import Literal
from pydantic import Field, BaseModel

from opentrons_shared_data.errors.exceptions import PositionUnknownError

from opentrons.types import MountType
from opentrons.protocol_engine.types import MotorAxis
from ..resources import ModelUtils, ensure_ot3_hardware
from ..types import PickUpTipWellLocation, FluidKind, AspiratedFluid
from .pipetting_common import (
Expand All @@ -27,7 +30,6 @@

from opentrons.hardware_control import HardwareControlAPI
from opentrons.hardware_control.types import Axis
from ..state.update_types import StateUpdate

if TYPE_CHECKING:
from ..state.state import StateView
Expand Down Expand Up @@ -138,28 +140,50 @@ async def relative_pickup_tip(
"""A relative press-fit pick up command using gantry moves."""
prep_distance = tip_pick_up_params.prepDistance
press_distance = tip_pick_up_params.pressDistance
retract_distance = -1 * (prep_distance + press_distance)
retract_distance = -1 * (press_distance) / 2

mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z

ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
# TODO chb, 2025-01-29): Factor out the movement constants and relocate this logic into the hardware controller
await self._gantry_mover.move_axes(
axis_map={mount_axis: prep_distance}, speed=10, relative_move=True
try:
await self._gantry_mover.move_axes(
axis_map={mount_axis: prep_distance},
speed=10,
relative_move=True,
expect_stalls=True,
)
except PositionUnknownError:
# if this happens it's from the get position after the move and we can ignore it
pass

await ot3_hardware_api.update_axis_position_estimations(
self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
)

# Drive mount down for press-fit
await self._gantry_mover.move_axes(
axis_map={mount_axis: press_distance},
speed=10.0,
relative_move=True,
expect_stalls=True,
)
# retract cam : 11.05
await self._gantry_mover.move_axes(
axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
try:
await self._gantry_mover.move_axes(
axis_map={mount_axis: press_distance},
speed=10.0,
relative_move=True,
expect_stalls=True,
)
except PositionUnknownError:
# if this happens it's from the get position after the move and we can ignore it
pass

await ot3_hardware_api.update_axis_position_estimations(
self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
)

ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
try:
await self._gantry_mover.move_axes(
axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
)
except PositionUnknownError:
# if this happens it's from the get position after the move and we can ignore it
pass

await ot3_hardware_api.update_axis_position_estimations(
self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
)
Expand Down Expand Up @@ -283,13 +307,10 @@ async def execute(
if hw_instr is not None:
hw_instr.set_current_volume(_SAFE_TOP_VOLUME)

state_update = StateUpdate()
state_update.update_pipette_tip_state(
state_update = move_result.state_update.update_pipette_tip_state(
pipette_id=pipette_id,
tip_geometry=tip_geometry,
)

state_update.set_fluid_aspirated(
).set_fluid_aspirated(
pipette_id=pipette_id,
fluid=AspiratedFluid(kind=FluidKind.LIQUID, volume=_SAFE_TOP_VOLUME),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
from __future__ import annotations

from pydantic import Field
from typing import TYPE_CHECKING, Optional, Type
from typing import TYPE_CHECKING, Optional, Type, Final
from typing_extensions import Literal

from opentrons.protocol_engine.resources.model_utils import ModelUtils
from opentrons.protocol_engine.types import MotorAxis
from opentrons.types import MountType

from ..types import DropTipWellLocation
from .pipetting_common import (
Expand Down Expand Up @@ -56,6 +54,8 @@ class UnsealPipetteFromTipResult(DestinationPositionResult):
SuccessData[UnsealPipetteFromTipResult] | DefinedErrorData[StallOrCollisionError]
)

CUSTOM_TIP_LENGTH_MARGIN: Final = 10


class UnsealPipetteFromTipImplementation(
AbstractCommandImpl[UnsealPipetteFromTipParams, _ExecuteReturn]
Expand Down Expand Up @@ -85,6 +85,10 @@ async def execute(self, params: UnsealPipetteFromTipParams) -> _ExecuteReturn:

well_location = params.wellLocation

tip_geometry = self._state_view.geometry.get_nominal_tip_geometry(
pipette_id, labware_id, well_name
)

is_partially_configured = self._state_view.pipettes.get_is_partially_configured(
pipette_id=pipette_id
)
Expand All @@ -93,6 +97,7 @@ async def execute(self, params: UnsealPipetteFromTipParams) -> _ExecuteReturn:
labware_id=labware_id,
well_location=well_location,
partially_configured=is_partially_configured,
override_default_offset=-(tip_geometry.length - CUSTOM_TIP_LENGTH_MARGIN),
)

move_result = await move_to_well(
Expand All @@ -106,14 +111,6 @@ async def execute(self, params: UnsealPipetteFromTipParams) -> _ExecuteReturn:
if isinstance(move_result, DefinedErrorData):
return move_result

# Move to an appropriate position
mount = self._state_view.pipettes.get_mount(pipette_id)

mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
await self._gantry_mover.move_axes(
axis_map={mount_axis: -14}, speed=10, relative_move=True
)

await self._tip_handler.drop_tip(
pipette_id=pipette_id,
home_after=None,
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 @@ -88,6 +88,7 @@
OffsetLocationInvalidError,
FlexStackerLabwarePoolNotYetDefinedError,
FlexStackerNotLogicallyEmptyError,
InvalidLabwarePositionError,
)

from .error_occurrence import ErrorOccurrence, ProtocolCommandFailedError
Expand Down Expand Up @@ -171,6 +172,7 @@
"OffsetLocationInvalidError",
"FlexStackerLabwarePoolNotYetDefinedError",
"FlexStackerNotLogicallyEmptyError",
"InvalidLabwarePositionError",
# error occurrence models
"ErrorOccurrence",
"CommandNotAllowedError",
Expand Down
12 changes: 12 additions & 0 deletions api/src/opentrons/protocol_engine/errors/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1281,3 +1281,15 @@ def __init__(
wrapping: Optional[Sequence[EnumeratedError]] = None,
) -> None:
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)


class InvalidLabwarePositionError(ProtocolEngineError):
"""Raised when a labware position is internally invalid."""

def __init__(
self,
message: Optional[str] = None,
details: Optional[dict[str, Any]] = None,
wrapping: Optional[Sequence[EnumeratedError]] = None,
) -> None:
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
2 changes: 1 addition & 1 deletion api/src/opentrons/protocol_engine/execution/pipetting.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ async def liquid_probe_in_place(
well_name: str,
well_location: WellLocation,
) -> LiquidTrackingType:
"""Detect liquid level."""
"""Return liquid level relative to the bottom of the well."""
hw_pipette = self._state_view.pipettes.get_hardware_pipette(
pipette_id=pipette_id,
attached_pipettes=self._hardware_api.attached_instruments,
Expand Down
Loading
Loading