diff --git a/api/src/opentrons/protocol_engine/commands/aspirate.py b/api/src/opentrons/protocol_engine/commands/aspirate.py index d96c71ffe03..94147e891b1 100644 --- a/api/src/opentrons/protocol_engine/commands/aspirate.py +++ b/api/src/opentrons/protocol_engine/commands/aspirate.py @@ -24,7 +24,7 @@ from opentrons.hardware_control import HardwareControlAPI -from ..state.update_types import StateUpdate +from ..state.update_types import StateUpdate, CLEAR from ..types import WellLocation, WellOrigin, CurrentWell, DeckPoint if TYPE_CHECKING: @@ -140,7 +140,7 @@ async def execute(self, params: AspirateParams) -> _ExecuteReturn: state_update.set_liquid_operated( labware_id=labware_id, well_name=well_name, - volume=None, + volume=CLEAR, ) return DefinedErrorData( public=OverpressureError( diff --git a/api/src/opentrons/protocol_engine/commands/aspirate_in_place.py b/api/src/opentrons/protocol_engine/commands/aspirate_in_place.py index 8caf9173d11..9a8d169b80d 100644 --- a/api/src/opentrons/protocol_engine/commands/aspirate_in_place.py +++ b/api/src/opentrons/protocol_engine/commands/aspirate_in_place.py @@ -24,7 +24,7 @@ ) from ..errors.error_occurrence import ErrorOccurrence from ..errors.exceptions import PipetteNotReadyToAspirateError -from ..state.update_types import StateUpdate +from ..state.update_types import StateUpdate, CLEAR from ..types import CurrentWell if TYPE_CHECKING: @@ -114,7 +114,7 @@ async def execute(self, params: AspirateInPlaceParams) -> _ExecuteReturn: state_update.set_liquid_operated( labware_id=current_location.labware_id, well_name=current_location.well_name, - volume=None, + volume=CLEAR, ) return DefinedErrorData( public=OverpressureError( diff --git a/api/src/opentrons/protocol_engine/commands/dispense.py b/api/src/opentrons/protocol_engine/commands/dispense.py index 2e024f981a9..1f2ce166e16 100644 --- a/api/src/opentrons/protocol_engine/commands/dispense.py +++ b/api/src/opentrons/protocol_engine/commands/dispense.py @@ -8,7 +8,7 @@ from pydantic import Field from ..types import DeckPoint -from ..state.update_types import StateUpdate +from ..state.update_types import StateUpdate, CLEAR from .pipetting_common import ( PipetteIdMixin, DispenseVolumeMixin, @@ -111,7 +111,7 @@ async def execute(self, params: DispenseParams) -> _ExecuteReturn: state_update.set_liquid_operated( labware_id=labware_id, well_name=well_name, - volume=None, + volume=CLEAR, ) return DefinedErrorData( public=OverpressureError( diff --git a/api/src/opentrons/protocol_engine/commands/dispense_in_place.py b/api/src/opentrons/protocol_engine/commands/dispense_in_place.py index 999e7d83ac4..d85bc31cf25 100644 --- a/api/src/opentrons/protocol_engine/commands/dispense_in_place.py +++ b/api/src/opentrons/protocol_engine/commands/dispense_in_place.py @@ -21,7 +21,7 @@ DefinedErrorData, ) from ..errors.error_occurrence import ErrorOccurrence -from ..state.update_types import StateUpdate +from ..state.update_types import StateUpdate, CLEAR from ..types import CurrentWell if TYPE_CHECKING: @@ -93,7 +93,7 @@ async def execute(self, params: DispenseInPlaceParams) -> _ExecuteReturn: state_update.set_liquid_operated( labware_id=current_location.labware_id, well_name=current_location.well_name, - volume=None, + volume=CLEAR, ) return DefinedErrorData( public=OverpressureError( diff --git a/api/src/opentrons/protocol_engine/commands/liquid_probe.py b/api/src/opentrons/protocol_engine/commands/liquid_probe.py index 057da787e78..677d62731f9 100644 --- a/api/src/opentrons/protocol_engine/commands/liquid_probe.py +++ b/api/src/opentrons/protocol_engine/commands/liquid_probe.py @@ -209,8 +209,8 @@ async def execute(self, params: _CommonParams) -> _LiquidProbeExecuteReturn: state_update.set_liquid_probed( labware_id=params.labwareId, well_name=params.wellName, - height=None, - volume=None, + height=update_types.CLEAR, + volume=update_types.CLEAR, last_probed=self._model_utils.get_timestamp(), ) return DefinedErrorData( @@ -229,13 +229,15 @@ async def execute(self, params: _CommonParams) -> _LiquidProbeExecuteReturn: ) else: try: - well_volume = self._state_view.geometry.get_well_volume_at_height( - labware_id=params.labwareId, - well_name=params.wellName, - height=z_pos_or_error, + well_volume: float | update_types.ClearType = ( + self._state_view.geometry.get_well_volume_at_height( + labware_id=params.labwareId, + well_name=params.wellName, + height=z_pos_or_error, + ) ) except IncompleteLabwareDefinitionError: - well_volume = None + well_volume = update_types.CLEAR state_update.set_liquid_probed( labware_id=params.labwareId, well_name=params.wellName, @@ -283,7 +285,7 @@ async def execute(self, params: _CommonParams) -> _TryLiquidProbeExecuteReturn: if isinstance(z_pos_or_error, PipetteLiquidNotFoundError): z_pos = None - well_volume = None + well_volume: float | update_types.ClearType = update_types.CLEAR else: z_pos = z_pos_or_error try: @@ -291,12 +293,12 @@ async def execute(self, params: _CommonParams) -> _TryLiquidProbeExecuteReturn: labware_id=params.labwareId, well_name=params.wellName, height=z_pos ) except IncompleteLabwareDefinitionError: - well_volume = None + well_volume = update_types.CLEAR state_update.set_liquid_probed( labware_id=params.labwareId, well_name=params.wellName, - height=z_pos, + height=z_pos if z_pos is not None else update_types.CLEAR, volume=well_volume, last_probed=self._model_utils.get_timestamp(), ) diff --git a/api/src/opentrons/protocol_engine/state/update_types.py b/api/src/opentrons/protocol_engine/state/update_types.py index 16993ec159e..06f68768657 100644 --- a/api/src/opentrons/protocol_engine/state/update_types.py +++ b/api/src/opentrons/protocol_engine/state/update_types.py @@ -192,8 +192,8 @@ class LiquidProbedUpdate: labware_id: str well_name: str last_probed: datetime - height: typing.Optional[float] = None - volume: typing.Optional[float] = None + height: float | ClearType + volume: float | ClearType @dataclasses.dataclass @@ -202,7 +202,7 @@ class LiquidOperatedUpdate: labware_id: str well_name: str - volume: typing.Optional[float] = None + volume: float | ClearType @dataclasses.dataclass @@ -385,8 +385,8 @@ def set_liquid_probed( labware_id: str, well_name: str, last_probed: datetime, - height: typing.Optional[float] = None, - volume: typing.Optional[float] = None, + height: float | ClearType, + volume: float | ClearType, ) -> None: """Add a liquid height and volume to well state. See `ProbeLiquidUpdate`.""" self.liquid_probed = LiquidProbedUpdate( @@ -398,10 +398,7 @@ def set_liquid_probed( ) def set_liquid_operated( - self, - labware_id: str, - well_name: str, - volume: typing.Optional[float] = None, + self, labware_id: str, well_name: str, volume: float | ClearType ) -> None: """Update liquid volumes in well state. See `OperateLiquidUpdate`.""" self.liquid_operated = LiquidOperatedUpdate( diff --git a/api/src/opentrons/protocol_engine/state/wells.py b/api/src/opentrons/protocol_engine/state/wells.py index 2f9a7b63293..23ac6907fff 100644 --- a/api/src/opentrons/protocol_engine/state/wells.py +++ b/api/src/opentrons/protocol_engine/state/wells.py @@ -1,6 +1,6 @@ """Basic well data state and store.""" from dataclasses import dataclass -from typing import Dict, List, Union, Iterator, Optional, Tuple, overload +from typing import Dict, List, Union, Iterator, Optional, Tuple, overload, TypeVar from opentrons.protocol_engine.types import ( ProbedHeightInfo, @@ -56,7 +56,7 @@ def _handle_liquid_loaded_update( self._state.loaded_volumes[labware_id] = {} for (well, volume) in state_update.volumes.items(): self._state.loaded_volumes[labware_id][well] = LoadedVolumeInfo( - volume=volume, + volume=_none_from_clear(volume), last_loaded=state_update.last_loaded, operations_since_load=0, ) @@ -71,11 +71,11 @@ def _handle_liquid_probed_update( if labware_id not in self._state.probed_volumes: self._state.probed_volumes[labware_id] = {} self._state.probed_heights[labware_id][well_name] = ProbedHeightInfo( - height=state_update.height, + height=_none_from_clear(state_update.height), last_probed=state_update.last_probed, ) self._state.probed_volumes[labware_id][well_name] = ProbedVolumeInfo( - volume=state_update.volume, + volume=_none_from_clear(state_update.volume), last_probed=state_update.last_probed, operations_since_probe=0, ) @@ -89,7 +89,7 @@ def _handle_liquid_operated_update( labware_id in self._state.loaded_volumes and well_name in self._state.loaded_volumes[labware_id] ): - if state_update.volume is None: + if state_update.volume is update_types.CLEAR: del self._state.loaded_volumes[labware_id][well_name] else: prev_loaded_vol_info = self._state.loaded_volumes[labware_id][well_name] @@ -109,7 +109,7 @@ def _handle_liquid_operated_update( labware_id in self._state.probed_volumes and well_name in self._state.probed_volumes[labware_id] ): - if state_update.volume is None: + if state_update.volume is update_types.CLEAR: del self._state.probed_volumes[labware_id][well_name] else: prev_probed_vol_info = self._state.probed_volumes[labware_id][well_name] @@ -223,3 +223,12 @@ def _height_from_info(info: Optional[ProbedHeightInfo]) -> Optional[float]: if info is None: return None return info.height + + +MaybeClear = TypeVar("MaybeClear") + + +def _none_from_clear(inval: MaybeClear | update_types.ClearType) -> MaybeClear | None: + if inval == update_types.CLEAR: + return None + return inval diff --git a/api/tests/opentrons/protocol_engine/commands/test_aspirate.py b/api/tests/opentrons/protocol_engine/commands/test_aspirate.py index cfe5fdfa75f..e67a0bc2622 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_aspirate.py +++ b/api/tests/opentrons/protocol_engine/commands/test_aspirate.py @@ -327,7 +327,7 @@ async def test_overpressure_error( liquid_operated=update_types.LiquidOperatedUpdate( labware_id=labware_id, well_name=well_name, - volume=None, + volume=update_types.CLEAR, ), ), ) diff --git a/api/tests/opentrons/protocol_engine/commands/test_aspirate_in_place.py b/api/tests/opentrons/protocol_engine/commands/test_aspirate_in_place.py index 645c7ef3fd5..16ad011dc8f 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_aspirate_in_place.py +++ b/api/tests/opentrons/protocol_engine/commands/test_aspirate_in_place.py @@ -270,7 +270,7 @@ async def test_overpressure_error( liquid_operated=update_types.LiquidOperatedUpdate( labware_id=stateupdateLabware, well_name=stateupdateWell, - volume=None, + volume=update_types.CLEAR, ) ), ) diff --git a/api/tests/opentrons/protocol_engine/commands/test_dispense.py b/api/tests/opentrons/protocol_engine/commands/test_dispense.py index e8752ecd171..dc455dc3d46 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_dispense.py +++ b/api/tests/opentrons/protocol_engine/commands/test_dispense.py @@ -169,7 +169,7 @@ async def test_overpressure_error( liquid_operated=update_types.LiquidOperatedUpdate( labware_id="labware-id", well_name="well-name", - volume=None, + volume=update_types.CLEAR, ), ), ) diff --git a/api/tests/opentrons/protocol_engine/commands/test_dispense_in_place.py b/api/tests/opentrons/protocol_engine/commands/test_dispense_in_place.py index c0db7eb4bc7..274a41b8a21 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_dispense_in_place.py +++ b/api/tests/opentrons/protocol_engine/commands/test_dispense_in_place.py @@ -175,7 +175,7 @@ async def test_overpressure_error( liquid_operated=update_types.LiquidOperatedUpdate( labware_id=stateupdateLabware, well_name=stateupdateWell, - volume=None, + volume=update_types.CLEAR, ) ), ) diff --git a/api/tests/opentrons/protocol_engine/commands/test_liquid_probe.py b/api/tests/opentrons/protocol_engine/commands/test_liquid_probe.py index fef29da18ce..5593ac9ca66 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_liquid_probe.py +++ b/api/tests/opentrons/protocol_engine/commands/test_liquid_probe.py @@ -235,8 +235,8 @@ async def test_liquid_not_found_error( liquid_probed=update_types.LiquidProbedUpdate( labware_id=labware_id, well_name=well_name, - height=None, - volume=None, + height=update_types.CLEAR, + volume=update_types.CLEAR, last_probed=error_timestamp, ), )