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) Support changing current based on tip configuration #13660

Merged
Merged
Show file tree
Hide file tree
Changes from 10 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
33 changes: 22 additions & 11 deletions api/src/opentrons/hardware_control/instruments/nozzle_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,18 @@ class NozzleConfigurationManager:
def __init__(
self,
nozzle_map: NozzleMap,
current_scalar: float,
pick_up_current_map: Dict[int, float],
) -> None:
self._physical_nozzle_map = nozzle_map
self._current_nozzle_configuration = nozzle_map
self._current_scalar: Final[float] = current_scalar
self._pick_up_current_map: Final[Dict[int, float]] = pick_up_current_map

@classmethod
def build_from_nozzlemap(
cls, nozzle_map: Dict[str, List[float]], current_scalar: float
cls,
nozzle_map: Dict[str, List[float]],
default_pickup_current: float,
pick_up_current_map: Optional[Dict[int, float]] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in what situations would we not have a pick_up_current_map? does it have to be = None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

single channels do not have a pick up current map.

) -> "NozzleConfigurationManager":

sorted_nozzlemap = list(nozzle_map.keys())
Expand All @@ -224,12 +227,23 @@ def build_from_nozzlemap(
back_left_nozzle=first_nozzle,
front_right_nozzle=last_nozzle,
)
return cls(starting_nozzle_config, current_scalar)
if pick_up_current_map:
current_map = pick_up_current_map
else:
current_map = {
num_tips + 1: default_pickup_current
for num_tips in range(len(starting_nozzle_config.map_store))
}
return cls(starting_nozzle_config, current_map)

@property
def starting_nozzle_offset(self) -> Point:
return self._current_nozzle_configuration.starting_nozzle_offset

@property
def current_configuration(self) -> NozzleMap:
return self._current_nozzle_configuration

