Skip to content

Commit

Permalink
chore: release->edge or 7.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sfoster1 committed Mar 6, 2024
2 parents 2044d6b + be34672 commit d2696ce
Show file tree
Hide file tree
Showing 304 changed files with 6,793 additions and 1,178 deletions.
4 changes: 2 additions & 2 deletions api-client/src/calibration/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface PipOffsetDeletionParams {

export interface TipLengthDeletionParams {
calType: 'tipLength'
tiprack_hash: string
tiprack_uri: string
pipette_id: string
}
export type DeleteCalRequestParams =
Expand Down Expand Up @@ -93,7 +93,7 @@ export interface TipLengthCalibration {
source: CalibrationSourceType
status: IndividualCalibrationHealthStatus
id: string
uri?: string | null
uri: string
}

export interface AllTipLengthCalibrations {
Expand Down
4 changes: 0 additions & 4 deletions api/docs/v2/versioning.rst
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,6 @@ This version introduces new features for Flex and adds and improves methods for

- :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
31 changes: 21 additions & 10 deletions api/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,34 @@ log][]. For a list of currently known issues, please see the [Opentrons issue tr

---

## Opentrons Robot Software Changes in [!!EDIT ME WITH THE ACTUAL NUMBER OF THE NEXT RELEASE!!]
## Opentrons Robot Software Changes in 7.2.0

### HTTP API
Welcome to the v7.2.0 release of the Opentrons robot software!

- In the `/runs/commands`, `/maintenance_runs/commands`, and `/protocols` endpoints, the `dispense` command will now return an error if you try to dispense more than you've aspirated, instead of silently clamping.
- The `/notifications/subscribe` WebSocket endpoint has been removed. See https://github.com/Opentrons/opentrons/pull/14280 for details.
- The `/runs/commands` endpoints are significantly faster when you request a small number of commands from a stored run.
This update may take longer than usual if your robot has a lot of long protocols and runs stored on it. Allow *approximately 20 minutes* for your robot to restart. This delay will only happen once.

### Other Changes
If you don't care about preserving your labware offsets and run history, you can avoid the delay by clearing your runs and protocols before starting this update. Go to **Robot Settings** > **Device Reset** and select **Clear protocol run history**.

### Improved Features

- The robot software now runs Python 3.10. Many built-in Python packages were updated to match. If you have installed your own Python packages on the robot, re-install them to ensure compatibility.
- Added error handling when dispensing. The `/runs/commands`, `/maintenance_runs/commands`, and `/protocols` HTTP API endpoints now return an error if you try to dispense more than you've aspirated.
- Improved performance of the `/runs/commands` endpoints. They are now significantly faster when requesting a small number of commands from a stored run.

- The `notify_server` Python package has been removed. See https://github.com/Opentrons/opentrons/pull/14280 for details.
### Bug Fixes

- The OT-2 now consistently applies tip length calibration. There used to be a height discrepancy between Labware Position Check and protocol runs. If you previously compensated for the inconsistent pipette height with labware offsets, re-run Labware Position Check to avoid pipette crashes.
- The OT-2 now accurately calculates the position of the Thermocycler. If you previously compensated for the incorrect position with labware offsets, re-run Labware Position Check to avoid pipette crashes.
- The Flex Gripper will no longer pick up large labware that could collide with tips held by an adjoining pipette.
- Flex now properly configures itself when connected by Ethernet directly to a computer.

### Upgrade Notes
### Removals

This update may take longer than usual if your robot has a lot of long protocols and runs stored on it. Allow **approximately 25 minutes** for your robot to restart. This delay will only happen once.
- Removed the `notify_server` Python package and `/notifications/subscribe` WebSocket endpoint, as they were never fully used. (See pull request [#14280](https://github.com/Opentrons/opentrons/pull/14280) for details.)

### Known Issues

If you don't care about preserving your labware offsets and run history, you can avoid the delay. Clear your runs and protocols before starting this update. Go to **Robot Settings** > **Device Reset** and select **Clear protocol run history**.
- Downgrading an OT-2 to an earlier software version will delete tip length calibrations created with version 7.2.0. If you need to downgrade, re-run all pipette calibrations afterward.

---

Expand Down
8 changes: 6 additions & 2 deletions api/src/opentrons/calibration_storage/ot2/models/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ class TipLengthModel(BaseModel):
default_factory=CalibrationStatus,
description="The status of the calibration data.",
)
uri: typing.Union[LabwareUri, Literal[""]] = Field(
..., description="The tiprack URI associated with the tip length data."
# Old data may have a `uri` field, replaced later by `definitionHash`.
# uri: typing.Union[LabwareUri, Literal[""]] = Field(
# ..., description="The tiprack URI associated with the tip length data."
# )
definitionHash: str = Field(
..., description="The tiprack hash associated with the tip length data."
)

@validator("tipLength")
Expand Down
78 changes: 51 additions & 27 deletions api/src/opentrons/calibration_storage/ot2/tip_length.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from opentrons import config

from .. import file_operators as io, helpers, types as local_types
from opentrons_shared_data.pipette.dev_types import LabwareUri

from opentrons.protocols.api_support.constants import OPENTRONS_NAMESPACE
from opentrons.util.helpers import utc_now
Expand All @@ -22,9 +23,9 @@
# Get Tip Length Calibration


def _conver_tip_length_model_to_dict(
to_dict: typing.Dict[str, v1.TipLengthModel]
) -> typing.Dict[str, typing.Any]:
def _convert_tip_length_model_to_dict(
to_dict: typing.Dict[LabwareUri, v1.TipLengthModel]
) -> typing.Dict[LabwareUri, typing.Any]:
# This is a workaround since pydantic doesn't have a nice way to
# add encoders when converting to a dict.
dict_of_tip_lengths = {}
Expand All @@ -35,17 +36,23 @@ def _conver_tip_length_model_to_dict(

def tip_lengths_for_pipette(
pipette_id: str,
) -> typing.Dict[str, v1.TipLengthModel]:
) -> typing.Dict[LabwareUri, v1.TipLengthModel]:
tip_lengths = {}
try:
tip_length_filepath = config.get_tip_length_cal_path() / f"{pipette_id}.json"
all_tip_lengths_for_pipette = io.read_cal_file(tip_length_filepath)
for tiprack, data in all_tip_lengths_for_pipette.items():
for tiprack_identifier, data in all_tip_lengths_for_pipette.items():
# We normally key these calibrations by their tip rack URI,
# but older software had them keyed by their tip rack hash.
# Migrate from the old format, if necessary.
if "/" not in tiprack_identifier:
data["definitionHash"] = tiprack_identifier
tiprack_identifier = data.pop("uri")
try:
tip_lengths[tiprack] = v1.TipLengthModel(**data)
tip_lengths[LabwareUri(tiprack_identifier)] = v1.TipLengthModel(**data)
except (json.JSONDecodeError, ValidationError):
log.warning(
f"Tip length calibration is malformed for {tiprack} on {pipette_id}"
f"Tip length calibration is malformed for {tiprack_identifier} on {pipette_id}"
)
pass
return tip_lengths
Expand All @@ -64,10 +71,10 @@ def load_tip_length_calibration(
:param pip_id: pipette you are using
:param definition: full definition of the tiprack
"""
labware_hash = helpers.hash_labware_def(definition)
labware_uri = helpers.uri_from_definition(definition)
load_name = definition["parameters"]["loadName"]
try:
return tip_lengths_for_pipette(pip_id)[labware_hash]
return tip_lengths_for_pipette(pip_id)[labware_uri]
except KeyError as e:
raise local_types.TipLengthCalNotFound(
f"Tip length of {load_name} has not been "
Expand All @@ -89,16 +96,16 @@ def get_all_tip_length_calibrations() -> typing.List[v1.TipLengthCalibration]:
if filepath.stem == "index":
continue
tip_lengths = tip_lengths_for_pipette(filepath.stem)
for tiprack_hash, tip_length in tip_lengths.items():
for tiprack_uri, tip_length in tip_lengths.items():
all_tip_lengths_available.append(
v1.TipLengthCalibration(
pipette=filepath.stem,
tiprack=tiprack_hash,
tiprack=tip_length.definitionHash,
tipLength=tip_length.tipLength,
lastModified=tip_length.lastModified,
source=tip_length.source,
status=tip_length.status,
uri=tip_length.uri,
uri=tiprack_uri,
)
)
return all_tip_lengths_available
Expand Down Expand Up @@ -129,28 +136,45 @@ def get_custom_tiprack_definition_for_tlc(labware_uri: str) -> "LabwareDefinitio
# Delete Tip Length Calibration


def delete_tip_length_calibration(tiprack: str, pipette_id: str) -> None:
def delete_tip_length_calibration(
pipette_id: str,
tiprack_uri: typing.Optional[LabwareUri] = None,
tiprack_hash: typing.Optional[str] = None,
) -> None:
"""
Delete tip length calibration based on tiprack hash and
pipette serial number
Delete tip length calibration based on an optional tiprack uri or
tiprack hash and pipette serial number.
:param tiprack: tiprack hash
:param tiprack_uri: tiprack uri
:param tiprack_hash: tiprack uri
:param pipette: pipette serial number
"""
tip_lengths = tip_lengths_for_pipette(pipette_id)

if tiprack in tip_lengths:
tip_length_dir = config.get_tip_length_cal_path()
if tiprack_uri in tip_lengths:
# maybe make modify and delete same file?
del tip_lengths[tiprack]
tip_length_dir = config.get_tip_length_cal_path()
del tip_lengths[tiprack_uri]

if tip_lengths:
dict_of_tip_lengths = _convert_tip_length_model_to_dict(tip_lengths)
io.save_to_file(tip_length_dir, pipette_id, dict_of_tip_lengths)
else:
io.delete_file(tip_length_dir / f"{pipette_id}.json")
elif tiprack_hash and any(tiprack_hash in v.dict() for v in tip_lengths.values()):
# NOTE this is for backwards compatibilty only
# TODO delete this check once the tip_length DELETE router
# no longer depends on a tiprack hash
for k, v in tip_lengths.items():
if tiprack_hash in v.dict():
tip_lengths.pop(k)
if tip_lengths:
dict_of_tip_lengths = _conver_tip_length_model_to_dict(tip_lengths)
dict_of_tip_lengths = _convert_tip_length_model_to_dict(tip_lengths)
io.save_to_file(tip_length_dir, pipette_id, dict_of_tip_lengths)
else:
io.delete_file(tip_length_dir / f"{pipette_id}.json")
else:
raise local_types.TipLengthCalNotFound(
f"Tip length for hash {tiprack} has not been "
f"Tip length for uri {tiprack_uri} and hash {tiprack_hash} has not been "
f"calibrated for this pipette: {pipette_id} and cannot"
"be loaded"
)
Expand All @@ -176,7 +200,7 @@ def create_tip_length_data(
cal_status: typing.Optional[
typing.Union[local_types.CalibrationStatus, v1.CalibrationStatus]
] = None,
) -> typing.Dict[str, v1.TipLengthModel]:
) -> typing.Dict[LabwareUri, v1.TipLengthModel]:
"""
Function to correctly format tip length data.
Expand All @@ -197,13 +221,13 @@ def create_tip_length_data(
lastModified=utc_now(),
source=local_types.SourceType.user,
status=cal_status_model,
uri=labware_uri,
definitionHash=labware_hash,
)

if not definition.get("namespace") == OPENTRONS_NAMESPACE:
_save_custom_tiprack_definition(labware_uri, definition)

data = {labware_hash: tip_length_data}
data = {labware_uri: tip_length_data}
return data


Expand All @@ -220,7 +244,7 @@ def _save_custom_tiprack_definition(

def save_tip_length_calibration(
pip_id: str,
tip_length_cal: typing.Dict[str, v1.TipLengthModel],
tip_length_cal: typing.Dict[LabwareUri, v1.TipLengthModel],
) -> None:
"""
Function used to save tip length calibration to file.
Expand All @@ -235,5 +259,5 @@ def save_tip_length_calibration(

all_tip_lengths.update(tip_length_cal)

dict_of_tip_lengths = _conver_tip_length_model_to_dict(all_tip_lengths)
dict_of_tip_lengths = _convert_tip_length_model_to_dict(all_tip_lengths)
io.save_to_file(tip_length_dir_path, pip_id, dict_of_tip_lengths)
Original file line number Diff line number Diff line change
Expand Up @@ -1157,7 +1157,7 @@ async def watch(self, loop: asyncio.AbstractEventLoop) -> None:
def axis_bounds(self) -> OT3AxisMap[Tuple[float, float]]:
"""Get the axis bounds."""
# TODO (AL, 2021-11-18): The bounds need to be defined
phony_bounds = (0, 10000)
phony_bounds = (0, 500)
return {
Axis.Z_L: phony_bounds,
Axis.Z_R: phony_bounds,
Expand Down
4 changes: 3 additions & 1 deletion api/src/opentrons/hardware_control/dev_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from opentrons_shared_data.pipette.pipette_definition import (
PipetteConfigurations,
SupportedTipsDefinition,
PipetteBoundingBoxOffsetDefinition,
)
from opentrons_shared_data.gripper import (
GripperModel,
Expand Down Expand Up @@ -95,7 +96,8 @@ class PipetteDict(InstrumentDict):
has_tip: bool
default_push_out_volume: Optional[float]
supported_tips: Dict[PipetteTipType, SupportedTipsDefinition]
current_nozzle_map: NozzleMap # spp: why was this Optional?
pipette_bounding_box_offsets: PipetteBoundingBoxOffsetDefinition
current_nozzle_map: NozzleMap


class PipetteStateDict(TypedDict):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,15 @@ def load_tip_length_for_pipette(
pipette_id, tiprack
)

# TODO (lc 09-26-2022) We shouldn't have to do a hash twice. We should figure out what
# information we actually need from the labware definition and pass it into
# the `load_tip_length_calibration` function.
tiprack_hash = helpers.hash_labware_def(tiprack)
tiprack_uri = helpers.uri_from_definition(tiprack)

return TipLengthCalibration(
tip_length=tip_length_data.tipLength,
source=tip_length_data.source,
pipette=pipette_id,
tiprack=tiprack_hash,
tiprack=tip_length_data.definitionHash,
last_modified=tip_length_data.lastModified,
uri=tip_length_data.uri,
uri=tiprack_uri,
status=types.CalibrationStatus(
markedAt=tip_length_data.status.markedAt,
markedBad=tip_length_data.status.markedBad,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
control API. Its only purpose is to gather state.
"""

DictType = Dict[str, Union[str, float, bool]]
DictType = Dict[
str, Union[str, float, bool]
] # spp: as_dict() has value items that aren't Union[str, float, bool]..
#: The type of this data class as a dict

def __init__(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,6 @@ def get_attached_instrument(self, mount: MountType) -> PipetteDict:
instr, instr.blow_out_flow_rate, "dispense"
)
result["ready_to_aspirate"] = instr.ready_to_aspirate
# TODO (12-5-2022) figure out why this is using default aspirate flow rate
# rather than default dispense flow rate.
result["default_blow_out_speeds"] = {
alvl: self.plunger_speed(instr, fr, "blowout")
for alvl, fr in instr.blow_out_flow_rates_lookup.items()
Expand All @@ -257,6 +255,9 @@ def get_attached_instrument(self, mount: MountType) -> PipetteDict:
alvl: self.plunger_speed(instr, fr, "aspirate")
for alvl, fr in instr.aspirate_flow_rates_lookup.items()
}
result[
"pipette_bounding_box_offsets"
] = instr.config.pipette_bounding_box_offsets
return cast(PipetteDict, result)

@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict:
result[
"default_push_out_volume"
] = instr.active_tip_settings.default_push_out_volume
result[
"pipette_bounding_box_offsets"
] = instr.config.pipette_bounding_box_offsets
return cast(PipetteDict, result)

@property
Expand Down
1 change: 0 additions & 1 deletion api/src/opentrons/hardware_control/modules/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ async def update_firmware(
kwargs: Dict[str, Any] = {
"stdout": asyncio.subprocess.PIPE,
"stderr": asyncio.subprocess.PIPE,
"loop": loop,
}
successful, res = await module.bootloader()(
flash_port_or_dfu_serial, str(firmware_file), kwargs
Expand Down
Loading

0 comments on commit d2696ce

Please sign in to comment.