#![windows_subsystem = "windows"] mod download_wrapper; mod mes_service; use crate::mes_service::MesService; use crossbeam::channel; use crossbeam::channel::Sender; use download_wrapper::DownloadType; use iced::application::Title; use iced::widget::progress_bar::Style; use iced::widget::text_editor::Action::Scroll; use iced::widget::text_editor::Edit; use iced::widget::{ button, checkbox, column, container, progress_bar, radio, row, text_editor, text_input, }; use iced::{event, time, window, Border, Color, Element, Event, Length, Subscription, Task}; use serde_json::Map; use std::any::Any; use std::fs; use std::io::Write; use std::sync::{Arc, LazyLock, Mutex}; use std::thread::sleep; pub fn main() -> iced::Result { iced::application("WisunDownload V0.1", MainWindow::update, MainWindow::view) .subscription(MainWindow::subscription) .exit_on_close_request(false) .window_size((830.0, 600.0)) .run() } static LOGS: LazyLock>>> = LazyLock::new(|| Arc::new(Mutex::new(Vec::new()))); struct MainWindow { is_online: bool, mysql_config: MysqlConfig, selection: Option, label: String, log_content: text_editor::Content, sender: Sender, receiver: channel::Receiver, result_background: Color } impl Default for MainWindow { fn default() -> Self { let (sender, receiver) = channel::unbounded(); Self { is_online: true, mysql_config: MysqlConfig::default(), selection: None, label: String::new(), log_content: text_editor::Content::default(), result_background: Color::from_rgb8(255,255,255), sender, receiver, } } } #[derive(Debug, Clone, Default)] struct MysqlConfig { ip: String, port: String, username: String, password: String, database: String, work_order: String, order_number: String } #[derive(Debug, Clone)] enum Message { Start(), IpChanged(String), PortChanged(String), UsernameChanged(String), PasswordChanged(String), DatabaseChanged(String), WorkOrderChanged(String), OrderNumberChanged(String), LabelChanged(String), TypeSelected(DownloadType), OnlineChecked(bool), AddLog(String), LogContentChanged(text_editor::Action), SaveConfig, DownloadEnd((String, bool)), WindowEvent(Event), ClearLog, Tick, } pub fn add_log<'a>(msg: String) { let time_now = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); let really_msg = format!("{time_now} [Info]: {msg}"); LOGS.lock().unwrap().push(really_msg); } impl MainWindow { fn update(&mut self, message: Message) -> Task { match message { Message::Start() => { self.result_background = Color::from_rgb8(255,255,255); self.start(self.sender.clone()); Task::none() } Message::IpChanged(ip) => { self.mysql_config.ip = ip; Task::none() } Message::PortChanged(port) => { self.mysql_config.port = port; Task::none() } Message::UsernameChanged(username) => { self.mysql_config.username = username; Task::none() } Message::PasswordChanged(password) => { self.mysql_config.password = password; Task::none() } Message::DatabaseChanged(database) => { self.mysql_config.database = database; Task::none() } Message::WorkOrderChanged(work_order) => { self.mysql_config.work_order = work_order; Task::none() } Message::OrderNumberChanged(order_number) =>{ self.mysql_config.order_number = order_number; Task::none() } Message::TypeSelected(download_type) => { self.selection = Some(download_type); Task::none() } Message::LabelChanged(label) => { self.label = label; Task::none() } Message::OnlineChecked(is_online) => { self.is_online = is_online; Task::none() } Message::ClearLog => { self.log_content.perform(text_editor::Action::SelectAll); self.log_content .perform(text_editor::Action::Edit(Edit::Backspace)); Task::none() } Message::AddLog(log) => { for c in log.chars() { self.log_content .perform(text_editor::Action::Edit(Edit::Insert(c))) } self.log_content .perform(text_editor::Action::Edit(Edit::Enter)); let line_size = self.log_content.lines().count(); self.log_content.perform(Scroll { lines: line_size as i32, }); Task::none() } Message::Tick => { for log in LOGS.lock().unwrap().iter() { for c in log.chars() { self.log_content .perform(text_editor::Action::Edit(Edit::Insert(c))) } self.log_content .perform(text_editor::Action::Edit(Edit::Enter)); let line_size = self.log_content.lines().count(); self.log_content.perform(Scroll { lines: line_size as i32, }); } LOGS.lock().unwrap().clear(); match self.receiver.try_recv() { Ok(event) => match event { Message::DownloadEnd((label, res)) => { let _ = self.update(Message::DownloadEnd((label, res))); } _ => {} }, Err(channel::TryRecvError::Empty) => {} Err(channel::TryRecvError::Disconnected) => { println!("Disconnected"); } }; Task::none() } Message::LogContentChanged(action) => match action { Scroll { lines } => { self.log_content.perform(Scroll { lines }); Task::none() } _ => Task::none(), }, Message::DownloadEnd((label, res)) => { if !fs::exists("./uilog").unwrap() { fs::create_dir("./uilog").expect("Cant Create UiLog Folder"); } let mut file = fs::OpenOptions::new() .create(true) .append(true) .open(format!("./uilog/{label}.txt")) .unwrap(); writeln!(file, "{:?}", self.log_content.text().clone()).unwrap(); if res{ self.result_background = Color::from_rgb8(0, 255,0); }else{ self.result_background = Color::from_rgb8(255, 0,0); } self.label = "".to_string(); Task::none() } Message::WindowEvent(event) => { //println!("{:?}", event); if let Event::Window(window::Event::Opened { position: _position, size: _size }) = event { println!("Opened"); self.read_config(); return Task::none() }; if let Event::Window(window::Event::CloseRequested) = event { println!("Request Close"); self.save_config(); return window::get_latest().and_then(window::close); }; Task::none() } _ => Task::none(), } } fn view(&self) -> Element { let ip_input = text_input("IP Address", &self.mysql_config.ip).on_input(Message::IpChanged); let port_input = text_input("Port", &self.mysql_config.port).on_input(Message::PortChanged); let username_input = text_input("Username", &self.mysql_config.username).on_input(Message::UsernameChanged); let password_input = text_input("Password", &self.mysql_config.password).on_input(Message::PasswordChanged); let database_input = text_input("Database", &self.mysql_config.database).on_input(Message::DatabaseChanged); let work_order_input = text_input("WorkOrder", &self.mysql_config.work_order) .on_input(Message::WorkOrderChanged); let order_number_input = text_input("OrderNumber", &self.mysql_config.order_number) .on_input(Message::OrderNumberChanged); let label_input = text_input("label", &self.label) .on_input(Message::LabelChanged) .on_submit(Message::Start()) .width(Length::Fill); let content = column![ row![ip_input, port_input].spacing(5), row![username_input, password_input].spacing(5), row![database_input, order_number_input].spacing(5), row![work_order_input].spacing(5), row![ radio( "App", DownloadType::App, self.selection, Message::TypeSelected ), radio( "Cal", DownloadType::Rail, self.selection, Message::TypeSelected ), checkbox("online", self.is_online).on_toggle(Message::OnlineChecked), ] .spacing(10), row![label_input, button("Start").on_press(Message::Start())].spacing(10), text_editor(&self.log_content) .on_action(Message::LogContentChanged) .height(Length::Fill), progress_bar(0.0..=100.0, 0.0).style(|_|{ Style { background: self.result_background.into(), bar: self.result_background.into(), border: Border::default(), } }), ] .spacing(10) .padding(5); let container = container(content).padding(20); container.into() } fn subscription(&self) -> Subscription { Subscription::batch(vec![ event::listen().map(Message::WindowEvent), time::every(time::Duration::from_millis(50)).map(|_| Message::Tick), ]) } fn start(&mut self, sender: Sender) -> () { let mes_config = self.mysql_config.clone(); let mut label = self.label.clone(); let download_type: DownloadType = self.selection.unwrap(); let online = self.is_online.clone(); if label == "" { add_log("请输入Label".to_string()); return; } label = label.replace(":", ""); label = label.to_uppercase(); label = self.mysql_config.order_number.clone() + &label[label.len() - 6..]; if label.contains(":"){ add_log("Label不合法".to_string()); self.label = "".to_string(); return; } add_log("Label: ".to_string() + label.as_str()); std::thread::spawn(move || { sleep(std::time::Duration::from_secs(5)); let mut download_wrapper = download_wrapper::DownloadWrapper::new(); let mut mes_service: MesService; sender.send(Message::ClearLog).unwrap(); if online { add_log("当前为在线模式, 正在连接数据库".to_string()); match MesService::new( mes_config.ip.clone(), mes_config.port.clone(), mes_config.username.clone(), mes_config.password.clone(), mes_config.database.clone(), ) { Ok(service) => mes_service = service, Err(e) => { add_log(format!("连接数据库失败: {:?}", e.to_string())); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } } add_log("连接成功".to_string()); add_log("正在过站检测".to_string()); let check_result = mes_service.check_station( mes_config.work_order.clone(), label.clone(), download_type, ); if let Err(res) = check_result { add_log(format!("过站检测失败: {:?}", res.to_string())); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } else if let Ok(res) = check_result { if !res { add_log("过站检测失败".to_string()); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } add_log("过站检测成功".to_string()); } } add_log("正在检查JLink连接".to_string()); if let Err(_) = download_wrapper.check_jlink() { add_log("JLink检查失败, 请检查是否安装驱动或连接是否异常".to_string()); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } add_log("JLink检查成功".to_string()); add_log("正在检查设备连接".to_string()); if let Err(_) = download_wrapper.check_device() { add_log("设备检查失败, 请检查设备是否连接".to_string()); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } add_log("设备检查成功".to_string()); match download_type { DownloadType::Bootloader => {} DownloadType::App => { add_log("正在擦除Flash".to_string()); if let Err(_) = download_wrapper.erase(Some(label.clone())) { add_log("擦除失败".to_string()); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } add_log("擦除成功".to_string()); add_log("正在下载BootLoader".to_string()); if let Err(_) = download_wrapper.download(DownloadType::Bootloader, Some(label.clone())) { add_log("下载BootLoader失败".to_string()); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } add_log("下载BootLoader成功".to_string()); add_log("正在下载App".to_string()); if let Err(_) = download_wrapper.download(download_type, Some(label.clone())) { add_log("下载App失败".to_string()); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } add_log("下载App成功".to_string()); } DownloadType::Rail => { add_log("正在下载Rail".to_string()); if let Err(_) = download_wrapper.download(download_type, Some(label.clone())) { add_log("下载Rail失败".to_string()); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } add_log("下载Rail成功".to_string()); } } return if online { match MesService::new( mes_config.ip.clone(), mes_config.port.clone(), mes_config.username.clone(), mes_config.password.clone(), mes_config.database.clone(), ) { Ok(service) => mes_service = service, Err(e) => { add_log(format!("连接数据库失败: {:?}", e.to_string())); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } } add_log("正在上传数据".to_string()); let update_result = mes_service.update_station(mes_config.work_order, label.clone(), download_type); if let Err(e) = update_result { add_log(format!("上传失败: {:?}", e.to_string())); sender.send(Message::DownloadEnd((label, false))).unwrap(); return; } sender.send(Message::DownloadEnd((label, true))).unwrap(); return; } else { add_log("当前为离线模式".to_string()); sender.send(Message::DownloadEnd((label, true))).unwrap(); return; }; }); } fn save_config(&mut self) { let mut config = Map::new(); config.insert("ip".to_string(), self.mysql_config.ip.clone().into()); config.insert("port".to_string(), self.mysql_config.port.clone().into()); config.insert( "username".to_string(), self.mysql_config.username.clone().into(), ); config.insert( "password".to_string(), self.mysql_config.password.clone().into(), ); config.insert( "database".to_string(), self.mysql_config.database.clone().into(), ); config.insert( "work_order".to_string(), self.mysql_config.work_order.clone().into(), ); config.insert( "order_number".to_string(), self.mysql_config.order_number.clone().into(), ); config.insert("is_online".to_string(), self.is_online.clone().into()); config.insert( "selection".to_string(), self.selection.unwrap().to_string().into(), ); let config_str = serde_json::to_string(&config).unwrap(); fs::write("./config.json", config_str).unwrap(); } fn read_config(&mut self) { if !fs::exists("./config.json").unwrap() { return; } let config_str = fs::read_to_string("./config.json").unwrap(); let config: Map = serde_json::from_str(&config_str).unwrap(); self.mysql_config.ip = config["ip"].as_str().unwrap().to_string(); self.mysql_config.port = config["port"].as_str().unwrap().to_string(); self.mysql_config.username = config["username"].as_str().unwrap().to_string(); self.mysql_config.password = config["password"].as_str().unwrap().to_string(); self.mysql_config.database = config["database"].as_str().unwrap().to_string(); self.mysql_config.work_order = config["work_order"].as_str().unwrap().to_string(); self.mysql_config.order_number = config["order_number"].as_str().unwrap().to_string(); self.is_online = config["is_online"].as_bool().unwrap(); match config["selection"].as_str().unwrap() { "BootLoader" => { self.selection = Some(DownloadType::Bootloader); } "App" => { self.selection = Some(DownloadType::App); } "Rail" => { self.selection = Some(DownloadType::Rail); } _ => { self.selection = None; } } } }