From 5478c0d61477e393749b90780bd03cccee45131f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Sat, 3 Jan 2026 20:23:25 +0100 Subject: [PATCH] tx bias chart --- .gitignore | 3 +- static/js/charts.js | 279 +++++++++++++++++++------------------------ templates/index.html | 102 ++++++++++------ 3 files changed, 190 insertions(+), 194 deletions(-) diff --git a/.gitignore b/.gitignore index 4969ae2..5c6bfff 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ env venv .env /data/* -__pycache__ \ No newline at end of file +__pycache__ +.DS_Store \ No newline at end of file diff --git a/static/js/charts.js b/static/js/charts.js index be57012..0dbd9c9 100644 --- a/static/js/charts.js +++ b/static/js/charts.js @@ -11,6 +11,7 @@ class GPONCharts { this.createTrafficChart(); this.createBytesChart(); this.createVolumeChart(); + this.createVoltageChart(); this.createFECChart(); // Period selectors @@ -38,6 +39,10 @@ class GPONCharts { this.updateVolumeChart(e.target.value); }); + document.getElementById('period-voltage').addEventListener('change', (e) => { + this.updateVoltageChart(e.target.value); + }); + document.getElementById('period-fec').addEventListener('change', (e) => { this.updateFECChart(e.target.value); }); @@ -55,10 +60,11 @@ class GPONCharts { // Initial load this.updateOpticalChart('1h'); this.updateTemperatureChart('1h'); - this.updateTXBiasChart('7d'); // Default 7d for trends + this.updateTXBiasChart('7d'); this.updateTrafficChart('1h'); this.updateBytesChart('1h'); this.updateVolumeChart('1h'); + this.updateVoltageChart('7d'); this.updateFECChart('1h'); // Auto-refresh every 30s @@ -69,6 +75,7 @@ class GPONCharts { const trafficPeriod = document.getElementById('period-traffic').value; const bytesPeriod = document.getElementById('period-bytes').value; const volumePeriod = document.getElementById('period-volume').value; + const voltagePeriod = document.getElementById('period-voltage').value; const fecPeriod = document.getElementById('period-fec').value; this.updateOpticalChart(optPeriod); @@ -77,6 +84,7 @@ class GPONCharts { this.updateTrafficChart(trafficPeriod); this.updateBytesChart(bytesPeriod); this.updateVolumeChart(volumePeriod); + this.updateVoltageChart(voltagePeriod); this.updateFECChart(fecPeriod); }, 30000); @@ -94,6 +102,7 @@ class GPONCharts { document.getElementById('period-traffic').value = period; document.getElementById('period-bytes').value = period; document.getElementById('period-volume').value = period; + document.getElementById('period-voltage').value = period; document.getElementById('period-fec').value = period; this.updateOpticalChart(period); @@ -102,6 +111,7 @@ class GPONCharts { this.updateTrafficChart(period); this.updateBytesChart(period); this.updateVolumeChart(period); + this.updateVoltageChart(period); this.updateFECChart(period); console.log('[Charts] Global period:', period); @@ -138,19 +148,10 @@ class GPONCharts { options: { responsive: true, maintainAspectRatio: false, - interaction: { - mode: 'index', - intersect: false - }, + interaction: { mode: 'index', intersect: false }, plugins: { - legend: { - display: true, - labels: { color: '#ffffff' } - }, - tooltip: { - mode: 'index', - intersect: false - } + legend: { display: true, labels: { color: '#ffffff' } }, + tooltip: { mode: 'index', intersect: false } }, scales: { x: { @@ -160,11 +161,7 @@ class GPONCharts { y: { ticks: { color: '#b0b0b0' }, grid: { color: 'rgba(255, 255, 255, 0.1)' }, - title: { - display: true, - text: 'dBm', - color: '#b0b0b0' - } + title: { display: true, text: 'dBm', color: '#b0b0b0' } } } } @@ -192,15 +189,12 @@ class GPONCharts { responsive: true, maintainAspectRatio: false, plugins: { - legend: { - display: true, - labels: { color: '#ffffff' } - }, + legend: { display: true, labels: { color: '#ffffff' } }, tooltip: { + mode: 'index', + intersect: false, callbacks: { - label: function(context) { - return context.parsed.y ? `${context.parsed.y.toFixed(1)}°C` : 'N/A'; - } + label: (ctx) => ctx.parsed.y !== null ? `${ctx.parsed.y.toFixed(1)}°C` : 'N/A' } } }, @@ -212,11 +206,7 @@ class GPONCharts { y: { ticks: { color: '#b0b0b0' }, grid: { color: 'rgba(255, 255, 255, 0.1)' }, - title: { - display: true, - text: '°C', - color: '#b0b0b0' - } + title: { display: true, text: '°C', color: '#b0b0b0' } } } } @@ -244,15 +234,12 @@ class GPONCharts { responsive: true, maintainAspectRatio: false, plugins: { - legend: { - display: true, - labels: { color: '#ffffff' } - }, + legend: { display: true, labels: { color: '#ffffff' } }, tooltip: { + mode: 'index', + intersect: false, callbacks: { - label: function(context) { - return context.parsed.y ? `${context.parsed.y.toFixed(2)} mA` : 'N/A'; - } + label: (ctx) => ctx.parsed.y !== null ? `${ctx.parsed.y.toFixed(2)} mA` : 'N/A' } } }, @@ -264,16 +251,10 @@ class GPONCharts { y: { ticks: { color: '#b0b0b0', - callback: function(value) { - return value.toFixed(1) + ' mA'; - } + callback: (value) => value.toFixed(1) + ' mA' }, grid: { color: 'rgba(255, 255, 255, 0.1)' }, - title: { - display: true, - text: 'mA', - color: '#b0b0b0' - } + title: { display: true, text: 'mA', color: '#b0b0b0' } } } } @@ -310,14 +291,8 @@ class GPONCharts { responsive: true, maintainAspectRatio: false, plugins: { - legend: { - display: true, - labels: { color: '#ffffff' } - }, - tooltip: { - mode: 'index', - intersect: false - } + legend: { display: true, labels: { color: '#ffffff' } }, + tooltip: { mode: 'index', intersect: false } }, scales: { x: { @@ -327,11 +302,7 @@ class GPONCharts { y: { ticks: { color: '#b0b0b0' }, grid: { color: 'rgba(255, 255, 255, 0.1)' }, - title: { - display: true, - text: 'packets/s', - color: '#b0b0b0' - } + title: { display: true, text: 'packets/s', color: '#b0b0b0' } } } } @@ -368,17 +339,12 @@ class GPONCharts { responsive: true, maintainAspectRatio: false, plugins: { - legend: { - display: true, - labels: { color: '#ffffff' } - }, + legend: { display: true, labels: { color: '#ffffff' } }, tooltip: { mode: 'index', intersect: false, callbacks: { - label: function(context) { - return `${context.dataset.label}: ${context.parsed.y ? context.parsed.y.toFixed(2) : 0} Mb/s`; - } + label: (ctx) => `${ctx.dataset.label}: ${ctx.parsed.y ? ctx.parsed.y.toFixed(2) : 0} Mb/s` } } }, @@ -390,16 +356,10 @@ class GPONCharts { y: { ticks: { color: '#b0b0b0', - callback: function(value) { - return value.toFixed(1); - } + callback: (value) => value.toFixed(1) }, grid: { color: 'rgba(255, 255, 255, 0.1)' }, - title: { - display: true, - text: 'Mb/s', - color: '#b0b0b0' - } + title: { display: true, text: 'Mb/s', color: '#b0b0b0' } } } } @@ -438,19 +398,12 @@ class GPONCharts { responsive: true, maintainAspectRatio: false, plugins: { - legend: { - display: true, - labels: { color: '#ffffff' } - }, + legend: { display: true, labels: { color: '#ffffff' } }, tooltip: { mode: 'index', intersect: false, callbacks: { - label: function(context) { - const bytes = context.parsed.y; - const formatted = self.formatBytes(bytes); - return `${context.dataset.label}: ${formatted}`; - } + label: (ctx) => `${ctx.dataset.label}: ${self.formatBytes(ctx.parsed.y)}` } } }, @@ -464,17 +417,59 @@ class GPONCharts { stacked: false, ticks: { color: '#b0b0b0', - callback: function(value) { - return self.formatBytes(value); - } + callback: (value) => self.formatBytes(value) }, grid: { color: 'rgba(255, 255, 255, 0.1)' }, - title: { - display: true, - text: 'Data per minute', - color: '#b0b0b0' + title: { display: true, text: 'Data per minute', color: '#b0b0b0' } + } + } + } + }); + } + + createVoltageChart() { + const ctx = document.getElementById('chart-voltage').getContext('2d'); + this.charts.voltage = new Chart(ctx, { + type: 'line', + data: { + labels: [], + datasets: [{ + label: 'Voltage (V)', + data: [], + borderColor: '#2ecc71', + backgroundColor: 'rgba(46, 204, 113, 0.1)', + borderWidth: 2, + tension: 0.4, + fill: true, + pointRadius: 0 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { display: true, labels: { color: '#ffffff' } }, + tooltip: { + mode: 'index', + intersect: false, + callbacks: { + label: (ctx) => ctx.parsed.y !== null ? `${ctx.parsed.y.toFixed(2)} V` : 'N/A' } } + }, + scales: { + x: { + ticks: { color: '#b0b0b0', maxRotation: 0 }, + grid: { color: 'rgba(255, 255, 255, 0.1)' } + }, + y: { + ticks: { + color: '#b0b0b0', + callback: (value) => value.toFixed(2) + ' V' + }, + grid: { color: 'rgba(255, 255, 255, 0.1)' }, + title: { display: true, text: 'Volts', color: '#b0b0b0' } + } } } }); @@ -510,10 +505,7 @@ class GPONCharts { responsive: true, maintainAspectRatio: false, plugins: { - legend: { - display: true, - labels: { color: '#ffffff' } - } + legend: { display: true, labels: { color: '#ffffff' } } }, scales: { x: { @@ -523,11 +515,7 @@ class GPONCharts { y: { ticks: { color: '#b0b0b0' }, grid: { color: 'rgba(255, 255, 255, 0.1)' }, - title: { - display: true, - text: 'errors/s', - color: '#b0b0b0' - } + title: { display: true, text: 'errors/s', color: '#b0b0b0' } } } } @@ -537,8 +525,6 @@ class GPONCharts { changeVolumeChartType(type) { if (!this.charts.volume) return; - console.log('[Charts] Changing volume chart to:', type); - const currentData = { labels: this.charts.volume.data.labels, datasets: this.charts.volume.data.datasets @@ -577,19 +563,12 @@ class GPONCharts { responsive: true, maintainAspectRatio: false, plugins: { - legend: { - display: true, - labels: { color: '#ffffff' } - }, + legend: { display: true, labels: { color: '#ffffff' } }, tooltip: { mode: 'index', intersect: false, callbacks: { - label: function(context) { - const bytes = context.parsed.y; - const formatted = self.formatBytes(bytes); - return `${context.dataset.label}: ${formatted}`; - } + label: (ctx) => `${ctx.dataset.label}: ${self.formatBytes(ctx.parsed.y)}` } } }, @@ -603,16 +582,10 @@ class GPONCharts { stacked: false, ticks: { color: '#b0b0b0', - callback: function(value) { - return self.formatBytes(value); - } + callback: (value) => self.formatBytes(value) }, grid: { color: 'rgba(255, 255, 255, 0.1)' }, - title: { - display: true, - text: 'Data per minute', - color: '#b0b0b0' - } + title: { display: true, text: 'Data per minute', color: '#b0b0b0' } } } } @@ -626,7 +599,7 @@ class GPONCharts { const response = await fetch(`/api/history/optical/${period}`); const data = await response.json(); - if (!data || !data.timestamps || data.timestamps.length === 0) { + if (!data?.timestamps?.length) { console.warn('[Charts] No optical data'); return; } @@ -646,9 +619,7 @@ class GPONCharts { const response = await fetch(`/api/history/optical/${period}`); const data = await response.json(); - if (!data || !data.timestamps || data.timestamps.length === 0) { - return; - } + if (!data?.timestamps?.length) return; const labels = this.formatLabels(data.timestamps, period); this.charts.temperature.data.labels = labels; @@ -664,9 +635,7 @@ class GPONCharts { const response = await fetch(`/api/history/optical/${period}`); const data = await response.json(); - if (!data || !data.timestamps || data.timestamps.length === 0) { - return; - } + if (!data?.timestamps?.length) return; const labels = this.formatLabels(data.timestamps, period); this.charts.txbias.data.labels = labels; @@ -677,22 +646,33 @@ class GPONCharts { } } + async updateVoltageChart(period) { + try { + const response = await fetch(`/api/history/optical/${period}`); + const data = await response.json(); + + if (!data?.timestamps?.length) return; + + const labels = this.formatLabels(data.timestamps, period); + this.charts.voltage.data.labels = labels; + this.charts.voltage.data.datasets[0].data = data.data.voltage; + this.charts.voltage.update(); + } catch (error) { + console.error('[Charts] Error voltage:', error); + } + } + async updateTrafficChart(period) { try { const response = await fetch(`/api/history/traffic/${period}`); const data = await response.json(); - if (!data || !data.timestamps || data.timestamps.length === 0) { - return; - } + if (!data?.timestamps?.length) return; const labels = this.formatLabels(data.timestamps, period); - const rxPps = data.data.rx_packets; - const txPps = data.data.tx_packets; - this.charts.traffic.data.labels = labels; - this.charts.traffic.data.datasets[0].data = rxPps; - this.charts.traffic.data.datasets[1].data = txPps; + this.charts.traffic.data.datasets[0].data = data.data.rx_packets; + this.charts.traffic.data.datasets[1].data = data.data.tx_packets; this.charts.traffic.update(); } catch (error) { console.error('[Charts] Error traffic:', error); @@ -704,9 +684,7 @@ class GPONCharts { const response = await fetch(`/api/history/traffic/${period}`); const data = await response.json(); - if (!data || !data.timestamps || data.timestamps.length === 0) { - return; - } + if (!data?.timestamps?.length) return; const labels = this.formatLabels(data.timestamps, period); const rxMbps = data.data.rx_bytes.map(v => v !== null ? (v * 8) / 1000000 : null); @@ -716,8 +694,6 @@ class GPONCharts { this.charts.bytes.data.datasets[0].data = rxMbps; this.charts.bytes.data.datasets[1].data = txMbps; this.charts.bytes.update(); - - console.log('[Charts] Bytes - max RX Mb/s:', Math.max(...rxMbps.filter(v => v !== null)).toFixed(2)); } catch (error) { console.error('[Charts] Error bytes:', error); } @@ -728,25 +704,17 @@ class GPONCharts { const response = await fetch(`/api/history/traffic/${period}`); const data = await response.json(); - if (!data || !data.timestamps || data.timestamps.length === 0) { - return; - } + if (!data?.timestamps?.length) return; const labels = this.formatLabels(data.timestamps, period); - const rxBytesRate = data.data.rx_bytes; - const txBytesRate = data.data.tx_bytes; const step = data.step || 60; - - const rxPerInterval = rxBytesRate.map(rate => rate !== null ? rate * step : null); - const txPerInterval = txBytesRate.map(rate => rate !== null ? rate * step : null); + const rxPerInterval = data.data.rx_bytes.map(rate => rate !== null ? rate * step : null); + const txPerInterval = data.data.tx_bytes.map(rate => rate !== null ? rate * step : null); this.charts.volume.data.labels = labels; this.charts.volume.data.datasets[0].data = rxPerInterval; this.charts.volume.data.datasets[1].data = txPerInterval; this.charts.volume.update(); - - const maxRx = Math.max(...rxPerInterval.filter(v => v !== null)); - console.log('[Charts] Volume - max per minute:', this.formatBytes(maxRx)); } catch (error) { console.error('[Charts] Error volume:', error); } @@ -757,17 +725,12 @@ class GPONCharts { const response = await fetch(`/api/history/fec/${period}`); const data = await response.json(); - if (!data || !data.timestamps || data.timestamps.length === 0) { - return; - } + if (!data?.timestamps?.length) return; const labels = this.formatLabels(data.timestamps, period); - const correctedRate = data.data.corrected; - const uncorrectedRate = data.data.uncorrected; - this.charts.fec.data.labels = labels; - this.charts.fec.data.datasets[0].data = correctedRate; - this.charts.fec.data.datasets[1].data = uncorrectedRate; + this.charts.fec.data.datasets[0].data = data.data.corrected; + this.charts.fec.data.datasets[1].data = data.data.uncorrected; this.charts.fec.update(); } catch (error) { console.error('[Charts] Error FEC:', error); @@ -777,7 +740,7 @@ class GPONCharts { formatLabels(timestamps, period) { return timestamps.map(ts => { const date = new Date(ts * 1000); - if (period === '7d' || period === '14d' || period === '30d' || period === '60d' || period === '90d') { + if (['7d', '14d', '30d', '60d', '90d'].includes(period)) { return date.toLocaleDateString('pl-PL', { day: '2-digit', month: '2-digit' }) + ' ' + date.toLocaleTimeString('pl-PL', { hour: '2-digit', minute: '2-digit' }); } @@ -798,4 +761,4 @@ class GPONCharts { document.addEventListener('DOMContentLoaded', () => { window.charts = new GPONCharts(); -}); \ No newline at end of file +}); diff --git a/templates/index.html b/templates/index.html index ee2cd6f..f82e89b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -21,6 +21,7 @@ - +
- -
+
Bytes Traffic (Speed) @@ -322,48 +323,20 @@
- -
-
-
- - -
-
-
- FEC Errors History - -
-
- +
-
+
Data Volume (Cumulative)
- +
- + +
+
+
+
+ + +
+ +
+
+
+ Supply Voltage + +
+
+ +
+
+
+ + +
+
+
+ FEC Errors History + +
+
+