更新Updater

This commit is contained in:
zqm
2026-04-08 16:15:28 +08:00
parent 97d4d5e8d6
commit 1f9a6bbe0f
3 changed files with 108 additions and 48 deletions

View File

@@ -11,6 +11,7 @@ dependencies = [
"cube_lib", "cube_lib",
"dirs", "dirs",
"md5", "md5",
"rand",
"serde", "serde",
"serde_json", "serde_json",
"tokio", "tokio",

View File

@@ -12,6 +12,7 @@ windows = { version = "0.56", features = ["Win32_System_Console"] }
chrono = "0.4" chrono = "0.4"
base64 = "0.22" base64 = "0.22"
md5 = "0.7" md5 = "0.7"
rand = "0.8"
# Local CubeLib for WebSocket # Local CubeLib for WebSocket
cube_lib = { path = "../../../Rust/CubeLib" } cube_lib = { path = "../../../Rust/CubeLib" }

View File

@@ -1,3 +1,4 @@
use rand::Rng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io::Read; use std::io::Read;
use std::fs::{self, File}; use std::fs::{self, File};
@@ -39,6 +40,10 @@ static DOWNLOAD_STATE: std::sync::Mutex<DownloadState> = std::sync::Mutex::new(D
static BOOTLOADER_DOWNLOADED: std::sync::Mutex<bool> = std::sync::Mutex::new(false); static BOOTLOADER_DOWNLOADED: std::sync::Mutex<bool> = std::sync::Mutex::new(false);
static UPDATER_DOWNLOADED: std::sync::Mutex<bool> = std::sync::Mutex::new(false); static UPDATER_DOWNLOADED: std::sync::Mutex<bool> = std::sync::Mutex::new(false);
static SERVER_UPDATER_VERSION: std::sync::Mutex<Option<String>> = std::sync::Mutex::new(None); static SERVER_UPDATER_VERSION: std::sync::Mutex<Option<String>> = std::sync::Mutex::new(None);
/// 标记一次更新检查是否已完成(用于主循环控制)
static UPDATE_CHECK_DONE: std::sync::Mutex<bool> = std::sync::Mutex::new(false);
/// 标记本次运行是否执行了更新(下载了文件)
static UPDATE_PERFORMED: std::sync::Mutex<bool> = std::sync::Mutex::new(false);
// ===================== 版本比较与下载 ===================== // ===================== 版本比较与下载 =====================
/// 获取 Updater 数据目录X:\AppData\,存放 BootLoader.exe 等) /// 获取 Updater 数据目录X:\AppData\,存放 BootLoader.exe 等)
@@ -101,36 +106,9 @@ fn get_local_file_version(filename: &str) -> String {
} }
/// 比较两个版本号a < b 返回 true /// 比较两个版本号a < b 返回 true
/// 使用 CubeLib 库的版本比较函数
fn version_less_than(a: &str, b: &str) -> bool { fn version_less_than(a: &str, b: &str) -> bool {
// 按 '.' 分割版本号 cube_lib::version_less_than(a, b)
let parse_v = |v: &str| -> Vec<u32> {
v.split('.')
.filter(|s| !s.is_empty())
.filter_map(|s| s.parse().ok())
.collect()
};
let a_parts: Vec<u32> = parse_v(a);
let b_parts: Vec<u32> = 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
} }
/// 计算本地文件前 N 字节的 MD5 hash字节数为 0 表示全部) /// 计算本地文件前 N 字节的 MD5 hash字节数为 0 表示全部)
@@ -249,7 +227,11 @@ fn check_and_download_updater(sender: &cube_lib::websocket::MessageSender, devic
} }
/// 安排启动 BootLoader.exe延迟执行等待所有下载完成 /// 安排启动 BootLoader.exe延迟执行等待所有下载完成
fn schedule_bootloader_launch(debug: bool) { /// 通过 shutdown_tx 发送断连信号,通知主循环优雅退出
fn schedule_bootloader_launch(
debug: bool,
shutdown_tx_arc: std::sync::Arc<std::sync::Mutex<Option<tokio::sync::oneshot::Sender<()>>>>,
) {
// 延迟 1 秒后启动,确保文件句柄已关闭 // 延迟 1 秒后启动,确保文件句柄已关闭
std::thread::spawn(move || { std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
@@ -285,13 +267,17 @@ fn schedule_bootloader_launch(debug: bool) {
if debug { if debug {
println!("{} [启动] BootLoader.exe 已启动Updater 即将退出", ts); println!("{} [启动] BootLoader.exe 已启动Updater 即将退出", ts);
} }
// 给自己发送退出信号(优雅退出 // 发送断连信号,通知主循环优雅退出
std::process::exit(0); if let Some(tx) = shutdown_tx_arc.lock().unwrap().take() {
let _ = tx.send(());
}
} }
Err(e) => { Err(e) => {
eprintln!("{} [错误] 启动 BootLoader.exe 失败: {}", ts, 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 { if debug {
println!("{} [启动] BootLoader.exe 已启动Updater 即将退出", ts); println!("{} [启动] BootLoader.exe 已启动Updater 即将退出", ts);
} }
std::process::exit(0); if let Some(tx) = shutdown_tx_arc.lock().unwrap().take() {
let _ = tx.send(());
}
} }
Err(e) => { Err(e) => {
eprintln!("{} [错误] 启动 BootLoader.exe 失败: {}", ts, 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 count > 0
} }
/// 主动断连信号(用于在消息回调中请求优雅退出)
/// - shutdown_tx: 从同步回调中发送,通知主循环主动断开
/// - disconnect_rx: 在主循环中等待
fn make_shutdown_channel() -> (
std::sync::Arc<std::sync::Mutex<Option<tokio::sync::oneshot::Sender<()>>>>,
tokio::sync::oneshot::Receiver<()>,
) {
let (tx, rx) = tokio::sync::oneshot::channel();
(std::sync::Arc::new(std::sync::Mutex::new(Some(tx))), rx)
}
/// 运行 Updater使用 CubeLib 内置的自动重连) /// 运行 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 // 加载初始 URL
let server_url = resolve_ws_url(); let server_url = resolve_ws_url();
@@ -660,6 +670,7 @@ async fn run_updater(debug_mode: bool) {
// 设置消息接收回调 // 设置消息接收回调
let debug_msg = debug_mode; let debug_msg = debug_mode;
let device_number = resolve_device_number(); let device_number = resolve_device_number();
let shutdown_tx_arc_clone = shutdown_tx_arc.clone();
client.on_message(move |msg_type, data, sender| { client.on_message(move |msg_type, data, sender| {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
if debug_msg { if debug_msg {
@@ -722,6 +733,7 @@ async fn run_updater(debug_mode: bool) {
println!("{} [续传] {} 发现未完成下载,请求 hash 校验...", ts, filename); println!("{} [续传] {} 发现未完成下载,请求 hash 校验...", ts, filename);
} }
request_file_md5(&sender, filename, tmp_size, debug_msg); request_file_md5(&sender, filename, tmp_size, debug_msg);
*UPDATE_PERFORMED.lock().unwrap() = true;
} else { } else {
// 无临时文件,从头下载 // 无临时文件,从头下载
if debug_msg { if debug_msg {
@@ -729,6 +741,7 @@ async fn run_updater(debug_mode: bool) {
println!("{} [升级] {} 需要更新,开始下载...", ts, filename); println!("{} [升级] {} 需要更新,开始下载...", ts, filename);
} }
request_download(&sender, filename, 0, debug_msg); request_download(&sender, filename, 0, debug_msg);
*UPDATE_PERFORMED.lock().unwrap() = true;
} }
} else { } else {
// 不需要更新 // 不需要更新
@@ -770,12 +783,12 @@ async fn run_updater(debug_mode: bool) {
// 请求 hash 校验,继续等待 // 请求 hash 校验,继续等待
request_file_md5(&sender, "Updater.exe", tmp_size, debug_msg); request_file_md5(&sender, "Updater.exe", tmp_size, debug_msg);
} else { } else {
// 真的不需要更新,也没有临时文件 → 退出 // 真的不需要更新,也没有临时文件 → 标记检查完成
*UPDATE_CHECK_DONE.lock().unwrap() = true;
if debug_msg { if debug_msg {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); 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"); let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [升级] Updater.new.exe 下载完成FileChunk准备启动 BootLoader...", ts); 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"); let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [升级] Updater.new.exe 下载完成DownloadComplete准备启动 BootLoader...", ts); 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 { if debug_mode {
println!("[启动] 开始连接..."); println!("[启动] 开始连接...");
} }
client.connect().await;
if debug_mode { // 等待断连信号或连接正常结束
println!("[启动] 连接已结束"); tokio::select! {
println!("Updater 已停止"); _ = &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] #[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 { if config.debug_mode {
println!("Updater 已退出"); println!("Updater 已退出");