diff --git a/api/tests/opentrons/hardware_control/test_instruments.py b/api/tests/opentrons/hardware_control/test_instruments.py index 7db8a5303ee..d3907451717 100644 --- a/api/tests/opentrons/hardware_control/test_instruments.py +++ b/api/tests/opentrons/hardware_control/test_instruments.py @@ -263,7 +263,7 @@ def fake_func2(mount, value): {types.Mount.LEFT: "p10_single", types.Mount.RIGHT: "p300_single_gen2"} ) attached = sim.attached_instruments - assert attached[types.Mount.LEFT]["model"] == "p10_single_v1" + assert attached[types.Mount.LEFT]["model"] == "p10_single_v1.5" assert attached[types.Mount.LEFT]["name"] == "p10_single" steps_mm_calls = [mock.call({"B": 768}), mock.call({"C": 3200})] @@ -291,7 +291,7 @@ def fake_func2(mount, value): # If we use prefixes, that should work too await sim.cache_instruments({types.Mount.RIGHT: "p300_single"}) attached = sim.attached_instruments - assert attached[types.Mount.RIGHT]["model"] == "p300_single_v1" + assert attached[types.Mount.RIGHT]["model"] == "p300_single_v1.5" assert attached[types.Mount.RIGHT]["name"] == "p300_single" # If we specify instruments at init time, we should get them without # passing an expectation diff --git a/api/tests/opentrons/protocol_api_old/test_context.py b/api/tests/opentrons/protocol_api_old/test_context.py index db45d3af6c6..c356c477f7f 100644 --- a/api/tests/opentrons/protocol_api_old/test_context.py +++ b/api/tests/opentrons/protocol_api_old/test_context.py @@ -85,7 +85,7 @@ async def test_motion(ctx, hardware): old_pos[Axis.X] = 0.0 old_pos[Axis.Y] = 0.0 old_pos[Axis.A] = 0.0 - old_pos[Axis.C] = 2.0 + old_pos[Axis.C] = 2.5 assert await hardware.current_position(instr._core.get_mount()) == pytest.approx( old_pos ) diff --git a/api/tests/opentrons/protocol_engine/resources/test_pipette_data_provider.py b/api/tests/opentrons/protocol_engine/resources/test_pipette_data_provider.py index 3463c574bbc..8f37ec6d2ff 100644 --- a/api/tests/opentrons/protocol_engine/resources/test_pipette_data_provider.py +++ b/api/tests/opentrons/protocol_engine/resources/test_pipette_data_provider.py @@ -32,7 +32,7 @@ def test_get_virtual_pipette_static_config( ) assert result == LoadedStaticPipetteData( - model="p20_single_v2.0", + model="p20_single_v2.2", display_name="P20 Single-Channel GEN2", min_volume=1, max_volume=20.0, @@ -69,7 +69,7 @@ def test_configure_virtual_pipette_for_volume( PipetteNameType.P50_SINGLE_FLEX.value, "my-pipette" ) assert result1 == LoadedStaticPipetteData( - model="p50_single_v3.0", + model="p50_single_v3.6", display_name="Flex 1-Channel 50 μL", min_volume=5, max_volume=50.0, @@ -77,9 +77,9 @@ def test_configure_virtual_pipette_for_volume( nozzle_offset_z=-259.15, home_position=230.15, flow_rates=FlowRates( - default_blow_out={"2.14": 4.0}, - default_aspirate={"2.14": 8.0}, - default_dispense={"2.14": 8.0}, + default_blow_out={"2.14": 57}, + default_aspirate={"2.14": 35}, + default_dispense={"2.14": 57}, ), tip_configuration_lookup_table=result1.tip_configuration_lookup_table, nominal_tip_overlap=result1.nominal_tip_overlap, @@ -95,7 +95,7 @@ def test_configure_virtual_pipette_for_volume( PipetteNameType.P50_SINGLE_FLEX.value, "my-pipette" ) assert result2 == LoadedStaticPipetteData( - model="p50_single_v3.0", + model="p50_single_v3.6", display_name="Flex 1-Channel 50 μL", min_volume=1, max_volume=30, @@ -103,9 +103,9 @@ def test_configure_virtual_pipette_for_volume( nozzle_offset_z=-259.15, home_position=230.15, flow_rates=FlowRates( - default_blow_out={"2.14": 4.0}, - default_aspirate={"2.14": 8.0}, - default_dispense={"2.14": 8.0}, + default_blow_out={"2.14": 26.7}, + default_aspirate={"2.14": 26.7}, + default_dispense={"2.14": 26.7}, ), tip_configuration_lookup_table=result2.tip_configuration_lookup_table, nominal_tip_overlap=result2.nominal_tip_overlap, @@ -165,19 +165,19 @@ def test_load_virtual_pipette_nozzle_layout( assert result.configuration.value == "FULL" subject_instance.configure_virtual_pipette_nozzle_layout( - "my-96-pipette", "p1000_96_v3.5", "A1", "A12", "A1" + "my-96-pipette", "p1000_96_v3.6", "A1", "A12", "A1" ) result = subject_instance.get_nozzle_layout_for_pipette("my-96-pipette") assert result.configuration.value == "ROW" subject_instance.configure_virtual_pipette_nozzle_layout( - "my-96-pipette", "p1000_96_v3.5", "A1", "A1" + "my-96-pipette", "p1000_96_v3.6", "A1", "A1" ) result = subject_instance.get_nozzle_layout_for_pipette("my-96-pipette") assert result.configuration.value == "SINGLE" subject_instance.configure_virtual_pipette_nozzle_layout( - "my-96-pipette", "p1000_96_v3.5", "A1", "H1" + "my-96-pipette", "p1000_96_v3.6", "A1", "H1" ) result = subject_instance.get_nozzle_layout_for_pipette("my-96-pipette") assert result.configuration.value == "COLUMN" diff --git a/shared-data/pipette/definitions/2/liquid/single_channel/p50/lowVolumeDefault/3_6.json b/shared-data/pipette/definitions/2/liquid/single_channel/p50/lowVolumeDefault/3_6.json index 639921290e8..06b676081ca 100644 --- a/shared-data/pipette/definitions/2/liquid/single_channel/p50/lowVolumeDefault/3_6.json +++ b/shared-data/pipette/definitions/2/liquid/single_channel/p50/lowVolumeDefault/3_6.json @@ -5,15 +5,15 @@ "uiMaxFlowRate": 26.7, "defaultAspirateFlowRate": { "default": 35, - "valuesByApiLevel": { "2.14": 35 } + "valuesByApiLevel": { "2.14": 26.7 } }, "defaultDispenseFlowRate": { "default": 57, - "valuesByApiLevel": { "2.14": 57 } + "valuesByApiLevel": { "2.14": 26.7 } }, "defaultBlowOutFlowRate": { "default": 57, - "valuesByApiLevel": { "2.14": 57 } + "valuesByApiLevel": { "2.14": 26.7 } }, "defaultFlowAcceleration": 1200.0, "defaultTipLength": 57.9, diff --git a/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py b/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py index e2122ed35fd..9853d58b4ae 100644 --- a/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py +++ b/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py @@ -1,6 +1,9 @@ import re -from typing import List, Optional, Union, cast +from functools import lru_cache +from typing import List, Optional, Union, cast, Literal, Tuple +from opentrons_shared_data import get_shared_data_root from .dev_types import PipetteModel, PipetteName + from .types import ( PipetteChannelType, PipetteModelType, @@ -106,26 +109,78 @@ def version_from_string(version: str) -> PipetteVersionType: return PipetteVersionType(major, minor) -def version_from_generation(pipette_name_list: List[str]) -> PipetteVersionType: - """Convert a string generation name to a py:obj:PipetteVersionType. +def get_channel_from_pipette_name(pipette_name_tuple: Tuple[str, ...]) -> str: + if "single" in pipette_name_tuple: + return "single_channel" + elif "96" in pipette_name_tuple: + return "ninety_six_channel" + else: + return "eight_channel" + + +def get_major_version_from_pipette_name( + pipette_name_tuple: Tuple[str, ...], +) -> Literal[1, 2, 3]: + # special-casing for 96-channel to return version 3 + if ( + "flex" in pipette_name_tuple + or "gen3" in pipette_name_tuple + or "96" in pipette_name_tuple + ): + return 3 + elif "gen2" in pipette_name_tuple: + return 2 + else: + return 1 + + +@lru_cache(4) +def version_from_generation(pipette_name_tuple: Tuple[str, ...]) -> PipetteVersionType: + """Convert pipetteName to a py:obj:PipetteVersionType - Pipette generations are strings in the format of "gen1" or "gen2", and - usually associated withe :py:data:PipetteName. + Given the pipette_name_tuple, cycle through each definition file path + and find the latest version (major and minor version combined) that + exists and return that version. Args: - pipette_name_list (List[str]): A list of strings from the separated by `_` - py:data:PipetteName. + pipette_name_tuple (Tuple[str, ...]): A tuple of strings from the separated + by `_` py:data:PipetteName. Returns: PipetteVersionType: A pipette version object. - """ - if "flex" in pipette_name_list or "gen3" in pipette_name_list: - return PipetteVersionType(3, 0) - elif "gen2" in pipette_name_list: - return PipetteVersionType(2, 0) - else: - return PipetteVersionType(1, 0) + major_version_from_pipette_name = get_major_version_from_pipette_name( + pipette_name_tuple + ) + model_from_pipette_name = pipette_name_tuple[0] + channel_from_pipette_name = get_channel_from_pipette_name(pipette_name_tuple) + + paths_to_validate = ( + get_shared_data_root() / "pipette" / "definitions" / "2" / "general" + ) + version_paths = ( + paths_to_validate / channel_from_pipette_name / model_from_pipette_name + ) + + highest_minor_version: PipetteModelMinorVersionType = 0 + + for version_file in version_paths.iterdir(): + version_list = version_file.stem.split("_") + major_version = version_list[0] + minor_version = version_list[1] + + # Check if the major version matches the expected major version + if major_version == str(major_version_from_pipette_name): + minor_version_int = int(minor_version) + minor_version_lit: PipetteModelMinorVersionType = cast( + PipetteModelMinorVersionType, minor_version_int + ) + + # Update the highest minor version if this version is higher + if highest_minor_version < minor_version_lit: + highest_minor_version = minor_version_lit + + return PipetteVersionType(major_version_from_pipette_name, highest_minor_version) def generation_from_string(pipette_name_list: List[str]) -> PipetteGenerationType: @@ -194,7 +249,12 @@ def convert_pipette_name( if provided_version: version = version_from_string(provided_version) else: - version = version_from_generation(split_pipette_name) + pipette_name_tuple: Tuple[str, str, str] = ( + split_pipette_name[0], + split_pipette_name[1], + split_pipette_name[2] if len(split_pipette_name) > 2 else "", + ) + version = version_from_generation(pipette_name_tuple) pipette_type = PipetteModelType[split_pipette_name[0]] diff --git a/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py b/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py index 2cb589ba636..a62880429e1 100644 --- a/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py +++ b/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py @@ -103,7 +103,7 @@ def test_convert_pipette_model_provided_version( pc.PipetteModelVersionType( PipetteModelType.p1000, PipetteChannelType.EIGHT_CHANNEL, - PipetteVersionType(3, 0), + PipetteVersionType(3, 5), ), ], [ @@ -111,7 +111,7 @@ def test_convert_pipette_model_provided_version( pc.PipetteModelVersionType( PipetteModelType.p1000, PipetteChannelType.NINETY_SIX_CHANNEL, - PipetteVersionType(1, 0), + PipetteVersionType(3, 6), ), ], ],