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

refactor(api): Allow aspirate, dispense and blow-out in place in PE and PAPI #12105

Merged
merged 89 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
0d7ee6a
raising error when using args that are deprecated and added to versio…
TamarZanzouri Jan 30, 2023
e4d70f4
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Jan 30, 2023
4c4fb2f
Update api/docs/v2/versioning.rst
TamarZanzouri Jan 30, 2023
6a2b20b
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Jan 30, 2023
4bc29d2
fixed docstrings and formatting
TamarZanzouri Jan 31, 2023
c5231a2
WIP started PE aspirate_in_place.py
TamarZanzouri Jan 31, 2023
964a5a9
added to pipetting aspirate_in_place
TamarZanzouri Feb 1, 2023
e963f9e
blow out in place pe command
TamarZanzouri Feb 1, 2023
a9f68c9
added sync client in-place
TamarZanzouri Feb 1, 2023
4fa3341
engine core asprirate in place
TamarZanzouri Feb 2, 2023
622b1f2
all engine core implementation. removed move_to_well arg
TamarZanzouri Feb 2, 2023
bb0736a
check if location is not the last location before move_to_coordinates
TamarZanzouri Feb 6, 2023
1501ca0
Merge branch 'edge' into RCORE-540-implement-in-place-commands
TamarZanzouri Feb 6, 2023
f7298ce
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 7, 2023
6879d11
Update api/src/opentrons/protocol_engine/commands/aspirate_in_place.py
TamarZanzouri Feb 7, 2023
70540d2
Update api/src/opentrons/protocol_engine/commands/blow_out_in_place.py
TamarZanzouri Feb 7, 2023
b2ffa4b
fixed move_to_well removal
TamarZanzouri Feb 7, 2023
e1c9c06
updated commands schema
TamarZanzouri Feb 7, 2023
1d2e505
linting js
TamarZanzouri Feb 7, 2023
80eecbd
fixed failing test in aspirate in place
TamarZanzouri Feb 7, 2023
ca8fcc9
fixed legacy simulator test failing
TamarZanzouri Feb 7, 2023
d252985
cnanged location to optional and added exception when not ready to as…
TamarZanzouri Feb 9, 2023
f4f7ea8
set last location
TamarZanzouri Feb 9, 2023
0f26c9d
Update api/src/opentrons/protocol_api/core/engine/instrument.py
TamarZanzouri Feb 9, 2023
7bc74cf
Update api/src/opentrons/protocol_api/core/engine/instrument.py
TamarZanzouri Feb 9, 2023
b98967d
Update api/src/opentrons/protocol_api/core/legacy_simulator/legacy_in…
TamarZanzouri Feb 9, 2023
949ca7e
Update api/src/opentrons/protocol_engine/execution/pipetting.py
TamarZanzouri Feb 9, 2023
c10ea27
Update api/tests/opentrons/protocol_engine/execution/test_pipetting_h…
TamarZanzouri Feb 9, 2023
94bbbe3
Update api/src/opentrons/protocol_engine/execution/pipetting.py
TamarZanzouri Feb 10, 2023
0c91bb7
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 10, 2023
c24f9f1
linting
TamarZanzouri Feb 9, 2023
502d464
Merge branch 'edge' into RCORE-540-implement-in-place-commands
TamarZanzouri Feb 10, 2023
e6d1c9e
moved aspirate_in_place logic into command impl, pr fixes
TamarZanzouri Feb 10, 2023
e3c5437
fixed failing tests
TamarZanzouri Feb 13, 2023
993469f
formatting
TamarZanzouri Feb 13, 2023
058c1ac
WIP refactor instrument_context.py
TamarZanzouri Feb 14, 2023
4221d68
reverted changs for the legacy core and added logic for PointTarget
TamarZanzouri Feb 14, 2023
bc0e584
fixed engine core
TamarZanzouri Feb 14, 2023
efc16cc
WIP dispense in public context. reverted optional location and revert…
TamarZanzouri Feb 14, 2023
dea7290
blow out and fixed validation method as_well
TamarZanzouri Feb 15, 2023
520b3a2
linting fixes
TamarZanzouri Feb 15, 2023
dedd7d8
merge conflicts
TamarZanzouri Feb 15, 2023
1fd6b66
Update api/src/opentrons/protocol_api/core/legacy/legacy_instrument_c…
TamarZanzouri Feb 15, 2023
ca44f97
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 15, 2023
f921902
Update api/src/opentrons/protocol_api/validation.py
TamarZanzouri Feb 15, 2023
f4f3dd1
Update api/src/opentrons/protocol_api/validation.py
TamarZanzouri Feb 15, 2023
c88e07d
merge conflicts and docstring
TamarZanzouri Feb 15, 2023
bd19514
Update api/src/opentrons/protocol_api/core/legacy_simulator/legacy_in…
TamarZanzouri Feb 16, 2023
ed3c8c0
Update api/src/opentrons/protocol_api/core/legacy_simulator/legacy_in…
TamarZanzouri Feb 16, 2023
d77b0ea
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 16, 2023
cc8c11f
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 16, 2023
479a859
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 16, 2023
accc27e
Update api/src/opentrons/protocol_api/validation.py
TamarZanzouri Feb 16, 2023
a6f3131
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 16, 2023
f4f234b
removed default arg for in_place. pr feedback
TamarZanzouri Feb 16, 2023
2c26a88
changed aspirate/dispense/blow_out to check against in_place. changed…
TamarZanzouri Feb 16, 2023
321d38b
added in_place to wellTarget
TamarZanzouri Feb 16, 2023
a59d08f
Update api/src/opentrons/protocol_api/core/engine/instrument.py
TamarZanzouri Feb 16, 2023
24ed944
Update api/src/opentrons/protocol_api/core/engine/instrument.py
TamarZanzouri Feb 16, 2023
6b518c4
Update api/src/opentrons/protocol_api/core/engine/instrument.py
TamarZanzouri Feb 16, 2023
1bfdaa0
Update api/src/opentrons/protocol_api/core/legacy_simulator/legacy_in…
TamarZanzouri Feb 16, 2023
aa57d8e
simulator dispense fix
TamarZanzouri Feb 16, 2023
2f877b9
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
c773f4b
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
a73c6c4
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
6cfd709
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
5ab87ce
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
5a692fd
Update api/tests/opentrons/protocol_api/test_validation.py
TamarZanzouri Feb 17, 2023
5443d9c
Update api/tests/opentrons/protocol_api/core/engine/test_instrument_c…
TamarZanzouri Feb 17, 2023
9020b30
Update api/tests/opentrons/protocol_api/core/engine/test_instrument_c…
TamarZanzouri Feb 17, 2023
bf33a18
Update api/tests/opentrons/protocol_api/test_validation.py
TamarZanzouri Feb 17, 2023
1d97ce3
Update api/tests/opentrons/protocol_api_old/core/simulator/test_instr…
TamarZanzouri Feb 17, 2023
4b5ac65
Update api/tests/opentrons/protocol_api/test_validation.py
TamarZanzouri Feb 17, 2023
4cafd32
Update api/tests/opentrons/protocol_api/core/engine/test_instrument_c…
TamarZanzouri Feb 17, 2023
dd97a14
Update api/tests/opentrons/protocol_api/core/engine/test_instrument_c…
TamarZanzouri Feb 17, 2023
e38b474
Update api/tests/opentrons/protocol_api/test_instrument_context.py
TamarZanzouri Feb 17, 2023
6c152fa
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
2601ded
Update api/src/opentrons/protocol_engine/commands/aspirate_in_place.py
TamarZanzouri Feb 17, 2023
51841b5
Update api/src/opentrons/protocol_engine/commands/blow_out_in_place.py
TamarZanzouri Feb 17, 2023
f720ee7
Update api/src/opentrons/protocol_api/validation.py
TamarZanzouri Feb 17, 2023
a7ad08a
Update api/src/opentrons/protocol_api/validation.py
TamarZanzouri Feb 17, 2023
2b7865b
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
51e0101
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
99f4bb1
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
a9a4ae1
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
6b42ec4
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
8a7d24b
Update api/src/opentrons/protocol_api/instrument_context.py
TamarZanzouri Feb 17, 2023
c2d62a0
pr fixes
TamarZanzouri Feb 17, 2023
16b6fba
pr fixes
TamarZanzouri Feb 17, 2023
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
Prev Previous commit
Next Next commit
cnanged location to optional and added exception when not ready to as…
…pirate in place
  • Loading branch information
