Skip to content

Commit

Permalink
refactor(api): Consolidate move_to in protocol_api
Browse files Browse the repository at this point in the history
  • Loading branch information
sfoster1 committed Nov 1, 2018
1 parent a6f82dd commit 14311b8
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 50 deletions.
2 changes: 2 additions & 0 deletions api/src/opentrons/hardware_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ def current_position(self, mount: top_types.Mount) -> Dict[Axis, float]:
This returns cached position to avoid hitting the smoothie driver
unless ``refresh`` is ``True``.
"""
if not self._current_position:
raise MustHomeError
if mount == mount.RIGHT:
offset = top_types.Point(0, 0, 0)
else:
Expand Down
63 changes: 30 additions & 33 deletions api/src/opentrons/protocol_api/contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,40 +197,28 @@ def update_config(self, **kwargs):
"""
self._hardware.update_config(**kwargs)

def move_to(self, mount: types.Mount,
location: types.Location):
""" Implement motions of the robot.
This should not need to be called by the user; it is called by
:py:meth:`InstrumentContext.move_to` (and thus all other
:py:class:`InstrumentContext` methods that involve moving, such as
:py:meth:`InstrumentContext.aspirate`) to move the pipettes around.
It encapsulates location caching and ensures that all moves are safe.
It does this by taking a :py:class:`.types.Location` that can have
a position attached to it, and its behavior depends on the state of
that location cache and the passed location.
"""
if self._location_cache:
from_lw = self._location_cache.labware
else:
from_lw = None
from_loc = types.Location(self._hardware.gantry_position(mount),
from_lw)
moves = geometry.plan_moves(from_loc, location, self._deck_layout)
self._log.debug("planned moves for {}->{}: {}"
.format(from_loc, location, moves))
self._location_cache = location
self._last_moved_instrument = mount
for move in moves:
self._hardware.move_to(mount, move)

def home(self):
""" Homes the robot.
"""
self._log.debug("home")
self._hardware.home()

@property
def location_cache(self) -> Optional[types.Location]:
""" The cache used by the robot to determine where it last was.
"""
return self._location_cache

@location_cache.setter
def location_cache(self, loc: Optional[types.Location]):
self._location_cache = loc

@property
def deck(self) -> geometry.Deck:
""" The object holding the deck layout of the robot.
"""
return self._deck_layout

