Skip to content

Commit 06c60c9

Browse files
authored
Merge branch 'edge' into AUTH-1295-add_get_load_name
2 parents 9181bc5 + b1684cb commit 06c60c9

File tree

70 files changed

+3915
-1577
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3915
-1577
lines changed

api-client/src/modules/api-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface PhysicalPort {
1010
port: number
1111
hub: boolean
1212
portGroup: PortGroup
13+
hubPort?: number
1314
}
1415

1516
type ModuleOffsetSource =

api/docs/v2/pipettes/characteristics.rst

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -184,29 +184,32 @@ These flow rates will remain in effect until you change the ``flow_rate`` attrib
184184
Flex Pipette Flow Rates
185185
-----------------------
186186

187-
Flex pipette flow rates depend on pipette volume and tip capacity. Each pipette–tip combination has a default flow rate for aspirating, dispensing, and blowing out liquid. When using a 50 µL pipette, you should only use 50 µL tips.
188-
189-
.. list-table::
190-
:header-rows: 1
191-
192-
* - Pipette Model
193-
- Tip Capacity (µL)
194-
- Flow Rate (µL/s)
195-
* - 50 µL (1- and 8-channel)
196-
- 50
197-
- 57
198-
* - 1000 µL (1-, 8-, and 96-channel)
199-
- 50
200-
- 478
201-
* - 1000 µL (1-, 8-, and 96-channel)
202-
- 200
203-
- 716
204-
* - 1000 µL (1-, 8-, and 96-channel)
205-
- 1000
206-
- 716
207-
208-
209-
Additionally, all Flex pipettes have a well bottom clearance of 1 mm for aspirate and dispense actions.
187+
The following table provides data on the default aspirate, dispense, and blowout flow rates (in µL/s) for Flex pipettes. Default flow rates for each pipette-tip combination are the same across all three actions.
188+
189+
.. Excludes low-vol 96 channel. Not yet released.
190+
191+
+-----------------------------+-------------------+------------------------+
192+
| Pipette Model | Tip Capacity (µL) | Default Flow Rate (µL) |
193+
+=============================+===================+========================+
194+
| 1- and 8-channel (50 µL) | 50 | 35 |
195+
+-----------------------------+-------------------+------------------------+
196+
| 1- and 8-channel (1000 µL) | 50 | 478 |
197+
+ +-------------------+------------------------+
198+
| | 200 | 716 |
199+
+ +-------------------+------------------------+
200+
| | 1000 | 716 |
201+
+-----------------------------+-------------------+------------------------+
202+
| 96-channel (5-1000 µL) | 50 | 6 |
203+
+ +-------------------+------------------------+
204+
| | 200 | 80 |
205+
+ +-------------------+------------------------+
206+
| | 1000 | 160 |
207+
+-----------------------------+-------------------+------------------------+
208+
209+
Additionally:
210+
211+
- When using a 50 µL pipette, you should only use 50 µL tips.
212+
- All Flex pipettes have a well bottom clearance of 1 mm for aspirate and dispense actions.
210213

211214
.. _ot2-flow-rates:
212215

