Skip to content

Commit 676942a

Browse files
committed
Merge branch 'edge' into feat_pd-pushout
2 parents 235cde4 + 7e44ff5 commit 676942a

File tree

17 files changed

+304
-52
lines changed

17 files changed

+304
-52
lines changed

protocol-designer/src/assets/localization/en/feature_flags.json

+4
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,9 @@
4646
"OT_PD_ENABLE_PARTIAL_TIP_SUPPORT": {
4747
"title": "Enable partial tip support",
4848
"description": "Partial tip configurations that are not released yet"
49+
},
50+
"OT_PD_ENABLE_STACKING": {
51+
"title": "Enable more labware capabilities",
52+
"description": "Ability to stack labware, add lids, etc"
4953
}
5054
}

protocol-designer/src/feature-flags/reducers.ts

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const initialFlags: Flags = {
3838
process.env.OT_PD_ENABLE_PYTHON_EXPORT === '1' || false,
3939
OT_PD_ENABLE_PARTIAL_TIP_SUPPORT:
4040
process.env.OT_PD_ENABLE_PARTIAL_TIP_SUPPORT === '1' || false,
41+
OT_PD_ENABLE_STACKING: process.env.OT_PD_ENABLE_STACKING === '1' || false,
4142
}
4243
// @ts-expect-error(sa, 2021-6-10): cannot use string literals as action type
4344
// TODO IMMEDIATELY: refactor this to the old fashioned way if we cannot have type safety: https://github.com/redux-utilities/redux-actions/issues/282#issuecomment-595163081

protocol-designer/src/feature-flags/selectors.ts

+4
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,7 @@ export const getEnablePartialTipSupport: Selector<boolean> = createSelector(
6161
getFeatureFlagData,
6262
flags => flags.OT_PD_ENABLE_PARTIAL_TIP_SUPPORT ?? false
6363
)
64+
export const getEnableStacking: Selector<boolean> = createSelector(
65+
getFeatureFlagData,
66+
flags => flags.OT_PD_ENABLE_STACKING ?? false
67+
)

