diff --git a/api/src/opentrons/hardware_control/api.py b/api/src/opentrons/hardware_control/api.py index ea4c44265c3..26589f98314 100644 --- a/api/src/opentrons/hardware_control/api.py +++ b/api/src/opentrons/hardware_control/api.py @@ -169,6 +169,14 @@ def _update_door_state(self, door_state: DoorState) -> None: def _reset_last_mount(self) -> None: self._last_moved_mount = None + def get_deck_from_machine(self, machine_pos: Dict[Axis, float]) -> Dict[Axis, float]: + return deck_from_machine( + machine_pos=machine_pos, + attitude=self._robot_calibration.deck_calibration.attitude, + offset=top_types.Point(0, 0, 0), + robot_type=cast(RobotType, "OT-2 Standard"), + ) + @classmethod async def build_hardware_controller( # noqa: C901 cls, @@ -657,12 +665,7 @@ async def home(self, axes: Optional[List[Axis]] = None) -> None: async with self._motion_lock: if smoothie_gantry: smoothie_pos.update(await self._backend.home(smoothie_gantry)) - self._current_position = deck_from_machine( - machine_pos=self._axis_map_from_string_map(smoothie_pos), - attitude=self._robot_calibration.deck_calibration.attitude, - offset=top_types.Point(0, 0, 0), - robot_type=cast(RobotType, "OT-2 Standard"), - ) + self._current_position = self.get_deck_from_machine(self._axis_map_from_string_map(smoothie_pos)) for plunger in plungers: await self._do_plunger_home(axis=plunger, acquire_lock=False) @@ -703,12 +706,7 @@ async def current_position( async with self._motion_lock: if refresh: smoothie_pos = await self._backend.update_position() - self._current_position = deck_from_machine( - machine_pos=self._axis_map_from_string_map(smoothie_pos), - attitude=self._robot_calibration.deck_calibration.attitude, - offset=top_types.Point(0, 0, 0), - robot_type=cast(RobotType, "OT-2 Standard"), - ) + self._current_position = self.get_deck_from_machine(self._axis_map_from_string_map(smoothie_pos)) if mount == top_types.Mount.RIGHT: offset = top_types.Point(0, 0, 0) else: @@ -938,12 +936,7 @@ async def retract_axis(self, axis: Axis, margin: float = 10) -> None: async with self._motion_lock: smoothie_pos = await self._fast_home(smoothie_ax, margin) - self._current_position = deck_from_machine( - machine_pos=self._axis_map_from_string_map(smoothie_pos), - attitude=self._robot_calibration.deck_calibration.attitude, - offset=top_types.Point(0, 0, 0), - robot_type=cast(RobotType, "OT-2 Standard"), - ) + self._current_position = self.get_deck_from_machine(self._axis_map_from_string_map(smoothie_pos)) # Gantry/frame (i.e. not pipette) config API @property @@ -1261,12 +1254,7 @@ async def drop_tip(self, mount: top_types.Mount, home_after: bool = True) -> Non axes=[ot2_axis_to_string(ax) for ax in move.home_axes], margin=move.home_after_safety_margin, ) - self._current_position = deck_from_machine( - machine_pos=self._axis_map_from_string_map(smoothie_pos), - attitude=self._robot_calibration.deck_calibration.attitude, - offset=top_types.Point(0, 0, 0), - robot_type=cast(RobotType, "OT-2 Standard"), - ) + self._current_position = self.get_deck_from_machine(self._axis_map_from_string_map(smoothie_pos)) for shake in spec.shake_moves: await self.move_rel(mount, shake[0], speed=shake[1]) diff --git a/api/src/opentrons/hardware_control/backends/ot3controller.py b/api/src/opentrons/hardware_control/backends/ot3controller.py index 95967e110bf..2a4d75de509 100644 --- a/api/src/opentrons/hardware_control/backends/ot3controller.py +++ b/api/src/opentrons/hardware_control/backends/ot3controller.py @@ -668,8 +668,12 @@ def _build_move_node_axis_runner( ) def _build_move_gear_axis_runner( - self, possible_q_axis_origin, possible_q_axis_target, speed, nodes_in_moves_only - ) -> Optional[MoveGroupRunner]: + self, + possible_q_axis_origin: Optional[float], + possible_q_axis_target: Optional[float], + speed: float, + nodes_in_moves_only: bool, + ) -> Tuple[Optional[MoveGroupRunner], bool]: if possible_q_axis_origin is None or possible_q_axis_target is None: return None, True tip_motor_move_group = self._build_tip_action_group( @@ -744,7 +748,9 @@ async def move( gather_moving_nodes, all_moving_nodes ) - async def _runner_coroutine(runner: MoveGroupRunner, is_gear_move: bool): + async def _runner_coroutine( + runner: MoveGroupRunner, is_gear_move: bool + ) -> Tuple[Dict[NodeId, MotorPositionStatus], bool]: positions = await runner.run(can_messenger=self._messenger) return positions, is_gear_move diff --git a/api/src/opentrons/hardware_control/motion_utilities.py b/api/src/opentrons/hardware_control/motion_utilities.py index d64e253d528..dd59437f7dc 100644 --- a/api/src/opentrons/hardware_control/motion_utilities.py +++ b/api/src/opentrons/hardware_control/motion_utilities.py @@ -84,6 +84,7 @@ def target_position_from_absolute( ) primary_cp = get_critical_point(mount) primary_z = Axis.by_mount(mount) + target_position = OrderedDict( ( (Axis.X, abs_position.x - offset.x - primary_cp.x), @@ -131,14 +132,15 @@ def target_axis_map_from_absolute( primary_z = Axis.by_mount(primary_mount) target_position = OrderedDict() - log.info(f"The primary critical point {primary_cp} and primary_mount {primary_mount} and type {type(primary_cp)}") - log.info(f"The offset {offset} and type is {type(offset)}") if Axis.X in keys_for_target_position: target_position[Axis.X] = axis_map[Axis.X] - offset.x - primary_cp.x if Axis.Y in keys_for_target_position: target_position[Axis.Y] = axis_map[Axis.Y] - offset.y - primary_cp.y if primary_z in keys_for_target_position: - target_position[primary_z] = axis_map[primary_z] - offset.z - primary_cp.z + # Since this function is intended to be used in conjunction with `API.move_axes` + # we must leave out the carriage offset subtraction from the target position as + # `move_axes` already does this calculation. + target_position[primary_z] = axis_map[primary_z] - primary_cp.z target_position.update( {ax: val for ax, val in axis_map.items() if ax not in Axis.gantry_axes()} @@ -158,6 +160,8 @@ def target_axis_map_from_relative( if ax in axis_map.keys() ) ) + log.info(f"Current position {current_position} and axis map delta {axis_map}") + log.info(f"Relative move target {target_position}") return target_position diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index 8244c3e1181..cd77735019a 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -352,7 +352,7 @@ def _update_estop_state(self, event: HardwareEvent) -> "List[Future[None]]": def _reset_last_mount(self) -> None: self._last_moved_mount = None - def _deck_from_machine(self, machine_pos: Dict[Axis, float]) -> Dict[Axis, float]: + def get_deck_from_machine(self, machine_pos: Dict[Axis, float]) -> Dict[Axis, float]: return deck_from_machine( machine_pos=machine_pos, attitude=self._robot_calibration.deck_calibration.attitude, @@ -1020,14 +1020,14 @@ async def _refresh_jaw_state(self) -> None: async def _cache_current_position(self) -> Dict[Axis, float]: """Cache current position from backend and return in absolute deck coords.""" - self._current_position = self._deck_from_machine( + self._current_position = self.get_deck_from_machine( await self._backend.update_position() ) return self._current_position async def _cache_encoder_position(self) -> Dict[Axis, float]: """Cache encoder position from backend and return in absolute deck coords.""" - self._encoder_position = self._deck_from_machine( + self._encoder_position = self.get_deck_from_machine( await self._backend.update_encoder_position() ) if self.has_gripper(): @@ -2558,7 +2558,7 @@ def get_instrument_max_height( mount: Union[top_types.Mount, OT3Mount], critical_point: Optional[CriticalPoint] = None, ) -> float: - carriage_pos = self._deck_from_machine(self._backend.home_position()) + carriage_pos = self.get_deck_from_machine(self._backend.home_position()) pos_at_home = self._effector_pos_from_carriage_pos( OT3Mount.from_mount(mount), carriage_pos, critical_point ) @@ -2653,7 +2653,7 @@ async def _liquid_probe_pass( ) machine_pos = await self._backend.update_position() machine_pos[Axis.by_mount(mount)] = end_z - deck_end_z = self._deck_from_machine(machine_pos)[Axis.by_mount(mount)] + deck_end_z = self.get_deck_from_machine(machine_pos)[Axis.by_mount(mount)] offset = offset_for_mount( mount, top_types.Point(*self._config.left_mount_offset), diff --git a/api/src/opentrons/hardware_control/protocols/liquid_handler.py b/api/src/opentrons/hardware_control/protocols/liquid_handler.py index 8baa786dc9f..505fbaad9aa 100644 --- a/api/src/opentrons/hardware_control/protocols/liquid_handler.py +++ b/api/src/opentrons/hardware_control/protocols/liquid_handler.py @@ -1,6 +1,8 @@ -from typing import Optional +from typing import Optional, Dict from typing_extensions import Protocol +from opentrons.types import Point +from opentrons.hardware_control.types import CriticalPoint, Axis from .types import MountArgType, CalibrationType, ConfigType from .instrument_configurer import InstrumentConfigurer @@ -16,6 +18,22 @@ class LiquidHandler( Calibratable[CalibrationType], Protocol[CalibrationType, MountArgType, ConfigType], ): + def critical_point_for( + self, + mount: MountArgType, + cp_override: Optional[CriticalPoint] = None, + ) -> Point: + """ + Determine the current critical point for the specified mount. + + :param mount: A robot mount that the instrument is on. + :param cp_override: The critical point override to use. + + If no critical point override is specified, the robot defaults to nozzle location `A1` or the mount critical point. + :return: Point. + """ + ... + async def update_nozzle_configuration_for_mount( self, mount: MountArgType, 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 576ac339166..8606ba8cc7c 100644 --- a/api/tests/opentrons/protocol_engine/execution/test_gantry_mover.py +++ b/api/tests/opentrons/protocol_engine/execution/test_gantry_mover.py @@ -472,33 +472,33 @@ async def test_home_z( ], argvalues=[ [ - {MotorAxis.X: 10, MotorAxis.Y: 15, MotorAxis.RIGHT_Z: 20}, - {MotorAxis.X: 2, MotorAxis.Y: 1, MotorAxis.RIGHT_Z: 1}, + {MotorAxis.X: 10.0, MotorAxis.Y: 15.0, MotorAxis.RIGHT_Z: 20.0}, + {MotorAxis.X: 2.0, MotorAxis.Y: 1.0, MotorAxis.RIGHT_Z: 1.0}, False, Mount.RIGHT, - {HardwareAxis.X: -2, HardwareAxis.Y: 4, HardwareAxis.A: 9}, - {HardwareAxis.X: -2, HardwareAxis.Y: 4, HardwareAxis.A: 9}, + {HardwareAxis.X: -2.0, HardwareAxis.Y: 4.0, HardwareAxis.A: 9.0}, + {HardwareAxis.X: -2.0, HardwareAxis.Y: 4.0, HardwareAxis.A: 9.0}, ], [ - {MotorAxis.RIGHT_Z: 20}, + {MotorAxis.RIGHT_Z: 20.0}, None, True, Mount.RIGHT, - {HardwareAxis.A: 30}, + {HardwareAxis.A: 30.0}, { - HardwareAxis.X: 10, - HardwareAxis.Y: 15, - HardwareAxis.Z: 10, - HardwareAxis.A: 30, + HardwareAxis.X: 10.0, + HardwareAxis.Y: 15.0, + HardwareAxis.Z: 10.0, + HardwareAxis.A: 30.0, }, ], [ - {MotorAxis.CLAMP_JAW_96_CHANNEL: 10}, + {MotorAxis.CLAMP_JAW_96_CHANNEL: 10.0}, None, False, Mount.LEFT, - {HardwareAxis.Q: 10}, - {HardwareAxis.Q: 10}, + {HardwareAxis.Q: 10.0}, + {HardwareAxis.Q: 10.0}, ], ], ) @@ -514,14 +514,14 @@ async def test_move_axes( final_position: Dict[HardwareAxis, float], ) -> None: curr_pos = { - HardwareAxis.X: 10, - HardwareAxis.Y: 15, - HardwareAxis.Z: 10, - HardwareAxis.A: 10, + HardwareAxis.X: 10.0, + HardwareAxis.Y: 15.0, + HardwareAxis.Z: 10.0, + HardwareAxis.A: 10.0, } call_count = 0 - def _current_position(mount, refresh) -> Dict[HardwareAxis, float]: + def _current_position(mount: Mount, refresh: bool) -> Dict[HardwareAxis, float]: nonlocal call_count nonlocal curr_pos nonlocal final_position @@ -543,13 +543,13 @@ def _current_position(mount, refresh) -> Dict[HardwareAxis, float]: Point(0.5, 0.5, 0.5) ) - decoy.when(mock_hardware_api._deck_from_machine(curr_pos)).then_return(curr_pos) + decoy.when(mock_hardware_api.get_deck_from_machine(curr_pos)).then_return(curr_pos) - decoy.when(mock_hardware_api._deck_from_machine(final_position)).then_return( + decoy.when(mock_hardware_api.get_deck_from_machine(final_position)).then_return( final_position ) if not critical_point: - decoy.when(mock_hardware_api._critical_point_for(expected_mount)).then_return( + decoy.when(mock_hardware_api.critical_point_for(expected_mount)).then_return( Point(1, 1, 1) )