diff --git a/src/Charts2/Components/LineChartComponent.jsx b/src/Charts2/Components/LineChartComponent.jsx index d7df0ef..e09476f 100644 --- a/src/Charts2/Components/LineChartComponent.jsx +++ b/src/Charts2/Components/LineChartComponent.jsx @@ -8,26 +8,37 @@ import { Tooltip, Legend, ResponsiveContainer, - ReferenceArea + ReferenceArea, + ReferenceLine } from 'recharts'; +import { Tag } from 'antd'; + +// Цвета для граничных значений +const rangeColors = { + 1: '#4CAF50', // зеленый (норма) + 2: '#FFC107', // желтый (предупреждение) + 3: '#FF9800', // оранжевый (опасно) + 4: '#F44336' // красный (критично) +}; -// ====== Вспомогательные функции ====== const getStatusColor = (status) => { - switch(status) { - case 0: return '#757575'; // тёмно-серый - case 1: return '#00C853'; // ярко-зелёный - case 2: return '#FF6D00'; // ярко-оранжевый - case 3: return '#D50000'; // ярко-красный - default: return '#BDBDBD'; // fallback-серый - } - }; + switch (status) { + case 0: return '#757575'; // серый (нет связи) + case 1: return rangeColors[1]; // зеленый + case 2: return rangeColors[2]; // желтый + case 3: return rangeColors[3]; // оранжевый + case 4: return rangeColors[4]; // красный + default: return '#BDBDBD'; // серый по умолчанию + } +}; const getStatusText = (status) => { return { 0: 'Нет соединения', 1: 'Норма', 2: 'Отклонение', - 3: 'Критично' + 3: 'Критично', + 4: 'Авария' }[status] || 'Неизвестно'; }; @@ -36,16 +47,17 @@ const getStatusDescription = (status) => { 0: 'Устройство не отвечает', 1: 'Параметры в норме', 2: 'Обнаружены отклонения от нормы', - 3: 'Критическое состояние системы' + 3: 'Критическое состояние системы', + 4: 'Авария' }[status] || 'Статус неизвестен'; }; const StatusIndicator = ({ cx, cy, payload }) => { const status = payload?.status ?? 0; return ( - { ); }; -// ====== Основной компонент ====== const LineChartComponent = ({ data, title, description, metaInfo, dataKey = 'value', - height = 400 + height = 400, + ranges = [], + statusBoundaries = [] }) => { const getStatusAreas = () => { @@ -128,11 +141,91 @@ const LineChartComponent = ({ return areas.map((area, i) => ( + + )); + }; + + const renderRangeLines = () => { + if (!ranges || ranges.length === 0) return null; + + // Собираем только уникальные граничные значения, исключая дубликаты на стыках диапазонов + const boundaryValues = []; + ranges.forEach((range, index) => { + // Для первого диапазона добавляем и min и max + if (index === 0) { + boundaryValues.push(range.min); + boundaryValues.push(range.max); + } + // Для остальных добавляем только max (min будет совпадать с max предыдущего) + else { + boundaryValues.push(range.max); + } + }); + + 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 ( + + ); + }); + }; + + const renderStatusBoundaries = () => { + if (!statusBoundaries || statusBoundaries.length === 0) return null; + + return statusBoundaries.map((boundary, index) => ( + )); }; @@ -149,31 +242,80 @@ const LineChartComponent = ({ )} - - - - new Date(ts).toLocaleTimeString()} - /> - - {getStatusAreas()} - } /> - - } - activeDot={{ r: 8 }} - isAnimationActive={false} - name={title} - /> - + {/* Легенда граничных значений */} + {ranges.length > 0 && ( +
+ Диапазоны: + {ranges + .sort((a, b) => a.min - b.min) + .map((range, index) => ( + + {range.min.toFixed(0)}-{range.max.toFixed(0)} (Ур. {range.status}) + + ))} +
+ )} + + {/* Легенда границ статусов */} + {statusBoundaries.length > 0 && ( +
+ Границы статусов: + {statusBoundaries.map((boundary, index) => ( + + {boundary.label || `Граница ${index + 1}`} ({new Date(boundary.timestamp).toLocaleString()}) + + ))} +
+ )} + + + + + new Date(ts).toLocaleTimeString()} + /> + + {renderRangeLines()} + {renderStatusBoundaries()} + {getStatusAreas()} + } /> + + } + activeDot={{ r: 8 }} + isAnimationActive={false} + name={title} + /> + + + {/* Легенда статусов */} @@ -185,16 +327,17 @@ const LineChartComponent = ({ flexWrap: 'wrap' }}> {[ - { status: 1, label: '1 - Норма', color: '#4CAF50' }, - { status: 2, label: '2 - Отклонение', color: '#FF9800' }, - { status: 3, label: '3 - Критично', color: '#F44336' }, - { status: 0, label: '0 - Нет связи', color: '#888' } + { status: 1, label: '1 - Норма' }, + { status: 2, label: '2 - Отклонение' }, + { status: 3, label: '3 - Критично' }, + { status: 4, label: '4 - Авария' }, + { status: 0, label: '0 - Нет связи' } ].map(item => (
@@ -206,4 +349,4 @@ const LineChartComponent = ({ ); }; -export default LineChartComponent; +export default LineChartComponent; \ No newline at end of file diff --git a/src/Charts2/Components/StatusLogTable.jsx b/src/Charts2/Components/StatusLogTable.jsx index 33dbc06..ccffd23 100644 --- a/src/Charts2/Components/StatusLogTable.jsx +++ b/src/Charts2/Components/StatusLogTable.jsx @@ -1,4 +1,3 @@ -// src/Components/StatusLogTable.jsx import React from 'react'; import { Table, @@ -12,11 +11,13 @@ import { Typography } from '@mui/material'; +// Используем те же цвета, что и в LineChartComponent const statusColors = { - '0': 'default', - '1': 'success', - '2': 'warning', - '3': 'error' + '0': '#757575', // серый (нет связи) + '1': '#4CAF50', // зеленый (норма) + '2': '#FFC107', // желтый (отклонение) + '3': '#FF9800', // оранжевый (критично) + '4': '#F44336' // красный (авария) }; const StatusLogTable = ({ logs }) => { @@ -42,9 +43,14 @@ const StatusLogTable = ({ logs }) => { {log.device} {log.source_id?.split('$')[1]} - @@ -62,13 +68,14 @@ const StatusLogTable = ({ logs }) => { ); }; -// Вспомогательные функции +// Вспомогательные функции (оставляем без изменений) const getStatusText = (status) => { const statusMap = { '0': 'Нет соединения', '1': 'Норма', '2': 'Отклонение', - '3': 'Критично' + '3': 'Критично', + '4': 'Авария' }; return statusMap[status] || 'Неизвестно'; }; @@ -78,7 +85,8 @@ const getStatusDescription = (status) => { '0': 'Устройство не отвечает', '1': 'Параметры в норме', '2': 'Обнаружены отклонения от нормы', - '3': 'Критическое состояние системы' + '3': 'Критическое состояние системы', + '4': 'Аварийное состояние системы' }; return descriptions[status] || 'Статус неизвестен'; }; diff --git a/src/Charts2/PrometheusChart.jsx b/src/Charts2/PrometheusChart.jsx index a0be805..65f0fb2 100644 --- a/src/Charts2/PrometheusChart.jsx +++ b/src/Charts2/PrometheusChart.jsx @@ -8,13 +8,14 @@ import StatusLogTable from './Components/StatusLogTable'; import { Box, IconButton, Tooltip as MuiTooltip } from '@mui/material'; import { ListAlt } from '@mui/icons-material'; -const PrometheusChart = ({ metricInfo, chartHeight = 560 }) => { +const PrometheusChart = ({ metricInfo, chartHeight = 580 }) => { const { name: metricName, filters = {}, title = metricName, description, - context = {} + context = {}, + ranges = [] } = metricInfo || {}; const { device, source_id } = context; @@ -44,7 +45,7 @@ const PrometheusChart = ({ metricInfo, chartHeight = 560 }) => { timestamp: item.timestamp, value: parseFloat(item.value), name: item.__name__ || metricName, - status: item.status?.toString() || '0', + status: parseInt(item.status) || 0, device: item.device?.trim() || null, source_id: item.source_id || null, description: item.description || description @@ -52,6 +53,19 @@ const PrometheusChart = ({ metricInfo, chartHeight = 560 }) => { .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(() => { if (chartData.length > 0) { @@ -64,7 +78,7 @@ const PrometheusChart = ({ metricInfo, chartHeight = 560 }) => { setStatusLogs(newLogs); } }, [chartData]); - + const fetchHistoricalData = async (start, end) => { setIsLoading(true); @@ -77,15 +91,17 @@ const PrometheusChart = ({ metricInfo, chartHeight = 560 }) => { ...(source_id && { source_id: source_id.toString() }) }; - const data = await metricsService.fetchMetricsRange( - metricName, - Math.floor(start.getTime() / 1000), - Math.floor(end.getTime() / 1000), - 15, - extendedFilters - ); + const step = calculateStep(start, end); +const data = await metricsService.fetchMetricsRange( + metricName, + Math.floor(start.getTime() / 1000), + Math.floor(end.getTime() / 1000), + step, + extendedFilters +); - const formattedData = formatMetricData(data); + + const formattedData = downsampleData(formatMetricData(data), 100); //КОЛИЧЕСТВО ТОЧЕК НА ГРАФИКЕ if (formattedData.length > 0) { setMetricMeta({ type: data[0]?.type, @@ -239,6 +255,13 @@ const PrometheusChart = ({ metricInfo, chartHeight = 560 }) => { device, source_id }} + ranges={ranges} + /*ranges={ranges.length > 0 ? ranges : [ + { min: 0, max: 60, status: 1 }, + { min: 60, max: 80, status: 2 }, + { min: 80, max: 90, status: 3 }, + { min: 90, max: 100, status: 4 } + ]}*/ /> {showLogs && ( diff --git a/src/Components/Layout/Dashboard.jsx b/src/Components/Layout/Dashboard.jsx index b5b3768..e0c2c42 100755 --- a/src/Components/Layout/Dashboard.jsx +++ b/src/Components/Layout/Dashboard.jsx @@ -84,7 +84,6 @@ const Dashboard = ({ isDarkMode, setIsDarkMode }) => { const tabId = `tab_${item.id}`; const tabTitle = item.title || 'Новая вкладка'; - // Если это метрика, создаём специальный контент с графиком const tabContent = item.metric ? { filters: item.filters, title: item.title, description: item.description, + ranges: item.ranges, context: { device: item.filters?.device, source_id: item.filters?.source_id, @@ -110,13 +110,14 @@ const Dashboard = ({ isDarkMode, setIsDarkMode }) => { content: tabContent, type: item.metric ? 'metric' : 'menuItem', metric: item.metric, - filters: item.filters + filters: item.filters, + ranges: item.ranges }; handleOpenTab(newTab); } else { setActiveTab(tabId); } - }; +}; // Вспомогательная функция для получения всех дочерних элементов const getAllChildren = (node) => {