diff --git a/src/lib.rs b/src/lib.rs index e8d4d92..d39a3eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,15 @@ #![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 ?")] +// #![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; +mod transmission; pub use message::Protocol300Message; -pub use enums::{FunctionCode, MessageIdentifier, ResponseCode}; \ No newline at end of file +pub use enums::{FunctionCode, MessageIdentifier, ResponseCode}; +pub use transmission::Protocol300Transmission; \ No newline at end of file diff --git a/src/message.rs b/src/message.rs index 35d41b1..ef85a89 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,4 +1,4 @@ -use log::{warn, error}; +use log::warn; extern crate alloc; diff --git a/src/transmission.rs b/src/transmission.rs new file mode 100644 index 0000000..3797e25 --- /dev/null +++ b/src/transmission.rs @@ -0,0 +1,142 @@ +use crate::{Protocol300Message, message}; +use thiserror::Error; + +#[derive(Debug, PartialEq, Eq)] +pub enum Protocol300Transmission { + /// 0x06 - ACK, used by all clients as acknowledgement + Ack, + /// 0x05 - Gets sent periodically to indicate idle/not initialized connection by the heater + Enq, + /// 0x04 - Resets the connection to a known state + Eot, + /// 0x16 0x00 0x00 - Initializes the connection, sent by the client + Init, + /// 0x15 - Negative Acknowledgement, indicates an error in the last message + Nack, + /// A valid decoded message + Message(Protocol300Message) +} + +impl Protocol300Transmission { + /// Try to convert bytes to a Protocol 300 Transmission + /// + /// # Errors + /// Returns a `FromBytesError` if the conversion fails in some way + pub fn try_from_bytes(bytes: &[u8]) -> Result { + match bytes { + [0x06] => Ok(Self::Ack), + [0x05] => Ok(Self::Enq), + [0x04] => Ok(Self::Eot), + [0x15] => Ok(Self::Nack), + [0x16, 0x00, 0x00] => Ok(Self::Init), + [0x41, ..] => Ok(Self::Message(Protocol300Message::try_from_bytes(bytes)?)), + _ => Err(FromBytesError::InvalidByte), + } + } + + #[must_use] + pub fn to_bytes(&self) -> Vec { + match self { + Self::Ack => vec![0x06], + Self::Enq => vec![0x05], + Self::Eot => vec![0x04], + Self::Init => vec![0x16, 0x00, 0x00], + Self::Nack => vec![0x15], + Self::Message(protocol300_message) => protocol300_message.to_bytes(), + } + } +} + +/// Errors that can occur when getting a Protocol 300 Transmission from bytes +#[derive(Error, Debug, PartialEq, Eq)] +pub enum FromBytesError{ + #[error("Failed to parse message")] + MessageParsing(#[from] message::MessageDecodingError), + #[error("Invalid bytes in vec")] + InvalidByte + +} + +#[cfg(test)] +mod tests { + use crate::{FunctionCode, MessageIdentifier, Protocol300Message, transmission::Protocol300Transmission}; + + #[test] + fn from_bytes_ack() { + assert_eq!(Protocol300Transmission::Ack, Protocol300Transmission::try_from_bytes(&[0x06]).unwrap()); + } + + #[test] + fn from_bytes_nack() { + assert_eq!(Protocol300Transmission::Nack, Protocol300Transmission::try_from_bytes(&[0x15]).unwrap()); + } + + #[test] + fn from_bytes_eot() { + assert_eq!(Protocol300Transmission::Eot, Protocol300Transmission::try_from_bytes(&[0x04]).unwrap()); + } + + #[test] + fn from_bytes_enq() { + assert_eq!(Protocol300Transmission::Enq, Protocol300Transmission::try_from_bytes(&[0x05]).unwrap()); + } + + #[test] + fn from_bytes_init() { + assert_eq!(Protocol300Transmission::Init, Protocol300Transmission::try_from_bytes(&[0x16, 0x00, 0x00]).unwrap()); + } + + #[test] + fn from_bytes_message() { + 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 + }; + let bytes = vec![0x41, 0x09, 0x01, 0x01, 0x55, 0x25, 0x04, 0x07, 0x01, 0x27, 0x11, 0xC9]; + assert_eq!(Protocol300Transmission::Message(message), Protocol300Transmission::try_from_bytes(&bytes).unwrap()); + } + + #[test] + fn to_bytes_ack() { + assert_eq!(Protocol300Transmission::Ack.to_bytes(), &[0x06]); + } + + #[test] + fn to_bytes_eot() { + assert_eq!(Protocol300Transmission::Eot.to_bytes(), &[0x04]); + } + + #[test] + fn to_bytes_nack() { + assert_eq!(Protocol300Transmission::Nack.to_bytes(), &[0x15]); + } + + #[test] + fn to_bytes_enq() { + assert_eq!(Protocol300Transmission::Enq.to_bytes(), &[0x05]); + } + + #[test] + fn to_bytes_init() { + assert_eq!(Protocol300Transmission::Init.to_bytes(), &[0x16, 0x00, 0x00]); + } + + #[test] + fn to_bytes_message() { + 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).to_bytes(), &[0x41, 0x09, 0x01, 0x01, 0x55, 0x25, 0x04, 0x07, 0x01, 0x27, 0x11, 0xC9]); + } +} \ No newline at end of file