Skip to content

Commit 7316d72

Browse files
authored
fix(app): Allow starting protocols on robots with completed runs (#15839)
When you click the button to start protocol setup, we try to filter to only robots that are currently capable of starting a protocol. We implemented that as - You can connect to the robot - The robot doesn't have a current run But, robots don't actually get rid of their current run until a user either clicks the little X button in the run screen on the desktop or goes back to the main screen of the ODD. That doesn't necessarily align with a user's view of "this protocol is done", and so people would think that the robot just disappeared from that little slideout. To fix this, we should also consider robots that have a completed protocol to be done. This is a small conceptual change and unfortunately a huge pain to implement because data about whether a run is done has to come from getting the state data for that run. ## Testing - [x] The setup protocol slideout should show robots that have completed, failed, or cancelled active runs - [x] If you start setup, it should work - [x] The setup protocol slideout should show robots that do not have an active run - [x] The setup protocol slideout should not show robots that have an active run that is not completed, failed, or canceled Closes EXEC-519
1 parent 2d38eb9 commit 7316d72

37 files changed

+128
-92
lines changed

app/src/organisms/CalibrationTaskList/__tests__/CalibrationTaskList.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
import { mockLeftProtoPipette } from '../../../redux/pipettes/__fixtures__'
2727

2828
vi.mock('../../Devices/hooks')
29-
vi.mock('../../ProtocolUpload/hooks')
29+
vi.mock('../../../resources/runs')
3030

3131
const render = (robotName: string = 'otie') => {
3232
return renderWithProviders(

app/src/organisms/CalibrationTaskList/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
useCalibrationTaskList,
2626
useRunHasStarted,
2727
} from '../Devices/hooks'
28-
import { useCurrentRunId } from '../ProtocolUpload/hooks'
28+
import { useCurrentRunId } from '../../resources/runs'
2929

3030
import type { DashboardCalOffsetInvoker } from '../../pages/Devices/CalibrationDashboard/hooks/useDashboardCalibratePipOffset'
3131
import type { DashboardCalTipLengthInvoker } from '../../pages/Devices/CalibrationDashboard/hooks/useDashboardCalibrateTipLength'

app/src/organisms/ChooseRobotSlideout/AvailableRobotOption.tsx

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { getNetworkInterfaces, fetchStatus } from '../../redux/networking'
2222
import { appShellRequestor } from '../../redux/shell/remote'
2323
import OT2_PNG from '../../assets/images/OT2-R_HERO.png'
2424
import FLEX_PNG from '../../assets/images/FLEX.png'
25-
import { useNotifyAllRunsQuery } from '../../resources/runs'
25+
import { useCurrentRunId, useNotifyRunQuery } from '../../resources/runs'
2626

2727
import type { IconName } from '@opentrons/components'
2828
import type { Runs } from '@opentrons/api-client'
@@ -59,14 +59,15 @@ export function AvailableRobotOption(
5959
getRobotModelByName(state, robotName)
6060
)
6161

62-
const { data: runsData } = useNotifyAllRunsQuery(
63-
{ pageLength: 0 },
62+
const [isBusy, setIsBusy] = React.useState(true)
63+
64+
const currentRunId = useCurrentRunId(
6465
{
6566
onSuccess: data => {
66-
if ((data as Runs)?.links?.current != null)
67-
registerRobotBusyStatus({ type: 'robotIsBusy', robotName })
68-
else {
67+
const definitelyIdle = (data as Runs)?.links?.current == null
68+
if (definitelyIdle) {
6969
registerRobotBusyStatus({ type: 'robotIsIdle', robotName })
70+
setIsBusy(false)
7071
}
7172
},
7273
},
@@ -75,7 +76,28 @@ export function AvailableRobotOption(
7576
requestor: ip === OPENTRONS_USB ? appShellRequestor : undefined,
7677
}
7778
)
78-
const robotHasCurrentRun = runsData?.links?.current != null
79+
80+
useNotifyRunQuery(
81+
currentRunId,
82+
{
83+
onSuccess: data => {
84+
const busy = data?.data != null && data.data.completedAt == null
85+
registerRobotBusyStatus({
86+
type: busy ? 'robotIsBusy' : 'robotIsIdle',
87+
robotName,
88+
})
89+
setIsBusy(busy)
90+
},
91+
onError: () => {
92+
registerRobotBusyStatus({ type: 'robotIsIdle', robotName })
93+
setIsBusy(false)
94+
},
95+
},
96+
{
97+
hostname: ip,
98+
requestor: ip === OPENTRONS_USB ? appShellRequestor : undefined,
99+
}
100+
)
79101

80102
const { ethernet, wifi } = useSelector((state: State) =>
81103
getNetworkInterfaces(state, robotName)
@@ -95,7 +117,7 @@ export function AvailableRobotOption(
95117
// eslint-disable-next-line react-hooks/exhaustive-deps
96118
}, [])
97119

98-
return showIdleOnly && robotHasCurrentRun ? null : (
120+
return showIdleOnly && isBusy ? null : (
99121
<>
100122
<MiniCard
101123
onClick={onClick}

app/src/organisms/ChooseRobotToRunProtocolSlideout/__tests__/ChooseRobotToRunProtocolSlideout.test.tsx

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ import { when } from 'vitest-when'
77
import { renderWithProviders } from '../../../__testing-utils__'
88
import { i18n } from '../../../i18n'
99
import { useTrackCreateProtocolRunEvent } from '../../../organisms/Devices/hooks'
10-
import {
11-
useCloseCurrentRun,
12-
useCurrentRunId,
13-
} from '../../../organisms/ProtocolUpload/hooks'
10+
import { useCloseCurrentRun } from '../../../organisms/ProtocolUpload/hooks'
1411
import { useCurrentRunStatus } from '../../../organisms/RunTimeControl/hooks'
1512
import {
1613
getConnectableRobots,
@@ -34,6 +31,7 @@ import { useCreateRunFromProtocol } from '../useCreateRunFromProtocol'
3431
import { useOffsetCandidatesForAnalysis } from '../../ApplyHistoricOffsets/hooks/useOffsetCandidatesForAnalysis'
3532
import { ChooseRobotToRunProtocolSlideout } from '../'
3633
import { useNotifyDataReady } from '../../../resources/useNotifyDataReady'
34+
import { useCurrentRunId } from '../../../resources/runs'
3735

3836
import type { State } from '../../../redux/types'
3937

@@ -46,6 +44,7 @@ vi.mock('../../../redux/networking')
4644
vi.mock('../useCreateRunFromProtocol')
4745
vi.mock('../../ApplyHistoricOffsets/hooks/useOffsetCandidatesForAnalysis')
4846
vi.mock('../../../resources/useNotifyDataReady')
47+
vi.mock('../../../resources/runs')
4948

5049
const render = (
5150
props: React.ComponentProps<typeof ChooseRobotToRunProtocolSlideout>
@@ -88,7 +87,7 @@ describe('ChooseRobotToRunProtocolSlideout', () => {
8887
isClosingCurrentRun: false,
8988
closeCurrentRun: mockCloseCurrentRun,
9089
})
91-
vi.mocked(useCurrentRunId).mockReturnValue(null)
90+
provideNullCurrentRunIdFor(mockConnectableRobot.ip)
9291
vi.mocked(useCurrentRunStatus).mockReturnValue(null)
9392
when(vi.mocked(useCreateRunFromProtocol))
9493
.calledWith(
@@ -191,6 +190,7 @@ describe('ChooseRobotToRunProtocolSlideout', () => {
191190
{ ...mockConnectableRobot, name: 'otherRobot', ip: 'otherIp' },
192191
mockConnectableRobot,
193192
])
193+
provideNullCurrentRunIdFor('otherIp')
194194
render({
195195
storedProtocolData: storedProtocolDataFixture,
196196
onCloseClick: vi.fn(),
@@ -372,6 +372,7 @@ describe('ChooseRobotToRunProtocolSlideout', () => {
372372
mockConnectableRobot,
373373
{ ...mockConnectableRobot, name: 'otherRobot', ip: 'otherIp' },
374374
])
375+
provideNullCurrentRunIdFor('otherIp')
375376
render({
376377
storedProtocolData: storedProtocolDataFixture,
377378
onCloseClick: vi.fn(),
@@ -387,7 +388,7 @@ describe('ChooseRobotToRunProtocolSlideout', () => {
387388
fireEvent.click(proceedButton)
388389
fireEvent.click(screen.getByRole('button', { name: 'Confirm values' }))
389390
expect(vi.mocked(useCreateRunFromProtocol)).nthCalledWith(
390-
2,
391+
3,
391392
expect.any(Object),
392393
{ hostname: '127.0.0.1' },
393394
[
@@ -450,3 +451,21 @@ describe('ChooseRobotToRunProtocolSlideout', () => {
450451
)
451452
})
452453
})
454+
455+
const provideNullCurrentRunIdFor = (hostname: string): void => {
456+
let once = true
457+
when(vi.mocked(useCurrentRunId))
458+
.calledWith(expect.any(Object), {
459+
hostname,
460+
requestor: undefined,
461+
})
462+
.thenDo(options => {
463+
void (options?.onSuccess != null && once
464+
? options.onSuccess({
465+
links: { current: null },
466+
} as any)
467+
: {})
468+
once = false
469+
return null
470+
})
471+
}

app/src/organisms/Devices/InstrumentsAndModules.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222

2323
import { Banner } from '../../atoms/Banner'
2424
import { PipetteRecalibrationWarning } from './PipetteCard/PipetteRecalibrationWarning'
25-
import { useCurrentRunId } from '../ProtocolUpload/hooks'
25+
import { useCurrentRunId } from '../../resources/runs'
2626
import { ModuleCard } from '../ModuleCard'
2727
import { useIsFlex, useIsRobotViewable, useRunStatuses } from './hooks'
2828
import { getShowPipetteCalibrationWarning } from './utils'

app/src/organisms/Devices/ProtocolRun/ProtocolRunHeader.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,7 @@ import {
6767
} from '../../../redux/analytics'
6868
import { getIsHeaterShakerAttached } from '../../../redux/config'
6969
import { Tooltip } from '../../../atoms/Tooltip'
70-
import {
71-
useCloseCurrentRun,
72-
useCurrentRunId,
73-
} from '../../../organisms/ProtocolUpload/hooks'
70+
import { useCloseCurrentRun } from '../../../organisms/ProtocolUpload/hooks'
7471
import { ConfirmCancelModal } from '../../../organisms/RunDetails/ConfirmCancelModal'
7572
import { HeaterShakerIsRunningModal } from '../HeaterShakerIsRunningModal'
7673
import {
@@ -103,7 +100,7 @@ import { getIsFixtureMismatch } from '../../../resources/deck_configuration/util
103100
import { useDeckConfigurationCompatibility } from '../../../resources/deck_configuration/hooks'
104101
import { useMostRecentCompletedAnalysis } from '../../LabwarePositionCheck/useMostRecentCompletedAnalysis'
105102
import { useMostRecentRunId } from '../../ProtocolUpload/hooks/useMostRecentRunId'
106-
import { useNotifyRunQuery } from '../../../resources/runs'
103+
import { useNotifyRunQuery, useCurrentRunId } from '../../../resources/runs'
107104
import {
108105
useErrorRecoveryFlows,
109106
ErrorRecoveryFlows,

app/src/organisms/Devices/ProtocolRun/__tests__/ProtocolRunHeader.test.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ import {
3434

3535
import { renderWithProviders } from '../../../../__testing-utils__'
3636
import { i18n } from '../../../../i18n'
37-
import {
38-
useCloseCurrentRun,
39-
useCurrentRunId,
40-
} from '../../../../organisms/ProtocolUpload/hooks'
37+
import { useCloseCurrentRun } from '../../../../organisms/ProtocolUpload/hooks'
4138
import { ConfirmCancelModal } from '../../../../organisms/RunDetails/ConfirmCancelModal'
4239
import {
4340
useRunTimestamps,
@@ -87,7 +84,7 @@ import { getIsFixtureMismatch } from '../../../../resources/deck_configuration/u
8784
import { useDeckConfigurationCompatibility } from '../../../../resources/deck_configuration/hooks'
8885
import { useMostRecentCompletedAnalysis } from '../../../LabwarePositionCheck/useMostRecentCompletedAnalysis'
8986
import { useMostRecentRunId } from '../../../ProtocolUpload/hooks/useMostRecentRunId'
90-
import { useNotifyRunQuery } from '../../../../resources/runs'
87+
import { useNotifyRunQuery, useCurrentRunId } from '../../../../resources/runs'
9188
import {
9289
useDropTipWizardFlows,
9390
useTipAttachmentStatus,

app/src/organisms/Devices/RecentProtocolRuns.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ import {
1616
TYPOGRAPHY,
1717
} from '@opentrons/components'
1818

19-
import { useCurrentRunId } from '../ProtocolUpload/hooks'
2019
import { HistoricalProtocolRun } from './HistoricalProtocolRun'
2120
import { useIsRobotViewable, useRunStatuses } from './hooks'
22-
import { useNotifyAllRunsQuery } from '../../resources/runs'
21+
import { useNotifyAllRunsQuery, useCurrentRunId } from '../../resources/runs'
2322
import { useFeatureFlag } from '../../redux/config'
2423

2524
interface RecentProtocolRunsProps {

app/src/organisms/Devices/RobotOverflowMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { Divider } from '../../atoms/structure'
2424
import { MenuItem } from '../../atoms/MenuList/MenuItem'
2525
import { getTopPortalEl } from '../../App/portal'
2626
import { ChooseProtocolSlideout } from '../ChooseProtocolSlideout'
27-
import { useCurrentRunId } from '../ProtocolUpload/hooks'
27+
import { useCurrentRunId } from '../../resources/runs'
2828
import { ConnectionTroubleshootingModal } from './ConnectionTroubleshootingModal'
2929
import { useMenuHandleClickOutside } from '../../atoms/MenuList/hooks'
3030
import { useIsRobotBusy } from './hooks'

app/src/organisms/Devices/RobotOverviewOverflowMenu.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import { Tooltip } from '../../atoms/Tooltip'
2525
import { ChooseProtocolSlideout } from '../../organisms/ChooseProtocolSlideout'
2626
import { DisconnectModal } from '../../organisms/Devices/RobotSettings/ConnectNetwork/DisconnectModal'
2727
import { handleUpdateBuildroot } from '../../organisms/Devices/RobotSettings/UpdateBuildroot'
28-
import { useCurrentRunId } from '../../organisms/ProtocolUpload/hooks'
2928
import { getRobotUpdateDisplayInfo } from '../../redux/robot-update'
3029
import { UNREACHABLE, CONNECTABLE, REACHABLE } from '../../redux/discovery'
3130
import { checkShellUpdate } from '../../redux/shell'
@@ -34,7 +33,7 @@ import { home, ROBOT } from '../../redux/robot-controls'
3433
import { useIsRobotBusy } from './hooks'
3534
import { useCanDisconnect } from '../../resources/networking/hooks'
3635
import { useIsEstopNotDisengaged } from '../../resources/devices/hooks/useIsEstopNotDisengaged'
37-
36+
import { useCurrentRunId } from '../../resources/runs'
3837
import type { DiscoveredRobot } from '../../redux/discovery/types'
3938
import type { Dispatch, State } from '../../redux/types'
4039

0 commit comments

Comments
 (0)