Compare commits
	
		
			10 Commits
		
	
	
		
			f1908d1fc4
			...
			49499a88b3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 49499a88b3 | |||
| 3dc2b3d432 | |||
| 61b2edca36 | |||
| db54dc28c0 | |||
| a72344e7cc | |||
| 3ce29dcd2b | |||
| e059757766 | |||
| b2de036c61 | |||
| 8a12c47a1f | |||
| 3ead5ce0b6 | 
							
								
								
									
										35
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										35
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -286,6 +286,15 @@ dependencies = [
 | 
			
		||||
 "cmake",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fltk-theme"
 | 
			
		||||
version = "0.7.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3539d27a815514b56af2afa6b8e7c6d6b9274a103239487d5a60daa6340a4868"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "fltk",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fluid-parser"
 | 
			
		||||
version = "0.1.15"
 | 
			
		||||
@@ -376,6 +385,12 @@ dependencies = [
 | 
			
		||||
 "mach2",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "itoa"
 | 
			
		||||
version = "1.0.11"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "js-sys"
 | 
			
		||||
version = "0.3.69"
 | 
			
		||||
@@ -769,6 +784,12 @@ dependencies = [
 | 
			
		||||
 "semver",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ryu"
 | 
			
		||||
version = "1.0.18"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "scopeguard"
 | 
			
		||||
version = "0.3.3"
 | 
			
		||||
@@ -816,6 +837,17 @@ dependencies = [
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_json"
 | 
			
		||||
version = "1.0.120"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "itoa",
 | 
			
		||||
 "ryu",
 | 
			
		||||
 "serde",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serialport"
 | 
			
		||||
version = "4.4.0"
 | 
			
		||||
@@ -1104,11 +1136,14 @@ dependencies = [
 | 
			
		||||
 "chrono",
 | 
			
		||||
 "fl2rust",
 | 
			
		||||
 "fltk",
 | 
			
		||||
 "fltk-theme",
 | 
			
		||||
 "lazy_static",
 | 
			
		||||
 "libsqlite3-sys",
 | 
			
		||||
 "log",
 | 
			
		||||
 "once_cell 0.1.8",
 | 
			
		||||
 "rusqlite",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "serialport",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ edition = "2021"
 | 
			
		||||
[dependencies]
 | 
			
		||||
anyhow = "^1.0.86"
 | 
			
		||||
fltk = { version = "^1.4", features = ["fltk-bundled"] }
 | 
			
		||||
fltk-theme = "0.7.2"
 | 
			
		||||
calamine = "0.25.0"
 | 
			
		||||
serialport = "4.4.0"
 | 
			
		||||
rusqlite = "0.25.4"
 | 
			
		||||
@@ -16,6 +17,13 @@ chrono = "0.4.38"
 | 
			
		||||
log = "0.4.22"
 | 
			
		||||
once_cell = "0.1.8"
 | 
			
		||||
lazy_static = "1.5.0"
 | 
			
		||||
serde= { version = "1.0.203", features = ["derive"] }
 | 
			
		||||
serde_json="1.0.120"
 | 
			
		||||
 | 
			
		||||
[build-dependencies]
 | 
			
		||||
fl2rust = "0.5.19"
 | 
			
		||||
 | 
			
		||||
[package.metadata.windows]
 | 
			
		||||
# For windows subsystem
 | 
			
		||||
link-args = ["/SUBSYSTEM:windows", "/ENTRY:mainCRTStartup"]
 | 
			
		||||
subsystem = "windows"
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								IMEI_SN_INDEX.xlsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								IMEI_SN_INDEX.xlsx
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1,10 +1,13 @@
 | 
			
		||||
use fltk::{prelude::*, window::Window, *};
 | 
			
		||||
use fltk::{*};
 | 
			
		||||
use crate::services::ui_service::UiService;
 | 
			
		||||
use fltk_theme::{WidgetTheme, ThemeType};
 | 
			
		||||
mod services;
 | 
			
		||||
mod models;
 | 
			
		||||
mod ui;
 | 
			
		||||
fn main() {
 | 
			
		||||
    let app = app::App::default();
 | 
			
		||||
    let widget_theme = WidgetTheme::new(ThemeType::Metro);
 | 
			
		||||
    widget_theme.apply();
 | 
			
		||||
    let mut ui_service = UiService::new();
 | 
			
		||||
    ui_service.init_components();
 | 
			
		||||
    app.run().unwrap();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								src/models/config_model.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/models/config_model.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug, Clone)]
 | 
			
		||||
pub(crate) struct ConfigModel{
 | 
			
		||||
    pub current_line: usize,
 | 
			
		||||
    pub excel_path: String,
 | 
			
		||||
    pub last_com: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for ConfigModel {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        ConfigModel {
 | 
			
		||||
            current_line: 0,
 | 
			
		||||
            excel_path: "".to_string(),
 | 
			
		||||
            last_com: "".to_string(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1 +1,2 @@
 | 
			
		||||
pub(crate) mod work_model;
 | 
			
		||||
pub(crate) mod config_model;
 | 
			
		||||
							
								
								
									
										21
									
								
								src/services/config_service.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/services/config_service.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
use std::fs;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
 | 
			
		||||
use crate::models::config_model::ConfigModel;
 | 
			
		||||
 | 
			
		||||
pub(crate) struct ConfigService;
 | 
			
		||||
 | 
			
		||||
impl ConfigService {
 | 
			
		||||
    pub fn save_config(model: ConfigModel) ->Result<()> {
 | 
			
		||||
        let json = serde_json::to_string(&model)?;
 | 
			
		||||
        fs::write("config.json", json)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn load_config() -> Result<ConfigModel> {
 | 
			
		||||
        let json = fs::read_to_string("config.json")?;
 | 
			
		||||
        let model: ConfigModel = serde_json::from_str(&json)?;
 | 
			
		||||
        Ok(model)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +1,10 @@
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::io::BufReader;
 | 
			
		||||
use calamine::{Reader, open_workbook, Xlsx, Data, DataType};
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use calamine::{Data, DataType, open_workbook, Reader, Xlsx};
 | 
			
		||||
 | 
			
		||||
use crate::models::work_model::WorkModel;
 | 
			
		||||
 | 
			
		||||
pub(crate) struct ExcelService {
 | 
			
		||||
    current_line: usize,
 | 
			
		||||
    work_book: Xlsx<BufReader<File>>,
 | 
			
		||||
    sheet: calamine::Range<Data>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +23,6 @@ impl ExcelService {
 | 
			
		||||
        };
 | 
			
		||||
        let mut service = ExcelService {
 | 
			
		||||
            current_line: 0,
 | 
			
		||||
            work_book,
 | 
			
		||||
            sheet,
 | 
			
		||||
        };
 | 
			
		||||
        service.current_line = service.find_first_useful_line();
 | 
			
		||||
@@ -52,6 +49,15 @@ impl ExcelService {
 | 
			
		||||
        current_line
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_current_line(&mut self, line: usize)->Result<(), String>{
 | 
			
		||||
        self.current_line = line;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn roll_back(&mut self){
 | 
			
		||||
        self.current_line -= 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_next_work_model(&mut self) -> Result<WorkModel, String> {
 | 
			
		||||
        let mut work_model = WorkModel::default();
 | 
			
		||||
        if let Some(imei) = self.sheet.get((self.current_line, 0)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,52 @@
 | 
			
		||||
use std::sync::{Arc, Mutex};
 | 
			
		||||
 | 
			
		||||
use chrono::Local;
 | 
			
		||||
use lazy_static::lazy_static;
 | 
			
		||||
use log::{Level, Metadata, Record, SetLoggerError};
 | 
			
		||||
 | 
			
		||||
type LogCallback = Box<dyn Fn(&str) + Send + Sync>;
 | 
			
		||||
 | 
			
		||||
pub struct FltkLogger {
 | 
			
		||||
    callback: Arc<Mutex<Option<LogCallback>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FltkLogger {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        FltkLogger {
 | 
			
		||||
            callback: Arc::new(Mutex::new(None)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_callback<F>(&self, callback: F)
 | 
			
		||||
    where
 | 
			
		||||
        F: Fn(&str) + 'static + Send + Sync,
 | 
			
		||||
    {
 | 
			
		||||
        let mut cb = self.callback.lock().unwrap();
 | 
			
		||||
        *cb = Some(Box::new(callback));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl log::Log for FltkLogger {
 | 
			
		||||
    fn enabled(&self, metadata: &Metadata) -> bool {
 | 
			
		||||
        metadata.level() <= Level::Info
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn log(&self, record: &Record) {
 | 
			
		||||
        if self.enabled(record.metadata()) {
 | 
			
		||||
            if let Some(callback) = self.callback.lock().unwrap().as_ref() {
 | 
			
		||||
                callback(&format!("{}: {} - {}\r\n",Local::now().format("%Y-%m-%d %H:%M:%S"), record.level(), record.args()));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn flush(&self) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lazy_static! {
 | 
			
		||||
   pub static ref LOGGER: FltkLogger = FltkLogger::new();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn init_logger() -> Result<(), SetLoggerError> {
 | 
			
		||||
    log::set_logger(&*LOGGER)
 | 
			
		||||
        .map(|()| log::set_max_level(log::LevelFilter::Info))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,3 +4,4 @@ pub(crate) mod excel_service;
 | 
			
		||||
pub(crate) mod ui_service;
 | 
			
		||||
pub(crate) mod work_service;
 | 
			
		||||
pub(crate) mod log_service;
 | 
			
		||||
pub(crate) mod config_service;
 | 
			
		||||
@@ -1,21 +1,23 @@
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use serialport::*;
 | 
			
		||||
use std::io::prelude::*;
 | 
			
		||||
use std::thread::sleep;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use serialport::*;
 | 
			
		||||
 | 
			
		||||
pub struct SerialService {
 | 
			
		||||
    port: Box<dyn SerialPort>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
impl SerialService {
 | 
			
		||||
    pub fn new(port_name: &str) -> Result<Box<Self>> {
 | 
			
		||||
        let port = serialport::new(port_name, 115_200)
 | 
			
		||||
        let port = new(port_name, 115_200)
 | 
			
		||||
            .timeout(Duration::from_millis(200))
 | 
			
		||||
            .open()?;
 | 
			
		||||
        Ok(Box::new(SerialService { port }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    pub fn get_ports() -> Result<Vec<String>> {
 | 
			
		||||
        let portinfos = serialport::available_ports()?;
 | 
			
		||||
        let mut portnames: Vec<String> = Vec::new();
 | 
			
		||||
@@ -54,6 +56,10 @@ impl SerialService {
 | 
			
		||||
            Err(err) => Err(err)
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    pub fn close_serial(&mut self)->Result<()>{
 | 
			
		||||
        todo!()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use crate::models::work_model::*;
 | 
			
		||||
use rusqlite::{Connection};
 | 
			
		||||
use chrono::Local;
 | 
			
		||||
use rusqlite::Connection;
 | 
			
		||||
 | 
			
		||||
use crate::models::work_model::*;
 | 
			
		||||
 | 
			
		||||
pub(crate) struct SqliteService {
 | 
			
		||||
    conn: Connection,
 | 
			
		||||
@@ -19,7 +20,9 @@ impl Default for SqliteService {
 | 
			
		||||
impl SqliteService {
 | 
			
		||||
    pub fn new(conn_str: String) -> Result<Self> {
 | 
			
		||||
        let conn = Connection::open(conn_str)?;
 | 
			
		||||
        Ok(Self { conn })
 | 
			
		||||
        let this = Self { conn };
 | 
			
		||||
        this.create_table()?;
 | 
			
		||||
        return Ok(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn create_table(&self) -> Result<()> {
 | 
			
		||||
@@ -45,20 +48,14 @@ impl SqliteService {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn check_sn(&self, sn: &str) -> Result<bool> {
 | 
			
		||||
    pub fn check_sn_has_insert(&self, sn: &str) -> Result<bool> {
 | 
			
		||||
        let mut stmt = self.conn.prepare("SELECT sn FROM write_results WHERE sn = ?1")?;
 | 
			
		||||
        let mut rows = stmt.query(&[&sn])?;
 | 
			
		||||
        Ok(rows.next()?.is_some())
 | 
			
		||||
    }
 | 
			
		||||
    pub fn check_imei(&self, imei: &str) -> Result<bool> {
 | 
			
		||||
        let mut stmt = self.conn.prepare("SELECT sn FROM write_results WHERE sn = ?1")?;
 | 
			
		||||
    pub fn check_imei_has_insert(&self, imei: &str) -> Result<bool> {
 | 
			
		||||
        let mut stmt = self.conn.prepare("SELECT imei FROM write_results WHERE imei = ?1")?;
 | 
			
		||||
        let mut rows = stmt.query(&[&imei])?;
 | 
			
		||||
        Ok(rows.next()?.is_some())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn create_sql_service_test() {
 | 
			
		||||
    let service = SqliteService::default();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,12 @@
 | 
			
		||||
use std::cell::RefCell;
 | 
			
		||||
use std::path::Path;
 | 
			
		||||
use std::process;
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
use fltk::prelude::{InputExt, WidgetExt};
 | 
			
		||||
use crate::models::work_model::WorkModel;
 | 
			
		||||
use crate::services::excel_service::ExcelService;
 | 
			
		||||
use crate::services::serial_service::SerialService;
 | 
			
		||||
use std::sync::{Arc, Mutex};
 | 
			
		||||
use fltk::prelude::{DisplayExt, InputExt, MenuExt, WidgetExt};
 | 
			
		||||
use fltk::text::TextBuffer;
 | 
			
		||||
 | 
			
		||||
use crate::services::log_service::LOGGER;
 | 
			
		||||
use crate::services::work_service::WorkService;
 | 
			
		||||
use crate::ui::main_ui;
 | 
			
		||||
use crate::ui::main_ui::*;
 | 
			
		||||
@@ -11,27 +14,106 @@ use crate::ui::main_ui::*;
 | 
			
		||||
pub(crate) struct UiService {
 | 
			
		||||
    ui: Rc<RefCell<UserInterface>>,
 | 
			
		||||
    work_service: Rc<RefCell<WorkService>>,
 | 
			
		||||
    current_com: String
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UiService {
 | 
			
		||||
    pub(crate) fn new() -> Self {
 | 
			
		||||
        // Create the Rc<RefCell<>> instances
 | 
			
		||||
        let ui = Rc::new(RefCell::new(main_ui::UserInterface::make_window()));
 | 
			
		||||
        UiService { ui, work_service: Rc::new(RefCell::new(WorkService::new())), current_com: "".to_string() }
 | 
			
		||||
        let work_service_rc = Rc::new(RefCell::new(WorkService::new()));
 | 
			
		||||
 | 
			
		||||
        // Clone the Rc<RefCell<>> to avoid moving them
 | 
			
		||||
        let work_service_clone = Rc::clone(&work_service_rc);
 | 
			
		||||
        let current_com;
 | 
			
		||||
        let should_current_line;
 | 
			
		||||
        let comports = WorkService::get_port_names();
 | 
			
		||||
        for com in comports {
 | 
			
		||||
            ui.borrow_mut().com_choice.add_choice(&com);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let work_service = work_service_clone.borrow_mut();
 | 
			
		||||
            current_com = work_service.config_model.last_com.clone();
 | 
			
		||||
            should_current_line = work_service.config_model.current_line;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if Path::exists(work_service_rc.borrow().config_model.excel_path.as_ref()) {
 | 
			
		||||
            ui.borrow_mut().excel_file_textbox.set_value(&work_service_rc.borrow().config_model.excel_path);
 | 
			
		||||
            ui.borrow_mut().current_line_textbox.set_value(&work_service_rc.borrow().config_model.current_line.to_string());
 | 
			
		||||
            let index = ui.borrow().com_choice.find_index(¤t_com);
 | 
			
		||||
            ui.borrow_mut().com_choice.set_value(index);
 | 
			
		||||
 | 
			
		||||
            work_service_rc.borrow_mut().excel_service.as_mut().unwrap().set_current_line(should_current_line).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let ui_service = UiService {
 | 
			
		||||
            ui,
 | 
			
		||||
            work_service: work_service_rc,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ui_service
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn init_components(&mut self) {
 | 
			
		||||
        self.init_excel();
 | 
			
		||||
        self.init_start_btn();
 | 
			
		||||
        self.init_set_line_btn();
 | 
			
		||||
        self.init_log();
 | 
			
		||||
        self.init_refresh_com_button();
 | 
			
		||||
        self.init_exit_callback();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn init_start_btn(&mut self) {
 | 
			
		||||
    fn init_refresh_com_button(&mut self) {
 | 
			
		||||
        let ui_rc = Rc::clone(&self.ui);
 | 
			
		||||
        let ui_rc_clone = Rc::clone(&ui_rc);
 | 
			
		||||
        self.ui.borrow_mut().refresh_com_btn.set_callback(move |_| {
 | 
			
		||||
            let comports = WorkService::get_port_names();
 | 
			
		||||
            let mut ui = ui_rc.borrow_mut();
 | 
			
		||||
            ui.com_choice.clear();
 | 
			
		||||
            for com in comports {
 | 
			
		||||
                ui.com_choice.add_choice(&com);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    fn init_exit_callback(&mut self) {
 | 
			
		||||
        let work_service_rc = Rc::clone(&self.work_service);
 | 
			
		||||
        ui_rc.borrow_mut().start_btn.set_callback(move |_| {
 | 
			
		||||
        self.ui.borrow_mut().main_window.set_callback(move |_| {
 | 
			
		||||
            let work_service = work_service_rc.borrow_mut();
 | 
			
		||||
            work_service.save_config();
 | 
			
		||||
            process::exit(0);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn init_set_line_btn(&mut self) {
 | 
			
		||||
        let ui_rc = Rc::clone(&self.ui);
 | 
			
		||||
        let work_service_rc = Rc::clone(&self.work_service);
 | 
			
		||||
        self.ui.borrow_mut().set_lines_btn.set_callback(move |_| {
 | 
			
		||||
            let ui_clone = ui_rc.clone();
 | 
			
		||||
            let mut set_dialog = UserInterface1::make_set_line_window();
 | 
			
		||||
            let work_service_rc_clone = work_service_rc.clone();
 | 
			
		||||
            set_dialog.ok_btn.set_callback(move |_| {
 | 
			
		||||
                if let Ok(line) = set_dialog.line_textbox.value().parse::<usize>() {
 | 
			
		||||
                    match work_service_rc_clone.borrow_mut().set_current_line(line) {
 | 
			
		||||
                        Ok(_) => {
 | 
			
		||||
                            ui_clone.borrow_mut().current_line_textbox.set_value(&line.to_string());
 | 
			
		||||
                        }
 | 
			
		||||
                        Err(msg) => fltk::dialog::message_default(&msg)
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    fltk::dialog::message_default(&"Only Support Number")
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn init_start_btn(&mut self) {
 | 
			
		||||
        let ui_rc_clone = Rc::clone(&self.ui);
 | 
			
		||||
        let work_service_rc = Rc::clone(&self.work_service);
 | 
			
		||||
        self.ui.borrow_mut().start_btn.set_callback(move |_| {
 | 
			
		||||
            let mut ui = ui_rc_clone.borrow_mut();
 | 
			
		||||
            let mut work_service = work_service_rc.borrow_mut();
 | 
			
		||||
            if let Some(mut buffer) = ui.log_content.buffer() {
 | 
			
		||||
                buffer.set_text("");
 | 
			
		||||
            }
 | 
			
		||||
            match work_service.excel_service {
 | 
			
		||||
                None => {
 | 
			
		||||
                    fltk::dialog::message_default("Please Select Excel File First;");
 | 
			
		||||
@@ -39,44 +121,54 @@ impl UiService {
 | 
			
		||||
                }
 | 
			
		||||
                Some(_) => {}
 | 
			
		||||
            }
 | 
			
		||||
            let selected_com = ui.com_textbox.value();
 | 
			
		||||
            let selected_com = ui.com_choice.choice().unwrap();
 | 
			
		||||
            if let Err(msg) = work_service.init_serial_service(selected_com) {
 | 
			
		||||
                fltk::dialog::message_default(&msg);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if let Some(mut model) = work_service.get_next_work_model() {
 | 
			
		||||
            if let Some(model) = work_service.get_next_work_model() {
 | 
			
		||||
                ui.current_write_model.set_value(&format!("{}: {}_{}", &model.line_number, &model.imei, &model.sn));
 | 
			
		||||
                match work_service.write_and_check(model) {
 | 
			
		||||
                    Ok(_) => {}
 | 
			
		||||
                    Err(msg) => { fltk::dialog::message_default(&msg) }
 | 
			
		||||
                    Ok(_) => {
 | 
			
		||||
                        let current_line = &work_service.get_current_line().to_string();
 | 
			
		||||
                        ui.current_line_textbox.set_value(current_line);
 | 
			
		||||
                    }
 | 
			
		||||
                    Err(msg) => {
 | 
			
		||||
                        work_service.roll_back();
 | 
			
		||||
                        fltk::dialog::message_default(&msg)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn init_log(&mut self) {
 | 
			
		||||
        let text_buffer = TextBuffer::default();
 | 
			
		||||
        self.ui.borrow_mut().log_content.set_buffer(text_buffer.clone());
 | 
			
		||||
        let text_buffer_arc = Arc::new(Mutex::new(text_buffer));
 | 
			
		||||
        LOGGER.set_callback(move |msg| {
 | 
			
		||||
            let mut text_buffer = text_buffer_arc.lock().unwrap();
 | 
			
		||||
            text_buffer.append(&msg);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn init_excel(&mut self) {
 | 
			
		||||
        let ui_rc = Rc::new(&self.ui);
 | 
			
		||||
        let ui_clone = Rc::clone(&self.ui);
 | 
			
		||||
        let ui_rc = Rc::clone(&self.ui);
 | 
			
		||||
        let work_service_clone = Rc::clone(&self.work_service);
 | 
			
		||||
        ui_rc.borrow_mut().select_file_btn.set_callback(move |_| {
 | 
			
		||||
            let mut ui = ui_clone.borrow_mut();
 | 
			
		||||
        self.ui.borrow_mut().select_file_btn.set_callback(move |_| {
 | 
			
		||||
            let mut ui = ui_rc.borrow_mut();
 | 
			
		||||
            let mut work_service = work_service_clone.borrow_mut();
 | 
			
		||||
            let mut dialog = fltk::dialog::FileDialog::new(fltk::dialog::FileDialogType::BrowseFile);
 | 
			
		||||
            dialog.show();
 | 
			
		||||
            let binding = dialog.filename();
 | 
			
		||||
            let path = binding.to_str().unwrap();
 | 
			
		||||
            let current_line = &work_service.get_current_line().to_string();
 | 
			
		||||
            if path == "" { return; }
 | 
			
		||||
            work_service.init_excel_service(path.to_string()).unwrap();
 | 
			
		||||
            let current_line = &work_service.get_current_line().to_string();
 | 
			
		||||
            work_service.config_model.excel_path = path.to_string();
 | 
			
		||||
            work_service.config_model.current_line = current_line.parse::<usize>().unwrap();
 | 
			
		||||
            ui.excel_file_textbox.set_value(path);
 | 
			
		||||
            ui.current_line_textbox.set_value(current_line);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn init_serial_service(&mut self) {
 | 
			
		||||
        let port_name = self.ui.borrow().com_textbox.value();
 | 
			
		||||
        match self.work_service.borrow_mut().init_serial_service(port_name) {
 | 
			
		||||
            Ok(_) => {}
 | 
			
		||||
            Err(_) => { fltk::dialog::message_default("Cant Open serial") }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +1,34 @@
 | 
			
		||||
use std::ptr::null;
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
 | 
			
		||||
use crate::models::config_model::ConfigModel;
 | 
			
		||||
use crate::models::work_model::WorkModel;
 | 
			
		||||
use crate::services::{serial_service::SerialService, sqlite_service::SqliteService, excel_service::ExcelService};
 | 
			
		||||
use crate::services::{config_service::ConfigService, excel_service::ExcelService, serial_service::SerialService, sqlite_service::SqliteService};
 | 
			
		||||
use crate::services::log_service::{init_logger};
 | 
			
		||||
 | 
			
		||||
pub struct WorkService {
 | 
			
		||||
    pub(crate) serial_service: Option<SerialService>,
 | 
			
		||||
    sqlite_service: SqliteService,
 | 
			
		||||
    pub(crate) excel_service: Option<ExcelService>,
 | 
			
		||||
    sqlite_service: SqliteService,
 | 
			
		||||
    pub(crate) config_model: ConfigModel,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WorkService {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        WorkService {
 | 
			
		||||
        init_logger().unwrap();
 | 
			
		||||
        let config = WorkService::load_config().unwrap_or_else(|_| ConfigModel::default());
 | 
			
		||||
        let mut this = WorkService {
 | 
			
		||||
            serial_service: Option::None,
 | 
			
		||||
            sqlite_service: SqliteService::new("data.db".to_string()).unwrap(),
 | 
			
		||||
            excel_service: Option::None,
 | 
			
		||||
            config_model: config,
 | 
			
		||||
        };
 | 
			
		||||
        if this.config_model.excel_path != "" {
 | 
			
		||||
            this.init_excel_service(this.config_model.excel_path.clone()).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    pub fn get_port_names() -> Vec<String> {
 | 
			
		||||
        //maybe should not use unwrap
 | 
			
		||||
        return SerialService::get_ports().unwrap();
 | 
			
		||||
@@ -24,6 +37,7 @@ impl WorkService {
 | 
			
		||||
    pub fn init_excel_service(&mut self, path: String) -> Result<bool, String> {
 | 
			
		||||
        return if let Ok(excel_service) = ExcelService::new(&path) {
 | 
			
		||||
            self.excel_service = Some(excel_service);
 | 
			
		||||
            self.config_model.excel_path = path;
 | 
			
		||||
            Ok(true)
 | 
			
		||||
        } else {
 | 
			
		||||
            Err("Cant Init ExcelService".to_string())
 | 
			
		||||
@@ -31,7 +45,51 @@ impl WorkService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn write_and_check(&mut self, model: WorkModel) -> Result<bool, String> {
 | 
			
		||||
        fltk::dialog::message_default(&format!("{}:{}:{}", model.imei, model.sn, model.line_number));
 | 
			
		||||
        log::info!("Write Start");
 | 
			
		||||
        match self.sqlite_service.check_imei_has_insert(&model.imei) {
 | 
			
		||||
            Ok(true) => {
 | 
			
		||||
                log::error!("Imei Exist: {}", model.imei);
 | 
			
		||||
                self.serial_service = None;
 | 
			
		||||
                return Err("Imei Exist".to_string());
 | 
			
		||||
            }
 | 
			
		||||
            _ => {log::info!("Check Imei From Db Pass")}
 | 
			
		||||
        }
 | 
			
		||||
        match self.sqlite_service.check_sn_has_insert(&model.sn) {
 | 
			
		||||
            Ok(true) => {
 | 
			
		||||
                log::error!("Imei Exist: {}", model.sn);
 | 
			
		||||
                self.serial_service = None;
 | 
			
		||||
                return Err("Imei Exist".to_string());
 | 
			
		||||
            }
 | 
			
		||||
            _ => {log::info!("Check Sn From Db Pass")}
 | 
			
		||||
        }
 | 
			
		||||
        if let Err(msg) = self.write_imei(model.imei.clone()) {
 | 
			
		||||
            log::error!("Write Imei Error:{}", msg);
 | 
			
		||||
            return Err(msg);
 | 
			
		||||
        }
 | 
			
		||||
        log::info!("Write Imei:{}", model.imei);
 | 
			
		||||
        if let Err(msg) = self.write_sn(model.sn.clone()) {
 | 
			
		||||
            log::error!("Write Sn Error:{}", msg);
 | 
			
		||||
            return Err(msg);
 | 
			
		||||
        }
 | 
			
		||||
        log::info!("Write Imei:{}", model.sn);
 | 
			
		||||
        log::info!("Write End");
 | 
			
		||||
        log::info!("Check Start");
 | 
			
		||||
        if let Err(msg) = self.check_imei(&model.imei) {
 | 
			
		||||
            log::error!("Check Imei Error:{}", msg);
 | 
			
		||||
            return Err(msg);
 | 
			
		||||
        }
 | 
			
		||||
        log::info!("Check Imei:{}", model.imei);
 | 
			
		||||
        if let Err(msg) = self.check_sn(&model.sn) {
 | 
			
		||||
            log::error!("Check Sn Error:{}", msg);
 | 
			
		||||
            return Err(msg);
 | 
			
		||||
        }
 | 
			
		||||
        log::info!("Check Sn:{}", model.sn);
 | 
			
		||||
        log::info!("Check End");
 | 
			
		||||
        self.serial_service = None;
 | 
			
		||||
        match self.sqlite_service.insert(model) {
 | 
			
		||||
            Err(msg) => { log::error!("Insert Error:{}",msg); }
 | 
			
		||||
            _ => {}
 | 
			
		||||
        }
 | 
			
		||||
        Ok(true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -42,15 +100,31 @@ impl WorkService {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_current_line(&mut self, line: usize) -> Result<(), String> {
 | 
			
		||||
        if let Some(service) = &mut self.excel_service {
 | 
			
		||||
            return service.set_current_line(line);
 | 
			
		||||
        } else {
 | 
			
		||||
            Err("Cant Set Line".to_string())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_next_work_model(&mut self) -> Option<WorkModel> {
 | 
			
		||||
        if let Some(service) = &mut self.excel_service {
 | 
			
		||||
            if let Ok(model) = service.get_next_work_model() {
 | 
			
		||||
                self.config_model.current_line = service.get_current_line();
 | 
			
		||||
                return Some(model);
 | 
			
		||||
            } else { return None; }
 | 
			
		||||
        }
 | 
			
		||||
        return None;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn roll_back(&mut self) {
 | 
			
		||||
        return if let Some(service) = &mut self.excel_service {
 | 
			
		||||
            service.roll_back();
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    pub fn init_sqlite_service(&mut self, db_path: String) -> Result<bool, String> {
 | 
			
		||||
        return if let Ok(sqlite_service) = SqliteService::new(db_path) {
 | 
			
		||||
            self.sqlite_service = sqlite_service;
 | 
			
		||||
@@ -63,6 +137,7 @@ impl WorkService {
 | 
			
		||||
    pub fn init_serial_service(&mut self, port_name: String) -> Result<bool, String> {
 | 
			
		||||
        return if let Ok(serial_service) = SerialService::new(&port_name) {
 | 
			
		||||
            self.serial_service = Some(*serial_service);
 | 
			
		||||
            self.config_model.last_com = port_name;
 | 
			
		||||
            Ok(true)
 | 
			
		||||
        } else {
 | 
			
		||||
            Err("Cant Init SerialService".to_string())
 | 
			
		||||
@@ -99,4 +174,12 @@ impl WorkService {
 | 
			
		||||
        }
 | 
			
		||||
        return Err("Cant Init ServialService".to_string());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn save_config(&self) {
 | 
			
		||||
        ConfigService::save_config(self.config_model.clone()).unwrap()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn load_config() -> Result<ConfigModel> {
 | 
			
		||||
        return ConfigService::load_config();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										68
									
								
								src/ui/main.fl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/ui/main.fl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
# data file for the Fltk User Interface Designer (fluid)
 | 
			
		||||
version 1.0400
 | 
			
		||||
header_name {.h}
 | 
			
		||||
code_name {.cxx}
 | 
			
		||||
class UserInterface {open
 | 
			
		||||
} {
 | 
			
		||||
  Function {make_window()} {open
 | 
			
		||||
  } {
 | 
			
		||||
    Fl_Window main_window {
 | 
			
		||||
      label WriteTool_RS open
 | 
			
		||||
      xywh {721 399 693 467} type Double visible
 | 
			
		||||
    } {
 | 
			
		||||
      Fl_Output current_write_model {
 | 
			
		||||
        label LastImeiAndSn
 | 
			
		||||
        xywh {135 107 375 35}
 | 
			
		||||
      }
 | 
			
		||||
      Fl_Output excel_file_textbox {
 | 
			
		||||
        label {CurrentExcelFile:}
 | 
			
		||||
        xywh {135 20 375 35}
 | 
			
		||||
      }
 | 
			
		||||
      Fl_Button select_file_btn {
 | 
			
		||||
        label SelectFile
 | 
			
		||||
        xywh {535 20 90 30}
 | 
			
		||||
      }
 | 
			
		||||
      Fl_Output current_line_textbox {
 | 
			
		||||
        label CurrentLine
 | 
			
		||||
        xywh {135 62 375 35}
 | 
			
		||||
      }
 | 
			
		||||
      Fl_Button set_lines_btn {
 | 
			
		||||
        label SetLines
 | 
			
		||||
        xywh {535 60 90 30}
 | 
			
		||||
      }
 | 
			
		||||
      Fl_Button start_btn {
 | 
			
		||||
        label Start
 | 
			
		||||
        xywh {532 156 138 35} shortcut 0xff0d color 51 labeltype ENGRAVED_LABEL labelsize 18
 | 
			
		||||
      }
 | 
			
		||||
      Fl_Text_Display log_content {selected
 | 
			
		||||
        xywh {10 210 670 250}
 | 
			
		||||
      }
 | 
			
		||||
      Fl_Choice com_choice {
 | 
			
		||||
        label {Com:} open
 | 
			
		||||
        xywh {135 156 235 35} down_box BORDER_BOX
 | 
			
		||||
      } {}
 | 
			
		||||
      Fl_Button refresh_com_btn {
 | 
			
		||||
        label Refresh
 | 
			
		||||
        xywh {390 156 120 35}
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class UserInterface1 {open
 | 
			
		||||
} {
 | 
			
		||||
  Function {make_set_line_window()} {} {
 | 
			
		||||
    Fl_Window {} {open
 | 
			
		||||
      xywh {1171 275 228 43} type Double visible
 | 
			
		||||
    } {
 | 
			
		||||
      Fl_Input line_textbox {
 | 
			
		||||
        label {Line:}
 | 
			
		||||
        xywh {40 5 130 35}
 | 
			
		||||
      }
 | 
			
		||||
      Fl_Button ok_btn {
 | 
			
		||||
        label ok
 | 
			
		||||
        xywh {175 5 45 35}
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								test_excel.xlsx
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test_excel.xlsx
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user