From 1f9a6bbe0ffcbd5b3b6a38b49b2a22fdca2654f0 Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 8 Apr 2026 16:15:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0Updater?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Windows/CS/Framework4.0/Updater/Cargo.lock | 1 + Windows/CS/Framework4.0/Updater/Cargo.toml | 1 + Windows/CS/Framework4.0/Updater/src/main.rs | 154 ++++++++++++++------ 3 files changed, 108 insertions(+), 48 deletions(-) diff --git a/Windows/CS/Framework4.0/Updater/Cargo.lock b/Windows/CS/Framework4.0/Updater/Cargo.lock index 53734b2..d575b8b 100644 --- a/Windows/CS/Framework4.0/Updater/Cargo.lock +++ b/Windows/CS/Framework4.0/Updater/Cargo.lock @@ -11,6 +11,7 @@ dependencies = [ "cube_lib", "dirs", "md5", + "rand", "serde", "serde_json", "tokio", diff --git a/Windows/CS/Framework4.0/Updater/Cargo.toml b/Windows/CS/Framework4.0/Updater/Cargo.toml index aa14816..6d9b601 100644 --- a/Windows/CS/Framework4.0/Updater/Cargo.toml +++ b/Windows/CS/Framework4.0/Updater/Cargo.toml @@ -12,6 +12,7 @@ windows = { version = "0.56", features = ["Win32_System_Console"] } chrono = "0.4" base64 = "0.22" md5 = "0.7" +rand = "0.8" # Local CubeLib for WebSocket cube_lib = { path = "../../../Rust/CubeLib" } diff --git a/Windows/CS/Framework4.0/Updater/src/main.rs b/Windows/CS/Framework4.0/Updater/src/main.rs index 1539bab..0099474 100644 --- a/Windows/CS/Framework4.0/Updater/src/main.rs +++ b/Windows/CS/Framework4.0/Updater/src/main.rs @@ -1,3 +1,4 @@ +use rand::Rng; use serde::{Deserialize, Serialize}; use std::io::Read; use std::fs::{self, File}; @@ -39,6 +40,10 @@ static DOWNLOAD_STATE: std::sync::Mutex = std::sync::Mutex::new(D static BOOTLOADER_DOWNLOADED: std::sync::Mutex = std::sync::Mutex::new(false); static UPDATER_DOWNLOADED: std::sync::Mutex = std::sync::Mutex::new(false); static SERVER_UPDATER_VERSION: std::sync::Mutex> = std::sync::Mutex::new(None); +/// 标记一次更新检查是否已完成(用于主循环控制) +static UPDATE_CHECK_DONE: std::sync::Mutex = std::sync::Mutex::new(false); +/// 标记本次运行是否执行了更新(下载了文件) +static UPDATE_PERFORMED: std::sync::Mutex = std::sync::Mutex::new(false); // ===================== 版本比较与下载 ===================== /// 获取 Updater 数据目录(X:\AppData\,存放 BootLoader.exe 等) @@ -101,36 +106,9 @@ fn get_local_file_version(filename: &str) -> String { } /// 比较两个版本号(a < b 返回 true) +/// 使用 CubeLib 库的版本比较函数 fn version_less_than(a: &str, b: &str) -> bool { - // 按 '.' 分割版本号 - let parse_v = |v: &str| -> Vec { - v.split('.') - .filter(|s| !s.is_empty()) - .filter_map(|s| s.parse().ok()) - .collect() - }; - let a_parts: Vec = parse_v(a); - let b_parts: Vec = parse_v(b); - - eprintln!("[DEBUG] version_less_than: a={:?} -> {:?}", a, a_parts); - eprintln!("[DEBUG] version_less_than: b={:?} -> {:?}", b, b_parts); - - let max_len = a_parts.len().max(b_parts.len()); - - for i in 0..max_len { - let av = if i < a_parts.len() { a_parts[i] } else { 0 }; - let bv = if i < b_parts.len() { b_parts[i] } else { 0 }; - if av < bv { - eprintln!("[DEBUG] version_less_than: {}[{}] < {}[{}] = true", a, i, b, i); - return true; - } - if av > bv { - eprintln!("[DEBUG] version_less_than: {}[{}] > {}[{}] = false", a, i, b, i); - return false; - } - } - eprintln!("[DEBUG] version_less_than: {} >= {} = false", a, b); - false + cube_lib::version_less_than(a, b) } /// 计算本地文件前 N 字节的 MD5 hash(字节数为 0 表示全部) @@ -249,7 +227,11 @@ fn check_and_download_updater(sender: &cube_lib::websocket::MessageSender, devic } /// 安排启动 BootLoader.exe(延迟执行,等待所有下载完成) -fn schedule_bootloader_launch(debug: bool) { +/// 通过 shutdown_tx 发送断连信号,通知主循环优雅退出 +fn schedule_bootloader_launch( + debug: bool, + shutdown_tx_arc: std::sync::Arc>>>, +) { // 延迟 1 秒后启动,确保文件句柄已关闭 std::thread::spawn(move || { std::thread::sleep(std::time::Duration::from_secs(1)); @@ -285,13 +267,17 @@ fn schedule_bootloader_launch(debug: bool) { if debug { println!("{} [启动] BootLoader.exe 已启动,Updater 即将退出", ts); } - // 给自己发送退出信号(优雅退出) - std::process::exit(0); + // 发送断连信号,通知主循环优雅退出 + if let Some(tx) = shutdown_tx_arc.lock().unwrap().take() { + let _ = tx.send(()); + } } Err(e) => { eprintln!("{} [错误] 启动 BootLoader.exe 失败: {}", ts, e); - // 仍然退出 - std::process::exit(1); + // 启动失败也发送断连信号 + if let Some(tx) = shutdown_tx_arc.lock().unwrap().take() { + let _ = tx.send(()); + } } } } @@ -306,11 +292,15 @@ fn schedule_bootloader_launch(debug: bool) { if debug { println!("{} [启动] BootLoader.exe 已启动,Updater 即将退出", ts); } - std::process::exit(0); + if let Some(tx) = shutdown_tx_arc.lock().unwrap().take() { + let _ = tx.send(()); + } } Err(e) => { eprintln!("{} [错误] 启动 BootLoader.exe 失败: {}", ts, e); - std::process::exit(1); + if let Some(tx) = shutdown_tx_arc.lock().unwrap().take() { + let _ = tx.send(()); + } } } } @@ -624,8 +614,28 @@ fn is_process_running(process_name: &str) -> bool { count > 0 } +/// 主动断连信号(用于在消息回调中请求优雅退出) +/// - shutdown_tx: 从同步回调中发送,通知主循环主动断开 +/// - disconnect_rx: 在主循环中等待 +fn make_shutdown_channel() -> ( + std::sync::Arc>>>, + tokio::sync::oneshot::Receiver<()>, +) { + let (tx, rx) = tokio::sync::oneshot::channel(); + (std::sync::Arc::new(std::sync::Mutex::new(Some(tx))), rx) +} + /// 运行 Updater(使用 CubeLib 内置的自动重连) -async fn run_updater(debug_mode: bool) { +/// 返回本次运行是否执行了更新(下载了文件) +async fn run_updater(debug_mode: bool) -> bool { + // 重置本次运行的更新状态 + *UPDATE_PERFORMED.lock().unwrap() = false; + *BOOTLOADER_DOWNLOADED.lock().unwrap() = false; + *UPDATER_DOWNLOADED.lock().unwrap() = false; + *SERVER_UPDATER_VERSION.lock().unwrap() = None; + + // 主动断连通道 + let (shutdown_tx_arc, mut disconnect_rx) = make_shutdown_channel(); // 加载初始 URL let server_url = resolve_ws_url(); @@ -660,6 +670,7 @@ async fn run_updater(debug_mode: bool) { // 设置消息接收回调 let debug_msg = debug_mode; let device_number = resolve_device_number(); + let shutdown_tx_arc_clone = shutdown_tx_arc.clone(); client.on_message(move |msg_type, data, sender| { let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); if debug_msg { @@ -722,6 +733,7 @@ async fn run_updater(debug_mode: bool) { println!("{} [续传] {} 发现未完成下载,请求 hash 校验...", ts, filename); } request_file_md5(&sender, filename, tmp_size, debug_msg); + *UPDATE_PERFORMED.lock().unwrap() = true; } else { // 无临时文件,从头下载 if debug_msg { @@ -729,6 +741,7 @@ async fn run_updater(debug_mode: bool) { println!("{} [升级] {} 需要更新,开始下载...", ts, filename); } request_download(&sender, filename, 0, debug_msg); + *UPDATE_PERFORMED.lock().unwrap() = true; } } else { // 不需要更新 @@ -770,12 +783,12 @@ async fn run_updater(debug_mode: bool) { // 请求 hash 校验,继续等待 request_file_md5(&sender, "Updater.exe", tmp_size, debug_msg); } else { - // 真的不需要更新,也没有临时文件 → 退出 + // 真的不需要更新,也没有临时文件 → 标记检查完成 + *UPDATE_CHECK_DONE.lock().unwrap() = true; if debug_msg { let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - println!("{} [升级] 所有文件已是最新版本,Updater 退出", ts); + println!("{} [升级] 所有文件已是最新版本,等待下次检查...", ts); } - std::process::exit(0); } } } @@ -844,7 +857,7 @@ async fn run_updater(debug_mode: bool) { 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); + schedule_bootloader_launch(debug_msg, shutdown_tx_arc_clone.clone()); } } } @@ -878,7 +891,7 @@ async fn run_updater(debug_mode: bool) { 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); + schedule_bootloader_launch(debug_msg, shutdown_tx_arc.clone()); } } } @@ -959,11 +972,31 @@ async fn run_updater(debug_mode: bool) { if debug_mode { println!("[启动] 开始连接..."); } - client.connect().await; - if debug_mode { - println!("[启动] 连接已结束"); - println!("Updater 已停止"); + + // 等待断连信号或连接正常结束 + tokio::select! { + _ = &mut disconnect_rx => { + // 收到断连信号,主动断开 + if debug_mode { + println!("[启动] 收到断连信号,主动断开..."); + } + client.disconnect().await; + } + _ = client.connect() => { + // 连接正常结束(CubeLib 自动重连后的最终退出) + if debug_mode { + println!("[启动] 连接已结束"); + } + } } + + if debug_mode { + println!("Updater 本次运行结束"); + } + + // 返回是否执行了更新(下载了文件) + let performed = *UPDATE_PERFORMED.lock().unwrap(); + performed } #[tokio::main] @@ -991,8 +1024,33 @@ async fn main() { } } - // 运行 Updater - run_updater(config.debug_mode).await; + // 主循环:常驻运行,定期检查更新 + loop { + // 运行一次 Updater(连接、检查、下载) + let update_performed = run_updater(config.debug_mode).await; + + if update_performed { + // 执行了更新(下载了文件)→ 退出循环,由 BootLoader 重启 Updater + if config.debug_mode { + println!("Updater 执行了更新,即将退出(等待 BootLoader 重启)"); + } + break; + } + + // 无更新 → 随机等待 5-10 分钟后再次检查 + let wait_seconds = { + let mut rng = rand::thread_rng(); + let minutes: f64 = rng.gen_range(5.0..=10.0); + (minutes * 60.0) as u64 + }; + + if config.debug_mode { + let wait_mins = wait_seconds as f64 / 60.0; + println!("Updater 本次检查完成,无更新,等待 {:.1} 分钟后再次检查...", wait_mins); + } + + tokio::time::sleep(std::time::Duration::from_secs(wait_seconds)).await; + } if config.debug_mode { println!("Updater 已退出");