@staticmethod
def _build_hardware_adapter(
loop: asyncio.AbstractEventLoop,
Expand Down Expand Up @@ -434,13 +422,22 @@ def transfer(self,
raise NotImplementedError

def move_to(self, location: types.Location):
""" Move this pipette to a specific location on the deck.
""" Move the instrument.
:param location: Where to move to.
:raises ValueError: if an argument is incorrect.
:param location: The location to move to.
"""
self._log.debug("move to {}".format(location))
self._ctx.move_to(self._mount, location)
if self._ctx.location_cache:
from_lw = self._ctx.location_cache.labware
else:
from_lw = None
from_loc = types.Location(self._hardware.gantry_position(self._mount),
from_lw)
moves = geometry.plan_moves(from_loc, location, self._ctx.deck)
self._log.debug("move {}->{}: {}"
.format(from_loc, location, moves))
self._ctx.location_cache = location
for move in moves:
self._hardware.move_to(self._mount, move)
return self

@property
Expand Down
28 changes: 11 additions & 17 deletions api/tests/opentrons/protocol_api/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ def test_location_cache(loop, monkeypatch, load_my_labware):
ctx = papi.ProtocolContext(loop)
ctx.connect(hardware)
right = ctx.load_instrument('p10_single', Mount.RIGHT)
left = ctx.load_instrument('p300_multi', Mount.LEFT)
lw = ctx.load_labware_by_name('generic_96_wellPlate_380_uL', 1)
ctx.home()

Expand All @@ -77,21 +76,16 @@ def fake_plan_move(from_loc, to_loc, deck,
assert test_args[0].point == Point(418, 353, 205)
assert test_args[0].labware is None

# Once we have a location cache, that should be our from_loc
# kOnce we have a location cache, that should be our from_loc
right.move_to(lw.wells()[1].top())
assert test_args[0] == lw.wells()[0].top()

# If we switch instruments, we should ignore the cache
here = hardware.gantry_position(Mount.LEFT)
left.move_to(lw.wells()[1].top())
assert test_args[0].point == here
assert test_args[0].labware is None
assert test_args[0].labware == lw.wells()[0]


def test_move_uses_arc(loop, monkeypatch, load_my_labware):
hardware = API.build_hardware_simulator(loop=loop)
ctx = papi.ProtocolContext(loop)
ctx.connect(hardware)
ctx.home()
right = ctx.load_instrument('p10_single', Mount.RIGHT)
lw = ctx.load_labware_by_name('generic_96_wellPlate_380_uL', 1)
ctx.home()
Expand Down Expand Up @@ -122,6 +116,7 @@ def test_pipette_info(loop):

def test_aspirate(loop, load_my_labware, monkeypatch):
ctx = papi.ProtocolContext(loop)
ctx.home()
lw = ctx.load_labware_by_name('generic_96_wellPlate_380_uL', 1)
instr = ctx.load_instrument('p10_single', Mount.RIGHT)

Expand All @@ -138,19 +133,18 @@ def fake_move(mount, loc):
move_called_with = (mount, loc)

monkeypatch.setattr(ctx._hardware._api, 'aspirate', fake_hw_aspirate)
monkeypatch.setattr(ctx, 'move_to', fake_move)
monkeypatch.setattr(ctx._hardware._api, 'move_to', fake_move)

instr.aspirate(2.0, lw.wells()[0].bottom())

assert asp_called_with == (Mount.RIGHT, 2.0, 1.0)
assert move_called_with == (Mount.RIGHT, lw.wells()[0].bottom())
assert move_called_with == (Mount.RIGHT, lw.wells()[0].bottom().point)

instr.well_bottom_clearance = 1.0
instr.aspirate(2.0, lw.wells()[0])
dest_point, dest_lw = lw.wells()[0].bottom()
dest_point = dest_point._replace(z=dest_point.z + 1.0)
assert move_called_with == (Mount.RIGHT,
Location(dest_point, dest_lw))
assert move_called_with == (Mount.RIGHT, dest_point)

move_called_with = None
instr.aspirate(2.0)
Expand All @@ -159,6 +153,7 @@ def fake_move(mount, loc):

def test_dispense(loop, load_my_labware, monkeypatch):
ctx = papi.ProtocolContext(loop)
ctx.home()
lw = ctx.load_labware_by_name('generic_96_wellPlate_380_uL', 1)
instr = ctx.load_instrument('p10_single', Mount.RIGHT)

Expand All @@ -175,19 +170,18 @@ def fake_move(mount, loc):
move_called_with = (mount, loc)

monkeypatch.setattr(ctx._hardware._api, 'dispense', fake_hw_dispense)
monkeypatch.setattr(ctx, 'move_to', fake_move)
monkeypatch.setattr(ctx._hardware._api, 'move_to', fake_move)

instr.dispense(2.0, lw.wells()[0].bottom())

assert disp_called_with == (Mount.RIGHT, 2.0, 1.0)
assert move_called_with == (Mount.RIGHT, lw.wells()[0].bottom())
assert move_called_with == (Mount.RIGHT, lw.wells()[0].bottom().point)

instr.well_bottom_clearance = 1.0
instr.dispense(2.0, lw.wells()[0])
dest_point, dest_lw = lw.wells()[0].bottom()
dest_point = dest_point._replace(z=dest_point.z + 1.0)
assert move_called_with == (Mount.RIGHT,
Location(dest_point, dest_lw))
assert move_called_with == (Mount.RIGHT, dest_point)

move_called_with = None
instr.dispense(2.0)
Expand Down

0 comments on commit 14311b8

Please sign in to comment.