Skip to content

Commit abf7f02

Browse files
feat(api): change liquid_probe to respect min well height (#15488)
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. -->
1 parent 3d99b54 commit abf7f02

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+478
-45
lines changed

api/src/opentrons/hardware_control/dev_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ class PipetteDict(InstrumentDict):
9999
supported_tips: Dict[PipetteTipType, SupportedTipsDefinition]
100100
pipette_bounding_box_offsets: PipetteBoundingBoxOffsetDefinition
101101
current_nozzle_map: NozzleMap
102+
lld_settings: Optional[Dict[str, Dict[str, float]]]
102103

103104

104105
class PipetteStateDict(TypedDict):

api/src/opentrons/hardware_control/instruments/ot2/pipette_handler.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ def get_attached_instrument(self, mount: MountType) -> PipetteDict:
220220
"default_dispense_flow_rates",
221221
"back_compat_names",
222222
"supported_tips",
223+
"lld_settings",
223224
]
224225

225226
instr_dict = instr.as_dict()
@@ -259,6 +260,7 @@ def get_attached_instrument(self, mount: MountType) -> PipetteDict:
259260
result[
260261
"pipette_bounding_box_offsets"
261262
] = instr.config.pipette_bounding_box_offsets
263+
result["lld_settings"] = instr.config.lld_settings
262264
return cast(PipetteDict, result)
263265

264266
@property

api/src/opentrons/hardware_control/instruments/ot3/pipette_handler.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict:
236236
"default_dispense_flow_rates",
237237
"back_compat_names",
238238
"supported_tips",
239+
"lld_settings",
239240
]
240241

241242
instr_dict = instr.as_dict()
@@ -280,6 +281,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict:
280281
result[
281282
"pipette_bounding_box_offsets"
282283
] = instr.config.pipette_bounding_box_offsets
284+
result["lld_settings"] = instr.config.lld_settings
283285
return cast(PipetteDict, result)
284286

285287
@property

api/src/opentrons/protocol_engine/execution/pipetting.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,11 @@ async def liquid_probe_in_place(
177177
)
178178
well_def = self._state_view.labware.get_well_definition(labware_id, well_name)
179179
well_depth = well_def.depth
180+
lld_min_height = self._state_view.pipettes.get_current_tip_lld_settings(
181+
pipette_id=pipette_id
182+
)
180183
z_pos = await self._hardware_api.liquid_probe(
181-
mount=hw_pipette.mount, max_z_dist=well_depth
184+
mount=hw_pipette.mount, max_z_dist=well_depth - lld_min_height
182185
)
183186
return float(z_pos)
184187

api/src/opentrons/protocol_engine/resources/pipette_data_provider.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class LoadedStaticPipetteData:
6666
nozzle_map: NozzleMap
6767
back_left_corner_offset: Point
6868
front_right_corner_offset: Point
69+
pipette_lld_settings: Optional[Dict[str, Dict[str, float]]]
6970

7071

7172
class VirtualPipetteDataProvider:
@@ -275,6 +276,7 @@ def _get_virtual_pipette_static_config_by_model( # noqa: C901
275276
front_right_corner_offset=Point(
276277
pip_front_right[0], pip_front_right[1], pip_front_right[2]
277278
),
279+
pipette_lld_settings=config.lld_settings,
278280
)
279281

280282
def get_virtual_pipette_static_config(
@@ -321,6 +323,7 @@ def get_pipette_static_config(
321323
front_right_corner_offset=Point(
322324
front_right_offset[0], front_right_offset[1], front_right_offset[2]
323325
),
326+
pipette_lld_settings=pipette_dict["lld_settings"],
324327
)
325328

326329

api/src/opentrons/protocol_engine/state/pipettes.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ class StaticPipetteConfig:
118118
pipette_bounding_box_offsets: PipetteBoundingBoxOffsets
119119
bounding_nozzle_offsets: BoundingNozzlesOffsets
120120
default_nozzle_map: NozzleMap
121+
lld_settings: Optional[Dict[str, Dict[str, float]]]
121122

122123

123124
@dataclass
@@ -199,6 +200,7 @@ def _handle_command( # noqa: C901
199200
front_right_offset=config.nozzle_map.front_right_nozzle_offset,
200201
),
201202
default_nozzle_map=config.nozzle_map,
203+
lld_settings=config.pipette_lld_settings,
202204
)
203205
self._state.flow_rates_by_id[private_result.pipette_id] = config.flow_rates
204206
self._state.nozzle_configuration_by_id[
@@ -623,6 +625,27 @@ def get_available_volume(self, pipette_id: str) -> Optional[float]:
623625

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

628+
def get_pipette_lld_settings(
629+
self, pipette_id: str
630+
) -> Optional[Dict[str, Dict[str, float]]]:
631+
"""Get the liquid level settings for all possible tips for a single pipette."""
632+
return self.get_config(pipette_id).lld_settings
633+
634+
def get_current_tip_lld_settings(self, pipette_id: str) -> float:
635+
"""Get the liquid level settings for pipette and its current tip."""
636+
attached_tip = self.get_attached_tip(pipette_id)
637+
if attached_tip is None or attached_tip.volume is None:
638+
return 0
639+
lld_settings = self.get_pipette_lld_settings(pipette_id)
640+
tipVolume = str(attached_tip.volume)
641+
if (
642+
lld_settings is None
643+
or lld_settings[tipVolume] is None
644+
or lld_settings[tipVolume]["minHeight"] is None
645+
):
646+
return 0
647+
return float(lld_settings[tipVolume]["minHeight"])
648+
626649
def validate_tip_state(self, pipette_id: str, expected_has_tip: bool) -> None:
627650
"""Validate that a pipette's tip state matches expectations."""
628651
attached_tip = self.get_attached_tip(pipette_id)

api/tests/opentrons/protocol_engine/commands/test_configure_for_volume.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ async def test_configure_for_volume_implementation(
5959
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_MULTI),
6060
back_left_corner_offset=Point(10, 20, 30),
6161
front_right_corner_offset=Point(40, 50, 60),
62+
pipette_lld_settings={},
6263
)
6364

