import assert from 'assert' ; import range from 'lodash/range' ; import isEmpty from 'lodash/isEmpty' ; import uniq from 'lodash/uniq' ; import { AIR , mergeLiquid , splitLiquid , getWellsForTips , getLocationTotalVolume , } from '../utils/misc' ; import * as warningCreators from '../warningCreators' ; import type { AspirateParams } from '@opentrons/shared-data/protocol/types/schemaV3' ; import type { InvariantContext , RobotStateAndWarnings } from '../types' ; export function forAspirate ( params , invariantContext , robotStateAndWarnings ) { const { pipette : pipetteId , volume , labware : labwareId } = params ; const { robotState , warnings } = robotStateAndWarnings ; const { liquidState } = robotState ; const pipetteSpec = invariantContext . pipetteEntities [ pipetteId ] . spec ; const labwareDef = invariantContext . labwareEntities [ labwareId ] . def ; const { allWellsShared , wellsForTips } = getWellsForTips ( pipetteSpec . channels , labwareDef , params . well ) ; assert ( uniq ( wellsForTips ) . length === allWellsShared ? 1 : wellsForTips . length , `expected all wells to be shared, or no wells to be shared. Got: ${ JSON . stringify ( wellsForTips ) }` ) ; if ( pipetteSpec . channels > 1 && allWellsShared ) { const commonWell = wellsForTips [ 0 ] ; const sourceLiquidState = liquidState . labware [ labwareId ] [ commonWell ] ; const isOveraspirate = volume * pipetteSpec . channels > getLocationTotalVolume ( sourceLiquidState ) ; if ( isEmpty ( sourceLiquidState ) ) { warnings . push ( warningCreators . aspirateFromPristineWell ( ) ) ; } else if ( isOveraspirate ) { warnings . push ( warningCreators . aspirateMoreThanWellContents ( ) ) ; } const volumePerTip = isOveraspirate ? getLocationTotalVolume ( sourceLiquidState ) / pipetteSpec . channels : volume ; const newLiquidFromWell = splitLiquid ( volumePerTip , sourceLiquidState ) . dest ; range ( pipetteSpec . channels ) . forEach ( ( tipIndex ) : void => { const pipette = liquidState . pipettes [ pipetteId ] ; const indexToString = tipIndex . toString ( ) ; const tipLiquidState = pipette [ indexToString ] ; const nextTipLiquidState = isOveraspirate ? mergeLiquid ( tipLiquidState , { ... newLiquidFromWell , [ AIR ] : { volume : volume - volumePerTip , } , } ) : mergeLiquid ( tipLiquidState , newLiquidFromWell ) ; pipette [ indexToString ] = nextTipLiquidState ; } ) ; liquidState . labware [ labwareId ] [ commonWell ] = splitLiquid ( volume * pipetteSpec . channels , liquidState . labware [ labwareId ] [ commonWell ] ) . source ; return ; } range ( pipetteSpec . channels ) . forEach ( tipIndex => { const indexToString = tipIndex . toString ( ) ; const pipette = liquidState . pipettes [ pipetteId ] ; const tipLiquidState = pipette [ indexToString ] ; const sourceLiquidState = liquidState . labware [ labwareId ] [ wellsForTips [ tipIndex ] ] ; const newLiquidFromWell = splitLiquid ( volume , sourceLiquidState ) . dest ; if ( isEmpty ( sourceLiquidState ) ) { warnings . push ( warningCreators . aspirateFromPristineWell ( ) ) ; } else if ( volume > getLocationTotalVolume ( sourceLiquidState ) ) { warnings . push ( warningCreators . aspirateMoreThanWellContents ( ) ) ; } pipette [ indexToString ] = mergeLiquid ( tipLiquidState , newLiquidFromWell ) ; } ) ; const labwareLiquidState = liquidState . labware [ labwareId ] ; wellsForTips . forEach ( well => { labwareLiquidState [ well ] = splitLiquid ( volume , labwareLiquidState [ well ] ) . source ; } ) ; }