diff --git a/agent/lib/src/external_api/comms.rs b/agent/lib/src/external_api/comms.rs new file mode 100644 index 0000000..17894c6 --- /dev/null +++ b/agent/lib/src/external_api/comms.rs @@ -0,0 +1,64 @@ +// This message is sent to the API server to indicate wether +// the agent is ready or not to receive messages. + +pub const MESSAGE_SIZE_NB_BYTES: usize = 8; + +pub struct Message { + pub message_size: [u8; MESSAGE_SIZE_NB_BYTES], // These are characters e.g. 00002048 + pub message: Vec // stringified json, vec because size is unknown +} + +impl Message { + pub fn new(message_to_send: String) -> Self { + let mut message_size = [0; MESSAGE_SIZE_NB_BYTES]; + let message = message_to_send.as_bytes().to_vec(); + + let string_size = format!("{:0>8}", message.len()); + //We can't call directly as bytes as both &str and String sizes are not known at + //compile time unlike message_size + + for (i, c) in string_size.chars().enumerate() { + message_size[i] = c as u8; + } + + Self { + message_size, + message + } + } + + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.extend_from_slice(&self.message_size); + bytes.extend_from_slice(&self.message); + bytes + } + + +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn new_message_well_encoded() { + let message_data = "Hello world".to_string(); + let message = Message::new(message_data); + assert_eq!(message.message, [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]); + assert_eq!(message.message_size, [48, 48, 48, 48, 48, 48, 49, 49]); + + assert_eq!(message.to_bytes(), [48, 48, 48, 48, 48, 48, 49, 49, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]); + } + + + #[test] + fn message_size_badly_encoded() { + let message_data = "Hello world".to_string(); + let message = Message::new(message_data); + assert_eq!(message.message, [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]); + assert_ne!(message.message_size, [48, 48, 48, 48, 48, 48, 49, 50]); // should be 11, is 12 + } + +} + \ No newline at end of file diff --git a/agent/lib/src/external_api/mod.rs b/agent/lib/src/external_api/mod.rs index 908db95..e3a6fa2 100644 --- a/agent/lib/src/external_api/mod.rs +++ b/agent/lib/src/external_api/mod.rs @@ -1,2 +1,3 @@ pub mod model; +pub mod comms; pub mod service; diff --git a/agent/lib/src/external_api/service.rs b/agent/lib/src/external_api/service.rs index 5a71e92..eadeb09 100644 --- a/agent/lib/src/external_api/service.rs +++ b/agent/lib/src/external_api/service.rs @@ -1,7 +1,8 @@ use anyhow::{anyhow, Result}; -use log::{error, info}; +use log::{error, info, debug, trace}; use super::model::{RequestMessage, ResponseMessage, StatusMessage}; +use super::comms::{Message, MESSAGE_SIZE_NB_BYTES}; pub struct ExternalApi { serial_path: String, @@ -25,20 +26,62 @@ impl ExternalApi { .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]; + let mut got_size = false; + let mut buf = [0; 128]; + let mut bytes_read: usize = 0; // Create the final vector to hold the data let mut data_received: Vec = Vec::new(); - let mut find_delimiter = false; + //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) { + Ok(t) => { + if t == 0 { + break; + } + + bytes_read += t; + data_received.extend_from_slice(&size_buffer[..t]); + + trace!("Received {} bytes", t); + trace!("Size buffer: {:?}", data_received.clone()); + + 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) + )?; + + trace!("Size string: {}", size_string); + + data_size = size_string.parse::().map_err( + |e| anyhow!("Failed to parse length of message: {}", e) + )?; + + data_received.drain(..MESSAGE_SIZE_NB_BYTES); + } + } + Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => (), + Err(e) => error!("{:?}", e), + } + } + + bytes_read = 0; - while !find_delimiter { + while bytes_read < data_size { match serial.read(&mut buf) { Ok(t) => { if t > 0 { - info!("Buffer received {:?}", &buf[..t]); - find_delimiter = - self.append_data_before_delimiter(&buf, &mut data_received)?; + bytes_read += t; + data_received.extend_from_slice(&buf[..t]); + debug!("Received {} bytes", t); } } Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => (), @@ -46,10 +89,13 @@ impl ExternalApi { } } - info!("Final received data: {:?}", data_received); + debug!("Final received data: {:?}", data_received); + debug!("Total bytes read {:?}", bytes_read); let code_entry = self.parse_json_payload(&data_received)?; + info!("Code entry: {:?}", code_entry); + // Flush the serial port serial .flush() @@ -58,29 +104,6 @@ impl ExternalApi { Ok(code_entry) } - pub fn append_data_before_delimiter( - &mut self, - buf: &[u8], - data_received: &mut Vec, - ) -> Result { - // find char 1c (record separator) in the buffer - if let Some(i) = buf.iter().position(|&r| r == 0x1c) { - // Split the buffer at the position of the record separator - let (data_to_add, _) = buf.split_at(i); - - // Add the data to the data vector - data_received.extend_from_slice(data_to_add); - - info!("Delimiter found at index: {}", i); - - Ok(true) - } else { - // Add the data to the data vector - data_received.extend_from_slice(&buf[..buf.len()]); - Ok(false) - } - } - pub fn parse_json_payload(&mut self, data: &[u8]) -> Result { // Convert the data vector to a codeEntry struct let request_message: RequestMessage = serde_json::from_slice(data) @@ -120,7 +143,8 @@ impl ExternalApi { .map_err(|e| anyhow!("Failed to open serial port: {}", e))?; // Conver the string to a byte array - let buf = data.as_bytes(); + let message = Message::new(data.to_string()).to_bytes(); + let buf = message.as_slice(); // Write the byte array to the serial port serial @@ -235,80 +259,4 @@ mod tests { Ok(()) } - - #[test] - fn test_data_cut_before_delimiter() -> Result<()> { - let mut internal_api = ExternalApi::new("".to_string(), 0); - - let data = [97, 98, 99, 28, 1, 2, 3, 4, 5, 6, 7]; - let mut data_received: Vec = Vec::new(); - - let find_demiliter = - internal_api.append_data_before_delimiter(&data, &mut data_received)?; - - assert!(find_demiliter); - assert_eq!(data_received, [97, 98, 99]); - - Ok(()) - } - - #[test] - fn test_data_transferred_without_delimiter() -> Result<()> { - let mut internal_api = ExternalApi::new("".to_string(), 0); - - let data = [97, 98, 99, 1, 2, 3, 4, 5, 6, 7]; - let mut data_received: Vec = Vec::new(); - - let find_demiliter = - internal_api.append_data_before_delimiter(&data, &mut data_received)?; - - assert!(!find_demiliter); - assert_eq!(data_received, [97, 98, 99, 1, 2, 3, 4, 5, 6, 7]); - - Ok(()) - } - - #[test] - fn test_data_transferred_multiple_time() -> Result<()> { - let mut internal_api = ExternalApi::new("".to_string(), 0); - - let data = [97, 98, 99]; - let data2 = [1, 2, 3, 4, 5, 6, 7]; - let mut data_received: Vec = Vec::new(); - - let find_demiliter = - internal_api.append_data_before_delimiter(&data, &mut data_received)?; - let find_demiliter2 = - internal_api.append_data_before_delimiter(&data2, &mut data_received)?; - - assert!(!find_demiliter); - assert!(!find_demiliter2); - assert_eq!(data_received, [97, 98, 99, 1, 2, 3, 4, 5, 6, 7]); - - Ok(()) - } - - #[test] - fn test_data_transferred_with_delimiter() -> Result<()> { - let mut internal_api = ExternalApi::new("".to_string(), 0); - - let data = [97, 98, 99]; - let data2 = [1, 2, 3, 4, 5, 6, 7]; - let data3 = [8, 9, 10, 28, 11, 12, 13]; - let mut data_received: Vec = Vec::new(); - - let find_demiliter = - internal_api.append_data_before_delimiter(&data, &mut data_received)?; - let find_demiliter2 = - internal_api.append_data_before_delimiter(&data2, &mut data_received)?; - let find_demiliter3 = - internal_api.append_data_before_delimiter(&data3, &mut data_received)?; - - assert!(!find_demiliter); - assert!(!find_demiliter2); - assert!(find_demiliter3); - assert_eq!(data_received, [97, 98, 99, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - - Ok(()) - } }