tx bias chart
This commit is contained in:
@@ -7,13 +7,13 @@ class GPONCharts {
|
|||||||
init() {
|
init() {
|
||||||
this.createOpticalChart();
|
this.createOpticalChart();
|
||||||
this.createTemperatureChart();
|
this.createTemperatureChart();
|
||||||
|
this.createTXBiasChart();
|
||||||
this.createTrafficChart();
|
this.createTrafficChart();
|
||||||
this.createBytesChart();
|
this.createBytesChart();
|
||||||
this.createVolumeChart();
|
this.createVolumeChart();
|
||||||
this.createFECChart();
|
this.createFECChart();
|
||||||
|
|
||||||
this.setupGlobalPeriodSelector();
|
// Period selectors
|
||||||
|
|
||||||
document.getElementById('period-optical').addEventListener('change', (e) => {
|
document.getElementById('period-optical').addEventListener('change', (e) => {
|
||||||
this.updateOpticalChart(e.target.value);
|
this.updateOpticalChart(e.target.value);
|
||||||
});
|
});
|
||||||
@@ -22,6 +22,10 @@ class GPONCharts {
|
|||||||
this.updateTemperatureChart(e.target.value);
|
this.updateTemperatureChart(e.target.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById('period-txbias').addEventListener('change', (e) => {
|
||||||
|
this.updateTXBiasChart(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
document.getElementById('period-traffic').addEventListener('change', (e) => {
|
document.getElementById('period-traffic').addEventListener('change', (e) => {
|
||||||
this.updateTrafficChart(e.target.value);
|
this.updateTrafficChart(e.target.value);
|
||||||
});
|
});
|
||||||
@@ -38,22 +42,30 @@ class GPONCharts {
|
|||||||
this.updateFECChart(e.target.value);
|
this.updateFECChart(e.target.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Volume chart type switcher
|
||||||
document.querySelectorAll('input[name="volume-chart-type"]').forEach(radio => {
|
document.querySelectorAll('input[name="volume-chart-type"]').forEach(radio => {
|
||||||
radio.addEventListener('change', (e) => {
|
radio.addEventListener('change', (e) => {
|
||||||
this.changeVolumeChartType(e.target.value);
|
this.changeVolumeChartType(e.target.value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Global period selector
|
||||||
|
this.setupGlobalPeriodSelector();
|
||||||
|
|
||||||
|
// Initial load
|
||||||
this.updateOpticalChart('1h');
|
this.updateOpticalChart('1h');
|
||||||
this.updateTemperatureChart('1h');
|
this.updateTemperatureChart('1h');
|
||||||
|
this.updateTXBiasChart('7d'); // Default 7d for trends
|
||||||
this.updateTrafficChart('1h');
|
this.updateTrafficChart('1h');
|
||||||
this.updateBytesChart('1h');
|
this.updateBytesChart('1h');
|
||||||
this.updateVolumeChart('1h');
|
this.updateVolumeChart('1h');
|
||||||
this.updateFECChart('1h');
|
this.updateFECChart('1h');
|
||||||
|
|
||||||
|
// Auto-refresh every 30s
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
const optPeriod = document.getElementById('period-optical').value;
|
const optPeriod = document.getElementById('period-optical').value;
|
||||||
const tempPeriod = document.getElementById('period-temperature').value;
|
const tempPeriod = document.getElementById('period-temperature').value;
|
||||||
|
const txbiasPeriod = document.getElementById('period-txbias').value;
|
||||||
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;
|
||||||
@@ -61,6 +73,7 @@ class GPONCharts {
|
|||||||
|
|
||||||
this.updateOpticalChart(optPeriod);
|
this.updateOpticalChart(optPeriod);
|
||||||
this.updateTemperatureChart(tempPeriod);
|
this.updateTemperatureChart(tempPeriod);
|
||||||
|
this.updateTXBiasChart(txbiasPeriod);
|
||||||
this.updateTrafficChart(trafficPeriod);
|
this.updateTrafficChart(trafficPeriod);
|
||||||
this.updateBytesChart(bytesPeriod);
|
this.updateBytesChart(bytesPeriod);
|
||||||
this.updateVolumeChart(volumePeriod);
|
this.updateVolumeChart(volumePeriod);
|
||||||
@@ -72,13 +85,12 @@ class GPONCharts {
|
|||||||
|
|
||||||
setupGlobalPeriodSelector() {
|
setupGlobalPeriodSelector() {
|
||||||
const radios = document.querySelectorAll('input[name="global-period"]');
|
const radios = document.querySelectorAll('input[name="global-period"]');
|
||||||
|
|
||||||
radios.forEach(radio => {
|
radios.forEach(radio => {
|
||||||
radio.addEventListener('change', (e) => {
|
radio.addEventListener('change', (e) => {
|
||||||
const period = e.target.value;
|
const period = e.target.value;
|
||||||
|
|
||||||
document.getElementById('period-optical').value = period;
|
document.getElementById('period-optical').value = period;
|
||||||
document.getElementById('period-temperature').value = period;
|
document.getElementById('period-temperature').value = period;
|
||||||
|
document.getElementById('period-txbias').value = period;
|
||||||
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;
|
||||||
@@ -86,25 +98,24 @@ class GPONCharts {
|
|||||||
|
|
||||||
this.updateOpticalChart(period);
|
this.updateOpticalChart(period);
|
||||||
this.updateTemperatureChart(period);
|
this.updateTemperatureChart(period);
|
||||||
|
this.updateTXBiasChart(period);
|
||||||
this.updateTrafficChart(period);
|
this.updateTrafficChart(period);
|
||||||
this.updateBytesChart(period);
|
this.updateBytesChart(period);
|
||||||
this.updateVolumeChart(period);
|
this.updateVolumeChart(period);
|
||||||
this.updateFECChart(period);
|
this.updateFECChart(period);
|
||||||
|
|
||||||
console.log('[Charts] Global period: ' + period);
|
console.log('[Charts] Global period:', period);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createOpticalChart() {
|
createOpticalChart() {
|
||||||
const ctx = document.getElementById('chart-optical').getContext('2d');
|
const ctx = document.getElementById('chart-optical').getContext('2d');
|
||||||
|
|
||||||
this.charts.optical = new Chart(ctx, {
|
this.charts.optical = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [
|
datasets: [{
|
||||||
{
|
|
||||||
label: 'RX Power (dBm)',
|
label: 'RX Power (dBm)',
|
||||||
data: [],
|
data: [],
|
||||||
borderColor: '#00ccff',
|
borderColor: '#00ccff',
|
||||||
@@ -113,8 +124,7 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
label: 'TX Power (dBm)',
|
label: 'TX Power (dBm)',
|
||||||
data: [],
|
data: [],
|
||||||
borderColor: '#ffcc00',
|
borderColor: '#ffcc00',
|
||||||
@@ -123,16 +133,24 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
interaction: { mode: 'index', intersect: false },
|
interaction: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false
|
||||||
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: true, labels: { color: '#ffffff' } },
|
legend: {
|
||||||
tooltip: { mode: 'index', intersect: false }
|
display: true,
|
||||||
|
labels: { color: '#ffffff' }
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
@@ -142,7 +160,11 @@ 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: { display: true, text: 'dBm', color: '#b0b0b0' }
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'dBm',
|
||||||
|
color: '#b0b0b0'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +173,6 @@ class GPONCharts {
|
|||||||
|
|
||||||
createTemperatureChart() {
|
createTemperatureChart() {
|
||||||
const ctx = document.getElementById('chart-temperature').getContext('2d');
|
const ctx = document.getElementById('chart-temperature').getContext('2d');
|
||||||
|
|
||||||
this.charts.temperature = new Chart(ctx, {
|
this.charts.temperature = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
@@ -171,11 +192,14 @@ class GPONCharts {
|
|||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: true, labels: { color: '#ffffff' } },
|
legend: {
|
||||||
|
display: true,
|
||||||
|
labels: { color: '#ffffff' }
|
||||||
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: function(context) {
|
||||||
return context.parsed.y ? context.parsed.y.toFixed(1) + '°C' : 'N/A';
|
return context.parsed.y ? `${context.parsed.y.toFixed(1)}°C` : 'N/A';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,7 +212,68 @@ 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: { display: true, text: '°C', color: '#b0b0b0' }
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: '°C',
|
||||||
|
color: '#b0b0b0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createTXBiasChart() {
|
||||||
|
const ctx = document.getElementById('chart-txbias').getContext('2d');
|
||||||
|
this.charts.txbias = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: 'TX Bias (mA)',
|
||||||
|
data: [],
|
||||||
|
borderColor: '#9b59b6',
|
||||||
|
backgroundColor: 'rgba(155, 89, 182, 0.1)',
|
||||||
|
borderWidth: 2,
|
||||||
|
tension: 0.4,
|
||||||
|
fill: true,
|
||||||
|
pointRadius: 0
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
labels: { color: '#ffffff' }
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function(context) {
|
||||||
|
return context.parsed.y ? `${context.parsed.y.toFixed(2)} mA` : 'N/A';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
ticks: { color: '#b0b0b0', maxRotation: 0 },
|
||||||
|
grid: { color: 'rgba(255, 255, 255, 0.1)' }
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
ticks: {
|
||||||
|
color: '#b0b0b0',
|
||||||
|
callback: function(value) {
|
||||||
|
return value.toFixed(1) + ' mA';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'mA',
|
||||||
|
color: '#b0b0b0'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,14 +282,12 @@ class GPONCharts {
|
|||||||
|
|
||||||
createTrafficChart() {
|
createTrafficChart() {
|
||||||
const ctx = document.getElementById('chart-traffic').getContext('2d');
|
const ctx = document.getElementById('chart-traffic').getContext('2d');
|
||||||
|
|
||||||
this.charts.traffic = new Chart(ctx, {
|
this.charts.traffic = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [
|
datasets: [{
|
||||||
{
|
label: 'RX (Packets/s)',
|
||||||
label: 'RX Packets/s',
|
|
||||||
data: [],
|
data: [],
|
||||||
borderColor: '#4bc0c0',
|
borderColor: '#4bc0c0',
|
||||||
backgroundColor: 'rgba(75, 192, 192, 0.1)',
|
backgroundColor: 'rgba(75, 192, 192, 0.1)',
|
||||||
@@ -212,9 +295,8 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
},
|
}, {
|
||||||
{
|
label: 'TX (Packets/s)',
|
||||||
label: 'TX Packets/s',
|
|
||||||
data: [],
|
data: [],
|
||||||
borderColor: '#ff6384',
|
borderColor: '#ff6384',
|
||||||
backgroundColor: 'rgba(255, 99, 132, 0.1)',
|
backgroundColor: 'rgba(255, 99, 132, 0.1)',
|
||||||
@@ -222,15 +304,20 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: true, labels: { color: '#ffffff' } },
|
legend: {
|
||||||
tooltip: { mode: 'index', intersect: false }
|
display: true,
|
||||||
|
labels: { color: '#ffffff' }
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
@@ -240,7 +327,11 @@ 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: { display: true, text: 'packets/s', color: '#b0b0b0' }
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'packets/s',
|
||||||
|
color: '#b0b0b0'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,13 +340,11 @@ class GPONCharts {
|
|||||||
|
|
||||||
createBytesChart() {
|
createBytesChart() {
|
||||||
const ctx = document.getElementById('chart-bytes').getContext('2d');
|
const ctx = document.getElementById('chart-bytes').getContext('2d');
|
||||||
|
|
||||||
this.charts.bytes = new Chart(ctx, {
|
this.charts.bytes = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [
|
datasets: [{
|
||||||
{
|
|
||||||
label: 'RX (Mb/s)',
|
label: 'RX (Mb/s)',
|
||||||
data: [],
|
data: [],
|
||||||
borderColor: '#4bc0c0',
|
borderColor: '#4bc0c0',
|
||||||
@@ -264,8 +353,7 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
label: 'TX (Mb/s)',
|
label: 'TX (Mb/s)',
|
||||||
data: [],
|
data: [],
|
||||||
borderColor: '#ff6384',
|
borderColor: '#ff6384',
|
||||||
@@ -274,21 +362,22 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: true, labels: { color: '#ffffff' } },
|
legend: {
|
||||||
|
display: true,
|
||||||
|
labels: { color: '#ffffff' }
|
||||||
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
intersect: false,
|
intersect: false,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: function(context) {
|
||||||
return context.dataset.label + ': ' +
|
return `${context.dataset.label}: ${context.parsed.y ? context.parsed.y.toFixed(2) : 0} Mb/s`;
|
||||||
(context.parsed.y ? context.parsed.y.toFixed(2) : '0') + ' Mb/s';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,7 +395,11 @@ class GPONCharts {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.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'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,9 +414,8 @@ class GPONCharts {
|
|||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [
|
datasets: [{
|
||||||
{
|
label: 'RX (per minute)',
|
||||||
label: 'RX per minute',
|
|
||||||
data: [],
|
data: [],
|
||||||
borderColor: '#4bc0c0',
|
borderColor: '#4bc0c0',
|
||||||
backgroundColor: 'rgba(75, 192, 192, 0.05)',
|
backgroundColor: 'rgba(75, 192, 192, 0.05)',
|
||||||
@@ -331,9 +423,8 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
},
|
}, {
|
||||||
{
|
label: 'TX (per minute)',
|
||||||
label: 'TX per minute',
|
|
||||||
data: [],
|
data: [],
|
||||||
borderColor: '#ff6384',
|
borderColor: '#ff6384',
|
||||||
backgroundColor: 'rgba(255, 99, 132, 0.05)',
|
backgroundColor: 'rgba(255, 99, 132, 0.05)',
|
||||||
@@ -341,14 +432,16 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: true, labels: { color: '#ffffff' } },
|
legend: {
|
||||||
|
display: true,
|
||||||
|
labels: { color: '#ffffff' }
|
||||||
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
intersect: false,
|
intersect: false,
|
||||||
@@ -356,7 +449,7 @@ class GPONCharts {
|
|||||||
label: function(context) {
|
label: function(context) {
|
||||||
const bytes = context.parsed.y;
|
const bytes = context.parsed.y;
|
||||||
const formatted = self.formatBytes(bytes);
|
const formatted = self.formatBytes(bytes);
|
||||||
return context.dataset.label + ': ' + formatted;
|
return `${context.dataset.label}: ${formatted}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,7 +469,11 @@ class GPONCharts {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
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'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -385,13 +482,11 @@ class GPONCharts {
|
|||||||
|
|
||||||
createFECChart() {
|
createFECChart() {
|
||||||
const ctx = document.getElementById('chart-fec').getContext('2d');
|
const ctx = document.getElementById('chart-fec').getContext('2d');
|
||||||
|
|
||||||
this.charts.fec = new Chart(ctx, {
|
this.charts.fec = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [
|
datasets: [{
|
||||||
{
|
|
||||||
label: 'FEC Corrected',
|
label: 'FEC Corrected',
|
||||||
data: [],
|
data: [],
|
||||||
borderColor: '#ffa500',
|
borderColor: '#ffa500',
|
||||||
@@ -400,8 +495,7 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
label: 'FEC Uncorrected',
|
label: 'FEC Uncorrected',
|
||||||
data: [],
|
data: [],
|
||||||
borderColor: '#ff0000',
|
borderColor: '#ff0000',
|
||||||
@@ -410,14 +504,16 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: true, labels: { color: '#ffffff' } }
|
legend: {
|
||||||
|
display: true,
|
||||||
|
labels: { color: '#ffffff' }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
@@ -427,7 +523,11 @@ 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: { display: true, text: 'errors/s', color: '#b0b0b0' }
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'errors/s',
|
||||||
|
color: '#b0b0b0'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,6 +545,7 @@ class GPONCharts {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.charts.volume.destroy();
|
this.charts.volume.destroy();
|
||||||
|
|
||||||
const ctx = document.getElementById('chart-volume').getContext('2d');
|
const ctx = document.getElementById('chart-volume').getContext('2d');
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
@@ -452,9 +553,8 @@ class GPONCharts {
|
|||||||
type: type,
|
type: type,
|
||||||
data: {
|
data: {
|
||||||
labels: currentData.labels,
|
labels: currentData.labels,
|
||||||
datasets: [
|
datasets: [{
|
||||||
{
|
label: 'RX (per minute)',
|
||||||
label: 'RX per minute',
|
|
||||||
data: currentData.datasets[0].data,
|
data: currentData.datasets[0].data,
|
||||||
borderColor: '#4bc0c0',
|
borderColor: '#4bc0c0',
|
||||||
backgroundColor: type === 'bar' ? 'rgba(75, 192, 192, 0.6)' : 'rgba(75, 192, 192, 0.05)',
|
backgroundColor: type === 'bar' ? 'rgba(75, 192, 192, 0.6)' : 'rgba(75, 192, 192, 0.05)',
|
||||||
@@ -462,9 +562,8 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
},
|
}, {
|
||||||
{
|
label: 'TX (per minute)',
|
||||||
label: 'TX per minute',
|
|
||||||
data: currentData.datasets[1].data,
|
data: currentData.datasets[1].data,
|
||||||
borderColor: '#ff6384',
|
borderColor: '#ff6384',
|
||||||
backgroundColor: type === 'bar' ? 'rgba(255, 99, 132, 0.6)' : 'rgba(255, 99, 132, 0.05)',
|
backgroundColor: type === 'bar' ? 'rgba(255, 99, 132, 0.6)' : 'rgba(255, 99, 132, 0.05)',
|
||||||
@@ -472,14 +571,16 @@ class GPONCharts {
|
|||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
pointRadius: 0
|
pointRadius: 0
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: true, labels: { color: '#ffffff' } },
|
legend: {
|
||||||
|
display: true,
|
||||||
|
labels: { color: '#ffffff' }
|
||||||
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
intersect: false,
|
intersect: false,
|
||||||
@@ -487,7 +588,7 @@ class GPONCharts {
|
|||||||
label: function(context) {
|
label: function(context) {
|
||||||
const bytes = context.parsed.y;
|
const bytes = context.parsed.y;
|
||||||
const formatted = self.formatBytes(bytes);
|
const formatted = self.formatBytes(bytes);
|
||||||
return context.dataset.label + ': ' + formatted;
|
return `${context.dataset.label}: ${formatted}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -507,7 +608,11 @@ class GPONCharts {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
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'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -518,7 +623,7 @@ class GPONCharts {
|
|||||||
|
|
||||||
async updateOpticalChart(period) {
|
async updateOpticalChart(period) {
|
||||||
try {
|
try {
|
||||||
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 || !data.timestamps || data.timestamps.length === 0) {
|
||||||
@@ -527,12 +632,10 @@ class GPONCharts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const labels = this.formatLabels(data.timestamps, period);
|
const labels = this.formatLabels(data.timestamps, period);
|
||||||
|
|
||||||
this.charts.optical.data.labels = labels;
|
this.charts.optical.data.labels = labels;
|
||||||
this.charts.optical.data.datasets[0].data = data.data.rx_power || [];
|
this.charts.optical.data.datasets[0].data = data.data.rx_power;
|
||||||
this.charts.optical.data.datasets[1].data = data.data.tx_power || [];
|
this.charts.optical.data.datasets[1].data = data.data.tx_power;
|
||||||
this.charts.optical.update();
|
this.charts.optical.update();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Charts] Error optical:', error);
|
console.error('[Charts] Error optical:', error);
|
||||||
}
|
}
|
||||||
@@ -540,39 +643,57 @@ class GPONCharts {
|
|||||||
|
|
||||||
async updateTemperatureChart(period) {
|
async updateTemperatureChart(period) {
|
||||||
try {
|
try {
|
||||||
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) return;
|
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
||||||
|
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;
|
||||||
this.charts.temperature.data.datasets[0].data = data.data.temperature || [];
|
this.charts.temperature.data.datasets[0].data = data.data.temperature;
|
||||||
this.charts.temperature.update();
|
this.charts.temperature.update();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Charts] Error temperature:', error);
|
console.error('[Charts] Error temperature:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateTrafficChart(period) {
|
async updateTXBiasChart(period) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/history/traffic/' + 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) return;
|
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
||||||
|
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.datasets[0].data = data.data.tx_bias;
|
||||||
|
this.charts.txbias.update();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Charts] Error TX Bias:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const rxPps = data.data.rx_packets || [];
|
async updateTrafficChart(period) {
|
||||||
const txPps = data.data.tx_packets || [];
|
try {
|
||||||
|
const response = await fetch(`/api/history/traffic/${period}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
||||||
|
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.labels = labels;
|
||||||
this.charts.traffic.data.datasets[0].data = rxPps;
|
this.charts.traffic.data.datasets[0].data = rxPps;
|
||||||
this.charts.traffic.data.datasets[1].data = txPps;
|
this.charts.traffic.data.datasets[1].data = txPps;
|
||||||
this.charts.traffic.update();
|
this.charts.traffic.update();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Charts] Error traffic:', error);
|
console.error('[Charts] Error traffic:', error);
|
||||||
}
|
}
|
||||||
@@ -580,19 +701,16 @@ class GPONCharts {
|
|||||||
|
|
||||||
async updateBytesChart(period) {
|
async updateBytesChart(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) return;
|
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
||||||
|
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 =>
|
const txMbps = data.data.tx_bytes.map(v => v !== null ? (v * 8) / 1000000 : null);
|
||||||
v !== null ? (v * 8) / 1000000 : null
|
|
||||||
);
|
|
||||||
const txMbps = (data.data.tx_bytes || []).map(v =>
|
|
||||||
v !== null ? (v * 8) / 1000000 : null
|
|
||||||
);
|
|
||||||
|
|
||||||
this.charts.bytes.data.labels = labels;
|
this.charts.bytes.data.labels = labels;
|
||||||
this.charts.bytes.data.datasets[0].data = rxMbps;
|
this.charts.bytes.data.datasets[0].data = rxMbps;
|
||||||
@@ -600,7 +718,6 @@ class GPONCharts {
|
|||||||
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));
|
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);
|
||||||
}
|
}
|
||||||
@@ -608,23 +725,20 @@ class GPONCharts {
|
|||||||
|
|
||||||
async updateVolumeChart(period) {
|
async updateVolumeChart(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) return;
|
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const labels = this.formatLabels(data.timestamps, period);
|
const labels = this.formatLabels(data.timestamps, period);
|
||||||
|
const rxBytesRate = data.data.rx_bytes;
|
||||||
const rxBytesRate = data.data.rx_bytes || [];
|
const txBytesRate = data.data.tx_bytes;
|
||||||
const txBytesRate = data.data.tx_bytes || [];
|
|
||||||
const step = data.step || 60;
|
const step = data.step || 60;
|
||||||
|
|
||||||
const rxPerInterval = rxBytesRate.map(rate =>
|
const rxPerInterval = rxBytesRate.map(rate => rate !== null ? rate * step : null);
|
||||||
rate !== null ? rate * step : null
|
const txPerInterval = txBytesRate.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;
|
||||||
@@ -633,7 +747,6 @@ class GPONCharts {
|
|||||||
|
|
||||||
const maxRx = Math.max(...rxPerInterval.filter(v => v !== null));
|
const maxRx = Math.max(...rxPerInterval.filter(v => v !== null));
|
||||||
console.log('[Charts] Volume - max per minute:', this.formatBytes(maxRx));
|
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);
|
||||||
}
|
}
|
||||||
@@ -641,21 +754,21 @@ class GPONCharts {
|
|||||||
|
|
||||||
async updateFECChart(period) {
|
async updateFECChart(period) {
|
||||||
try {
|
try {
|
||||||
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) return;
|
if (!data || !data.timestamps || data.timestamps.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const labels = this.formatLabels(data.timestamps, period);
|
const labels = this.formatLabels(data.timestamps, period);
|
||||||
|
const correctedRate = data.data.corrected;
|
||||||
const correctedRate = data.data.corrected || [];
|
const uncorrectedRate = data.data.uncorrected;
|
||||||
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 = correctedRate;
|
||||||
this.charts.fec.data.datasets[1].data = uncorrectedRate;
|
this.charts.fec.data.datasets[1].data = uncorrectedRate;
|
||||||
this.charts.fec.update();
|
this.charts.fec.update();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Charts] Error FEC:', error);
|
console.error('[Charts] Error FEC:', error);
|
||||||
}
|
}
|
||||||
@@ -664,12 +777,10 @@ 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 (period === '7d' || period === '14d' || period === '30d' || period === '60d' || period === '90d') {
|
||||||
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' });
|
||||||
}
|
}
|
||||||
|
|
||||||
return date.toLocaleTimeString('pl-PL', { hour: '2-digit', minute: '2-digit' });
|
return date.toLocaleTimeString('pl-PL', { hour: '2-digit', minute: '2-digit' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -677,12 +788,10 @@ class GPONCharts {
|
|||||||
formatBytes(bytes, decimals = 2) {
|
formatBytes(bytes, decimals = 2) {
|
||||||
if (bytes === null || bytes === undefined) return 'N/A';
|
if (bytes === null || bytes === undefined) return 'N/A';
|
||||||
if (bytes === 0) return '0 B';
|
if (bytes === 0) return '0 B';
|
||||||
|
|
||||||
const k = 1024;
|
const k = 1024;
|
||||||
const dm = decimals < 0 ? 0 : decimals;
|
const dm = decimals < 0 ? 0 : decimals;
|
||||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
<div id="alerts-container" class="mb-4"></div>
|
<div id="alerts-container" class="mb-4"></div>
|
||||||
|
|
||||||
<!-- Global Period Selector -->
|
<!-- Global Period Selector -->
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="card">
|
||||||
<div class="card-body py-3">
|
<div class="card-body py-3">
|
||||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||||
<div class="flex-shrink-0 text-white-50">
|
<div class="flex-shrink-0 text-white-50">
|
||||||
@@ -69,6 +72,9 @@
|
|||||||
<input type="hidden" id="global-period-hidden" name="global-period" value="1h">
|
<input type="hidden" id="global-period-hidden" name="global-period" value="1h">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Main Metrics Row -->
|
<!-- Main Metrics Row -->
|
||||||
<div class="row g-3 mb-4">
|
<div class="row g-3 mb-4">
|
||||||
@@ -173,7 +179,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Charts Row -->
|
<!-- Optical Metrics Row (RX/TX Power + Temperature + TX Bias) -->
|
||||||
<div class="row g-3 mb-4">
|
<div class="row g-3 mb-4">
|
||||||
<!-- Optical Power -->
|
<!-- Optical Power -->
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
@@ -231,11 +237,42 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Traffic -->
|
<!-- TX Bias -->
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<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-activity"></i> Traffic</span>
|
<span><i class="bi bi-lightning-charge"></i> TX Bias Current</span>
|
||||||
|
<select class="form-select form-select-sm w-auto" id="period-txbias">
|
||||||
|
<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-txbias" height="250"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Traffic Row -->
|
||||||
|
<div class="row g-3 mb-4">
|
||||||
|
<!-- Traffic Packets -->
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<span><i class="bi bi-activity"></i> Traffic (Packets/s)</span>
|
||||||
<select class="form-select form-select-sm w-auto" id="period-traffic">
|
<select class="form-select form-select-sm w-auto" id="period-traffic">
|
||||||
<option value="1h" selected>1h</option>
|
<option value="1h" selected>1h</option>
|
||||||
<option value="6h">6h</option>
|
<option value="6h">6h</option>
|
||||||
@@ -254,7 +291,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<canvas id="chart-traffic" height="250"></canvas>
|
<canvas id="chart-traffic" height="200"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -326,7 +363,7 @@
|
|||||||
<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">
|
||||||
|
|||||||
Reference in New Issue
Block a user