From 6299b2b4800308f14bb89fe2f4475a2bea1fbb5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Sat, 7 Mar 2026 23:02:58 +0100 Subject: [PATCH] small fix --- static/js/chart.js | 6 ++ static/js/chart_old.js | 140 ++++++++++++++++++++++++++++++++++ static/js/data.js | 13 +++- static/js/data_old.js | 168 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 static/js/chart_old.js create mode 100644 static/js/data_old.js diff --git a/static/js/chart.js b/static/js/chart.js index 375a91c..2dbe8ce 100644 --- a/static/js/chart.js +++ b/static/js/chart.js @@ -91,12 +91,18 @@ window.setupMainChart = function setupMainChart() { callbacks: { label: (ctx) => { const datasetLabel = ctx.dataset.label || ''; + + if (datasetLabel.includes('Brak danych')) { + return 'BRAK DANYCH'; + } + const raw = ctx.raw; const val = (raw && raw.realV !== undefined) ? raw.realV : ctx.parsed.y; if (datasetLabel.includes('Zanik')) return 'ZANIK FAZY'; if (datasetLabel.includes('Powrot')) return `POWROT: ${val.toFixed(1)}V`; if (datasetLabel.includes('Awaria')) return null; + return `${datasetLabel}: ${val.toFixed(1)}V`; } } diff --git a/static/js/chart_old.js b/static/js/chart_old.js new file mode 100644 index 0000000..2dbe8ce --- /dev/null +++ b/static/js/chart_old.js @@ -0,0 +1,140 @@ +window.setupMainChart = function setupMainChart() { + const ctx = document.getElementById('voltageChart'); + if (!ctx) return; + + window.voltageChart = new Chart(ctx, { + type: 'line', + data: { datasets: [] }, + options: { + responsive: true, + maintainAspectRatio: false, + interaction: { mode: 'nearest', axis: 'x', intersect: false }, + scales: { + x: { + type: 'time', + time: { + displayFormats: { + millisecond: 'HH:mm:ss.SSS', + second: 'HH:mm:ss', + minute: 'HH:mm', + hour: 'HH:mm', + day: 'dd LLL', + week: 'dd LLL', + month: 'LLL yyyy', + quarter: 'LLL yyyy', + year: 'yyyy' + }, + tooltipFormat: 'dd.MM.yyyy HH:mm:ss' + }, + grid: { color: '#2d3139' }, + ticks: { color: '#8b949e' } + }, + y: { + beginAtZero: false, + min: 190, + max: 255, + grid: { color: '#2d3139' }, + ticks: { stepSize: 5, color: '#c9d1d9' } + } + }, + plugins: { + zoom: { + limits: { + x: { min: 'original', max: () => Date.now(), minRange: 60 * 1000 }, + y: { min: 190, max: 255 } + }, + zoom: { + wheel: { enabled: true }, + pinch: { enabled: true }, + drag: { enabled: true, backgroundColor: 'rgba(54, 162, 235, 0.3)' }, + mode: 'x', + onZoomComplete: async ({ chart }) => { + const now = Date.now(); + if (chart.scales.x.max > now) { + chart.scales.x.max = now; + chart.update('none'); + } + + document.querySelectorAll('.time-btn').forEach(b => b.classList.remove('active')); + window.currentTimeRange = 'precise'; + + await window.reloadDataForRange(chart.scales.x.min, chart.scales.x.max); + } + }, + pan: { + enabled: true, + mode: 'x', + onPanComplete: async ({ chart }) => { + const now = Date.now(); + if (chart.scales.x.max > now) { + const rangeWidth = chart.scales.x.max - chart.scales.x.min; + chart.scales.x.max = now; + chart.scales.x.min = now - rangeWidth; + chart.update('none'); + } + + document.querySelectorAll('.time-btn').forEach(b => b.classList.remove('active')); + window.currentTimeRange = 'precise'; + await window.reloadDataForRange(chart.scales.x.min, chart.scales.x.max); + } + } + }, + legend: { + labels: { + color: '#c9d1d9', + filter: (item) => !['Zanik', 'Powrot', 'Awaria', 'Brak danych'].some(word => item.text.includes(word)) + } + }, + tooltip: { + enabled: true, + position: 'nearest', + callbacks: { + label: (ctx) => { + const datasetLabel = ctx.dataset.label || ''; + + if (datasetLabel.includes('Brak danych')) { + return 'BRAK DANYCH'; + } + + const raw = ctx.raw; + const val = (raw && raw.realV !== undefined) ? raw.realV : ctx.parsed.y; + + if (datasetLabel.includes('Zanik')) return 'ZANIK FAZY'; + if (datasetLabel.includes('Powrot')) return `POWROT: ${val.toFixed(1)}V`; + if (datasetLabel.includes('Awaria')) return null; + + return `${datasetLabel}: ${val.toFixed(1)}V`; + } + } + } + } + } + }); +}; + +window.updateRangeLabel = function updateRangeLabel(min, max) { + const start = new Date(min); + const end = new Date(max); + const optTime = { hour: '2-digit', minute: '2-digit', hour12: false }; + const optDate = { day: '2-digit', month: '2-digit' }; + + let rangeText = ''; + if (start.toDateString() === end.toDateString()) { + rangeText = `Zakres: ${start.toLocaleDateString('pl-PL', optDate)}, ${start.toLocaleTimeString('pl-PL', optTime)} - ${end.toLocaleTimeString('pl-PL', optTime)}`; + } else { + rangeText = `Zakres: ${start.toLocaleDateString('pl-PL', optDate)} ${start.toLocaleTimeString('pl-PL', optTime)} - ${end.toLocaleDateString('pl-PL', optDate)} ${end.toLocaleTimeString('pl-PL', optTime)}`; + } + + const label = document.getElementById('eventRangeLabel'); + if (label) label.textContent = rangeText; + + const chartDisplay = document.getElementById('chartRangeDisplay'); + if (chartDisplay) { + const optDateShort = { day: '2-digit', month: '2-digit' }; + if (start.toDateString() === end.toDateString()) { + chartDisplay.textContent = `${start.toLocaleDateString('pl-PL', optDateShort)} ${start.toLocaleTimeString('pl-PL', optTime)} - ${end.toLocaleTimeString('pl-PL', optTime)}`; + } else { + chartDisplay.textContent = `${start.toLocaleDateString('pl-PL', optDateShort)} ${start.toLocaleTimeString('pl-PL', optTime)} - ${end.toLocaleDateString('pl-PL', optDateShort)} ${end.toLocaleTimeString('pl-PL', optTime)}`; + } + } +}; \ No newline at end of file diff --git a/static/js/data.js b/static/js/data.js index 58421ee..6219846 100644 --- a/static/js/data.js +++ b/static/js/data.js @@ -7,8 +7,9 @@ window.processPhaseData = function processPhaseData(id, data) { const outageBars = []; let wasOutage = false; + let gapCount = 0; - const Y_TOP = 255; + const Y_TOP = 255; const GAP_BAR_VALUE = Y_TOP; const OUT_BAR_VALUE = Y_TOP; @@ -17,12 +18,20 @@ window.processPhaseData = function processPhaseData(id, data) { const t = new Date(p.time); if (v === null || v === undefined) { + gapCount++; + lineData.push({ x: t, y: null }); outageLineData.push({ x: t, y: null }); - gapBars.push({ x: t, y: GAP_BAR_VALUE }); + + if (gapCount >= 2) { + gapBars.push({ x: t, y: GAP_BAR_VALUE }); + } + return; } + gapCount = 0; + if (v < 180) { outageBars.push({ x: t, y: OUT_BAR_VALUE }); diff --git a/static/js/data_old.js b/static/js/data_old.js new file mode 100644 index 0000000..6219846 --- /dev/null +++ b/static/js/data_old.js @@ -0,0 +1,168 @@ +window.processPhaseData = function processPhaseData(id, data) { + const lineData = []; + const recoveryPoints = []; + const outageLineData = []; + + const gapBars = []; + const outageBars = []; + + let wasOutage = false; + let gapCount = 0; + + const Y_TOP = 255; + const GAP_BAR_VALUE = Y_TOP; + const OUT_BAR_VALUE = Y_TOP; + + data.forEach(p => { + const v = p.voltage; + const t = new Date(p.time); + + if (v === null || v === undefined) { + gapCount++; + + lineData.push({ x: t, y: null }); + outageLineData.push({ x: t, y: null }); + + if (gapCount >= 2) { + gapBars.push({ x: t, y: GAP_BAR_VALUE }); + } + + return; + } + + gapCount = 0; + + if (v < 180) { + outageBars.push({ x: t, y: OUT_BAR_VALUE }); + + lineData.push({ x: t, y: null }); + outageLineData.push({ x: t, y: 195, realV: v }); + + wasOutage = true; + return; + } + + if (wasOutage) { + const pRec = { x: t, y: v, realV: v }; + recoveryPoints.push(pRec); + outageLineData.push(pRec); + wasOutage = false; + } else { + outageLineData.push({ x: t, y: null }); + } + + lineData.push({ x: t, y: v }); + }); + + return { + gapDataset: gapBars.length ? { + type: 'bar', + label: 'Brak danych ' + window.phasesConfig[id].label, + data: gapBars, + backgroundColor: 'rgba(160,160,160,0.14)', + borderWidth: 0, + barPercentage: 1.0, + categoryPercentage: 1.0, + order: -50 + } : null, + + outageShadeDataset: outageBars.length ? { + type: 'bar', + label: 'Zanik ' + window.phasesConfig[id].label, + data: outageBars, + backgroundColor: 'rgba(255,0,0,0.10)', + borderWidth: 0, + barPercentage: 1.0, + categoryPercentage: 1.0, + order: -40 + } : null, + + lineDataset: { + label: window.phasesConfig[id].label, + data: lineData, + borderColor: window.phasesConfig[id].color, + backgroundColor: window.phasesConfig[id].color + '15', + tension: 0.1, + borderWidth: 2, + spanGaps: false, + pointRadius: 0, + order: 0 + }, + + outageLine: { + label: 'Awaria ' + window.phasesConfig[id].label, + data: outageLineData, + borderColor: '#ff0000', + borderDash: [5, 5], + borderWidth: 1, + pointRadius: 0, + fill: false, + spanGaps: false, + showLine: true, + order: 1 + }, + + recoveryDataset: recoveryPoints.length ? { + label: 'Powrot ' + window.phasesConfig[id].label, + data: recoveryPoints, + type: 'scatter', + pointRadius: 4, + pointBackgroundColor: '#3fb950', + pointBorderColor: '#fff', + pointBorderWidth: 1, + z: 99, + order: 11 + } : null + }; +}; + +window.reloadDataForRange = async function reloadDataForRange(min, max, rangeName = null) { + const now = Date.now(); + if (max && max > now) max = now; + if (min && min > now) min = now - 3600000; + + const urlParams = rangeName + ? `range=${rangeName}` + : `start=${new Date(min).toISOString()}&end=${new Date(max).toISOString()}`; + + const newDatasets = []; + + for (const id of Object.keys(window.phasesConfig)) { + try { + const raw = await fetch(`/api/timeseries/${id}?${urlParams}`).then(r => r.json()); + const proc = window.processPhaseData(id, raw); + + if (proc.gapDataset) newDatasets.push(proc.gapDataset); + if (proc.outageShadeDataset) newDatasets.push(proc.outageShadeDataset); + + + newDatasets.push(proc.lineDataset); + if (proc.outageLine) newDatasets.push(proc.outageLine); + if (proc.outageDataset) newDatasets.push(proc.outageDataset); + if (proc.recoveryDataset) newDatasets.push(proc.recoveryDataset); + } catch (e) { + console.error("Błąd pobierania fazy " + id, e); + } + } + + try { + const events = await fetch(`/api/events?${urlParams}`).then(r => r.json()); + window.renderEventLog(events, rangeName || 'precise'); + } catch (e) { + console.error("Błąd pobierania zdarzeń", e); + } + + window.voltageChart.data.datasets = newDatasets; + + if (window.disableChartAnimationOnce) { + window.voltageChart.update('none'); + window.disableChartAnimationOnce = false; + } else { + if (rangeName) window.voltageChart.update(); + else window.voltageChart.update('none'); + } + + const finalMin = rangeName ? window.voltageChart.scales.x.min : (min || window.voltageChart.scales.x.min); + const finalMax = rangeName ? window.voltageChart.scales.x.max : (max || window.voltageChart.scales.x.max); + window.updateRangeLabel(finalMin, finalMax); +}; \ No newline at end of file