From aae527258a3f0d4ba843503a5b675685c6595441 Mon Sep 17 00:00:00 2001 From: zqm Date: Thu, 9 Apr 2026 14:08:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=86=E9=98=B6=E6=AE=B5=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Windows/CS/Framework4.0/Updater/src/main.rs | 754 ++++++++++---------- 1 file changed, 365 insertions(+), 389 deletions(-) diff --git a/Windows/CS/Framework4.0/Updater/src/main.rs b/Windows/CS/Framework4.0/Updater/src/main.rs index 841c36f..83e9e5f 100644 --- a/Windows/CS/Framework4.0/Updater/src/main.rs +++ b/Windows/CS/Framework4.0/Updater/src/main.rs @@ -2,6 +2,16 @@ use rand::Rng; use std::collections::{HashMap, HashSet}; use std::sync::{Arc, Mutex}; +// ===================== 更新阶段枚举 ===================== +/// 更新流程的三个阶段 +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum UpdatePhase { + BootLoader, // 阶段1:检查 BootLoader.exe + Updater, // 阶段2:检查 Updater.exe + Apps, // 阶段3:检查其他应用 + Complete, // 所有阶段完成 +} + // ===================== 更新上下文(局部状态 + Arc 共享)===================== /// 更新流程的状态上下文 /// 使用 Mutex 实现 Sync(即使单线程也需要满足 Rust 的线程安全约束) @@ -14,6 +24,10 @@ struct UpdateContext { app_completed_set: Mutex>, /// Updater.exe 的服务端版本号 server_updater_version: Mutex>, + /// 当前更新阶段 + current_phase: Mutex, + /// 候选应用列表(阶段3使用) + candidates: Mutex>, } impl Default for UpdateContext { @@ -23,6 +37,8 @@ impl Default for UpdateContext { app_download_map: Mutex::new(HashMap::new()), app_completed_set: Mutex::new(HashSet::new()), server_updater_version: Mutex::new(None), + current_phase: Mutex::new(UpdatePhase::BootLoader), + candidates: Mutex::new(Vec::new()), } } } @@ -897,268 +913,6 @@ fn handle_app_download_complete( Some((app_name.to_string(), filename.to_string())) } -/// 批量查询所有候选应用的版本号 -fn build_get_file_ver_request(app_names: &[String]) -> Option { - if app_names.is_empty() { - return None; - } - - let mut file_list = Vec::new(); - for name in app_names { - file_list.push(format!("{}.exe", name)); - } - - let file_list_json = serde_json::to_string(&file_list).ok()?; - Some(format!( - r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":{}}}}}"#, - resolve_device_number(), - file_list_json - )) -} - -/// 检查并更新其他应用程序 -/// 返回是否执行了更新 -async fn check_other_apps_updates( - debug: bool, - _shutdown_tx_arc: std::sync::Arc>>>, -) -> bool { - // 创建本次运行的上下文 - let ctx = Arc::new(UpdateContext::default()); - let ctx_clone = ctx.clone(); - - // 标记本次运行是否对其他应用执行了更新 - let other_app_updated = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); - let other_app_updated_clone = other_app_updated.clone(); - - // 1. 扫描候选应用 - let candidates = get_app_candidates(debug); - if candidates.is_empty() { - if debug { - println!("[应用] 没有正在运行的候选应用,跳过检查"); - } - return false; - } - - if debug { - println!("[应用] 找到 {} 个候选应用: {:?}", candidates.len(), candidates); - } - - // 2. 解析服务器地址 - let server_url = resolve_ws_url(); - let device_number = resolve_device_number(); - - if debug { - println!("[应用] 服务器地址: {}", server_url); - println!("[应用] 设备编号: {}", device_number); - } - - // 3. 创建 WebSocket 连接 - let config = WebSocketConfig::new(&server_url) - .with_client_type("Updater") - .with_debug(debug) - .with_reconnect(false); // 应用更新只查一次,不重连 - - let mut client = WebSocketClient::new(config); - - // 用于存储每个应用是否需要更新的标志 - let apps_to_update = std::sync::Arc::new(std::sync::Mutex::new(Vec::::new())); - let apps_to_update_clone = apps_to_update.clone(); - - // 文件版本响应处理器 - let file_ver_received = std::sync::Arc::new(std::sync::Mutex::new(false)); - let file_ver_clone = file_ver_received.clone(); - - // 设置消息回调 - let debug_msg = debug; - let candidates_clone = candidates.clone(); - client.on_message(move |msg_type, data, sender| { - if msg_type == "welcome" { - // 发送批量版本查询 - if let Some(req) = build_get_file_ver_request(&candidates_clone) { - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [应用] 发送: {}", ts, req); - } - sender.send(req); - } - } - - if msg_type == "FileVer" { - *file_ver_clone.lock().unwrap() = true; - if let Some(file_versions) = data.get("Data").and_then(|d| d.get("file_versions")).and_then(|v| v.as_object()) { - for app_name in &candidates_clone { - let exe_name = format!("{}.exe", app_name); - if let Some(server_ver) = file_versions.get(&exe_name) { - let server_version = server_ver.as_str().unwrap_or("0.0.0"); - let local_version = get_local_app_version(app_name, &exe_name, debug_msg); - - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [应用] {}: 服务端={}, 本地={}", ts, app_name, server_version, local_version); - } - - if local_version == "0.0.0" || version_less_than(&local_version, server_version) { - apps_to_update_clone.lock().unwrap().push(app_name.clone()); - - // 扫描升级目录 - let upgrade_files = scan_upgrade_dir(app_name, debug_msg); - if upgrade_files.is_empty() { - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [应用] {} 没有升级文件,跳过", ts, app_name); - } - continue; - } - - // 对每个文件发起下载(检查断点续传) - for (rel_path, _) in upgrade_files { - let tmp_size = get_app_tmp_file_size(app_name, &rel_path); - if tmp_size > 0 { - // 有临时文件,先 md5 校验 - request_file_md5_for_app(&sender, app_name, &rel_path, tmp_size, debug_msg); - } else { - // 无临时文件,从头下载 - request_download_for_app(&sender, &rel_path, 0, debug_msg); - } - other_app_updated_clone.store(true, std::sync::atomic::Ordering::SeqCst); - } - } - } - } - } - } - - // 处理 md5 响应(其他应用) - if msg_type == "Md5" { - if let Some(md5_data) = data.get("Data").and_then(|v| v.as_object()) { - // Md5 响应的 filename 是相对路径,需要匹配当前查询的应用 - // 注意:这里无法知道是哪个应用的 md5,需要从 candidates 逐一尝试 - // 简化处理:遍历 candidates 找匹配的相对路径 - for app_name in &candidates_clone { - let rel_path = md5_data.get("filename").and_then(|v| v.as_str()).unwrap_or(""); - if rel_path.is_empty() { - continue; - } - - // 检查这个 app 的升级目录是否有这个文件 - let upgrade_path = get_updater_data_dir() - .join("Updater") - .join("UpGrade") - .join(app_name) - .join(rel_path); - if !upgrade_path.exists() { - continue; - } - - let server_md5 = md5_data.get("md5").and_then(|v| v.as_str()).unwrap_or(""); - let bytes = md5_data.get("bytes").and_then(|v| v.as_u64()).unwrap_or(0); - - // 计算本地 tmp 文件的 md5 - let tmp_filename = format!("{}.tmp", rel_path); - let local_md5 = compute_file_hash_in_dir(app_name, &tmp_filename, bytes, debug_msg); - - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - if let Some(ref lm) = local_md5 { - println!("{} [应用] {} md5: 本地={}, 服务端={}", ts, rel_path, lm, server_md5); - } - } - - if local_md5.as_deref() == Some(server_md5) { - request_download_for_app(&sender, rel_path, bytes, debug_msg); - } else { - let tmp_path = get_updater_data_dir() - .join("Updater") - .join("UpGrade") - .join(app_name) - .join(&tmp_filename); - let _ = fs::remove_file(&tmp_path); - request_download_for_app(&sender, rel_path, 0, debug_msg); - } - break; // 找到匹配的应用后跳出 - } - } - } - - // 处理文件块(其他应用) - if msg_type == "FileChunk" { - if let Some(data_obj) = data.get("Data").and_then(|v| v.as_object()) { - // 遍历所有候选应用,找匹配的相对路径 - for app_name in &candidates_clone { - let filename = data_obj.get("filename").and_then(|v| v.as_str()).unwrap_or(""); - let upgrade_path = get_updater_data_dir() - .join("Updater") - .join("UpGrade") - .join(app_name) - .join(filename); - if !upgrade_path.exists() { - continue; - } - - let completed = handle_app_file_chunk(&ctx_clone, app_name, data_obj, debug_msg); - if let Some((app, file)) = completed { - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [应用] {} / {} 下载完成", ts, app, file); - } - ctx_clone.app_completed_set.lock().unwrap().insert(format!("{}/{}", app, file)); - } - break; - } - } - } - - // 处理下载完成(其他应用) - if msg_type == "DownloadComplete" { - if let Some(data_obj) = data.get("Data").and_then(|v| v.as_object()) { - let filename = data_obj.get("filename").and_then(|v| v.as_str()).unwrap_or(""); - if filename.is_empty() { - return; - } - - for app_name in &candidates_clone { - let upgrade_path = get_updater_data_dir() - .join("Updater") - .join("UpGrade") - .join(app_name) - .join(filename); - if !upgrade_path.exists() { - continue; - } - - let size = data_obj.get("size").and_then(|v| v.as_u64()).unwrap_or(0); - handle_app_download_complete(&ctx_clone, app_name, filename, size, debug_msg); - ctx_clone.app_completed_set.lock().unwrap().insert(format!("{}/{}", app_name, filename)); - break; - } - } - } - }); - - // 4. 连接并等待版本查询完成 - if debug { - println!("[应用] 开始连接服务器查询版本..."); - } - - client.connect().await; - - // 等待版本响应(最多等 10 秒) - let start = std::time::Instant::now(); - while start.elapsed().as_secs() < 10 { - if *file_ver_received.lock().unwrap() { - // 收到 FileVer 后,等待下载完成 - tokio::time::sleep(std::time::Duration::from_secs(3)).await; - break; - } - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - } - - if debug { - println!("[应用] 版本查询/下载流程结束"); - } - - other_app_updated.load(std::sync::atomic::Ordering::SeqCst) -} /// 只负责 Updater 自己的行为参数,连接地址从公共 config.json 加载 #[derive(Debug, Serialize, Deserialize)] struct Config { @@ -1417,11 +1171,24 @@ async fn run_updater(debug_mode: bool) -> bool { // 加载初始 URL let server_url = resolve_ws_url(); + // 扫描候选应用(阶段3使用) + let app_candidates = get_app_candidates(debug_mode); + + // 将候选应用保存到 ctx + *ctx.candidates.lock().unwrap() = app_candidates.clone(); + + // 初始化阶段为 BootLoader + *ctx.current_phase.lock().unwrap() = UpdatePhase::BootLoader; + if debug_mode { println!("========================================"); println!("Updater 启动 (调试模式)"); println!("服务器地址: {}", server_url); println!("自动重连: 启用 (指数退避: 1s - 30s)"); + println!("更新阶段: BootLoader -> Updater -> Apps -> Complete"); + if !app_candidates.is_empty() { + println!("候选应用: {:?}", app_candidates); + } println!("========================================"); } @@ -1467,113 +1234,228 @@ async fn run_updater(debug_mode: bool) -> bool { ); } - // 收到 welcome 后,发送 GetFileVer(同时请求 BootLoader 和 Updater) + // 收到 welcome 后,根据当前阶段发送 GetFileVer if msg_type == "welcome" { if !device_number.is_empty() && device_number != "UNKNOWN" { - let msg_str = format!( - r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["BootLoader.exe","Updater.exe"]}}}}"#, - device_number - ); - if debug_msg { - let ts2 = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} 发送消息:{}", ts2, msg_str); + let current_phase = *ctx_clone.current_phase.lock().unwrap(); + let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); + + match current_phase { + UpdatePhase::BootLoader => { + // 阶段1:只请求 BootLoader.exe + println!("{} [阶段1] 检查 BootLoader...", ts); + let msg_str = format!( + r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["BootLoader.exe"]}}}}"#, + device_number + ); + if debug_msg { + println!("{} 发送消息:{}", ts, msg_str); + } + sender.send(msg_str); + } + _ => { + // 其他阶段由 FileVer 响应后推进,不在这里处理 + } } - sender.send(msg_str); } } - // 处理 FileVer 响应 + // 处理 FileVer 响应(根据当前阶段分发) if msg_type == "FileVer" { if let Some(file_versions) = data.get("Data").and_then(|d| d.get("file_versions")).and_then(|v| v.as_object()) { - // 第一步:先遍历保存所有版本号 - for (filename, server_ver) in file_versions { - let server_version = server_ver.as_str().unwrap_or("0.0.0"); - // 保存 Updater.exe 的服务端版本号 - if filename == "Updater.exe" { - *ctx_clone.server_updater_version.lock().unwrap() = Some(server_version.to_string()); - } - } + let current_phase = *ctx_clone.current_phase.lock().unwrap(); + let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - // 第二步:再遍历处理每个文件 - let mut download_initiated = false; - for (filename, server_ver) in file_versions { - let server_version = server_ver.as_str().unwrap_or("0.0.0"); - let local_version = get_local_file_version(filename); - let tmp_size = get_tmp_file_size(filename); + match current_phase { + UpdatePhase::BootLoader => { + // ========== 阶段1:处理 BootLoader ========== + if let Some(server_ver) = file_versions.get("BootLoader.exe") { + let server_version = server_ver.as_str().unwrap_or("0.0.0"); + let local_version = get_local_file_version("BootLoader.exe"); + let tmp_size = get_tmp_file_size("BootLoader.exe"); - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [版本] {}: 服务端={}, 本地={}, tmp大小={}", - ts, filename, server_version, local_version, tmp_size); - } + println!("{} [版本] BootLoader.exe: 服务端={}, 本地={}", ts, server_version, local_version); - // 比较版本:如果本地不存在或比服务端旧,则下载 - let need_update = local_version == "0.0.0" || version_less_than(&local_version, server_version); - if need_update { - if tmp_size > 0 { - // 有临时文件,请求 hash 校验 - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [续传] {} 发现未完成下载,请求 hash 校验...", ts, filename); + let need_update = local_version == "0.0.0" || version_less_than(&local_version, server_version); + if need_update { + if tmp_size > 0 { + println!("{} [续传] BootLoader.exe 发现未完成下载,请求 hash 校验...", ts); + request_file_md5(&sender, "BootLoader.exe", tmp_size, debug_msg); + } else { + println!("{} [升级] BootLoader.exe 需要更新,开始下载...", ts); + request_download(&sender, "BootLoader.exe", 0, debug_msg); + } + update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + } else { + println!("{} [版本] BootLoader.exe 版本已是最新", ts); + // 推进到阶段2 + let next_phase = UpdatePhase::Updater; + *ctx_clone.current_phase.lock().unwrap() = next_phase; + println!("{} [阶段2] 检查 Updater...", ts); + + let msg_str = format!( + r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["Updater.exe"]}}}}"#, + device_number + ); + sender.send(msg_str); } - request_file_md5(&sender, filename, tmp_size, debug_msg); - update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst); - download_initiated = true; } else { - // 无临时文件,从头下载 - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [升级] {} 需要更新,开始下载...", ts, filename); - } - request_download(&sender, filename, 0, debug_msg); - update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst); - download_initiated = true; - } - } else { - // 不需要更新 - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [版本] {} 版本已是最新,无需更新", ts, filename); - } + // BootLoader 不在响应中,推进到阶段2 + *ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Updater; + println!("{} [阶段2] 检查 Updater...", ts); - // 对于 Updater.exe,检查是否有 Updater.new.exe - if filename == "Updater.exe" { - if has_updater_new_exe() { - // 删除旧版 Updater.new.exe(因为会重新下载) - let updater_new_path = get_updater_data_dir().join("Updater.new.exe"); - let _ = fs::remove_file(&updater_new_path); - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); + let msg_str = format!( + r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["Updater.exe"]}}}}"#, + device_number + ); + sender.send(msg_str); + } + } + + UpdatePhase::Updater => { + // ========== 阶段2:处理 Updater ========== + if let Some(server_ver) = file_versions.get("Updater.exe") { + let server_version = server_ver.as_str().unwrap_or("0.0.0"); + let local_version = get_local_file_version("Updater.exe"); + let tmp_size = get_tmp_file_size("Updater.exe"); + + // 保存 Updater.exe 的服务端版本号 + *ctx_clone.server_updater_version.lock().unwrap() = Some(server_version.to_string()); + + println!("{} [版本] Updater.exe: 服务端={}, 本地={}", ts, server_version, local_version); + + let need_update = local_version == "0.0.0" || version_less_than(&local_version, server_version); + if need_update { + if tmp_size > 0 { + println!("{} [续传] Updater.exe 发现未完成下载,请求 hash 校验...", ts); + request_file_md5(&sender, "Updater.exe", tmp_size, debug_msg); + } else { + println!("{} [升级] Updater.exe 需要更新,开始下载...", ts); + request_download(&sender, "Updater.exe", 0, debug_msg); + } + update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + } else { + println!("{} [版本] Updater.exe 版本已是最新", ts); + // 检查是否有旧的 Updater.new.exe + if has_updater_new_exe() { + let updater_new_path = get_updater_data_dir().join("Updater.new.exe"); + let _ = fs::remove_file(&updater_new_path); println!("{} [清理] 删除旧的 Updater.new.exe", ts); } + + // 推进到阶段3 + let candidates = ctx_clone.candidates.lock().unwrap().clone(); + if candidates.is_empty() { + // 没有候选应用,所有阶段完成 + *ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Complete; + println!("{} [阶段3] 没有候选应用,所有阶段完成", ts); + update_check_done_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + // 发送断连信号 + if let Some(tx) = shutdown_tx_arc_clone.lock().unwrap().take() { + let _ = tx.send(()); + } + } else { + *ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Apps; + println!("{} [阶段3] 检查应用: {:?}", ts, candidates); + + // 构建应用版本查询 + let mut file_list = Vec::new(); + for app in &candidates { + file_list.push(format!("{}.exe", app)); + } + let file_list_json = serde_json::to_string(&file_list).unwrap_or_else(|_| "[]".to_string()); + let msg_str = format!( + r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":{}}}}}"#, + device_number, file_list_json + ); + sender.send(msg_str); + } + } + } else { + // Updater 不在响应中,推进到阶段3 + let candidates = ctx_clone.candidates.lock().unwrap().clone(); + if candidates.is_empty() { + *ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Complete; + println!("{} [阶段3] 没有候选应用,所有阶段完成", ts); + update_check_done_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + if let Some(tx) = shutdown_tx_arc_clone.lock().unwrap().take() { + let _ = tx.send(()); + } + } else { + *ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Apps; + println!("{} [阶段3] 检查应用: {:?}", ts, candidates); + + let mut file_list = Vec::new(); + for app in &candidates { + file_list.push(format!("{}.exe", app)); + } + let file_list_json = serde_json::to_string(&file_list).unwrap_or_else(|_| "[]".to_string()); + let msg_str = format!( + r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":{}}}}}"#, + device_number, file_list_json + ); + sender.send(msg_str); + } + } + } + + UpdatePhase::Apps => { + // ========== 阶段3:处理应用 ========== + let candidates = ctx_clone.candidates.lock().unwrap().clone(); + let mut apps_to_update = Vec::new(); + + for app_name in &candidates { + let exe_name = format!("{}.exe", app_name); + if let Some(server_ver) = file_versions.get(&exe_name) { + let server_version = server_ver.as_str().unwrap_or("0.0.0"); + let local_version = get_local_app_version(app_name, &exe_name, debug_msg); + + println!("{} [应用] {}: 服务端={}, 本地={}", ts, app_name, server_version, local_version); + + if local_version == "0.0.0" || version_less_than(&local_version, server_version) { + apps_to_update.push(app_name.clone()); + } } } - // 标记 BootLoader 已"下载完成"(无需更新) - if filename == "BootLoader.exe" { - bootloader_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst); - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [升级] BootLoader.exe 版本已是最新", ts); + // 处理需要更新的应用 + let mut has_downloads = false; + for app_name in &apps_to_update { + let upgrade_files = scan_upgrade_dir(app_name, debug_msg); + if upgrade_files.is_empty() { + println!("{} [应用] {} 没有升级文件,跳过", ts, app_name); + continue; + } + + for (rel_path, _) in upgrade_files { + let tmp_size = get_app_tmp_file_size(app_name, &rel_path); + if tmp_size > 0 { + request_file_md5_for_app(&sender, app_name, &rel_path, tmp_size, debug_msg); + } else { + request_download_for_app(&sender, &rel_path, 0, debug_msg); + } + update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + has_downloads = true; } } - } - } - // 循环结束后:没有发起下载 → 全部已是最新 - if !download_initiated { - update_check_done_clone2.store(true, std::sync::atomic::Ordering::SeqCst); - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [升级] 所有文件已是最新版本,等待下次检查...", ts); + // 所有阶段完成 + if !has_downloads { + println!("{} [阶段完成] 所有阶段检查完成,等待下次检查...", ts); + } else { + println!("{} [阶段完成] 应用下载进行中...", ts); + } + *ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Complete; + update_check_done_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + + // 发送断连信号 + if let Some(tx) = shutdown_tx_arc_clone.lock().unwrap().take() { + let _ = tx.send(()); + } } - // 主动发送断连信号,让 run_updater 返回主循环 - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - if debug_msg { - println!("{} [连接] 主动断开连接,返回主循环...", ts); - } - if let Some(tx) = shutdown_tx_arc_clone.lock().unwrap().take() { - let _ = tx.send(()); + + UpdatePhase::Complete => { + // 已在 Complete 阶段,不再处理 } } } @@ -1583,6 +1465,56 @@ async fn run_updater(debug_mode: bool) -> bool { if msg_type == "Md5" { if let Some(md5_data) = data.get("Data").and_then(|v| v.as_object()) { let filename = md5_data.get("filename").and_then(|v| v.as_str()).unwrap_or(""); + if filename.is_empty() { + return; + } + + let current_phase = *ctx_clone.current_phase.lock().unwrap(); + + // ========== 阶段3:处理应用 Md5 ========== + if current_phase == UpdatePhase::Apps { + // 遍历候选应用,找匹配的相对路径 + let candidates = ctx_clone.candidates.lock().unwrap().clone(); + for app_name in &candidates { + let upgrade_path = get_updater_data_dir() + .join("Updater") + .join("UpGrade") + .join(app_name) + .join(filename); + if !upgrade_path.exists() { + continue; + } + + let server_md5 = md5_data.get("md5").and_then(|v| v.as_str()).unwrap_or(""); + let bytes = md5_data.get("bytes").and_then(|v| v.as_u64()).unwrap_or(0); + + let tmp_filename = format!("{}.tmp", filename); + let local_md5 = compute_file_hash_in_dir(app_name, &tmp_filename, bytes, debug_msg); + + if debug_msg { + let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); + if let Some(ref lm) = local_md5 { + println!("{} [应用] {} md5: 本地={}, 服务端={}", ts, filename, lm, server_md5); + } + } + + if local_md5.as_deref() == Some(server_md5) { + request_download_for_app(&sender, filename, bytes, debug_msg); + } else { + let tmp_path = get_updater_data_dir() + .join("Updater") + .join("UpGrade") + .join(app_name) + .join(&tmp_filename); + let _ = fs::remove_file(&tmp_path); + request_download_for_app(&sender, filename, 0, debug_msg); + } + break; + } + return; + } + + // ========== BootLoader/Updater 的 Md5 处理 ========== let server_md5 = md5_data.get("md5").and_then(|v| v.as_str()).unwrap_or(""); let bytes = md5_data.get("bytes").and_then(|v| v.as_u64()).unwrap_or(0); // Updater.exe 的临时文件名是 Updater.new.exe.tmp @@ -1628,17 +1560,47 @@ async fn run_updater(debug_mode: bool) -> bool { // 处理文件块 if msg_type == "FileChunk" { if let Some(data_obj) = data.get("Data").and_then(|v| v.as_object()) { - let completed = handle_file_chunk(&ctx_clone, data_obj, debug_msg); - // 只处理 Updater 下载完成(BootLoader 由 DownloadComplete 统一处理,避免重复) - if let Some(ref completed_file) = completed { - if completed_file == "Updater.new.exe" { - if !updater_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) { - updater_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + let filename = data_obj.get("filename").and_then(|v| v.as_str()).unwrap_or(""); + + let current_phase = *ctx_clone.current_phase.lock().unwrap(); + + // ========== 阶段3:处理应用 FileChunk ========== + if current_phase == UpdatePhase::Apps && !filename.is_empty() { + let candidates = ctx_clone.candidates.lock().unwrap().clone(); + for app_name in &candidates { + let upgrade_path = get_updater_data_dir() + .join("Updater") + .join("UpGrade") + .join(app_name) + .join(filename); + if !upgrade_path.exists() { + continue; + } + + let completed = handle_app_file_chunk(&ctx_clone, app_name, data_obj, debug_msg); + if let Some((app, file)) = completed { if debug_msg { let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [升级] Updater.new.exe 下载完成(FileChunk),准备启动 BootLoader...", ts); + println!("{} [应用] {} / {} 下载完成", ts, app, file); + } + ctx_clone.app_completed_set.lock().unwrap().insert(format!("{}/{}", app, file)); + } + break; + } + } else { + // ========== BootLoader/Updater 的 FileChunk 处理 ========== + let completed = handle_file_chunk(&ctx_clone, data_obj, debug_msg); + // 只处理 Updater 下载完成(BootLoader 由 DownloadComplete 统一处理,避免重复) + if let Some(ref completed_file) = completed { + if completed_file == "Updater.new.exe" { + if !updater_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) { + updater_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + if debug_msg { + let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); + println!("{} [升级] Updater.new.exe 下载完成(FileChunk),准备启动 BootLoader...", ts); + } + schedule_bootloader_launch(debug_msg, shutdown_tx_arc_clone.clone()); } - schedule_bootloader_launch(debug_msg, shutdown_tx_arc_clone.clone()); } } } @@ -1648,29 +1610,57 @@ async fn run_updater(debug_mode: bool) -> bool { // 处理下载完成(统一处理,避免 FileChunk 和 DownloadComplete 重复处理) if msg_type == "DownloadComplete" { if let Some(data_obj) = data.get("Data").and_then(|v| v.as_object()) { - let completed = handle_download_complete(&ctx_clone, data_obj, debug_msg); - if let Some(ref completed_file) = completed { - // BootLoader.exe 下载完成 - if completed_file == "BootLoader.exe" { - if !bootloader_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) { - bootloader_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst); - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [升级] BootLoader.exe 下载完成,开始下载 Updater.exe...", ts); - } - // 开始下载 Updater.exe - request_download(&sender, "Updater.exe", 0, debug_msg); + let filename = data_obj.get("filename").and_then(|v| v.as_str()).unwrap_or(""); + if filename.is_empty() { + return; + } + + let current_phase = *ctx_clone.current_phase.lock().unwrap(); + + // ========== 阶段3:处理应用 DownloadComplete ========== + if current_phase == UpdatePhase::Apps { + let candidates = ctx_clone.candidates.lock().unwrap().clone(); + for app_name in &candidates { + let upgrade_path = get_updater_data_dir() + .join("Updater") + .join("UpGrade") + .join(app_name) + .join(filename); + if !upgrade_path.exists() { + continue; } + + let size = data_obj.get("size").and_then(|v| v.as_u64()).unwrap_or(0); + handle_app_download_complete(&ctx_clone, app_name, filename, size, debug_msg); + ctx_clone.app_completed_set.lock().unwrap().insert(format!("{}/{}", app_name, filename)); + break; } - // Updater.new.exe 下载完成 - else if completed_file == "Updater.new.exe" { - if !updater_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) { - updater_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst); - if debug_msg { - let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [升级] Updater.new.exe 下载完成(DownloadComplete),准备启动 BootLoader...", ts); + } else { + // ========== BootLoader/Updater 的 DownloadComplete 处理 ========== + let completed = handle_download_complete(&ctx_clone, data_obj, debug_msg); + if let Some(ref completed_file) = completed { + // BootLoader.exe 下载完成 + if completed_file == "BootLoader.exe" { + if !bootloader_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) { + bootloader_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + if debug_msg { + let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); + println!("{} [升级] BootLoader.exe 下载完成,开始下载 Updater.exe...", ts); + } + // 开始下载 Updater.exe + request_download(&sender, "Updater.exe", 0, debug_msg); + } + } + // Updater.new.exe 下载完成 + else if completed_file == "Updater.new.exe" { + if !updater_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) { + updater_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + if debug_msg { + let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); + println!("{} [升级] Updater.new.exe 下载完成(DownloadComplete),准备启动 BootLoader...", ts); + } + schedule_bootloader_launch(debug_msg, shutdown_tx_arc.clone()); } - schedule_bootloader_launch(debug_msg, shutdown_tx_arc.clone()); } } } @@ -1692,7 +1682,7 @@ async fn run_updater(debug_mode: bool) -> bool { eprintln!("[错误] WebSocket: {}", error); }); - // 设置首次连接回调(GetFileVer 改到收到 welcome 后发送) + // 设置首次连接回调 let debug_first = debug_mode; let device_number_first = resolve_device_number(); client.on_first_connect(move |_url, _sender| { @@ -1818,20 +1808,6 @@ async fn main() { std::process::exit(0); } - // 检查其他应用的更新 - if config.debug_mode { - println!("[应用] 开始检查其他应用程序更新..."); - } - let dummy_shutdown_tx = std::sync::Arc::new(std::sync::Mutex::new(None::>)); - let other_updated = check_other_apps_updates(config.debug_mode, dummy_shutdown_tx).await; - if other_updated { - if config.debug_mode { - println!("[应用] 其他应用程序更新完成"); - } - } else if config.debug_mode { - println!("[应用] 其他应用程序无需更新"); - } - // 无更新 → 随机等待 5-10 分钟后再次检查 let wait_seconds = { let mut rng = rand::thread_rng();