Files
protocol_300/src/transmission.rs
2026-01-18 19:20:13 +01:00

210 lines
6.4 KiB
Rust

use crate::{Protocol300Message, message};
use thiserror::Error;
#[cfg(feature = "defmt")]
use defmt::{error};
#[cfg(feature = "log")]
use log::{error};
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use alloc::borrow::ToOwned;
#[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<Self, FromBytesError> {
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)?)),
b => {
error!("Failed to parse Protocol300 transmission: invalid byte sequence (length: {}, first byte: 0x{:02X})",
bytes.len(),
bytes.first().unwrap_or(&0x00));
Err(FromBytesError::InvalidBytes(b.to_owned()))
},
}
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
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(),
}
}
/// Returns the length in bytes of the transmission
#[must_use]
pub fn length_in_bytes(&self) -> usize {
match self {
Self::Ack | Self::Enq | Self::Eot | Self::Nack => 1,
Self::Init => 3,
Self::Message(protocol300_message) => (protocol300_message.telegram_length + 3).into(),
}
}
}
/// 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")]
InvalidBytes(Vec<u8>)
}
#[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]);
}
#[test]
fn length_in_bytes_ack() {
assert_eq!(Protocol300Transmission::Ack.length_in_bytes(), 1);
}
#[test]
fn length_in_bytes_enq() {
assert_eq!(Protocol300Transmission::Enq.length_in_bytes(), 1);
}
#[test]
fn length_in_bytes_eot() {
assert_eq!(Protocol300Transmission::Eot.length_in_bytes(), 1);
}
#[test]
fn length_in_bytes_nack() {
assert_eq!(Protocol300Transmission::Nack.length_in_bytes(), 1);
}
#[test]
fn length_in_bytes_init() {
assert_eq!(Protocol300Transmission::Init.length_in_bytes(), 3);
}
#[test]
fn length_in_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
};
// telegram_length is 0x09, so expected length is 0x09 + 3 = 12
assert_eq!(Protocol300Transmission::Message(message).length_in_bytes(), 12);
}
}