improving charts
test-org/trust-module-frontend/pipeline/pr-rc This commit looks good Details

pull/44/head
DmitriyA 2025-06-06 07:25:32 -04:00
parent b5b758ffa0
commit f87274d41a
3 changed files with 135 additions and 101 deletions

View File

@ -37,7 +37,8 @@ const getStatusText = (status) => {
0: 'Нет соединения', 0: 'Нет соединения',
1: 'Норма', 1: 'Норма',
2: 'Отклонение', 2: 'Отклонение',
3: 'Критично' 3: 'Критично',
4: 'Авария'
}[status] || 'Неизвестно'; }[status] || 'Неизвестно';
}; };
@ -46,7 +47,8 @@ const getStatusDescription = (status) => {
0: 'Устройство не отвечает', 0: 'Устройство не отвечает',
1: 'Параметры в норме', 1: 'Параметры в норме',
2: 'Обнаружены отклонения от нормы', 2: 'Обнаружены отклонения от нормы',
3: 'Критическое состояние системы' 3: 'Критическое состояние системы',
4: 'Авария'
}[status] || 'Статус неизвестен'; }[status] || 'Статус неизвестен';
}; };
@ -143,62 +145,69 @@ const LineChartComponent = ({
x1={area.start} x1={area.start}
x2={area.end} x2={area.end}
fill={getStatusColor(area.status)} fill={getStatusColor(area.status)}
fillOpacity={0.1} fillOpacity={0.12}
/> stroke={getStatusColor(area.status)}
));
};
const renderRangeAreas = () => {
if (!ranges || ranges.length === 0) return null;
return ranges.map((range, index) => {
const hasData = data && data.length > 0;
const minX = hasData ? data[0].timestamp : 0;
const maxX = hasData ? data[data.length - 1].timestamp : 0;
return (
<ReferenceArea
key={`range-${index}`}
y1={range.min}
y2={range.max}
x1={minX}
x2={maxX}
fill={rangeColors[range.status] || '#e8e8e8'}
fillOpacity={0.15}
stroke={rangeColors[range.status] || '#e8e8e8'}
strokeWidth={1} strokeWidth={1}
strokeDasharray="3 3" strokeOpacity={0.5}
ifOverflow="extendDomain" />
/>
); ));
});
}; };
const renderRangeLines = () => { const renderRangeLines = () => {
if (!ranges || ranges.length === 0) return null; if (!ranges || ranges.length === 0) return null;
const uniqueValues = new Set(); // Собираем только уникальные граничные значения, исключая дубликаты на стыках диапазонов
ranges.forEach(range => { const boundaryValues = [];
uniqueValues.add(range.min); ranges.forEach((range, index) => {
uniqueValues.add(range.max); // Для первого диапазона добавляем и min и max
if (index === 0) {
boundaryValues.push(range.min);
boundaryValues.push(range.max);
}
// Для остальных добавляем только max (min будет совпадать с max предыдущего)
else {
boundaryValues.push(range.max);
}
}); });
return Array.from(uniqueValues).map((value, index) => ( return boundaryValues.map((value, index) => {
// Находим диапазон, к которому принадлежит эта граница
const range = ranges.find(r => r.min === value || r.max === value);
const status = range ? range.status : 1;
const lineStyle = {
1: { strokeWidth: 1, strokeDasharray: "none", opacity: 0.7 },
2: { strokeWidth: 2, strokeDasharray: "none", opacity: 0.9 },
3: { strokeWidth: 2, strokeDasharray: "none", opacity: 1 },
4: { strokeWidth: 2, strokeDasharray: "none", opacity: 1 }
}[status] || { strokeWidth: 1, strokeDasharray: "3 3", opacity: 0.7 };
return (
<ReferenceLine <ReferenceLine
key={`line-${index}`} key={`line-${value}`} // Используем значение как ключ для стабильности
y={value} y={value}
stroke="#888" stroke={rangeColors[status] || '#888'}
strokeDasharray="3 3" strokeWidth={lineStyle.strokeWidth}
strokeOpacity={0.7} strokeDasharray={lineStyle.strokeDasharray}
strokeOpacity={lineStyle.opacity}
ifOverflow="extendDomain" ifOverflow="extendDomain"
label={{ label={{
value: value.toFixed(1), value: value.toFixed(1),
position: 'insideRight', position: 'right',
fill: '#888', fill: rangeColors[status] || '#888',
fontSize: 12 fontSize: 12,
fontWeight: 'bold',
background: 'rgba(255, 255, 255, 0.9)',
padding: [4, 6],
borderRadius: 4,
stroke: 'none',
boxShadow: '0 0 2px rgba(0,0,0,0.1)',
textAnchor: 'start'
}} }}
/> />
)); );
});
}; };
const renderStatusBoundaries = () => { const renderStatusBoundaries = () => {
@ -282,7 +291,7 @@ const LineChartComponent = ({
<LineChart <LineChart
data={data} data={data}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
> >
<CartesianGrid strokeDasharray="3 3" /> <CartesianGrid strokeDasharray="3 3" />
<XAxis <XAxis
dataKey="timestamp" dataKey="timestamp"
@ -290,8 +299,7 @@ const LineChartComponent = ({
/> />
<YAxis /> <YAxis />
{renderRangeLines()} {renderRangeLines()}
{renderRangeAreas()} {renderStatusBoundaries()}
{renderStatusBoundaries()} {/* Добавляем отображение границ */}
{getStatusAreas()} {getStatusAreas()}
<Tooltip content={<CustomTooltip />} /> <Tooltip content={<CustomTooltip />} />
<Legend /> <Legend />
@ -305,7 +313,9 @@ const LineChartComponent = ({
isAnimationActive={false} isAnimationActive={false}
name={title} name={title}
/> />
</LineChart> </LineChart>
</ResponsiveContainer> </ResponsiveContainer>
{/* Легенда статусов */} {/* Легенда статусов */}
@ -320,6 +330,7 @@ const LineChartComponent = ({
{ status: 1, label: '1 - Норма' }, { status: 1, label: '1 - Норма' },
{ status: 2, label: '2 - Отклонение' }, { status: 2, label: '2 - Отклонение' },
{ status: 3, label: '3 - Критично' }, { status: 3, label: '3 - Критично' },
{ status: 4, label: '4 - Авария' },
{ status: 0, label: '0 - Нет связи' } { status: 0, label: '0 - Нет связи' }
].map(item => ( ].map(item => (
<div key={item.status} style={{ display: 'flex', alignItems: 'center' }}> <div key={item.status} style={{ display: 'flex', alignItems: 'center' }}>

View File

@ -1,4 +1,3 @@
// src/Components/StatusLogTable.jsx
import React from 'react'; import React from 'react';
import { import {
Table, Table,
@ -12,11 +11,13 @@ import {
Typography Typography
} from '@mui/material'; } from '@mui/material';
// Используем те же цвета, что и в LineChartComponent
const statusColors = { const statusColors = {
'0': 'default', '0': '#757575', // серый (нет связи)
'1': 'success', '1': '#4CAF50', // зеленый (норма)
'2': 'warning', '2': '#FFC107', // желтый (отклонение)
'3': 'error' '3': '#FF9800', // оранжевый (критично)
'4': '#F44336' // красный (авария)
}; };
const StatusLogTable = ({ logs }) => { const StatusLogTable = ({ logs }) => {
@ -44,7 +45,12 @@ const StatusLogTable = ({ logs }) => {
<TableCell> <TableCell>
<Chip <Chip
label={getStatusText(log.status)} label={getStatusText(log.status)}
color={statusColors[log.status] || 'default'} style={{
backgroundColor: statusColors[log.status],
color: '#ffffff', // белый текст для лучшей читаемости
fontWeight: 'bold',
border: 'none'
}}
size="small" size="small"
/> />
</TableCell> </TableCell>
@ -62,13 +68,14 @@ const StatusLogTable = ({ logs }) => {
); );
}; };
// Вспомогательные функции // Вспомогательные функции (оставляем без изменений)
const getStatusText = (status) => { const getStatusText = (status) => {
const statusMap = { const statusMap = {
'0': 'Нет соединения', '0': 'Нет соединения',
'1': 'Норма', '1': 'Норма',
'2': 'Отклонение', '2': 'Отклонение',
'3': 'Критично' '3': 'Критично',
'4': 'Авария'
}; };
return statusMap[status] || 'Неизвестно'; return statusMap[status] || 'Неизвестно';
}; };
@ -78,7 +85,8 @@ const getStatusDescription = (status) => {
'0': 'Устройство не отвечает', '0': 'Устройство не отвечает',
'1': 'Параметры в норме', '1': 'Параметры в норме',
'2': 'Обнаружены отклонения от нормы', '2': 'Обнаружены отклонения от нормы',
'3': 'Критическое состояние системы' '3': 'Критическое состояние системы',
'4': 'Аварийное состояние системы'
}; };
return descriptions[status] || 'Статус неизвестен'; return descriptions[status] || 'Статус неизвестен';
}; };

View File

@ -53,6 +53,19 @@ const PrometheusChart = ({ metricInfo, chartHeight = 580 }) => {
.sort((a, b) => a.timestamp - b.timestamp); .sort((a, b) => a.timestamp - b.timestamp);
}; };
const downsampleData = (data, maxPoints = 500) => {
if (data.length <= maxPoints) return data;
const ratio = Math.ceil(data.length / maxPoints);
return data.filter((_, index) => index % ratio === 0);
};
const calculateStep = (startTime, endTime, maxPoints = 10000) => {
const seconds = (endTime.getTime() - startTime.getTime()) / 1000;
return Math.max(Math.ceil(seconds / maxPoints), 1); // в секундах
};
// Обновляем логи при изменении данных // Обновляем логи при изменении данных
useEffect(() => { useEffect(() => {
if (chartData.length > 0) { if (chartData.length > 0) {
@ -78,15 +91,17 @@ const PrometheusChart = ({ metricInfo, chartHeight = 580 }) => {
...(source_id && { source_id: source_id.toString() }) ...(source_id && { source_id: source_id.toString() })
}; };
const data = await metricsService.fetchMetricsRange( const step = calculateStep(start, end);
const data = await metricsService.fetchMetricsRange(
metricName, metricName,
Math.floor(start.getTime() / 1000), Math.floor(start.getTime() / 1000),
Math.floor(end.getTime() / 1000), Math.floor(end.getTime() / 1000),
15, step,
extendedFilters extendedFilters
); );
const formattedData = formatMetricData(data);
const formattedData = downsampleData(formatMetricData(data), 100); //КОЛИЧЕСТВО ТОЧЕК НА ГРАФИКЕ
if (formattedData.length > 0) { if (formattedData.length > 0) {
setMetricMeta({ setMetricMeta({
type: data[0]?.type, type: data[0]?.type,