Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions protocol-designer/src/assets/localization/en/shared.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
"slot_detail": "Slot Detail",
"software_manual": "Software manual",
"source_well": "Source well",
"stacker": "Stacker {{slot}}",
"stagingArea": "Staging area",
"start_point": "Edit {{prefix}} start point",
"step_count": "Step {{current}}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'

import { getModuleDisplayName } from '@opentrons/shared-data'
import { getTopLocationInStack } from '@opentrons/step-generation'
import {
getIsSlotAHopper,
getTopLocationInStack,
} from '@opentrons/step-generation'

import { getLiquidEntities } from '/protocol-designer/step-forms/selectors'
import { getDeckSetupForActiveItem } from '/protocol-designer/top-selectors/labware-locations'
Expand Down Expand Up @@ -32,10 +35,10 @@ export function SlotDetailsContainer(
)
const nickNames = useSelector(uiLabwareSelectors.getLabwareNicknamesById)
const liquidEntities = useSelector(getLiquidEntities)

if (slot == null || (slot === 'offDeck' && offDeckLabwareId == null)) {
return null
}
const isSlotAHopper = getIsSlotAHopper(slot)

const {
modules: deckSetupModules,
Expand All @@ -47,11 +50,13 @@ export function SlotDetailsContainer(
offDeckLabwareId != null ? nickNames[offDeckLabwareId] : null

const moduleOnSlot = Object.values(deckSetupModules).find(
module => module.slot === slot
module =>
module.slot === slot || (slot.includes(module.slot) && isSlotAHopper)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the includes() for? What value does slot have such that you would need includes() to search for a substring?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh its because the slot for on the hopper is hopperA4, etc. hmmm maybe we can map the fake hopper slot to a real slot by that point so we don't need that extra logic. let me see

)
const fullStackFromLabwares = getFullStackFromLabwaresOnDeck(
Object.values(deckSetupLabwares),
slot
slot,
isSlotAHopper
)
const topLocationLabwareId =
fullStackFromLabwares?.length > 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@ import {
THERMOCYCLER_MODULE_V1,
THERMOCYCLER_MODULE_V2,
} from '@opentrons/shared-data'
import {
getIsSlotAHopper,
HOPPER_LOCATION_MAP,
} from '@opentrons/step-generation'

import { LINE_CLAMP_TEXT_STYLE } from '/protocol-designer/components/atoms'
import { useDeckSetupWindowBreakPoint } from '/protocol-designer/pages/Designer/DeckSetup/utils'

import type { FC } from 'react'
import type { RobotType } from '@opentrons/shared-data'
import type { HopperLocationMapKey } from '@opentrons/step-generation'

interface SlotInformationProps {
location: string
Expand All @@ -51,11 +56,18 @@ export const SlotInformation: FC<SlotInformationProps> = ({
robotType === FLEX_ROBOT_TYPE
? TC_MODULE_LOCATION_OT3
: TC_MODULE_LOCATION_OT2
const modifiedLocation =

let modifiedLocation = location
if (
modules.includes(getModuleDisplayName(THERMOCYCLER_MODULE_V2)) ||
modules.includes(getModuleDisplayName(THERMOCYCLER_MODULE_V1))
? tcDisplayLocation
: location
) {
modifiedLocation = tcDisplayLocation
} else if (getIsSlotAHopper(location)) {
modifiedLocation = t('stacker', {
slot: HOPPER_LOCATION_MAP[location as HopperLocationMapKey],
})
}

return (
<Flex
Expand Down
3 changes: 3 additions & 0 deletions protocol-designer/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,6 @@ export const CHANNELS_MAPPED_TO_MAX_SPEED: Record<
export const MINIMUM_LIQUID_CLASS_VOLUME = 1

export const ACCEPTED_PROTOCOL_FILE_TYPES = '.json,.py'

export const HOPPER_LABWARE_X_OFFSET = 178
export const HOPPER_ZOOM_OFFSET_POSTITION = 230
1 change: 1 addition & 0 deletions protocol-designer/src/labware-ingred/actions/thunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const createContainer: (
const id = `${uuid()}:${labwareUri}`
const labwareDef =
labwareDefSelectors.getLabwareDefsByURI(state)[labwareUri]

const labwareDisplayCategory = labwareDef.metadata.displayCategory
const isTiprack = getIsTiprack(labwareDef)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ import {
WASTE_CHUTE_CUTOUT,
} from '@opentrons/shared-data'

import { DECK_SETUP_TOOLS_WIDTH_REM } from '../../../constants'
import {
DECK_SETUP_TOOLS_WIDTH_REM,
HOPPER_ZOOM_OFFSET_POSTITION,
} from '../../../constants'
import { getDisableModuleRestrictions } from '../../../feature-flags/selectors'
import {
editSlotInfo,
Expand Down Expand Up @@ -143,6 +146,8 @@ export function DeckSetupContainer(
}, '')

const addEquipment = (slotId: string): void => {
const isOnHopper = slotId.includes('hopper')
const slot = isOnHopper ? slotId.split('hopper')[1] : slotId
const { createdModuleForSlot, preSelectedFixture } = getSlotInformation({
deckSetup: activeDeckSetup,
slot: slotId,
Expand All @@ -151,15 +156,19 @@ export function DeckSetupContainer(

const cutoutId =
getCutoutIdForAddressableArea(
slotId as AddressableAreaName,
slot as AddressableAreaName,
deckDef.cutoutFixtures
) ?? null
if (cutoutId == null) {
console.error('expected to find a cutoutId but could not')
}
dispatch(selectZoomedIntoSlot({ slot: slotId, cutout: cutoutId }))

const zoomInSlotPosition = getPositionFromSlotId(slotId ?? '', deckDef)
const zoomInSlotPosition = getPositionFromSlotId(
slot ?? '',
deckDef,
isOnHopper ? HOPPER_ZOOM_OFFSET_POSTITION : undefined
)
if (zoomInSlotPosition != null) {
const zoomedInViewBox = zoomInOnCoordinate({
x: zoomInSlotPosition[0],
Expand Down Expand Up @@ -210,7 +219,6 @@ export function DeckSetupContainer(
)

const svgContainerWidth = getSVGContainerWidth(robotType, isZoomed)

return (
<>
<Flex
Expand Down
96 changes: 79 additions & 17 deletions protocol-designer/src/pages/Designer/DeckSetup/DeckSetupDetails.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would love to refactor this component at some point since it's quite large and a bit unwieldy

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import {
isAddressableAreaStandardSlot,
THERMOCYCLER_MODULE_TYPE,
} from '@opentrons/shared-data'
import { getSlotInLocationStack } from '@opentrons/step-generation'
import {
getIsSlotAHopper,
getSlotInLocationStack,
HOPPER_LOCATION_MAP,
} from '@opentrons/step-generation'

import { HOPPER_LABWARE_X_OFFSET } from '/protocol-designer/constants'

import { LabwareOnDeck } from '../../../components/organisms'
import { getSlotsWithCollisions } from '../../../components/organisms/utils'
Expand Down Expand Up @@ -53,6 +59,7 @@ import type {
DeckSlotId,
} from '@opentrons/shared-data'
import type {
HopperLocationMapKey,
ModuleTemporalProperties,
ThermocyclerModuleState,
} from '@opentrons/step-generation'
Expand Down Expand Up @@ -86,6 +93,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
showGen1MultichannelCollisionWarnings,
stagingAreaCutoutIds,
} = props
const { labware: activeLabware } = activeDeckSetup
const robotType = useSelector(getRobotType)
const slotIdsBlockedBySpanning = getSlotIdsBlockedBySpanningForThermocycler(
activeDeckSetup,
Expand Down Expand Up @@ -118,7 +126,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
draggedLabware,
})
const swapBlockedAdapter = getSwapBlockedAdapter({
labwareById: activeDeckSetup.labware,
labwareById: activeLabware,
hoveredLabware,
draggedLabware,
})
Expand All @@ -134,6 +142,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
createdModuleForSlot,
preSelectedFixture,
slotPosition,
isSlotAHopper,
} = useMemo(() => {
return getSlotInformation({
deckSetup: activeDeckSetup,
Expand All @@ -142,8 +151,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
})
}, [activeDeckSetup, selectedZoomInSlot])

const createdTopLabwareForSlot =
activeDeckSetup.labware[createdStackForSlot[0]]
const createdTopLabwareForSlot = activeLabware[createdStackForSlot[0]]
const amount = createdStackForSlot?.length ?? 1
// initiate the slot's info
useEffect(() => {
Expand Down Expand Up @@ -171,11 +179,19 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
selectedZoomInSlot,
])

const allLabware = Object.values(activeDeckSetup.labware)
const allLabware = Object.values(activeLabware)

const allModules: ModuleOnDeck[] = values(activeDeckSetup.modules)
const menuListSlotPosition = getPositionFromSlotId(menuListId ?? '', deckDef)

const isMenuListIdForHopper =
menuListId != null && getIsSlotAHopper(menuListId)
const adjustedMenuListId = isMenuListIdForHopper
? HOPPER_LOCATION_MAP[menuListId as HopperLocationMapKey]
: menuListId
const menuListSlotPosition = getPositionFromSlotId(
adjustedMenuListId ?? '',
deckDef,
isMenuListIdForHopper ? HOPPER_LABWARE_X_OFFSET : undefined
)
const multichannelWarningSlotIds: AddressableAreaName[] =
showGen1MultichannelCollisionWarnings
? getSlotsWithCollisions(deckDef, allModules)
Expand All @@ -186,7 +202,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
? getAdjacentLabware(
preSelectedFixture,
selectedSlot.cutout,
activeDeckSetup.labware
activeLabware
)
: null

Expand Down Expand Up @@ -245,10 +261,8 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
}
}

const { topMostId, rightBelowTopId } = getLabwaresOnModuleFromStack(
moduleOnDeck.id,
allLabware
)
const { topMostId, rightBelowTopId, hopperTopMostId } =
getLabwaresOnModuleFromStack(moduleOnDeck.id, allLabware)
const labwareInterfaceBoundingBox = {
xDimension: moduleDef.dimensions.labwareInterfaceXDimension ?? 0,
yDimension: moduleDef.dimensions.labwareInterfaceYDimension ?? 0,
Expand All @@ -273,11 +287,9 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
}
: tempInnerProps
const labwareOnModule =
topMostId != null ? activeDeckSetup.labware[topMostId] : null
topMostId != null ? activeLabware[topMostId] : null
const labwareRightBelowTopMostLabware =
rightBelowTopId != null
? activeDeckSetup.labware[rightBelowTopId]
: null
rightBelowTopId != null ? activeLabware[rightBelowTopId] : null
const isAdapter = labwareOnModule?.def.allowedRoles?.includes('adapter')

return moduleOnDeck.slot !== selectedSlot.slot ? (
Expand All @@ -299,6 +311,34 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
: 'offsetToSlot'
}
>
{hopperTopMostId != null ? (
<>
<LabwareOnDeck
x={HOPPER_LABWARE_X_OFFSET}
y={0}
labwareOnDeck={activeLabware[hopperTopMostId]}
/>
<HighlightLabware
labwareOnDeck={activeLabware[hopperTopMostId]}
position={[HOPPER_LABWARE_X_OFFSET, 0, 0]}
isZoomed={selectedZoomInSlot != null}
/>
<LabwareControls
terminalItemId={terminalItemId}
itemId={`hopper${slotId}`}
setHover={setHover}
setShowMenuListForId={setShowMenuListForId}
hover={hover}
slotPosition={[HOPPER_LABWARE_X_OFFSET, 0, 0]} // Module Component already handles nested positioning
setHoveredLabware={setHoveredLabware}
setDraggedLabware={setDraggedLabware}
// TODO: disallow the ability to drag/drop labware from the hopper and shuttle
swapBlocked={false}
labwareOnDeck={activeLabware[hopperTopMostId]}
isSelected={selectedZoomInSlot != null}
/>
</>
) : null}
{labwareOnModule != null &&
!isLabwareOccludedByThermocyclerLid ? (
<>
Expand Down Expand Up @@ -381,6 +421,26 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
addEquipment={addEquipment}
/>
) : null}
{hopperTopMostId == null &&
moduleOnDeck.type === FLEX_STACKER_MODULE_TYPE ? (
<SlotControls
terminalItemId={terminalItemId}
itemId={`hopper${slotId}`}
key={`${moduleOnDeck.slot}_flexHopper`}
slotPosition={[HOPPER_LABWARE_X_OFFSET, 0, 0]}
slotBoundingBox={labwareInterfaceBoundingBox}
moduleType={moduleOnDeck.type}
handleDragHover={handleHoverEmptySlot}
slotId={moduleOnDeck.id}
hover={hover}
setHover={setHover}
setShowMenuListForId={setShowMenuListForId}
isSelected={selectedZoomInSlot != null}
deckDef={deckDef}
stagingAreaAddressableAreas={[]}
addEquipment={addEquipment}
/>
) : null}
</Module>
</Fragment>
) : null
Expand Down Expand Up @@ -431,6 +491,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
const moduleOnSlot = Object.values(activeDeckSetup.modules).find(
module => module.slot === addressableArea.id
)

return (
<SlotControls
terminalItemId={terminalItemId}
Expand Down Expand Up @@ -465,7 +526,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
}
const slot = getSlotInLocationStack(labware.stack)
const labwareAmount = labware.stack.reduce(
(amount, item) => amount + (activeDeckSetup.labware[item] ? 1 : 0),
(amount, item) => amount + (activeLabware[item] ? 1 : 0),
0
)
const isTopLabware = labware.stack[0] === labware.id
Expand Down Expand Up @@ -649,6 +710,7 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element {
deckDef={deckDef}
robotType={robotType}
slotPosition={slotPosition}
isSlotAHopper={isSlotAHopper}
/>

{/* slot overflow menu */}
Expand Down
Loading
Loading