更新Updater
This commit is contained in:
1
Windows/CS/Framework4.0/Updater/Cargo.lock
generated
1
Windows/CS/Framework4.0/Updater/Cargo.lock
generated
@@ -11,6 +11,7 @@ dependencies = [
|
||||
"cube_lib",
|
||||
"dirs",
|
||||
"md5",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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<DownloadState> = std::sync::Mutex::new(D
|
||||
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 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 等)
|
||||
@@ -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<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
|
||||
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<std::sync::Mutex<Option<tokio::sync::oneshot::Sender<()>>>>,
|
||||
) {
|
||||
// 延迟 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<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 内置的自动重连)
|
||||
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,12 +972,32 @@ async fn run_updater(debug_mode: bool) {
|
||||
if debug_mode {
|
||||
println!("[启动] 开始连接...");
|
||||
}
|
||||
client.connect().await;
|
||||
|
||||
// 等待断连信号或连接正常结束
|
||||
tokio::select! {
|
||||
_ = &mut disconnect_rx => {
|
||||
// 收到断连信号,主动断开
|
||||
if debug_mode {
|
||||
println!("[启动] 收到断连信号,主动断开...");
|
||||
}
|
||||
client.disconnect().await;
|
||||
}
|
||||
_ = client.connect() => {
|
||||
// 连接正常结束(CubeLib 自动重连后的最终退出)
|
||||
if debug_mode {
|
||||
println!("[启动] 连接已结束");
|
||||
println!("Updater 已停止");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if debug_mode {
|
||||
println!("Updater 本次运行结束");
|
||||
}
|
||||
|
||||
// 返回是否执行了更新(下载了文件)
|
||||
let performed = *UPDATE_PERFORMED.lock().unwrap();
|
||||
performed
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn 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 已退出");
|
||||
|
||||
Reference in New Issue
Block a user