Files
JoyD/Windows/CS/Framework4.0/Updater/src/main.rs

315 lines
11 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use std::pin::Pin;
use std::process::Command;
use cube_lib::websocket::{WebSocketClient, WebSocketConfig};
/// Updater 自身配置AppData/Updater/config.json
/// 只负责 Updater 自己的行为参数,连接地址从公共 config.json 加载
#[derive(Debug, Serialize, Deserialize)]
struct Config {
/// 调试模式true 时保留控制台窗口并输出日志
debug_mode: bool,
}
impl Default for Config {
fn default() -> Self {
Self { debug_mode: false }
}
}
/// 获取 Updater 自身配置路径 AppData/Updater/config.json
fn get_updater_config_path() -> PathBuf {
let exe_path = std::env::current_exe().expect("Failed to get executable path");
let drive = exe_path
.parent()
.and_then(|p| p.as_os_str().to_str())
.and_then(|s| s.split('\\').next())
.unwrap_or("C:");
let appdata = PathBuf::from(format!("{}/AppData", drive));
let updater_dir = appdata.join("Updater");
let _ = fs::create_dir_all(&updater_dir);
updater_dir.join("config.json")
}
/// 获取公共配置路径 AppData/config.json与 BootLoader 同级)
fn get_public_config_path() -> PathBuf {
let exe_path = std::env::current_exe().expect("Failed to get executable path");
let drive = exe_path
.parent()
.and_then(|p| p.as_os_str().to_str())
.and_then(|s| s.split('\\').next())
.unwrap_or("C:");
PathBuf::from(format!("{}/AppData/config.json", drive))
}
/// 加载 Updater 自身配置;若文件不存在则写入默认值
fn load_updater_config() -> Config {
let config_path = get_updater_config_path();
if config_path.exists() {
if let Ok(content) = fs::read_to_string(&config_path) {
if let Ok(config) = serde_json::from_str::<Config>(&content) {
return config;
}
}
}
// 文件不存在或解析失败 → 写入默认值
let default_config = Config::default();
if let Ok(content) = serde_json::to_string_pretty(&default_config) {
let _ = fs::write(&config_path, content);
}
default_config
}
/// 从公共 config.json 读取 ServerUrl 字段
fn resolve_ws_url() -> String {
let config_path = get_public_config_path();
if let Ok(content) = fs::read_to_string(&config_path) {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
if let Some(url) = json.get("ServerUrl").and_then(|v| v.as_str()) {
return url.to_string();
}
}
}
// 读取失败 → 降级到默认值
"ws://127.0.0.1:8087/ws".to_string()
}
/// 从公共 config.json 读取 DeviceNumber 字段
fn resolve_device_number() -> String {
let config_path = get_public_config_path();
if let Ok(content) = fs::read_to_string(&config_path) {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
// 尝试多个可能的字段名
if let Some(id) = json.get("DeviceNumber").and_then(|v| v.as_str()) {
if !id.is_empty() {
return id.to_string();
}
}
if let Some(id) = json.get("StationId").and_then(|v| v.as_str()) {
if !id.is_empty() {
return id.to_string();
}
}
if let Some(id) = json.get("Station").and_then(|v| v.as_str()) {
if !id.is_empty() {
return id.to_string();
}
}
}
}
// 读取失败 → 降级到默认值
"UNKNOWN".to_string()
}
fn is_process_running(process_name: &str) -> bool {
use std::process::id;
let current_pid = id().to_string();
let output = Command::new("tasklist")
.args(["/FI", &format!("IMAGENAME eq {}", process_name), "/FO", "CSV"])
.output()
.expect("Failed to execute tasklist");
let output_str = String::from_utf8_lossy(&output.stdout);
let lines: Vec<&str> = output_str.lines().collect();
let mut count = 0;
for line in lines {
if line.contains(&format!("\"{}\"", process_name)) && !line.contains(&current_pid) {
count += 1;
}
}
count > 0
}
/// 运行 Updater使用 CubeLib 内置的自动重连)
async fn run_updater(debug_mode: bool) {
// 加载初始 URL
let server_url = resolve_ws_url();
if debug_mode {
println!("========================================");
println!("Updater 启动 (调试模式)");
println!("服务器地址: {}", server_url);
println!("自动重连: 启用 (指数退避: 1s - 30s)");
println!("========================================");
}
// 创建 WebSocket 配置(启用自动重连)
let config = WebSocketConfig::new(&server_url)
.with_client_type("Updater")
.with_debug(debug_mode)
.with_reconnect(true) // 启用自动重连
.with_reconnect_delay(1000) // 初始延迟 1s
.with_max_reconnect_delay(30000); // 最大延迟 30s
// 创建 WebSocket 客户端
let mut client = WebSocketClient::new(config);
// 设置连接成功回调
let debug_connected = debug_mode;
client.on_connected(move |url| {
if debug_connected {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} 收到消息:{}", ts, url);
}
});
// 设置消息接收回调
let debug_msg = debug_mode;
let device_number = resolve_device_number();
client.on_message(move |msg_type, data, sender| {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
if debug_msg {
// 收到消息日志Type 在前
let data_str = serde_json::to_string(&data).unwrap_or_else(|_| "{}".to_string());
println!("{} 收到消息:{{\"Type\":{},\"Data\":{}}}",
ts,
serde_json::to_string(&msg_type).unwrap_or_default(),
data_str
);
}
// 收到 welcome 后,发送 GetFileVer
if msg_type == "welcome" {
if !device_number.is_empty() && device_number != "UNKNOWN" {
let msg_str = format!(
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);
}
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, version) in file_versions {
let ver_str = version.as_str().unwrap_or("");
println!("{} [版本] {} = {}", ts, filename, if ver_str.is_empty() { "未知" } else { ver_str });
}
}
}
});
// 设置断开连接回调
let debug_disconnect = debug_mode;
client.on_disconnected(move || {
if debug_disconnect {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [断开] 连接已断开", ts);
}
});
// 设置错误回调
client.on_error(|error| {
eprintln!("[错误] WebSocket: {}", error);
});
// 设置首次连接回调GetFileVer 改到收到 welcome 后发送)
let debug_first = debug_mode;
let device_number_first = resolve_device_number();
client.on_first_connect(move |_url, _sender| {
let device_number = device_number_first.clone();
Box::pin(async move {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
if device_number.is_empty() || device_number == "UNKNOWN" {
if debug_first {
println!("{} [连接] 已连接,未配置设备号,仅维持心跳", ts);
}
} else {
if debug_first {
println!("{} [连接] 已连接,等待服务器欢迎消息...", ts);
}
}
}) as Pin<Box<dyn std::future::Future<Output = ()> + Send + Sync>>
});
// 设置重连回调
let debug_reconnect = debug_mode;
client.on_reconnecting(move |attempt, url_arc| {
Box::pin(async move {
if debug_reconnect {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [重连] 第 {} 次重连中...", ts, attempt);
}
let new_url = resolve_ws_url();
*url_arc.lock().await = new_url.clone();
if debug_reconnect {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [重连] 配置已更新,服务器: {}", ts, new_url);
}
}) as Pin<Box<dyn std::future::Future<Output = ()> + Send + Sync>>
});
// 设置重连成功回调
let debug_reconnected = debug_mode;
let device_number_reconn = resolve_device_number();
client.on_reconnected(move |_url, _sender| {
let device_number = device_number_reconn.clone();
Box::pin(async move {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
if device_number.is_empty() || device_number == "UNKNOWN" {
if debug_reconnected {
println!("{} [重连] 已重连,未配置设备号", ts);
}
} else {
if debug_reconnected {
println!("{} [重连] 已重连,等待服务器欢迎消息...", ts);
}
}
}) as Pin<Box<dyn std::future::Future<Output = ()> + Send + Sync>>
});
// 连接CubeLib 会自动处理重连)
if debug_mode {
println!("[启动] 开始连接...");
}
client.connect().await;
if debug_mode {
println!("[启动] 连接已结束");
println!("Updater 已停止");
}
}
#[tokio::main]
async fn main() {
// 检查是否已有 Updater 进程在运行
if is_process_running("Updater.exe") {
return;
}
// 加载 Updater 自身配置debug_mode
let config = load_updater_config();
// 非 debug 模式下释放控制台,后台静默运行
if !config.debug_mode {
#[cfg(windows)]
{
use windows::Win32::System::Console;
use windows::Win32::Foundation::HWND;
unsafe {
let console = Console::GetConsoleWindow();
if console != HWND::default() {
let _ = Console::FreeConsole();
}
}
}
}
// 运行 Updater
run_updater(config.debug_mode).await;
if config.debug_mode {
println!("Updater 已退出");
}
}