Skip to content

Commit

Permalink
refactor(api): refactor aspirate/dispense
Browse files Browse the repository at this point in the history
Closes #2235
  • Loading branch information
sanni-t committed Oct 4, 2018
1 parent 928dcab commit 9015a79
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 10 deletions.
36 changes: 29 additions & 7 deletions api/opentrons/hardware_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from typing import Dict, Union
from opentrons import types
from .simulator import Simulator
from . import pipette_config
try:
from .controller import Controller
except ModuleNotFoundError:
Expand All @@ -40,12 +41,18 @@ class _Axis(enum.Enum):
Y = enum.auto()
Z = enum.auto()
A = enum.auto()
B = enum.auto()
C = enum.auto()

@classmethod
def by_mount(cls, mount):
def pipette_axis_by_mount(cls, mount):
bm = {types.Mount.LEFT: cls.Z, types.Mount.RIGHT: cls.A}
return bm[mount]

@classmethod
def plunger_axis_by_mount(cls, mount):
pm = {types.Mount.LEFT: cls.B, types.Mount.RIGHT: cls.C}


class MustHomeError(RuntimeError):
pass
Expand Down Expand Up @@ -85,9 +92,9 @@ def __init__(self,
self._loop = loop
# {'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'A': 0.0, 'B': 0.0, 'C': 0.0}
self._current_position: Dict[str, float] = {}

self._attached_instruments = {types.Mount.LEFT: None,
types.Mount.RIGHT: None}
self._attached_instruments: Dict[str, str] = {}
# self._attached_instrument_properties: Dict[str, tuple] = {}
# self._pipette_current_volume: Dict[str, float] = {}

@classmethod
def build_hardware_controller(
Expand Down Expand Up @@ -171,6 +178,10 @@ async def resume(self):
async def halt(self):
pass

@_log_call
async def reset(self):
pass

# Gantry/frame (i.e. not pipette) action API
@_log_call
async def home(self, *args, **kwargs):
Expand All @@ -186,7 +197,7 @@ async def move_to(
self, mount: types.Mount, abs_position: types.Point):
if not self._current_position:
raise MustHomeError
z_axis = _Axis.by_mount(mount)
z_axis = _Axis.pipette_axis_by_mount(mount)
try:
target_position = {_Axis.X.name: abs_position.x,
_Axis.Y.name: abs_position.y,
Expand All @@ -199,7 +210,7 @@ async def move_to(
async def move_rel(self, mount: types.Mount, delta: types.Point):
if not self._current_position:
raise MustHomeError
z_axis = _Axis.by_mount(mount)
z_axis = _Axis.pipette_axis_by_mount(mount)
try:
target_position = \
{_Axis.X.name: self._current_position[_Axis.X.name] + delta.x,
Expand All @@ -226,7 +237,18 @@ async def head_speed(self, combined_speed=None,

# Pipette action API
@_log_call
async def aspirate(self, mount, volume=None, rate=None):
async def aspirate(self, mount, volume, aspirate_speed, rate=None):
# Assume correct volume
ul_per_mm = pipette_config.key_map_pipette_functions(
self._attached_instruments[mount], volume, 'aspirate')
mm = volume / ul_per_mm
plunger_displacement = 0 # TODO: Calculate plunger distance
# Calculate speed = 'aspirate speed' * rate
# speed.push()
# Set active current
target_position = {
_Axis.plunger_axis_by_mount(mount).name: plunger_displacement}
self._backend.move(target_position)
pass

@_log_call
Expand Down
120 changes: 120 additions & 0 deletions api/opentrons/hardware_control/pipette_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@

def key_map_pipette_functions(model, ul, func):
function_map = {
'p10_single_v1': lambda: _p10_single_piecewise(ul, func),
'p10_single_v1.3': lambda: _p10_single_piecewise(ul, func),
'p10_multi_v1': lambda: _p10_multi_piecewise(ul, func),
'p10_multi_v1.3': lambda: _p10_multi_piecewise(ul, func),
'p50_single_v1': lambda:_p50_single_piecewise(ul, func),
'p50_single_v1.3': lambda: _p50_single_piecewise(ul, func),
'p50_multi_v1': lambda: _p50_multi_piecewise(ul, func),
'p50_multi_v1.3': lambda: _p50_multi_piecewise(ul, func),
'p300_single_v1': lambda: _p300_single_piecewise(ul, func),
'p300_single_v1.3': lambda: _p300_single_piecewise(ul, func),
'p300_multi_v1': lambda: _p300_multi_piecewise(ul, func),
'p300_multi_v1.3': lambda: _p300_multi_piecewise(ul, func),
'p1000_single_v1': lambda: _p1000_piecewise(ul, func),
'p1000_single_v1.3': lambda: _p1000_piecewise(ul, func)
}

return function_map[model]


def _p10_single_piecewise(ul, func):
# Piecewise function that calculates ul_per_mm for a p10 single
# pipette model given a particular ul value
if func == 'aspirate':
if (ul > 0) and (ul < 1.8263):
return -0.0958 * ul + 1.088
elif (ul >= 1.8263) and (ul < 2.5222):
return -0.104 * ul + 1.1031
elif (ul >= 2.5222) and (ul < 3.2354):
return -0.0447 * ul + 0.9536
elif (ul >= 3.2354) and (ul < 3.9984):
return -0.012 * ul + 0.8477
elif (ul >= 3.9984) and (ul <= 12.5135):
return -0.0021 * ul + 0.8079
else:
# Constant function for dispense
return 0 * ul + 0.7945


def _p10_multi_piecewise(ul, func):
# Piecewise function that calculates ul_per_mm for a p10 multi
# pipette model given a particular ul value
if func == 'aspirate':
if (ul > 0) and (ul < 1.893415617):
return -1.1069 * ul + 3.042593193
elif (ul >= 1.893415617) and (ul < 2.497849452):
return -0.1888 * ul + 1.30410391
elif (ul >= 2.497849452) and (ul < 5.649462387):
return -0.0081 * ul + 0.8528667891
elif (ul >= 5.649462387) and (ul <= 12.74444519):
return -0.0018 * ul + 0.8170558891
else:
# Constant function for dispense
return 0 * ul + 0.8058688085


def _p50_single_piecewise(ul, func):
# Piecewise function that calculates ul_per_mm for a p50 single
# pipette model given a particular ul value
if func == 'aspirate':
if (ul > 0) and (ul < 11.79687499):
return -0.0098 * ul + 3.064988953
elif (ul >= 11.79687499) and (ul <= 50):
return -0.0004 * ul + 2.954068131
else:
return 0 * ul + 2.931601299


def _p50_multi_piecewise(ul, func):
# Piecewise function that calculates ul_per_mm for a p50 multi
# pipette model given a particular ul value
if func == 'aspirate':
if (ul > 0) and (ul < 12.29687531):
return -0.0049 * ul + 3.134703694
elif (ul >= 12.29687531) and (ul <= 50):
return -0.0002 * ul + 3.077116024
else:
# Constant function for dispense
return 0 * ul + 3.06368702


def _p300_single_piecewise(ul, func):
# Piecewise function that calculates ul_per_mm for a p300 single
# pipette model given a particular ul value
if func == 'aspirate':
if (ul > 0) and (ul < 36.19844973):
return 0.043 * ul + 16.548
elif (ul >= 36.19844973) and (ul < 54.98518519):
return 0.012 * ul + 17.658
elif (ul >= 54.98518519) and (ul < 73.90077516):
return 0.008 * ul + 17.902
elif (ul >= 73.90077516) and (ul < 111.8437953):
return 0.004 * ul + 18.153
elif (ul >= 111.8437953) and (ul <= 302.3895337):
return 0.001 * ul + 18.23
else:
# Constant function for dispense
return 0 * ul + 18.83156277


def _p300_multi_piecewise(ul, func):
# Piecewise function that calculates ul_per_mm for a p300 multi
# pipette model given a particular ul value
if func == 'aspirate':
if (ul > 0) and (ul < 57.25698968):
return 0.017 * ul + 18.132
elif (ul >= 57.25698968) and (ul <= 309.2612689):
return 0.001 * ul + 19.03
else:
# Constant function for dispense
return 0 * ul + 19.29389273


def _p1000_piecewise(ul, func):
if func == 'aspirate':
return 65
else:
return 65
6 changes: 3 additions & 3 deletions api/tests/opentrons/hardware_control/test_instruments.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


async def test_cache_instruments(loop):
dummy_instruments_attached = {types.Mount.LEFT: 'model_abc',
dummy_instruments_attached = {types.Mount.LEFT: 'p10_single_v1',
types.Mount.RIGHT: None}
hw_api = hc.API.build_hardware_simulator(
attached_instruments=dummy_instruments_attached, loop=loop)
Expand All @@ -17,12 +17,12 @@ async def test_cache_instruments(loop):
'(probably windows)')
async def test_cache_instruments_hc(monkeypatch, hardware_controller_lockfile,
running_on_pi, loop):
dummy_instruments_attached = {types.Mount.LEFT: 'model_abc',
dummy_instruments_attached = {types.Mount.LEFT: 'p10_single_v1',
types.Mount.RIGHT: None}
hw_api_cntrlr = hc.API.build_hardware_controller(loop=loop)

def mock_driver_method(mount):
attached_pipette = {'left': 'model_abc', 'right': None}
attached_pipette = {'left': 'p10_single_v1', 'right': None}
return attached_pipette[mount]
monkeypatch.setattr(hw_api_cntrlr._backend._smoothie_driver,
'read_pipette_model', mock_driver_method)
Expand Down

0 comments on commit 9015a79

Please sign in to comment.