Skip to content

Commit 9ac34e3

Browse files
authored
feat(app,shared-data): Add basic support for the Flex Stacker module to the front-end. (#17295)
fix EXEC-1088, EXEC-1089
1 parent 7393c92 commit 9ac34e3

File tree

27 files changed

+177
-59
lines changed

27 files changed

+177
-59
lines changed

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

+10
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ export interface AbsorbanceReaderData {
8686
sampleWavelength: number | null
8787
status: AbsorbanceReaderStatus
8888
}
89+
export interface FlexStackerData {
90+
latchState: 'opened' | 'closed' | 'unknown'
91+
platformState: 'extended' | 'retracted' | 'unknown'
92+
hopperDoorState: 'opened' | 'closed' | 'unknown'
93+
axisStateX: 'extended' | 'retracted' | 'unknown'
94+
axisStateZ: 'extended' | 'retracted' | 'unknown'
95+
status: FlexStackerStatus
96+
}
8997

9098
export type TemperatureStatus =
9199
| 'idle'
@@ -120,3 +128,5 @@ export type LatchStatus =
120128
| 'unknown'
121129

122130
export type AbsorbanceReaderStatus = 'idle' | 'measuring' | 'error'
131+
132+
export type FlexStackerStatus = 'idle' | 'dispensing' | 'storing' | 'error'

api-client/src/modules/types.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import type {
44
MagneticModuleModel,
55
HeaterShakerModuleModel,
66
AbsorbanceReaderModel,
7+
FlexStackerModuleModel,
78
TEMPERATURE_MODULE_TYPE,
89
MAGNETIC_MODULE_TYPE,
910
THERMOCYCLER_MODULE_TYPE,
1011
HEATERSHAKER_MODULE_TYPE,
1112
ABSORBANCE_READER_TYPE,
13+
FLEX_STACKER_MODULE_TYPE,
1214
} from '@opentrons/shared-data'
1315

1416
import type * as ApiTypes from './api-types'
@@ -51,13 +53,19 @@ export interface AbsorbanceReaderModule extends CommonModuleInfo {
5153
moduleModel: AbsorbanceReaderModel
5254
data: ApiTypes.AbsorbanceReaderData
5355
}
54-
56+
export interface FlexStackerModule extends CommonModuleInfo {
57+
moduleType: typeof FLEX_STACKER_MODULE_TYPE
58+
moduleModel: FlexStackerModuleModel
59+
data: ApiTypes.FlexStackerData
60+
moduleOffset?: ApiTypes.ModuleOffset
61+
}
5562
export type AttachedModule =
5663
| TemperatureModule
5764
| MagneticModule
5865
| ThermocyclerModule
5966
| HeaterShakerModule
6067
| AbsorbanceReaderModule
68+
| FlexStackerModule
6169

6270
export interface ModulesMeta {
6371
cursor: number

api/src/opentrons/protocol_engine/types.py

+1
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,7 @@ class AreaType(Enum):
971971
TEMPERATURE = "temperatureModule"
972972
MAGNETICBLOCK = "magneticBlock"
973973
ABSORBANCE_READER = "absorbanceReader"
974+
FLEX_STACKER = "flexStacker"
974975
LID_DOCK = "lidDock"
975976

976977

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"firmware_updated_successfully": "Firmware updated successfully",
6565
"firmware_update_occurring": "Firmware update in progress...",
6666
"fixture": "Fixture",
67+
"flex_stacker_door_status": "Door status: {{status}}",
6768
"have_not_run_description": "After you run some protocols, they will appear here.",
6869
"have_not_run": "No recent runs",
6970
"heater": "Heater",
@@ -186,7 +187,7 @@
186187
"update_now": "Update now",
187188
"updating_firmware": "Updating firmware...",
188189
"usb_port_not_connected": "usb not connected",
189-
"usb_port": "usb-{{port}}",
190+
"usb_port": "usb-{{port}}{{hubPort}}",
190191
"version": "Version {{version}}",
191192
"view_pipette_setting": "Pipette Settings",
192193
"view_run_record": "View protocol run record",

app/src/organisms/ModuleCard/AbsorbanceReaderSlideout.tsx

-43
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { useTranslation } from 'react-i18next'
2+
import { StyledText, COLORS } from '@opentrons/components'
3+
import { StatusLabel } from '/app/atoms/StatusLabel'
4+
5+
import type { FlexStackerModule } from '/app/redux/modules/types'
6+
7+
interface FlexStackerModuleProps {
8+
moduleData: FlexStackerModule['data']
9+
}
10+
11+
export function FlexStackerModuleData(
12+
props: FlexStackerModuleProps
13+
): JSX.Element | null {
14+
const { moduleData } = props
15+
const { t, i18n } = useTranslation(['device_details', 'shared'])
16+
17+
const StatusLabelProps = {
18+
status: 'Idle',
19+
backgroundColor: COLORS.grey30,
20+
iconColor: COLORS.grey60,
21+
textColor: COLORS.grey60,
22+
pulse: false,
23+
}
24+
switch (moduleData.status) {
25+
case 'storing':
26+
case 'dispensing': {
27+
StatusLabelProps.status = moduleData.status
28+
StatusLabelProps.backgroundColor = COLORS.blue30
29+
StatusLabelProps.iconColor = COLORS.blue60
30+
StatusLabelProps.textColor = COLORS.blue60
31+
break
32+
}
33+
case 'error': {
34+
StatusLabelProps.status = 'Error'
35+
StatusLabelProps.backgroundColor = COLORS.yellow30
36+
StatusLabelProps.iconColor = COLORS.yellow60
37+
StatusLabelProps.textColor = COLORS.yellow60
38+
break
39+
}
40+
}
41+
const lidDisplayStatus =
42+
moduleData.hopperDoorState === 'closed'
43+
? i18n.format(t('shared:closed'), 'capitalize')
44+
: i18n.format(t('shared:open'), 'capitalize')
45+
return (
46+
<>
47+
<StatusLabel {...StatusLabelProps} />
48+
<StyledText
49+
desktopStyle="bodyDefaultRegular"
50+
data-testid="stacker_module_data"
51+
>
52+
{t('flex_stacker_door_status', {
53+
status: lidDisplayStatus,
54+
})}
55+
</StyledText>
56+
</>
57+
)
58+
}

app/src/organisms/ModuleCard/ModuleOverflowMenu.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
MODULE_MODELS_OT2_ONLY,
1818
TEMPERATURE_MODULE_TYPE,
1919
THERMOCYCLER_MODULE_TYPE,
20+
FLEX_STACKER_MODULE_TYPE,
2021
} from '@opentrons/shared-data'
2122
import { useCurrentRunId, useRunStatuses } from '/app/resources/runs'
2223
import { useIsLegacySessionInProgress } from '/app/resources/legacy_sessions'
@@ -121,6 +122,7 @@ export const ModuleOverflowMenu = (
121122
<MenuList>
122123
{isFlex &&
123124
module.moduleType !== ABSORBANCE_READER_TYPE &&
125+
module.moduleType !== FLEX_STACKER_MODULE_TYPE &&
124126
!MODULE_MODELS_OT2_ONLY.some(
125127
modModel => modModel === module.moduleModel
126128
) ? (

app/src/organisms/ModuleCard/hooks.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,15 @@ export function useModuleOverflowMenu(
369369
onClick: handleAboutClick,
370370
},
371371
],
372+
flexStackerModuleType: [
373+
{
374+
setSetting: t('overflow_menu_about'),
375+
isSecondary: false,
376+
isSettingDisabled: false,
377+
menuButtons: [],
378+
onClick: handleAboutClick,
379+
},
380+
],
372381
}
373382

374383
return {

app/src/organisms/ModuleCard/index.tsx

+15-10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
THERMOCYCLER_MODULE_TYPE,
3232
MODULE_MODELS_OT2_ONLY,
3333
ABSORBANCE_READER_TYPE,
34+
FLEX_STACKER_MODULE_TYPE,
3435
} from '@opentrons/shared-data'
3536
import { RUN_STATUS_FINISHING, RUN_STATUS_RUNNING } from '@opentrons/api-client'
3637

@@ -75,7 +76,7 @@ import type {
7576
import type { State, Dispatch } from '/app/redux/types'
7677
import type { RequestState } from '/app/redux/robot-api/types'
7778
import { AbsorbanceReaderData } from './AbsorbanceReaderData'
78-
import { AbsorbanceReaderSlideout } from './AbsorbanceReaderSlideout'
79+
import { FlexStackerModuleData } from './FlexStackerModuleData'
7980

8081
interface ModuleCardProps {
8182
module: AttachedModule
@@ -132,6 +133,7 @@ export const ModuleCard = (props: ModuleCardProps): JSX.Element | null => {
132133
isFlex &&
133134
!MODULE_MODELS_OT2_ONLY.some(modModel => modModel === module.moduleModel) &&
134135
module.moduleType !== ABSORBANCE_READER_TYPE &&
136+
module.moduleType !== FLEX_STACKER_MODULE_TYPE &&
135137
module.moduleOffset?.last_modified == null
136138
const isPipetteReady =
137139
!Boolean(attachPipetteRequired) &&
@@ -214,6 +216,11 @@ export const ModuleCard = (props: ModuleCardProps): JSX.Element | null => {
214216
moduleData = <AbsorbanceReaderData moduleData={module.data} />
215217
break
216218
}
219+
220+
case FLEX_STACKER_MODULE_TYPE: {
221+
moduleData = <FlexStackerModuleData moduleData={module.data} />
222+
break
223+
}
217224
}
218225

219226
const handleMenuItemClick = (isSecondary: boolean = false): void => {
@@ -419,6 +426,10 @@ export const ModuleCard = (props: ModuleCardProps): JSX.Element | null => {
419426
{module?.usbPort !== null
420427
? t('usb_port', {
421428
port: module?.usbPort?.port,
429+
hubPort:
430+
module?.usbPort?.hubPort != null
431+
? `.${module.usbPort.hubPort}`
432+
: '',
422433
})
423434
: t('usb_port_not_connected')}
424435
</LegacyStyledText>
@@ -532,21 +543,15 @@ const ModuleSlideout = (props: ModuleSlideoutProps): JSX.Element => {
532543
isExpanded={showSlideout}
533544
/>
534545
)
535-
} else if (module.moduleType === ABSORBANCE_READER_TYPE) {
536-
return (
537-
<AbsorbanceReaderSlideout
538-
module={module}
539-
onCloseClick={onCloseClick}
540-
isExpanded={showSlideout}
541-
/>
542-
)
543-
} else {
546+
} else if (module.moduleType === HEATERSHAKER_MODULE_TYPE) {
544547
return (
545548
<HeaterShakerSlideout
546549
module={module}
547550
onCloseClick={onCloseClick}
548551
isExpanded={showSlideout}
549552
/>
550553
)
554+
} else {
555+
return <></>
551556
}
552557
}

app/src/organisms/ModuleCard/utils.ts

+5
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ import heaterShakerModule from '/app/assets/images/heater_shaker_module_transpar
1212
import thermoModuleGen2Closed from '/app/assets/images/thermocycler_gen_2_closed.png'
1313
import thermoModuleGen2Opened from '/app/assets/images/thermocycler_gen_2_opened.png'
1414
import absorbanceReader from '/app/assets/images/opentrons_plate_reader.png'
15+
// TODO (sb, 1/25): add correct flex stacker asset when it exits
16+
import flexStacker from '/app/assets/images/FLEX.png'
1517

1618
import type { AttachedModule } from '/app/redux/modules/types'
1719

1820
export function getModuleCardImage(attachedModule: AttachedModule): string {
1921
// TODO(jr, 9/22/22): add images for V1 of magneticModule and temperatureModule
2022
switch (attachedModule.moduleModel) {
23+
// TODO: Add correct image for flex stacker
2124
case 'magneticModuleV1':
2225
case 'magneticModuleV2':
2326
return magneticModule
@@ -40,6 +43,8 @@ export function getModuleCardImage(attachedModule: AttachedModule): string {
4043
}
4144
case 'absorbanceReaderV1':
4245
return absorbanceReader
46+
case 'flexStackerModuleV1':
47+
return flexStacker
4348
// this should never be reached
4449
default:
4550
return 'unknown module model, this is an error'

app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/ModuleTable.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
NON_CONNECTING_MODULE_TYPES,
2525
TC_MODULE_LOCATION_OT3,
2626
THERMOCYCLER_MODULE_TYPE,
27+
FLEX_STACKER_MODULE_TYPE,
2728
} from '@opentrons/shared-data'
2829

2930
import { SmallButton } from '/app/atoms/buttons'
@@ -206,7 +207,8 @@ function ModuleTableItem({
206207
} else if (
207208
isModuleReady &&
208209
(module.attachedModuleMatch?.moduleOffset?.last_modified != null ||
209-
module.attachedModuleMatch?.moduleType === ABSORBANCE_READER_TYPE)
210+
module.attachedModuleMatch?.moduleType === ABSORBANCE_READER_TYPE ||
211+
module.attachedModuleMatch?.moduleType === FLEX_STACKER_MODULE_TYPE)
210212
) {
211213
moduleStatus = (
212214
<Chip
@@ -269,8 +271,9 @@ function ModuleTableItem({
269271
backgroundColor={
270272
isModuleReady &&
271273
(module.attachedModuleMatch?.moduleOffset?.last_modified != null ||
274+
module.attachedModuleMatch?.moduleType === ABSORBANCE_READER_TYPE ||
272275
module.attachedModuleMatch?.moduleType ===
273-
ABSORBANCE_READER_TYPE) &&
276+
FLEX_STACKER_MODULE_TYPE) &&
274277
conflictedFixture == null
275278
? COLORS.green35
276279
: isNonConnectingModule && conflictedFixture == null

app/src/redux/modules/api-types.ts

+11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface PhysicalPort {
1616
path: string | null
1717
port: number
1818
hub: boolean
19+
hubPort?: number
1920
portGroup: PortGroup
2021
}
2122

@@ -84,6 +85,14 @@ export interface AbsorbanceReaderData {
8485
sampleWavelength: number | null
8586
status: AbsorbanceReaderStatus
8687
}
88+
export interface FlexStackerData {
89+
latchState: 'opened' | 'closed' | 'unknown'
90+
platformState: 'extended' | 'retracted' | 'unknown'
91+
hopperDoorState: 'opened' | 'closed' | 'unknown'
92+
axisStateX: 'extended' | 'retracted' | 'unknown'
93+
axisStateZ: 'extended' | 'retracted' | 'unknown'
94+
status: FlexStackerStatus
95+
}
8796

8897
export type TemperatureStatus =
8998
| 'idle'
@@ -119,6 +128,8 @@ export type LatchStatus =
119128

120129
export type AbsorbanceReaderStatus = 'idle' | 'measuring' | 'error'
121130

131+
export type FlexStackerStatus = 'idle' | 'dispensing' | 'storing' | 'error'
132+
122133
export interface ApiTemperatureModule extends ApiBaseModule {
123134
moduleModel: TemperatureModuleModel
124135
name: typeof TEMPDECK

0 commit comments

Comments
 (0)