Skip to content

Commit

Permalink
fix(step-generation): h-S collision warning for Flex protocols (#15144)
Browse files Browse the repository at this point in the history
closes RQA-2596
  • Loading branch information
jerader authored and Carlos-fernandez committed May 20, 2024
1 parent e9ef396 commit 7e2409c
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 43 deletions.
21 changes: 21 additions & 0 deletions shared-data/js/helpers/__tests__/getAreFlexSlotsAdjacent.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { describe, it, expect } from 'vitest'
import { getAreFlexSlotsAdjacent } from '../getAreFlexSlotsAdjacent'

describe('getAreFlexSlotsAdjacent', () => {
it('returns false when slots are apart', () => {
const results = getAreFlexSlotsAdjacent('A1', 'A3')
expect(results).toStrictEqual(false)
})
it('returns true when slots are left/right', () => {
const results = getAreFlexSlotsAdjacent('A1', 'A2')
expect(results).toStrictEqual(true)
})
it('returns true when slots are north/south', () => {
const results = getAreFlexSlotsAdjacent('A1', 'B1')
expect(results).toStrictEqual(true)
})
it('returns true when slots are diagonal', () => {
const results = getAreFlexSlotsAdjacent('A1', 'B2')
expect(results).toStrictEqual(true)
})
})
36 changes: 36 additions & 0 deletions shared-data/js/helpers/getAreFlexSlotsAdjacent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { FLEX_GRID } from './getFlexSurroundingSlots'
import type { DeckSlotId } from '../types'

export const getAreFlexSlotsAdjacent = (
slot1: DeckSlotId,
slot2: DeckSlotId
): boolean => {
const findSlotPosition = (slot: DeckSlotId): [number, number] | null => {
for (let row = 0; row < FLEX_GRID.length; row++) {
const col = FLEX_GRID[row].indexOf(slot)
if (col !== -1) {
return [row, col]
}
}
return null
}

const pos1 = findSlotPosition(slot1)
const pos2 = findSlotPosition(slot2)

if (pos1 === null || pos2 === null) {
return false
}

const [row1, col1] = pos1
const [row2, col2] = pos2

const rowDiff = Math.abs(row1 - row2)
const colDiff = Math.abs(col1 - col2)

if ((rowDiff === 1 && colDiff <= 1) || (rowDiff === 0 && colDiff === 1)) {
return true
}

return false
}
2 changes: 1 addition & 1 deletion shared-data/js/helpers/getFlexSurroundingSlots.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { DeckSlotId } from '../types'

const FLEX_GRID = [
export const FLEX_GRID = [
['A1', 'A2', 'A3'],
['B1', 'B2', 'B3'],
['C1', 'C2', 'C3'],
Expand Down
1 change: 1 addition & 0 deletions shared-data/js/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { get96Channel384WellPlateWells } from './get96Channel384WellPlateWells'
export * from './parseProtocolData'
export * from './volume'
export * from './wellSets'
export * from './getAreFlexSlotsAdjacent'
export * from './getModuleVizDims'
export * from './getVectorDifference'
export * from './getVectorSum'
Expand Down
6 changes: 4 additions & 2 deletions step-generation/src/__tests__/aspirate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { beforeEach, describe, vi, it, expect, afterEach } from 'vitest'
import { expectTimelineError } from '../__utils__/testMatchers'
import { aspirate } from '../commandCreators/atomic/aspirate'
import {
OT2_ROBOT_TYPE,
getLabwareDefURI,
getPipetteSpecsV2,
fixtureTiprack10ul as tip10,
Expand Down Expand Up @@ -480,11 +481,12 @@ describe('aspirate', () => {
type: 'HEATER_SHAKER_EAST_WEST_LATCH_OPEN',
})
})
it('should return an error when aspirating north/south/east/west of a heater shaker while it is shaking', () => {
it('should return an error when aspirating north/south/east/west of a heater shaker while it is shaking for ot-2', () => {
when(pipetteAdjacentHeaterShakerWhileShaking)
.calledWith(
robotStateWithTip.modules,
robotStateWithTip.labware[SOURCE_LABWARE].slot
robotStateWithTip.labware[SOURCE_LABWARE].slot,
OT2_ROBOT_TYPE
)
.thenReturn(true)

Expand Down
5 changes: 3 additions & 2 deletions step-generation/src/__tests__/dispense.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { when } from 'vitest-when'
import { beforeEach, describe, it, expect, vi, afterEach } from 'vitest'
import { getPipetteSpecsV2 } from '@opentrons/shared-data'
import { OT2_ROBOT_TYPE, getPipetteSpecsV2 } from '@opentrons/shared-data'
import {
thermocyclerPipetteCollision,
pipetteIntoHeaterShakerLatchOpen,
Expand Down Expand Up @@ -283,7 +283,8 @@ describe('dispense', () => {
when(pipetteAdjacentHeaterShakerWhileShaking)
.calledWith(
robotStateWithTip.modules,
robotStateWithTip.labware[SOURCE_LABWARE].slot
robotStateWithTip.labware[SOURCE_LABWARE].slot,
OT2_ROBOT_TYPE
)
.thenReturn(true)

Expand Down
5 changes: 3 additions & 2 deletions step-generation/src/__tests__/moveToWell.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { when } from 'vitest-when'
import { beforeEach, describe, it, expect, afterEach, vi } from 'vitest'
import { getPipetteSpecsV2 } from '@opentrons/shared-data'
import { OT2_ROBOT_TYPE, getPipetteSpecsV2 } from '@opentrons/shared-data'
import { expectTimelineError } from '../__utils__/testMatchers'
import { moveToWell } from '../commandCreators/atomic/moveToWell'
import {
Expand Down Expand Up @@ -472,7 +472,8 @@ describe('moveToWell', () => {
when(pipetteAdjacentHeaterShakerWhileShaking)
.calledWith(
robotStateWithTip.modules,
robotStateWithTip.labware[SOURCE_LABWARE].slot
robotStateWithTip.labware[SOURCE_LABWARE].slot,
OT2_ROBOT_TYPE
)
.thenReturn(true)

Expand Down
29 changes: 22 additions & 7 deletions step-generation/src/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
fixtureTiprack300ul as _fixtureTiprack300ul,
fixtureP10SingleV2Specs,
fixtureP300MultiV2Specs,
OT2_ROBOT_TYPE,
} from '@opentrons/shared-data'
import { FIXED_TRASH_ID, TEMPERATURE_DEACTIVATED } from '../constants'
import {
Expand Down Expand Up @@ -995,36 +996,50 @@ describe('pipetteAdjacentHeaterShakerWhileShaking', () => {

it('should return false when there are no modules', () => {
modules = {}
expect(pipetteAdjacentHeaterShakerWhileShaking(modules, slot)).toBe(false)
expect(
pipetteAdjacentHeaterShakerWhileShaking(modules, slot, OT2_ROBOT_TYPE)
).toBe(false)
})
it('should return false when there is no heater shaker ajacent to labware', () => {
slot = '9'
expect(pipetteAdjacentHeaterShakerWhileShaking(modules, slot)).toBe(false)
expect(
pipetteAdjacentHeaterShakerWhileShaking(modules, slot, OT2_ROBOT_TYPE)
).toBe(false)
})
it('should return false when the heater shaker is not shaking', () => {
expect(pipetteAdjacentHeaterShakerWhileShaking(modules, slot)).toBe(false)
expect(
pipetteAdjacentHeaterShakerWhileShaking(modules, slot, OT2_ROBOT_TYPE)
).toBe(false)
})
it('should return true when there is a heater shaker north of labware shaking', () => {
modules.heaterShakerId.slot = '5'
;(modules.heaterShakerId.moduleState as any).targetSpeed = 300
expect(pipetteAdjacentHeaterShakerWhileShaking(modules, slot)).toBe(true)
expect(
pipetteAdjacentHeaterShakerWhileShaking(modules, slot, OT2_ROBOT_TYPE)
).toBe(true)
})
it('should return true when there is a heater shaker south of labware shaking', () => {
slot = '9'
modules.heaterShakerId.slot = '6'
;(modules.heaterShakerId.moduleState as any).targetSpeed = 300
expect(pipetteAdjacentHeaterShakerWhileShaking(modules, slot)).toBe(true)
expect(
pipetteAdjacentHeaterShakerWhileShaking(modules, slot, OT2_ROBOT_TYPE)
).toBe(true)
})
it('should return true when there is a heater shaker east of labware shaking', () => {
slot = '5'
modules.heaterShakerId.slot = '6'
;(modules.heaterShakerId.moduleState as any).targetSpeed = 300
expect(pipetteAdjacentHeaterShakerWhileShaking(modules, slot)).toBe(true)
expect(
pipetteAdjacentHeaterShakerWhileShaking(modules, slot, OT2_ROBOT_TYPE)
).toBe(true)
})
it('should return true when there is a heater shaker west of labware shaking', () => {
slot = '5'
modules.heaterShakerId.slot = '4'
;(modules.heaterShakerId.moduleState as any).targetSpeed = 300
expect(pipetteAdjacentHeaterShakerWhileShaking(modules, slot)).toBe(true)
expect(
pipetteAdjacentHeaterShakerWhileShaking(modules, slot, OT2_ROBOT_TYPE)
).toBe(true)
})
})
16 changes: 11 additions & 5 deletions step-generation/src/commandCreators/atomic/aspirate.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data'
import * as errorCreators from '../../errorCreators'
import { getPipetteWithTipMaxVol } from '../../robotStateSelectors'
import {
Expand Down Expand Up @@ -136,12 +137,17 @@ export const aspirate: CommandCreator<ExtendedAspirateParams> = (
) {
errors.push(errorCreators.heaterShakerIsShaking())
}
if (
pipetteAdjacentHeaterShakerWhileShaking(
prevRobotState.modules,
slotName,
isFlexPipette ? FLEX_ROBOT_TYPE : OT2_ROBOT_TYPE
)
) {
errors.push(errorCreators.heaterShakerNorthSouthEastWestShaking())
}

if (!isFlexPipette) {
if (
pipetteAdjacentHeaterShakerWhileShaking(prevRobotState.modules, slotName)
) {
errors.push(errorCreators.heaterShakerNorthSouthEastWestShaking())
}
if (
getIsHeaterShakerEastWestWithLatchOpen(prevRobotState.modules, slotName)
) {
Expand Down
16 changes: 10 additions & 6 deletions step-generation/src/commandCreators/atomic/dispense.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data'
import * as errorCreators from '../../errorCreators'
import {
modulePipetteCollision,
Expand Down Expand Up @@ -132,13 +133,16 @@ export const dispense: CommandCreator<ExtendedDispenseParams> = (
) {
errors.push(errorCreators.heaterShakerIsShaking())
}
if (
pipetteAdjacentHeaterShakerWhileShaking(
prevRobotState.modules,
slotName,
isFlexPipette ? FLEX_ROBOT_TYPE : OT2_ROBOT_TYPE
)
) {
errors.push(errorCreators.heaterShakerNorthSouthEastWestShaking())
}
if (!isFlexPipette) {
if (
pipetteAdjacentHeaterShakerWhileShaking(prevRobotState.modules, slotName)
) {
errors.push(errorCreators.heaterShakerNorthSouthEastWestShaking())
}

if (
getIsHeaterShakerEastWestWithLatchOpen(prevRobotState.modules, slotName)
) {
Expand Down
16 changes: 10 additions & 6 deletions step-generation/src/commandCreators/atomic/moveToWell.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data'
import * as errorCreators from '../../errorCreators'
import {
modulePipetteCollision,
Expand Down Expand Up @@ -113,13 +114,16 @@ export const moveToWell: CommandCreator<v5MoveToWellParams> = (
) {
errors.push(errorCreators.heaterShakerIsShaking())
}
if (
pipetteAdjacentHeaterShakerWhileShaking(
prevRobotState.modules,
slotName,
isFlexPipette ? FLEX_ROBOT_TYPE : OT2_ROBOT_TYPE
)
) {
errors.push(errorCreators.heaterShakerNorthSouthEastWestShaking())
}
if (!isFlexPipette) {
if (
pipetteAdjacentHeaterShakerWhileShaking(prevRobotState.modules, slotName)
) {
errors.push(errorCreators.heaterShakerNorthSouthEastWestShaking())
}

if (
getIsHeaterShakerEastWestWithLatchOpen(prevRobotState.modules, slotName)
) {
Expand Down
25 changes: 18 additions & 7 deletions step-generation/src/commandCreators/atomic/replaceTip.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { ALL, COLUMN, NozzleConfigurationStyle } from '@opentrons/shared-data'
import {
ALL,
COLUMN,
NozzleConfigurationStyle,
FLEX_ROBOT_TYPE,
OT2_ROBOT_TYPE,
} from '@opentrons/shared-data'
import { getNextTiprack } from '../../robotStateSelectors'
import * as errorCreators from '../../errorCreators'
import { COLUMN_4_SLOTS } from '../../constants'
Expand Down Expand Up @@ -200,13 +206,18 @@ export const replaceTip: CommandCreator<ReplaceTipArgs> = (
prevRobotState.labware,
prevRobotState.modules
)

if (!isFlexPipette) {
if (
pipetteAdjacentHeaterShakerWhileShaking(prevRobotState.modules, slotName)
) {
return { errors: [errorCreators.heaterShakerNorthSouthEastWestShaking()] }
if (
pipetteAdjacentHeaterShakerWhileShaking(
prevRobotState.modules,
slotName,
isFlexPipette ? FLEX_ROBOT_TYPE : OT2_ROBOT_TYPE
)
) {
return {
errors: [errorCreators.heaterShakerNorthSouthEastWestShaking()],
}
}
if (!isFlexPipette) {
if (
getIsHeaterShakerEastWestWithLatchOpen(prevRobotState.modules, slotName)
) {
Expand Down
16 changes: 11 additions & 5 deletions step-generation/src/utils/heaterShakerCollision.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import some from 'lodash/some'
import {
getAreFlexSlotsAdjacent,
getAreSlotsAdjacent,
getAreSlotsHorizontallyAdjacent,
getAreSlotsVerticallyAdjacent,
getIsLabwareAboveHeight,
HEATERSHAKER_MODULE_TYPE,
MAX_LABWARE_HEIGHT_EAST_WEST_HEATER_SHAKER_MM,
OT2_ROBOT_TYPE,
} from '@opentrons/shared-data'

import type { PipetteV2Specs } from '@opentrons/shared-data'
import type { PipetteV2Specs, RobotType } from '@opentrons/shared-data'

import type {
LabwareEntities,
Expand Down Expand Up @@ -100,16 +102,20 @@ export const pipetteIntoHeaterShakerLatchOpen = (

export const pipetteAdjacentHeaterShakerWhileShaking = (
hwModules: RobotState['modules'],
slot: DeckSlot
): boolean =>
some(
slot: DeckSlot,
robotType: RobotType
): boolean => {
return some(
hwModules,
hwModule =>
hwModule.moduleState.type === HEATERSHAKER_MODULE_TYPE &&
hwModule.moduleState.targetSpeed != null &&
hwModule.moduleState.targetSpeed > 0 &&
getAreSlotsAdjacent(hwModule.slot, slot)
(robotType === OT2_ROBOT_TYPE
? getAreSlotsAdjacent(hwModule.slot, slot)
: getAreFlexSlotsAdjacent(hwModule.slot, slot))
)
}

export const pipetteIntoHeaterShakerWhileShaking = (
modules: RobotState['modules'],
Expand Down

0 comments on commit 7e2409c

Please sign in to comment.