Skip to content

Commit

Permalink
refactor(api): Save full nozzle map configuration and update state st…
Browse files Browse the repository at this point in the history
…ore accordingly
  • Loading branch information
Laura-Danielle committed Feb 20, 2024
1 parent 818292c commit 3099ca7
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 68 deletions.
6 changes: 2 additions & 4 deletions api/src/opentrons/protocol_engine/execution/equipment.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@ async def load_pipette(
)

pipette_id = pipette_id or self._model_utils.generate_id()

if not use_virtual_pipettes:
cache_request = {mount.to_hw_mount(): pipette_name_value}

Expand Down Expand Up @@ -244,11 +243,10 @@ async def load_pipette(
)
)
serial = serial_number or ""

return LoadedPipetteData(
pipette_id=pipette_id,
serial_number=serial,
static_config=static_pipette_config,
static_config=static_pipette_config
)

async def load_magnetic_block(
Expand Down Expand Up @@ -390,7 +388,7 @@ async def configure_nozzle_layout(
primary_nozzle: Optional[str] = None,
front_right_nozzle: Optional[str] = None,
back_left_nozzle: Optional[str] = None,
) -> Optional[NozzleMap]:
) -> NozzleMap:
"""Ensure the requested nozzle layout is compatible with the current pipette.
Args:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ class LoadedStaticPipetteData:
float, pipette_definition.SupportedTipsDefinition
]
nominal_tip_overlap: Dict[str, float]
back_left_nozzle_offset: Point
front_right_nozzle_offset: Point
nozzle_map: NozzleMap


class VirtualPipetteDataProvider:
Expand Down Expand Up @@ -173,8 +172,7 @@ def _get_virtual_pipette_static_config_by_model(
nominal_tip_overlap=config.liquid_properties[
liquid_class
].tip_overlap_dictionary,
back_left_nozzle_offset=nozzle_manager.current_configuration.back_left_nozzle_offset,
front_right_nozzle_offset=nozzle_manager.current_configuration.front_right_nozzle_offset,
nozzle_map=nozzle_manager.current_configuration
)

def get_virtual_pipette_static_config(
Expand Down Expand Up @@ -208,11 +206,5 @@ def get_pipette_static_config(pipette_dict: PipetteDict) -> LoadedStaticPipetteD
# https://opentrons.atlassian.net/browse/RCORE-655
home_position=0,
nozzle_offset_z=0,
# TODO (spp): Confirm that the nozzle map is populated by the hardware api by default
back_left_nozzle_offset=pipette_dict[
"current_nozzle_map"
].back_left_nozzle_offset,
front_right_nozzle_offset=pipette_dict[
"current_nozzle_map"
].front_right_nozzle_offset,
nozzle_map=pipette_dict["current_nozzle_map"]
)
7 changes: 5 additions & 2 deletions api/src/opentrons/protocol_engine/state/pipettes.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,14 @@ def _handle_command( # noqa: C901
home_position=config.home_position,
nozzle_offset_z=config.nozzle_offset_z,
bounding_nozzle_offsets=BoundingNozzlesOffsets(
back_left_offset=config.back_left_nozzle_offset,
front_right_offset=config.front_right_nozzle_offset,
back_left_offset=config.nozzle_map.back_left_nozzle_offset,
front_right_offset=config.nozzle_map.front_right_nozzle_offset,
),
)
self._state.flow_rates_by_id[private_result.pipette_id] = config.flow_rates
self._state.nozzle_configuration_by_id[
private_result.pipette_id
] = config.nozzle_map
elif isinstance(private_result, PipetteNozzleLayoutResultMixin):
self._state.nozzle_configuration_by_id[
private_result.pipette_id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
ConfigureForVolumePrivateResult,
ConfigureForVolumeImplementation,
)
from opentrons.types import Point

from opentrons_shared_data.pipette.dev_types import PipetteNameType
from ..pipette_fixtures import get_default_nozzle_map

async def test_configure_for_volume_implementation(
decoy: Decoy,
Expand All @@ -44,8 +44,7 @@ async def test_configure_for_volume_implementation(
),
tip_configuration_lookup_table={},
nominal_tip_overlap={},
back_left_nozzle_offset=Point(x=1, y=2, z=3),
front_right_nozzle_offset=Point(x=4, y=5, z=6),
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_MULTI),
)

decoy.when(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Test configure nozzle layout commands."""
import pytest
from decoy import Decoy
from typing import Union, Optional, Dict
from typing import Union, Dict
from collections import OrderedDict

