Files
AyposWeb/src/pages/Maintenance.tsx

248 lines
7.8 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Box, Paper, Typography, Fade, useTheme, AppBar, Toolbar, Chip } from '@mui/material';
import Plot from 'react-plotly.js';
import { Layout, PlotData } from 'plotly.js';
interface DataItem {
now_timestamp: string;
future_timestamp: string;
power: string;
power_future_15min: string;
positive_3p: string;
negative_3p: string;
positive_7p: string;
negative_7p: string;
flag: string;
}
const Maintenance = () => {
const theme = useTheme();
const [chartData, setChartData] = useState<Partial<PlotData>[]>([]);
const [currentFlag, setCurrentFlag] = useState<string>('');
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('http://141.196.83.136:8003/prom/get_chart_data/maintenance/20');
const result = await response.json();
if (result.data && result.data.length > 0) {
const last20Data = result.data.slice(-20);
setCurrentFlag(last20Data[last20Data.length - 1].flag);
const traces: Partial<PlotData>[] = [
{
x: last20Data.map((item: DataItem) => item.now_timestamp),
y: last20Data.map((item: DataItem) => parseFloat(item.power)),
type: 'scatter' as const,
mode: 'lines+markers' as const,
name: 'Current Power',
line: { color: '#2196f3', width: 2 },
marker: { size: 6, symbol: 'circle' }
},
{
x: last20Data.map((item: DataItem) => item.future_timestamp),
y: last20Data.map((item: DataItem) => parseFloat(item.power_future_15min)),
type: 'scatter' as const,
mode: 'lines+markers' as const,
name: 'Predicted (15min)',
line: { color: '#4caf50', width: 2, dash: 'dot' },
marker: { size: 6, symbol: 'circle' }
},
{
x: last20Data.map((item: DataItem) => item.future_timestamp),
y: last20Data.map((item: DataItem) => parseFloat(item.positive_3p)),
type: 'scatter' as const,
mode: 'lines' as const,
name: '+3% Positive',
line: { color: '#2ca02c', width: 1.5 },
showlegend: true,
},
{
x: last20Data.map((item: DataItem) => item.future_timestamp),
y: last20Data.map((item: DataItem) => parseFloat(item.negative_3p)),
type: 'scatter' as const,
mode: 'lines' as const,
name: '-3% Negative',
line: { color: '#d62728', width: 1.5 },
showlegend: true,
},
{
x: last20Data.map((item: DataItem) => item.future_timestamp),
y: last20Data.map((item: DataItem) => parseFloat(item.positive_7p)),
type: 'scatter' as const,
mode: 'lines' as const,
name: '+7% Positive',
line: { color: '#9467bd', width: 1.5 },
showlegend: true,
},
{
x: last20Data.map((item: DataItem) => item.future_timestamp),
y: last20Data.map((item: DataItem) => parseFloat(item.negative_7p)),
type: 'scatter' as const,
mode: 'lines' as const,
name: '-7% Negative',
line: { color: '#8c564b', width: 1.5 },
showlegend: true,
}
];
setChartData(traces);
}
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchData();
const interval = setInterval(fetchData, 5000);
return () => clearInterval(interval);
}, []);
const layout: Partial<Layout> = {
xaxis: {
title: {
text: 'Time',
font: { size: 14, color: '#666', family: undefined }
},
type: 'date',
gridcolor: '#eee',
tickfont: { size: 12, family: undefined, color: '#666' },
showgrid: true,
gridwidth: 1,
rangeslider: { visible: true }
},
yaxis: {
title: {
text: 'Power (W)',
font: { size: 14, color: '#666', family: undefined }
},
gridcolor: '#eee',
tickfont: { size: 12, family: undefined, color: '#666' },
showgrid: true,
gridwidth: 1,
rangemode: 'tozero' as const,
fixedrange: false,
range: [0, Math.max(...chartData.flatMap(trace => trace.y as number[]).filter(Boolean)) * 1.1]
},
showlegend: true,
legend: {
orientation: 'h',
y: -0.2,
x: 0.5,
xanchor: 'center',
yanchor: 'top',
font: {
size: 12,
family: theme.typography.fontFamily,
color: theme.palette.text.secondary
},
bgcolor: 'rgba(255, 255, 255, 0)',
bordercolor: 'rgba(255, 255, 255, 0)'
},
margin: { t: 60, b: 100, l: 60, r: 20 },
plot_bgcolor: 'rgba(0,0,0,0)',
paper_bgcolor: 'rgba(0,0,0,0)',
hovermode: 'closest',
modebar: {
bgcolor: 'rgba(255, 255, 255, 0)',
color: theme.palette.text.secondary,
activecolor: theme.palette.primary.main
}
};
return (
<Box sx={{ flexGrow: 1, bgcolor: theme.palette.background.default }}>
<AppBar
position="static"
elevation={0}
sx={{
bgcolor: 'background.paper',
borderBottom: `1px solid ${theme.palette.divider}`,
mb: 3
}}
>
<Toolbar sx={{ px: { xs: 2, sm: 4 } }}>
<Typography
variant="h5"
component="h1"
sx={{
color: 'text.primary',
fontWeight: 500,
flex: 1,
textAlign: 'center',
letterSpacing: '-0.5px'
}}
>
Preventive Maintenance
</Typography>
{currentFlag && (
<Chip
label={currentFlag}
color={currentFlag === 'Correct Estimation for PM energy' ? 'success' : 'warning'}
size="medium"
sx={{
height: 32,
'& .MuiChip-label': {
px: 2,
fontSize: '0.875rem',
fontWeight: 600
},
animation: 'pulse 2s infinite',
'@keyframes pulse': {
'0%': {
boxShadow: '0 0 0 0 rgba(0, 0, 0, 0.2)',
},
'70%': {
boxShadow: '0 0 0 6px rgba(0, 0, 0, 0)',
},
'100%': {
boxShadow: '0 0 0 0 rgba(0, 0, 0, 0)',
},
}
}}
/>
)}
</Toolbar>
</AppBar>
<Box sx={{ p: { xs: 2, sm: 4 } }}>
<Fade in timeout={800}>
<Paper
elevation={0}
sx={{
p: { xs: 2, sm: 3 },
bgcolor: 'background.paper',
borderRadius: 2,
border: `1px solid ${theme.palette.divider}`,
}}
>
<Box sx={{ height: 'calc(100vh - 200px)', minHeight: '500px' }}>
<Plot
data={chartData}
layout={layout}
config={{
responsive: true,
displayModeBar: true,
displaylogo: false,
modeBarButtonsToRemove: ['lasso2d', 'select2d'],
toImageButtonOptions: {
format: 'png',
filename: 'power_consumption_chart',
height: 1000,
width: 1500,
scale: 2
}
}}
style={{ width: '100%', height: '100%' }}
/>
</Box>
</Paper>
</Fade>
</Box>
</Box>
);
};
export default Maintenance;