Skip to content

Commit

Permalink
feat(api): Support changing return tip height by tip size type (#13157)
Browse files Browse the repository at this point in the history
  • Loading branch information
Laura-Danielle authored Jul 26, 2023
1 parent a8ecf08 commit b985fc1
Show file tree
Hide file tree
Showing 23 changed files with 444 additions and 37 deletions.
7 changes: 6 additions & 1 deletion api/src/opentrons/hardware_control/dev_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
PipetteName,
ChannelCount,
)
from opentrons_shared_data.pipette.pipette_definition import PipetteConfigurations
from opentrons_shared_data.pipette.types import PipetteTipType
from opentrons_shared_data.pipette.pipette_definition import (
PipetteConfigurations,
SupportedTipsDefinition,
)
from opentrons_shared_data.gripper import (
GripperModel,
GripperDefinition,
Expand Down Expand Up @@ -89,6 +93,7 @@ class PipetteDict(InstrumentDict):
ready_to_aspirate: bool
has_tip: bool
default_blow_out_volume: float
supported_tips: Dict[PipetteTipType, SupportedTipsDefinition]


class GripperDict(InstrumentDict):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ def as_dict(self) -> "Pipette.DictType":
"return_tip_height": self.active_tip_settings.default_return_tip_height,
"tip_overlap": self.tip_overlap,
"back_compat_names": self._config.pipette_backcompat_names,
"supported_tips": self._config.supported_tips,
}
)
return self._config_as_dict
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ def get_attached_instrument(self, mount: MountType) -> PipetteDict:
"default_blow_out_flow_rates",
"default_dispense_flow_rates",
"back_compat_names",
"supported_tips",
]

instr_dict = instr.as_dict()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ def as_dict(self) -> "Pipette.DictType":
"return_tip_height": self.active_tip_settings.default_return_tip_height,
"tip_overlap": self.tip_overlap,
"back_compat_names": self._config.pipette_backcompat_names,
"supported_tips": self._config.supported_tips,
}
)
return self._config_as_dict
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict:
"default_blow_out_flow_rates",
"default_dispense_flow_rates",
"back_compat_names",
"supported_tips",
]

instr_dict = instr.as_dict()
Expand Down
4 changes: 1 addition & 3 deletions api/src/opentrons/protocol_engine/execution/tip_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ async def pick_up_tip(
hw_mount = self._state_view.pipettes.get_mount(pipette_id).to_hw_mount()

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

actual_tip_length = await self._labware_data_provider.get_calibrated_tip_length(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
pipette_load_name_conversions as pipette_load_name,
load_data as load_pipette_data,
types as pip_types,
pipette_definition,
)

from opentrons.hardware_control.dev_types import PipetteDict
Expand All @@ -26,7 +27,9 @@ class LoadedStaticPipetteData:
home_position: float
nozzle_offset_z: float
flow_rates: FlowRates
return_tip_scale: float
tip_configuration_lookup_table: Dict[
float, pipette_definition.SupportedTipsDefinition
]
nominal_tip_overlap: Dict[str, float]


Expand All @@ -52,12 +55,14 @@ def get_virtual_pipette_static_config(
channels=config.channels,
home_position=config.mount_configurations.homePosition,
nozzle_offset_z=config.nozzle_offset[2],
tip_configuration_lookup_table={
k.value: v for k, v in config.supported_tips.items()
},
flow_rates=FlowRates(
default_blow_out=tip_configuration.default_blowout_flowrate.values_by_api_level,
default_aspirate=tip_configuration.default_aspirate_flowrate.values_by_api_level,
default_dispense=tip_configuration.default_dispense_flowrate.values_by_api_level,
),
return_tip_scale=tip_configuration.default_return_tip_height,
nominal_tip_overlap=config.tip_overlap_dictionary,
)

Expand All @@ -75,7 +80,9 @@ def get_pipette_static_config(pipette_dict: PipetteDict) -> LoadedStaticPipetteD
default_aspirate=pipette_dict["default_aspirate_flow_rates"],
default_dispense=pipette_dict["default_dispense_flow_rates"],
),
return_tip_scale=pipette_dict["return_tip_height"],
tip_configuration_lookup_table={
k.value: v for k, v in pipette_dict["supported_tips"].items()
},
nominal_tip_overlap=pipette_dict["tip_overlap"],
# TODO(mc, 2023-02-28): these two values are not present in PipetteDict
# https://opentrons.atlassian.net/browse/RCORE-655
Expand Down
43 changes: 40 additions & 3 deletions api/src/opentrons/protocol_engine/state/pipettes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from dataclasses import dataclass
from typing import Dict, List, Mapping, Optional, Tuple

