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

feat(protocol-designer, step-generation): timeline warnings and error… #16566

Merged
merged 2 commits into from
Oct 23, 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
90 changes: 48 additions & 42 deletions protocol-designer/src/assets/localization/en/alert.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,122 +97,128 @@
"timeline": {
"error": {
"LABWARE_DISCARDED_IN_WASTE_CHUTE": {
"title": "The labware has been previously discarded into the waste chute",
"body": "Please select a different labware to move."
"title": "Labware not available",
"body": "This step uses labware that has previously been discarded into a Waste Chute."
},
"LABWARE_ON_ANOTHER_ENTITY": {
"title": "Attempting to move a labware on top of another entity",
"title": "Attempting to move labware on top of another entity",
"body": "Please reselect which slot your labware should move to."
},
"INSUFFICIENT_TIPS": {
"title": "Not enough tips to complete action",
"body": "Add another tip rack to an empty slot in "
"body": "Add another tip rack to your deck or change your tip management during transfer and mix steps.",
"link": "Edit starting deck"
},
"NO_TIP_SELECTED": {
"title": "No tip rack was selected to complete action",
"body": "Add a tip rack in the step"
},
"NO_TIP_ON_PIPETTE": {
"title": "No tip on pipette at the start of step",
"body1": "Choose a different Change Tip setting. Change Tip cannot be \"Never\" the first time a pipette is used in a protocol, or following a step that used the ",
"link": "Air gap dispense setting",
"body2": ". Pipetting steps must begin with a tip on."
"title": "No tip on pipette at start of step",
"body": "Use a different tip handling setting. Don't set it to Never the first time a pipette is used in a protocol, or following a step that air gaps when dispensing."
},
"MODULE_PIPETTE_COLLISION_DANGER": {
"title": "Pipette cannot access labware",
"body": "Gen 1 8-Channel pipettes cannot access labware or tip racks in slot 4 or 6 because they are adjacent to modules. Read more "
"title": "GEN1 8-Channel Pipettes can't move adjacent to modules",
"body": "Move labware and modules or use a different pipette."
},
"MISSING_MODULE": {
"title": "Missing module for step",
"body": "A step requires a module that does not exist"
"title": "Module not in protocol",
"body": "This step tries to use a module not in the protocol. Add the module to your protocol or remove this step."
},
"MISSING_TEMPERATURE_STEP": {
"title": "Missing Temperature step",
"body": "Add a Temperature step prior to this Pause step. The module is not currently changing temperature because it has either been deactivated or is holding a temperature"
"title": "Unreachable target temperature",
"body": "The protocol can't proceed beyond this pause step, because the module is not changing temperature. Add or modify a temperature step before this step."
},
"THERMOCYCLER_LID_CLOSED": {
"title": "Thermocycler lid is closed",
"body": "Before the robot can interact with labware in the Thermocycler, the lid must be open. To resolve this error, please add a thermocycler step ahead of the current step, and set the lid status to \"open\"."
"title": "Thermocycler lid closed",
"body": "This step tries to use labware in the Thermocycler. Open the lid before this step."
},
"HEATER_SHAKER_LATCH_OPEN": {
"title": "Heater-Shaker labware latch is open",
"title": "Heater-Shaker latch open",
"body": "Before the robot can interact with labware on the Heater-Shaker module, the labware latch must be closed. To resolve this error, please add a Heater-Shaker step ahead of the current step, and set the labware latch status to \"closed\"."
},
"HEATER_SHAKER_IS_SHAKING": {
"title": "Heater-Shaker is shaking",
"body": "the robot cannot interact with labware on the Heater-Shaker Module while it is shaking."
"body": "The robot cannot interact with labware on the Heater-Shaker Module while it is shaking. Add a step to stop shaking to interact with the labware."
},
"TALL_LABWARE_EAST_WEST_OF_HEATER_SHAKER": {
"body": "The Heater-Shaker labware latch will collide with labware over 53 mm. Move labware to a different slot."
},
"HEATER_SHAKER_EAST_WEST_LATCH_OPEN": {
"title": "Heater-Shaker labware latch open",
"body": "Pipettes cannot access labware on, or to the left or right of, the Heater-Shaker while the labware latch is open. Create a step before this one that closes the latch."
},
"HEATER_SHAKER_NORTH_SOUTH_EAST_WEST_SHAKING": {
"title": "The Heater-Shaker is shaking",
"body": "Pipettes cannot access labware on or adjacent to the Heater-Shaker while it is shaking. Create a step before this one that deactivates the shaker."
"title": "Robot unable to perform step",
"body": "The robot cannot interact with labware on or next to the Heater-Shaker Module while it is shaking. Add a heater-shaker step to stop shaking."
},
"HEATER_SHAKER_EAST_WEST_MULTI_CHANNEL": {
"title": "8-Channel pipette cannot access labware",
"body": "8-Channel pipettes cannot access labware or tip racks to the left or right of a Heater-Shaker GEN1 module. Move labware to a different slot to access it with an 8-Channel pipette."
"title": "8-Channel unable to access slot",
"body": "8-Channel pipettes cannot access labware to the left or right of a Heater-Shaker GEN1 module. Move labware to a different slot to access it with an 8-Channel pipette."
},
"HEATER_SHAKER_NORTH_SOUTH__OF_NON_TIPRACK_WITH_MULTI_CHANNEL": {
"title": "8-Channel pipette cannot access labware",
"body": "8-Channel pipettes cannot access labware in front of or behind a Heater-Shaker. They can access Opentrons Tip Racks in this slot. Move labware to a different slot."
"title": "8-Channel unable to access slot",
"body": "8-Channel pipettes cannot access non-tiprack labware to the top or bottom of a Heater-Shaker GEN1 module. Move labware to a different slot to access it with an 8-Channel pipette."
},
"LABWARE_OFF_DECK": {
"title": "Labware is off-deck",
"title": "Labware not on deck",
"body": "The robot can only perform steps on labware that is on the deck. Add or change a Move Labware step to put it on the deck before this step."
},
"HEATER_SHAKER_LATCH_CLOSED": {
"title": "Heater-Shaker labware latch is closed",
"title": "Heater-Shaker latch closed",
"body": "The Heater-Shaker’s labware latch must be open when moving labware to or from the module. Add a Heater-Shaker step that opens the latch before this step."
},
"DROP_TIP_LOCATION_DOES_NOT_EXIST": {
"title": "Attempting to drop tip in an unknown location",
"body": "The Waste Chute or Trash Bin to drop tip in does not exist."
},
"MISSING_96_CHANNEL_TIPRACK_ADAPTER": {
"title": "Missing 96-channel tip rack adapter",
"body": "The tip rack must be placed in an adapter when picking up 96 tips simultaneously."
"title": "Tip rack adapter required",
"body": "The 96-channel pipette uses a tip rack adapter to pick up a full rack of tips. Add one to your starting deck or use partial tip pickup.",
"link": "Edit starting deck"
},
"EQUIPMENT_DOES_NOT_EXIST": {
"title": "Attempting to interact with an unknown entity",
"title": "Unable to perform step",
"body": "An entity you are interacting with does not exist."
},
"GRIPPER_REQUIRED": {
"title": "A gripper is required to complete this action",
"body": "Attempting to move a labware without a gripper into the waste chute. Please add a gripper to this step."
"title": "Cannot move with gripper",
"body": "The gripper cannot move aluminum blocks. Deselect the 'Use Gripper' checkbox."
},
"REMOVE_96_CHANNEL_TIPRACK_ADAPTER": {
"title": "Do not use tip rack adapter for partial tip pickup",
"body": "Partial tip pickup requires a tip rack placed directly on the deck. Remove the adapter, or add a new tip rack without an adapter."
"title": "Extra tip rack adapter",
"body": "When picking up fewer than 96 tips, the tip rack must be placed directly on the deck, not in the tip rack adapter.",
"link": "Edit starting deck"
},
"CANNOT_MOVE_WITH_GRIPPER": {
"title": "Cannot move with gripper",
"body": "The gripper cannot move aluminum blocks. Edit the step and deselect the 'Use Gripper' checkbox."
},
"PIPETTE_HAS_TIP": {
"title": "Possible collision with tip",
"body": "The gripper cannot pick up labware while pipettes have tips attached. Drop all tips before this move labware step."
"title": "Gripper movement with tips attached",
"body": "Picking up labware with the gripper while tips are on an adjacent pipette can cause collisions. Drop tips from all pipettes before this step."
},
"POSSIBLE_PIPETTE_COLLISION": {
"title": "Pipette collisions likely",
"body": "There is a possibility that the pipette will collide with the adjascent labware or module for partial tip pick up."
}
},
"warning": {
"ASPIRATE_MORE_THAN_WELL_CONTENTS": {
"title": "Not enough liquid in well(s)",
"body": "You are trying to aspirate more than the current volume of one of your well(s)"
"title": "Not enough liquid",
"body": "This step tries to aspirate more than the current volume of a source well."
},
"ASPIRATE_FROM_PRISTINE_WELL": {
"title": "Source well is empty",
"body": "The well(s) you're trying to aspirate from are empty. To add liquids, hover over labware in "
"body": "This step tries to aspirate from an empty well."
},
"LABWARE_IN_WASTE_CHUTE_HAS_LIQUID": {
"title": "Moving labware into waste chute",
"body": "This labware has remaining liquid, be advised that once you dispose of it, there is no way to get it back later in the protocol."
"title": "Disposing liquid-filled labware",
"body": "This step moves a labware that contains liquid to the waste chute. There is no way to retrieve the liquid after disposal."
},
"TIPRACK_IN_WASTE_CHUTE_HAS_TIPS": {
"title": "Moving tiprack into waste chute",
"body": "This tiprack has remaining tips, be advised that once you dispose of it, there is no way to get it back later in the protocol. "
"title": "Disposing unused tips",
"body": "This step moves a tip rack that contains unused tips to the waste chute. There is no way to retrieve the tips after disposal."
}
}
},
Expand Down
17 changes: 16 additions & 1 deletion protocol-designer/src/file-data/actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { FileMetadataFields, SaveFileMetadataAction } from './types'
import type {
FileMetadataFields,
SaveFileMetadataAction,
SelectDesignerTabAction,
} from './types'
import type { WorkerResponse } from '../timelineMiddleware/types'
export const saveFileMetadata = (
payload: FileMetadataFields
Expand All @@ -22,3 +26,14 @@ export const computeRobotStateTimelineSuccess = (
type: 'COMPUTE_ROBOT_STATE_TIMELINE_SUCCESS',
payload,
})

