diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0fe6b87..803389b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,9 +11,9 @@ jobs: steps: - name: Install dependencies - run: sudo apt-get update && sudo apt-get install -y libudev-dev + run: sudo apt-get install -y libudev-dev - uses: actions/checkout@v2 - name: Build step - run: cargo build --verbose + run: cargo build --verbose --release - name: Run tests run: cargo test --verbose \ No newline at end of file diff --git a/src/configuration.rs b/src/configuration.rs new file mode 100644 index 0000000..b11b3d4 --- /dev/null +++ b/src/configuration.rs @@ -0,0 +1,84 @@ +use crate::Config; +use log::warn; + +pub fn get_configuration(args: Vec) -> Config { + // Set defaults in case arguments are not provided + let mut configuration = Config { + test_mode: false, + serial_port: "/dev/ttyUSB0".to_string(), + baud_rate: 115200, + ws_port: "9002".to_string(), + }; + + if args.len() > 4 { + let ws_port = args[1].clone(); + let serial_port = args[2].clone(); + let baudrate = args[3].clone(); + let test_arg = args[4].clone(); + + configuration = Config { + test_mode: matches!(test_arg.to_lowercase().as_str(), "true"), + baud_rate: match baudrate.parse::() { + Ok(br) => br, + Err(_) => { + warn!("Failed to parse baudrate. Using default baudrate 115200"); + 115200 + } + }, + serial_port, + ws_port, + }; + } + + configuration +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_configuration_defaults() { + let args: Vec = vec![]; + let config = get_configuration(args); + + assert_eq!(config.test_mode, false); + assert_eq!(config.serial_port, "/dev/ttyUSB0"); + assert_eq!(config.baud_rate, 115200); + assert_eq!(config.ws_port, "9002"); + } + + #[test] + fn test_get_configuration_with_args() { + let args: Vec = vec![ + "program_name".to_string(), + "8080".to_string(), + "/dev/ttyS0".to_string(), + "9600".to_string(), + "true".to_string(), + ]; + let config = get_configuration(args); + + assert_eq!(config.test_mode, true); + assert_eq!(config.serial_port, "/dev/ttyS0"); + assert_eq!(config.baud_rate, 9600); + assert_eq!(config.ws_port, "8080"); + } + + #[test] + fn test_get_configuration_invalid_baudrate() { + let args: Vec = vec![ + "program_name".to_string(), + "8080".to_string(), + "/dev/ttyS0".to_string(), + "invalid_baudrate".to_string(), + "false".to_string(), + ]; + let config = get_configuration(args); + + assert_eq!(config.test_mode, false); + assert_eq!(config.serial_port, "/dev/ttyS0"); + assert_eq!(config.baud_rate, 115200); // Default baud rate + assert_eq!(config.ws_port, "8080"); + } +} diff --git a/src/main.rs b/src/main.rs index c1a4f8f..1cd62ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,15 @@ -use log::{info, warn}; +use log::info; use std::env; use tokio::net::TcpListener; +mod configuration; mod commands; mod serialcom; mod structs; mod wscom; use crate::structs::{Config, MessageType, MessageWS}; +use crate::configuration::get_configuration; use crate::wscom::accept_connection; #[tokio::main] @@ -16,34 +18,9 @@ async fn main() { info!("Starting xcontroller..."); - let mut configuration = Config { - test_mode: false, - serial_port: "/dev/ttyUSB0".to_string(), - baud_rate: 115200, - ws_port: "9002".to_string(), - }; - // Set config from start params let args: Vec = env::args().collect(); - if args.len() > 4 { - let ws_port = args[1].clone(); // Convert String to &str - let serial_port = args[2].clone(); - let baudrate = args[3].clone(); - let test_arg = args[4].clone(); - - configuration = Config { - test_mode: matches!(test_arg.to_lowercase().as_str(), "true"), - baud_rate: match baudrate.parse::() { - Ok(br) => br, - Err(_) => { - warn!("Failed to parse baudrate. Using default baudrate 115200"); - 115200 - } - }, - serial_port, - ws_port, - }; - } + let configuration = get_configuration(args); let addr = format!("0.0.0.0:{}", configuration.ws_port); info!("Listening on {}", addr); diff --git a/src/serialcom.rs b/src/serialcom.rs index d1ebb1f..e678f2d 100644 --- a/src/serialcom.rs +++ b/src/serialcom.rs @@ -23,10 +23,8 @@ pub fn create_serialcom(cmd: &str, serial_port: String, baud_rate: u32) -> Resul } if let Ok(response) = read_from_port(&mut port) { - // Parse message - //Send this message back to WS for broadcast + // Parse message and send this response back to WS for broadcast info!("{}", response); - Ok(response) } else { //Send this message back to WS for broadcast @@ -88,3 +86,70 @@ fn write_to_port(port: &mut T, command: &[u8]) -> io::Result<()> { Err(e) => Err(e), } } + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Cursor; + + #[test] + fn test_read_from_port_ok() { + let data = b"ok\n"; + let mut cursor = Cursor::new(data); + let result = read_from_port(&mut cursor).unwrap(); + assert_eq!(result, "ok\n"); + } + + // TODO: For this to work a end of message delimiter is needed + + // #[test] + // fn test_read_from_port_partial_ok() { + // let data = b"data and more data"; + // let mut cursor = Cursor::new(data); + // let result = read_from_port(&mut cursor).unwrap(); + // assert_eq!(result, "data and more data"); + // } + + #[test] + fn test_read_from_port_timeout() { + struct TimeoutReader; + impl Read for TimeoutReader { + fn read(&mut self, _: &mut [u8]) -> io::Result { + Err(io::Error::new(io::ErrorKind::TimedOut, "timeout")) + } + } + + let mut reader = TimeoutReader; + let result = read_from_port(&mut reader); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), io::ErrorKind::TimedOut); + } + + #[test] + fn test_write_to_port_success() { + let mut buffer = Vec::new(); + let command = b"test command"; + let _result = write_to_port(&mut buffer, command).unwrap(); + assert_eq!(buffer, command); + } + + #[test] + fn test_write_to_port_error() { + struct ErrorWriter; + impl Write for ErrorWriter { + fn write(&mut self, _: &[u8]) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, "write error")) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + let mut writer = ErrorWriter; + let command = b"test command"; + let result = write_to_port(&mut writer, command); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), io::ErrorKind::Other); + } +} diff --git a/src/structs.rs b/src/structs.rs index 06cfb9d..84371f7 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -77,3 +77,11 @@ pub struct Config { pub baud_rate: u32, pub ws_port: String, } + +#[derive(Debug, Serialize, Deserialize)] +pub struct MessageSender<'a> { + pub message_type: &'a str, + pub message: &'a str, + pub raw_message: String, + pub timestamp: u64, +} \ No newline at end of file diff --git a/src/wscom.rs b/src/wscom.rs index 253761b..e3c40c9 100644 --- a/src/wscom.rs +++ b/src/wscom.rs @@ -1,6 +1,5 @@ use futures::{stream::StreamExt, SinkExt}; use log::{debug, error, info}; -use serde::{Deserialize, Serialize}; use std::net::SocketAddr; use std::time::{SystemTime, UNIX_EPOCH}; use tokio::net::TcpStream; @@ -16,14 +15,7 @@ use crate::serialcom::create_serialcom; use crate::Config; use crate::MessageType; use crate::MessageWS; - -#[derive(Debug, Serialize, Deserialize)] -pub struct MessageSender<'a> { - pub message_type: &'a str, - pub message: &'a str, - pub raw_message: String, - pub timestamp: u64, -} +use crate::structs::MessageSender; // Accept incoming connection from client pub async fn accept_connection(peer: SocketAddr, stream: TcpStream, configuration: Config) { @@ -190,4 +182,4 @@ async fn handle_connection( error!("ConnectionClosed for {}", peer); Err(Error::ConnectionClosed) -} +} \ No newline at end of file