diff --git a/api/src/opentrons/protocol_api/core/engine/well.py b/api/src/opentrons/protocol_api/core/engine/well.py
index e18bdb8ecf6a..964e1f7576c1 100644
--- a/api/src/opentrons/protocol_api/core/engine/well.py
+++ b/api/src/opentrons/protocol_api/core/engine/well.py
@@ -3,7 +3,7 @@
from opentrons_shared_data.labware.constants import WELL_NAME_PATTERN
-from opentrons.types import Point
+from opentrons.types import Point, Mount, MountType
from opentrons.protocol_engine import WellLocation, WellOrigin, WellOffset
from opentrons.protocol_engine import commands as cmd
@@ -13,6 +13,7 @@
SimulatedProbeResult,
LiquidTrackingType,
)
+from opentrons.protocol_engine.errors import PipetteNotAttachedError
from . import point_calculations
from . import stringify
@@ -181,15 +182,25 @@ def from_center_cartesian(self, x: float, y: float, z: float) -> Point:
def estimate_liquid_height_after_pipetting(
self,
+ mount: Mount | str,
operation_volume: float,
) -> LiquidTrackingType:
"""Return an estimate of liquid height after pipetting without raising an error."""
labware_id = self.labware_id
well_name = self._name
+ if isinstance(mount, Mount):
+ mount_type = MountType.from_hw_mount(mount)
+ else:
+ mount_type = MountType(mount)
+ pipette_from_mount = self._engine_client.state.pipettes.get_by_mount(mount_type)
+ if pipette_from_mount is None:
+ raise PipetteNotAttachedError(f"No pipette present on mount {mount}")
+ pipette_id = pipette_from_mount.id
starting_liquid_height = self.current_liquid_height()
projected_final_height = self._engine_client.state.geometry.get_well_height_after_liquid_handling_no_error(
labware_id=labware_id,
well_name=well_name,
+ pipette_id=pipette_id,
initial_height=starting_liquid_height,
volume=operation_volume,
)
diff --git a/api/src/opentrons/protocol_api/core/legacy/legacy_well_core.py b/api/src/opentrons/protocol_api/core/legacy/legacy_well_core.py
index e0a12039c518..671409c9587a 100644
--- a/api/src/opentrons/protocol_api/core/legacy/legacy_well_core.py
+++ b/api/src/opentrons/protocol_api/core/legacy/legacy_well_core.py
@@ -5,7 +5,7 @@
from opentrons.protocols.api_support.util import APIVersionError
-from opentrons.types import Point
+from opentrons.types import Point, Mount
from opentrons.protocol_engine.types.liquid_level_detection import (
SimulatedProbeResult,
@@ -129,6 +129,7 @@ def from_center_cartesian(self, x: float, y: float, z: float) -> Point:
def estimate_liquid_height_after_pipetting(
self,
+ mount: Mount | str,
operation_volume: float,
) -> LiquidTrackingType:
"""Estimate what the liquid height will be after pipetting, without raising an error."""
diff --git a/api/src/opentrons/protocol_api/core/well.py b/api/src/opentrons/protocol_api/core/well.py
index d02cea5cb24b..02fe1f47f424 100644
--- a/api/src/opentrons/protocol_api/core/well.py
+++ b/api/src/opentrons/protocol_api/core/well.py
@@ -3,7 +3,7 @@
from abc import ABC, abstractmethod
from typing import TypeVar, Optional, Union
-from opentrons.types import Point
+from opentrons.types import Point, Mount
from opentrons.protocol_engine.types import LiquidTrackingType
from .._liquid import Liquid
@@ -91,6 +91,7 @@ def from_center_cartesian(self, x: float, y: float, z: float) -> Point:
@abstractmethod
def estimate_liquid_height_after_pipetting(
self,
+ mount: Mount | str,
operation_volume: float,
) -> LiquidTrackingType:
"""Estimate what the liquid height will be after pipetting, without raising an error."""
diff --git a/api/src/opentrons/protocol_api/instrument_context.py b/api/src/opentrons/protocol_api/instrument_context.py
index 1d2da5739c96..4975c848b9b8 100644
--- a/api/src/opentrons/protocol_api/instrument_context.py
+++ b/api/src/opentrons/protocol_api/instrument_context.py
@@ -606,7 +606,20 @@ def blow_out(
"Blow_out being performed on a tiprack. "
"Please re-check your code"
)
- move_to_location = target.location or target.well.top()
+ if target.location:
+ # because the lower levels of blowout don't handle LiquidHandlingWellLocation and
+ # there is no "operation_volume" for blowout we need to convert the relative location
+ # given with a .meniscus to an absolute point. To maintain the meniscus behavior
+ # we can just add the offset to the current liquid height.
+ if target.location.meniscus_tracking:
+ move_to_location = target.well.bottom(
+ target.well.current_liquid_height() # type: ignore [arg-type]
+ + target.location.point.z
+ )
+ else:
+ move_to_location = target.location
+ else:
+ move_to_location = target.well.top()
well = target.well
elif isinstance(target, validation.PointTarget):
move_to_location = target.location
@@ -2698,7 +2711,8 @@ def measure_liquid_height(self, well: labware.Well) -> LiquidTrackingType:
"""
self._raise_if_pressure_not_supported_by_pipette()
loc = well.top()
- return self._core.liquid_probe_without_recovery(well._core, loc)
+ self._core.liquid_probe_with_recovery(well._core, loc)
+ return well.current_liquid_height()
def _raise_if_configuration_not_supported_by_pipette(
self, style: NozzleLayout
diff --git a/api/src/opentrons/protocol_api/labware.py b/api/src/opentrons/protocol_api/labware.py
index 51f1301bf7b3..6580c004fa33 100644
--- a/api/src/opentrons/protocol_api/labware.py
+++ b/api/src/opentrons/protocol_api/labware.py
@@ -39,6 +39,7 @@
Point,
NozzleMapInterface,
MeniscusTrackingTarget,
+ Mount,
)
from opentrons.protocols.api_support.types import APIVersion
from opentrons.protocols.api_support.util import (
@@ -348,6 +349,7 @@ def current_liquid_volume(self) -> LiquidTrackingType:
@requires_version(2, 21)
def estimate_liquid_height_after_pipetting(
self,
+ mount: Mount | str,
operation_volume: float,
) -> LiquidTrackingType:
"""Check the height of the liquid within a well.
@@ -360,7 +362,7 @@ def estimate_liquid_height_after_pipetting(
"""
projected_final_height = self._core.estimate_liquid_height_after_pipetting(
- operation_volume=operation_volume,
+ operation_volume=operation_volume, mount=mount
)
return projected_final_height
diff --git a/api/src/opentrons/protocol_engine/execution/pipetting.py b/api/src/opentrons/protocol_engine/execution/pipetting.py
index b503f4ae69f4..1d21542411dc 100644
--- a/api/src/opentrons/protocol_engine/execution/pipetting.py
+++ b/api/src/opentrons/protocol_engine/execution/pipetting.py
@@ -199,6 +199,7 @@ async def aspirate_while_tracking(
labware_id=labware_id,
well_name=well_name,
operation_volume=volume * -1,
+ pipette_id=pipette_id,
)
if isinstance(aspirate_z_distance, SimulatedProbeResult):
raise InvalidLiquidHeightFound(
@@ -234,6 +235,7 @@ async def dispense_while_tracking(
labware_id=labware_id,
well_name=well_name,
operation_volume=volume,
+ pipette_id=pipette_id,
)
if isinstance(dispense_z_distance, SimulatedProbeResult):
raise InvalidLiquidHeightFound(
diff --git a/api/src/opentrons/protocol_engine/state/geometry.py b/api/src/opentrons/protocol_engine/state/geometry.py
index 4ba4c35a32f2..84f66844c318 100644
--- a/api/src/opentrons/protocol_engine/state/geometry.py
+++ b/api/src/opentrons/protocol_engine/state/geometry.py
@@ -520,6 +520,7 @@ def get_well_position(
well_location=well_location,
well_depth=well_depth,
operation_volume=operation_volume,
+ pipette_id=pipette_id,
)
if not isinstance(offset_adjustment, SimulatedProbeResult):
offset = offset.model_copy(update={"z": offset.z + offset_adjustment})
@@ -1844,6 +1845,7 @@ def get_liquid_handling_z_change(
self,
labware_id: str,
well_name: str,
+ pipette_id: str,
operation_volume: float,
) -> float:
"""Get the change in height from a liquid handling operation."""
@@ -1853,6 +1855,7 @@ def get_liquid_handling_z_change(
final_height = self.get_well_height_after_liquid_handling(
labware_id=labware_id,
well_name=well_name,
+ pipette_id=pipette_id,
initial_height=initial_handling_height,
volume=operation_volume,
)
@@ -1873,6 +1876,7 @@ def get_well_offset_adjustment(
well_name: str,
well_location: WellLocationType,
well_depth: float,
+ pipette_id: Optional[str] = None,
operation_volume: Optional[float] = None,
) -> LiquidTrackingType:
"""Return a z-axis distance that accounts for well handling height and operation volume.
@@ -1906,9 +1910,14 @@ def get_well_offset_adjustment(
volume = well_location.volumeOffset
if volume:
+ if pipette_id is None:
+ raise ValueError(
+ "cannot get liquid handling offset without pipette id."
+ )
liquid_height_after = self.get_well_height_after_liquid_handling(
labware_id=labware_id,
well_name=well_name,
+ pipette_id=pipette_id,
initial_height=initial_handling_height,
volume=volume,
)
@@ -2030,6 +2039,7 @@ def get_well_height_after_liquid_handling(
self,
labware_id: str,
well_name: str,
+ pipette_id: str,
initial_height: LiquidTrackingType,
volume: float,
) -> LiquidTrackingType:
@@ -2044,7 +2054,14 @@ def get_well_height_after_liquid_handling(
initial_volume = find_volume_at_well_height(
target_height=initial_height, well_geometry=well_geometry
)
- final_volume = initial_volume + volume
+ final_volume = initial_volume + (
+ volume
+ * self.get_nozzles_per_well(
+ labware_id=labware_id,
+ target_well_name=well_name,
+ pipette_id=pipette_id,
+ )
+ )
return find_height_at_well_volume(
target_volume=final_volume, well_geometry=well_geometry
)
@@ -2058,6 +2075,7 @@ def get_well_height_after_liquid_handling_no_error(
self,
labware_id: str,
well_name: str,
+ pipette_id: str,
initial_height: LiquidTrackingType,
volume: float,
) -> LiquidTrackingType:
@@ -2073,7 +2091,14 @@ def get_well_height_after_liquid_handling_no_error(
initial_volume = find_volume_at_well_height(
target_height=initial_height, well_geometry=well_geometry
)
- final_volume = initial_volume + volume
+ final_volume = initial_volume + (
+ volume
+ * self.get_nozzles_per_well(
+ labware_id=labware_id,
+ target_well_name=well_name,
+ pipette_id=pipette_id,
+ )
+ )
well_volume = find_height_at_well_volume(
target_volume=final_volume,
well_geometry=well_geometry,
diff --git a/api/src/opentrons/protocol_engine/types/__init__.py b/api/src/opentrons/protocol_engine/types/__init__.py
index 435d23dcca61..9d22d8be216b 100644
--- a/api/src/opentrons/protocol_engine/types/__init__.py
+++ b/api/src/opentrons/protocol_engine/types/__init__.py
@@ -135,6 +135,7 @@
WellInfoSummary,
WellLiquidInfo,
LiquidTrackingType,
+ SimulatedProbeResult,
)
from .liquid_handling import FlowRates
from .labware_movement import LabwareMovementStrategy, LabwareMovementOffsetData
@@ -280,6 +281,7 @@
"WellInfoSummary",
"WellLiquidInfo",
"LiquidTrackingType",
+ "SimulatedProbeResult",
# Liquid handling
"FlowRates",
# Labware movement
diff --git a/api/src/opentrons/protocol_engine/types/liquid_level_detection.py b/api/src/opentrons/protocol_engine/types/liquid_level_detection.py
index 235add1caf16..4d6bd82329e4 100644
--- a/api/src/opentrons/protocol_engine/types/liquid_level_detection.py
+++ b/api/src/opentrons/protocol_engine/types/liquid_level_detection.py
@@ -1,9 +1,10 @@
"""Protocol Engine types to do with liquid level detection."""
+
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
-from typing import Optional, List
-from pydantic import BaseModel, model_serializer, field_validator
+from typing import Optional, List, Any
+from pydantic import BaseModel, model_serializer, model_validator
class SimulatedProbeResult(BaseModel):
@@ -17,6 +18,14 @@ def serialize_model(self) -> str:
"""Serialize instances of this class as a string."""
return "SimulatedProbeResult"
+ @model_validator(mode="before")
+ @classmethod
+ def validate_model(cls, data: object) -> Any:
+ """Handle deserializing from a simulated probe result."""
+ if isinstance(data, str) and data == "SimulatedProbeResult":
+ return {}
+ return data
+
def __add__(
self, other: float | SimulatedProbeResult
) -> float | SimulatedProbeResult:
@@ -75,7 +84,9 @@ def simulate_probed_aspirate_dispense(self, volume: float) -> None:
self.operations_after_probe.append(volume)
-LiquidTrackingType = SimulatedProbeResult | float
+# Work around https://github.com/pydantic/pydantic/issues/6830 - do not change the order of
+# this union
+LiquidTrackingType = float | SimulatedProbeResult
class LoadedVolumeInfo(BaseModel):
@@ -104,23 +115,6 @@ class ProbedVolumeInfo(BaseModel):
class WellInfoSummary(BaseModel):
"""Payload for a well's liquid info in StateSummary."""
- # TODO(cm): 3/21/25: refactor SimulatedLiquidProbe in a way that
- # doesn't require models like this one that are just using it to
- # need a custom validator
- @field_validator("probed_height", "probed_volume", mode="before")
- @classmethod
- def validate_simulated_probe_result(
- cls, input_val: object
- ) -> LiquidTrackingType | None:
- """Return the appropriate input to WellInfoSummary from json data."""
- if input_val is None:
- return None
- if isinstance(input_val, LiquidTrackingType):
- return input_val
- if isinstance(input_val, str) and input_val == "SimulatedProbeResult":
- return SimulatedProbeResult()
- raise ValueError(f"Invalid input value {input_val} to WellInfoSummary")
-
labware_id: str
well_name: str
loaded_volume: Optional[float] = None
diff --git a/api/tests/opentrons/protocol_api/core/engine/test_well_core.py b/api/tests/opentrons/protocol_api/core/engine/test_well_core.py
index a45ba03aac22..cad3a1fff8c9 100644
--- a/api/tests/opentrons/protocol_api/core/engine/test_well_core.py
+++ b/api/tests/opentrons/protocol_api/core/engine/test_well_core.py
@@ -10,9 +10,15 @@
RectangularWellDefinition2,
CircularWellDefinition2,
)
+from opentrons_shared_data.pipette.types import PipetteNameType
from opentrons.protocol_api import MAX_SUPPORTED_VERSION
-from opentrons.protocol_engine import WellLocation, WellOrigin, WellOffset
+from opentrons.protocol_engine import (
+ WellLocation,
+ WellOrigin,
+ WellOffset,
+ LoadedPipette,
+)
from opentrons.protocol_engine import commands as cmd
from opentrons.protocol_engine.clients import SyncClient as EngineClient
from opentrons.protocol_engine.errors.exceptions import (
@@ -21,7 +27,7 @@
)
from opentrons.protocols.api_support.types import APIVersion
from opentrons.protocols.api_support.util import UnsupportedAPIError
-from opentrons.types import Point
+from opentrons.types import Point, Mount, MountType
from opentrons_shared_data.labware.labware_definition import (
InnerWellGeometry,
ConicalFrustum,
@@ -312,11 +318,13 @@ def test_current_liquid_volume(
@pytest.mark.parametrize("operation_volume", [0.0, 100, -100, 2, -4, 5])
+@pytest.mark.parametrize("mount", [Mount.LEFT, "left"])
def test_estimate_liquid_height_after_pipetting(
decoy: Decoy,
subject: WellCore,
mock_engine_client: EngineClient,
operation_volume: float,
+ mount: Mount | str,
) -> None:
"""Make sure estimate_liquid_height_after_pipetting returns the correct value and does not raise an error."""
fake_well_geometry = InnerWellGeometry(
@@ -355,14 +363,24 @@ def test_estimate_liquid_height_after_pipetting(
mock_engine_client.state.geometry.get_well_height_after_liquid_handling_no_error(
labware_id="labware-id",
well_name="well-name",
+ pipette_id="pipette-id",
initial_height=initial_liquid_height,
volume=operation_volume,
)
).then_return(fake_final_height)
+ decoy.when(
+ mock_engine_client.state.pipettes.get_by_mount(MountType.LEFT)
+ ).then_return(
+ LoadedPipette(
+ id="pipette-id",
+ pipetteName=PipetteNameType.P300_SINGLE,
+ mount=MountType.LEFT,
+ )
+ )
# make sure that no error was raised
final_height = subject.estimate_liquid_height_after_pipetting(
- operation_volume=operation_volume,
+ operation_volume=operation_volume, mount=mount
)
assert final_height == fake_final_height
diff --git a/api/tests/opentrons/protocol_api/test_instrument_context.py b/api/tests/opentrons/protocol_api/test_instrument_context.py
index 6b8fc4648b1f..9ac1aae69fd6 100644
--- a/api/tests/opentrons/protocol_api/test_instrument_context.py
+++ b/api/tests/opentrons/protocol_api/test_instrument_context.py
@@ -59,7 +59,13 @@
from opentrons.hardware_control.nozzle_manager import NozzleMap
from opentrons.protocol_api.disposal_locations import TrashBin, WasteChute
-from opentrons.types import Location, Mount, Point, NozzleMapInterface
+from opentrons.types import (
+ Location,
+ Mount,
+ Point,
+ NozzleMapInterface,
+ MeniscusTrackingTarget,
+)
from opentrons_shared_data.pipette.pipette_definition import ValidNozzleMaps
from opentrons_shared_data.errors.exceptions import (
@@ -553,6 +559,53 @@ def test_blow_out_to_well_location(
)
+def test_blow_out_to_well_meniscus_location(
+ decoy: Decoy,
+ mock_instrument_core: InstrumentCore,
+ subject: InstrumentContext,
+ mock_protocol_core: ProtocolCore,
+) -> None:
+ """It should blow out to a well location."""
+ liquid_height = 10.0
+ well_bottom = Point(2, 2, 2)
+ relative_height = 3
+ mock_well = decoy.mock(cls=Well)
+ input_location_absolute = Location(
+ point=well_bottom + Point(0, 0, liquid_height) + Point(0, 0, relative_height),
+ labware=mock_well,
+ )
+ decoy.when(mock_well.current_liquid_height()).then_return(liquid_height)
+ decoy.when(mock_well.bottom(liquid_height + relative_height)).then_return(
+ Location(
+ point=well_bottom + Point(0, 0, liquid_height + relative_height),
+ labware=mock_well,
+ )
+ )
+
+ input_location = Location(
+ point=Point(0, 0, relative_height),
+ labware=mock_well,
+ _meniscus_tracking=MeniscusTrackingTarget.END,
+ )
+ last_location = Location(point=Point(9, 9, 9), labware=None)
+ decoy.when(mock_instrument_core.get_mount()).then_return(Mount.RIGHT)
+
+ decoy.when(mock_protocol_core.get_last_location(Mount.RIGHT)).then_return(
+ last_location
+ )
+ decoy.when(
+ mock_validation.validate_location(
+ location=input_location, last_location=last_location
+ )
+ ).then_return(WellTarget(well=mock_well, location=input_location, in_place=False))
+
+ subject.blow_out(location=input_location)
+
+ mock_instrument_core.blow_out(
+ location=input_location_absolute, well_core=mock_well._core, in_place=False
+ )
+
+
def test_blow_out_to_location(
decoy: Decoy,
mock_instrument_core: InstrumentCore,
@@ -1493,8 +1546,15 @@ def test_measure_liquid_height(
original_error=lnfe,
message=f"{lnfe.errorType}: {lnfe.detail}",
)
+ decoy.when(mock_well.current_liquid_height()).then_return(123)
decoy.when(
- mock_instrument_core.liquid_probe_without_recovery(
+ mock_instrument_core.liquid_probe_with_recovery(
+ mock_well._core, mock_well.top()
+ )
+ )
+ assert subject.measure_liquid_height(mock_well) == 123
+ decoy.when(
+ mock_instrument_core.liquid_probe_with_recovery(
mock_well._core, mock_well.top()
)
).then_raise(errorToRaise)
diff --git a/api/tests/opentrons/protocol_engine/execution/test_pipetting_handler.py b/api/tests/opentrons/protocol_engine/execution/test_pipetting_handler.py
index e94dc074d5d7..8fd017ec194a 100644
--- a/api/tests/opentrons/protocol_engine/execution/test_pipetting_handler.py
+++ b/api/tests/opentrons/protocol_engine/execution/test_pipetting_handler.py
@@ -332,7 +332,10 @@ async def test_hw_aspirate_while_tracking(
decoy.when(
mock_state_view.geometry.get_liquid_handling_z_change(
- labware_id="labware-id", well_name="A1", operation_volume=-25.0
+ labware_id="labware-id",
+ well_name="A1",
+ pipette_id="pipette_id",
+ operation_volume=-25.0,
)
).then_return(4.544)
diff --git a/api/tests/opentrons/protocol_engine/state/test_geometry_view.py b/api/tests/opentrons/protocol_engine/state/test_geometry_view.py
index c29cb066e563..4e301b3a007a 100644
--- a/api/tests/opentrons/protocol_engine/state/test_geometry_view.py
+++ b/api/tests/opentrons/protocol_engine/state/test_geometry_view.py
@@ -1773,6 +1773,10 @@ def test_get_well_position_with_meniscus_and_literal_volume_offset(
slot_pos = Point(4, 5, 6)
well_def = well_plate_def.wells["B2"]
+ pip_type = PipetteNameType.P300_SINGLE
+ decoy.when(mock_pipette_view.get_nozzle_configuration("pipette-id")).then_return(
+ get_default_nozzle_map(pip_type)
+ )
decoy.when(mock_labware_view.get("labware-id")).then_return(labware_data)
decoy.when(mock_labware_view.get_definition("labware-id")).then_return(
well_plate_def
@@ -1843,7 +1847,10 @@ def test_get_well_position_with_meniscus_and_float_volume_offset(
calibration_offset = LabwareOffsetVector(x=1, y=-2, z=3)
slot_pos = Point(4, 5, 6)
well_def = well_plate_def.wells["B2"]
-
+ pip_type = PipetteNameType.P300_SINGLE
+ decoy.when(mock_pipette_view.get_nozzle_configuration("pipette-id")).then_return(
+ get_default_nozzle_map(pip_type)
+ )
decoy.when(mock_labware_view.get("labware-id")).then_return(labware_data)
decoy.when(mock_labware_view.get_definition("labware-id")).then_return(
well_plate_def
@@ -1941,6 +1948,10 @@ def test_get_well_position_raises_validation_error(
decoy.when(mock_labware_view.get_well_geometry("labware-id", "B2")).then_return(
_TEST_INNER_WELL_GEOMETRY
)
+ pip_type = PipetteNameType.P300_SINGLE
+ decoy.when(mock_pipette_view.get_nozzle_configuration("pipette-id")).then_return(
+ get_default_nozzle_map(pip_type)
+ )
decoy.when(
mock_pipette_view.get_current_tip_lld_settings(pipette_id="pipette-id")
).then_return(0.5)
@@ -4184,17 +4195,27 @@ def test_virtual_get_well_height_after_liquid_handling_no_error(
decoy: Decoy,
subject: GeometryView,
mock_labware_view: LabwareView,
+ mock_pipette_view: PipetteView,
+ well_plate_def: LabwareDefinition,
initial_liquid_height: LiquidTrackingType,
) -> None:
"""Make sure SimulatedLiquidProbe doesn't change geometry behavior."""
+ pip_type = PipetteNameType.P300_SINGLE
+ decoy.when(mock_pipette_view.get_nozzle_configuration("pipette-id")).then_return(
+ get_default_nozzle_map(pip_type)
+ )
+ decoy.when(mock_labware_view.get_definition("labware-id")).then_return(
+ well_plate_def
+ )
+
decoy.when(mock_labware_view.get_well_geometry("labware-id", "B2")).then_return(
_TEST_INNER_WELL_GEOMETRY
)
operation_volume = 1000.0
-
result_estimate = subject.get_well_height_after_liquid_handling_no_error(
labware_id="labware-id",
well_name="B2",
+ pipette_id="pipette-id",
initial_height=initial_liquid_height,
volume=operation_volume,
)
@@ -4228,13 +4249,23 @@ def test_virtual_find_height_and_volume(
def test_get_liquid_handling_z_change(
decoy: Decoy,
subject: GeometryView,
+ well_plate_def: LabwareDefinition,
mock_labware_view: LabwareView,
+ mock_pipette_view: PipetteView,
mock_well_view: WellView,
) -> None:
"""Test for get_liquid_handling_z_change math."""
+ pip_type = PipetteNameType.P300_SINGLE
+ decoy.when(mock_pipette_view.get_nozzle_configuration("pipette-id")).then_return(
+ get_default_nozzle_map(pip_type)
+ )
+
decoy.when(mock_labware_view.get_well_definition("labware-id", "A1")).then_return(
RectangularWellDefinition3.model_construct(totalLiquidVolume=1100000) # type: ignore[call-arg]
)
+ decoy.when(mock_labware_view.get_definition("labware-id")).then_return(
+ well_plate_def
+ )
decoy.when(mock_labware_view.get_well_geometry("labware-id", "A1")).then_return(
_TEST_INNER_WELL_GEOMETRY
)
@@ -4251,7 +4282,10 @@ def test_get_liquid_handling_z_change(
)
# make sure that liquid handling z change math stays the same
change = subject.get_liquid_handling_z_change(
- labware_id="labware-id", well_name="A1", operation_volume=199.0
+ labware_id="labware-id",
+ well_name="A1",
+ pipette_id="pipette-id",
+ operation_volume=199.0,
)
expected_change = 3.2968
assert isclose(change, expected_change)
diff --git a/api/tests/opentrons/protocol_engine/test_types.py b/api/tests/opentrons/protocol_engine/test_types.py
index d48c67ee61ed..fb9a0978bb0e 100644
--- a/api/tests/opentrons/protocol_engine/test_types.py
+++ b/api/tests/opentrons/protocol_engine/test_types.py
@@ -1,8 +1,14 @@
"""Test protocol engine types."""
+
import pytest
-from pydantic import ValidationError
+from pydantic import ValidationError, BaseModel
-from opentrons.protocol_engine.types import HexColor
+from opentrons.protocol_engine.types import (
+ HexColor,
+ SimulatedProbeResult,
+ LiquidTrackingType,
+ WellInfoSummary,
+)
@pytest.mark.parametrize("hex_color", ["#F00", "#FFCC00CC", "#FC0C", "#98e2d1"])
@@ -20,3 +26,51 @@ def test_handles_invalid_hex(invalid_hex_color: str) -> None:
HexColor(invalid_hex_color)
with pytest.raises(ValidationError):
HexColor.model_validate_json(f'"{invalid_hex_color}"')
+
+
+class _TestModel(BaseModel):
+ """Test model for deserializing SimulatedProbeResults."""
+
+ value: LiquidTrackingType
+
+
+def test_roundtrips_simulated_liquid_probe() -> None:
+ """Should be able to roundtrip our simulated results."""
+ base = _TestModel(value=SimulatedProbeResult())
+ serialized = base.model_dump_json()
+ deserialized = _TestModel.model_validate_json(serialized)
+ assert isinstance(deserialized.value, SimulatedProbeResult)
+
+
+def test_roundtrips_nonsimulated_liquid_probe() -> None:
+ """Should be able to roundtrip our simulated results."""
+ base = _TestModel(value=10.0)
+ serialized = base.model_dump_json()
+ deserialized = _TestModel.model_validate_json(serialized)
+ assert deserialized.value == 10.0
+
+
+def test_fails_deser_wrong_string() -> None:
+ """Should fail to deserialize the wrong string."""
+ with pytest.raises(ValidationError):
+ _TestModel.model_validate_json('{"value": "not the right string"}')
+
+
+@pytest.mark.parametrize("height", [None, 10.0, SimulatedProbeResult()])
+def test_roundtrips_well_info_summary(height: LiquidTrackingType | None) -> None:
+ """It should round trip a WellInfoSummary."""
+ inp = WellInfoSummary(
+ labware_id="hi",
+ well_name="lo",
+ loaded_volume=None,
+ probed_height=height,
+ probed_volume=height,
+ )
+ outp = WellInfoSummary.model_validate_json(inp.model_dump_json())
+ if isinstance(height, SimulatedProbeResult):
+ assert outp.labware_id == inp.labware_id
+ assert outp.well_name == inp.well_name
+ assert isinstance(outp.probed_height, SimulatedProbeResult)
+ assert isinstance(outp.probed_volume, SimulatedProbeResult)
+ else:
+ assert outp == inp
diff --git a/app/src/assets/localization/zh/anonymous.json b/app/src/assets/localization/zh/anonymous.json
index 0981184ba676..6d555af80d04 100644
--- a/app/src/assets/localization/zh/anonymous.json
+++ b/app/src/assets/localization/zh/anonymous.json
@@ -40,7 +40,7 @@
"module_error_contact_support": "尝试关闭模块电源,然后再打开。如果报错仍然存在,请与支持人员联系。",
"network_setup_menu_description": "您将使用此连接来运行软件更新,并将协议加载到您的工作站上。",
"new_robot_instructions": "设置新工作站时,请遵循触摸屏上的指示。有关更多信息,请参阅您的工作站快速入门指南。",
- "oem_mode_description": "启用OEM模式,以从Flex触摸屏中移除Opentrons的相关信息。",
+ "oem_mode_description": "启用 OEM 模式以从 Flex 触摸屏中删除所有 Opentrons 实例。",
"opentrons_app_successfully_updated": "应用程序已成功更新。",
"opentrons_app_update": "应用程序更新",
"opentrons_app_update_available": "应用程序可更新",
@@ -65,9 +65,9 @@
"share_app_analytics": "共享应用程序分析数据",
"share_app_analytics_description": "通过自动发送匿名诊断和使用数据来帮助改进此产品。",
"share_display_usage_description": "关于工作站触摸屏的交互数据。",
- "share_logs_with_opentrons": "共享工作站日志",
+ "share_logs_with_opentrons": "分享机器人日志",
"share_logs_with_opentrons_description": "通过自动发送匿名的工作站日志来帮助改进此产品。这些日志用于解决工作站问题和发现错误趋势。",
- "show_labware_offset_snippets_description": "仅适用于需要在应用程序之外应用耗材校准数据的用户。启用后,在设置协议过程中可访问Jupyter Notebook和SSH的代码片段。",
+ "show_labware_offset_snippets_description": "仅适用于需要在应用程序外部应用实验室器具偏移数据的用户。启用后,在配置所有必需的偏移量后,可在协议设置期间使用 Jupyter Notebook 和 SSH 的代码片段。",
"something_seems_wrong": "您的移液器可能有问题。退出设置并联系支持人员以获取帮助。",
"storage_limit_reached_description": "您的工作站已达到可存储的快速移液数量上限。在创建新的快速移液之前,您必须删除一个现有的快速移液。",
"system_language_preferences_update_description": "您系统的语言最近已更新。您想使用更新后的语言作为应用的默认语言吗?",
diff --git a/app/src/assets/localization/zh/device_settings.json b/app/src/assets/localization/zh/device_settings.json
index 591c9cfcb7ad..cf4b25b02303 100644
--- a/app/src/assets/localization/zh/device_settings.json
+++ b/app/src/assets/localization/zh/device_settings.json
@@ -50,6 +50,7 @@
"clear_option_deck_calibration": "清除甲板校准",
"clear_option_gripper_calibration": "清除转板抓手校准",
"clear_option_gripper_offset_calibrations": "清除转板抓手校准",
+ "clear_option_labware_offsets": "清除耗材校准数据",
"clear_option_module_calibration": "清除模块校准",
"clear_option_pipette_calibrations": "清除移液器校准",
"clear_option_pipette_offset_calibrations": "清除移液器偏移校准",
@@ -231,6 +232,7 @@
"privacy": "隐私",
"problem_during_update": "此次更新耗时比平常要长。",
"proceed_without_updating": "跳过更新以继续",
+ "protocol_run_data": "协议运行数据",
"recalibrate_deck": "重新校准甲板",
"recalibrate_gripper": "重新校准转板抓手",
"recalibrate_module": "重新校准模块",
diff --git a/app/src/assets/localization/zh/error_recovery.json b/app/src/assets/localization/zh/error_recovery.json
index a740eac779dd..ca9d718c2379 100644
--- a/app/src/assets/localization/zh/error_recovery.json
+++ b/app/src/assets/localization/zh/error_recovery.json
@@ -21,23 +21,24 @@
"continue_to_drop_tip": "继续丢弃吸头",
"do_you_need_to_blowout": "首先,请问需要排出枪头内的液体吗?",
"door_open_robot_home": "在手动移动实验用品前,设备需要安全归位。",
+ "droplets_or_liquid_cause_failure": "吸头内的液滴或液体可能会导致液位检测失败",
"ensure_lw_is_accurately_placed": "确保实验耗材已准确放置在甲板槽中,防止进一步出现错误",
"error": "错误",
"error_details": "错误详情",
"error_on_robot": "{{robot}}上的错误",
"failed_dispense_step_not_completed": "中断运行的最后一步液体排出失败,恢复程序将不会继续运行这一步骤,请手动完成这一步的移液操作。运行将继续从下一步开始。继续之前,请关闭工作站门。",
"failed_step": "失败步骤",
- "first_is_gripper_holding_labware": "首先,抓扳手是否夹着实验耗材?",
+ "first_is_gripper_holding_labware": "首先,转板抓手是否夹着实验耗材?",
"go_back": "返回",
- "gripper_error": "抓扳手错误",
- "gripper_errors_occur_when": "当抓扳手停滞或与甲板上另一物体碰撞时,会发生抓扳手错误,这通常是由于实验耗材放置不当或实验耗材偏移不准确所致",
- "gripper_releasing_labware": "抓扳手正在释放实验耗材",
- "gripper_will_release_in_s": "抓扳手将在{{seconds}}秒后释放实验耗材",
+ "gripper_error": "转板抓手错误",
+ "gripper_errors_occur_when": "当转板抓手停滞或与甲板上另一物体碰撞时,会发生转板抓手错误,这通常是由于实验耗材放置不当或实验耗材偏移不准确所致",
+ "gripper_releasing_labware": "转板抓手正在释放实验耗材",
+ "gripper_will_release_in_s": "转板抓手将在{{seconds}}秒后释放实验耗材",
"home_and_retry": "归位并重试该步骤",
"home_gantry": "归位",
"home_now": "现在归位",
"homing_pipette_dangerous": "如果移液器中有液体,将{{mount}}移液器归位可能会损坏它。您必须在使用移液器之前取下所有吸头。",
- "if_issue_persists_gripper_error": "如果问题持续存在,请取消运行并重新运行抓扳手校准",
+ "if_issue_persists_gripper_error": "如果问题持续存在,请取消运行并重新运行转板抓手校准",
"if_issue_persists_overpressure": "如果问题持续存在,请取消运行并对协议进行必要的更改",
"if_issue_persists_tip_not_detected": "如果问题持续存在,请取消运行并启动实验耗材位置检查",
"if_tips_are_attached": "如果吸头还在移液器上,您可以在运行终止前选择吹出已吸取的液体并丢弃吸头。",
@@ -49,7 +50,8 @@
"labware_released_from_current_height": "将从当前高度释放实验耗材",
"launch_recovery_mode": "启动恢复模式",
"manually_fill_liquid_in_well": "手动填充孔位{{well}}中的液体",
- "manually_fill_well_and_skip": "手动填充孔位并跳到下一步",
+ "manually_fill_well_and_retry_new_tips": "手动填充好并用新吸头重试",
+ "manually_fill_well_and_retry_same_tips": "手动填充并用相同的提示重试",
"manually_move_lw_and_skip": "手动移动实验耗材并跳至下一步",
"manually_move_lw_on_deck": "手动移动实验耗材",
"manually_replace_lw_and_retry": "手动更换实验耗材并重试此步骤",
@@ -109,6 +111,7 @@
"stand_back_resuming": "请远离,正在恢复当前步骤",
"stand_back_retrying": "请远离,正在重试失败步骤",
"stand_back_skipping_to_next_step": "请远离,正在跳到下一步骤",
+ "static_meniscus_less_accurate": "如果使用静态弯月面移液,跳过液体存在检测时液体跟踪可能不太准确。",
"take_any_necessary_precautions": "在接住实验耗材之前,请采取必要的预防措施。确认后,夹爪将开始倒计时再释放。",
"take_necessary_actions": "首先,采取任何必要的行动,让工作站重新尝试失败的步骤。然后,在继续之前关闭工作站门。",
"take_necessary_actions_failed_pickup": "首先,采取任何必要的行动,让工作站重新尝试移液器拾取。然后,在继续之前关闭工作站门。",
@@ -119,6 +122,7 @@
"tip_drop_failed": "丢弃吸头失败",
"tip_not_detected": "未检测到吸头",
"tip_presence_errors_are_caused": "吸头识别错误通常是由实验器皿放置不当或器皿偏移不准确引起的。",
+ "use_dry_unused_tips": "使用干燥、未使用过的吸头以获得最佳效果",
"view_error_details": "查看错误详情",
"view_recovery_options": "查看恢复选项",
"you_can_still_drop_tips": "在继续选择吸头之前,您仍然可以丢弃移液器上现存的吸头。"
diff --git a/app/src/assets/localization/zh/labware_position_check.json b/app/src/assets/localization/zh/labware_position_check.json
index 3c66cff922dc..3ecbec490626 100644
--- a/app/src/assets/localization/zh/labware_position_check.json
+++ b/app/src/assets/localization/zh/labware_position_check.json
@@ -2,41 +2,83 @@
"adapter_in_mod_in_slot": "{{slot}}中{{module}}上的{{adapter}}",
"adapter_in_slot": "{{slot}}上的{{adapter}}",
"adapter_in_tc": "{{module}}上的{{adapter}}",
+ "add": "添加",
+ "add_a_default_offset": "添加耗材默认校准数据,并自动应用于甲板上所有位置",
+ "add_default_labware_offset": "添加耗材默认校准数据",
+ "adjust": "调整",
+ "adjust_applied_location_offset": "调整已应用的校准数据",
+ "adjust_default_labware_offset": "调整默认耗材校准数据",
+ "align_to_top_of_labware": "与耗材顶部对齐",
"all_modules_and_labware_from_protocol": "{{protocol_name}}协议中使用的所有模块与耗材 ",
+ "applied_location_offset_adjusted": "已调整位置校准数据",
+ "applied_location_offsets": "已应用的位置校准数据",
"applied_offset_data": "已应用的耗材校准数据",
"apply_offset_data": "应用耗材校准数据",
"apply_offsets": "应用校准数据",
"attach_probe": "安装校准探头",
"backmost": "最靠后的",
"calibration_probe": "校准探头",
+ "calibration_probe_not_detected": "未检测到校准探头",
+ "cancel": "取消",
+ "changing_default_not_update_hardcoded": "更改默认校准数据不会自动更新代码内的校准数据",
"check_item_in_location": "检查{{location}}上的{{item}}",
"check_labware_in_slot_title": "检查板位{{slot}}中的耗材{{labware_display_name}}",
"check_remaining_labware_with_primary_pipette_section": "使用{{primary_mount}}移液器和吸头检查剩余耗材",
"check_tip_location": "A1的吸头尖端",
"check_well_location": "耗材上的A1孔",
+ "clear_deck_all_lw_all_modules_from": "清除所有甲板位上的耗材和 {{slot}}上的所有模块",
+ "clear_deck_all_lw_leave_modules": "轻触所有甲板位上的耗材,保留模块不动",
"cli_ssh": "命令行界面 (SSH)",
"close_and_apply_offset_data": "关闭并保存耗材校准数据",
+ "complete": "完成",
+ "confirm": "确认",
"confirm_detached": "确认移除",
+ "confirm_go_back_without_saving": "请问您确定要不保存并返回耗材列表吗?",
"confirm_pick_up_tip_modal_title": "移液器是否成功拾取了吸头?",
"confirm_pick_up_tip_modal_try_again_text": "否,重试",
+ "confirm_placement": "确认放置",
"confirm_position_and_move": "确认位置,移动到板位{{next_slot}}",
"confirm_position_and_pick_up_tip": "确认位置,取吸头",
"confirm_position_and_return_tip": "确认位置,将吸头返回至板位{{next_slot}}并复位",
+ "confirm_removal": "确认移除",
+ "continue": "继续",
+ "default": "默认",
+ "default_labware_offset": "默认耗材校准数据",
+ "default_location_offset_added": "添加了默认耗材校准数据",
+ "default_location_offset_adjusted": "已调整默认耗材校准数据",
+ "default_offset_description": "除非您调整校准数据,否则放置的耗材都将使用默认数据。",
"detach_probe": "移除校准探头",
"ensure_nozzle_position_desktop": "请检查并确认{{tip_type}}位于{{item_location}}正上方并水平对齐。如果位置不正确,请使用下方的控制按键或键盘微调移液器直至完全对齐。",
"ensure_nozzle_position_odd": "请检查并确认{{tip_type}}位于{{item_location}}正上方并水平对齐。如果位置不正确,请点击移动移液器,然后微调移液器直至完全对齐。",
+ "ensure_probe_attached": "继续操作之前请确保其已正确连接。",
+ "ensure_probe_position_desktop": "确保校准探头居中并与 {{item_location}}齐平。否则,请使用下面的操作台或键盘来移动移液器,直到其正确对齐。",
+ "ensure_probe_position_odd": "确保校准探头居中并与 {{item_location}}齐平。否则,请点击 移动移液器 然后调整移液器与其正确对齐。",
+ "ensure_tip_rack_accurately_placed": "确保吸头盒按照上述步骤准确放置在甲板位中,防止损坏耗材。",
+ "exit": "退出",
"exit_screen_confirm_exit": "不保存耗材校准数据并退出",
"exit_screen_go_back": "返回耗材位置校准",
"exit_screen_subtitle": "如果您现在退出,所有耗材校准数据都将不保留,且无法恢复。",
"exit_screen_title": "确定要在完成耗材位置校准前退出?",
+ "failed_to_save_final_position": "保存最终位置失败",
"get_labware_offset_data": "获取耗材校准数据",
+ "go_back": "返回",
+ "go_back_confirmation": "您确定要不保存并返回耗材列表吗?",
+ "hardcoded": "代码",
+ "hardcoded_offsets_changed_in_python": "必须在 Python 协议中更改代码耗材校准数据",
+ "install_probe_1ch": "将校准探针从存放位置取出,确保套环已完全解锁。将探针牢牢地安装到移液器喷嘴上,直至完全卡住。旋转套环,锁定探针。轻触探头,确认已完全固定。探头应该绑定牢固。",
+ "install_probe_8ch": "将校准探头从存放位置取出,确保套环完全解锁。将探头安装到 最后面 移液器喷嘴处,往上尽可能地压紧。旋转套环,锁定探头。轻触探头,确认已完全固定。探头应该绑定牢固。",
+ "install_probe_96ch": "将校准探头从存放位置取出,确保套环完全解锁。将探头安装到 A1(左后角) 移液器喷嘴处,往上尽可能地压紧。旋转套环,锁定探头。轻触探头,确认已完全固定。探头应该绑定牢固。",
"jog_controls_adjustment": "需要进行调整吗?",
+ "jog_too_far": "移动过远?",
"jupyter_notebook": "Jupyter Notebook",
"labware": "耗材",
+ "labware_default_offset_added": "添加了默认校准数据{{labware}} ",
+ "labware_default_offset_updated": "{{labware}} 默认校准数据已更新",
"labware_display_location_text": "甲板板位{{slot}}",
"labware_offset": "耗材校准数据",
"labware_offset_data": "耗材校准数据",
"labware_offsets_deleted_warning": "一旦开始耗材位置校准,之前创建的耗材校准数据将会丢失。",
+ "labware_offsets_saved": "{{labware}} 校准数据已保存",
"labware_offsets_summary_labware": "耗材",
"labware_offsets_summary_location": "位置",
"labware_offsets_summary_offset": "耗材校准数据",
@@ -59,25 +101,49 @@
"labware_step_detail_modal_nozzle_or_tip_image_3_text": "如遇觉得难以判断,请在吸嘴与吸头之间放入一张常规纸张。当这张纸能刚好卡在两者之间时,可确认高度位置。",
"labware_step_detail_tiprack": "移液器吸嘴应居中于{{tiprack_name}}的A1位置上方,并且与吸头顶部水平对齐。",
"labware_step_detail_tiprack_plural": "移液器吸嘴应位于{{tiprack_name}}第一列正上方并居中对齐,并且与吸头顶部水平对齐。",
+ "labware_type": "耗材类型",
"learn_more": "了解更多",
+ "legacy_calibration_probe": "校准探头",
+ "legacy_clear_all_slots": "清除所有甲板位上的耗材,保留模块在原位",
+ "legacy_clear_all_slots_odd": "清除所有甲板位上的耗材",
+ "legacy_install_probe": "将校准探头从存放位置取出,确保套环已完全解锁。将探头安装到 {{location}} 移液器喷嘴处,往上尽可能地压紧。旋转套环,锁定探头。轻触探头,确认已完全固定。",
+ "legacy_labware_offset_data": "耗材校准数据",
+ "legacy_labware_position_check_description": "耗材校准是一种引导式工作流程,用于校准甲板位上的耗材,以提高协议运行的精确度。耗材校准首先校准吸头架位置,然后校准协议中使用的所有其他耗材。",
+ "legacy_no_offset_data": "无可用校准数据",
"location": "位置",
+ "location_header": "位置",
+ "lpc_complete": "耗材校准完成",
"lpc_complete_summary_screen_heading": "耗材位置校准完成",
+ "manual": "手动地",
+ "modify_hardcoded_offsets_in_protocol": "代码校准数据必须要在您的Python协议文件中进行修改",
"module_display_location_text": "{{moduleName}}位于甲板板位{{slot}}",
"module_in_slot": "{{module}}位于板位{{slot}}",
+ "move_gantry_to_front": "将龙门架移至前端",
"move_pipette": "移动移液器",
"move_to_a1_position": "将移液器移到A1位置并对齐",
"moving_to_slot_title": "正在移动到板位{{slot}}",
+ "need_help": "请问需要帮助吗?",
"new_labware_offset_data": "新的耗材校准数据",
+ "next_place_a_full_tip_rack_in_location": "接下来,放置 完整的 {{tip_rack}} 到 {{location}}",
+ "next_place_labware_in_location": "接下来,放置 {{labware}} 到 {{location}}",
"ninety_six_probe_location": "A1(左上角)",
"no_labware_offsets": "无耗材校准数据",
"no_offset_data": "没有可用的校准数据",
"no_offset_data_available": "没有可用的耗材校准数据",
"no_offset_data_on_robot": "这轮运行中此工作站没有可用的耗材校准数据。",
+ "not_applicable": "N/A",
+ "num_missing_offsets": "{{num}} 缺失校准数据",
+ "num_offsets": "{{num}} 校准数据",
+ "offset_values": "X {{x}},Y {{y}},Z {{z}}",
"offsets": "校准数据",
+ "offsets_already_applied": "耗材校准数据已应用",
+ "one_missing_offset": "缺少 1 个校准数据",
+ "one_offset": "1 个校准数据",
"pick_up_tip_from_rack_in_location": "从位于{{location}}的吸头盒上拾取吸头",
"picking_up_tip_title": "在板位{{slot}}拾取吸头",
"pipette_nozzle": "离您最远的移液器吸嘴",
"place_a_full_tip_rack_in_location": "将装满吸头的{{tip_rack}}放入{{location}}",
+ "place_a_full_tip_rack_in_location_96ch_default": "放置完整的 {{tip_rack}} 到 {{location}} ,不要放置吸头架适配器",
"place_labware_in_adapter_in_location": "在{{location}}先放置{{adapter}},再放置{{labware}}",
"place_labware_in_location": "将{{labware}}放入{{location}}",
"place_modules": "在甲板上放置模块",
@@ -88,6 +154,8 @@
"remove_calibration_probe": "移除校准探头",
"remove_probe": "将校准探头解锁,将其从吸嘴上拆下,并放回存储位置。",
"remove_probe_before_exit": "退出前请移除校准探头",
+ "remove_probe_before_exiting_error": "退出前,请先移除校准探头。然后,重新启动耗材校准,然后继续。",
+ "reset_to_default": "恢复默认设置",
"return_tip_rack_to_location": "将吸头盒放回{{location}}",
"return_tip_section": "放回吸头",
"returning_tip_title": "正在板位{{slot}}放回吸头",
@@ -96,17 +164,30 @@
"robot_has_offsets_from_previous_runs": "此工作站具有本协议中所用耗材的校准数据。如果您应用了这些校准数据,仍可通过耗材位置校准程序进行调整。",
"robot_in_motion": "工作站正在运行,请远离。",
"run": "运行",
+ "save": "保存",
"secondary_pipette_tipracks_section": "使用{{secondary_mount}}移液器检查吸头盒",
"see_how_offsets_work": "了解耗材校准的工作原理",
+ "select_labware_to_view_data": "选择一个耗材来查看其存储的校准数据。",
+ "skip": "跳过",
"slot": "板位{{slotName}}",
+ "slot_applied_location_offset_updated": "{{slot}} 的位置数据已更新",
+ "slot_in_module_applied_location_offset_updated": "在{{slot}} 上的 {{module}} 位置校准数据已更新",
"slot_location": "板位位置",
"slot_name": "板位{{slotName}}",
+ "something_went_wrong": "出了点问题",
+ "specific_slots_can_be_adjusted": "可以根据需要调整特定的甲板位置",
+ "start_over": "重新开始",
"start_position_check": "开始耗材位置校准程序,移至板位{{initial_labware_slot}}",
+ "store_probe": "退出之前,请解锁校准探头,将其从移液器下方取出,然后将其放回存放位置。",
"stored_offset_data": "应用已存储的耗材校准数据?",
"stored_offsets_for_this_protocol": "适用于本协议的已存储耗材校准数据",
"table_view": "表格视图",
"tip_rack": "吸头盒",
+ "total_offsets": "全部校准数据",
+ "try_again": "再次尝试",
+ "unsaved_changes_will_be_lost": "未保存的更改将会丢失",
"view_current_offsets": "查看当前校准数据",
"view_data": "查看数据",
+ "view_labware_list": "查看耗材列表",
"what_is_labware_offset_data": "什么是耗材校准数据?"
}
diff --git a/app/src/assets/localization/zh/protocol_setup.json b/app/src/assets/localization/zh/protocol_setup.json
index bd936d8540a1..47383f748bf6 100644
--- a/app/src/assets/localization/zh/protocol_setup.json
+++ b/app/src/assets/localization/zh/protocol_setup.json
@@ -82,7 +82,6 @@
"deck_conflict_info": "通过移除位置 {{cutout}} 中的 {{currentFixture}} 来更新甲板配置。从甲板配置中移除对应装置或更新协议。",
"deck_conflict_info_thermocycler": "通过移除位置 A1 和 B1 中的固定装置来更新甲板配置。从甲板配置中移除对应装置或更新协议。",
"deck_hardware": "甲板硬件",
- "deck_hardware_ready": "甲板硬件准备",
"deck_map": "甲板布局图",
"default_values": "默认值",
"download_files": "下载文件",
@@ -112,6 +111,7 @@
"instrument_calibrations_missing": "缺少{{count}}个校准",
"instrument_calibrations_missing_plural": "缺少{{count}}个校准",
"instruments": "硬件",
+ "instruments_attached": "已附仪器",
"instruments_connected": "已连接{{count}}个硬件",
"instruments_connected_plural": "已连接{{count}}个硬件",
"jupyter_notebook": "Jupyter Notebook",
@@ -183,8 +183,10 @@
"module_setup_step_title": "甲板硬件",
"module_slot_location": "{{slotName}}号板位,{{moduleName}}",
"modules": "模块",
+ "modules_and_fixtures_ready": "模块和固定装置已准备就绪",
"modules_connected": "连接了{{count}}个模块",
"modules_connected_plural": "连接了{{count}}个模块",
+ "modules_ready": "模块已准备就绪",
"modules_setup_step_title": "模块设置",
"mount": "{{mount}}安装支架",
"mount_title": "{{mount}}安装支架:",
@@ -240,6 +242,7 @@
"on_adapter": "在{{adapterName}}上",
"on_adapter_in_mod": "在{{moduleName}}中的{{adapterName}}上",
"on_deck": "在甲板上",
+ "one_missing_offset": "缺少 1 个校准数据",
"opening": "打开中...",
"parameters": "参数",
"pipette_mismatch": "移液器型号不匹配。",
diff --git a/app/src/redux/analytics/constants.ts b/app/src/redux/analytics/constants.ts
index 4d986162b31f..945d37c4681f 100644
--- a/app/src/redux/analytics/constants.ts
+++ b/app/src/redux/analytics/constants.ts
@@ -120,8 +120,7 @@ export const ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS: 'languageUpdatedDe
* LPC Analytics
*/
-export const ANALYTICS_LPC_ANALYSIS_KIND: 'analytics:lpcAnalysisKind' =
- 'analytics:lpcAnalysisKind'
+export const ANALYTICS_LPC_ANALYSIS_KIND: 'lpcAnalysisKind' = 'lpcAnalysisKind'
/**
* Module Actions Analytics
diff --git a/shared-data/labware/definitions/2/ev_resin_tips_flex_96_tiprack_adapter/1.json b/shared-data/labware/definitions/2/ev_resin_tips_flex_96_tiprack_adapter/1.json
index 25d2e12b366e..edbb53416762 100644
--- a/shared-data/labware/definitions/2/ev_resin_tips_flex_96_tiprack_adapter/1.json
+++ b/shared-data/labware/definitions/2/ev_resin_tips_flex_96_tiprack_adapter/1.json
@@ -8,7 +8,7 @@
]
},
"metadata": {
- "displayName": "Opentrons Flex EV Resin Tips Tall Adapter for Third-party Evotips in Flex 96 Tip Rack Adapter",
+ "displayName": "Opentrons Flex EV Resin Tips Tall Adapter for Third-party Evotips in 96 Tip Rack Adapter",
"displayCategory": "adapter",
"displayVolumeUnits": "µL",
"tags": []