protocol-designer/src/feature-flags/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export type FlagTypes =
4141
| 'OT_PD_ENABLE_TIMELINE_SCRUBBER'
4242
| 'OT_PD_ENABLE_PYTHON_EXPORT'
4343
| 'OT_PD_ENABLE_PARTIAL_TIP_SUPPORT'
44+
| 'OT_PD_ENABLE_STACKING'
4445
// flags that are not in this list only show in prerelease mode
4546
export const userFacingFlags: FlagTypes[] = [
4647
'OT_PD_DISABLE_MODULE_RESTRICTIONS',
@@ -58,5 +59,6 @@ export const allFlags: FlagTypes[] = [
5859
'OT_PD_ENABLE_TIMELINE_SCRUBBER',
5960
'OT_PD_ENABLE_PYTHON_EXPORT',
6061
'OT_PD_ENABLE_PARTIAL_TIP_SUPPORT',
62+
'OT_PD_ENABLE_STACKING',
6163
]
6264
export type Flags = Partial<Record<FlagTypes, boolean | null | undefined>>

protocol-designer/src/pages/Designer/DeckSetup/LabwareTools.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { getRobotType } from '../../../file-data/selectors'
4545
import { getCustomLabwareDefsByURI } from '../../../labware-defs/selectors'
4646
import { getPipetteEntities } from '../../../step-forms/selectors'
4747
import { selectors } from '../../../labware-ingred/selectors'
48+
import { getEnableStacking } from '../../../feature-flags/selectors'
4849
import {
4950
selectLabware,
5051
selectNestedLabware,
@@ -103,6 +104,7 @@ export function LabwareTools(props: LabwareToolsProps): JSX.Element {
103104
const customLabwareDefs = useSelector(getCustomLabwareDefsByURI)
104105
const has96Channel = getHas96Channel(pipetteEntities)
105106
const defs = getOnlyLatestDefs()
107+
const enableStacking = useSelector(getEnableStacking)
106108
const deckSetup = useSelector(stepFormSelectors.getInitialDeckSetup)
107109
const zoomedInSlotInfo = useSelector(selectors.getZoomedInSlotInfo)
108110
const {
@@ -381,8 +383,11 @@ export function LabwareTools(props: LabwareToolsProps): JSX.Element {
381383
/>
382384

383385
{uri === selectedLabwareDefUri &&
384-
getLabwareCompatibleWithAdapter(defs, loadName)
385-
?.length > 0 && (
386+
getLabwareCompatibleWithAdapter(
387+
defs,
388+
enableStacking,
389+
loadName
390+
)?.length > 0 && (
386391
<ListButtonAccordionContainer
387392
id={`nestedAccordionContainer_${loadName}`}
388393
>
@@ -433,6 +438,7 @@ export function LabwareTools(props: LabwareToolsProps): JSX.Element {
433438
)
434439
: getLabwareCompatibleWithAdapter(
435440
{ ...defs, ...customLabwareDefs },
441+
enableStacking,
436442
loadName
437443
).map(nestedDefUri => {
438444
const nestedDef =

protocol-designer/src/pages/Designer/DeckSetup/utils.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,21 @@ export const getLabwareIsRecommended = (
150150
)
151151
}
152152

153+
const STACKING_LABWARE_LOADNAME_FILTER = [
154+
'opentrons_96_wellplate_200ul_pcr_full_skirt',
155+
]
156+
153157
export const getLabwareCompatibleWithAdapter = (
154158
defs: LabwareDefByDefURI,
159+
enableStacking: boolean,
155160
adapterLoadName?: string
156161
): string[] => {
157-
if (adapterLoadName == null) {
162+
if (
163+
adapterLoadName == null ||
164+
(adapterLoadName != null &&
165+
!enableStacking &&
166+
STACKING_LABWARE_LOADNAME_FILTER.includes(adapterLoadName))
167+
) {
158168
return []
159169
}
160170
return Object.entries(defs)

protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MoveLiquidTools/SecondStepsMoveLiquidTools.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,9 @@ export const SecondStepsMoveLiquidTools = ({
154154
pipetteSpec
155155
)
156156

157-
const minXYDimension = getMinXYDimension(
158-
labwares[formData[`${tab}_labware`]].def,
159-
['A1']
160-
)
157+
const minXYDimension = isDestinationTrash
158+
? null
159+
: getMinXYDimension(labwares[formData[`${tab}_labware`]]?.def, ['A1'])
161160
const minRadiusForTouchTip =
162161
minXYDimension != null ? round(minXYDimension / 2, 1) : null
163162

@@ -450,6 +449,7 @@ export const SecondStepsMoveLiquidTools = ({
450449
tooltipText={
451450
propsForFields[`${tab}_touchTip_checkbox`].tooltipContent
452451
}
452+
disabled={propsForFields[`${tab}_touchTip_checkbox`].disabled}
453453
>
454454
{formData[`${tab}_touchTip_checkbox`] === true ? (
455455
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing10}>

protocol-designer/src/step-forms/reducers/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1260,7 +1260,8 @@ export const additionalEquipmentInvariantProperties = handleActions<NormalizedAd
12601260
location,
12611261
pythonName: getAdditionalEquipmentPythonName(
12621262
'trashBin',
1263-
index + 1
1263+
index + 1,
1264+
location
12641265
),
12651266
},
12661267
}),
@@ -1345,7 +1346,7 @@ export const additionalEquipmentInvariantProperties = handleActions<NormalizedAd
13451346
pythonName:
13461347
name === 'stagingArea'
13471348
? undefined
1348-
: getAdditionalEquipmentPythonName(name, typeCount + 1),
1349+
: getAdditionalEquipmentPythonName(name, typeCount + 1, location),
13491350
},
13501351
}
13511352
},

protocol-designer/src/utils/index.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
INTERACTIVE_WELL_DATA_ATTRIBUTE,
1111
LOW_VOLUME_PIPETTES,
1212
} from '@opentrons/shared-data'
13+
import { PROTOCOL_CONTEXT_NAME } from '@opentrons/step-generation'
1314
import type {
1415
AdditionalEquipmentEntity,
1516
LabwareEntities,
@@ -273,11 +274,21 @@ export const getLabwarePythonName = (
273274

274275
export const getAdditionalEquipmentPythonName = (
275276
fixtureName: 'wasteChute' | 'trashBin',
276-
typeCount: number
277+
typeCount: number,
278+
location?: string
277279
): string => {
278-
return fixtureName === 'wasteChute'
279-
? snakeCase(fixtureName)
280-
: `${snakeCase(fixtureName)}_${typeCount}`
280+
switch (fixtureName) {
281+
case 'wasteChute': {
282+
return snakeCase(fixtureName)
283+
}
284+
case 'trashBin': {
285+
if (location === 'cutout12') {
286+
return `${PROTOCOL_CONTEXT_NAME}.fixed_trash`
287+
} else {
288+
return `${snakeCase(fixtureName)}_${typeCount}`
289+
}
290+
}
291+
}
281292
}
282293

283294
/**
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,41 @@
11
import { describe, it, expect, vi } from 'vitest'
22
import {
3+
DEFAULT_PIPETTE,
34
getInitialRobotStateStandard,
45
getSuccessResult,
56
makeContext,
67
} from '../fixtures'
8+
import { PROTOCOL_CONTEXT_NAME } from '../utils'
79
import { blowOutInTrash } from '../commandCreators/compound'
810
import type { CutoutId } from '@opentrons/shared-data'
911
import type { InvariantContext, RobotState } from '../types'
1012

1113
vi.mock('../getNextRobotStateAndWarnings/dispenseUpdateLiquidState')
1214

13-
const mockId = 'mockId'
1415
const mockCutout: CutoutId = 'cutoutA3'
15-
const invariantContext: InvariantContext = makeContext()
16+
const mockTrashId = 'mockTrashId'
17+
let invariantContext: InvariantContext = {
18+
...makeContext(),
19+
additionalEquipmentEntities: {
20+
[mockTrashId]: {
21+
id: mockTrashId,
22+
name: 'trashBin',
23+
pythonName: 'mock_trash_bin_1',
24+
location: mockCutout,
25+
},
26+
},
27+
}
1628
const prevRobotState: RobotState = getInitialRobotStateStandard(
1729
invariantContext
1830
)
1931

2032
describe('blowOutInTrash', () => {
21-
it('returns correct commands for blowout in a trash bin', () => {
33+
it('returns correct commands for blowout in a trash bin for a flex', () => {
2234
const result = blowOutInTrash(
2335
{
24-
pipetteId: mockId,
36+
pipetteId: DEFAULT_PIPETTE,
2537
flowRate: 10,
26-
trashLocation: mockCutout,
38+
trashId: mockTrashId,
2739
},
2840
invariantContext,
2941
prevRobotState
@@ -33,7 +45,7 @@ describe('blowOutInTrash', () => {
3345
commandType: 'moveToAddressableArea',
3446
key: expect.any(String),
3547
params: {
36-
pipetteId: mockId,
48+
pipetteId: DEFAULT_PIPETTE,
3749
addressableAreaName: 'movableTrashA3',
3850
offset: { x: 0, y: 0, z: 0 },
3951
},
@@ -42,10 +54,62 @@ describe('blowOutInTrash', () => {
4254
commandType: 'blowOutInPlace',
4355
key: expect.any(String),
4456
params: {
45-
pipetteId: mockId,
57+
pipetteId: DEFAULT_PIPETTE,
4658
flowRate: 10,
4759
},
4860
},
4961
])
62+
expect(getSuccessResult(result).python).toBe(
63+
`
64+
mockPythonName.flow_rate.blow_out = 10
65+
mockPythonName.blow_out(mock_trash_bin_1)`.trim()
66+
)
67+
})
68+
it('returns correct commands for blowout in a trash bin for an ot-2', () => {
69+
const mockFixedTrashId = 'fixedTrashId'
70+
invariantContext = {
71+
...invariantContext,
72+
additionalEquipmentEntities: {
73+
[mockFixedTrashId]: {
74+
id: mockFixedTrashId,
75+
name: 'trashBin',
76+
pythonName: `${PROTOCOL_CONTEXT_NAME}.fixed_trash`,
77+
location: 'cutout12',
78+
},
79+
},
80+
}
81+
const result = blowOutInTrash(
82+
{
83+
pipetteId: DEFAULT_PIPETTE,
84+
flowRate: 10,
85+
trashId: mockFixedTrashId,
86+
},
87+
invariantContext,
88+
prevRobotState
89+
)
90+
expect(getSuccessResult(result).commands).toEqual([
91+
{
92+
commandType: 'moveToAddressableArea',
93+
key: expect.any(String),
94+
params: {
95+
pipetteId: DEFAULT_PIPETTE,
96+
addressableAreaName: 'fixedTrash',
97+
offset: { x: 0, y: 0, z: 0 },
98+
},
99+
},
100+
{
101+
commandType: 'blowOutInPlace',
102+
key: expect.any(String),
103+
params: {
104+
pipetteId: DEFAULT_PIPETTE,
105+
flowRate: 10,
106+
},
107+
},
108+
])
109+
expect(getSuccessResult(result).python).toBe(
110+
`
111+
mockPythonName.flow_rate.blow_out = 10
112+
mockPythonName.blow_out(protocol.fixed_trash)`.trim()
113+
)
50114
})
51115
})

0 commit comments

Comments
 (0)