adding memory and cpu display to test.amqtt.io dashboard

pull/257/head
Andrew Mirsky 2025-07-08 11:29:42 -04:00
rodzic 57597dfea4
commit fab7c36d86
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: A98E67635CDF2C39
3 zmienionych plików z 176 dodań i 155 usunięć

Wyświetl plik

@ -1,4 +1,17 @@
import React from "react";
export type DataPoint = { export type DataPoint = {
timestamp: string; // ISO format timestamp: string; // ISO format
value: number; value: number;
}; };
// Define the type for each entry in the topic_map
export type TopicEntry<T> = {
current: T;
update: React.Dispatch<React.SetStateAction<T>>;
};
// Define the topic_map type
export type TopicMap = {
[topic: string]: TopicEntry<DataPoint[]>;
};

Wyświetl plik

@ -7,7 +7,7 @@ import SessionsChart from './SessionsChart';
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
// @ts-ignore // @ts-ignore
import useMqtt from '../../assets/usemqtt'; import useMqtt from '../../assets/usemqtt';
import type {DataPoint} from '../../assets/helpers'; import type {DataPoint, TopicMap} from '../../assets/helpers';
import {Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow} from "@mui/material"; import {Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow} from "@mui/material";
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
import {faGithub, faPython, faDocker, faDiscord} from "@fortawesome/free-brands-svg-icons"; import {faGithub, faPython, faDocker, faDiscord} from "@fortawesome/free-brands-svg-icons";
@ -23,6 +23,8 @@ export default function MainGrid() {
const [clientsConnected, setClientsConnected] = useState<DataPoint[]>([]); const [clientsConnected, setClientsConnected] = useState<DataPoint[]>([]);
const [serverStart, setServerStart] = useState<string>(''); const [serverStart, setServerStart] = useState<string>('');
const [serverUptime, setServerUptime] = useState<string>(''); const [serverUptime, setServerUptime] = useState<string>('');
const [cpuPercent, setCpuPercent] = useState<DataPoint[]>([]);
const [memSize, setMemSize] = useState<DataPoint[]>([]);
function getRandomInt(min: number, max: number) { function getRandomInt(min: number, max: number) {
min = Math.ceil(min); min = Math.ceil(min);
@ -65,9 +67,21 @@ export default function MainGrid() {
mqttSubscribe('$SYS/broker/uptime/formatted'); mqttSubscribe('$SYS/broker/uptime/formatted');
mqttSubscribe('$SYS/broker/uptime'); mqttSubscribe('$SYS/broker/uptime');
mqttSubscribe('$SYS/broker/clients/connected'); mqttSubscribe('$SYS/broker/clients/connected');
mqttSubscribe('$SYS/broker/cpu/percent');
mqttSubscribe('$SYS/broker/heap/size')
} }
}, [isConnected, mqttSubscribe]); }, [isConnected, mqttSubscribe]);
const topic_map: TopicMap = {
'$SYS/broker/messages/publish/sent': { current: sent, update: setSent },
'$SYS/broker/messages/publish/received': { current: received, update: setReceived },
'$SYS/broker/load/bytes/received': { current: bytesIn, update: setBytesIn },
'$SYS/broker/load/bytes/sent': { current: bytesOut, update: setBytesOut },
'$SYS/broker/clients/connected': { current: clientsConnected, update: setClientsConnected },
'$SYS/broker/cpu/percent': { current: cpuPercent, update: setCpuPercent},
'$SYS/broker/heap/size': { current: memSize, update: setMemSize}
};
useEffect(() => { useEffect(() => {
while (messageQueue.current.length > 0) { while (messageQueue.current.length > 0) {
@ -75,37 +89,14 @@ export default function MainGrid() {
try { try {
const d = payload.message; const d = payload.message;
if (payload.topic === '$SYS/broker/messages/publish/sent') {
if(payload.topic in topic_map) {
const { update } = topic_map[payload.topic];
const newPoint: DataPoint = { const newPoint: DataPoint = {
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
value: d value: d
}; };
setSent(sent => [...sent, newPoint]); update(current => [...current, newPoint])
} else if (payload.topic === '$SYS/broker/messages/publish/received') {
const newPoint: DataPoint = {
timestamp: new Date().toISOString(),
value: d
}
setReceived(received => [...received, newPoint]);
} else if (payload.topic === '$SYS/broker/load/bytes/received') {
const newPoint: DataPoint = {
timestamp: new Date().toISOString(),
value: d
}
setBytesIn(bytesIn => [...bytesIn, newPoint]);
} else if (payload.topic === '$SYS/broker/load/bytes/sent') {
const newPoint: DataPoint = {
timestamp: new Date().toISOString(),
value: d
}
setBytesOut(bytesOut => [...bytesOut, newPoint]);
} else if (payload.topic === '$SYS/broker/clients/connected') {
const newPoint: DataPoint = {
timestamp: new Date().toISOString(),
value: d
}
setClientsConnected(clientsConnected => [...clientsConnected, newPoint]);
} else if (payload.topic === '$SYS/broker/uptime/formatted') { } else if (payload.topic === '$SYS/broker/uptime/formatted') {
const dt = new Date(d + "Z"); const dt = new Date(d + "Z");
setServerStart(dt.toLocaleString()); setServerStart(dt.toLocaleString());
@ -235,10 +226,10 @@ export default function MainGrid() {
<strong>up for</strong> {serverUptime} <strong>up for</strong> {serverUptime}
</Grid> </Grid>
<Grid size={{xs: 12, md: 6}}> <Grid size={{xs: 12, md: 6}}>
<SessionsChart title={'Sent Messages'} label={'Messages'} data={sent} isConnected={isConnected}/> <SessionsChart title={'Sent Messages'} label={''} data={sent} isConnected={isConnected}/>
</Grid> </Grid>
<Grid size={{xs: 12, md: 6}}> <Grid size={{xs: 12, md: 6}}>
<SessionsChart title={'Received Messages'} label={'Messages'} data={received} isConnected={isConnected}/> <SessionsChart title={'Received Messages'} label={''} data={received} isConnected={isConnected}/>
</Grid> </Grid>
<Grid size={{xs: 12, md: 6}}> <Grid size={{xs: 12, md: 6}}>
<SessionsChart title={'Bytes Out'} label={'Bytes'} data={bytesOut} isConnected={isConnected}/> <SessionsChart title={'Bytes Out'} label={'Bytes'} data={bytesOut} isConnected={isConnected}/>
@ -249,6 +240,16 @@ export default function MainGrid() {
<Grid size={{xs: 12, md: 6}}> <Grid size={{xs: 12, md: 6}}>
<SessionsChart title={'Clients Connected'} label={''} data={clientsConnected} isConnected={isConnected}/> <SessionsChart title={'Clients Connected'} label={''} data={clientsConnected} isConnected={isConnected}/>
</Grid> </Grid>
<Grid size={{xs: 12, md: 6}}>
<Grid container spacing={2} columns={2}>
<Grid size={{lg:1}}>
<SessionsChart title={'CPU'} label={'%'} data={cpuPercent} decimals={2} isConnected={isConnected}/>
</Grid>
<Grid size={{lg:1}}>
<SessionsChart title={'Memory'} label={'MB'} data={memSize} decimals={1} isConnected={isConnected}/>
</Grid>
</Grid>
</Grid>
</Grid> </Grid>
<Grid container spacing={2} columns={12}> <Grid container spacing={2} columns={12}>

Wyświetl plik

@ -1,16 +1,16 @@
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import Card from '@mui/material/Card'; import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent'; import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import { LineChart } from '@mui/x-charts/LineChart'; import { LineChart } from '@mui/x-charts/LineChart';
import CountUp from 'react-countup'; import CountUp from 'react-countup';
import type { DataPoint } from '../../assets/helpers.jsx'; import type { DataPoint } from '../../assets/helpers.jsx';
import {CircularProgress} from "@mui/material"; import {CircularProgress} from "@mui/material";
const currentTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; const currentTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
function formatDate(date: Date) { function formatDate(date: Date) {
return date.toLocaleTimeString('en-US', { return date.toLocaleTimeString('en-US', {
timeZone: currentTimeZone, timeZone: currentTimeZone,
@ -19,10 +19,10 @@ function formatDate(date: Date) {
second: '2-digit', second: '2-digit',
}); });
} }
function AreaGradient({ color, id }: { color: string; id: string }) { function AreaGradient({ color, id }: { color: string; id: string }) {
return ( return (
<defs> <defs>
<linearGradient id={id} x1="50%" y1="0%" x2="50%" y2="100%"> <linearGradient id={id} x1="50%" y1="0%" x2="50%" y2="100%">
@ -31,9 +31,9 @@ function AreaGradient({ color, id }: { color: string; id: string }) {
</linearGradient> </linearGradient>
</defs> </defs>
); );
} }
function NoDataDisplay(props: any) { function NoDataDisplay(props: any) {
return <> return <>
{!props.isConnected ? <div style={{height: 250, width: 600, paddingTop: 50}}> {!props.isConnected ? <div style={{height: 250, width: 600, paddingTop: 50}}>
<Typography component="h2" variant="subtitle2" gutterBottom> <Typography component="h2" variant="subtitle2" gutterBottom>
@ -49,9 +49,9 @@ function NoDataDisplay(props: any) {
</div>} </div>}
</> </>
} }
function LinearChart(props: any) { function LinearChart(props: any) {
const theme = useTheme(); const theme = useTheme();
@ -63,6 +63,8 @@ function LinearChart(props: any) {
const label: string = props.label || '--'; const label: string = props.label || '--';
const baseline: number = props.baseline || 0;
return <LineChart return <LineChart
colors={colorPalette} colors={colorPalette}
xAxis={[ xAxis={[
@ -84,10 +86,12 @@ function LinearChart(props: any) {
area: true, area: true,
stackOrder: 'ascending', stackOrder: 'ascending',
data: props.data.map( (dp:DataPoint) => dp.value), data: props.data.map( (dp:DataPoint) => dp.value),
baseline: baseline
} }
]} ]}
height={175} height={175}
margin={{ left: 50, right: 20, top: 20, bottom: 20 }}
margin={{ left: 0, right: 20, top: 20, bottom: 20 }}
grid={{ horizontal: true }} grid={{ horizontal: true }}
sx={{ sx={{
'& .MuiAreaElement-series-organic': { '& .MuiAreaElement-series-organic': {
@ -109,9 +113,9 @@ function LinearChart(props: any) {
<AreaGradient color={theme.palette.primary.main} id="direct" /> <AreaGradient color={theme.palette.primary.main} id="direct" />
</LineChart> </LineChart>
} }
export default function SessionsChart(props: any) { export default function SessionsChart(props: any) {
return ( return (
<Card variant="outlined" sx={{ width: '100%' }}> <Card variant="outlined" sx={{ width: '100%' }}>
@ -134,14 +138,17 @@ export default function SessionsChart(props: any) {
<CountUp <CountUp
start={props.data[props.data.length - 2].value} start={props.data[props.data.length - 2].value}
end={props.data[props.data.length - 1].value} end={props.data[props.data.length - 1].value}
duration={5}/>} duration={5}
decimals={props.decimals}
/>} {props.label}
</Typography> </Typography>
</Stack> </Stack>
</Stack> </Stack>
{ props.data.length < 2 ? <NoDataDisplay isConnected={props.isConnected}/> : { props.data.length < 2 ? <NoDataDisplay isConnected={props.isConnected}/> :
<LinearChart {...props} /> } <LinearChart { ...props} baseline={props.data[0].value} /> }
</CardContent> </CardContent>
</Card> </Card>
); );
} }