分阶段执行

This commit is contained in:
zqm
2026-04-09 14:08:20 +08:00
parent fe71dc7012
commit aae527258a

View File

@@ -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<HashSet<String>>,
/// Updater.exe 的服务端版本号
server_updater_version: Mutex<Option<String>>,
/// 当前更新阶段
current_phase: Mutex<UpdatePhase>,
/// 候选应用列表阶段3使用
candidates: Mutex<Vec<String>>,
}
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<String> {
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<std::sync::Mutex<Option<tokio::sync::oneshot::Sender<()>>>>,
) -> 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::<String>::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,114 +1234,229 @@ 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 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","Updater.exe"]}}}}"#,
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["BootLoader.exe"]}}}}"#,
device_number
);
if debug_msg {
let ts2 = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} 发送消息:{}", ts2, msg_str);
println!("{} 发送消息:{}", ts, msg_str);
}
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()) {
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 ==========
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");
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 {
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);
}
} else {
// BootLoader 不在响应中推进到阶段2
*ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Updater;
println!("{} [阶段2] 检查 Updater...", ts);
let msg_str = format!(
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["Updater.exe"]}}}}"#,
device_number
);
sender.send(msg_str);
}
}
// 处理 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 {
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 的服务端版本号
if filename == "Updater.exe" {
*ctx_clone.server_updater_version.lock().unwrap() = Some(server_version.to_string());
}
}
// 第二步:再遍历处理每个文件
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);
println!("{} [版本] Updater.exe: 服务端={}, 本地={}", ts, server_version, local_version);
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);
}
// 比较版本:如果本地不存在或比服务端旧,则下载
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);
}
request_file_md5(&sender, filename, tmp_size, debug_msg);
update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
download_initiated = true;
println!("{} [续传] Updater.exe 发现未完成下载,请求 hash 校验...", ts);
request_file_md5(&sender, "Updater.exe", tmp_size, debug_msg);
} else {
// 无临时文件,从头下载
if debug_msg {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [升级] {} 需要更新,开始下载...", ts, filename);
println!("{} [升级] Updater.exe 需要更新,开始下载...", ts);
request_download(&sender, "Updater.exe", 0, debug_msg);
}
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);
}
// 对于 Updater.exe检查是否有 Updater.new.exe
if filename == "Updater.exe" {
println!("{} [版本] Updater.exe 版本已是最新", ts);
// 检查是否有旧的 Updater.new.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");
println!("{} [清理] 删除旧的 Updater.new.exe", ts);
}
}
}
// 标记 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);
}
}
}
}
// 循环结束后:没有发起下载 → 全部已是最新
if !download_initiated {
// 推进到阶段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 debug_msg {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [升级] 所有文件已是最新版本,等待下次检查...", ts);
}
// 主动发送断连信号,让 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(());
}
} 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());
}
}
}
// 处理需要更新的应用
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 !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(());
}
}
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,6 +1560,35 @@ 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 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!("{} [应用] {} / {} 下载完成", 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 {
@@ -1644,10 +1605,38 @@ 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 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;
}
} else {
// ========== BootLoader/Updater 的 DownloadComplete 处理 ==========
let completed = handle_download_complete(&ctx_clone, data_obj, debug_msg);
if let Some(ref completed_file) = completed {
// BootLoader.exe 下载完成
@@ -1676,6 +1665,7 @@ async fn run_updater(debug_mode: bool) -> bool {
}
}
}
}
});
// 设置断开连接回调
@@ -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::<tokio::sync::oneshot::Sender<()>>));
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();