Skip to content

Commit

Permalink
feat(protocol-designer): deck v4 touchups and staging area support (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jerader authored Nov 14, 2023
1 parent e401da0 commit c43d2d7
Show file tree
Hide file tree
Showing 36 changed files with 451 additions and 186 deletions.
14 changes: 9 additions & 5 deletions components/src/hardware-sim/BaseDeck/StagingAreaFixture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { SlotClip } from './SlotClip'

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

export type StagingAreaLocation = 'A3' | 'B3' | 'C3' | 'D3'
export type StagingAreaLocation =
| 'cutoutA3'
| 'cutoutB3'
| 'cutoutC3'
| 'cutoutD3'

interface StagingAreaFixtureProps extends React.SVGProps<SVGGElement> {
cutoutId: StagingAreaLocation
Expand Down Expand Up @@ -40,7 +44,7 @@ export function StagingAreaFixture(
const contentsByCutoutLocation: {
[cutoutId in StagingAreaLocation]: JSX.Element
} = {
A3: (
cutoutA3: (
<>
<SlotBase
d="M314.8,417.1h329.9c2.4,0,4.3-1.9,4.3-4.3v-97.4c0-2.4-1.9-4.3-4.3-4.3H314.8c-2.4,0-4.3,1.9-4.3,4.3v97.4C310.5,415.1,312.4,417.1,314.8,417.1z"
Expand All @@ -56,7 +60,7 @@ export function StagingAreaFixture(
<SlotClip d="M619.8,329.8v-10.7H609" stroke={slotClipColor} />
</>
),
B3: (
cutoutB3: (
<>
<SlotBase
d="M314.8,310h329.9c2.4,0,4.3-1.9,4.3-4.3v-97.2c0-2.4-1.9-4.3-4.3-4.3H314.8c-2.4,0-4.3,1.9-4.3,4.3v97.2C310.5,308.1,312.4,310,314.8,310z"
Expand All @@ -72,7 +76,7 @@ export function StagingAreaFixture(
<SlotClip d="M619.8,222.8v-10.7H609" stroke={slotClipColor} />
</>
),
C3: (
cutoutC3: (
<>
<SlotBase
d="M314.8,203.1h329.9c2.4,0,4.3-1.9,4.3-4.3v-97.4c0-2.4-1.9-4.3-4.3-4.3H314.8c-2.4,0-4.3,1.9-4.3,4.3v97.4C310.5,201.2,312.4,203.1,314.8,203.1z"
Expand All @@ -88,7 +92,7 @@ export function StagingAreaFixture(
<SlotClip d="M619.8,115.8v-10.7H609" stroke={slotClipColor} />
</>
),
D3: (
cutoutD3: (
<>
<SlotBase
d="M314.8,96.1h329.9c2.4,0,4.3-1.9,4.3-4.3V-5.6c0-2.4-1.9-4.3-4.3-4.3H314.8c-2.4,0-4.3,1.9-4.3,4.3v97.4C310.5,94.2,312.4,96.1,314.8,96.1z"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function FlexModuleTag(props: FlexModuleTagProps): JSX.Element {
return (
<RobotCoordsForeignDiv
width={dimensions.labwareInterfaceXDimension}
height={16}
height={20}
y={-22}
innerDivProps={{
backgroundColor: COLORS.darkGreyEnabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { START_TERMINAL_ITEM_ID, TerminalItemId } from '../../../steplist'
import { BlockedSlot } from './BlockedSlot'

import type {
AddressableArea,
CoordinateTuple,
Dimensions,
ModuleType,
Expand All @@ -42,7 +41,8 @@ interface DNDP {
interface OP {
slotPosition: CoordinateTuple | null
slotBoundingBox: Dimensions
slotId: AddressableArea['id']
// NOTE: slotId can be either AddressableAreaName or moduleId
slotId: string
moduleType: ModuleType | null
selectedTerminalItemId?: TerminalItemId | null
handleDragHover?: () => unknown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('FlexModuleTag', () => {
.calledWith(
partialComponentPropsMatcher({
width: 5,
height: 16,
height: 20,
})
)
.mockImplementation(({ children }) => (
Expand All @@ -48,7 +48,7 @@ describe('FlexModuleTag', () => {
.calledWith(
partialComponentPropsMatcher({
width: 5,
height: 16,
height: 20,
})
)
.mockImplementation(({ children }) => (
Expand Down
96 changes: 59 additions & 37 deletions protocol-designer/src/components/DeckSetup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,47 @@ import { useDispatch, useSelector } from 'react-redux'
import compact from 'lodash/compact'
import values from 'lodash/values'
import {
useOnClickOutside,
RobotWorkSpaceRenderProps,
Module,
COLORS,
TrashLocation,
DeckFromLayers,
FlexTrash,
Module,
RobotCoordinateSpaceWithDOMCoords,
WasteChuteFixture,
RobotWorkSpaceRenderProps,
SingleSlotFixture,
StagingAreaFixture,
StagingAreaLocation,
SingleSlotFixture,
DeckFromLayers,
TrashLocation,
useOnClickOutside,
WasteChuteFixture,
} from '@opentrons/components'
import {
AdditionalEquipmentEntity,
MODULES_WITH_COLLISION_ISSUES,
ModuleTemporalProperties,
} from '@opentrons/step-generation'
import {
getLabwareHasQuirk,
inferModuleOrientationFromSlot,
FLEX_ROBOT_TYPE,
getAddressableAreaFromSlotId,
getDeckDefFromRobotType,
OT2_ROBOT_TYPE,
getLabwareHasQuirk,
getModuleDef2,
getModuleDisplayName,
getPositionFromSlotId,
inferModuleOrientationFromSlot,
inferModuleOrientationFromXCoordinate,
isAddressableAreaStandardSlot,
OT2_ROBOT_TYPE,
STAGING_AREA_LOAD_NAME,
THERMOCYCLER_MODULE_TYPE,
getModuleDisplayName,
DeckDefinition,
RobotType,
FLEX_ROBOT_TYPE,
TRASH_BIN_LOAD_NAME,
STAGING_AREA_LOAD_NAME,
WASTE_CHUTE_CUTOUT,
WASTE_CHUTE_LOAD_NAME,
AddressableAreaName,
CutoutFixture,
CutoutId,
} from '@opentrons/shared-data'
import { FLEX_TRASH_DEF_URI, OT_2_TRASH_DEF_URI } from '../../constants'
import { selectors as labwareDefSelectors } from '../../labware-defs'

import { selectors as featureFlagSelectors } from '../../feature-flags'
import { getStagingAreaAddressableAreas } from '../../utils'
import {
getSlotIdsBlockedBySpanning,
getSlotIsEmpty,
Expand Down Expand Up @@ -72,12 +71,15 @@ import { Ot2ModuleTag } from './Ot2ModuleTag'
import { SlotLabels } from './SlotLabels'
import { getHasGen1MultiChannelPipette, getSwapBlocked } from './utils'

import type {
AddressableAreaName,
CutoutFixture,
CutoutId,
DeckDefinition,
RobotType,
} from '@opentrons/shared-data'

import styles from './DeckSetup.css'
import {
getAddressableAreaFromSlotId,
getPositionFromSlotId,
isAddressableAreaStandardSlot,
} from '@opentrons/shared-data/js'

export const DECK_LAYER_BLOCKLIST = [
'calibrationMarkings',
Expand All @@ -89,13 +91,24 @@ export const DECK_LAYER_BLOCKLIST = [
'screwHoles',
]

const OT2_STANDARD_DECK_VIEW_LAYER_BLOCK_LIST: string[] = [
'calibrationMarkings',
'fixedBase',
'doorStops',
'metalFrame',
'removalHandle',
'removableDeckOutline',
'screwHoles',
]

interface ContentsProps {
getRobotCoordsFromDOMCoords: RobotWorkSpaceRenderProps['getRobotCoordsFromDOMCoords']
activeDeckSetup: InitialDeckSetup
selectedTerminalItemId?: TerminalItemId | null
showGen1MultichannelCollisionWarnings: boolean
deckDef: DeckDefinition
robotType: RobotType
stagingAreaCutoutIds: CutoutId[]
trashSlot: string | null
}

Expand All @@ -110,6 +123,7 @@ export const DeckSetupContents = (props: ContentsProps): JSX.Element => {
deckDef,
robotType,
trashSlot,
stagingAreaCutoutIds,
} = props
// NOTE: handling module<>labware compat when moving labware to empty module
// is handled by SlotControls.
Expand Down Expand Up @@ -173,12 +187,6 @@ export const DeckSetupContents = (props: ContentsProps): JSX.Element => {
])
: []

console.log(
'AA',
deckDef.locations.addressableAreas.filter(addressableArea =>
isAddressableAreaStandardSlot(addressableArea.id, deckDef)
)
)
return (
<>
{/* all modules */}
Expand Down Expand Up @@ -300,6 +308,7 @@ export const DeckSetupContents = (props: ContentsProps): JSX.Element => {
selectedTerminalItemId={props.selectedTerminalItemId}
moduleType={moduleOnDeck.type}
handleDragHover={handleHoverEmptySlot}
slotId={moduleOnDeck.id}
/>
) : null}
{robotType === FLEX_ROBOT_TYPE ? (
Expand Down Expand Up @@ -336,16 +345,22 @@ export const DeckSetupContents = (props: ContentsProps): JSX.Element => {
) : null
})}

{/* SlotControls for all empty deck + module slots */}
{/* SlotControls for all empty deck */}
{deckDef.locations.addressableAreas
// only render standard slot fixture components
.filter(
addressableArea =>
isAddressableAreaStandardSlot(addressableArea.id, deckDef) &&
.filter(addressableArea => {
const stagingAreaAddressableAreas = getStagingAreaAddressableAreas(
stagingAreaCutoutIds
)
const addressableAreas =
isAddressableAreaStandardSlot(addressableArea.id, deckDef) ||
stagingAreaAddressableAreas.includes(addressableArea.id)
return (
addressableAreas &&
!slotIdsBlockedBySpanning.includes(addressableArea.id) &&
getSlotIsEmpty(activeDeckSetup, addressableArea.id) &&
addressableArea.id !== trashSlot
)
)
})
.map(addressableArea => {
return (
// @ts-expect-error
Expand Down Expand Up @@ -534,7 +549,6 @@ export const DeckSetup = (): JSX.Element => {
const filteredAddressableAreas = deckDef.locations.addressableAreas.filter(
aa => isAddressableAreaStandardSlot(aa.id, deckDef)
)

return (
<div className={styles.deck_row}>
{drilledDown && <BrowseLabwareModal />}
Expand All @@ -547,7 +561,10 @@ export const DeckSetup = (): JSX.Element => {
{({ getRobotCoordsFromDOMCoords }) => (
<>
{robotType === OT2_ROBOT_TYPE ? (
<DeckFromLayers robotType={robotType} layerBlocklist={[]} />
<DeckFromLayers
robotType={robotType}
layerBlocklist={OT2_STANDARD_DECK_VIEW_LAYER_BLOCK_LIST}
/>
) : (
<>
{filteredAddressableAreas.map(addressableArea => {
Expand Down Expand Up @@ -613,6 +630,11 @@ export const DeckSetup = (): JSX.Element => {
robotType={robotType}
activeDeckSetup={activeDeckSetup}
selectedTerminalItemId={selectedTerminalItemId}
stagingAreaCutoutIds={stagingAreaFixtures.map(
// TODO(jr, 11/13/23): fix this type since AdditionalEquipment['location'] is type string
// instead of CutoutId
areas => areas.location as CutoutId
)}
{...{
deckDef,
getRobotCoordsFromDOMCoords,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,11 @@ describe('FileSidebar', () => {
// @ts-expect-error(sa, 2021-6-22): props.fileData might be null
props.fileData.commands = commands
props.additionalEquipment = {
[stagingArea]: { name: 'stagingArea', id: stagingArea, location: 'A3' },
[stagingArea]: {
name: 'stagingArea',
id: stagingArea,
location: 'cutoutA3',
},
}

const wrapper = shallow(<FileSidebar {...props} />)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ describe('getUnusedStagingAreas', () => {
it('returns true for unused staging area', () => {
const stagingArea = 'stagingAreaId'
const mockAdditionalEquipment = {
[stagingArea]: { name: 'stagingArea', id: stagingArea, location: 'A3' },
[stagingArea]: {
name: 'stagingArea',
id: stagingArea,
location: 'cutoutA3',
},
} as AdditionalEquipment

expect(getUnusedStagingAreas(mockAdditionalEquipment, [])).toEqual(['A4'])
Expand All @@ -15,8 +19,16 @@ describe('getUnusedStagingAreas', () => {
const stagingArea = 'stagingAreaId'
const stagingArea2 = 'stagingAreaId2'
const mockAdditionalEquipment = {
[stagingArea]: { name: 'stagingArea', id: stagingArea, location: 'A3' },
[stagingArea2]: { name: 'stagingArea', id: stagingArea2, location: 'B3' },
[stagingArea]: {
name: 'stagingArea',
id: stagingArea,
location: 'cutoutA3',
},
[stagingArea2]: {
name: 'stagingArea',
id: stagingArea2,
location: 'cutoutB3',
},
} as AdditionalEquipment

expect(getUnusedStagingAreas(mockAdditionalEquipment, [])).toEqual([
Expand All @@ -27,7 +39,11 @@ describe('getUnusedStagingAreas', () => {
it('returns false for unused staging area', () => {
const stagingArea = 'stagingAreaId'
const mockAdditionalEquipment = {
[stagingArea]: { name: 'stagingArea', id: stagingArea, location: 'A3' },
[stagingArea]: {
name: 'stagingArea',
id: stagingArea,
location: 'cutoutA3',
},
} as AdditionalEquipment
const mockCommand = ([
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { CreateCommand } from '@opentrons/shared-data'
import { getStagingAreaAddressableAreas } from '../../../utils'
import type { CreateCommand, CutoutId } from '@opentrons/shared-data'
import type { AdditionalEquipment } from '../FileSidebar'

export const getUnusedStagingAreas = (
additionalEquipment: AdditionalEquipment,
commands?: CreateCommand[]
): string[] => {
const stagingAreaSlots = Object.values(additionalEquipment)
const stagingAreaCutoutIds = Object.values(additionalEquipment)
.filter(equipment => equipment?.name === 'stagingArea')
.map(equipment => {
if (equipment.location == null) {
Expand All @@ -16,19 +17,12 @@ export const getUnusedStagingAreas = (
return equipment.location ?? ''
})

const corresponding4thColumnSlots = stagingAreaSlots.map(slot => {
const letter = slot.charAt(0)
const correspondingLocation = stagingAreaSlots.find(slot =>
slot.startsWith(letter)
)
if (correspondingLocation) {
return letter + '4'
}

return slot
})
const stagingAreaAddressableAreaNames = getStagingAreaAddressableAreas(
// TODO(jr, 11/13/23): fix AdditionalEquipment['location'] from type string to CutoutId
stagingAreaCutoutIds as CutoutId[]
)

const stagingAreaCommandSlots: string[] = corresponding4thColumnSlots.filter(
const stagingAreaCommandSlots: string[] = stagingAreaAddressableAreaNames.filter(
location =>
commands?.filter(
command =>
Expand Down
Loading

0 comments on commit c43d2d7

Please sign in to comment.