// network_monitor.rs — 网速监控模块 // // 职责: // - 后台每秒采样所有网络接口的收发字节数 // - 计算与上一次采样的差值(速率,bytes/s) // - 聚合所有接口得到总下载/上传速率 // - 通过 Tauri 事件 "monitor-network" 推送给前端 // // 设计说明: // - 不依赖 ThingHK(ThingHK 当前不支持网络传感器扩展) // - 使用 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, } impl NetworkMonitor { pub fn new() -> Self { Self { running: Arc::new(AtomicBool::new(false)), } } /// 启动后台网速采样任务。幂等:重复调用不会启动多个任务。 pub fn start(self: Arc, 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) }