From ea6513514ec75212dcf17e765cc3547c2bc60adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Jos=C3=A9=20Pereira?= Date: Sun, 24 Dec 2023 15:28:05 -0300 Subject: [PATCH] Add decoder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick José Pereira --- src/decoder.rs | 101 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 8 ++-- src/message.rs | 25 +++++++++++ tests/deserialize.rs | 26 ++++++++++- 4 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 src/decoder.rs diff --git a/src/decoder.rs b/src/decoder.rs new file mode 100644 index 000000000..0202f5e77 --- /dev/null +++ b/src/decoder.rs @@ -0,0 +1,101 @@ +use crate::message::{ProtocolMessage, HEADER}; + +#[derive(Debug)] +pub enum ParseError { + InvalidStartByte, + IncompleteData, + ChecksumError, +} + +#[derive(Debug)] +pub enum DecoderResult { + Success(ProtocolMessage), + InProgress, + Error(ParseError), +} + +#[derive(Debug)] +pub enum DecoderState { + AwaitingStart1, + AwaitingStart2, + ReadingHeader, + ReadingPayload, + ReadingChecksum, +} + +pub struct Decoder { + pub state: DecoderState, + buffer: Vec, + message: ProtocolMessage, +} + +impl Decoder { + pub fn new() -> Self { + Self { + state: DecoderState::AwaitingStart1, + buffer: Vec::new(), + message: ProtocolMessage::new(), + } + } + + pub fn parse_byte(&mut self, byte: u8) -> DecoderResult { + match self.state { + DecoderState::AwaitingStart1 => { + if byte == HEADER[0] { + self.state = DecoderState::AwaitingStart2; + return DecoderResult::InProgress; + } + return DecoderResult::Error(ParseError::InvalidStartByte); + } + DecoderState::AwaitingStart2 => { + if byte == HEADER[1] { + self.state = DecoderState::ReadingHeader; + self.buffer.clear(); + return DecoderResult::InProgress; + } + self.state = DecoderState::AwaitingStart1; + return DecoderResult::Error(ParseError::InvalidStartByte); + } + DecoderState::ReadingHeader => { + self.buffer.push(byte); + // Basic information is available, moving to payload state + if self.buffer.len() == 6 { + self.message.payload_length = + u16::from_le_bytes([self.buffer[0], self.buffer[1]]); + self.message.message_id = u16::from_le_bytes([self.buffer[2], self.buffer[3]]); + self.message.src_device_id = self.buffer[4]; + self.message.dst_device_id = self.buffer[5]; + self.state = DecoderState::ReadingPayload; + self.buffer.clear(); + } + return DecoderResult::InProgress; + } + DecoderState::ReadingPayload => { + self.buffer.push(byte); + dbg!(self.buffer.len(), self.message.payload_length); + if self.buffer.len() == self.message.payload_length as usize { + self.message.payload = self.buffer.clone(); + self.state = DecoderState::ReadingChecksum; + self.buffer.clear(); + } + return DecoderResult::InProgress; + } + DecoderState::ReadingChecksum => { + self.buffer.push(byte); + if self.buffer.len() == 2 { + self.message.checksum = u16::from_le_bytes([self.buffer[0], self.buffer[1]]); + let message = self.message.clone(); + self.message = ProtocolMessage::new(); + self.reset(); + return DecoderResult::Success(message); + } + return DecoderResult::InProgress; + } + } + } + + fn reset(&mut self) { + self.state = DecoderState::AwaitingStart1; + self.buffer.clear(); + } +} diff --git a/src/lib.rs b/src/lib.rs index d21315355..506a6f290 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,15 @@ include!(concat!(env!("OUT_DIR"), "/mod.rs")); -use crate::message::{Deserialize, PingMessage}; +use crate::message::{Deserialize, PingMessage, HEADER}; const PAYLOAD_SIZE: usize = 255; use std::fmt; use std::{convert::TryFrom, io::Write}; +pub mod decoder; pub mod message; -pub const HEADER: [u8; 2] = ['B' as u8, 'R' as u8]; - #[derive(Copy, Clone, PartialEq, Eq)] pub struct PingMessagePack([u8; 1 + Self::HEADER_SIZE + PAYLOAD_SIZE + 2]); @@ -49,8 +48,7 @@ impl TryFrom<&Vec> for Messages { fn try_from(buffer: &Vec) -> Result { // Parse start1 and start2 - if !((buffer[0] == HEADER[0]) && (buffer[1] == HEADER[1])) - { + if !((buffer[0] == HEADER[0]) && (buffer[1] == HEADER[1])) { return Err(format!("Message should start with \"BR\" ASCII sequence, received: [{0}({:0x}), {1}({:0x})]", buffer[0], buffer[1])); } diff --git a/src/message.rs b/src/message.rs index da7ba7ea0..1889cc672 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,3 +1,28 @@ +pub const HEADER: [u8; 2] = ['B' as u8, 'R' as u8]; + +#[derive(Clone, Debug)] +pub struct ProtocolMessage { + pub payload_length: u16, + pub message_id: u16, + pub src_device_id: u8, + pub dst_device_id: u8, + pub payload: Vec, + pub checksum: u16, +} + +impl ProtocolMessage { + pub fn new() -> Self { + ProtocolMessage { + payload_length: 0, + message_id: 0, + src_device_id: 0, + dst_device_id: 0, + payload: Vec::new(), + checksum: 0, + } + } +} + pub trait PingMessage where Self: Sized + Serialize + Deserialize, diff --git a/tests/deserialize.rs b/tests/deserialize.rs index b01e1ce57..51b822f21 100644 --- a/tests/deserialize.rs +++ b/tests/deserialize.rs @@ -1,22 +1,44 @@ use std::convert::TryFrom; use ping_rs::common::Messages as common_messages; +use ping_rs::decoder::*; use ping_rs::{common, Messages}; #[test] fn test_simple_deserialization() { + let mut decoder = Decoder::new(); let general_request = common_messages::GeneralRequest(common::GeneralRequestStruct { requested_id: 5 }); + // From official ping protocol documentation let buffer: Vec = vec![ - 0x42, 0x52, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0xa1, 0x00, + 0x42, 0x52, 0x02, 0x00, // payload length + 0x06, 0x00, // message id + 0x00, 0x00, // src and dst id + 0x05, 0x00, // payload + 0xa1, 0x00, // crc ]; let Messages::Common(parsed) = Messages::try_from(&buffer).unwrap() else { panic!("Failed to parse common message."); }; - // From official ping protocol documentation assert_eq!(general_request, parsed); + for byte in &buffer[0..buffer.len() - 2] { + dbg!(byte, &decoder.state); + assert!(matches!( + decoder.parse_byte(byte.clone()), + DecoderResult::InProgress + )); + } + assert!(matches!( + decoder.parse_byte(buffer[buffer.len() - 2]), + DecoderResult::InProgress + )); + let DecoderResult::Success(_message) = decoder.parse_byte(buffer[buffer.len()-1]) else { + dbg!(decoder.state); + panic!("Failed to use decoder with valid message"); + }; + // Wrong CRC test let buffer: Vec = vec![ 0x42, 0x52, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0xa1, 0x01,