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): disable return tip for partially configured pipette #13972

Merged
merged 5 commits into from
Nov 16, 2023
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
6 changes: 6 additions & 0 deletions api/src/opentrons/protocol_api/core/engine/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

from opentrons_shared_data.pipette.dev_types import PipetteNameType
from opentrons.protocol_api._nozzle_layout import NozzleLayout
from opentrons.hardware_control.nozzle_manager import NozzleConfigurationType

from ..instrument import AbstractInstrument
from .well import WellCore
Expand Down Expand Up @@ -574,6 +575,11 @@ def get_dispense_flow_rate(self, rate: float = 1.0) -> float:
def get_blow_out_flow_rate(self, rate: float = 1.0) -> float:
return self._blow_out_flow_rate * rate

def get_nozzle_configuration(self) -> NozzleConfigurationType:
return self._engine_client.state.pipettes.get_nozzle_layout_type(
self._pipette_id
)

def set_flow_rate(
self,
aspirate: Optional[float] = None,
Expand Down
8 changes: 7 additions & 1 deletion api/src/opentrons/protocol_engine/commands/drop_tip.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,14 @@ async def execute(self, params: DropTipParams) -> DropTipResult:
else:
well_location = params.wellLocation

is_partially_configured = self._state_view.pipettes.get_is_partially_configured(
pipette_id=pipette_id
)
tip_drop_location = self._state_view.geometry.get_checked_tip_drop_location(
pipette_id=pipette_id, labware_id=labware_id, well_location=well_location
pipette_id=pipette_id,
labware_id=labware_id,
well_location=well_location,
partially_configured=is_partially_configured,
)

position = await self._movement_handler.move_to_well(
Expand Down
8 changes: 8 additions & 0 deletions api/src/opentrons/protocol_engine/state/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,12 +416,20 @@ def get_checked_tip_drop_location(
pipette_id: str,
labware_id: str,
well_location: DropTipWellLocation,
partially_configured: bool = False,
) -> WellLocation:
"""Get tip drop location given labware and hardware pipette.

This makes sure that the well location has an appropriate origin & offset
if one is not already set previously.
"""
if (
self._labware.get_definition(labware_id).parameters.isTiprack
and partially_configured
):
raise errors.UnexpectedProtocolError(
"Cannot return tip to a tiprack while the pipette is configured for partial tip."
)
if well_location.origin != DropTipWellOrigin.DEFAULT:
return WellLocation(
origin=WellOrigin(well_location.origin.value),
Expand Down
4 changes: 4 additions & 0 deletions api/src/opentrons/protocol_engine/state/pipettes.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,3 +623,7 @@ def get_nozzle_layout_type(self, pipette_id: str) -> NozzleConfigurationType:
return nozzle_map_for_pipette.configuration
else:
return NozzleConfigurationType.FULL

def get_is_partially_configured(self, pipette_id: str) -> bool:
"""Determine if the provided pipette is partially configured."""
return self.get_nozzle_layout_type(pipette_id) != NozzleConfigurationType.FULL
14 changes: 13 additions & 1 deletion api/tests/opentrons/protocol_engine/commands/test_drop_tip.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,16 @@ async def test_drop_tip_implementation(
homeAfter=True,
)

decoy.when(
mock_state_view.pipettes.get_is_partially_configured(pipette_id="abc")
).then_return(False)

decoy.when(
mock_state_view.geometry.get_checked_tip_drop_location(
pipette_id="abc",
labware_id="123",
well_location=DropTipWellLocation(offset=WellOffset(x=1, y=2, z=3)),
partially_configured=False,
)
).then_return(WellLocation(offset=WellOffset(x=4, y=5, z=6)))

Expand Down Expand Up @@ -142,9 +147,16 @@ async def test_drop_tip_with_alternating_locations(
)
).then_return(drop_location)

decoy.when(
mock_state_view.pipettes.get_is_partially_configured(pipette_id="abc")
).then_return(False)

decoy.when(
mock_state_view.geometry.get_checked_tip_drop_location(
pipette_id="abc", labware_id="123", well_location=drop_location
pipette_id="abc",
labware_id="123",
well_location=drop_location,
partially_configured=False,
)
).then_return(WellLocation(offset=WellOffset(x=4, y=5, z=6)))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ def test_load_virtual_pipette_nozzle_layout(
)
result = subject_instance.get_nozzle_layout_for_pipette("my-96-pipette")
assert result.configuration.value == "ROW"

subject_instance.configure_virtual_pipette_nozzle_layout(
"my-96-pipette", "p1000_96_v3.5", "A1", "A1"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1116,15 +1116,22 @@ def test_get_tip_drop_location_with_non_tiprack(
)


def test_get_tip_drop_explicit_location(subject: GeometryView) -> None:
def test_get_tip_drop_explicit_location(
decoy: Decoy,
labware_view: LabwareView,
subject: GeometryView,
tip_rack_def: LabwareDefinition,
) -> None:
"""It should pass the location through if origin is not WellOrigin.DROP_TIP."""
decoy.when(labware_view.get_definition("tip-rack-id")).then_return(tip_rack_def)

input_location = DropTipWellLocation(
origin=DropTipWellOrigin.TOP,
offset=WellOffset(x=1, y=2, z=3),
)

result = subject.get_checked_tip_drop_location(
pipette_id="pipette-id", labware_id="labware-id", well_location=input_location
pipette_id="pipette-id", labware_id="tip-rack-id", well_location=input_location
)

assert result == WellLocation(
Expand Down