Skip to content

Commit 2fbf636

Browse files
authored
Merge branch 'edge' into EXEC-1228
2 parents 2527891 + 318145f commit 2fbf636

File tree

26 files changed

+247
-101
lines changed

26 files changed

+247
-101
lines changed

api/src/opentrons/protocol_engine/execution/pipetting.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,9 @@ def get_state_view(self) -> StateView:
385385

386386
def get_is_ready_to_aspirate(self, pipette_id: str) -> bool:
387387
"""Get whether a pipette is ready to aspirate."""
388-
return self._state_view.pipettes.get_aspirated_volume(pipette_id) is not None
388+
return self._state_view.pipettes.get_aspirated_volume(
389+
pipette_id
390+
) is not None and self._state_view.pipettes.get_ready_to_aspirate(pipette_id)
389391

390392
async def prepare_for_aspirate(self, pipette_id: str) -> None:
391393
"""Virtually prepare to aspirate (no-op)."""

api/tests/opentrons/protocol_engine/execution/test_pipetting_handler.py

+14
Original file line numberDiff line numberDiff line change
@@ -453,15 +453,29 @@ def test_virtual_get_is_ready_to_aspirate(
453453
mock_state_view.pipettes.get_aspirated_volume(pipette_id="pipette-id")
454454
).then_raise(TipNotAttachedError())
455455

456+
decoy.when(
457+
mock_state_view.pipettes.get_ready_to_aspirate(pipette_id="pipette-id")
458+
).then_return(True)
459+
456460
with pytest.raises(TipNotAttachedError):
457461
subject.get_is_ready_to_aspirate(pipette_id="pipette-id")
458462

459463
decoy.when(
460464
mock_state_view.pipettes.get_aspirated_volume(pipette_id="pipette-id-123")
461465
).then_return(0)
462466

467+
decoy.when(
468+
mock_state_view.pipettes.get_ready_to_aspirate(pipette_id="pipette-id-123")
469+
).then_return(True)
470+
463471
assert subject.get_is_ready_to_aspirate(pipette_id="pipette-id-123") is True
464472

473+
decoy.when(
474+
mock_state_view.pipettes.get_ready_to_aspirate(pipette_id="pipette-id-123")
475+
).then_return(False)
476+
477+
assert subject.get_is_ready_to_aspirate(pipette_id="pipette-id-123") is False
478+
465479

