kopia lustrzana https://github.com/Yakifo/amqtt
adding memory and cpu display to test.amqtt.io dashboard
rodzic
57597dfea4
commit
fab7c36d86
|
@ -1,4 +1,17 @@
|
|||
import React from "react";
|
||||
|
||||
export type DataPoint = {
|
||||
timestamp: string; // ISO format
|
||||
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[]>;
|
||||
};
|
|
@ -7,7 +7,7 @@ import SessionsChart from './SessionsChart';
|
|||
import {useEffect, useState} from "react";
|
||||
// @ts-ignore
|
||||
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 {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
|
||||
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 [serverStart, setServerStart] = useState<string>('');
|
||||
const [serverUptime, setServerUptime] = useState<string>('');
|
||||
const [cpuPercent, setCpuPercent] = useState<DataPoint[]>([]);
|
||||
const [memSize, setMemSize] = useState<DataPoint[]>([]);
|
||||
|
||||
function getRandomInt(min: number, max: number) {
|
||||
min = Math.ceil(min);
|
||||
|
@ -65,9 +67,21 @@ export default function MainGrid() {
|
|||
mqttSubscribe('$SYS/broker/uptime/formatted');
|
||||
mqttSubscribe('$SYS/broker/uptime');
|
||||
mqttSubscribe('$SYS/broker/clients/connected');
|
||||
mqttSubscribe('$SYS/broker/cpu/percent');
|
||||
mqttSubscribe('$SYS/broker/heap/size')
|
||||
}
|
||||
}, [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(() => {
|
||||
|
||||
while (messageQueue.current.length > 0) {
|
||||
|
@ -75,37 +89,14 @@ export default function MainGrid() {
|
|||
try {
|
||||
|
||||
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 = {
|
||||
timestamp: new Date().toISOString(),
|
||||
value: d
|
||||
};
|
||||
setSent(sent => [...sent, 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]);
|
||||
|
||||
update(current => [...current, newPoint])
|
||||
} else if (payload.topic === '$SYS/broker/uptime/formatted') {
|
||||
const dt = new Date(d + "Z");
|
||||
setServerStart(dt.toLocaleString());
|
||||
|
@ -235,10 +226,10 @@ export default function MainGrid() {
|
|||
<strong>up for</strong> {serverUptime}
|
||||
</Grid>
|
||||
<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 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 size={{xs: 12, md: 6}}>
|
||||
<SessionsChart title={'Bytes Out'} label={'Bytes'} data={bytesOut} isConnected={isConnected}/>
|
||||
|
@ -249,6 +240,16 @@ export default function MainGrid() {
|
|||
<Grid size={{xs: 12, md: 6}}>
|
||||
<SessionsChart title={'Clients Connected'} label={''} data={clientsConnected} isConnected={isConnected}/>
|
||||
</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 container spacing={2} columns={12}>
|
||||
|
|
|
@ -1,147 +1,154 @@
|
|||
import { useTheme } from '@mui/material/styles';
|
||||
import Card from '@mui/material/Card';
|
||||
import CardContent from '@mui/material/CardContent';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import { LineChart } from '@mui/x-charts/LineChart';
|
||||
import CountUp from 'react-countup';
|
||||
import type { DataPoint } from '../../assets/helpers.jsx';
|
||||
import {CircularProgress} from "@mui/material";
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Card from '@mui/material/Card';
|
||||
import CardContent from '@mui/material/CardContent';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import { LineChart } from '@mui/x-charts/LineChart';
|
||||
import CountUp from 'react-countup';
|
||||
import type { DataPoint } from '../../assets/helpers.jsx';
|
||||
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', {
|
||||
timeZone: currentTimeZone,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
});
|
||||
return date.toLocaleTimeString('en-US', {
|
||||
timeZone: currentTimeZone,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function AreaGradient({ color, id }: { color: string; id: string }) {
|
||||
return (
|
||||
<defs>
|
||||
<linearGradient id={id} x1="50%" y1="0%" x2="50%" y2="100%">
|
||||
<stop offset="0%" stopColor={color} stopOpacity={0.5} />
|
||||
<stop offset="100%" stopColor={color} stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
);
|
||||
}
|
||||
function AreaGradient({ color, id }: { color: string; id: string }) {
|
||||
return (
|
||||
<defs>
|
||||
<linearGradient id={id} x1="50%" y1="0%" x2="50%" y2="100%">
|
||||
<stop offset="0%" stopColor={color} stopOpacity={0.5} />
|
||||
<stop offset="100%" stopColor={color} stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
);
|
||||
}
|
||||
|
||||
function NoDataDisplay(props: any) {
|
||||
return <>
|
||||
{!props.isConnected ? <div style={{height: 250, width: 600, paddingTop: 50}}>
|
||||
<Typography component="h2" variant="subtitle2" gutterBottom>
|
||||
Connecting...
|
||||
</Typography>
|
||||
<CircularProgress size={60}/>
|
||||
</div> :
|
||||
<div style={{height: 250, width: 600, paddingTop: 50}}>
|
||||
<Typography component="h2" variant="subtitle2" gutterBottom>
|
||||
Connected, waiting for data...
|
||||
</Typography>
|
||||
function NoDataDisplay(props: any) {
|
||||
return <>
|
||||
{!props.isConnected ? <div style={{height: 250, width: 600, paddingTop: 50}}>
|
||||
<Typography component="h2" variant="subtitle2" gutterBottom>
|
||||
Connecting...
|
||||
</Typography>
|
||||
<CircularProgress size={60}/>
|
||||
</div>}
|
||||
</>
|
||||
</div> :
|
||||
<div style={{height: 250, width: 600, paddingTop: 50}}>
|
||||
<Typography component="h2" variant="subtitle2" gutterBottom>
|
||||
Connected, waiting for data...
|
||||
</Typography>
|
||||
<CircularProgress size={60}/>
|
||||
</div>}
|
||||
</>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function LinearChart(props: any) {
|
||||
function LinearChart(props: any) {
|
||||
|
||||
const theme = useTheme();
|
||||
const theme = useTheme();
|
||||
|
||||
const colorPalette = [
|
||||
theme.palette.primary.light,
|
||||
theme.palette.primary.main,
|
||||
theme.palette.primary.dark,
|
||||
];
|
||||
const colorPalette = [
|
||||
theme.palette.primary.light,
|
||||
theme.palette.primary.main,
|
||||
theme.palette.primary.dark,
|
||||
];
|
||||
|
||||
const label: string = props.label || '--';
|
||||
const label: string = props.label || '--';
|
||||
|
||||
return <LineChart
|
||||
colors={colorPalette}
|
||||
xAxis={[
|
||||
{
|
||||
scaleType: 'point',
|
||||
data: props.data.map( (dp:DataPoint) =>
|
||||
formatDate(new Date(dp.timestamp))
|
||||
),
|
||||
tickInterval: (_index, i) => (i + 1) % (Math.floor(props.data.length/10) + 1) === 0,
|
||||
},
|
||||
]}
|
||||
series={[
|
||||
{
|
||||
id: 'direct',
|
||||
label: label,
|
||||
showMark: false,
|
||||
curve: 'linear',
|
||||
stack: 'total',
|
||||
area: true,
|
||||
stackOrder: 'ascending',
|
||||
data: props.data.map( (dp:DataPoint) => dp.value),
|
||||
}
|
||||
]}
|
||||
height={175}
|
||||
margin={{ left: 50, right: 20, top: 20, bottom: 20 }}
|
||||
grid={{ horizontal: true }}
|
||||
sx={{
|
||||
'& .MuiAreaElement-series-organic': {
|
||||
fill: "url('#organic')",
|
||||
},
|
||||
'& .MuiAreaElement-series-referral': {
|
||||
fill: "url('#referral')",
|
||||
},
|
||||
'& .MuiAreaElement-series-direct': {
|
||||
fill: "url('#direct')",
|
||||
},
|
||||
}}
|
||||
hideLegend
|
||||
slotProps={{
|
||||
legend: {
|
||||
},
|
||||
}}
|
||||
>
|
||||
const baseline: number = props.baseline || 0;
|
||||
|
||||
<AreaGradient color={theme.palette.primary.main} id="direct" />
|
||||
</LineChart>
|
||||
}
|
||||
return <LineChart
|
||||
colors={colorPalette}
|
||||
xAxis={[
|
||||
{
|
||||
scaleType: 'point',
|
||||
data: props.data.map( (dp:DataPoint) =>
|
||||
formatDate(new Date(dp.timestamp))
|
||||
),
|
||||
tickInterval: (_index, i) => (i + 1) % (Math.floor(props.data.length/10) + 1) === 0,
|
||||
},
|
||||
]}
|
||||
series={[
|
||||
{
|
||||
id: 'direct',
|
||||
label: label,
|
||||
showMark: false,
|
||||
curve: 'linear',
|
||||
stack: 'total',
|
||||
area: true,
|
||||
stackOrder: 'ascending',
|
||||
data: props.data.map( (dp:DataPoint) => dp.value),
|
||||
baseline: baseline
|
||||
}
|
||||
]}
|
||||
height={175}
|
||||
|
||||
export default function SessionsChart(props: any) {
|
||||
|
||||
return (
|
||||
<Card variant="outlined" sx={{ width: '100%' }}>
|
||||
<CardContent>
|
||||
<Typography component="h1" variant="subtitle1" gutterBottom>
|
||||
{props.title}
|
||||
</Typography>
|
||||
<Stack sx={{ justifyContent: 'space-between' }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
margin={{ left: 0, right: 20, top: 20, bottom: 20 }}
|
||||
grid={{ horizontal: true }}
|
||||
sx={{
|
||||
alignContent: { xs: 'center', sm: 'flex-start' },
|
||||
alignItems: 'center',
|
||||
gap: 1,
|
||||
'& .MuiAreaElement-series-organic': {
|
||||
fill: "url('#organic')",
|
||||
},
|
||||
'& .MuiAreaElement-series-referral': {
|
||||
fill: "url('#referral')",
|
||||
},
|
||||
'& .MuiAreaElement-series-direct': {
|
||||
fill: "url('#direct')",
|
||||
},
|
||||
}}
|
||||
hideLegend
|
||||
slotProps={{
|
||||
legend: {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" component="p">
|
||||
|
||||
{ props.data.length < 2 ? "" :
|
||||
<CountUp
|
||||
start={props.data[props.data.length - 2].value}
|
||||
end={props.data[props.data.length - 1].value}
|
||||
duration={5}/>}
|
||||
</Typography>
|
||||
<AreaGradient color={theme.palette.primary.main} id="direct" />
|
||||
</LineChart>
|
||||
}
|
||||
|
||||
export default function SessionsChart(props: any) {
|
||||
|
||||
return (
|
||||
<Card variant="outlined" sx={{ width: '100%' }}>
|
||||
<CardContent>
|
||||
<Typography component="h1" variant="subtitle1" gutterBottom>
|
||||
{props.title}
|
||||
</Typography>
|
||||
<Stack sx={{ justifyContent: 'space-between' }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
alignContent: { xs: 'center', sm: 'flex-start' },
|
||||
alignItems: 'center',
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" component="p">
|
||||
|
||||
{ props.data.length < 2 ? "" :
|
||||
<CountUp
|
||||
start={props.data[props.data.length - 2].value}
|
||||
end={props.data[props.data.length - 1].value}
|
||||
duration={5}
|
||||
decimals={props.decimals}
|
||||
|
||||
/>} {props.label}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{ props.data.length < 2 ? <NoDataDisplay isConnected={props.isConnected}/> :
|
||||
<LinearChart {...props} /> }
|
||||
{ props.data.length < 2 ? <NoDataDisplay isConnected={props.isConnected}/> :
|
||||
<LinearChart { ...props} baseline={props.data[0].value} /> }
|
||||
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue