Skip to content

Commit

Permalink
feat(app,shared-data): Add basic support for the Flex Stacker module …
Browse files Browse the repository at this point in the history
…to the front-end. (#17295)

fix EXEC-1088, EXEC-1089
  • Loading branch information
vegano1 authored Jan 17, 2025
1 parent 7393c92 commit 9ac34e3
Show file tree
Hide file tree
Showing 27 changed files with 177 additions and 59 deletions.
10 changes: 10 additions & 0 deletions api-client/src/modules/api-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ export interface AbsorbanceReaderData {
sampleWavelength: number | null
status: AbsorbanceReaderStatus
}
export interface FlexStackerData {
latchState: 'opened' | 'closed' | 'unknown'
platformState: 'extended' | 'retracted' | 'unknown'
hopperDoorState: 'opened' | 'closed' | 'unknown'
axisStateX: 'extended' | 'retracted' | 'unknown'
axisStateZ: 'extended' | 'retracted' | 'unknown'
status: FlexStackerStatus
}

export type TemperatureStatus =
| 'idle'
Expand Down Expand Up @@ -120,3 +128,5 @@ export type LatchStatus =
| 'unknown'

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

export type FlexStackerStatus = 'idle' | 'dispensing' | 'storing' | 'error'
10 changes: 9 additions & 1 deletion api-client/src/modules/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import type {
MagneticModuleModel,
HeaterShakerModuleModel,
AbsorbanceReaderModel,
FlexStackerModuleModel,
TEMPERATURE_MODULE_TYPE,
MAGNETIC_MODULE_TYPE,
THERMOCYCLER_MODULE_TYPE,
HEATERSHAKER_MODULE_TYPE,
ABSORBANCE_READER_TYPE,
FLEX_STACKER_MODULE_TYPE,
} from '@opentrons/shared-data'

import type * as ApiTypes from './api-types'
Expand Down Expand Up @@ -51,13 +53,19 @@ export interface AbsorbanceReaderModule extends CommonModuleInfo {
moduleModel: AbsorbanceReaderModel
data: ApiTypes.AbsorbanceReaderData
}

export interface FlexStackerModule extends CommonModuleInfo {
moduleType: typeof FLEX_STACKER_MODULE_TYPE
moduleModel: FlexStackerModuleModel
data: ApiTypes.FlexStackerData
moduleOffset?: ApiTypes.ModuleOffset
}
export type AttachedModule =
| TemperatureModule
| MagneticModule
| ThermocyclerModule
| HeaterShakerModule
| AbsorbanceReaderModule
| FlexStackerModule

export interface ModulesMeta {
cursor: number
Expand Down
1 change: 1 addition & 0 deletions api/src/opentrons/protocol_engine/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,7 @@ class AreaType(Enum):
TEMPERATURE = "temperatureModule"
MAGNETICBLOCK = "magneticBlock"
ABSORBANCE_READER = "absorbanceReader"
FLEX_STACKER = "flexStacker"
LID_DOCK = "lidDock"


Expand Down
3 changes: 2 additions & 1 deletion app/src/assets/localization/en/device_details.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"firmware_updated_successfully": "Firmware updated successfully",
"firmware_update_occurring": "Firmware update in progress...",
"fixture": "Fixture",
"flex_stacker_door_status": "Door status: {{status}}",
"have_not_run_description": "After you run some protocols, they will appear here.",
"have_not_run": "No recent runs",
"heater": "Heater",
Expand Down Expand Up @@ -186,7 +187,7 @@
"update_now": "Update now",
"updating_firmware": "Updating firmware...",
"usb_port_not_connected": "usb not connected",
"usb_port": "usb-{{port}}",
"usb_port": "usb-{{port}}{{hubPort}}",
"version": "Version {{version}}",
"view_pipette_setting": "Pipette Settings",
"view_run_record": "View protocol run record",
Expand Down
43 changes: 0 additions & 43 deletions app/src/organisms/ModuleCard/AbsorbanceReaderSlideout.tsx

This file was deleted.

58 changes: 58 additions & 0 deletions app/src/organisms/ModuleCard/FlexStackerModuleData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useTranslation } from 'react-i18next'
import { StyledText, COLORS } from '@opentrons/components'
import { StatusLabel } from '/app/atoms/StatusLabel'

import type { FlexStackerModule } from '/app/redux/modules/types'

interface FlexStackerModuleProps {
moduleData: FlexStackerModule['data']
}

export function FlexStackerModuleData(
props: FlexStackerModuleProps
): JSX.Element | null {
const { moduleData } = props
const { t, i18n } = useTranslation(['device_details', 'shared'])

const StatusLabelProps = {
status: 'Idle',
backgroundColor: COLORS.grey30,
iconColor: COLORS.grey60,
textColor: COLORS.grey60,
pulse: false,
}
switch (moduleData.status) {
case 'storing':
case 'dispensing': {
StatusLabelProps.status = moduleData.status
StatusLabelProps.backgroundColor = COLORS.blue30
StatusLabelProps.iconColor = COLORS.blue60
StatusLabelProps.textColor = COLORS.blue60
break
}
case 'error': {
StatusLabelProps.status = 'Error'
StatusLabelProps.backgroundColor = COLORS.yellow30
StatusLabelProps.iconColor = COLORS.yellow60
StatusLabelProps.textColor = COLORS.yellow60
break
}
}
const lidDisplayStatus =
moduleData.hopperDoorState === 'closed'
? i18n.format(t('shared:closed'), 'capitalize')
: i18n.format(t('shared:open'), 'capitalize')
return (
<>
<StatusLabel {...StatusLabelProps} />
<StyledText
desktopStyle="bodyDefaultRegular"
data-testid="stacker_module_data"
>
{t('flex_stacker_door_status', {
status: lidDisplayStatus,
})}
</StyledText>
</>
)
}
2 changes: 2 additions & 0 deletions app/src/organisms/ModuleCard/ModuleOverflowMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
MODULE_MODELS_OT2_ONLY,
TEMPERATURE_MODULE_TYPE,
THERMOCYCLER_MODULE_TYPE,
FLEX_STACKER_MODULE_TYPE,
} from '@opentrons/shared-data'
import { useCurrentRunId, useRunStatuses } from '/app/resources/runs'
import { useIsLegacySessionInProgress } from '/app/resources/legacy_sessions'
Expand Down Expand Up @@ -121,6 +122,7 @@ export const ModuleOverflowMenu = (
<MenuList>
{isFlex &&
module.moduleType !== ABSORBANCE_READER_TYPE &&
module.moduleType !== FLEX_STACKER_MODULE_TYPE &&
!MODULE_MODELS_OT2_ONLY.some(
modModel => modModel === module.moduleModel
) ? (
Expand Down
9 changes: 9 additions & 0 deletions app/src/organisms/ModuleCard/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,15 @@ export function useModuleOverflowMenu(
onClick: handleAboutClick,
},
],
flexStackerModuleType: [
{
setSetting: t('overflow_menu_about'),
isSecondary: false,
isSettingDisabled: false,
menuButtons: [],
onClick: handleAboutClick,
},
],
}

return {
Expand Down
25 changes: 15 additions & 10 deletions app/src/organisms/ModuleCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
THERMOCYCLER_MODULE_TYPE,
MODULE_MODELS_OT2_ONLY,
ABSORBANCE_READER_TYPE,
FLEX_STACKER_MODULE_TYPE,
} from '@opentrons/shared-data'
import { RUN_STATUS_FINISHING, RUN_STATUS_RUNNING } from '@opentrons/api-client'

Expand Down Expand Up @@ -75,7 +76,7 @@ import type {
import type { State, Dispatch } from '/app/redux/types'
import type { RequestState } from '/app/redux/robot-api/types'
import { AbsorbanceReaderData } from './AbsorbanceReaderData'
import { AbsorbanceReaderSlideout } from './AbsorbanceReaderSlideout'
import { FlexStackerModuleData } from './FlexStackerModuleData'

interface ModuleCardProps {
module: AttachedModule
Expand Down Expand Up @@ -132,6 +133,7 @@ export const ModuleCard = (props: ModuleCardProps): JSX.Element | null => {
isFlex &&
!MODULE_MODELS_OT2_ONLY.some(modModel => modModel === module.moduleModel) &&
module.moduleType !== ABSORBANCE_READER_TYPE &&
module.moduleType !== FLEX_STACKER_MODULE_TYPE &&
module.moduleOffset?.last_modified == null
const isPipetteReady =
!Boolean(attachPipetteRequired) &&
Expand Down Expand Up @@ -214,6 +216,11 @@ export const ModuleCard = (props: ModuleCardProps): JSX.Element | null => {
moduleData = <AbsorbanceReaderData moduleData={module.data} />
break
}

case FLEX_STACKER_MODULE_TYPE: {
moduleData = <FlexStackerModuleData moduleData={module.data} />
break
}
}

const handleMenuItemClick = (isSecondary: boolean = false): void => {
Expand Down Expand Up @@ -419,6 +426,10 @@ export const ModuleCard = (props: ModuleCardProps): JSX.Element | null => {
{module?.usbPort !== null
? t('usb_port', {
port: module?.usbPort?.port,
hubPort:
module?.usbPort?.hubPort != null
? `.${module.usbPort.hubPort}`
: '',
})
: t('usb_port_not_connected')}
</LegacyStyledText>
Expand Down Expand Up @@ -532,21 +543,15 @@ const ModuleSlideout = (props: ModuleSlideoutProps): JSX.Element => {
isExpanded={showSlideout}
/>
)
} else if (module.moduleType === ABSORBANCE_READER_TYPE) {
return (
<AbsorbanceReaderSlideout
module={module}
onCloseClick={onCloseClick}
isExpanded={showSlideout}
/>
)
} else {
} else if (module.moduleType === HEATERSHAKER_MODULE_TYPE) {
return (
<HeaterShakerSlideout
module={module}
onCloseClick={onCloseClick}
isExpanded={showSlideout}
/>
)
} else {
return <></>
}
}
5 changes: 5 additions & 0 deletions app/src/organisms/ModuleCard/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ import heaterShakerModule from '/app/assets/images/heater_shaker_module_transpar
import thermoModuleGen2Closed from '/app/assets/images/thermocycler_gen_2_closed.png'
import thermoModuleGen2Opened from '/app/assets/images/thermocycler_gen_2_opened.png'
import absorbanceReader from '/app/assets/images/opentrons_plate_reader.png'
// TODO (sb, 1/25): add correct flex stacker asset when it exits
import flexStacker from '/app/assets/images/FLEX.png'

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

export function getModuleCardImage(attachedModule: AttachedModule): string {
// TODO(jr, 9/22/22): add images for V1 of magneticModule and temperatureModule
switch (attachedModule.moduleModel) {
// TODO: Add correct image for flex stacker
case 'magneticModuleV1':
case 'magneticModuleV2':
return magneticModule
Expand All @@ -40,6 +43,8 @@ export function getModuleCardImage(attachedModule: AttachedModule): string {
}
case 'absorbanceReaderV1':
return absorbanceReader
case 'flexStackerModuleV1':
return flexStacker
// this should never be reached
default:
return 'unknown module model, this is an error'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
NON_CONNECTING_MODULE_TYPES,
TC_MODULE_LOCATION_OT3,
THERMOCYCLER_MODULE_TYPE,
FLEX_STACKER_MODULE_TYPE,
} from '@opentrons/shared-data'

import { SmallButton } from '/app/atoms/buttons'
Expand Down Expand Up @@ -206,7 +207,8 @@ function ModuleTableItem({
} else if (
isModuleReady &&
(module.attachedModuleMatch?.moduleOffset?.last_modified != null ||
module.attachedModuleMatch?.moduleType === ABSORBANCE_READER_TYPE)
module.attachedModuleMatch?.moduleType === ABSORBANCE_READER_TYPE ||
module.attachedModuleMatch?.moduleType === FLEX_STACKER_MODULE_TYPE)
) {
moduleStatus = (
<Chip
Expand Down Expand Up @@ -269,8 +271,9 @@ function ModuleTableItem({
backgroundColor={
isModuleReady &&
(module.attachedModuleMatch?.moduleOffset?.last_modified != null ||
module.attachedModuleMatch?.moduleType === ABSORBANCE_READER_TYPE ||
module.attachedModuleMatch?.moduleType ===
ABSORBANCE_READER_TYPE) &&
FLEX_STACKER_MODULE_TYPE) &&
conflictedFixture == null
? COLORS.green35
: isNonConnectingModule && conflictedFixture == null
Expand Down
11 changes: 11 additions & 0 deletions app/src/redux/modules/api-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface PhysicalPort {
path: string | null
port: number
hub: boolean
hubPort?: number
portGroup: PortGroup
}

Expand Down Expand Up @@ -84,6 +85,14 @@ export interface AbsorbanceReaderData {
sampleWavelength: number | null
status: AbsorbanceReaderStatus
}
export interface FlexStackerData {
latchState: 'opened' | 'closed' | 'unknown'
platformState: 'extended' | 'retracted' | 'unknown'
hopperDoorState: 'opened' | 'closed' | 'unknown'
axisStateX: 'extended' | 'retracted' | 'unknown'
axisStateZ: 'extended' | 'retracted' | 'unknown'
status: FlexStackerStatus
}

export type TemperatureStatus =
| 'idle'
Expand Down Expand Up @@ -119,6 +128,8 @@ export type LatchStatus =

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

export type FlexStackerStatus = 'idle' | 'dispensing' | 'storing' | 'error'

export interface ApiTemperatureModule extends ApiBaseModule {
moduleModel: TemperatureModuleModel
name: typeof TEMPDECK
Expand Down
Loading

0 comments on commit 9ac34e3

Please sign in to comment.