def update_nozzle_configuration(
self,
back_left_nozzle: str,
Expand All @@ -256,13 +270,10 @@ def update_nozzle_configuration(
front_right_nozzle=front_right_nozzle,
)

def get_current_for_tip_configuration(self) -> float:
# TODO While implementing this PR I realized that
# the current scalar truly is inefficient for
# encapsulating varying currents needed for
# pick up tip so we'll need to follow this up
# with a lookup table.
return self._current_scalar
def get_tip_configuration_current(self) -> float:
return self._pick_up_current_map[
len(self._current_nozzle_configuration.map_store)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i would love it if the nozzle configuration objects had like tip_count property that did this or something but it's ok if it works

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure can do that

]

def critical_point_with_tip_length(
self,
Expand Down
13 changes: 10 additions & 3 deletions api/src/opentrons/hardware_control/instruments/ot2/pipette.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
""" Classes and functions for pipette state tracking
"""
import logging
from typing import Any, Dict, Optional, Set, Tuple, Union, cast, List
from typing import Any, Dict, Optional, Set, Tuple, Union, cast

from opentrons_shared_data.pipette.pipette_definition import (
PipetteConfigurations,
Expand Down Expand Up @@ -90,6 +90,7 @@ def __init__(
self._pipette_version = self._config.version
self._max_channels = self._config.channels
self._backlash_distance = config.backlash_distance
self._pick_up_configurations = config.pick_up_tip_configurations

self._liquid_class_name = pip_types.LiquidClasses.default
self._liquid_class = self._config.liquid_properties[self._liquid_class_name]
Expand All @@ -110,6 +111,7 @@ def __init__(
self._nozzle_manager = (
nozzle_manager.NozzleConfigurationManager.build_from_nozzlemap(
self._config.nozzle_map,
self._pick_up_configurations.current,
self._config.partial_tip_configurations.per_tip_pickup_current,
)
)
Expand Down Expand Up @@ -201,8 +203,12 @@ def liquid_class_name(self) -> pip_types.LiquidClasses:
return self._liquid_class_name

@property
def nozzle_offset(self) -> List[float]:
return self._nozzle_offset
def nozzle_offset(self) -> Point:
return self._nozzle_manager.starting_nozzle_offset

@property
def nozzle_manager(self) -> nozzle_manager.NozzleConfigurationManager:
return self._nozzle_manager

@property
def pipette_offset(self) -> PipetteOffsetByPipetteMount:
Expand Down Expand Up @@ -289,6 +295,7 @@ def reset_state(self) -> None:
self._nozzle_manager = (
nozzle_manager.NozzleConfigurationManager.build_from_nozzlemap(
self._config.nozzle_map,
self._pick_up_configurations.current,
self._config.partial_tip_configurations.per_tip_pickup_current,
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ def add_tip_to_instr() -> None:
current={
Axis.by_mount(
mount
): instrument.pick_up_configurations.current
): instrument.nozzle_manager.get_tip_configuration_current()
},
speed=pick_up_speed,
relative_down=top_types.Point(0, 0, press_dist),
Expand Down
14 changes: 11 additions & 3 deletions api/src/opentrons/hardware_control/instruments/ot3/pipette.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
import functools

from typing import Any, List, Dict, Optional, Set, Tuple, Union, cast
from typing import Any, Dict, Optional, Set, Tuple, Union, cast

from opentrons.types import Point

Expand Down Expand Up @@ -97,6 +97,7 @@ def __init__(
self._nozzle_manager = (
nozzle_manager.NozzleConfigurationManager.build_from_nozzlemap(
self._config.nozzle_map,
self._pick_up_configurations.current,
self._config.partial_tip_configurations.per_tip_pickup_current,
)
)
Expand Down Expand Up @@ -165,8 +166,12 @@ def tip_overlap(self) -> Dict[str, float]:
return self._tip_overlap_lookup

@property
def nozzle_offset(self) -> List[float]:
return self._nozzle_offset
def nozzle_offset(self) -> Point:
return self._nozzle_manager.starting_nozzle_offset

@property
def nozzle_manager(self) -> nozzle_manager.NozzleConfigurationManager:
return self._nozzle_manager

@property
def pipette_offset(self) -> PipetteOffsetByPipetteMount:
Expand Down Expand Up @@ -256,6 +261,7 @@ def reset_state(self) -> None:
self._nozzle_manager = (
nozzle_manager.NozzleConfigurationManager.build_from_nozzlemap(
self._config.nozzle_map,
self._pick_up_configurations.current,
self._config.partial_tip_configurations.per_tip_pickup_current,
)
)
Expand Down Expand Up @@ -315,6 +321,8 @@ def critical_point(self, cp_override: Optional[CriticalPoint] = None) -> Point:
cp_with_tip_length = self._nozzle_manager.critical_point_with_tip_length(
cp_override,
self.current_tip_length if cp_override != CriticalPoint.NOZZLE else 0.0,
cp_override, self.current_tip_length

)
cp = cp_with_tip_length + instr

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
)

from opentrons.hardware_control.dev_types import PipetteDict
from ..nozzle_manager import NozzleConfigurationType
from .pipette import Pipette
from .instrument_calibration import PipetteOffsetByPipetteMount

Expand Down Expand Up @@ -742,7 +743,11 @@ def build_presses() -> Iterator[Tuple[float, float]]:
backup_dist = -press_dist
yield (press_dist, backup_dist)

if instrument.channels == 96:
if (
instrument.channels == 96
and instrument.nozzle_manager.current_configuration.configuration
== NozzleConfigurationType.FULL
):
return (
PickUpTipSpec(
plunger_prep_pos=instrument.plunger_positions.bottom,
Expand Down Expand Up @@ -779,7 +784,7 @@ def build_presses() -> Iterator[Tuple[float, float]]:
current={
Axis.by_mount(
mount
): instrument.pick_up_configurations.current
): instrument.nozzle_manager.get_tip_configuration_current()
},
speed=pick_up_speed,
relative_down=top_types.Point(0, 0, press_dist),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@
from opentrons.types import Point
from opentrons.hardware_control.types import CriticalPoint

from opentrons.types import Point
from opentrons.hardware_control.types import CriticalPoint


def build_nozzle_manger(
nozzle_map: Dict[str, List[float]]
) -> nozzle_manager.NozzleConfigurationManager:
return nozzle_manager.NozzleConfigurationManager.build_from_nozzlemap(
nozzle_map, current_scalar=1
nozzle_map, default_pickup_current=1
)


Expand Down
2 changes: 1 addition & 1 deletion api/tests/opentrons/hardware_control/test_pipette.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def test_critical_points_pipette_offset(
) -> None:
hw_pipette = pipette_builder(model, calibration)
# pipette offset + nozzle offset to determine critical point
offsets = calibration.offset + Point(*hw_pipette.nozzle_offset)
offsets = calibration.offset + hw_pipette.nozzle_offset
assert hw_pipette.critical_point() == offsets
assert hw_pipette.critical_point(types.CriticalPoint.NOZZLE) == offsets
assert hw_pipette.critical_point(types.CriticalPoint.TIP) == offsets
Expand Down
6 changes: 6 additions & 0 deletions api/tests/opentrons/hardware_control/test_pipette_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from opentrons.hardware_control.instruments.ot2.pipette_handler import (
PipetteHandlerProvider,
)
from opentrons.hardware_control.instruments.nozzle_manager import (
NozzleConfigurationType,
)
from opentrons.hardware_control.instruments.ot3.pipette import Pipette as OT3Pipette
from opentrons.hardware_control.instruments.ot3.pipette_handler import (
OT3PipetteHandler,
Expand Down Expand Up @@ -117,6 +120,9 @@ def test_plan_check_pick_up_tip_with_presses_argument_ot3(
decoy.when(mock_pipette_ot3.pick_up_configurations.current).then_return(1)
decoy.when(mock_pipette_ot3.config.quirks).then_return([])
decoy.when(mock_pipette_ot3.channels).then_return(channels)
decoy.when(
mock_pipette_ot3.nozzle_manager.current_configuration.configuration
).then_return(NozzleConfigurationType.FULL)

if presses_input is None:
decoy.when(mock_pipette_ot3.config.pick_up_presses).then_return(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,17 @@
},
"partialTipConfigurations": {
"partialTipSupported": true,
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8]
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8],
"perTipPickupCurrent": {
"1": 0.2,
"2": 0.3,
"3": 0.4,
"4": 0.5,
"5": 0.6,
"6": 0.7,
"7": 0.8,
"8": 0.9
}
},
"channels": 8,
"shaftDiameter": 1.0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,17 @@
},
"partialTipConfigurations": {
"partialTipSupported": true,
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8]
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8],
"perTipPickupCurrent": {
"1": 0.2,
"2": 0.3,
"3": 0.4,
"4": 0.5,
"5": 0.6,
"6": 0.7,
"7": 0.8,
"8": 0.9
}
},
"channels": 8,
"shaftDiameter": 1.0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,17 @@
},
"partialTipConfigurations": {
"partialTipSupported": true,
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8]
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8],
"perTipPickupCurrent": {
"1": 0.2,
"2": 0.3,
"3": 0.4,
"4": 0.5,
"5": 0.6,
"6": 0.7,
"7": 0.8,
"8": 0.9
}
},
"channels": 8,
"shaftDiameter": 1.0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,17 @@
},
"partialTipConfigurations": {
"partialTipSupported": true,
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8]
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8],
"perTipPickupCurrent": {
"1": 0.2,
"2": 0.3,
"3": 0.4,
"4": 0.5,
"5": 0.6,
"6": 0.7,
"7": 0.8,
"8": 0.9
}
},
"channels": 8,
"shaftDiameter": 1.0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,17 @@
},
"partialTipConfigurations": {
"partialTipSupported": true,
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8]
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8],
"perTipPickupCurrent": {
"1": 0.2,
"2": 0.3,
"3": 0.4,
"4": 0.5,
"5": 0.6,
"6": 0.7,
"7": 0.8,
"8": 0.9
}
},
"channels": 8,
"shaftDiameter": 1.0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$otSharedSchema": "#/pipette/schemas/2/pipettePropertiesSchema.json",
"displayName": "Flex 8-Channel 1000 μL",
"displayName": "Flex 8-Channel 1000 \u03bcL",
Laura-Danielle marked this conversation as resolved.
Show resolved Hide resolved
"model": "p1000",
"displayCategory": "FLEX",
"pickUpTipConfigurations": {
Expand Down Expand Up @@ -40,7 +40,17 @@
},
"partialTipConfigurations": {
"partialTipSupported": true,
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8]
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8],
"perTipPickupCurrent": {
"1": 0.2,
"2": 0.3,
"3": 0.4,
"4": 0.5,
"5": 0.6,
"6": 0.7,
"7": 0.8,
"8": 0.9
}
},
"backCompatNames": [],
"channels": 8,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,17 @@
},
"partialTipConfigurations": {
"partialTipSupported": true,
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8]
"availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8],
"perTipPickupCurrent": {
"1": 0.2,
"2": 0.3,
"3": 0.4,
"4": 0.5,
"5": 0.6,
"6": 0.7,
"7": 0.8,
"8": 0.9
}
},
"backCompatNames": [],
"channels": 8,
Expand Down
Loading
Loading