Skip to content

Commit

Permalink
fix: Implement new communication protocol
Browse files Browse the repository at this point in the history
Signed-off-by: WoodenMaiden <[email protected]>
  • Loading branch information
WoodenMaiden committed Mar 28, 2023
1 parent fb006ec commit b62fbea
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 107 deletions.
64 changes: 64 additions & 0 deletions agent/lib/src/external_api/comms.rs
Original file line number Diff line number Diff line change
@@ -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<u8> // 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<u8> {
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
}

}

1 change: 1 addition & 0 deletions agent/lib/src/external_api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod model;
pub mod comms;
pub mod service;
162 changes: 55 additions & 107 deletions agent/lib/src/external_api/service.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -25,31 +26,76 @@ 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<u8> = 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::<usize>().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 => (),
Err(e) => error!("{:?}", e),
}
}

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()
Expand All @@ -58,29 +104,6 @@ impl ExternalApi {
Ok(code_entry)
}

pub fn append_data_before_delimiter(
&mut self,
buf: &[u8],
data_received: &mut Vec<u8>,
) -> Result<bool> {
// 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<RequestMessage> {
// Convert the data vector to a codeEntry struct
let request_message: RequestMessage = serde_json::from_slice(data)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<u8> = 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<u8> = 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<u8> = 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<u8> = 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(())
}
}

0 comments on commit b62fbea

Please sign in to comment.