Skip to content

Commit

Permalink
fix(protocol-designer): getPipetteCapacity correctly updates tip capa… (
Browse files Browse the repository at this point in the history
#15595)

closes AUTH-553

This is a bug from PR 15096, in that PR, i changed it so for multi tiprack support, the tiprack key that is required for the transfer and mix forms changed from the tiprack id to the tiprack def uri. But i forgot to migrate it to this util!! This resulted in the user being unable to get accurate tip volume capacity.
  • Loading branch information
jerader authored Jul 8, 2024
1 parent f3bef1e commit 771d4f6
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export const DisposalVolumeField = (

const disposalOptions = useSelector(uiLabwareSelectors.getDisposalOptions)
const pipetteEntities = useSelector(stepFormSelectors.getPipetteEntities)
const labwareEntities = useSelector(stepFormSelectors.getLabwareEntities)
const blowoutLocationOptions = getBlowoutLocationOptionsForForm({
path,
stepType,
Expand All @@ -83,8 +82,7 @@ export const DisposalVolumeField = (
volume,
tipRack,
},
pipetteEntities,
labwareEntities
pipetteEntities
)
const disposalDestinationOptions = [
...disposalOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ export const PathField = (props: PathFieldProps): JSX.Element => {
} = props
const { t } = useTranslation('form')
const pipetteEntities = useSelector(stepFormSelectors.getPipetteEntities)
const labwareEntities = useSelector(stepFormSelectors.getLabwareEntities)
const disabledPathMap = getDisabledPathMap(
{
aspirate_airGap_checkbox,
Expand All @@ -137,7 +136,6 @@ export const PathField = (props: PathFieldProps): JSX.Element => {
tipRack,
},
pipetteEntities,
labwareEntities,
t
)
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
} from '../../../../steplist/formLevel/handleFormChange/utils'
import type {
ChangeTipOptions,
LabwareEntities,
PipetteEntities,
} from '@opentrons/step-generation'
import type { PathOption } from '../../../../form-types'
Expand All @@ -24,7 +23,6 @@ export interface ValuesForPath {
export function getDisabledPathMap(
values: ValuesForPath,
pipetteEntities: PipetteEntities,
labwareEntities: LabwareEntities,
t: any
): DisabledPathMap {
const {
Expand Down Expand Up @@ -59,7 +57,7 @@ export function getDisabledPathMap(
// transfer volume overwrites change tip disable reasoning
const pipetteEntity = pipetteEntities[pipette]
const pipetteCapacity =
pipetteEntity && getPipetteCapacity(pipetteEntity, labwareEntities, tipRack)
pipetteEntity && getPipetteCapacity(pipetteEntity, tipRack)
const volume = Number(values.volume)
const airGapChecked = aspirate_airGap_checkbox
let airGapVolume = airGapChecked ? Number(values.aspirate_airGap_volume) : 0
Expand Down
29 changes: 10 additions & 19 deletions protocol-designer/src/pipettes/pipetteData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from '@opentrons/shared-data'
import type { PipetteName } from '@opentrons/shared-data'
import type { Options } from '@opentrons/components'
import type { LabwareEntities, PipetteEntity } from '@opentrons/step-generation'
import type { PipetteEntity } from '@opentrons/step-generation'
import type { DropdownOption } from '../../../components/lib/forms/DropdownField.d'
const supportedPipetteNames: PipetteName[] = [
'p10_single',
Expand Down Expand Up @@ -35,36 +35,27 @@ export const pipetteOptions: Options = supportedPipetteNames
// NOTE: this is similar to getPipetteWithTipMaxVol, the fns
export const getPipetteCapacity = (
pipetteEntity: PipetteEntity,
labwareEntities: LabwareEntities,
tipRack?: string | null
tipRackDefUri?: string | null
): number => {
const spec = pipetteEntity.spec
const tiprackDefs = pipetteEntity.tiprackLabwareDef
const tipRackDefUri =
tipRack != null && labwareEntities[tipRack] != null
? labwareEntities[tipRack]?.labwareDefURI
: ''
const maxVolume = pipetteEntity.spec.liquids.default.maxVolume
const tipRackDefs = pipetteEntity.tiprackLabwareDef
let chosenTipRack = null

for (const def of tiprackDefs) {
for (const def of tipRackDefs) {
if (getLabwareDefURI(def) === tipRackDefUri) {
chosenTipRack = def
break
}
}
if (spec && tiprackDefs) {
return Math.min(
spec.liquids.default.maxVolume,
// not sure if this is a good way to handle this. chosenTipRack is null until you select it
getTiprackVolume(chosenTipRack ?? tiprackDefs[0])
)
}
const tipRackTipVol = getTiprackVolume(chosenTipRack ?? tipRackDefs[0])

if (maxVolume != null && tipRackTipVol != null) {
return Math.min(maxVolume, tipRackTipVol)
}
console.assert(
false,
`Expected spec and tiprack def for pipette ${
pipetteEntity ? pipetteEntity.id : '???'
} and ${tipRack ?? '???'}`
} and ${tipRackDefUri ?? '???'}`
)
return NaN
}
Expand Down
11 changes: 2 additions & 9 deletions protocol-designer/src/steplist/formLevel/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,18 +238,11 @@ export const wellRatioMoveLiquid = (
? null
: wellRatioFormError
}
export const volumeTooHigh = (
fields: HydratedFormData,
labwareEntities?: LabwareEntities
): FormError | null => {
export const volumeTooHigh = (fields: HydratedFormData): FormError | null => {
const { pipette, tipRack } = fields
const volume = Number(fields.volume)

const pipetteCapacity = getPipetteCapacity(
pipette,
labwareEntities ?? {},
tipRack
)
const pipetteCapacity = getPipetteCapacity(pipette, tipRack)
if (
!Number.isNaN(volume) &&
!Number.isNaN(pipetteCapacity) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,7 @@ const wellRatioUpdater = makeConditionalPatchUpdater(wellRatioUpdatesMap)
export function updatePatchPathField(
patch: FormPatch,
rawForm: FormData,
pipetteEntities: PipetteEntities,
labwareEntities: LabwareEntities
pipetteEntities: PipetteEntities
): FormPatch {
const { id, stepType, ...stepData } = rawForm
const appliedPatch = { ...(stepData as FormPatch), ...patch }
Expand All @@ -154,8 +153,7 @@ export function updatePatchPathField(
pipetteCapacityExceeded = !volumeInCapacityForMulti(
// @ts-expect-error(sa, 2021-6-14): appliedPatch is not of type FormData, address in #3161
appliedPatch,
pipetteEntities,
labwareEntities
pipetteEntities
)
}

Expand Down Expand Up @@ -266,8 +264,7 @@ const getClearedDisposalVolumeFields = (): FormPatch =>
const clampAspirateAirGapVolume = (
patch: FormPatch,
rawForm: FormData,
pipetteEntities: PipetteEntities,
labwareEntities: LabwareEntities
pipetteEntities: PipetteEntities
): FormPatch => {
const patchedAspirateAirgapVolume =
patch.aspirate_airGap_volume ?? rawForm?.aspirate_airGap_volume
Expand All @@ -284,8 +281,7 @@ const clampAspirateAirGapVolume = (
const minAirGapVolume = 0 // NOTE: a form level warning will occur if the air gap volume is below the pipette min volume

const maxAirGapVolume =
getPipetteCapacity(pipetteEntity, labwareEntities, tipRack) -
minPipetteVolume
getPipetteCapacity(pipetteEntity, tipRack) - minPipetteVolume
const clampedAirGapVolume = clamp(
Number(patchedAspirateAirgapVolume),
minAirGapVolume,
Expand All @@ -302,8 +298,7 @@ const clampAspirateAirGapVolume = (
const clampDispenseAirGapVolume = (
patch: FormPatch,
rawForm: FormData,
pipetteEntities: PipetteEntities,
labwareEntities: LabwareEntities
pipetteEntities: PipetteEntities
): FormPatch => {
const { id, stepType, ...stepData } = rawForm
const appliedPatch = { ...(stepData as FormPatch), ...patch, id, stepType }
Expand All @@ -327,7 +322,7 @@ const clampDispenseAirGapVolume = (
pipetteId in pipetteEntities
) {
const pipetteEntity = pipetteEntities[pipetteId]
const capacity = getPipetteCapacity(pipetteEntity, labwareEntities, tipRack)
const capacity = getPipetteCapacity(pipetteEntity, tipRack)
const minAirGapVolume = 0 // NOTE: a form level warning will occur if the air gap volume is below the pipette min volume

const maxAirGapVolume =
Expand Down Expand Up @@ -407,8 +402,7 @@ const updatePatchDisposalVolumeFields = (
const clampDisposalVolume = (
patch: FormPatch,
rawForm: FormData,
pipetteEntities: PipetteEntities,
labwareEntities: LabwareEntities
pipetteEntities: PipetteEntities
): FormPatch => {
const { id, stepType, ...stepData } = rawForm
const appliedPatch = { ...(stepData as FormPatch), ...patch, id, stepType }
Expand All @@ -419,8 +413,7 @@ const clampDisposalVolume = (
const maxDisposalVolume = getMaxDisposalVolumeForMultidispense(
// @ts-expect-error(sa, 2021-6-14): appliedPatch isn't well-typed, address in #3161
appliedPatch,
pipetteEntities,
labwareEntities
pipetteEntities
)

if (maxDisposalVolume == null) {
Expand Down Expand Up @@ -644,37 +637,15 @@ export function dependentFieldsUpdateMoveLiquid(
chainPatch =>
updatePatchOnPipetteChange(chainPatch, rawForm, pipetteEntities),
chainPatch => updatePatchOnWellRatioChange(chainPatch, rawForm),
chainPatch =>
updatePatchPathField(
chainPatch,
rawForm,
pipetteEntities,
labwareEntities
),
chainPatch => updatePatchPathField(chainPatch, rawForm, pipetteEntities),
chainPatch =>
updatePatchDisposalVolumeFields(chainPatch, rawForm, pipetteEntities),
chainPatch =>
clampAspirateAirGapVolume(
chainPatch,
rawForm,
pipetteEntities,
labwareEntities
),
chainPatch =>
clampDisposalVolume(
chainPatch,
rawForm,
pipetteEntities,
labwareEntities
),
clampAspirateAirGapVolume(chainPatch, rawForm, pipetteEntities),
chainPatch => clampDisposalVolume(chainPatch, rawForm, pipetteEntities),
chainPatch => updatePatchMixFields(chainPatch, rawForm),
chainPatch => updatePatchBlowoutFields(chainPatch, rawForm),
chainPatch =>
clampDispenseAirGapVolume(
chainPatch,
rawForm,
pipetteEntities,
labwareEntities
),
clampDispenseAirGapVolume(chainPatch, rawForm, pipetteEntities),
])
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import {
} from '../utils'
import { fixture_tiprack_300_ul } from '@opentrons/shared-data/labware/fixtures/2'
import type { LabwareDefinition2 } from '@opentrons/shared-data'
import type {
PipetteEntities,
LabwareEntities,
} from '@opentrons/step-generation'
import type { PipetteEntities } from '@opentrons/step-generation'
import type { FormData } from '../../../../form-types'

const fixtureTiprack300ul = fixture_tiprack_300_ul as LabwareDefinition2
Expand All @@ -19,7 +16,6 @@ describe('utils', () => {
describe('volumeInCapacityForMulti', () => {
let sharedForm: FormData
let pipetteEntities: PipetteEntities
let labwareEntities: LabwareEntities
beforeEach(() => {
sharedForm = {
pipette: 'p300_single',
Expand All @@ -30,7 +26,6 @@ describe('utils', () => {
tiprackLabwareDef: [fixtureTiprack300ul],
},
} as any
labwareEntities = {}
})
describe('multi dispense path', () => {
const testCases = [
Expand Down Expand Up @@ -92,8 +87,7 @@ describe('utils', () => {
expect(
volumeInCapacityForMulti(
{ ...sharedForm, ...form },
pipetteEntities,
labwareEntities
pipetteEntities
)
).toBe(expected)
})
Expand Down Expand Up @@ -150,8 +144,7 @@ describe('utils', () => {
expect(
volumeInCapacityForMulti(
{ ...sharedForm, ...form },
pipetteEntities,
labwareEntities
pipetteEntities
)
).toBe(expected)
})
Expand Down
16 changes: 5 additions & 11 deletions protocol-designer/src/steplist/formLevel/handleFormChange/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ export function getMaxDisposalVolumeForMultidispense(
volume: string | null
tipRack?: string | null
},
pipetteEntities: PipetteEntities,
labwareEntities: LabwareEntities
pipetteEntities: PipetteEntities
): number | null | undefined {
// calculate max disposal volume for given volume & pipette. Might be negative!
const pipetteId = values?.pipette
Expand All @@ -76,11 +75,7 @@ export function getMaxDisposalVolumeForMultidispense(
`getMaxDisposalVolumeForMultidispense expected multiDispense, got path ${values.path}`
)
const pipetteEntity = pipetteEntities[pipetteId]
const pipetteCapacity = getPipetteCapacity(
pipetteEntity,
labwareEntities,
values.tipRack
)
const pipetteCapacity = getPipetteCapacity(pipetteEntity, values.tipRack)
const volume = Number(values.volume)
const airGapChecked = values.aspirate_airGap_checkbox
let airGapVolume = airGapChecked ? Number(values.aspirate_airGap_volume) : 0
Expand All @@ -92,17 +87,15 @@ export function getMaxDisposalVolumeForMultidispense(
// is responsibility of dependentFieldsUpdateMoveLiquid's clamp fn
export function volumeInCapacityForMulti(
rawForm: FormData,
pipetteEntities: PipetteEntities,
labwareEntities: LabwareEntities
pipetteEntities: PipetteEntities
): boolean {
console.assert(
rawForm.pipette in pipetteEntities,
`volumeInCapacityForMulti expected pipette ${rawForm.pipette} to be in pipetteEntities`
)
const pipetteEntity = pipetteEntities[rawForm.pipette]
const pipetteCapacity =
pipetteEntity &&
getPipetteCapacity(pipetteEntity, labwareEntities, rawForm.tipRack)
pipetteEntity && getPipetteCapacity(pipetteEntity, rawForm.tipRack)
const volume = Number(rawForm.volume)
const airGapChecked = rawForm.aspirate_airGap_checkbox
let airGapVolume = airGapChecked ? Number(rawForm.aspirate_airGap_volume) : 0
Expand Down Expand Up @@ -137,6 +130,7 @@ export function volumeInCapacityForMultiDispense(args: {
airGapVolume: number
}): boolean {
const { volume, pipetteCapacity, airGapVolume } = args

return (
volume > 0 &&
pipetteCapacity > 0 &&
Expand Down

0 comments on commit 771d4f6

Please sign in to comment.