TamarZanzouri committed Feb 9, 2023
commit d25298558281073ddf560e9c1687a306f8a12779
28 changes: 17 additions & 11 deletions api/src/opentrons/protocol_api/core/engine/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def set_default_speed(self, speed: float) -> None:

def aspirate(
self,
location: Location,
location: Optional[Location],
well_core: Optional[WellCore],
volume: float,
rate: float,
Expand All @@ -81,7 +81,7 @@ def aspirate(
flow_rate: The flow rate in µL/s to aspirate at.
"""
if well_core is None:
if location != self._protocol_core.get_last_location():
if location is not None:
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
self._engine_client.move_to_coordinates(
pipette_id=self._pipette_id,
coordinates=DeckPoint(
Expand All @@ -96,7 +96,7 @@ def aspirate(
pipette_id=self._pipette_id, volume=volume, flow_rate=flow_rate
)

else:
elif location is not None:
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
well_name = well_core.get_name()
labware_id = well_core.labware_id

Expand All @@ -121,7 +121,7 @@ def aspirate(

def dispense(
self,
location: Location,
location: Optional[Location],
well_core: Optional[WellCore],
volume: float,
rate: float,
Expand All @@ -136,7 +136,7 @@ def dispense(
flow_rate: The flow rate in µL/s to dispense at.
"""
if well_core is None:
if location != self._protocol_core.get_last_location():
if location:
self._engine_client.move_to_coordinates(
pipette_id=self._pipette_id,
coordinates=DeckPoint(
Expand All @@ -150,7 +150,7 @@ def dispense(
self._engine_client.dispense_in_place(
pipette_id=self._pipette_id, volume=volume, flow_rate=flow_rate
)
else:
elif location:
well_name = well_core.get_name()
labware_id = well_core.labware_id

Expand All @@ -171,9 +171,13 @@ def dispense(
flow_rate=flow_rate,
)

self._protocol_core.set_last_location(location=location, mount=self.get_mount())
self._protocol_core.set_last_location(
location=location, mount=self.get_mount()
)

def blow_out(self, location: Location, well_core: Optional[WellCore]) -> None:
def blow_out(
self, location: Optional[Location], well_core: Optional[WellCore]
) -> None:
"""Blow liquid out of the tip.

Args:
Expand All @@ -182,7 +186,7 @@ def blow_out(self, location: Location, well_core: Optional[WellCore]) -> None:
"""
flow_rate = self.get_absolute_blow_out_flow_rate(1.0)
if well_core is None:
if location != self._protocol_core.get_last_location():
if location:
self._engine_client.move_to_coordinates(
pipette_id=self._pipette_id,
coordinates=DeckPoint(
Expand All @@ -196,7 +200,7 @@ def blow_out(self, location: Location, well_core: Optional[WellCore]) -> None:
self._engine_client.blow_out_in_place(
pipette_id=self._pipette_id, flow_rate=flow_rate
)
else:
elif location:
well_name = well_core.get_name()
labware_id = well_core.labware_id

Expand All @@ -218,7 +222,9 @@ def blow_out(self, location: Location, well_core: Optional[WellCore]) -> None:
flow_rate=flow_rate,
)

self._protocol_core.set_last_location(location=location, mount=self.get_mount())
self._protocol_core.set_last_location(
location=location, mount=self.get_mount()
)

def touch_tip(
self,
Expand Down
6 changes: 3 additions & 3 deletions api/src/opentrons/protocol_api/core/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def set_default_speed(self, speed: float) -> None:
@abstractmethod
def aspirate(
self,
location: types.Location,
location: Optional[types.Location],
well_core: Optional[WellCoreType],
volume: float,
rate: float,
Expand All @@ -43,7 +43,7 @@ def aspirate(
@abstractmethod
def dispense(
self,
location: types.Location,
location: Optional[types.Location],
well_core: Optional[WellCoreType],
volume: float,
rate: float,
Expand All @@ -62,7 +62,7 @@ def dispense(
@abstractmethod
def blow_out(
self,
location: types.Location,
location: Optional[types.Location],
well_core: Optional[WellCoreType],
) -> None:
"""Blow liquid out of the tip.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def set_default_speed(self, speed: float) -> None:

def aspirate(
self,
location: types.Location,
location: Optional[types.Location],
well_core: Optional[LegacyWellCore],
volume: float,
rate: float,
Expand All @@ -75,7 +75,7 @@ def aspirate(
rate: The rate in µL/s to aspirate at.
flow_rate: Not used in this core.
"""
if self.get_current_volume() == 0:
if location and self.get_current_volume() == 0:
# Make sure we're at the top of the labware and clear of any
# liquid to prepare the pipette for aspiration
if self._api_version < APIVersion(2, 3) or not self.is_ready_to_aspirate():
Expand All @@ -93,14 +93,14 @@ def aspirate(
)
self.prepare_for_aspirate()
self.move_to(location=location)
elif location != self._protocol_interface.get_last_location():
elif location:
self.move_to(location=location)

self._protocol_interface.get_hardware().aspirate(self._mount, volume, rate)

def dispense(
self,
location: types.Location,
location: Optional[types.Location],
well_core: Optional[LegacyWellCore],
volume: float,
rate: float,
Expand All @@ -114,13 +114,14 @@ def dispense(
rate: The rate in µL/s to dispense at.
flow_rate: Not used in this core.
"""
self.move_to(location=location)
if location:
self.move_to(location=location)

self._protocol_interface.get_hardware().dispense(self._mount, volume, rate)

def blow_out(
self,
location: types.Location,
location: Optional[types.Location],
well_core: Optional[LegacyWellCore],
) -> None:
"""Blow liquid out of the tip.
Expand All @@ -129,7 +130,7 @@ def blow_out(
location: The location to blow out into.
well_core: Unused by legacy core.
"""
if location != self._protocol_interface.get_last_location():
if location:
self.move_to(location=location)
self._protocol_interface.get_hardware().blow_out(self._mount)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ def set_default_speed(self, speed: float) -> None:

def aspirate(
self,
location: types.Location,
location: Optional[types.Location],
well_core: Optional[LegacyWellCore],
volume: float,
rate: float,
flow_rate: float,
) -> None:
if self.get_current_volume() == 0:
if location and self.get_current_volume() == 0:
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
# Make sure we're at the top of the labware and clear of any
# liquid to prepare the pipette for aspiration
if self._api_version < APIVersion(2, 3) or not self.is_ready_to_aspirate():
Expand All @@ -90,7 +90,7 @@ def aspirate(
)
self.prepare_for_aspirate()
self.move_to(location=location, well_core=well_core)
elif location != self._protocol_interface.get_last_location():
elif location:
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
self.move_to(location=location, well_core=well_core)

self._raise_if_no_tip(HardwareAction.ASPIRATE.name)
Expand All @@ -103,22 +103,23 @@ def aspirate(

def dispense(
self,
location: types.Location,
location: Optional[types.Location],
well_core: Optional[LegacyWellCore],
volume: float,
rate: float,
flow_rate: float,
) -> None:
self.move_to(location=location, well_core=well_core)
if location:
self.move_to(location=location, well_core=well_core)
self._raise_if_no_tip(HardwareAction.DISPENSE.name)
self._update_volume(self.get_current_volume() - volume)

def blow_out(
self,
location: types.Location,
location: Optional[types.Location],
well_core: Optional[LegacyWellCore],
) -> None:
if location != self._protocol_interface.get_last_location():
if location:
self.move_to(location=location, well_core=well_core)
self._raise_if_no_tip(HardwareAction.BLOWOUT.name)
self._update_volume(0)
Expand Down
46 changes: 33 additions & 13 deletions api/src/opentrons/protocol_api/instrument_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,14 @@ def aspirate(
)

well: Optional[labware.Well]
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
last_location = self._protocol_core.get_last_location()
move_to_location: Optional[types.Location]

if self._api_version >= APIVersion(2, 14):
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
last_location = self._protocol_core.get_last_location(
mount=types.Mount.string_to_mount(self.mount)
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
)
else:
last_location = self._protocol_core.get_last_location()

if isinstance(location, labware.Well):
move_to_location = location.bottom(z=self._well_bottom_clearances.aspirate)
Expand All @@ -187,8 +194,8 @@ def aspirate(
"location should be a Well or Location, but it is {}".format(location)
)
elif last_location:
move_to_location = last_location
_, well = move_to_location.labware.get_parent_labware_and_well()
move_to_location = None
_, well = last_location.labware.get_parent_labware_and_well()
else:
raise RuntimeError(
"If aspirate is called without an explicit location, another"
Expand All @@ -198,7 +205,7 @@ def aspirate(
)
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
if self.api_version >= APIVersion(2, 11):
instrument.validate_takes_liquid(
location=move_to_location,
location=move_to_location or last_location, # type: ignore[arg-type]
reject_module=self.api_version >= APIVersion(2, 13),
)

Expand All @@ -210,7 +217,7 @@ def aspirate(
command=cmds.aspirate(
instrument=self,
volume=c_vol,
location=move_to_location,
location=move_to_location or last_location, # type: ignore[arg-type]
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
flow_rate=flow_rate,
rate=rate,
),
Expand Down Expand Up @@ -278,7 +285,12 @@ def dispense(
)
)
well: Optional[labware.Well]
last_location = self._protocol_core.get_last_location()
if self._api_version >= APIVersion(2, 14):
last_location = self._protocol_core.get_last_location(
mount=types.Mount.string_to_mount(self.mount)
)
else:
last_location = self._protocol_core.get_last_location()

if isinstance(location, labware.Well):
well = location
Expand Down Expand Up @@ -434,26 +446,32 @@ def blow_out(
"""

well: Optional[labware.Well]
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
last_location = self._protocol_core.get_last_location()
move_to_location: Optional[types.Location]
if self._api_version >= APIVersion(2, 14):
last_location = self._protocol_core.get_last_location(
mount=types.Mount.string_to_mount(self.mount)
)
else:
last_location = self._protocol_core.get_last_location()

if isinstance(location, labware.Well):
if location.parent.is_tiprack:
_log.warning(
"Blow_out being performed on a tiprack. "
"Please re-check your code"
)
checked_loc = location.top()
move_to_location = location.top()
well = location
elif isinstance(location, types.Location):
checked_loc = location
move_to_location = location
_, well = location.labware.get_parent_labware_and_well()
elif location is not None:
raise TypeError(
"location should be a Well or Location, but it is {}".format(location)
)
elif last_location:
checked_loc = last_location
_, well = checked_loc.labware.get_parent_labware_and_well()
move_to_location = None
_, well = last_location.labware.get_parent_labware_and_well()
else:
raise RuntimeError(
"If blow out is called without an explicit location, another"
Expand All @@ -464,10 +482,12 @@ def blow_out(

with publisher.publish_context(
broker=self.broker,
command=cmds.blow_out(instrument=self, location=checked_loc),
command=cmds.blow_out(
instrument=self, location=move_to_location or last_location # type: ignore[arg-type]
),
):
self._core.blow_out(
location=checked_loc,
location=move_to_location,
well_core=well._core if well is not None else None,
)

Expand Down
14 changes: 14 additions & 0 deletions api/src/opentrons/protocol_engine/execution/pipetting.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,20 @@ async def aspirate_in_place(
attached_pipettes=self._hardware_api.attached_instruments,
)

ready_to_aspirate = self._state_store.pipettes.get_is_ready_to_aspirate(
pipette_id=pipette_id,
pipette_config=hw_pipette.config,
)

if not ready_to_aspirate:
raise ValueError(
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
"When aspirate is called on something other than a "
"well relative position, we can't move to the top of"
" the well to prepare for aspiration. This might "
"cause over aspiration if the previous command is a "
"blow_out."
TamarZanzouri marked this conversation as resolved.
Show resolved Hide resolved
)

with self.set_flow_rate(pipette=hw_pipette, dispense_flow_rate=flow_rate):
await self._hardware_api.aspirate(mount=hw_pipette.mount, volume=volume)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def test_blow_out_in_place(

decoy.verify(
mock_instrument_core.blow_out(
location=location,
location=None,
well_core=mock_well._core,
),
times=1,
Expand Down
Loading