Skip to content

Commit

Permalink
refactor(api): Add protocol API back-compatibility layer (#2395)
Browse files Browse the repository at this point in the history
* feat(api): Add back compat  shim for new protocol API and restructure old

This commit puts the components of the old protocol api (robot, instruments, etc) into a new `legacy_api` subpackage. It moves the singletons previously defined in`opentrons/__init__` into a file in `legacy_api`, and creates similar ones in `protocol_api/back_compat`. Then, `opentrons/__init__` now conditionally imports the right one based on a feature flag.

This means it is now possible (using the feature flag) to import opentrons without the robot singleton being instantiated. It also means it is now possible to import components of the legacy API without actually instantiating them.

In addition, this commit adds a new file, `simulate_protocol.py`, that will be the frontend for simulating protocols without a robot.

Closes #2246
  • Loading branch information
sfoster1 authored Oct 2, 2018
1 parent bcc7040 commit 5b13d9f
Show file tree
Hide file tree
Showing 57 changed files with 488 additions and 374 deletions.
268 changes: 11 additions & 257 deletions api/opentrons/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import os
import sys
import json
from opentrons.robot.robot import Robot
from opentrons.instruments import pipette_config
from opentrons import instruments as inst, containers as cnt
from opentrons.data_storage import database_migration
from opentrons.config import feature_flags as ff

Expand All @@ -24,262 +21,19 @@

if not ff.split_labware_definitions():
database_migration.check_version_and_perform_necessary_migrations()
robot = Robot()

if ff.use_protocol_api_v2():
import protocol_api
from protocol_api.back_compat\
import robot, reset as bcreset, instruments, containers, labware

def reset():
global robot
robot = Robot()
return robot
def reset():
ctx = protocol_api.ProtocolContext()
bcreset(ctx)

else:
from .legacy_api.api import robot, reset, instruments, containers, labware

class ContainersWrapper(object):
def __init__(self, robot):
self.robot = robot

def create(self, *args, **kwargs):
return cnt.create(*args, **kwargs)

def list(self, *args, **kwargs):
return cnt.list(*args, **kwargs)

def load(self, *args, **kwargs):
return cnt.load(self.robot, *args, **kwargs)


class InstrumentsWrapper(object):
def __init__(self, robot):
self.robot = robot

def Pipette(self, *args, **kwargs):
"""
Deprecated -- do not use this constructor directly. Use the model-
specific constructors available in this module.
"""
return inst.Pipette(self.robot, *args, **kwargs)

def P10_Single(
self,
mount,
trash_container='',
tip_racks=[],
aspirate_flow_rate=None,
dispense_flow_rate=None,
min_volume=None,
max_volume=None):

pipette_model_version = self._retrieve_version_number(
mount, 'p10_single')
config = pipette_config.load(pipette_model_version)

return self._create_pipette_from_config(
config=config,
mount=mount,
trash_container=trash_container,
tip_racks=tip_racks,
aspirate_flow_rate=aspirate_flow_rate,
dispense_flow_rate=dispense_flow_rate,
min_volume=min_volume,
max_volume=max_volume)

def P10_Multi(
self,
mount,
trash_container='',
tip_racks=[],
aspirate_flow_rate=None,
dispense_flow_rate=None,
min_volume=None,
max_volume=None):

pipette_model_version = self._retrieve_version_number(
mount, 'p10_multi')
config = pipette_config.load(pipette_model_version)

return self._create_pipette_from_config(
config=config,
mount=mount,
trash_container=trash_container,
tip_racks=tip_racks,
aspirate_flow_rate=aspirate_flow_rate,
dispense_flow_rate=dispense_flow_rate,
min_volume=min_volume,
max_volume=max_volume)

def P50_Single(
self,
mount,
trash_container='',
tip_racks=[],
aspirate_flow_rate=None,
dispense_flow_rate=None,
min_volume=None,
max_volume=None):

pipette_model_version = self._retrieve_version_number(
mount, 'p50_single')
config = pipette_config.load(pipette_model_version)

return self._create_pipette_from_config(
config=config,
mount=mount,
trash_container=trash_container,
tip_racks=tip_racks,
aspirate_flow_rate=aspirate_flow_rate,
dispense_flow_rate=dispense_flow_rate,
min_volume=min_volume,
max_volume=max_volume)

def P50_Multi(
self,
mount,
trash_container='',
tip_racks=[],
aspirate_flow_rate=None,
dispense_flow_rate=None,
min_volume=None,
max_volume=None):

pipette_model_version = self._retrieve_version_number(
mount, 'p50_multi')
config = pipette_config.load(pipette_model_version)

return self._create_pipette_from_config(
config=config,
mount=mount,
trash_container=trash_container,
tip_racks=tip_racks,
aspirate_flow_rate=aspirate_flow_rate,
dispense_flow_rate=dispense_flow_rate,
min_volume=min_volume,
max_volume=max_volume)

def P300_Single(
self,
mount,
trash_container='',
tip_racks=[],
aspirate_flow_rate=None,
dispense_flow_rate=None,
min_volume=None,
max_volume=None):

pipette_model_version = self._retrieve_version_number(
mount, 'p300_single')
config = pipette_config.load(pipette_model_version)

return self._create_pipette_from_config(
config=config,
mount=mount,
trash_container=trash_container,
tip_racks=tip_racks,
aspirate_flow_rate=aspirate_flow_rate,
dispense_flow_rate=dispense_flow_rate,
min_volume=min_volume,
max_volume=max_volume)

def P300_Multi(
self,
mount,
trash_container='',
tip_racks=[],
aspirate_flow_rate=None,
dispense_flow_rate=None,
min_volume=None,
max_volume=None):

pipette_model_version = self._retrieve_version_number(
mount, 'p300_multi')
config = pipette_config.load(pipette_model_version)

return self._create_pipette_from_config(
config=config,
mount=mount,
trash_container=trash_container,
tip_racks=tip_racks,
aspirate_flow_rate=aspirate_flow_rate,
dispense_flow_rate=dispense_flow_rate,
min_volume=min_volume,
max_volume=max_volume)

def P1000_Single(
self,
mount,
trash_container='',
tip_racks=[],
aspirate_flow_rate=None,
dispense_flow_rate=None,
min_volume=None,
max_volume=None):

pipette_model_version = self._retrieve_version_number(
mount, 'p1000_single')
config = pipette_config.load(pipette_model_version)

return self._create_pipette_from_config(
config=config,
mount=mount,
trash_container=trash_container,
tip_racks=tip_racks,
aspirate_flow_rate=aspirate_flow_rate,
dispense_flow_rate=dispense_flow_rate,
min_volume=min_volume,
max_volume=max_volume)

def _create_pipette_from_config(
self,
config,
mount,
trash_container='',
tip_racks=[],
aspirate_flow_rate=None,
dispense_flow_rate=None,
min_volume=None,
max_volume=None):

if aspirate_flow_rate is not None:
config = config._replace(aspirate_flow_rate=aspirate_flow_rate)
if dispense_flow_rate is not None:
config = config._replace(dispense_flow_rate=dispense_flow_rate)

if min_volume is not None:
config = config._replace(min_volume=min_volume)
if max_volume is not None:
config = config._replace(max_volume=max_volume)

p = self.Pipette(
model_offset=config.model_offset,
mount=mount,
name=config.name,
trash_container=trash_container,
tip_racks=tip_racks,
channels=config.channels,
aspirate_flow_rate=config.aspirate_flow_rate,
dispense_flow_rate=config.dispense_flow_rate,
min_volume=config.min_volume,
max_volume=config.max_volume,
plunger_current=config.plunger_current,
drop_tip_current=config.drop_tip_current,
plunger_positions=config.plunger_positions.copy(),
fallback_tip_length=config.tip_length) # TODO move to labware

p.set_pick_up_current(config.pick_up_current)
return p

def _retrieve_version_number(self, mount, expected_model_substring):
attached_model = robot.get_attached_pipettes()[mount]['model']

if attached_model and expected_model_substring in attached_model:
return attached_model
else:
# pass a default pipette model-version for when robot is simulating
# this allows any pipette to be simulated, regardless of what is
# actually attached/cached on the robot's mounts
return expected_model_substring + '_v1' # default to v1


instruments = InstrumentsWrapper(robot)
containers = ContainersWrapper(robot)
labware = ContainersWrapper(robot)

__all__ = [containers, instruments, labware, robot, reset, __version__]
__all__ = ['containers', 'instruments', 'labware', 'robot', 'reset',
'__version__']
8 changes: 4 additions & 4 deletions api/opentrons/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from .calibration import CalibrationManager

__all__ = [
Session,
SessionManager,
MainRouter,
CalibrationManager
'Session',
'SessionManager',
'MainRouter',
'CalibrationManager'
]
2 changes: 1 addition & 1 deletion api/opentrons/api/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from opentrons.containers import Slot
from opentrons.legacy_api.containers import Slot


def _get_parent_slot(placeable):
Expand Down
4 changes: 2 additions & 2 deletions api/opentrons/api/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import json

from opentrons.broker import publish, subscribe
from opentrons.containers import get_container, location_to_list
from opentrons.containers.placeable import Module as ModulePlaceable
from opentrons.legacy_api.containers import get_container, location_to_list
from opentrons.legacy_api.containers.placeable import Module as ModulePlaceable
from opentrons.commands import tree, types
from opentrons.protocols import execute_protocol
from opentrons import robot, modules
Expand Down
3 changes: 2 additions & 1 deletion api/opentrons/commands/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from ..broker import broker
import functools
import inspect
from opentrons.containers import Well, Container, Slot, location_to_list
from opentrons.legacy_api.containers\
import Well, Container, Slot, location_to_list


def stringify_location(location):
Expand Down
5 changes: 5 additions & 0 deletions api/opentrons/config/advanced_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ def __repr__(self):
old_id='disable-home-on-boot',
title='Disable home on boot',
description='Prevent robot from homing motors on boot'
),
Setting(
_id='useProtocolApi2',
title='Use Protocol API version 2',
description='Use new implementation of protocol API. This should not be activated except by developers.' # noqa
)
]

