Skip to content

Commit

Permalink
Merge branch 'edge' into fix_inherit_enumerated_errors
Browse files Browse the repository at this point in the history
  • Loading branch information
pmoegenburg committed Jun 26, 2024
2 parents 66ddd35 + 6888ffd commit 16fc115
Show file tree
Hide file tree
Showing 249 changed files with 3,093 additions and 7,715 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ update-server/LICENSE
shared-data/python/LICENSE
shared-data/LICENSE
robot-server/LICENSE
performance-metrics/LICENSE

# typescript incremental files
*.tsbuildinfo
Expand Down
4 changes: 4 additions & 0 deletions api-client/src/runs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const RUN_STATUS_FINISHING = 'finishing' as const
export const RUN_STATUS_SUCCEEDED = 'succeeded' as const
export const RUN_STATUS_BLOCKED_BY_OPEN_DOOR = 'blocked-by-open-door' as const
export const RUN_STATUS_AWAITING_RECOVERY = 'awaiting-recovery' as const
export const RUN_STATUS_AWAITING_RECOVERY_BLOCKED_BY_OPEN_DOOR = 'awaiting-recovery-blocked-by-open-door' as const
export const RUN_STATUS_AWAITING_RECOVERY_PAUSED = 'awaiting-recovery-paused' as const

export type RunStatus =
| typeof RUN_STATUS_IDLE
Expand All @@ -33,6 +35,8 @@ export type RunStatus =
| typeof RUN_STATUS_SUCCEEDED
| typeof RUN_STATUS_BLOCKED_BY_OPEN_DOOR
| typeof RUN_STATUS_AWAITING_RECOVERY
| typeof RUN_STATUS_AWAITING_RECOVERY_BLOCKED_BY_OPEN_DOOR
| typeof RUN_STATUS_AWAITING_RECOVERY_PAUSED

export interface LegacyGoodRunData {
id: string
Expand Down
1 change: 1 addition & 0 deletions api/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ opentrons-shared-data = { editable = true, path = "../shared-data/python" }
opentrons = { editable = true, path = "." }
opentrons-hardware = { editable = true, path = "./../hardware", extras=["FLEX"] }
numpy = "==1.22.3"
packaging = "==21.3"
pyusb = "==1.2.1"

[dev-packages]
Expand Down
209 changes: 110 additions & 99 deletions api/Pipfile.lock

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions api/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ log][]. For a list of currently known issues, please see the [Opentrons issue tr

---

## Opentrons Robot Software Changes in 7.3.1

Welcome to the v7.3.1 release of the Opentrons robot software!

### Improved Features

- Updated values for how much a tip overlaps with the pipette nozzle when the pipette picks up tips, in order to make protocols more reliable. These new values only apply to JSON protocols and Python protocols specifying API version 2.19.

---

## Opentrons Robot Software Changes in 7.3.0

Welcome to the v7.3.0 release of the Opentrons robot software!
Expand Down
1 change: 1 addition & 0 deletions api/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def get_version():
"typing-extensions>=4.0.0,<5",
"click>=8.0.0,<9",
'importlib-metadata >= 1.0 ; python_version < "3.8"',
"packaging>=21.0",
]

