네트워크 사용량 이력 차트 추가

This commit is contained in:
seo
2026-07-25 22:24:35 +09:00
parent 677fad56f7
commit bc89dcf9c0
4 changed files with 122 additions and 3 deletions
+87
View File
@@ -105,6 +105,8 @@
settingsOpen: false,
settingsDirty: false,
customServiceScrollHoldUntil: 0,
networkLast: null,
networkHistory: [],
};
const storageKeys = {
@@ -160,6 +162,11 @@
temperature: 'CPU Temperature',
rp1Temp: 'RP1 Temp',
cpuWatt: 'CPU Watt',
networkHistory: 'Network History',
downloadPerSecond: 'Download / s',
uploadPerSecond: 'Upload / s',
rxPacketsPerSecond: 'RX Packets / s',
txPacketsPerSecond: 'TX Packets / s',
remaining: 'Remaining',
batteryVoltage: 'Battery Voltage',
wifiClients: 'DHCP Clients',
@@ -355,6 +362,11 @@
temperature: 'CPU 온도',
rp1Temp: 'RP1 온도',
cpuWatt: 'CPU 전력',
networkHistory: '네트워크 이력',
downloadPerSecond: '초당 다운로드 속도',
uploadPerSecond: '초당 업로드 속도',
rxPacketsPerSecond: '초당 RX 패킷',
txPacketsPerSecond: '초당 TX 패킷',
remaining: '잔여 시간',
batteryVoltage: '배터리 전압',
wifiClients: 'DHCP 클라이언트',
@@ -1823,6 +1835,74 @@
}
}
function primaryNetworkInterface(network) {
const rows = Array.isArray(network?.interfaces) ? network.interfaces : [];
const preferred = String(network?.default_interface || 'eth0');
return rows.find(row => row.name === preferred)
|| rows.find(row => row.name === 'eth0')
|| rows.find(row => row.carrier === 'up' && row.name !== 'br0')
|| rows[0]
|| null;
}
function networkCounter(row, key) {
const value = Number(row?.[key]);
return Number.isFinite(value) && value >= 0 ? value : null;
}
function pushNetworkSample(data) {
const iface = primaryNetworkInterface(data.network);
if (!iface) return;
const nowMs = Number(data.time_ms || Date.now());
const sample = {
iface: String(iface.name || ''),
time_ms: nowMs,
rx_bytes: networkCounter(iface, 'rx_bytes'),
tx_bytes: networkCounter(iface, 'tx_bytes'),
rx_packets: networkCounter(iface, 'rx_packets'),
tx_packets: networkCounter(iface, 'tx_packets'),
};
if (!sample.iface || sample.rx_bytes === null || sample.tx_bytes === null || sample.rx_packets === null || sample.tx_packets === null) {
return;
}
const previous = state.networkLast;
state.networkLast = sample;
if (!previous || previous.iface !== sample.iface || sample.time_ms <= previous.time_ms) {
return;
}
const seconds = (sample.time_ms - previous.time_ms) / 1000;
if (seconds <= 0 || seconds > 30) {
return;
}
const delta = key => sample[key] - previous[key];
const rxBytes = delta('rx_bytes');
const txBytes = delta('tx_bytes');
const rxPackets = delta('rx_packets');
const txPackets = delta('tx_packets');
if (rxBytes < 0 || txBytes < 0 || rxPackets < 0 || txPackets < 0) {
return;
}
state.networkHistory.push({
time: String(data.generated_at || '').replace('T', ' ') || new Date(sample.time_ms).toISOString().slice(0, 19).replace('T', ' '),
rx_mbps: (rxBytes * 8) / seconds / 1000000,
tx_mbps: (txBytes * 8) / seconds / 1000000,
rx_pps: rxPackets / seconds,
tx_pps: txPackets / seconds,
});
if (state.networkHistory.length > 180) {
state.networkHistory.splice(0, state.networkHistory.length - 180);
}
}
function renderSystemStatus(data) {
const system = data.system || {};
const disk = system.disk || {};
@@ -2476,6 +2556,12 @@
if (document.getElementById('batteryVoltageChart')) {
chart('batteryVoltageChart', 'BATTERYV', rows, 'battery_voltage', '#14b8a6', 'V', dynamicScale(rows, 'battery_voltage', { minSpan: 0.2, padding: 0.06 }));
}
const networkRows = state.networkHistory.slice(-180);
chart('netRxChart', 'NETRX', networkRows, 'rx_mbps', '#0ea5e9', ' Mbit/s', dynamicScale(networkRows, 'rx_mbps', { minSpan: 1, padding: 0.3 }));
chart('netTxChart', 'NETTX', networkRows, 'tx_mbps', '#22c55e', ' Mbit/s', dynamicScale(networkRows, 'tx_mbps', { minSpan: 1, padding: 0.3 }));
chart('netRxPacketsChart', 'NETRXPKT', networkRows, 'rx_pps', '#f97316', ' pps', dynamicScale(networkRows, 'rx_pps', { minSpan: 10, padding: 5 }));
chart('netTxPacketsChart', 'NETTXPKT', networkRows, 'tx_pps', '#a855f7', ' pps', dynamicScale(networkRows, 'tx_pps', { minSpan: 10, padding: 5 }));
}
function resizeChartsSoon() {
@@ -2496,6 +2582,7 @@
renderSettings(data.settings);
}
}
pushNetworkSample(data);
renderTop(data);
renderSystemStatus(data);
renderWifi(data);