Skip to content

Commit 7a13b78

Browse files
authored
Add restart monitoring for manual firmware updates (#271)
* add restart monitoring for firmware updates
1 parent 50b1f43 commit 7a13b78

File tree

5 files changed

+115
-21
lines changed

5 files changed

+115
-21
lines changed

interface/src/AppRouting.tsx

+12-4
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@ import AuthenticatedRouting from './AuthenticatedRouting';
1212
interface SecurityRedirectProps {
1313
message: string;
1414
variant?: VariantType;
15+
signOut?: boolean;
1516
}
1617

17-
const SecurityRedirect: FC<SecurityRedirectProps> = ({ message, variant }) => {
18+
const RootRedirect: FC<SecurityRedirectProps> = ({ message, variant, signOut }) => {
1819
const authenticationContext = useContext(AuthenticationContext);
1920
const { enqueueSnackbar } = useSnackbar();
2021
useEffect(() => {
21-
authenticationContext.signOut(false);
22+
signOut && authenticationContext.signOut(false);
2223
enqueueSnackbar(message, { variant });
23-
}, [message, variant, authenticationContext, enqueueSnackbar]);
24+
}, [message, variant, signOut, authenticationContext, enqueueSnackbar]);
2425
return (<Navigate to="/" />);
2526
};
2627

@@ -43,7 +44,14 @@ const AppRouting: FC = () => {
4344
<Authentication>
4445
<RemoveTrailingSlashes />
4546
<Routes>
46-
<Route path="/unauthorized" element={<SecurityRedirect message="Please log in to continue" />} />
47+
<Route
48+
path="/unauthorized"
49+
element={<RootRedirect message="Please log in to continue" signOut />}
50+
/>
51+
<Route
52+
path="/firmwareUpdated"
53+
element={<RootRedirect message="Firmware update successful" variant="success" />}
54+
/>
4755
{features.security &&
4856
<Route
4957
path="/"

interface/src/api/system.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { AxiosPromise } from 'axios';
33
import { OTASettings, SystemStatus } from '../types';
44
import { AXIOS, FileUploadConfig, uploadFile } from './endpoints';
55

6-
export function readSystemStatus(): AxiosPromise<SystemStatus> {
7-
return AXIOS.get('/systemStatus');
6+
export function readSystemStatus(timeout?: number): AxiosPromise<SystemStatus> {
7+
return AXIOS.get('/systemStatus', { timeout });
88
}
99

1010
export function restart(): AxiosPromise<void> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { AxiosPromise } from 'axios';
2+
import { FC } from 'react';
3+
4+
import { FileUploadConfig } from '../../api/endpoints';
5+
import { MessageBox, SingleUpload, useFileUpload } from '../../components';
6+
7+
interface UploadFirmwareProps {
8+
uploadFirmware: ((file: File, config?: FileUploadConfig) => AxiosPromise<void>)
9+
}
10+
11+
const FirmwareFileUpload: FC<UploadFirmwareProps> = ({ uploadFirmware }) => {
12+
13+
const [uploadFile, cancelUpload, uploading, uploadProgress] = useFileUpload({ upload: uploadFirmware });
14+
15+
return (
16+
<>
17+
<MessageBox
18+
message="Upload a new firmware (.bin) file below to replace the existing firmware."
19+
level="warning"
20+
my={2}
21+
/>
22+
<SingleUpload
23+
accept="application/octet-stream"
24+
onDrop={uploadFile}
25+
onCancel={cancelUpload}
26+
uploading={uploading}
27+
progress={uploadProgress}
28+
/>
29+
</>
30+
);
31+
32+
};
33+
34+
export default FirmwareFileUpload;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { useEffect } from 'react';
2+
import { FC, useRef, useState } from 'react';
3+
4+
import * as SystemApi from "../../api/system";
5+
import { FormLoader } from '../../components';
6+
7+
const RESTART_TIMEOUT = 2 * 60 * 1000;
8+
const POLL_TIMEOUT = 2000;
9+
const POLL_INTERVAL = 5000;
10+
11+
const FirmwareRestartMonitor: FC = () => {
12+
13+
const [failed, setFailed] = useState<boolean>(false);
14+
const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout>();
15+
16+
const timeoutAt = useRef(new Date().getTime() + RESTART_TIMEOUT);
17+
const poll = useRef(async () => {
18+
try {
19+
await SystemApi.readSystemStatus(POLL_TIMEOUT);
20+
document.location.href = "/firmwareUpdated";
21+
} catch (error: any) {
22+
if (new Date().getTime() < timeoutAt.current) {
23+
setTimeoutId(setTimeout(poll.current, POLL_INTERVAL));
24+
} else {
25+
setFailed(true);
26+
}
27+
}
28+
});
29+
30+
useEffect(
31+
() => {
32+
poll.current();
33+
},
34+
[]
35+
);
36+
37+
useEffect(() => () => timeoutId && clearTimeout(timeoutId), [timeoutId]);
38+
39+
return (
40+
<FormLoader
41+
message="Device restarting, please wait&hellip;"
42+
errorMessage={failed ? "Timed out waiting for device to restart." : undefined}
43+
/>
44+
);
45+
46+
};
47+
48+
export default FirmwareRestartMonitor;

interface/src/framework/system/UploadFirmwareForm.tsx

+19-15
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
1-
import { FC } from 'react';
1+
import { FC, useRef, useState } from 'react';
22

33
import * as SystemApi from "../../api/system";
4-
import { MessageBox, SectionContent, SingleUpload, useFileUpload } from '../../components';
4+
import { SectionContent } from '../../components';
5+
import { FileUploadConfig } from '../../api/endpoints';
6+
7+
import FirmwareFileUpload from './FirmwareFileUpload';
8+
import FirmwareRestartMonitor from './FirmwareRestartMonitor';
59

610
const UploadFirmwareForm: FC = () => {
711

8-
const [uploadFile, cancelUpload, uploading, uploadProgress] = useFileUpload({ upload: SystemApi.uploadFirmware });
12+
const [restarting, setRestarting] = useState<boolean>();
13+
14+
const uploadFirmware = useRef(async (file: File, config?: FileUploadConfig) => {
15+
const response = await SystemApi.uploadFirmware(file, config);
16+
setRestarting(true);
17+
return response;
18+
});
919

1020
return (
1121
<SectionContent title='Upload Firmware' titleGutter>
12-
<MessageBox
13-
message="Upload a new firmware (.bin) file below to replace the existing firmware."
14-
level="warning"
15-
my={2}
16-
/>
17-
<SingleUpload
18-
accept="application/octet-stream"
19-
onDrop={uploadFile}
20-
onCancel={cancelUpload}
21-
uploading={uploading}
22-
progress={uploadProgress}
23-
/>
22+
{
23+
restarting ?
24+
<FirmwareRestartMonitor />
25+
:
26+
<FirmwareFileUpload uploadFirmware={uploadFirmware.current} />
27+
}
2428
</SectionContent>
2529
);
2630

0 commit comments

Comments
 (0)