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): allow labware calibration on non liquid handling events #7812

Merged
merged 2 commits into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 15 additions & 0 deletions api/src/opentrons/commands/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,18 @@ def drop_tip(
'text': text
}
}


def move_to(
instrument: InstrumentContext,
location: Union[Location, Well]) -> command_types.MoveToCommand:
location_text = stringify_location(location)
text = 'Moving to {location}'.format(location=location_text)
return {
'name': command_types.MOVE_TO,
'payload': {
'instrument': instrument,
'location': location,
'text': text
}
}
15 changes: 15 additions & 0 deletions api/src/opentrons/commands/paired_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,18 @@ def paired_drop_tip(
'text': text
}
}


def paired_move_to(
instruments: Apiv2Instruments,
locations: Apiv2Locations, pub_type: str) -> command_types.MoveToCommand:
location_text = combine_locations(locations)
text = f'{pub_type}: Moving to {location_text}'
return {
'name': command_types.MOVE_TO,
'payload': {
'instruments': instruments,
'locations': locations,
'text': text
}
}
27 changes: 24 additions & 3 deletions api/src/opentrons/commands/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
AIR_GAP: Final = 'command.AIR_GAP'
TOUCH_TIP: Final = 'command.TOUCH_TIP'
RETURN_TIP: Final = 'command.RETURN_TIP'
MOVE_TO: Final = 'command.MOVE_TO'

# Modules #

Expand Down Expand Up @@ -450,6 +451,21 @@ class DropTipCommand(TypedDict):
payload: Union[DropTipCommandPayload, PairedDropTipCommandPayload]


class MoveToCommand(TypedDict):
name: Literal['command.MOVE_TO']
payload: Union[MoveToCommandPayload, PairedMoveToCommandPayload]


class MoveToCommandPayload(
TextOnlyPayload, SingleLocationPayload, SingleInstrumentPayload):
pass


class PairedMoveToCommandPayload(
TextOnlyPayload, MultiLocationPayload, MultiInstrumentPayload):
pass


Command = Union[
DropTipCommand, PickUpTipCommand, ReturnTipCommand, AirGapCommand,
TouchTipCommand, BlowOutCommand, MixCommand, TransferCommand,
Expand All @@ -463,7 +479,7 @@ class DropTipCommand(TypedDict):
TempdeckDeactivateCommand, TempdeckAwaitTempCommand,
TempdeckSetTempCommand, MagdeckCalibrateCommand, MagdeckDisengageCommand,
MagdeckEngageCommand, ResumeCommand, PauseCommand, DelayCommand,
CommentCommand]
CommentCommand, MoveToCommand]


CommandPayload = Union[
Expand Down Expand Up @@ -492,7 +508,8 @@ class DropTipCommand(TypedDict):
ThermocyclerSetBlockTempCommandPayload,
TempdeckAwaitTempCommandPayload,
TempdeckSetTempCommandPayload,
PauseCommandPayload, DelayCommandPayload
PauseCommandPayload, DelayCommandPayload,
MoveToCommandPayload, PairedMoveToCommandPayload
]


Expand All @@ -510,6 +527,10 @@ class CommandMessageFields(CommandMessageMeta, CommandMessageSequence):
pass


class MoveToMessage(CommandMessageFields, MoveToCommand):
pass


class DropTipMessage(CommandMessageFields, DropTipCommand):
pass

