diff --git a/app/src/analytics/__tests__/sessions-events.test.js b/app/src/analytics/__tests__/sessions-events.test.js deleted file mode 100644 index 4758afbd25b..00000000000 --- a/app/src/analytics/__tests__/sessions-events.test.js +++ /dev/null @@ -1,52 +0,0 @@ -// @flow - -import { makeEvent } from '../make-event' - -import * as Sessions from '../../sessions' -import * as sessionsFixtures from '../../sessions/__fixtures__' - -import type { State } from '../../types' -import type { SessionAnalyticsProps } from '../../sessions/types' - -jest.mock('../../sessions/selectors') - -const getAnalyticsPropsForRobotSessionById: JestMockFn< - [State, string, string], - SessionAnalyticsProps | null -> = Sessions.getAnalyticsPropsForRobotSessionById - -const MOCK_STATE: State = ({ mockState: true }: any) - -describe('events with calibration check session data', () => { - afterEach(() => { - jest.resetAllMocks() - }) - - it('sessions:DELETE_SESSION of type Calibration Check > sessionExit', () => { - getAnalyticsPropsForRobotSessionById.mockImplementation(state => { - expect(state).toBe(MOCK_STATE) - return sessionsFixtures.mockCalibrationCheckSessionAnalyticsProps - }) - const deleteSession = Sessions.deleteSession( - 'sacrosanct_coelacanth', - 'fake_session_id' - ) - - return expect(makeEvent(deleteSession, MOCK_STATE)).resolves.toEqual({ - name: 'calibrationCheckSessionExit', - properties: sessionsFixtures.mockCalibrationCheckSessionAnalyticsProps, - }) - }) - it('sessions:DELETE_SESSION of type Calibration Check > null', () => { - getAnalyticsPropsForRobotSessionById.mockImplementation(state => { - expect(state).toBe(MOCK_STATE) - return null - }) - const deleteSession = Sessions.deleteSession( - 'sacrosanct_coelacanth', - 'fake_session_id' - ) - - return expect(makeEvent(deleteSession, MOCK_STATE)).resolves.toEqual(null) - }) -}) diff --git a/app/src/analytics/make-event.js b/app/src/analytics/make-event.js index 28cf4c983d6..6e8010250cc 100644 --- a/app/src/analytics/make-event.js +++ b/app/src/analytics/make-event.js @@ -272,12 +272,7 @@ export function makeEvent( } case Sessions.DELETE_SESSION: { - const { robotName, sessionId } = action.payload - const analyticsProps = Sessions.getAnalyticsPropsForRobotSessionById( - state, - robotName, - sessionId - ) + const analyticsProps = null if (analyticsProps) { return Promise.resolve({ name: `${analyticsProps.sessionType}SessionExit`, diff --git a/app/src/calibration/pipette-offset/epic/fetchPipetteOffsetCalibrationsOnCalibrationEnd.js b/app/src/calibration/pipette-offset/epic/fetchPipetteOffsetCalibrationsOnCalibrationEnd.js index 872f5a7d23d..b10b7f7e95f 100644 --- a/app/src/calibration/pipette-offset/epic/fetchPipetteOffsetCalibrationsOnCalibrationEnd.js +++ b/app/src/calibration/pipette-offset/epic/fetchPipetteOffsetCalibrationsOnCalibrationEnd.js @@ -10,7 +10,7 @@ import type { Epic, State } from '../../../types' import type { SessionType } from '../../../sessions/types' import { DELETE_SESSION, - SESSION_TYPE_CALIBRATION_CHECK, + SESSION_TYPE_CALIBRATION_HEALTH_CHECK, SESSION_TYPE_TIP_LENGTH_CALIBRATION, SESSION_TYPE_DECK_CALIBRATION, SESSION_TYPE_PIPETTE_OFFSET_CALIBRATION, @@ -19,7 +19,7 @@ import { getRobotSessionById } from '../../../sessions/selectors' const isTargetSessionType: SessionType => boolean = sessionType => [ - SESSION_TYPE_CALIBRATION_CHECK, + SESSION_TYPE_CALIBRATION_HEALTH_CHECK, SESSION_TYPE_TIP_LENGTH_CALIBRATION, SESSION_TYPE_DECK_CALIBRATION, SESSION_TYPE_PIPETTE_OFFSET_CALIBRATION, diff --git a/app/src/calibration/tip-length/epic/fetchTipLengthCalibrationsOnCalibrationEnd.js b/app/src/calibration/tip-length/epic/fetchTipLengthCalibrationsOnCalibrationEnd.js index 63e2b64d361..5bc7d4ce8df 100644 --- a/app/src/calibration/tip-length/epic/fetchTipLengthCalibrationsOnCalibrationEnd.js +++ b/app/src/calibration/tip-length/epic/fetchTipLengthCalibrationsOnCalibrationEnd.js @@ -10,7 +10,7 @@ import type { Epic, State } from '../../../types' import type { SessionType } from '../../../sessions/types' import { DELETE_SESSION, - SESSION_TYPE_CALIBRATION_CHECK, + SESSION_TYPE_CALIBRATION_HEALTH_CHECK, SESSION_TYPE_TIP_LENGTH_CALIBRATION, SESSION_TYPE_PIPETTE_OFFSET_CALIBRATION, } from '../../../sessions/constants' @@ -18,7 +18,7 @@ import { getRobotSessionById } from '../../../sessions/selectors' const isTargetSessionType: SessionType => boolean = sessionType => [ - SESSION_TYPE_CALIBRATION_CHECK, + SESSION_TYPE_CALIBRATION_HEALTH_CHECK, SESSION_TYPE_TIP_LENGTH_CALIBRATION, SESSION_TYPE_PIPETTE_OFFSET_CALIBRATION, ].includes(sessionType) diff --git a/app/src/components/CalibrationPanels/DeckSetup.js b/app/src/components/CalibrationPanels/DeckSetup.js index 0fa26d81c8b..26aa61227d9 100644 --- a/app/src/components/CalibrationPanels/DeckSetup.js +++ b/app/src/components/CalibrationPanels/DeckSetup.js @@ -41,6 +41,9 @@ const contentsBySessionType: { [Sessions.SESSION_TYPE_PIPETTE_OFFSET_CALIBRATION]: { moveCommandString: Sessions.sharedCalCommands.MOVE_TO_TIP_RACK, }, + [Sessions.SESSION_TYPE_CALIBRATION_HEALTH_CHECK]: { + moveCommandString: Sessions.sharedCalCommands.MOVE_TO_TIP_RACK, + }, [Sessions.SESSION_TYPE_TIP_LENGTH_CALIBRATION]: { moveCommandString: Sessions.sharedCalCommands.MOVE_TO_REFERENCE_POINT, }, diff --git a/app/src/components/CalibrationPanels/Introduction.js b/app/src/components/CalibrationPanels/Introduction.js index fd943882216..73282a06fd0 100644 --- a/app/src/components/CalibrationPanels/Introduction.js +++ b/app/src/components/CalibrationPanels/Introduction.js @@ -37,6 +37,11 @@ const DECK_CAL_HEADER = 'deck calibration' const DECK_CAL_BODY = 'Deck calibration ensures positional accuracy so that your robot moves as expected. It will accurately establish the OT-2’s deck orientation relative to the gantry.' +const HEALTH_CHECK_HEADER = 'calibration health check' +const HEALTH_CHECK_BODY = + 'Checking the OT-2’s calibration is a first step towards diagnosing and troubleshooting common pipette positioning problems you may be experiencing.' +const HEALTH_CHECK_PROCEDURE = 'to calibration health check' + const PIP_OFFSET_CAL_HEADER = 'pipette offset calibration' const PIP_OFFSET_CAL_BODY = 'Calibrating pipette offset enables robot to accurately establish the location of the mounted pipette’s nozzle, relative to the deck.' @@ -54,6 +59,8 @@ const NOTE_BODY_OUTSIDE_PROTOCOL = 'important you perform this calibration using the Opentrons tips and tip racks specified above, as the robot determines accuracy based on the measurements of these tips.' const NOTE_BODY_PRE_PROTOCOL = 'important you perform this calibration using the exact tips specified in your protocol, as the robot uses the corresponding labware definition data to find the tip.' +const NOTE_HEALTH_CHECK_OUTCOMES = + 'If the difference between the two coordinates falls within the acceptable tolerance range for the given pipette, the check will pass. Otherwise, it will fail and you’ll be provided with troubleshooting guidance. You may exit at any point or continue through to the end to check the overall calibration status of your robot.' const VIEW_TIPRACK_MEASUREMENTS = 'View measurements' const contentsBySessionType: { @@ -82,6 +89,13 @@ const contentsBySessionType: { continueButtonText: `${START} ${TIP_LENGTH_CAL_HEADER}`, noteBody: NOTE_BODY_PRE_PROTOCOL, }, + [Sessions.SESSION_TYPE_CALIBRATION_HEALTH_CHECK]: { + headerText: HEALTH_CHECK_HEADER, + bodyText: HEALTH_CHECK_BODY, + continueButtonText: `${START} ${HEALTH_CHECK_HEADER}`, + continuingToText: HEALTH_CHECK_PROCEDURE, + noteBody: NOTE_HEALTH_CHECK_OUTCOMES, + }, } export function Introduction(props: CalibrationPanelProps): React.Node { @@ -100,6 +114,8 @@ export function Introduction(props: CalibrationPanelProps): React.Node { const lookupType = isExtendedPipOffset ? Sessions.SESSION_TYPE_TIP_LENGTH_CALIBRATION : sessionType + const isHealthCheck = + sessionType === Sessions.SESSION_TYPE_CALIBRATION_HEALTH_CHECK const proceed = () => sendCommands({ command: Sessions.sharedCalCommands.LOAD_LABWARE }) @@ -149,16 +165,27 @@ export function Introduction(props: CalibrationPanelProps): React.Node { )} - - {`${NOTE_HEADER} `} - {IT_IS} - {` ${EXTREMELY} `} - {noteBody} - + {!isHealthCheck ? ( + + {`${NOTE_HEADER} `} + {IT_IS} + {` ${EXTREMELY} `} + {noteBody} + + ) : ( + + {`${NOTE_HEADER} `} + {noteBody} + + )} diff --git a/app/src/components/CalibrationPanels/SaveXYPoint.js b/app/src/components/CalibrationPanels/SaveXYPoint.js index 059bb7c28a4..8c0cfd40267 100644 --- a/app/src/components/CalibrationPanels/SaveXYPoint.js +++ b/app/src/components/CalibrationPanels/SaveXYPoint.js @@ -21,9 +21,9 @@ import * as Sessions from '../../sessions' import type { JogAxis, JogDirection, JogStep } from '../../http-api-client' import type { CalibrationPanelProps } from './types' import type { - SessionCommandString, SessionType, CalibrationSessionStep, + SessionCommandString, } from '../../sessions/types' import { JogControls } from '../JogControls' import { formatJogVector } from './utils' @@ -76,23 +76,30 @@ const assetMap = { } const SAVE_XY_POINT_HEADER = 'Calibrate the X and Y-axis in' +const CHECK_POINT_XY_HEADER = 'Check the X and Y-axis in' const SLOT = 'slot' const JOG_UNTIL = 'Jog the robot until the tip is' const PRECISELY_CENTERED = 'precisely centered' const ABOVE_THE_CROSS = 'above the cross in' const THEN = 'Then press the' const TO_SAVE = 'button to calibrate the x and y-axis in' +const TO_CHECK = + 'button to determine how this position compares to the previously-saved x and y-axis calibration coordinates.' const BASE_BUTTON_TEXT = 'save calibration' +const HEALTH_BUTTON_TEXT = 'check x and y-axis' const MOVE_TO_POINT_TWO_BUTTON_TEXT = `${BASE_BUTTON_TEXT} and move to slot 3` const MOVE_TO_POINT_THREE_BUTTON_TEXT = `${BASE_BUTTON_TEXT} and move to slot 7` +const HEALTH_POINT_TWO_BUTTON_TEXT = `${HEALTH_BUTTON_TEXT} and move to slot 3` +const HEALTH_POINT_THREE_BUTTON_TEXT = `${HEALTH_BUTTON_TEXT} and move to slot 7` const contentsBySessionTypeByCurrentStep: { [SessionType]: { [CalibrationSessionStep]: { slotNumber: string, buttonText: string, - moveCommandString: SessionCommandString | null, + moveCommand: SessionCommandString | null, + finalCommand?: SessionCommandString | null, }, }, } = { @@ -100,35 +107,61 @@ const contentsBySessionTypeByCurrentStep: { [Sessions.DECK_STEP_SAVING_POINT_ONE]: { slotNumber: '1', buttonText: MOVE_TO_POINT_TWO_BUTTON_TEXT, - moveCommandString: Sessions.deckCalCommands.MOVE_TO_POINT_TWO, + moveCommand: Sessions.deckCalCommands.MOVE_TO_POINT_TWO, }, [Sessions.DECK_STEP_SAVING_POINT_TWO]: { slotNumber: '3', buttonText: MOVE_TO_POINT_THREE_BUTTON_TEXT, - moveCommandString: Sessions.deckCalCommands.MOVE_TO_POINT_THREE, + moveCommand: Sessions.deckCalCommands.MOVE_TO_POINT_THREE, }, [Sessions.DECK_STEP_SAVING_POINT_THREE]: { slotNumber: '7', buttonText: BASE_BUTTON_TEXT, - moveCommandString: Sessions.sharedCalCommands.MOVE_TO_TIP_RACK, + moveCommand: Sessions.sharedCalCommands.MOVE_TO_TIP_RACK, }, }, [Sessions.SESSION_TYPE_PIPETTE_OFFSET_CALIBRATION]: { [Sessions.PIP_OFFSET_STEP_SAVING_POINT_ONE]: { slotNumber: '1', buttonText: BASE_BUTTON_TEXT, - moveCommandString: null, + moveCommand: null, + }, + }, + [Sessions.SESSION_TYPE_CALIBRATION_HEALTH_CHECK]: { + [Sessions.CHECK_STEP_COMPARING_POINT_ONE]: { + slotNumber: '1', + buttonText: HEALTH_POINT_TWO_BUTTON_TEXT, + moveCommand: Sessions.deckCalCommands.MOVE_TO_POINT_TWO, + finalCommand: Sessions.sharedCalCommands.MOVE_TO_TIP_RACK, + }, + [Sessions.CHECK_STEP_COMPARING_POINT_TWO]: { + slotNumber: '3', + buttonText: HEALTH_POINT_THREE_BUTTON_TEXT, + moveCommand: Sessions.deckCalCommands.MOVE_TO_POINT_THREE, + }, + [Sessions.CHECK_STEP_COMPARING_POINT_THREE]: { + slotNumber: '7', + buttonText: HEALTH_BUTTON_TEXT, + moveCommand: Sessions.sharedCalCommands.MOVE_TO_TIP_RACK, }, }, } export function SaveXYPoint(props: CalibrationPanelProps): React.Node { - const { isMulti, mount, sendCommands, currentStep, sessionType } = props + const { + isMulti, + mount, + sendCommands, + currentStep, + sessionType, + activePipette, + } = props const { slotNumber, buttonText, - moveCommandString, + moveCommand, + finalCommand, } = contentsBySessionTypeByCurrentStep[sessionType][currentStep] const demoAsset = React.useMemo( @@ -137,6 +170,8 @@ export function SaveXYPoint(props: CalibrationPanelProps): React.Node { [slotNumber, mount, isMulti] ) + const isHealthCheck = + sessionType === Sessions.SESSION_TYPE_CALIBRATION_HEALTH_CHECK const jog = (axis: JogAxis, dir: JogDirection, step: JogStep) => { sendCommands({ command: Sessions.sharedCalCommands.JOG, @@ -147,10 +182,19 @@ export function SaveXYPoint(props: CalibrationPanelProps): React.Node { } const savePoint = () => { - let commands = [{ command: Sessions.sharedCalCommands.SAVE_OFFSET }] - - if (moveCommandString) { - commands = [...commands, { command: moveCommandString }] + let commands = null + if (isHealthCheck) { + commands = [{ command: Sessions.checkCommands.COMPARE_POINT }] + } else { + commands = [{ command: Sessions.sharedCalCommands.SAVE_OFFSET }] + } + if ( + finalCommand && + activePipette?.rank === Sessions.CHECK_PIPETTE_RANK_SECOND + ) { + commands = [...commands, { command: finalCommand }] + } else if (moveCommand) { + commands = [...commands, { command: moveCommand }] } sendCommands(...commands) } @@ -167,7 +211,7 @@ export function SaveXYPoint(props: CalibrationPanelProps): React.Node { fontWeight={FONT_WEIGHT_SEMIBOLD} textTransform={TEXT_TRANSFORM_UPPERCASE} > - {SAVE_XY_POINT_HEADER} + {isHealthCheck ? CHECK_POINT_XY_HEADER : SAVE_XY_POINT_HEADER} {` ${SLOT} ${slotNumber || ''}`} {THEN} {` ${buttonText} `} - {`${TO_SAVE} ${SLOT} ${slotNumber}`}. + {`${isHealthCheck ? TO_CHECK : TO_SAVE} ${SLOT} ${slotNumber}`}.