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

84
Cargo.lock generated
View File

@@ -25,9 +25,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "cc"
version = "1.2.45"
version = "1.2.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe"
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -88,15 +88,15 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "find-msvc-tools"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
[[package]]
name = "hashbrown"
version = "0.16.0"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "iana-time-zone"
@@ -124,19 +124,25 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.12.0"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "js-sys"
version = "0.3.82"
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
version = "0.3.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -144,15 +150,15 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.177"
version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "log"
version = "0.4.28"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "memchr"
@@ -224,6 +230,7 @@ dependencies = [
"log",
"num_enum",
"serde",
"serde_json",
"thiserror",
]
@@ -242,6 +249,12 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "serde"
version = "1.0.228"
@@ -271,6 +284,19 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
name = "shlex"
version = "1.3.0"
@@ -279,9 +305,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
version = "2.0.109"
version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
@@ -319,9 +345,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.23.7"
version = "0.23.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d"
checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832"
dependencies = [
"indexmap",
"toml_datetime",
@@ -352,9 +378,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "wasm-bindgen"
version = "0.2.105"
version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
dependencies = [
"cfg-if",
"once_cell",
@@ -365,9 +391,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.105"
version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -375,9 +401,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.105"
version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -388,9 +414,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.105"
version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
dependencies = [
"unicode-ident",
]
@@ -456,9 +482,9 @@ dependencies = [
[[package]]
name = "winnow"
version = "0.7.13"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
dependencies = [
"memchr",
]

View File

@@ -9,16 +9,14 @@ readme = "README.md"
keywords = ["decode", "encode", "serial"]
categories = ["encoding", "decoding"]
# [features]
# json = ["serde_json"]
[features]
default = ["std"]
std = []
json = ["serde_json"]
[dependencies]
serde = { version = "1.0"}
# serde_json = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }
thiserror = "2.0.17"
chrono = { version = "0.4", features = ["serde"] }
enum_stringify = "0.6.4"
@@ -32,4 +30,3 @@ suspicious = { level = "deny", priority = -1 }
perf = { level = "deny", priority = -1 }
cargo = "warn"
nursery = "warn"
restrictions = "warn"

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);
}