diff --git a/app/src/assets/images/lpc_level_probe_with_labware.svg b/app/src/assets/images/lpc_level_probe_with_labware.svg
new file mode 100644
index 00000000000..5e75128d9fc
--- /dev/null
+++ b/app/src/assets/images/lpc_level_probe_with_labware.svg
@@ -0,0 +1,315 @@
+
+
+
diff --git a/app/src/assets/images/lpc_level_probe_with_tip.svg b/app/src/assets/images/lpc_level_probe_with_tip.svg
new file mode 100644
index 00000000000..799e78dd3d1
--- /dev/null
+++ b/app/src/assets/images/lpc_level_probe_with_tip.svg
@@ -0,0 +1,127 @@
+
+
+
diff --git a/app/src/assets/localization/en/labware_position_check.json b/app/src/assets/localization/en/labware_position_check.json
index 58fe6550ddd..f08c465f7fa 100644
--- a/app/src/assets/localization/en/labware_position_check.json
+++ b/app/src/assets/localization/en/labware_position_check.json
@@ -6,22 +6,27 @@
"applied_offset_data": "Applied Labware Offset data",
"apply_offset_data": "Apply labware offset data",
"apply_offsets": "apply offsets",
- "attach_probe": "Attach probe to pipette",
+ "attach_probe": "Attach calibration probe",
+ "backmost": "backmost",
+ "calibration_probe": "calibration probe",
"check_item_in_location": "Check {{item}} in {{location}}",
"check_labware_in_slot_title": "Check Labware {{labware_display_name}} in slot {{slot}}",
"check_remaining_labware_with_primary_pipette_section": "Check remaining labware with {{primary_mount}} Pipette and tip",
+ "check_tip_location": "the top of the tip in the A1 position",
+ "check_well_location": "well A1 on the labware",
"clear_all_slots": "Clear all deck slots of labware, leaving modules in place",
"clear_all_slots_odd": "Clear all deck slots of labware",
"cli_ssh": "Command Line Interface (SSH)",
"close_and_apply_offset_data": "Close and apply labware offset data",
+ "confirm_detached": "Confirm removal",
"confirm_pick_up_tip_modal_title": "Did the pipette pick up a tip successfully?",
"confirm_pick_up_tip_modal_try_again_text": "No, try again",
"confirm_position_and_move": "Confirm position, move to slot {{next_slot}}",
"confirm_position_and_pick_up_tip": "Confirm position, pick up tip",
"confirm_position_and_return_tip": "Confirm position, return tip to Slot {{next_slot}} and home",
- "ensure_nozzle_is_above_tip_odd": "Ensure that the pipette nozzle furthest from you is centered above and level with the top of the tip in the A1 position. If it isn't, tap Move pipette and then jog the pipette until it is properly aligned.",
- "ensure_nozzle_is_above_tip_desktop": "Ensure that the pipette nozzle furthest from you is centered above and level with the top of the tip in the A1 position. If it isn't, use the controls below or your keyboard to jog the pipette until it is properly aligned.",
- "ensure_tip_is_above_well": "Ensure that the pipette tip furthest from you is centered above and level with well A1 on the labware.",
+ "detach_probe": "Remove calibration probe",
+ "ensure_nozzle_position_odd": "Ensure that the {{tip_type}} is centered above and level with {{item_location}}. If it isn't, tap Move pipette and then jog the pipette until it is properly aligned.",
+ "ensure_nozzle_position_desktop": "Ensure that the {{tip_type}} is centered above and level with {{item_location}}. If it isn't, use the controls below or your keyboard to jog the pipette until it is properly aligned.",
"error_modal_header": "Something went wrong",
"error_modal_problem_in_app": "There was an error performing Labware Position Check. Please restart the app. If the problem persists, please contact Opentrons Support",
"error_modal_problem_on_robot": "There was an error processing your request on the robot",
@@ -30,8 +35,7 @@
"exit_screen_subtitle": "If you exit now, all labware offsets will be discarded. This cannot be undone.",
"exit_screen_title": "Exit before completing Labware Position Check?",
"get_labware_offset_data": "Get Labware Offset Data",
- "install_probe_8_channel": "Take the calibration probe from its storage location. Ensure its collar is unlocked. Push the pipette ejector up and press the probe firmly onto the backmost pipette nozzle. Twist the collar to lock the probe. Test that the probe is secure by gently pulling it back and forth.",
- "install_probe_96_channel": "Take the calibration probe from its storage location. Ensure its collar is unlocked. Push the pipette ejector up and press the probe firmly onto the A1 (back left corner) pipette nozzle. Twist the collar to lock the probe. Test that the probe is secure by gently pulling it back and forth.",
+ "install_probe": "Take the calibration probe from its storage location. Ensure its collar is fully unlocked. Push the pipette ejector up and press the probe firmly onto the {{location}} pipette nozzle as far as it can go. Twist the collar to lock the probe. Test that the probe is secure by gently pulling it back and forth.",
"jog_controls_adjustment": "Need to make an adjustment?",
"jupyter_notebook": "Jupyter Notebook",
"labware_display_location_text": "Deck Slot {{slot}}",
@@ -70,6 +74,7 @@
"move_to_a1_position": "Move the pipette to line up in the A1 position",
"moving_to_slot_title": "Moving to slot {{slot}}",
"new_labware_offset_data": "New labware offset data",
+ "ninety_six_probe_location": "A1 (back left corner)",
"no_labware_offsets": "No Labware Offset",
"no_offset_data_available": "No labware offset data available",
"no_offset_data_on_robot": "This robot has no useable labware offset data for this run.",
@@ -77,6 +82,7 @@
"offsets": "offsets",
"pick_up_tip_from_rack_in_location": "Pick up tip from tip rack in {{location}}",
"picking_up_tip_title": "Picking up tip in slot {{slot}}",
+ "pipette_nozzle": "pipette nozzle furthest from you",
"place_a_full_tip_rack_in_location": "Place a full {{tip_rack}} into {{location}}",
"place_labware_in_adapter_in_location": "Place a {{adapter}} followed by a {{labware}} into {{location}}",
"place_labware_in_location": "Place a {{labware}} into {{location}}",
@@ -85,6 +91,9 @@
"position_check_description": "Labware Position Check is a guided workflow that checks every labware on the deck for an added degree of precision in your protocol.When you check a labware, the OT-2’s pipette nozzle or attached tip will stop at the center of the A1 well. If the pipette nozzle or tip is not centered, you can reveal the OT-2’s jog controls to make an adjustment. This Labware Offset will be applied to the entire labware. Offset data is measured to the nearest 1/10th mm and can be made in the X, Y and/or Z directions.",
"prepare_item_in_location": "Prepare {{item}} in {{location}}",
"primary_pipette_tipracks_section": "Check tip racks with {{primary_mount}} Pipette and pick up a tip",
+ "remove_calibration_probe": "Remove calibration probe",
+ "remove_probe": "Unlock the calibraiton probe, remove it from the nozzle, and return it to its storage location.",
+ "remove_probe_before_exit": "Remove the calibration probe before exiting",
"return_tip_rack_to_location": "Return tip rack to {{location}}",
"return_tip_section": "Return tip",
"returning_tip_title": "Returning tip in slot {{slot}}",
diff --git a/app/src/organisms/LabwarePositionCheck/AttachProbe.tsx b/app/src/organisms/LabwarePositionCheck/AttachProbe.tsx
index 5381c23aed5..f54032e48f1 100644
--- a/app/src/organisms/LabwarePositionCheck/AttachProbe.tsx
+++ b/app/src/organisms/LabwarePositionCheck/AttachProbe.tsx
@@ -1,15 +1,18 @@
import * as React from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { RESPONSIVENESS, SPACING, TYPOGRAPHY } from '@opentrons/components'
-import { css } from 'styled-components'
-import { StyledText } from '../../atoms/text'
-import { RobotMotionLoader } from './RobotMotionLoader'
+import { useInstrumentsQuery } from '@opentrons/react-api-client'
import {
CompletedProtocolAnalysis,
getPipetteNameSpecs,
} from '@opentrons/shared-data'
+import { css } from 'styled-components'
+import { StyledText } from '../../atoms/text'
+import { ProbeNotAttached } from '../PipetteWizardFlows/ProbeNotAttached'
+import { RobotMotionLoader } from './RobotMotionLoader'
import attachProbe1 from '../../assets/videos/pipette-wizard-flows/Pipette_Attach_Probe_1.webm'
import attachProbe8 from '../../assets/videos/pipette-wizard-flows/Pipette_Attach_Probe_8.webm'
+import attachProbe96 from '../../assets/videos/pipette-wizard-flows/Pipette_Attach_Probe_96.webm'
import { useChainRunCommands } from '../../resources/runs/hooks'
import { GenericWizardTile } from '../../molecules/GenericWizardTile'
@@ -19,7 +22,7 @@ import type {
RegisterPositionAction,
WorkingOffset,
} from './types'
-import type { LabwareOffset } from '@opentrons/api-client'
+import type { LabwareOffset, PipetteData } from '@opentrons/api-client'
interface AttachProbeProps extends AttachProbeStep {
protocolData: CompletedProtocolAnalysis
@@ -31,7 +34,9 @@ interface AttachProbeProps extends AttachProbeStep {
existingOffsets: LabwareOffset[]
handleJog: Jog
isRobotMoving: boolean
+ isOnDevice: boolean
}
+
export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => {
const { t, i18n } = useTranslation(['labware_position_check', 'shared'])
const {
@@ -41,43 +46,96 @@ export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => {
chainRunCommands,
isRobotMoving,
setFatalError,
+ isOnDevice,
} = props
+ const [isPending, setIsPending] = React.useState(false)
+ const [showUnableToDetect, setShowUnableToDetect] = React.useState(
+ false
+ )
const pipette = protocolData.pipettes.find(p => p.id === pipetteId)
const pipetteName = pipette?.pipetteName
const pipetteChannels =
pipetteName != null ? getPipetteNameSpecs(pipetteName)?.channels ?? 1 : 1
- const pipetteMount = pipette?.mount
- if (pipetteName == null || pipetteMount == null) return null
+ let probeVideoSrc = attachProbe1
+ let probeLocation = ''
+ if (pipetteChannels === 8) {
+ probeLocation = t('backmost')
+ probeVideoSrc = attachProbe8
+ } else if (pipetteChannels === 96) {
+ probeLocation = t('ninety_six_probe_location')
+ probeVideoSrc = attachProbe96
+ }
- const pipetteZMotorAxis: 'leftZ' | 'rightZ' =
- pipetteMount === 'left' ? 'leftZ' : 'rightZ'
+ const pipetteMount = pipette?.mount
+ const { refetch, data: attachedInstrumentsData } = useInstrumentsQuery({
+ enabled: false,
+ onSettled: () => {
+ setIsPending(false)
+ },
+ })
+ const attachedPipette = attachedInstrumentsData?.data.find(
+ (instrument): instrument is PipetteData =>
+ instrument.ok && instrument.mount === pipetteMount
+ )
- const handleProbeAttached = (): void => {
+ React.useEffect(() => {
+ // move into correct position for probe attach on mount
chainRunCommands(
[
{
- commandType: 'retractAxis' as const,
+ commandType: 'calibration/moveToMaintenancePosition' as const,
params: {
- axis: pipetteZMotorAxis,
+ mount: pipetteMount ?? 'left',
},
},
- {
- commandType: 'retractAxis' as const,
- params: { axis: 'x' },
- },
- {
- commandType: 'retractAxis' as const,
- params: { axis: 'y' },
- },
],
false
- )
- .then(() => proceed())
- .catch((e: Error) => {
- setFatalError(
- `AttachProbe failed to move to safe location after probe attach with message: ${e.message}`
- )
+ ).catch(error => setFatalError(error.message))
+ }, [])
+
+ if (pipetteName == null || pipetteMount == null) return null
+
+ const pipetteZMotorAxis: 'leftZ' | 'rightZ' =
+ pipetteMount === 'left' ? 'leftZ' : 'rightZ'
+
+ const handleProbeAttached = (): void => {
+ setIsPending(true)
+ refetch()
+ .then(() => {
+ if (attachedPipette?.state?.tipDetected) {
+ chainRunCommands(
+ [
+ { commandType: 'home', params: { axes: [pipetteZMotorAxis] } },
+ {
+ commandType: 'retractAxis' as const,
+ params: {
+ axis: pipetteZMotorAxis,
+ },
+ },
+ {
+ commandType: 'retractAxis' as const,
+ params: { axis: 'x' },
+ },
+ {
+ commandType: 'retractAxis' as const,
+ params: { axis: 'y' },
+ },
+ ],
+ false
+ )
+ .then(() => proceed())
+ .catch((e: Error) => {
+ setFatalError(
+ `AttachProbe failed to move to safe location after probe attach with message: ${e.message}`
+ )
+ })
+ } else {
+ setShowUnableToDetect(true)
+ }
+ })
+ .catch(error => {
+ setFatalError(error.message)
})
}
@@ -85,11 +143,19 @@ export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => {
return (
)
+ else if (showUnableToDetect)
+ return (
+
+ )
return (
{
loop={true}
controls={false}
>
-