diff --git a/api/src/opentrons/protocol_engine/clients/sync_client.py b/api/src/opentrons/protocol_engine/clients/sync_client.py index c553b737ec0..2277ce815a4 100644 --- a/api/src/opentrons/protocol_engine/clients/sync_client.py +++ b/api/src/opentrons/protocol_engine/clients/sync_client.py @@ -130,19 +130,11 @@ def load_labware( def reload_labware( self, labware_id: str, - load_name: str, - namespace: str, - version: int, - display_name: Optional[str] = None, ) -> commands.ReloadLabwareResult: """Execute a ReloadLabware command and return the result.""" request = commands.ReloadLabwareCreate( params=commands.ReloadLabwareParams( labwareId=labware_id, - loadName=load_name, - namespace=namespace, - version=version, - displayName=display_name, ) ) result = self._transport.execute_command(request=request) diff --git a/api/src/opentrons/protocol_engine/commands/reload_labware.py b/api/src/opentrons/protocol_engine/commands/reload_labware.py index 132ef9b3bef..5cabe9bb855 100644 --- a/api/src/opentrons/protocol_engine/commands/reload_labware.py +++ b/api/src/opentrons/protocol_engine/commands/reload_labware.py @@ -29,23 +29,6 @@ class ReloadLabwareParams(BaseModel): labwareId: str = Field( ..., description="The already-loaded labware instance to update." ) - loadName: str = Field( - ..., - description="Name used to reference a labware definition.", - ) - namespace: str = Field( - ..., - description="The namespace the labware definition belongs to.", - ) - version: int = Field( - ..., - description="The labware definition version.", - ) - displayName: Optional[str] = Field( - None, - description="An optional user-specified display name " - "or label for this labware.", - ) class ReloadLabwareResult(BaseModel): @@ -55,10 +38,6 @@ class ReloadLabwareResult(BaseModel): ..., description="An ID to reference this labware in subsequent commands. Same as the one in the parameters.", ) - definition: LabwareDefinition = Field( - ..., - description="The full definition data for this labware.", - ) offsetId: Optional[str] = Field( # Default `None` instead of `...` so this field shows up as non-required in # OpenAPI. The server is allowed to omit it or make it null. @@ -89,33 +68,10 @@ async def execute(self, params: ReloadLabwareParams) -> ReloadLabwareResult: """Reload the definition and calibration data for a specific labware.""" reloaded_labware = await self._equipment.reload_labware( labware_id=params.labwareId, - load_name=params.loadName, - namespace=params.namespace, - version=params.version, ) - # note: this check must be kept because somebody might specify the trash loadName - if ( - labware_validation.is_flex_trash(params.loadName) - and isinstance(reloaded_labware.location, DeckSlotLocation) - and self._state_view.geometry.get_slot_column( - reloaded_labware.location.slotName - ) - != 3 - ): - raise LabwareIsNotAllowedInLocationError( - f"{params.loadName} is not allowed in slot {reloaded_labware.location.slotName}" - ) - - if isinstance(reloaded_labware.location, OnLabwareLocation): - self._state_view.labware.raise_if_labware_cannot_be_stacked( - top_labware_definition=reloaded_labware.definition, - bottom_labware_id=reloaded_labware.location.labwareId, - ) - return ReloadLabwareResult( labwareId=params.labwareId, - definition=reloaded_labware.definition, offsetId=reloaded_labware.offsetId, ) diff --git a/api/src/opentrons/protocol_engine/execution/equipment.py b/api/src/opentrons/protocol_engine/execution/equipment.py index 12eb3df6787..711e95a3f59 100644 --- a/api/src/opentrons/protocol_engine/execution/equipment.py +++ b/api/src/opentrons/protocol_engine/execution/equipment.py @@ -60,7 +60,6 @@ class LoadedLabwareData: class ReloadedLabwareData: """The result of a reload labware procedure.""" - definition: LabwareDefinition location: LabwareLocation offsetId: Optional[str] @@ -180,47 +179,25 @@ async def load_labware( labware_id=labware_id, definition=definition, offsetId=offset_id ) - async def reload_labware( - self, labware_id: str, load_name: str, namespace: str, version: int - ) -> ReloadedLabwareData: + async def reload_labware(self, labware_id: str) -> ReloadedLabwareData: """Reload an already-loaded labware. This cannot change the labware location. Args: labware_id: The ID of the already-loaded labware. - load_name: The labware's load name. - namespace: The labware's namespace. - version: The labware's version. Raises: LabwareNotLoadedError: If `labware_id` does not reference a loaded labware. """ - definition_uri = uri_from_details( - load_name=load_name, - namespace=namespace, - version=version, - ) - location = self._state_store.labware.get_location(labware_id) - try: - # Try to use existing definition in state. - definition = self._state_store.labware.get_definition_by_uri(definition_uri) - except LabwareDefinitionDoesNotExistError: - definition = await self._labware_data_provider.get_labware_definition( - load_name=load_name, - namespace=namespace, - version=version, - ) - - # Allow propagation of ModuleNotLoadedError. + location = self._state_store.labware.get_location(labware_id) + definition_uri = self._state_store.labware.get_definition_uri(labware_id) offset_id = self.find_applicable_labware_offset_id( labware_definition_uri=definition_uri, labware_location=location, ) - return ReloadedLabwareData( - definition=definition, location=location, offsetId=offset_id - ) + return ReloadedLabwareData(location=location, offsetId=offset_id) async def load_pipette( self, diff --git a/api/src/opentrons/protocol_engine/state/labware.py b/api/src/opentrons/protocol_engine/state/labware.py index 743c582678b..e9750a652b4 100644 --- a/api/src/opentrons/protocol_engine/state/labware.py +++ b/api/src/opentrons/protocol_engine/state/labware.py @@ -176,7 +176,7 @@ def handle_action(self, action: Action) -> None: def _handle_command(self, command: Command) -> None: """Modify state in reaction to a command.""" - if isinstance(command.result, (LoadLabwareResult, ReloadLabwareResult)): + if isinstance(command.result, LoadLabwareResult): # If the labware load refers to an offset, that offset must actually exist. if command.result.offsetId is not None: assert command.result.offsetId in self._state.labware_offsets_by_id @@ -204,6 +204,11 @@ def _handle_command(self, command: Command) -> None: displayName=command.params.displayName, ) + elif isinstance(command.result, ReloadLabwareResult): + labware_id = command.params.labwareId + new_offset_id = command.result.offsetId + self._state.labware_by_id[labware_id].offsetId = new_offset_id + elif isinstance(command.result, MoveLabwareResult): labware_id = command.params.labwareId new_location = command.params.newLocation diff --git a/api/src/opentrons/protocol_engine/state/tips.py b/api/src/opentrons/protocol_engine/state/tips.py index 96e7bafe327..f5d68d61ee5 100644 --- a/api/src/opentrons/protocol_engine/state/tips.py +++ b/api/src/opentrons/protocol_engine/state/tips.py @@ -3,10 +3,6 @@ from enum import Enum from typing import Dict, Optional, List, Union -from opentrons_shared_data.labware.labware_definition import LabwareDefinition - -from opentrons.hardware_control.nozzle_manager import NozzleMap - from .abstract_store import HasState, HandlesActions from ..actions import ( Action, @@ -17,7 +13,6 @@ from ..commands import ( Command, LoadLabwareResult, - ReloadLabwareResult, PickUpTip, PickUpTipResult, DropTipResult, @@ -29,6 +24,8 @@ ) from ..error_recovery_policy import ErrorRecoveryType +from opentrons.hardware_control.nozzle_manager import NozzleMap + class TipRackWellState(Enum): """The state of a single tip in a tip rack's well.""" @@ -103,16 +100,6 @@ def handle_action(self, action: Action) -> None: well_name ] = TipRackWellState.CLEAN - def _add_new_tiprack(self, labware_id: str, definition: LabwareDefinition) -> None: - 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_succeeded_command(self, command: Command) -> None: if ( isinstance(command.result, LoadLabwareResult) @@ -120,7 +107,14 @@ def _handle_succeeded_command(self, command: Command) -> None: ): labware_id = command.result.labwareId definition = command.result.definition - self._add_new_tiprack(labware_id, 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 + ] elif isinstance(command.result, PickUpTipResult): labware_id = command.params.labwareId @@ -136,21 +130,6 @@ def _handle_succeeded_command(self, command: Command) -> None: pipette_id = command.params.pipetteId self._state.length_by_pipette_id.pop(pipette_id, None) - elif isinstance(command.result, ReloadLabwareResult): - if ( - command.result.definition.parameters.isTiprack - and command.result.labwareId not in self._state.tips_by_labware_id - ): - self._add_new_tiprack( - command.result.labwareId, command.result.definition - ) - elif ( - not command.result.definition.parameters.isTiprack - and command.result.labwareId in self._state.tips_by_labware_id - ): - self._state.tips_by_labware_id.pop(command.result.labwareId) - self._state.column_by_labware_id.pop(command.result.labwareId) - def _handle_failed_command( self, action: FailCommandAction, diff --git a/api/tests/opentrons/protocol_engine/clients/test_sync_client.py b/api/tests/opentrons/protocol_engine/clients/test_sync_client.py index c1e7be8f4bf..e4f5d7602ca 100644 --- a/api/tests/opentrons/protocol_engine/clients/test_sync_client.py +++ b/api/tests/opentrons/protocol_engine/clients/test_sync_client.py @@ -164,32 +164,23 @@ def test_load_labware( def test_reload_labware( decoy: Decoy, transport: ChildThreadTransport, - tip_rack_def: LabwareDefinition, subject: SyncClient, ) -> None: """It should execute a reload labware command.""" expected_request = commands.ReloadLabwareCreate( params=commands.ReloadLabwareParams( labwareId="some-labware-id", - loadName="some_labware", - namespace="opentrons", - version=1, - displayName="some_display_name", ) ) expected_result = commands.ReloadLabwareResult( - labwareId="some-labware-id", definition=tip_rack_def, offsetId=None + labwareId="some-labware-id", offsetId=None ) decoy.when(transport.execute_command(request=expected_request)).then_return( expected_result ) result = subject.reload_labware( labware_id="some-labware-id", - namespace="opentrons", - load_name="some_labware", - version=1, - display_name="some_display_name", ) assert result == expected_result diff --git a/api/tests/opentrons/protocol_engine/commands/test_reload_labware.py b/api/tests/opentrons/protocol_engine/commands/test_reload_labware.py index 73d751a6609..29057d88ebb 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_reload_labware.py +++ b/api/tests/opentrons/protocol_engine/commands/test_reload_labware.py @@ -46,23 +46,11 @@ async def test_reload_labware_implementation( data = ReloadLabwareParams( labwareId="my-labware-id", - loadName="some-load-name", - namespace="opentrons-test", - version=1, - displayName="My custom display name", ) - decoy.when( - await equipment.reload_labware( - labware_id="my-labware-id", - load_name="some-load-name", - namespace="opentrons-test", - version=1, - ) - ).then_return( + decoy.when(await equipment.reload_labware(labware_id="my-labware-id",)).then_return( ReloadedLabwareData( location=DeckSlotLocation(slotName=DeckSlotName.SLOT_4), - definition=well_plate_def, offsetId="labware-offset-id", ) ) @@ -71,7 +59,6 @@ async def test_reload_labware_implementation( assert result == ReloadLabwareResult( labwareId="my-labware-id", - definition=well_plate_def, offsetId="labware-offset-id", ) @@ -87,55 +74,13 @@ async def test_reload_labware_raises_labware_does_not_exist( data = ReloadLabwareParams( labwareId="my-labware-id", - loadName="some-load-name", - namespace="opentrons-test", - version=1, - displayName="My custom display name", ) decoy.when( await equipment.reload_labware( labware_id="my-labware-id", - load_name="some-load-name", - namespace="opentrons-test", - version=1, ) ).then_raise(LabwareNotLoadedError("What labware is this!")) with pytest.raises(LabwareNotLoadedError): await subject.execute(data) - - -async def test_load_labware_raises_location_not_allowed( - decoy: Decoy, - equipment: EquipmentHandler, - state_view: StateView, - well_plate_def: LabwareDefinition, -) -> None: - """A ReloadLabware command should raise if the flex trash definition is not in a valid slot.""" - subject = ReloadLabwareImplementation(equipment=equipment, state_view=state_view) - decoy.when(labware_validation.is_flex_trash("some-load-name")).then_return(True) - decoy.when( - await equipment.reload_labware( - labware_id="my-labware-id", - load_name="some-load-name", - namespace="opentrons-test", - version=1, - ) - ).then_return( - ReloadedLabwareData( - location=DeckSlotLocation(slotName=DeckSlotName.SLOT_A3), - definition=well_plate_def, - offsetId="labware-offset-id", - ) - ) - data = ReloadLabwareParams( - labwareId="my-labware-id", - loadName="some-load-name", - namespace="opentrons-test", - version=1, - displayName="My custom display name", - ) - - with pytest.raises(LabwareIsNotAllowedInLocationError): - await subject.execute(data) diff --git a/api/tests/opentrons/protocol_engine/state/command_fixtures.py b/api/tests/opentrons/protocol_engine/state/command_fixtures.py index aeab8eb621b..82d5c801611 100644 --- a/api/tests/opentrons/protocol_engine/state/command_fixtures.py +++ b/api/tests/opentrons/protocol_engine/state/command_fixtures.py @@ -581,22 +581,15 @@ def create_prepare_to_aspirate_command(pipette_id: str) -> cmd.PrepareToAspirate def create_reload_labware_command( labware_id: str, - definition: LabwareDefinition, offset_id: Optional[str], - display_name: Optional[str], ) -> cmd.ReloadLabware: """Create a completed ReloadLabware command.""" params = cmd.ReloadLabwareParams( - loadName=definition.parameters.loadName, - namespace=definition.namespace, - version=definition.version, labwareId=labware_id, - displayName=display_name, ) result = cmd.ReloadLabwareResult( labwareId=labware_id, - definition=definition, offsetId=offset_id, ) diff --git a/api/tests/opentrons/protocol_engine/state/test_labware_store.py b/api/tests/opentrons/protocol_engine/state/test_labware_store.py index 77de86b4a65..960ce423194 100644 --- a/api/tests/opentrons/protocol_engine/state/test_labware_store.py +++ b/api/tests/opentrons/protocol_engine/state/test_labware_store.py @@ -136,7 +136,6 @@ def test_handles_load_labware( def test_handles_reload_labware( subject: LabwareStore, well_plate_def: LabwareDefinition, - flex_50uL_tiprack: LabwareDefinition, ) -> None: """It should override labware data in the state.""" load_labware = create_load_labware_command( @@ -150,13 +149,15 @@ def test_handles_reload_labware( subject.handle_action( SucceedCommandAction(private_result=None, command=load_labware) ) - assert subject.state.labware_by_id[ - "test-labware-id" - ].definitionUri == uri_from_details( + expected_definition_uri = uri_from_details( load_name=well_plate_def.parameters.loadName, namespace=well_plate_def.namespace, version=well_plate_def.version, ) + assert ( + subject.state.labware_by_id["test-labware-id"].definitionUri + == expected_definition_uri + ) offset_request = LabwareOffsetCreate( definitionUri="offset-definition-uri", @@ -172,32 +173,22 @@ def test_handles_reload_labware( ) reload_labware = create_reload_labware_command( labware_id="test-labware-id", - definition=flex_50uL_tiprack, offset_id="offset-id", - display_name="display-name-2", ) subject.handle_action( SucceedCommandAction(private_result=None, command=reload_labware) ) - expected_definition_uri = uri_from_details( - load_name=flex_50uL_tiprack.parameters.loadName, - namespace=flex_50uL_tiprack.namespace, - version=flex_50uL_tiprack.version, - ) - expected_labware_data = LoadedLabware( id="test-labware-id", - loadName=flex_50uL_tiprack.parameters.loadName, + loadName=well_plate_def.parameters.loadName, definitionUri=expected_definition_uri, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_A1), offsetId="offset-id", - displayName="display-name-2", + displayName="display-name", ) assert subject.state.labware_by_id["test-labware-id"] == expected_labware_data - assert ( - subject.state.definitions_by_uri[expected_definition_uri] == flex_50uL_tiprack - ) + assert subject.state.definitions_by_uri[expected_definition_uri] == well_plate_def def test_handles_add_labware_definition( 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 2af4a486319..25894554027 100644 --- a/api/tests/opentrons/protocol_engine/state/test_tip_state.py +++ b/api/tests/opentrons/protocol_engine/state/test_tip_state.py @@ -36,9 +36,9 @@ def subject() -> TipStore: return TipStore() -def _default_labware_def( - parameters: Optional[LabwareParameters] = None, -) -> LabwareDefinition: +@pytest.fixture +def labware_definition() -> LabwareDefinition: + """Get a labware definition value object.""" return LabwareDefinition.construct( # type: ignore[call-arg] ordering=[ ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"], @@ -54,22 +54,10 @@ def _default_labware_def( ["A11", "B11", "C11", "D11", "E11", "F11", "G11", "H11"], ["A12", "B12", "C12", "D12", "E12", "F12", "G12", "H12"], ], - parameters=parameters or _tip_rack_parameters, + parameters=_tip_rack_parameters, ) -@pytest.fixture -def labware_definition() -> LabwareDefinition: - """Get a labware definition value object.""" - return _default_labware_def() - - -@pytest.fixture -def reload_labware_definition() -> LabwareDefinition: - """Get a labware definition for reloading.""" - return _default_labware_def() - - @pytest.fixture def load_labware_command(labware_definition: LabwareDefinition) -> commands.LoadLabware: """Get a load labware command value object.""" @@ -81,18 +69,6 @@ def load_labware_command(labware_definition: LabwareDefinition) -> commands.Load ) -@pytest.fixture -def reload_labware_command( - reload_labware_definition: LabwareDefinition, -) -> commands.ReloadLabware: - """Get a reload labware command.""" - return commands.ReloadLabware.construct( # type: ignore[call-arg] - result=commands.ReloadLabwareResult.construct( - labwareId="cool-labware", definition=reload_labware_definition - ) - ) - - @pytest.fixture def pick_up_tip_command() -> commands.PickUpTip: """Get a pick-up tip command value object.""" @@ -1159,157 +1135,3 @@ def _reconfigure_nozzle_layout(start: str, back_l: str, front_r: str) -> NozzleM _assert_and_pickup("B1", map) map = _reconfigure_nozzle_layout("A1", "A1", "A1") _assert_and_pickup("B2", map) - - -def test_reload_tiprack_does_not_alter_tip_state( - subject: TipStore, - load_labware_command: commands.LoadLabware, - pick_up_tip_command: commands.PickUpTip, - reload_labware_command: commands.ReloadLabware, - supported_tip_fixture: pipette_definition.SupportedTipsDefinition, -) -> None: - """Reloading a tiprack is not the same as resetting the tips.""" - 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( - pipette_id="pipette-id", - serial_number="pipette-serial", - config=LoadedStaticPipetteData( - channels=1, - max_volume=15, - min_volume=3, - model="gen a", - display_name="display name", - flow_rates=FlowRates( - default_aspirate={}, - default_dispense={}, - default_blow_out={}, - ), - tip_configuration_lookup_table={15: supported_tip_fixture}, - nominal_tip_overlap={}, - nozzle_offset_z=1.23, - home_position=4.56, - nozzle_map=get_default_nozzle_map(PipetteNameType.P300_SINGLE_GEN2), - back_left_corner_offset=Point(x=1, y=2, z=3), - front_right_corner_offset=Point(x=4, y=5, z=6), - ), - ) - - subject.handle_action( - actions.SucceedCommandAction( - private_result=load_pipette_private_result, command=load_pipette_command - ) - ) - - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=pick_up_tip_command) - ) - subject.handle_action( - actions.SucceedCommandAction( - private_result=None, command=reload_labware_command - ) - ) - result = TipView(subject.state).get_next_tip( - labware_id="cool-labware", - num_tips=1, - starting_tip_name=None, - nozzle_map=None, - ) - - assert result == "B1" - - -@pytest.mark.parametrize( - "labware_definition", - [ - _default_labware_def( - parameters=LabwareParameters.construct(isTiprack=False) # type: ignore[call-arg] - ) - ], -) -def test_reload_to_tiprack_loads( - subject: TipStore, - load_labware_command: commands.LoadLabware, - reload_labware_command: commands.ReloadLabware, -) -> None: - """If you have a loaded non-tiprack and reload with a tiprack, state should appear.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - assert load_labware_command.result - assert ( - TipView(subject.state).get_next_tip( - labware_id=load_labware_command.result.labwareId, - num_tips=1, - starting_tip_name=None, - nozzle_map=None, - ) - is None - ) - - subject.handle_action( - actions.SucceedCommandAction( - private_result=None, command=reload_labware_command - ) - ) - result = TipView(subject.state).get_next_tip( - labware_id="cool-labware", - num_tips=1, - starting_tip_name=None, - nozzle_map=None, - ) - - assert result == "A1" - - -@pytest.mark.parametrize( - "reload_labware_definition", - [ - _default_labware_def( - parameters=LabwareParameters.construct(isTiprack=False) # type: ignore[call-arg] - ) - ], -) -def test_reload_to_non_tiprack_unloads( - subject: TipStore, - load_labware_command: commands.LoadLabware, - reload_labware_command: commands.ReloadLabware, -) -> None: - """If you have a loaded tiprack and reload with a non-tiprack def, state should go away.""" - subject.handle_action( - actions.SucceedCommandAction(private_result=None, command=load_labware_command) - ) - result = TipView(subject.state).get_next_tip( - labware_id="cool-labware", - num_tips=1, - starting_tip_name=None, - nozzle_map=None, - ) - assert result == "A1" - - subject.handle_action( - actions.SucceedCommandAction( - private_result=None, command=reload_labware_command - ) - ) - result = TipView(subject.state).get_next_tip( - labware_id="cool-labware", - num_tips=1, - starting_tip_name=None, - nozzle_map=None, - ) - assert load_labware_command.result - - assert ( - TipView(subject.state).get_next_tip( - labware_id=load_labware_command.result.labwareId, - num_tips=1, - starting_tip_name=None, - nozzle_map=None, - ) - is None - ) diff --git a/shared-data/command/schemas/8.json b/shared-data/command/schemas/8.json index d481179157d..8699cc998f9 100644 --- a/shared-data/command/schemas/8.json +++ b/shared-data/command/schemas/8.json @@ -1420,29 +1420,9 @@ "title": "Labwareid", "description": "The already-loaded labware instance to update.", "type": "string" - }, - "loadName": { - "title": "Loadname", - "description": "Name used to reference a labware definition.", - "type": "string" - }, - "namespace": { - "title": "Namespace", - "description": "The namespace the labware definition belongs to.", - "type": "string" - }, - "version": { - "title": "Version", - "description": "The labware definition version.", - "type": "integer" - }, - "displayName": { - "title": "Displayname", - "description": "An optional user-specified display name or label for this labware.", - "type": "string" } }, - "required": ["labwareId", "loadName", "namespace", "version"] + "required": ["labwareId"] }, "ReloadLabwareCreate": { "title": "ReloadLabwareCreate",