Skip to content

Commit

Permalink
feat(app): update pipette attach flow to include calibration
Browse files Browse the repository at this point in the history
When attaching a pipette, if you do not currently have a pipette offset calibration stored for that
combination of pipette serial number and mount, attach pipette flow will directly feed into the
pipette offset calibration flow (which may also include tip length cal for the tip used in pipette
offset cal)

Closes #2130
  • Loading branch information
b-cooper committed Oct 13, 2020
1 parent fd03cde commit 9d06d9c
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 210 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// @flow
import * as React from 'react'
import { Provider } from 'react-redux'
import uniqueId from 'lodash/uniqueId'
import { mountWithStore } from '@opentrons/components/__utils__'
import { act } from 'react-dom/test-utils'
Expand All @@ -10,7 +9,6 @@ import * as Sessions from '../../../sessions'
import { mockPipetteOffsetCalibrationSessionAttributes } from '../../../sessions/__fixtures__'

import { useCalibratePipetteOffset } from '../useCalibratePipetteOffset'
import { mount } from 'enzyme'

import type { State } from '../../../types'
import type { SessionType } from '../../../sessions'
Expand All @@ -32,13 +30,20 @@ const mockGetRequestById: JestMockFn<
describe('useCalibratePipetteOffset hook', () => {
let startCalibration
let CalWizardComponent
let store
const robotName = 'robotName'
const mountString = 'left'
const onComplete = jest.fn()

const TestUseCalibratePipetteOffset = () => {
const [_startCalibration, _CalWizardComponent] = useCalibratePipetteOffset(
robotName,
mountString
{
mount: mountString,
shouldPerformTipLength: false,
hasCalibrationBlock: false,
tipRackDefinition: null,
},
onComplete
)
React.useEffect(() => {
startCalibration = _startCalibration
Expand Down Expand Up @@ -69,7 +74,12 @@ describe('useCalibratePipetteOffset hook', () => {
...Sessions.ensureSession(
robotName,
Sessions.SESSION_TYPE_PIPETTE_OFFSET_CALIBRATION,
{ mount: mountString }
{
mount: mountString,
shouldPerformTipLength: false,
hasCalibrationBlock: false,
tipRackDefinition: null,
}
),
meta: { requestId: expect.any(String) },
})
Expand All @@ -85,12 +95,9 @@ describe('useCalibratePipetteOffset hook', () => {
currentStep: Sessions.PIP_OFFSET_STEP_CALIBRATION_COMPLETE,
},
}
const { store, wrapper } = mountWithStore(
<TestUseCalibratePipetteOffset />,
{
initialState: { robotApi: {} },
}
)
const { wrapper } = mountWithStore(<TestUseCalibratePipetteOffset />, {
initialState: { robotApi: {} },
})
mockGetRobotSessionOfType.mockReturnValue(mockPipOffsetCalSession)
mockGetRequestById.mockReturnValue({
status: RobotApi.SUCCESS,
Expand All @@ -105,8 +112,11 @@ describe('useCalibratePipetteOffset hook', () => {
wrapper.setProps({})
expect(CalWizardComponent).not.toBe(null)

wrapper.find('button[children="exit"]').invoke('onClick')()
wrapper
.find('button[title="Return tip to tip rack and exit"]')
.invoke('onClick')()
wrapper.setProps({})
expect(CalWizardComponent).toBe(null)
expect(onComplete).toHaveBeenCalled()
})
})
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// @flow
import * as React from 'react'
import { useSelector } from 'react-redux'
import { SecondaryBtn, SPACING_2 } from '@opentrons/components'

import * as RobotApi from '../../robot-api'
import * as Sessions from '../../sessions'
Expand All @@ -10,8 +9,8 @@ import type { State } from '../../types'
import type {
SessionCommandString,
PipetteOffsetCalibrationSession,
PipetteOffsetCalibrationSessionParams,
} from '../../sessions/types'
import type { Mount } from '../../pipettes/types'
import type { RequestState } from '../../robot-api/types'

import { Portal } from '../portal'
Expand All @@ -24,7 +23,7 @@ const spinnerCommandBlockList: Array<SessionCommandString> = [

export function useCalibratePipetteOffset(
robotName: string,
mount: Mount,
sessionParams: $Shape<PipetteOffsetCalibrationSessionParams>,
onComplete: (() => mixed) | null = null
): [() => void, React.Node | null] {
const [showWizard, setShowWizard] = React.useState(false)
Expand Down Expand Up @@ -74,24 +73,47 @@ export function useCalibratePipetteOffset(
: null
)?.status === RobotApi.SUCCESS

const closeWizard = React.useCallback(() => {
setShowWizard(false)
onComplete && onComplete()
}, [onComplete])

React.useEffect(() => {
if (shouldOpen) {
setShowWizard(true)
createRequestId.current = null
}
if (shouldClose) {
setShowWizard(false)
onComplete && onComplete()
closeWizard()
deleteRequestId.current = null
}
}, [shouldOpen, shouldClose])
}, [shouldOpen, shouldClose, closeWizard])

const {
mount,
shouldPerformTipLength = false,
hasCalibrationBlock = false,
tipRackDefinition = null,
} = sessionParams
const handleStartPipOffsetCalSession = () => {
console.log('HANDLE START')
console.table({
robotName,
mount,
shouldPerformTipLength,
hasCalibrationBlock,
tipRackDefinition,
})
dispatchRequests(
Sessions.ensureSession(
robotName,
Sessions.SESSION_TYPE_PIPETTE_OFFSET_CALIBRATION,
{ mount }
{
mount,
shouldPerformTipLength,
hasCalibrationBlock,
tipRackDefinition,
}
)
)
}
Expand All @@ -118,7 +140,7 @@ export function useCalibratePipetteOffset(
<CalibratePipetteOffset
session={pipOffsetCalSession}
robotName={robotName}
closeWizard={() => setShowWizard(false)}
closeWizard={closeWizard}
showSpinner={showSpinner}
dispatchRequests={dispatchRequests}
/>
Expand Down
13 changes: 1 addition & 12 deletions app/src/components/ChangePipette/ConfirmPipette.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,7 @@
import * as React from 'react'
import cx from 'classnames'

import {
Icon,
PrimaryBtn,
ModalPage,
SPACING_2,
SPACING_4,
} from '@opentrons/components'
import { PipetteOffsetCalibrationControl } from '../InstrumentSettings/PipetteOffsetCalibrationControl'
import { Icon, PrimaryBtn, ModalPage, SPACING_2 } from '@opentrons/components'
import { getDiagramsSrc } from './InstructionStep'
import { CheckPipettesButton } from './CheckPipettesButton'
import styles from './styles.css'
Expand All @@ -21,7 +14,6 @@ import type {
} from '@opentrons/shared-data'
import type { Mount } from '../../pipettes/types'
import type { PipetteOffsetCalibration } from '../../calibration/types'
import { CalibratePipetteOffset } from '../CalibratePipetteOffset'

const EXIT_BUTTON_MESSAGE = 'exit pipette setup'
const EXIT_BUTTON_MESSAGE_WRONG = 'keep pipette and exit setup'
Expand Down Expand Up @@ -54,9 +46,6 @@ export function ConfirmPipette(props: Props): React.Node {
actualPipette,
actualPipetteOffset,
back,
robotName,
mount,
startPipetteOffsetCalibration,
} = props

return (
Expand Down
64 changes: 41 additions & 23 deletions app/src/components/ChangePipette/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// @flow
import * as React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import last from 'lodash/last'
import { getPipetteNameSpecs, shouldLevel } from '@opentrons/shared-data'

import { useDispatchApiRequest, getRequestById, PENDING } from '../../robot-api'
import {
useDispatchApiRequests,
getRequestById,
SUCCESS,
} from '../../robot-api'
import { getCalibrationForPipette } from '../../calibration'
import { getAttachedPipettes } from '../../pipettes'
import {
Expand All @@ -13,8 +16,10 @@ import {
getMovementStatus,
HOMING,
MOVING,
ROBOT,
PIPETTE,
CHANGE_PIPETTE,
HOME,
} from '../../robot-controls'

import { useCalibratePipetteOffset } from '../CalibratePipetteOffset'
Expand Down Expand Up @@ -53,7 +58,16 @@ const MOUNT = 'mount'
export function ChangePipette(props: Props): React.Node {
const { robotName, mount, closeModal } = props
const dispatch = useDispatch<Dispatch>()
const [dispatchApiRequest, requestIds] = useDispatchApiRequest()
const homePipRequestId = React.useRef<string | null>(null)
const [dispatchApiRequests] = useDispatchApiRequests(dispatchedAction => {
if (
dispatchedAction.type === HOME &&
dispatchedAction.payload.target === PIPETTE
) {
// track final home pipette request, its success closes modal
homePipRequestId.current = dispatchedAction.meta.requestId
}
})
const [wizardStep, setWizardStep] = React.useState<WizardStep>(CLEAR_DECK)
const [wantedName, setWantedName] = React.useState<string | null>(null)
const [confirmExit, setConfirmExit] = React.useState(false)
Expand All @@ -72,25 +86,28 @@ export function ChangePipette(props: Props): React.Node {
return getMovementStatus(state, robotName)
})

const homeRequest = useSelector((state: State) => {
return getRequestById(state, last(requestIds))
})?.status
const homePipSuccess =
useSelector((state: State) => {
return homePipRequestId.current
? getRequestById(state, homePipRequestId.current)
: null
})?.status === SUCCESS

React.useEffect(() => {
if (homeRequest && homeRequest !== PENDING) {
if (homePipSuccess) {
closeModal()
}
}, [homeRequest, closeModal])
}, [homePipSuccess, closeModal])

const homeAndExit = React.useCallback(
() => dispatchApiRequest(home(robotName, PIPETTE, mount)),
[dispatchApiRequest, robotName, mount]
const homePipAndExit = React.useCallback(
() => dispatchApiRequests(home(robotName, PIPETTE, mount)),
[dispatchApiRequests, robotName, mount]
)

const [
startPipetteOffsetCalibration,
PipetteOffsetCalibrationWizard,
] = useCalibratePipetteOffset(robotName, mount, closeModal)
] = useCalibratePipetteOffset(robotName, { mount }, closeModal)

const baseProps = {
title: PIPETTE_SETUP,
Expand Down Expand Up @@ -139,7 +156,7 @@ export function ChangePipette(props: Props): React.Node {
{confirmExit && (
<ExitAlertModal
back={() => setConfirmExit(false)}
exit={homeAndExit}
exit={homePipAndExit}
/>
)}
<Instructions
Expand All @@ -164,19 +181,23 @@ export function ChangePipette(props: Props): React.Node {

const attachedWrong = Boolean(!success && wantedPipette && actualPipette)

const launchPOC = () => {
// home before cal flow to account for skips when attaching pipette
setWizardStep(CALIBRATE_PIPETTE)
dispatchApiRequests(home(robotName, ROBOT))
startPipetteOffsetCalibration()
}

if (success && wantedPipette && shouldLevel(wantedPipette)) {
return (
<LevelPipette
{...{
pipetteModelName: actualPipette ? actualPipette.name : '',
...basePropsWithPipettes,
back: () => setWizardStep(INSTRUCTIONS),
exit: homeAndExit,
exit: homePipAndExit,
actualPipetteOffset: actualPipetteOffset,
startPipetteOffsetCalibration: () => {
startPipetteOffsetCalibration()
setWizardStep(CALIBRATE_PIPETTE)
},
startPipetteOffsetCalibration: launchPOC,
}}
/>
)
Expand All @@ -192,12 +213,9 @@ export function ChangePipette(props: Props): React.Node {
setWizardStep(INSTRUCTIONS)
},
back: () => setWizardStep(INSTRUCTIONS),
exit: homeAndExit,
exit: homePipAndExit,
actualPipetteOffset: actualPipetteOffset,
startPipetteOffsetCalibration: () => {
startPipetteOffsetCalibration()
setWizardStep(CALIBRATE_PIPETTE)
},
startPipetteOffsetCalibration: launchPOC,
}}
/>
)
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/InstrumentSettings/PipetteInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function PipetteInfo(props: PipetteInfoProps): React.Node {
const [
startPipetteOffsetCalibration,
PipetteOffsetCalibrationWizard,
] = useCalibratePipetteOffset(robotName, mount)
] = useCalibratePipetteOffset(robotName, { mount })

const pipImage = (
<Box
Expand Down
Loading

0 comments on commit 9d06d9c

Please sign in to comment.