From 5e62dd17a430f4a8095aa9c9b4e0d456f5207c8d Mon Sep 17 00:00:00 2001 From: caila-marashaj Date: Wed, 22 Jan 2025 10:45:13 -0500 Subject: [PATCH] Revert "make em track in place" This reverts commit 55f557943585f7e83a845346b457008140be330b. --- .../commands/aspirate_while_tracking.py | 194 +++++++++++------- .../commands/dispense_while_tracking.py | 123 +++++------ .../protocol_engine/execution/pipetting.py | 38 ++++ 3 files changed, 207 insertions(+), 148 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/aspirate_while_tracking.py b/api/src/opentrons/protocol_engine/commands/aspirate_while_tracking.py index 157d3d9115b..4f3ec9cf6db 100644 --- a/api/src/opentrons/protocol_engine/commands/aspirate_while_tracking.py +++ b/api/src/opentrons/protocol_engine/commands/aspirate_while_tracking.py @@ -11,11 +11,13 @@ FlowRateMixin, BaseLiquidHandlingResult, aspirate_while_tracking, + prepare_for_aspirate, ) from .movement_common import ( LiquidHandlingWellLocationMixin, DestinationPositionResult, StallOrCollisionError, + move_to_well, ) from .command import ( AbstractCommandImpl, @@ -24,13 +26,18 @@ DefinedErrorData, SuccessData, ) -from ..errors.exceptions import PipetteNotReadyToAspirateError + from opentrons.hardware_control import HardwareControlAPI -from ..state.update_types import CLEAR -from ..types import CurrentWell, DeckPoint + +from ..state.update_types import StateUpdate, CLEAR +from ..types import ( + WellLocation, + WellOrigin, + CurrentWell, +) if TYPE_CHECKING: - from ..execution import MovementHandler, PipettingHandler, GantryMover + from ..execution import MovementHandler, PipettingHandler from ..resources import ModelUtils from ..state.state import StateView from ..notes import CommandNoteAdder @@ -75,7 +82,6 @@ def __init__( movement: MovementHandler, command_note_adder: CommandNoteAdder, model_utils: ModelUtils, - gantry_mover: GantryMover, **kwargs: object, ) -> None: self._pipetting = pipetting @@ -84,106 +90,136 @@ def __init__( self._movement = movement self._command_note_adder = command_note_adder self._model_utils = model_utils - self._gantry_mover = gantry_mover async def execute(self, params: AspirateWhileTrackingParams) -> _ExecuteReturn: """Move to and aspirate from the requested well. Raises: TipNotAttachedError: if no tip is attached to the pipette. - PipetteNotReadyToAspirateError: pipette plunger is not ready. """ + pipette_id = params.pipetteId + labware_id = params.labwareId + well_name = params.wellName + well_location = params.wellLocation + + state_update = StateUpdate() + + final_location = self._state_view.geometry.get_well_position( + labware_id=labware_id, + well_name=well_name, + well_location=well_location, + operation_volume=-params.volume, + pipette_id=pipette_id, + ) + ready_to_aspirate = self._pipetting.get_is_ready_to_aspirate( - pipette_id=params.pipetteId, + pipette_id=pipette_id ) + + current_well = None + if not ready_to_aspirate: - raise PipetteNotReadyToAspirateError( - "Pipette cannot aspirate in place because of a previous blow out." - " The first aspirate following a blow-out must be from a specific well" - " so the plunger can be reset in a known safe position." + move_result = await move_to_well( + movement=self._movement, + model_utils=self._model_utils, + pipette_id=pipette_id, + labware_id=labware_id, + well_name=well_name, + well_location=WellLocation(origin=WellOrigin.TOP), + ) + state_update.append(move_result.state_update) + if isinstance(move_result, DefinedErrorData): + return DefinedErrorData(move_result.public, state_update=state_update) + + prepare_result = await prepare_for_aspirate( + pipette_id=pipette_id, + pipetting=self._pipetting, + model_utils=self._model_utils, + # Note that the retryLocation is the final location, inside the liquid, + # because that's where we'd want the client to try re-aspirating if this + # command fails and the run enters error recovery. + location_if_error={"retryLocation": final_location}, ) + state_update.append(prepare_result.state_update) + if isinstance(prepare_result, DefinedErrorData): + return DefinedErrorData( + public=prepare_result.public, state_update=state_update + ) - current_position = await self._gantry_mover.get_position(params.pipetteId) - current_location = self._state_view.pipettes.get_current_location() + # set our current deck location to the well now that we've made + # an intermediate move for the "prepare for aspirate" step + current_well = CurrentWell( + pipette_id=pipette_id, + labware_id=labware_id, + well_name=well_name, + ) + move_result = await move_to_well( + movement=self._movement, + model_utils=self._model_utils, + pipette_id=pipette_id, + labware_id=labware_id, + well_name=well_name, + well_location=well_location, + current_well=current_well, + ) + state_update.append(move_result.state_update) + if isinstance(move_result, DefinedErrorData): + return DefinedErrorData( + public=move_result.public, state_update=state_update + ) aspirate_result = await aspirate_while_tracking( - pipette_id=params.pipetteId, - labware_id=params.labwareId, - well_name=params.wellName, + pipette_id=pipette_id, + labware_id=labware_id, + well_name=well_name, volume=params.volume, flow_rate=params.flowRate, location_if_error={ "retryLocation": ( - current_position.x, - current_position.y, - current_position.z, + move_result.public.position.x, + move_result.public.position.y, + move_result.public.position.z, ) }, command_note_adder=self._command_note_adder, pipetting=self._pipetting, model_utils=self._model_utils, ) - position_after_aspirate = await self._gantry_mover.get_position( - params.pipetteId + state_update.append(aspirate_result.state_update) + if isinstance(aspirate_result, DefinedErrorData): + state_update.set_liquid_operated( + labware_id=labware_id, + well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well( + labware_id, + well_name, + params.pipetteId, + ), + volume_added=CLEAR, + ) + return DefinedErrorData( + public=aspirate_result.public, state_update=state_update + ) + + state_update.set_liquid_operated( + labware_id=labware_id, + well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well( + labware_id, well_name, pipette_id + ), + volume_added=-aspirate_result.public.volume + * self._state_view.geometry.get_nozzles_per_well( + labware_id, + well_name, + params.pipetteId, + ), ) - result_deck_point = DeckPoint.model_construct( - x=position_after_aspirate.x, - y=position_after_aspirate.y, - z=position_after_aspirate.z, + + return SuccessData( + public=AspirateWhileTrackingResult( + volume=aspirate_result.public.volume, + position=move_result.public.position, + ), + state_update=state_update, ) - if isinstance(aspirate_result, DefinedErrorData): - if ( - isinstance(current_location, CurrentWell) - and current_location.pipette_id == params.pipetteId - ): - return DefinedErrorData( - public=aspirate_result.public, - state_update=aspirate_result.state_update.set_liquid_operated( - labware_id=current_location.labware_id, - well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well( - current_location.labware_id, - current_location.well_name, - params.pipetteId, - ), - volume_added=CLEAR, - ), - state_update_if_false_positive=aspirate_result.state_update_if_false_positive, - ) - else: - return aspirate_result - else: - if ( - isinstance(current_location, CurrentWell) - and current_location.pipette_id == params.pipetteId - ): - return SuccessData( - public=AspirateWhileTrackingResult( - volume=aspirate_result.public.volume, - position=result_deck_point, - ), - state_update=aspirate_result.state_update.set_liquid_operated( - labware_id=current_location.labware_id, - well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well( - current_location.labware_id, - current_location.well_name, - params.pipetteId, - ), - volume_added=-aspirate_result.public.volume - * self._state_view.geometry.get_nozzles_per_well( - current_location.labware_id, - current_location.well_name, - params.pipetteId, - ), - ), - ) - else: - return SuccessData( - public=AspirateWhileTrackingResult( - volume=aspirate_result.public.volume, - position=result_deck_point, - ), - state_update=aspirate_result.state_update, - ) class AspirateWhileTracking( diff --git a/api/src/opentrons/protocol_engine/commands/dispense_while_tracking.py b/api/src/opentrons/protocol_engine/commands/dispense_while_tracking.py index 4a5a80cab81..7d287c7c01d 100644 --- a/api/src/opentrons/protocol_engine/commands/dispense_while_tracking.py +++ b/api/src/opentrons/protocol_engine/commands/dispense_while_tracking.py @@ -8,8 +8,7 @@ from pydantic import Field from pydantic.json_schema import SkipJsonSchema -from ..state.update_types import CLEAR -from ..types import CurrentWell, DeckPoint +from ..state.update_types import StateUpdate, CLEAR from .pipetting_common import ( PipetteIdMixin, DispenseVolumeMixin, @@ -22,6 +21,7 @@ LiquidHandlingWellLocationMixin, DestinationPositionResult, StallOrCollisionError, + move_to_well, ) from .command import ( AbstractCommandImpl, @@ -32,7 +32,7 @@ ) if TYPE_CHECKING: - from ..execution import MovementHandler, PipettingHandler, GantryMover + from ..execution import MovementHandler, PipettingHandler from ..resources import ModelUtils from ..state.state import StateView @@ -82,24 +82,31 @@ def __init__( movement: MovementHandler, pipetting: PipettingHandler, model_utils: ModelUtils, - gantry_mover: GantryMover, **kwargs: object, ) -> None: self._state_view = state_view self._movement = movement self._pipetting = pipetting self._model_utils = model_utils - self._gantry_mover = gantry_mover async def execute(self, params: DispenseWhileTrackingParams) -> _ExecuteReturn: """Move to and dispense to the requested well.""" + well_location = params.wellLocation labware_id = params.labwareId well_name = params.wellName # TODO(pbm, 10-15-24): call self._state_view.geometry.validate_dispense_volume_into_well() - current_location = self._state_view.pipettes.get_current_location() - current_position = await self._gantry_mover.get_position(params.pipetteId) + move_result = await move_to_well( + movement=self._movement, + model_utils=self._model_utils, + pipette_id=params.pipetteId, + labware_id=labware_id, + well_name=well_name, + well_location=well_location, + ) + if isinstance(move_result, DefinedErrorData): + return move_result dispense_result = await dispense_while_tracking( pipette_id=params.pipetteId, labware_id=labware_id, @@ -109,85 +116,63 @@ async def execute(self, params: DispenseWhileTrackingParams) -> _ExecuteReturn: push_out=params.pushOut, location_if_error={ "retryLocation": ( - current_position.x, - current_position.y, - current_position.z, + move_result.public.position.x, + move_result.public.position.y, + move_result.public.position.z, ) }, pipetting=self._pipetting, model_utils=self._model_utils, ) - position_after_dispense = await self._gantry_mover.get_position( - params.pipetteId - ) - result_deck_point = DeckPoint.model_construct( - x=position_after_dispense.x, - y=position_after_dispense.y, - z=position_after_dispense.z, - ) if isinstance(dispense_result, DefinedErrorData): - if ( - isinstance(current_location, CurrentWell) - and current_location.pipette_id == params.pipetteId - ): - return DefinedErrorData( - public=dispense_result.public, - state_update=dispense_result.state_update.set_liquid_operated( - labware_id=current_location.labware_id, + return DefinedErrorData( + public=dispense_result.public, + state_update=( + StateUpdate.reduce( + move_result.state_update, dispense_result.state_update + ).set_liquid_operated( + labware_id=labware_id, well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well( - current_location.labware_id, - current_location.well_name, - params.pipetteId, + labware_id, well_name, params.pipetteId ), volume_added=CLEAR, - ), - state_update_if_false_positive=dispense_result.state_update_if_false_positive, - ) - else: - return dispense_result - else: - if ( - isinstance(current_location, CurrentWell) - and current_location.pipette_id == params.pipetteId - ): - volume_added = ( - self._state_view.pipettes.get_liquid_dispensed_by_ejecting_volume( - pipette_id=params.pipetteId, - volume=dispense_result.public.volume, ) + ), + state_update_if_false_positive=StateUpdate.reduce( + move_result.state_update, + dispense_result.state_update_if_false_positive, + ), + ) + else: + volume_added = ( + self._state_view.pipettes.get_liquid_dispensed_by_ejecting_volume( + pipette_id=params.pipetteId, volume=dispense_result.public.volume ) - if volume_added is not None: - volume_added *= self._state_view.geometry.get_nozzles_per_well( - current_location.labware_id, - current_location.well_name, - params.pipetteId, - ) - return SuccessData( - public=DispenseWhileTrackingResult( - volume=dispense_result.public.volume, - position=result_deck_point, - ), - state_update=dispense_result.state_update.set_liquid_operated( - labware_id=current_location.labware_id, + ) + if volume_added is not None: + volume_added *= self._state_view.geometry.get_nozzles_per_well( + labware_id, well_name, params.pipetteId + ) + return SuccessData( + public=DispenseWhileTrackingResult( + volume=dispense_result.public.volume, + position=move_result.public.position, + ), + state_update=( + StateUpdate.reduce( + move_result.state_update, dispense_result.state_update + ).set_liquid_operated( + labware_id=labware_id, well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well( - current_location.labware_id, - current_location.well_name, - params.pipetteId, + labware_id, well_name, params.pipetteId ), volume_added=volume_added if volume_added is not None else CLEAR, - ), - ) - else: - return SuccessData( - public=DispenseWhileTrackingResult( - volume=dispense_result.public.volume, - position=result_deck_point, - ), - state_update=dispense_result.state_update, - ) + ) + ), + ) class DispenseWhileTracking( diff --git a/api/src/opentrons/protocol_engine/execution/pipetting.py b/api/src/opentrons/protocol_engine/execution/pipetting.py index 2fab81f1454..2365347348e 100644 --- a/api/src/opentrons/protocol_engine/execution/pipetting.py +++ b/api/src/opentrons/protocol_engine/execution/pipetting.py @@ -171,6 +171,25 @@ async def aspirate_while_tracking( Raises: PipetteOverpressureError, propagated as-is from the hardware controller. """ + # get mount and config data from state and hardware controller + """hw_pipette, adjusted_volume = self.get_hw_aspirate_params( + pipette_id, volume, command_note_adder + ) + + aspirate_z_distance = self._state_view.geometry.get_liquid_handling_z_change( + labware_id=labware_id, + well_name=well_name, + operation_volume=volume * -1, + ) + with self._set_flow_rate(pipette=hw_pipette, aspirate_flow_rate=flow_rate): + await self._hardware_api.aspirate_while_tracking( + mount=hw_pipette.mount, + z_distance=aspirate_z_distance, + flow_rate=flow_rate, + volume=adjusted_volume, + ) + return adjusted_volume + """ return 0.0 async def dispense_while_tracking( @@ -187,6 +206,25 @@ async def dispense_while_tracking( Raises: PipetteOverpressureError, propagated as-is from the hardware controller. """ + # get mount and config data from state and hardware controller + """hw_pipette, adjusted_volume = self.get_hw_dispense_params(pipette_id, volume) + + dispense_z_distance = self._state_view.geometry.get_liquid_handling_z_change( + labware_id=labware_id, + well_name=well_name, + operation_volume=volume, + ) + with self._set_flow_rate(pipette=hw_pipette, dispense_flow_rate=flow_rate): + await self._hardware_api.dispense_while_tracking( + mount=hw_pipette.mount, + z_distance=dispense_z_distance, + flow_rate=flow_rate, + volume=adjusted_volume, + push_out=push_out, + ) + + return adjusted_volume + """ return 0.0 async def aspirate_in_place(