Skip to content

Commit

Permalink
fix(protocol-designer): if no space for trashBin, auto-generate waste… (
Browse files Browse the repository at this point in the history
#17028)

…Chute

closes RQA-3747
  • Loading branch information
jerader authored Dec 4, 2024
1 parent 06f9601 commit 2e81f29
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 24 deletions.
52 changes: 32 additions & 20 deletions protocol-designer/src/step-forms/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
THERMOCYCLER_MODULE_TYPE,
WASTE_CHUTE_ADDRESSABLE_AREAS,
MOVABLE_TRASH_ADDRESSABLE_AREAS,
WASTE_CHUTE_CUTOUT,
} from '@opentrons/shared-data'
import { rootReducer as labwareDefsRootReducer } from '../../labware-defs'
import { getCutoutIdByAddressableArea, uuid } from '../../utils'
Expand Down Expand Up @@ -45,7 +46,7 @@ import {
createPresavedStepForm,
getDeckItemIdInSlot,
getIdsInRange,
getUnoccupiedSlotForMoveableTrash,
getUnoccupiedSlotForTrash,
} from '../utils'

import type { Reducer } from 'redux'
Expand Down Expand Up @@ -1414,9 +1415,9 @@ export const additionalEquipmentInvariantProperties = handleActions<NormalizedAd
]),
]

const unoccupiedSlotForMovableTrash = hasWasteChuteCommands
const unoccupiedSlotForTrash = hasWasteChuteCommands
? ''
: getUnoccupiedSlotForMoveableTrash(
: getUnoccupiedSlotForTrash(
file,
hasWasteChuteCommands,
stagingAreaSlotNames
Expand Down Expand Up @@ -1570,7 +1571,6 @@ export const additionalEquipmentInvariantProperties = handleActions<NormalizedAd
},
}
: {}

