From 562bad77637eaf9930d412af92b9a2261121b7ff Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Mon, 18 Dec 2023 15:30:06 -0500 Subject: [PATCH 01/12] remove StyledDeck in favor of OT-2 DeckFromLayers --- .../hardware-sim/Deck/MoveLabwareOnDeck.tsx | 2 - .../src/hardware-sim/Deck/RobotWorkSpace.tsx | 15 +---- .../src/hardware-sim/Deck/StyledDeck.tsx | 61 ------------------- 3 files changed, 2 insertions(+), 76 deletions(-) delete mode 100644 components/src/hardware-sim/Deck/StyledDeck.tsx diff --git a/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx b/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx index 8f629cc5695..5aa94680cb4 100644 --- a/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx +++ b/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx @@ -24,7 +24,6 @@ import type { DeckConfiguration, } from '@opentrons/shared-data' import type { StyleProps } from '../../primitives' -import type { TrashCutoutId } from './FlexTrash' const getModulePosition = ( deckDef: DeckDefinition, @@ -141,7 +140,6 @@ interface MoveLabwareOnDeckProps extends StyleProps { deckConfig: DeckConfiguration backgroundItems?: React.ReactNode deckFill?: string - trashCutoutId?: TrashCutoutId } export function MoveLabwareOnDeck( props: MoveLabwareOnDeckProps diff --git a/components/src/hardware-sim/Deck/RobotWorkSpace.tsx b/components/src/hardware-sim/Deck/RobotWorkSpace.tsx index 3678d1acfea..34c780f569f 100644 --- a/components/src/hardware-sim/Deck/RobotWorkSpace.tsx +++ b/components/src/hardware-sim/Deck/RobotWorkSpace.tsx @@ -1,10 +1,9 @@ import * as React from 'react' import { OT2_ROBOT_TYPE } from '@opentrons/shared-data' import { StyleProps, Svg } from '../../primitives' -import { StyledDeck } from './StyledDeck' +import { DeckFromLayers } from './DeckFromLayers' import type { DeckDefinition, DeckSlot } from '@opentrons/shared-data' -import type { TrashCutoutId } from './FlexTrash' export interface RobotWorkSpaceRenderProps { deckSlotsById: { [slotId: string]: DeckSlot } @@ -18,13 +17,9 @@ export interface RobotWorkSpaceProps extends StyleProps { deckDef?: DeckDefinition viewBox?: string | null children?: (props: RobotWorkSpaceRenderProps) => React.ReactNode - deckFill?: string deckLayerBlocklist?: string[] // optional boolean to show the OT-2 deck from deck defintion layers showDeckLayers?: boolean - // TODO(bh, 2023-10-09): remove - trashCutoutId?: TrashCutoutId - trashColor?: string id?: string } @@ -34,12 +29,9 @@ export function RobotWorkSpace(props: RobotWorkSpaceProps): JSX.Element | null { const { children, deckDef, - deckFill = '#CCCCCC', deckLayerBlocklist = [], showDeckLayers = false, - trashCutoutId, viewBox, - trashColor, id, ...styleProps } = props @@ -85,12 +77,9 @@ export function RobotWorkSpace(props: RobotWorkSpaceProps): JSX.Element | null { {...styleProps} > {showDeckLayers ? ( - ) : null} {children?.({ deckSlotsById, getRobotCoordsFromDOMCoords })} diff --git a/components/src/hardware-sim/Deck/StyledDeck.tsx b/components/src/hardware-sim/Deck/StyledDeck.tsx deleted file mode 100644 index 7ac0130fbc0..00000000000 --- a/components/src/hardware-sim/Deck/StyledDeck.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import * as React from 'react' -import styled from 'styled-components' - -import { DeckFromLayers } from './DeckFromLayers' -import { FlexTrash } from './FlexTrash' - -import type { RobotType } from '@opentrons/shared-data' -import type { DeckFromLayersProps } from './DeckFromLayers' -import type { TrashCutoutId } from './FlexTrash' - -interface StyledDeckProps { - deckFill: string - robotType: RobotType - trashColor?: string - trashCutoutId?: TrashCutoutId -} - -// apply fill to .SLOT_BASE class from ot3_standard deck definition -const StyledG = styled.g>` - .SLOT_BASE { - fill: ${props => props.deckFill}; - } -` - -export function StyledDeck( - props: StyledDeckProps & DeckFromLayersProps -): JSX.Element { - const { - deckFill, - robotType, - trashCutoutId, - trashColor = '#757070', - ...DeckFromLayersProps - } = props - const trashSlotClipId = - trashCutoutId != null ? `SLOT_CLIPS_${trashCutoutId}` : null - - const trashLayerBlocklist = - trashSlotClipId != null - ? DeckFromLayersProps.layerBlocklist.concat(trashSlotClipId) - : DeckFromLayersProps.layerBlocklist - - return ( - - - {/* TODO(bh, 2023-11-06): remove trash and trashCutoutId prop when StyledDeck removed from MoveLabwareOnDeck */} - {trashCutoutId != null ? ( - - ) : null} - - ) -} From d98cbcddccac83c96a86359186e126d3ad92df1b Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Mon, 18 Dec 2023 15:45:16 -0500 Subject: [PATCH 02/12] remove loadFixture command and references --- api-client/src/protocols/utils.ts | 21 ------------------- shared-data/command/types/setup.ts | 19 ----------------- .../protocol/types/schemaV7/command/setup.ts | 18 ---------------- 3 files changed, 58 deletions(-) diff --git a/api-client/src/protocols/utils.ts b/api-client/src/protocols/utils.ts index 2bf8d70b27e..76f120b6c07 100644 --- a/api-client/src/protocols/utils.ts +++ b/api-client/src/protocols/utils.ts @@ -8,7 +8,6 @@ import type { LoadedLabware, LoadedModule, LoadedPipette, - LoadFixtureRunTimeCommand, LoadLabwareRunTimeCommand, LoadLiquidRunTimeCommand, LoadModuleRunTimeCommand, @@ -228,26 +227,6 @@ export function parseInitialLoadedModulesBySlot( ) } -export interface LoadedFixturesBySlot { - [slotName: string]: LoadFixtureRunTimeCommand -} -// TODO(bh, 2023-11-09): remove this util, there will be no loadFixture command -export function parseInitialLoadedFixturesByCutout( - commands: RunTimeCommand[] -): LoadedFixturesBySlot { - const loadFixtureCommandsReversed = commands - .filter( - (command): command is LoadFixtureRunTimeCommand => - command.commandType === 'loadFixture' - ) - .reverse() - return reduce( - loadFixtureCommandsReversed, - (acc, command) => ({ ...acc, [command.params.location.cutout]: command }), - {} - ) -} - export interface LiquidsById { [liquidId: string]: { displayName: string diff --git a/shared-data/command/types/setup.ts b/shared-data/command/types/setup.ts index fa6e3e564b9..960dd8a26d8 100644 --- a/shared-data/command/types/setup.ts +++ b/shared-data/command/types/setup.ts @@ -5,7 +5,6 @@ import type { LabwareOffset, PipetteName, ModuleModel, - Cutout, } from '../../js' export interface LoadPipetteCreateCommand extends CommonCommandCreateInfo { @@ -59,16 +58,6 @@ export interface LoadLiquidRunTimeCommand LoadLiquidCreateCommand { result?: LoadLiquidResult } -// TODO(jr, 10/31/23): update `loadFixture` to `loadAddressableArea` -export interface LoadFixtureCreateCommand extends CommonCommandCreateInfo { - commandType: 'loadFixture' - params: LoadFixtureParams -} -export interface LoadFixtureRunTimeCommand - extends CommonCommandRunTimeInfo, - LoadFixtureCreateCommand { - result?: LoadLabwareResult -} export interface ConfigureNozzleLayoutCreateCommand extends CommonCommandCreateInfo { @@ -86,7 +75,6 @@ export type SetupRunTimeCommand = | ConfigureNozzleLayoutRunTimeCommand | LoadPipetteRunTimeCommand | LoadLabwareRunTimeCommand - | LoadFixtureRunTimeCommand | LoadModuleRunTimeCommand | LoadLiquidRunTimeCommand | MoveLabwareRunTimeCommand @@ -95,7 +83,6 @@ export type SetupCreateCommand = | ConfigureNozzleLayoutCreateCommand | LoadPipetteCreateCommand | LoadLabwareCreateCommand - | LoadFixtureCreateCommand | LoadModuleCreateCommand | LoadLiquidCreateCommand | MoveLabwareCreateCommand @@ -169,12 +156,6 @@ interface LoadLiquidResult { liquidId: string } -interface LoadFixtureParams { - location: { cutout: Cutout } - loadName: string - fixtureId?: string -} - const COLUMN = 'COLUMN' const SINGLE = 'SINGLE' const ROW = 'ROW' diff --git a/shared-data/protocol/types/schemaV7/command/setup.ts b/shared-data/protocol/types/schemaV7/command/setup.ts index e6048ef58c9..84f17313f25 100644 --- a/shared-data/protocol/types/schemaV7/command/setup.ts +++ b/shared-data/protocol/types/schemaV7/command/setup.ts @@ -5,7 +5,6 @@ import type { LabwareOffset, PipetteName, ModuleModel, - Cutout, } from '../../../../js' export interface LoadPipetteCreateCommand extends CommonCommandCreateInfo { @@ -59,20 +58,10 @@ export interface LoadLiquidRunTimeCommand LoadLiquidCreateCommand { result?: LoadLiquidResult } -export interface LoadFixtureCreateCommand extends CommonCommandCreateInfo { - commandType: 'loadFixture' - params: LoadFixtureParams -} -export interface LoadFixtureRunTimeCommand - extends CommonCommandRunTimeInfo, - LoadFixtureCreateCommand { - result?: LoadLabwareResult -} export type SetupRunTimeCommand = | LoadPipetteRunTimeCommand | LoadLabwareRunTimeCommand - | LoadFixtureRunTimeCommand | LoadModuleRunTimeCommand | LoadLiquidRunTimeCommand | MoveLabwareRunTimeCommand @@ -80,7 +69,6 @@ export type SetupRunTimeCommand = export type SetupCreateCommand = | LoadPipetteCreateCommand | LoadLabwareCreateCommand - | LoadFixtureCreateCommand | LoadModuleCreateCommand | LoadLiquidCreateCommand | MoveLabwareCreateCommand @@ -150,9 +138,3 @@ interface LoadLiquidParams { interface LoadLiquidResult { liquidId: string } - -interface LoadFixtureParams { - location: { cutout: Cutout } - loadName: string - fixtureId?: string -} From e9e750df40e446d7d82420c5df02d5ced6928126 Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Mon, 18 Dec 2023 15:59:26 -0500 Subject: [PATCH 03/12] remove unused useLabwareRenderInfoForRunById --- .../LiquidsLabwareDetailsModal.test.tsx | 9 - .../useLabwareRenderInfoForRunById.test.tsx | 160 ------------------ app/src/organisms/Devices/hooks/index.ts | 1 - .../hooks/useLabwareRenderInfoForRunById.ts | 19 --- 4 files changed, 189 deletions(-) delete mode 100644 app/src/organisms/Devices/hooks/__tests__/useLabwareRenderInfoForRunById.test.tsx delete mode 100644 app/src/organisms/Devices/hooks/useLabwareRenderInfoForRunById.ts diff --git a/app/src/organisms/Devices/ProtocolRun/SetupLiquids/__tests__/LiquidsLabwareDetailsModal.test.tsx b/app/src/organisms/Devices/ProtocolRun/SetupLiquids/__tests__/LiquidsLabwareDetailsModal.test.tsx index 77a1500f575..fe87a2a6f59 100644 --- a/app/src/organisms/Devices/ProtocolRun/SetupLiquids/__tests__/LiquidsLabwareDetailsModal.test.tsx +++ b/app/src/organisms/Devices/ProtocolRun/SetupLiquids/__tests__/LiquidsLabwareDetailsModal.test.tsx @@ -9,7 +9,6 @@ import { } from '@opentrons/components' import { parseLiquidsInLoadOrder } from '@opentrons/api-client' import { getIsOnDevice } from '../../../../../redux/config' -import { useLabwareRenderInfoForRunById } from '../../../../Devices/hooks' import { useMostRecentCompletedAnalysis } from '../../../../LabwarePositionCheck/useMostRecentCompletedAnalysis' import { mockDefinition } from '../../../../../redux/custom-labware/__fixtures__' import { getLocationInfoNames } from '../../utils/getLocationInfoNames' @@ -57,9 +56,6 @@ const mockLabwareRender = LabwareRender as jest.MockedFunction< const mockGetWellFillFromLabwareId = getWellFillFromLabwareId as jest.MockedFunction< typeof getWellFillFromLabwareId > -const mockUseLabwareRenderInfoForRunById = useLabwareRenderInfoForRunById as jest.MockedFunction< - typeof useLabwareRenderInfoForRunById -> const mockUseMostRecentCompletedAnalysis = useMostRecentCompletedAnalysis as jest.MockedFunction< typeof useMostRecentCompletedAnalysis > @@ -116,11 +112,6 @@ describe('LiquidsLabwareDetailsModal', () => { ]) mockLiquidDetailCard.mockReturnValue(
) mockGetWellFillFromLabwareId.mockReturnValue({}) - mockUseLabwareRenderInfoForRunById.mockReturnValue({ - '123': { - labwareDef: {}, - }, - } as any) mockUseMostRecentCompletedAnalysis.mockReturnValue( {} as CompletedProtocolAnalysis ) diff --git a/app/src/organisms/Devices/hooks/__tests__/useLabwareRenderInfoForRunById.test.tsx b/app/src/organisms/Devices/hooks/__tests__/useLabwareRenderInfoForRunById.test.tsx deleted file mode 100644 index fa6e2ed8996..00000000000 --- a/app/src/organisms/Devices/hooks/__tests__/useLabwareRenderInfoForRunById.test.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { renderHook } from '@testing-library/react-hooks' -import { when, resetAllWhenMocks } from 'jest-when' - -import standardDeckDef from '@opentrons/shared-data/deck/definitions/4/ot2_standard.json' -import _heaterShakerCommandsWithResultsKey from '@opentrons/shared-data/protocol/fixtures/6/heaterShakerCommandsWithResultsKey.json' -import { useMostRecentCompletedAnalysis } from '../../../LabwarePositionCheck/useMostRecentCompletedAnalysis' - -import { getLabwareRenderInfo } from '../../ProtocolRun/utils/getLabwareRenderInfo' -import { - useLabwareRenderInfoForRunById, - useProtocolDetailsForRun, - useStoredProtocolAnalysis, -} from '..' - -import type { ProtocolAnalysisOutput } from '@opentrons/shared-data' -import type { ProtocolDetails } from '..' - -jest.mock('../../ProtocolRun/utils/getLabwareRenderInfo') -jest.mock('../../../LabwarePositionCheck/useMostRecentCompletedAnalysis') -jest.mock('../useProtocolDetailsForRun') -jest.mock('../useStoredProtocolAnalysis') - -const mockGetLabwareRenderInfo = getLabwareRenderInfo as jest.MockedFunction< - typeof getLabwareRenderInfo -> -const mockUseProtocolDetailsForRun = useProtocolDetailsForRun as jest.MockedFunction< - typeof useProtocolDetailsForRun -> -const mockUseMostRecentCompletedAnalysis = useMostRecentCompletedAnalysis as jest.MockedFunction< - typeof useMostRecentCompletedAnalysis -> -const mockUseStoredProtocolAnalysis = useStoredProtocolAnalysis as jest.MockedFunction< - typeof useStoredProtocolAnalysis -> - -const heaterShakerCommandsWithResultsKey = (_heaterShakerCommandsWithResultsKey as unknown) as ProtocolAnalysisOutput - -const PROTOCOL_DETAILS = { - displayName: 'fake protocol', - protocolData: heaterShakerCommandsWithResultsKey, - protocolKey: 'fakeProtocolKey', - robotType: 'OT-2 Standard' as const, -} - -// these are just taken from the ot-2 deck def for readability -const SLOT_2_COORDS = [132.5, 0.0, 0.0] -const SLOT_4_COORDS = [0.0, 90.5, 0.0] -const SLOT_5_COORDS = [132.5, 90.5, 0.0] -const SLOT_6_COORDS = [265.0, 90.5, 0.0] -const SLOT_9_COORDS = [265.0, 181.0, 0.0] -const SLOT_10_COORDS = [0.0, 271.5, 0.0] - -// labware ids come from the fixture protocol, they are just here for readability -const OPENTRONS_96_TIPRACK_1000UL_TIPRACK_ID = - '3e047fb0-3412-11eb-ad93-ed232a2337cf:opentrons/opentrons_96_tiprack_1000ul/1' -const NEST_1_RESEVOIR_195ML_ID = - '5ae317e0-3412-11eb-ad93-ed232a2337cf:opentrons/nest_1_reservoir_195ml/1' -const CORNING_24_WELLPLATE_1_ID = '53d3b350-a9c0-11eb-bce6-9f1d5b9c1a1b' -const CORNING_24_WELLPLATE_2_ID = - '60e8b050-3412-11eb-ad93-ed232a2337cf:opentrons/corning_24_wellplate_3.4ml_flat/1' -const OPENTRONS_96_TIPRACK_20UL_TIPRACK_ID = - 'faa13a50-a9bf-11eb-bce6-9f1d5b9c1a1b:opentrons/opentrons_96_tiprack_20ul/1' -const labeledLabwareDisplayName = 'customLabwareDisplayName' - -const LABWARE_RENDER_INFO = { - // slot 1 has mag mod - // slot 2 - [OPENTRONS_96_TIPRACK_1000UL_TIPRACK_ID]: { - labwareDef: expect.anything(), - displayName: null, - x: SLOT_2_COORDS[0], - y: SLOT_2_COORDS[1], - z: SLOT_2_COORDS[2], - slotName: '2', - }, - // slot 3 has temp mod - // slot 4 - [NEST_1_RESEVOIR_195ML_ID]: { - labwareDef: expect.anything(), - displayName: null, - x: SLOT_4_COORDS[0], - y: SLOT_4_COORDS[1], - z: SLOT_4_COORDS[2], - slotName: '4', - }, - // slot 5 - [CORNING_24_WELLPLATE_2_ID]: { - labwareDef: expect.anything(), - displayName: null, - x: SLOT_5_COORDS[0], - y: SLOT_5_COORDS[1], - z: SLOT_5_COORDS[2], - slotName: '5', - }, - // slot 6 - [CORNING_24_WELLPLATE_1_ID]: { - labwareDef: expect.anything(), - displayName: null, - x: SLOT_6_COORDS[0], - y: SLOT_6_COORDS[1], - z: SLOT_6_COORDS[2], - slotName: '6', - }, - // slot 7 has TC - // slot 9 - [OPENTRONS_96_TIPRACK_20UL_TIPRACK_ID]: { - labwareDef: expect.anything(), - displayName: null, - x: SLOT_9_COORDS[0], - y: SLOT_9_COORDS[1], - z: SLOT_9_COORDS[2], - slotName: '9', - }, - // slot 10 - abc123: { - labwareDef: expect.anything(), - displayName: labeledLabwareDisplayName, - x: SLOT_10_COORDS[0], - y: SLOT_10_COORDS[1], - z: SLOT_10_COORDS[2], - slotName: '10', - }, -} - -describe('useLabwareRenderInfoForRunById hook', () => { - beforeEach(() => { - when(mockUseProtocolDetailsForRun) - .calledWith('1') - .mockReturnValue(PROTOCOL_DETAILS as any) - when(mockUseMostRecentCompletedAnalysis) - .calledWith('1') - .mockReturnValue(PROTOCOL_DETAILS.protocolData as any) - when(mockUseStoredProtocolAnalysis) - .calledWith('1') - .mockReturnValue((PROTOCOL_DETAILS as unknown) as ProtocolAnalysisOutput) - when(mockGetLabwareRenderInfo) - .calledWith(heaterShakerCommandsWithResultsKey, standardDeckDef as any) - .mockReturnValue(LABWARE_RENDER_INFO) - }) - afterEach(() => { - resetAllWhenMocks() - }) - - it('should return no labware render info when protocol details not found', () => { - when(mockUseProtocolDetailsForRun) - .calledWith('1') - .mockReturnValue({} as ProtocolDetails) - when(mockUseMostRecentCompletedAnalysis) - .calledWith('1') - .mockReturnValue(null) - when(mockUseStoredProtocolAnalysis).calledWith('1').mockReturnValue(null) - const { result } = renderHook(() => useLabwareRenderInfoForRunById('1')) - expect(result.current).toStrictEqual({}) - }) - - it('should return labware render info', () => { - const { result } = renderHook(() => useLabwareRenderInfoForRunById('1')) - expect(result.current).toStrictEqual(LABWARE_RENDER_INFO) - }) -}) diff --git a/app/src/organisms/Devices/hooks/index.ts b/app/src/organisms/Devices/hooks/index.ts index 0e131b5a426..f8cdeaa68e8 100644 --- a/app/src/organisms/Devices/hooks/index.ts +++ b/app/src/organisms/Devices/hooks/index.ts @@ -8,7 +8,6 @@ export * from './useCalibrationTaskList' export * from './useIsFlex' export * from './useIsRobotBusy' export * from './useIsRobotViewable' -export * from './useLabwareRenderInfoForRunById' export * from './useLights' export * from './useLEDLights' export * from './useLPCDisabledReason' diff --git a/app/src/organisms/Devices/hooks/useLabwareRenderInfoForRunById.ts b/app/src/organisms/Devices/hooks/useLabwareRenderInfoForRunById.ts deleted file mode 100644 index 726493a63ac..00000000000 --- a/app/src/organisms/Devices/hooks/useLabwareRenderInfoForRunById.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { getDeckDefFromRobotType } from '@opentrons/shared-data' -import { getLabwareRenderInfo } from '../ProtocolRun/utils/getLabwareRenderInfo' -import { useMostRecentCompletedAnalysis } from '../../LabwarePositionCheck/useMostRecentCompletedAnalysis' -import { useProtocolDetailsForRun, useStoredProtocolAnalysis } from '.' - -import type { LabwareRenderInfoById } from '../ProtocolRun/utils/getLabwareRenderInfo' - -export function useLabwareRenderInfoForRunById( - runId: string -): LabwareRenderInfoById { - const { robotType } = useProtocolDetailsForRun(runId) - const robotProtocolAnalysis = useMostRecentCompletedAnalysis(runId) - - const storedProtocolAnalysis = useStoredProtocolAnalysis(runId) - const protocolData = robotProtocolAnalysis ?? storedProtocolAnalysis - const deckDef = getDeckDefFromRobotType(robotType) - - return protocolData != null ? getLabwareRenderInfo(protocolData, deckDef) : {} -} From 42ba76185eff9b6f7be4f0442cb13fa7e85b13fc Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Mon, 18 Dec 2023 17:57:08 -0500 Subject: [PATCH 04/12] refactor(app,components,shared-data): clean up deck config unused functions and types removes various unused utilities, components, and types related to deck config work --- .../DeckConfigurator/EmptyConfigFixture.tsx | 6 +-- .../StagingAreaConfigFixture.tsx | 6 +-- .../TrashBinConfigFixture.tsx | 6 +-- .../WasteChuteConfigFixture.tsx | 6 +-- .../components/modules/AdditionalItemsRow.tsx | 4 +- .../src/components/modules/FlexSlotMap.tsx | 4 +- .../components/modules/StagingAreasRow.tsx | 4 +- shared-data/deck/types/schemaV4.ts | 23 +++++++-- shared-data/js/fixtures.ts | 16 ++----- shared-data/js/types.ts | 48 +------------------ 10 files changed, 43 insertions(+), 80 deletions(-) diff --git a/components/src/hardware-sim/DeckConfigurator/EmptyConfigFixture.tsx b/components/src/hardware-sim/DeckConfigurator/EmptyConfigFixture.tsx index f20f402eed6..2ba6c225fdc 100644 --- a/components/src/hardware-sim/DeckConfigurator/EmptyConfigFixture.tsx +++ b/components/src/hardware-sim/DeckConfigurator/EmptyConfigFixture.tsx @@ -8,12 +8,12 @@ import { BORDERS, COLORS } from '../../ui-style-constants' import { RobotCoordsForeignObject } from '../Deck/RobotCoordsForeignObject' import { FIXTURE_HEIGHT, SINGLE_SLOT_FIXTURE_WIDTH } from './constants' -import type { Cutout, DeckDefinition } from '@opentrons/shared-data' +import type { CutoutId, DeckDefinition } from '@opentrons/shared-data' interface EmptyConfigFixtureProps { deckDefinition: DeckDefinition - fixtureLocation: Cutout - handleClickAdd: (fixtureLocation: Cutout) => void + fixtureLocation: CutoutId + handleClickAdd: (fixtureLocation: CutoutId) => void } export function EmptyConfigFixture( diff --git a/components/src/hardware-sim/DeckConfigurator/StagingAreaConfigFixture.tsx b/components/src/hardware-sim/DeckConfigurator/StagingAreaConfigFixture.tsx index 9129d089712..5244ca45c2c 100644 --- a/components/src/hardware-sim/DeckConfigurator/StagingAreaConfigFixture.tsx +++ b/components/src/hardware-sim/DeckConfigurator/StagingAreaConfigFixture.tsx @@ -12,12 +12,12 @@ import { STAGING_AREA_FIXTURE_WIDTH, } from './constants' -import type { Cutout, DeckDefinition } from '@opentrons/shared-data' +import type { CutoutId, DeckDefinition } from '@opentrons/shared-data' interface StagingAreaConfigFixtureProps { deckDefinition: DeckDefinition - fixtureLocation: Cutout - handleClickRemove?: (fixtureLocation: Cutout) => void + fixtureLocation: CutoutId + handleClickRemove?: (fixtureLocation: CutoutId) => void } export function StagingAreaConfigFixture( diff --git a/components/src/hardware-sim/DeckConfigurator/TrashBinConfigFixture.tsx b/components/src/hardware-sim/DeckConfigurator/TrashBinConfigFixture.tsx index 32a2ec11708..5ca73e00ece 100644 --- a/components/src/hardware-sim/DeckConfigurator/TrashBinConfigFixture.tsx +++ b/components/src/hardware-sim/DeckConfigurator/TrashBinConfigFixture.tsx @@ -12,12 +12,12 @@ import { TRASH_BIN_DISPLAY_NAME, } from './constants' -import type { Cutout, DeckDefinition } from '@opentrons/shared-data' +import type { CutoutId, DeckDefinition } from '@opentrons/shared-data' interface TrashBinConfigFixtureProps { deckDefinition: DeckDefinition - fixtureLocation: Cutout - handleClickRemove?: (fixtureLocation: Cutout) => void + fixtureLocation: CutoutId + handleClickRemove?: (fixtureLocation: CutoutId) => void } export function TrashBinConfigFixture( diff --git a/components/src/hardware-sim/DeckConfigurator/WasteChuteConfigFixture.tsx b/components/src/hardware-sim/DeckConfigurator/WasteChuteConfigFixture.tsx index a834d538736..9ebe3e7f89c 100644 --- a/components/src/hardware-sim/DeckConfigurator/WasteChuteConfigFixture.tsx +++ b/components/src/hardware-sim/DeckConfigurator/WasteChuteConfigFixture.tsx @@ -13,12 +13,12 @@ import { SINGLE_SLOT_FIXTURE_WIDTH, } from './constants' -import type { Cutout, DeckDefinition } from '@opentrons/shared-data' +import type { CutoutId, DeckDefinition } from '@opentrons/shared-data' interface WasteChuteConfigFixtureProps { deckDefinition: DeckDefinition - fixtureLocation: Cutout - handleClickRemove?: (fixtureLocation: Cutout) => void + fixtureLocation: CutoutId + handleClickRemove?: (fixtureLocation: CutoutId) => void hasStagingAreas?: boolean } diff --git a/protocol-designer/src/components/modules/AdditionalItemsRow.tsx b/protocol-designer/src/components/modules/AdditionalItemsRow.tsx index 69672553acc..dc5b0510000 100644 --- a/protocol-designer/src/components/modules/AdditionalItemsRow.tsx +++ b/protocol-designer/src/components/modules/AdditionalItemsRow.tsx @@ -27,7 +27,7 @@ import { FlexSlotMap } from './FlexSlotMap' import styles from './styles.css' -import type { Cutout } from '@opentrons/shared-data' +import type { CutoutId } from '@opentrons/shared-data' interface AdditionalItemsRowProps { handleAttachment: () => void @@ -105,7 +105,7 @@ export function AdditionalItemsRow( value={`${getCutoutDisplayName( (name === 'trashBin' ? trashBinSlot ?? '' - : WASTE_CHUTE_CUTOUT) as Cutout + : WASTE_CHUTE_CUTOUT) as CutoutId )}`} /> diff --git a/protocol-designer/src/components/modules/FlexSlotMap.tsx b/protocol-designer/src/components/modules/FlexSlotMap.tsx index 4f0d84fd969..dcabe5df72d 100644 --- a/protocol-designer/src/components/modules/FlexSlotMap.tsx +++ b/protocol-designer/src/components/modules/FlexSlotMap.tsx @@ -16,8 +16,6 @@ import { SPACING, } from '@opentrons/components' -import type { Cutout } from '@opentrons/shared-data' - const X_ADJUSTMENT_LEFT_SIDE = -101.5 const X_ADJUSTMENT = -17 const X_DIMENSION_MIDDLE_SLOTS = 160.3 @@ -49,7 +47,7 @@ export function FlexSlotMap(props: FlexSlotMapProps): JSX.Element { {deckDef.locations.cutouts.map(cutout => ( - getCutoutDisplayName(location as Cutout) + getCutoutDisplayName(location as CutoutId) )}`} /> diff --git a/shared-data/deck/types/schemaV4.ts b/shared-data/deck/types/schemaV4.ts index ed7f726be3c..9ae9a37ba54 100644 --- a/shared-data/deck/types/schemaV4.ts +++ b/shared-data/deck/types/schemaV4.ts @@ -60,6 +60,20 @@ export type CutoutId = | 'cutoutA2' | 'cutoutA3' +export type OT2CutoutId = + | 'cutout1' + | 'cutout2' + | 'cutout3' + | 'cutout4' + | 'cutout5' + | 'cutout6' + | 'cutout7' + | 'cutout8' + | 'cutout9' + | 'cutout10' + | 'cutout11' + | 'cutout12' + export type SingleSlotCutoutFixtureId = | 'singleLeftSlot' | 'singleCenterSlot' @@ -75,11 +89,14 @@ export type WasteChuteCutoutFixtureId = | 'stagingAreaSlotWithWasteChuteRightAdapterCovered' | 'stagingAreaSlotWithWasteChuteRightAdapterNoCover' +export type OT2SingleStandardSlot = 'singleStandardSlot' + +export type OT2FixedTrashSlot = 'fixedTrashSlot' + export type CutoutFixtureId = | SingleSlotCutoutFixtureId | StagingAreaRightSlotFixtureId | TrashBinAdapterCutoutFixtureId | WasteChuteCutoutFixtureId - | 'stagingAreaRightSlot' - | 'trashBinAdapter' - | 'fixedTrashSlot' + | OT2SingleStandardSlot + | OT2FixedTrashSlot diff --git a/shared-data/js/fixtures.ts b/shared-data/js/fixtures.ts index 64413d130c8..f0b906575d8 100644 --- a/shared-data/js/fixtures.ts +++ b/shared-data/js/fixtures.ts @@ -7,21 +7,15 @@ import { STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_COVERED_FIXTURE, STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE, } from './constants' -import type { CutoutFixtureId } from '../deck' -import type { - AddressableArea, - CoordinateTuple, - Cutout, - DeckDefinition, - OT2Cutout, -} from './types' +import type { CutoutFixtureId, CutoutId, OT2CutoutId } from '../deck' +import type { AddressableArea, CoordinateTuple, DeckDefinition } from './types' -export function getCutoutDisplayName(cutout: Cutout): string { +export function getCutoutDisplayName(cutout: CutoutId): string { return cutout.replace('cutout', '') } // mapping of OT-2 deck slots to cutouts -export const OT2_CUTOUT_BY_SLOT_ID: { [slotId: string]: OT2Cutout } = { +export const OT2_CUTOUT_BY_SLOT_ID: { [slotId: string]: OT2CutoutId } = { 1: 'cutout1', 2: 'cutout2', 3: 'cutout3', @@ -37,7 +31,7 @@ export const OT2_CUTOUT_BY_SLOT_ID: { [slotId: string]: OT2Cutout } = { } // mapping of Flex deck slots to cutouts -export const FLEX_CUTOUT_BY_SLOT_ID: { [slotId: string]: Cutout } = { +export const FLEX_CUTOUT_BY_SLOT_ID: { [slotId: string]: CutoutId } = { A1: 'cutoutA1', A2: 'cutoutA2', A3: 'cutoutA3', diff --git a/shared-data/js/types.ts b/shared-data/js/types.ts index 114b696cb4f..38f5c590636 100644 --- a/shared-data/js/types.ts +++ b/shared-data/js/types.ts @@ -294,7 +294,7 @@ export interface DeckMetadata { } export interface DeckCutout { - id: string + id: CutoutId position: CoordinateTuple displayName: string } @@ -550,52 +550,6 @@ export type StatusBarAnimation = export type StatusBarAnimations = StatusBarAnimation[] -export type Cutout = - | 'cutoutA1' - | 'cutoutB1' - | 'cutoutC1' - | 'cutoutD1' - | 'cutoutA2' - | 'cutoutB2' - | 'cutoutC2' - | 'cutoutD2' - | 'cutoutA3' - | 'cutoutB3' - | 'cutoutC3' - | 'cutoutD3' - -export type OT2Cutout = - | 'cutout1' - | 'cutout2' - | 'cutout3' - | 'cutout4' - | 'cutout5' - | 'cutout6' - | 'cutout7' - | 'cutout8' - | 'cutout9' - | 'cutout10' - | 'cutout11' - | 'cutout12' - -export type FlexSlot = - | 'A1' - | 'B1' - | 'C1' - | 'D1' - | 'A2' - | 'B2' - | 'C2' - | 'D2' - | 'A3' - | 'B3' - | 'C3' - | 'D3' - | 'A4' - | 'B4' - | 'C4' - | 'D4' - export interface CutoutConfig { cutoutId: CutoutId cutoutFixtureId: CutoutFixtureId | null From 807cf39e4718c8e2b029312b434f58aad8a947dd Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Tue, 19 Dec 2023 11:26:26 -0500 Subject: [PATCH 05/12] adjust basedeck fixture filtering and config fixtures --- .../src/hardware-sim/BaseDeck/BaseDeck.tsx | 64 +++++++++++-------- .../DeckConfigurator/EmptyConfigFixture.tsx | 16 +++-- .../StagingAreaConfigFixture.tsx | 14 ++-- .../TrashBinConfigFixture.tsx | 15 +++-- .../WasteChuteConfigFixture.tsx | 14 ++-- shared-data/js/constants.ts | 11 ++++ 6 files changed, 90 insertions(+), 44 deletions(-) diff --git a/components/src/hardware-sim/BaseDeck/BaseDeck.tsx b/components/src/hardware-sim/BaseDeck/BaseDeck.tsx index 2cf40694da5..3d7270966b1 100644 --- a/components/src/hardware-sim/BaseDeck/BaseDeck.tsx +++ b/components/src/hardware-sim/BaseDeck/BaseDeck.tsx @@ -6,7 +6,9 @@ import { getPositionFromSlotId, inferModuleOrientationFromXCoordinate, OT2_ROBOT_TYPE, + MOVABLE_TRASH_CUTOUTS, SINGLE_SLOT_FIXTURES, + STAGING_AREA_CUTOUTS, STAGING_AREA_RIGHT_SLOT_FIXTURE, TRASH_BIN_ADAPTER_FIXTURE, WASTE_CHUTE_CUTOUT, @@ -105,10 +107,14 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element { SINGLE_SLOT_FIXTURES.includes(fixture.cutoutFixtureId) ) const stagingAreaFixtures = deckConfig.filter( - fixture => fixture.cutoutFixtureId === STAGING_AREA_RIGHT_SLOT_FIXTURE + fixture => + fixture.cutoutFixtureId === STAGING_AREA_RIGHT_SLOT_FIXTURE && + STAGING_AREA_CUTOUTS.includes(fixture.cutoutId) ) const trashBinFixtures = deckConfig.filter( - fixture => fixture.cutoutFixtureId === TRASH_BIN_ADAPTER_FIXTURE + fixture => + fixture.cutoutFixtureId === TRASH_BIN_ADAPTER_FIXTURE && + MOVABLE_TRASH_CUTOUTS.includes(fixture.cutoutId) ) const wasteChuteOnlyFixtures = deckConfig.filter( fixture => @@ -159,7 +165,6 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element { {stagingAreaFixtures.map(fixture => ( ))} - {wasteChuteOnlyFixtures.map(fixture => ( - - ))} - {wasteChuteStagingAreaFixtures.map(fixture => ( - - ))} + {wasteChuteOnlyFixtures.map(fixture => { + if (fixture.cutoutId === WASTE_CHUTE_CUTOUT) { + return ( + + ) + } else { + return null + } + })} + {wasteChuteStagingAreaFixtures.map(fixture => { + if (fixture.cutoutId === WASTE_CHUTE_CUTOUT) { + return ( + + ) + } else { + return null + } + })} )} <> diff --git a/components/src/hardware-sim/DeckConfigurator/EmptyConfigFixture.tsx b/components/src/hardware-sim/DeckConfigurator/EmptyConfigFixture.tsx index 2ba6c225fdc..38e5ac674d6 100644 --- a/components/src/hardware-sim/DeckConfigurator/EmptyConfigFixture.tsx +++ b/components/src/hardware-sim/DeckConfigurator/EmptyConfigFixture.tsx @@ -21,14 +21,18 @@ export function EmptyConfigFixture( ): JSX.Element { const { deckDefinition, handleClickAdd, fixtureLocation } = props - // TODO: migrate to fixture location for v4 - const standardSlot = deckDefinition.locations.cutouts.find( - slot => slot.id === fixtureLocation + const standardSlotCutout = deckDefinition.locations.cutouts.find( + cutout => cutout.id === fixtureLocation ) - const [xSlotPosition = 0, ySlotPosition = 0] = standardSlot?.position ?? [] - // TODO: remove adjustment when reading from fixture position - // adjust x differently for right side/left side + /** + * deck definition cutout position is the position of the single slot located within that cutout + * so, to get the position of the cutout itself we must add an adjustment to the slot position + * the adjustment for x is different for right side/left side + */ + const [xSlotPosition = 0, ySlotPosition = 0] = + standardSlotCutout?.position ?? [] + const isLeftSideofDeck = fixtureLocation === 'cutoutA1' || fixtureLocation === 'cutoutB1' || diff --git a/components/src/hardware-sim/DeckConfigurator/StagingAreaConfigFixture.tsx b/components/src/hardware-sim/DeckConfigurator/StagingAreaConfigFixture.tsx index 5244ca45c2c..5a0478dde6a 100644 --- a/components/src/hardware-sim/DeckConfigurator/StagingAreaConfigFixture.tsx +++ b/components/src/hardware-sim/DeckConfigurator/StagingAreaConfigFixture.tsx @@ -25,11 +25,17 @@ export function StagingAreaConfigFixture( ): JSX.Element { const { deckDefinition, handleClickRemove, fixtureLocation } = props - const stagingAreaSlot = deckDefinition.locations.cutouts.find( - slot => slot.id === fixtureLocation + const stagingAreaCutout = deckDefinition.locations.cutouts.find( + cutout => cutout.id === fixtureLocation ) - const [xSlotPosition = 0, ySlotPosition = 0] = stagingAreaSlot?.position ?? [] - // TODO: remove adjustment when reading from fixture position + + /** + * deck definition cutout position is the position of the single slot located within that cutout + * so, to get the position of the cutout itself we must add an adjustment to the slot position + */ + const [xSlotPosition = 0, ySlotPosition = 0] = + stagingAreaCutout?.position ?? [] + const xAdjustment = -17 const x = xSlotPosition + xAdjustment const yAdjustment = -10 diff --git a/components/src/hardware-sim/DeckConfigurator/TrashBinConfigFixture.tsx b/components/src/hardware-sim/DeckConfigurator/TrashBinConfigFixture.tsx index 5ca73e00ece..900cbdf526f 100644 --- a/components/src/hardware-sim/DeckConfigurator/TrashBinConfigFixture.tsx +++ b/components/src/hardware-sim/DeckConfigurator/TrashBinConfigFixture.tsx @@ -25,12 +25,17 @@ export function TrashBinConfigFixture( ): JSX.Element { const { deckDefinition, handleClickRemove, fixtureLocation } = props - const trashBinSlot = deckDefinition.locations.cutouts.find( - slot => slot.id === fixtureLocation + const trashBinCutout = deckDefinition.locations.cutouts.find( + cutout => cutout.id === fixtureLocation ) - const [xSlotPosition = 0, ySlotPosition = 0] = trashBinSlot?.position ?? [] - // TODO: remove adjustment when reading from fixture position - // adjust x differently for right side/left side + + /** + * deck definition cutout position is the position of the single slot located within that cutout + * so, to get the position of the cutout itself we must add an adjustment to the slot position + * the adjustment for x is different for right side/left side + */ + const [xSlotPosition = 0, ySlotPosition = 0] = trashBinCutout?.position ?? [] + const isLeftSideofDeck = fixtureLocation === 'cutoutA1' || fixtureLocation === 'cutoutB1' || diff --git a/components/src/hardware-sim/DeckConfigurator/WasteChuteConfigFixture.tsx b/components/src/hardware-sim/DeckConfigurator/WasteChuteConfigFixture.tsx index 9ebe3e7f89c..851a2c2bc8c 100644 --- a/components/src/hardware-sim/DeckConfigurator/WasteChuteConfigFixture.tsx +++ b/components/src/hardware-sim/DeckConfigurator/WasteChuteConfigFixture.tsx @@ -32,11 +32,17 @@ export function WasteChuteConfigFixture( hasStagingAreas = false, } = props - const wasteChuteSlot = deckDefinition.locations.cutouts.find( - slot => slot.id === fixtureLocation + const wasteChuteCutout = deckDefinition.locations.cutouts.find( + cutout => cutout.id === fixtureLocation ) - const [xSlotPosition = 0, ySlotPosition = 0] = wasteChuteSlot?.position ?? [] - // TODO: remove adjustment when reading from fixture position + + /** + * deck definition cutout position is the position of the single slot located within that cutout + * so, to get the position of the cutout itself we must add an adjustment to the slot position + */ + const [xSlotPosition = 0, ySlotPosition = 0] = + wasteChuteCutout?.position ?? [] + const xAdjustment = -17 const x = xSlotPosition + xAdjustment const yAdjustment = -10 diff --git a/shared-data/js/constants.ts b/shared-data/js/constants.ts index 17c5f9b8b70..71807f95704 100644 --- a/shared-data/js/constants.ts +++ b/shared-data/js/constants.ts @@ -184,6 +184,17 @@ export const TC_MODULE_LOCATION_OT3: 'A1+B1' = 'A1+B1' export const WEIGHT_OF_96_CHANNEL: '~10kg' = '~10kg' +export const MOVABLE_TRASH_CUTOUTS: CutoutId[] = [ + 'cutoutA1', + 'cutoutB1', + 'cutoutC1', + 'cutoutD1', + 'cutoutA3', + 'cutoutB3', + 'cutoutC3', + 'cutoutD3', +] + export const SINGLE_LEFT_CUTOUTS: CutoutId[] = [ 'cutoutA1', 'cutoutB1', From 6456b63421f2ead06221566a91f8a0cb9514f8cb Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Tue, 2 Jan 2024 16:27:19 -0500 Subject: [PATCH 06/12] delete useProtocolDetailsForRun references` --- .../ChooseRobotToRunProtocolSlideout.test.tsx | 12 +- .../__tests__/SetupLabware.test.tsx | 54 ----- .../SetupLabwarePositionCheck.test.tsx | 191 +----------------- .../ProtocolRunModuleControls.test.tsx | 23 +-- ...seModuleRenderInfoForProtocolById.test.tsx | 1 - .../useRunPipetteInfoByMount.test.tsx | 1 - 6 files changed, 5 insertions(+), 277 deletions(-) diff --git a/app/src/organisms/ChooseRobotToRunProtocolSlideout/__tests__/ChooseRobotToRunProtocolSlideout.test.tsx b/app/src/organisms/ChooseRobotToRunProtocolSlideout/__tests__/ChooseRobotToRunProtocolSlideout.test.tsx index 75a6156063f..39304bd76c7 100644 --- a/app/src/organisms/ChooseRobotToRunProtocolSlideout/__tests__/ChooseRobotToRunProtocolSlideout.test.tsx +++ b/app/src/organisms/ChooseRobotToRunProtocolSlideout/__tests__/ChooseRobotToRunProtocolSlideout.test.tsx @@ -5,10 +5,7 @@ import { fireEvent, screen } from '@testing-library/react' import { when, resetAllWhenMocks } from 'jest-when' import { i18n } from '../../../i18n' -import { - useProtocolDetailsForRun, - useTrackCreateProtocolRunEvent, -} from '../../../organisms/Devices/hooks' +import { useTrackCreateProtocolRunEvent } from '../../../organisms/Devices/hooks' import { useCloseCurrentRun, useCurrentRunId, @@ -33,7 +30,6 @@ import { useCreateRunFromProtocol } from '../useCreateRunFromProtocol' import { useOffsetCandidatesForAnalysis } from '../../ApplyHistoricOffsets/hooks/useOffsetCandidatesForAnalysis' import { ChooseRobotToRunProtocolSlideout } from '../' -import type { ProtocolDetails } from '../../../organisms/Devices/hooks' import type { State } from '../../../redux/types' jest.mock('../../../organisms/Devices/hooks') @@ -77,9 +73,6 @@ const mockUseCurrentRunStatus = useCurrentRunStatus as jest.MockedFunction< typeof useCurrentRunStatus > -const mockUseProtocolDetailsForRun = useProtocolDetailsForRun as jest.MockedFunction< - typeof useProtocolDetailsForRun -> const mockUseCreateRunFromProtocol = useCreateRunFromProtocol as jest.MockedFunction< typeof useCreateRunFromProtocol > @@ -132,9 +125,6 @@ describe('ChooseRobotToRunProtocolSlideout', () => { }) mockUseCurrentRunId.mockReturnValue(null) mockUseCurrentRunStatus.mockReturnValue(null) - mockUseProtocolDetailsForRun.mockReturnValue({ - displayName: 'A Protocol for Otie', - } as ProtocolDetails) when(mockUseCreateRunFromProtocol) .calledWith( expect.any(Object), diff --git a/app/src/organisms/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabware.test.tsx b/app/src/organisms/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabware.test.tsx index 8707dbf854a..7e88284a87b 100644 --- a/app/src/organisms/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabware.test.tsx +++ b/app/src/organisms/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabware.test.tsx @@ -11,7 +11,6 @@ import { getModuleTypesThatRequireExtraAttention } from '../../utils/getModuleTy import { getIsLabwareOffsetCodeSnippetsOn } from '../../../../../redux/config' import { useLPCDisabledReason, - useProtocolDetailsForRun, useRunCalibrationStatus, useRunHasStarted, useUnmatchedModulesForProtocol, @@ -38,9 +37,6 @@ const mockLabwarePostionCheck = LabwarePositionCheck as jest.MockedFunction< const mockUseRunHasStarted = useRunHasStarted as jest.MockedFunction< typeof useRunHasStarted > -const mockUseProtocolDetailsForRun = useProtocolDetailsForRun as jest.MockedFunction< - typeof useProtocolDetailsForRun -> const mockUseUnmatchedModulesForProtocol = useUnmatchedModulesForProtocol as jest.MockedFunction< typeof useUnmatchedModulesForProtocol > @@ -64,28 +60,6 @@ const mockUseLPCDisabledReason = useLPCDisabledReason as jest.MockedFunction< > const ROBOT_NAME = 'otie' const RUN_ID = '1' -const PICKUP_TIP_LABWARE_ID = 'PICKUP_TIP_LABWARE_ID' -const PRIMARY_PIPETTE_ID = 'PRIMARY_PIPETTE_ID' -const PRIMARY_PIPETTE_NAME = 'PRIMARY_PIPETTE_NAME' -const LABWARE_DEF_ID = 'LABWARE_DEF_ID' -const LABWARE_DEF = { - ordering: [['A1', 'A2']], - parameters: { isTiprack: true }, -} -const mockLabwarePositionCheckStepTipRack = { - labwareId: - '1d57fc10-67ad-11ea-9f8b-3b50068bd62d:opentrons/opentrons_96_filtertiprack_200ul/1', - section: '', - commands: [ - { - commandType: 'pickUpTip', - params: { - pipetteId: PRIMARY_PIPETTE_ID, - labwareId: PICKUP_TIP_LABWARE_ID, - }, - }, - ], -} as any const render = () => { return renderWithProviders( @@ -130,34 +104,6 @@ describe('SetupLabware', () => { complete: true, }) when(mockUseRunHasStarted).calledWith(RUN_ID).mockReturnValue(false) - when(mockUseProtocolDetailsForRun) - .calledWith(RUN_ID) - .mockReturnValue({ - protocolData: { - labware: { - [mockLabwarePositionCheckStepTipRack.labwareId]: { - slot: '1', - displayName: 'someDisplayName', - definitionId: LABWARE_DEF_ID, - }, - }, - labwareDefinitions: { - [LABWARE_DEF_ID]: LABWARE_DEF, - }, - pipettes: { - [PRIMARY_PIPETTE_ID]: { - name: PRIMARY_PIPETTE_NAME, - mount: 'left', - }, - }, - commands: [ - { - commandType: 'pickUpTip', - params: { pipetteId: PRIMARY_PIPETTE_ID }, - } as any, - ], - }, - } as any) when(mockGetIsLabwareOffsetCodeSnippetsOn).mockReturnValue(false) when(mockSetupLabwareMap).mockReturnValue(
mock setup labware map
) when(mockSetupLabwareList).mockReturnValue( diff --git a/app/src/organisms/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/SetupLabwarePositionCheck.test.tsx b/app/src/organisms/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/SetupLabwarePositionCheck.test.tsx index 86204d8623d..4c86e2e9011 100644 --- a/app/src/organisms/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/SetupLabwarePositionCheck.test.tsx +++ b/app/src/organisms/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/SetupLabwarePositionCheck.test.tsx @@ -16,7 +16,6 @@ import { useLaunchLPC } from '../../../../LabwarePositionCheck/useLaunchLPC' import { getIsLabwareOffsetCodeSnippetsOn } from '../../../../../redux/config' import { useLPCDisabledReason, - useProtocolDetailsForRun, useRunCalibrationStatus, useRunHasStarted, useUnmatchedModulesForProtocol, @@ -39,9 +38,6 @@ const mockGetModuleTypesThatRequireExtraAttention = getModuleTypesThatRequireExt const mockUseRunHasStarted = useRunHasStarted as jest.MockedFunction< typeof useRunHasStarted > -const mockUseProtocolDetailsForRun = useProtocolDetailsForRun as jest.MockedFunction< - typeof useProtocolDetailsForRun -> const mockUseUnmatchedModulesForProtocol = useUnmatchedModulesForProtocol as jest.MockedFunction< typeof useUnmatchedModulesForProtocol > @@ -73,28 +69,6 @@ const mockUseProtocolAnalysisAsDocumentQuery = useProtocolAnalysisAsDocumentQuer const DISABLED_REASON = 'MOCK_DISABLED_REASON' const ROBOT_NAME = 'otie' const RUN_ID = '1' -const PICKUP_TIP_LABWARE_ID = 'PICKUP_TIP_LABWARE_ID' -const PRIMARY_PIPETTE_ID = 'PRIMARY_PIPETTE_ID' -const PRIMARY_PIPETTE_NAME = 'PRIMARY_PIPETTE_NAME' -const LABWARE_DEF_ID = 'LABWARE_DEF_ID' -const LABWARE_DEF = { - ordering: [['A1', 'A2']], - parameters: { isTiprack: true }, -} -const mockLabwarePositionCheckStepTipRack = { - labwareId: - '1d57fc10-67ad-11ea-9f8b-3b50068bd62d:opentrons/opentrons_96_filtertiprack_200ul/1', - section: '', - commands: [ - { - commandType: 'pickUpTip', - params: { - pipetteId: PRIMARY_PIPETTE_ID, - labwareId: PICKUP_TIP_LABWARE_ID, - }, - }, - ], -} as any const render = () => { return renderWithProviders( @@ -111,7 +85,7 @@ const render = () => { )[0] } -describe('SetupLabware', () => { +describe('SetupLabwarePositionCheck', () => { let mockLaunchLPC: jest.Mock beforeEach(() => { @@ -137,34 +111,6 @@ describe('SetupLabware', () => { complete: true, }) when(mockUseRunHasStarted).calledWith(RUN_ID).mockReturnValue(false) - when(mockUseProtocolDetailsForRun) - .calledWith(RUN_ID) - .mockReturnValue({ - protocolData: { - labware: { - [mockLabwarePositionCheckStepTipRack.labwareId]: { - slot: '1', - displayName: 'someDisplayName', - definitionId: LABWARE_DEF_ID, - }, - }, - labwareDefinitions: { - [LABWARE_DEF_ID]: LABWARE_DEF, - }, - pipettes: { - [PRIMARY_PIPETTE_ID]: { - name: PRIMARY_PIPETTE_NAME, - mount: 'left', - }, - }, - commands: [ - { - commandType: 'pickUpTip', - params: { pipetteId: PRIMARY_PIPETTE_ID }, - } as any, - ], - }, - } as any) when(mockGetIsLabwareOffsetCodeSnippetsOn).mockReturnValue(false) when(mockUseLPCDisabledReason).mockReturnValue(null) when(mockUseRobotType) @@ -200,7 +146,7 @@ describe('SetupLabware', () => { }).click() expect(mockLaunchLPC).toHaveBeenCalled() }) - it('should render a disabled LPC button when a run has started', () => { + it('should render a disabled LPC button when disabled LPC reason exists', () => { when(mockUseRunHasStarted).calledWith(RUN_ID).mockReturnValue(true) when(mockUseLPCDisabledReason).mockReturnValue(DISABLED_REASON) const { getByRole, queryByText } = render() @@ -224,137 +170,4 @@ describe('SetupLabware', () => { fireEvent.click(button) expect(mockSetIsShowingLPCSuccessToast).toHaveBeenCalledWith(false) }) - it('should render a disabled LPC button when a robot-side protocol analysis is not complete', () => { - when(mockUseProtocolDetailsForRun) - .calledWith(RUN_ID) - .mockReturnValue({ - protocolData: null, - } as any) - when(mockUseLPCDisabledReason).mockReturnValue(DISABLED_REASON) - const { getByRole } = render() - const button = getByRole('button', { - name: 'run labware position check', - }) - expect(button).toBeDisabled() - }) - it('should render a disabled LPC button when a protocol without a pipette AND without a labware is uploaded', () => { - when(mockUseProtocolDetailsForRun) - .calledWith(RUN_ID) - .mockReturnValue({ - protocolData: { labware: {}, pipettes: {} }, - } as any) - when(mockUseLPCDisabledReason).mockReturnValue(DISABLED_REASON) - const { getByRole } = render() - const button = getByRole('button', { - name: 'run labware position check', - }) - expect(button).toBeDisabled() - }) - it('should render a disabled LPC button when robot calibration is incomplete', () => { - when(mockUseRunCalibrationStatus) - .calledWith(ROBOT_NAME, RUN_ID) - .mockReturnValue({ - complete: false, - }) - when(mockUseLPCDisabledReason).mockReturnValue(DISABLED_REASON) - const { getByRole } = render() - const button = getByRole('button', { - name: 'run labware position check', - }) - expect(button).toBeDisabled() - }) - it('should render a disabled LPC button when modules are not connected', () => { - when(mockUseUnmatchedModulesForProtocol) - .calledWith(ROBOT_NAME, RUN_ID) - .mockReturnValue({ - missingModuleIds: ['temperatureModuleV1'], - remainingAttachedModules: [], - }) - when(mockUseLPCDisabledReason).mockReturnValue(DISABLED_REASON) - const { getByRole } = render() - const button = getByRole('button', { - name: 'run labware position check', - }) - expect(button).toBeDisabled() - }) - it('should render a disabled LPC button when modules are not connected and robot calibration is incomplete', () => { - when(mockUseRunCalibrationStatus) - .calledWith(ROBOT_NAME, RUN_ID) - .mockReturnValue({ - complete: false, - }) - when(mockUseUnmatchedModulesForProtocol) - .calledWith(ROBOT_NAME, RUN_ID) - .mockReturnValue({ - missingModuleIds: ['temperatureModuleV1'], - remainingAttachedModules: [], - }) - when(mockUseLPCDisabledReason).mockReturnValue(DISABLED_REASON) - const { getByRole } = render() - const button = getByRole('button', { - name: 'run labware position check', - }) - expect(button).toBeDisabled() - }) - it('should render a disabled LPC button when a protocol does not load a tip rack', () => { - when(mockUseProtocolDetailsForRun) - .calledWith(RUN_ID) - .mockReturnValue({ - protocolData: { - labware: { - 'labware-0': { - slot: '1', - displayName: 'someDisplayName', - definitionId: LABWARE_DEF_ID, - }, - }, - labwareDefinitions: { - [LABWARE_DEF_ID]: { parameters: { isTiprack: false } }, - }, - pipettes: { - [PRIMARY_PIPETTE_ID]: { - name: PRIMARY_PIPETTE_NAME, - mount: 'left', - }, - }, - }, - } as any) - when(mockUseLPCDisabledReason).mockReturnValue(DISABLED_REASON) - const { getByRole } = render() - const button = getByRole('button', { - name: 'run labware position check', - }) - expect(button).toBeDisabled() - }) - it('should render a disabled LPC button when a protocol does not include a pickUpTip', () => { - when(mockUseProtocolDetailsForRun) - .calledWith(RUN_ID) - .mockReturnValue({ - protocolData: { - labware: { - [mockLabwarePositionCheckStepTipRack.labwareId]: { - slot: '1', - displayName: 'someDisplayName', - definitionId: LABWARE_DEF_ID, - }, - }, - labwareDefinitions: { - [LABWARE_DEF_ID]: LABWARE_DEF, - }, - pipettes: { - [PRIMARY_PIPETTE_ID]: { - name: PRIMARY_PIPETTE_NAME, - mount: 'left', - }, - }, - commands: [], - }, - } as any) - when(mockUseLPCDisabledReason).mockReturnValue(DISABLED_REASON) - const { getByRole } = render() - const button = getByRole('button', { - name: 'run labware position check', - }) - expect(button).toBeDisabled() - }) }) diff --git a/app/src/organisms/Devices/ProtocolRun/__tests__/ProtocolRunModuleControls.test.tsx b/app/src/organisms/Devices/ProtocolRun/__tests__/ProtocolRunModuleControls.test.tsx index 877ae596328..28bcd5617ce 100644 --- a/app/src/organisms/Devices/ProtocolRun/__tests__/ProtocolRunModuleControls.test.tsx +++ b/app/src/organisms/Devices/ProtocolRun/__tests__/ProtocolRunModuleControls.test.tsx @@ -2,25 +2,17 @@ import * as React from 'react' import { resetAllWhenMocks, when } from 'jest-when' import { i18n } from '../../../../i18n' import { renderWithProviders } from '@opentrons/components' -import { - CompletedProtocolAnalysis, - ModuleModel, - ModuleType, -} from '@opentrons/shared-data' +import { ModuleModel, ModuleType } from '@opentrons/shared-data' import { useInstrumentsQuery } from '@opentrons/react-api-client' import { ProtocolRunModuleControls } from '../ProtocolRunModuleControls' import { ModuleCard } from '../../../ModuleCard' -import { - useModuleRenderInfoForProtocolById, - useProtocolDetailsForRun, -} from '../../hooks' +import { useModuleRenderInfoForProtocolById } from '../../hooks' import { mockMagneticModuleGen2, mockTemperatureModuleGen2, mockThermocycler, mockHeaterShaker, } from '../../../../redux/modules/__fixtures__' -import fixtureAnalysis from '../../../../organisms/RunDetails/__fixtures__/analysis.json' jest.mock('@opentrons/react-api-client') jest.mock('../../../ModuleCard') @@ -30,15 +22,10 @@ const mockModuleCard = ModuleCard as jest.MockedFunction const mockUseModuleRenderInfoForProtocolById = useModuleRenderInfoForProtocolById as jest.MockedFunction< typeof useModuleRenderInfoForProtocolById > -const mockUseProtocolDetailsForRun = useProtocolDetailsForRun as jest.MockedFunction< - typeof useProtocolDetailsForRun -> const mockUseInstrumentsQuery = useInstrumentsQuery as jest.MockedFunction< typeof useInstrumentsQuery > -const _fixtureAnalysis = (fixtureAnalysis as unknown) as CompletedProtocolAnalysis - const RUN_ID = 'test123' const mockTempMod = { @@ -78,12 +65,6 @@ const render = ( describe('ProtocolRunModuleControls', () => { beforeEach(() => { - when(mockUseProtocolDetailsForRun).calledWith(RUN_ID).mockReturnValue({ - protocolData: _fixtureAnalysis, - displayName: 'mock display name', - protocolKey: 'fakeProtocolKey', - robotType: 'OT-2 Standard', - }) when(mockUseInstrumentsQuery).mockReturnValue({ data: { data: [] }, } as any) diff --git a/app/src/organisms/Devices/hooks/__tests__/useModuleRenderInfoForProtocolById.test.tsx b/app/src/organisms/Devices/hooks/__tests__/useModuleRenderInfoForProtocolById.test.tsx index 87ff6adc660..ae14d61fb6f 100644 --- a/app/src/organisms/Devices/hooks/__tests__/useModuleRenderInfoForProtocolById.test.tsx +++ b/app/src/organisms/Devices/hooks/__tests__/useModuleRenderInfoForProtocolById.test.tsx @@ -31,7 +31,6 @@ import type { jest.mock('@opentrons/react-api-client/src/deck_configuration') jest.mock('../../ProtocolRun/utils/getProtocolModulesInfo') jest.mock('../useAttachedModules') -jest.mock('../useProtocolDetailsForRun') jest.mock('../useStoredProtocolAnalysis') jest.mock('../../../LabwarePositionCheck/useMostRecentCompletedAnalysis') diff --git a/app/src/organisms/Devices/hooks/__tests__/useRunPipetteInfoByMount.test.tsx b/app/src/organisms/Devices/hooks/__tests__/useRunPipetteInfoByMount.test.tsx index 0144832cc96..6bbe18a90db 100644 --- a/app/src/organisms/Devices/hooks/__tests__/useRunPipetteInfoByMount.test.tsx +++ b/app/src/organisms/Devices/hooks/__tests__/useRunPipetteInfoByMount.test.tsx @@ -50,7 +50,6 @@ jest.mock('../../../LabwarePositionCheck/useMostRecentCompletedAnalysis') jest.mock('../useAttachedPipetteCalibrations') jest.mock('../useAttachedPipettes') jest.mock('../useTipLengthCalibrations') -jest.mock('../useProtocolDetailsForRun') jest.mock('../useStoredProtocolAnalysis') const mockGetPipetteNameSpecs = getPipetteNameSpecs as jest.MockedFunction< From 83a3ef4e74665da1b41a42bbaacbc74732b82ffd Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Tue, 2 Jan 2024 16:31:53 -0500 Subject: [PATCH 07/12] remove DeckFromLayers TODO --- components/src/hardware-sim/Deck/DeckFromLayers.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/src/hardware-sim/Deck/DeckFromLayers.tsx b/components/src/hardware-sim/Deck/DeckFromLayers.tsx index b5dd4c519f0..1e2c5b98634 100644 --- a/components/src/hardware-sim/Deck/DeckFromLayers.tsx +++ b/components/src/hardware-sim/Deck/DeckFromLayers.tsx @@ -57,8 +57,6 @@ export function DeckFromLayers(props: DeckFromLayersProps): JSX.Element | null { return ( <> {parseHtml( - // TODO(bh, 2023-7-12): use svgson stringify option to apply individual attributes https://github.com/elrumordelaluz/svgson#svgsonstringify - // the goal would be to give more styling control over individual deck map elements stringify(groupNodeWrapper, { selfClose: false, }) From 1e2f9186e032e1cc31d0d1f90752ace1d01c69a5 Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Tue, 2 Jan 2024 16:45:58 -0500 Subject: [PATCH 08/12] add getSimplestDeckConfigForProtocol legacy trash test --- .../getSimplestFlexDeckConfig.test.ts | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/shared-data/js/helpers/__tests__/getSimplestFlexDeckConfig.test.ts b/shared-data/js/helpers/__tests__/getSimplestFlexDeckConfig.test.ts index 354b3a94e21..293433b0457 100644 --- a/shared-data/js/helpers/__tests__/getSimplestFlexDeckConfig.test.ts +++ b/shared-data/js/helpers/__tests__/getSimplestFlexDeckConfig.test.ts @@ -17,7 +17,6 @@ const RUN_TIME_COMMAND_STUB_MIXIN: Pick< status: 'succeeded', } -// TODO(bh, 2023-12-4): test cases for legacy fixed trash describe('getSimplestDeckConfigForProtocol', () => { it('returns simplest deck if no commands alter addressable areas', () => { expect(getSimplestDeckConfigForProtocol(null)).toEqual( @@ -151,4 +150,27 @@ describe('getSimplestDeckConfigForProtocol', () => { }, ]) }) + it('returns deck with trash in A3 when legacy trash labware is present', () => { + const cutoutConfigs = getSimplestDeckConfigForProtocol(({ + commands: [], + labware: [ + { + id: 'trash_id', + definitionUri: 'opentrons/opentrons_1_trash_3200ml_fixed/1', + displayName: 'Trash', + loadName: 'opentrons_1_trash_3200ml_fixed', + location: { slotName: 'A3' }, + }, + ], + } as unknown) as CompletedProtocolAnalysis) + expect(cutoutConfigs).toEqual([ + ...FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC.slice(0, 8), + { + cutoutId: 'cutoutA3', + cutoutFixtureId: 'trashBinAdapter', + requiredAddressableAreas: ['movableTrashA3'], + }, + ...FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC.slice(9), + ]) + }) }) From e7a5c12e74555fce26efc2c67a6a02aae1665586 Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Tue, 2 Jan 2024 17:44:20 -0500 Subject: [PATCH 09/12] type addressable area names as AddressableAreaName for commands --- .../utils/getAddressableAreaDisplayName.ts | 5 +- app/src/resources/deck_configuration/hooks.ts | 2 +- .../src/file-data/selectors/fileCreator.ts | 6 +- .../src/steplist/fieldLevel/index.ts | 3 +- shared-data/command/types/gantry.ts | 3 +- shared-data/command/types/pipetting.ts | 3 +- shared-data/command/types/setup.ts | 5 +- .../helpers/getAddressableAreasInProtocol.ts | 69 ++++---- .../js/helpers/getSimplestFlexDeckConfig.ts | 2 +- .../__tests__/wasteChuteCommandsUtil.test.ts | 4 +- .../atomic/moveToAddressableArea.ts | 4 +- .../atomic/moveToAddressableAreaForDropTip.ts | 4 +- .../src/fixtures/commandFixtures.ts | 3 +- step-generation/src/utils/misc.ts | 7 +- .../src/utils/movableTrashCommandsUtil.ts | 161 +++++++++--------- .../src/utils/wasteChuteCommandsUtil.ts | 3 +- 16 files changed, 149 insertions(+), 135 deletions(-) diff --git a/app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts b/app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts index 27872299aef..cba0f3ca2cd 100644 --- a/app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts +++ b/app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts @@ -1,4 +1,5 @@ import type { + AddressableAreaName, CompletedProtocolAnalysis, MoveToAddressableAreaParams, } from '@opentrons/shared-data' @@ -32,7 +33,9 @@ export function getAddressableAreaDisplayName( else return addressableAreaName } -const getMovableTrashSlot = (addressableAreaName: string): string => { +const getMovableTrashSlot = ( + addressableAreaName: AddressableAreaName +): string => { switch (addressableAreaName) { case 'movableTrashA1': return 'A1' diff --git a/app/src/resources/deck_configuration/hooks.ts b/app/src/resources/deck_configuration/hooks.ts index a05c5e68f62..05be68a3e8a 100644 --- a/app/src/resources/deck_configuration/hooks.ts +++ b/app/src/resources/deck_configuration/hooks.ts @@ -35,7 +35,7 @@ export function useDeckConfigurationCompatibility( const deckDef = getDeckDefFromRobotType(robotType) const allAddressableAreas = protocolAnalysis != null - ? getAddressableAreasInProtocol(protocolAnalysis) + ? getAddressableAreasInProtocol(protocolAnalysis, deckDef) : [] const labwareOnDeck = protocolAnalysis != null ? getLabwareOnDeck(protocolAnalysis) : [] diff --git a/protocol-designer/src/file-data/selectors/fileCreator.ts b/protocol-designer/src/file-data/selectors/fileCreator.ts index f7b75d84ff5..2a842c50072 100644 --- a/protocol-designer/src/file-data/selectors/fileCreator.ts +++ b/protocol-designer/src/file-data/selectors/fileCreator.ts @@ -44,6 +44,7 @@ import { COLUMN_4_SLOTS, } from '@opentrons/step-generation' import type { + AddressableAreaName, CommandAnnotationV1Mixin, CommandV8Mixin, CreateCommand, @@ -281,7 +282,10 @@ export const createFile: Selector = createSelector( } else if (isOnAdapter) { location = { labwareId: labware.slot } } else if (isAddressableAreaName) { - location = { addressableAreaName: labware.slot } + // TODO(bh, 2024-01-02): check slots against addressable areas via the deck definition + location = { + addressableAreaName: labware.slot as AddressableAreaName, + } } else if (labware.slot === 'offDeck') { location = 'offDeck' } diff --git a/protocol-designer/src/steplist/fieldLevel/index.ts b/protocol-designer/src/steplist/fieldLevel/index.ts index c9984c9310d..03ff1751a45 100644 --- a/protocol-designer/src/steplist/fieldLevel/index.ts +++ b/protocol-designer/src/steplist/fieldLevel/index.ts @@ -152,7 +152,8 @@ const getLabwareLocation = ( return { addressableAreaName: isWasteChuteLocation ? 'gripperWasteChute' - : newLocationString, + : // TODO(bh, 2024-01-02): check new location against addressable areas via the deck definition + (newLocationString as AddressableAreaName), } } else { return { slotName: newLocationString } diff --git a/shared-data/command/types/gantry.ts b/shared-data/command/types/gantry.ts index c016cf66678..1762c608499 100644 --- a/shared-data/command/types/gantry.ts +++ b/shared-data/command/types/gantry.ts @@ -1,4 +1,5 @@ import type { CommonCommandRunTimeInfo, CommonCommandCreateInfo } from '.' +import type { AddressableAreaName } from '../../deck' import type { Coordinates, MotorAxes, @@ -175,7 +176,7 @@ interface AddressableOffsetVector { } export interface MoveToAddressableAreaParams { pipetteId: string - addressableAreaName: string + addressableAreaName: AddressableAreaName offset: AddressableOffsetVector speed?: number minimumZHeight?: number diff --git a/shared-data/command/types/pipetting.ts b/shared-data/command/types/pipetting.ts index 34ba48b0d93..a7364add50b 100644 --- a/shared-data/command/types/pipetting.ts +++ b/shared-data/command/types/pipetting.ts @@ -1,3 +1,4 @@ +import type { AddressableAreaName } from '../../deck' import type { CommonCommandRunTimeInfo, CommonCommandCreateInfo } from '.' export type PipettingRunTimeCommand = | AspirateInPlaceRunTimeCommand @@ -224,7 +225,7 @@ export interface DropTipInPlaceParams { export interface MoveToAddressableAreaForDropTipParams { pipetteId: string - addressableAreaName: string + addressableAreaName: AddressableAreaName offset?: AddressableOffsetVector alternateDropLocation?: boolean speed?: number diff --git a/shared-data/command/types/setup.ts b/shared-data/command/types/setup.ts index c0c05b1e8a7..b3f54cdaad8 100644 --- a/shared-data/command/types/setup.ts +++ b/shared-data/command/types/setup.ts @@ -1,4 +1,5 @@ import type { + AddressableAreaName, CommonCommandRunTimeInfo, CommonCommandCreateInfo, LabwareDefinition2, @@ -92,13 +93,13 @@ export type LabwareLocation = | { slotName: string } | { moduleId: string } | { labwareId: string } - | { addressableAreaName: string } + | { addressableAreaName: AddressableAreaName } export type NonStackedLocation = | 'offDeck' | { slotName: string } | { moduleId: string } - | { addressableAreaName: string } + | { addressableAreaName: AddressableAreaName } export interface ModuleLocation { slotName: string diff --git a/shared-data/js/helpers/getAddressableAreasInProtocol.ts b/shared-data/js/helpers/getAddressableAreasInProtocol.ts index 66ae4287c32..1ca3013930a 100644 --- a/shared-data/js/helpers/getAddressableAreasInProtocol.ts +++ b/shared-data/js/helpers/getAddressableAreasInProtocol.ts @@ -1,11 +1,12 @@ import { MOVABLE_TRASH_A3_ADDRESSABLE_AREA } from '../constants' - +import { getAddressableAreaFromSlotId } from '../fixtures' import type { AddressableAreaName } from '../../deck' import type { ProtocolAnalysisOutput } from '../../protocol' -import type { CompletedProtocolAnalysis } from '../types' +import type { CompletedProtocolAnalysis, DeckDefinition } from '../types' export function getAddressableAreasInProtocol( - protocolAnalysis: CompletedProtocolAnalysis | ProtocolAnalysisOutput + protocolAnalysis: CompletedProtocolAnalysis | ProtocolAnalysisOutput, + deckDef: DeckDefinition ): AddressableAreaName[] { const { commands, labware } = protocolAnalysis @@ -19,22 +20,23 @@ export function getAddressableAreasInProtocol( command.params.newLocation.slotName as AddressableAreaName ) ) { - return [ - ...acc, - command.params.newLocation.slotName as AddressableAreaName, - ] + const addressableAreaName = getAddressableAreaFromSlotId( + command.params.newLocation.slotName, + deckDef + )?.id + + if (addressableAreaName == null) { + return acc + } else { + return [...acc, addressableAreaName] + } } else if ( command.commandType === 'moveLabware' && command.params.newLocation !== 'offDeck' && 'addressableAreaName' in command.params.newLocation && - !acc.includes( - command.params.newLocation.addressableAreaName as AddressableAreaName - ) + !acc.includes(command.params.newLocation.addressableAreaName) ) { - return [ - ...acc, - command.params.newLocation.addressableAreaName as AddressableAreaName, - ] + return [...acc, command.params.newLocation.addressableAreaName] } else if ( (command.commandType === 'loadLabware' || command.commandType === 'loadModule') && @@ -42,47 +44,38 @@ export function getAddressableAreasInProtocol( 'slotName' in command.params.location && !acc.includes(command.params.location.slotName as AddressableAreaName) ) { + const addressableAreaName = getAddressableAreaFromSlotId( + command.params.location.slotName, + deckDef + )?.id + // do not add addressable area name for legacy trash labware if ( - 'loadName' in command.params && - command.params.loadName === 'opentrons_1_trash_3200ml_fixed' + addressableAreaName == null || + ('loadName' in command.params && + command.params.loadName === 'opentrons_1_trash_3200ml_fixed') ) { return acc } else { - // TODO(bh, 2023-12-4): use getAddressableAreaFromSlotId helper - return [ - ...acc, - command.params.location.slotName as AddressableAreaName, - ] + return [...acc, addressableAreaName] } } else if ( command.commandType === 'loadLabware' && command.params.location !== 'offDeck' && 'addressableAreaName' in command.params.location && - !acc.includes( - command.params.location.addressableAreaName as AddressableAreaName - ) + !acc.includes(command.params.location.addressableAreaName) ) { - return [ - ...acc, - command.params.location.addressableAreaName as AddressableAreaName, - ] + return [...acc, command.params.location.addressableAreaName] } else if ( command.commandType === 'moveToAddressableArea' && - !acc.includes(command.params.addressableAreaName as AddressableAreaName) + !acc.includes(command.params.addressableAreaName) ) { - return [ - ...acc, - command.params.addressableAreaName as AddressableAreaName, - ] + return [...acc, command.params.addressableAreaName] } else if ( command.commandType === 'moveToAddressableAreaForDropTip' && - !acc.includes(command.params.addressableAreaName as AddressableAreaName) + !acc.includes(command.params.addressableAreaName) ) { - return [ - ...acc, - command.params.addressableAreaName as AddressableAreaName, - ] + return [...acc, command.params.addressableAreaName] } else { return acc } diff --git a/shared-data/js/helpers/getSimplestFlexDeckConfig.ts b/shared-data/js/helpers/getSimplestFlexDeckConfig.ts index c922d5fc64f..d8a16033050 100644 --- a/shared-data/js/helpers/getSimplestFlexDeckConfig.ts +++ b/shared-data/js/helpers/getSimplestFlexDeckConfig.ts @@ -43,7 +43,7 @@ export function getSimplestDeckConfigForProtocol( const addressableAreas = protocolAnalysis != null - ? getAddressableAreasInProtocol(protocolAnalysis) + ? getAddressableAreasInProtocol(protocolAnalysis, deckDef) : [] const simplestDeckConfig = addressableAreas.reduce< CutoutConfigProtocolSpec[] diff --git a/step-generation/src/__tests__/wasteChuteCommandsUtil.test.ts b/step-generation/src/__tests__/wasteChuteCommandsUtil.test.ts index 7abca61ff39..9377cbaecd2 100644 --- a/step-generation/src/__tests__/wasteChuteCommandsUtil.test.ts +++ b/step-generation/src/__tests__/wasteChuteCommandsUtil.test.ts @@ -19,13 +19,13 @@ const curryCommandCreatorMock = curryCommandCreator as jest.MockedFunction< > const mockWasteChuteId = 'mockWasteChuteId' -const mockAddressableAreaName = 'mockName' +const mockAddressableAreaName: 'A3' = 'A3' const mockId = 'mockId' let invariantContext = makeContext() const args = { pipetteId: mockId, - addressableAreaName: 'mockName', + addressableAreaName: mockAddressableAreaName, volume: 10, flowRate: 10, prevRobotState: getInitialRobotStateStandard(invariantContext), diff --git a/step-generation/src/commandCreators/atomic/moveToAddressableArea.ts b/step-generation/src/commandCreators/atomic/moveToAddressableArea.ts index 085102b45fe..be5314fba36 100644 --- a/step-generation/src/commandCreators/atomic/moveToAddressableArea.ts +++ b/step-generation/src/commandCreators/atomic/moveToAddressableArea.ts @@ -1,9 +1,11 @@ +import type { AddressableAreaName } from '@opentrons/shared-data' + import { uuid } from '../../utils' import type { CommandCreator } from '../../types' export interface MoveToAddressableAreaArgs { pipetteId: string - addressableAreaName: string + addressableAreaName: AddressableAreaName } export const moveToAddressableArea: CommandCreator = ( args, diff --git a/step-generation/src/commandCreators/atomic/moveToAddressableAreaForDropTip.ts b/step-generation/src/commandCreators/atomic/moveToAddressableAreaForDropTip.ts index 617e8a71bd5..103f3db43db 100644 --- a/step-generation/src/commandCreators/atomic/moveToAddressableAreaForDropTip.ts +++ b/step-generation/src/commandCreators/atomic/moveToAddressableAreaForDropTip.ts @@ -1,9 +1,11 @@ +import type { AddressableAreaName } from '@opentrons/shared-data' + import { uuid } from '../../utils' import type { CommandCreator } from '../../types' export interface MoveToAddressableAreaForDropTipArgs { pipetteId: string - addressableAreaName: string + addressableAreaName: AddressableAreaName } export const moveToAddressableAreaForDropTip: CommandCreator = ( args, diff --git a/step-generation/src/fixtures/commandFixtures.ts b/step-generation/src/fixtures/commandFixtures.ts index 83930c1c86e..647844c8657 100644 --- a/step-generation/src/fixtures/commandFixtures.ts +++ b/step-generation/src/fixtures/commandFixtures.ts @@ -1,5 +1,6 @@ import { tiprackWellNamesFlat } from './data' import { + AddressableAreaName, AspDispAirgapParams, BlowoutParams, CreateCommand, @@ -364,7 +365,7 @@ export const dropTipInPlaceHelper = (params?: { }) export const moveToAddressableAreaHelper = (params?: { pipetteId?: string - addressableAreaName: string + addressableAreaName: AddressableAreaName }): CreateCommand => ({ commandType: 'moveToAddressableArea', key: expect.any(String), diff --git a/step-generation/src/utils/misc.ts b/step-generation/src/utils/misc.ts index f2a4f952258..f6e46fcfeee 100644 --- a/step-generation/src/utils/misc.ts +++ b/step-generation/src/utils/misc.ts @@ -23,7 +23,10 @@ import { import { blowout } from '../commandCreators/atomic/blowout' import { curryCommandCreator } from './curryCommandCreator' import { movableTrashCommandsUtil } from './movableTrashCommandsUtil' -import type { LabwareDefinition2 } from '@opentrons/shared-data' +import type { + AddressableAreaName, + LabwareDefinition2, +} from '@opentrons/shared-data' import type { BlowoutParams } from '@opentrons/shared-data/protocol/types/schemaV4' import type { AdditionalEquipmentEntities, @@ -46,7 +49,7 @@ type trashOrLabware = 'wasteChute' | 'trashBin' | 'labware' | null export function getWasteChuteAddressableAreaNamePip( channels: PipetteChannels -): string { +): AddressableAreaName { switch (channels) { case 1: { return ONE_CHANNEL_WASTE_CHUTE_ADDRESSABLE_AREA diff --git a/step-generation/src/utils/movableTrashCommandsUtil.ts b/step-generation/src/utils/movableTrashCommandsUtil.ts index 0a7ad544487..4f3fde6a5a9 100644 --- a/step-generation/src/utils/movableTrashCommandsUtil.ts +++ b/step-generation/src/utils/movableTrashCommandsUtil.ts @@ -69,93 +69,94 @@ export const movableTrashCommandsUtil = ( )?.providesAddressableAreas ?? null } - const addressableAreaName = (trashLocation != null && cutouts != null - ? cutouts[trashLocation] ?? [''] - : [''])[0] + let inPlaceCommands: CurriedCommandCreator[] = [] + + const addressableAreaName = + trashLocation != null && cutouts != null ? cutouts[trashLocation][0] : null - if (addressableAreaName === '') { + if (addressableAreaName == null) { console.error( `expected to find addressableAreaName with trashLocation ${trashLocation} but could not` ) - } - - let inPlaceCommands: CurriedCommandCreator[] = [] - switch (type) { - case 'airGap': { - inPlaceCommands = - flowRate != null && volume != null - ? [ - curryCommandCreator(moveToAddressableArea, { - pipetteId, - addressableAreaName, - }), - curryCommandCreator(aspirateInPlace, { - pipetteId, - volume, - flowRate, - }), - ] - : [] + } else { + switch (type) { + case 'airGap': { + inPlaceCommands = + flowRate != null && volume != null + ? [ + curryCommandCreator(moveToAddressableArea, { + pipetteId, + addressableAreaName, + }), + curryCommandCreator(aspirateInPlace, { + pipetteId, + volume, + flowRate, + }), + ] + : [] - break - } - case 'dropTip': { - inPlaceCommands = - prevRobotState != null && !prevRobotState.tipState.pipettes[pipetteId] - ? [] - : [ - curryCommandCreator(moveToAddressableAreaForDropTip, { - pipetteId, - addressableAreaName, - }), - curryCommandCreator(dropTipInPlace, { - pipetteId, - }), - ] + break + } + case 'dropTip': { + inPlaceCommands = + prevRobotState != null && !prevRobotState.tipState.pipettes[pipetteId] + ? [] + : [ + curryCommandCreator(moveToAddressableAreaForDropTip, { + pipetteId, + addressableAreaName, + }), + curryCommandCreator(dropTipInPlace, { + pipetteId, + }), + ] - break - } - case 'dispense': { - inPlaceCommands = - flowRate != null && volume != null - ? [ - curryCommandCreator(moveToAddressableArea, { - pipetteId, - addressableAreaName, - }), - curryCommandCreator(dispenseInPlace, { - pipetteId, - volume, - flowRate, - }), - ] - : [] - break - } - case 'blowOut': { - inPlaceCommands = - flowRate != null - ? [ - curryCommandCreator(moveToAddressableArea, { - pipetteId, - addressableAreaName, - }), - curryCommandCreator(blowOutInPlace, { - pipetteId, - flowRate, - }), - ] - : [] - break - } - case 'moveToWell': { - inPlaceCommands = [ - curryCommandCreator(moveToAddressableArea, { - pipetteId, - addressableAreaName, - }), - ] + break + } + case 'dispense': { + inPlaceCommands = + flowRate != null && volume != null + ? [ + curryCommandCreator(moveToAddressableArea, { + pipetteId, + addressableAreaName, + }), + curryCommandCreator(dispenseInPlace, { + pipetteId, + volume, + flowRate, + }), + ] + : [] + break + } + case 'blowOut': { + inPlaceCommands = + flowRate != null + ? [ + curryCommandCreator(moveToAddressableArea, { + pipetteId, + addressableAreaName, + }), + curryCommandCreator(blowOutInPlace, { + pipetteId, + flowRate, + }), + ] + : [] + break + } + case 'moveToWell': { + inPlaceCommands = [ + curryCommandCreator(moveToAddressableArea, { + pipetteId, + addressableAreaName, + }), + ] + } } } + return inPlaceCommands } diff --git a/step-generation/src/utils/wasteChuteCommandsUtil.ts b/step-generation/src/utils/wasteChuteCommandsUtil.ts index 84aab98b5ad..2ceeb2b19b5 100644 --- a/step-generation/src/utils/wasteChuteCommandsUtil.ts +++ b/step-generation/src/utils/wasteChuteCommandsUtil.ts @@ -6,6 +6,7 @@ import { moveToAddressableArea, } from '../commandCreators/atomic' import { curryCommandCreator } from './curryCommandCreator' +import type { AddressableAreaName } from '@opentrons/shared-data' import type { RobotState, CurriedCommandCreator } from '../types' export type WasteChuteCommandsTypes = @@ -17,7 +18,7 @@ export type WasteChuteCommandsTypes = interface WasteChuteCommandArgs { type: WasteChuteCommandsTypes pipetteId: string - addressableAreaName: string + addressableAreaName: AddressableAreaName prevRobotState: RobotState volume?: number flowRate?: number From b8f59375b0bcefc3da0240d4925b2a639530f144 Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Fri, 5 Jan 2024 11:27:24 -0500 Subject: [PATCH 10/12] update some tests --- .../SetupLiquids/SetupLiquidsMap.tsx | 1 - .../__tests__/SetupLiquidsMap.test.tsx | 4 +- .../SetupModuleAndDeck/SetupModulesList.tsx | 1 - .../__tests__/SetupFixtureList.test.tsx | 62 ++++++++++++++----- .../ModulesAndDeckMapViewModal.test.tsx | 8 ++- 5 files changed, 55 insertions(+), 21 deletions(-) diff --git a/app/src/organisms/Devices/ProtocolRun/SetupLiquids/SetupLiquidsMap.tsx b/app/src/organisms/Devices/ProtocolRun/SetupLiquids/SetupLiquidsMap.tsx index 965c36f100c..0519b557065 100644 --- a/app/src/organisms/Devices/ProtocolRun/SetupLiquids/SetupLiquidsMap.tsx +++ b/app/src/organisms/Devices/ProtocolRun/SetupLiquids/SetupLiquidsMap.tsx @@ -123,7 +123,6 @@ export function SetupLiquidsMap( > { .mockReturnValue({}) when(mockGetSimplestDeckConfigForProtocol) .calledWith(mockProtocolAnalysis) - // TODO(bh, 2023-11-13): mock the cutout config protocol spec - .mockReturnValue([]) + .mockReturnValue(FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC) when(mockParseLiquidsInLoadOrder) .calledWith( mockProtocolAnalysis.liquids as any, diff --git a/app/src/organisms/Devices/ProtocolRun/SetupModuleAndDeck/SetupModulesList.tsx b/app/src/organisms/Devices/ProtocolRun/SetupModuleAndDeck/SetupModulesList.tsx index 8affe91872e..238e05bfb4c 100644 --- a/app/src/organisms/Devices/ProtocolRun/SetupModuleAndDeck/SetupModulesList.tsx +++ b/app/src/organisms/Devices/ProtocolRun/SetupModuleAndDeck/SetupModulesList.tsx @@ -366,7 +366,6 @@ export function ModulesListItem({ {showLocationConflictModal && cutoutIdForSlotName != null ? ( setShowLocationConflictModal(false)} - // TODO(bh, 2023-10-10): when module caddies are fixtures, narrow slotName to Cutout and remove type assertion cutoutId={cutoutIdForSlotName} requiredModule={moduleModel} /> diff --git a/app/src/organisms/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupFixtureList.test.tsx b/app/src/organisms/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupFixtureList.test.tsx index c94fbc73085..2653aff9316 100644 --- a/app/src/organisms/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupFixtureList.test.tsx +++ b/app/src/organisms/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupFixtureList.test.tsx @@ -1,7 +1,11 @@ import * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { renderWithProviders } from '@opentrons/components' -import { STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE } from '@opentrons/shared-data' +import { + SINGLE_RIGHT_SLOT_FIXTURE, + STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE, + TRASH_BIN_ADAPTER_FIXTURE, +} from '@opentrons/shared-data' import { i18n } from '../../../../../i18n' import { SetupFixtureList } from '../SetupFixtureList' import { NotConfiguredModal } from '../NotConfiguredModal' @@ -39,6 +43,30 @@ const mockDeckConfigCompatibility: CutoutConfigAndCompatibility[] = [ }, ] +const mockNotConfiguredDeckConfigCompatibility: CutoutConfigAndCompatibility[] = [ + { + cutoutId: 'cutoutD3', + cutoutFixtureId: SINGLE_RIGHT_SLOT_FIXTURE, + requiredAddressableAreas: ['D4'], + compatibleCutoutFixtureIds: [ + STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE, + ], + missingLabwareDisplayName: null, + }, +] + +const mockConflictDeckConfigCompatibility: CutoutConfigAndCompatibility[] = [ + { + cutoutId: 'cutoutD3', + cutoutFixtureId: TRASH_BIN_ADAPTER_FIXTURE, + requiredAddressableAreas: ['D4'], + compatibleCutoutFixtureIds: [ + STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE, + ], + missingLabwareDisplayName: null, + }, +] + const render = (props: React.ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, @@ -79,17 +107,23 @@ describe('SetupFixtureList', () => { screen.getByText('mock DeckFixtureSetupInstructionsModal') }) - // TODO(bh, 2023-11-14): implement test cases when example JSON protocol fixtures exist - // it('should render the headers and a fixture with conflicted status', () => { - // render(props) - // screen.getByText('Location conflict') - // screen.getByRole('button', { name: 'Update deck' }).click() - // screen.getByText('mock location conflict modal') - // }) - // it('should render the headers and a fixture with not configured status and button', () => { - // render(props) - // screen.getByText('Not configured') - // screen.getByRole('button', { name: 'Update deck' }).click() - // screen.getByText('mock not configured modal') - // }) + it('should render the headers and a fixture with conflicted status', () => { + props = { + deckConfigCompatibility: mockConflictDeckConfigCompatibility, + } + render(props) + screen.getByText('Location conflict') + fireEvent.click(screen.getByRole('button', { name: 'Resolve' })) + screen.getByText('mock location conflict modal') + }) + + it('should render the headers and a fixture with not configured status and button', () => { + props = { + deckConfigCompatibility: mockNotConfiguredDeckConfigCompatibility, + } + render(props) + screen.getByText('Not configured') + fireEvent.click(screen.getByRole('button', { name: 'Resolve' })) + screen.getByText('mock not configured modal') + }) }) diff --git a/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/ModulesAndDeckMapViewModal.test.tsx b/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/ModulesAndDeckMapViewModal.test.tsx index 7a403394e2f..661da3a1eb6 100644 --- a/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/ModulesAndDeckMapViewModal.test.tsx +++ b/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/ModulesAndDeckMapViewModal.test.tsx @@ -2,7 +2,10 @@ import * as React from 'react' import { when, resetAllWhenMocks } from 'jest-when' import { renderWithProviders, BaseDeck } from '@opentrons/components' -import { getSimplestDeckConfigForProtocol } from '@opentrons/shared-data' +import { + FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC, + getSimplestDeckConfigForProtocol, +} from '@opentrons/shared-data' import { i18n } from '../../../i18n' import { ModulesAndDeckMapViewModal } from '../ModulesAndDeckMapViewModal' @@ -104,8 +107,7 @@ describe('ModulesAndDeckMapViewModal', () => { protocolAnalysis: PROTOCOL_ANALYSIS, } when(mockGetSimplestDeckConfigForProtocol).mockReturnValue( - // TODO(bh, 2023-11-13): mock cutout config protocol spec - [] + FLEX_SIMPLEST_DECK_CONFIG_PROTOCOL_SPEC ) mockBaseDeck.mockReturnValue(
mock BaseDeck
) }) From fd4b942bdfcc20a1fe3b449afb87898957fb6782 Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Fri, 5 Jan 2024 14:24:46 -0500 Subject: [PATCH 11/12] add more tests --- .../useUnmatchedModulesForProtocol.test.tsx | 22 ++-- .../__tests__/FixtureTable.test.tsx | 101 ++++++++++-------- .../__tests__/utils.test.tsx | 96 ++++++++++++++++- 3 files changed, 162 insertions(+), 57 deletions(-) diff --git a/app/src/organisms/Devices/hooks/__tests__/useUnmatchedModulesForProtocol.test.tsx b/app/src/organisms/Devices/hooks/__tests__/useUnmatchedModulesForProtocol.test.tsx index be403e978fc..6fe469954b9 100644 --- a/app/src/organisms/Devices/hooks/__tests__/useUnmatchedModulesForProtocol.test.tsx +++ b/app/src/organisms/Devices/hooks/__tests__/useUnmatchedModulesForProtocol.test.tsx @@ -10,7 +10,7 @@ import { useUnmatchedModulesForProtocol, } from '..' -import type { ModuleModel, ModuleType } from '@opentrons/shared-data' +import type { ModuleDefinition } from '@opentrons/shared-data' jest.mock('../useAttachedModules') jest.mock('../useModuleRenderInfoForProtocolById') @@ -27,22 +27,22 @@ const mockUseRobot = useRobot as jest.MockedFunction const mockMagneticBlockDef = { labwareOffset: { x: 5, y: 5, z: 5 }, moduleId: 'someMagneticBlock', - model: 'magneticBlockV1' as ModuleModel, - type: 'magneticBlockType' as ModuleType, + model: 'magneticBlockV1', + type: 'magneticBlockType', compatibleWith: [], } const mockMagneticModuleDef = { labwareOffset: { x: 5, y: 5, z: 5 }, moduleId: 'someMagneticModule', - model: 'magneticModuleV2' as ModuleModel, - type: 'magneticModuleType' as ModuleType, + model: 'magneticModuleV2', + type: 'magneticModuleType', compatibleWith: [], } const mockTemperatureModuleDef = { labwareOffset: { x: 5, y: 5, z: 5 }, moduleId: 'someTempModule', - model: 'temperatureModuleV2' as ModuleModel, - type: 'temperatureModuleType' as ModuleType, + model: 'temperatureModuleV2', + type: 'temperatureModuleType', compatibleWith: ['temperatureModuleV1'], } describe('useModuleMatchResults', () => { @@ -74,7 +74,7 @@ describe('useModuleMatchResults', () => { x: 0, y: 0, z: 0, - moduleDef: mockMagneticBlockDef as any, + moduleDef: (mockMagneticBlockDef as unknown) as ModuleDefinition, nestedLabwareDef: null, nestedLabwareId: null, nestedLabwareDisplayName: null, @@ -130,7 +130,7 @@ describe('useModuleMatchResults', () => { x: 0, y: 0, z: 0, - moduleDef: mockTemperatureModuleDef as any, + moduleDef: (mockTemperatureModuleDef as unknown) as ModuleDefinition, nestedLabwareDef: null, nestedLabwareId: null, nestedLabwareDisplayName: null, @@ -157,10 +157,10 @@ describe('useModuleMatchResults', () => { x: 0, y: 0, z: 0, - moduleDef: { + moduleDef: ({ ...mockTemperatureModuleDef, compatibleWith: ['fakeModuleModel'], - } as any, + } as unknown) as ModuleDefinition, nestedLabwareDef: null, nestedLabwareId: null, nestedLabwareDisplayName: null, diff --git a/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/FixtureTable.test.tsx b/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/FixtureTable.test.tsx index b29f25b1c6a..b85b592a66b 100644 --- a/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/FixtureTable.test.tsx +++ b/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/FixtureTable.test.tsx @@ -1,17 +1,24 @@ import * as React from 'react' +import { fireEvent, screen } from '@testing-library/react' + import { renderWithProviders } from '@opentrons/components' import { FLEX_ROBOT_TYPE, + MOVABLE_TRASH_D3_ADDRESSABLE_AREA, + SINGLE_RIGHT_SLOT_FIXTURE, STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE, + TRASH_BIN_ADAPTER_FIXTURE, } from '@opentrons/shared-data' import { i18n } from '../../../i18n' +import { LocationConflictModal } from '../../../organisms/Devices/ProtocolRun/SetupModuleAndDeck/LocationConflictModal' import { useDeckConfigurationCompatibility } from '../../../resources/deck_configuration/hooks' -import { LocationConflictModal } from '../../Devices/ProtocolRun/SetupModuleAndDeck/LocationConflictModal' import { FixtureTable } from '../FixtureTable' jest.mock('../../../resources/deck_configuration/hooks') -jest.mock('../../Devices/ProtocolRun/SetupModuleAndDeck/LocationConflictModal') +jest.mock( + '../../../organisms/Devices/ProtocolRun/SetupModuleAndDeck/LocationConflictModal' +) const mockLocationConflictModal = LocationConflictModal as jest.MockedFunction< typeof LocationConflictModal @@ -55,49 +62,59 @@ describe('FixtureTable', () => { }, ]) }) + afterEach(() => { + jest.clearAllMocks() + }) it('should render table header and contents', () => { - const [{ getByText }] = render(props) - getByText('Fixture') - getByText('Location') - getByText('Status') + render(props) + screen.getByText('Fixture') + screen.getByText('Location') + screen.getByText('Status') }) + it('should render the current status - configured', () => { - props = { - ...props, - // TODO(bh, 2023-11-13): mock load labware etc commands - mostRecentAnalysis: { commands: [], labware: [] } as any, - } - const [{ getByText }] = render(props) - getByText('Configured') + render(props) + screen.getByText('Configured') + }) + + it('should render the current status - not configured', () => { + mockUseDeckConfigurationCompatibility.mockReturnValue([ + { + cutoutId: 'cutoutD3', + cutoutFixtureId: SINGLE_RIGHT_SLOT_FIXTURE, + requiredAddressableAreas: [MOVABLE_TRASH_D3_ADDRESSABLE_AREA], + compatibleCutoutFixtureIds: [TRASH_BIN_ADAPTER_FIXTURE], + missingLabwareDisplayName: null, + }, + ]) + + render(props) + + screen.getByText('Not configured') + fireEvent.click(screen.getByText('Configure')) + expect(mockSetCutoutId).toHaveBeenCalledWith('cutoutD3') + expect(mockSetSetupScreen).toHaveBeenCalledWith('deck configuration') + expect(mockSetProvidedFixtureOptions).toHaveBeenCalledWith([ + TRASH_BIN_ADAPTER_FIXTURE, + ]) + }) + + it('should render the current status - conflicting', () => { + mockUseDeckConfigurationCompatibility.mockReturnValue([ + { + cutoutId: 'cutoutD3', + cutoutFixtureId: STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE, + requiredAddressableAreas: [MOVABLE_TRASH_D3_ADDRESSABLE_AREA], + compatibleCutoutFixtureIds: [TRASH_BIN_ADAPTER_FIXTURE], + missingLabwareDisplayName: null, + }, + ]) + + render(props) + + screen.getByText('Location conflict') + fireEvent.click(screen.getByText('Resolve')) + screen.getByText('mock location conflict modal') }) - // TODO(bh, 2023-11-14): implement test cases when example JSON protocol fixtures exist - // it('should render the current status - not configured', () => { - // props = { - // ...props, - // mostRecentAnalysis: { commands: [] } as any, - // } - // const [{ getByText }] = render(props) - // getByText('Not configured') - // }) - // it('should render the current status - conflicting', () => { - // props = { - // ...props, - // mostRecentAnalysis: { commands: [] } as any, - // } - // const [{ getByText, getAllByText }] = render(props) - // getByText('Location conflict').click() - // getAllByText('mock location conflict modal') - // }) - // it('should call a mock function when tapping not configured row', () => { - // props = { - // ...props, - // mostRecentAnalysis: { commands: [] } as any, - // } - // const [{ getByText }] = render(props) - // getByText('Not configured').click() - // expect(mockSetCutoutId).toHaveBeenCalledWith('cutoutD3') - // expect(mockSetSetupScreen).toHaveBeenCalledWith('deck configuration') - // expect(mockSetProvidedFixtureOptions).toHaveBeenCalledWith(['wasteChute']) - // }) }) diff --git a/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/utils.test.tsx b/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/utils.test.tsx index 7999bff7505..69ae0167d00 100644 --- a/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/utils.test.tsx +++ b/app/src/organisms/ProtocolSetupModulesAndDeck/__tests__/utils.test.tsx @@ -1,15 +1,72 @@ +import { getModuleDef2 } from '@opentrons/shared-data' + +import { mockTemperatureModule } from '../../../redux/modules/__fixtures__' import { getAttachedProtocolModuleMatches, getUnmatchedModulesForProtocol, } from '../utils' +const temperatureProtocolModule = { + moduleId: 'mockTempModuleId', + x: 0, + y: 0, + z: 0, + moduleDef: getModuleDef2('temperatureModuleV1'), + nestedLabwareDef: null, + nestedLabwareId: null, + nestedLabwareDisplayName: null, + protocolLoadOrder: 0, + slotName: 'D1', +} + +const magneticProtocolModule = { + moduleId: 'mockMagneticModuleId', + x: 0, + y: 0, + z: 0, + moduleDef: getModuleDef2('magneticModuleV2'), + nestedLabwareDef: null, + nestedLabwareId: null, + nestedLabwareDisplayName: null, + protocolLoadOrder: 0, + slotName: 'D1', +} + describe('getAttachedProtocolModuleMatches', () => { it('returns no module matches when no modules attached', () => { - const result = getAttachedProtocolModuleMatches([], []) - expect(result).toEqual([]) + const result = getAttachedProtocolModuleMatches( + [], + [temperatureProtocolModule, magneticProtocolModule] + ) + expect(result).toEqual([ + { ...temperatureProtocolModule, attachedModuleMatch: null }, + { ...magneticProtocolModule, attachedModuleMatch: null }, + ]) + }) + + it('returns no module matches when no modules match', () => { + const result = getAttachedProtocolModuleMatches( + [mockTemperatureModule], + [magneticProtocolModule] + ) + expect(result).toEqual([ + { ...magneticProtocolModule, attachedModuleMatch: null }, + ]) }) - // TODO(bh, 2023-02-27): additional test coverage + it('returns module match when modules match', () => { + const result = getAttachedProtocolModuleMatches( + [mockTemperatureModule], + [temperatureProtocolModule, magneticProtocolModule] + ) + expect(result).toEqual([ + { + ...temperatureProtocolModule, + attachedModuleMatch: mockTemperatureModule, + }, + { ...magneticProtocolModule, attachedModuleMatch: null }, + ]) + }) }) describe('getUnmatchedModulesForProtocol', () => { @@ -21,5 +78,36 @@ describe('getUnmatchedModulesForProtocol', () => { }) }) - // TODO(bh, 2023-02-27): additional test coverage + it('returns no missing module ids or remaining attached modules when attached modules match', () => { + const result = getUnmatchedModulesForProtocol( + [mockTemperatureModule], + [temperatureProtocolModule] + ) + expect(result).toEqual({ + missingModuleIds: [], + remainingAttachedModules: [], + }) + }) + + it('returns missing module ids when protocol modules missing', () => { + const result = getUnmatchedModulesForProtocol( + [], + [temperatureProtocolModule, magneticProtocolModule] + ) + expect(result).toEqual({ + missingModuleIds: ['mockTempModuleId', 'mockMagneticModuleId'], + remainingAttachedModules: [], + }) + }) + + it('returns remaining attached modules when protocol modules and attached modules do not match', () => { + const result = getUnmatchedModulesForProtocol( + [mockTemperatureModule], + [magneticProtocolModule] + ) + expect(result).toEqual({ + missingModuleIds: ['mockMagneticModuleId'], + remainingAttachedModules: [mockTemperatureModule], + }) + }) }) From 0394ce2d0333322f1cab19e8e3b064cc98da9f2f Mon Sep 17 00:00:00 2001 From: Brent Hagen Date: Fri, 5 Jan 2024 14:32:25 -0500 Subject: [PATCH 12/12] optional chain array access --- step-generation/src/utils/movableTrashCommandsUtil.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/step-generation/src/utils/movableTrashCommandsUtil.ts b/step-generation/src/utils/movableTrashCommandsUtil.ts index 4f3fde6a5a9..280eda3445b 100644 --- a/step-generation/src/utils/movableTrashCommandsUtil.ts +++ b/step-generation/src/utils/movableTrashCommandsUtil.ts @@ -72,7 +72,9 @@ export const movableTrashCommandsUtil = ( let inPlaceCommands: CurriedCommandCreator[] = [] const addressableAreaName = - trashLocation != null && cutouts != null ? cutouts[trashLocation][0] : null + trashLocation != null && cutouts != null + ? cutouts[trashLocation]?.[0] ?? null + : null if (addressableAreaName == null) { console.error(