Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(api): ignore non-present axes in unsafe cmds #16982

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions api/src/opentrons/hardware_control/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,16 @@ def engaged_axes(self) -> Dict[Axis, bool]:
async def disengage_axes(self, which: List[Axis]) -> None:
await self._backend.disengage_axes([ot2_axis_to_string(ax) for ax in which])

def axis_is_present(self, axis: Axis) -> bool:
is_ot2 = axis in Axis.ot2_axes()
if not is_ot2:
return False
if axis in Axis.pipette_axes():
mount = Axis.to_ot2_mount(axis)
if self.attached_pipettes.get(mount) is None:
return False
return True

@ExecutionManagerProvider.wait_for_running
async def _fast_home(self, axes: Sequence[str], margin: float) -> Dict[str, float]:
converted_axes = "".join(axes)
Expand Down
7 changes: 6 additions & 1 deletion api/src/opentrons/hardware_control/ot3api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1640,7 +1640,12 @@ async def disengage_axes(self, which: List[Axis]) -> None:
await self._backend.disengage_axes(which)

async def engage_axes(self, which: List[Axis]) -> None:
await self._backend.engage_axes(which)
await self._backend.engage_axes(
[axis for axis in which if self._backend.axis_is_present(axis)]
)

def axis_is_present(self, axis: Axis) -> bool:
return self._backend.axis_is_present(axis)

async def get_limit_switches(self) -> Dict[Axis, bool]:
res = await self._backend.get_limit_switches()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Dict, Optional
from typing_extensions import Protocol

from ..types import SubSystem, SubSystemState
from ..types import SubSystem, SubSystemState, Axis


class HardwareManager(Protocol):
Expand Down Expand Up @@ -45,3 +45,7 @@ def attached_subsystems(self) -> Dict[SubSystem, SubSystemState]:
async def get_serial_number(self) -> Optional[str]:
"""Get the robot serial number, if provisioned. If not provisioned, will be None."""
...

