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

chore: release->edge for 7.1.0 #14251

Merged
merged 32 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fa83dd3
chore: add direct Ethernet feature to release notes (#14199)
ecormany Dec 14, 2023
ae8f637
feat(api): Allow omitting the mount when loading a 96-channel (#14187)
SyntaxColoring Dec 14, 2023
42efa31
chore: add probe-based LPC to app release notes (#14202)
ecormany Dec 14, 2023
4ab142a
fix(shared-data): add moveToAddressableAreaForDropTip to getAddressab…
brenthagen Dec 14, 2023
a7d1187
fix(app): fix various drop tip wizard issues (#14207)
mjhuff Dec 14, 2023
9a3065f
fix(app): get addressable area from configured deck for drop tip wiza…
brenthagen Dec 15, 2023
8c4f1d3
fix(app-shell): use a wrapping stream for usb (#14214)
sfoster1 Dec 15, 2023
3f1c70f
fix(api): do not tip check during attach (#14216)
sfoster1 Dec 15, 2023
9d4ce72
refactor(app): Remove drop tip banner after OT-2 protocol run (#14212)
mjhuff Dec 15, 2023
9842207
fix(protocol-engine): Fix hanging after cancellation (#14215)
SyntaxColoring Dec 15, 2023
ef937ee
fix(app): e-stop modal button state issue (#14217)
koji Dec 15, 2023
5d4845f
fix(app): support addressable areas in manual intervention modal loca…
b-cooper Dec 15, 2023
f5a99be
refactor(components): adjust BaseDeck styling (#14219)
brenthagen Dec 15, 2023
4da8206
fix(app): ODD - Remove dismisscurrentrun on protocol cancel button (#…
mjhuff Dec 15, 2023
8c82d7c
fix(api): allow double remove tip (#14224)
sfoster1 Dec 15, 2023
3135a08
fix(app): allow users to use the same name after device reset (#14222)
koji Dec 15, 2023
9a05d1f
fix(api): Flex 2 15 api fixed trash auto tipdrop support (#14225)
CaseyBatten Dec 15, 2023
32605a5
fix(api): center only active column on 12-well (#14226)
sfoster1 Dec 15, 2023
2d0bcfd
fix(app): Fix persistent cancelled state (#14228)
mjhuff Dec 15, 2023
5fc0e29
test(api): Add integration tests for load conflicts with the fixed tr…
SyntaxColoring Dec 18, 2023
5a939ea
fix(api): allow volume 0 commands in engine (#14211)
DerekMaggio Dec 18, 2023
d2e9f57
reverting the grip force back to 15N (#14221)
ahiuchingau Dec 18, 2023
70819cb
docs(api): Versioning page updates for Python API 2.16 (#14073)
ecormany Dec 18, 2023
87b2993
fix(app): Don't render ProtocolRunHeader TerminalBanner on CurrentRun…
mjhuff Dec 18, 2023
0716cad
fix(shared-data): add slot transforms for A3 for heater-shaker and te…
jbleon95 Dec 18, 2023
24310ce
fix(app): add new text to updatebanner for module card (#14237)
koji Dec 18, 2023
c45ed00
feat(api): raise error if tip tracking is not available for current n…
sanni-t Dec 18, 2023
45b52ed
feat(app): add loading state to ODD protocol list (#14238)
shlokamin Dec 18, 2023
0ea0fb8
feat(protocol-engine): Allow moving to addressable areas without desc…
SyntaxColoring Dec 18, 2023
6417cf7
feat(app): add gradient scrim to ODD protocol command list (#14232)
ncdiehl11 Dec 18, 2023
e8fc541
docs(api): corrections to docstrings involving `TrashBin`s (#14194)
ecormany Dec 18, 2023
2bc8378
refactor(app): remove excessive axis homing in drop tip wizard (#14239)
mjhuff Dec 18, 2023
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
2 changes: 1 addition & 1 deletion api/docs/v2/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
# use rst_prolog to hold the subsitution
# update the apiLevel value whenever a new minor version is released
rst_prolog = f"""
.. |apiLevel| replace:: 2.15
.. |apiLevel| replace:: 2.16
.. |release| replace:: {release}
"""

Expand Down
6 changes: 3 additions & 3 deletions api/docs/v2/new_pipette.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ If you're writing a protocol that uses the Flex Gripper, you might think that th
Loading a Flex 96-Channel Pipette
---------------------------------

This code sample loads the Flex 96-Channel Pipette. Because of its size, the Flex 96-Channel Pipette requires the left *and* right pipette mounts. You cannot use this pipette with 1- or 8-Channel Pipette in the same protocol or when these instruments are attached to the robot. To load the 96-Channel Pipette, specify its position as ``mount='left'`` as shown here:
This code sample loads the Flex 96-Channel Pipette. Because of its size, the Flex 96-Channel Pipette requires the left *and* right pipette mounts. You cannot use this pipette with 1- or 8-Channel Pipette in the same protocol or when these instruments are attached to the robot. When loading the 96-Channel Pipette, you can omit the ``mount`` argument from ``load_instrument()`` as shown here:

.. code-block:: python

def run(protocol: protocol_api.ProtocolContext):
left = protocol.load_instrument(
instrument_name='flex_96channel_1000', mount='left')
pipette = protocol.load_instrument(
instrument_name='flex_96channel_1000')

.. versionadded:: 2.15

Expand Down
32 changes: 30 additions & 2 deletions api/docs/v2/versioning.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ The maximum supported API version for your robot is listed in the Opentrons App

If you upload a protocol that specifies a higher API level than the maximum supported, your robot won't be able to analyze or run your protocol. You can increase the maximum supported version by updating your robot software and Opentrons App.

Opentrons robots running the latest software (7.0.0) support the following version ranges:
Opentrons robots running the latest software (7.1.0) support the following version ranges:

* **Flex:** version 2.15.
* **Flex:** version 2.15–|apiLevel|.
* **OT-2:** versions 2.0–|apiLevel|.


Expand All @@ -82,6 +82,8 @@ This table lists the correspondence between Protocol API versions and robot soft
+-------------+------------------------------+
| API Version | Introduced in Robot Software |
+=============+==============================+
| 2.16 | 7.1.0 |
+-------------+------------------------------+
| 2.15 | 7.0.0 |
+-------------+------------------------------+
| 2.14 | 6.3.0 |
Expand Down Expand Up @@ -122,6 +124,32 @@ This table lists the correspondence between Protocol API versions and robot soft
Changes in API Versions
=======================

Version 2.16
------------

This version introduces new features for Flex and adds and improves methods for aspirating and dispensing. Note that when updating Flex protocols to version 2.16, you *must* load a trash container before dropping tips.

- New features

- Use :py:meth:`.configure_nozzle_layout` to pick up a single column of tips with the 96-channel pipette. See :ref:`Partial Tip Pickup <partial-tip-pickup>`.
- Specify the trash containers attached to your Flex with :py:meth:`.load_waste_chute` and :py:meth:`.load_trash_bin`.
- Dispense, blow out, drop tips, and dispose labware in the waste chute. Disposing labware requires the gripper and calling :py:meth:`.move_labware` with ``use_gripper=True``.
- Perform actions in staging area slots by referencing slots A4 through D4. See :ref:`deck-slots`.
- Explicitly command a pipette to :py:meth:`.prepare_to_aspirate`. The API usually prepares pipettes to aspirate automatically, but this is useful for certain applications, like pre-wetting routines.

- Improved features

- :py:meth:`.aspirate`, :py:meth:`.dispense`, and :py:meth:`.mix` will not move any liquid when called with ``volume=0``.

- Other changes

- :py:obj:`.ProtocolContext.fixed_trash` and :py:obj:`.InstrumentContext.trash_container` now return :py:class:`.TrashBin` objects instead of :py:class:`.Labware` objects.
- Flex will no longer automatically drop tips in the trash at the end of a protocol. You can add a :py:meth:`.drop_tip()` command to your protocol or use the Opentrons App to drop the tips.

- Known issues

- It's possible to load a Thermocycler and then load another item in slot A1. Don't do this, as it could lead to unexpected pipetting behavior and crashes.

Version 2.15
------------

Expand Down
1 change: 1 addition & 0 deletions api/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Welcome to the v7.1.0 release of the Opentrons robot software! This release incl

### Improved Features

- The Ethernet port on Flex now supports direct connection to a computer.
- Improves aspirate, dispense, and mix behavior with volumes set to zero.
- The `opentrons_simulate` command-line tool now works with all Python API versions.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,6 @@ def remove_tip(self) -> None:
Remove the tip from the pipette (effectively updates the pipette's
critical point)
"""
assert self.has_tip
self._has_tip = False
self._current_tip_length = 0.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,6 @@ def remove_tip(self) -> None:
Remove the tip from the pipette (effectively updates the pipette's
critical point)
"""
assert self.has_tip_length
self._current_tip_length = 0.0
self._has_tip_length = False

Expand Down
9 changes: 9 additions & 0 deletions api/src/opentrons/hardware_control/nozzle_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ def xy_center_offset(self) -> Point:
difference[0] / 2, difference[1] / 2, 0
)

@property
def y_center_offset(self) -> Point:
"""The position in the center of the primary column of the map."""
front_left = next(reversed(list(self.rows.values())))[0]
difference = self.map_store[front_left] - self.map_store[self.back_left]
return self.map_store[self.back_left] + Point(0, difference[1] / 2, 0)

@property
def front_nozzle_offset(self) -> Point:
"""The offset for the front_left nozzle."""
Expand Down Expand Up @@ -319,6 +326,8 @@ def critical_point_with_tip_length(
) -> Point:
if cp_override == CriticalPoint.XY_CENTER:
current_nozzle = self._current_nozzle_configuration.xy_center_offset
elif cp_override == CriticalPoint.Y_CENTER:
current_nozzle = self._current_nozzle_configuration.y_center_offset
elif cp_override == CriticalPoint.FRONT_NOZZLE:
current_nozzle = self._current_nozzle_configuration.front_nozzle_offset
else:
Expand Down
2 changes: 1 addition & 1 deletion api/src/opentrons/hardware_control/ot3api.py
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ async def _configure_instruments(self) -> None:
"""Configure instruments"""
await self.set_gantry_load(self._gantry_load_from_instruments())
await self.refresh_positions()
await self.reset_tip_detectors()
await self.reset_tip_detectors(False)

async def reset_tip_detectors(
self,
Expand Down
10 changes: 10 additions & 0 deletions api/src/opentrons/hardware_control/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,16 @@ class CriticalPoint(enum.Enum):
back calibration pin slot.
"""

Y_CENTER = enum.auto()
"""
Y_CENTER means the critical point under consideration is at the same X
coordinate as the default nozzle point (i.e. TIP | NOZZLE | FRONT_NOZZLE)
but halfway in between the Y axis bounding box of the pipette - it is the
XY center of the first column in the pipette. It's really only relevant for
the 96; it will produce the same position as XY_CENTER on an eight or one
channel pipette.
"""


class ExecutionState(enum.Enum):
RUNNING = enum.auto()
Expand Down
22 changes: 22 additions & 0 deletions api/src/opentrons/protocol_api/core/engine/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,28 @@ def get_nozzle_configuration(self) -> NozzleConfigurationType:
self._pipette_id
)

def is_tip_tracking_available(self) -> bool:
primary_nozzle = self._engine_client.state.pipettes.get_primary_nozzle(
self._pipette_id
)
if self.get_nozzle_configuration() == NozzleConfigurationType.FULL:
return True
else:
if self.get_channels() == 96:
# SINGLE configuration with H12 nozzle is technically supported by the
# current tip tracking implementation but we don't do any deck conflict
# checks for it, so we won't provide full support for it yet.
return (
self.get_nozzle_configuration() == NozzleConfigurationType.COLUMN
and primary_nozzle == "A12"
)
if self.get_channels() == 8:
return (
self.get_nozzle_configuration() == NozzleConfigurationType.SINGLE
and primary_nozzle == "H1"
)
return False

def set_flow_rate(
self,
aspirate: Optional[float] = None,
Expand Down
4 changes: 4 additions & 0 deletions api/src/opentrons/protocol_api/core/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,5 +274,9 @@ def configure_nozzle_layout(
"""
...

def is_tip_tracking_available(self) -> bool:
"""Return whether auto tip tracking is available for the pipette's current nozzle configuration."""
...


InstrumentCoreType = TypeVar("InstrumentCoreType", bound=AbstractInstrument[Any])
Original file line number Diff line number Diff line change
Expand Up @@ -547,3 +547,7 @@ def configure_nozzle_layout(
def get_active_channels(self) -> int:
"""This will never be called because it was added in API 2.16."""
assert False, "get_active_channels only supported in API 2.16 & later"

def is_tip_tracking_available(self) -> bool:
# Tip tracking is always available in legacy context
return True
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,7 @@ def configure_nozzle_layout(
def get_active_channels(self) -> int:
"""This will never be called because it was added in API 2.16."""
assert False, "get_active_channels only supported in API 2.16 & later"

def is_tip_tracking_available(self) -> bool:
# Tip tracking is always available in legacy context
return True
26 changes: 18 additions & 8 deletions api/src/opentrons/protocol_api/instrument_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from opentrons_shared_data.errors.exceptions import (
CommandPreconditionViolated,
CommandParameterLimitViolated,
UnexpectedTipRemovalError,
)
from opentrons.legacy_broker import LegacyBroker
from opentrons.hardware_control.dev_types import PipetteDict
Expand All @@ -26,7 +27,6 @@
requires_version,
APIVersionError,
)
from opentrons_shared_data.errors.exceptions import UnexpectedTipRemovalError

from .core.common import InstrumentCore, ProtocolCore
from .core.engine import ENGINE_CORE_API_VERSION
Expand Down Expand Up @@ -860,6 +860,14 @@ def pick_up_tip(
)

if location is None:
if not self._core.is_tip_tracking_available():
raise CommandPreconditionViolated(
"Automatic tip tracking is not available for the current pipette"
" nozzle configuration. We suggest switching to a configuration"
" that supports automatic tip tracking or specifying the exact tip"
" to pick up."
)

tip_rack, well = labware.next_available_tip(
starting_tip=self.starting_tip,
tip_racks=self.tip_racks,
Expand Down Expand Up @@ -950,7 +958,7 @@ def drop_tip(
See :ref:`pipette-drop-tip` for examples.

If no location is passed (e.g. ``pipette.drop_tip()``), the pipette will drop
the attached tip into its default :py:attr:`trash_container`.
the attached tip into its :py:attr:`trash_container`.

Starting with API version 2.15, if the trash container is the default fixed
trash, the API will instruct the pipette to drop tips in different locations
Expand Down Expand Up @@ -1530,12 +1538,14 @@ def trash_container(self) -> Union[labware.Labware, TrashBin, WasteChute]:
This is the property used to determine where to drop tips and blow out liquids
when calling :py:meth:`drop_tip` or :py:meth:`blow_out` without arguments.

On a Flex running a protocol with API version 2.16 or higher, ``trash_container`` is
the first ``TrashBin`` or ``WasteChute`` object loaded in the protocol.
On a Flex running a protocol with API version 2.15, ``trash_container`` is
a single-well fixed trash labware in slot D3.
On a an OT-2, ``trash_container`` is always a single-well fixed trash labware
in slot 12.
You can set this to a :py:obj:`Labware`, :py:obj:`TrashBin`, or :py:obj:`WasteChute`.

The default value depends on the robot type and API version:

- :py:obj:`ProtocolContext.fixed_trash`, if it exists.
- Otherwise, the first item previously loaded with
:py:obj:`ProtocolContext.load_trash_bin()` or
:py:obj:`ProtocolContext.load_waste_chute()`.

.. versionchanged:: 2.16
Added support for ``TrashBin`` and ``WasteChute`` objects.
Expand Down
37 changes: 22 additions & 15 deletions api/src/opentrons/protocol_api/protocol_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,7 @@ def loaded_modules(self) -> Dict[int, ModuleTypes]:
def load_instrument(
self,
instrument_name: str,
mount: Union[Mount, str],
mount: Union[Mount, str, None] = None,
tip_racks: Optional[List[Labware]] = None,
replace: bool = False,
) -> InstrumentContext:
Expand All @@ -831,15 +831,16 @@ def load_instrument(
ensure that the correct instrument is attached in the specified
location.

:param str instrument_name: The name of the instrument model, or a
prefix. For instance, 'p10_single' may be
used to request a P10 single regardless of
the version.
:param mount: The mount in which this instrument should be attached.
:param str instrument_name: Which instrument you want to load. See :ref:`new-pipette-models`
for the valid values.
:param mount: The mount where this instrument should be attached.
This can either be an instance of the enum type
:py:class:`.types.Mount` or one of the strings `'left'`
and `'right'`.
:type mount: types.Mount or str
:py:class:`.types.Mount` or one of the strings ``"left"``
or ``"right"``. If you're loading a Flex 96-Channel Pipette
(``instrument_name="flex_96channel_1000"``), you can leave this unspecified,
since it always occupies both mounts; if you do specify a value, it will be
ignored.
:type mount: types.Mount or str or ``None``
:param tip_racks: A list of tip racks from which to pick tips if
:py:meth:`.InstrumentContext.pick_up_tip` is called
without arguments.
Expand All @@ -850,9 +851,11 @@ def load_instrument(
"""
instrument_name = validation.ensure_lowercase_name(instrument_name)
checked_instrument_name = validation.ensure_pipette_name(instrument_name)
is_96_channel = checked_instrument_name == PipetteNameType.P1000_96
checked_mount = validation.ensure_mount_for_pipette(
mount, checked_instrument_name
)

checked_mount = Mount.LEFT if is_96_channel else validation.ensure_mount(mount)
is_96_channel = checked_instrument_name == PipetteNameType.P1000_96

tip_racks = tip_racks or []

Expand Down Expand Up @@ -1052,12 +1055,16 @@ def deck(self) -> Deck:
@property # type: ignore
@requires_version(2, 0)
def fixed_trash(self) -> Union[Labware, TrashBin]:
"""The trash fixed to slot 12 of the robot deck.
"""The trash fixed to slot 12 of an OT-2's deck.

In API version 2.15 and earlier, the fixed trash is a :py:class:`.Labware` object with one well. Access it like labware in your protocol. For example, ``protocol.fixed_trash['A1']``.

In API version 2.15 only, Flex protocols have a fixed trash in slot A3.

In API Versions prior to 2.16 it has one well and should be accessed like labware in your protocol.
e.g. ``protocol.fixed_trash['A1']``
In API version 2.16 and later, the fixed trash only exists in OT-2 protocols. It is a :py:class:`.TrashBin` object, which doesn't have any wells. Trying to access ``fixed_trash`` in a Flex protocol will raise an error. See :ref:`configure-trash-bin` for details on using the movable trash in Flex protocols.

In API Version 2.16 and above it returns a Trash fixture for OT-2 Protocols.
.. versionchanged:: 2.16
Returns a :py:class:`.TrashBin` object.
"""
if self._api_version >= APIVersion(2, 16):
if self._core.robot_type == "OT-3 Standard":
Expand Down
23 changes: 21 additions & 2 deletions api/src/opentrons/protocol_api/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,27 @@ class InvalidTrashBinLocationError(ValueError):
"""An error raised when attempting to load trash bins in invalid slots."""


def ensure_mount(mount: Union[str, Mount]) -> Mount:
def ensure_mount_for_pipette(
mount: Union[str, Mount, None], pipette: PipetteNameType
) -> Mount:
"""Ensure that an input value represents a valid mount, and is valid for the given pipette."""
if pipette == PipetteNameType.P1000_96:
# Always validate the raw mount input, even if the pipette is a 96-channel and we're not going
# to use the mount value.
if mount is not None:
_ensure_mount(mount)
# Internal layers treat the 96-channel as being on the left mount.
return Mount.LEFT
else:
if mount is None:
raise InvalidPipetteMountError(
f"You must specify a left or right mount to load {pipette.value}."
)
else:
return _ensure_mount(mount)


def _ensure_mount(mount: Union[str, Mount]) -> Mount:
"""Ensure that an input value represents a valid Mount."""
if mount in [Mount.EXTENSION, "extension"]:
# This would cause existing protocols that might be iterating over mount types
Expand Down Expand Up @@ -274,7 +294,6 @@ def ensure_module_model(load_name: str) -> ModuleModel:
def ensure_and_convert_trash_bin_location(
deck_slot: Union[int, str], api_version: APIVersion, robot_type: RobotType
) -> str:

"""Ensure trash bin load location is valid.

Also, convert the deck slot to a valid trash bin addressable area.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ class MoveToAddressableAreaParams(PipetteIdMixin, MovementMixin):
AddressableOffsetVector(x=0, y=0, z=0),
description="Relative offset of addressable area to move pipette's critical point.",
)
stayAtHighestPossibleZ: bool = Field(
False,
description=(
"If `true`, the pipette will retract to its highest possible height"
" and stay there instead of descending to the destination."
" `minimumZHeight` will be ignored."
),
)


class MoveToAddressableAreaResult(DestinationPositionResult):
Expand Down Expand Up @@ -93,6 +101,7 @@ async def execute(
force_direct=params.forceDirect,
minimum_z_height=params.minimumZHeight,
speed=params.speed,
stay_at_highest_possible_z=params.stayAtHighestPossibleZ,
)

return MoveToAddressableAreaResult(position=DeckPoint(x=x, y=y, z=z))
Expand Down
Loading
Loading