Skip to content

Commit 1715bfe

Browse files
authored
fix(api): Ensure validate pipette movement safety for liquid probe (#18285)
Covers RQA-4174 Ensures Liquid probe calls were not check that movement was within the deck bounds and avoiding certain collisions.
1 parent d95d96d commit 1715bfe

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

api/src/opentrons/protocol_api/core/engine/instrument.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2283,6 +2283,13 @@ def liquid_probe_with_recovery(self, well_core: WellCore, loc: Location) -> None
22832283
well_location = WellLocation(
22842284
origin=WellOrigin.TOP, offset=WellOffset(x=offset.x, y=offset.y, z=offset.z)
22852285
)
2286+
pipette_movement_conflict.check_safe_for_pipette_movement(
2287+
engine_state=self._engine_client.state,
2288+
pipette_id=self._pipette_id,
2289+
labware_id=labware_id,
2290+
well_name=well_name,
2291+
well_location=well_location,
2292+
)
22862293
self._engine_client.execute_command(
22872294
cmd.LiquidProbeParams(
22882295
labwareId=labware_id,
@@ -2303,6 +2310,13 @@ def liquid_probe_without_recovery(
23032310
well_location = WellLocation(
23042311
origin=WellOrigin.TOP, offset=WellOffset(x=0, y=0, z=2)
23052312
)
2313+
pipette_movement_conflict.check_safe_for_pipette_movement(
2314+
engine_state=self._engine_client.state,
2315+
pipette_id=self._pipette_id,
2316+
labware_id=labware_id,
2317+
well_name=well_name,
2318+
well_location=well_location,
2319+
)
23062320
result = self._engine_client.execute_command_without_recovery(
23072321
cmd.LiquidProbeParams(
23082322
labwareId=labware_id,

api/tests/opentrons/protocol_api/core/engine/test_instrument_core.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,6 +1776,53 @@ def test_liquid_probe_without_recovery(
17761776
subject.liquid_probe_without_recovery(well_core=well_core, loc=loc)
17771777

17781778

1779+
def test_liquid_probe_without_recovery_unsafe(
1780+
decoy: Decoy,
1781+
mock_engine_client: EngineClient,
1782+
subject: InstrumentCore,
1783+
) -> None:
1784+
"""It should raise an exception on when attempting to liquid probe out of bounds when the pipette is in partial tip."""
1785+
well_core = WellCore(
1786+
name="my cool well", labware_id="123abc", engine_client=mock_engine_client
1787+
)
1788+
decoy.when(
1789+
pipette_movement_conflict.check_safe_for_pipette_movement(
1790+
engine_state=mock_engine_client.state,
1791+
pipette_id=subject.pipette_id,
1792+
labware_id=well_core.labware_id,
1793+
well_name=well_core.get_name(),
1794+
well_location=WellLocation(
1795+
origin=WellOrigin.TOP, offset=WellOffset(x=0, y=0, z=2)
1796+
),
1797+
)
1798+
).then_raise(
1799+
pipette_movement_conflict.PartialTipMovementNotAllowedError(
1800+
"Requested motion with the A1 nozzle partial configuration is outside of robot bounds for the pipette."
1801+
)
1802+
)
1803+
1804+
decoy.when(
1805+
mock_engine_client.execute_command_without_recovery(
1806+
cmd.LiquidProbeParams(
1807+
pipetteId=subject.pipette_id,
1808+
wellLocation=WellLocation(
1809+
origin=WellOrigin.TOP, offset=WellOffset(x=0, y=0, z=2)
1810+
),
1811+
wellName=well_core.get_name(),
1812+
labwareId=well_core.labware_id,
1813+
)
1814+
)
1815+
).then_return(
1816+
cmd.LiquidProbeResult(position=DeckPoint(x=0, y=0, z=0), z_position=0)
1817+
)
1818+
loc = Location(Point(0, 0, 0), None)
1819+
with pytest.raises(
1820+
pipette_movement_conflict.PartialTipMovementNotAllowedError,
1821+
match="Requested motion with the A1 nozzle partial configuration is outside of robot bounds for the pipette.",
1822+
):
1823+
subject.liquid_probe_without_recovery(well_core=well_core, loc=loc)
1824+
1825+
17791826
def test_liquid_probe_with_recovery(
17801827
decoy: Decoy,
17811828
mock_engine_client: EngineClient,
@@ -1801,6 +1848,53 @@ def test_liquid_probe_with_recovery(
18011848
)
18021849

18031850

1851+
def test_liquid_probe_with_recovery_unsafe(
1852+
decoy: Decoy,
1853+
mock_engine_client: EngineClient,
1854+
subject: InstrumentCore,
1855+
) -> None:
1856+
"""It should raise an exception on when attempting to liquid probe out of bounds when the pipette is in partial tip."""
1857+
well_core = WellCore(
1858+
name="my cool well", labware_id="123abc", engine_client=mock_engine_client
1859+
)
1860+
decoy.when(
1861+
pipette_movement_conflict.check_safe_for_pipette_movement(
1862+
engine_state=mock_engine_client.state,
1863+
pipette_id=subject.pipette_id,
1864+
labware_id=well_core.labware_id,
1865+
well_name=well_core.get_name(),
1866+
well_location=WellLocation(
1867+
origin=WellOrigin.TOP, offset=WellOffset(x=0, y=0, z=2)
1868+
),
1869+
)
1870+
).then_raise(
1871+
pipette_movement_conflict.PartialTipMovementNotAllowedError(
1872+
"Requested motion with the A1 nozzle partial configuration is outside of robot bounds for the pipette."
1873+
)
1874+
)
1875+
1876+
decoy.when(
1877+
mock_engine_client.execute_command(
1878+
cmd.LiquidProbeParams(
1879+
pipetteId=subject.pipette_id,
1880+
wellLocation=WellLocation(
1881+
origin=WellOrigin.TOP, offset=WellOffset(x=0, y=0, z=2.0)
1882+
),
1883+
wellName=well_core.get_name(),
1884+
labwareId=well_core.labware_id,
1885+
)
1886+
)
1887+
).then_return(
1888+
cmd.LiquidProbeResult(position=DeckPoint(x=0, y=0, z=0), z_position=0)
1889+
)
1890+
loc = Location(Point(0, 0, 0), None)
1891+
with pytest.raises(
1892+
pipette_movement_conflict.PartialTipMovementNotAllowedError,
1893+
match="Requested motion with the A1 nozzle partial configuration is outside of robot bounds for the pipette.",
1894+
):
1895+
subject.liquid_probe_with_recovery(well_core=well_core, loc=loc)
1896+
1897+
18041898
@pytest.mark.parametrize("version", versions_at_or_above(APIVersion(2, 23)))
18051899
def test_load_liquid_class(
18061900
decoy: Decoy,

0 commit comments

Comments
 (0)