def axis_is_present(self, axis: Axis) -> bool:
"""Get whether a motor axis is present on the machine."""
...
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,7 @@ async def execute(
"""Enable exes."""
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
await ot3_hardware_api.engage_axes(
[
self._gantry_mover.motor_axis_to_hardware_axis(axis)
for axis in params.axes
]
self._gantry_mover.motor_axes_to_present_hardware_axes(params.axes)
)
return SuccessData(
public=UnsafeEngageAxesResult(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@ async def execute(
"""Update axis position estimators from their encoders."""
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
await ot3_hardware_api.update_axis_position_estimations(
[
self._gantry_mover.motor_axis_to_hardware_axis(axis)
for axis in params.axes
]
self._gantry_mover.motor_axes_to_present_hardware_axes(params.axes)
)
return SuccessData(
public=UpdatePositionEstimatorsResult(),
Expand Down
26 changes: 26 additions & 0 deletions api/src/opentrons/protocol_engine/execution/gantry_mover.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ def motor_axis_to_hardware_axis(self, motor_axis: MotorAxis) -> HardwareAxis:
"""Transform an engine motor axis into a hardware axis."""
...

def motor_axes_to_present_hardware_axes(
self, motor_axes: List[MotorAxis]
) -> List[HardwareAxis]:
"""Transform a list of engine axes into a list of hardware axes, filtering out non-present axes."""
...


class HardwareGantryMover(GantryMover):
"""Hardware API based gantry movement handler."""
Expand All @@ -93,6 +99,18 @@ def __init__(self, hardware_api: HardwareControlAPI, state_view: StateView) -> N
self._hardware_api = hardware_api
self._state_view = state_view

def motor_axes_to_present_hardware_axes(
self, motor_axes: List[MotorAxis]
) -> List[HardwareAxis]:
"""Get hardware axes from engine axes while filtering out non-present axes."""
return [
self.motor_axis_to_hardware_axis(motor_axis)
for motor_axis in motor_axes
if self._hardware_api.axis_is_present(
self.motor_axis_to_hardware_axis(motor_axis)
)
]

def motor_axis_to_hardware_axis(self, motor_axis: MotorAxis) -> HardwareAxis:
"""Transform an engine motor axis into a hardware axis."""
return _MOTOR_AXIS_TO_HARDWARE_AXIS[motor_axis]
Expand Down Expand Up @@ -313,6 +331,14 @@ async def prepare_for_mount_movement(self, mount: Mount) -> None:
"""Retract the 'idle' mount if necessary."""
pass

def motor_axes_to_present_hardware_axes(
self, motor_axes: List[MotorAxis]
) -> List[HardwareAxis]:
"""Get present hardware axes from a list of engine axes. In simulation, all axes are present."""
return [
self.motor_axis_to_hardware_axis(motor_axis) for motor_axis in motor_axes
]


def create_gantry_mover(
state_view: StateView, hardware_api: HardwareControlAPI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,28 @@ async def test_engage_axes_implementation(
)

data = UnsafeEngageAxesParams(
axes=[MotorAxis.LEFT_Z, MotorAxis.LEFT_PLUNGER, MotorAxis.X, MotorAxis.Y]
)

decoy.when(gantry_mover.motor_axis_to_hardware_axis(MotorAxis.LEFT_Z)).then_return(
Axis.Z_L
axes=[
MotorAxis.LEFT_Z,
MotorAxis.LEFT_PLUNGER,
MotorAxis.X,
MotorAxis.Y,
MotorAxis.RIGHT_Z,
MotorAxis.RIGHT_PLUNGER,
]
)
decoy.when(
gantry_mover.motor_axis_to_hardware_axis(MotorAxis.LEFT_PLUNGER)
).then_return(Axis.P_L)
decoy.when(gantry_mover.motor_axis_to_hardware_axis(MotorAxis.X)).then_return(
Axis.X
)
decoy.when(gantry_mover.motor_axis_to_hardware_axis(MotorAxis.Y)).then_return(
Axis.Y
)
gantry_mover.motor_axes_to_present_hardware_axes(
[
MotorAxis.LEFT_Z,
MotorAxis.LEFT_PLUNGER,
MotorAxis.X,
MotorAxis.Y,
MotorAxis.RIGHT_Z,
MotorAxis.RIGHT_PLUNGER,
]
)
).then_return([Axis.Z_L, Axis.P_L, Axis.X, Axis.Y])

decoy.when(
await ot3_hardware_api.update_axis_position_estimations(
[Axis.Z_L, Axis.P_L, Axis.X, Axis.Y]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,27 @@ async def test_update_position_estimators_implementation(
)

data = UpdatePositionEstimatorsParams(
axes=[MotorAxis.LEFT_Z, MotorAxis.LEFT_PLUNGER, MotorAxis.X, MotorAxis.Y]
)

decoy.when(gantry_mover.motor_axis_to_hardware_axis(MotorAxis.LEFT_Z)).then_return(
Axis.Z_L
axes=[
MotorAxis.LEFT_Z,
MotorAxis.LEFT_PLUNGER,
MotorAxis.X,
MotorAxis.Y,
MotorAxis.RIGHT_Z,
MotorAxis.RIGHT_PLUNGER,
]
)
decoy.when(
gantry_mover.motor_axis_to_hardware_axis(MotorAxis.LEFT_PLUNGER)
).then_return(Axis.P_L)
decoy.when(gantry_mover.motor_axis_to_hardware_axis(MotorAxis.X)).then_return(
Axis.X
)
decoy.when(gantry_mover.motor_axis_to_hardware_axis(MotorAxis.Y)).then_return(
Axis.Y
)
gantry_mover.motor_axes_to_present_hardware_axes(
[
MotorAxis.LEFT_Z,
MotorAxis.LEFT_PLUNGER,
MotorAxis.X,
MotorAxis.Y,
MotorAxis.RIGHT_Z,
MotorAxis.RIGHT_PLUNGER,
]
)
).then_return([Axis.Z_L, Axis.P_L, Axis.X, Axis.Y])
decoy.when(
await ot3_hardware_api.update_axis_position_estimations(
[Axis.Z_L, Axis.P_L, Axis.X, Axis.Y]
Expand Down