diff --git a/api/src/opentrons/protocol_engine/commands/__init__.py b/api/src/opentrons/protocol_engine/commands/__init__.py index b8ad7ab0b57..3be0917b835 100644 --- a/api/src/opentrons/protocol_engine/commands/__init__.py +++ b/api/src/opentrons/protocol_engine/commands/__init__.py @@ -153,7 +153,6 @@ LoadPipetteCreate, LoadPipetteResult, LoadPipetteCommandType, - LoadPipettePrivateResult, ) from .move_labware import ( diff --git a/api/src/opentrons/protocol_engine/commands/command_unions.py b/api/src/opentrons/protocol_engine/commands/command_unions.py index 7623cc09f68..c86724c1124 100644 --- a/api/src/opentrons/protocol_engine/commands/command_unions.py +++ b/api/src/opentrons/protocol_engine/commands/command_unions.py @@ -1,7 +1,7 @@ """Union types of concrete command definitions.""" from collections.abc import Collection -from typing import Annotated, Type, Union, get_type_hints +from typing import Annotated, Literal, Type, Union, get_type_hints from pydantic import Field @@ -141,7 +141,6 @@ LoadPipetteCreate, LoadPipetteResult, LoadPipetteCommandType, - LoadPipettePrivateResult, ) from .move_labware import ( @@ -272,7 +271,6 @@ ConfigureForVolumeCreate, ConfigureForVolumeResult, ConfigureForVolumeCommandType, - ConfigureForVolumePrivateResult, ) from .prepare_to_aspirate import ( @@ -701,15 +699,9 @@ unsafe.UnsafeUngripLabwareResult, ] -# todo(mm, 2024-06-12): Ideally, command return types would have specific -# CommandPrivateResults paired with specific CommandResults. For example, -# a TouchTipResult can never be paired with a LoadPipettePrivateResult in practice, -# and ideally our types would reflect that. -CommandPrivateResult = Union[ - None, - LoadPipettePrivateResult, - ConfigureForVolumePrivateResult, -] +# todo(mm, 2024-10-28): This has been obsoleted by StateUpdate. Delete this. +# https://opentrons.atlassian.net/browse/EXEC-639 +CommandPrivateResult = Literal[None] # All `DefinedErrorData`s that implementations will actually return in practice. CommandDefinedErrorData = Union[ diff --git a/api/src/opentrons/protocol_engine/commands/configure_for_volume.py b/api/src/opentrons/protocol_engine/commands/configure_for_volume.py index 93a56ca8805..f4caee72c7f 100644 --- a/api/src/opentrons/protocol_engine/commands/configure_for_volume.py +++ b/api/src/opentrons/protocol_engine/commands/configure_for_volume.py @@ -7,7 +7,6 @@ from .pipetting_common import PipetteIdMixin from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData from ..errors.error_occurrence import ErrorOccurrence -from .configuring_common import PipetteConfigUpdateResultMixin from ..state.update_types import StateUpdate if TYPE_CHECKING: @@ -35,12 +34,6 @@ class ConfigureForVolumeParams(PipetteIdMixin): ) -class ConfigureForVolumePrivateResult(PipetteConfigUpdateResultMixin): - """Result sent to the store but not serialized.""" - - pass - - class ConfigureForVolumeResult(BaseModel): """Result data from execution of an ConfigureForVolume command.""" @@ -50,7 +43,7 @@ class ConfigureForVolumeResult(BaseModel): class ConfigureForVolumeImplementation( AbstractCommandImpl[ ConfigureForVolumeParams, - SuccessData[ConfigureForVolumeResult, ConfigureForVolumePrivateResult], + SuccessData[ConfigureForVolumeResult, None], ] ): """Configure for volume command implementation.""" @@ -60,7 +53,7 @@ def __init__(self, equipment: EquipmentHandler, **kwargs: object) -> None: async def execute( self, params: ConfigureForVolumeParams - ) -> SuccessData[ConfigureForVolumeResult, ConfigureForVolumePrivateResult]: + ) -> SuccessData[ConfigureForVolumeResult, None]: """Check that requested pipette can be configured for the given volume.""" pipette_result = await self._equipment.configure_for_volume( pipette_id=params.pipetteId, @@ -77,11 +70,7 @@ async def execute( return SuccessData( public=ConfigureForVolumeResult(), - private=ConfigureForVolumePrivateResult( - pipette_id=pipette_result.pipette_id, - serial_number=pipette_result.serial_number, - config=pipette_result.static_config, - ), + private=None, state_update=state_update, ) diff --git a/api/src/opentrons/protocol_engine/commands/configuring_common.py b/api/src/opentrons/protocol_engine/commands/configuring_common.py deleted file mode 100644 index f69cf41fef6..00000000000 --- a/api/src/opentrons/protocol_engine/commands/configuring_common.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Common configuration command base models.""" - -from dataclasses import dataclass -from ..resources import pipette_data_provider - - -@dataclass -class PipetteConfigUpdateResultMixin: - """A mixin-suitable model for adding pipette config to private results.""" - - pipette_id: str - serial_number: str - config: pipette_data_provider.LoadedStaticPipetteData diff --git a/api/src/opentrons/protocol_engine/commands/load_pipette.py b/api/src/opentrons/protocol_engine/commands/load_pipette.py index 5961272ae7c..3d9bdd83050 100644 --- a/api/src/opentrons/protocol_engine/commands/load_pipette.py +++ b/api/src/opentrons/protocol_engine/commands/load_pipette.py @@ -17,7 +17,6 @@ from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData from ..errors.error_occurrence import ErrorOccurrence -from .configuring_common import PipetteConfigUpdateResultMixin from ..errors import InvalidSpecificationForRobotTypeError, InvalidLoadPipetteSpecsError if TYPE_CHECKING: @@ -28,12 +27,6 @@ LoadPipetteCommandType = Literal["loadPipette"] -class LoadPipettePrivateResult(PipetteConfigUpdateResultMixin): - """The not-to-be-exposed results of a load pipette call.""" - - ... - - class LoadPipetteParams(BaseModel): """Payload needed to load a pipette on to a mount.""" @@ -73,9 +66,7 @@ class LoadPipetteResult(BaseModel): class LoadPipetteImplementation( - AbstractCommandImpl[ - LoadPipetteParams, SuccessData[LoadPipetteResult, LoadPipettePrivateResult] - ] + AbstractCommandImpl[LoadPipetteParams, SuccessData[LoadPipetteResult, None]] ): """Load pipette command implementation.""" @@ -87,7 +78,7 @@ def __init__( async def execute( self, params: LoadPipetteParams - ) -> SuccessData[LoadPipetteResult, LoadPipettePrivateResult]: + ) -> SuccessData[LoadPipetteResult, None]: """Check that requested pipette is attached and assign its identifier.""" pipette_generation = convert_to_pipette_name_type( params.pipetteName.value @@ -139,11 +130,7 @@ async def execute( return SuccessData( public=LoadPipetteResult(pipetteId=loaded_pipette.pipette_id), - private=LoadPipettePrivateResult( - pipette_id=loaded_pipette.pipette_id, - serial_number=loaded_pipette.serial_number, - config=loaded_pipette.static_config, - ), + private=None, state_update=state_update, ) diff --git a/api/src/opentrons/protocol_engine/state/tips.py b/api/src/opentrons/protocol_engine/state/tips.py index 7427c78ac4c..1ac3e91f795 100644 --- a/api/src/opentrons/protocol_engine/state/tips.py +++ b/api/src/opentrons/protocol_engine/state/tips.py @@ -6,12 +6,7 @@ from opentrons.protocol_engine.state import update_types from ._abstract_store import HasState, HandlesActions -from ..actions import Action, SucceedCommandAction, ResetTipsAction, get_state_updates -from ..commands import ( - Command, - LoadLabwareResult, -) -from ..commands.configuring_common import PipetteConfigUpdateResultMixin +from ..actions import Action, ResetTipsAction, get_state_updates from opentrons.hardware_control.nozzle_manager import NozzleMap @@ -66,19 +61,7 @@ def handle_action(self, action: Action) -> None: for state_update in get_state_updates(action): self._handle_state_update(state_update) - if isinstance(action, SucceedCommandAction): - if isinstance(action.private_result, PipetteConfigUpdateResultMixin): - pipette_id = action.private_result.pipette_id - config = action.private_result.config - self._state.pipette_info_by_pipette_id[pipette_id] = _PipetteInfo( - channels=config.channels, - active_channels=config.channels, - nozzle_map=config.nozzle_map, - ) - - self._handle_succeeded_command(action.command) - - elif isinstance(action, ResetTipsAction): + if isinstance(action, ResetTipsAction): labware_id = action.labware_id for well_name in self._state.tips_by_labware_id[labware_id].keys(): @@ -86,23 +69,16 @@ def handle_action(self, action: Action) -> None: well_name ] = TipRackWellState.CLEAN - def _handle_succeeded_command(self, command: Command) -> None: - if ( - isinstance(command.result, LoadLabwareResult) - and command.result.definition.parameters.isTiprack - ): - labware_id = command.result.labwareId - definition = command.result.definition - self._state.tips_by_labware_id[labware_id] = { - well_name: TipRackWellState.CLEAN - for column in definition.ordering - for well_name in column - } - self._state.column_by_labware_id[labware_id] = [ - column for column in definition.ordering - ] - def _handle_state_update(self, state_update: update_types.StateUpdate) -> None: + if state_update.pipette_config != update_types.NO_CHANGE: + self._state.pipette_info_by_pipette_id[ + state_update.pipette_config.pipette_id + ] = _PipetteInfo( + channels=state_update.pipette_config.config.channels, + active_channels=state_update.pipette_config.config.channels, + nozzle_map=state_update.pipette_config.config.nozzle_map, + ) + if state_update.tips_used != update_types.NO_CHANGE: self._set_used_tips( pipette_id=state_update.tips_used.pipette_id, @@ -119,6 +95,19 @@ def _handle_state_update(self, state_update: update_types.StateUpdate) -> None: ) pipette_info.nozzle_map = state_update.pipette_nozzle_map.nozzle_map + if state_update.loaded_labware != update_types.NO_CHANGE: + labware_id = state_update.loaded_labware.labware_id + definition = state_update.loaded_labware.definition + if definition.parameters.isTiprack: + self._state.tips_by_labware_id[labware_id] = { + well_name: TipRackWellState.CLEAN + for column in definition.ordering + for well_name in column + } + self._state.column_by_labware_id[labware_id] = [ + column for column in definition.ordering + ] + def _set_used_tips( # noqa: C901 self, pipette_id: str, well_name: str, labware_id: str ) -> None: diff --git a/api/src/opentrons/protocol_runner/legacy_command_mapper.py b/api/src/opentrons/protocol_runner/legacy_command_mapper.py index 686560c1ca2..2a1ef28a359 100644 --- a/api/src/opentrons/protocol_runner/legacy_command_mapper.py +++ b/api/src/opentrons/protocol_runner/legacy_command_mapper.py @@ -731,7 +731,14 @@ def _map_instrument_load( result=pe_commands.LoadPipetteResult.construct(pipetteId=pipette_id), ) serial = instrument_load_info.pipette_dict.get("pipette_id", None) or "" - pipette_config_result = pe_commands.LoadPipettePrivateResult( + state_update = StateUpdate() + state_update.set_load_pipette( + pipette_id=pipette_id, + mount=succeeded_command.params.mount, + pipette_name=succeeded_command.params.pipetteName, + liquid_presence_detection=succeeded_command.params.liquidPresenceDetection, + ) + state_update.update_pipette_config( pipette_id=pipette_id, serial_number=serial, config=pipette_data_provider.get_pipette_static_config( @@ -754,16 +761,10 @@ def _map_instrument_load( # We just set this above, so we know it's not None. started_at=succeeded_command.startedAt, # type: ignore[arg-type] ) - state_update = StateUpdate() - state_update.set_load_pipette( - pipette_id=pipette_id, - mount=succeeded_command.params.mount, - pipette_name=succeeded_command.params.pipetteName, - liquid_presence_detection=succeeded_command.params.liquidPresenceDetection, - ) + succeed_action = pe_actions.SucceedCommandAction( command=succeeded_command, - private_result=pipette_config_result, + private_result=None, state_update=state_update, ) diff --git a/api/tests/opentrons/protocol_engine/commands/test_configure_for_volume.py b/api/tests/opentrons/protocol_engine/commands/test_configure_for_volume.py index 2279f2a0ebf..59e65ede181 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_configure_for_volume.py +++ b/api/tests/opentrons/protocol_engine/commands/test_configure_for_volume.py @@ -19,7 +19,6 @@ from opentrons.protocol_engine.commands.configure_for_volume import ( ConfigureForVolumeParams, ConfigureForVolumeResult, - ConfigureForVolumePrivateResult, ConfigureForVolumeImplementation, ) from opentrons_shared_data.pipette.types import PipetteNameType @@ -85,9 +84,7 @@ async def test_configure_for_volume_implementation( assert result == SuccessData( public=ConfigureForVolumeResult(), - private=ConfigureForVolumePrivateResult( - pipette_id="pipette-id", serial_number="some number", config=config - ), + private=None, state_update=StateUpdate( pipette_config=PipetteConfigUpdate( pipette_id="pipette-id", serial_number="some number", config=config diff --git a/api/tests/opentrons/protocol_engine/commands/test_load_pipette.py b/api/tests/opentrons/protocol_engine/commands/test_load_pipette.py index 5884e015342..4d757f49a54 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_load_pipette.py +++ b/api/tests/opentrons/protocol_engine/commands/test_load_pipette.py @@ -22,7 +22,6 @@ from opentrons.protocol_engine.commands.load_pipette import ( LoadPipetteParams, LoadPipetteResult, - LoadPipettePrivateResult, LoadPipetteImplementation, ) from ..pipette_fixtures import get_default_nozzle_map @@ -90,9 +89,7 @@ async def test_load_pipette_implementation( assert result == SuccessData( public=LoadPipetteResult(pipetteId="some id"), - private=LoadPipettePrivateResult( - pipette_id="some id", serial_number="some-serial-number", config=config_data - ), + private=None, state_update=StateUpdate( loaded_pipette=LoadPipetteUpdate( pipette_name=PipetteNameType.P300_SINGLE, @@ -158,9 +155,7 @@ async def test_load_pipette_implementation_96_channel( assert result == SuccessData( public=LoadPipetteResult(pipetteId="pipette-id"), - private=LoadPipettePrivateResult( - pipette_id="pipette-id", serial_number="some id", config=config_data - ), + private=None, state_update=StateUpdate( loaded_pipette=LoadPipetteUpdate( pipette_name=PipetteNameType.P1000_96, diff --git a/api/tests/opentrons/protocol_engine/state/test_pipette_store.py b/api/tests/opentrons/protocol_engine/state/test_pipette_store.py index caab429e26b..10ae35e5f4a 100644 --- a/api/tests/opentrons/protocol_engine/state/test_pipette_store.py +++ b/api/tests/opentrons/protocol_engine/state/test_pipette_store.py @@ -638,13 +638,10 @@ def test_add_pipette_config( pipette_lld_settings={}, ) - private_result = cmd.LoadPipettePrivateResult( - pipette_id="pipette-id", serial_number="pipette-serial", config=config - ) subject.handle_action( SucceedCommandAction( command=command, - private_result=private_result, + private_result=None, state_update=update_types.StateUpdate( pipette_config=update_types.PipetteConfigUpdate( pipette_id="pipette-id", diff --git a/api/tests/opentrons/protocol_engine/state/test_tip_state.py b/api/tests/opentrons/protocol_engine/state/test_tip_state.py index e0f0fd15669..96e0451dbbe 100644 --- a/api/tests/opentrons/protocol_engine/state/test_tip_state.py +++ b/api/tests/opentrons/protocol_engine/state/test_tip_state.py @@ -16,11 +16,11 @@ from opentrons.protocol_engine import actions, commands from opentrons.protocol_engine.state import update_types from opentrons.protocol_engine.state.tips import TipStore, TipView -from opentrons.protocol_engine.types import FlowRates +from opentrons.protocol_engine.types import DeckSlotLocation, FlowRates from opentrons.protocol_engine.resources.pipette_data_provider import ( LoadedStaticPipetteData, ) -from opentrons.types import Point +from opentrons.types import DeckSlotName, Point from opentrons_shared_data.pipette.types import PipetteNameType from ..pipette_fixtures import ( NINETY_SIX_MAP, @@ -61,13 +61,22 @@ def labware_definition() -> LabwareDefinition: @pytest.fixture -def load_labware_command(labware_definition: LabwareDefinition) -> commands.LoadLabware: +def load_labware_action( + labware_definition: LabwareDefinition, +) -> actions.SucceedCommandAction: """Get a load labware command value object.""" - return commands.LoadLabware.construct( # type: ignore[call-arg] - result=commands.LoadLabwareResult.construct( - labwareId="cool-labware", - definition=labware_definition, - ) + return actions.SucceedCommandAction( + private_result=None, + command=_dummy_command(), + state_update=update_types.StateUpdate( + loaded_labware=update_types.LoadedLabwareUpdate( + labware_id="cool-labware", + definition=labware_definition, + new_location=DeckSlotLocation(slotName=DeckSlotName.SLOT_A1), + display_name=None, + offset_id=None, + ) + ), ) @@ -83,18 +92,13 @@ def _dummy_command() -> commands.Command: ], ) def test_get_next_tip_returns_none( - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, subject: TipStore, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, ) -> None: """It should start at the first tip in the labware.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + subject.handle_action(load_labware_action) + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -120,7 +124,9 @@ def test_get_next_tip_returns_none( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -136,18 +142,14 @@ def test_get_next_tip_returns_none( @pytest.mark.parametrize("input_tip_amount", [1, 8, 96]) def test_get_next_tip_returns_first_tip( - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, subject: TipStore, input_tip_amount: int, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, ) -> None: """It should start at the first tip in the labware.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) + subject.handle_action(load_labware_action) + pipette_name_type = PipetteNameType.P1000_96 if input_tip_amount == 1: pipette_name_type = PipetteNameType.P300_SINGLE_GEN2 @@ -155,7 +157,7 @@ def test_get_next_tip_returns_first_tip( pipette_name_type = PipetteNameType.P300_MULTI_GEN2 else: pipette_name_type = PipetteNameType.P1000_96 - load_pipette_private_result = commands.LoadPipettePrivateResult( + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -181,7 +183,9 @@ def test_get_next_tip_returns_first_tip( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -197,20 +201,16 @@ def test_get_next_tip_returns_first_tip( @pytest.mark.parametrize("input_tip_amount, result_well_name", [(1, "B1"), (8, "A2")]) def test_get_next_tip_used_starting_tip( - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, subject: TipStore, input_tip_amount: int, result_well_name: str, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, ) -> None: """It should start searching at the given starting tip.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + subject.handle_action(load_labware_action) + + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -236,7 +236,9 @@ def test_get_next_tip_used_starting_tip( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -265,7 +267,7 @@ def test_get_next_tip_used_starting_tip( ], ) def test_get_next_tip_skips_picked_up_tip( - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, subject: TipStore, input_tip_amount: int, get_next_tip_tips: int, @@ -274,13 +276,8 @@ def test_get_next_tip_skips_picked_up_tip( supported_tip_fixture: pipette_definition.SupportedTipsDefinition, ) -> None: """It should get the next tip in the column if one has been picked up.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) + subject.handle_action(load_labware_action) - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) channels_num = input_tip_amount if input_starting_tip is not None: pipette_name_type = PipetteNameType.P1000_96 @@ -299,7 +296,7 @@ def test_get_next_tip_skips_picked_up_tip( pipette_name_type = PipetteNameType.P300_MULTI_GEN2 else: pipette_name_type = PipetteNameType.P1000_96 - load_pipette_private_result = commands.LoadPipettePrivateResult( + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -325,7 +322,9 @@ def test_get_next_tip_skips_picked_up_tip( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -348,7 +347,7 @@ def test_get_next_tip_skips_picked_up_tip( labware_id="cool-labware", num_tips=get_next_tip_tips, starting_tip_name=input_starting_tip, - nozzle_map=load_pipette_private_result.config.nozzle_map, + nozzle_map=config_update.config.nozzle_map, ) assert result == result_well_name @@ -356,17 +355,13 @@ def test_get_next_tip_skips_picked_up_tip( def test_get_next_tip_with_starting_tip( subject: TipStore, - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, ) -> None: """It should return the starting tip, and then the following tip after that.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + subject.handle_action(load_labware_action) + + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -392,14 +387,16 @@ def test_get_next_tip_with_starting_tip( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) result = TipView(subject.state).get_next_tip( labware_id="cool-labware", num_tips=1, starting_tip_name="B2", - nozzle_map=load_pipette_private_result.config.nozzle_map, + nozzle_map=config_update.config.nozzle_map, ) assert result == "B2" @@ -419,7 +416,7 @@ def test_get_next_tip_with_starting_tip( labware_id="cool-labware", num_tips=1, starting_tip_name="B2", - nozzle_map=load_pipette_private_result.config.nozzle_map, + nozzle_map=config_update.config.nozzle_map, ) assert result == "C2" @@ -427,17 +424,13 @@ def test_get_next_tip_with_starting_tip( def test_get_next_tip_with_starting_tip_8_channel( subject: TipStore, - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, ) -> None: """It should return the starting tip, and then the following tip after that.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + subject.handle_action(load_labware_action) + + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -463,7 +456,9 @@ def test_get_next_tip_with_starting_tip_8_channel( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -501,17 +496,13 @@ def test_get_next_tip_with_starting_tip_8_channel( def test_get_next_tip_with_1_channel_followed_by_8_channel( subject: TipStore, - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, ) -> None: """It should return the first tip of column 2 for the 8 channel after performing a single tip pickup on column 1.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + subject.handle_action(load_labware_action) + + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -537,13 +528,13 @@ def test_get_next_tip_with_1_channel_followed_by_8_channel( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) - load_pipette_command_2 = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id2") - ) - load_pipette_private_result_2 = commands.LoadPipettePrivateResult( + + config_update_2 = update_types.PipetteConfigUpdate( pipette_id="pipette-id2", serial_number="pipette-serial2", config=LoadedStaticPipetteData( @@ -569,7 +560,9 @@ def test_get_next_tip_with_1_channel_followed_by_8_channel( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result_2, command=load_pipette_command_2 + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update_2), + command=_dummy_command(), ) ) @@ -607,17 +600,13 @@ def test_get_next_tip_with_1_channel_followed_by_8_channel( def test_get_next_tip_with_starting_tip_out_of_tips( subject: TipStore, - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, ) -> None: """It should return the starting tip of H12 and then None after that.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + subject.handle_action(load_labware_action) + + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -643,7 +632,9 @@ def test_get_next_tip_with_starting_tip_out_of_tips( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -681,17 +672,13 @@ def test_get_next_tip_with_starting_tip_out_of_tips( def test_get_next_tip_with_column_and_starting_tip( subject: TipStore, - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, ) -> None: """It should return the first tip in a column, taking starting tip into account.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + subject.handle_action(load_labware_action) + + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -717,7 +704,9 @@ def test_get_next_tip_with_column_and_starting_tip( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -733,17 +722,13 @@ def test_get_next_tip_with_column_and_starting_tip( def test_reset_tips( subject: TipStore, - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, ) -> None: """It should be able to reset tip tracking state.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + subject.handle_action(load_labware_action) + + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -770,7 +755,9 @@ def test_reset_tips( subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -805,10 +792,7 @@ def test_handle_pipette_config_action( subject: TipStore, supported_tip_fixture: pipette_definition.SupportedTipsDefinition ) -> None: """Should add pipette channel to state.""" - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -834,7 +818,9 @@ def test_handle_pipette_config_action( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -852,12 +838,10 @@ def test_handle_pipette_config_action( ], ) def test_has_tip_not_tip_rack( - load_labware_command: commands.LoadLabware, subject: TipStore + load_labware_action: actions.SucceedCommandAction, subject: TipStore ) -> None: """It should return False if labware isn't a tip rack.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) + subject.handle_action(load_labware_action) result = TipView(state=subject.state).has_clean_tip("cool-labware", "A1") @@ -865,12 +849,10 @@ def test_has_tip_not_tip_rack( def test_has_tip_tip_rack( - load_labware_command: commands.LoadLabware, subject: TipStore + load_labware_action: actions.SucceedCommandAction, subject: TipStore ) -> None: """It should return False if labware isn't a tip rack.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) + subject.handle_action(load_labware_action) result = TipView(state=subject.state).has_clean_tip("cool-labware", "A1") @@ -944,10 +926,7 @@ def test_active_channels( ) -> None: """Should update active channels after pipette configuration change.""" # Load pipette to update state - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -973,7 +952,9 @@ def test_active_channels( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -1000,19 +981,14 @@ def test_active_channels( def test_next_tip_uses_active_channels( subject: TipStore, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, ) -> None: """Test that tip tracking logic uses pipette's active channels.""" # Load labware - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) + subject.handle_action(load_labware_action) # Load pipette - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -1038,7 +1014,9 @@ def test_next_tip_uses_active_channels( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -1104,19 +1082,14 @@ def test_next_tip_uses_active_channels( def test_next_tip_automatic_tip_tracking_with_partial_configurations( subject: TipStore, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, ) -> None: """Test tip tracking logic using multiple pipette configurations.""" # Load labware - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) + subject.handle_action(load_labware_action) # Load pipette - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -1142,7 +1115,9 @@ def test_next_tip_automatic_tip_tracking_with_partial_configurations( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) @@ -1262,19 +1237,14 @@ def _reconfigure_nozzle_layout(start: str, back_l: str, front_r: str) -> NozzleM def test_next_tip_automatic_tip_tracking_tiprack_limits( subject: TipStore, supported_tip_fixture: pipette_definition.SupportedTipsDefinition, - load_labware_command: commands.LoadLabware, + load_labware_action: actions.SucceedCommandAction, ) -> None: """Test tip tracking logic to ensure once a tiprack is consumed it returns None when consuming tips using multiple pipette configurations.""" # Load labware - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) + subject.handle_action(load_labware_action) # Load pipette - load_pipette_command = commands.LoadPipette.construct( # type: ignore[call-arg] - result=commands.LoadPipetteResult(pipetteId="pipette-id") - ) - load_pipette_private_result = commands.LoadPipettePrivateResult( + config_update = update_types.PipetteConfigUpdate( pipette_id="pipette-id", serial_number="pipette-serial", config=LoadedStaticPipetteData( @@ -1300,7 +1270,9 @@ def test_next_tip_automatic_tip_tracking_tiprack_limits( ) subject.handle_action( actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command + private_result=None, + state_update=update_types.StateUpdate(pipette_config=config_update), + command=_dummy_command(), ) ) diff --git a/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py b/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py index 8663c3e0a8d..c31c6a2e551 100644 --- a/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py +++ b/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py @@ -6,6 +6,7 @@ from opentrons.protocol_engine.state.update_types import ( LoadPipetteUpdate, LoadedLabwareUpdate, + PipetteConfigUpdate, StateUpdate, ) import pytest @@ -380,16 +381,19 @@ def test_map_instrument_load(decoy: Decoy) -> None: result=pe_commands.LoadPipetteResult(pipetteId="pipette-0"), notes=[], ), - private_result=pe_commands.LoadPipettePrivateResult( - pipette_id="pipette-0", serial_number="fizzbuzz", config=pipette_config - ), + private_result=None, state_update=StateUpdate( loaded_pipette=LoadPipetteUpdate( pipette_id="pipette-0", mount=expected_params.mount, pipette_name=expected_params.pipetteName, liquid_presence_detection=expected_params.liquidPresenceDetection, - ) + ), + pipette_config=PipetteConfigUpdate( + pipette_id="pipette-0", + serial_number="fizzbuzz", + config=pipette_config, + ), ), )