tx bias chart

This commit is contained in:
Mateusz Gruszczyński
2026-01-03 20:23:25 +01:00
parent 840d41bbb8
commit 5478c0d614
3 changed files with 190 additions and 194 deletions

View File

@@ -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();
});
});