This commit is contained in:
2025-12-07 16:50:10 +01:00
parent c2b9eaa8d3
commit a000c0854b
5 changed files with 123 additions and 67 deletions

View File

@@ -1,5 +1,10 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![expect(clippy::implicit_return, reason="Implicit return is fine in this crate")]
#![expect(clippy::exhaustive_structs, reason="All Structs are based on a fixed protocol")]
#![expect(clippy::arbitrary_source_item_ordering, reason="Order is the same as in the protocl")]
#![allow(clippy::question_mark_used, reason="I like ?")]
mod message;
mod enums;
mod utils;

View File

@@ -1,16 +1,8 @@
use log::{warn, error};
// Use std::vec when std exists, otherwise alloc::vec
#[cfg(feature = "std")]
use std::vec;
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use thiserror::Error;
@@ -38,6 +30,7 @@ pub struct Protocol300Message {
}
impl Protocol300Message {
#[inline]
/// Creates a new `Protocol300message` used to request data from the device
///
/// # Arguments
@@ -60,6 +53,7 @@ impl Protocol300Message {
}
}
#[inline]
/// Creates a new `Protocol300message` used to write data to the device
///
/// # Arguments
@@ -86,8 +80,25 @@ impl Protocol300Message {
})
}
#[inline]
/// Decode a byte string into a `Protocol300Message`
pub fn try_from_bytes(bytes: &[u8]) -> Result<Protocol300Message, MessageDecodingError> {
///
/// Tries to decode a vec of bytes into an `Protocol300Message`. Includes
/// checking for valid start bytes and checking the checksum to detect
/// transmission errors
///
/// # Errors
/// Returns a `MessageDecodingError` on failure. Can be caused by multiple
/// things (for details look at `MessageDecodingError`):
/// - Invalid Start byte
/// - 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> {
if bytes.len() < 8 {
return Err(MessageDecodingError::VecTooShort)
}
if bytes[0] != 0x41 {
return Err(MessageDecodingError::InvalidTelegramStart(bytes[0]));
}
@@ -123,7 +134,13 @@ impl Protocol300Message {
},
};
let checksum = calculate_checksum(telegram_length, message_identifier, function_code, data_address, data_length, &data);
let checksum_calculated = calculate_checksum(telegram_length, message_identifier, function_code, data_address, data_length, &data);
let checksum = bytes[telegram_length as usize + 2];
// Fail if the checksums do not match
if checksum != checksum_calculated {
return Err(MessageDecodingError::Checksum)
}
Ok(Self {
telegram_length,
@@ -137,6 +154,9 @@ impl Protocol300Message {
}
#[must_use]
#[inline]
/// Converts a `Protocol300Message` to a vec of bytes
pub fn to_bytes(&self) -> Vec<u8> {
let telegram_start_byte = 0x41;
let address_as_bytes = self.data_address.to_be_bytes();
@@ -157,13 +177,13 @@ impl Protocol300Message {
}
// TODO docs and test
#[inline]
/// Returns the expected length of the return telegram, include start byte but excluding ACK byte
///
/// Currently only implemented for Read and Write requests, as the rest is not needed and/or
/// unknown behaviour
///
/// # Errors:
/// # Errors
/// - `MessageReturnLengthError` - Describes the error which occured when converting
pub const fn get_expected_return_telegram_length(&self) -> Result<usize, MessageReturnLengthError> {
match (self.message_identifier, self.function_code) {
@@ -213,6 +233,12 @@ pub enum MessageDecodingError {
/// Error getting function code enum variant by number
#[error("Function Code not valid")]
InvalidFunctionCode(#[from] num_enum::TryFromPrimitiveError<FunctionCode>),
/// Checksum does not match the calculated checksum
#[error("Checksums do not match")]
Checksum,
/// The message vec is too short
#[error("The message vec is too short")]
VecTooShort
}
#[cfg(test)]
@@ -229,7 +255,7 @@ mod tests {
#[test]
fn test_new_request_message() {
fn new_request_message_works() {
let expected_message = Protocol300Message{
telegram_length: 5,
message_identifier: MessageIdentifier::Request,
@@ -244,7 +270,7 @@ mod tests {
}
#[test]
fn test_new_write_message() {
fn new_write_message() {
let expected_message = Protocol300Message{
telegram_length: 9,
message_identifier: MessageIdentifier::Request,
@@ -259,14 +285,14 @@ mod tests {
}
#[test]
fn test_new_write_message_data_too_long() {
fn new_write_message_data_too_long() {
let generated_message = Protocol300Message::try_new_write_message(0x0101, &vec![0x00; 300]);
assert!(generated_message.is_err());
assert!(matches!(generated_message.err().unwrap(), MessageCreationError::DataTooLong(_)));
}
#[test]
fn test_received_message_read_response_from_bytes() {
fn received_message_read_response_from_bytes() {
let expected_msg = Protocol300Message{
data_address: 0x5525,
telegram_length: 0x09,
@@ -282,7 +308,7 @@ mod tests {
}
#[test]
fn test_received_message_write_response_from_bytes() {
fn received_message_write_response_from_bytes() {
let expected_msg = Protocol300Message{
data_address: 0x2101,
telegram_length: 0x05,
@@ -298,7 +324,7 @@ mod tests {
}
#[test]
fn test_received_message_read_request_from_bytes() {
fn received_message_read_request_from_bytes() {
let expected_msg = Protocol300Message{
data_address: 0x5525,
telegram_length: 0x05,
@@ -314,7 +340,7 @@ mod tests {
}
#[test]
fn test_received_message_write_request_from_bytes() {
fn received_message_write_request_from_bytes() {
let expected_msg = Protocol300Message{
data_address: 0x2323,
telegram_length: 0x06,
@@ -330,7 +356,7 @@ mod tests {
}
// TO BYTES
#[test]
fn test_received_message_write_request_to_bytes() {
fn received_message_write_request_to_bytes() {
let test_msg = Protocol300Message{
data_address: 0x2323,
telegram_length: 0x06,
@@ -346,7 +372,7 @@ mod tests {
}
#[test]
fn test_received_message_read_request_to_bytes() {
fn received_message_read_request_to_bytes() {
let test_msg = Protocol300Message{
data_address: 0x5525,
telegram_length: 0x05,
@@ -362,7 +388,7 @@ mod tests {
}
#[test]
fn test_received_message_write_response_to_bytes() {
fn received_message_write_response_to_bytes() {
let test_msg = Protocol300Message{
data_address: 0x2101,
telegram_length: 0x05,
@@ -378,7 +404,7 @@ mod tests {
}
#[test]
fn test_received_message_read_response_to_bytes() {
fn received_message_read_response_to_bytes() {
let test_msg = Protocol300Message{
data_address: 0x5525,
telegram_length: 0x09,
@@ -394,7 +420,7 @@ mod tests {
}
#[test]
fn test_expected_return_length_read() {
fn expected_return_length_read() {
let expected_message = Protocol300Message{
telegram_length: 5,
message_identifier: MessageIdentifier::Request,
@@ -408,7 +434,7 @@ mod tests {
}
#[test]
fn test_expected_return_length_write() {
fn expected_return_length_write() {
let expected_message = Protocol300Message{
telegram_length: 9,
message_identifier: MessageIdentifier::Request,

View File

@@ -1,10 +1,15 @@
use crate::enums::{FunctionCode, MessageIdentifier};
#[expect(clippy::big_endian_bytes, reason="Function is needed for the Protocol 300")]
/// Calculate the checksum for a Protocol 300 message
///
/// The inputs are seperate as the function is needed without have a construced
/// `Protocol300Message` sometimes.
pub fn calculate_checksum(length: u8, message_identifier: MessageIdentifier, function_code: FunctionCode, address: u16, data_length: u8, data: &[u8]) -> u8 {
let mut checksum = 0u8;
let mut checksum: u8 = 0;
let [addr_hi, addr_lo] = address.to_be_bytes();
for byte in [length, message_identifier as u8, function_code as u8, data_length, addr_hi, addr_lo].iter().chain(data) {
for byte in [length, message_identifier.into(), function_code.into(), data_length, addr_hi, addr_lo].iter().chain(data) {
checksum = checksum.wrapping_add(*byte);
}
@@ -12,9 +17,6 @@ pub fn calculate_checksum(length: u8, message_identifier: MessageIdentifier, fun
}
#[cfg(test)]
mod tests {
use crate::{enums::{FunctionCode, MessageIdentifier}, message::Protocol300Message, utils::calculate_checksum};
@@ -28,7 +30,7 @@ mod tests {
use alloc::vec::Vec;
#[test]
fn test_check_checksum_1() {
fn check_checksum_1() {
let test_msg = Protocol300Message {
data_address: 0x5525,
telegram_length: 0x05,
@@ -42,7 +44,7 @@ mod tests {
}
#[test]
fn test_check_checksum_2() {
fn check_checksum_2() {
let test_msg = Protocol300Message {
data_address: 0x5525,
telegram_length: 0x07,
@@ -56,7 +58,7 @@ mod tests {
}
#[test]
fn test_received_message_wrapping_add_from_bytes() {
fn received_message_wrapping_add_from_bytes() {
let checksum = calculate_checksum(0x06, MessageIdentifier::Request, FunctionCode::VirtualWRITE, 0x2323, 0x01, &[0xB6]);
assert_eq!(checksum, 0x05);
}