tx bias chart
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,4 +2,5 @@ env
|
|||||||
venv
|
venv
|
||||||
.env
|
.env
|
||||||
/data/*
|
/data/*
|
||||||
__pycache__
|
__pycache__
|
||||||
|
.DS_Store
|
||||||
@@ -11,6 +11,7 @@ class GPONCharts {
|
|||||||
this.createTrafficChart();
|
this.createTrafficChart();
|
||||||
this.createBytesChart();
|
this.createBytesChart();
|
||||||
this.createVolumeChart();
|
this.createVolumeChart();
|
||||||
|
this.createVoltageChart();
|
||||||
this.createFECChart();
|
this.createFECChart();
|
||||||
|
|
||||||
// Period selectors
|
// Period selectors
|
||||||
@@ -38,6 +39,10 @@ class GPONCharts {
|
|||||||
this.updateVolumeChart(e.target.value);
|
this.updateVolumeChart(e.target.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById('period-voltage').addEventListener('change', (e) => {
|
||||||
|
this.updateVoltageChart(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
document.getElementById('period-fec').addEventListener('change', (e) => {
|
document.getElementById('period-fec').addEventListener('change', (e) => {
|
||||||
this.updateFECChart(e.target.value);
|
this.updateFECChart(e.target.value);
|
||||||
});
|
});
|
||||||
@@ -55,10 +60,11 @@ class GPONCharts {
|
|||||||
// Initial load
|
// Initial load
|
||||||
this.updateOpticalChart('1h');
|
this.updateOpticalChart('1h');
|
||||||
this.updateTemperatureChart('1h');
|
this.updateTemperatureChart('1h');
|
||||||
this.updateTXBiasChart('7d'); // Default 7d for trends
|
this.updateTXBiasChart('7d');
|
||||||
this.updateTrafficChart('1h');
|
this.updateTrafficChart('1h');
|
||||||
this.updateBytesChart('1h');
|
this.updateBytesChart('1h');
|
||||||
this.updateVolumeChart('1h');
|
this.updateVolumeChart('1h');
|
||||||
|
this.updateVoltageChart('7d');
|
||||||
this.updateFECChart('1h');
|
this.updateFECChart('1h');
|
||||||
|
|
||||||
// Auto-refresh every 30s
|
// Auto-refresh every 30s
|
||||||
@@ -69,6 +75,7 @@ class GPONCharts {
|
|||||||
const trafficPeriod = document.getElementById('period-traffic').value;
|
const trafficPeriod = document.getElementById('period-traffic').value;
|
||||||
const bytesPeriod = document.getElementById('period-bytes').value;
|
const bytesPeriod = document.getElementById('period-bytes').value;
|
||||||
const volumePeriod = document.getElementById('period-volume').value;
|
const volumePeriod = document.getElementById('period-volume').value;
|
||||||
|
const voltagePeriod = document.getElementById('period-voltage').value;
|
||||||
const fecPeriod = document.getElementById('period-fec').value;
|
const fecPeriod = document.getElementById('period-fec').value;
|
||||||
|
|
||||||
this.updateOpticalChart(optPeriod);
|
this.updateOpticalChart(optPeriod);
|
||||||
@@ -77,6 +84,7 @@ class GPONCharts {
|
|||||||
this.updateTrafficChart(trafficPeriod);
|
this.updateTrafficChart(trafficPeriod);
|
||||||
this.updateBytesChart(bytesPeriod);
|
this.updateBytesChart(bytesPeriod);
|
||||||
this.updateVolumeChart(volumePeriod);
|
this.updateVolumeChart(volumePeriod);
|
||||||
|
this.updateVoltageChart(voltagePeriod);
|
||||||
this.updateFECChart(fecPeriod);
|
this.updateFECChart(fecPeriod);
|
||||||
}, 30000);
|
}, 30000);
|
||||||
|
|
||||||
@@ -94,6 +102,7 @@ class GPONCharts {
|
|||||||
document.getElementById('period-traffic').value = period;
|
document.getElementById('period-traffic').value = period;
|
||||||
document.getElementById('period-bytes').value = period;
|
document.getElementById('period-bytes').value = period;
|
||||||
document.getElementById('period-volume').value = period;
|
document.getElementById('period-volume').value = period;
|
||||||
|
document.getElementById('period-voltage').value = period;
|
||||||
document.getElementById('period-fec').value = period;
|
document.getElementById('period-fec').value = period;
|
||||||
|
|
||||||
this.updateOpticalChart(period);
|
this.updateOpticalChart(period);
|
||||||
@@ -102,6 +111,7 @@ class GPONCharts {
|
|||||||
this.updateTrafficChart(period);
|
this.updateTrafficChart(period);
|
||||||
this.updateBytesChart(period);
|
this.updateBytesChart(period);
|
||||||
this.updateVolumeChart(period);
|
this.updateVolumeChart(period);
|
||||||
|
this.updateVoltageChart(period);
|
||||||
this.updateFECChart(period);
|
this.updateFECChart(period);
|
||||||
|
|
||||||
console.log('[Charts] Global period:', period);
|
console.log('[Charts] Global period:', period);
|
||||||
@@ -138,19 +148,10 @@ class GPONCharts {
|
|||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
interaction: {
|
interaction: { mode: 'index', intersect: false },
|
||||||
mode: 'index',
|
|
||||||
intersect: false
|
|
||||||
},
|
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: { display: true, labels: { color: '#ffffff' } },
|
||||||
display: true,
|
tooltip: { mode: 'index', intersect: false }
|
||||||
labels: { color: '#ffffff' }
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
mode: 'index',
|
|
||||||
intersect: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
@@ -160,11 +161,7 @@ class GPONCharts {
|
|||||||
y: {
|
y: {
|
||||||
ticks: { color: '#b0b0b0' },
|
ticks: { color: '#b0b0b0' },
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||||
title: {
|
title: { display: true, text: 'dBm', color: '#b0b0b0' }
|
||||||
display: true,
|
|
||||||
text: 'dBm',
|
|
||||||
color: '#b0b0b0'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,15 +189,12 @@ class GPONCharts {
|
|||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: { display: true, labels: { color: '#ffffff' } },
|
||||||
display: true,
|
|
||||||
labels: { color: '#ffffff' }
|
|
||||||
},
|
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: (ctx) => ctx.parsed.y !== null ? `${ctx.parsed.y.toFixed(1)}°C` : 'N/A'
|
||||||
return context.parsed.y ? `${context.parsed.y.toFixed(1)}°C` : 'N/A';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -212,11 +206,7 @@ class GPONCharts {
|
|||||||
y: {
|
y: {
|
||||||
ticks: { color: '#b0b0b0' },
|
ticks: { color: '#b0b0b0' },
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||||
title: {
|
title: { display: true, text: '°C', color: '#b0b0b0' }
|
||||||
display: true,
|
|
||||||
text: '°C',
|
|
||||||
color: '#b0b0b0'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,15 +234,12 @@ class GPONCharts {
|
|||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: { display: true, labels: { color: '#ffffff' } },
|
||||||
display: true,
|
|
||||||
labels: { color: '#ffffff' }
|
|
||||||
},
|
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: (ctx) => ctx.parsed.y !== null ? `${ctx.parsed.y.toFixed(2)} mA` : 'N/A'
|
||||||
return context.parsed.y ? `${context.parsed.y.toFixed(2)} mA` : 'N/A';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -264,16 +251,10 @@ class GPONCharts {
|
|||||||
y: {
|
y: {
|
||||||
ticks: {
|
ticks: {
|
||||||
color: '#b0b0b0',
|
color: '#b0b0b0',
|
||||||
callback: function(value) {
|
callback: (value) => value.toFixed(1) + ' mA'
|
||||||
return value.toFixed(1) + ' mA';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||||
title: {
|
title: { display: true, text: 'mA', color: '#b0b0b0' }
|
||||||
display: true,
|
|
||||||
text: 'mA',
|
|
||||||
color: '#b0b0b0'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,14 +291,8 @@ class GPONCharts {
|
|||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: { display: true, labels: { color: '#ffffff' } },
|
||||||
display: true,
|
tooltip: { mode: 'index', intersect: false }
|
||||||
labels: { color: '#ffffff' }
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
mode: 'index',
|
|
||||||
intersect: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
@@ -327,11 +302,7 @@ class GPONCharts {
|
|||||||
y: {
|
y: {
|
||||||
ticks: { color: '#b0b0b0' },
|
ticks: { color: '#b0b0b0' },
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||||
title: {
|
title: { display: true, text: 'packets/s', color: '#b0b0b0' }
|
||||||
display: true,
|
|
||||||
text: 'packets/s',
|
|
||||||
color: '#b0b0b0'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,17 +339,12 @@ class GPONCharts {
|
|||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: { display: true, labels: { color: '#ffffff' } },
|
||||||
display: true,
|
|
||||||
labels: { color: '#ffffff' }
|
|
||||||
},
|
|
||||||
tooltip: {
|
tooltip: {
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
intersect: false,
|
intersect: false,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: (ctx) => `${ctx.dataset.label}: ${ctx.parsed.y ? ctx.parsed.y.toFixed(2) : 0} Mb/s`
|
||||||
return `${context.dataset.label}: ${context.parsed.y ? context.parsed.y.toFixed(2) : 0} Mb/s`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -390,16 +356,10 @@ class GPONCharts {
|
|||||||
y: {
|
y: {
|
||||||
ticks: {
|
ticks: {
|
||||||
color: '#b0b0b0',
|
color: '#b0b0b0',
|
||||||
callback: function(value) {
|
callback: (value) => value.toFixed(1)
|
||||||
return value.toFixed(1);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||||
title: {
|
title: { display: true, text: 'Mb/s', color: '#b0b0b0' }
|
||||||
display: true,
|
|
||||||
text: 'Mb/s',
|
|
||||||
color: '#b0b0b0'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,19 +398,12 @@ class GPONCharts {
|
|||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: { display: true, labels: { color: '#ffffff' } },
|
||||||
display: true,
|
|
||||||
labels: { color: '#ffffff' }
|
|
||||||
},
|
|
||||||
tooltip: {
|
tooltip: {
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
intersect: false,
|
intersect: false,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: (ctx) => `${ctx.dataset.label}: ${self.formatBytes(ctx.parsed.y)}`
|
||||||
const bytes = context.parsed.y;
|
|
||||||
const formatted = self.formatBytes(bytes);
|
|
||||||
return `${context.dataset.label}: ${formatted}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -464,17 +417,59 @@ class GPONCharts {
|
|||||||
stacked: false,
|
stacked: false,
|
||||||
ticks: {
|
ticks: {
|
||||||
color: '#b0b0b0',
|
color: '#b0b0b0',
|
||||||
callback: function(value) {
|
callback: (value) => self.formatBytes(value)
|
||||||
return self.formatBytes(value);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||||
title: {
|
title: { display: true, text: 'Data per minute', color: '#b0b0b0' }
|
||||||
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,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: { display: true, labels: { color: '#ffffff' } }
|
||||||
display: true,
|
|
||||||
labels: { color: '#ffffff' }
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
@@ -523,11 +515,7 @@ class GPONCharts {
|
|||||||
y: {
|
y: {
|
||||||
ticks: { color: '#b0b0b0' },
|
ticks: { color: '#b0b0b0' },
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||||
title: {
|
title: { display: true, text: 'errors/s', color: '#b0b0b0' }
|
||||||
display: true,
|
|
||||||
text: 'errors/s',
|
|
||||||
color: '#b0b0b0'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -537,8 +525,6 @@ class GPONCharts {
|
|||||||
changeVolumeChartType(type) {
|
changeVolumeChartType(type) {
|
||||||
if (!this.charts.volume) return;
|
if (!this.charts.volume) return;
|
||||||
|
|
||||||
console.log('[Charts] Changing volume chart to:', type);
|
|
||||||
|
|
||||||
const currentData = {
|
const currentData = {
|
||||||
labels: this.charts.volume.data.labels,
|
labels: this.charts.volume.data.labels,
|
||||||
datasets: this.charts.volume.data.datasets
|
datasets: this.charts.volume.data.datasets
|
||||||
@@ -577,19 +563,12 @@ class GPONCharts {
|
|||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: { display: true, labels: { color: '#ffffff' } },
|
||||||
display: true,
|
|
||||||
labels: { color: '#ffffff' }
|
|
||||||
},
|
|
||||||
tooltip: {
|
tooltip: {
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
intersect: false,
|
intersect: false,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: (ctx) => `${ctx.dataset.label}: ${self.formatBytes(ctx.parsed.y)}`
|
||||||
const bytes = context.parsed.y;
|
|
||||||
const formatted = self.formatBytes(bytes);
|
|
||||||
return `${context.dataset.label}: ${formatted}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -603,16 +582,10 @@ class GPONCharts {
|
|||||||
stacked: false,
|
stacked: false,
|
||||||
ticks: {
|
ticks: {
|
||||||
color: '#b0b0b0',
|
color: '#b0b0b0',
|
||||||
callback: function(value) {
|
callback: (value) => self.formatBytes(value)
|
||||||
return self.formatBytes(value);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||||
title: {
|
title: { display: true, text: 'Data per minute', color: '#b0b0b0' }
|
||||||
display: true,
|
|
||||||
text: 'Data per minute',
|
|
||||||
color: '#b0b0b0'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,7 +599,7 @@ class GPONCharts {
|
|||||||
const response = await fetch(`/api/history/optical/${period}`);
|
const response = await fetch(`/api/history/optical/${period}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
if (!data?.timestamps?.length) {
|
||||||
console.warn('[Charts] No optical data');
|
console.warn('[Charts] No optical data');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -646,9 +619,7 @@ class GPONCharts {
|
|||||||
const response = await fetch(`/api/history/optical/${period}`);
|
const response = await fetch(`/api/history/optical/${period}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
if (!data?.timestamps?.length) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const labels = this.formatLabels(data.timestamps, period);
|
const labels = this.formatLabels(data.timestamps, period);
|
||||||
this.charts.temperature.data.labels = labels;
|
this.charts.temperature.data.labels = labels;
|
||||||
@@ -664,9 +635,7 @@ class GPONCharts {
|
|||||||
const response = await fetch(`/api/history/optical/${period}`);
|
const response = await fetch(`/api/history/optical/${period}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
if (!data?.timestamps?.length) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const labels = this.formatLabels(data.timestamps, period);
|
const labels = this.formatLabels(data.timestamps, period);
|
||||||
this.charts.txbias.data.labels = labels;
|
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) {
|
async updateTrafficChart(period) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/history/traffic/${period}`);
|
const response = await fetch(`/api/history/traffic/${period}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
if (!data?.timestamps?.length) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const labels = this.formatLabels(data.timestamps, period);
|
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.labels = labels;
|
||||||
this.charts.traffic.data.datasets[0].data = rxPps;
|
this.charts.traffic.data.datasets[0].data = data.data.rx_packets;
|
||||||
this.charts.traffic.data.datasets[1].data = txPps;
|
this.charts.traffic.data.datasets[1].data = data.data.tx_packets;
|
||||||
this.charts.traffic.update();
|
this.charts.traffic.update();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Charts] Error traffic:', error);
|
console.error('[Charts] Error traffic:', error);
|
||||||
@@ -704,9 +684,7 @@ class GPONCharts {
|
|||||||
const response = await fetch(`/api/history/traffic/${period}`);
|
const response = await fetch(`/api/history/traffic/${period}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
if (!data?.timestamps?.length) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const labels = this.formatLabels(data.timestamps, period);
|
const labels = this.formatLabels(data.timestamps, period);
|
||||||
const rxMbps = data.data.rx_bytes.map(v => v !== null ? (v * 8) / 1000000 : null);
|
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[0].data = rxMbps;
|
||||||
this.charts.bytes.data.datasets[1].data = txMbps;
|
this.charts.bytes.data.datasets[1].data = txMbps;
|
||||||
this.charts.bytes.update();
|
this.charts.bytes.update();
|
||||||
|
|
||||||
console.log('[Charts] Bytes - max RX Mb/s:', Math.max(...rxMbps.filter(v => v !== null)).toFixed(2));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Charts] Error bytes:', error);
|
console.error('[Charts] Error bytes:', error);
|
||||||
}
|
}
|
||||||
@@ -728,25 +704,17 @@ class GPONCharts {
|
|||||||
const response = await fetch(`/api/history/traffic/${period}`);
|
const response = await fetch(`/api/history/traffic/${period}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
if (!data?.timestamps?.length) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const labels = this.formatLabels(data.timestamps, period);
|
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 step = data.step || 60;
|
||||||
|
const rxPerInterval = data.data.rx_bytes.map(rate => rate !== null ? rate * step : null);
|
||||||
const rxPerInterval = rxBytesRate.map(rate => rate !== null ? rate * step : null);
|
const txPerInterval = data.data.tx_bytes.map(rate => rate !== null ? rate * step : null);
|
||||||
const txPerInterval = txBytesRate.map(rate => rate !== null ? rate * step : null);
|
|
||||||
|
|
||||||
this.charts.volume.data.labels = labels;
|
this.charts.volume.data.labels = labels;
|
||||||
this.charts.volume.data.datasets[0].data = rxPerInterval;
|
this.charts.volume.data.datasets[0].data = rxPerInterval;
|
||||||
this.charts.volume.data.datasets[1].data = txPerInterval;
|
this.charts.volume.data.datasets[1].data = txPerInterval;
|
||||||
this.charts.volume.update();
|
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) {
|
} catch (error) {
|
||||||
console.error('[Charts] Error volume:', error);
|
console.error('[Charts] Error volume:', error);
|
||||||
}
|
}
|
||||||
@@ -757,17 +725,12 @@ class GPONCharts {
|
|||||||
const response = await fetch(`/api/history/fec/${period}`);
|
const response = await fetch(`/api/history/fec/${period}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
if (!data?.timestamps?.length) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const labels = this.formatLabels(data.timestamps, period);
|
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.labels = labels;
|
||||||
this.charts.fec.data.datasets[0].data = correctedRate;
|
this.charts.fec.data.datasets[0].data = data.data.corrected;
|
||||||
this.charts.fec.data.datasets[1].data = uncorrectedRate;
|
this.charts.fec.data.datasets[1].data = data.data.uncorrected;
|
||||||
this.charts.fec.update();
|
this.charts.fec.update();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Charts] Error FEC:', error);
|
console.error('[Charts] Error FEC:', error);
|
||||||
@@ -777,7 +740,7 @@ class GPONCharts {
|
|||||||
formatLabels(timestamps, period) {
|
formatLabels(timestamps, period) {
|
||||||
return timestamps.map(ts => {
|
return timestamps.map(ts => {
|
||||||
const date = new Date(ts * 1000);
|
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' }) + ' ' +
|
return date.toLocaleDateString('pl-PL', { day: '2-digit', month: '2-digit' }) + ' ' +
|
||||||
date.toLocaleTimeString('pl-PL', { hour: '2-digit', minute: '2-digit' });
|
date.toLocaleTimeString('pl-PL', { hour: '2-digit', minute: '2-digit' });
|
||||||
}
|
}
|
||||||
@@ -798,4 +761,4 @@ class GPONCharts {
|
|||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
window.charts = new GPONCharts();
|
window.charts = new GPONCharts();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
<div class="btn-toolbar d-none d-sm-flex" role="toolbar" id="global-quick-pickers"
|
<div class="btn-toolbar d-none d-sm-flex" role="toolbar" id="global-quick-pickers"
|
||||||
style="overflow-x: auto; scrollbar-width: thin; max-width: 100%;">
|
style="overflow-x: auto; scrollbar-width: thin; max-width: 100%;">
|
||||||
|
|
||||||
|
<!-- Grupa 1 -->
|
||||||
<div class="btn-group btn-group-sm me-1" role="group">
|
<div class="btn-group btn-group-sm me-1" role="group">
|
||||||
<input type="radio" class="btn-check" name="global-period" id="period-1h" value="1h" checked>
|
<input type="radio" class="btn-check" name="global-period" id="period-1h" value="1h" checked>
|
||||||
<label class="btn btn-outline-light btn-sm px-2 py-0.5 text-nowrap" for="period-1h">1h</label>
|
<label class="btn btn-outline-light btn-sm px-2 py-0.5 text-nowrap" for="period-1h">1h</label>
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
<label class="btn btn-outline-light btn-sm px-2 py-0.5 text-nowrap" for="period-24h">24h</label>
|
<label class="btn btn-outline-light btn-sm px-2 py-0.5 text-nowrap" for="period-24h">24h</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Grupa 2 -->
|
||||||
<div class="btn-group btn-group-sm" role="group">
|
<div class="btn-group btn-group-sm" role="group">
|
||||||
<input type="radio" class="btn-check" name="global-period" id="period-3d" value="3d">
|
<input type="radio" class="btn-check" name="global-period" id="period-3d" value="3d">
|
||||||
<label class="btn btn-outline-light btn-sm px-2 py-0.5 text-nowrap" for="period-3d">3d</label>
|
<label class="btn btn-outline-light btn-sm px-2 py-0.5 text-nowrap" for="period-3d">3d</label>
|
||||||
@@ -297,10 +299,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- FEC Errors & Bytes Traffic Row -->
|
<!-- Bytes Traffic -->
|
||||||
<div class="row g-3 mb-4">
|
<div class="row g-3 mb-4">
|
||||||
<!-- Bytes Traffic -->
|
<div class="col-md-12">
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<span><i class="bi bi-speedometer2"></i> Bytes Traffic (Speed)</span>
|
<span><i class="bi bi-speedometer2"></i> Bytes Traffic (Speed)</span>
|
||||||
@@ -322,48 +323,20 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<canvas id="chart-bytes" height="150"></canvas>
|
<canvas id="chart-bytes" height="200"></canvas>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- FEC Errors -->
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
|
||||||
<span><i class="bi bi-exclamation-triangle"></i> FEC Errors History</span>
|
|
||||||
<select class="form-select form-select-sm w-auto" id="period-fec">
|
|
||||||
<option value="1h" selected>1h</option>
|
|
||||||
<option value="6h">6h</option>
|
|
||||||
<option value="12h">12h</option>
|
|
||||||
<option value="24h">24h</option>
|
|
||||||
<option value="3d">3d</option>
|
|
||||||
<option value="7d">7d</option>
|
|
||||||
<option value="14d">14d</option>
|
|
||||||
<option value="30d">30d</option>
|
|
||||||
<option value="60d">60d</option>
|
|
||||||
<option value="90d">90d</option>
|
|
||||||
<option value="120d">120d</option>
|
|
||||||
<option value="1y">1 Year</option>
|
|
||||||
<option value="2y">2 Years</option>
|
|
||||||
<option value="5y">5 Years</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<canvas id="chart-fec" height="150"></canvas>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Data Volume Row -->
|
<!-- Data Volume Row -->
|
||||||
<div class="row g-3">
|
<div class="row g-3 mb-4">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<span><i class="bi bi-database"></i> Data Volume (Cumulative)</span>
|
<span><i class="bi bi-database"></i> Data Volume (Cumulative)</span>
|
||||||
<div class="d-flex gap-2 align-items-center">
|
<div class="d-flex gap-2 align-items-center">
|
||||||
|
<!-- Przełącznik Line/Bar -->
|
||||||
<div class="btn-group btn-group-sm" role="group">
|
<div class="btn-group btn-group-sm" role="group">
|
||||||
<input type="radio" class="btn-check" name="volume-chart-type" id="volume-line" value="line" checked>
|
<input type="radio" class="btn-check" name="volume-chart-type" id="volume-line" value="line" checked>
|
||||||
<label class="btn btn-outline-secondary" for="volume-line">
|
<label class="btn btn-outline-secondary" for="volume-line">
|
||||||
@@ -396,7 +369,66 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<canvas id="chart-volume" height="150"></canvas>
|
<canvas id="chart-volume" height="200"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Voltage + FEC Errors -->
|
||||||
|
<div class="row g-3 mb-4">
|
||||||
|
<!-- Voltage Chart -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<span><i class="bi bi-battery-charging"></i> Supply Voltage</span>
|
||||||
|
<select class="form-select form-select-sm w-auto" id="period-voltage">
|
||||||
|
<option value="1h">1h</option>
|
||||||
|
<option value="6h">6h</option>
|
||||||
|
<option value="12h">12h</option>
|
||||||
|
<option value="24h">24h</option>
|
||||||
|
<option value="3d">3d</option>
|
||||||
|
<option value="7d" selected>7d</option>
|
||||||
|
<option value="14d">14d</option>
|
||||||
|
<option value="30d">30d</option>
|
||||||
|
<option value="60d">60d</option>
|
||||||
|
<option value="90d">90d</option>
|
||||||
|
<option value="120d">120d</option>
|
||||||
|
<option value="1y">1 Year</option>
|
||||||
|
<option value="2y">2 Years</option>
|
||||||
|
<option value="5y">5 Years</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<canvas id="chart-voltage" height="200"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- FEC Errors -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<span><i class="bi bi-exclamation-triangle"></i> FEC Errors History</span>
|
||||||
|
<select class="form-select form-select-sm w-auto" id="period-fec">
|
||||||
|
<option value="1h" selected>1h</option>
|
||||||
|
<option value="6h">6h</option>
|
||||||
|
<option value="12h">12h</option>
|
||||||
|
<option value="24h">24h</option>
|
||||||
|
<option value="3d">3d</option>
|
||||||
|
<option value="7d">7d</option>
|
||||||
|
<option value="14d">14d</option>
|
||||||
|
<option value="30d">30d</option>
|
||||||
|
<option value="60d">60d</option>
|
||||||
|
<option value="90d">90d</option>
|
||||||
|
<option value="120d">120d</option>
|
||||||
|
<option value="1y">1 Year</option>
|
||||||
|
<option value="2y">2 Years</option>
|
||||||
|
<option value="5y">5 Years</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<canvas id="chart-fec" height="200"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user