Skip to content

Commit

Permalink
Merge branch 'edge' into fix_vitest-unit-test
Browse files Browse the repository at this point in the history
  • Loading branch information
koji committed Oct 3, 2024
2 parents ff4898e + 8f81026 commit 730d6b4
Show file tree
Hide file tree
Showing 126 changed files with 8,497 additions and 1,019 deletions.
17 changes: 17 additions & 0 deletions app/src/assets/localization/en/error_recovery.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,35 @@
"continue": "Continue",
"continue_run_now": "Continue run now",
"continue_to_drop_tip": "Continue to drop tip",
"ensure_lw_is_accurately_placed": "Ensure labware is accurately placed in the slot to prevent further errors",
"error": "Error",
"error_details": "Error details",
"error_on_robot": "Error on {{robot}}",
"failed_dispense_step_not_completed": "<block>The failed dispense step will not be completed. The run will continue from the next step with the attached tips.</block><block>Close the robot door before proceeding.</block>",
"failed_step": "Failed step",
"first_is_gripper_holding_labware": "First, is the gripper holding labware?",
"go_back": "Go back",
"gripper_error": "Gripper error",
"gripper_errors_occur_when": "Gripper errors occur when the gripper stalls or collides with another object on the deck and are usually caused by improperly placed labware or inaccurate labware offsets",
"gripper_releasing_labware": "Gripper releasing labware",
"gripper_will_release_in_s": "Gripper will release labware in {{seconds}} seconds",
"homing_pipette_dangerous": "Homing the <bold>{{mount}} pipette</bold> with liquid in the tips may damage it. You must remove all tips before using the pipette again.",
"if_issue_persists_gripper_error": " If the issue persists, cancel the run and rerun gripper calibration",
"if_issue_persists_overpressure": " If the issue persists, cancel the run and make the necessary changes to the protocol",
"if_issue_persists_tip_not_detected": " If the issue persists, cancel the run and initiate Labware Position Check",
"if_tips_are_attached": "If tips are attached, you can choose to blow out any aspirated liquid and drop tips before the run is terminated.",
"ignore_all_errors_of_this_type": "Ignore all errors of this type",
"ignore_error_and_skip": "Ignore error and skip to next step",
"ignore_only_this_error": "Ignore only this error",
"ignore_similar_errors_later_in_run": "Ignore similar errors later in the run?",
"labware_released_from_current_height": "The labware will be released from its current height",
"launch_recovery_mode": "Launch Recovery Mode",
"manually_fill_liquid_in_well": "Manually fill liquid in well {{well}}",
"manually_fill_well_and_skip": "Manually fill well and skip to next step",
"manually_move_lw_and_skip": "Manually move labware and skip to next step",
"manually_move_lw_on_deck": "Manually move labware on deck",
"manually_replace_lw_and_retry": "Manually replace labware on deck and retry step",
"manually_replace_lw_on_deck": "Manually replace labware on deck",
"next_step": "Next step",
"next_try_another_action": "Next, you can try another recovery action or cancel the run.",
"no_liquid_detected": "No liquid detected",
Expand All @@ -46,6 +58,8 @@
"recovery_action_failed": "{{action}} failed",
"recovery_mode": "Recovery Mode",
"recovery_mode_explanation": "<block>Recovery Mode provides you with guided and manual controls for handling errors at runtime.</block><br/><block>You can make changes to ensure the step in progress when the error occurred can be completed or choose to cancel the protocol. When changes are made and no subsequent errors are detected, the method completes. Depending on the conditions that caused the error, you will only be provided with appropriate options.</block>",
"release": "Release",
"release_labware_from_gripper": "Release labware from gripper",
"remove_any_attached_tips": "Remove any attached tips",
"replace_tips_and_select_location": "It's best to replace tips and select the last location used for tip pickup.",
"replace_used_tips_in_rack_location": "Replace used tips in rack location {{location}} in slot {{slot}}",
Expand All @@ -60,6 +74,8 @@
"robot_door_is_open": "Robot door is open",
"robot_is_canceling_run": "Robot is canceling the run",
"robot_is_in_recovery_mode": "Robot is in recovery mode",
"robot_not_attempt_to_move_lw": "<block>The robot will not attempt to move the labware again. The run will continue from the next step.</block><block>Close the robot door before proceeding.</block>",
"robot_retry_failed_lw_movement": "<block>The robot will retry the failed labware movement step from where the labware was replaced on the deck.</block><block>Close the robot door before proceeding.</block>",
"robot_will_not_check_for_liquid": "<block>The robot will not check for liquid again. The run will continue from the next step.</block><block>Close the robot door before proceeding.</block>",
"robot_will_retry_with_new_tips": "<block>The robot will retry the failed step with the new tips.</block><block>Close the robot door before proceeding.</block>",
"robot_will_retry_with_same_tips": "<block>The robot will retry the failed step with the same tips.</block><block>Close the robot door before proceeding.</block>",
Expand All @@ -76,6 +92,7 @@
"stand_back_resuming": "Stand back, resuming current step",
"stand_back_retrying": "Stand back, retrying failed step",
"stand_back_skipping_to_next_step": "Stand back, skipping to next step",
"take_any_necessary_precautions": "Take any necessary precautions before positioning yourself to stabilize or catch the labware. Once confirmed, a countdown will begin before the gripper releases.",
"take_necessary_actions": "<block>First, take any necessary actions to prepare the robot to retry the failed step.</block><block>Then, close the robot door before proceeding.</block>",
"take_necessary_actions_failed_pickup": "<block>First, take any necessary actions to prepare the robot to retry the failed tip pickup.</block><block>Then, close the robot door before proceeding.</block>",
"terminate_remote_activity": "Terminate remote activity",
Expand Down
3 changes: 2 additions & 1 deletion app/src/assets/localization/en/run_details.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"clear_protocol": "Clear protocol",
"clear_protocol_to_make_available": "Clear protocol from robot to make it available.",
"close_door": "Close robot door",
"close_door_to_resume": "Close robot door to resume run",
"close_door_to_resume": "Close robot door to resume",
"close_door_to_resume_run": "Close robot door to resume run",
"closing_protocol": "Closing Protocol",
"comment": "Comment",
"comment_step": "Comment",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function RunHeaderBannerContainer(
) : null}
{showDoorOpenDuringRunBanner ? (
<Banner type="warning" iconMarginLeft={SPACING.spacing4}>
{t('close_door_to_resume')}
{t('close_door_to_resume_run')}
</Banner>
) : null}
{terminalBannerType != null ? (
Expand Down
3 changes: 2 additions & 1 deletion app/src/organisms/Desktop/RunProgressMeter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
RUN_STATUS_IDLE,
RUN_STATUS_FINISHING,
RUN_STATUS_RUNNING,
RUN_STATUS_BLOCKED_BY_OPEN_DOOR,
} from '@opentrons/api-client'

import { getModalPortalEl } from '/app/App/portal'
Expand Down Expand Up @@ -98,7 +99,6 @@ export function RunProgressMeter(props: RunProgressMeterProps): JSX.Element {
e.stopPropagation()
downloadRunLog()
}

const {
showModal: showIntervention,
modalProps: interventionProps,
Expand All @@ -108,6 +108,7 @@ export function RunProgressMeter(props: RunProgressMeterProps): JSX.Element {
runData,
analysis,
lastRunCommand,
doorIsOpen: runStatus === RUN_STATUS_BLOCKED_BY_OPEN_DOOR,
})

const {
Expand Down
17 changes: 16 additions & 1 deletion app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
SkipStepSameTips,
SkipStepNewTips,
IgnoreErrorSkipStep,
ManualMoveLwAndSkip,
ManualReplaceLwAndRetry,
} from './RecoveryOptions'
import {
useErrorDetailsModal,
Expand Down Expand Up @@ -218,6 +220,14 @@ export function ErrorRecoveryContent(props: RecoveryContentProps): JSX.Element {
return <IgnoreErrorSkipStep {...props} />
}

const buildManualMoveLwAndSkip = (): JSX.Element => {
return <ManualMoveLwAndSkip {...props} />
}

const buildManualReplaceLwAndRetry = (): JSX.Element => {
return <ManualReplaceLwAndRetry {...props} />
}

const buildManuallyRouteToDoorOpen = (): JSX.Element => {
return <RecoveryDoorOpen {...props} />
}
Expand All @@ -237,20 +247,25 @@ export function ErrorRecoveryContent(props: RecoveryContentProps): JSX.Element {
return buildRetryNewTips()
case RECOVERY_MAP.RETRY_SAME_TIPS.ROUTE:
return buildRetrySameTips()
case RECOVERY_MAP.FILL_MANUALLY_AND_SKIP.ROUTE:
case RECOVERY_MAP.MANUAL_FILL_AND_SKIP.ROUTE:
return buildFillWellAndSkip()
case RECOVERY_MAP.SKIP_STEP_WITH_SAME_TIPS.ROUTE:
return buildSkipStepSameTips()
case RECOVERY_MAP.SKIP_STEP_WITH_NEW_TIPS.ROUTE:
return buildSkipStepNewTips()
case RECOVERY_MAP.IGNORE_AND_SKIP.ROUTE:
return buildIgnoreErrorSkipStep()
case RECOVERY_MAP.MANUAL_MOVE_AND_SKIP.ROUTE:
return buildManualMoveLwAndSkip()
case RECOVERY_MAP.MANUAL_REPLACE_AND_RETRY.ROUTE:
return buildManualReplaceLwAndRetry()
case RECOVERY_MAP.ROBOT_IN_MOTION.ROUTE:
case RECOVERY_MAP.ROBOT_RESUMING.ROUTE:
case RECOVERY_MAP.ROBOT_RETRYING_STEP.ROUTE:
case RECOVERY_MAP.ROBOT_CANCELING.ROUTE:
case RECOVERY_MAP.ROBOT_PICKING_UP_TIPS.ROUTE:
case RECOVERY_MAP.ROBOT_SKIPPING_STEP.ROUTE:
case RECOVERY_MAP.ROBOT_RELEASING_LABWARE.ROUTE:
return buildRecoveryInProgress()
case RECOVERY_MAP.ROBOT_DOOR_OPEN.ROUTE:
return buildManuallyRouteToDoorOpen()
Expand Down
96 changes: 96 additions & 0 deletions app/src/organisms/ErrorRecoveryFlows/RecoveryInProgress.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { css } from 'styled-components'

Expand All @@ -17,6 +18,8 @@ import type { RobotMovingRoute, RecoveryContentProps } from './types'

export function RecoveryInProgress({
recoveryMap,
recoveryCommands,
routeUpdateActions,
}: RecoveryContentProps): JSX.Element {
const {
ROBOT_CANCELING,
Expand All @@ -25,10 +28,17 @@ export function RecoveryInProgress({
ROBOT_RETRYING_STEP,
ROBOT_PICKING_UP_TIPS,
ROBOT_SKIPPING_STEP,
ROBOT_RELEASING_LABWARE,
} = RECOVERY_MAP
const { t } = useTranslation('error_recovery')
const { route } = recoveryMap

const gripperReleaseCountdown = useGripperRelease({
recoveryMap,
recoveryCommands,
routeUpdateActions,
})

const buildDescription = (): RobotMovingRoute => {
switch (route) {
case ROBOT_CANCELING.ROUTE:
Expand All @@ -43,6 +53,15 @@ export function RecoveryInProgress({
return t('stand_back_picking_up_tips')
case ROBOT_SKIPPING_STEP.ROUTE:
return t('stand_back_skipping_to_next_step')
case ROBOT_RELEASING_LABWARE.ROUTE: {
if (gripperReleaseCountdown > 0) {
return t('gripper_will_release_in_s', {
seconds: gripperReleaseCountdown,
})
} else {
return t('gripper_releasing_labware')
}
}
default:
return t('stand_back')
}
Expand All @@ -57,6 +76,83 @@ export function RecoveryInProgress({
)
}

const GRIPPER_RELEASE_COUNTDOWN_S = 5

type UseGripperReleaseProps = Pick<
RecoveryContentProps,
'recoveryMap' | 'recoveryCommands' | 'routeUpdateActions'
>

// Handles the gripper release copy and action, which operates on an interval. At T=0, release the labware then proceed
// to the next step in the active route.
export function useGripperRelease({
recoveryMap,
recoveryCommands,
routeUpdateActions,
}: UseGripperReleaseProps): number {
const { releaseGripperJaws } = recoveryCommands
const {
proceedToRouteAndStep,
proceedNextStep,
handleMotionRouting,
stashedMap,
} = routeUpdateActions
const { MANUAL_MOVE_AND_SKIP, MANUAL_REPLACE_AND_RETRY } = RECOVERY_MAP
const [countdown, setCountdown] = useState(GRIPPER_RELEASE_COUNTDOWN_S)

const proceedToValidNextStep = (): void => {
switch (stashedMap?.route) {
case MANUAL_MOVE_AND_SKIP.ROUTE:
void proceedToRouteAndStep(
MANUAL_MOVE_AND_SKIP.ROUTE,
MANUAL_MOVE_AND_SKIP.STEPS.MANUAL_MOVE
)
break
case MANUAL_REPLACE_AND_RETRY.ROUTE:
void proceedToRouteAndStep(
MANUAL_REPLACE_AND_RETRY.ROUTE,
MANUAL_REPLACE_AND_RETRY.STEPS.MANUAL_REPLACE
)
break
default:
console.error('Unhandled post grip-release routing.')
void proceedNextStep()
}
}

useEffect(() => {
let intervalId: NodeJS.Timeout | null = null

if (recoveryMap.route === RECOVERY_MAP.ROBOT_RELEASING_LABWARE.ROUTE) {
intervalId = setInterval(() => {
setCountdown(prevCountdown => {
const updatedCountdown = prevCountdown - 1

if (updatedCountdown === 0) {
if (intervalId != null) {
clearInterval(intervalId)
}
void releaseGripperJaws()
.finally(() => handleMotionRouting(false))
.then(() => {
proceedToValidNextStep()
})
}

return updatedCountdown
})
}, 1000)
}

return () => {
if (intervalId != null) {
clearInterval(intervalId)
}
}
}, [recoveryMap.route])

return countdown
}
const CONTAINER_STYLE = css`
align-items: ${ALIGN_CENTER};
justify-content: ${JUSTIFY_CENTER};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ import type { RecoveryContentProps } from '../types'
export function FillWellAndSkip(props: RecoveryContentProps): JSX.Element {
const { recoveryMap } = props
const { step, route } = recoveryMap
const { FILL_MANUALLY_AND_SKIP, CANCEL_RUN } = RECOVERY_MAP
const { MANUAL_FILL_AND_SKIP, CANCEL_RUN } = RECOVERY_MAP

const buildContent = (): JSX.Element => {
switch (step) {
case FILL_MANUALLY_AND_SKIP.STEPS.MANUALLY_FILL:
case MANUAL_FILL_AND_SKIP.STEPS.MANUAL_FILL:
return <FillWell {...props} />
case FILL_MANUALLY_AND_SKIP.STEPS.SKIP:
case MANUAL_FILL_AND_SKIP.STEPS.SKIP:
return <SkipToNextStep {...props} />
case CANCEL_RUN.STEPS.CONFIRM_CANCEL:
return <CancelRun {...props} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function IgnoreErrorStepHome({
routeUpdateActions,
}: RecoveryContentProps): JSX.Element | null {
const { t } = useTranslation('error_recovery')
const { FILL_MANUALLY_AND_SKIP } = RECOVERY_MAP
const { MANUAL_FILL_AND_SKIP } = RECOVERY_MAP
const { ignoreErrorKindThisRun } = recoveryCommands
const { proceedToRouteAndStep, goBackPrevStep } = routeUpdateActions

Expand All @@ -63,17 +63,17 @@ export function IgnoreErrorStepHome({
// and route appropriately.
const ignoreOnce = (): void => {
void proceedToRouteAndStep(
FILL_MANUALLY_AND_SKIP.ROUTE,
FILL_MANUALLY_AND_SKIP.STEPS.SKIP
MANUAL_FILL_AND_SKIP.ROUTE,
MANUAL_FILL_AND_SKIP.STEPS.SKIP
)
}

// See ignoreOnce comment.
const ignoreAlways = (): void => {
void ignoreErrorKindThisRun().then(() =>
proceedToRouteAndStep(
FILL_MANUALLY_AND_SKIP.ROUTE,
FILL_MANUALLY_AND_SKIP.STEPS.SKIP
MANUAL_FILL_AND_SKIP.ROUTE,
MANUAL_FILL_AND_SKIP.STEPS.SKIP
)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { RECOVERY_MAP } from '../constants'
import {
GripperIsHoldingLabware,
GripperReleaseLabware,
SkipStepInfo,
TwoColLwInfoAndDeck,
} from '../shared'
import { SelectRecoveryOption } from './SelectRecoveryOption'

import type { RecoveryContentProps } from '../types'

export function ManualMoveLwAndSkip(props: RecoveryContentProps): JSX.Element {
const { recoveryMap } = props
const { step, route } = recoveryMap
const { MANUAL_MOVE_AND_SKIP } = RECOVERY_MAP

const buildContent = (): JSX.Element => {
switch (step) {
case MANUAL_MOVE_AND_SKIP.STEPS.GRIPPER_HOLDING_LABWARE:
return <GripperIsHoldingLabware {...props} />
case MANUAL_MOVE_AND_SKIP.STEPS.GRIPPER_RELEASE_LABWARE:
return <GripperReleaseLabware {...props} />
case MANUAL_MOVE_AND_SKIP.STEPS.MANUAL_MOVE:
return <TwoColLwInfoAndDeck {...props} />
case MANUAL_MOVE_AND_SKIP.STEPS.SKIP:
return <SkipStepInfo {...props} />
default:
console.warn(`${step} in ${route} not explicitly handled. Rerouting.`)
return <SelectRecoveryOption {...props} />
}
}

return buildContent()
}
Loading

0 comments on commit 730d6b4

Please sign in to comment.