Skip to content

Commit

Permalink
fix(app): Disable run start button if missing modules (#3994)
Browse files Browse the repository at this point in the history
Closes #2676
  • Loading branch information
mcous authored Sep 17, 2019
1 parent 1272e59 commit 5c75152
Show file tree
Hide file tree
Showing 14 changed files with 180 additions and 214 deletions.
1 change: 0 additions & 1 deletion app/src/components/CalibratePanel/LabwareListItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export default function LabwareListItem(props: LabwareListItemProps) {
activeClassName={styles.active}
>
<HoverTooltip
placement="bottom-end"
tooltipComponent={
<LabwareNameTooltip
name={name}
Expand Down
40 changes: 10 additions & 30 deletions app/src/components/ConnectModules/index.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
// @flow
import * as React from 'react'
import { connect } from 'react-redux'
import countBy from 'lodash/countBy'

import { getModulesState, fetchModules } from '../../robot-api'
import { getMissingModules, fetchModules } from '../../robot-api'
import {
selectors as robotSelectors,
actions as robotActions,
} from '../../robot'

import { RefreshWrapper } from '../Page'
import DeckMap from '../DeckMap'
import Prompt from './Prompt'
import styles from './styles.css'

import type { State, Dispatch } from '../../types'
import type { RobotService, SessionModule } from '../../robot'
import type { Module } from '../../robot-api'
import type { RobotService } from '../../robot/types'

type OP = {| robot: RobotService |}

type SP = {| modulesRequired: boolean, modulesMissing: boolean |}

type DP = {| setReviewed: () => mixed, fetchModules: () => mixed |}

type Props = { ...OP, ...SP, ...DP }
type Props = {| ...OP, ...SP, ...DP |}

export default connect<Props, OP, SP, DP, State, Dispatch>(
mapStateToProps,
Expand All @@ -38,24 +35,19 @@ function ConnectModules(props: Props) {
const onPromptClick = modulesMissing ? fetchModules : setReviewed

return (
<RefreshWrapper refresh={fetchModules}>
<div className={styles.page_content_dark}>
<Prompt modulesMissing={modulesMissing} onClick={onPromptClick} />
<div className={styles.deck_map_wrapper}>
<DeckMap className={styles.deck_map} modulesRequired />
</div>
<div className={styles.page_content_dark}>
<Prompt modulesMissing={modulesMissing} onClick={onPromptClick} />
<div className={styles.deck_map_wrapper}>
<DeckMap className={styles.deck_map} modulesRequired />
</div>
</RefreshWrapper>
</div>
)
}

function mapStateToProps(state: State, ownProps: OP): SP {
const sessionModules = robotSelectors.getModules(state)
const actualModules = getModulesState(state, ownProps.robot.name)

return {
modulesRequired: sessionModules.length !== 0,
modulesMissing: checkModulesMissing(sessionModules, actualModules),
modulesRequired: robotSelectors.getModules(state).length > 0,
modulesMissing: getMissingModules(state).length > 0,
}
}

Expand All @@ -65,15 +57,3 @@ function mapDispatchToProps(dispatch: Dispatch, ownProps: OP): DP {
fetchModules: () => dispatch(fetchModules(ownProps.robot)),
}
}

function checkModulesMissing(
required: Array<SessionModule>,
actual: ?Array<Module>
): boolean {
const requiredNames = countBy(required, 'name')
const actualNames = countBy(actual, 'name')

return Object.keys(requiredNames).some(
n => requiredNames[n] !== actualNames[n]
)
}
28 changes: 7 additions & 21 deletions app/src/components/DeckMap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { withRouter } from 'react-router'
import some from 'lodash/some'
import map from 'lodash/map'
import mapValues from 'lodash/mapValues'
import countBy from 'lodash/countBy'
import { type DeckSlotId } from '@opentrons/shared-data'
import type { ContextRouter } from 'react-router'

Expand All @@ -19,8 +18,7 @@ import {
type SessionModule,
} from '../../robot'

import { getModulesState } from '../../robot-api'
import { getConnectedRobot } from '../../discovery'
import { getMissingModules } from '../../robot-api'

import LabwareItem from './LabwareItem'

Expand Down Expand Up @@ -128,30 +126,18 @@ function DeckMap(props: Props) {
function mapStateToProps(state: State, ownProps: OP): SP {
let modulesBySlot = mapValues(
robotSelectors.getModulesBySlot(state),
module => ({
...module,
mode: 'default',
})
module => ({ ...module, mode: 'default' })
)

// only show necessary modules if still need to connect some
if (ownProps.modulesRequired) {
const robot = getConnectedRobot(state)
const sessionModules = robotSelectors.getModules(state)
const actualModules = robot ? getModulesState(state, robot.name) : []

const requiredNames = countBy(sessionModules, 'name')
const actualNames = countBy(actualModules, 'name')
if (ownProps.modulesRequired === true) {
const missingModules = getMissingModules(state)

modulesBySlot = mapValues(
robotSelectors.getModulesBySlot(state),
module => {
const present =
!module || requiredNames[module.name] === actualNames[module.name]
return {
...module,
mode: present ? 'present' : 'missing',
}
const present = !missingModules.some(mm => mm.name === module.name)
return { ...module, mode: present ? 'present' : 'missing' }
}
)
return {
Expand All @@ -166,7 +152,7 @@ function mapStateToProps(state: State, ownProps: OP): SP {
}),
{}
)
if (!ownProps.enableLabwareSelection) {
if (ownProps.enableLabwareSelection !== true) {
return {
labwareBySlot,
modulesBySlot,
Expand Down
48 changes: 19 additions & 29 deletions app/src/components/FileInfo/ProtocolModulesCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import { connect } from 'react-redux'

import { getModuleDisplayName } from '@opentrons/shared-data'
import { selectors as robotSelectors } from '../../robot'
import { getModulesState, fetchModules } from '../../robot-api'
import { getModulesState } from '../../robot-api'

import { RefreshWrapper } from '../Page'
import InfoSection from './InfoSection'
import { SectionContentHalf } from '../layout'
import InstrumentItem from './InstrumentItem'
Expand All @@ -26,19 +25,18 @@ type SP = {|
attachModulesUrl: string,
|}

type DP = {| fetchModules: () => mixed |}
type DP = {| dispatch: Dispatch |}

type Props = { ...OP, ...SP, ...DP }
type Props = {| ...OP, ...SP, ...DP |}

const TITLE = 'Required Modules'

export default connect<Props, OP, SP, DP, _, _>(
mapStateToProps,
mapDispatchToProps
)(ProtocolModulesCard)
export default connect<Props, OP, SP, DP, _, _>(mapStateToProps)(
ProtocolModulesCard
)

function ProtocolModulesCard(props: Props) {
const { modules, actualModules, fetchModules, attachModulesUrl } = props
const { modules, actualModules, attachModulesUrl } = props

if (modules.length < 1) return null

Expand All @@ -52,20 +50,18 @@ function ProtocolModulesCard(props: Props) {
const modulesMatch = moduleInfo.every(m => m.modulesMatch)

return (
<RefreshWrapper refresh={fetchModules}>
<InfoSection title={TITLE}>
<SectionContentHalf>
{moduleInfo.map(m => (
<InstrumentItem key={m.slot} match={m.modulesMatch}>
{m.displayName}{' '}
</InstrumentItem>
))}
</SectionContentHalf>
{!modulesMatch && (
<InstrumentWarning instrumentType="module" url={attachModulesUrl} />
)}
</InfoSection>
</RefreshWrapper>
<InfoSection title={TITLE}>
<SectionContentHalf>
{moduleInfo.map(m => (
<InstrumentItem key={m.slot} match={m.modulesMatch}>
{m.displayName}{' '}
</InstrumentItem>
))}
</SectionContentHalf>
{!modulesMatch && (
<InstrumentWarning instrumentType="module" url={attachModulesUrl} />
)}
</InfoSection>
)
}

Expand All @@ -80,9 +76,3 @@ function mapStateToProps(state: State, ownProps: OP): SP {
attachModulesUrl: `/robots/${robot.name}/instruments`,
}
}

function mapDispatchToProps(dispatch: Dispatch, ownProps: OP): DP {
return {
fetchModules: () => dispatch(fetchModules(ownProps.robot)),
}
}
71 changes: 27 additions & 44 deletions app/src/components/InstrumentSettings/AttachedModulesCard.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,47 @@
// @flow
// attached modules container card
import * as React from 'react'
import { connect } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'

import { Card, IntervalWrapper } from '@opentrons/components'
import { Card, useInterval } from '@opentrons/components'
import { fetchModules, getModulesState } from '../../robot-api'
import ModulesCardContents from './ModulesCardContents'
import { getConfig } from '../../config'

import type { State, Dispatch } from '../../types'
import type { Module } from '../../robot-api'
import type { Robot } from '../../discovery'

type OP = {| robot: Robot |}

type SP = {|
modules: Array<Module>,
__tempControlsEnabled: boolean,
|}

type DP = {| fetchModules: () => mixed |}

type Props = { ...OP, ...SP, ...DP }
type Props = {| robot: Robot |}

const TITLE = 'Modules'
const POLL_MODULE_INTERVAL_MS = 5000

export default connect<Props, OP, SP, DP, State, Dispatch>(
mapStateToProps,
mapDispatchToProps
)(AttachedModulesCard)
export default function AttachedModulesCard(props: Props) {
const { robot } = props
const dispatch = useDispatch<Dispatch>()

function AttachedModulesCard(props: Props) {
return (
<IntervalWrapper
interval={POLL_MODULE_INTERVAL_MS}
refresh={props.fetchModules}
>
<Card title={TITLE} column>
<ModulesCardContents
modules={props.modules}
robot={props.robot}
showControls={props.__tempControlsEnabled}
/>
</Card>
</IntervalWrapper>
const modules = useSelector((state: State) =>
getModulesState(state, robot.name)
)
const __tempControlsEnabled = Boolean(
useSelector(getConfig).devInternal?.tempdeckControls
)
}

function mapStateToProps(state: State, ownProps: OP): SP {
return {
modules: getModulesState(state, ownProps.robot.name),
__tempControlsEnabled: Boolean(
getConfig(state).devInternal?.tempdeckControls
),
}
}
// this component may be mounted if the robot is not currently connected, so
// GET /modules ourselves instead of relying on the poll while connected epic
useInterval(
() => dispatch(fetchModules(robot)),
POLL_MODULE_INTERVAL_MS,
true
)

function mapDispatchToProps(dispatch: Dispatch, ownProps: OP): DP {
return {
fetchModules: () => dispatch(fetchModules(ownProps.robot)),
}
return (
<Card title={TITLE} column>
<ModulesCardContents
robot={robot}
modules={modules}
showControls={__tempControlsEnabled}
/>
</Card>
)
}
4 changes: 1 addition & 3 deletions app/src/components/InstrumentSettings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import { CardContainer, CardRow } from '../layout'

import type { Robot } from '../../discovery'

type Props = {
robot: Robot,
}
type Props = {| robot: Robot |}

export default function InstrumentSettings(props: Props) {
return (
Expand Down
Loading

0 comments on commit 5c75152

Please sign in to comment.