// use std::time::Duration; // use std::{thread, time}; // use serialport::{SerialPort, StopBits, Parity, FlowControl, DataBits}; // use std::io::{self, Read, Write, BufWriter}; use regex::Regex; use rustyline::Editor; use rustyline::error::ReadlineError; // use std::fs::OpenOptions; use std::io::{BufReader, BufRead, Write}; use std::net::{TcpListener, TcpStream}; // use std::collections::HashMap; use clap::{Parser}; pub mod perf { use chrono::{DateTime, Utc, Local}; pub struct Perf { start: DateTime, end: DateTime, delta_ms: i64, delta_mks: i64, delta_ns: i64, } impl Perf { pub fn new() -> Perf { Perf { start: Utc::now(), end: Utc::now(), delta_ms: 0, delta_mks: 0, delta_ns: 0, } } pub fn start(&mut self) { self.start = Utc::now(); } // measure: "ms" or "mks" pub fn stop(&mut self, out_msg: &str, measure: &str) { self.end = Utc::now(); self.delta_ms = (self.end - self.start).num_microseconds().unwrap()/1000; self.delta_mks = (self.end - self.start).num_microseconds().unwrap(); self.delta_ns = (self.end - self.start).num_nanoseconds().unwrap(); if measure == "mks" { println!("{} (mks): {:?}", out_msg, self.delta_mks); } else if measure == "ns" { println!("{} (ns): {:?}", out_msg, self.delta_ns); } else { println!("{} (ms): {:?}", out_msg, self.delta_ms); } } pub fn stop_restart(&mut self, out_msg: &str, measure: &str) { self.stop(out_msg, measure); self.start = Utc::now(); } pub fn cur_time(format: &str) -> String { let now = Local::now(); let fmt = now.format(format).to_string(); return fmt; } } } use crate::perf::Perf; pub mod uart_transact { const MAX_BUF: usize = 4096; use serialport::{SerialPort, StopBits, Parity, FlowControl, DataBits}; use std::time::Duration; use crate::perf::Perf; use std::io::{self, Write, BufWriter, BufReader, BufRead}; use std::fs::{File, OpenOptions}; use std::path::Path; #[derive(PartialEq)] pub enum TypeEn { Rx = 0, Tx, RxTx, TxRx, } #[derive(Debug, Clone)] pub struct PortSettingsT { baud_rate: u32, data_bits: DataBits, parity: Parity, stop_bits: StopBits, flow_ctrl: FlowControl, timeout_msec: u64, } impl PortSettingsT { pub fn new(baud_rate: u32, data_bits_: u8, parity_: &str, stop_bits_: u8, flow_ctrl_: &str, timeout_msec: u64) -> Result { let data_bits = match data_bits_ { 5 => DataBits::Five, 6 => DataBits::Six, 7 => DataBits::Seven, 8 => DataBits::Eight, _ => { return Err(format!( "error: the number of data bits specified is incorrect (type from 5 to 8)")); } }; let parity = match parity_ { "Even" => Parity::Even, "Odd" => Parity::Odd, "None" => Parity::None, _ => { return Err(format!( "error: the parity of data specified is incorrect (type \"Even\", \"Odd\" or \"None\")")); } }; let stop_bits = match stop_bits_ { 1 => StopBits::One, 2 => StopBits::Two, _ => { return Err(format!( "error: the stop bits of data specified is incorrect (type 1 or 2)")); } }; let flow_ctrl = match flow_ctrl_ { "None" => FlowControl::None, "Software" => FlowControl::Software, "Hardware" => FlowControl::Hardware, _ => { return Err(format!( "error: the parity of data specified is incorrect (type \"None\", \"Software\" or \"Hardware\")")); } }; Ok(Self { baud_rate, data_bits, parity, stop_bits, flow_ctrl, timeout_msec }) } } struct Logger { out: BufWriter, log_flag: bool, } impl Logger { fn new>(filename: P, log_flag: bool) -> Result { let path = Path::new(filename.as_ref()); let file = match path.exists() { true => OpenOptions::new().write(true).append(true).open(filename.as_ref())?, false => File::create(filename.as_ref())? }; let out = BufWriter::new(file); Ok(Self { out, log_flag }) } fn log(&mut self, msg: &str) -> Result<(), io::Error> { if self.log_flag == true { writeln!(&mut self.out, "{} {};{:?}", Perf::cur_time("%Y.%m.%d"), Perf::cur_time("%H:%M:%S.%3f"), msg.trim())?; self.out.flush()?; } Ok(()) } } pub struct UartTransact { type_: TypeEn, port_name: String, port_settings: PortSettingsT, tx_packet: String, rx_packet: String, port: Box, save_data_flag: bool, save_cmd_flag: bool, data_writer: Logger, cmd_writer: Logger, } impl UartTransact { pub fn new(trans_type: &str, port_name: &str, port_settings: PortSettingsT, save_data_flag: bool, save_cmd_flag: bool) -> Result { let mut type_ = TypeEn::TxRx; if trans_type == "TxRx" { type_ = TypeEn::TxRx; } else if trans_type == "Tx" { type_ = TypeEn::Rx; } else if trans_type == "RxTx" { type_ = TypeEn::RxTx; } else if trans_type == "Rx" { type_ = TypeEn::Rx; } let mut filename = format!("../log/data_{}.log", Perf::cur_time("%Y%m%d")); let data_writer = Logger::new(filename.clone(), save_data_flag)?; filename = format!("../log/commands_{}.log", Perf::cur_time("%Y%m%d")); let cmd_writer = Logger::new(filename.clone(), save_data_flag)?; let port = serialport::new(port_name, port_settings.baud_rate) .timeout(Duration::from_millis(port_settings.timeout_msec)).open().unwrap(); let tx_packet = String::new(); let rx_packet = String::new(); Ok( Self { type_, port_name: port_name.to_string(), port_settings, save_data_flag, save_cmd_flag, data_writer, cmd_writer, port, tx_packet, rx_packet, }) } pub fn add_packet_for_tx(&mut self, buf: String, size: u32) -> Result<(), String> { if size as usize >= MAX_BUF { return Err(format!("too much data for transmit! Data size must be < {} bytes", MAX_BUF)); } else { self.tx_packet = buf; } return Ok(()); } pub fn start_transact(&mut self) -> Result<(), String> { if self.type_ == TypeEn::TxRx { self.cmd_writer.log(&self.tx_packet).unwrap(); // println!("write to port: ({:?})", self.tx_packet.as_bytes()); match self.port.write(&self.tx_packet.as_bytes()) { Ok(_) => {}, Err(e) => eprintln!("Error sending data: {}", e), } let mut reader = BufReader::new(&mut self.port); let mut s = String::new(); if let Err(err) = reader.read_line(&mut s) { println!("error: {}", err); return Ok(()); } if s.len() > 0 { self.rx_packet = s.clone(); self.data_writer.log(&s).unwrap(); } } Ok(()) } pub fn get_received_packet(&mut self) -> String { self.rx_packet.clone() } fn set_port_settings(port_name: &str, set: PortSettingsT) -> Result, String> { let mut port = match serialport::new(port_name, set.baud_rate) .timeout(Duration::from_millis(set.timeout_msec)).open() { Ok(p) => p, Err(msg) => return Err(format!("error: failed to open serial port {} ({})", port_name, msg)), }; match port.set_baud_rate(set.baud_rate) { Ok(_) => {}, Err(msg) => return Err(format!("error: failed to open serial port {} ({})", port_name, msg)), } match port.set_data_bits(set.data_bits) { Ok(_) => {}, Err(msg) => return Err(format!("error: failed to open serial port {} ({})", port_name, msg)), } match port.set_parity(set.parity) { Ok(_) => {}, Err(msg) => return Err(format!("error: failed to open serial port {} ({})", port_name, msg)), } match port.set_stop_bits(set.stop_bits) { Ok(_) => {}, Err(msg) => return Err(format!("error: failed to open serial port {} ({})", port_name, msg)), } match port.set_flow_control(set.flow_ctrl) { Ok(_) => {}, Err(msg) => return Err(format!("error: failed to open serial port {} ({})", port_name, msg)), } return Ok(port); } } } use crate::uart_transact::*; fn read_str(s: &str) -> Result, String> { let re = match Regex::new(r#""([^"]+)"|(\S+)"#) { Ok(val) => val, Err(_) => { return Err(format!("Error in regex!")); } }; let res: Vec = re.find_iter(&s) .map(|match_| match_.as_str().to_owned().replace("\"", "")) .collect(); Ok(res) } fn uart_cmd_interp(cmd: &str) -> String { let mut out = String::new(); let args = match read_str(&cmd) { Ok(arg) => arg, Err(msg) => { return format!("error: failed read input string: {}", msg); } }; let args_cnt = args.len(); if args_cnt == 0 { return format!("{}", out); } let cmd_head = &args[0].clone(); let mut timeout: u64 = 200; let mut port_set = PortSettingsT::new(9600, 8, "None", 1, "None", timeout).unwrap(); let mut save_data_flag = true; let mut save_cmd_flag = true; let mut trans_type = "TxRx"; if cmd_head == "exit" { return format!("exit"); } else if cmd_head.contains("help") { out.push_str(&format!("Serial port terminal supports the following commands:")); out.push_str(&format!("\n1. exit - \t\texcecute quit from this program")); out.push_str(&format!("\n2. help - \t\texplain how to use this program")); out.push_str(&format!("\n3. show_ports - \tshows all available serial ports")); out.push_str(&format!("\n4. send - \t\tsends specified \n\tUse: send \n\tExample: send /dev/ttyUSB0 \"command data1 data2\"")); out.push_str(&format!("\n5. set_port - set port with specified settings\n \tUse: set_port \n \tWhere: \n\t\t - port name \n\t\t - speed (9600 for example) \n\t\t: 5, 6, 7, 8 \n\t\t: None, Even, Odd \n\t\t: 1, 2 \n\t\t: None, Software, Hardware \n\t\t - timeout for receive and transmit in msec \n\t\t: tx, rx, TxRx, RxTx (tx - transmit, rx - receive) \n\t\t: true, false \n\tExample: set_port /dev/ttyUSB0 9600 8 Even 1 None 1000 TxRx true\n")); return out; } else if cmd_head.contains("show_ports") { match serialport::available_ports() { Ok(ports) => { for port in ports { out.push_str(&format!("{}: {:?}\n", port.port_name, port.port_type)); } } Err(msg) => { return format!("error while get all available serial port list: {}", msg); } } return out; } else if cmd_head == "send" { if args_cnt != 3 { return format!("Error while parse command! {}", "arguments count must be 2"); } else { let port_name = &args[1]; let mut uart_trans = UartTransact::new(trans_type, port_name, port_set, save_data_flag, save_cmd_flag).unwrap(); let mut data_out = args[2].clone(); data_out.push_str(std::str::from_utf8(b"\x0D\x0A").unwrap()); // CR + LF uart_trans.add_packet_for_tx(data_out.clone(), data_out.len() as u32).unwrap(); uart_trans.start_transact().unwrap(); let s = uart_trans.get_received_packet(); let data = format!("{}: {}", Perf::cur_time("%H:%M:%S.%3f"), s.trim()); return data; } } else if cmd_head.contains("set_port") { if args_cnt != 10 { return format!("Error while parse command! {}", "arguments count must be 9"); } else { let port_name = &args[1]; let speed = args[2].parse::().unwrap(); let data_bits = args[3].parse::().unwrap(); let parity = &args[4]; let stop_bits = args[5].parse::().unwrap(); let flow_ctrl = &args[6]; timeout = args[7].parse::().unwrap(); trans_type = &args[8]; save_data_flag = args[9].parse::().unwrap(); save_cmd_flag = save_data_flag; port_set = PortSettingsT::new(speed, data_bits, parity, stop_bits, flow_ctrl, timeout).unwrap(); let uart_trans = UartTransact::new(trans_type, &port_name, port_set.clone(), save_data_flag, save_cmd_flag).unwrap(); let data = format!("{}: port {} was set with the following settings: {:?}", Perf::cur_time("%H:%M:%S.%3f"), port_name, port_set); return data; } } return out; } fn cli_mode() { let fname_hist = "../settings/cmd_history.log".to_string(); let mut editor = Editor::<()>::new(); if editor.load_history(&fname_hist).is_err() { println!("error while reading cmd_history.log file!", ); return; } loop { let s: String = match editor.readline(">> ") { Ok(s_) => s_.to_string(), Err(ReadlineError::Interrupted) => { editor.save_history(&fname_hist) .expect("error while save the command to the history file (cmd_history.log)"); return; }, Err(ReadlineError::Eof) => { editor.save_history(&fname_hist) .expect("error while save the command to the history file (cmd_history.log)"); return; }, Err(msg) => { println!("{}{:?}", "unknown command", msg); return; } }; editor.add_history_entry(&s); let reply = uart_cmd_interp(&s); if reply == "exit" { break; } else { println!("{}", reply); } } editor.save_history(&fname_hist).unwrap(); } fn tcp_server() { let listener = TcpListener::bind("127.0.0.1:10000").unwrap(); println!("TCP-server is running"); for stream in listener.incoming() { match stream { Ok(stream) => { println!("connect to client: {:?}", stream); handle_client(stream); }, Err(ref msg) => { println!("error: failed attempt to set connection with client: ({:?}): {}", stream, msg); } } } } fn handle_client(mut stream: TcpStream) { loop { let mut perf = Perf::new(); let mut reader = BufReader::new(&stream); let mut rx_data = String::new(); if let Err(msg) = reader.read_line(&mut rx_data) { println!("error: failed attempt to read data from client: {}", msg); return; } // println!("rx_data: ({})", rx_data.trim()); perf.start(); if rx_data.starts_with("send") || rx_data.starts_with("help") || rx_data.starts_with("set_port") || rx_data.starts_with("show_ports") { let reply = uart_cmd_interp(&rx_data.trim()); // reply.push('\n'); if let Err(msg) = stream.write_all(reply.as_bytes()) { println!("error: failed attempt to send data to client: {}", msg); return; } } else if rx_data.starts_with("exit") { println!("The client requested to close the connection"); return; } else if rx_data == "" { let data = format!("empty string! "); if let Err(msg) = stream.write_all(data.as_bytes()) { println!("error: failed attempt to send data to client: {}", msg); return; } return; } else { let data = format!("unsuppoted command: ({})\n", rx_data.trim()); if let Err(msg) = stream.write_all(data.as_bytes()) { println!("error: failed attempt to send data to client: {}", msg); return; } } perf.stop("speed: ", "mks"); } } #[derive(Parser, Debug)] #[command(version, author, about, long_about = None)] struct Cli { /// working mode: tcp - remote control with TCP/IP, cli - command line interface #[arg(long, short = 'm', value_name = "MODE")] mode: Option, } fn main() { let cli = Cli::parse(); // println!("{:#?}", cli); if let Some(mode) = &cli.mode { if mode == "cli" { cli_mode(); } else if mode == "tcp" { tcp_server(); } else { println!("error: incorrect parameter. type --help"); } } } // fn print_type_of(_: &T) { // println!("{}", std::any::type_name::()); // } // perf.stop("time for transaction", "mks"); // let mut buffer = Vec::new(); // match port.read_to_end(&mut buffer) { // Ok(_) => { // if buffer.len() > 0 { // let data = format!("{}: {:?}", cur_time("%H:%M:%S"), buffer); // println!("{}", data); // if save_data_flag == true { // writeln!(data_writer, "{}", // format!("{};{};{}", // cur_time("%Y.%m.%d"), // cur_time("%H:%M:%S"), // String::from_utf8(buffer).expect("msg"))) // .expect("err"); // } // } // }, // Err(msg) => eprintln!("{}", msg), // } // let mut cnt: u32 = 0; // let mut i = 0; // let mut buf: String = String::new(); // let start = Utc::now(); // let mut timeout_flag = false; // let mut delta1 = 0; // while cnt == 0 { // cnt = match port.bytes_to_read() { // Ok(c) => c, // Err(msg) => { println!("{}", msg); return; } // }; // let delta = Utc::now() - start; // delta1 = delta.num_microseconds().unwrap()/1000; // if delta1 > timeout_ms { // println!("timeout: {}", delta1); // timeout_flag = true; // break; // }; // } // println!("time (ms): {}", delta1); // // println!("time (ms): {:?}", delta.num_microseconds().unwrap()/1000); // if timeout_flag == false { // while i < 500000 { // // thread::sleep(time::Duration::from_millis(10)); // i += 1; // let mut buffer = [0u8; 4096]; // cnt = match port.bytes_to_read() { // Ok(c) => c, // Err(msg) => { println!("{}", msg); return; } // }; // if cnt > 0 { // match port.read(&mut buffer) { // Ok(count) => { // let s = std::str::from_utf8(&buffer[..count]).unwrap(); // buf.push_str(s); // println!("{}", s); // }, // Err(ref e) if e.kind() == io::ErrorKind::TimedOut => { println!("timeout!"); }, // Err(e) => eprintln!("error reading from port ({}): {}", port_name, e), // } // } // else {} // } // } // if buf.len() > 0 { // let data = format!("{}: {:?}", cur_time("%H:%M:%S"), buf); // println!("{}", data); // if save_data_flag == true { // writeln!(data_writer, "{};{};{}", cur_time("%Y.%m.%d"), cur_time("%H:%M:%S"), buf.trim()).expect("err"); // } // }