from opentrons.protocol_engine.execution import (
Expand Down Expand Up @@ -90,7 +90,7 @@ async def test_configure_nozzle_layout_implementation(
QuadrantNozzleLayoutConfiguration,
SingleNozzleLayoutConfiguration,
],
expected_nozzlemap: Optional[NozzleMap],
expected_nozzlemap: NozzleMap,
nozzle_params: Dict[str, str],
) -> None:
"""A ConfigureForVolume command should have an execution implementation."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
LoadPipettePrivateResult,
LoadPipetteImplementation,
)

from opentrons_shared_data.pipette.dev_types import PipetteNameType
from ..pipette_fixtures import get_default_nozzle_map

async def test_load_pipette_implementation(
decoy: Decoy,
Expand All @@ -41,8 +42,7 @@ async def test_load_pipette_implementation(
),
tip_configuration_lookup_table={},
nominal_tip_overlap={},
back_left_nozzle_offset=Point(x=1, y=2, z=3),
front_right_nozzle_offset=Point(x=4, y=5, z=6),
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_MULTI),
)
data = LoadPipetteParams(
pipetteName=PipetteNameType.P300_SINGLE,
Expand Down Expand Up @@ -98,8 +98,7 @@ async def test_load_pipette_implementation_96_channel(
),
tip_configuration_lookup_table={},
nominal_tip_overlap={},
back_left_nozzle_offset=Point(x=1, y=2, z=3),
front_right_nozzle_offset=Point(x=4, y=5, z=6),
nozzle_map=get_default_nozzle_map(PipetteNameType.P1000_96),
)

decoy.when(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
LoadedPipetteData,
LoadedModuleData,
)
from opentrons_shared_data.pipette.dev_types import PipetteNameType
from ..pipette_fixtures import get_default_nozzle_map


def _make_config(use_virtual_modules: bool) -> Config:
Expand Down Expand Up @@ -147,8 +149,7 @@ def loaded_static_pipette_data(
nominal_tip_overlap={"default": 9.87},
home_position=10.11,
nozzle_offset_z=12.13,
back_left_nozzle_offset=Point(x=1, y=2, z=3),
front_right_nozzle_offset=Point(x=4, y=5, z=6),
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE)
)


Expand Down
34 changes: 34 additions & 0 deletions api/tests/opentrons/protocol_engine/pipette_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from collections import OrderedDict

from opentrons.types import Point
from opentrons.hardware_control.nozzle_manager import NozzleMap
from opentrons_shared_data.pipette.dev_types import PipetteNameType


NINETY_SIX_ROWS = OrderedDict(
(
Expand Down Expand Up @@ -317,3 +320,34 @@
("H1", Point(0.0, -31.5, 35.52)),
)
)


def get_default_nozzle_map(pipette_type: PipetteNameType) -> NozzleMap:
"""Get default nozzle map for a given pipette type."""
if "multi" in pipette_type.value:
return NozzleMap.build(
physical_nozzles=EIGHT_CHANNEL_MAP,
physical_rows=EIGHT_CHANNEL_ROWS,
physical_columns=EIGHT_CHANNEL_COLS,
starting_nozzle="A1",
back_left_nozzle="A1",
front_right_nozzle="A1",
)
elif "96" in pipette_type.value:
return NozzleMap.build(
physical_nozzles=NINETY_SIX_MAP,
physical_rows=NINETY_SIX_ROWS,
physical_columns=NINETY_SIX_COLS,
starting_nozzle="A1",
back_left_nozzle="A1",
front_right_nozzle="A1",
)
else:
return NozzleMap.build(
physical_nozzles=OrderedDict({"A1": Point(0, 0, 0)}),
physical_rows=OrderedDict({"A": ["A1"]}),
physical_columns=OrderedDict({"1": ["A1"]}),
starting_nozzle="A1",
back_left_nozzle="A1",
front_right_nozzle="A1",
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
from opentrons_shared_data.pipette import pipette_definition, types as pip_types

from opentrons.hardware_control.dev_types import PipetteDict
from opentrons.hardware_control.nozzle_manager import NozzleMap
from opentrons.protocol_engine.types import FlowRates
from opentrons.protocol_engine.resources.pipette_data_provider import (
LoadedStaticPipetteData,
VirtualPipetteDataProvider,
)

from opentrons.protocol_engine.resources import pipette_data_provider as subject
from opentrons.types import Point
from opentrons_shared_data.pipette.dev_types import PipetteNameType
from ..pipette_fixtures import get_default_nozzle_map


@pytest.fixture
Expand Down Expand Up @@ -54,8 +54,7 @@ def test_get_virtual_pipette_static_config(
"opentrons/opentrons_96_tiprack_10ul/1": 8.25,
"opentrons/opentrons_96_tiprack_20ul/1": 8.25,
},
back_left_nozzle_offset=Point(x=0.0, y=0.0, z=10.45),
front_right_nozzle_offset=Point(x=0.0, y=0.0, z=10.45),
nozzle_map=result.nozzle_map,
)


Expand All @@ -81,8 +80,7 @@ def test_configure_virtual_pipette_for_volume(
),
tip_configuration_lookup_table=result1.tip_configuration_lookup_table,
nominal_tip_overlap=result1.nominal_tip_overlap,
back_left_nozzle_offset=Point(x=-8.0, y=-22.0, z=-259.15),
front_right_nozzle_offset=Point(x=-8.0, y=-22.0, z=-259.15),
nozzle_map=result1.nozzle_map,
)
subject_instance.configure_virtual_pipette_for_volume(
"my-pipette", 1, result1.model
Expand All @@ -105,8 +103,7 @@ def test_configure_virtual_pipette_for_volume(
),
tip_configuration_lookup_table=result2.tip_configuration_lookup_table,
nominal_tip_overlap=result2.nominal_tip_overlap,
back_left_nozzle_offset=Point(x=-8.0, y=-22.0, z=-259.15),
front_right_nozzle_offset=Point(x=-8.0, y=-22.0, z=-259.15),
nozzle_map=result2.nozzle_map,
)


Expand All @@ -132,8 +129,7 @@ def test_load_virtual_pipette_by_model_string(
),
tip_configuration_lookup_table=result.tip_configuration_lookup_table,
nominal_tip_overlap=result.nominal_tip_overlap,
back_left_nozzle_offset=Point(x=0.0, y=31.5, z=35.52),
front_right_nozzle_offset=Point(x=0.0, y=-31.5, z=35.52),
nozzle_map=result.nozzle_map,
)


Expand Down Expand Up @@ -179,6 +175,7 @@ def test_get_pipette_static_config(
supported_tip_fixture: pipette_definition.SupportedTipsDefinition,
) -> None:
"""It should return config data given a PipetteDict."""
dummy_nozzle_map = get_default_nozzle_map(PipetteNameType.P300_SINGLE_GEN2),
pipette_dict: PipetteDict = {
"name": "p300_single_gen2",
"min_volume": 20,
Expand Down Expand Up @@ -214,14 +211,7 @@ def test_get_pipette_static_config(
"default_aspirate_speeds": {"2.0": 5.021202, "2.6": 10.042404},
"default_push_out_volume": 3,
"supported_tips": {pip_types.PipetteTipType.t300: supported_tip_fixture},
"current_nozzle_map": NozzleMap.build(
physical_nozzles=OrderedDict({"A1": Point(1, 2, 3), "B1": Point(4, 5, 6)}),
physical_rows=OrderedDict({"A": ["A1"], "B": ["B1"]}),
physical_columns=OrderedDict({"1": ["A1", "B1"]}),
starting_nozzle="A1",
back_left_nozzle="A1",
front_right_nozzle="B1",
),
"current_nozzle_map": dummy_nozzle_map,
}

result = subject.get_pipette_static_config(pipette_dict)
Expand All @@ -247,6 +237,5 @@ def test_get_pipette_static_config(
# https://opentrons.atlassian.net/browse/RCORE-655
nozzle_offset_z=0,
home_position=0,
back_left_nozzle_offset=Point(x=1, y=2, z=3),
front_right_nozzle_offset=Point(x=4, y=5, z=6),
nozzle_map=dummy_nozzle_map,
)
10 changes: 5 additions & 5 deletions api/tests/opentrons/protocol_engine/state/test_pipette_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
create_move_relative_command,
create_prepare_to_aspirate_command,
)

from opentrons_shared_data.pipette.dev_types import PipetteNameType
from ..pipette_fixtures import get_default_nozzle_map

@pytest.fixture
def subject() -> PipetteStore:
Expand Down Expand Up @@ -682,8 +683,7 @@ def test_add_pipette_config(
nominal_tip_overlap={"default": 5},
home_position=8.9,
nozzle_offset_z=10.11,
back_left_nozzle_offset=Point(x=1, y=2, z=3),
front_right_nozzle_offset=Point(x=4, y=5, z=6),
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE)
),
)
subject.handle_action(
Expand All @@ -702,8 +702,8 @@ def test_add_pipette_config(
home_position=8.9,
nozzle_offset_z=10.11,
bounding_nozzle_offsets=BoundingNozzlesOffsets(
back_left_offset=Point(x=1, y=2, z=3),
front_right_offset=Point(x=4, y=5, z=6),
back_left_offset=Point(x=0, y=0, z=0),
front_right_offset=Point(x=0, y=0, z=0)
),
)
assert subject.state.flow_rates_by_id["pipette-id"].default_aspirate == {"a": 1.0}
Expand Down
Loading

0 comments on commit 3099ca7

Please sign in to comment.