Skip to content

Commit

Permalink
fix(app): resolve module location conflicts through deck config durin…
Browse files Browse the repository at this point in the history
…g protocol setup on ODD (#14966)

Resolve location conflicts in the on device display's protocol setup flow by updating deck configuration accordingly.

Closes PLAT-287,
Closes PLAT-291
  • Loading branch information
b-cooper authored Apr 23, 2024
1 parent a9dcb20 commit 5415917
Show file tree
Hide file tree
Showing 38 changed files with 384 additions and 216 deletions.
7 changes: 5 additions & 2 deletions app/src/assets/localization/en/protocol_setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
"action_needed": "Action needed",
"adapter_slot_location_module": "Slot {{slotName}}, {{adapterName}} on {{moduleName}}",
"adapter_slot_location": "Slot {{slotName}}, {{adapterName}}",
"add_fixture_to_deck": "Add this fixture to your deck configuration. It will be referenced during protocol analysis.",
"add_fixture": "Add {{fixtureName}} to deck configuration",
"additional_labware": "{{count}} additional labware",
"additional_off_deck_labware": "Additional Off-Deck Labware",
"add_this_deck_hardware": "Add this deck hardware to your deck configuration. It will be referenced during protocol analysis.",
"add_to_slot": "Add to slot {{slotName}}",
"attach_gripper_failure_reason": "Attach the required gripper to continue",
"attach_gripper": "attach gripper",
"attach_module": "Attach module before calibrating",
Expand Down Expand Up @@ -38,6 +39,7 @@
"calibration_status": "calibration status",
"calibration": "Calibration",
"cancel_and_restart_to_edit": "Cancel the run and restart setup to edit",
"cancel_protocol_and_edit_deck_config": "Cancel protocol and edit deck configuration",
"choose_enum": "Choose {{displayName}}",
"closing": "Closing...",
"complete_setup_before_proceeding": "complete setup before continuing run",
Expand Down Expand Up @@ -142,7 +144,6 @@
"module_setup_step_title": "Modules",
"module_slot_location": "Slot {{slotName}}, {{moduleName}}",
"module": "Module",
"modules_and_deck": "Modules & deck",
"modules_connected_plural": "{{count}} modules attached",
"modules_connected": "{{count}} module attached",
"modules_setup_step_title": "Module Setup",
Expand Down Expand Up @@ -249,12 +250,14 @@
"slot_number": "Slot Number",
"status": "Status",
"step": "STEP {{index}}",
"there_are_no_unconfigured_modules": "There are no un-configured {{module}} connected to the robot. Plug one in or remove an existing {{module}}, move it to the right place, and update the deck configuration.",
"tip_length_cal_description_bullet": "Perform Tip Length Calibration for each new tip type used on a pipette.",
"tip_length_cal_description": "This measures the Z distance between the bottom of the tip and the pipette’s nozzle. If you redo the tip length calibration for the tip you used to calibrate a pipette, you will also have to redo that Pipette Offset Calibration.",
"tip_length_cal_title": "Tip Length Calibration",
"tip_length_calibration": "tip length calibration",
"total_vol": "total volume",
"update_deck": "Update deck",
"update_deck_config": "Update deck configuration",
"updated": "Updated",
"usb_connected_no_port_info": "USB Port Connected",
"usb_port_connected": "USB Port {{port}}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ import type {
import type { ModalHeaderBaseProps } from '../../molecules/Modal/types'
import type { LegacyModalProps } from '../../molecules/LegacyModal'

// type CutoutContents = Omit<CutoutConfig, 'cutoutId'>

interface AddFixtureModalProps {
cutoutId: CutoutId
setShowAddFixtureModal: (showAddFixtureModal: boolean) => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import {
} from '@opentrons/shared-data'

import { getLabwareSetupItemGroups } from '../../../../pages/Protocols/utils'
import { getAttachedProtocolModuleMatches } from '../../../ProtocolSetupModulesAndDeck/utils'
import { useAttachedModules } from '../../hooks'
import { LabwareInfoOverlay } from '../LabwareInfoOverlay'
import { getLabwareRenderInfo } from '../utils/getLabwareRenderInfo'
import { getProtocolModulesInfo } from '../utils/getProtocolModulesInfo'
Expand All @@ -30,8 +28,6 @@ import type {
ProtocolAnalysisOutput,
} from '@opentrons/shared-data'

const ATTACHED_MODULE_POLL_MS = 5000

interface SetupLabwareMapProps {
runId: string
protocolAnalysis: CompletedProtocolAnalysis | ProtocolAnalysisOutput | null
Expand All @@ -41,11 +37,6 @@ export function SetupLabwareMap({
runId,
protocolAnalysis,
}: SetupLabwareMapProps): JSX.Element | null {
const attachedModules =
useAttachedModules({
refetchInterval: ATTACHED_MODULE_POLL_MS,
}) ?? []

// early return null if no protocol analysis
if (protocolAnalysis == null) return null

Expand All @@ -56,16 +47,11 @@ export function SetupLabwareMap({

const protocolModulesInfo = getProtocolModulesInfo(protocolAnalysis, deckDef)

const attachedProtocolModuleMatches = getAttachedProtocolModuleMatches(
attachedModules,
protocolModulesInfo
)

const initialLoadedLabwareByAdapter = parseInitialLoadedLabwareByAdapter(
commands
)

const modulesOnDeck = attachedProtocolModuleMatches.map(module => {
const modulesOnDeck = protocolModulesInfo.map(module => {
const labwareInAdapterInMod =
module.nestedLabwareId != null
? initialLoadedLabwareByAdapter[module.nestedLabwareId]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,18 @@ import {
THERMOCYCLER_MODULE_V1,
} from '@opentrons/shared-data'

import { useAttachedModules } from '../../hooks'
import { LabwareInfoOverlay } from '../LabwareInfoOverlay'
import { LiquidsLabwareDetailsModal } from './LiquidsLabwareDetailsModal'
import { getWellFillFromLabwareId } from './utils'
import { getLabwareRenderInfo } from '../utils/getLabwareRenderInfo'
import { getStandardDeckViewLayerBlockList } from '../utils/getStandardDeckViewLayerBlockList'
import { getAttachedProtocolModuleMatches } from '../../../ProtocolSetupModulesAndDeck/utils'
import { getProtocolModulesInfo } from '../utils/getProtocolModulesInfo'

import type {
CompletedProtocolAnalysis,
ProtocolAnalysisOutput,
} from '@opentrons/shared-data'

const ATTACHED_MODULE_POLL_MS = 5000

interface SetupLiquidsMapProps {
runId: string
protocolAnalysis: CompletedProtocolAnalysis | ProtocolAnalysisOutput | null
Expand All @@ -50,10 +46,6 @@ export function SetupLiquidsMap(
const [liquidDetailsLabwareId, setLiquidDetailsLabwareId] = React.useState<
string | null
>(null)
const attachedModules =
useAttachedModules({
refetchInterval: ATTACHED_MODULE_POLL_MS,
}) ?? []

if (protocolAnalysis == null) return null

Expand All @@ -75,12 +67,7 @@ export function SetupLiquidsMap(

const protocolModulesInfo = getProtocolModulesInfo(protocolAnalysis, deckDef)

const attachedProtocolModuleMatches = getAttachedProtocolModuleMatches(
attachedModules,
protocolModulesInfo
)

const modulesOnDeck = attachedProtocolModuleMatches.map(module => {
const modulesOnDeck = protocolModulesInfo.map(module => {
const labwareInAdapterInMod =
module.nestedLabwareId != null
? initialLoadedLabwareByAdapter[module.nestedLabwareId]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ describe('SetupLiquidsMap', () => {
when(vi.mocked(getAttachedProtocolModuleMatches))
.calledWith(
mockFetchModulesSuccessActionPayloadModules,
mockProtocolModuleInfo
mockProtocolModuleInfo,
[]
)
.thenReturn([
{
Expand Down Expand Up @@ -299,7 +300,8 @@ describe('SetupLiquidsMap', () => {
when(vi.mocked(getAttachedProtocolModuleMatches))
.calledWith(
mockFetchModulesSuccessActionPayloadModules,
mockProtocolModuleInfo
mockProtocolModuleInfo,
[]
)
.thenReturn([
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import * as React from 'react'
import { createPortal } from 'react-dom'
import { useTranslation } from 'react-i18next'
import { useHistory } from 'react-router-dom'
import {
useDeckConfigurationQuery,
useModulesQuery,
} from '@opentrons/react-api-client'
import {
ALIGN_CENTER,
COLORS,
DIRECTION_COLUMN,
DIRECTION_ROW,
Flex,
Icon,
PrimaryButton,
SPACING,
StyledText,
TYPOGRAPHY,
Expand All @@ -20,14 +20,19 @@ import {
getFixtureDisplayName,
getCutoutFixturesForModuleModel,
MAGNETIC_BLOCK_V1,
getModuleDisplayName,
} from '@opentrons/shared-data'
import { getTopPortalEl } from '../../../../App/portal'
import { LegacyModal } from '../../../../molecules/LegacyModal'
import { Modal } from '../../../../molecules/Modal'
import { FixtureOption } from '../../../DeviceDetailsDeckConfiguration/AddFixtureModal'

import { SmallButton } from '../../../../atoms/buttons'
import { useCloseCurrentRun } from '../../../ProtocolUpload/hooks'

import type { ModuleModel, DeckDefinition } from '@opentrons/shared-data'
import { FixtureOption } from '../../../DeviceDetailsDeckConfiguration/AddFixtureModal'

const EQUIPMENT_POLL_MS = 5000
interface ModuleFixtureOption {
moduleModel: ModuleModel
usbPort?: number
Expand All @@ -39,6 +44,8 @@ interface ChooseModuleToConfigureModalProps {
deckDef: DeckDefinition
isOnDevice: boolean
requiredModuleModel: ModuleModel
robotName: string
displaySlotName: string
}

export const ChooseModuleToConfigureModal = (
Expand All @@ -50,9 +57,14 @@ export const ChooseModuleToConfigureModal = (
deckDef,
requiredModuleModel,
isOnDevice,
robotName,
displaySlotName,
} = props
const { t } = useTranslation(['protocol_setup', 'shared'])
const attachedModules = useModulesQuery().data?.data ?? []
const history = useHistory()
const { closeCurrentRun } = useCloseCurrentRun()
const attachedModules =
useModulesQuery({ refetchInterval: EQUIPMENT_POLL_MS })?.data?.data ?? []
const deckConfig = useDeckConfigurationQuery()?.data ?? []
const unconfiguredModuleMatches =
attachedModules.filter(
Expand Down Expand Up @@ -94,17 +106,52 @@ export const ChooseModuleToConfigureModal = (
)
}
)
const handleCancelRun = (): void => {
closeCurrentRun()
}
const handleNavigateToDeviceDetails = (): void => {
history.push(`/devices/${robotName}`)
}
const emptyState = (
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing16}>
<StyledText as="p">
{t('there_are_no_unconfigured_modules', {
module: getModuleDisplayName(requiredModuleModel),
})}
</StyledText>
{isOnDevice ? (
<SmallButton
onClick={handleCancelRun}
buttonText={t('cancel_protocol_and_edit_deck_config')}
/>
) : (
<PrimaryButton onClick={handleNavigateToDeviceDetails}>
{t('update_deck_config')}
</PrimaryButton>
)}
</Flex>
)

const contents =
fixtureOptions.length > 0 ? (
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing16}>
<StyledText as="p">{t('add_this_deck_hardware')}</StyledText>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing8}>
{fixtureOptions}
</Flex>
</Flex>
) : (
emptyState
)

return createPortal(
isOnDevice ? (
<Modal
onOutsideClick={onCloseClick}
header={{
title: t('deck_conflict'),
title: t('add_to_slot', { slotName: displaySlotName }),
hasExitIcon: true,
onClick: onCloseClick,
iconName: 'ot-alert',
iconColor: COLORS.yellow50,
}}
>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing32}>
Expand All @@ -114,7 +161,7 @@ export const ChooseModuleToConfigureModal = (
paddingTop={SPACING.spacing8}
gridGap={SPACING.spacing8}
>
{fixtureOptions}
{contents}
</Flex>
</Flex>
</Flex>
Expand All @@ -127,9 +174,8 @@ export const ChooseModuleToConfigureModal = (
gridGap={SPACING.spacing10}
alignItems={ALIGN_CENTER}
>
<Icon name="ot-alert" size="1rem" color={COLORS.yellow50} />
<StyledText as="h3" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{t('deck_conflict')}
{t('add_to_slot', { slotName: displaySlotName })}
</StyledText>
</Flex>
}
Expand All @@ -143,7 +189,7 @@ export const ChooseModuleToConfigureModal = (
paddingTop={SPACING.spacing8}
gridGap={SPACING.spacing8}
>
{fixtureOptions}
{contents}
</Flex>
</Flex>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ interface LocationConflictModalProps {
onCloseClick: () => void
cutoutId: CutoutId
deckDef: DeckDefinition
robotName: string
missingLabwareDisplayName?: string | null
requiredFixtureId?: CutoutFixtureId
requiredModule?: ModuleModel
Expand All @@ -60,6 +61,7 @@ export const LocationConflictModal = (
const {
onCloseClick,
cutoutId,
robotName,
missingLabwareDisplayName,
requiredFixtureId,
requiredModule,
Expand Down Expand Up @@ -153,18 +155,25 @@ export const LocationConflictModal = (
protocolSpecifiesDisplayName = getModuleDisplayName(requiredModule)
}

if (showModuleSelect && requiredModule) {
const displaySlotName = isThermocycler
? 'A1 + B1'
: getCutoutDisplayName(cutoutId)

if (showModuleSelect && requiredModule != null) {
return createPortal(
<ChooseModuleToConfigureModal
handleConfigureModule={handleConfigureModule}
requiredModuleModel={requiredModule}
onCloseClick={onCloseClick}
isOnDevice={isOnDevice}
deckDef={deckDef}
robotName={robotName}
displaySlotName={displaySlotName}
/>,
getTopPortalEl()
)
}

return createPortal(
isOnDevice ? (
<Modal
Expand Down Expand Up @@ -200,11 +209,7 @@ export const LocationConflictModal = (
fontWeight={TYPOGRAPHY.fontWeightBold}
paddingBottom={SPACING.spacing8}
>
{t('slot_location', {
slotName: isThermocycler
? 'A1 + B1'
: getCutoutDisplayName(cutoutId),
})}
{t('slot_location', { slotName: displaySlotName })}
</StyledText>
<Flex
flexDirection={DIRECTION_COLUMN}
Expand Down Expand Up @@ -303,11 +308,7 @@ export const LocationConflictModal = (
fontSize={TYPOGRAPHY.fontSizeH4}
fontWeight={TYPOGRAPHY.fontWeightBold}
>
{t('slot_location', {
slotName: isThermocycler
? 'A1 + B1'
: getCutoutDisplayName(cutoutId),
})}
{t('slot_location', { slotName: displaySlotName })}
</StyledText>
<Flex
flexDirection={DIRECTION_COLUMN}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const NotConfiguredModal = (
width="27.75rem"
>
<Flex flexDirection={DIRECTION_COLUMN}>
<StyledText as="p">{t('add_fixture_to_deck')}</StyledText>
<StyledText as="p">{t('add_this_deck_hardware')}</StyledText>
<Flex paddingTop={SPACING.spacing16} flexDirection={DIRECTION_COLUMN}>
<Flex
padding={`${SPACING.spacing8} ${SPACING.spacing16}`}
Expand Down
Loading

0 comments on commit 5415917

Please sign in to comment.