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(app): handle dtwiz after estop #16168

Merged
merged 9 commits into from
Sep 3, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ async def disengage_axes(self, which: List[Axis]) -> None:
"""Disengage some axes."""
...

async def engage_axes(self, which: List[Axis]) -> None:
"""Engage some axes."""
...

async def retract(self, mount: MountArgType, margin: float = 10) -> None:
"""Pull the specified mount up to its home position.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@
unsafe.UnsafeBlowOutInPlace,
unsafe.UnsafeDropTipInPlace,
unsafe.UpdatePositionEstimators,
unsafe.UnsafeEngageAxes,
],
Field(discriminator="commandType"),
]
Expand Down Expand Up @@ -463,6 +464,7 @@
unsafe.UnsafeBlowOutInPlaceParams,
unsafe.UnsafeDropTipInPlaceParams,
unsafe.UpdatePositionEstimatorsParams,
unsafe.UnsafeEngageAxesParams,
]

CommandType = Union[
Expand Down Expand Up @@ -533,6 +535,7 @@
unsafe.UnsafeBlowOutInPlaceCommandType,
unsafe.UnsafeDropTipInPlaceCommandType,
unsafe.UpdatePositionEstimatorsCommandType,
unsafe.UnsafeEngageAxesCommandType,
]

CommandCreate = Annotated[
Expand Down Expand Up @@ -604,6 +607,7 @@
unsafe.UnsafeBlowOutInPlaceCreate,
unsafe.UnsafeDropTipInPlaceCreate,
unsafe.UpdatePositionEstimatorsCreate,
unsafe.UnsafeEngageAxesCreate,
],
Field(discriminator="commandType"),
]
Expand Down Expand Up @@ -676,6 +680,7 @@
unsafe.UnsafeBlowOutInPlaceResult,
unsafe.UnsafeDropTipInPlaceResult,
unsafe.UpdatePositionEstimatorsResult,
unsafe.UnsafeEngageAxesResult,
]

# todo(mm, 2024-06-12): Ideally, command return types would have specific
Expand Down
14 changes: 14 additions & 0 deletions api/src/opentrons/protocol_engine/commands/unsafe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
UpdatePositionEstimatorsCreate,
)

from .unsafe_engage_axes import (
UnsafeEngageAxesCommandType,
UnsafeEngageAxesParams,
UnsafeEngageAxesResult,
UnsafeEngageAxes,
UnsafeEngageAxesCreate,
)

__all__ = [
# Unsafe blow-out-in-place command models
"UnsafeBlowOutInPlaceCommandType",
Expand All @@ -42,4 +50,10 @@
"UpdatePositionEstimatorsResult",
"UpdatePositionEstimators",
"UpdatePositionEstimatorsCreate",
# Unsafe engage axes
"UnsafeEngageAxesCommandType",
"UnsafeEngageAxesParams",
"UnsafeEngageAxesResult",
"UnsafeEngageAxes",
"UnsafeEngageAxesCreate",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Update position estimators payload, result, and implementaiton."""

from __future__ import annotations
from pydantic import BaseModel, Field
from typing import TYPE_CHECKING, Optional, List, Type
from typing_extensions import Literal

from ...types import MotorAxis
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
from ...errors.error_occurrence import ErrorOccurrence
from ...resources import ensure_ot3_hardware

from opentrons.hardware_control import HardwareControlAPI

if TYPE_CHECKING:
from ...execution import GantryMover


UnsafeEngageAxesCommandType = Literal["unsafe/engageAxes"]


class UnsafeEngageAxesParams(BaseModel):
"""Payload required for an UnsafeEngageAxes command."""

axes: List[MotorAxis] = Field(..., description="The axes for which to enable.")


class UnsafeEngageAxesResult(BaseModel):
"""Result data from the execution of an UnsafeEngageAxes command."""


class UnsafeEngageAxesImplementation(
AbstractCommandImpl[
UnsafeEngageAxesParams,
SuccessData[UnsafeEngageAxesResult, None],
]
):
"""Enable axes command implementation."""

def __init__(
self,
hardware_api: HardwareControlAPI,
gantry_mover: GantryMover,
**kwargs: object,
) -> None:
self._hardware_api = hardware_api
self._gantry_mover = gantry_mover

async def execute(
self, params: UnsafeEngageAxesParams
) -> SuccessData[UnsafeEngageAxesResult, None]:
"""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
]
)
return SuccessData(public=UnsafeEngageAxesResult(), private=None)


class UnsafeEngageAxes(
BaseCommand[UnsafeEngageAxesParams, UnsafeEngageAxesResult, ErrorOccurrence]
):
"""UnsafeEngageAxes command model."""

commandType: UnsafeEngageAxesCommandType = "unsafe/engageAxes"
params: UnsafeEngageAxesParams
result: Optional[UnsafeEngageAxesResult]

_ImplementationCls: Type[
UnsafeEngageAxesImplementation
] = UnsafeEngageAxesImplementation


class UnsafeEngageAxesCreate(BaseCommandCreate[UnsafeEngageAxesParams]):
"""UnsafeEngageAxes command request model."""

commandType: UnsafeEngageAxesCommandType = "unsafe/engageAxes"
params: UnsafeEngageAxesParams

_CommandCls: Type[UnsafeEngageAxes] = UnsafeEngageAxes
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Test update-position-estimator commands."""
from decoy import Decoy