const hardcodedTrashBinIdOt2 = `${uuid()}:fixedTrash`
const hardcodedTrashBinOt2 = {
[hardcodedTrashBinIdOt2]: {
Expand All @@ -1583,22 +1583,34 @@ export const additionalEquipmentInvariantProperties = handleActions<NormalizedAd
),
},
}
const hardcodedTrashAddressableAreaName = `movableTrash${unoccupiedSlotForMovableTrash}`
const hardcodedTrashBinIdFlex = `${uuid()}:${hardcodedTrashAddressableAreaName}`
const hardcodedTrashBinFlex = {
[hardcodedTrashBinIdFlex]: {
name: 'trashBin' as const,
id: hardcodedTrashBinIdFlex,
location: hasWasteChuteCommands
? ''
: getCutoutIdByAddressableArea(
hardcodedTrashAddressableAreaName as AddressableAreaName,
'trashBinAdapter',
FLEX_ROBOT_TYPE
),
const hardcodedTrashAddressableAreaName =
unoccupiedSlotForTrash === WASTE_CHUTE_CUTOUT
? 'wasteChute'
: `movableTrash${unoccupiedSlotForTrash}`

const hardcodedTrashIdFlex = `${uuid()}:${hardcodedTrashAddressableAreaName}`

const hardCodedTrashLocation =
unoccupiedSlotForTrash === ''
? ''
: unoccupiedSlotForTrash === WASTE_CHUTE_CUTOUT
? WASTE_CHUTE_CUTOUT
: getCutoutIdByAddressableArea(
hardcodedTrashAddressableAreaName as AddressableAreaName,
'trashBinAdapter',
FLEX_ROBOT_TYPE
)

const hardcodedTrashFlex = {
[hardcodedTrashIdFlex]: {
name:
unoccupiedSlotForTrash === WASTE_CHUTE_CUTOUT
? ('wasteChute' as const)
: ('trashBin' as const),
id: hardcodedTrashIdFlex,
location: hasWasteChuteCommands ? '' : hardCodedTrashLocation,
},
}

if (isFlex) {
if (trashBin != null) {
return {
Expand All @@ -1609,12 +1621,12 @@ export const additionalEquipmentInvariantProperties = handleActions<NormalizedAd
...stagingAreas,
}
} else if (trashBin == null && !hasWasteChuteCommands) {
// always hardcode a trash bin when no pipetting command is provided since return tip
// always hardcode a trash bin or waste chute when no pipetting command is provided since return tip
// is not supported
return {
...state,
...gripper,
...hardcodedTrashBinFlex,
...hardcodedTrashFlex,
...wasteChute,
...stagingAreas,
}
Expand Down
233 changes: 230 additions & 3 deletions protocol-designer/src/step-forms/test/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, it, expect } from 'vitest'
import { getIdsInRange, getUnoccupiedSlotForMoveableTrash } from '../utils'
import { WASTE_CHUTE_CUTOUT } from '@opentrons/shared-data'
import { getIdsInRange, getUnoccupiedSlotForTrash } from '../utils'
import type { AddressableAreaName, CreateCommand } from '@opentrons/shared-data'

describe('getIdsInRange', () => {
Expand Down Expand Up @@ -31,7 +32,7 @@ describe('getIdsInRange', () => {
expect(getIdsInRange(orderedIds, 'T', 'T')).toEqual(['T'])
})
})
describe('getUnoccupiedSlotForMoveableTrash', () => {
describe('getUnoccupiedSlotForTrash', () => {
it('returns slot C1 when all other slots are occupied by modules, labware, moveLabware, and staging areas', () => {
const mockPDFile: any = {
commands: [
Expand Down Expand Up @@ -146,11 +147,237 @@ describe('getUnoccupiedSlotForMoveableTrash', () => {
const mockHasWasteChuteCommands = false

expect(
getUnoccupiedSlotForMoveableTrash(
getUnoccupiedSlotForTrash(
mockPDFile,
mockHasWasteChuteCommands,
mockStagingAreaSlotNames
)
).toStrictEqual('C3')
})
it('returns cutoutD3 for waste chute when every slot is occupied except for D3 on a staging area', () => {
const mockPDFile: any = {
commands: [
{
key: '159e778d-0fc5-4d24-a662-b1e59a7babda',
commandType: 'loadModule',
params: {
model: 'thermocyclerModuleV2',
location: { slotName: 'B1' },
moduleId:
'8932e104-7d57-42cf-88e4-ade334c84a76:thermocyclerModuleType',
},
},
{
key: '7d1fdcce-fa27-4520-8f97-a901751a4396',
commandType: 'loadModule',
params: {
model: 'temperatureModuleV2',
location: { slotName: 'C1' },
moduleId:
'2944a6a5-45f7-4d96-a4a2-d2853206a9f0:temperatureModuleType',
},
},
{
key: '1c223945-bfa3-4174-9923-5ed84afd1820',
commandType: 'loadModule',
params: {
model: 'heaterShakerModuleV1',
location: { slotName: 'D1' },
moduleId:
'528620a6-6eb9-4000-bce3-a58809e16d4c:heaterShakerModuleType',
},
},
{
key: 'e06d0fd5-2ca8-4d0a-bcfd-4121849604da',
commandType: 'loadModule',
params: {
model: 'magneticBlockV1',
location: { slotName: 'D2' },
moduleId: 'c8f8c89f-06df-468c-895d-33006db69beb:magneticBlockType',
},
},
{
key: 'f49ebdff-9780-4ca0-994c-2d2dd7c04b1d',
commandType: 'loadLabware',
params: {
displayName: 'Opentrons 96 Well Aluminum Block',
labwareId:
'a69bcf2e-9461-4d43-be63-f3b8db66e5e7:opentrons/opentrons_96_well_aluminum_block/1',
loadName: 'opentrons_96_well_aluminum_block',
namespace: 'opentrons',
version: 1,
location: {
moduleId:
'2944a6a5-45f7-4d96-a4a2-d2853206a9f0:temperatureModuleType',
},
},
},
{
key: 'dda244f9-ff80-4ede-a585-1a546a88ee77',
commandType: 'loadLabware',
params: {
displayName: 'Opentrons 96 PCR Heater-Shaker Adapter',
labwareId:
'723a9551-ebba-4b4a-a92e-8d1fa0e813df:opentrons/opentrons_96_pcr_adapter/1',
loadName: 'opentrons_96_pcr_adapter',
namespace: 'opentrons',
version: 1,
location: {
moduleId:
'528620a6-6eb9-4000-bce3-a58809e16d4c:heaterShakerModuleType',
},
},
},
{
key: '8c28ac95-c8d0-4481-8204-26b1babb54bf',
commandType: 'loadLabware',
params: {
displayName: 'Opentrons Flex 96 Tip Rack 50 µL',
labwareId:
'c80cffe7-d89d-430e-ba96-3c12f879e993:opentrons/opentrons_flex_96_tiprack_50ul/1',
loadName: 'opentrons_flex_96_tiprack_50ul',
namespace: 'opentrons',
version: 1,
location: { slotName: 'C3' },
},
},
{
key: 'f0357fde-125a-464c-98ed-b1b9492daab8',
commandType: 'loadLabware',
params: {
displayName: 'Opentrons Flex 96 Filter Tip Rack 200 µL (1)',
labwareId:
'0a2d4b6f-a43d-428a-98f2-284809596776:opentrons/opentrons_flex_96_filtertiprack_200ul/1',
loadName: 'opentrons_flex_96_filtertiprack_200ul',
namespace: 'opentrons',
version: 1,
location: { slotName: 'A3' },
},
},
{
key: 'e27ba758-8d28-486f-a443-6e2276842ad0',
commandType: 'loadLabware',
params: {
displayName: 'Opentrons Flex 96 Filter Tip Rack 200 µL (2)',
labwareId:
'417a6bb2-8831-4b4d-840b-7d9329606865:opentrons/opentrons_flex_96_filtertiprack_200ul/1',
loadName: 'opentrons_flex_96_filtertiprack_200ul',
namespace: 'opentrons',
version: 1,
location: { slotName: 'B3' },
},
},
{
key: '37848c2a-4a1b-44f0-851a-d264368c47f8',
commandType: 'loadLabware',
params: {
displayName: 'Opentrons Flex 96 Filter Tip Rack 200 µL (3)',
labwareId:
'ebb13651-0a60-4f42-ab85-f7084aeb0c08:opentrons/opentrons_flex_96_filtertiprack_200ul/1',
loadName: 'opentrons_flex_96_filtertiprack_200ul',
namespace: 'opentrons',
version: 1,
location: { slotName: 'A2' },
},
},
{
key: '768626df-b249-4d68-8f95-193b03113457',
commandType: 'loadLabware',
params: {
displayName: 'Opentrons Flex 96 Filter Tip Rack 200 µL (4)',
labwareId:
'b17e8c1b-a308-4eaa-a852-10ad300ddea8:opentrons/opentrons_flex_96_filtertiprack_200ul/1',
loadName: 'opentrons_flex_96_filtertiprack_200ul',
namespace: 'opentrons',
version: 1,
location: { slotName: 'B2' },
},
},
{
key: 'b12a4e6e-7ffc-421f-a2b6-44ae49d6f7bf',
commandType: 'loadLabware',
params: {
displayName: 'Reagent Plate',
labwareId:
'aab3280f-6e7b-4e60-8326-c1d38999e08f:opentrons/opentrons_96_wellplate_200ul_pcr_full_skirt/2',
loadName: 'opentrons_96_wellplate_200ul_pcr_full_skirt',
namespace: 'opentrons',
version: 2,
location: {
labwareId:
'a69bcf2e-9461-4d43-be63-f3b8db66e5e7:opentrons/opentrons_96_well_aluminum_block/1',
},
},
},
{
key: 'e6863a1e-8aa0-4484-9aff-74ea9195a815',
commandType: 'loadLabware',
params: {
displayName: 'Sample Plate 1',
labwareId:
'8e755287-33cb-483f-b525-fff876893754:opentrons/opentrons_96_wellplate_200ul_pcr_full_skirt/2',
loadName: 'opentrons_96_wellplate_200ul_pcr_full_skirt',
namespace: 'opentrons',
version: 2,
location: {
labwareId:
'723a9551-ebba-4b4a-a92e-8d1fa0e813df:opentrons/opentrons_96_pcr_adapter/1',
},
},
},
{
key: 'b29f48ef-3b20-457e-8499-df709818c47f',
commandType: 'loadLabware',
params: {
displayName: 'NEST 96 Deep Well Plate 2mL',
labwareId:
'f0d30267-b0f6-493a-b0ea-70303428fa83:opentrons/nest_96_wellplate_2ml_deep/2',
loadName: 'nest_96_wellplate_2ml_deep',
namespace: 'opentrons',
version: 2,
location: {
moduleId:
'c8f8c89f-06df-468c-895d-33006db69beb:magneticBlockType',
},
},
},
{
key: '50be2f72-c7bc-4fd4-b10c-2054b90f922d',
commandType: 'loadLabware',
params: {
displayName: 'NEST 12 Well Reservoir 15 mL',
labwareId:
'b60bbc39-cd82-4ede-b744-e88777a32b62:opentrons/nest_12_reservoir_15ml/1',
loadName: 'nest_12_reservoir_15ml',
namespace: 'opentrons',
version: 1,
location: { slotName: 'C2' },
},
},
{
key: 'a2f0c011-9983-46d9-a3ae-763a04856651',
commandType: 'loadLabware',
params: {
displayName: 'Opentrons Flex 96 Tip Rack 50 µL (1)',
labwareId:
'0d3d02a6-6501-4f28-81b9-12b2fe66998b:opentrons/opentrons_flex_96_tiprack_50ul/1',
loadName: 'opentrons_flex_96_tiprack_50ul',
namespace: 'opentrons',
version: 1,
location: { addressableAreaName: 'D4' },
},
},
],
}
const mockStagingAreaSlotNames: AddressableAreaName[] = ['D4']
const mockHasWasteChuteCommands = false

expect(
getUnoccupiedSlotForTrash(
mockPDFile,
mockHasWasteChuteCommands,
mockStagingAreaSlotNames
)
).toStrictEqual(WASTE_CHUTE_CUTOUT)
})
})
11 changes: 10 additions & 1 deletion protocol-designer/src/step-forms/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ export function getHydratedForm(
return hydratedForm
}

export const getUnoccupiedSlotForMoveableTrash = (
export const getUnoccupiedSlotForTrash = (
file: PDProtocolFile,
hasWasteChuteCommands: boolean,
stagingAreaSlotNames: AddressableAreaName[]
Expand Down Expand Up @@ -385,6 +385,15 @@ export const getUnoccupiedSlotForMoveableTrash = (
!wasteChuteSlot.includes(cutout.value as typeof WASTE_CHUTE_CUTOUT) &&
!stagingAreaCutoutIds.includes(cutout.value as CutoutId)
)
// if all slots are occupied except for D3 on a staging area, then auto-generate the waste chute
if (
unoccupiedSlot == null &&
!allLoadLabwareSlotNames.includes('D3') &&
stagingAreaCutoutIds.includes(WASTE_CHUTE_CUTOUT)
) {
return WASTE_CHUTE_CUTOUT
}

if (unoccupiedSlot == null) {
console.error(
'Expected to find an unoccupied slot for auto-generating a trash bin but could not'
Expand Down

0 comments on commit 2e81f29

Please sign in to comment.