Skip to content

Commit

Permalink
feat(app): Stub out flex stacker in deck config (#17381)
Browse files Browse the repository at this point in the history
  • Loading branch information
smb2268 authored Jan 29, 2025
1 parent 6123b81 commit 8004d59
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 22 deletions.
1 change: 1 addition & 0 deletions api-client/src/modules/api-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface PhysicalPort {
port: number
hub: boolean
portGroup: PortGroup
hubPort?: number
}

type ModuleOffsetSource =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ import {
TRASH_BIN_ADAPTER_FIXTURE,
WASTE_CHUTE_CUTOUT,
WASTE_CHUTE_FIXTURES,
FLEX_STACKER_MODULE_V1,
FLEX_STACKER_V1_FIXTURE,
FLEX_STACKER_WITH_WASTE_CHUTE_ADAPTER_COVERED_FIXTURE,
FLEX_STACKER_WTIH_WASTE_CHUTE_ADAPTER_NO_COVER_FIXTURE,
} from '@opentrons/shared-data'

import { ODD_FOCUS_VISIBLE } from '/app/atoms/buttons/constants'
Expand Down Expand Up @@ -249,6 +253,52 @@ export function AddFixtureModal({
]
}
}
if (
cutoutId === 'cutoutD3' &&
unconfiguredMods.some(m => m.moduleModel === FLEX_STACKER_MODULE_V1)
) {
const unconfiguredFlexStackers: CutoutConfig[][] = []
unconfiguredMods
.filter(mod => mod.moduleModel === FLEX_STACKER_MODULE_V1)
.forEach(mod => {
unconfiguredFlexStackers.push([
{
cutoutId,
cutoutFixtureId: FLEX_STACKER_V1_FIXTURE,
opentronsModuleSerialNumber: mod.serialNumber,
},
])
unconfiguredFlexStackers.push([
{
cutoutId,
cutoutFixtureId: FLEX_STACKER_WITH_WASTE_CHUTE_ADAPTER_COVERED_FIXTURE,
opentronsModuleSerialNumber: mod.serialNumber,
},
])
unconfiguredFlexStackers.push([
{
cutoutId,
cutoutFixtureId: FLEX_STACKER_WTIH_WASTE_CHUTE_ADAPTER_NO_COVER_FIXTURE,
opentronsModuleSerialNumber: mod.serialNumber,
},
])
})
availableOptions.push(...unconfiguredFlexStackers)
} else if (
STAGING_AREA_CUTOUTS.includes(cutoutId) &&
unconfiguredMods.some(m => m.moduleModel === FLEX_STACKER_MODULE_V1)
) {
const unconfiguredFlexStackers = unconfiguredMods
.filter(mod => mod.moduleModel === FLEX_STACKER_MODULE_V1)
.map(mod => [
{
cutoutId,
cutoutFixtureId: FLEX_STACKER_V1_FIXTURE,
opentronsModuleSerialNumber: mod.serialNumber,
},
])
availableOptions = [...availableOptions, ...unconfiguredFlexStackers]
}
} else if (optionStage === 'wasteChuteOptions') {
availableOptions = WASTE_CHUTE_FIXTURES.map(fixture => [
{
Expand Down Expand Up @@ -315,22 +365,30 @@ export function AddFixtureModal({
closeModal()
}

const fixtureOptions = availableOptions.map(cutoutConfigs => (
<FixtureOption
key={cutoutConfigs[0].cutoutFixtureId}
optionName={getFixtureDisplayName(
cutoutConfigs[0].cutoutFixtureId,
(modulesData?.data ?? []).find(
m => m.serialNumber === cutoutConfigs[0].opentronsModuleSerialNumber
)?.usbPort.port
)}
buttonText={t('add')}
onClickHandler={() => {
handleAddFixture(cutoutConfigs)
}}
isOnDevice={isOnDevice}
/>
))
const fixtureOptions = availableOptions.map(cutoutConfigs => {
const usbPort = (modulesData?.data ?? []).find(
m => m.serialNumber === cutoutConfigs[0].opentronsModuleSerialNumber
)?.usbPort
const portDisplay =
usbPort?.hubPort != null
? `${usbPort.port}.${usbPort.hubPort}`
: usbPort?.port

return (
<FixtureOption
key={cutoutConfigs[0].cutoutFixtureId}
optionName={getFixtureDisplayName(
cutoutConfigs[0].cutoutFixtureId,
portDisplay
)}
buttonText={t('add')}
onClickHandler={() => {
handleAddFixture(cutoutConfigs)
}}
isOnDevice={isOnDevice}
/>
)
})

return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Icon } from '../../icons'
import { Btn, Text } from '../../primitives'
import { TYPOGRAPHY } from '../../ui-style-constants'
import { COLORS } from '../../helix-design-system'
import { RobotCoordsForeignObject } from '../Deck/RobotCoordsForeignObject'
import {
COLUMN_3_X_ADJUSTMENT,
CONFIG_STYLE_EDITABLE,
CONFIG_STYLE_READ_ONLY,
FIXTURE_HEIGHT,
STAGING_AREA_FIXTURE_WIDTH,
Y_ADJUSTMENT,
CONFIG_STYLE_SELECTED,
} from './constants'

import type {
CutoutFixtureId,
CutoutId,
DeckDefinition,
} from '@opentrons/shared-data'

interface FlexStackerFixtureProps {
deckDefinition: DeckDefinition
fixtureLocation: CutoutId
cutoutFixtureId: CutoutFixtureId
hasWasteChute: boolean
handleClickRemove?: (
fixtureLocation: CutoutId,
cutoutFixtureId: CutoutFixtureId
) => void
selected?: boolean
}

const FLEX_STACKER_FIXTURE_DISPLAY_NAME = 'Stacker'
const FLEX_STACKER_WASTE_CHUTE_DISPLAY_NAME = 'Stacker + Waste chute'

export function FlexStackerFixture(
props: FlexStackerFixtureProps
): JSX.Element {
const {
deckDefinition,
handleClickRemove,
fixtureLocation,
cutoutFixtureId,
hasWasteChute,
selected = false,
} = props

const cutoutDef = deckDefinition.locations.cutouts.find(
cutout => cutout.id === fixtureLocation
)

/**
* deck definition cutout position is the position of the single slot located within that cutout
* so, to get the position of the cutout itself we must add an adjustment to the slot position
* the adjustment for x is different for right side/left side
*/
const [xSlotPosition = 0, ySlotPosition = 0] = cutoutDef?.position ?? []

const x = xSlotPosition + COLUMN_3_X_ADJUSTMENT

const y = ySlotPosition + Y_ADJUSTMENT

const editableStyle = selected ? CONFIG_STYLE_SELECTED : CONFIG_STYLE_EDITABLE
return (
<RobotCoordsForeignObject
width={STAGING_AREA_FIXTURE_WIDTH}
height={FIXTURE_HEIGHT}
x={x}
y={y}
flexProps={{ flex: '1' }}
foreignObjectProps={{ flex: '1' }}
>
<Btn
css={handleClickRemove != null ? editableStyle : CONFIG_STYLE_READ_ONLY}
cursor={handleClickRemove != null ? 'pointer' : 'default'}
onClick={
handleClickRemove != null
? () => {
handleClickRemove(fixtureLocation, cutoutFixtureId)
}
: () => {}
}
>
<Text css={TYPOGRAPHY.smallBodyTextSemiBold}>
{hasWasteChute
? FLEX_STACKER_WASTE_CHUTE_DISPLAY_NAME
: FLEX_STACKER_FIXTURE_DISPLAY_NAME}
</Text>
{handleClickRemove != null ? (
<Icon name="remove" color={COLORS.white} size="2rem" />
) : null}
</Btn>
</RobotCoordsForeignObject>
)
}
29 changes: 24 additions & 5 deletions components/src/hardware-sim/DeckConfigurator/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
TEMPERATURE_MODULE_V2_FIXTURE,
MAGNETIC_BLOCK_V1_FIXTURE,
ABSORBANCE_READER_V1_FIXTURE,
FLEX_STACKER_V1_FIXTURE,
FLEX_STACKER_FIXTURES,
STAGING_AREA_SLOT_WITH_MAGNETIC_BLOCK_V1_FIXTURE,
THERMOCYCLER_MODULE_CUTOUTS,
} from '@opentrons/shared-data'
Expand All @@ -24,18 +26,19 @@ import { StagingAreaConfigFixture } from './StagingAreaConfigFixture'
import { TrashBinConfigFixture } from './TrashBinConfigFixture'
import { WasteChuteConfigFixture } from './WasteChuteConfigFixture'
import { StaticFixture } from './StaticFixture'
import { TemperatureModuleFixture } from './TemperatureModuleFixture'
import { HeaterShakerFixture } from './HeaterShakerFixture'
import { MagneticBlockFixture } from './MagneticBlockFixture'
import { ThermocyclerFixture } from './ThermocyclerFixture'
import { AbsorbanceReaderFixture } from './AbsorbanceReaderFixture'
import { FlexStackerFixture } from './FlexStackerFixture'

import type { ReactNode } from 'react'
import type {
CutoutFixtureId,
CutoutId,
DeckConfiguration,
} from '@opentrons/shared-data'
import { TemperatureModuleFixture } from './TemperatureModuleFixture'
import { HeaterShakerFixture } from './HeaterShakerFixture'
import { MagneticBlockFixture } from './MagneticBlockFixture'
import { ThermocyclerFixture } from './ThermocyclerFixture'
import { AbsorbanceReaderFixture } from './AbsorbanceReaderFixture'

export * from './constants'

Expand Down Expand Up @@ -116,6 +119,9 @@ export function DeckConfigurator(props: DeckConfiguratorProps): JSX.Element {
({ cutoutFixtureId }) =>
cutoutFixtureId === STAGING_AREA_SLOT_WITH_MAGNETIC_BLOCK_V1_FIXTURE
)
const flexStackerFixtures = deckConfig.filter(({ cutoutFixtureId }) =>
FLEX_STACKER_FIXTURES.includes(cutoutFixtureId)
)

return (
<RobotCoordinateSpace
Expand Down Expand Up @@ -262,6 +268,19 @@ export function DeckConfigurator(props: DeckConfiguratorProps): JSX.Element {
selected={cutoutId === selectedCutoutId}
/>
))}
{flexStackerFixtures.map(({ cutoutId, cutoutFixtureId }) => (
<FlexStackerFixture
key={cutoutId}
deckDefinition={deckDef}
handleClickRemove={
editableCutoutIds.includes(cutoutId) ? handleClickRemove : undefined
}
fixtureLocation={cutoutId}
cutoutFixtureId={cutoutFixtureId}
hasWasteChute={cutoutFixtureId !== FLEX_STACKER_V1_FIXTURE}
selected={cutoutId === selectedCutoutId}
/>
))}
{additionalStaticFixtures?.map(staticFixture => (
<StaticFixture
key={staticFixture.location}
Expand Down
10 changes: 10 additions & 0 deletions shared-data/js/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,10 @@ export const ABSORBANCE_READER_V1_FIXTURE: 'absorbanceReaderV1' =
'absorbanceReaderV1'
export const FLEX_STACKER_V1_FIXTURE: 'flexStackerModuleV1' =
'flexStackerModuleV1'
export const FLEX_STACKER_WITH_WASTE_CHUTE_ADAPTER_COVERED_FIXTURE: 'flexStackerModuleV1WithWasteChuteRightAdapterCovered' =
'flexStackerModuleV1WithWasteChuteRightAdapterCovered'
export const FLEX_STACKER_WTIH_WASTE_CHUTE_ADAPTER_NO_COVER_FIXTURE: 'flexStackerModuleV1WithWasteChuteRightAdapterNoCover' =
'flexStackerModuleV1WithWasteChuteRightAdapterNoCover'

export const MODULE_FIXTURES_BY_MODEL: {
[moduleModel in ModuleModel]?: CutoutFixtureId[]
Expand Down Expand Up @@ -617,6 +621,12 @@ export const WASTE_CHUTE_STAGING_AREA_FIXTURES: CutoutFixtureId[] = [
STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE,
]

export const FLEX_STACKER_FIXTURES: CutoutFixtureId[] = [
FLEX_STACKER_V1_FIXTURE,
FLEX_STACKER_WITH_WASTE_CHUTE_ADAPTER_COVERED_FIXTURE,
FLEX_STACKER_WTIH_WASTE_CHUTE_ADAPTER_NO_COVER_FIXTURE,
]

export const LOW_VOLUME_PIPETTES = ['p50_single_flex', 'p50_multi_flex']

// default hex values for liquid colors
Expand Down
26 changes: 25 additions & 1 deletion shared-data/js/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ import {
ABSORBANCE_READER_V1,
MODULE_FIXTURES_BY_MODEL,
STAGING_AREA_SLOT_WITH_MAGNETIC_BLOCK_V1_FIXTURE,
FLEX_STACKER_MODULE_V1,
FLEX_STACKER_V1_FIXTURE,
FLEX_STACKER_WITH_WASTE_CHUTE_ADAPTER_COVERED_FIXTURE,
FLEX_STACKER_WTIH_WASTE_CHUTE_ADAPTER_NO_COVER_FIXTURE,
} from './constants'
import { getModuleDisplayName } from './modules'
import { getCutoutIdForSlotName } from './helpers'
Expand Down Expand Up @@ -241,7 +245,7 @@ export function getAddressableAreaNamesFromLoadedModule(
// note: we've decided not to translate these strings
export function getFixtureDisplayName(
cutoutFixtureId: CutoutFixtureId | null,
usbPortNumber?: number
usbPortNumber?: number | string
): string {
switch (cutoutFixtureId) {
case STAGING_AREA_RIGHT_SLOT_FIXTURE:
Expand Down Expand Up @@ -290,6 +294,26 @@ export function getFixtureDisplayName(
ABSORBANCE_READER_V1
)} in USB-${usbPortNumber}`
: getModuleDisplayName(ABSORBANCE_READER_V1)
case FLEX_STACKER_V1_FIXTURE:
return usbPortNumber != null
? `${getModuleDisplayName(
FLEX_STACKER_MODULE_V1
)} in USB-${usbPortNumber}`
: getModuleDisplayName(FLEX_STACKER_MODULE_V1)
case FLEX_STACKER_WITH_WASTE_CHUTE_ADAPTER_COVERED_FIXTURE:
return usbPortNumber != null
? `${getModuleDisplayName(
FLEX_STACKER_MODULE_V1
)} in USB-${usbPortNumber} and waste chute with cover`
: `${getModuleDisplayName(
FLEX_STACKER_MODULE_V1
)} and waste chute with cover`
case FLEX_STACKER_WTIH_WASTE_CHUTE_ADAPTER_NO_COVER_FIXTURE:
return usbPortNumber != null
? `${getModuleDisplayName(
FLEX_STACKER_MODULE_V1
)} in USB-${usbPortNumber} and waste chute`
: `${getModuleDisplayName(FLEX_STACKER_MODULE_V1)} and waste chute`
default:
return 'Slot'
}
Expand Down

0 comments on commit 8004d59

Please sign in to comment.