From 0b88d87d6419ad8923c3ef5f5f22041929e16192 Mon Sep 17 00:00:00 2001 From: Max Marrone Date: Mon, 14 Oct 2024 15:41:21 -0400 Subject: [PATCH] refactor(api): Port `TipState`'s nozzle layout to `StateUpdate` (#16479) --- .../protocol_engine/commands/__init__.py | 2 - .../commands/command_unions.py | 2 - .../commands/configure_nozzle_layout.py | 18 +- .../commands/configuring_common.py | 13 - .../opentrons/protocol_engine/state/tips.py | 21 +- .../commands/test_configure_nozzle_layout.py | 6 +- .../protocol_engine/state/test_tip_state.py | 264 +++++++++--------- 7 files changed, 148 insertions(+), 178 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/__init__.py b/api/src/opentrons/protocol_engine/commands/__init__.py index d0550fce8c5..b8ad7ab0b57 100644 --- a/api/src/opentrons/protocol_engine/commands/__init__.py +++ b/api/src/opentrons/protocol_engine/commands/__init__.py @@ -306,7 +306,6 @@ ConfigureNozzleLayoutCreate, ConfigureNozzleLayoutParams, ConfigureNozzleLayoutResult, - ConfigureNozzleLayoutPrivateResult, ConfigureNozzleLayoutCommandType, ) @@ -569,7 +568,6 @@ "ConfigureNozzleLayoutParams", "ConfigureNozzleLayoutResult", "ConfigureNozzleLayoutCommandType", - "ConfigureNozzleLayoutPrivateResult", # get pipette tip presence bundle "GetTipPresence", "GetTipPresenceCreate", diff --git a/api/src/opentrons/protocol_engine/commands/command_unions.py b/api/src/opentrons/protocol_engine/commands/command_unions.py index 2c7f768945f..80df6710f8b 100644 --- a/api/src/opentrons/protocol_engine/commands/command_unions.py +++ b/api/src/opentrons/protocol_engine/commands/command_unions.py @@ -288,7 +288,6 @@ ConfigureNozzleLayoutParams, ConfigureNozzleLayoutResult, ConfigureNozzleLayoutCommandType, - ConfigureNozzleLayoutPrivateResult, ) from .verify_tip_presence import ( @@ -709,7 +708,6 @@ None, LoadPipettePrivateResult, ConfigureForVolumePrivateResult, - ConfigureNozzleLayoutPrivateResult, ] # All `DefinedErrorData`s that implementations will actually return in practice. diff --git a/api/src/opentrons/protocol_engine/commands/configure_nozzle_layout.py b/api/src/opentrons/protocol_engine/commands/configure_nozzle_layout.py index 0d9a5e8c5e7..d04eee55c94 100644 --- a/api/src/opentrons/protocol_engine/commands/configure_nozzle_layout.py +++ b/api/src/opentrons/protocol_engine/commands/configure_nozzle_layout.py @@ -10,9 +10,6 @@ ) from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData from ..errors.error_occurrence import ErrorOccurrence -from .configuring_common import ( - PipetteNozzleLayoutResultMixin, -) from ..types import ( AllNozzleLayoutConfiguration, SingleNozzleLayoutConfiguration, @@ -40,12 +37,6 @@ class ConfigureNozzleLayoutParams(PipetteIdMixin): ] -class ConfigureNozzleLayoutPrivateResult(PipetteNozzleLayoutResultMixin): - """Result sent to the store but not serialized.""" - - pass - - class ConfigureNozzleLayoutResult(BaseModel): """Result data from execution of an configureNozzleLayout command.""" @@ -55,7 +46,7 @@ class ConfigureNozzleLayoutResult(BaseModel): class ConfigureNozzleLayoutImplementation( AbstractCommandImpl[ ConfigureNozzleLayoutParams, - SuccessData[ConfigureNozzleLayoutResult, ConfigureNozzleLayoutPrivateResult], + SuccessData[ConfigureNozzleLayoutResult, None], ] ): """Configure nozzle layout command implementation.""" @@ -68,7 +59,7 @@ def __init__( async def execute( self, params: ConfigureNozzleLayoutParams - ) -> SuccessData[ConfigureNozzleLayoutResult, ConfigureNozzleLayoutPrivateResult]: + ) -> SuccessData[ConfigureNozzleLayoutResult, None]: """Check that requested pipette can support the requested nozzle layout.""" primary_nozzle = params.configurationParams.dict().get("primaryNozzle") front_right_nozzle = params.configurationParams.dict().get("frontRightNozzle") @@ -93,10 +84,7 @@ async def execute( return SuccessData( public=ConfigureNozzleLayoutResult(), - private=ConfigureNozzleLayoutPrivateResult( - pipette_id=params.pipetteId, - nozzle_map=nozzle_map, - ), + private=None, state_update=update_state, ) diff --git a/api/src/opentrons/protocol_engine/commands/configuring_common.py b/api/src/opentrons/protocol_engine/commands/configuring_common.py index 6998bcbac7b..f69cf41fef6 100644 --- a/api/src/opentrons/protocol_engine/commands/configuring_common.py +++ b/api/src/opentrons/protocol_engine/commands/configuring_common.py @@ -1,9 +1,6 @@ """Common configuration command base models.""" from dataclasses import dataclass -from opentrons.hardware_control.nozzle_manager import ( - NozzleMap, -) from ..resources import pipette_data_provider @@ -14,13 +11,3 @@ class PipetteConfigUpdateResultMixin: pipette_id: str serial_number: str config: pipette_data_provider.LoadedStaticPipetteData - - -@dataclass -class PipetteNozzleLayoutResultMixin: - """A nozzle layout result for updating the pipette state.""" - - pipette_id: str - - nozzle_map: NozzleMap - """A dataclass object holding information about the current nozzle configuration.""" diff --git a/api/src/opentrons/protocol_engine/state/tips.py b/api/src/opentrons/protocol_engine/state/tips.py index 4ed78c1df96..f744b1a01b4 100644 --- a/api/src/opentrons/protocol_engine/state/tips.py +++ b/api/src/opentrons/protocol_engine/state/tips.py @@ -11,10 +11,7 @@ Command, LoadLabwareResult, ) -from ..commands.configuring_common import ( - PipetteConfigUpdateResultMixin, - PipetteNozzleLayoutResultMixin, -) +from ..commands.configuring_common import PipetteConfigUpdateResultMixin from opentrons.hardware_control.nozzle_manager import NozzleMap @@ -82,13 +79,6 @@ def handle_action(self, action: Action) -> None: self._handle_succeeded_command(action.command) - if isinstance(action.private_result, PipetteNozzleLayoutResultMixin): - pipette_id = action.private_result.pipette_id - nozzle_map = action.private_result.nozzle_map - pipette_info = self._state.pipette_info_by_pipette_id[pipette_id] - pipette_info.active_channels = nozzle_map.tip_count - pipette_info.nozzle_map = nozzle_map - elif isinstance(action, ResetTipsAction): labware_id = action.labware_id @@ -121,6 +111,15 @@ def _handle_state_update(self, state_update: update_types.StateUpdate) -> None: well_name=state_update.tips_used.well_name, ) + if state_update.pipette_nozzle_map != update_types.NO_CHANGE: + pipette_info = self._state.pipette_info_by_pipette_id[ + state_update.pipette_nozzle_map.pipette_id + ] + pipette_info.active_channels = ( + state_update.pipette_nozzle_map.nozzle_map.tip_count + ) + pipette_info.nozzle_map = state_update.pipette_nozzle_map.nozzle_map + def _set_used_tips( # noqa: C901 self, pipette_id: str, well_name: str, labware_id: str ) -> None: diff --git a/api/tests/opentrons/protocol_engine/commands/test_configure_nozzle_layout.py b/api/tests/opentrons/protocol_engine/commands/test_configure_nozzle_layout.py index d4a9c671dd3..e72b659a83c 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_configure_nozzle_layout.py +++ b/api/tests/opentrons/protocol_engine/commands/test_configure_nozzle_layout.py @@ -19,7 +19,6 @@ from opentrons.protocol_engine.commands.configure_nozzle_layout import ( ConfigureNozzleLayoutParams, ConfigureNozzleLayoutResult, - ConfigureNozzleLayoutPrivateResult, ConfigureNozzleLayoutImplementation, ) @@ -146,10 +145,7 @@ async def test_configure_nozzle_layout_implementation( assert result == SuccessData( public=ConfigureNozzleLayoutResult(), - private=ConfigureNozzleLayoutPrivateResult( - pipette_id="pipette-id", - nozzle_map=expected_nozzlemap, - ), + private=None, state_update=StateUpdate( pipette_nozzle_map=PipetteNozzleMapUpdate( 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 dc603ac4ca8..fb07e4696ff 100644 --- a/api/tests/opentrons/protocol_engine/state/test_tip_state.py +++ b/api/tests/opentrons/protocol_engine/state/test_tip_state.py @@ -978,17 +978,17 @@ def test_active_channels( ) # Configure nozzle for partial configuration - configure_nozzle_layout_cmd = commands.ConfigureNozzleLayout.construct( # type: ignore[call-arg] - result=commands.ConfigureNozzleLayoutResult() - ) - configure_nozzle_private_result = commands.ConfigureNozzleLayoutPrivateResult( - pipette_id="pipette-id", - nozzle_map=nozzle_map, + state_update = update_types.StateUpdate( + pipette_nozzle_map=update_types.PipetteNozzleMapUpdate( + pipette_id="pipette-id", + nozzle_map=nozzle_map, + ) ) subject.handle_action( actions.SucceedCommandAction( - private_result=configure_nozzle_private_result, - command=configure_nozzle_layout_cmd, + command=_dummy_command(), + private_result=None, + state_update=state_update, ) ) assert ( @@ -1043,29 +1043,38 @@ def test_next_tip_uses_active_channels( ) # Configure nozzle for partial configuration - configure_nozzle_layout_cmd = commands.ConfigureNozzleLayout.construct( # type: ignore[call-arg] - result=commands.ConfigureNozzleLayoutResult() - ) - configure_nozzle_private_result = commands.ConfigureNozzleLayoutPrivateResult( - pipette_id="pipette-id", - nozzle_map=NozzleMap.build( - physical_nozzles=NINETY_SIX_MAP, - physical_rows=NINETY_SIX_ROWS, - physical_columns=NINETY_SIX_COLS, - starting_nozzle="A12", - back_left_nozzle="A12", - front_right_nozzle="H12", - valid_nozzle_maps=ValidNozzleMaps( - maps={ - "A12_H12": ["A12", "B12", "C12", "D12", "E12", "F12", "G12", "H12"] - } + state_update = update_types.StateUpdate( + pipette_nozzle_map=update_types.PipetteNozzleMapUpdate( + pipette_id="pipette-id", + nozzle_map=NozzleMap.build( + physical_nozzles=NINETY_SIX_MAP, + physical_rows=NINETY_SIX_ROWS, + physical_columns=NINETY_SIX_COLS, + starting_nozzle="A12", + back_left_nozzle="A12", + front_right_nozzle="H12", + valid_nozzle_maps=ValidNozzleMaps( + maps={ + "A12_H12": [ + "A12", + "B12", + "C12", + "D12", + "E12", + "F12", + "G12", + "H12", + ] + } + ), ), - ), + ) ) subject.handle_action( actions.SucceedCommandAction( - private_result=configure_nozzle_private_result, - command=configure_nozzle_layout_cmd, + command=_dummy_command(), + private_result=None, + state_update=state_update, ) ) # Pick up partial tips @@ -1162,78 +1171,77 @@ def _assert_and_pickup(well: str, nozzle_map: NozzleMap) -> None: ) ) - # Configure nozzle for partial configurations - configure_nozzle_layout_cmd = commands.ConfigureNozzleLayout.construct( # type: ignore[call-arg] - result=commands.ConfigureNozzleLayoutResult() - ) - def _reconfigure_nozzle_layout(start: str, back_l: str, front_r: str) -> NozzleMap: - configure_nozzle_private_result = commands.ConfigureNozzleLayoutPrivateResult( - pipette_id="pipette-id", - nozzle_map=NozzleMap.build( - physical_nozzles=NINETY_SIX_MAP, - physical_rows=NINETY_SIX_ROWS, - physical_columns=NINETY_SIX_COLS, - starting_nozzle=start, - back_left_nozzle=back_l, - front_right_nozzle=front_r, - valid_nozzle_maps=ValidNozzleMaps( - maps={ - "A1": ["A1"], - "H1": ["H1"], - "A12": ["A12"], - "H12": ["H12"], - "A1_H3": [ - "A1", - "A2", - "A3", - "B1", - "B2", - "B3", - "C1", - "C2", - "C3", - "D1", - "D2", - "D3", - "E1", - "E2", - "E3", - "F1", - "F2", - "F3", - "G1", - "G2", - "G3", - "H1", - "H2", - "H3", - ], - "A1_F2": [ - "A1", - "A2", - "B1", - "B2", - "C1", - "C2", - "D1", - "D2", - "E1", - "E2", - "F1", - "F2", - ], - } - ), + nozzle_map = NozzleMap.build( + physical_nozzles=NINETY_SIX_MAP, + physical_rows=NINETY_SIX_ROWS, + physical_columns=NINETY_SIX_COLS, + starting_nozzle=start, + back_left_nozzle=back_l, + front_right_nozzle=front_r, + valid_nozzle_maps=ValidNozzleMaps( + maps={ + "A1": ["A1"], + "H1": ["H1"], + "A12": ["A12"], + "H12": ["H12"], + "A1_H3": [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + "E1", + "E2", + "E3", + "F1", + "F2", + "F3", + "G1", + "G2", + "G3", + "H1", + "H2", + "H3", + ], + "A1_F2": [ + "A1", + "A2", + "B1", + "B2", + "C1", + "C2", + "D1", + "D2", + "E1", + "E2", + "F1", + "F2", + ], + } ), ) + state_update = update_types.StateUpdate( + pipette_nozzle_map=update_types.PipetteNozzleMapUpdate( + pipette_id="pipette-id", + nozzle_map=nozzle_map, + ) + ) subject.handle_action( actions.SucceedCommandAction( - private_result=configure_nozzle_private_result, - command=configure_nozzle_layout_cmd, + command=_dummy_command(), + private_result=None, + state_update=state_update, ) ) - return configure_nozzle_private_result.nozzle_map + return nozzle_map map = _reconfigure_nozzle_layout("A1", "A1", "H3") _assert_and_pickup("A10", map) @@ -1320,51 +1328,47 @@ def _get_next_and_pickup(nozzle_map: NozzleMap) -> str | None: return result - # Configure nozzle for partial configurations - configure_nozzle_layout_cmd = commands.ConfigureNozzleLayout.construct( # type: ignore[call-arg] - result=commands.ConfigureNozzleLayoutResult() - ) - def _reconfigure_nozzle_layout(start: str, back_l: str, front_r: str) -> NozzleMap: - configure_nozzle_private_result = commands.ConfigureNozzleLayoutPrivateResult( - pipette_id="pipette-id", - nozzle_map=NozzleMap.build( - physical_nozzles=NINETY_SIX_MAP, - physical_rows=NINETY_SIX_ROWS, - physical_columns=NINETY_SIX_COLS, - starting_nozzle=start, - back_left_nozzle=back_l, - front_right_nozzle=front_r, - valid_nozzle_maps=ValidNozzleMaps( - maps={ - "A1": ["A1"], - "H1": ["H1"], - "A12": ["A12"], - "H12": ["H12"], - "Full": sum( - [ - NINETY_SIX_ROWS["A"], - NINETY_SIX_ROWS["B"], - NINETY_SIX_ROWS["C"], - NINETY_SIX_ROWS["D"], - NINETY_SIX_ROWS["E"], - NINETY_SIX_ROWS["F"], - NINETY_SIX_ROWS["G"], - NINETY_SIX_ROWS["H"], - ], - [], - ), - } - ), + nozzle_map = NozzleMap.build( + physical_nozzles=NINETY_SIX_MAP, + physical_rows=NINETY_SIX_ROWS, + physical_columns=NINETY_SIX_COLS, + starting_nozzle=start, + back_left_nozzle=back_l, + front_right_nozzle=front_r, + valid_nozzle_maps=ValidNozzleMaps( + maps={ + "A1": ["A1"], + "H1": ["H1"], + "A12": ["A12"], + "H12": ["H12"], + "Full": sum( + [ + NINETY_SIX_ROWS["A"], + NINETY_SIX_ROWS["B"], + NINETY_SIX_ROWS["C"], + NINETY_SIX_ROWS["D"], + NINETY_SIX_ROWS["E"], + NINETY_SIX_ROWS["F"], + NINETY_SIX_ROWS["G"], + NINETY_SIX_ROWS["H"], + ], + [], + ), + } ), ) + state_update = update_types.StateUpdate( + pipette_nozzle_map=update_types.PipetteNozzleMapUpdate( + pipette_id="pipette-id", nozzle_map=nozzle_map + ) + ) subject.handle_action( actions.SucceedCommandAction( - private_result=configure_nozzle_private_result, - command=configure_nozzle_layout_cmd, + command=_dummy_command(), private_result=None, state_update=state_update ) ) - return configure_nozzle_private_result.nozzle_map + return nozzle_map map = _reconfigure_nozzle_layout("A1", "A1", "A1") for x in range(96):