Skip to content

Commit

Permalink
fix(api): disengage Z motors during 96-channel pipettes bracket detac…
Browse files Browse the repository at this point in the history
…h/attach flows (#14999)

<!--
Thanks for taking the time to open a pull request! Please make sure
you've read the "Opening Pull Requests" section of our Contributing
Guide:


https://github.com/Opentrons/opentrons/blob/edge/CONTRIBUTING.md#opening-pull-requests

To ensure your code is reviewed quickly and thoroughly, please fill out
the sections below to the best of your ability!
-->

# Overview
This PR makes sure we are engaging ebrakes when we're
attaching/detaching the 96-channel pipette and mount bracket.
On top of that, we can now use retract instead of using a movement Point
just to move the z up.
Fixes [RQA-2631](https://opentrons.atlassian.net/browse/RQA-2631).


[RQA-2631]:
https://opentrons.atlassian.net/browse/RQA-2631?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
  • Loading branch information
ahiuchingau authored Apr 29, 2024
1 parent f0d880a commit 0eb749f
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,46 +80,25 @@ async def execute(
ot3_api = ensure_ot3_hardware(
self._hardware_api,
)
# the 96-channel mount is disengaged during gripper calibration and
# must be homed before the gantry position can be called
max_height_z_tip = ot3_api.get_instrument_max_height(Mount.LEFT)
# disengage the gripper z mount if present and enabled
await ot3_api.prepare_for_mount_movement(Mount.LEFT)
current_position_mount = await ot3_api.gantry_position(

await ot3_api.retract(Mount.LEFT)
current_pos = await ot3_api.gantry_position(
Mount.LEFT, critical_point=CriticalPoint.MOUNT
)
max_height_z_mount = ot3_api.get_instrument_max_height(
Mount.LEFT, critical_point=CriticalPoint.MOUNT
await ot3_api.move_to(
mount=Mount.LEFT,
abs_position=Point(x=_ATTACH_POINT.x, y=_ATTACH_POINT.y, z=current_pos.z),
critical_point=CriticalPoint.MOUNT,
)
max_height_z_tip = ot3_api.get_instrument_max_height(Mount.LEFT)
# avoid using motion planning waypoints because we do not need to move the z at this moment
movement_points = [
# move the z to the highest position
Point(
x=current_position_mount.x,
y=current_position_mount.y,
z=max_height_z_mount,
),
# move in x,y without going down the z
Point(x=_ATTACH_POINT.x, y=_ATTACH_POINT.y, z=max_height_z_mount),
]

await ot3_api.prepare_for_mount_movement(Mount.LEFT)
for movement in movement_points:
await ot3_api.move_to(
mount=Mount.LEFT,
abs_position=movement,
critical_point=CriticalPoint.MOUNT,
)

if params.mount != MountType.EXTENSION:

# disengage the gripper z to enable the e-brake, this prevents the gripper
# z from dropping when the right mount carriage gets released from the
# mount during 96-channel detach flow
if ot3_api.has_gripper():
await ot3_api.disengage_axes([Axis.Z_G])

if params.maintenancePosition == MaintenancePosition.ATTACH_INSTRUMENT:
mount_to_axis = Axis.by_mount(params.mount.to_hw_mount())
mount = params.mount.to_hw_mount()
mount_to_axis = Axis.by_mount(mount)
await ot3_api.prepare_for_mount_movement(mount)
await ot3_api.move_axes(
{
mount_to_axis: _INSTRUMENT_ATTACH_Z_POINT,
Expand All @@ -134,6 +113,7 @@ async def execute(
Axis.Z_R: max_motion_range + _RIGHT_MOUNT_Z_MARGIN,
}
)
await ot3_api.disengage_axes([Axis.Z_L, Axis.Z_R])

return MoveToMaintenancePositionResult()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Test for Calibration Set Up Position Implementation."""
from __future__ import annotations
from typing import TYPE_CHECKING, Mapping
from typing import TYPE_CHECKING

import pytest
from decoy import Decoy
Expand Down Expand Up @@ -32,128 +32,125 @@ def subject(


@pytest.mark.ot3_only
@pytest.mark.parametrize(
"maintenance_position, verify_axes",
[
(
MaintenancePosition.ATTACH_INSTRUMENT,
{Axis.Z_L: 400},
),
(
MaintenancePosition.ATTACH_PLATE,
{Axis.Z_L: 90, Axis.Z_R: 105},
),
],
)
async def test_calibration_move_to_location_implementation(
@pytest.mark.parametrize("mount_type", [MountType.LEFT, MountType.RIGHT])
async def test_calibration_move_to_location_implementatio_for_attach_instrument(
decoy: Decoy,
subject: MoveToMaintenancePositionImplementation,
state_view: StateView,
ot3_hardware_api: OT3API,
maintenance_position: MaintenancePosition,
verify_axes: Mapping[Axis, float],
mount_type: MountType,
) -> None:
"""Command should get a move to target location and critical point and should verify move_to call."""
params = MoveToMaintenancePositionParams(
mount=MountType.LEFT, maintenancePosition=maintenance_position
mount=mount_type, maintenancePosition=MaintenancePosition.ATTACH_INSTRUMENT
)

decoy.when(
await ot3_hardware_api.gantry_position(
Mount.LEFT, critical_point=CriticalPoint.MOUNT
)
).then_return(Point(x=1, y=2, z=3))

decoy.when(
ot3_hardware_api.get_instrument_max_height(
Mount.LEFT, critical_point=CriticalPoint.MOUNT
)
).then_return(250)
).then_return(Point(x=1, y=2, z=250))

decoy.when(ot3_hardware_api.get_instrument_max_height(Mount.LEFT)).then_return(300)

result = await subject.execute(params=params)
assert result == MoveToMaintenancePositionResult()

hw_mount = mount_type.to_hw_mount()
decoy.verify(
await ot3_hardware_api.move_to(
mount=Mount.LEFT,
abs_position=Point(x=1, y=2, z=250),
critical_point=CriticalPoint.MOUNT,
),
times=1,
)

decoy.verify(
await ot3_hardware_api.prepare_for_mount_movement(Mount.LEFT),
await ot3_hardware_api.retract(Mount.LEFT),
await ot3_hardware_api.move_to(
mount=Mount.LEFT,
abs_position=Point(x=0, y=110, z=250),
critical_point=CriticalPoint.MOUNT,
),
times=1,
)

decoy.verify(
await ot3_hardware_api.prepare_for_mount_movement(hw_mount),
await ot3_hardware_api.move_axes(
position=verify_axes,
position={Axis.by_mount(hw_mount): 400},
),
await ot3_hardware_api.disengage_axes(
[Axis.by_mount(hw_mount)],
),
times=1,
)

if params.maintenancePosition == MaintenancePosition.ATTACH_INSTRUMENT:
decoy.verify(
await ot3_hardware_api.disengage_axes(
list(verify_axes.keys()),
),
times=1,
)


@pytest.mark.ot3_only
async def test_calibration_move_to_location_implementation_for_gripper(
@pytest.mark.parametrize("mount_type", [MountType.LEFT, MountType.RIGHT])
async def test_calibration_move_to_location_implementatio_for_attach_plate(
decoy: Decoy,
subject: MoveToMaintenancePositionImplementation,
state_view: StateView,
ot3_hardware_api: OT3API,
mount_type: MountType,
) -> None:
"""Command should get a move to target location and critical point and should verify move_to call."""
params = MoveToMaintenancePositionParams(
mount=MountType.LEFT, maintenancePosition=MaintenancePosition.ATTACH_INSTRUMENT
mount=mount_type, maintenancePosition=MaintenancePosition.ATTACH_PLATE
)

decoy.when(
await ot3_hardware_api.gantry_position(
Mount.LEFT, critical_point=CriticalPoint.MOUNT
)
).then_return(Point(x=1, y=2, z=3))

decoy.when(
ot3_hardware_api.get_instrument_max_height(
Mount.LEFT, critical_point=CriticalPoint.MOUNT
)
).then_return(250)
).then_return(Point(x=1, y=2, z=250))

decoy.when(ot3_hardware_api.get_instrument_max_height(Mount.LEFT)).then_return(300)

result = await subject.execute(params=params)
assert result == MoveToMaintenancePositionResult()

decoy.verify(
await ot3_hardware_api.prepare_for_mount_movement(Mount.LEFT),
await ot3_hardware_api.retract(Mount.LEFT),
await ot3_hardware_api.move_to(
mount=Mount.LEFT,
abs_position=Point(x=1, y=2, z=250),
abs_position=Point(x=0, y=110, z=250),
critical_point=CriticalPoint.MOUNT,
),
times=1,
await ot3_hardware_api.move_axes(
position={
Axis.Z_L: 90,
Axis.Z_R: 105,
}
),
await ot3_hardware_api.disengage_axes(
[Axis.Z_L, Axis.Z_R],
),
)


@pytest.mark.ot3_only
async def test_calibration_move_to_location_implementation_for_gripper(
decoy: Decoy,
subject: MoveToMaintenancePositionImplementation,
state_view: StateView,
ot3_hardware_api: OT3API,
) -> None:
"""Command should get a move to target location and critical point and should verify move_to call."""
params = MoveToMaintenancePositionParams(
mount=MountType.EXTENSION,
maintenancePosition=MaintenancePosition.ATTACH_INSTRUMENT,
)

decoy.when(
await ot3_hardware_api.gantry_position(
Mount.LEFT, critical_point=CriticalPoint.MOUNT
)
).then_return(Point(x=1, y=2, z=250))
decoy.when(ot3_hardware_api.get_instrument_max_height(Mount.LEFT)).then_return(300)

result = await subject.execute(params=params)
assert result == MoveToMaintenancePositionResult()

decoy.verify(
await ot3_hardware_api.prepare_for_mount_movement(Mount.LEFT),
await ot3_hardware_api.retract(Mount.LEFT),
await ot3_hardware_api.move_to(
mount=Mount.LEFT,
abs_position=Point(x=0, y=110, z=250),
critical_point=CriticalPoint.MOUNT,
),
times=1,
)

decoy.verify(
Expand All @@ -162,7 +159,6 @@ async def test_calibration_move_to_location_implementation_for_gripper(
),
times=0,
)

decoy.verify(
await ot3_hardware_api.disengage_axes(
[Axis.Z_G],
Expand Down

0 comments on commit 0eb749f

Please sign in to comment.