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

UI: Runtime info on about page #1089

Merged
merged 1 commit into from
Sep 17, 2021
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
9 changes: 9 additions & 0 deletions frontend/src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
RobotInformation,
Segment,
SystemHostInfo,
SystemRuntimeInfo,
Timer,
TimerInformation,
TimerProperties,
Expand Down Expand Up @@ -278,6 +279,14 @@ export const fetchSystemHostInfo = async (): Promise<SystemHostInfo> => {
});
};

export const fetchSystemRuntimeInfo = async (): Promise<SystemRuntimeInfo> => {
return valetudoAPI
.get<SystemRuntimeInfo>("/system/runtime/info")
.then(({data}) => {
return data;
});
};

export const fetchLatestGitHubRelease = async (): Promise<GitHubRelease> => {
return axios
.get<GitHubRelease[]>(
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/api/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
fetchSegments,
fetchStateAttributes,
fetchSystemHostInfo,
fetchSystemRuntimeInfo,
fetchTimerInformation,
fetchTimerProperties,
fetchValetudoInformation,
Expand Down Expand Up @@ -74,6 +75,7 @@ enum CacheKey {
ValetudoVersion = "valetudo_version",
GitHubRelease = "github_release",
SystemHostInfo = "system_host_info",
SystemRuntimeInfo = "system_runtime_info",
MQTTConfiguration = "mqtt_configuration",
MQTTProperties = "mqtt_properties",
Timers = "timers",
Expand Down Expand Up @@ -398,6 +400,10 @@ export const useSystemHostInfoQuery = () => {
return useQuery(CacheKey.SystemHostInfo, fetchSystemHostInfo);
};

export const useSystemRuntimeInfoQuery = () => {
return useQuery(CacheKey.SystemRuntimeInfo, fetchSystemRuntimeInfo);
};

export const useMQTTConfigurationQuery = () => {
return useQuery(CacheKey.MQTTConfiguration, fetchMQTTConfiguration);
};
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ export interface SystemHostInfo {
};
}

export interface SystemRuntimeInfo {
uptime: number;
argv: Array<string>;
execArgv: Array<string>;
execPath: string;
uid: number;
gid: number;
pid: number;
versions: Record<string, string>;
env: Record<string, string>
}

export interface MapSegmentationActionRequestParameters {
segment_ids: string[];
iterations?: number;
Expand Down
228 changes: 222 additions & 6 deletions frontend/src/settings/About.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
import {
Box,
Button,
ButtonGroup,
Card,
CardContent,
CircularProgress,
Container,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Divider,
Fade,
Grid, LinearProgress,
Grid,
LinearProgress,
Link,
Paper,
Skeleton,
Stack,
styled,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
} from "@material-ui/core";
import { withStyles } from "@material-ui/styles";
import {withStyles} from "@material-ui/styles";
import {Refresh as RefreshIcon} from "@material-ui/icons";
import React from "react";
import {
useLatestGitHubReleaseLazyQuery,
useRobotInformationQuery,
useSystemHostInfoQuery,
useSystemRuntimeInfoQuery,
useValetudoVersionQuery,
} from "../api";
import RatioBar from "../compontents/RatioBar";
import {convertSecondsToHumans} from "../utils";
import {useIsMobileView} from "../hooks";

const TopRightIconButton = styled(Button)(({theme}) => {
return {
Expand All @@ -37,6 +54,200 @@ const ThickLinearProgressWithTopMargin = withStyles({
}
})(LinearProgress);

const SystemRuntimeInfo = (): JSX.Element => {
const {
data: systemRuntimeInfo,
isLoading: systemRuntimeInfoLoading,
refetch: fetchSystemRuntimeInfo,
} = useSystemRuntimeInfoQuery();

const [nodeDialogOpen, setNodeDialogOpen] = React.useState(false);
const [envDialogOpen, setEnvDialogOpen] = React.useState(false);

const mobileView = useIsMobileView();

const systemRuntimeInformation = React.useMemo(() => {
if (systemRuntimeInfoLoading) {
return <Skeleton/>;
}

if (!systemRuntimeInfo) {
return <Typography color="error">No runtime information</Typography>;
}

const topItems: Array<[header: string, body: string]> = [
["Valetudo uptime", convertSecondsToHumans(systemRuntimeInfo.uptime)],
["UID", String(systemRuntimeInfo.uid)],
["GID", String(systemRuntimeInfo.gid)],
["PID", String(systemRuntimeInfo.pid)],
["argv", systemRuntimeInfo.argv.join(" ")]
];

const versionRows = Object.keys(systemRuntimeInfo.versions).map(lib => {
const version = systemRuntimeInfo.versions[lib];
return (
<TableRow
key={lib}
sx={{"&:last-child td, &:last-child th": {border: 0}}}
>
<TableCell component="th" scope="row">{lib}</TableCell>
<TableCell align="right">{version}</TableCell>
</TableRow>
);
});

const environmentRows = Object.keys(systemRuntimeInfo.env).sort().map(key => {
const value = systemRuntimeInfo.env[key];
return (
<TableRow
key={key}
sx={{"&:last-child td, &:last-child th": {border: 0}}}
>
<TableCell component="th" scope="row">{key}</TableCell>
<TableCell>{value}</TableCell>
</TableRow>
);
});

return (
<Stack spacing={2}>
<Grid container spacing={2}>
{topItems.map(([header, body]) => {
return (
<Grid item key={header}>
<Typography variant="caption" color="textSecondary">
{header}
</Typography>
<Typography variant="body2">{body}</Typography>
</Grid>
);
})}
</Grid>
<ButtonGroup variant="outlined">
<Button onClick={() => {
setNodeDialogOpen(true);
}}>Node</Button>
<Button onClick={() => {
setEnvDialogOpen(true);
}}>Environment</Button>
</ButtonGroup>

<Dialog
open={nodeDialogOpen}
onClose={() => {
setNodeDialogOpen(false);
}}
scroll={"body"}
fullScreen={mobileView}
>
<DialogTitle>Node information</DialogTitle>
<DialogContent dividers>
<Stack spacing={2}>
<Grid container spacing={2}>
<Grid item>
<Typography variant="caption" color="textSecondary">
execPath
</Typography>
<Typography variant="body2">{systemRuntimeInfo.execPath}</Typography>
</Grid>
<Grid item>
<Typography variant="caption" color="textSecondary">
execArgv
</Typography>
<Typography
variant="body2">{systemRuntimeInfo.execArgv.join(" ")}</Typography>
</Grid>
</Grid>

<TableContainer component={Paper}>
<Table size="small">
<TableHead>
<TableRow>
<TableCell>Dependency</TableCell>
<TableCell align="right">Version</TableCell>
</TableRow>
</TableHead>
<TableBody>
{versionRows}
</TableBody>
</Table>
</TableContainer>
</Stack>
</DialogContent>
<DialogActions>
<Button onClick={() => {
setNodeDialogOpen(false);
}}>Close</Button>
</DialogActions>
</Dialog>

<Dialog
open={envDialogOpen}
onClose={() => {
setEnvDialogOpen(false);
}}
fullScreen={mobileView}
maxWidth={"xl"}
scroll={"body"}
>
<DialogTitle>Environment</DialogTitle>
<DialogContent>
<TableContainer component={Paper}>
<Table size="small" stickyHeader>
<TableHead>
<TableRow>
<TableCell>Key</TableCell>
<TableCell>Value</TableCell>
</TableRow>
</TableHead>
<TableBody>
{environmentRows}
</TableBody>
</Table>
</TableContainer>
</DialogContent>
<DialogActions>
<Button onClick={() => {
setEnvDialogOpen(false);
}}>Close</Button>
</DialogActions>
</Dialog>
</Stack>
);
}, [systemRuntimeInfoLoading, systemRuntimeInfo, nodeDialogOpen, envDialogOpen, mobileView]);

return (
<Card>
<CardContent>
<Grid
container
spacing={4}
alignItems="center"
justifyContent="space-between"
>
<Grid item>
<Typography variant="h6" gutterBottom>
Runtime Information
</Typography>
</Grid>
<Grid item>
<TopRightIconButton
disabled={systemRuntimeInfoLoading}
onClick={() => {
return fetchSystemRuntimeInfo();
}}
>
<RefreshIcon/>
</TopRightIconButton>
</Grid>
</Grid>
<Divider/>
{systemRuntimeInformation}
</CardContent>
</Card>
);
};

const About = (): JSX.Element => {
const {
data: robotInformation,
Expand All @@ -57,7 +268,6 @@ const About = (): JSX.Element => {
refetch: fetchSystemHostInfo,
} = useSystemHostInfoQuery();


const systemLoading = robotInformationLoading || versionLoading || systemHostInfoLoading;
const newerReleaseAvailable = (githubReleaseInformation?.tag_name ?? "0.0.0") > (version?.release ?? "a");

Expand Down Expand Up @@ -232,9 +442,12 @@ const About = (): JSX.Element => {
System Load (1, 5, 15 Minutes)
</Typography>

<ThickLinearProgressWithTopMargin variant="determinate" value={Math.min(100, systemHostInfo.load["1"] * 100)} />
<ThickLinearProgressWithTopMargin variant="determinate" value={Math.min(100, systemHostInfo.load["5"] * 100)} />
<ThickLinearProgressWithTopMargin variant="determinate" value={Math.min(100, systemHostInfo.load["15"] * 100)} />
<ThickLinearProgressWithTopMargin variant="determinate"
value={Math.min(100, systemHostInfo.load["1"] * 100)}/>
<ThickLinearProgressWithTopMargin variant="determinate"
value={Math.min(100, systemHostInfo.load["5"] * 100)}/>
<ThickLinearProgressWithTopMargin variant="determinate"
value={Math.min(100, systemHostInfo.load["15"] * 100)}/>
</Grid>
</Grid>

Expand Down Expand Up @@ -322,6 +535,9 @@ const About = (): JSX.Element => {
</CardContent>
</Card>
</Grid>
<Grid item>
<SystemRuntimeInfo/>
</Grid>
</Grid>
</Container>
);
Expand Down