Files
Thing/src-tauri/src/network_monitor.rs
T
2026-07-24 18:16:34 +08:00

124 lines
4.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// network_monitor.rs — 网速监控模块
//
// 职责:
// - 后台每秒采样所有网络接口的收发字节数
// - 计算与上一次采样的差值(速率,bytes/s)
// - 聚合所有接口得到总下载/上传速率
// - 通过 Tauri 事件 "monitor-network" 推送给前端
//
// 设计说明:
// - 不依赖 ThingHKThingHK 当前不支持网络传感器扩展)
// - 使用 sysinfo crate 的 Networks(轻量、跨平台)
// - 采样间隔与 Kernel 快通道一致(1s),保证 UI 曲线流畅
// - 前端通过监听事件更新 UI,无需主动 invoke
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use serde::Serialize;
use sysinfo::Networks;
use tauri::{AppHandle, Emitter};
/// 推送给前端的网速数据
#[derive(Serialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct NetworkSpeed {
/// 下载速率(bytes/s),所有接口聚合
pub download_bps: u64,
/// 上传速率(bytes/s),所有接口聚合
pub upload_bps: u64,
/// 时间戳(ms
pub timestamp: u64,
}
/// 网速监控器:后台任务采样并 emit 事件。
///
/// 使用方式:
/// 1. app.manage(NetworkMonitor::new()) 注册到 Tauri State
/// 2. 在 setup 中调用 NetworkMonitor::start(&app) 启动后台采样
/// 3. 前端监听 "monitor-network" 事件
pub struct NetworkMonitor {
/// 是否正在运行(用于 start/stop 幂等控制)
running: Arc<AtomicBool>,
}
impl NetworkMonitor {
pub fn new() -> Self {
Self {
running: Arc::new(AtomicBool::new(false)),
}
}
/// 启动后台网速采样任务。幂等:重复调用不会启动多个任务。
pub fn start(self: Arc<Self>, app: AppHandle) {
if self.running.swap(true, Ordering::SeqCst) {
return; // 已在运行
}
let running = self.running.clone();
tauri::async_runtime::spawn(async move {
// sysinfo Networks 需要可变借用,放在独立线程的本地变量
let mut networks = Networks::new_with_refreshed_list();
// 首次采样仅记录基线,不 emit(速率需要两次采样差值)
let mut last_rx: u64 = networks.iter().map(|(_, data)| data.total_received()).sum();
let mut last_tx: u64 = networks.iter().map(|(_, data)| data.total_transmitted()).sum();
// 1s 间隔采样(与 Kernel 快通道一致)
let mut interval = tokio::time::interval(Duration::from_secs(1));
// 跳过第一次立即触发(已记录基线)
interval.tick().await;
loop {
interval.tick().await;
if !running.load(Ordering::SeqCst) {
break;
}
// 刷新网络数据(sysinfo 0.32: refresh() 刷新接口列表和统计数据)
networks.refresh();
let cur_rx: u64 = networks.iter().map(|(_, data)| data.total_received()).sum();
let cur_tx: u64 = networks.iter().map(|(_, data)| data.total_transmitted()).sum();
// 速率 = 当前累计 - 上次累计(sysinfo 的 total_received 是单调递增的累计值)
// 若接口移除/添加可能导致差值为负或异常大,做饱和减法保护
let download_bps = cur_rx.saturating_sub(last_rx);
let upload_bps = cur_tx.saturating_sub(last_tx);
last_rx = cur_rx;
last_tx = cur_tx;
let speed = NetworkSpeed {
download_bps,
upload_bps,
timestamp: chrono::Local::now().timestamp_millis() as u64,
};
// emit 给前端(失败忽略:窗口可能未就绪)
let _ = app.emit("monitor-network", &speed);
}
eprintln!("[network] 网速采样任务已退出");
});
}
/// 停止后台采样
#[allow(dead_code)]
pub fn stop(&self) {
self.running.store(false, Ordering::SeqCst);
}
}
impl Default for NetworkMonitor {
fn default() -> Self {
Self::new()
}
}
/// Tauri 命令:获取网速监控是否运行
#[tauri::command]
pub fn network_monitor_status(
state: tauri::State<'_, NetworkMonitor>,
) -> bool {
state.running.load(Ordering::SeqCst)
}