Initial
This commit is contained in:
33
src/enums.rs
Normal file
33
src/enums.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use enum_stringify::EnumStringify;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
|
||||
/// Message Identifier used in the communication protocol. This is specified by Viessmann
|
||||
/// and UNACKED is rarely to never used.
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, TryFromPrimitive, IntoPrimitive, Clone, Copy, PartialEq, EnumStringify)]
|
||||
pub enum MessageIdentifier {
|
||||
Request = 0x00,
|
||||
Response = 0x01,
|
||||
Unacked = 0x02,
|
||||
CommError = 0x03,
|
||||
}
|
||||
|
||||
/// Function Code used in the communication protocol. This is specified by Viessmann
|
||||
/// and Read and Write are usually used for simpler values, RPC can maybe be used for
|
||||
/// more complex things, but this is currently unclear
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, TryFromPrimitive, IntoPrimitive, Clone, Copy, PartialEq, EnumStringify)]
|
||||
pub enum FunctionCode {
|
||||
VirtualREAD = 0x01,
|
||||
VirtualWRITE = 0x02,
|
||||
RemoteProcedureCall = 0x07,
|
||||
}
|
||||
|
||||
/// Response code used in the communication protocol. This is specified by Viessmann
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, TryFromPrimitive)]
|
||||
pub enum ResponseCode {
|
||||
Ack = 0x06,
|
||||
Nack = 0x15,
|
||||
Enq = 0x05,
|
||||
}
|
||||
3
src/lib.rs
Normal file
3
src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod message;
|
||||
mod enums;
|
||||
mod utils;
|
||||
57
src/message.rs
Normal file
57
src/message.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::{enums::{FunctionCode, MessageIdentifier}, utils::calculate_checksum};
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
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<u8>,
|
||||
/// Checksum
|
||||
pub checksum: u8,
|
||||
}
|
||||
|
||||
impl Protocol300Message {
|
||||
pub fn new_request_message(data_address: u16, data_length: u8) -> Protocol300Message{
|
||||
let telegram_length: u8 = 5;
|
||||
let message_identifier = MessageIdentifier::Request;
|
||||
let function_code = FunctionCode::VirtualREAD;
|
||||
let data: Vec<u8> = vec![];
|
||||
let checksum = calculate_checksum(telegram_length, message_identifier, function_code, data_address, data_length, &data);
|
||||
Protocol300Message {
|
||||
telegram_length,
|
||||
message_identifier,
|
||||
function_code,
|
||||
data_address,
|
||||
data_length,
|
||||
data: vec![],
|
||||
checksum
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_write_message(data_address: u16, data: &[u8]) -> Protocol300Message{
|
||||
let data_length: u8 = data.len().try_into().expect("Data too long");
|
||||
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);
|
||||
Protocol300Message {
|
||||
telegram_length,
|
||||
message_identifier,
|
||||
function_code,
|
||||
data_address,
|
||||
data_length,
|
||||
data: data.to_vec(),
|
||||
checksum
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/utils.rs
Normal file
49
src/utils.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use crate::enums::{FunctionCode, MessageIdentifier};
|
||||
|
||||
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 [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) {
|
||||
checksum = checksum.wrapping_add(*byte);
|
||||
}
|
||||
|
||||
checksum
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{enums::{FunctionCode, MessageIdentifier}, message::Protocol300Message, utils::calculate_checksum};
|
||||
|
||||
#[test]
|
||||
fn test_check_checksum_1() {
|
||||
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,
|
||||
};
|
||||
assert_eq!(calculate_checksum(test_msg.telegram_length, test_msg.message_identifier, test_msg.function_code, test_msg.data_address, test_msg.data_length, &test_msg.data), 0x82);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_checksum_2() {
|
||||
let test_msg = Protocol300Message {
|
||||
data_address: 0x5525,
|
||||
telegram_length: 0x07,
|
||||
function_code: FunctionCode::VirtualREAD,
|
||||
message_identifier: MessageIdentifier::Response,
|
||||
data_length: 0x02,
|
||||
data: vec![0x07, 0x01],
|
||||
checksum: 0x8D,
|
||||
};
|
||||
assert_eq!(calculate_checksum(test_msg.telegram_length, test_msg.message_identifier, test_msg.function_code, test_msg.data_address, test_msg.data_length, &test_msg.data), 0x8D);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user