EXTRAS = {
Expand Down
43 changes: 39 additions & 4 deletions api/src/opentrons/hardware_control/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
RobotCalibration,
)
from .protocols import HardwareControlInterface
from .instruments.ot2.pipette_handler import PipetteHandlerProvider, PickUpTipSpec
from .instruments.ot2.pipette_handler import PipetteHandlerProvider
from .instruments.ot2.instrument_calibration import load_pipette_offset
from .motion_utilities import (
target_position_from_absolute,
Expand Down Expand Up @@ -1155,8 +1155,21 @@ async def update_nozzle_configuration_for_mount(
async def tip_pickup_moves(
self,
mount: top_types.Mount,
spec: PickUpTipSpec,
presses: Optional[int] = None,
increment: Optional[float] = None,
) -> None:
spec, _ = self.plan_check_pick_up_tip(
mount=mount, presses=presses, increment=increment
)
self._backend.set_active_current(spec.plunger_currents)
target_absolute = target_position_from_plunger(
mount, spec.plunger_prep_pos, self._current_position
)
await self._move(
target_absolute,
home_flagged_axes=False,
)

for press in spec.presses:
with self._backend.save_current():
self._backend.set_active_current(press.current)
Expand All @@ -1176,6 +1189,11 @@ async def tip_pickup_moves(

await self.retract(mount, spec.retract_target)

def cache_tip(self, mount: top_types.Mount, tip_length: float) -> None:
instrument = self.get_pipette(mount)
instrument.add_tip(tip_length=tip_length)
instrument.set_current_volume(0)

async def pick_up_tip(
self,
mount: top_types.Mount,
Expand All @@ -1189,7 +1207,7 @@ async def pick_up_tip(
"""

spec, _add_tip_to_instrs = self.plan_check_pick_up_tip(
mount, tip_length, presses, increment
mount=mount, presses=presses, increment=increment, tip_length=tip_length
)
self._backend.set_active_current(spec.plunger_currents)
target_absolute = target_position_from_plunger(
Expand All @@ -1200,7 +1218,24 @@ async def pick_up_tip(
home_flagged_axes=False,
)

await self.tip_pickup_moves(mount, spec)
for press in spec.presses:
with self._backend.save_current():
self._backend.set_active_current(press.current)
target_down = target_position_from_relative(
mount, press.relative_down, self._current_position
)
await self._move(target_down, speed=press.speed)
target_up = target_position_from_relative(
mount, press.relative_up, self._current_position
)
await self._move(target_up)
# neighboring tips tend to get stuck in the space between
# the volume chamber and the drop-tip sleeve on p1000.
# This extra shake ensures those tips are removed
for rel_point, speed in spec.shake_off_list:
await self.move_rel(mount, rel_point, speed=speed)

await self.retract(mount, spec.retract_target)
_add_tip_to_instrs()

if prep_after:
Expand Down
1 change: 1 addition & 0 deletions api/src/opentrons/hardware_control/dev_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class PipetteDict(InstrumentDict):
tip_length: float
working_volume: float
tip_overlap: Dict[str, float]
versioned_tip_overlap: Dict[str, Dict[str, float]]
available_volume: float
return_tip_height: float
default_aspirate_flow_rates: Dict[str, float]
Expand Down
5 changes: 2 additions & 3 deletions api/src/opentrons/hardware_control/instruments/ot2/pipette.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,9 +631,8 @@ def as_dict(self) -> "Pipette.DictType":
"default_dispense_flow_rates": self.dispense_flow_rates_lookup,
"tip_length": self.current_tip_length,
"return_tip_height": self.active_tip_settings.default_return_tip_height,
"tip_overlap": self.tip_overlap[
"v0"
], # TODO(cb, 2024-06-11): hard coded to "v0" - when versioned tip overlaps are fully integrated this must change
"tip_overlap": self.tip_overlap["v0"],
"versioned_tip_overlap": self.tip_overlap,
"back_compat_names": self._config.pipette_backcompat_names,
"supported_tips": self.liquid_class.supported_tips,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ def get_attached_instrument(self, mount: MountType) -> PipetteDict:
"blow_out_flow_rate",
"working_volume",
"tip_overlap",
"versioned_tip_overlap",
"available_volume",
"return_tip_height",
"default_aspirate_flow_rates",
Expand Down Expand Up @@ -744,28 +745,28 @@ def build_one_shake() -> List[Tuple[top_types.Point, Optional[float]]]:
def plan_check_pick_up_tip(
self,
mount: top_types.Mount,
tip_length: float,
presses: Optional[int],
increment: Optional[float],
tip_length: float = 0,
) -> Tuple[PickUpTipSpec, Callable[[], None]]:
...

@overload
def plan_check_pick_up_tip(
self,
mount: OT3Mount,
tip_length: float,
presses: Optional[int],
increment: Optional[float],
tip_length: float = 0,
) -> Tuple[PickUpTipSpec, Callable[[], None]]:
...

def plan_check_pick_up_tip( # type: ignore[no-untyped-def]
self,
mount,
tip_length,
presses,
increment,
tip_length=0,
):
# Prechecks: ready for pickup tip and press/increment are valid
instrument = self.get_pipette(mount)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ def as_dict(self) -> "Pipette.DictType":
"tip_length": self.current_tip_length,
"return_tip_height": self.active_tip_settings.default_return_tip_height,
"tip_overlap": self.tip_overlap["v0"],
"versioned_tip_overlap": self.tip_overlap,
"back_compat_names": self._config.pipette_backcompat_names,
"supported_tips": self.liquid_class.supported_tips,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict:
"blow_out_flow_rate",
"working_volume",
"tip_overlap",
"versioned_tip_overlap",
"available_volume",
"return_tip_height",
"default_aspirate_flow_rates",
Expand Down
9 changes: 7 additions & 2 deletions api/src/opentrons/hardware_control/modules/mod_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import asyncio
import logging
import re
from typing import ClassVar, Mapping, Optional, TypeVar
from typing import ClassVar, Mapping, Optional, TypeVar, cast
from packaging.version import InvalidVersion, parse, Version
from opentrons.config import IS_ROBOT, ROBOT_FIRMWARE_DIR
from opentrons.drivers.rpi_drivers.types import USBPort
Expand All @@ -18,9 +18,14 @@
def parse_fw_version(version: str) -> Version:
try:
device_version = parse(version)
# This is a patch for older versions of packaging - they would try and parse old
# kidns of versions and return a LegacyVersion object. We can't check for that
# explicitly because they removed it in modern versions of packaging.
if not isinstance(device_version, Version):
raise InvalidVersion()
except InvalidVersion:
device_version = parse("v0.0.0")
return device_version
return cast(Version, device_version)


class AbstractModule(abc.ABC):
Expand Down
13 changes: 11 additions & 2 deletions api/src/opentrons/hardware_control/ot3api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1799,7 +1799,7 @@ async def hold_jaw_width(self, jaw_width_mm: int) -> None:

async def tip_pickup_moves(
self,
mount: OT3Mount,
mount: Union[top_types.Mount, OT3Mount],
presses: Optional[int] = None,
increment: Optional[float] = None,
) -> None:
Expand Down Expand Up @@ -2191,6 +2191,15 @@ async def _tip_motor_action(
)
await self.home_gear_motors()

def cache_tip(
self, mount: Union[top_types.Mount, OT3Mount], tip_length: float
) -> None:
realmount = OT3Mount.from_mount(mount)
instrument = self._pipette_handler.get_pipette(realmount)

instrument.add_tip(tip_length=tip_length)
instrument.set_current_volume(0)

async def pick_up_tip(
self,
mount: Union[top_types.Mount, OT3Mount],
Expand All @@ -2209,7 +2218,7 @@ def add_tip_to_instr() -> None:

await self._move_to_plunger_bottom(realmount, rate=1.0)

await self.tip_pickup_moves(realmount, presses, increment)
await self.tip_pickup_moves(mount, presses, increment)

add_tip_to_instr()

Expand Down
6 changes: 6 additions & 0 deletions api/src/opentrons/hardware_control/protocols/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ class HardwareControlInterface(
def get_robot_type(self) -> Type[OT2RobotType]:
return OT2RobotType

def cache_tip(self, mount: MountArgType, tip_length: float) -> None:
...


class FlexHardwareControlInterface(
ModuleProvider,
Expand Down Expand Up @@ -90,6 +93,9 @@ def motor_status_ok(self, axis: Axis) -> bool:
def encoder_status_ok(self, axis: Axis) -> bool:
...

def cache_tip(self, mount: MountArgType, tip_length: float) -> None:
...


__all__ = [
"HardwareControlAPI",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ async def blow_out(
"""
...

async def tip_pickup_moves(
self,
mount: MountArgType,
presses: Optional[int] = None,
increment: Optional[float] = None,
) -> None:
...

async def pick_up_tip(
self,
mount: MountArgType,
Expand Down
10 changes: 8 additions & 2 deletions api/src/opentrons/protocol_api/core/engine/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from opentrons.protocol_api._nozzle_layout import NozzleLayout
from opentrons.hardware_control.nozzle_manager import NozzleConfigurationType
from opentrons.hardware_control.nozzle_manager import NozzleMap
from . import deck_conflict
from . import deck_conflict, overlap_versions

from ..instrument import AbstractInstrument
from .well import WellCore
Expand Down Expand Up @@ -782,7 +782,13 @@ def set_flow_rate(

def configure_for_volume(self, volume: float) -> None:
self._engine_client.execute_command(
cmd.ConfigureForVolumeParams(pipetteId=self._pipette_id, volume=volume)
cmd.ConfigureForVolumeParams(
pipetteId=self._pipette_id,
volume=volume,
tipOverlapNotAfterVersion=overlap_versions.overlap_for_api_version(
self._protocol_core.api_version
),
)
)

def prepare_to_aspirate(self) -> None:
Expand Down
16 changes: 16 additions & 0 deletions api/src/opentrons/protocol_api/core/engine/overlap_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Mappings between API versions and overlap versions."""
from functools import lru_cache
from typing_extensions import Final
from opentrons.protocols.api_support.types import APIVersion

_OVERLAP_VERSION_MAP: Final = {APIVersion(2, 0): "v0", APIVersion(2, 19): "v1"}


@lru_cache(1)
def overlap_for_api_version(api_version: APIVersion) -> str:
"""Get the overlap version for a specific API version."""
defined = list(reversed(sorted(_OVERLAP_VERSION_MAP.keys())))
for version in defined:
if version <= api_version:
return _OVERLAP_VERSION_MAP[version]
return _OVERLAP_VERSION_MAP[APIVersion(2, 0)]
17 changes: 13 additions & 4 deletions api/src/opentrons/protocol_api/core/engine/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@
AbsorbanceReaderCore,
)
from .exceptions import InvalidModuleLocationError
from . import load_labware_params
from . import deck_conflict
from . import load_labware_params, deck_conflict, overlap_versions

if TYPE_CHECKING:
from ...labware import Labware
Expand Down Expand Up @@ -497,7 +496,10 @@ def _get_module_core(
)

def load_instrument(
self, instrument_name: PipetteNameType, mount: Mount
self,
instrument_name: PipetteNameType,
mount: Mount,
liquid_presence_detection: bool = False,
) -> InstrumentCore:
"""Load an instrument into the protocol.
Expand All @@ -510,7 +512,14 @@ def load_instrument(
"""
engine_mount = MountType[mount.name]
load_result = self._engine_client.execute_command_without_recovery(
cmd.LoadPipetteParams(pipetteName=instrument_name, mount=engine_mount)
cmd.LoadPipetteParams(
pipetteName=instrument_name,
mount=engine_mount,
tipOverlapNotAfterVersion=overlap_versions.overlap_for_api_version(
self._api_version
),
liquidPresenceDetection=liquid_presence_detection,
)
)

return InstrumentCore(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,10 @@ def load_module(
return module_core

def load_instrument(
self, instrument_name: PipetteNameType, mount: Mount
self,
instrument_name: PipetteNameType,
mount: Mount,
liquid_presence_detection: bool = False,
) -> LegacyInstrumentCore:
"""Load an instrument."""
attached = {
Expand Down
Loading

0 comments on commit 16fc115

Please sign in to comment.