This commit is contained in:
sebseb7
2025-12-25 00:24:48 +01:00
parent 0eb05b1cd5
commit 2d7bfe247d
7 changed files with 630 additions and 524 deletions

View File

@@ -1,23 +1,50 @@
import React, { useEffect, useState } from 'react';
import React, { Component } from 'react';
import { Paper, Typography, Box, CircularProgress } from '@mui/material';
import { LineChart } from '@mui/x-charts/LineChart';
import { useTheme } from '@mui/material/styles';
export default function Chart({ selectedChannels = [], channelConfig = null, axisConfig = null }) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const theme = useTheme();
export default class Chart extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
loading: true
};
this.intervalId = null;
}
// Determine effective channels list
const effectiveChannels = channelConfig
? channelConfig.map(c => c.id)
: selectedChannels;
componentDidMount() {
this.fetchData();
this.intervalId = setInterval(this.fetchData, 60000);
}
componentDidUpdate(prevProps) {
// Compare props to see if we need to refetch
const prevEffective = this.getEffectiveChannels(prevProps);
const currEffective = this.getEffectiveChannels(this.props);
if (prevEffective.join(',') !== currEffective.join(',')) {
this.fetchData();
}
}
componentWillUnmount() {
if (this.intervalId) {
clearInterval(this.intervalId);
}
}
getEffectiveChannels(props) {
return props.channelConfig
? props.channelConfig.map(c => c.id)
: props.selectedChannels;
}
fetchData = () => {
const effectiveChannels = this.getEffectiveChannels(this.props);
const fetchData = () => {
// Only fetch if selection exists
if (effectiveChannels.length === 0) {
setData([]);
setLoading(false);
this.setState({ data: [], loading: false });
return;
}
@@ -47,48 +74,15 @@ export default function Chart({ selectedChannels = [], channelConfig = null, axi
});
const sortedData = Array.from(timeMap.values()).sort((a, b) => a.time - b.time);
setData(sortedData);
setLoading(false);
this.setState({ data: sortedData, loading: false });
})
.catch(err => {
console.error("Failed to fetch data", err);
setLoading(false);
this.setState({ loading: false });
});
};
useEffect(() => {
fetchData();
const interval = setInterval(fetchData, 60000);
return () => clearInterval(interval);
}, [selectedChannels, channelConfig]);
if (loading) return <Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}><CircularProgress /></Box>;
if (effectiveChannels.length === 0) return <Box sx={{ p: 4 }}><Typography>No channels selected.</Typography></Box>;
const series = effectiveChannels.map(id => {
// Find alias and axis if config exists
let label = id;
let yAxisKey = 'left';
if (channelConfig) {
const item = channelConfig.find(c => c.id === id);
if (item) {
if (item.alias) label = item.alias;
if (item.yAxis) yAxisKey = item.yAxis;
}
}
return {
dataKey: id,
label: label,
connectNulls: true,
showMark: false,
yAxisKey: yAxisKey,
};
});
const hasRightAxis = series.some(s => s.yAxisKey === 'right');
const computeAxisLimits = (axisKey) => {
computeAxisLimits(axisKey, effectiveChannels, series) {
// Collect all data points for this axis
let axisMin = Infinity;
let axisMax = -Infinity;
@@ -98,6 +92,7 @@ export default function Chart({ selectedChannels = [], channelConfig = null, axi
if (axisSeries.length === 0) return {}; // No data for this axis
// Check if config exists for this axis
const { axisConfig } = this.props;
let cfgMin = NaN;
let cfgMax = NaN;
if (axisConfig && axisConfig[axisKey]) {
@@ -110,7 +105,7 @@ export default function Chart({ selectedChannels = [], channelConfig = null, axi
// Calculate data bounds
let hasData = false;
data.forEach(row => {
this.state.data.forEach(row => {
axisSeries.forEach(key => {
const val = row[key];
if (val !== null && val !== undefined) {
@@ -128,43 +123,75 @@ export default function Chart({ selectedChannels = [], channelConfig = null, axi
if (!isNaN(cfgMax)) axisMax = Math.max(axisMax, cfgMax);
return { min: axisMin, max: axisMax };
};
const leftLimits = computeAxisLimits('left');
const rightLimits = computeAxisLimits('right');
const yAxes = [
{ id: 'left', scaleType: 'linear', ...leftLimits }
];
if (hasRightAxis) {
yAxes.push({ id: 'right', scaleType: 'linear', ...rightLimits });
}
return (
<Box sx={{ width: '100%', height: '80vh', p: 2 }}>
<Typography variant="h6" gutterBottom>Last 24 Hours</Typography>
<Paper sx={{ p: 2, height: '100%', display: 'flex', flexDirection: 'column' }}>
<Box sx={{ flexGrow: 1 }}>
<LineChart
dataset={data}
series={series}
xAxis={[{
dataKey: 'time',
scaleType: 'time',
valueFormatter: (date) => date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
}]}
yAxis={yAxes}
rightAxis={hasRightAxis ? 'right' : null}
slotProps={{
legend: {
direction: 'row',
position: { vertical: 'top', horizontal: 'middle' },
padding: 0,
},
}}
/>
</Box>
</Paper>
</Box>
);
render() {
const { loading, data } = this.state;
const { channelConfig } = this.props;
const effectiveChannels = this.getEffectiveChannels(this.props);
if (loading) return <Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}><CircularProgress /></Box>;
if (effectiveChannels.length === 0) return <Box sx={{ p: 4 }}><Typography>No channels selected.</Typography></Box>;
const series = effectiveChannels.map(id => {
// Find alias and axis if config exists
let label = id;
let yAxisKey = 'left';
if (channelConfig) {
const item = channelConfig.find(c => c.id === id);
if (item) {
if (item.alias) label = item.alias;
if (item.yAxis) yAxisKey = item.yAxis;
}
}
return {
dataKey: id,
label: label,
connectNulls: true,
showMark: false,
yAxisKey: yAxisKey,
};
});
const hasRightAxis = series.some(s => s.yAxisKey === 'right');
const leftLimits = this.computeAxisLimits('left', effectiveChannels, series);
const rightLimits = this.computeAxisLimits('right', effectiveChannels, series);
const yAxes = [
{ id: 'left', scaleType: 'linear', ...leftLimits }
];
if (hasRightAxis) {
yAxes.push({ id: 'right', scaleType: 'linear', ...rightLimits });
}
return (
<Box sx={{ width: '100%', height: '80vh', p: 2 }}>
<Typography variant="h6" gutterBottom>Last 24 Hours</Typography>
<Paper sx={{ p: 2, height: '100%', display: 'flex', flexDirection: 'column' }}>
<Box sx={{ flexGrow: 1 }}>
<LineChart
dataset={data}
series={series}
xAxis={[{
dataKey: 'time',
scaleType: 'time',
valueFormatter: (date) => date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
}]}
yAxis={yAxes}
rightAxis={hasRightAxis ? 'right' : null}
slotProps={{
legend: {
direction: 'row',
position: { vertical: 'top', horizontal: 'middle' },
padding: 0,
},
}}
/>
</Box>
</Paper>
</Box>
);
}
}