diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..58defa7 --- /dev/null +++ b/README.markdown @@ -0,0 +1,84 @@ +# uart_server + +The uart server is intended for work with serial ports (COM-ports): UART (RS232), RS485 via RS485-USB and etc. interface converters + +## Contents + +- **Setup** +- **Using** +- work with uart_server in CLI mode +- work with uart_server in TCP mode +- **Output uart_server data files description** +- **Contacts** + +## Setup +1. Install Rust compiler (if you don't have). +Installation on Linux: +``` +curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh +``` + +Installation on Windows: +Go to address https://www.rust-lang.org/tools/install and follow instructions +For more detailed information you can go to: https://doc.rust-lang.ru/book/ch01-01-installation.html + +**Instruction for setup uart_server project** + +2. Clone the repo to your computer: + +``` +git clone http://heagit.cosmos.ru/gamkov/uart_server.git +``` + +3. Enter the repo and compile it: +``` +cd +cargo build --release +``` + +After running this commands you will get an execution file (uart_server) in the following directory: +\/target/release/ + +## Using +### work with uart_server in CLI mode +1. See current available serial ports (Ubuntu): +``` +sudo setserial -g /dev/ttyUSB* +``` +2. for access to operation with hardware run program with sudo (specify available serial port): +``` +sudo ./uart_server -m cli -p /dev/ttyUSB1 +``` +3. In cli mode type 'help' in order to get information about programs command system + +### work with uart_server in TCP mode +1. See current available serial ports (Ubuntu): +``` +sudo setserial -g /dev/ttyUSB* +``` +2. for access to operation with hardware run program with sudo (specify available serial port and desired IP-address, port for uart-server): +``` +sudo ./uart_server -m tcp:127.0.0.1:10000 -p /dev/ttyUSB1 +``` +## Output uart_server data files description +log files are located in directory: +\/log/ + +**description:** +cmd_\\_\.log - file with output data to serial port +data_\\_\.log - file with input data from serial port +cmd_data_\\_\.log - file with output and input data to and from serial port + +where \ - file generation date, + \ - user filename addition (for more information type help in CLI mode) + +**File data log fromat:** +data represented in csv-format (with \';\' separation) +*column 1:* timestamp in format: YYYY.mm.dd HH:MM:SS.zzz (for example: 2025.03.26 16:00:01.158) +*column 2 for cmd and cmd_data log files:* data that was sent to the serial port +*column 2 for data log file:* data that was received from serial port +*column 3 for cmd_data log file:* data that was received from serial port +*column 3 for cmd and data log files:* no data available + +## Contatcs +For questions about the program, please contact Danila Gamkov, mail: danila_gamkov@cosmos.ru diff --git a/release/uart_server b/release/uart_server new file mode 100755 index 0000000..73bfaf3 Binary files /dev/null and b/release/uart_server differ diff --git a/src/main.rs b/src/main.rs index 2e9da08..622bcc0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -146,7 +146,10 @@ pub mod uart_transact { let file = match path.exists() { true => OpenOptions::new().write(true).append(true).open(filename.as_ref())?, - false => File::create(filename.as_ref())? + false => { + println!("path: {:?}", path); + File::create(filename.as_ref())? + }, }; let out = BufWriter::new(file); @@ -185,12 +188,13 @@ pub mod uart_transact { tx_packet: String, rx_packet: String, - port: Box, + pub port: Box, save_data_flag: bool, save_cmd_flag: bool, data_writer: Logger, cmd_writer: Logger, cmd_data_writer: Logger, + specific_fname: String } impl UartTransact { @@ -198,7 +202,8 @@ pub mod uart_transact { port_name: &str, port_settings: PortSettingsT, save_data_flag: bool, - save_cmd_flag: bool) -> Result { + save_cmd_flag: bool, + specific_fname: &str) -> Result { let mut type_ = TypeEn::TxRx; if trans_type == "TxRx" { type_ = TypeEn::TxRx; } @@ -206,17 +211,23 @@ pub mod uart_transact { 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 mut filename = format!("../log/data_{}_{}.log", + Perf::cur_time("%Y%m%d"), + specific_fname); let data_writer = Logger::new(filename.clone(), save_data_flag)?; - filename = format!("../log/commands_{}.log", Perf::cur_time("%Y%m%d")); + filename = format!("../log/cmd_{}_{}.log", + Perf::cur_time("%Y%m%d"), + specific_fname); let cmd_writer = Logger::new(filename.clone(), save_data_flag)?; - filename = format!("../log/cmd_data_{}.log", Perf::cur_time("%Y%m%d")); + filename = format!("../log/cmd_data_{}_{}.log", + Perf::cur_time("%Y%m%d"), + specific_fname); let cmd_data_writer = Logger::new(filename.clone(), save_data_flag)?; - let mut port = Self::set_port_settings(port_name, + let port = Self::open_port_settings(port_name, port_settings.clone()).unwrap(); - println!("{:#?}", port); + // println!("port: {:#?}", port); let tx_packet = String::new(); let rx_packet = String::new(); @@ -231,6 +242,7 @@ pub mod uart_transact { port, tx_packet, rx_packet, + specific_fname: specific_fname.to_string(), }) } @@ -244,9 +256,10 @@ pub mod uart_transact { return Ok(()); } - pub fn start_transact(&mut self) -> Result<(), String> { + pub fn start_transact(&mut self, type_: &str) -> + Result<(), String> { - if self.type_ == TypeEn::TxRx { + if type_ == "txRx" { self.cmd_writer.log(&self.tx_packet).unwrap(); match self.port.write(&self.tx_packet.as_bytes()) { @@ -259,7 +272,10 @@ pub mod uart_transact { 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); + let msg = format!("serial port error: {}", err); + self.rx_packet = msg.clone(); + self.data_writer.log(&msg).unwrap(); + self.cmd_data_writer.log_cmd_data(&self.tx_packet, &msg).unwrap(); return Ok(()); } @@ -269,6 +285,18 @@ pub mod uart_transact { self.cmd_data_writer.log_cmd_data(&self.tx_packet, &s).unwrap(); } } + else if type_ == "tx" { + self.cmd_writer.log(&self.tx_packet).unwrap(); + self.cmd_data_writer.log_cmd_data(&self.tx_packet, + "").unwrap(); + + match self.port.write(&self.tx_packet.as_bytes()) { + Ok(_) => {}, + Err(e) => eprintln!("Error sending data: {}", e), + } + + self.port.flush().unwrap(); + } Ok(()) } @@ -276,7 +304,7 @@ pub mod uart_transact { self.rx_packet.clone() } - fn set_port_settings(port_name: &str, + fn open_port_settings(port_name: &str, set: PortSettingsT) -> Result, String> { let mut port = match serialport::new(port_name, set.baud_rate) @@ -326,7 +354,7 @@ fn read_str(s: &str) -> Result, String> { Ok(res) } -fn uart_cmd_interp(cmd: &str) -> String { +fn uart_cmd_interp(cmd: &str, uart_trans: &mut UartTransact) -> String { let mut out = String::new(); let args = match read_str(&cmd) { @@ -338,25 +366,19 @@ fn uart_cmd_interp(cmd: &str) -> String { let args_cnt = args.len(); if args_cnt == 0 { return format!("{}", out); } - let cmd_head = &args[0].clone(); + let cmd_head = &args[0].trim(); - let mut timeout: u64 = 1000; - let mut port_set = PortSettingsT::new(19200, 8, "Even", 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"); } + 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")); + out.push_str(&format!("\n4. send - \t\tsends specified to serial port\n\tUse: send \n\tExample: send txRx \"command data1 data2\"")); + out.push_str(&format!("\n5. open_port - open serial port with specified settings\n \tUse: open_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\t\t: specify a specific string that will be added to the filename for logging commands and data \n\tExample: open_port /dev/ttyUSB0 9600 8 Even 1 None 1000 TxRx true device1\n")); return out; } - else if cmd_head.contains("show_ports") { + else if *cmd_head == "show_ports" { match serialport::available_ports() { Ok(ports) => { for port in ports { @@ -368,33 +390,38 @@ fn uart_cmd_interp(cmd: &str) -> String { return out; } - else if cmd_head == "send" { + 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 transact_type = &args[1]; // let mut perf = Perf::new(); // perf.start(); + // println!("port_set: {:#?}", uart_trans.port); - println!("port_set: {:#?}", port_set); - let mut uart_trans = UartTransact::new(trans_type, port_name, - port_set, save_data_flag, save_cmd_flag).unwrap(); - - // perf.stop("time for create port: ", "mks"); let mut data_out = args[2].clone(); data_out.push_str(std::str::from_utf8(b"\x0D\x0A").unwrap()); // CR + LF - println!("data out: {:?}", data_out); + // perf.stop_restart("time for push_str: ", "mks"); + // println!("data out: {:?}", data_out); uart_trans.add_packet_for_tx(data_out.clone(), data_out.len() as u32).unwrap(); - uart_trans.start_transact().unwrap(); + // perf.stop_restart("time for add packet for tx: ", "mks"); + uart_trans.start_transact(&transact_type).unwrap(); + // perf.stop_restart("time for transaction: ", "mks"); - let s = uart_trans.get_received_packet(); - let data = format!("{}: {}", Perf::cur_time("%H:%M:%S.%3f"), s.trim()); + let mut data = "".to_string(); + + if transact_type == "txRx" { + let s = uart_trans.get_received_packet(); + data = format!("{}: {}", Perf::cur_time("%H:%M:%S.%3f"), s.trim()); + + } + return data; } } - else if cmd_head.contains("set_port") { + else if *cmd_head == "open_port" { - if args_cnt != 10 { return format!("Error while parse command! {}", "arguments count must be 9"); } + if args_cnt != 11 { return format!("Error while parse command! {}", "arguments count must be 10"); } else { let port_name = &args[1]; let speed = args[2].parse::().unwrap(); @@ -402,17 +429,22 @@ fn uart_cmd_interp(cmd: &str) -> String { 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; + let timeout = args[7].parse::().unwrap(); + let trans_type = &args[8]; + let save_data_flag = args[9].parse::().unwrap(); + let save_cmd_flag = save_data_flag; + let specific_fname = &args[10]; - port_set = PortSettingsT::new(speed, data_bits, parity, stop_bits, + let port_set = PortSettingsT::new(speed, data_bits, parity, stop_bits, flow_ctrl, timeout).unwrap(); - println!("port_set: {:#?}", port_set); - let uart_trans = UartTransact::new(trans_type, &port_name, - port_set.clone(), save_data_flag, save_cmd_flag).unwrap(); + // println!("port_set: {:#?}", port_set); + *uart_trans = UartTransact::new(trans_type, + &port_name, + port_set.clone(), + save_data_flag, + save_cmd_flag, + &specific_fname).unwrap(); let data = format!("{}: port {} was set with the following settings: {:?}", Perf::cur_time("%H:%M:%S.%3f"), @@ -421,12 +453,14 @@ fn uart_cmd_interp(cmd: &str) -> String { return data; } } + else { + return "unknown command!".to_string(); + } - return out; } -fn cli_mode() { +fn cli_mode(uart_trans: &mut UartTransact) { let fname_hist = "../settings/cmd_history.log".to_string(); let mut editor = Editor::<()>::new(); if editor.load_history(&fname_hist).is_err() { @@ -457,7 +491,7 @@ fn cli_mode() { editor.add_history_entry(&s); - let reply = uart_cmd_interp(&s); + let reply = uart_cmd_interp(&s, uart_trans); if reply == "exit" { break; } else { println!("{}", reply); } @@ -467,15 +501,15 @@ fn cli_mode() { editor.save_history(&fname_hist).unwrap(); } -fn tcp_server() { - let listener = TcpListener::bind("127.0.0.1:10000").unwrap(); - println!("TCP-server is running"); +fn tcp_server(uart_transact: &mut UartTransact, addr: &str) { + let listener = TcpListener::bind(addr).unwrap(); + println!("TCP-server is running on address: {}", addr); for stream in listener.incoming() { match stream { Ok(stream) => { println!("connect to client: {:?}", stream); - handle_client(stream); + handle_client(stream, uart_transact); }, Err(ref msg) => { @@ -486,7 +520,7 @@ fn tcp_server() { } } -fn handle_client(mut stream: TcpStream) { +fn handle_client(mut stream: TcpStream, uart_transact: &mut UartTransact) { loop { // let mut perf = Perf::new(); // perf.start(); @@ -500,12 +534,15 @@ fn handle_client(mut stream: TcpStream) { // println!("rx_data: ({})", rx_data.trim()); if rx_data.starts_with("send") || rx_data.starts_with("help") || - rx_data.starts_with("set_port") || + rx_data.starts_with("open_port") || rx_data.starts_with("show_ports") { - let reply = uart_cmd_interp(&rx_data.trim()); + let reply = uart_cmd_interp(&rx_data.trim(), uart_transact); // reply.push('\n'); - println!("rx_uart: ({})", reply.trim()); + if rx_data.starts_with("send") || + rx_data.starts_with("open_port") { + println!("rx_uart: ({})", reply.trim()); + } if let Err(msg) = stream.write_all(reply.as_bytes()) { println!("error: failed attempt to send data to client: {}", msg); return; @@ -540,20 +577,39 @@ fn handle_client(mut stream: TcpStream) { #[derive(Parser, Debug)] #[command(version, author, about, long_about = None)] struct Cli { - /// working mode: tcp - remote control with TCP/IP, cli - command line interface + /// working mode: + /// tcp:ip:port - remote control with TCP/IP, or cli - command line interface. + /// Example1: -m tcp:127.0.0.1:10000. + /// Example2: -m cli #[arg(long, short = 'm', value_name = "MODE")] mode: Option, + + /// port name: type serial port name in order to start working + #[arg(long, short = 'p', value_name = "PORT_NAME")] + port_name: Option, } fn main() { let cli = Cli::parse(); - // println!("{:#?}", cli); + + let mut port_name = "/dev/ttyUSB1".to_string(); + let port_set = PortSettingsT::new(9600, 8, "None", 1, "None", 1000).unwrap(); + + if let Some(port) = &cli.port_name { port_name = port.to_string(); } + else + { println!("error: incorrect parameter: PORT_NAME. Type --help"); return; } + + let mut uart_trans = UartTransact::new("TxRx", &port_name, + port_set.clone(), + false, + false, + "data").unwrap(); if let Some(mode) = &cli.mode { - if mode == "cli" { cli_mode(); } - else if mode == "tcp" { tcp_server(); } - else { println!("error: incorrect parameter. type --help"); } + if mode.contains("cli") { cli_mode(&mut uart_trans); } + else if mode.contains("tcp") { tcp_server(&mut uart_trans, &mode[4..]); } + else { println!("error: incorrect parameter: MODE. type --help"); } } }