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
This commit is contained in:
2026-01-18 21:08:54 +01:00
parent 025331989f
commit f9a72fe9a3
2 changed files with 74 additions and 13 deletions

View File

@@ -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<Self, MessageDecodingError> {
pub fn try_from_bytes(bytes: &[u8], allow_too_long: bool) -> Result<Self, MessageDecodingError> {
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

View File

@@ -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<Self, FromBytesError> {
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<u8>)
InvalidBytes(Vec<u8>),
#[error("More bytes than expected were in the input")]
TooManyBytes(Vec<u8>),
}
#[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{