Skip to content

Commit

Permalink
feat(app): Overhaul OT-2 calibration experience
Browse files Browse the repository at this point in the history
Enable the previously under-development flows to support the new
calibration procedures for the OT-2.

The new development flows are based on a new session model, with a
limited number of endpoints that accept structured commands. The
middleware for this lives in app/src/sessions.

In addition, much more data about calibration is exposed via HTTP in
app/src/calibrations.

The new deck calibration doesn't have much that is different, but the
new pipette offset calibration and tip length calibration flows are
integrated in many different parts of the app ecosystem:
- In the attach pipette flow, you now calibrate pipette offset (and
possibly tip length, if necessary) in the attach flow
- There are also buttons for same in the pipettes card
- In the pre protocol flow, you now calibrate tip length for each kind
of tiprack used in your protocol, possibly also recalibrating pipette
offset if you recalibrate a tip length used for pipette offset
- You must have a pipette offset and deck calibration to enter the
protocol flows
- Robot calibration data is now exposed on the robots pages

Closes #6736, #6737
  • Loading branch information
sfoster1 committed Oct 16, 2020
1 parent d51b958 commit f7beeba
Show file tree
Hide file tree
Showing 32 changed files with 128 additions and 1,062 deletions.
19 changes: 2 additions & 17 deletions app/src/components/CalibratePanel/PipetteList.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { withRouter } from 'react-router-dom'
import uniqBy from 'lodash/uniqBy'

import { selectors as robotSelectors } from '../../robot'
import { getFeatureFlags } from '../../config'
import { PIPETTE_MOUNTS, getAttachedPipettes } from '../../pipettes'
import { getCalibratePipettesLocations } from '../../nav'
import { fetchTipLengthCalibrations } from '../../calibration'
Expand All @@ -20,14 +19,12 @@ import {
FONT_WEIGHT_SEMIBOLD,
TEXT_TRANSFORM_UPPERCASE,
} from '@opentrons/components'
import { PipetteListItem } from './PipetteListItem'
import { PipetteTiprackListItem } from './PipetteTiprackListItem'

import type { BaseProtocolLabware } from '../../calibration/types'
import type { Dispatch, State } from '../../types'

// TODO(mc, 2019-12-10): i18n
const PIPETTE_CALIBRATION = 'Pipette Calibration'
const TIP_LENGTH_CALIBRATION = 'Tip Length Calibration'

