Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol-designer): magnetic block can be added to staging area slot #17242

Merged
merged 5 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 5 additions & 1 deletion protocol-designer/src/modules/moduleData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,11 @@ export function getNextAvailableModuleSlot(
const addressableAreaName = stagingAreaAddressableAreaNames.find(
aa => aa === slot
)
let isSlotEmpty: boolean = getSlotIsEmpty(initialDeckSetup, slot, true)
let isSlotEmpty: boolean = getSlotIsEmpty(
initialDeckSetup,
slot,
!isMagneticBlock
)
if (addressableAreaName == null && COLUMN_4_SLOTS.includes(slot)) {
isSlotEmpty = false
} else if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,78 @@ describe('getNumSlotsAvailable', () => {
)
expect(result).toBe(1)
})
it('should return 8 when there are 4 staging area for magnetic block', () => {
it('should return 4 when there are 12 magnetic blocks for staging area', () => {
const mockModules = {
0: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
1: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
2: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
3: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
4: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
5: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
6: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'D2',
},
7: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
8: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
9: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
10: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
11: {
model: MAGNETIC_BLOCK_V1,
type: MAGNETIC_BLOCK_TYPE,
slot: 'C2',
},
} as any
const mockAdditionalEquipment: AdditionalEquipment[] = []
const result = getNumSlotsAvailable(
mockModules,
mockAdditionalEquipment,
'stagingArea'
)
expect(result).toBe(4)
})
it('should return 12 when there are 4 staging area for magnetic block', () => {
const mockAdditionalEquipment: AdditionalEquipment[] = [
'stagingArea',
'stagingArea',
Expand All @@ -247,9 +318,9 @@ describe('getNumSlotsAvailable', () => {
mockAdditionalEquipment,
MAGNETIC_BLOCK_V1
)
expect(result).toBe(8)
expect(result).toBe(12)
})
it('should return 4 when there are 4 modules, 4 staging area for magnetic block', () => {
it('should return 8 when there are 4 modules, 4 staging area for magnetic block since magnetic blocks can now go on staging areas', () => {
const mockModules = {
0: {
model: HEATERSHAKER_MODULE_V1,
Expand Down Expand Up @@ -278,7 +349,7 @@ describe('getNumSlotsAvailable', () => {
mockAdditionalEquipment,
MAGNETIC_BLOCK_V1
)
expect(result).toBe(4)
expect(result).toBe(8)
})
})

Expand Down
52 changes: 38 additions & 14 deletions protocol-designer/src/pages/CreateNewProtocolWizard/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ import type { AdditionalEquipment, WizardFormState } from './types'
const TOTAL_OUTER_SLOTS = 8
const MIDDLE_SLOT_NUM = 4
const MAX_MAGNETIC_BLOCK_SLOTS = 12

const TOTAL_OUTER_RIGHT_SLOTS = 4
const TOTAL_OUTER_LEFT_SLOTS = 4
export const getNumOptions = (length: number): DropdownOption[] => {
return Array.from({ length }, (_, i) => ({
name: `${i + 1}`,
Expand All @@ -49,31 +50,37 @@ export const getNumSlotsAvailable = (
const additionalEquipmentLength = additionalEquipment.filter(
ae => ae !== 'gripper'
).length

const hasTC = Object.values(modules || {}).some(
module => module.type === THERMOCYCLER_MODULE_TYPE
)
const numStagingAreas = additionalEquipment.filter(ae => ae === 'stagingArea')
?.length

const numStagingAreas =
additionalEquipment.filter(ae => ae === 'stagingArea')?.length || 0

const hasWasteChute = additionalEquipment.some(ae => ae === 'wasteChute')

const magneticBlocks = Object.values(modules || {}).filter(
module => module.type === MAGNETIC_BLOCK_TYPE
)
const magneticBlockCount = magneticBlocks.length

let filteredModuleLength = modules != null ? Object.keys(modules).length : 0
if (magneticBlocks.length > 0) {
// once blocks exceed 4, then we dont' want to subtract the amount available
// because block can go into the center slots where all other modules/trashes can not
const numBlocks =
magneticBlocks.length >= 4 ? MIDDLE_SLOT_NUM : magneticBlocks.length
filteredModuleLength =
filteredModuleLength - (type !== 'magneticBlockV1' ? numBlocks : 0)
if (magneticBlockCount <= MIDDLE_SLOT_NUM) {
// Subtract magnetic blocks directly if their count is ≤ 4
filteredModuleLength -= magneticBlockCount
} else {
// Subtract the excess magnetic blocks beyond 4
const extraMagneticBlocks = magneticBlockCount - MIDDLE_SLOT_NUM
filteredModuleLength -= extraMagneticBlocks
}
if (hasTC) {
filteredModuleLength = filteredModuleLength + 1
filteredModuleLength += 1
}

let filteredAdditionalEquipmentLength = additionalEquipmentLength
if (numStagingAreas >= 1 && hasWasteChute && type !== 'stagingArea') {
filteredAdditionalEquipmentLength = filteredAdditionalEquipmentLength - 1
filteredAdditionalEquipmentLength -= 1
}
switch (type) {
case 'gripper': {
Expand All @@ -99,14 +106,27 @@ export const getNumSlotsAvailable = (
}
}
case 'trashBin':
case 'stagingArea':
case HEATERSHAKER_MODULE_V1:
case TEMPERATURE_MODULE_V2: {
return (
TOTAL_OUTER_SLOTS -
(filteredModuleLength + filteredAdditionalEquipmentLength)
)
}
// TODO: need to do additional overhaul for this logic
// it mostly works but still some edge cases - seems like it's not properly subtracting mag blocks
// that go into the 3rd column for the staging area to be added as well
case 'stagingArea': {
const adjustedModuleLength =
filteredModuleLength > 4
? filteredModuleLength - TOTAL_OUTER_LEFT_SLOTS
: 0

const occupiedSlots =
adjustedModuleLength + filteredAdditionalEquipmentLength

return TOTAL_OUTER_RIGHT_SLOTS - occupiedSlots
}
jerader marked this conversation as resolved.
Show resolved Hide resolved
case 'wasteChute': {
const adjustmentForStagingArea = numStagingAreas >= 1 ? 1 : 0
return (
Expand All @@ -117,9 +137,13 @@ export const getNumSlotsAvailable = (
)
}
case MAGNETIC_BLOCK_V1: {
const filteredAdditionalEquipmentForMagneticBlockLength = additionalEquipment.filter(
ae => ae !== 'gripper' && ae !== 'stagingArea'
)?.length
return (
MAX_MAGNETIC_BLOCK_SLOTS -
(filteredModuleLength + filteredAdditionalEquipmentLength)
(filteredModuleLength +
filteredAdditionalEquipmentForMagneticBlockLength)
)
}
}
Expand Down
66 changes: 52 additions & 14 deletions protocol-designer/src/pages/Designer/DeckSetup/DeckSetupTools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
FLEX_STAGING_AREA_SLOT_ADDRESSABLE_AREAS,
getModuleDisplayName,
getModuleType,
MAGNETIC_BLOCK_V1,
MAGNETIC_MODULE_TYPE,
MAGNETIC_MODULE_V1,
MAGNETIC_MODULE_V2,
Expand Down Expand Up @@ -67,7 +68,11 @@
import { ALL_ORDERED_CATEGORIES, FIXTURES, MOAM_MODELS } from './constants'
import { LabwareTools } from './LabwareTools'
import { MagnetModuleChangeContent } from './MagnetModuleChangeContent'
import { getModuleModelsBySlot, getDeckErrors } from './utils'
import {

Check failure on line 71 in protocol-designer/src/pages/Designer/DeckSetup/DeckSetupTools.tsx

View workflow job for this annotation

GitHub Actions / js checks

Import "ModuleModelsExtended" is only used as types

Check failure on line 71 in protocol-designer/src/pages/Designer/DeckSetup/DeckSetupTools.tsx

View workflow job for this annotation

GitHub Actions / js checks

Import "ModuleModelsExtended" is only used as types
getModuleModelsBySlot,
getDeckErrors,
ModuleModelsExtended,
} from './utils'

import type { AddressableAreaName, ModuleModel } from '@opentrons/shared-data'
import type { ThunkDispatch } from '../../../types'
Expand Down Expand Up @@ -131,14 +136,21 @@
equipment => equipment?.name === 'gripper'
)
const [selectedHardware, setSelectedHardware] = useState<
ModuleModel | Fixture | null
ModuleModelsExtended | Fixture | null
>(null)

// initialize the previously selected hardware because for some reason it does not
// work initiating it in the above useState
useEffect(() => {
if (selectedModuleModel !== null || selectedFixture != null) {
setSelectedHardware(selectedModuleModel ?? selectedFixture ?? null)
if (
selectedModuleModel === MAGNETIC_BLOCK_V1 &&
selectedFixture === 'stagingArea'
) {
setSelectedHardware('stagingAreaAndMagneticBlock')
} else {
setSelectedHardware(selectedModuleModel ?? selectedFixture ?? null)
}
}
}, [selectedModuleModel, selectedFixture])

Expand Down Expand Up @@ -513,6 +525,10 @@
gridGap={SPACING.spacing4}
>
{moduleModels?.map(model => {
const selectedModel =
model === 'stagingAreaAndMagneticBlock'
? MAGNETIC_BLOCK_V1
: model
const modelSomewhereOnDeck = Object.values(
deckSetupModules
).filter(
Expand All @@ -522,15 +538,15 @@
deckSetupModules
).filter(
module =>
module.type === getModuleType(model) &&
module.type === getModuleType(selectedModel) &&
module.slot !== slot
)
const moamModels = MOAM_MODELS

const collisionError = getDeckErrors({
modules: deckSetupModules,
selectedSlot: slot,
selectedModel: model,
selectedModel,
labware: deckSetupLabware,
robotType,
})
Expand All @@ -541,10 +557,19 @@
if (onDeckProps?.setHoveredModule != null) {
onDeckProps.setHoveredModule(null)
}
if (onDeckProps?.setHoveredFixture != null) {
onDeckProps.setHoveredFixture(null)
}
}}
setHovered={() => {
if (onDeckProps?.setHoveredModule != null) {
onDeckProps.setHoveredModule(model)
onDeckProps.setHoveredModule(selectedModel)
}
if (
onDeckProps?.setHoveredFixture != null &&
model === 'stagingAreaAndMagneticBlock'
) {
onDeckProps.setHoveredFixture('stagingArea')
}
}}
largeDesktopBorderRadius
Expand All @@ -555,10 +580,12 @@
>
<ModuleIcon
size="1rem"
moduleType={getModuleType(model)}
moduleType={getModuleType(selectedModel)}
/>
<StyledText desktopStyle="bodyDefaultRegular">
{getModuleDisplayName(model)}
{model === 'stagingAreaAndMagneticBlock'
? 'Magnetic Block and Staging Area'
: getModuleDisplayName(selectedModel)}
</StyledText>
</Flex>
}
Expand All @@ -567,12 +594,12 @@
onChange={() => {
if (
modelSomewhereOnDeck.length === 1 &&
!moamModels.includes(model) &&
!moamModels.includes(selectedModel) &&
robotType === FLEX_ROBOT_TYPE
) {
makeSnackbar(
t('one_item', {
hardware: getModuleDisplayName(model),
hardware: getModuleDisplayName(selectedModel),
}) as string
)
} else if (
Expand All @@ -582,7 +609,9 @@
makeSnackbar(
t('one_item', {
hardware: t(
`shared:${getModuleType(model).toLowerCase()}`
`shared:${getModuleType(
selectedModel
).toLowerCase()}`
),
}) as string
)
Expand All @@ -600,11 +629,20 @@
(selectedFixture === 'wasteChuteAndStagingArea' &&
matchingLabwareFor4thColumn != null)
) {
setShowDeleteLabwareModal(model)
setShowDeleteLabwareModal(selectedModel)
} else {
setSelectedHardware(model)
dispatch(selectFixture({ fixture: null }))
dispatch(selectModule({ moduleModel: model }))
dispatch(
selectFixture({
fixture:
model === 'stagingAreaAndMagneticBlock'
? 'stagingArea'
: null,
})
)
dispatch(
selectModule({ moduleModel: selectedModel })
)
dispatch(selectLabware({ labwareDefUri: null }))
dispatch(
selectNestedLabware({ nestedLabwareDefUri: null })
Expand Down
Loading
Loading