api/src/opentrons/drivers/flex_stacker/driver.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,14 @@
6767
max_speed=10.0,
6868
acceleration=100.0,
6969
max_speed_discont=40,
70-
current=1.8,
70+
current=1.5,
7171
),
7272
"move": MoveParams(
7373
StackerAxis.Z,
7474
max_speed=200.0,
7575
acceleration=500.0,
7676
max_speed_discont=40,
77-
current=1.8,
77+
current=1.5,
7878
),
7979
},
8080
StackerAxis.L: {

api/src/opentrons/hardware_control/modules/flex_stacker.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,9 @@ async def move_axis(
273273
await self.reset_stall_detected()
274274
motion_params = STACKER_MOTION_CONFIG[axis]["move"]
275275
await self._driver.set_run_current(axis, current or motion_params.current or 0)
276-
if any([speed, acceleration]):
276+
if any([speed, acceleration, current]):
277+
motion_params = self._reader.motion_params[axis]
278+
motion_params.current = current or motion_params.current
277279
motion_params.max_speed = speed or motion_params.max_speed
278280
motion_params.acceleration = acceleration or motion_params.acceleration
279281
distance = direction.distance(distance)
@@ -391,8 +393,11 @@ async def store_labware(self, labware_height: float) -> bool:
391393

392394
# Transfer
393395
await self.open_latch()
394-
await self.move_axis(StackerAxis.Z, Direction.EXTEND, (labware_height / 2))
395-
await self.home_axis(StackerAxis.Z, Direction.EXTEND)
396+
z_speed = (STACKER_MOTION_CONFIG[StackerAxis.Z]["move"].max_speed or 0) / 2
397+
await self.move_axis(
398+
StackerAxis.Z, Direction.EXTEND, (labware_height / 2), z_speed
399+
)
400+
await self.home_axis(StackerAxis.Z, Direction.EXTEND, z_speed)
396401
await self.close_latch()
397402

398403
# Move Z then X axis
@@ -448,7 +453,7 @@ async def get_limit_switch_status(self) -> None:
448453

449454
async def get_motion_parameters(self) -> None:
450455
"""Get the motion parameters used by the axis motors."""
451-
self.move_params = {
456+
self.motion_params = {
452457
axis: await self._driver.get_motion_params(axis) for axis in StackerAxis
453458
}
454459

app/src/assets/localization/en/quick_transfer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,10 @@
132132
"touch_tip": "Touch tip",
133133
"touch_tip_after_aspirating": "Touch tip after aspirating",
134134
"touch_tip_before_dispensing": "Touch tip before dispensing",
135-
"touch_tip_position_mm": "Touch tip position from bottom of well (mm)",
135+
"touch_tip_position_mm": "Touch tip position from top of well (mm)",
136136
"touch_tip_value": "{{position}} mm from bottom",
137137
"use_deck_slots": "<block>Quick transfers use deck slots B2-D2. These slots hold a tip rack, a source labware, and a destination labware.</block><block>Make sure that your deck configuration is up to date to avoid collisions.</block>",
138-
"value_out_of_range": "Value must be between {{min}}-{{max}}",
138+
"value_out_of_range": "Value must be between {{min}} to {{max}}",
139139
"too_many_pins_body": "Remove a quick transfer in order to add more transfers to your pinned list.",
140140
"too_many_pins_header": "You've hit your max!",
141141
"transfer_analysis_failed": "quick transfer analysis failed",

app/src/organisms/DeviceDetailsDeckConfiguration/AddFixtureModal.tsx

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ import {
4747
TRASH_BIN_ADAPTER_FIXTURE,
4848
WASTE_CHUTE_CUTOUT,
4949
WASTE_CHUTE_FIXTURES,
50+
FLEX_STACKER_MODULE_V1,
51+
FLEX_STACKER_V1_FIXTURE,
52+
FLEX_STACKER_WITH_WASTE_CHUTE_ADAPTER_COVERED_FIXTURE,
53+
FLEX_STACKER_WTIH_WASTE_CHUTE_ADAPTER_NO_COVER_FIXTURE,
5054
} from '@opentrons/shared-data'
5155

5256
import { ODD_FOCUS_VISIBLE } from '/app/atoms/buttons/constants'
@@ -249,6 +253,52 @@ export function AddFixtureModal({
249253
]
250254
}
251255
}
256+
if (
257+
cutoutId === 'cutoutD3' &&
258+
unconfiguredMods.some(m => m.moduleModel === FLEX_STACKER_MODULE_V1)
259+
) {
260+
const unconfiguredFlexStackers: CutoutConfig[][] = []
261+
unconfiguredMods
262+
.filter(mod => mod.moduleModel === FLEX_STACKER_MODULE_V1)
263+
.forEach(mod => {
264+
unconfiguredFlexStackers.push([
265+
{
266+
cutoutId,
267+
cutoutFixtureId: FLEX_STACKER_V1_FIXTURE,
268+
opentronsModuleSerialNumber: mod.serialNumber,
269+
},
270+
])
271+
unconfiguredFlexStackers.push([
272+
{
273+
cutoutId,
274+
cutoutFixtureId: FLEX_STACKER_WITH_WASTE_CHUTE_ADAPTER_COVERED_FIXTURE,
275+
opentronsModuleSerialNumber: mod.serialNumber,
276+
},
277+
])
278+
unconfiguredFlexStackers.push([
279+
{
280+
cutoutId,
281+
cutoutFixtureId: FLEX_STACKER_WTIH_WASTE_CHUTE_ADAPTER_NO_COVER_FIXTURE,
282+
opentronsModuleSerialNumber: mod.serialNumber,
283+
},
284+
])
285+
})
286+
availableOptions.push(...unconfiguredFlexStackers)
287+
} else if (
288+
STAGING_AREA_CUTOUTS.includes(cutoutId) &&
289+
unconfiguredMods.some(m => m.moduleModel === FLEX_STACKER_MODULE_V1)
290+
) {
291+
const unconfiguredFlexStackers = unconfiguredMods
292+
.filter(mod => mod.moduleModel === FLEX_STACKER_MODULE_V1)
293+
.map(mod => [
294+
{
295+
cutoutId,
296+
cutoutFixtureId: FLEX_STACKER_V1_FIXTURE,
297+
opentronsModuleSerialNumber: mod.serialNumber,
298+
},
299+
])
300+
availableOptions = [...availableOptions, ...unconfiguredFlexStackers]
301+
}
252302
} else if (optionStage === 'wasteChuteOptions') {
253303
availableOptions = WASTE_CHUTE_FIXTURES.map(fixture => [
254304
{
@@ -315,22 +365,30 @@ export function AddFixtureModal({
315365
closeModal()
316366
}
317367

318-
const fixtureOptions = availableOptions.map(cutoutConfigs => (
319-
<FixtureOption
320-
key={cutoutConfigs[0].cutoutFixtureId}
321-
optionName={getFixtureDisplayName(
322-
cutoutConfigs[0].cutoutFixtureId,
323-
(modulesData?.data ?? []).find(
324-
m => m.serialNumber === cutoutConfigs[0].opentronsModuleSerialNumber
325-
)?.usbPort.port
326-
)}
327-
buttonText={t('add')}
328-
onClickHandler={() => {
329-
handleAddFixture(cutoutConfigs)
330-
}}
331-
isOnDevice={isOnDevice}
332-
/>
333-
))
368+
const fixtureOptions = availableOptions.map(cutoutConfigs => {
369+
const usbPort = (modulesData?.data ?? []).find(
370+
m => m.serialNumber === cutoutConfigs[0].opentronsModuleSerialNumber
371+
)?.usbPort
372+
const portDisplay =
373+
usbPort?.hubPort != null
374+
? `${usbPort.port}.${usbPort.hubPort}`
375+
: usbPort?.port
376+
377+
return (
378+
<FixtureOption
379+
key={cutoutConfigs[0].cutoutFixtureId}
380+
optionName={getFixtureDisplayName(
381+
cutoutConfigs[0].cutoutFixtureId,
382+
portDisplay
383+
)}
384+
buttonText={t('add')}
385+
onClickHandler={() => {
386+
handleAddFixture(cutoutConfigs)
387+
}}
388+
isOnDevice={isOnDevice}
389+
/>
390+
)
391+
})
334392

335393
return (
336394
<>

app/src/organisms/ODD/QuickTransferFlow/QuickTransferAdvancedSettings/TouchTip.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ export function TouchTip(props: TouchTipProps): JSX.Element {
4747
: state.touchTipDispense != null
4848
)
4949
const [currentStep, setCurrentStep] = useState<number>(1)
50-
const [position, setPosition] = useState<number | null>(
51-
kind === 'aspirate'
52-
? state.touchTipAspirate ?? null
53-
: state.touchTipDispense ?? null
50+
const touchTipAspirate =
51+
state.touchTipAspirate != null ? state.touchTipAspirate.toString() : null
52+
const touchTipDispense =
53+
state.touchTipDispense != null ? state.touchTipDispense.toString() : null
54+
const [position, setPosition] = useState<string | null>(
55+
kind === 'aspirate' ? touchTipAspirate : touchTipDispense
5456
)
5557

5658
const touchTipAction =
@@ -94,7 +96,10 @@ export function TouchTip(props: TouchTipProps): JSX.Element {
9496
setCurrentStep(2)
9597
}
9698
} else if (currentStep === 2) {
97-
dispatch({ type: touchTipAction, position: position ?? undefined })
99+
dispatch({
100+
type: touchTipAction,
101+
position: position != null ? parseInt(position) : undefined,
102+
})
98103
trackEventWithRobotSerial({
99104
name: ANALYTICS_QUICK_TRANSFER_SETTING_SAVED,
100105
properties: {
@@ -130,10 +135,13 @@ export function TouchTip(props: TouchTipProps): JSX.Element {
130135
}
131136

132137
// the allowed range for touch tip is half the height of the well to 1x the height
133-
const positionRange = { min: Math.round(wellHeight / 2), max: wellHeight }
138+
const positionRange = { min: -Math.round(wellHeight / 2), max: 0 }
134139
const positionError =
135140
position !== null &&
136-
(position < positionRange.min || position > positionRange.max)
141+
(position === '-' ||
142+
position.indexOf('-') !== position.lastIndexOf('-') ||
143+
Number(position) < positionRange.min ||
144+
Number(position) > positionRange.max)
137145
? t(`value_out_of_range`, {
138146
min: positionRange.min,
139147
max: Math.floor(positionRange.max),
@@ -197,8 +205,8 @@ export function TouchTip(props: TouchTipProps): JSX.Element {
197205
marginTop={SPACING.spacing68}
198206
>
199207
<InputField
200-
type="number"
201-
value={position}
208+
type="text"
209+
value={String(position ?? '')}
202210
title={t('touch_tip_position_mm')}
203211
error={positionError}
204212
readOnly
@@ -211,10 +219,11 @@ export function TouchTip(props: TouchTipProps): JSX.Element {
211219
borderRadius="0"
212220
>
213221
<NumericalKeyboard
222+
hasHyphen
214223
keyboardRef={keyboardRef}
215224
initialValue={String(position ?? '')}
216225
onChange={e => {
217-
setPosition(Number(e))
226+
setPosition(e)
218227
}}
219228
/>
220229
</Flex>

app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/AirGap.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ describe('AirGap', () => {
123123
expect(vi.mocked(InputField)).toHaveBeenCalledWith(
124124
{
125125
title: 'Air gap volume (µL)',
126-
error: 'Value must be between 1-180',
126+
error: 'Value must be between 1 to 180',
127127
readOnly: true,
128128
type: 'number',
129129
value: 0,
@@ -152,7 +152,7 @@ describe('AirGap', () => {
152152
expect(vi.mocked(InputField)).toHaveBeenCalledWith(
153153
{
154154
title: 'Air gap volume (µL)',
155-
error: 'Value must be between 1-80',
155+
error: 'Value must be between 1 to 80',
156156
readOnly: true,
157157
type: 'number',
158158
value: 0,
@@ -179,7 +179,7 @@ describe('AirGap', () => {
179179
expect(vi.mocked(InputField)).toHaveBeenCalledWith(
180180
{
181181
title: 'Air gap volume (µL)',
182-
error: 'Value must be between 1-140',
182+
error: 'Value must be between 1 to 140',
183183
readOnly: true,
184184
type: 'number',
185185
value: 0,
@@ -204,7 +204,7 @@ describe('AirGap', () => {
204204
expect(vi.mocked(InputField)).toHaveBeenCalledWith(
205205
{
206206
title: 'Air gap volume (µL)',
207-
error: 'Value must be between 1-200',
207+
error: 'Value must be between 1 to 200',
208208
readOnly: true,
209209
type: 'number',
210210
value: 0,

app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/Delay.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ describe('Delay', () => {
132132
expect(vi.mocked(InputField)).toHaveBeenCalledWith(
133133
{
134134
title: 'Delay duration (seconds)',
135-
error: 'Value must be between 1-9999999999',
135+
error: 'Value must be between 1 to 9999999999',
136136
readOnly: true,
137137
type: 'number',
138138
value: 0,
@@ -158,7 +158,7 @@ describe('Delay', () => {
158158
expect(vi.mocked(InputField)).toHaveBeenCalledWith(
159159
{
160160
title: 'Delay position from bottom of well (mm)',
161-
error: 'Value must be between 1-100',
161+
error: 'Value must be between 1 to 100',
162162
readOnly: true,
163163
type: 'number',
164164
value: 0,
@@ -188,7 +188,7 @@ describe('Delay', () => {
188188
expect(vi.mocked(InputField)).toHaveBeenCalledWith(
189189
{
190190
title: 'Delay position from bottom of well (mm)',
191-
error: 'Value must be between 1-400',
191+
error: 'Value must be between 1 to 400',
192192
readOnly: true,
193193
type: 'number',
194194
value: 0,

app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/FlowRate.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ describe('FlowRate', () => {
133133
expect(vi.mocked(InputField)).toHaveBeenCalledWith(
134134
{
135135
title: 'Aspirate flow rate (µL/s)',
136-
error: 'Value must be between 1-92',
136+
error: 'Value must be between 1 to 92',
137137
readOnly: true,
138138
type: 'number',
139139
value: 0,

app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/Mix.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ describe('Mix', () => {
132132
expect(vi.mocked(InputField)).toHaveBeenCalledWith(
133133
{
134134
title: 'Mix volume (µL)',
135-
error: 'Value must be between 1-200',
135+
error: 'Value must be between 1 to 200',
136136
readOnly: true,
137137
type: 'number',
138138
value: 0,
@@ -158,7 +158,7 @@ describe('Mix', () => {
158158
expect(vi.mocked(InputField)).toHaveBeenCalledWith(
159159
{
160160
title: 'Mix repetitions',
161-
error: 'Value must be between 1-999',
161+
error: 'Value must be between 1 to 999',
162162
readOnly: true,
163163
type: 'number',
164164
value: 0,

0 commit comments

Comments
 (0)