Expand Down
4 changes: 4 additions & 0 deletions api/opentrons/config/feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ def dots_deck_type():

def disable_home_on_boot():
return advs.get_adv_setting('disableHomeOnBoot')


def use_protocol_api_v2():
return advs.get_adv_setting('useProtocolApi2')
3 changes: 2 additions & 1 deletion api/opentrons/data_storage/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import sqlite3
# import warnings
from typing import List
from opentrons.containers.placeable import Container, Well, Module
from opentrons.legacy_api.containers.placeable\
import Container, Well, Module
from opentrons.data_storage import database_queries as db_queries
from opentrons.util import environment
from opentrons.util.vector import Vector
Expand Down
2 changes: 1 addition & 1 deletion api/opentrons/data_storage/old_container_loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import os
import pkg_resources
from collections import OrderedDict
from opentrons.containers.placeable import Container, Well
from opentrons.legacy_api.containers.placeable import Container, Well
from opentrons.util import environment
from opentrons.util.vector import Vector

Expand Down
2 changes: 1 addition & 1 deletion api/opentrons/data_storage/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# pylama:ignore=E252
from opentrons.containers.placeable import Well, Container
from opentrons.legacy_api.containers.placeable import Well, Container
from opentrons.util.vector import Vector
from numbers import Number
from typing import Tuple
Expand Down
3 changes: 1 addition & 2 deletions api/opentrons/deck_calibration/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from opentrons import robot
from opentrons.robot.robot import Robot
from opentrons.config import feature_flags as ff

# Application constants
Expand Down Expand Up @@ -80,7 +79,7 @@ def jog(axis, direction, step):
return position(axis)


def set_calibration_value(rbt: Robot, axis: str, value: float):
def set_calibration_value(rbt, axis: str, value: float):
if axis == 'x':
row = x_row
elif axis == 'y':
Expand Down
2 changes: 1 addition & 1 deletion api/opentrons/deck_calibration/dc_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from typing import Tuple
from numpy.linalg import inv
from numpy import dot, array
from opentrons.robot import robot_configs
from opentrons.legacy_api.robot import robot_configs
from opentrons import robot, instruments
from opentrons.util.calibration_functions import probe_instrument
from opentrons.deck_calibration.linal import solve, add_z, apply_transform
Expand Down
Loading

0 comments on commit 5b13d9f

Please sign in to comment.