6465
decoy.when(

api/tests/opentrons/protocol_engine/commands/test_load_pipette.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ async def test_load_pipette_implementation(
6363
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_MULTI),
6464
back_left_corner_offset=Point(x=1, y=2, z=3),
6565
front_right_corner_offset=Point(x=4, y=5, z=6),
66+
pipette_lld_settings={},
6667
)
6768

6869
decoy.when(
@@ -119,6 +120,7 @@ async def test_load_pipette_implementation_96_channel(
119120
nozzle_map=get_default_nozzle_map(PipetteNameType.P1000_96),
120121
back_left_corner_offset=Point(x=1, y=2, z=3),
121122
front_right_corner_offset=Point(x=4, y=5, z=6),
123+
pipette_lld_settings={},
122124
)
123125

124126
decoy.when(

api/tests/opentrons/protocol_engine/execution/test_equipment_handler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ def loaded_static_pipette_data(
152152
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE),
153153
back_left_corner_offset=Point(x=1, y=2, z=3),
154154
front_right_corner_offset=Point(x=4, y=5, z=6),
155+
pipette_lld_settings={},
155156
)
156157

157158

api/tests/opentrons/protocol_engine/resources/test_pipette_data_provider.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def test_get_virtual_pipette_static_config(
6464
nozzle_map=result.nozzle_map,
6565
back_left_corner_offset=Point(0, 0, 10.45),
6666
front_right_corner_offset=Point(0, 0, 10.45),
67+
pipette_lld_settings={},
6768
)
6869

6970

@@ -92,6 +93,7 @@ def test_configure_virtual_pipette_for_volume(
9293
nozzle_map=result1.nozzle_map,
9394
back_left_corner_offset=Point(-8.0, -22.0, -259.15),
9495
front_right_corner_offset=Point(-8.0, -22.0, -259.15),
96+
pipette_lld_settings={"t50": {"minHeight": 0.5, "minVolume": 0.0}},
9597
)
9698
subject_instance.configure_virtual_pipette_for_volume(
9799
"my-pipette", 1, result1.model
@@ -117,6 +119,7 @@ def test_configure_virtual_pipette_for_volume(
117119
nozzle_map=result2.nozzle_map,
118120
back_left_corner_offset=Point(-8.0, -22.0, -259.15),
119121
front_right_corner_offset=Point(-8.0, -22.0, -259.15),
122+
pipette_lld_settings={"t50": {"minHeight": 0.5, "minVolume": 0.0}},
120123
)
121124

122125

@@ -145,6 +148,7 @@ def test_load_virtual_pipette_by_model_string(
145148
nozzle_map=result.nozzle_map,
146149
back_left_corner_offset=Point(-16.0, 43.15, 35.52),
147150
front_right_corner_offset=Point(16.0, -43.15, 35.52),
151+
pipette_lld_settings={},
148152
)
149153

150154

@@ -237,6 +241,11 @@ def pipette_dict(
237241
backLeftCorner=[10, 20, 30],
238242
frontRightCorner=[40, 50, 60],
239243
),
244+
"lld_settings": {
245+
"t50": {"minHeight": 0.5, "minVolume": 0},
246+
"t200": {"minHeight": 0.5, "minVolume": 0},
247+
"t1000": {"minHeight": 0.5, "minVolume": 0},
248+
},
240249
}
241250

242251

@@ -278,6 +287,11 @@ def test_get_pipette_static_config(
278287
nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE_GEN2),
279288
back_left_corner_offset=Point(10, 20, 30),
280289
front_right_corner_offset=Point(40, 50, 60),
290+
pipette_lld_settings={
291+
"t50": {"minHeight": 0.5, "minVolume": 0},
292+
"t200": {"minHeight": 0.5, "minVolume": 0},
293+
"t1000": {"minHeight": 0.5, "minVolume": 0},
294+
},
281295
)
282296

283297

0 commit comments

Comments
 (0)