Expand Down Expand Up @@ -673,4 +694,4 @@ class CommentMessage(CommandMessageFields, CommentCommand):
ThermocyclerExecuteProfileMessage, ThermocyclerSetBlockTempMessage,
ThermocyclerOpenMessage, TempdeckSetTempMessage, TempdeckDeactivateMessage,
MagdeckEngageMessage, MagdeckDisengageMessage, MagdeckCalibrateMessage,
CommentMessage, DelayMessage, PauseMessage, ResumeMessage]
CommentMessage, DelayMessage, PauseMessage, ResumeMessage, MoveToMessage]
34 changes: 22 additions & 12 deletions api/src/opentrons/protocol_api/instrument_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ def aspirate(self,
if self.api_version < APIVersion(2, 3) or \
not self._implementation.is_ready_to_aspirate():
if dest.labware.is_well:
self.move_to(dest.labware.as_well().top())
self.move_to(dest.labware.as_well().top(),
publish=False)
else:
# TODO(seth,2019/7/29): This should be a warning exposed
# via rpc to the runapp
Expand All @@ -202,9 +203,9 @@ def aspirate(self,
"cause over aspiration if the previous command is a "
"blow_out.")
self._implementation.prepare_for_aspirate()
self.move_to(dest)
self.move_to(dest, publish=False)
elif dest != self._ctx.location_cache:
self.move_to(dest)
self.move_to(dest, publish=False)

c_vol = self._implementation.get_available_volume() \
if not volume else volume
Expand Down Expand Up @@ -271,10 +272,10 @@ def dispense(self,
else:
loc = location.bottom().move(
types.Point(0, 0, self.well_bottom_clearance.dispense))
self.move_to(loc)
self.move_to(loc, publish=False)
elif isinstance(location, types.Location):
loc = location
self.move_to(location)
self.move_to(location, publish=False)
elif location is not None:
raise TypeError(
f'location should be a Well or Location, but it is {location}')
Expand Down Expand Up @@ -393,10 +394,10 @@ def blow_out(self,
self._log.warning('Blow_out being performed on a tiprack. '
'Please re-check your code')
loc = location.top()
self.move_to(loc)
self.move_to(loc, publish=False)
elif isinstance(location, types.Location):
loc = location
self.move_to(loc)
self.move_to(loc, publish=False)
elif location is not None:
raise TypeError(
'location should be a Well or Location, but it is {}'
Expand Down Expand Up @@ -496,7 +497,7 @@ def touch_tip(self,
move_with_z_offset =\
well.as_well().top().point + types.Point(0, 0, v_offset)
to_loc = types.Location(move_with_z_offset, well)
self.move_to(to_loc)
self.move_to(to_loc, publish=False)
else:
raise TypeError(
'location should be a Well, but it is {}'.format(location))
Expand Down Expand Up @@ -554,7 +555,7 @@ def air_gap(self,
if not loc or not loc.labware.is_well:
raise RuntimeError('No previous Well cached to perform air gap')
target = loc.labware.as_well().top(height)
self.move_to(target)
self.move_to(target, publish=False)
self.aspirate(volume)
return self

Expand Down Expand Up @@ -660,7 +661,7 @@ def pick_up_tip(
do_publish(self.broker, cmds.pick_up_tip, self.pick_up_tip,
'before', None, None, self, location=target)

self.move_to(target.top())
self.move_to(target.top(), publish=False)

self._implementation.pick_up_tip(
well=target._impl,
Expand Down Expand Up @@ -780,7 +781,7 @@ def drop_tip(
" However, it is a {}".format(location))
do_publish(self.broker, cmds.drop_tip, self.drop_tip,
'before', None, None, self, location=target)
self.move_to(target)
self.move_to(target, publish=False)

self._implementation.drop_tip(home_after=home_after)
do_publish(self.broker, cmds.drop_tip, self.drop_tip,
Expand Down Expand Up @@ -1086,7 +1087,8 @@ def move_to(self,
location: types.Location,
force_direct: bool = False,
minimum_z_height: Optional[float] = None,
speed: Optional[float] = None
speed: Optional[float] = None,
publish: bool = True
) -> InstrumentContext:
""" Move the instrument.

Expand All @@ -1101,6 +1103,8 @@ def move_to(self,
the straight linear speed of the motion; to limit
individual axis speeds, you can use
:py:attr:`.ProtocolContext.max_speeds`.
:param publish: Whether a call to this function should publish to the
runlog or not.
"""
from_loc = self._ctx.location_cache
if not from_loc:
Expand All @@ -1110,12 +1114,18 @@ def move_to(self,
if isinstance(mod, ThermocyclerContext):
mod.flag_unsafe_move(to_loc=location, from_loc=from_loc)

if publish:
do_publish(self.broker, cmds.move_to, self.move_to, 'before',
None, None, self, location or self._ctx.location_cache)
self._implementation.move_to(
location=location,
force_direct=force_direct,
minimum_z_height=minimum_z_height,
speed=speed
)
if publish:
do_publish(self.broker, cmds.move_to, self.move_to, 'after',
None, None, self, location or self._ctx.location_cache)
return self

@property # type: ignore
Expand Down
9 changes: 9 additions & 0 deletions api/src/opentrons/protocol_api/paired_instrument_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,8 +808,17 @@ def move_to(self, location: types.Location, force_direct: bool = False,
to limit individual axis speeds, you can use
:py:attr:`.ProtocolContext.max_speeds`.
"""
instruments = list(self._instruments.values())
locations: Optional[List] = None
if location:
locations = self._get_locations(location)

publish_paired(self.broker, cmds.paired_move_to,
'before', None, instruments, locations)
self.paired_instrument_obj.move_to(
location, force_direct, minimum_z_height, speed)
publish_paired(self.broker, cmds.paired_move_to,
'after', None, instruments, locations)
return self

def _next_available_tip(self) -> Tuple[Labware, Well]:
Expand Down
25 changes: 23 additions & 2 deletions api/tests/opentrons/api/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ def run(ctx):
contents=proto)


@pytest.mark.api2_only
async def test_session_extra_labware(main_router, get_labware_fixture,
virtual_smoothie_env):
proto = '''
Expand Down Expand Up @@ -205,7 +204,6 @@ def run(ctx):
contents=proto)


@pytest.mark.api2_only
async def test_session_bundle(main_router, get_bundle_fixture,
virtual_smoothie_env):
bundle = get_bundle_fixture('simple_bundle')
Expand Down Expand Up @@ -257,6 +255,29 @@ def run(ctx):
assert 'p10_single_v1' in [pip.name for pip in session2.instruments]


async def test_session_move_to_labware(main_router,
virtual_smoothie_env):
proto = '''
metadata = {"apiLevel": "2.0"}
def run(ctx):
rack1 = ctx.load_labware('opentrons_96_tiprack_300ul', '1')
rack2 = ctx.load_labware('opentrons_96_tiprack_300ul', '2')
left = ctx.load_instrument('p300_single', 'left', tip_racks=[rack1])
plate = ctx.load_labware('corning_96_wellplate_360ul_flat', '4')
left.pick_up_tip()
left.move_to(plate['A1'].top())
left.move_to(plate['A2'].top())
left.drop_tip()
'''
session = main_router.session_manager.create('dummy-pipette',
proto)
assert 'p300_single_v1' in [pip.name for pip in session.instruments]

# Labware that does not have a liquid handling event, but is interacted
# with using a pipette should still show up in the list of labware.
assert 'corning_96_wellplate_360ul_flat' in [lw.type for lw in session.containers]


async def test_session_run_concurrently(
main_router,
get_labware_fixture,
Expand Down
3 changes: 0 additions & 3 deletions api/tests/opentrons/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,6 @@ def model(request, hardware, loop):
# Use with pytest.mark.parametrize(’labware’, [some-labware-name])
# to have a different labware loaded as .container. If not passed,
# defaults to the version-appropriate way to do 96 flat
if request.node.get_closest_marker('api2_only')\
and request.param != build_v2_model:
pytest.skip('only works with hardware controller')
try:
lw_name = request.getfixturevalue('labware_name')
except Exception:
Expand Down
2 changes: 1 addition & 1 deletion api/tests/opentrons/protocol_api/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ def test_blow_out(ctx, monkeypatch):
instr.pick_up_tip()
instr.aspirate(10, lw.wells()[0])

def fake_move(loc):
def fake_move(loc, publish):
nonlocal move_location
move_location = loc

Expand Down
5 changes: 5 additions & 0 deletions api/tests/opentrons/test_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def emit_runlog(entry):
'Dispensing 4.5 uL into B1 of Dest Plate on 3 at 2.5 uL/sec',
'Touching tip',
'Blowing out at B1 of Dest Plate on 3',
'Moving to 5',
'Dropping tip into A1 of Trash on 12'
]

Expand All @@ -106,6 +107,7 @@ def emit_runlog(entry):
'Dispensing 4.5 uL into B1 of Dest Plate on 3 at 2.5 uL/sec',
'Touching tip',
'Blowing out at B1 of Dest Plate on 3',
'Moving to 5',
'Dropping tip into A1 of Trash on 12'
]

Expand All @@ -132,6 +134,9 @@ def emit_runlog(entry):
'Dispensing 4.5 uL into B1 of Dest Plate on 3 at 2.5 uL/sec',
'Touching tip',
'Blowing out at B1 of Dest Plate on 3',
'Moving to 5',
'Moving to B2 of Dest Plate on 3',
'Moving to B2 of Dest Plate on 3',
'Dropping tip into A1 of Trash on 12'
]

Expand Down
1 change: 1 addition & 0 deletions api/tests/opentrons/test_simulate.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def test_simulate_function_json_apiv2(get_json_protocol_fixture):
'Dispensing 4.5 uL into B1 of Dest Plate on 3 at 2.5 uL/sec',
'Touching tip',
'Blowing out at B1 of Dest Plate on 3',
'Moving to 5',
'Dropping tip into A1 of Trash on 12'
]

Expand Down