765 lines
28 KiB
JavaScript
765 lines
28 KiB
JavaScript
class GPONCharts {
|
|
constructor() {
|
|
this.charts = {};
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.createOpticalChart();
|
|
this.createTemperatureChart();
|
|
this.createTXBiasChart();
|
|
this.createTrafficChart();
|
|
this.createBytesChart();
|
|
this.createVolumeChart();
|
|
this.createVoltageChart();
|
|
this.createFECChart();
|
|
|
|
// Period selectors
|
|
document.getElementById('period-optical').addEventListener('change', (e) => {
|
|
this.updateOpticalChart(e.target.value);
|
|
});
|
|
|
|
document.getElementById('period-temperature').addEventListener('change', (e) => {
|
|
this.updateTemperatureChart(e.target.value);
|
|
});
|
|
|
|
document.getElementById('period-txbias').addEventListener('change', (e) => {
|
|
this.updateTXBiasChart(e.target.value);
|
|
});
|
|
|
|
document.getElementById('period-traffic').addEventListener('change', (e) => {
|
|
this.updateTrafficChart(e.target.value);
|
|
});
|
|
|
|
document.getElementById('period-bytes').addEventListener('change', (e) => {
|
|
this.updateBytesChart(e.target.value);
|
|
});
|
|
|
|
document.getElementById('period-volume').addEventListener('change', (e) => {
|
|
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);
|
|
});
|
|
|
|
// Volume chart type switcher
|
|
document.querySelectorAll('input[name="volume-chart-type"]').forEach(radio => {
|
|
radio.addEventListener('change', (e) => {
|
|
this.changeVolumeChartType(e.target.value);
|
|
});
|
|
});
|
|
|
|
// Global period selector
|
|
this.setupGlobalPeriodSelector();
|
|
|
|
// Initial load
|
|
this.updateOpticalChart('1h');
|
|
this.updateTemperatureChart('1h');
|
|
this.updateTXBiasChart('7d');
|
|
this.updateTrafficChart('1h');
|
|
this.updateBytesChart('1h');
|
|
this.updateVolumeChart('1h');
|
|
this.updateVoltageChart('7d');
|
|
this.updateFECChart('1h');
|
|
|
|
// Auto-refresh every 30s
|
|
setInterval(() => {
|
|
const optPeriod = document.getElementById('period-optical').value;
|
|
const tempPeriod = document.getElementById('period-temperature').value;
|
|
const txbiasPeriod = document.getElementById('period-txbias').value;
|
|
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);
|
|
this.updateTemperatureChart(tempPeriod);
|
|
this.updateTXBiasChart(txbiasPeriod);
|
|
this.updateTrafficChart(trafficPeriod);
|
|
this.updateBytesChart(bytesPeriod);
|
|
this.updateVolumeChart(volumePeriod);
|
|
this.updateVoltageChart(voltagePeriod);
|
|
this.updateFECChart(fecPeriod);
|
|
}, 30000);
|
|
|
|
console.log('[Charts] Initialized');
|
|
}
|
|
|
|
setupGlobalPeriodSelector() {
|
|
const radios = document.querySelectorAll('input[name="global-period"]');
|
|
radios.forEach(radio => {
|
|
radio.addEventListener('change', (e) => {
|
|
const period = e.target.value;
|
|
document.getElementById('period-optical').value = period;
|
|
document.getElementById('period-temperature').value = period;
|
|
document.getElementById('period-txbias').value = period;
|
|
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);
|
|
this.updateTemperatureChart(period);
|
|
this.updateTXBiasChart(period);
|
|
this.updateTrafficChart(period);
|
|
this.updateBytesChart(period);
|
|
this.updateVolumeChart(period);
|
|
this.updateVoltageChart(period);
|
|
this.updateFECChart(period);
|
|
|
|
console.log('[Charts] Global period:', period);
|
|
});
|
|
});
|
|
}
|
|
|
|
createOpticalChart() {
|
|
const ctx = document.getElementById('chart-optical').getContext('2d');
|
|
this.charts.optical = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: [],
|
|
datasets: [{
|
|
label: 'RX Power (dBm)',
|
|
data: [],
|
|
borderColor: '#00ccff',
|
|
backgroundColor: 'rgba(0, 204, 255, 0.1)',
|
|
borderWidth: 2,
|
|
tension: 0.4,
|
|
fill: true,
|
|
pointRadius: 0
|
|
}, {
|
|
label: 'TX Power (dBm)',
|
|
data: [],
|
|
borderColor: '#ffcc00',
|
|
backgroundColor: 'rgba(255, 204, 0, 0.1)',
|
|
borderWidth: 2,
|
|
tension: 0.4,
|
|
fill: true,
|
|
pointRadius: 0
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
interaction: { mode: 'index', intersect: false },
|
|
plugins: {
|
|
legend: { display: true, labels: { color: '#ffffff' } },
|
|
tooltip: { mode: 'index', intersect: false }
|
|
},
|
|
scales: {
|
|
x: {
|
|
ticks: { color: '#b0b0b0', maxRotation: 0 },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' }
|
|
},
|
|
y: {
|
|
ticks: { color: '#b0b0b0' },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
|
title: { display: true, text: 'dBm', color: '#b0b0b0' }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
createTemperatureChart() {
|
|
const ctx = document.getElementById('chart-temperature').getContext('2d');
|
|
this.charts.temperature = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: [],
|
|
datasets: [{
|
|
label: 'Temperature (°C)',
|
|
data: [],
|
|
borderColor: '#ff6b6b',
|
|
backgroundColor: 'rgba(255, 107, 107, 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(1)}°C` : 'N/A'
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
ticks: { color: '#b0b0b0', maxRotation: 0 },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' }
|
|
},
|
|
y: {
|
|
ticks: { color: '#b0b0b0' },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
|
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: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
callbacks: {
|
|
label: (ctx) => ctx.parsed.y !== null ? `${ctx.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: (value) => value.toFixed(1) + ' mA'
|
|
},
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
|
title: { display: true, text: 'mA', color: '#b0b0b0' }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
createTrafficChart() {
|
|
const ctx = document.getElementById('chart-traffic').getContext('2d');
|
|
this.charts.traffic = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: [],
|
|
datasets: [{
|
|
label: 'RX (Packets/s)',
|
|
data: [],
|
|
borderColor: '#4bc0c0',
|
|
backgroundColor: 'rgba(75, 192, 192, 0.1)',
|
|
borderWidth: 2,
|
|
tension: 0.4,
|
|
fill: true,
|
|
pointRadius: 0
|
|
}, {
|
|
label: 'TX (Packets/s)',
|
|
data: [],
|
|
borderColor: '#ff6384',
|
|
backgroundColor: 'rgba(255, 99, 132, 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 }
|
|
},
|
|
scales: {
|
|
x: {
|
|
ticks: { color: '#b0b0b0', maxRotation: 0 },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' }
|
|
},
|
|
y: {
|
|
ticks: { color: '#b0b0b0' },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
|
title: { display: true, text: 'packets/s', color: '#b0b0b0' }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
createBytesChart() {
|
|
const ctx = document.getElementById('chart-bytes').getContext('2d');
|
|
this.charts.bytes = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: [],
|
|
datasets: [{
|
|
label: 'RX (Mb/s)',
|
|
data: [],
|
|
borderColor: '#4bc0c0',
|
|
backgroundColor: 'rgba(75, 192, 192, 0.1)',
|
|
borderWidth: 2,
|
|
tension: 0.4,
|
|
fill: true,
|
|
pointRadius: 0
|
|
}, {
|
|
label: 'TX (Mb/s)',
|
|
data: [],
|
|
borderColor: '#ff6384',
|
|
backgroundColor: 'rgba(255, 99, 132, 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.dataset.label}: ${ctx.parsed.y ? ctx.parsed.y.toFixed(2) : 0} Mb/s`
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
ticks: { color: '#b0b0b0', maxRotation: 0 },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' }
|
|
},
|
|
y: {
|
|
ticks: {
|
|
color: '#b0b0b0',
|
|
callback: (value) => value.toFixed(1)
|
|
},
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
|
title: { display: true, text: 'Mb/s', color: '#b0b0b0' }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
createVolumeChart() {
|
|
const ctx = document.getElementById('chart-volume').getContext('2d');
|
|
const self = this;
|
|
|
|
this.charts.volume = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: [],
|
|
datasets: [{
|
|
label: 'RX (per minute)',
|
|
data: [],
|
|
borderColor: '#4bc0c0',
|
|
backgroundColor: 'rgba(75, 192, 192, 0.05)',
|
|
borderWidth: 2,
|
|
tension: 0.4,
|
|
fill: true,
|
|
pointRadius: 0
|
|
}, {
|
|
label: 'TX (per minute)',
|
|
data: [],
|
|
borderColor: '#ff6384',
|
|
backgroundColor: 'rgba(255, 99, 132, 0.05)',
|
|
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.dataset.label}: ${self.formatBytes(ctx.parsed.y)}`
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
stacked: false,
|
|
ticks: { color: '#b0b0b0', maxRotation: 0 },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' }
|
|
},
|
|
y: {
|
|
stacked: false,
|
|
ticks: {
|
|
color: '#b0b0b0',
|
|
callback: (value) => self.formatBytes(value)
|
|
},
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
|
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' }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
createFECChart() {
|
|
const ctx = document.getElementById('chart-fec').getContext('2d');
|
|
this.charts.fec = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: [],
|
|
datasets: [{
|
|
label: 'FEC Corrected',
|
|
data: [],
|
|
borderColor: '#ffa500',
|
|
backgroundColor: 'rgba(255, 165, 0, 0.1)',
|
|
borderWidth: 2,
|
|
tension: 0.4,
|
|
fill: true,
|
|
pointRadius: 0
|
|
}, {
|
|
label: 'FEC Uncorrected',
|
|
data: [],
|
|
borderColor: '#ff0000',
|
|
backgroundColor: 'rgba(255, 0, 0, 0.1)',
|
|
borderWidth: 2,
|
|
tension: 0.4,
|
|
fill: true,
|
|
pointRadius: 0
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: { display: true, labels: { color: '#ffffff' } }
|
|
},
|
|
scales: {
|
|
x: {
|
|
ticks: { color: '#b0b0b0', maxRotation: 0 },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' }
|
|
},
|
|
y: {
|
|
ticks: { color: '#b0b0b0' },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
|
title: { display: true, text: 'errors/s', color: '#b0b0b0' }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
changeVolumeChartType(type) {
|
|
if (!this.charts.volume) return;
|
|
|
|
const currentData = {
|
|
labels: this.charts.volume.data.labels,
|
|
datasets: this.charts.volume.data.datasets
|
|
};
|
|
|
|
this.charts.volume.destroy();
|
|
|
|
const ctx = document.getElementById('chart-volume').getContext('2d');
|
|
const self = this;
|
|
|
|
const config = {
|
|
type: type,
|
|
data: {
|
|
labels: currentData.labels,
|
|
datasets: [{
|
|
label: 'RX (per minute)',
|
|
data: currentData.datasets[0].data,
|
|
borderColor: '#4bc0c0',
|
|
backgroundColor: type === 'bar' ? 'rgba(75, 192, 192, 0.6)' : 'rgba(75, 192, 192, 0.05)',
|
|
borderWidth: type === 'bar' ? 1 : 2,
|
|
tension: 0.4,
|
|
fill: true,
|
|
pointRadius: 0
|
|
}, {
|
|
label: 'TX (per minute)',
|
|
data: currentData.datasets[1].data,
|
|
borderColor: '#ff6384',
|
|
backgroundColor: type === 'bar' ? 'rgba(255, 99, 132, 0.6)' : 'rgba(255, 99, 132, 0.05)',
|
|
borderWidth: type === 'bar' ? 1 : 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.dataset.label}: ${self.formatBytes(ctx.parsed.y)}`
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
stacked: false,
|
|
ticks: { color: '#b0b0b0', maxRotation: 0 },
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' }
|
|
},
|
|
y: {
|
|
stacked: false,
|
|
ticks: {
|
|
color: '#b0b0b0',
|
|
callback: (value) => self.formatBytes(value)
|
|
},
|
|
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
|
title: { display: true, text: 'Data per minute', color: '#b0b0b0' }
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
this.charts.volume = new Chart(ctx, config);
|
|
}
|
|
|
|
async updateOpticalChart(period) {
|
|
try {
|
|
const response = await fetch(`/api/history/optical/${period}`);
|
|
const data = await response.json();
|
|
|
|
if (!data?.timestamps?.length) {
|
|
console.warn('[Charts] No optical data');
|
|
return;
|
|
}
|
|
|
|
const labels = this.formatLabels(data.timestamps, period);
|
|
this.charts.optical.data.labels = labels;
|
|
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.update();
|
|
} catch (error) {
|
|
console.error('[Charts] Error optical:', error);
|
|
}
|
|
}
|
|
|
|
async updateTemperatureChart(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.temperature.data.labels = labels;
|
|
this.charts.temperature.data.datasets[0].data = data.data.temperature;
|
|
this.charts.temperature.update();
|
|
} catch (error) {
|
|
console.error('[Charts] Error temperature:', error);
|
|
}
|
|
}
|
|
|
|
async updateTXBiasChart(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.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);
|
|
}
|
|
}
|
|
|
|
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?.timestamps?.length) return;
|
|
|
|
const labels = this.formatLabels(data.timestamps, period);
|
|
this.charts.traffic.data.labels = labels;
|
|
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);
|
|
}
|
|
}
|
|
|
|
async updateBytesChart(period) {
|
|
try {
|
|
const response = await fetch(`/api/history/traffic/${period}`);
|
|
const data = await response.json();
|
|
|
|
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);
|
|
const txMbps = data.data.tx_bytes.map(v => v !== null ? (v * 8) / 1000000 : null);
|
|
|
|
this.charts.bytes.data.labels = labels;
|
|
this.charts.bytes.data.datasets[0].data = rxMbps;
|
|
this.charts.bytes.data.datasets[1].data = txMbps;
|
|
this.charts.bytes.update();
|
|
} catch (error) {
|
|
console.error('[Charts] Error bytes:', error);
|
|
}
|
|
}
|
|
|
|
async updateVolumeChart(period) {
|
|
try {
|
|
const response = await fetch(`/api/history/traffic/${period}`);
|
|
const data = await response.json();
|
|
|
|
if (!data?.timestamps?.length) return;
|
|
|
|
const labels = this.formatLabels(data.timestamps, period);
|
|
const step = data.step || 60;
|
|
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();
|
|
} catch (error) {
|
|
console.error('[Charts] Error volume:', error);
|
|
}
|
|
}
|
|
|
|
async updateFECChart(period) {
|
|
try {
|
|
const response = await fetch(`/api/history/fec/${period}`);
|
|
const data = await response.json();
|
|
|
|
if (!data?.timestamps?.length) return;
|
|
|
|
const labels = this.formatLabels(data.timestamps, period);
|
|
this.charts.fec.data.labels = labels;
|
|
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);
|
|
}
|
|
}
|
|
|
|
formatLabels(timestamps, period) {
|
|
return timestamps.map(ts => {
|
|
const date = new Date(ts * 1000);
|
|
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' });
|
|
}
|
|
return date.toLocaleTimeString('pl-PL', { hour: '2-digit', minute: '2-digit' });
|
|
});
|
|
}
|
|
|
|
formatBytes(bytes, decimals = 2) {
|
|
if (bytes === null || bytes === undefined) return 'N/A';
|
|
if (bytes === 0) return '0 B';
|
|
const k = 1024;
|
|
const dm = decimals < 0 ? 0 : decimals;
|
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
window.charts = new GPONCharts();
|
|
});
|