export interface DesignerTabPayload {
tab: 'protocolSteps' | 'startingDeck'
}

export const selectDesignerTab = (
payload: DesignerTabPayload
): SelectDesignerTabAction => ({
type: 'SELECT_DESIGNER_TAB',
payload,
})
25 changes: 23 additions & 2 deletions protocol-designer/src/file-data/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ import type { RobotType } from '@opentrons/shared-data'
import type { Action } from '../../types'
import type { LoadFileAction, NewProtocolFields } from '../../load-file'
import type { Substeps } from '../../steplist/types'
import type { ComputeRobotStateTimelineSuccessAction } from '../actions'
import type { FileMetadataFields, SaveFileMetadataAction } from '../types'
import type {
ComputeRobotStateTimelineSuccessAction,
DesignerTabPayload,
} from '../actions'
import type {
FileMetadataFields,
SaveFileMetadataAction,
SelectDesignerTabAction,
} from '../types'

export const timelineIsBeingComputed: Reducer<boolean, any> = handleActions(
{
Expand Down Expand Up @@ -110,13 +117,26 @@ const robotTypeReducer = (
}
return state
}

const designerTabReducer = (
state: DesignerTabPayload['tab'] = 'startingDeck',
action: SelectDesignerTabAction
): DesignerTabPayload['tab'] => {
if (action.type === 'SELECT_DESIGNER_TAB') {
return action.payload.tab
} else {
return state
}
}

export interface RootState {
computedRobotStateTimeline: Timeline
computedSubsteps: Substeps
currentProtocolExists: boolean
fileMetadata: FileMetadataFields
timelineIsBeingComputed: boolean
robotType: RobotType
designerTab: DesignerTabPayload['tab']
}
const _allReducers = {
computedRobotStateTimeline,
Expand All @@ -125,6 +145,7 @@ const _allReducers = {
fileMetadata,
timelineIsBeingComputed,
robotType: robotTypeReducer,
designerTab: designerTabReducer,
}
export const rootReducer: Reducer<RootState, Action> = combineReducers(
_allReducers
Expand Down
5 changes: 5 additions & 0 deletions protocol-designer/src/file-data/selectors/fileFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { BaseState, Selector } from '../../types'
import type { RootState } from '../reducers'
import type { FileMetadataFields } from '../types'
import type { RobotType } from '@opentrons/shared-data'
import type { DesignerTabPayload } from '../actions'

export const rootSelector = (state: BaseState): RootState => state.fileData
export const getCurrentProtocolExists: Selector<boolean> = createSelector(
Expand All @@ -21,3 +22,7 @@ export const getRobotType: Selector<RobotType> = createSelector(
rootSelector,
state => state.robotType
)

export const getDesignerTab: Selector<
DesignerTabPayload['tab']
> = createSelector(rootSelector, state => state.designerTab)
6 changes: 6 additions & 0 deletions protocol-designer/src/file-data/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import type { ProtocolFile } from '@opentrons/shared-data'
import type { DesignerTabPayload } from './actions'
export type FileMetadataFields = ProtocolFile<{}>['metadata']
export type FileMetadataFieldAccessors = keyof FileMetadataFields
export interface SaveFileMetadataAction {
type: 'SAVE_FILE_METADATA'
payload: FileMetadataFields
}

export interface SelectDesignerTabAction {
type: 'SELECT_DESIGNER_TAB'
payload: DesignerTabPayload
}
65 changes: 43 additions & 22 deletions protocol-designer/src/organisms/Alerts/ErrorContents.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { useTranslation } from 'react-i18next'
import { START_TERMINAL_ITEM_ID } from '../../steplist'
import { KnowledgeBaseLink } from '../../components/KnowledgeBaseLink'
import { TerminalItemLink } from './TerminalItemLink'
import { useDispatch } from 'react-redux'
import {
Btn,
Flex,
JUSTIFY_SPACE_BETWEEN,
SPACING,
TYPOGRAPHY,
} from '@opentrons/components'
import { BUTTON_LINK_STYLE } from '../../atoms'
import { selectDesignerTab } from '../../file-data/actions'

import type { AlertLevel } from './types'

Expand All @@ -14,6 +21,7 @@ export const ErrorContents = (
): JSX.Element | null => {
const { errorType, level } = props
const { t } = useTranslation(['alert', 'shared'])
const dispatch = useDispatch()

if (level === 'timeline') {
const bodyText = t(`timeline.error.${errorType}.body`, {
Expand All @@ -22,29 +30,42 @@ export const ErrorContents = (
switch (errorType) {
case 'INSUFFICIENT_TIPS':
return (
<>
<Flex
justifyContent={JUSTIFY_SPACE_BETWEEN}
gridGap={SPACING.spacing8}
>
{bodyText}
<TerminalItemLink terminalId={START_TERMINAL_ITEM_ID} />
</>
)
case 'MODULE_PIPETTE_COLLISION_DANGER':
return (
<>
{bodyText}
<KnowledgeBaseLink to="pipetteGen1MultiModuleCollision">
{t('shared:here')}
</KnowledgeBaseLink>
</>
<Btn
width="7.25rem"
textDecoration={TYPOGRAPHY.textDecorationUnderline}
css={BUTTON_LINK_STYLE}
onClick={() => {
dispatch(selectDesignerTab({ tab: 'startingDeck' }))
}}
>
{t(`timeline.error.${errorType}.link`)}
</Btn>
</Flex>
)
case 'NO_TIP_ON_PIPETTE':
case 'REMOVE_96_CHANNEL_TIPRACK_ADAPTER':
case 'MISSING_96_CHANNEL_TIPRACK_ADAPTER':
return (
<>
{t(`timeline.error.${errorType}.body1`)}
<KnowledgeBaseLink to="airGap">
<Flex
justifyContent={JUSTIFY_SPACE_BETWEEN}
gridGap={SPACING.spacing8}
>
{t(`timeline.error.${errorType}.body`)}
<Btn
width="7.25rem"
textDecoration={TYPOGRAPHY.textDecorationUnderline}
css={BUTTON_LINK_STYLE}
onClick={() => {
dispatch(selectDesignerTab({ tab: 'startingDeck' }))
}}
>
{t(`timeline.error.${errorType}.link`)}
</KnowledgeBaseLink>
{t(`timeline.error.${errorType}.body2`)}
</>
</Btn>
</Flex>
)
default:
return bodyText
Expand Down
9 changes: 2 additions & 7 deletions protocol-designer/src/organisms/Alerts/TimelineAlerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import {
ALIGN_CENTER,
Banner,
DIRECTION_COLUMN,
Flex,
Expand Down Expand Up @@ -31,13 +30,9 @@ function TimelineAlertsComponent(): JSX.Element {
<Banner
type={alertType === 'error' ? 'error' : 'warning'}
key={`${alertType}:${key}`}
width="50%"
width="100%"
>
<Flex
flexDirection={DIRECTION_COLUMN}
gridGap={SPACING.spacing4}
alignItems={ALIGN_CENTER}
>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing4}>
<StyledText desktopStyle="bodyDefaultSemiBold">{data.title}</StyledText>
<StyledText desktopStyle="bodyDefaultRegular">
{data.description}
Expand Down
Loading
Loading