466480
async def test_virtual_aspirate_in_place(
467481
mock_state_view: StateView, decoy: Decoy, mock_command_note_adder: CommandNoteAdder

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/PipetteFields/TipWellSelectionField.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function TipWellSelectionField(
5454
<SelectWellsModal
5555
isOpen={openModal}
5656
key={`${labwareId}_${name}_TipField`}
57-
labwareId={String(labwareId)}
57+
labwareId={typeof labwareId === 'string' ? labwareId : null}
5858
name={name}
5959
onCloseClick={() => {
6060
setOpenModal(false)

protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MixTools/index.tsx

+15-3
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,11 @@ export function MixTools(props: StepFormProps): JSX.Element {
118118
{...propsForFields.wells}
119119
labwareId={formData.labware}
120120
pipetteId={formData.pipette}
121-
nozzles={String(propsForFields.nozzles.value) ?? null}
121+
nozzles={
122+
typeof propsForFields.nozzles.value === 'string'
123+
? propsForFields.nozzles.value
124+
: null
125+
}
122126
hasFormError={
123127
visibleFormErrors?.some(error =>
124128
error.dependentFields.includes('wells')
@@ -156,7 +160,11 @@ export function MixTools(props: StepFormProps): JSX.Element {
156160
<Divider marginY="0" />
157161
<TipWellSelectionField
158162
{...propsForFields.pickUpTip_wellNames}
159-
nozzles={String(propsForFields.nozzles.value) ?? null}
163+
nozzles={
164+
typeof propsForFields.nozzles.value === 'string'
165+
? propsForFields.nozzles.value
166+
: null
167+
}
160168
labwareId={propsForFields.pickUpTip_location.value}
161169
pipetteId={propsForFields.pipette.value}
162170
/>
@@ -174,7 +182,11 @@ export function MixTools(props: StepFormProps): JSX.Element {
174182
<Divider marginY="0" />
175183
<TipWellSelectionField
176184
{...propsForFields.dropTip_wellNames}
177-
nozzles={String(propsForFields.nozzles.value) ?? null}
185+
nozzles={
186+
typeof propsForFields.nozzles.value === 'string'
187+
? propsForFields.nozzles.value
188+
: null
189+
}
178190
labwareId={propsForFields.dropTip_location.value}
179191
pipetteId={propsForFields.pipette.value}
180192
/>

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

+30-6
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,17 @@ export function FirstStepMoveLiquidTools({
9898
/>
9999
<WellSelectionField
100100
{...propsForFields.aspirate_wells}
101-
labwareId={String(propsForFields.aspirate_labware.value)}
101+
labwareId={
102+
typeof propsForFields.aspirate_labware.value === 'string'
103+
? propsForFields.aspirate_labware.value
104+
: null
105+
}
102106
pipetteId={formData.pipette}
103-
nozzles={String(propsForFields.nozzles.value) ?? null}
107+
nozzles={
108+
typeof propsForFields.nozzles.value === 'string'
109+
? propsForFields.nozzles.value
110+
: null
111+
}
104112
hasFormError={
105113
visibleFormErrors?.some(error =>
106114
error.dependentFields.includes('aspirate_wells')
@@ -121,9 +129,17 @@ export function FirstStepMoveLiquidTools({
121129
{isDisposalLocation ? null : (
122130
<WellSelectionField
123131
{...propsForFields.dispense_wells}
124-
labwareId={String(propsForFields.dispense_labware.value)}
132+
labwareId={
133+
typeof propsForFields.dispense_labware.value === 'string'
134+
? propsForFields.dispense_labware.value
135+
: null
136+
}
125137
pipetteId={formData.pipette}
126-
nozzles={String(propsForFields.nozzles.value) ?? null}
138+
nozzles={
139+
typeof propsForFields.nozzles.value === 'string'
140+
? propsForFields.nozzles.value
141+
: null
142+
}
127143
hasFormError={
128144
visibleFormErrors?.some(error =>
129145
error.dependentFields.includes('dispense_wells')
@@ -173,7 +189,11 @@ export function FirstStepMoveLiquidTools({
173189
<>
174190
<TipWellSelectionField
175191
{...propsForFields.pickUpTip_wellNames}
176-
nozzles={String(propsForFields.nozzles.value) ?? null}
192+
nozzles={
193+
typeof propsForFields.nozzles.value === 'string'
194+
? propsForFields.nozzles.value
195+
: null
196+
}
177197
labwareId={propsForFields.pickUpTip_location.value}
178198
pipetteId={propsForFields.pipette.value}
179199
/>
@@ -191,7 +211,11 @@ export function FirstStepMoveLiquidTools({
191211
<Divider marginY="0" />
192212
<TipWellSelectionField
193213
{...propsForFields.dropTip_wellNames}
194-
nozzles={String(propsForFields.nozzles.value) ?? null}
214+
nozzles={
215+
typeof propsForFields.nozzles.value === 'string'
216+
? propsForFields.nozzles.value
217+
: null
218+
}
195219
labwareId={propsForFields.dropTip_location.value}
196220
pipetteId={propsForFields.pipette.value}
197221
/>

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,9 @@ export const SecondStepsMoveLiquidTools = ({
147147
]
148148
}
149149

150-
const minXYDimension = getMinXYDimension(
151-
labwares[formData[`${tab}_labware`]].def,
152-
['A1']
153-
)
150+
const minXYDimension = isDestinationTrash
151+
? null
152+
: getMinXYDimension(labwares[formData[`${tab}_labware`]]?.def, ['A1'])
154153
const minRadiusForTouchTip =
155154
minXYDimension != null ? round(minXYDimension / 2, 1) : null
156155

@@ -411,6 +410,7 @@ export const SecondStepsMoveLiquidTools = ({
411410
tooltipText={
412411
propsForFields[`${tab}_touchTip_checkbox`].tooltipContent
413412
}
413+
disabled={propsForFields[`${tab}_touchTip_checkbox`].disabled}
414414
>
415415
{formData[`${tab}_touchTip_checkbox`] === true ? (
416416
<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
@@ -9,6 +9,7 @@ import {
99
INTERACTIVE_WELL_DATA_ATTRIBUTE,
1010
LOW_VOLUME_PIPETTES,
1111
} from '@opentrons/shared-data'
12+
import { PROTOCOL_CONTEXT_NAME } from '@opentrons/step-generation'
1213
import type {
1314
AdditionalEquipmentEntity,
1415
LabwareEntities,
@@ -271,9 +272,19 @@ export const getLabwarePythonName = (
271272

272273
export const getAdditionalEquipmentPythonName = (
273274
fixtureName: 'wasteChute' | 'trashBin',
274-
typeCount: number
275+
typeCount: number,
276+
location?: string
275277
): string => {
276-
return fixtureName === 'wasteChute'
277-
? snakeCase(fixtureName)
278-
: `${snakeCase(fixtureName)}_${typeCount}`
278+
switch (fixtureName) {
279+
case 'wasteChute': {
280+
return snakeCase(fixtureName)
281+
}
282+
case 'trashBin': {
283+
if (location === 'cutout12') {
284+
return `${PROTOCOL_CONTEXT_NAME}.fixed_trash`
285+
} else {
286+
return `${snakeCase(fixtureName)}_${typeCount}`
287+
}
288+
}
289+
}
279290
}

0 commit comments

Comments
 (0)