124 lines
4.4 KiB
Rust
124 lines
4.4 KiB
Rust
// 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<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(crate::constants::events::MONITOR_NETWORK, &speed);
|
||
}
|
||
crate::logger::log_info("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_status(
|
||
state: tauri::State<'_, NetworkMonitor>,
|
||
) -> bool {
|
||
state.running.load(Ordering::SeqCst)
|
||
}
|