from opentrons_shared_data.pipette import pipette_definition
from opentrons.config.defaults_ot2 import Z_RETRACT_DISTANCE
from opentrons.hardware_control.dev_types import PipetteDict
from opentrons.types import MountType, Mount as HwMount
Expand Down Expand Up @@ -71,7 +72,9 @@ class StaticPipetteConfig:
min_volume: float
max_volume: float
channels: int
return_tip_scale: float
tip_configuration_lookup_table: Dict[
float, pipette_definition.SupportedTipsDefinition
]
nominal_tip_overlap: Dict[str, float]
home_position: float
nozzle_offset_z: float
Expand Down Expand Up @@ -124,7 +127,7 @@ def handle_action(self, action: Action) -> None:
min_volume=config.min_volume,
max_volume=config.max_volume,
channels=config.channels,
return_tip_scale=config.return_tip_scale,
tip_configuration_lookup_table=config.tip_configuration_lookup_table,
nominal_tip_overlap=config.nominal_tip_overlap,
home_position=config.home_position,
nozzle_offset_z=config.nozzle_offset_z,
Expand Down Expand Up @@ -171,11 +174,32 @@ def _handle_command(self, command: Command) -> None:
self._state.attached_tip_by_id[pipette_id] = attached_tip
self._state.aspirated_volume_by_id[pipette_id] = 0

static_config = self._state.static_config_by_id.get(pipette_id)
if static_config:
tip_configuration = static_config.tip_configuration_lookup_table[
attached_tip.volume
]
self._state.flow_rates_by_id[pipette_id] = FlowRates(
default_blow_out=tip_configuration.default_blowout_flowrate.values_by_api_level,
default_aspirate=tip_configuration.default_aspirate_flowrate.values_by_api_level,
default_dispense=tip_configuration.default_dispense_flowrate.values_by_api_level,
)

elif isinstance(command.result, (DropTipResult, DropTipInPlaceResult)):
pipette_id = command.params.pipetteId
self._state.aspirated_volume_by_id[pipette_id] = None
self._state.attached_tip_by_id[pipette_id] = None

static_config = self._state.static_config_by_id.get(pipette_id)
if static_config:
tip_configuration = static_config.tip_configuration_lookup_table[
static_config.max_volume
]
self._state.flow_rates_by_id[pipette_id] = FlowRates(
default_blow_out=tip_configuration.default_blowout_flowrate.values_by_api_level,
default_aspirate=tip_configuration.default_aspirate_flowrate.values_by_api_level,
default_dispense=tip_configuration.default_dispense_flowrate.values_by_api_level,
)
elif isinstance(command.result, BlowOutResult):
pipette_id = command.params.pipetteId
self._state.aspirated_volume_by_id[pipette_id] = None
Expand Down Expand Up @@ -504,7 +528,20 @@ def get_instrument_max_height_ot2(self, pipette_id: str) -> float:

def get_return_tip_scale(self, pipette_id: str) -> float:
"""Return the given pipette's return tip height scale."""
return self.get_config(pipette_id).return_tip_scale
max_volume = self.get_maximum_volume(pipette_id)
working_volume = max_volume
if self.get_attached_tip(pipette_id):
working_volume = self.get_working_volume(pipette_id)

if working_volume in self.get_config(pipette_id).tip_configuration_lookup_table:
tip_lookup = self.get_config(pipette_id).tip_configuration_lookup_table[
working_volume
]
else:
tip_lookup = self.get_config(pipette_id).tip_configuration_lookup_table[
working_volume
]
return tip_lookup.default_return_tip_height