export type PipetteListComponentProps = {|
Expand All @@ -50,17 +47,13 @@ export function PipetteListComponent(
const attachedPipettes = useSelector((state: State) =>
getAttachedPipettes(state, robotName)
)
const ff = useSelector(getFeatureFlags)

React.useEffect(() => {
robotName && dispatch(fetchTipLengthCalibrations(robotName))
}, [dispatch, robotName])

const titleListTitle = !ff.enableCalibrationOverhaul
? PIPETTE_CALIBRATION
: TIP_LENGTH_CALIBRATION
return (
<TitledList key={titleListTitle} title={titleListTitle}>
<TitledList key={TIP_LENGTH_CALIBRATION} title={TIP_LENGTH_CALIBRATION}>
{PIPETTE_MOUNTS.map(mount => {
const protocolPipette =
protocolPipettes.find(i => i.mount === mount) || null
Expand All @@ -86,15 +79,7 @@ export function PipetteListComponent(
urlsByMount[mount]['default']
return url.path
}
return !ff.enableCalibrationOverhaul ? (
<PipetteListItem
key={mount}
mount={mount}
pipette={protocolPipette}
calibrateUrl={urlsByMount[mount].default.path}
disabledReason={disabledReason}
/>
) : (
return (
<>
<Flex key={`${mount} box`}>
<Text
Expand Down
49 changes: 0 additions & 49 deletions app/src/components/CalibratePanel/PipetteListItem.js

This file was deleted.

25 changes: 1 addition & 24 deletions app/src/components/CalibratePanel/__tests__/PipetteList.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { StaticRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import { mount } from 'enzyme'

import * as Config from '../../../config'
import * as PipettesSelectors from '../../../pipettes/selectors'
import * as robotSelectors from '../../../robot/selectors'
import * as calibrateSelectors from '../../../nav/calibrate-selectors'
Expand All @@ -19,7 +18,6 @@ import {

import type { State } from '../../../types'

jest.mock('../../../config/selectors')
jest.mock('../../../robot/selectors')
jest.mock('../../../nav/calibrate-selectors')
jest.mock('../../../pipettes/selectors')
Expand All @@ -37,11 +35,6 @@ const mockGetCalibratePipetteLocations: JestMockFn<
const mockGetAttachedPipettes: JestMockFn<[any, string], mixed> =
PipettesSelectors.getAttachedPipettes

const mockGetFeatureFlags: JestMockFn<
[State],
$Call<typeof Config.getFeatureFlags, State>
> = Config.getFeatureFlags

const mockLeftSpecs: any = {
displayName: 'Left Pipette',
name: 'mock_left',
Expand Down Expand Up @@ -132,7 +125,6 @@ describe('PipetteListComponent', () => {
mockGetProtocolPipettes.mockReturnValue([mockLeftProtoPipette])
mockGetCalibratePipetteLocations.mockReturnValue(mockPipetteLocations)
mockGetAttachedPipettes.mockReturnValue(mockAttachedPipettes)
mockGetFeatureFlags.mockReturnValue({})

render = (props: PipetteListComponentProps, location: string = '/') => {
return mount(
Expand Down Expand Up @@ -162,22 +154,7 @@ describe('PipetteListComponent', () => {
)
})

it('renders pipette calibration when feature flag for calibration overhaul is falsy', () => {
const wrapper = render({
robotName: 'robotName',
tipracks: stubTipRacks,
})
const component = wrapper.find(PipetteListComponent)

expect(
component.find('TitledList[title="Pipette Calibration"]').exists()
).toBe(true)

expect(component.find('PipetteListItem[mount="left"]').exists()).toBe(true)
})

it('render tip length calibration when feature flag for calibration overhaul is truthy ', () => {
mockGetFeatureFlags.mockReturnValue({ enableCalibrationOverhaul: true })
it('renders tip length calibration', () => {
const wrapper = render({
robotName: 'robotName',
tipracks: stubTipRacks,
Expand Down
11 changes: 3 additions & 8 deletions app/src/components/ChangePipette/ConfirmPipette.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
// @flow
import * as React from 'react'
import cx from 'classnames'
import { useSelector } from 'react-redux'

import { Icon, PrimaryBtn, ModalPage, SPACING_2 } from '@opentrons/components'
import { getDiagramsSrc } from './InstructionStep'
import { CheckPipettesButton } from './CheckPipettesButton'
import styles from './styles.css'
import { getFeatureFlags } from '../../config'

import type {
PipetteNameSpecs,
Expand Down Expand Up @@ -51,8 +49,6 @@ export function ConfirmPipette(props: Props): React.Node {
back,
} = props

const ff = useSelector(getFeatureFlags)

return (
<ModalPage
titleBar={{
Expand All @@ -65,10 +61,9 @@ export function ConfirmPipette(props: Props): React.Node {
<StatusDetails {...props} />
{!success && <TryAgainButton {...props} />}
{success && !actualPipette && <AttachAnotherButton {...props} />}
{ff.enableCalibrationOverhaul &&
success &&
actualPipette &&
!actualPipetteOffset && <CalibratePipetteOffsetButton {...props} />}
{success && actualPipette && !actualPipetteOffset && (
<CalibratePipetteOffsetButton {...props} />
)}
<ExitButton {...props} />
</ModalPage>
)
Expand Down
6 changes: 1 addition & 5 deletions app/src/components/ChangePipette/LevelPipette.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import * as React from 'react'
import cx from 'classnames'
import { useSelector } from 'react-redux'

import {
Icon,
Expand All @@ -12,7 +11,6 @@ import {
SPACING_2,
} from '@opentrons/components'
import styles from './styles.css'
import { getFeatureFlags } from '../../config'

import type {
PipetteNameSpecs,
Expand Down Expand Up @@ -100,8 +98,6 @@ export function LevelPipette(props: Props): React.Node {
startPipetteOffsetCalibration,
} = props

const ff = useSelector(getFeatureFlags)

return (
<ModalPage
titleBar={{
Expand All @@ -114,7 +110,7 @@ export function LevelPipette(props: Props): React.Node {
<Status displayName={displayName} />
<LevelingInstruction displayName={displayName} />
<LevelingVideo pipetteName={pipetteModelName} mount={mount} />
{ff.enableCalibrationOverhaul && !actualPipetteOffset && (
{!actualPipetteOffset && (
<PrimaryBtn
marginBottom={SPACING_2}
width="100%"
Expand Down
12 changes: 3 additions & 9 deletions app/src/components/InstrumentSettings/AttachedPipettesCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import {
getAttachedPipetteSettings,
} from '../../pipettes'

import { PipetteInfo, LegacyPipetteInfo } from './PipetteInfo'
import { getFeatureFlags } from '../../config'
import { PipetteInfo } from './PipetteInfo'
import { CardContentFlex } from '../layout'
import { Card, useInterval } from '@opentrons/components'

Expand All @@ -39,8 +38,6 @@ export function AttachedPipettesCard(props: Props): React.Node {
const { robotName, makeChangeUrl, makeConfigureUrl } = props
const dispatch = useDispatch<Dispatch>()

const ff = useSelector(getFeatureFlags)

const pipettes = useSelector((state: State) =>
getAttachedPipettes(state, robotName)
)
Expand All @@ -59,20 +56,17 @@ export function AttachedPipettesCard(props: Props): React.Node {
true
)

const PipetteEl = ff.enableCalibrationOverhaul
? PipetteInfo
: LegacyPipetteInfo
return (
<Card title={PIPETTES}>
<CardContentFlex>
<PipetteEl
<PipetteInfo
robotName={robotName}
mount={LEFT}
pipette={pipettes.left}
changeUrl={makeChangeUrl(LEFT)}
settingsUrl={settings.left ? makeConfigureUrl(LEFT) : null}
/>
<PipetteEl
<PipetteInfo
robotName={robotName}
mount={RIGHT}
pipette={pipettes.right}
Expand Down
51 changes: 0 additions & 51 deletions app/src/components/InstrumentSettings/PipetteInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as React from 'react'
import { useSelector } from 'react-redux'
import { css } from 'styled-components'
import { Link } from 'react-router-dom'
import cx from 'classnames'

import { getLabwareDisplayName } from '@opentrons/shared-data'
import type { LabwareDefinition2 } from '@opentrons/shared-data'
Expand Down Expand Up @@ -288,53 +287,3 @@ export function PipetteInfo(props: PipetteInfoProps): React.Node {
</>
)
}

// TODO: BC 2020-09-02 remove this component once calibration overhaul feature flag is removed
export function LegacyPipetteInfo(props: PipetteInfoProps): React.Node {
const { mount, pipette, changeUrl, settingsUrl } = props
const label = LABEL_BY_MOUNT[mount]
const displayName = pipette ? pipette.modelSpecs.displayName : null
const serialNumber = pipette ? pipette.id : null
const channels = pipette ? pipette.modelSpecs.channels : null
const direction = pipette ? 'change' : 'attach'
const className = cx(styles.pipette_card, {
[styles.right]: mount === 'right',
})

return (
<div className={className}>
<div className={styles.pipette_info_block}>
<LabeledValue
label={label}
value={(displayName || 'None').replace(/-/, '‑')} // non breaking hyphen
valueClassName={styles.pipette_info_element}
/>
<LabeledValue
label={SERIAL_NUMBER}
value={serialNumber || 'None'}
valueClassName={styles.pipette_info_element}
/>
</div>

<div className={styles.button_group}>
<OutlineButton Component={Link} to={changeUrl}>
{direction}
</OutlineButton>
{settingsUrl !== null && (
<OutlineButton Component={Link} to={settingsUrl}>
settings
</OutlineButton>
)}
</div>
<div className={styles.image}>
{channels && (
<InstrumentDiagram
pipetteSpecs={pipette?.modelSpecs}
mount={mount}
className={styles.pipette_diagram}
/>
)}
</div>
</div>
)
}
Loading

0 comments on commit f7beeba

Please sign in to comment.