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:
@@ -151,6 +151,11 @@ impl Protocol300Message {
|
|||||||
/// checking for valid start bytes and checking the checksum to detect
|
/// checking for valid start bytes and checking the checksum to detect
|
||||||
/// transmission errors
|
/// 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
|
/// # Errors
|
||||||
/// Returns a `MessageDecodingError` on failure. Can be caused by multiple
|
/// Returns a `MessageDecodingError` on failure. Can be caused by multiple
|
||||||
/// things (for details look at `MessageDecodingError`):
|
/// things (for details look at `MessageDecodingError`):
|
||||||
@@ -158,7 +163,7 @@ impl Protocol300Message {
|
|||||||
/// - Length does not match actual length
|
/// - Length does not match actual length
|
||||||
/// - Invalid values for Message Identifier or Function code
|
/// - Invalid values for Message Identifier or Function code
|
||||||
/// - Checksum mismatch
|
/// - 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 {
|
if bytes.len() < 8 {
|
||||||
error!("Failed to decode message: byte vector too short (length: {}, minimum required: 8)", bytes.len());
|
error!("Failed to decode message: byte vector too short (length: {}, minimum required: 8)", bytes.len());
|
||||||
return Err(MessageDecodingError::VecTooShort)
|
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);
|
error!("Failed to decode message: length mismatch - advertised telegram length: {}, actual length: {}, expected: {}", telegram_length, actual_length, telegram_length + 3);
|
||||||
return Err(MessageDecodingError::LengthMismatch());
|
return Err(MessageDecodingError::LengthMismatch());
|
||||||
}
|
}
|
||||||
@@ -336,7 +341,7 @@ pub enum MessageDecodingError {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{enums::{FunctionCode, MessageIdentifier}, message::{MessageCreationError, Protocol300Message}};
|
use crate::{MessageDecodingError, enums::{FunctionCode, MessageIdentifier}, message::{MessageCreationError, Protocol300Message}};
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
@@ -396,7 +401,7 @@ mod tests {
|
|||||||
checksum: 0xC9
|
checksum: 0xC9
|
||||||
};
|
};
|
||||||
let bytes = vec![0x41, 0x09, 0x01, 0x01, 0x55, 0x25, 0x04, 0x07, 0x01, 0x27, 0x11, 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);
|
assert_eq!(expected_msg, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,7 +417,7 @@ mod tests {
|
|||||||
checksum: 0x2E
|
checksum: 0x2E
|
||||||
};
|
};
|
||||||
let bytes = vec![0x41, 0x05, 0x01, 0x02, 0x21, 0x01, 0x04, 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);
|
assert_eq!(expected_msg, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,7 +433,7 @@ mod tests {
|
|||||||
checksum: 0x82
|
checksum: 0x82
|
||||||
};
|
};
|
||||||
let bytes = vec![0x41, 0x05, 0x00, 0x01, 0x55, 0x25, 0x02, 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);
|
assert_eq!(expected_msg, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,7 +449,28 @@ mod tests {
|
|||||||
checksum: 0x51
|
checksum: 0x51
|
||||||
};
|
};
|
||||||
let bytes = vec![0x41, 0x06, 0x00, 0x02, 0x23, 0x23, 0x01, 0x02, 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);
|
assert_eq!(expected_msg, message);
|
||||||
}
|
}
|
||||||
// TO BYTES
|
// TO BYTES
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ impl Protocol300Transmission {
|
|||||||
/// Try to convert bytes to a Protocol 300 Transmission
|
/// Try to convert bytes to a Protocol 300 Transmission
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # 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> {
|
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, FromBytesError> {
|
||||||
match bytes {
|
match bytes {
|
||||||
[0x06] => Ok(Self::Ack),
|
[0x06] => Ok(Self::Ack),
|
||||||
@@ -41,7 +43,7 @@ impl Protocol300Transmission {
|
|||||||
[0x04] => Ok(Self::Eot),
|
[0x04] => Ok(Self::Eot),
|
||||||
[0x15] => Ok(Self::Nack),
|
[0x15] => Ok(Self::Nack),
|
||||||
[0x16, 0x00, 0x00] => Ok(Self::Init),
|
[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 => {
|
b => {
|
||||||
error!("Failed to parse Protocol300 transmission: invalid byte sequence (length: {}, first byte: 0x{:02X})",
|
error!("Failed to parse Protocol300 transmission: invalid byte sequence (length: {}, first byte: 0x{:02X})",
|
||||||
bytes.len(),
|
bytes.len(),
|
||||||
@@ -62,7 +64,7 @@ impl Protocol300Transmission {
|
|||||||
[0x04, ..] => Ok(Self::Eot),
|
[0x04, ..] => Ok(Self::Eot),
|
||||||
[0x15, ..] => Ok(Self::Nack),
|
[0x15, ..] => Ok(Self::Nack),
|
||||||
[0x16, 0x00, 0x00, ..] => Ok(Self::Init),
|
[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 => {
|
b => {
|
||||||
error!("Failed to parse Protocol300 transmission: invalid byte sequence (length: {}, first byte: 0x{:02X})",
|
error!("Failed to parse Protocol300 transmission: invalid byte sequence (length: {}, first byte: 0x{:02X})",
|
||||||
bytes.len(),
|
bytes.len(),
|
||||||
@@ -101,13 +103,14 @@ pub enum FromBytesError{
|
|||||||
#[error("Failed to parse message")]
|
#[error("Failed to parse message")]
|
||||||
MessageParsing(#[from] message::MessageDecodingError),
|
MessageParsing(#[from] message::MessageDecodingError),
|
||||||
#[error("Invalid bytes in vec")]
|
#[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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{FunctionCode, MessageIdentifier, Protocol300Message, transmission::Protocol300Transmission};
|
use crate::{FromBytesError, FunctionCode, MessageDecodingError, MessageIdentifier, Protocol300Message, transmission::Protocol300Transmission};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_bytes_ack() {
|
fn from_bytes_ack() {
|
||||||
@@ -134,6 +137,38 @@ mod tests {
|
|||||||
assert_eq!(Protocol300Transmission::Init, Protocol300Transmission::try_from_bytes(&[0x16, 0x00, 0x00]).unwrap());
|
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]
|
#[test]
|
||||||
fn from_bytes_message() {
|
fn from_bytes_message() {
|
||||||
let message = Protocol300Message{
|
let message = Protocol300Message{
|
||||||
|
|||||||
Reference in New Issue
Block a user