diff --git a/agent/lib/src/external_api/service.rs b/agent/lib/src/external_api/service.rs index eadeb09..7643348 100644 --- a/agent/lib/src/external_api/service.rs +++ b/agent/lib/src/external_api/service.rs @@ -1,30 +1,32 @@ use anyhow::{anyhow, Result}; -use log::{error, info, debug, trace}; +use log::{debug, error, info, trace}; + +use serialport::SerialPort; -use super::model::{RequestMessage, ResponseMessage, StatusMessage}; use super::comms::{Message, MESSAGE_SIZE_NB_BYTES}; +use super::model::{RequestMessage, ResponseMessage, StatusMessage}; pub struct ExternalApi { serial_path: String, serial_baud_rate: u32, + + serial_port: Box, // So we don't open it multiple times } impl ExternalApi { pub fn new(serial_path: String, serial_baud_rate: u32) -> Self { Self { - serial_path, + serial_path: serial_path.clone(), serial_baud_rate, + serial_port: serialport::new(serial_path, serial_baud_rate) + .open() + .unwrap(), } } pub fn read_from_serial(&mut self) -> Result { info!("Reading from serial port: {}", self.serial_path); - // Open the serial port - let mut serial = serialport::new(&self.serial_path, self.serial_baud_rate) - .open() - .map_err(|e| anyhow!("Failed to open serial port: {}", e))?; - // Create a buffer to hold the data let mut data_size: usize = 0; let mut size_buffer: [u8; MESSAGE_SIZE_NB_BYTES] = [0; MESSAGE_SIZE_NB_BYTES]; @@ -36,9 +38,9 @@ impl ExternalApi { // Create the final vector to hold the data let mut data_received: Vec = Vec::new(); - //we read the buffer and retrieve the first 8 bytes which are the size of the message + //we read the buffer and retrieve the first 8 bytes which are the size of the message while !got_size { - match serial.read(&mut size_buffer) { + match self.serial_port.read(&mut size_buffer) { Ok(t) => { if t == 0 { break; @@ -52,18 +54,15 @@ impl ExternalApi { if bytes_read >= MESSAGE_SIZE_NB_BYTES { got_size = true; - - let size_string = String::from_utf8( - data_received.clone() - ).map_err( - |e| anyhow!("Failed to get message size as string: {}", e) - )?; - + + let size_string = String::from_utf8(data_received.clone()) + .map_err(|e| anyhow!("Failed to get message size as string: {}", e))?; + trace!("Size string: {}", size_string); - - data_size = size_string.parse::().map_err( - |e| anyhow!("Failed to parse length of message: {}", e) - )?; + + data_size = size_string + .parse::() + .map_err(|e| anyhow!("Failed to parse length of message: {}", e))?; data_received.drain(..MESSAGE_SIZE_NB_BYTES); } @@ -76,7 +75,7 @@ impl ExternalApi { bytes_read = 0; while bytes_read < data_size { - match serial.read(&mut buf) { + match self.serial_port.read(&mut buf) { Ok(t) => { if t > 0 { bytes_read += t; @@ -97,7 +96,7 @@ impl ExternalApi { info!("Code entry: {:?}", code_entry); // Flush the serial port - serial + self.serial_port .flush() .map_err(|e| anyhow!("Failed to flush serial port: {}", e))?; @@ -137,22 +136,25 @@ impl ExternalApi { } pub fn write_to_serial(&mut self, data: &str) -> Result<()> { - // Open the serial port - let mut serial = serialport::new(&self.serial_path, self.serial_baud_rate) - .open() - .map_err(|e| anyhow!("Failed to open serial port: {}", e))?; + info!("Writing to serial port: {}", self.serial_path); - // Conver the string to a byte array + // Convert the string to a byte array let message = Message::new(data.to_string()).to_bytes(); let buf = message.as_slice(); // Write the byte array to the serial port - serial + self.serial_port .write_all(buf) .map_err(|e| anyhow!("Failed to write to serial port: {}", e))?; + // In order to still be readable by ``readline`` on the other side, we add a carriage return + // (not included in the message size) + self.serial_port + .write("\r\n".as_bytes()) + .map_err(|e| anyhow!("Failed to write to serial port: {}", e))?; + // Flush the serial port - serial + self.serial_port .flush() .map_err(|e| anyhow!("Failed to flush serial port: {}", e))?; Ok(()) diff --git a/agent/lib/src/internal_api/model.rs b/agent/lib/src/internal_api/model.rs index 0ea8bf9..47358be 100644 --- a/agent/lib/src/internal_api/model.rs +++ b/agent/lib/src/internal_api/model.rs @@ -2,6 +2,13 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; +/// A struct to represent a file in the request message +/// +/// # Attributes +/// +/// * `path` - The path of the file +/// * `file_name` - The name of the file +/// * `content` - The content of the file #[derive(Deserialize, Serialize, Debug)] pub struct FileModel { pub path: PathBuf, @@ -19,6 +26,13 @@ impl FileModel { } } +/// A struct to represent the result of a command +/// +/// # Attributes +/// +/// * `stdout` - The stdout of the command +/// * `stderr` - The stderr of the command +/// * `exit_code` - The exit code of the command #[derive(Deserialize, Serialize, Debug)] pub struct CodeReturn { pub stdout: String, diff --git a/agent/lib/src/internal_api/service.rs b/agent/lib/src/internal_api/service.rs index 662d04a..dcfef32 100644 --- a/agent/lib/src/internal_api/service.rs +++ b/agent/lib/src/internal_api/service.rs @@ -12,17 +12,33 @@ use std::{ process::Command, }; +/// The path where the workspace will be created const WORKSPACE_PATH: &str = "/tmp"; +/// The internal API pub struct InternalApi { pub request_message: RequestMessage, } impl InternalApi { + /// Create a new instance of InternalApi + /// + /// # Arguments + /// + /// * `request_message` - The request message + /// + /// # Returns + /// + /// * `Self` - The new instance of InternalApi pub fn new(request_message: RequestMessage) -> Self { Self { request_message } } + /// Create the workspace for the code execution + /// + /// # Returns + /// + /// * `Result<()>` - Nothing or an error pub fn create_workspace(&mut self) -> Result<()> { info!("Creating workspace for code execution"); @@ -101,6 +117,11 @@ impl InternalApi { Ok(()) } + /// Run all the steps of the request message + /// + /// # Returns + /// + /// * `Result` - The response message or an error pub fn run(&mut self) -> Result { info!("Running all steps"); let mut steps: Vec = Vec::new(); @@ -135,6 +156,15 @@ impl InternalApi { Ok(response_message) } + /// Run a command + /// + /// # Arguments + /// + /// * `command` - The command to run + /// + /// # Returns + /// + /// * `Result` - The code return or an error pub fn run_one(&mut self, command: &str) -> Result { info!("Running command : {}", command); @@ -167,6 +197,15 @@ mod tests { use std::fs::File; use std::io::Read; + /// Generate a random integer + /// + /// # Arguments + /// + /// * `max` - The maximum value + /// + /// # Returns + /// + /// * `usize` - The random integer fn random_usize(max: usize) -> usize { let mut f = File::open("/dev/urandom").unwrap(); let mut buf = [0u8; 1]; @@ -180,6 +219,15 @@ mod tests { } } + /// Generate a random string + /// + /// # Arguments + /// + /// * `len` - The length of the string + /// + /// # Returns + /// + /// * `String` - The random string fn native_rand_string(len: usize) -> String { let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; let mut string = String::new(); @@ -191,6 +239,7 @@ mod tests { string } + /// Test the creation of a file #[test] fn workload_runs_correctly() { let files: Vec = Vec::new(); @@ -218,6 +267,7 @@ mod tests { assert!(res.data.steps[0].enable_output); } + /// Test the execution of a command with a workspace #[test] fn workspace_created_sucessfully() { let mut base_dir = PathBuf::from(WORKSPACE_PATH); diff --git a/agent/src/main.rs b/agent/src/main.rs index 5ca0bc6..5597b73 100644 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -5,6 +5,7 @@ use anyhow::{anyhow, Result}; use clap::Parser; use log::{debug, info, trace}; +/// Agent CLI options #[derive(Parser)] #[clap( version = "0.1", @@ -17,13 +18,19 @@ pub struct AgentOpts { config: String, } +/// Main function fn main() -> Result<()> { + // Initialize logger env_logger::init(); + info!("Starting agent"); + // Parse CLI options let options = AgentOpts::parse(); debug!("loading config file at {}", options.config); + + // Load config file let config = AgentConfig::load(options.config.as_str())?; trace!( @@ -31,14 +38,25 @@ fn main() -> Result<()> { config ); + // Initialize external API let mut external_api = ExternalApi::new(config.serial.path, config.serial.baud_rate); + // Send status message to serial port external_api.send_status_message()?; + // Read request message from serial port let request_message = external_api.read_from_serial()?; + + // Initialize internal API let mut internal_api = InternalApi::new(request_message); + + // Create the workspace internal_api.create_workspace()?; + + // Run the steps of the request message let response_message = internal_api.run().map_err(|e| anyhow!("{:?}", e))?; + + // Send response message to serial port external_api.send_response_message(response_message)?; info!("Stopping agent");