Skip to content

Commit

Permalink
feat(api): change liquid_probe to respect min well height (#15488)
Browse files Browse the repository at this point in the history
Add logic to liquid_probe_in_place that will not go to the very bottom
of the well, but stop
slightly above it(as specified by the well's minimum height)

fix EXEC-576

<!--
Thanks for taking the time to open a pull request! Please make sure
you've read the "Opening Pull Requests" section of our Contributing
Guide:


https://github.com/Opentrons/opentrons/blob/edge/CONTRIBUTING.md#opening-pull-requests

To ensure your code is reviewed quickly and thoroughly, please fill out
the sections below to the best of your ability!
-->

# Overview

<!--
Use this section to describe your pull-request at a high level. If the
PR addresses any open issues, please tag the issues here.
-->

# Test Plan

<!--
Use this section to describe the steps that you took to test your Pull
Request.
If you did not perform any testing provide justification why.

OT-3 Developers: You should default to testing on actual physical
hardware.
Once again, if you did not perform testing against hardware, justify
why.

Note: It can be helpful to write a test plan before doing development

Example Test Plan (HTTP API Change)

- Verified that new optional argument `dance-party` causes the robot to
flash its lights, move the pipettes,
then home.
- Verified that when you omit the `dance-party` option the robot homes
normally
- Added protocol that uses `dance-party` argument to G-Code Testing
Suite
- Ran protocol that did not use `dance-party` argument and everything
was successful
- Added unit tests to validate that changes to pydantic model are
correct

-->

# Changelog

<!--
List out the changes to the code in this PR. Please try your best to
categorize your changes and describe what has changed and why.

Example changelog:
- Fixed app crash when trying to calibrate an illegal pipette
- Added state to API to track pipette usage
- Updated API docs to mention only two pipettes are supported

IMPORTANT: MAKE SURE ANY BREAKING CHANGES ARE PROPERLY COMMUNICATED
-->

# Review requests

<!--
Describe any requests for your reviewers here.
-->

# Risk assessment

<!--
Carefully go over your pull request and look at the other parts of the
codebase it may affect. Look for the possibility, even if you think it's
small, that your change may affect some other part of the system - for
instance, changing return tip behavior in protocol may also change the
behavior of labware calibration.

Identify the other parts of the system your codebase may affect, so that
in addition to your own review and testing, other people who may not
have the system internalized as much as you can focus their attention
and testing there.
-->
  • Loading branch information
aaron-kulkarni authored Jun 26, 2024
1 parent 3d99b54 commit abf7f02
Show file tree
Hide file tree
Showing 85 changed files with 478 additions and 45 deletions.
1 change: 1 addition & 0 deletions api/src/opentrons/hardware_control/dev_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class PipetteDict(InstrumentDict):
supported_tips: Dict[PipetteTipType, SupportedTipsDefinition]
pipette_bounding_box_offsets: PipetteBoundingBoxOffsetDefinition
current_nozzle_map: NozzleMap
lld_settings: Optional[Dict[str, Dict[str, float]]]


class PipetteStateDict(TypedDict):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ def get_attached_instrument(self, mount: MountType) -> PipetteDict:
"default_dispense_flow_rates",
"back_compat_names",
"supported_tips",
"lld_settings",
]

instr_dict = instr.as_dict()
Expand Down Expand Up @@ -259,6 +260,7 @@ def get_attached_instrument(self, mount: MountType) -> PipetteDict:
result[
"pipette_bounding_box_offsets"
] = instr.config.pipette_bounding_box_offsets
result["lld_settings"] = instr.config.lld_settings
return cast(PipetteDict, result)

@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict:
"default_dispense_flow_rates",
"back_compat_names",
"supported_tips",
"lld_settings",
]

instr_dict = instr.as_dict()
Expand Down Expand Up @@ -280,6 +281,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict:
result[
"pipette_bounding_box_offsets"
] = instr.config.pipette_bounding_box_offsets
result["lld_settings"] = instr.config.lld_settings
return cast(PipetteDict, result)

@property
Expand Down
5 changes: 4 additions & 1 deletion api/src/opentrons/protocol_engine/execution/pipetting.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,11 @@ async def liquid_probe_in_place(
)
well_def = self._state_view.labware.get_well_definition(labware_id, well_name)
well_depth = well_def.depth
lld_min_height = self._state_view.pipettes.get_current_tip_lld_settings(
pipette_id=pipette_id
)
z_pos = await self._hardware_api.liquid_probe(
mount=hw_pipette.mount, max_z_dist=well_depth
mount=hw_pipette.mount, max_z_dist=well_depth - lld_min_height
)
return float(z_pos)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class LoadedStaticPipetteData:
nozzle_map: NozzleMap
back_left_corner_offset: Point
front_right_corner_offset: Point
pipette_lld_settings: Optional[Dict[str, Dict[str, float]]]


