네트워크 오류 드롭 누적 표시 정리
This commit is contained in:
+30
-116
@@ -2507,6 +2507,27 @@
|
||||
return rows.filter(row => String(row.iface || '') === iface).slice(-180);
|
||||
}
|
||||
|
||||
function formatCount(value) {
|
||||
const numeric = Number(value);
|
||||
if (!Number.isFinite(numeric)) return '0';
|
||||
return Math.max(0, Math.round(numeric)).toLocaleString();
|
||||
}
|
||||
|
||||
function networkQualitySummary(elementId, totals, direction) {
|
||||
const el = $('#' + elementId);
|
||||
if (!el) return;
|
||||
|
||||
const errorKey = direction === 'rx' ? 'rx_errors_total' : 'tx_errors_total';
|
||||
const dropKey = direction === 'rx' ? 'rx_dropped_total' : 'tx_dropped_total';
|
||||
const title = direction === 'rx' ? t('rxErrorsDrops') : t('txErrorsDrops');
|
||||
const row = totals || {};
|
||||
|
||||
el.innerHTML = `
|
||||
<span>${escapeHtml(title)}</span>
|
||||
<span><strong>${escapeHtml(formatCount(row[errorKey]))}</strong> ${escapeHtml(t('errorsLine'))} / <strong>${escapeHtml(formatCount(row[dropKey]))}</strong> ${escapeHtml(t('dropsLine'))}</span>
|
||||
`;
|
||||
}
|
||||
|
||||
function networkChart(canvasId, rows, speedKey, speedLabel, packetKey, packetLabel, speedColor = '#0ea5e9') {
|
||||
const canvas = $('#' + canvasId);
|
||||
if (!canvas || typeof Chart === 'undefined') return;
|
||||
@@ -2634,114 +2655,6 @@
|
||||
c.update('none');
|
||||
}
|
||||
|
||||
function networkQualityChart(canvasId, rows, errorKey, dropKey) {
|
||||
const canvas = $('#' + canvasId);
|
||||
if (!canvas || typeof Chart === 'undefined') return;
|
||||
|
||||
const { labels, xTickCallback, xGridColor } = timeAxis(rows);
|
||||
const errorValues = rows.map(row => {
|
||||
const value = Number(row[errorKey]);
|
||||
return Number.isFinite(value) ? value : null;
|
||||
});
|
||||
const dropValues = rows.map(row => {
|
||||
const value = Number(row[dropKey]);
|
||||
return Number.isFinite(value) ? value : null;
|
||||
});
|
||||
const scale = dynamicScaleForValues([...errorValues, ...dropValues], { minSpan: 1, padding: 0.2 });
|
||||
const datasets = [
|
||||
{
|
||||
label: t('errorsLine'),
|
||||
data: errorValues,
|
||||
borderColor: '#ef4444',
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 2.1,
|
||||
pointRadius: 0,
|
||||
tension: 0.2,
|
||||
fill: false,
|
||||
},
|
||||
{
|
||||
label: t('dropsLine'),
|
||||
data: dropValues,
|
||||
borderColor: '#f59e0b',
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 2,
|
||||
borderDash: [5, 4],
|
||||
pointRadius: 0,
|
||||
tension: 0.2,
|
||||
fill: false,
|
||||
},
|
||||
];
|
||||
const options = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: false,
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: 'index',
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
boxWidth: 18,
|
||||
boxHeight: 2,
|
||||
color: '#8d98aa',
|
||||
padding: 10,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
displayColors: true,
|
||||
padding: 8,
|
||||
callbacks: {
|
||||
label: item => `${item.dataset.label}: ${formatChartNumber(item.parsed.y, '/s')}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: {
|
||||
color: '#64748b',
|
||||
maxRotation: 0,
|
||||
autoSkip: false,
|
||||
callback: xTickCallback,
|
||||
},
|
||||
grid: {
|
||||
color: xGridColor,
|
||||
drawTicks: false,
|
||||
},
|
||||
border: { display: false },
|
||||
},
|
||||
y: {
|
||||
min: scale.min,
|
||||
max: scale.max,
|
||||
ticks: {
|
||||
color: '#8d98aa',
|
||||
maxTicksLimit: 4,
|
||||
callback: value => formatChartNumber(value, '/s'),
|
||||
},
|
||||
grid: { color: 'rgba(255,255,255,.045)', drawTicks: false },
|
||||
border: { display: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (!state.charts[canvasId]) {
|
||||
state.charts[canvasId] = new Chart(canvas, {
|
||||
type: 'line',
|
||||
data: { labels, datasets },
|
||||
options,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const c = state.charts[canvasId];
|
||||
c.data.labels = labels;
|
||||
c.data.datasets = datasets;
|
||||
c.options = options;
|
||||
c.update('none');
|
||||
}
|
||||
|
||||
function dynamicScaleForValues(values, options = {}) {
|
||||
const floor = Number(options.floor ?? 0);
|
||||
const ceiling = Number.isFinite(Number(options.ceiling)) ? Number(options.ceiling) : null;
|
||||
@@ -2804,22 +2717,23 @@
|
||||
const lanRows = networkRowsForIface(networkRows, 'eth1');
|
||||
const wifi24Rows = networkRowsForIface(networkRows, 'wlan0');
|
||||
const wifi5Rows = networkRowsForIface(networkRows, 'wlan1');
|
||||
const networkQualityTotals = data.network_quality_totals || {};
|
||||
networkChart('netWanRxChart', wanRows, 'rx_mbytes', t('downloadLine'), 'rx_pps', t('rxPacketsLine'), '#0ea5e9');
|
||||
networkChart('netWanTxChart', wanRows, 'tx_mbytes', t('uploadLine'), 'tx_pps', t('txPacketsLine'), '#22c55e');
|
||||
networkQualityChart('netWanRxQualityChart', wanRows, 'rx_errors_per_sec', 'rx_dropped_per_sec');
|
||||
networkQualityChart('netWanTxQualityChart', wanRows, 'tx_errors_per_sec', 'tx_dropped_per_sec');
|
||||
networkQualitySummary('netWanRxQualitySummary', networkQualityTotals[wanIface], 'rx');
|
||||
networkQualitySummary('netWanTxQualitySummary', networkQualityTotals[wanIface], 'tx');
|
||||
networkChart('netLanRxChart', lanRows, 'rx_mbytes', t('downloadLine'), 'rx_pps', t('rxPacketsLine'), '#0ea5e9');
|
||||
networkChart('netLanTxChart', lanRows, 'tx_mbytes', t('uploadLine'), 'tx_pps', t('txPacketsLine'), '#22c55e');
|
||||
networkQualityChart('netLanRxQualityChart', lanRows, 'rx_errors_per_sec', 'rx_dropped_per_sec');
|
||||
networkQualityChart('netLanTxQualityChart', lanRows, 'tx_errors_per_sec', 'tx_dropped_per_sec');
|
||||
networkQualitySummary('netLanRxQualitySummary', networkQualityTotals.eth1, 'rx');
|
||||
networkQualitySummary('netLanTxQualitySummary', networkQualityTotals.eth1, 'tx');
|
||||
networkChart('netWifi24RxChart', wifi24Rows, 'rx_mbytes', t('downloadLine'), 'rx_pps', t('rxPacketsLine'), '#0ea5e9');
|
||||
networkChart('netWifi24TxChart', wifi24Rows, 'tx_mbytes', t('uploadLine'), 'tx_pps', t('txPacketsLine'), '#22c55e');
|
||||
networkQualityChart('netWifi24RxQualityChart', wifi24Rows, 'rx_errors_per_sec', 'rx_dropped_per_sec');
|
||||
networkQualityChart('netWifi24TxQualityChart', wifi24Rows, 'tx_errors_per_sec', 'tx_dropped_per_sec');
|
||||
networkQualitySummary('netWifi24RxQualitySummary', networkQualityTotals.wlan0, 'rx');
|
||||
networkQualitySummary('netWifi24TxQualitySummary', networkQualityTotals.wlan0, 'tx');
|
||||
networkChart('netWifi5RxChart', wifi5Rows, 'rx_mbytes', t('downloadLine'), 'rx_pps', t('rxPacketsLine'), '#0ea5e9');
|
||||
networkChart('netWifi5TxChart', wifi5Rows, 'tx_mbytes', t('uploadLine'), 'tx_pps', t('txPacketsLine'), '#22c55e');
|
||||
networkQualityChart('netWifi5RxQualityChart', wifi5Rows, 'rx_errors_per_sec', 'rx_dropped_per_sec');
|
||||
networkQualityChart('netWifi5TxQualityChart', wifi5Rows, 'tx_errors_per_sec', 'tx_dropped_per_sec');
|
||||
networkQualitySummary('netWifi5RxQualitySummary', networkQualityTotals.wlan1, 'rx');
|
||||
networkQualitySummary('netWifi5TxQualitySummary', networkQualityTotals.wlan1, 'tx');
|
||||
}
|
||||
|
||||
function resizeChartsSoon() {
|
||||
|
||||
Reference in New Issue
Block a user