From 58b1cb23d97cae8f335c029a7e7437439b352747 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Fri, 30 Aug 2024 10:12:00 -0400 Subject: [PATCH 1/4] refactor(app): pipettewizard: separate logic Get the steps payload separate from the logic that decides which payload to return so we have a human hope of comprehending said logic --- .../getPipetteWizardStepsForProtocol.ts | 746 +++++++++--------- 1 file changed, 385 insertions(+), 361 deletions(-) diff --git a/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts b/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts index 064b65b7f95..47dc2a4fd27 100644 --- a/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts +++ b/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts @@ -5,6 +5,382 @@ import type { Mount } from '../../redux/pipettes/types' import type { AttachedPipettesFromInstrumentsQuery } from '../Devices/hooks' import type { PipetteWizardStep } from './types' +const calibrateAlreadyAttachedPipetteOn = ( + mount: Mount +): PipetteWizardStep[] => [ + { + section: SECTIONS.BEFORE_BEGINNING, + mount, + flowType: FLOWS.CALIBRATE, + }, + { + section: SECTIONS.ATTACH_PROBE, + mount, + flowType: FLOWS.CALIBRATE, + }, + { + section: SECTIONS.DETACH_PROBE, + mount, + flowType: FLOWS.CALIBRATE, + }, + { section: SECTIONS.RESULTS, mount, flowType: FLOWS.CALIBRATE }, +] + +const detachNinetySixAndAttachSingleMountOn = ( + mount: Mount +): PipetteWizardStep[] => [ + { + section: SECTIONS.BEFORE_BEGINNING, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.DETACH_PIPETTE, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.MOUNTING_PLATE, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.CARRIAGE, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.RESULTS, + mount: LEFT, + flowType: FLOWS.DETACH, + nextMount: mount, + }, + { + section: SECTIONS.MOUNT_PIPETTE, + mount, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.FIRMWARE_UPDATE, + mount, + flowType: FLOWS.ATTACH, + }, + { section: SECTIONS.RESULTS, mount, flowType: FLOWS.ATTACH }, + { + section: SECTIONS.ATTACH_PROBE, + mount, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.DETACH_PROBE, + mount, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.RESULTS, + mount, + flowType: FLOWS.CALIBRATE, + }, +] + +const detachSingleMountAndAttachSingleMountOn = ( + mount: Mount +): PipetteWizardStep[] => [ + { + section: SECTIONS.BEFORE_BEGINNING, + mount, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.DETACH_PIPETTE, + mount, + flowType: FLOWS.DETACH, + }, + { section: SECTIONS.RESULTS, mount, flowType: FLOWS.DETACH }, + { + section: SECTIONS.MOUNT_PIPETTE, + mount, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.FIRMWARE_UPDATE, + mount, + flowType: FLOWS.ATTACH, + }, + { section: SECTIONS.RESULTS, mount, flowType: FLOWS.ATTACH }, + { + section: SECTIONS.ATTACH_PROBE, + mount, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.DETACH_PROBE, + mount, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.RESULTS, + mount, + flowType: FLOWS.CALIBRATE, + }, +] + +const detachTwoSingleMountsAndAttachNinetySix = (): PipetteWizardStep[] => [ + { + section: SECTIONS.BEFORE_BEGINNING, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.DETACH_PIPETTE, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.RESULTS, + mount: LEFT, + flowType: FLOWS.DETACH, + nextMount: RIGHT, + }, + { + section: SECTIONS.DETACH_PIPETTE, + mount: RIGHT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.RESULTS, + mount: RIGHT, + flowType: FLOWS.DETACH, + nextMount: 'both', + }, + { + section: SECTIONS.CARRIAGE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.MOUNTING_PLATE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.MOUNT_PIPETTE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.FIRMWARE_UPDATE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { section: SECTIONS.RESULTS, mount: LEFT, flowType: FLOWS.ATTACH }, + { + section: SECTIONS.ATTACH_PROBE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.DETACH_PROBE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.RESULTS, + mount: LEFT, + flowType: FLOWS.CALIBRATE, + }, +] + +const detachSingleMountOnLeftAndAttachNinetySix = (): PipetteWizardStep[] => [ + { + section: SECTIONS.BEFORE_BEGINNING, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.DETACH_PIPETTE, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.RESULTS, + mount: LEFT, + flowType: FLOWS.DETACH, + nextMount: 'both', + }, + { + section: SECTIONS.CARRIAGE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.MOUNTING_PLATE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.MOUNT_PIPETTE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.FIRMWARE_UPDATE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { section: SECTIONS.RESULTS, mount: LEFT, flowType: FLOWS.ATTACH }, + { + section: SECTIONS.ATTACH_PROBE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.DETACH_PROBE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.RESULTS, + mount: LEFT, + flowType: FLOWS.CALIBRATE, + }, +] + +const detachSingleMountOnRightAndAttachNinetySix = (): PipetteWizardStep[] => [ + { + section: SECTIONS.BEFORE_BEGINNING, + mount: RIGHT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.DETACH_PIPETTE, + mount: RIGHT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.RESULTS, + mount: RIGHT, + flowType: FLOWS.DETACH, + nextMount: 'both', + }, + { + section: SECTIONS.CARRIAGE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.MOUNTING_PLATE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.MOUNT_PIPETTE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.FIRMWARE_UPDATE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { section: SECTIONS.RESULTS, mount: LEFT, flowType: FLOWS.ATTACH }, + { + section: SECTIONS.ATTACH_PROBE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.DETACH_PROBE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.RESULTS, + mount: LEFT, + flowType: FLOWS.CALIBRATE, + }, +] + +const fromEmptyGantryAttachNinetySix = (): PipetteWizardStep[] => [ + { + section: SECTIONS.BEFORE_BEGINNING, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.CARRIAGE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.MOUNTING_PLATE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.MOUNT_PIPETTE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.FIRMWARE_UPDATE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { section: SECTIONS.RESULTS, mount: LEFT, flowType: FLOWS.ATTACH }, + { + section: SECTIONS.ATTACH_PROBE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.DETACH_PROBE, + mount: LEFT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.RESULTS, + mount: LEFT, + flowType: FLOWS.CALIBRATE, + }, +] + +const fromEmptyGantryAttachSingleMountOn = ( + mount: Mount +): PipetteWizardStep[] => [ + { + section: SECTIONS.BEFORE_BEGINNING, + mount, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.MOUNT_PIPETTE, + mount, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.FIRMWARE_UPDATE, + mount, + flowType: FLOWS.ATTACH, + }, + { section: SECTIONS.RESULTS, mount, flowType: FLOWS.ATTACH }, + { + section: SECTIONS.ATTACH_PROBE, + mount, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.DETACH_PROBE, + mount, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.RESULTS, + mount, + flowType: FLOWS.CALIBRATE, + }, +] + export const getPipetteWizardStepsForProtocol = ( attachedPipettes: AttachedPipettesFromInstrumentsQuery, pipetteInfo: LoadedPipette[], @@ -13,7 +389,6 @@ export const getPipetteWizardStepsForProtocol = ( const requiredPipette = pipetteInfo.find(pipette => pipette.mount === mount) const nintySixChannelAttached = attachedPipettes[LEFT]?.instrumentName === 'p1000_96' - // return empty array if no pipette is required in the protocol if (requiredPipette == null) { return null @@ -21,125 +396,18 @@ export const getPipetteWizardStepsForProtocol = ( } else if ( requiredPipette?.pipetteName === attachedPipettes[mount]?.instrumentName ) { - return [ - { - section: SECTIONS.BEFORE_BEGINNING, - mount, - flowType: FLOWS.CALIBRATE, - }, - { - section: SECTIONS.ATTACH_PROBE, - mount, - flowType: FLOWS.CALIBRATE, - }, - { - section: SECTIONS.DETACH_PROBE, - mount, - flowType: FLOWS.CALIBRATE, - }, - { section: SECTIONS.RESULTS, mount, flowType: FLOWS.CALIBRATE }, - ] + return calibrateAlreadyAttachedPipetteOn(mount) } else if ( requiredPipette.pipetteName !== 'p1000_96' && attachedPipettes[mount] != null ) { // 96-channel pipette attached and need to attach single mount pipette + if (nintySixChannelAttached) { - return [ - { - section: SECTIONS.BEFORE_BEGINNING, - mount: LEFT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.DETACH_PIPETTE, - mount: LEFT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.MOUNTING_PLATE, - mount: LEFT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.CARRIAGE, - mount: LEFT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.RESULTS, - mount: LEFT, - flowType: FLOWS.DETACH, - nextMount: mount, - }, - { - section: SECTIONS.MOUNT_PIPETTE, - mount, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.FIRMWARE_UPDATE, - mount, - flowType: FLOWS.ATTACH, - }, - { section: SECTIONS.RESULTS, mount, flowType: FLOWS.ATTACH }, - { - section: SECTIONS.ATTACH_PROBE, - mount, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.DETACH_PROBE, - mount, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.RESULTS, - mount, - flowType: FLOWS.CALIBRATE, - }, - ] + return detachNinetySixAndAttachSingleMountOn(mount) // Single mount pipette attached and need to attach new single mount pipette } else { - return [ - { - section: SECTIONS.BEFORE_BEGINNING, - mount, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.DETACH_PIPETTE, - mount, - flowType: FLOWS.DETACH, - }, - { section: SECTIONS.RESULTS, mount, flowType: FLOWS.DETACH }, - { - section: SECTIONS.MOUNT_PIPETTE, - mount, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.FIRMWARE_UPDATE, - mount, - flowType: FLOWS.ATTACH, - }, - { section: SECTIONS.RESULTS, mount, flowType: FLOWS.ATTACH }, - { - section: SECTIONS.ATTACH_PROBE, - mount, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.DETACH_PROBE, - mount, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.RESULTS, - mount, - flowType: FLOWS.CALIBRATE, - }, - ] + return detachSingleMountAndAttachSingleMountOn(mount) } // Single mount pipette attached to both mounts and need to attach 96-channel pipette } else if ( @@ -147,273 +415,29 @@ export const getPipetteWizardStepsForProtocol = ( attachedPipettes[LEFT] != null && attachedPipettes[RIGHT] != null ) { - return [ - { - section: SECTIONS.BEFORE_BEGINNING, - mount: LEFT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.DETACH_PIPETTE, - mount: LEFT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.RESULTS, - mount: LEFT, - flowType: FLOWS.DETACH, - nextMount: RIGHT, - }, - { - section: SECTIONS.DETACH_PIPETTE, - mount: RIGHT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.RESULTS, - mount: RIGHT, - flowType: FLOWS.DETACH, - nextMount: 'both', - }, - { - section: SECTIONS.CARRIAGE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.MOUNTING_PLATE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.MOUNT_PIPETTE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.FIRMWARE_UPDATE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { section: SECTIONS.RESULTS, mount: LEFT, flowType: FLOWS.ATTACH }, - { - section: SECTIONS.ATTACH_PROBE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.DETACH_PROBE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.RESULTS, - mount: LEFT, - flowType: FLOWS.CALIBRATE, - }, - ] + return detachTwoSingleMountsAndAttachNinetySix() // Single mount pipette attached to left mount and need to attach 96-channel pipette } else if ( requiredPipette.pipetteName === 'p1000_96' && attachedPipettes[LEFT] != null && attachedPipettes[RIGHT] == null ) { - return [ - { - section: SECTIONS.BEFORE_BEGINNING, - mount: LEFT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.DETACH_PIPETTE, - mount: LEFT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.RESULTS, - mount: LEFT, - flowType: FLOWS.DETACH, - nextMount: 'both', - }, - { - section: SECTIONS.CARRIAGE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.MOUNTING_PLATE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.MOUNT_PIPETTE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.FIRMWARE_UPDATE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { section: SECTIONS.RESULTS, mount: LEFT, flowType: FLOWS.ATTACH }, - { - section: SECTIONS.ATTACH_PROBE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.DETACH_PROBE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.RESULTS, - mount: LEFT, - flowType: FLOWS.CALIBRATE, - }, - ] + return detachSingleMountOnLeftAndAttachNinetySix() // Single mount pipette attached to right mount and need to attach 96-channel pipette } else if ( requiredPipette.pipetteName === 'p1000_96' && attachedPipettes[LEFT] == null && attachedPipettes[RIGHT] != null ) { - return [ - { - section: SECTIONS.BEFORE_BEGINNING, - mount: RIGHT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.DETACH_PIPETTE, - mount: RIGHT, - flowType: FLOWS.DETACH, - }, - { - section: SECTIONS.RESULTS, - mount: RIGHT, - flowType: FLOWS.DETACH, - nextMount: 'both', - }, - { - section: SECTIONS.CARRIAGE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.MOUNTING_PLATE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.MOUNT_PIPETTE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.FIRMWARE_UPDATE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { section: SECTIONS.RESULTS, mount: LEFT, flowType: FLOWS.ATTACH }, - { - section: SECTIONS.ATTACH_PROBE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.DETACH_PROBE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.RESULTS, - mount: LEFT, - flowType: FLOWS.CALIBRATE, - }, - ] + return detachSingleMountOnRightAndAttachNinetySix() // if no pipette is attached to gantry } else { // Gantry empty and need to attach 96-channel pipette if (requiredPipette.pipetteName === 'p1000_96') { - return [ - { - section: SECTIONS.BEFORE_BEGINNING, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.CARRIAGE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.MOUNTING_PLATE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.MOUNT_PIPETTE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.FIRMWARE_UPDATE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { section: SECTIONS.RESULTS, mount: LEFT, flowType: FLOWS.ATTACH }, - { - section: SECTIONS.ATTACH_PROBE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.DETACH_PROBE, - mount: LEFT, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.RESULTS, - mount: LEFT, - flowType: FLOWS.CALIBRATE, - }, - ] + return fromEmptyGantryAttachNinetySix() // Gantry empty and need to attach single mount pipette } else { - return [ - { - section: SECTIONS.BEFORE_BEGINNING, - mount, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.MOUNT_PIPETTE, - mount, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.FIRMWARE_UPDATE, - mount, - flowType: FLOWS.ATTACH, - }, - { section: SECTIONS.RESULTS, mount, flowType: FLOWS.ATTACH }, - { - section: SECTIONS.ATTACH_PROBE, - mount, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.DETACH_PROBE, - mount, - flowType: FLOWS.ATTACH, - }, - { - section: SECTIONS.RESULTS, - mount, - flowType: FLOWS.CALIBRATE, - }, - ] + return fromEmptyGantryAttachSingleMountOn(mount) } } } From 2361bca2f860a7cd165d75fabce286e488449108 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Fri, 30 Aug 2024 10:33:59 -0400 Subject: [PATCH 2/4] refactor(app): pipettewizard: add a lovely table This table illustrates the reason behind the issue to solve in the next commit, renames a function since it had a bad name, and will serve as docs for later use. --- .../getPipetteWizardStepsForProtocol.ts | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts b/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts index 47dc2a4fd27..40786101e6b 100644 --- a/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts +++ b/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts @@ -345,7 +345,7 @@ const fromEmptyGantryAttachNinetySix = (): PipetteWizardStep[] => [ }, ] -const fromEmptyGantryAttachSingleMountOn = ( +const fromEmptyMountAttachSingleMountOn = ( mount: Mount ): PipetteWizardStep[] => [ { @@ -380,6 +380,31 @@ const fromEmptyGantryAttachSingleMountOn = ( flowType: FLOWS.CALIBRATE, }, ] +/** ++-------------+-----------------------------------------------+----------------------------------------------+-----------------------------------------------+ +| |96 |left |right | +| | | | | +| | | | | ++-------------+-----------------------------------------------+----------------------------------------------+-----------------------------------------------+ +| 96 | calibrateAlreadyAttachedPipetteOn(left) | detachNinetySixAndAttachSingleMountOn(left) | X1 | ++-------------+-----------------------------------------------+----------------------------------------------+-----------------------------------------------+ +| | | calibrateAlreadyAttachedPipetteOn(left) or | fromEmptyMountAttachSingleMountOn(right) | +| left only | detachSingleMountOnLeftAndAttachNinetySix() | detachSingleMountAndAttachSingleMountOn(left)| | +| | | | | ++-------------+-----------------------------------------------+----------------------------------------------+-----------------------------------------------+ +| | | | calibrateAlreadyAttachedPipetteOn(right) or | +| right only | detachSingleMountOnRightAndAttachNinetySix() |fromEmptyMountAttachSingleMountOn(left) | detachSingleMountAndAttachSingleMountOn(right)| +| | | | | ++-------------+-----------------------------------------------+----------------------------------------------+-----------------------------------------------+ +| left and | | calibrateAlreadyAttachedPipetteOn(left) or | calibrateAlreadyAttachedPipetteOn(right) or | +| right | detachTwoSingleMountsAndAttachNinetySix() | detachSingleMountAndAttachSingleMountOn(left)| detachSingleMountAndAttachSingleMountOn(right)| +| | | | | ++-------------+-----------------------------------------------+----------------------------------------------+-----------------------------------------------+ +| | | | | +| nothing | fromEmptyGantryAttachNinetySix() | fromEmptyMountAttachSingleMountOn(left) | fromEmptyMountAttachSingleMountOn(right) | +| | | | | ++-------------+-----------------------------------------------+----------------------------------------------+-----------------------------------------------+ + **/ export const getPipetteWizardStepsForProtocol = ( attachedPipettes: AttachedPipettesFromInstrumentsQuery, @@ -401,8 +426,9 @@ export const getPipetteWizardStepsForProtocol = ( requiredPipette.pipetteName !== 'p1000_96' && attachedPipettes[mount] != null ) { - // 96-channel pipette attached and need to attach single mount pipette - + // 96-channel pipette attached and need to attach single mount pipette + // X1: this check can only be reached if mount is LEFT, because if mount is RIGHT + // then the 96 won't show up in attached pipettes if (nintySixChannelAttached) { return detachNinetySixAndAttachSingleMountOn(mount) // Single mount pipette attached and need to attach new single mount pipette @@ -437,7 +463,7 @@ export const getPipetteWizardStepsForProtocol = ( return fromEmptyGantryAttachNinetySix() // Gantry empty and need to attach single mount pipette } else { - return fromEmptyGantryAttachSingleMountOn(mount) + return fromEmptyMountAttachSingleMountOn(mount) } } } From 7108e8e6468f1dbf8d09c7b9fe333eb2a1543b12 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Fri, 30 Aug 2024 10:42:44 -0400 Subject: [PATCH 3/4] fix(app): handle detaching 96 when attaching right When we're switching pipettes from a preprotocol flow, we were not properly handling the case where a 96 is attached and we want to take it off and attach a pipette to the right mount. Add handling for that. Closes RQA-3123 --- .../getPipetteWizardStepsForProtocol.test.tsx | 68 ++++++++++++++++++- .../getPipetteWizardStepsForProtocol.ts | 52 +++++++------- 2 files changed, 91 insertions(+), 29 deletions(-) diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/getPipetteWizardStepsForProtocol.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/getPipetteWizardStepsForProtocol.test.tsx index 1fc4ea2464e..aa4ad467729 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/getPipetteWizardStepsForProtocol.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/getPipetteWizardStepsForProtocol.test.tsx @@ -17,6 +17,9 @@ const mockPipetteInfo = [ const mockPipettesInProtocolNotEmpty = [ { id: '123', pipetteName: 'p1000_single_flex', mount: 'left' }, ] +const mockPipettesInProtocolOnRight = [ + { id: '123', pipetteName: 'p1000_single_flex', mount: 'right' }, +] const mockPipettesInProtocolMulti = [ { id: '123', pipetteName: 'p1000_multi_flex', mount: 'left' }, ] @@ -113,7 +116,7 @@ describe('getPipetteWizardStepsForProtocol', () => { ).toStrictEqual(mockFlowSteps) }) - it('returns the correct array of info when the attached 96-channel pipette needs to be switched out for single mount', () => { + it('returns the correct array of info when the attached 96-channel pipette needs to be switched out for single mount on left', () => { const mockFlowSteps = [ { section: SECTIONS.BEFORE_BEGINNING, @@ -176,6 +179,69 @@ describe('getPipetteWizardStepsForProtocol', () => { ) ).toStrictEqual(mockFlowSteps) }) + it('returns the correct array of info when the attached 96-channel pipette needs to be switched out for single mount on right', () => { + const mockFlowSteps = [ + { + section: SECTIONS.BEFORE_BEGINNING, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.DETACH_PIPETTE, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.MOUNTING_PLATE, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.CARRIAGE, + mount: LEFT, + flowType: FLOWS.DETACH, + }, + { + section: SECTIONS.RESULTS, + mount: LEFT, + flowType: FLOWS.DETACH, + nextMount: RIGHT, + }, + { + section: SECTIONS.MOUNT_PIPETTE, + mount: RIGHT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.FIRMWARE_UPDATE, + mount: RIGHT, + flowType: FLOWS.ATTACH, + }, + { section: SECTIONS.RESULTS, mount: RIGHT, flowType: FLOWS.ATTACH }, + { + section: SECTIONS.ATTACH_PROBE, + mount: RIGHT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.DETACH_PROBE, + mount: RIGHT, + flowType: FLOWS.ATTACH, + }, + { + section: SECTIONS.RESULTS, + mount: RIGHT, + flowType: FLOWS.CALIBRATE, + }, + ] as PipetteWizardStep[] + expect( + getPipetteWizardStepsForProtocol( + { left: mock96ChannelAttachedPipetteInformation, right: null }, + mockPipettesInProtocolOnRight as any, + RIGHT + ) + ).toStrictEqual(mockFlowSteps) + }) it('returns the correct array of info when the attached pipette on left mount needs to be switched out for 96-channel', () => { const mockFlowSteps = [ { diff --git a/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts b/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts index 40786101e6b..c5fa4739161 100644 --- a/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts +++ b/app/src/organisms/PipetteWizardFlows/getPipetteWizardStepsForProtocol.ts @@ -382,11 +382,11 @@ const fromEmptyMountAttachSingleMountOn = ( ] /** +-------------+-----------------------------------------------+----------------------------------------------+-----------------------------------------------+ -| |96 |left |right | -| | | | | +| requested > |96 |left |right | | | | | | +| v attached | | | | +-------------+-----------------------------------------------+----------------------------------------------+-----------------------------------------------+ -| 96 | calibrateAlreadyAttachedPipetteOn(left) | detachNinetySixAndAttachSingleMountOn(left) | X1 | +| 96 | calibrateAlreadyAttachedPipetteOn(left) | detachNinetySixAndAttachSingleMountOn(left) | detachNinetySixAndAttachSingleMountOn(right) | +-------------+-----------------------------------------------+----------------------------------------------+-----------------------------------------------+ | | | calibrateAlreadyAttachedPipetteOn(left) or | fromEmptyMountAttachSingleMountOn(right) | | left only | detachSingleMountOnLeftAndAttachNinetySix() | detachSingleMountAndAttachSingleMountOn(left)| | @@ -412,57 +412,53 @@ export const getPipetteWizardStepsForProtocol = ( mount: Mount ): PipetteWizardStep[] | null => { const requiredPipette = pipetteInfo.find(pipette => pipette.mount === mount) - const nintySixChannelAttached = + const ninetySixChannelAttached = attachedPipettes[LEFT]?.instrumentName === 'p1000_96' - // return empty array if no pipette is required in the protocol + const ninetySixChannelRequested = requiredPipette?.pipetteName === 'p1000_96' + if (requiredPipette == null) { + // return empty array if no pipette is required in the protocol return null - // return calibration flow if correct pipette is attached } else if ( requiredPipette?.pipetteName === attachedPipettes[mount]?.instrumentName ) { + // return calibration flow if correct pipette is attached return calibrateAlreadyAttachedPipetteOn(mount) - } else if ( - requiredPipette.pipetteName !== 'p1000_96' && - attachedPipettes[mount] != null - ) { + } else if (!ninetySixChannelRequested && ninetySixChannelAttached) { // 96-channel pipette attached and need to attach single mount pipette - // X1: this check can only be reached if mount is LEFT, because if mount is RIGHT - // then the 96 won't show up in attached pipettes - if (nintySixChannelAttached) { - return detachNinetySixAndAttachSingleMountOn(mount) - // Single mount pipette attached and need to attach new single mount pipette - } else { - return detachSingleMountAndAttachSingleMountOn(mount) - } - // Single mount pipette attached to both mounts and need to attach 96-channel pipette + return detachNinetySixAndAttachSingleMountOn(mount) + } else if (!ninetySixChannelRequested && attachedPipettes[mount] != null) { + // Single mount pipette attached and need to attach new single mount pipette + return detachSingleMountAndAttachSingleMountOn(mount) } else if ( - requiredPipette.pipetteName === 'p1000_96' && + ninetySixChannelRequested && attachedPipettes[LEFT] != null && attachedPipettes[RIGHT] != null ) { + // Single mount pipette attached to both mounts and need to attach 96-channel pipette return detachTwoSingleMountsAndAttachNinetySix() - // Single mount pipette attached to left mount and need to attach 96-channel pipette } else if ( - requiredPipette.pipetteName === 'p1000_96' && + ninetySixChannelRequested && attachedPipettes[LEFT] != null && attachedPipettes[RIGHT] == null ) { + // Single mount pipette attached to left mount and need to attach 96-channel pipette return detachSingleMountOnLeftAndAttachNinetySix() - // Single mount pipette attached to right mount and need to attach 96-channel pipette } else if ( - requiredPipette.pipetteName === 'p1000_96' && + ninetySixChannelRequested && attachedPipettes[LEFT] == null && attachedPipettes[RIGHT] != null ) { + // Single mount pipette attached to right mount and need to attach 96-channel pipette return detachSingleMountOnRightAndAttachNinetySix() - // if no pipette is attached to gantry } else { - // Gantry empty and need to attach 96-channel pipette - if (requiredPipette.pipetteName === 'p1000_96') { + // if no pipette is attached to gantry + + if (ninetySixChannelRequested) { + // Gantry empty and need to attach 96-channel pipette return fromEmptyGantryAttachNinetySix() - // Gantry empty and need to attach single mount pipette } else { + // Gantry empty and need to attach single mount pipette return fromEmptyMountAttachSingleMountOn(mount) } } From 46d6295964f575c65e5a3cccfff5ae2f48743651 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Fri, 30 Aug 2024 12:09:49 -0400 Subject: [PATCH 4/4] fix(api): move axes separately in movetomaint I don't know why this would stop working now, but something we did makes the right mount move faster than the left mount when we're moving to the maintenance position to detach the 96 mount plate, so it trips over its own feet. --- .../commands/calibration/move_to_maintenance_position.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api/src/opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py b/api/src/opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py index 81d9e30d1cc..168ade95a2e 100644 --- a/api/src/opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +++ b/api/src/opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py @@ -108,10 +108,15 @@ async def execute( await ot3_api.move_axes( { Axis.Z_L: max_motion_range + _LEFT_MOUNT_Z_MARGIN, + } + ) + await ot3_api.disengage_axes([Axis.Z_L]) + await ot3_api.move_axes( + { Axis.Z_R: max_motion_range + _RIGHT_MOUNT_Z_MARGIN, } ) - await ot3_api.disengage_axes([Axis.Z_L, Axis.Z_R]) + await ot3_api.disengage_axes([Axis.Z_R]) return SuccessData(public=MoveToMaintenancePositionResult(), private=None)