Skip to content

Commit

Permalink
fix(app): fix dqa ODD protocol setup deck config (#14169)
Browse files Browse the repository at this point in the history
* fix(app): fix dqa ODD protocol setup deck config
  • Loading branch information
koji authored Dec 12, 2023
1 parent ec23072 commit 683b2a1
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 19 deletions.
2 changes: 1 addition & 1 deletion app/src/assets/localization/en/protocol_setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@
"modules": "Modules",
"mount_title": "{{mount}} MOUNT:",
"mount": "{{mount}} mount",
"multiple_fixtures_missing": "Missing {{count}} fixtures",
"multiple_fixtures_missing": "{{count}} fixtures missing",
"multiple_modules_example": "Your protocol has two Temperature Modules. The Temperature Module attached to the first port starting from the left will be related to the first Temperature Module in your protocol while the second Temperature Module loaded would be related to the Temperature Module connected to the next port to the right. If using a hub, follow the same logic with the port ordering.",
"multiple_modules_explanation": "To use more than one of the same module in a protocol, you first need to plug in the module that’s called first in your protocol to the lowest numbered USB port on the robot. Continue in the same manner with additional modules.",
"multiple_modules_help_link_title": "See How To Set Up Modules of the Same Type",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ import * as React from 'react'
import { when, resetAllWhenMocks } from 'jest-when'

import { renderWithProviders, BaseDeck } from '@opentrons/components'
import { useUpdateDeckConfigurationMutation } from '@opentrons/react-api-client'
import {
useDeckConfigurationQuery,
useUpdateDeckConfigurationMutation,
} from '@opentrons/react-api-client'

import { i18n } from '../../../i18n'
import { useMostRecentCompletedAnalysis } from '../../LabwarePositionCheck/useMostRecentCompletedAnalysis'
import { ProtocolSetupDeckConfiguration } from '..'

import type { CompletedProtocolAnalysis } from '@opentrons/shared-data'
import type { UseQueryResult } from 'react-query'
import type {
CompletedProtocolAnalysis,
DeckConfiguration,
} from '@opentrons/shared-data'

jest.mock('@opentrons/components/src/hardware-sim/BaseDeck/index')
jest.mock('@opentrons/react-api-client')
Expand All @@ -33,6 +40,9 @@ const mockUseUpdateDeckConfigurationMutation = useUpdateDeckConfigurationMutatio
typeof useUpdateDeckConfigurationMutation
>
const mockBaseDeck = BaseDeck as jest.MockedFunction<typeof BaseDeck>
const mockUseDeckConfigurationQuery = useDeckConfigurationQuery as jest.MockedFunction<
typeof useDeckConfigurationQuery
>

const render = (
props: React.ComponentProps<typeof ProtocolSetupDeckConfiguration>
Expand All @@ -59,6 +69,9 @@ describe('ProtocolSetupDeckConfiguration', () => {
mockUseUpdateDeckConfigurationMutation.mockReturnValue({
updateDeckConfiguration: mockUpdateDeckConfiguration,
} as any)
mockUseDeckConfigurationQuery.mockReturnValue(({
data: [],
} as unknown) as UseQueryResult<DeckConfiguration>)
})

afterEach(() => {
Expand Down
23 changes: 19 additions & 4 deletions app/src/organisms/ProtocolSetupDeckConfiguration/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {
FLEX_ROBOT_TYPE,
getSimplestDeckConfigForProtocol,
} from '@opentrons/shared-data'
import { useUpdateDeckConfigurationMutation } from '@opentrons/react-api-client'
import {
useDeckConfigurationQuery,
useUpdateDeckConfigurationMutation,
} from '@opentrons/react-api-client'

import { ChildNavigation } from '../ChildNavigation'
import { AddFixtureModal } from '../DeviceDetailsDeckConfiguration/AddFixtureModal'
Expand Down Expand Up @@ -52,15 +55,26 @@ export function ProtocolSetupDeckConfiguration({
] = React.useState<boolean>(false)

const mostRecentAnalysis = useMostRecentCompletedAnalysis(runId)
const { data: deckConfig = [] } = useDeckConfigurationQuery()

const simplestDeckConfig = getSimplestDeckConfigForProtocol(
mostRecentAnalysis
).map(({ cutoutId, cutoutFixtureId }) => ({ cutoutId, cutoutFixtureId }))

const targetDeckConfig = simplestDeckConfig.find(
deck => deck.cutoutId === cutoutId
)

const mergedDeckConfig = deckConfig.map(config =>
targetDeckConfig != null && config.cutoutId === targetDeckConfig.cutoutId
? targetDeckConfig
: config
)

const [
currentDeckConfig,
setCurrentDeckConfig,
] = React.useState<DeckConfiguration>(simplestDeckConfig)
] = React.useState<DeckConfiguration>(mergedDeckConfig)

const { updateDeckConfiguration } = useUpdateDeckConfigurationMutation()
const handleClickConfirm = (): void => {
Expand Down Expand Up @@ -94,12 +108,13 @@ export function ProtocolSetupDeckConfiguration({
onClickButton={handleClickConfirm}
/>
<Flex
marginTop="7.75rem"
marginTop="4rem"
paddingX={SPACING.spacing40}
justifyContent={JUSTIFY_CENTER}
height="28.4375rem"
>
<BaseDeck
deckConfig={simplestDeckConfig}
deckConfig={currentDeckConfig}
robotType={FLEX_ROBOT_TYPE}
/>
</Flex>
Expand Down
51 changes: 39 additions & 12 deletions app/src/pages/OnDeviceDisplay/ProtocolSetup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
getDeckDefFromRobotType,
getModuleDisplayName,
getFixtureDisplayName,
SINGLE_SLOT_FIXTURES,
} from '@opentrons/shared-data'

import { StyledText } from '../../../atoms/text'
Expand Down Expand Up @@ -80,6 +81,8 @@ import { getIsHeaterShakerAttached } from '../../../redux/config'
import { ConfirmAttachedModal } from './ConfirmAttachedModal'
import { getLatestCurrentOffsets } from '../../../organisms/Devices/ProtocolRun/SetupLabwarePositionCheck/utils'
import { CloseButton, PlayButton } from './Buttons'
import { useDeckConfigurationCompatibility } from '../../../resources/deck_configuration/hooks'
import { getRequiredDeckConfig } from '../../../resources/deck_configuration/utils'

import type { CutoutFixtureId, CutoutId } from '@opentrons/shared-data'
import type { OnDeviceRouteParams } from '../../../App/types'
Expand Down Expand Up @@ -302,6 +305,14 @@ function PrepareToRun({
setShowConfirmCancelModal,
] = React.useState<boolean>(false)

const deckConfigCompatibility = useDeckConfigurationCompatibility(
robotType,
mostRecentAnalysis
)
const requiredDeckConfigCompatibility = getRequiredDeckConfig(
deckConfigCompatibility
)

// True if any server request is still pending.
const isLoading =
mostRecentAnalysis == null ||
Expand All @@ -314,24 +325,43 @@ function PrepareToRun({
(getProtocolUsesGripper(mostRecentAnalysis) ? 1 : 0)
: 0

const missingProtocolHardware = useMissingProtocolHardwareFromAnalysis(
const { missingProtocolHardware } = useMissingProtocolHardwareFromAnalysis(
robotType,
mostRecentAnalysis
)
const isLocationConflict = missingProtocolHardware.conflictedSlots.length > 0

const missingPipettes = missingProtocolHardware.missingProtocolHardware.filter(
const locationConflictSlots = requiredDeckConfigCompatibility.map(
fixtureCompatibility => {
const {
compatibleCutoutFixtureIds,
cutoutFixtureId,
} = fixtureCompatibility
const isCurrentFixtureCompatible =
cutoutFixtureId != null &&
compatibleCutoutFixtureIds.includes(cutoutFixtureId)
return (
!isCurrentFixtureCompatible &&
cutoutFixtureId != null &&
!SINGLE_SLOT_FIXTURES.includes(cutoutFixtureId)
)
}
)
const isLocationConflict = locationConflictSlots.some(
conflictSlot => conflictSlot
)

const missingPipettes = missingProtocolHardware.filter(
hardware => hardware.hardwareType === 'pipette'
)

const missingGripper = missingProtocolHardware.missingProtocolHardware.filter(
const missingGripper = missingProtocolHardware.filter(
hardware => hardware.hardwareType === 'gripper'
)

const missingModules = missingProtocolHardware.missingProtocolHardware.filter(
const missingModules = missingProtocolHardware.filter(
hardware => hardware.hardwareType === 'module'
)
const missingFixtures = missingProtocolHardware.missingProtocolHardware.filter(
const missingFixtures = missingProtocolHardware.filter(
(hardware): hardware is ProtocolFixture =>
hardware.hardwareType === 'fixture'
)
Expand Down Expand Up @@ -374,11 +404,8 @@ function PrepareToRun({
? 'ready'
: 'not ready'

// TODO: (ND: 11/6/23) check for areFixturesReady once we removed stubbed fixtures in useRequiredProtocolHardwareFromAnalysis
// const isReadyToRun =
// incompleteInstrumentCount === 0 && areModulesReady && areFixturesReady

const isReadyToRun = incompleteInstrumentCount === 0 && areModulesReady
const isReadyToRun =
incompleteInstrumentCount === 0 && areModulesReady && areFixturesReady
const onPlay = (): void => {
if (isDoorOpen) {
makeSnackbar(t('shared:close_robot_door'))
Expand Down Expand Up @@ -748,7 +775,7 @@ export function ProtocolSetup(): JSX.Element {
padding={
setupScreen === 'prepare to run'
? `0 ${SPACING.spacing32} ${SPACING.spacing40}`
: `${SPACING.spacing32} ${SPACING.spacing40}`
: `${SPACING.spacing32} ${SPACING.spacing40} ${SPACING.spacing40}`
}
>
{setupComponentByScreen[setupScreen]}
Expand Down

0 comments on commit 683b2a1

Please sign in to comment.