class VirtualPipetteDataProvider:
Expand Down Expand Up @@ -275,6 +276,7 @@ def _get_virtual_pipette_static_config_by_model( # noqa: C901
front_right_corner_offset=Point(
pip_front_right[0], pip_front_right[1], pip_front_right[2]
),
pipette_lld_settings=config.lld_settings,
)

def get_virtual_pipette_static_config(
Expand Down Expand Up @@ -321,6 +323,7 @@ def get_pipette_static_config(
front_right_corner_offset=Point(
front_right_offset[0], front_right_offset[1], front_right_offset[2]
),
pipette_lld_settings=pipette_dict["lld_settings"],
)


Expand Down
23 changes: 23 additions & 0 deletions api/src/opentrons/protocol_engine/state/pipettes.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class StaticPipetteConfig:
pipette_bounding_box_offsets: PipetteBoundingBoxOffsets
bounding_nozzle_offsets: BoundingNozzlesOffsets
default_nozzle_map: NozzleMap
lld_settings: Optional[Dict[str, Dict[str, float]]]


@dataclass
Expand Down Expand Up @@ -199,6 +200,7 @@ def _handle_command( # noqa: C901
front_right_offset=config.nozzle_map.front_right_nozzle_offset,
),
default_nozzle_map=config.nozzle_map,
lld_settings=config.pipette_lld_settings,
)
self._state.flow_rates_by_id[private_result.pipette_id] = config.flow_rates
self._state.nozzle_configuration_by_id[
Expand Down Expand Up @@ -623,6 +625,27 @@ def get_available_volume(self, pipette_id: str) -> Optional[float]:

return max(0.0, working_volume - current_volume) if current_volume else None

def get_pipette_lld_settings(
self, pipette_id: str
) -> Optional[Dict[str, Dict[str, float]]]:
"""Get the liquid level settings for all possible tips for a single pipette."""
return self.get_config(pipette_id).lld_settings

def get_current_tip_lld_settings(self, pipette_id: str) -> float:
"""Get the liquid level settings for pipette and its current tip."""
attached_tip = self.get_attached_tip(pipette_id)
if attached_tip is None or attached_tip.volume is None:
return 0
lld_settings = self.get_pipette_lld_settings(pipette_id)
tipVolume = str(attached_tip.volume)
if (
lld_settings is None
or lld_settings[tipVolume] is None
or lld_settings[tipVolume]["minHeight"] is None
):
return 0
return float(lld_settings[tipVolume]["minHeight"])

def validate_tip_state(self, pipette_id: str, expected_has_tip: bool) -> None:
"""Validate that a pipette's tip state matches expectations."""
attached_tip = self.get_attached_tip(pipette_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ async def test_configure_for_volume_implementation(
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_MULTI),
back_left_corner_offset=Point(10, 20, 30),
front_right_corner_offset=Point(40, 50, 60),
pipette_lld_settings={},
)

decoy.when(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ async def test_load_pipette_implementation(
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_MULTI),
back_left_corner_offset=Point(x=1, y=2, z=3),
front_right_corner_offset=Point(x=4, y=5, z=6),
pipette_lld_settings={},
)

decoy.when(
Expand Down Expand Up @@ -119,6 +120,7 @@ async def test_load_pipette_implementation_96_channel(
nozzle_map=get_default_nozzle_map(PipetteNameType.P1000_96),
back_left_corner_offset=Point(x=1, y=2, z=3),
front_right_corner_offset=Point(x=4, y=5, z=6),
pipette_lld_settings={},
)

decoy.when(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def loaded_static_pipette_data(
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE),
back_left_corner_offset=Point(x=1, y=2, z=3),
front_right_corner_offset=Point(x=4, y=5, z=6),
pipette_lld_settings={},
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def test_get_virtual_pipette_static_config(
nozzle_map=result.nozzle_map,
back_left_corner_offset=Point(0, 0, 10.45),
front_right_corner_offset=Point(0, 0, 10.45),
pipette_lld_settings={},
)


Expand Down Expand Up @@ -92,6 +93,7 @@ def test_configure_virtual_pipette_for_volume(
nozzle_map=result1.nozzle_map,
back_left_corner_offset=Point(-8.0, -22.0, -259.15),
front_right_corner_offset=Point(-8.0, -22.0, -259.15),
pipette_lld_settings={"t50": {"minHeight": 0.5, "minVolume": 0.0}},
)
subject_instance.configure_virtual_pipette_for_volume(
"my-pipette", 1, result1.model
Expand All @@ -117,6 +119,7 @@ def test_configure_virtual_pipette_for_volume(
nozzle_map=result2.nozzle_map,
back_left_corner_offset=Point(-8.0, -22.0, -259.15),
front_right_corner_offset=Point(-8.0, -22.0, -259.15),
pipette_lld_settings={"t50": {"minHeight": 0.5, "minVolume": 0.0}},
)


Expand Down Expand Up @@ -145,6 +148,7 @@ def test_load_virtual_pipette_by_model_string(
nozzle_map=result.nozzle_map,
back_left_corner_offset=Point(-16.0, 43.15, 35.52),
front_right_corner_offset=Point(16.0, -43.15, 35.52),
pipette_lld_settings={},
)


Expand Down Expand Up @@ -237,6 +241,11 @@ def pipette_dict(
backLeftCorner=[10, 20, 30],
frontRightCorner=[40, 50, 60],
),
"lld_settings": {
"t50": {"minHeight": 0.5, "minVolume": 0},
"t200": {"minHeight": 0.5, "minVolume": 0},
"t1000": {"minHeight": 0.5, "minVolume": 0},
},
}


Expand Down Expand Up @@ -278,6 +287,11 @@ def test_get_pipette_static_config(
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE_GEN2),
back_left_corner_offset=Point(10, 20, 30),
front_right_corner_offset=Point(40, 50, 60),
pipette_lld_settings={
"t50": {"minHeight": 0.5, "minVolume": 0},
"t200": {"minHeight": 0.5, "minVolume": 0},
"t1000": {"minHeight": 0.5, "minVolume": 0},
},
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2078,6 +2078,7 @@ def test_get_next_drop_tip_location(
back_left_corner=Point(x=10, y=20, z=30),
front_right_corner=Point(x=40, y=50, z=60),
),
lld_settings={},
)
)
decoy.when(mock_pipette_view.get_mount("pip-123")).then_return(pipette_mount)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ def test_add_pipette_config(
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE),
back_left_corner_offset=Point(x=1, y=2, z=3),
front_right_corner_offset=Point(x=4, y=5, z=6),
pipette_lld_settings={},
),
)
subject.handle_action(
Expand All @@ -775,6 +776,7 @@ def test_add_pipette_config(
back_left_corner=Point(x=1, y=2, z=3),
front_right_corner=Point(x=4, y=5, z=6),
),
lld_settings={},
)
assert subject.state.flow_rates_by_id["pipette-id"].default_aspirate == {"a": 1.0}
assert subject.state.flow_rates_by_id["pipette-id"].default_dispense == {"b": 2.0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ def test_get_pipette_working_volume(
bounding_nozzle_offsets=_SAMPLE_NOZZLE_BOUNDS_OFFSETS,
default_nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE),
pipette_bounding_box_offsets=_SAMPLE_PIPETTE_BOUNDING_BOX_OFFSETS,
lld_settings={},
)
},
)
Expand Down Expand Up @@ -308,6 +309,7 @@ def test_get_pipette_working_volume_raises_if_tip_volume_is_none(
bounding_nozzle_offsets=_SAMPLE_NOZZLE_BOUNDS_OFFSETS,
default_nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE),
pipette_bounding_box_offsets=_SAMPLE_PIPETTE_BOUNDING_BOX_OFFSETS,
lld_settings={},
)
},
)
Expand Down Expand Up @@ -347,6 +349,7 @@ def test_get_pipette_available_volume(
bounding_nozzle_offsets=_SAMPLE_NOZZLE_BOUNDS_OFFSETS,
default_nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE),
pipette_bounding_box_offsets=_SAMPLE_PIPETTE_BOUNDING_BOX_OFFSETS,
lld_settings={},
),
"pipette-id-none": StaticPipetteConfig(
min_volume=1,
Expand All @@ -362,6 +365,7 @@ def test_get_pipette_available_volume(
bounding_nozzle_offsets=_SAMPLE_NOZZLE_BOUNDS_OFFSETS,
default_nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE),
pipette_bounding_box_offsets=_SAMPLE_PIPETTE_BOUNDING_BOX_OFFSETS,
lld_settings={},
),
},
)
Expand Down Expand Up @@ -473,6 +477,7 @@ def test_get_static_config(
bounding_nozzle_offsets=_SAMPLE_NOZZLE_BOUNDS_OFFSETS,
default_nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE),
pipette_bounding_box_offsets=_SAMPLE_PIPETTE_BOUNDING_BOX_OFFSETS,
lld_settings={},
)

subject = get_pipette_view(
Expand Down Expand Up @@ -523,6 +528,7 @@ def test_get_nominal_tip_overlap(
bounding_nozzle_offsets=_SAMPLE_NOZZLE_BOUNDS_OFFSETS,
default_nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE),
pipette_bounding_box_offsets=_SAMPLE_PIPETTE_BOUNDING_BOX_OFFSETS,
lld_settings={},
)

subject = get_pipette_view(static_config_by_id={"pipette-id": config})
Expand Down Expand Up @@ -783,6 +789,7 @@ def test_get_nozzle_bounds_at_location(
default_nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE),
bounding_nozzle_offsets=_SAMPLE_NOZZLE_BOUNDS_OFFSETS,
pipette_bounding_box_offsets=bounding_box_offsets,
lld_settings={},
)
},
)
Expand Down
Loading

0 comments on commit abf7f02

Please sign in to comment.