stream cards

This commit is contained in:
Julian Freeman
2025-11-25 23:35:54 -04:00
parent d18f36bc13
commit 4c8322d96b
2 changed files with 393 additions and 665 deletions

View File

@@ -8,22 +8,13 @@ use serde::Serialize;
use sysinfo::{System, Disks};
use wmi::{COMLibrary, WMIConnection};
use std::fs;
// 引入 chrono 用于时间计算和格式化
// 引入 tauri::Emitter 用于发送事件 (Tauri v2)
use tauri::Emitter;
use chrono::{Duration, FixedOffset, Local, NaiveDate, TimeZone};
// --- 1. 数据结构 ---
// --- 1. 数据结构 (保持不变,用于序列化部分数据) ---
#[derive(Serialize)]
struct SystemHealthReport {
hardware: HardwareSummary,
storage: Vec<StorageDevice>,
events: Vec<SystemEvent>,
minidumps: MinidumpInfo,
drivers: Vec<DriverIssue>,
battery: Option<BatteryInfo>,
}
#[derive(Serialize)]
#[derive(Serialize, Clone)] // 增加 Clone trait 方便使用
struct HardwareSummary {
cpu_name: String,
sys_vendor: String,
@@ -38,7 +29,7 @@ struct HardwareSummary {
c_drive_used_gb: u64,
}
#[derive(Serialize)]
#[derive(Serialize, Clone)]
struct StorageDevice {
model: String,
health_status: String,
@@ -46,7 +37,7 @@ struct StorageDevice {
is_danger: bool,
}
#[derive(Serialize)]
#[derive(Serialize, Clone)]
struct SystemEvent {
time_generated: String,
event_id: u32,
@@ -55,21 +46,21 @@ struct SystemEvent {
analysis_hint: String,
}
#[derive(Serialize)]
#[derive(Serialize, Clone)]
struct MinidumpInfo {
found: bool,
count: usize,
explanation: String,
}
#[derive(Serialize)]
#[derive(Serialize, Clone)]
struct DriverIssue {
device_name: String,
error_code: u32,
description: String,
}
#[derive(Serialize)]
#[derive(Serialize, Clone)]
struct BatteryInfo {
health_percentage: u32,
is_ac_connected: bool,
@@ -122,269 +113,255 @@ struct Win32_ComputerSystem {
Model: Option<String>,
}
// --- 辅助函数:生成 WMI 查询用的时间字符串 ---
// WMI 要求的时间格式类似于: 20231125000000.000000+000
// --- 辅助函数 ---
fn get_wmi_query_time(days_ago: i64) -> String {
let target_date = Local::now() - Duration::days(days_ago);
// 这里我们只生成前半部分 YYYYMMDDHHMMSSWMI 字符串比较支持这种前缀
// 或者生成完整格式 .000000+000 (简化处理,假设本地时区)
target_date.format("%Y%m%d%H%M%S.000000+000").to_string()
}
// --- 辅助函数:格式化显示用的时间 ---
fn format_wmi_time(wmi_str: &str) -> String {
if wmi_str.len() < 25 {
return wmi_str.to_string();
}
let year = wmi_str[0..4].parse::<i32>().unwrap_or(1970);
let month = wmi_str[4..6].parse::<u32>().unwrap_or(1);
let day = wmi_str[6..8].parse::<u32>().unwrap_or(1);
let hour = wmi_str[8..10].parse::<u32>().unwrap_or(0);
let min = wmi_str[10..12].parse::<u32>().unwrap_or(0);
let sec = wmi_str[12..14].parse::<u32>().unwrap_or(0);
let sign = &wmi_str[21..22];
let offset_val = wmi_str[22..25].parse::<i32>().unwrap_or(0);
let offset_mins = if sign == "-" { -offset_val } else { offset_val };
let offset = FixedOffset::east_opt(offset_mins * 60).unwrap_or(FixedOffset::east_opt(0).unwrap());
let naive_date = NaiveDate::from_ymd_opt(year, month, day).unwrap_or(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
let naive_dt = naive_date.and_hms_opt(hour, min, sec).unwrap_or_default();
match offset.from_local_datetime(&naive_dt).single() {
Some(dt) => {
dt.with_timezone(&Local).format("%Y-%m-%d %H:%M:%S").to_string()
},
Some(dt) => dt.with_timezone(&Local).format("%Y-%m-%d %H:%M:%S").to_string(),
None => format!("{}-{:02}-{:02} {:02}:{:02}:{:02}", year, month, day, hour, min, sec)
}
}
// --- 核心逻辑 ---
// --- 核心逻辑:分步流式传输 ---
#[tauri::command]
async fn run_diagnosis() -> Result<SystemHealthReport, String> {
let report = tokio::task::spawn_blocking(|| {
// 1. 高效初始化:只创建空实例,不扫描进程
let mut sys = System::new();
sys.refresh_memory();
sys.refresh_cpu();
async fn run_diagnosis(window: tauri::Window) -> Result<(), String> {
// 使用 spawn 在后台线程运行,避免阻塞 Tauri 主线程
// 注意:这里不等待 join而是让它在后台跑通过 window.emit 发送进度
std::thread::spawn(move || {
// 1. 硬件概览 (最快)
{
let mut sys = System::new();
sys.refresh_memory();
sys.refresh_cpu();
let wmi_con = WMIConnection::new(COMLibrary::new().unwrap()).ok();
let mut bios_ver = "Unknown".to_string();
let mut mobo_vendor = "Unknown".to_string();
let mut mobo_product = "Unknown".to_string();
let mut sys_vendor = "Unknown".to_string();
let mut sys_product = "Unknown".to_string();
if let Some(con) = &wmi_con {
if let Ok(results) = con.raw_query::<Win32_BIOS>("SELECT SMBIOSBIOSVersion FROM Win32_BIOS") {
if let Some(bios) = results.first() { bios_ver = bios.SMBIOSBIOSVersion.clone().unwrap_or_default(); }
}
if let Ok(results) = con.raw_query::<Win32_BaseBoard>("SELECT Manufacturer, Product FROM Win32_BaseBoard") {
if let Some(board) = results.first() {
mobo_vendor = board.Manufacturer.clone().unwrap_or_default();
mobo_product = board.Product.clone().unwrap_or_default();
}
}
if let Ok(results) = con.raw_query::<Win32_ComputerSystem>("SELECT Manufacturer, Model FROM Win32_ComputerSystem") {
if let Some(cs) = results.first() {
sys_vendor = cs.Manufacturer.clone().unwrap_or_default();
sys_product = cs.Model.clone().unwrap_or_default();
}
}
}
// C盘
let mut c_total = 0u64;
let mut c_used = 0u64;
let disks = Disks::new_with_refreshed_list();
for disk in &disks {
if disk.mount_point().to_string_lossy().starts_with("C:") {
c_total = disk.total_space() / 1024 / 1024 / 1024;
let free = disk.available_space() / 1024 / 1024 / 1024;
c_used = c_total - free;
break;
}
}
let cpu_brand = if let Some(cpu) = sys.cpus().first() {
cpu.brand().trim().to_string()
} else {
"Unknown CPU".to_string()
};
let hardware = HardwareSummary {
cpu_name: cpu_brand,
sys_vendor, sys_product, mobo_vendor, mobo_product,
memory_total_gb: sys.total_memory() / 1024 / 1024 / 1024,
memory_used_gb: sys.used_memory() / 1024 / 1024 / 1024,
os_version: System::long_os_version().unwrap_or("Unknown".to_string()),
bios_version: bios_ver,
c_drive_total_gb: c_total,
c_drive_used_gb: c_used,
};
// 发送硬件事件
let _ = window.emit("report-hardware", hardware);
}
// WMI 连接复用 (如果需要) 或者重新建立
let wmi_con = WMIConnection::new(COMLibrary::new().unwrap()).ok();
// 1. 硬件基础信息
let mut bios_ver = "Unknown".to_string();
let mut mobo_vendor = "Unknown".to_string();
let mut mobo_product = "Unknown".to_string();
let mut sys_vendor = "Unknown".to_string();
let mut sys_product = "Unknown".to_string();
if let Some(con) = &wmi_con {
if let Ok(results) = con.raw_query::<Win32_BIOS>("SELECT SMBIOSBIOSVersion FROM Win32_BIOS") {
if let Some(bios) = results.first() {
bios_ver = bios.SMBIOSBIOSVersion.clone().unwrap_or("Unknown".to_string());
}
}
if let Ok(results) = con.raw_query::<Win32_BaseBoard>("SELECT Manufacturer, Product FROM Win32_BaseBoard") {
if let Some(board) = results.first() {
mobo_vendor = board.Manufacturer.clone().unwrap_or("Unknown".to_string());
mobo_product = board.Product.clone().unwrap_or("Unknown".to_string());
}
}
if let Ok(results) = con.raw_query::<Win32_ComputerSystem>("SELECT Manufacturer, Model FROM Win32_ComputerSystem") {
if let Some(cs) = results.first() {
sys_vendor = cs.Manufacturer.clone().unwrap_or("Unknown".to_string());
sys_product = cs.Model.clone().unwrap_or("Unknown".to_string());
}
}
}
// --- C 盘空间检查 ---
let mut c_total = 0u64;
let mut c_used = 0u64;
let disks = Disks::new_with_refreshed_list();
for disk in &disks {
if disk.mount_point().to_string_lossy().starts_with("C:") {
c_total = disk.total_space() / 1024 / 1024 / 1024;
let free = disk.available_space() / 1024 / 1024 / 1024;
c_used = c_total - free;
break;
}
}
let cpu_brand = if let Some(cpu) = sys.cpus().first() {
cpu.brand().trim().to_string()
} else {
"Unknown CPU".to_string()
};
let hardware = HardwareSummary {
cpu_name: cpu_brand,
mobo_vendor,
mobo_product,
sys_vendor,
sys_product,
memory_total_gb: sys.total_memory() / 1024 / 1024 / 1024,
memory_used_gb: sys.used_memory() / 1024 / 1024 / 1024,
os_version: System::long_os_version().unwrap_or("Unknown".to_string()),
bios_version: bios_ver,
c_drive_total_gb: c_total,
c_drive_used_gb: c_used,
};
// 2. 存储设备健康度
let mut storage = Vec::new();
if let Some(con) = &wmi_con {
let query = "SELECT Model, Status FROM Win32_DiskDrive";
if let Ok(results) = con.raw_query::<Win32_DiskDrive>(query) {
for disk in results {
let status = disk.Status.unwrap_or("Unknown".to_string());
let (explanation, is_danger) = match status.as_str() {
"OK" => ("健康".to_string(), false),
"Pred Fail" => ("预测即将损坏".to_string(), true),
_ => ("状态异常".to_string(), true),
};
storage.push(StorageDevice {
model: disk.Model.unwrap_or("Generic Disk".to_string()),
health_status: status,
human_explanation: explanation,
is_danger,
});
}
}
}
// 3. 关键日志 (严格模式 + 时间优化 + 扩展)
let mut events = Vec::new();
if let Some(con) = &wmi_con {
// [关键优化] 计算30天前的时间字符串
let start_time_str = get_wmi_query_time(30);
// [关键优化] SQL 中加入 TimeGenerated >= '...' 过滤,大幅减少扫描量
let query = format!(
"SELECT TimeGenerated, EventCode, SourceName, Message FROM Win32_NTLogEvent WHERE Logfile = 'System' AND TimeGenerated >= '{}' AND (EventCode = 41 OR EventCode = 18 OR EventCode = 19 OR EventCode = 7 OR EventCode = 1001 OR EventCode = 4101)",
start_time_str
);
if let Ok(results) = con.raw_query::<Win32_NTLogEvent>(&query) {
for event in results {
let mut is_target_event = false;
let mut hint = String::new();
// 严格来源校验
if event.EventCode == 41 && event.SourceName == "Microsoft-Windows-Kernel-Power" {
is_target_event = true;
hint = "系统意外断电 (电源/强关)".to_string();
} else if (event.EventCode == 18 || event.EventCode == 19) && event.SourceName == "Microsoft-Windows-WHEA-Logger" {
is_target_event = true;
hint = "WHEA 硬件致命错误 (CPU/超频/PCIe)".to_string();
} else if event.EventCode == 7 && (event.SourceName == "Disk" || event.SourceName == "disk") {
is_target_event = true;
hint = "硬盘出现坏道 (Disk Bad Block)".to_string();
} else if event.EventCode == 1001 && event.SourceName == "BugCheck" {
is_target_event = true;
hint = "系统发生蓝屏死机 (BSOD)".to_string();
} else if event.EventCode == 4101 && event.SourceName == "Display" {
is_target_event = true;
hint = "显卡驱动停止响应并已恢复 (TDR)".to_string();
}
if is_target_event {
events.push(SystemEvent {
time_generated: format_wmi_time(&event.TimeGenerated),
event_id: event.EventCode,
source: event.SourceName,
message: event.Message.unwrap_or("无详细信息".to_string()),
analysis_hint: hint,
});
}
// 显示最近 10 条(因为加了时间范围,可以多显示一点)
if events.len() >= 10 {
break;
}
}
}
}
// 4. Minidump
let mut minidump = MinidumpInfo { found: false, count: 0, explanation: "无蓝屏记录".to_string() };
if let Ok(entries) = fs::read_dir("C:\\Windows\\Minidump") {
let count = entries.count();
if count > 0 {
minidump = MinidumpInfo {
found: true,
count,
explanation: format!("发现 {} 次蓝屏崩溃", count),
};
}
}
// 5. 驱动设备检查
let mut driver_issues = Vec::new();
if let Some(con) = &wmi_con {
let query = "SELECT Name, ConfigManagerErrorCode FROM Win32_PnPEntity WHERE ConfigManagerErrorCode <> 0";
if let Ok(results) = con.raw_query::<Win32_PnPEntity>(query) {
for dev in results {
let code = dev.ConfigManagerErrorCode.unwrap_or(0);
let desc = match code {
10 => "设备无法启动 (Code 10)。通常是驱动不兼容。",
28 => "驱动程序未安装 (Code 28)。",
43 => "硬件报告问题已被停止 (Code 43)。显卡常见,可能虚焊。",
_ => "设备状态异常。",
};
driver_issues.push(DriverIssue {
device_name: dev.Name.unwrap_or("未知设备".to_string()),
error_code: code,
description: desc.to_string(),
});
}
}
}
// 6. 电池健康度
let mut battery_info = None;
if let Some(con) = &wmi_con {
if let Ok(results) = con.raw_query::<Win32_Battery>("SELECT DesignCapacity, FullChargeCapacity, BatteryStatus FROM Win32_Battery") {
if let Some(bat) = results.first() {
let design = bat.DesignCapacity.unwrap_or(0);
let full = bat.FullChargeCapacity.unwrap_or(0);
let status = bat.BatteryStatus.unwrap_or(0);
if design > 0 {
let health = ((full as f64 / design as f64) * 100.0) as u32;
let ac_plugged = status == 2 || status == 6 || status == 1;
let explain = if health < 60 {
"电池老化严重,建议更换,否则可能导致供电不稳。".to_string()
} else {
"电池状态良好。".to_string()
// 2. 存储设备 (较快)
{
let mut storage = Vec::new();
if let Some(con) = &wmi_con {
let query = "SELECT Model, Status FROM Win32_DiskDrive";
if let Ok(results) = con.raw_query::<Win32_DiskDrive>(query) {
for disk in results {
let status = disk.Status.unwrap_or("Unknown".to_string());
let (explanation, is_danger) = match status.as_str() {
"OK" => ("健康".to_string(), false),
"Pred Fail" => ("预测即将损坏".to_string(), true),
_ => ("状态异常".to_string(), true),
};
battery_info = Some(BatteryInfo {
health_percentage: health,
is_ac_connected: ac_plugged,
explanation: explain,
storage.push(StorageDevice {
model: disk.Model.unwrap_or("Generic Disk".to_string()),
health_status: status,
human_explanation: explanation,
is_danger,
});
}
}
}
let _ = window.emit("report-storage", storage);
}
SystemHealthReport {
hardware,
storage,
events,
minidumps: minidump,
drivers: driver_issues,
battery: battery_info
// 3. 驱动 (快)
{
let mut driver_issues = Vec::new();
if let Some(con) = &wmi_con {
let query = "SELECT Name, ConfigManagerErrorCode FROM Win32_PnPEntity WHERE ConfigManagerErrorCode <> 0";
if let Ok(results) = con.raw_query::<Win32_PnPEntity>(query) {
for dev in results {
let code = dev.ConfigManagerErrorCode.unwrap_or(0);
let desc = match code {
10 => "设备无法启动 (Code 10)。通常是驱动不兼容。",
28 => "驱动程序未安装 (Code 28)。",
43 => "硬件报告问题已被停止 (Code 43)。显卡常见,可能虚焊。",
_ => "设备状态异常。",
};
driver_issues.push(DriverIssue {
device_name: dev.Name.unwrap_or("未知设备".to_string()),
error_code: code,
description: desc.to_string(),
});
}
}
}
let _ = window.emit("report-drivers", driver_issues);
}
})
.await
.map_err(|e| e.to_string())?;
Ok(report)
// 4. Minidump (快)
{
let mut minidump = MinidumpInfo { found: false, count: 0, explanation: "无蓝屏记录".to_string() };
if let Ok(entries) = fs::read_dir("C:\\Windows\\Minidump") {
let count = entries.count();
if count > 0 {
minidump = MinidumpInfo {
found: true,
count,
explanation: format!("发现 {} 次蓝屏崩溃", count),
};
}
}
let _ = window.emit("report-minidumps", minidump);
}
// 5. 电池 (快)
{
let mut battery_info = None;
if let Some(con) = &wmi_con {
if let Ok(results) = con.raw_query::<Win32_Battery>("SELECT DesignCapacity, FullChargeCapacity, BatteryStatus FROM Win32_Battery") {
if let Some(bat) = results.first() {
let design = bat.DesignCapacity.unwrap_or(0);
let full = bat.FullChargeCapacity.unwrap_or(0);
let status = bat.BatteryStatus.unwrap_or(0);
if design > 0 {
let health = ((full as f64 / design as f64) * 100.0) as u32;
let ac_plugged = status == 2 || status == 6 || status == 1;
let explain = if health < 60 {
"电池老化严重,建议更换,否则可能导致供电不稳。".to_string()
} else {
"电池状态良好。".to_string()
};
battery_info = Some(BatteryInfo {
health_percentage: health,
is_ac_connected: ac_plugged,
explanation: explain,
});
}
}
}
}
// 即使是 None 也要发,以便前端知道检查完成了
// 这里为了简化,直接发 Option
let _ = window.emit("report-battery", battery_info);
}
// 6. 日志 (最慢,放在最后)
{
let mut events = Vec::new();
if let Some(con) = &wmi_con {
let start_time_str = get_wmi_query_time(30);
let query = format!(
"SELECT TimeGenerated, EventCode, SourceName, Message FROM Win32_NTLogEvent WHERE Logfile = 'System' AND TimeGenerated >= '{}' AND (EventCode = 41 OR EventCode = 18 OR EventCode = 19 OR EventCode = 7 OR EventCode = 1001 OR EventCode = 4101)",
start_time_str
);
if let Ok(results) = con.raw_query::<Win32_NTLogEvent>(&query) {
for event in results {
let mut is_target_event = false;
let mut hint = String::new();
if event.EventCode == 41 && event.SourceName == "Microsoft-Windows-Kernel-Power" {
is_target_event = true; hint = "系统意外断电 (电源/强关)".to_string();
} else if (event.EventCode == 18 || event.EventCode == 19) && event.SourceName == "Microsoft-Windows-WHEA-Logger" {
is_target_event = true; hint = "WHEA 硬件致命错误 (CPU/超频/PCIe)".to_string();
} else if event.EventCode == 7 && (event.SourceName == "Disk" || event.SourceName == "disk") {
is_target_event = true; hint = "硬盘出现坏道 (Disk Bad Block)".to_string();
} else if event.EventCode == 1001 && event.SourceName == "BugCheck" {
is_target_event = true; hint = "系统发生蓝屏死机 (BSOD)".to_string();
} else if event.EventCode == 4101 && event.SourceName == "Display" {
is_target_event = true; hint = "显卡驱动停止响应并已恢复 (TDR)".to_string();
}
if is_target_event {
events.push(SystemEvent {
time_generated: format_wmi_time(&event.TimeGenerated),
event_id: event.EventCode,
source: event.SourceName,
message: event.Message.unwrap_or("无详细信息".to_string()),
analysis_hint: hint,
});
}
if events.len() >= 10 { break; }
}
}
}
let _ = window.emit("report-events", events);
}
// 7. 全部完成信号
let _ = window.emit("diagnosis-finished", ());
});
Ok(())
}
fn main() {