use log::{warn, error}; extern crate alloc; use alloc::vec; use alloc::vec::Vec; use alloc::borrow::ToOwned; use thiserror::Error; use crate::{enums::{FunctionCode, MessageIdentifier}, utils::calculate_checksum}; #[derive(PartialEq, Eq, Debug)] #[must_use] pub struct Protocol300Message { /// Length of the user data in the telegram pub telegram_length: u8, /// Message Identifier, Request, Response etc. pub message_identifier: MessageIdentifier, /// Function Code, Read Write or RPC pub function_code: FunctionCode, /// Data address, 2 bytes represented as u16 pub data_address: u16, /// Expected return bytes (if it is a request) or /// Length of the data to write (if it is a write) pub data_length: u8, /// Return value of the Vitocontrol if it's a read, if it's a write message /// it contains the number ofexpected return bytes pub data: Vec, /// Checksum pub checksum: u8, } impl Protocol300Message { #[inline] /// Creates a new `Protocol300message` used to request data from the device /// /// # Arguments /// * `data_address`: Address of the data to read /// * `data_length`: Length of the data to be read. pub fn new_request_message(data_address: u16, data_length: u8) -> Self{ let telegram_length: u8 = 5; let message_identifier = MessageIdentifier::Request; let function_code = FunctionCode::VirtualREAD; let data: Vec = vec![]; let checksum = calculate_checksum(telegram_length, message_identifier, function_code, data_address, data_length, &data); Self { telegram_length, message_identifier, function_code, data_address, data_length, data: vec![], checksum } } #[inline] /// Creates a new `Protocol300message` used to respond to a message requesting data /// /// # Arguments /// * `data_address`: Address of the data to read /// * `data`: Data which should be used to respond to the message /// /// # Panics /// Panics if the data vec is longer than 256 (u8), can never happen in protocol 300 pub fn new_read_response_message(data_address: u16, data: &[u8]) -> Self{ let message_identifier = MessageIdentifier::Response; let function_code = FunctionCode::VirtualREAD; let data_length = u8::try_from(data.len()).expect("Data should never be longer than 256"); let telegram_length: u8 = 5 + data_length; let data = data.to_owned(); let checksum = calculate_checksum(telegram_length, message_identifier, function_code, data_address, data_length, &data); Self { telegram_length, message_identifier, function_code, data_address, data_length, data, checksum } } #[inline] /// Creates a new `Protocol300message` used to respond to a message writing data /// /// # Arguments /// * `data_address`: Address of the data to read /// * `data`: Data which should be used to respond to the message /// /// # Panics /// Panics if the data vec is longer than 256 (u8), can never happen in protocol 300 pub fn new_write_response_message(data_address: u16, written_data_length: u8) -> Self{ let message_identifier = MessageIdentifier::Response; let function_code = FunctionCode::VirtualWRITE; let telegram_length: u8 = 5; let checksum = calculate_checksum(telegram_length, message_identifier, function_code, data_address, written_data_length, &[]); Self { telegram_length, message_identifier, function_code, data_address, data_length: written_data_length, data: vec![], checksum } } #[inline] /// Creates a new `Protocol300message` used to write data to the device /// /// # Arguments /// * `data_address`: Address of the data to read /// * `data`: The data that should be written as vec of u8. Data length will be automatically infered from the length /// of that vec /// /// # Errors /// Returns an error if the data vec is longer than u8. This is also against the spec of Protocol 300, so it's safe to panic pub fn try_new_write_message(data_address: u16, data: &[u8]) -> Result{ let data_length: u8 = data.len().try_into()?; let telegram_length: u8 = 5 + data_length; let message_identifier = MessageIdentifier::Request; let function_code = FunctionCode::VirtualWRITE; let checksum = calculate_checksum(telegram_length, message_identifier, function_code, data_address, data_length, data); Ok(Self { telegram_length, message_identifier, function_code, data_address, data_length, data: data.to_vec(), checksum }) } #[inline] /// Decode a byte string into a `Protocol300Message` /// /// 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 { if bytes.len() < 8 { return Err(MessageDecodingError::VecTooShort) } if bytes[0] != 0x41 { return Err(MessageDecodingError::InvalidTelegramStart(bytes[0])); } let telegram_length: u8 = bytes[1]; if u8::try_from(bytes.len())? != telegram_length + 3 { return Err(MessageDecodingError::LengthMismatch()); } let message_identifier = MessageIdentifier::try_from(bytes[2])?; let function_code = FunctionCode::try_from(bytes[3])?; let data_address = (u16::from(bytes[4]) << 8) | u16::from(bytes[5]); let data_length = bytes[6]; let data = match (message_identifier, function_code) { // Read requests, write responses and CommError messages have no data content (MessageIdentifier::Request, FunctionCode::VirtualREAD) | (MessageIdentifier::Response, FunctionCode::VirtualWRITE) | (MessageIdentifier::CommError, _) => vec![], (MessageIdentifier::Request, FunctionCode::VirtualWRITE) | (MessageIdentifier::Response, FunctionCode::VirtualREAD) => { let mut data: Vec = Vec::new(); for i in 0..data_length { data.push(bytes[7 + i as usize]); } data } (_, FunctionCode::RemoteProcedureCall) => { warn!("Decoding of RPCs is currently not implemented, do not expect correct data, mainly in the 'data' field"); vec![] }, (MessageIdentifier::Unacked, _) => { warn!("No idea what 'unacked' messages are, these will not decode correctly"); vec![] }, }; 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, message_identifier, function_code, data_address, data_length, data, checksum }) } #[must_use] #[inline] /// Converts a `Protocol300Message` to a vec of bytes pub fn to_bytes(&self) -> Vec { let telegram_start_byte = 0x41; let address_as_bytes = self.data_address.to_be_bytes(); let checksum = calculate_checksum(self.telegram_length, self.message_identifier, self.function_code, self.data_address, self.data_length, &self.data); let mut bytes_vec = vec![ telegram_start_byte, self.telegram_length, self.message_identifier.into(), self.function_code.into(), address_as_bytes[0], address_as_bytes[1], self.data_length, ]; bytes_vec.extend_from_slice(&self.data); bytes_vec.push(checksum); bytes_vec } #[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 /// - `MessageReturnLengthError` - Describes the error which occured when converting pub const fn get_expected_return_telegram_length(&self) -> Result { match (self.message_identifier, self.function_code) { (MessageIdentifier::Request, FunctionCode::VirtualREAD) => Ok(8 + self.data_length as usize), (MessageIdentifier::Request, FunctionCode::VirtualWRITE) => Ok(8), (MessageIdentifier::Response, _) => Err(MessageReturnLengthError::NonsensicalRequest), _ => Err(MessageReturnLengthError::Unimplemented()) } } } /// Errors that can occur while creating a message #[derive(Error, Debug, PartialEq, Eq)] pub enum MessageCreationError { /// The data length is longer than u8 #[error("Data too long")] DataTooLong(#[from] core::num::TryFromIntError), } /// Errors that can occur while getting the expected return length of a message #[derive(Error, Debug, PartialEq, Eq)] pub enum MessageReturnLengthError { /// The data length is longer than u8 #[error("Unimplemented")] Unimplemented(), /// It makes no sense to get the expected length for a response #[error("Nonsensical request")] NonsensicalRequest } /// Errors that can occur while decoding a message from bytes #[derive(Error, Debug, PartialEq, Eq)] pub enum MessageDecodingError { /// The data length is longer than u8 #[error("Start of the telegram is not 0x41 but {0:02X?}")] InvalidTelegramStart(u8), /// Error getiting the length of the bytes slice, should never happen #[error("Error getting the length of the bytes slice")] NoLength(#[from] core::num::TryFromIntError), /// The advertiesed telegram length and actual length do not match #[error("The advertiesed telegram length and actual length do not match")] LengthMismatch(), /// Error getting message identifier enum variant by number #[error("Message Identifier not valid")] InvalidMessageIdentifier(#[from] num_enum::TryFromPrimitiveError), /// Error getting function code enum variant by number #[error("Function Code not valid")] InvalidFunctionCode(#[from] num_enum::TryFromPrimitiveError), /// 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)] mod tests { use crate::{enums::{FunctionCode, MessageIdentifier}, message::{MessageCreationError, Protocol300Message}}; #[cfg(not(feature = "std"))] extern crate alloc; #[cfg(not(feature = "std"))] use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; #[test] fn new_request_message_works() { let expected_message = Protocol300Message{ telegram_length: 5, message_identifier: MessageIdentifier::Request, function_code: FunctionCode::VirtualREAD, data_address: 0x0101, data_length: 2, data: vec![], checksum: 0x0A }; let generated_message = Protocol300Message::new_request_message(0x0101, 2); assert_eq!(expected_message, generated_message); } #[test] fn new_write_message() { let expected_message = Protocol300Message{ telegram_length: 9, message_identifier: MessageIdentifier::Request, function_code: FunctionCode::VirtualWRITE, data_address: 0x0101, data_length: 4, data: vec![0x01, 0x02, 0x03, 0x04], checksum: 0x1B }; let generated_message = Protocol300Message::try_new_write_message(0x0101, &[0x01, 0x02, 0x03, 0x04]); assert_eq!(expected_message, generated_message.unwrap()); } #[test] 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 received_message_read_response_from_bytes() { let expected_msg = 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]; let message = Protocol300Message::try_from_bytes(&bytes).unwrap(); assert_eq!(expected_msg, message); } #[test] fn received_message_write_response_from_bytes() { 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]; let message = Protocol300Message::try_from_bytes(&bytes).unwrap(); assert_eq!(expected_msg, message); } #[test] fn received_message_read_request_from_bytes() { let expected_msg = Protocol300Message{ data_address: 0x5525, telegram_length: 0x05, function_code: FunctionCode::VirtualREAD, message_identifier: MessageIdentifier::Request, data_length: 0x02, data: vec![], checksum: 0x82 }; let bytes = vec![0x41, 0x05, 0x00, 0x01, 0x55, 0x25, 0x02, 0x82]; let message = Protocol300Message::try_from_bytes(&bytes).unwrap(); assert_eq!(expected_msg, message); } #[test] fn received_message_write_request_from_bytes() { let expected_msg = Protocol300Message{ data_address: 0x2323, telegram_length: 0x06, function_code: FunctionCode::VirtualWRITE, message_identifier: MessageIdentifier::Request, data_length: 0x01, data: vec![0x02], checksum: 0x51 }; let bytes = vec![0x41, 0x06, 0x00, 0x02, 0x23, 0x23, 0x01, 0x02, 0x51]; let message = Protocol300Message::try_from_bytes(&bytes).unwrap(); assert_eq!(expected_msg, message); } // TO BYTES #[test] fn received_message_write_request_to_bytes() { let test_msg = Protocol300Message{ data_address: 0x2323, telegram_length: 0x06, function_code: FunctionCode::VirtualWRITE, message_identifier: MessageIdentifier::Request, data_length: 0x01, data: vec![0x02], checksum: 0x51 }; let bytes_expected = vec![0x41, 0x06, 0x00, 0x02, 0x23, 0x23, 0x01, 0x02, 0x51]; let bytes_generated = test_msg.to_bytes(); assert_eq!(bytes_expected, bytes_generated); } #[test] fn received_message_read_request_to_bytes() { let test_msg = Protocol300Message{ data_address: 0x5525, telegram_length: 0x05, function_code: FunctionCode::VirtualREAD, message_identifier: MessageIdentifier::Request, data_length: 0x02, data: vec![], checksum: 0x82 }; let bytes_expected = vec![0x41, 0x05, 0x00, 0x01, 0x55, 0x25, 0x02, 0x82]; let bytes_generated = test_msg.to_bytes(); assert_eq!(bytes_expected, bytes_generated); } #[test] fn received_message_write_response_to_bytes() { let test_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_expected = vec![0x41, 0x05, 0x01, 0x02, 0x21, 0x01, 0x04, 0x2E]; let bytes_generated = test_msg.to_bytes(); assert_eq!(bytes_expected, bytes_generated); } #[test] fn received_message_read_response_to_bytes() { let test_msg = 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_expected = vec![0x41, 0x09, 0x01, 0x01, 0x55, 0x25, 0x04, 0x07, 0x01, 0x27, 0x11, 0xC9]; let bytes_generated = test_msg.to_bytes(); assert_eq!(bytes_expected, bytes_generated); } #[test] fn expected_return_length_read() { let expected_message = Protocol300Message{ telegram_length: 5, message_identifier: MessageIdentifier::Request, function_code: FunctionCode::VirtualREAD, data_address: 0x0101, data_length: 2, data: vec![], checksum: 0x0A }; assert_eq!(10_usize, expected_message.get_expected_return_telegram_length().unwrap()); } #[test] fn expected_return_length_write() { let expected_message = Protocol300Message{ telegram_length: 9, message_identifier: MessageIdentifier::Request, function_code: FunctionCode::VirtualWRITE, data_address: 0x0101, data_length: 4, data: vec![0x01, 0x02, 0x03, 0x04], checksum: 0x1B }; assert_eq!(8_usize, expected_message.get_expected_return_telegram_length().unwrap()); } #[test] fn create_read_response_message() { let generated_message = Protocol300Message::new_read_response_message(0x5525, &[0x07, 0x01]).to_bytes(); let expected_message = vec![0x41, 0x07, 0x01, 0x01, 0x55, 0x25, 0x02, 0x07, 0x01, 0x8D]; assert_eq!(generated_message, expected_message); } #[test] fn create_write_response_message() { let generated_message = Protocol300Message::new_write_response_message(0x2323, 1).to_bytes(); let expected_message = vec![0x41, 0x05, 0x01, 0x02, 0x23, 0x23, 0x01, 0x4F]; assert_eq!(generated_message, expected_message); } }