Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app,shared-data): Add basic support for the Flex Stacker module to the front-end. #17295

Merged
merged 9 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -964,6 +964,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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend adding tests

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make a TODO to add tests when we build this out to design specs. This PR is just meant to unblock science & ABR from using the stacker in a protocol!

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
Loading