mod download_wrapper; mod mes_service; use std::any::Any; use std::fs; use crossbeam::channel; use std::sync::{Arc, Mutex, LazyLock}; use crossbeam::channel::Sender; use iced::widget::{button, checkbox, column, container, radio, row, text_editor, text_input}; use iced::{event, window, Element, Event, Length, Subscription, Task, time}; use iced::application::Title; use iced::widget::text_editor::Action::Scroll; use iced::widget::text_editor::Edit; use serde_json::Map; use download_wrapper::DownloadType; use crate::mes_service::MesService; 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, } 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(), sender, receiver } } } #[derive(Debug, Clone, Default)] struct MysqlConfig { ip: String, port: String, username: String, password: String, database: String, work_order: String, } #[derive(Debug, Clone)] enum Message { Start(), IpChanged(String), PortChanged(String), UsernameChanged(String), PasswordChanged(String), DatabaseChanged(String), WorkOrderChanged(String), LabelChanged(String), TypeSelected(DownloadType), OnlineChecked(bool), AddLog(String), LogContentChanged(text_editor::Action), SaveConfig, DownloadEnd(bool), WindowEvent(Event), 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.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::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::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(res)=>{ if res{ add_log("下载成功 By Ui Thread".to_string()); }else{ add_log("下载失败 By Ui Thread".to_string()); } } _=>{ } } } 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(res)=>{ if res{ add_log("下载成功 By Ui Thread".to_string()); }else{ add_log("下载失败 By Ui Thread".to_string()); } Task::none() } Message::WindowEvent(event) => { //println!("{:?}", event); if let Event::Window(window::Event::Opened { position, 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 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, work_order_input].spacing(5), row![ radio("BootLoader", DownloadType::Bootloader,self.selection,Message::TypeSelected), 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) ].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 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; } std::thread::spawn(move || { let mut download_wrapper = download_wrapper::DownloadWrapper::new(); let mut mes_service: MesService; 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(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(false)).unwrap(); return; } else if let Ok(res) = check_result { if !res { add_log("过站检测失败".to_string()); sender.send(Message::DownloadEnd(false)).unwrap(); return; } add_log("过站检测成功".to_string()); } } add_log("正在检查JLink连接".to_string()); if let Err(e) = download_wrapper.check_jlink() { add_log("JLink检查失败, 请检查是否安装驱动或连接是否异常".to_string()); sender.send(Message::DownloadEnd(false)).unwrap(); return; } add_log("JLink检查成功".to_string()); add_log("正在检查设备连接".to_string()); if let Err(e) = download_wrapper.check_device() { add_log("设备检查失败, 请检查设备是否连接".to_string()); sender.send(Message::DownloadEnd(false)).unwrap(); return; } add_log("设备检查成功".to_string()); match download_type { DownloadType::Bootloader =>{ add_log("正在擦除Flash".to_string()); if let Err(e) = download_wrapper.erase(){ add_log("擦除失败".to_string()); sender.send(Message::DownloadEnd(false)).unwrap(); return; } add_log("擦除成功".to_string()); add_log("正在下载BootLoader".to_string()); if let Err(e) = download_wrapper.download(download_type){ add_log("下载BootLoader失败".to_string()); sender.send(Message::DownloadEnd(false)).unwrap(); return; } add_log("下载成功".to_string()); } DownloadType::App =>{ add_log("正在下载App".to_string()); if let Err(e) = download_wrapper.download(download_type){ add_log("下载App失败".to_string()); sender.send(Message::DownloadEnd(false)).unwrap(); return; } add_log("下载成功".to_string()); } DownloadType::Rail=>{ add_log("正在下载Rail".to_string()); if let Err(e) = download_wrapper.download(download_type){ add_log("下载Rail失败".to_string()); sender.send(Message::DownloadEnd(false)).unwrap(); return; } add_log("下载成功".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(false)).unwrap(); return; } } add_log("正在上传数据".to_string()); let update_result = mes_service.update_station(mes_config.work_order, label, download_type); if let Err(e) = update_result { add_log(format!("上传失败: {:?}", e.to_string())); sender.send(Message::DownloadEnd(false)).unwrap(); return; } sender.send(Message::DownloadEnd(true)).unwrap(); return; } else { add_log("当前为离线模式".to_string()); sender.send(Message::DownloadEnd(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("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.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; } } } fn save_ui_log(&mut self){ todo!() } }