From f9a72fe9a3005dab68b378780d7af05dc3cb3e22 Mon Sep 17 00:00:00 2001 From: Tobias Maier Date: Sun, 18 Jan 2026 21:08:54 +0100 Subject: [PATCH] feat(message): add support for decoding messages with trailing data Added a new parameter `allow_too_long` to `Protocol300Message::try_from_bytes` to optionally ignore trailing data when decoding messages. This is useful when receiving messages with extra bytes that should not cause decoding to fail. - Updated function signature and documentation to include the new parameter - Modified length validation logic to skip error when `allow_too_long` is true - Updated all existing test cases to use the new parameter - Added new test cases to verify behavior with and without trailing data --- src/message.rs | 40 +++++++++++++++++++++++++++++++------- src/transmission.rs | 47 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/message.rs b/src/message.rs index 9b2db82..11a399a 100644 --- a/src/message.rs +++ b/src/message.rs @@ -151,6 +151,11 @@ impl Protocol300Message { /// checking for valid start bytes and checking the checksum to detect /// transmission errors /// + /// # Arguments + /// - `bytes`: Byte slice to decode + /// - `allow_too_long`: Don't throw an error if the byte slice is too long, decode the message and + /// ignore trailing data + /// /// # Errors /// Returns a `MessageDecodingError` on failure. Can be caused by multiple /// things (for details look at `MessageDecodingError`): @@ -158,7 +163,7 @@ impl Protocol300Message { /// - Length does not match actual length /// - Invalid values for Message Identifier or Function code /// - Checksum mismatch - pub fn try_from_bytes(bytes: &[u8]) -> Result { + pub fn try_from_bytes(bytes: &[u8], allow_too_long: bool) -> Result { if bytes.len() < 8 { error!("Failed to decode message: byte vector too short (length: {}, minimum required: 8)", bytes.len()); return Err(MessageDecodingError::VecTooShort) @@ -178,7 +183,7 @@ impl Protocol300Message { } }; - if actual_length != telegram_length + 3 { + if (actual_length < telegram_length + 3) || ((actual_length > telegram_length + 3) && !allow_too_long) { error!("Failed to decode message: length mismatch - advertised telegram length: {}, actual length: {}, expected: {}", telegram_length, actual_length, telegram_length + 3); return Err(MessageDecodingError::LengthMismatch()); } @@ -336,7 +341,7 @@ pub enum MessageDecodingError { #[cfg(test)] mod tests { - use crate::{enums::{FunctionCode, MessageIdentifier}, message::{MessageCreationError, Protocol300Message}}; + use crate::{MessageDecodingError, enums::{FunctionCode, MessageIdentifier}, message::{MessageCreationError, Protocol300Message}}; #[cfg(not(feature = "std"))] extern crate alloc; @@ -396,7 +401,7 @@ mod tests { checksum: 0xC9 }; let bytes = vec![0x41, 0x09, 0x01, 0x01, 0x55, 0x25, 0x04, 0x07, 0x01, 0x27, 0x11, 0xC9]; - let message = Protocol300Message::try_from_bytes(&bytes).unwrap(); + let message = Protocol300Message::try_from_bytes(&bytes, false).unwrap(); assert_eq!(expected_msg, message); } @@ -412,7 +417,7 @@ mod tests { checksum: 0x2E }; let bytes = vec![0x41, 0x05, 0x01, 0x02, 0x21, 0x01, 0x04, 0x2E]; - let message = Protocol300Message::try_from_bytes(&bytes).unwrap(); + let message = Protocol300Message::try_from_bytes(&bytes, false).unwrap(); assert_eq!(expected_msg, message); } @@ -428,7 +433,7 @@ mod tests { checksum: 0x82 }; let bytes = vec![0x41, 0x05, 0x00, 0x01, 0x55, 0x25, 0x02, 0x82]; - let message = Protocol300Message::try_from_bytes(&bytes).unwrap(); + let message = Protocol300Message::try_from_bytes(&bytes, false).unwrap(); assert_eq!(expected_msg, message); } @@ -444,7 +449,28 @@ mod tests { checksum: 0x51 }; let bytes = vec![0x41, 0x06, 0x00, 0x02, 0x23, 0x23, 0x01, 0x02, 0x51]; - let message = Protocol300Message::try_from_bytes(&bytes).unwrap(); + let message = Protocol300Message::try_from_bytes(&bytes, false).unwrap(); + assert_eq!(expected_msg, message); + } + + fn received_message_write_response_from_bytes_allow_too_long_false() { + let bytes = vec![0x41, 0x05, 0x01, 0x02, 0x21, 0x01, 0x04, 0x2E, 0x05]; + let message = Protocol300Message::try_from_bytes(&bytes, false); + assert_eq!(Err(MessageDecodingError::LengthMismatch()), message); + } + + fn received_message_write_response_from_bytes_allow_too_long_true() { + let expected_msg = Protocol300Message{ + data_address: 0x2101, + telegram_length: 0x05, + function_code: FunctionCode::VirtualWRITE, + message_identifier: MessageIdentifier::Response, + data_length: 0x04, + data: vec![], + checksum: 0x2E + }; + let bytes = vec![0x41, 0x05, 0x01, 0x02, 0x21, 0x01, 0x04, 0x2E, 0x05]; + let message = Protocol300Message::try_from_bytes(&bytes, true).unwrap(); assert_eq!(expected_msg, message); } // TO BYTES diff --git a/src/transmission.rs b/src/transmission.rs index 91d2cb3..7fb14f0 100644 --- a/src/transmission.rs +++ b/src/transmission.rs @@ -33,7 +33,9 @@ impl Protocol300Transmission { /// Try to convert bytes to a Protocol 300 Transmission /// /// # Errors - /// Returns a `FromBytesError` if the conversion fails in some way + /// - `FromBytesError` if the conversion fails in some way + /// - `TooManyBytes` if the input slice has more bytes than the decoded transmission + /// if that is wanted, use `try_from_bytes_incomplete` pub fn try_from_bytes(bytes: &[u8]) -> Result { match bytes { [0x06] => Ok(Self::Ack), @@ -41,7 +43,7 @@ impl Protocol300Transmission { [0x04] => Ok(Self::Eot), [0x15] => Ok(Self::Nack), [0x16, 0x00, 0x00] => Ok(Self::Init), - [0x41, ..] => Ok(Self::Message(Protocol300Message::try_from_bytes(bytes)?)),//TODO fail if too many bytes + [0x41, ..] => Ok(Self::Message(Protocol300Message::try_from_bytes(bytes, false)?)), b => { error!("Failed to parse Protocol300 transmission: invalid byte sequence (length: {}, first byte: 0x{:02X})", bytes.len(), @@ -62,7 +64,7 @@ impl Protocol300Transmission { [0x04, ..] => Ok(Self::Eot), [0x15, ..] => Ok(Self::Nack), [0x16, 0x00, 0x00, ..] => Ok(Self::Init), - [0x41, ..] => Ok(Self::Message(Protocol300Message::try_from_bytes(bytes)?)), + [0x41, ..] => Ok(Self::Message(Protocol300Message::try_from_bytes(bytes, true)?)), b => { error!("Failed to parse Protocol300 transmission: invalid byte sequence (length: {}, first byte: 0x{:02X})", bytes.len(), @@ -101,13 +103,14 @@ pub enum FromBytesError{ #[error("Failed to parse message")] MessageParsing(#[from] message::MessageDecodingError), #[error("Invalid bytes in vec")] - InvalidBytes(Vec) - + InvalidBytes(Vec), + #[error("More bytes than expected were in the input")] + TooManyBytes(Vec), } #[cfg(test)] mod tests { - use crate::{FunctionCode, MessageIdentifier, Protocol300Message, transmission::Protocol300Transmission}; + use crate::{FromBytesError, FunctionCode, MessageDecodingError, MessageIdentifier, Protocol300Message, transmission::Protocol300Transmission}; #[test] fn from_bytes_ack() { @@ -134,6 +137,38 @@ mod tests { assert_eq!(Protocol300Transmission::Init, Protocol300Transmission::try_from_bytes(&[0x16, 0x00, 0x00]).unwrap()); } + #[test] + fn from_bytes_init_incomplete_success() { + assert_eq!(Protocol300Transmission::Init, Protocol300Transmission::try_from_bytes_incomplete(&[0x16, 0x00, 0x00, 0x05]).unwrap()); + } + + #[test] + fn from_bytes_init_incomplete_error() { + let res = Protocol300Transmission::try_from_bytes(&[0x16, 0x00, 0x00, 0x05]); + assert_eq!(res, Err(FromBytesError::InvalidBytes(vec![0x16, 0x00, 0x00, 0x05]))); + } + + #[test] + fn from_bytes_message_incomplete_error() { + let res = Protocol300Transmission::try_from_bytes(&[0x41, 0x09, 0x01, 0x01, 0x55, 0x25, 0x04, 0x07, 0x01, 0x27, 0x11, 0xC9, 0x05]); + assert_eq!(res, Err(FromBytesError::MessageParsing(MessageDecodingError::LengthMismatch()))); + } + + #[test] + fn from_bytes_message_incomplete_success() { + let res = Protocol300Transmission::try_from_bytes_incomplete(&[0x41, 0x09, 0x01, 0x01, 0x55, 0x25, 0x04, 0x07, 0x01, 0x27, 0x11, 0xC9, 0x05]); + let message = Protocol300Message{ + data_address: 0x5525, + telegram_length: 0x09, + function_code: FunctionCode::VirtualREAD, + message_identifier: MessageIdentifier::Response, + data_length: 0x04, + data: vec![0x07, 0x01, 0x27, 0x11], + checksum: 0xC9 + }; + assert_eq!(Protocol300Transmission::Message(message), res.unwrap()); + } + #[test] fn from_bytes_message() { let message = Protocol300Message{