diff --git a/api/src/opentrons/hardware_control/protocols/liquid_handler.py b/api/src/opentrons/hardware_control/protocols/liquid_handler.py index f2f3f2ecef4c..2aea15bd55b7 100644 --- a/api/src/opentrons/hardware_control/protocols/liquid_handler.py +++ b/api/src/opentrons/hardware_control/protocols/liquid_handler.py @@ -1,8 +1,8 @@ -from typing import Optional, Dict +from typing import Optional from typing_extensions import Protocol from opentrons.types import Point -from opentrons.hardware_control.types import CriticalPoint, Axis +from opentrons.hardware_control.types import CriticalPoint from .types import MountArgType, CalibrationType, ConfigType from .instrument_configurer import InstrumentConfigurer diff --git a/api/src/opentrons/hardware_control/protocols/motion_controller.py b/api/src/opentrons/hardware_control/protocols/motion_controller.py index 6fda13f8f382..e95a9d2e24f1 100644 --- a/api/src/opentrons/hardware_control/protocols/motion_controller.py +++ b/api/src/opentrons/hardware_control/protocols/motion_controller.py @@ -1,4 +1,4 @@ -from typing import Dict, Union, List, Optional, Mapping +from typing import Dict, List, Optional, Mapping from typing_extensions import Protocol from opentrons.types import Point diff --git a/api/src/opentrons/protocol_api/core/protocol.py b/api/src/opentrons/protocol_api/core/protocol.py index c367c2b4bbab..62e2d7cd1d71 100644 --- a/api/src/opentrons/protocol_api/core/protocol.py +++ b/api/src/opentrons/protocol_api/core/protocol.py @@ -3,7 +3,7 @@ from __future__ import annotations from abc import abstractmethod, ABC -from typing import Generic, Type, List, Optional, Union, Tuple, Dict, TYPE_CHECKING +from typing import Generic, List, Optional, Union, Tuple, Dict, TYPE_CHECKING from opentrons_shared_data.deck.types import DeckDefinitionV5, SlotDefV3 from opentrons_shared_data.pipette.types import PipetteNameType diff --git a/api/src/opentrons/protocol_api/validation.py b/api/src/opentrons/protocol_api/validation.py index 9baba2268ce8..6b491d7e9b44 100644 --- a/api/src/opentrons/protocol_api/validation.py +++ b/api/src/opentrons/protocol_api/validation.py @@ -193,29 +193,24 @@ def ensure_pipette_name(pipette_name: str) -> PipetteNameType: ) from None -def ensure_axis_map_type( - axis_map: Union[AxisMapType, StringAxisMap], - robot_type: RobotType, - is_96_channel: bool = False, -) -> AxisMapType: - """Ensure that the axis map provided is in the correct shape and contains the correct keys.""" - axis_map_keys = list(axis_map.keys()) - key_type = set(type(k) for k in axis_map_keys) - - if len(key_type) > 1: - raise IncorrectAxisError( - "Please provide an `axis_map` with only string or only AxisType keys." - ) +def _check_ot2_axis_type( + robot_type: RobotType, axis_map_keys: Union[List[str], List[AxisType]] +) -> None: if robot_type == "OT-2 Standard" and isinstance(axis_map_keys[0], AxisType): if any(k not in AxisType.ot2_axes() for k in axis_map_keys): raise IncorrectAxisError( f"An OT-2 Robot only accepts the following axes {AxisType.ot2_axes()}" ) if robot_type == "OT-2 Standard" and isinstance(axis_map_keys[0], str): - if any(k.upper() not in [axis.value for axis in AxisType.ot2_axes()] for k in axis_map_keys): # type: ignore [attr-defined] + if any(k.upper() not in [axis.value for axis in AxisType.ot2_axes()] for k in axis_map_keys): # type: ignore [union-attr] raise IncorrectAxisError( f"An OT-2 Robot only accepts the following axes {AxisType.ot2_axes()}" ) + + +def _check_96_channel_axis_type( + is_96_channel: bool, axis_map_keys: Union[List[str], List[AxisType]] +) -> None: if is_96_channel and any( key_variation in axis_map_keys for key_variation in ["Z_R", "z_r", AxisType.Z_R] ): @@ -229,6 +224,23 @@ def ensure_axis_map_type( "A 96 channel is not attached. The clamp `Q` motor does not exist." ) + +def ensure_axis_map_type( + axis_map: Union[AxisMapType, StringAxisMap], + robot_type: RobotType, + is_96_channel: bool = False, +) -> AxisMapType: + """Ensure that the axis map provided is in the correct shape and contains the correct keys.""" + axis_map_keys: Union[List[str], List[AxisType]] = list(axis_map.keys()) # type: ignore + key_type = set(type(k) for k in axis_map_keys) + + if len(key_type) > 1: + raise IncorrectAxisError( + "Please provide an `axis_map` with only string or only AxisType keys." + ) + _check_ot2_axis_type(robot_type, axis_map_keys) + _check_96_channel_axis_type(is_96_channel, axis_map_keys) + if all(isinstance(k, AxisType) for k in axis_map_keys): return_map: AxisMapType = axis_map # type: ignore return return_map @@ -241,6 +253,7 @@ def ensure_axis_map_type( def ensure_only_gantry_axis_map_type( axis_map: AxisMapType, robot_type: RobotType ) -> None: + """Ensure that the axis map provided is in the correct shape and matches the gantry axes for the robot.""" if robot_type == "OT-2 Standard": if any(k not in AxisType.ot2_gantry_axes() for k in axis_map.keys()): raise IncorrectAxisError( diff --git a/api/src/opentrons/protocol_engine/commands/robot/common.py b/api/src/opentrons/protocol_engine/commands/robot/common.py index e5401e3045fe..1cd0b0d17b33 100644 --- a/api/src/opentrons/protocol_engine/commands/robot/common.py +++ b/api/src/opentrons/protocol_engine/commands/robot/common.py @@ -1,3 +1,4 @@ +"""Shared result types for robot API commands.""" from pydantic import BaseModel, Field from typing import Dict @@ -9,6 +10,8 @@ class DestinationRobotPositionResult(BaseModel): + """The result dictionary of `MotorAxis` type.""" + position: MotorAxisMapType = Field( default=default_position, description="The position of all axes on the robot. If no mount was provided, the last moved mount is used to determine the location.", diff --git a/api/src/opentrons/protocol_engine/commands/robot/move_axes_relative.py b/api/src/opentrons/protocol_engine/commands/robot/move_axes_relative.py index bd1da7b60c6f..238229ebce65 100644 --- a/api/src/opentrons/protocol_engine/commands/robot/move_axes_relative.py +++ b/api/src/opentrons/protocol_engine/commands/robot/move_axes_relative.py @@ -58,6 +58,7 @@ def __init__( async def execute( self, params: MoveAxesRelativeParams ) -> SuccessData[MoveAxesRelativeResult]: + """Move the axes on a flex a relative distance.""" # TODO (lc 08-16-2024) implement `move_axes` for OT 2 hardware controller # and then we can remove this validation. ensure_ot3_hardware(self._hardware_api) diff --git a/api/src/opentrons/protocol_engine/commands/robot/move_axes_to.py b/api/src/opentrons/protocol_engine/commands/robot/move_axes_to.py index 98b8128db292..0d17d5f171f7 100644 --- a/api/src/opentrons/protocol_engine/commands/robot/move_axes_to.py +++ b/api/src/opentrons/protocol_engine/commands/robot/move_axes_to.py @@ -3,7 +3,6 @@ from typing import Literal, Optional, Type, TYPE_CHECKING from pydantic import Field, BaseModel -from opentrons.protocol_engine.types import MotorAxis from opentrons.hardware_control import HardwareControlAPI from opentrons.protocol_engine.resources import ensure_ot3_hardware @@ -59,6 +58,7 @@ def __init__( self._hardware_api = hardware_api async def execute(self, params: MoveAxesToParams) -> SuccessData[MoveAxesToResult]: + """Move the axes on a flex an absolute distance.""" # TODO (lc 08-16-2024) implement `move_axes` for OT 2 hardware controller # and then we can remove this validation. ensure_ot3_hardware(self._hardware_api) diff --git a/api/src/opentrons/protocol_engine/commands/robot/move_to.py b/api/src/opentrons/protocol_engine/commands/robot/move_to.py index 4b310bcd9ab2..44b8c4fdbe24 100644 --- a/api/src/opentrons/protocol_engine/commands/robot/move_to.py +++ b/api/src/opentrons/protocol_engine/commands/robot/move_to.py @@ -4,7 +4,6 @@ from pydantic import BaseModel, Field from opentrons.types import MountType -from opentrons.hardware_control.protocols.types import FlexRobotType from ..pipetting_common import DestinationPositionResult from ..command import ( @@ -60,6 +59,7 @@ def __init__( self._movement = movement async def execute(self, params: MoveToParams) -> SuccessData[MoveToResult]: + """Move to a given destination on a flex.""" x, y, z = await self._movement.move_mount_to( mount=params.mount, destination=params.destination, speed=params.speed ) diff --git a/api/src/opentrons/protocol_engine/execution/gantry_mover.py b/api/src/opentrons/protocol_engine/execution/gantry_mover.py index c76be093c7f4..1cb50f21418d 100644 --- a/api/src/opentrons/protocol_engine/execution/gantry_mover.py +++ b/api/src/opentrons/protocol_engine/execution/gantry_mover.py @@ -246,6 +246,13 @@ async def get_position_from_mount( critical_point: Optional[CriticalPoint] = None, fail_on_not_homed: bool = False, ) -> Point: + """Get the current position of the gantry based on the mount. + + Args: + mount: The mount to get the position for. + critical_point: Optional parameter for getting instrument location data, effects critical point. + fail_on_not_homed: Raise PositionUnknownError if gantry position is not known. + """ try: point = await self._hardware_api.gantry_position( mount=mount, @@ -319,8 +326,9 @@ async def move_axes( Args: axis_map: The mapping of axes to command. - relative_move: Specifying whether a relative move needs to be handled or not. + critical_point: A critical point override for axes speed: Optional speed parameter for the move. + relative_move: Specifying whether a relative move needs to be handled or not. """ try: pos_hw = self._convert_axis_map_for_hw(axis_map) @@ -496,6 +504,13 @@ async def get_position_from_mount( critical_point: Optional[CriticalPoint] = None, fail_on_not_homed: bool = False, ) -> Point: + """Get the current position of the gantry based on the mount. + + Args: + mount: The mount to get the position for. + critical_point: Optional parameter for getting instrument location data, effects critical point. + fail_on_not_homed: Raise PositionUnknownError if gantry position is not known. + """ pipette = self._state_view.pipettes.get_by_mount(MountType[mount.name]) origin_deck_point = ( self._state_view.pipettes.get_deck_point(pipette.id) if pipette else None diff --git a/api/tests/opentrons/hardware_control/backends/test_ot3_utils.py b/api/tests/opentrons/hardware_control/backends/test_ot3_utils.py index 7077718d9c08..2e650a2c2467 100644 --- a/api/tests/opentrons/hardware_control/backends/test_ot3_utils.py +++ b/api/tests/opentrons/hardware_control/backends/test_ot3_utils.py @@ -1,9 +1,6 @@ import pytest from typing import List from opentrons_hardware.hardware_control.motion_planning import Move -from opentrons_hardware.hardware_control.motion import ( - create_step, -) from opentrons.hardware_control.backends import ot3utils from opentrons_hardware.firmware_bindings.constants import NodeId from opentrons.hardware_control.types import Axis, OT3Mount diff --git a/api/tests/opentrons/protocol_api/test_robot_context.py b/api/tests/opentrons/protocol_api/test_robot_context.py index 7d1531705b85..f60869c3813e 100644 --- a/api/tests/opentrons/protocol_api/test_robot_context.py +++ b/api/tests/opentrons/protocol_api/test_robot_context.py @@ -76,6 +76,7 @@ def test_move_to( destination: Location, speed: Optional[float], ) -> None: + """Test `RobotContext.move_to`.""" subject.move_to(mount, destination, speed) core_mount: Mount if isinstance(mount, str): @@ -119,6 +120,7 @@ def test_move_axes_to( expected_critical_point: AxisMapType, speed: Optional[float], ) -> None: + """Test `RobotContext.move_axes_to`.""" subject.move_axes_to(axis_map, critical_point, speed) decoy.verify( subject._core.move_axes_to(expected_axis_map, expected_critical_point, speed) @@ -143,6 +145,7 @@ def test_move_axes_relative( converted_map: AxisMapType, speed: Optional[float], ) -> None: + """Test `RobotContext.move_axes_relative`.""" subject.move_axes_relative(axis_map, speed) decoy.verify(subject._core.move_axes_relative(converted_map, speed)) @@ -168,5 +171,6 @@ def test_get_axes_coordinates_for( location_to_move: Union[Location, ModuleContext, DeckLocation], expected_axis_map: AxisMapType, ) -> None: + """Test `RobotContext.get_axis_coordinates_for`.""" res = subject.axis_coordinates_for(mount, location_to_move) assert res == expected_axis_map diff --git a/api/tests/opentrons/protocol_engine/commands/robot/test_move_axes_relative_to.py b/api/tests/opentrons/protocol_engine/commands/robot/test_move_axes_relative_to.py index 9fb31ba4e82e..11c6e13b54fb 100644 --- a/api/tests/opentrons/protocol_engine/commands/robot/test_move_axes_relative_to.py +++ b/api/tests/opentrons/protocol_engine/commands/robot/test_move_axes_relative_to.py @@ -5,8 +5,7 @@ from opentrons.protocol_engine.execution import GantryMover from opentrons.protocol_engine.types import MotorAxis -from opentrons.hardware_control.protocols.types import FlexRobotType, OT2RobotType -from opentrons.types import Point, MountType +from opentrons.hardware_control.protocols.types import FlexRobotType from opentrons.protocol_engine.commands.command import SuccessData from opentrons.protocol_engine.commands.robot.move_axes_relative import ( diff --git a/api/tests/opentrons/protocol_engine/commands/robot/test_move_axes_to.py b/api/tests/opentrons/protocol_engine/commands/robot/test_move_axes_to.py index f85d1b24058b..3caa8b03ec86 100644 --- a/api/tests/opentrons/protocol_engine/commands/robot/test_move_axes_to.py +++ b/api/tests/opentrons/protocol_engine/commands/robot/test_move_axes_to.py @@ -5,8 +5,7 @@ from opentrons.protocol_engine.execution import GantryMover from opentrons.protocol_engine.types import MotorAxis -from opentrons.hardware_control.protocols.types import FlexRobotType, OT2RobotType -from opentrons.types import Point, MountType +from opentrons.hardware_control.protocols.types import FlexRobotType from opentrons.protocol_engine.commands.command import SuccessData from opentrons.protocol_engine.commands.robot.move_axes_to import ( diff --git a/api/tests/opentrons/protocol_engine/execution/test_gantry_mover.py b/api/tests/opentrons/protocol_engine/execution/test_gantry_mover.py index 7712faf14080..7baaf4df4810 100644 --- a/api/tests/opentrons/protocol_engine/execution/test_gantry_mover.py +++ b/api/tests/opentrons/protocol_engine/execution/test_gantry_mover.py @@ -705,5 +705,6 @@ async def test_virtual_move_axes( relative_move: bool, expected_position: Dict[MotorAxis, float], ) -> None: + """It should simulate moving a set of axis by a certain distance.""" pos = await virtual_subject.move_axes(axis_map, critical_point, 100, relative_move) assert pos == expected_position diff --git a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py index f9fafc04945b..4f06b7597ce4 100644 --- a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py +++ b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py @@ -619,15 +619,14 @@ async def move_tip_motor_relative_ot3( if not api.hardware_pipettes[OT3Mount.LEFT.to_mount()]: raise RuntimeError("No pipette found on LEFT mount") - current_gear_pos_float = api._backend.gear_motor_position or 0.0 - current_gear_pos_dict = {Axis.Q: current_gear_pos_float} - target_pos_dict = {Axis.Q: current_gear_pos_float + distance} + current_gear_pos = api._backend.gear_motor_position or 0.0 + target_pos = {Axis.Q: current_gear_pos + distance} if speed is not None and distance < 0: speed *= -1 _move_coro = api._backend.tip_action( - current_gear_pos_dict, [(target_pos_dict, speed or 400)] + current_gear_pos, [(target_pos, speed or 400)] ) if motor_current is None: await _move_coro