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, step-generation): single channel partial tip support for 'default' primaryNozzle only #17222

Open
wants to merge 5 commits into
base: edge
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,9 @@
"OT_PD_ENABLE_LIQUID_CLASSES": {
"title": "Enable liquid classes",
"description": "Enable liquid classes support"
},
"OT_PD_ENABLE_PARTIAL_TIP_SUPPORT": {
"title": "Enable partial tip support",
"description": "Partial tip configurations that are not released yet"
}
}
2 changes: 1 addition & 1 deletion protocol-designer/src/assets/localization/en/form.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
"COLUMN": "Column"
},
"option_tooltip": {
"COLUMN": "To use column partial tip pickup, a tiprack without an adapter must be placed on the deck."
"partial": "To use partial tip pickup, a tiprack without an adapter must be placed on the deck."
}
},
"path": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
"select_volume": "Select a volume",
"shake": "Shake",
"single": "Single path",
"single_nozzle": "Single",
"speed": "Speed",
"starting_deck": "Starting deck",
"step_substeps": "{{stepType}} details",
Expand Down
4 changes: 3 additions & 1 deletion protocol-designer/src/feature-flags/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ const initialFlags: Flags = {
process.env.OT_PD_ENABLE_HOT_KEYS_DISPLAY === '1' || true,
OT_PD_ENABLE_REACT_SCAN: process.env.OT_PD_ENABLE_REACT_SCAN === '1' || false,
OT_PD_ENABLE_LIQUID_CLASSES:
process.env.OT_PD_ENABLE_LIQUID_CLASSES === '1' || false,
process.env.OT_PD_ENABLE_REACT_SCAN === '1' || false,
Copy link
Contributor

Choose a reason for hiding this comment

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

i guess needs to merge edge into this branch since you fixed this already, don't you?

Suggested change
process.env.OT_PD_ENABLE_REACT_SCAN === '1' || false,
process.env.OT_PD_ENABLE_LIQUID_CLASSES === '1' || false,

OT_PD_ENABLE_PARTIAL_TIP_SUPPORT:
process.env.OT_PD_ENABLE_PARTIAL_TIP_SUPPORT === '1' || false,
}
// @ts-expect-error(sa, 2021-6-10): cannot use string literals as action type
// TODO IMMEDIATELY: refactor this to the old fashioned way if we cannot have type safety: https://github.com/redux-utilities/redux-actions/issues/282#issuecomment-595163081
Expand Down
4 changes: 4 additions & 0 deletions protocol-designer/src/feature-flags/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ export const getEnableLiquidClasses: Selector<boolean> = createSelector(
getFeatureFlagData,
flags => flags.OT_PD_ENABLE_LIQUID_CLASSES ?? false
)
export const getEnablePartialTipSupport: Selector<boolean> = createSelector(
getFeatureFlagData,
flags => flags.OT_PD_ENABLE_PARTIAL_TIP_SUPPORT ?? false
)
2 changes: 2 additions & 0 deletions protocol-designer/src/feature-flags/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export type FlagTypes =
| 'OT_PD_ENABLE_HOT_KEYS_DISPLAY'
| 'OT_PD_ENABLE_REACT_SCAN'
| 'OT_PD_ENABLE_LIQUID_CLASSES'
| 'OT_PD_ENABLE_PARTIAL_TIP_SUPPORT'
// flags that are not in this list only show in prerelease mode
export const userFacingFlags: FlagTypes[] = [
'OT_PD_DISABLE_MODULE_RESTRICTIONS',
Expand All @@ -51,5 +52,6 @@ export const allFlags: FlagTypes[] = [
'OT_PD_ENABLE_RETURN_TIP',
'OT_PD_ENABLE_REACT_SCAN',
'OT_PD_ENABLE_LIQUID_CLASSES',
'OT_PD_ENABLE_PARTIAL_TIP_SUPPORT',
]
export type Flags = Partial<Record<FlagTypes, boolean | null | undefined>>
20 changes: 10 additions & 10 deletions protocol-designer/src/organisms/Labware/SelectableLabware.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import reduce from 'lodash/reduce'

import { COLUMN } from '@opentrons/shared-data'
import { COLUMN, SINGLE } from '@opentrons/shared-data'
import { COLORS } from '@opentrons/components'

import {
Expand Down Expand Up @@ -39,7 +39,7 @@ export interface SelectableLabwareProps {

type ChannelType = 8 | 96

const getChannelsFromNozleType = (nozzleType: NozzleType): ChannelType => {
const getChannelsFromNozzleType = (nozzleType: NozzleType): ChannelType => {
if (nozzleType === '8-channel' || nozzleType === COLUMN) {
return 8
} else {
Expand Down Expand Up @@ -67,8 +67,8 @@ export const SelectableLabware = (
selectedWells: WellGroup
) => WellGroup = selectedWells => {
// Returns PRIMARY WELLS from the selection.
if (nozzleType != null) {
const channels = getChannelsFromNozleType(nozzleType)
if (nozzleType != null && nozzleType !== SINGLE) {
const channels = getChannelsFromNozzleType(nozzleType)
// for the wells that have been highlighted,
// get all 8-well well sets and merge them
const primaryWells: WellGroup = reduce(
Expand Down Expand Up @@ -101,8 +101,8 @@ export const SelectableLabware = (
rect
) => {
if (!e.shiftKey) {
if (nozzleType != null) {
const channels = getChannelsFromNozleType(nozzleType)
if (nozzleType != null && nozzleType !== SINGLE) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (nozzleType != null && nozzleType !== SINGLE) {
if (nozzleType !== null && nozzleType !== SINGLE) {

const channels = getChannelsFromNozzleType(nozzleType)
const selectedWells = _getWellsFromRect(rect)
const allWellsForMulti: WellGroup = reduce(
selectedWells,
Expand Down Expand Up @@ -142,8 +142,8 @@ export const SelectableLabware = (
}

const handleMouseEnterWell: (args: WellMouseEvent) => void = args => {
if (nozzleType != null) {
const channels = getChannelsFromNozleType(nozzleType)
if (nozzleType != null && nozzleType !== SINGLE) {
Copy link
Contributor

Choose a reason for hiding this comment

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

same as the above

const channels = getChannelsFromNozzleType(nozzleType)
const wellSet = getWellSetForMultichannel({
labwareDef,
wellName: args.wellName,
Expand All @@ -158,11 +158,11 @@ export const SelectableLabware = (

// For rendering, show all wells not just primary wells
const allSelectedWells =
nozzleType != null
nozzleType != null && nozzleType !== SINGLE
Copy link
Contributor

Choose a reason for hiding this comment

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

same as the above

? reduce<WellGroup, WellGroup>(
selectedPrimaryWells,
(acc, _, wellName): WellGroup => {
const channels = getChannelsFromNozleType(nozzleType)
const channels = getChannelsFromNozzleType(nozzleType)
const wellSet = getWellSetForMultichannel({
labwareDef,
wellName,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,74 @@
import { useEffect, useState } from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ALL, COLUMN } from '@opentrons/shared-data'
import { ALL, COLUMN, SINGLE } from '@opentrons/shared-data'
import { Flex, DropdownMenu, SPACING } from '@opentrons/components'
import { getEnablePartialTipSupport } from '../../../../../feature-flags/selectors'
import { getInitialDeckSetup } from '../../../../../step-forms/selectors'
import type { PipetteV2Specs } from '@opentrons/shared-data'
import type { DropdownOption } from '@opentrons/components'
import type { FieldProps } from '../types'

export function PartialTipField(props: FieldProps): JSX.Element {
interface PartialTipFieldProps extends FieldProps {
pipetteSpecs: PipetteV2Specs
}
export function PartialTipField(props: PartialTipFieldProps): JSX.Element {
const {
value: dropdownItem,
updateValue,
errorToShow,
padding = `0 ${SPACING.spacing16}`,
tooltipContent,
pipetteSpecs,
} = props
const { t } = useTranslation('protocol_steps')
const deckSetup = useSelector(getInitialDeckSetup)
const enablePartialTip = useSelector(getEnablePartialTipSupport)
const is96Channel = pipetteSpecs.channels === 96

const tipracks = Object.values(deckSetup.labware).filter(
labware => labware.def.parameters.isTiprack
)
const tipracksNotOnAdapter = tipracks.filter(
tiprack => deckSetup.labware[tiprack.slot] == null
)
const noTipracksOnAdapter = tipracksNotOnAdapter.length === 0

const options = [
const options: DropdownOption[] = [
{
name: t('all'),
value: ALL,
},
{
]
if (is96Channel) {
options.push({
name: t('column'),
value: COLUMN,
disabled: tipracksNotOnAdapter.length === 0,
tooltipText:
tipracksNotOnAdapter.length === 0
? t('form:step_edit_form.field.nozzles.option_tooltip.COLUMN')
disabled: noTipracksOnAdapter,
tooltipText: noTipracksOnAdapter
? t('form:step_edit_form.field.nozzles.option_tooltip.partial')
: undefined,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

can we use '' or null?

if (enablePartialTip) {
options.push({
name: t('single_nozzle'),
value: SINGLE,
disabled: noTipracksOnAdapter,
tooltipText: noTipracksOnAdapter
? t('form:step_edit_form.field.nozzles.option_tooltip.partial')
: undefined,
},
]
})
}
} else {
options.push({
name: t('single_nozzle'),
value: SINGLE,
})
}

const [selectedValue, setSelectedValue] = useState(
dropdownItem || options[0].value
)
useEffect(() => {
updateValue(selectedValue)
}, [selectedValue])

return (
<Flex padding={padding}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import {
getLabwareEntities,
getPipetteEntities,
} from '../../../../../../step-forms/selectors'
import { getEnableReturnTip } from '../../../../../../feature-flags/selectors'
import {
getEnablePartialTipSupport,
getEnableReturnTip,
} from '../../../../../../feature-flags/selectors'
import {
BlowoutLocationField,
BlowoutOffsetField,
Expand Down Expand Up @@ -53,6 +56,7 @@ export function MixTools(props: StepFormProps): JSX.Element {
} = props
const pipettes = useSelector(getPipetteEntities)
const enableReturnTip = useSelector(getEnableReturnTip)
const enablePartialTip = useSelector(getEnablePartialTipSupport)
const labwares = useSelector(getLabwareEntities)
const { t, i18n } = useTranslation(['application', 'form'])
const aspirateTab = {
Expand All @@ -74,6 +78,9 @@ export function MixTools(props: StepFormProps): JSX.Element {
const is96Channel =
propsForFields.pipette.value != null &&
pipettes[String(propsForFields.pipette.value)].name === 'p1000_96'
Copy link
Contributor

Choose a reason for hiding this comment

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

can we use .spec.channels since is8Channel uses that.

Suggested change
pipettes[String(propsForFields.pipette.value)].name === 'p1000_96'
pipettes[String(propsForFields.pipette.value)].spec.channels === 96

const is8Channel =
propsForFields.pipette.value != null &&
pipettes[String(propsForFields.pipette.value)].spec.channels === 8
const userSelectedPickUpTipLocation =
labwares[String(propsForFields.pickUpTip_location.value)] != null
const userSelectedDropTipLocation =
Expand All @@ -88,7 +95,13 @@ export function MixTools(props: StepFormProps): JSX.Element {
paddingY={SPACING.spacing16}
>
<PipetteField {...propsForFields.pipette} />
{is96Channel ? <PartialTipField {...propsForFields.nozzles} /> : null}
{propsForFields.pipette.value != null &&
(is96Channel || (is8Channel && enablePartialTip)) ? (
<PartialTipField
{...propsForFields.nozzles}
pipetteSpecs={pipettes[String(propsForFields.pipette.value)]?.spec}
/>
) : null}
<Divider marginY="0" />
<TiprackField
{...propsForFields.tipRack}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import {
Tabs,
} from '@opentrons/components'
import { getTrashOrLabware } from '@opentrons/step-generation'
import { getEnableReturnTip } from '../../../../../../feature-flags/selectors'
import {
getEnablePartialTipSupport,
getEnableReturnTip,
} from '../../../../../../feature-flags/selectors'
import {
getAdditionalEquipmentEntities,
getLabwareEntities,
Expand Down Expand Up @@ -66,11 +69,11 @@ export function MoveLiquidTools(props: StepFormProps): JSX.Element {
const additionalEquipmentEntities = useSelector(
getAdditionalEquipmentEntities
)
const enablePartialTip = useSelector(getEnablePartialTipSupport)
const enableReturnTip = useSelector(getEnableReturnTip)
const labwares = useSelector(getLabwareEntities)
const pipettes = useSelector(getPipetteEntities)
const addFieldNamePrefix = makeAddFieldNamePrefix(tab)

const isWasteChuteSelected =
propsForFields.dispense_labware?.value != null
? additionalEquipmentEntities[
Expand All @@ -91,6 +94,9 @@ export function MoveLiquidTools(props: StepFormProps): JSX.Element {
const is96Channel =
propsForFields.pipette.value != null &&
pipettes[String(propsForFields.pipette.value)].name === 'p1000_96'
Copy link
Contributor

Choose a reason for hiding this comment

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

same as the above

const is8Channel =
propsForFields.pipette.value != null &&
pipettes[String(propsForFields.pipette.value)].spec.channels === 8
const isDisposalLocation =
additionalEquipmentEntities[String(propsForFields.dispense_labware.value)]
?.name === 'wasteChute' ||
Expand Down Expand Up @@ -143,10 +149,14 @@ export function MoveLiquidTools(props: StepFormProps): JSX.Element {
paddingY={SPACING.spacing16}
>
<PipetteField {...propsForFields.pipette} />
{is96Channel ? (
{propsForFields.pipette.value != null &&
(is96Channel || (is8Channel && enablePartialTip)) ? (
<>
<Divider marginY="0" />
<PartialTipField {...propsForFields.nozzles} />
<PartialTipField
{...propsForFields.nozzles}
pipetteSpecs={pipettes[String(propsForFields.pipette.value)]?.spec}
/>
</>
) : null}
<Divider marginY="0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
SOURCE_WELL_BLOWOUT_DESTINATION,
DEST_WELL_BLOWOUT_DESTINATION,
} from '@opentrons/step-generation'
import { ALL, COLUMN } from '@opentrons/shared-data'
import { SINGLE } from '@opentrons/shared-data'
import { getFieldErrors } from '../../../../steplist/fieldLevel'
import {
getDisabledFields,
Expand Down Expand Up @@ -227,12 +227,10 @@ export const getNozzleType = (
nozzles: string | null
): NozzleType | null => {
const is8Channel = pipette != null && pipette.spec.channels === 8
if (is8Channel) {
if (is8Channel && nozzles !== SINGLE) {
return '8-channel'
} else if (nozzles === COLUMN) {
return COLUMN
} else if (nozzles === ALL) {
return ALL
} else if (nozzles != null) {
return nozzles as NozzleType
} else {
return null
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import last from 'lodash/last'
import {
ALL,
HEATERSHAKER_MODULE_TYPE,
MAGNETIC_MODULE_TYPE,
TEMPERATURE_MODULE_TYPE,
Expand Down Expand Up @@ -85,6 +86,34 @@ const _patchDefaultPipette = (args: {
return null
}

const _patchDefaultNozzle = (args: {
labwareEntities: LabwareEntities
pipetteEntities: PipetteEntities
}): FormUpdater => formData => {
const { labwareEntities, pipetteEntities } = args
const hasPartialTipSupportedChannel = Object.values(pipetteEntities).find(
pip => pip.spec.channels === 96
// || pip.spec.channels === 8
// TODO: add this in once we remove enablePartialTip feature flag
)

const formHasNozzlesField = formData && 'nozzles' in formData

if (formHasNozzlesField && hasPartialTipSupportedChannel) {
const updatedFields = handleFormChange(
{
nozzles: ALL,
},
formData,
pipetteEntities,
labwareEntities
)
return updatedFields
}

return null
}

const _patchDefaultDropTipLocation = (args: {
additionalEquipmentEntities: AdditionalEquipmentEntities
labwareEntities: LabwareEntities
Expand Down Expand Up @@ -335,6 +364,11 @@ export const createPresavedStepForm = ({
stepType,
})

const updateDefaultNozzles = _patchDefaultNozzle({
labwareEntities,
pipetteEntities,
})

const updateDefaultDropTip = _patchDefaultDropTipLocation({
labwareEntities,
pipetteEntities,
Expand Down Expand Up @@ -392,6 +426,7 @@ export const createPresavedStepForm = ({
updateHeaterShakerModuleId,
updateMagneticModuleId,
updateDefaultLabwareLocations,
updateDefaultNozzles,
].reduce<FormData>(
(acc, updater: FormUpdater) => {
const updates = updater(acc)
Expand Down
Loading
Loading