分阶段执行
This commit is contained in:
@@ -2,6 +2,16 @@ use rand::Rng;
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::{Arc, Mutex};
|
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 共享)=====================
|
// ===================== 更新上下文(局部状态 + Arc 共享)=====================
|
||||||
/// 更新流程的状态上下文
|
/// 更新流程的状态上下文
|
||||||
/// 使用 Mutex 实现 Sync(即使单线程也需要满足 Rust 的线程安全约束)
|
/// 使用 Mutex 实现 Sync(即使单线程也需要满足 Rust 的线程安全约束)
|
||||||
@@ -14,6 +24,10 @@ struct UpdateContext {
|
|||||||
app_completed_set: Mutex<HashSet<String>>,
|
app_completed_set: Mutex<HashSet<String>>,
|
||||||
/// Updater.exe 的服务端版本号
|
/// Updater.exe 的服务端版本号
|
||||||
server_updater_version: Mutex<Option<String>>,
|
server_updater_version: Mutex<Option<String>>,
|
||||||
|
/// 当前更新阶段
|
||||||
|
current_phase: Mutex<UpdatePhase>,
|
||||||
|
/// 候选应用列表(阶段3使用)
|
||||||
|
candidates: Mutex<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UpdateContext {
|
impl Default for UpdateContext {
|
||||||
@@ -23,6 +37,8 @@ impl Default for UpdateContext {
|
|||||||
app_download_map: Mutex::new(HashMap::new()),
|
app_download_map: Mutex::new(HashMap::new()),
|
||||||
app_completed_set: Mutex::new(HashSet::new()),
|
app_completed_set: Mutex::new(HashSet::new()),
|
||||||
server_updater_version: Mutex::new(None),
|
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()))
|
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 加载
|
/// 只负责 Updater 自己的行为参数,连接地址从公共 config.json 加载
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct Config {
|
struct Config {
|
||||||
@@ -1417,11 +1171,24 @@ async fn run_updater(debug_mode: bool) -> bool {
|
|||||||
// 加载初始 URL
|
// 加载初始 URL
|
||||||
let server_url = resolve_ws_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 {
|
if debug_mode {
|
||||||
println!("========================================");
|
println!("========================================");
|
||||||
println!("Updater 启动 (调试模式)");
|
println!("Updater 启动 (调试模式)");
|
||||||
println!("服务器地址: {}", server_url);
|
println!("服务器地址: {}", server_url);
|
||||||
println!("自动重连: 启用 (指数退避: 1s - 30s)");
|
println!("自动重连: 启用 (指数退避: 1s - 30s)");
|
||||||
|
println!("更新阶段: BootLoader -> Updater -> Apps -> Complete");
|
||||||
|
if !app_candidates.is_empty() {
|
||||||
|
println!("候选应用: {:?}", app_candidates);
|
||||||
|
}
|
||||||
println!("========================================");
|
println!("========================================");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1467,113 +1234,228 @@ async fn run_updater(debug_mode: bool) -> bool {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 收到 welcome 后,发送 GetFileVer(同时请求 BootLoader 和 Updater)
|
// 收到 welcome 后,根据当前阶段发送 GetFileVer
|
||||||
if msg_type == "welcome" {
|
if msg_type == "welcome" {
|
||||||
if !device_number.is_empty() && device_number != "UNKNOWN" {
|
if !device_number.is_empty() && device_number != "UNKNOWN" {
|
||||||
let msg_str = format!(
|
let current_phase = *ctx_clone.current_phase.lock().unwrap();
|
||||||
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["BootLoader.exe","Updater.exe"]}}}}"#,
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
device_number
|
|
||||||
);
|
match current_phase {
|
||||||
if debug_msg {
|
UpdatePhase::BootLoader => {
|
||||||
let ts2 = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
// 阶段1:只请求 BootLoader.exe
|
||||||
println!("{} 发送消息:{}", ts2, msg_str);
|
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 msg_type == "FileVer" {
|
||||||
if let Some(file_versions) = data.get("Data").and_then(|d| d.get("file_versions")).and_then(|v| v.as_object()) {
|
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();
|
||||||
for (filename, server_ver) in file_versions {
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第二步:再遍历处理每个文件
|
match current_phase {
|
||||||
let mut download_initiated = false;
|
UpdatePhase::BootLoader => {
|
||||||
for (filename, server_ver) in file_versions {
|
// ========== 阶段1:处理 BootLoader ==========
|
||||||
let server_version = server_ver.as_str().unwrap_or("0.0.0");
|
if let Some(server_ver) = file_versions.get("BootLoader.exe") {
|
||||||
let local_version = get_local_file_version(filename);
|
let server_version = server_ver.as_str().unwrap_or("0.0.0");
|
||||||
let tmp_size = get_tmp_file_size(filename);
|
let local_version = get_local_file_version("BootLoader.exe");
|
||||||
|
let tmp_size = get_tmp_file_size("BootLoader.exe");
|
||||||
|
|
||||||
if debug_msg {
|
println!("{} [版本] BootLoader.exe: 服务端={}, 本地={}", ts, server_version, local_version);
|
||||||
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);
|
||||||
let need_update = local_version == "0.0.0" || version_less_than(&local_version, server_version);
|
if need_update {
|
||||||
if need_update {
|
if tmp_size > 0 {
|
||||||
if tmp_size > 0 {
|
println!("{} [续传] BootLoader.exe 发现未完成下载,请求 hash 校验...", ts);
|
||||||
// 有临时文件,请求 hash 校验
|
request_file_md5(&sender, "BootLoader.exe", tmp_size, debug_msg);
|
||||||
if debug_msg {
|
} else {
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
println!("{} [升级] BootLoader.exe 需要更新,开始下载...", ts);
|
||||||
println!("{} [续传] {} 发现未完成下载,请求 hash 校验...", ts, filename);
|
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 {
|
} else {
|
||||||
// 无临时文件,从头下载
|
// BootLoader 不在响应中,推进到阶段2
|
||||||
if debug_msg {
|
*ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Updater;
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
println!("{} [阶段2] 检查 Updater...", ts);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对于 Updater.exe,检查是否有 Updater.new.exe
|
let msg_str = format!(
|
||||||
if filename == "Updater.exe" {
|
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["Updater.exe"]}}}}"#,
|
||||||
if has_updater_new_exe() {
|
device_number
|
||||||
// 删除旧版 Updater.new.exe(因为会重新下载)
|
);
|
||||||
let updater_new_path = get_updater_data_dir().join("Updater.new.exe");
|
sender.send(msg_str);
|
||||||
let _ = fs::remove_file(&updater_new_path);
|
}
|
||||||
if debug_msg {
|
}
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
|
||||||
|
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);
|
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" {
|
let mut has_downloads = false;
|
||||||
bootloader_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
|
for app_name in &apps_to_update {
|
||||||
if debug_msg {
|
let upgrade_files = scan_upgrade_dir(app_name, debug_msg);
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
if upgrade_files.is_empty() {
|
||||||
println!("{} [升级] BootLoader.exe 版本已是最新", ts);
|
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 {
|
if !has_downloads {
|
||||||
update_check_done_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
|
println!("{} [阶段完成] 所有阶段检查完成,等待下次检查...", ts);
|
||||||
if debug_msg {
|
} else {
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
println!("{} [阶段完成] 应用下载进行中...", ts);
|
||||||
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");
|
UpdatePhase::Complete => {
|
||||||
if debug_msg {
|
// 已在 Complete 阶段,不再处理
|
||||||
println!("{} [连接] 主动断开连接,返回主循环...", ts);
|
|
||||||
}
|
|
||||||
if let Some(tx) = shutdown_tx_arc_clone.lock().unwrap().take() {
|
|
||||||
let _ = tx.send(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1583,6 +1465,56 @@ async fn run_updater(debug_mode: bool) -> bool {
|
|||||||
if msg_type == "Md5" {
|
if msg_type == "Md5" {
|
||||||
if let Some(md5_data) = data.get("Data").and_then(|v| v.as_object()) {
|
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("");
|
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 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 bytes = md5_data.get("bytes").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||||
// Updater.exe 的临时文件名是 Updater.new.exe.tmp
|
// Updater.exe 的临时文件名是 Updater.new.exe.tmp
|
||||||
@@ -1628,17 +1560,47 @@ async fn run_updater(debug_mode: bool) -> bool {
|
|||||||
// 处理文件块
|
// 处理文件块
|
||||||
if msg_type == "FileChunk" {
|
if msg_type == "FileChunk" {
|
||||||
if let Some(data_obj) = data.get("Data").and_then(|v| v.as_object()) {
|
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);
|
let filename = data_obj.get("filename").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
// 只处理 Updater 下载完成(BootLoader 由 DownloadComplete 统一处理,避免重复)
|
|
||||||
if let Some(ref completed_file) = completed {
|
let current_phase = *ctx_clone.current_phase.lock().unwrap();
|
||||||
if completed_file == "Updater.new.exe" {
|
|
||||||
if !updater_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) {
|
// ========== 阶段3:处理应用 FileChunk ==========
|
||||||
updater_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
|
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 {
|
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.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 重复处理)
|
// 处理下载完成(统一处理,避免 FileChunk 和 DownloadComplete 重复处理)
|
||||||
if msg_type == "DownloadComplete" {
|
if msg_type == "DownloadComplete" {
|
||||||
if let Some(data_obj) = data.get("Data").and_then(|v| v.as_object()) {
|
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);
|
let filename = data_obj.get("filename").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
if let Some(ref completed_file) = completed {
|
if filename.is_empty() {
|
||||||
// BootLoader.exe 下载完成
|
return;
|
||||||
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);
|
let current_phase = *ctx_clone.current_phase.lock().unwrap();
|
||||||
if debug_msg {
|
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
// ========== 阶段3:处理应用 DownloadComplete ==========
|
||||||
println!("{} [升级] BootLoader.exe 下载完成,开始下载 Updater.exe...", ts);
|
if current_phase == UpdatePhase::Apps {
|
||||||
}
|
let candidates = ctx_clone.candidates.lock().unwrap().clone();
|
||||||
// 开始下载 Updater.exe
|
for app_name in &candidates {
|
||||||
request_download(&sender, "Updater.exe", 0, debug_msg);
|
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 {
|
||||||
else if completed_file == "Updater.new.exe" {
|
// ========== BootLoader/Updater 的 DownloadComplete 处理 ==========
|
||||||
if !updater_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) {
|
let completed = handle_download_complete(&ctx_clone, data_obj, debug_msg);
|
||||||
updater_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
|
if let Some(ref completed_file) = completed {
|
||||||
if debug_msg {
|
// BootLoader.exe 下载完成
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
if completed_file == "BootLoader.exe" {
|
||||||
println!("{} [升级] Updater.new.exe 下载完成(DownloadComplete),准备启动 BootLoader...", ts);
|
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);
|
eprintln!("[错误] WebSocket: {}", error);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 设置首次连接回调(GetFileVer 改到收到 welcome 后发送)
|
// 设置首次连接回调
|
||||||
let debug_first = debug_mode;
|
let debug_first = debug_mode;
|
||||||
let device_number_first = resolve_device_number();
|
let device_number_first = resolve_device_number();
|
||||||
client.on_first_connect(move |_url, _sender| {
|
client.on_first_connect(move |_url, _sender| {
|
||||||
@@ -1818,20 +1808,6 @@ async fn main() {
|
|||||||
std::process::exit(0);
|
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 分钟后再次检查
|
// 无更新 → 随机等待 5-10 分钟后再次检查
|
||||||
let wait_seconds = {
|
let wait_seconds = {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|||||||
Reference in New Issue
Block a user