pub const FC_DIAGNOSTICS: u8 = 0x08;
pub const DIAG_RETURN_QUERY_DATA: u16 = 0x0000;
pub const DIAG_RESTART_COMM: u16 = 0x0001;
pub const DIAG_RETURN_DIAG_REG: u16 = 0x0002;
pub const DIAG_CHANGE_ASCII_DELIM: u16 = 0x0003;
pub const DIAG_FORCE_LISTEN_ONLY: u16 = 0x0004;
pub const DIAG_CLEAR_COUNTERS: u16 = 0x000A;
pub const DIAG_BUS_MSG_COUNT: u16 = 0x000B;
pub const DIAG_BUS_COMM_ERR_COUNT: u16 = 0x000C;
pub const DIAG_BUS_EXCEPT_ERR_COUNT: u16 = 0x000D;
pub const DIAG_SLAVE_MSG_COUNT: u16 = 0x000E;
pub const DIAG_SLAVE_NO_RESP_COUNT: u16 = 0x000F;
pub const DIAG_SLAVE_NAK_COUNT: u16 = 0x0010;
pub const DIAG_SLAVE_BUSY_COUNT: u16 = 0x0011;
pub const DIAG_BUS_OVERRUN_COUNT: u16 = 0x0012;
pub const DIAG_CLEAR_OVERRUN: u16 = 0x0014;
#[derive(Debug, Clone, Copy, Default)]
pub struct DiagnosticCounters {
pub bus_message_count: u32,
pub crc_error_count: u32,
pub exception_count: u32,
pub nak_count: u32,
pub no_response_count: u32,
pub busy_count: u32,
pub overrun_count: u32,
pub slave_message_count: u32,
pub diagnostic_register: u16,
}
impl DiagnosticCounters {
pub fn new() -> Self {
Self::default()
}
pub fn inc_bus_message(&mut self) {
self.bus_message_count += 1;
}
pub fn inc_crc_error(&mut self) {
self.crc_error_count += 1;
}
pub fn inc_exception(&mut self) {
self.exception_count += 1;
}
pub fn inc_nak(&mut self) {
self.nak_count += 1;
}
pub fn inc_no_response(&mut self) {
self.no_response_count += 1;
}
pub fn inc_busy(&mut self) {
self.busy_count += 1;
}
pub fn inc_overrun(&mut self) {
self.overrun_count += 1;
}
pub fn inc_slave_message(&mut self) {
self.slave_message_count += 1;
}
pub fn clear_all(&mut self) {
*self = Self::new();
}
pub fn clear_overrun(&mut self) {
self.overrun_count = 0;
self.diagnostic_register &= !0x0001; }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiagError {
InvalidFrame,
UnknownSubFunction,
ListenOnlyMode,
}
pub struct DiagnosticServer {
node_id: u8,
counters: DiagnosticCounters,
ascii_delim: u8,
listen_only: bool,
}
impl DiagnosticServer {
pub fn new(node_id: u8) -> Self {
Self {
node_id,
counters: DiagnosticCounters::new(),
ascii_delim: 0x0A, listen_only: false,
}
}
pub fn counters(&self) -> &DiagnosticCounters {
&self.counters
}
pub fn counters_mut(&mut self) -> &mut DiagnosticCounters {
&mut self.counters
}
pub fn is_listen_only(&self) -> bool {
self.listen_only
}
pub fn node_id(&self) -> u8 {
self.node_id
}
pub fn process_frame(&mut self, data: &[u8]) -> Result<[u8; 8], DiagError> {
if data.len() < 6 {
return Err(DiagError::InvalidFrame);
}
if data[0] != self.node_id && data[0] != 0x00 {
self.counters.inc_bus_message();
return Err(DiagError::InvalidFrame);
}
if data[1] != FC_DIAGNOSTICS {
return Err(DiagError::InvalidFrame);
}
self.counters.inc_bus_message();
self.counters.inc_slave_message();
if self.listen_only {
return Err(DiagError::ListenOnlyMode);
}
let sub_fn = u16::from_be_bytes([data[2], data[3]]);
let sub_data = u16::from_be_bytes([data[4], data[5]]);
let mut resp = [0u8; 8];
resp[0] = self.node_id;
resp[1] = FC_DIAGNOSTICS;
let sf_bytes = sub_fn.to_be_bytes();
resp[2] = sf_bytes[0];
resp[3] = sf_bytes[1];
match sub_fn {
DIAG_RETURN_QUERY_DATA => {
resp[4] = data[4];
resp[5] = data[5];
}
DIAG_RESTART_COMM => {
self.listen_only = false;
if sub_data == 0xFF00 {
self.counters.clear_all();
}
resp[4] = 0x00;
resp[5] = 0x00;
}
DIAG_RETURN_DIAG_REG => {
let dr = self.counters.diagnostic_register.to_be_bytes();
resp[4] = dr[0];
resp[5] = dr[1];
}
DIAG_CHANGE_ASCII_DELIM => {
self.ascii_delim = (sub_data >> 8) as u8;
resp[4] = 0x00;
resp[5] = 0x00;
}
DIAG_FORCE_LISTEN_ONLY => {
self.listen_only = true;
resp[4] = 0x00;
resp[5] = 0x00;
}
DIAG_CLEAR_COUNTERS => {
self.counters.clear_all();
resp[4] = 0x00;
resp[5] = 0x00;
}
DIAG_BUS_MSG_COUNT => {
let v = (self.counters.bus_message_count as u16).to_be_bytes();
resp[4] = v[0];
resp[5] = v[1];
}
DIAG_BUS_COMM_ERR_COUNT => {
let v = (self.counters.crc_error_count as u16).to_be_bytes();
resp[4] = v[0];
resp[5] = v[1];
}
DIAG_BUS_EXCEPT_ERR_COUNT => {
let v = (self.counters.exception_count as u16).to_be_bytes();
resp[4] = v[0];
resp[5] = v[1];
}
DIAG_SLAVE_MSG_COUNT => {
let v = (self.counters.slave_message_count as u16).to_be_bytes();
resp[4] = v[0];
resp[5] = v[1];
}
DIAG_SLAVE_NO_RESP_COUNT => {
let v = (self.counters.no_response_count as u16).to_be_bytes();
resp[4] = v[0];
resp[5] = v[1];
}
DIAG_SLAVE_NAK_COUNT => {
let v = (self.counters.nak_count as u16).to_be_bytes();
resp[4] = v[0];
resp[5] = v[1];
}
DIAG_SLAVE_BUSY_COUNT => {
let v = (self.counters.busy_count as u16).to_be_bytes();
resp[4] = v[0];
resp[5] = v[1];
}
DIAG_BUS_OVERRUN_COUNT => {
let v = (self.counters.overrun_count as u16).to_be_bytes();
resp[4] = v[0];
resp[5] = v[1];
}
DIAG_CLEAR_OVERRUN => {
self.counters.clear_overrun();
resp[4] = 0x00;
resp[5] = 0x00;
}
_ => {
self.counters.inc_exception();
return Err(DiagError::UnknownSubFunction);
}
}
Ok(resp)
}
pub fn build_request(node_id: u8, sub_fn: u16, data: u16) -> [u8; 6] {
let sf = sub_fn.to_be_bytes();
let d = data.to_be_bytes();
[node_id, FC_DIAGNOSTICS, sf[0], sf[1], d[0], d[1]]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_loopback() {
let mut srv = DiagnosticServer::new(1);
let req = DiagnosticServer::build_request(1, DIAG_RETURN_QUERY_DATA, 0xABCD);
let resp = srv.process_frame(&req).unwrap();
assert_eq!(resp[0], 1);
assert_eq!(resp[1], FC_DIAGNOSTICS);
assert_eq!(u16::from_be_bytes([resp[4], resp[5]]), 0xABCD);
}
#[test]
fn test_bus_message_counter() {
let mut srv = DiagnosticServer::new(1);
for _ in 0..3 {
let req = DiagnosticServer::build_request(1, DIAG_RETURN_QUERY_DATA, 0x0000);
srv.process_frame(&req).unwrap();
}
let req = DiagnosticServer::build_request(1, DIAG_BUS_MSG_COUNT, 0x0000);
let resp = srv.process_frame(&req).unwrap();
let count = u16::from_be_bytes([resp[4], resp[5]]);
assert_eq!(count, 4);
}
#[test]
fn test_clear_counters() {
let mut srv = DiagnosticServer::new(1);
srv.counters_mut().inc_crc_error();
srv.counters_mut().inc_exception();
let req = DiagnosticServer::build_request(1, DIAG_CLEAR_COUNTERS, 0x0000);
srv.process_frame(&req).unwrap();
assert_eq!(srv.counters().crc_error_count, 0);
assert_eq!(srv.counters().exception_count, 0);
}
#[test]
fn test_listen_only_mode() {
let mut srv = DiagnosticServer::new(1);
let req = DiagnosticServer::build_request(1, DIAG_FORCE_LISTEN_ONLY, 0x0000);
let _ = srv.process_frame(&req); srv.listen_only = true;
let req2 = DiagnosticServer::build_request(1, DIAG_RETURN_QUERY_DATA, 0x1234);
assert_eq!(srv.process_frame(&req2), Err(DiagError::ListenOnlyMode));
}
#[test]
fn test_exception_count_unknown_subfn() {
let mut srv = DiagnosticServer::new(1);
let req = [1u8, FC_DIAGNOSTICS, 0xFF, 0xFF, 0x00, 0x00];
assert_eq!(srv.process_frame(&req), Err(DiagError::UnknownSubFunction));
assert_eq!(srv.counters().exception_count, 1);
}
#[test]
fn test_diagnostic_counters_inc() {
let mut c = DiagnosticCounters::new();
c.inc_bus_message();
c.inc_bus_message();
c.inc_crc_error();
c.inc_nak();
assert_eq!(c.bus_message_count, 2);
assert_eq!(c.crc_error_count, 1);
assert_eq!(c.nak_count, 1);
c.clear_all();
assert_eq!(c.bus_message_count, 0);
}
}