from opentrons.protocol_engine.commands.unsafe.unsafe_engage_axes import (
UnsafeEngageAxesParams,
UnsafeEngageAxesResult,
UnsafeEngageAxesImplementation,
)
from opentrons.protocol_engine.commands.command import SuccessData
from opentrons.protocol_engine.execution import GantryMover
from opentrons.protocol_engine.types import MotorAxis
from opentrons.hardware_control import OT3HardwareControlAPI
from opentrons.hardware_control.types import Axis


async def test_engage_axes_implementation(
decoy: Decoy, ot3_hardware_api: OT3HardwareControlAPI, gantry_mover: GantryMover
) -> None:
"""Test EngageAxes command execution."""
subject = UnsafeEngageAxesImplementation(
hardware_api=ot3_hardware_api, gantry_mover=gantry_mover
)

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
)
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
)
decoy.when(
await ot3_hardware_api.update_axis_position_estimations(
[Axis.Z_L, Axis.P_L, Axis.X, Axis.Y]
)
).then_return(None)

result = await subject.execute(data)

assert result == SuccessData(public=UnsafeEngageAxesResult(), private=None)

decoy.verify(
await ot3_hardware_api.engage_axes([Axis.Z_L, Axis.P_L, Axis.X, Axis.Y]),
)
9 changes: 4 additions & 5 deletions app/src/App/DesktopApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { ProtocolTimeline } from '../pages/Protocols/ProtocolDetails/ProtocolTim
import { PortalRoot as ModalPortalRoot } from './portal'
import { DesktopAppFallback } from './DesktopAppFallback'

import type { RouteProps, DesktopRouteParams } from './types'
import type { RouteProps } from './types'

export const DesktopApp = (): JSX.Element => {
useSoftwareUpdatePoll()
Expand Down Expand Up @@ -158,11 +158,10 @@ export const DesktopApp = (): JSX.Element => {
}

function RobotControlTakeover(): JSX.Element | null {
const deviceRouteMatch = useMatch('/devices/:robotName')
const params = deviceRouteMatch?.params as DesktopRouteParams
const robotName = params?.robotName
const deviceRouteMatch = useMatch('/devices/:robotName/*')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super funny/weird change between RR versions.

It looks like this is the only place in the app we use useMatch.

const params = deviceRouteMatch?.params
const robotName = params?.robotName ?? null
const robot = useRobot(robotName)

if (deviceRouteMatch == null || robot == null || robotName == null)
return null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export function useDropTipCommands({
)
return chainRunCommands(
isFlex
? [UPDATE_ESTIMATORS_EXCEPT_PLUNGERS, moveToAACommand]
? [ENGAGE_AXES, UPDATE_ESTIMATORS_EXCEPT_PLUNGERS, moveToAACommand]
: [moveToAACommand],
true
)
Expand Down Expand Up @@ -288,6 +288,13 @@ const HOME: CreateCommand = {
params: {},
}

const ENGAGE_AXES: CreateCommand = {
commandType: 'unsafe/engageAxes' as const,
params: {
axes: ['leftZ', 'rightZ', 'x', 'y', 'leftPlunger', 'rightPlunger'],
},
}

const HOME_EXCEPT_PLUNGERS: CreateCommand = {
commandType: 'home' as const,
params: { axes: ['leftZ', 'rightZ', 'x', 'y'] },
Expand All @@ -298,6 +305,11 @@ const UPDATE_ESTIMATORS_EXCEPT_PLUNGERS: CreateCommand = {
params: { axes: ['leftZ', 'rightZ', 'x', 'y'] },
}

const UPDATE_PLUNGER_ESTIMATORS: CreateCommand = {
commandType: 'unsafe/updatePositionEstimators' as const,
params: { axes: ['leftPlunger', 'rightPlunger'] },
}

const buildDropTipInPlaceCommand = (
isFlex: boolean,
pipetteId: string | null
Expand All @@ -323,6 +335,8 @@ const buildBlowoutCommands = (
): CreateCommand[] =>
isFlex
? [
ENGAGE_AXES,
UPDATE_PLUNGER_ESTIMATORS,
{
commandType: 'unsafe/blowOutInPlace',
params: {
Expand All @@ -342,6 +356,8 @@ const buildBlowoutCommands = (
},
]
: [
ENGAGE_AXES,
UPDATE_PLUNGER_ESTIMATORS,
{
commandType: 'blowOutInPlace',
params: {
Expand Down
Loading
Loading