def get_flow_rates(self, pipette_id: str) -> FlowRates:
"""Get the default flow rates for the pipette."""
Expand Down
22 changes: 22 additions & 0 deletions api/tests/opentrons/protocol_engine/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from opentrons_shared_data.deck import load as load_deck
from opentrons_shared_data.deck.dev_types import DeckDefinitionV3
from opentrons_shared_data.labware import load_definition
from opentrons_shared_data.pipette import pipette_definition
from opentrons.protocols.models import LabwareDefinition
from opentrons.protocols.api_support.deck_type import (
STANDARD_OT2_DECK,
Expand Down Expand Up @@ -187,3 +188,24 @@ def mag_block_v1_def() -> ModuleDefinition:
"""Get the definition of a V1 Mag Block."""
definition = load_shared_data("module/definitions/3/magneticBlockV1.json")
return ModuleDefinition.parse_raw(definition)


@pytest.fixture(scope="session")
def supported_tip_fixture() -> pipette_definition.SupportedTipsDefinition:
"""Get a mock supported tip definition."""
return pipette_definition.SupportedTipsDefinition(
defaultAspirateFlowRate=pipette_definition.FlowRateDefinition(
default=10, valuesByApiLevel={}
),
defaultDispenseFlowRate=pipette_definition.FlowRateDefinition(
default=10, valuesByApiLevel={}
),
defaultBlowOutFlowRate=pipette_definition.FlowRateDefinition(
default=10, valuesByApiLevel={}
),
defaultTipLength=40,
defaultReturnTipHeight=0.5,
aspirate=pipette_definition.ulPerMMDefinition(default={"1": [(0, 0, 0)]}),
dispense=pipette_definition.ulPerMMDefinition(default={"1": [(0, 0, 0)]}),
defaultBlowoutVolume=5,
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Any, Optional, cast

from opentrons_shared_data.pipette.dev_types import PipetteNameType
from opentrons_shared_data.pipette import pipette_definition
from opentrons_shared_data.labware.dev_types import LabwareUri

from opentrons.calibration_storage.helpers import uri_from_details
Expand Down Expand Up @@ -126,7 +127,9 @@ async def temp_module_v2(decoy: Decoy) -> TempDeck:


@pytest.fixture
def loaded_static_pipette_data() -> LoadedStaticPipetteData:
def loaded_static_pipette_data(
supported_tip_fixture: pipette_definition.SupportedTipsDefinition,
) -> LoadedStaticPipetteData:
"""Get a pipette config data value object."""
return LoadedStaticPipetteData(
model="pipette_model",
Expand All @@ -139,7 +142,7 @@ def loaded_static_pipette_data() -> LoadedStaticPipetteData:
default_aspirate={"b": 4.56},
default_dispense={"c": 7.89},
),
return_tip_scale=0.5,
tip_configuration_lookup_table={4.56: supported_tip_fixture},
nominal_tip_overlap={"default": 9.87},
home_position=10.11,
nozzle_offset_z=12.13,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Test pipette data provider."""
from opentrons_shared_data.pipette.dev_types import PipetteNameType, PipetteModel
from opentrons_shared_data.pipette import pipette_definition, types as pip_types

from opentrons.hardware_control.dev_types import PipetteDict
from opentrons.protocol_engine.types import FlowRates
Expand Down Expand Up @@ -29,7 +30,7 @@ def test_get_virtual_pipette_static_config() -> None:
default_dispense={"2.0": 3.78, "2.6": 7.56},
default_blow_out={"2.0": 3.78, "2.6": 7.56},
),
return_tip_scale=0.5,
tip_configuration_lookup_table=result.tip_configuration_lookup_table,
nominal_tip_overlap={
"default": 8.25,
"opentrons/eppendorf_96_tiprack_10ul_eptips/1": 8.4,
Expand All @@ -42,7 +43,9 @@ def test_get_virtual_pipette_static_config() -> None:
)


def test_get_pipette_static_config() -> None:
def test_get_pipette_static_config(
supported_tip_fixture: pipette_definition.SupportedTipsDefinition,
) -> None:
"""It should return config data given a PipetteDict."""
pipette_dict: PipetteDict = {
"name": "p300_single_gen2",
Expand Down Expand Up @@ -78,6 +81,7 @@ def test_get_pipette_static_config() -> None:
"default_dispense_speeds": {"2.0": 5.021202, "2.6": 10.042404},
"default_aspirate_speeds": {"2.0": 5.021202, "2.6": 10.042404},
"default_blow_out_volume": 10,
"supported_tips": {pip_types.PipetteTipType.t300: supported_tip_fixture},
}

result = subject.get_pipette_static_config(pipette_dict)
Expand All @@ -93,7 +97,7 @@ def test_get_pipette_static_config() -> None:
default_dispense={"2.0": 46.43, "2.3": 92.86},
default_blow_out={"2.0": 46.43, "2.2": 92.86},
),
return_tip_scale=0.5,
tip_configuration_lookup_table={300: supported_tip_fixture},
nominal_tip_overlap={
"default": 8.2,
"opentrons/opentrons_96_tiprack_300ul/1": 8.2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from opentrons_shared_data.deck.dev_types import DeckDefinitionV3
from opentrons_shared_data.labware.dev_types import LabwareUri
from opentrons_shared_data.pipette import pipette_definition
from opentrons.calibration_storage.helpers import uri_from_details
from opentrons.protocols.models import LabwareDefinition
from opentrons.types import Point, DeckSlotName, MountType
Expand Down Expand Up @@ -1354,6 +1355,7 @@ def test_get_next_drop_tip_location(
pipette_channels: int,
pipette_mount: MountType,
expected_locations: List[DropTipWellLocation],
supported_tip_fixture: pipette_definition.SupportedTipsDefinition,
) -> None:
"""It should provide the next location to drop tips into within a labware."""
decoy.when(labware_view.is_fixed_trash(labware_id="abc")).then_return(True)
Expand All @@ -1368,7 +1370,7 @@ def test_get_next_drop_tip_location(
model="blah",
display_name="bleh",
serial_number="",
return_tip_scale=0,
tip_configuration_lookup_table={9001: supported_tip_fixture},
nominal_tip_overlap={},
home_position=0,
nozzle_offset_z=0,
Expand Down
10 changes: 7 additions & 3 deletions api/tests/opentrons/protocol_engine/state/test_pipette_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Optional

from opentrons_shared_data.pipette.dev_types import PipetteNameType
from opentrons_shared_data.pipette import pipette_definition

from opentrons.types import DeckSlotName, MountType
from opentrons.protocol_engine import commands as cmd
Expand Down Expand Up @@ -589,7 +590,10 @@ def test_set_movement_speed(subject: PipetteStore) -> None:
assert subject.state.movement_speed_by_id[pipette_id] == 123.456


def test_add_pipette_config(subject: PipetteStore) -> None:
def test_add_pipette_config(
subject: PipetteStore,
supported_tip_fixture: pipette_definition.SupportedTipsDefinition,
) -> None:
"""It should issue an action to add a pipette config."""
subject.handle_action(
AddPipetteConfigAction(
Expand All @@ -606,7 +610,7 @@ def test_add_pipette_config(subject: PipetteStore) -> None:
default_dispense={"b": 2},
default_blow_out={"c": 3},
),
return_tip_scale=4,
tip_configuration_lookup_table={4: supported_tip_fixture},
nominal_tip_overlap={"default": 5},
home_position=8.9,
nozzle_offset_z=10.11,
Expand All @@ -621,7 +625,7 @@ def test_add_pipette_config(subject: PipetteStore) -> None:
min_volume=1.23,
max_volume=4.56,
channels=7,
return_tip_scale=4,
tip_configuration_lookup_table={4: supported_tip_fixture},
nominal_tip_overlap={"default": 5},
home_position=8.9,
nozzle_offset_z=10.11,
Expand Down
Loading

0 comments on commit b985fc1

Please sign in to comment.