From e8e5e57ce100ddc69dc6c766786b408e3f55e84d Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 12 May 2021 23:20:28 +0100 Subject: [PATCH 1/8] Add draft of InterfaceAbstractionLayer --- app_rust/src/commapi/comm_api.rs | 50 +- app_rust/src/commapi/iface.rs | 491 ++++++++++++++++++ app_rust/src/commapi/mod.rs | 1 + app_rust/src/commapi/passthru_api.rs | 85 +-- app_rust/src/commapi/pdu_api.rs | 7 +- app_rust/src/commapi/protocols/kwp2000/mod.rs | 57 +- app_rust/src/commapi/protocols/mod.rs | 85 +-- app_rust/src/commapi/protocols/obd2/mod.rs | 38 +- app_rust/src/commapi/protocols/uds/mod.rs | 101 +--- app_rust/src/commapi/socket_can_api.rs | 41 +- app_rust/src/main_ba.rs | 202 +++++++ app_rust/src/windows/cantracer.rs | 54 +- app_rust/src/windows/diag_scanner.rs | 200 ++++--- .../src/windows/diag_session/json_session.rs | 21 +- .../windows/diag_session/kwp2000_session.rs | 25 +- .../src/windows/diag_session/uds_session.rs | 22 +- app_rust/src/windows/obd.rs | 26 +- 17 files changed, 1125 insertions(+), 381 deletions(-) create mode 100644 app_rust/src/commapi/iface.rs create mode 100644 app_rust/src/main_ba.rs diff --git a/app_rust/src/commapi/comm_api.rs b/app_rust/src/commapi/comm_api.rs index 8ad84e4..e919ff5 100644 --- a/app_rust/src/commapi/comm_api.rs +++ b/app_rust/src/commapi/comm_api.rs @@ -98,10 +98,11 @@ pub struct ISO15765Config { unsafe impl Send for ISO15765Config {} unsafe impl Sync for ISO15765Config {} -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum FilterType { - Pass, - Block, + Pass { id: u32, mask: u32 }, + Block { id: u32, mask: u32 }, + IsoTP { id: u32, mask: u32, fc: u32 } } #[derive(Debug, Clone)] @@ -326,7 +327,7 @@ pub trait ComServer: Send + Sync + Debug { /// /// ## Returns /// The filter ID provided by the adapter. Use this when destroying the filter - fn add_can_filter(&mut self, filter: FilterType, id: u32, mask: u32) + fn add_can_filter(&mut self, f: FilterType) -> Result; /// Tells the adapter to remove an active filter on an open CAN channel @@ -334,19 +335,7 @@ pub trait ComServer: Send + Sync + Debug { /// * filter_idx - Filter ID to remove, this should be the value given by [`add_can_filter`](fn@add_can_filter) fn rem_can_filter(&mut self, filter_idx: u32) -> Result<(), ComServerError>; - fn add_iso15765_filter(&mut self, id: u32, mask: u32, fc_id: u32) -> Result; - - fn configure_iso15765(&mut self, cfg: &ISO15765Config) -> Result { - self.add_iso15765_filter(cfg.recv_id, 0xFFFF, cfg.send_id) - .and_then(|idx| { - self.set_iso15765_params(cfg.sep_time, cfg.block_size) - .map(|_| idx) - .map_err(|e| match self.rem_iso15765_filter(idx) { - Ok(_) => e, - Err(e1) => e1, - }) - }) - } + fn add_iso15765_filter(&mut self, f: FilterType) -> Result; /// Tells the adapter to remove an active filter on an open ISO15765 channel /// # Params @@ -369,33 +358,6 @@ pub trait ComServer: Send + Sync + Debug { block_size: u32, ) -> Result<(), ComServerError>; - /// Sends an ISOTP payload and attempts to read the ECUs response - /// IMPORTANT - This function assumes the ISO15765 interface is ALREADY open - fn send_receive_iso15765( - &self, - p: ISO15765Data, - max_timeout_ms: u128, - max_resp: usize, - ) -> Result, ComServerError> { - self.clear_iso15765_rx_buffer()?; // Clear the receive buffer - self.send_iso15765_data(&[p], 0)?; // Send data - let mut timeout = max_timeout_ms; - let mut payloads: Vec = Vec::new(); - let start = Instant::now(); - while start.elapsed().as_millis() < timeout { - if let Ok(d) = self.read_iso15765_packets(0, 10) { - for msg in d { - payloads.push(msg); - if max_resp != 0 && payloads.len() >= max_resp { - timeout = 0; // Return now! - } - } - } - std::thread::sleep(std::time::Duration::from_millis(1)); - } - Ok(payloads) - } - /// Tells the adapter to clear any data in its Rx buffer /// that is from CAN protocol fn clear_can_rx_buffer(&self) -> Result<(), ComServerError>; diff --git a/app_rust/src/commapi/iface.rs b/app_rust/src/commapi/iface.rs new file mode 100644 index 0000000..c08033b --- /dev/null +++ b/app_rust/src/commapi/iface.rs @@ -0,0 +1,491 @@ +use std::{borrow::{Borrow, BorrowMut}, cell::{Cell, RefCell}, collections::HashMap, sync::{Arc, Mutex}}; +use std::fmt::Debug; +use lazy_static::__Deref; + +use super::{comm_api::{Capability, ComServer, ComServerError, FilterType, CanFrame, ISO15765Data}, protocols::ProtocolResult}; + + +pub type InterfaceResult = std::result::Result; + +#[derive(Debug)] +#[allow(non_camel_case_types)] +pub enum IFACE_CFG { + BAUDRATE, + SEND_ID, + RECV_ID, + EXT_CAN_ADDR, + EXT_ISOTP_ADDR, + PAD_FLOW_CONTROL, + ISOTP_BS, + ISOTP_ST_MIN, +} + +impl ToString for IFACE_CFG { + fn to_string(&self) -> String { + format!("{:?}", self) + } +} + + +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq)] +#[allow(non_camel_case_types)] +pub enum PayloadFlag { + ISOTP_PAD_FRAME, + ISOTP_EXT_ADDR +} + +#[derive(Debug, Clone)] +pub struct InterfacePayload { + pub id: u32, + pub data: Vec, + pub flags: Vec +} + +impl InterfacePayload { + pub fn is_flag_set(&self, f: PayloadFlag) -> bool { + self.flags.iter().find(|x| *x == &f).is_some() + } + + pub fn new(id: u32, data: &[u8]) -> Self { + Self { + id, + data: Vec::from(data), + flags: vec![], + } + } +} + +#[derive(Debug, Clone)] +pub struct InterfaceConfig { + params: HashMap +} + +impl InterfaceConfig { + pub fn new() -> Self { + Self { + params: HashMap::new() + } + } + + pub fn add_param(&mut self, param_name: IFACE_CFG, param_value: u32) { + self.params.insert(param_name.to_string(), param_value); + } + + pub fn get_param_or_default(&self, param_name: IFACE_CFG, default: u32) -> u32 { + *self.params.get(¶m_name.to_string()).unwrap_or(&default) + } + + pub fn get_param(&self, param_name: IFACE_CFG) -> InterfaceResult { + match self.params.get(¶m_name.to_string()) { + Some(x) => Ok(*x), + None => Err(ComServerError{ err_code: 999, err_desc: format!("Interface configuration has missing parameter {:?}", param_name)}) + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum BufferType { + TX, + RX, + BOTH +} + +pub trait Interface: Send + Sync + Debug { + fn setup(&mut self, cfg: &InterfaceConfig) -> InterfaceResult<()>; + fn send_data(&mut self, data: &[InterfacePayload], timeout: u32) -> InterfaceResult; + fn recv_data(&mut self, max: usize, timeout: u32) -> InterfaceResult>; + fn add_filter(&mut self, f: FilterType) -> InterfaceResult; + fn rem_filter(&mut self, f_id: u32) -> InterfaceResult<()>; + fn close(&mut self) -> InterfaceResult<()>; + fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()>; + fn send_recv_data(&mut self, request: InterfacePayload, write_timeout: u32, read_timeout: u32) -> InterfaceResult { + self.clearBuffer(BufferType::RX); + self.send_data(&[request], write_timeout)?; + self.recv_data(1, read_timeout).map(|p| p[0].clone()) + } + fn get_server(&self) -> Box; + fn clone_box(&self) -> Box; +} + +#[derive(Debug, Clone)] +pub struct CanbusInterface { + dev: Box +} + +impl CanbusInterface { + pub fn new(dev: Box) -> InterfaceResult> { + if dev.get_capabilities().support_can_fd() != Capability::Yes { + Err(ComServerError { err_code: 1, err_desc: "Device does not support CAN".into() }) + } else { + Ok(Box::new(CanbusInterface{dev : dev.clone_box()})) + } + } + + pub fn new_raw(dev: Box) -> Self { + CanbusInterface{dev : dev.clone_box()} + } +} + +impl Interface for CanbusInterface { + + fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()> { + match bufType { + BufferType::TX => self.dev.clear_can_tx_buffer(), + BufferType::RX => self.dev.clear_can_rx_buffer(), + BufferType::BOTH => { + self.dev.clear_can_tx_buffer()?; + self.dev.clear_can_rx_buffer() + } + } + } + + fn setup(&mut self, cfg: &InterfaceConfig) -> InterfaceResult<()> { + self.dev.open_can_interface( + cfg.get_param(IFACE_CFG::BAUDRATE)?, + cfg.get_param_or_default(IFACE_CFG::EXT_CAN_ADDR, 0) > 0 + ) + } + + fn send_data(&mut self, data: &[InterfacePayload], timeout: u32) -> InterfaceResult { + let can_packets: Vec = data.iter().map(|f|{ + CanFrame::new(f.id, &f.data) + }).collect(); + self.dev.send_can_packets(&can_packets, timeout) + } + + fn recv_data(&mut self, max: usize, timeout: u32) -> InterfaceResult> { + self.dev.read_can_packets(timeout, max).map(|v| { + v.iter().map(|f| { + InterfacePayload { + id: f.id, + data: Vec::from(f.get_data()), + flags: vec![] + } + }).collect() + }) + } + + fn close(&mut self) -> InterfaceResult<()> { + self.dev.close_can_interface() + } + + fn add_filter(&mut self, f_type: FilterType) -> InterfaceResult { + self.dev.add_can_filter(f_type) + } + + fn rem_filter(&mut self, f_id: u32) -> InterfaceResult<()> { + self.dev.rem_can_filter(f_id) + } + + fn get_server(&self) -> Box { + self.dev.clone_box() + } + + fn clone_box(&self) -> Box { + Box::new(Self { + dev: self.dev.clone() + }) + } +} + +#[derive(Debug, Clone)] +pub struct IsoTPInterface { + dev: Box +} + +impl IsoTPInterface { + pub fn new(dev: Box) -> InterfaceResult> { + if dev.get_capabilities().supports_iso15765() != Capability::Yes { + Err(ComServerError { err_code: 1, err_desc: "Device does not support IsoTP".into() }) + } else { + Ok(Box::new(IsoTPInterface{dev : dev.clone_box()})) + } + } +} + +impl Interface for IsoTPInterface { + + fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()> { + match bufType { + BufferType::TX => self.dev.clear_iso15765_tx_buffer(), + BufferType::RX => self.dev.clear_iso15765_rx_buffer(), + BufferType::BOTH => { + self.dev.clear_iso15765_tx_buffer()?; + self.dev.clear_iso15765_rx_buffer() + } + } + } + + fn setup(&mut self, cfg: &InterfaceConfig) -> InterfaceResult<()> { + self.dev.open_iso15765_interface( + cfg.get_param(IFACE_CFG::BAUDRATE)?, + cfg.get_param_or_default(IFACE_CFG::EXT_CAN_ADDR, 0) > 0, + cfg.get_param_or_default(IFACE_CFG::EXT_ISOTP_ADDR, 0) > 0 + )?; + // Use default if not specified + self.dev.set_iso15765_params( + cfg.get_param_or_default(IFACE_CFG::ISOTP_ST_MIN, 20), + cfg.get_param_or_default(IFACE_CFG::ISOTP_BS, 8) + ) + } + + fn send_data(&mut self, data: &[InterfacePayload], timeout: u32) -> InterfaceResult { + let isotp_data: Vec = data.iter().map(|t|{ + ISO15765Data { + id: t.id, + data: t.data.clone(), + pad_frame: t.is_flag_set(PayloadFlag::ISOTP_PAD_FRAME), + ext_addressing: t.is_flag_set(PayloadFlag::ISOTP_EXT_ADDR), + } + }).collect(); + self.dev.send_iso15765_data(&isotp_data, timeout) + } + + fn recv_data(&mut self, max: usize, timeout: u32) -> InterfaceResult> { + self.dev.read_iso15765_packets(timeout, max).map(|v|{ + v.iter().map(|f| { + InterfacePayload { + id: f.id, + data: f.data.clone(), + flags: vec![], + } + }).collect() + }) + } + + fn close(&mut self) -> InterfaceResult<()> { + self.dev.close_iso15765_interface() + } + + fn add_filter(&mut self, f: FilterType) -> InterfaceResult { + self.dev.add_iso15765_filter(f) + } + + fn rem_filter(&mut self, f_id: u32) -> InterfaceResult<()> { + self.dev.rem_iso15765_filter(f_id) + } + + fn get_server(&self) -> Box { + self.dev.clone_box() + } + + fn clone_box(&self) -> Box { + Box::new(Self { + dev: self.dev.clone() + }) + } +} +#[derive(Debug, Clone)] +pub struct Iso14230Interface { + dev: Box +} + +impl Iso14230Interface { + pub fn new(dev: Box) -> InterfaceResult> { + if dev.get_capabilities().supports_iso14230() != Capability::Yes { + Err(ComServerError { err_code: 1, err_desc: "Device does not support ISO14230".into() }) + } else { + Ok(Box::new(Iso14230Interface{dev : dev.clone_box()})) + } + } +} + +impl Interface for Iso14230Interface { + fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()> { + todo!() + } + + fn setup(&mut self, cfg: &InterfaceConfig) -> InterfaceResult<()> { + todo!() + } + + fn send_data(&mut self, data: &[InterfacePayload], timeout: u32) -> InterfaceResult { + todo!() + } + + fn recv_data(&mut self, max: usize, timeout: u32) -> InterfaceResult> { + todo!() + } + + fn add_filter(&mut self, f: FilterType) -> InterfaceResult { + todo!() + } + + fn rem_filter(&mut self, f_id: u32) -> InterfaceResult<()> { + todo!() + } + + fn close(&mut self) -> InterfaceResult<()> { + todo!() + } + + fn get_server(&self) -> Box { + self.dev.clone_box() + } + + fn clone_box(&self) -> Box { + Box::new(Self { + dev: self.dev.clone() + }) + } +} +#[derive(Debug, Clone)] +pub struct Iso9141Interface { + dev: Box +} + +impl Iso9141Interface { + pub fn new(dev: Box) -> InterfaceResult> { + if dev.get_capabilities().supports_iso9141() != Capability::Yes { + Err(ComServerError { err_code: 1, err_desc: "Device does not support ISO9141".into() }) + } else { + Ok(Box::new(Iso9141Interface{dev : dev.clone_box()})) + } + } +} + +impl Interface for Iso9141Interface { + fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()> { + todo!() + } + + fn setup(&mut self, cfg: &InterfaceConfig) -> InterfaceResult<()> { + todo!() + } + + fn send_data(&mut self, data: &[InterfacePayload], timeout: u32) -> InterfaceResult { + todo!() + } + + fn recv_data(&mut self, max: usize, timeout: u32) -> InterfaceResult> { + todo!() + } + + fn close(&mut self) -> InterfaceResult<()> { + todo!() + } + + fn add_filter(&mut self, f: FilterType) -> InterfaceResult { + todo!() + } + + fn rem_filter(&mut self, f_id: u32) -> InterfaceResult<()> { + todo!() + } + + fn get_server(&self) -> Box { + self.dev.clone_box() + } + + fn clone_box(&self) -> Box { + Box::new(Self { + dev: self.dev.clone() + }) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum InterfaceType { + Can, + IsoTp, + Iso14230, + Iso9141 +} + +#[derive(Debug, Clone)] +pub struct DynamicInterface { + iface_type: Option, + iface: Option>>> +} + +impl DynamicInterface { + pub fn new(server: &Box, iface_type: InterfaceType, cfg: &InterfaceConfig) -> InterfaceResult { + let mut iface = match iface_type { + InterfaceType::Can => CanbusInterface::new(server.clone_box())?, + InterfaceType::IsoTp => IsoTPInterface::new(server.clone_box())?, + InterfaceType::Iso14230 => Iso14230Interface::new(server.clone_box())?, + InterfaceType::Iso9141 => Iso9141Interface::new(server.clone_box())? + }; + iface.setup(cfg)?; + Ok(Self { + iface_type: Some(iface_type), + iface: Some(Arc::new(Mutex::new(iface))) + }) + } + + pub fn blank() -> Self { + Self { + iface_type: None, + iface: None + } + } + + pub fn get_name(&self) -> &str { + if let Some(s) = self.iface_type { + match &s { + InterfaceType::Can => "CAN", + InterfaceType::IsoTp => "ISO15765 (ISO-TP)", + InterfaceType::Iso14230 => "ISO14230 (KWP2000 over LIN)", + InterfaceType::Iso9141 => "ISO9141 (OBD-II)" + } + } else { + "Not configured" + } + } + + pub fn exec) -> InterfaceResult>(&mut self, func: F) -> InterfaceResult { + match self.iface.as_mut() { + Some(x) => func(x.lock().unwrap().borrow_mut()), + None => Err(ComServerError{ err_code: 98, err_desc: "Dynamic interface not configured!".into() }) + } + } +} + +impl Interface for DynamicInterface { + + fn send_data(&mut self, data: &[InterfacePayload], timeout: u32) -> InterfaceResult { + self.exec(|iface| iface.send_data(data, timeout)) + } + + fn recv_data(&mut self, max: usize, timeout: u32) -> InterfaceResult> { + self.exec(|iface| iface.recv_data(max, timeout)) + } + + fn add_filter(&mut self, f: FilterType) -> InterfaceResult { + self.exec(|iface| iface.add_filter(f)) + } + + fn rem_filter(&mut self, f_id: u32) -> InterfaceResult<()> { + self.exec(|iface| iface.rem_filter(f_id)) + } + + fn close(&mut self) -> InterfaceResult<()> { + self.exec(|iface| iface.close()) + } + + fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()> { + self.exec(|iface| iface.clearBuffer(bufType)) + } + + fn send_recv_data(&mut self, request: InterfacePayload, write_timeout: u32, read_timeout: u32) -> InterfaceResult { + self.exec(|iface| iface.send_recv_data(request.clone(), write_timeout, read_timeout)) + } + + fn setup(&mut self, cfg: &InterfaceConfig) -> InterfaceResult<()> { + self.exec(|iface| iface.setup(cfg)) + } + + fn get_server(&self) -> Box { + match &self.iface { + Some(x) => x.lock().unwrap().get_server(), + None => panic!("Illegal access of null interface") + } + } + + fn clone_box(&self) -> Box { + match &self.iface { + Some(x) => x.lock().unwrap().clone_box(), + None => panic!("Illegal access of null interface") + } + } +} \ No newline at end of file diff --git a/app_rust/src/commapi/mod.rs b/app_rust/src/commapi/mod.rs index ee01770..f3016a3 100644 --- a/app_rust/src/commapi/mod.rs +++ b/app_rust/src/commapi/mod.rs @@ -2,6 +2,7 @@ pub mod comm_api; pub mod passthru_api; pub mod pdu_api; pub mod protocols; +pub mod iface; #[cfg(target_os = "linux")] pub mod socket_can_api; diff --git a/app_rust/src/commapi/passthru_api.rs b/app_rust/src/commapi/passthru_api.rs index 96eb6fe..b29ea28 100644 --- a/app_rust/src/commapi/passthru_api.rs +++ b/app_rust/src/commapi/passthru_api.rs @@ -243,16 +243,24 @@ impl ComServer for PassthruApi { fn add_can_filter( &mut self, - filter: FilterType, - id: u32, - mask: u32, - ) -> Result { + f: FilterType) -> Result { match *self.can_channel_idx.read().unwrap() { None => Err(self.convert_error(ERR_INVALID_CHANNEL_ID)), Some(idx) => { - let f_type = match filter { - FilterType::Pass => PASS_FILTER, - FilterType::Block => BLOCK_FILTER, + let mut apply_mask = 0; + let mut apply_id = 0; + let f_type = match f { + FilterType::Pass {id, mask} => { + apply_mask = mask; + apply_id = id; + PASS_FILTER + }, + FilterType::Block {id, mask} => { + apply_mask = mask; + apply_id = id; + BLOCK_FILTER + }, + _ => return Err(ComServerError{ err_code: 99, err_desc: "Cannot apply a flow control filter to CAN".into()}) }; let mut mask_msg = PASSTHRU_MSG { @@ -260,14 +268,14 @@ impl ComServer for PassthruApi { data_size: 4, ..Default::default() }; - PassthruApi::u32_to_msg_id(mask, &mut mask_msg); + PassthruApi::u32_to_msg_id(apply_mask, &mut mask_msg); let mut ptn_msg = PASSTHRU_MSG { protocol_id: Protocol::CAN as u32, data_size: 4, ..Default::default() }; - PassthruApi::u32_to_msg_id(id, &mut ptn_msg); + PassthruApi::u32_to_msg_id(apply_id, &mut ptn_msg); self.driver .lock() .unwrap() @@ -291,38 +299,41 @@ impl ComServer for PassthruApi { fn add_iso15765_filter( &mut self, - id: u32, - mask: u32, - flow_control_id: u32, + f: FilterType ) -> Result { match *self.iso15765_channel_idx.read().unwrap() { None => Err(self.convert_error(ERR_INVALID_CHANNEL_ID)), Some(idx) => { - let mut mask_msg = PASSTHRU_MSG { - protocol_id: Protocol::ISO15765 as u32, - data_size: 4, - ..Default::default() - }; - PassthruApi::u32_to_msg_id(mask, &mut mask_msg); - - let mut ptn_msg = PASSTHRU_MSG { - protocol_id: Protocol::ISO15765 as u32, - data_size: 4, - ..Default::default() - }; - PassthruApi::u32_to_msg_id(id, &mut ptn_msg); - - let mut fc_msg = PASSTHRU_MSG { - protocol_id: Protocol::ISO15765 as u32, - data_size: 4, - ..Default::default() - }; - PassthruApi::u32_to_msg_id(flow_control_id, &mut fc_msg); - self.driver - .lock() - .unwrap() - .start_msg_filter(idx, FLOW_CONTROL_FILTER, &mask_msg, &ptn_msg, Some(fc_msg)) - .map_err(|e| self.convert_error(e)) + if let FilterType::IsoTP{id, mask, fc} = f { + let mut mask_msg = PASSTHRU_MSG { + protocol_id: Protocol::ISO15765 as u32, + data_size: 4, + ..Default::default() + }; + PassthruApi::u32_to_msg_id(mask, &mut mask_msg); + + let mut ptn_msg = PASSTHRU_MSG { + protocol_id: Protocol::ISO15765 as u32, + data_size: 4, + ..Default::default() + }; + PassthruApi::u32_to_msg_id(id, &mut ptn_msg); + + let mut fc_msg = PASSTHRU_MSG { + protocol_id: Protocol::ISO15765 as u32, + data_size: 4, + ..Default::default() + }; + PassthruApi::u32_to_msg_id(fc, &mut fc_msg); + self.driver + .lock() + .unwrap() + .start_msg_filter(idx, FLOW_CONTROL_FILTER, &mask_msg, &ptn_msg, Some(fc_msg)) + .map_err(|e| self.convert_error(e)) + } else { + // Error out + Err(ComServerError{ err_code: 99, err_desc: "Cannot apply a pass/block filter to ISOTP".into()}) + } } } } diff --git a/app_rust/src/commapi/pdu_api.rs b/app_rust/src/commapi/pdu_api.rs index ff1cbae..71a129d 100644 --- a/app_rust/src/commapi/pdu_api.rs +++ b/app_rust/src/commapi/pdu_api.rs @@ -78,10 +78,7 @@ impl ComServer for DpduAPI { fn add_can_filter( &mut self, - filter: FilterType, - id: u32, - mask: u32, - ) -> Result { + f: FilterType) -> Result { unimplemented!() } @@ -89,7 +86,7 @@ impl ComServer for DpduAPI { unimplemented!() } - fn add_iso15765_filter(&mut self, id: u32, mask: u32, resp_id: u32) -> Result { + fn add_iso15765_filter(&mut self, f: FilterType) -> Result { unimplemented!() } diff --git a/app_rust/src/commapi/protocols/kwp2000/mod.rs b/app_rust/src/commapi/protocols/kwp2000/mod.rs index 45baa80..2f4bda5 100644 --- a/app_rust/src/commapi/protocols/kwp2000/mod.rs +++ b/app_rust/src/commapi/protocols/kwp2000/mod.rs @@ -10,14 +10,9 @@ use std::{ }; use self::start_diag_session::DiagSession; -use crate::{ - commapi::{self}, -}; +use crate::{commapi::{self, comm_api::FilterType, iface::{DynamicInterface, Interface, InterfaceConfig, InterfaceType, IsoTPInterface, PayloadFlag}}}; -use super::{ - CautionLevel, CommandError, ECUCommand, ProtocolError, ProtocolResult, ProtocolServer, - Selectable, DTC, -}; +use super::{CautionLevel, CommandError, DTC, DiagCfg, ECUCommand, ProtocolError, ProtocolResult, ProtocolServer, Selectable}; pub mod clear_diag_information; pub mod ecu_reset; @@ -342,7 +337,6 @@ impl CommandError for KwpNegativeCode { #[derive(Debug, Clone)] pub struct KWP2000ECU { - iso_tp_settings: ISO15765Config, should_run: Arc, last_error: Arc>>, cmd_tx: Sender<(u8, Vec, bool)>, @@ -416,16 +410,22 @@ impl ProtocolServer for KWP2000ECU { type Command = Service; type Error = KwpNegativeCode; fn start_diag_session( - mut comm_server: Box, - cfg: &ISO15765Config, - global_tester_present_addr: Option, + comm_server: &Box, + interface_type: InterfaceType, + interface_cfg: InterfaceConfig, + tx_flags: Option>, + diag_cfg: DiagCfg, ) -> ProtocolResult { - comm_server - .open_iso15765_interface(cfg.baud, cfg.use_ext_can, cfg.use_ext_isotp) - .map_err(ProtocolError::CommError)?; - comm_server - .configure_iso15765(cfg) - .map_err(ProtocolError::CommError)?; + if interface_type != InterfaceType::IsoTp && interface_type != InterfaceType::Iso14230 { + return Err(ProtocolError::CustomError("KWP2000 Can only be executed over ISO-TP or ISO14230".into())) + } + + let mut dyn_interface = DynamicInterface::new(comm_server, interface_type, &interface_cfg)?.clone_box(); + if interface_type == InterfaceType::IsoTp { + dyn_interface.add_filter(FilterType::IsoTP{id: diag_cfg.recv_id, mask: 0xFFFF, fc: diag_cfg.send_id})?; + } else { + return Err(ProtocolError::CustomError("KWP2000 over ISO14230 is a WIP".into())) + } let should_run = Arc::new(AtomicBool::new(true)); let should_run_t = should_run.clone(); @@ -446,14 +446,15 @@ impl ProtocolServer for KWP2000ECU { let session_type_t = session_type.clone(); // Enter extended diagnostic session (Full features) - let s_id = cfg.send_id; + let s_id = diag_cfg.send_id; std::thread::spawn(move || { println!("KWP2000 Diag server start!"); let mut timer = Instant::now(); while should_run_t.load(Relaxed) { if let Ok(data) = channel_tx_receiver.try_recv() { - let res = Self::run_command_iso_tp( - comm_server.as_ref(), + let res = Self::run_command_resp( + &mut dyn_interface, + &tx_flags, s_id, data.0, &data.1, @@ -471,17 +472,18 @@ impl ProtocolServer for KWP2000ECU { timer = Instant::now(); //if let Err(e) = Self::run_command_iso_tp(comm_server.as_ref(), 0x001C, Service::TesterPresent.into(), &[0x02], false) { - let tp_cmd = match global_tester_present_addr { + let tp_cmd = match diag_cfg.global_id { // Global tester present - No response from ECU - Some(x) => Self::run_command_iso_tp(comm_server.as_ref(), x, Service::TesterPresent.into(), &[0x02], false), - None => Self::run_command_iso_tp(comm_server.as_ref(), s_id, Service::TesterPresent.into(), &[0x01], true) + Some(x) => Self::run_command_resp(&mut dyn_interface, &tx_flags, x, Service::TesterPresent.into(), &[0x02], false), + None => Self::run_command_resp(&mut dyn_interface, &tx_flags, s_id, Service::TesterPresent.into(), &[0x01], true) }; if let Err(e) = tp_cmd { if e.is_timeout() { println!("Lost connection with ECU! - {:?}", e); // Try to regain connection - if Self::run_command_iso_tp( - comm_server.as_ref(), + if Self::run_command_resp( + &mut dyn_interface, + &tx_flags, s_id, Service::StartDiagSession.into(), &[0x92], @@ -502,16 +504,15 @@ impl ProtocolServer for KWP2000ECU { std::thread::sleep(std::time::Duration::from_micros(100)) } println!("Diag server stop!"); - comm_server.close_iso15765_interface(); + dyn_interface.close(); }); let mut ecu = KWP2000ECU { - iso_tp_settings: *cfg, should_run, last_error, cmd_tx: channel_tx_sender, cmd_rx: Arc::new(channel_rx_receiver), - send_id: cfg.send_id, + send_id: diag_cfg.send_id, curr_session_type: session_type, // Assumed, cmd_mutex: Arc::new(Mutex::new(())), }; diff --git a/app_rust/src/commapi/protocols/mod.rs b/app_rust/src/commapi/protocols/mod.rs index 6747175..a2b3e46 100644 --- a/app_rust/src/commapi/protocols/mod.rs +++ b/app_rust/src/commapi/protocols/mod.rs @@ -6,7 +6,7 @@ use uds::UDSECU; use self::{kwp2000::read_ecu_identification, uds::read_data}; -use super::comm_api::{self, ComServer, ISO15765Data}; +use super::{comm_api::{self, ComServer, ISO15765Data}, iface::{Interface, InterfaceConfig, InterfacePayload, InterfaceType, PayloadFlag}}; pub mod kwp2000; pub mod obd2; @@ -119,6 +119,13 @@ impl Display for DTC { } } +#[derive(Debug, Clone, Copy)] +pub struct DiagCfg { + pub send_id: u32, + pub recv_id: u32, + pub global_id: Option, +} + #[derive(Debug, Copy, Clone)] pub enum DiagProtocol { KWP2000, @@ -133,14 +140,16 @@ pub enum DiagServer { impl DiagServer { pub fn new( - comm_server: Box, - cfg: &ISO15765Config, - global_test_present_addr: Option, protocol: DiagProtocol, + comm_server: &Box, + interface_type: InterfaceType, + interface_cfg: InterfaceConfig, + tx_flags: Option>, + diag_cfg: DiagCfg ) -> ProtocolResult { Ok(match protocol { - DiagProtocol::KWP2000 => Self::KWP2000(KWP2000ECU::start_diag_session(comm_server, cfg, global_test_present_addr)?), - DiagProtocol::UDS => Self::UDS(UDSECU::start_diag_session(comm_server, cfg, global_test_present_addr)?), + DiagProtocol::KWP2000 => Self::KWP2000(KWP2000ECU::start_diag_session(comm_server, interface_type, interface_cfg, tx_flags, diag_cfg)?), + DiagProtocol::UDS => Self::UDS(UDSECU::start_diag_session(comm_server, interface_type, interface_cfg, tx_flags, diag_cfg)?), }) } @@ -212,9 +221,11 @@ pub trait ProtocolServer: Sized { type Command: Selectable + ECUCommand; type Error: CommandError + 'static; fn start_diag_session( - comm_server: Box, - cfg: &ISO15765Config, - global_tester_present_addr: Option, + comm_server: &Box, + interface_type: InterfaceType, + interface_cfg: InterfaceConfig, + tx_flags: Option>, + diag_cfg: DiagCfg ) -> ProtocolResult; fn exit_diag_session(&mut self); fn run_command(&self, cmd: u8, args: &[u8]) -> ProtocolResult>; @@ -222,54 +233,56 @@ pub trait ProtocolServer: Sized { fn is_in_diag_session(&self) -> bool; fn get_last_error(&self) -> Option; - fn run_command_iso_tp( - server: &dyn ComServer, + fn run_command_resp( + interface: &mut Box, + flags: &Option>, send_id: u32, cmd: u8, args: &[u8], receive_require: bool, ) -> std::result::Result, ProtocolError> { - let mut data = ISO15765Data { - id: send_id, - data: vec![cmd], - pad_frame: false, - ext_addressing: true, - }; - data.data.extend_from_slice(args); + let mut tx_data = vec![cmd]; + tx_data.extend_from_slice(args); + let mut tx = InterfacePayload::new(send_id, &tx_data); + if let Some(f) = flags { + tx.flags = f.clone(); + } if !receive_require { - server - .send_iso15765_data(&[data], 0) + interface.send_data(&[tx], 0) .map(|_| vec![]) .map_err(ProtocolError::CommError) } else { // Await max 1 second for response - let res = server.send_receive_iso15765(data, 1000, 1)?; - if res.is_empty() { - return Err(ProtocolError::Timeout); - } - let mut tmp_res = res[0].data.clone(); - if tmp_res[0] == 0x7F && tmp_res[2] == 0x78 { + let mut res = interface.send_recv_data(tx, 1000, 1000)?; + if res.data[0] == 0x7F && res.data[2] == 0x78 { // ResponsePending println!("DIAG - ECU is processing request - Waiting!"); - let start = Instant::now(); - while start.elapsed().as_millis() < 1000 { - // ECU is sending a response, but its busy right now. just gotta wait for the ECU to give us its response! - if let Some(msg) = server.read_iso15765_packets(0, 1)?.get(0) { - tmp_res = msg.data.clone(); + match interface.recv_data(1, 2000) { + Ok(data) => { + if let Some(d) = data.get(0) { + res = d.clone(); + } else { + return Err(ProtocolError::ProtocolError(Box::new( + Self::Error::from_byte(res.data[2]), + ))) + } + } + Err(e) => { + return Err(ProtocolError::CommError(e)) } } } - if tmp_res[0] == 0x7F { + if res.data[0] == 0x7F { // Still error :( Err(ProtocolError::ProtocolError(Box::new( - Self::Error::from_byte(tmp_res[2]), + Self::Error::from_byte(res.data[2]), ))) - } else if tmp_res[0] == (cmd + 0x40) { - Ok(tmp_res) + } else if res.data[0] == (cmd + 0x40) { + Ok(res.data) } else { eprintln!( "DIAG - Command response did not match request? Send: {:02X} - Recv: {:02X}", - cmd, tmp_res[0] + cmd, res.data[0] ); Err(ProtocolError::Timeout) } diff --git a/app_rust/src/commapi/protocols/obd2/mod.rs b/app_rust/src/commapi/protocols/obd2/mod.rs index f611054..8cab4b0 100644 --- a/app_rust/src/commapi/protocols/obd2/mod.rs +++ b/app_rust/src/commapi/protocols/obd2/mod.rs @@ -1,12 +1,12 @@ -use std::{sync::{Arc, Mutex, RwLock, atomic::{AtomicBool, Ordering}, mpsc::{self, Receiver, Sender}}, vec}; +use std::{borrow::{Borrow, BorrowMut}, sync::{Arc, Mutex, RwLock, atomic::{AtomicBool, Ordering}, mpsc::{self, Receiver, Sender}}, vec}; use commapi::protocols::ProtocolError; -use crate::commapi; +use crate::commapi::{self, comm_api::{ComServer, FilterType}, iface::{DynamicInterface, Interface, InterfaceConfig, InterfaceType, PayloadFlag}}; use self::{service01::Service01, service02::Service02, service03::Service03, service04::Service04, service05::Service05, service06::Service06, service07::Service07, service08::Service08, service09::Service09, service10::Service0A}; -use super::{CommandError, DTC, DTCState, ECUCommand, ProtocolResult, ProtocolServer, Selectable}; +use super::{CommandError, DTC, DTCState, DiagCfg, ECUCommand, ProtocolResult, ProtocolServer, Selectable}; pub mod service01; pub mod service02; @@ -228,14 +228,23 @@ impl ProtocolServer for ObdServer { type Error = ObdError; fn start_diag_session( - mut comm_server: Box, - cfg: &crate::commapi::comm_api::ISO15765Config, - _global_tester_present_addr: Option, // Should always be none for OBD + comm_server: &Box, + interface_type: InterfaceType, + interface_cfg: InterfaceConfig, + tx_flags: Option>, + diag_cfg: DiagCfg, ) -> super::ProtocolResult { - comm_server.open_iso15765_interface(cfg.baud, cfg.use_ext_can, cfg.use_ext_isotp)?; // For OBD this should work - comm_server.add_iso15765_filter(cfg.recv_id, 0xFFFF, cfg.send_id)?; - comm_server.set_iso15765_params(cfg.sep_time, cfg.block_size)?; - + if interface_type != InterfaceType::IsoTp && interface_type != InterfaceType::Iso9141 { + return Err(ProtocolError::CustomError("OBD-II Can only be executed over ISO-TP or ISO9141".into())) + } + + let mut dyn_interface = DynamicInterface::new(comm_server, interface_type, &interface_cfg)?.clone_box(); + if interface_type == InterfaceType::IsoTp { + dyn_interface.add_filter(FilterType::IsoTP{id: diag_cfg.recv_id, mask: 0xFFFF, fc: diag_cfg.send_id})?; + } else { + return Err(ProtocolError::CustomError("OBD-II over ISO9141 is a WIP".into())) + } + let should_run = Arc::new(AtomicBool::new(true)); let should_run_t = should_run.clone(); @@ -251,13 +260,14 @@ impl ProtocolServer for ObdServer { Receiver>>, ) = mpsc::channel(); - let s_id = cfg.send_id; + let s_id = diag_cfg.send_id; std::thread::spawn(move || { println!("OBD2 server start!"); while should_run_t.load(Ordering::Relaxed) { if let Ok(data) = channel_tx_receiver.try_recv() { - let res = Self::run_command_iso_tp( - comm_server.as_ref(), + let res = Self::run_command_resp( + &mut dyn_interface, + &tx_flags, s_id, data.0, &data.1, @@ -272,7 +282,7 @@ impl ProtocolServer for ObdServer { std::thread::sleep(std::time::Duration::from_micros(100)) } println!("OBD2 Server stop!"); - comm_server.close_iso15765_interface(); + dyn_interface.close(); }); let mut server = ObdServer { diff --git a/app_rust/src/commapi/protocols/uds/mod.rs b/app_rust/src/commapi/protocols/uds/mod.rs index b53baa6..d370ba4 100644 --- a/app_rust/src/commapi/protocols/uds/mod.rs +++ b/app_rust/src/commapi/protocols/uds/mod.rs @@ -1,9 +1,6 @@ use self::diag_session_control::DiagSession; -use super::{ - CautionLevel, CommandError, ECUCommand, ProtocolError, ProtocolResult, ProtocolServer, - Selectable, DTC, -}; -use crate::commapi::comm_api::{ComServer, ISO15765Config}; +use super::{CautionLevel, CommandError, DTC, DiagCfg, ECUCommand, ProtocolError, ProtocolResult, ProtocolServer, Selectable}; +use crate::commapi::{comm_api::{ComServer, FilterType, ISO15765Config}, iface::{Interface, InterfaceConfig, InterfaceType, IsoTPInterface, PayloadFlag}}; use std::sync::atomic::Ordering::Relaxed; use std::{ sync::{ @@ -395,7 +392,6 @@ impl CommandError for UDSNegativeCode { #[derive(Debug, Clone)] pub struct UDSECU { - iso_tp_settings: ISO15765Config, should_run: Arc, last_error: Arc>>, cmd_tx: Sender<(u8, Vec, bool)>, @@ -436,16 +432,20 @@ impl ProtocolServer for UDSECU { type Command = UDSCommand; type Error = UDSNegativeCode; fn start_diag_session( - mut comm_server: Box, - cfg: &ISO15765Config, - global_test_present_addr: Option, + comm_server: &Box, + interface_type: InterfaceType, + interface_cfg: InterfaceConfig, + tx_flags: Option>, + diag_cfg: DiagCfg, ) -> ProtocolResult { - comm_server - .open_iso15765_interface(cfg.baud, cfg.use_ext_can, cfg.use_ext_isotp) - .map_err(ProtocolError::CommError)?; - comm_server - .configure_iso15765(cfg) - .map_err(ProtocolError::CommError)?; + if interface_type != InterfaceType::IsoTp { + return Err(ProtocolError::CustomError("UDS Can only be executed over ISO-TP".into())) + } + + let mut interface = IsoTPInterface::new(comm_server.clone_box())?; + + interface.setup(&interface_cfg)?; + interface.add_filter(FilterType::IsoTP{id: diag_cfg.recv_id, mask: 0xFFFF, fc: diag_cfg.send_id})?; let should_run = Arc::new(AtomicBool::new(true)); let should_run_t = should_run.clone(); @@ -466,14 +466,15 @@ impl ProtocolServer for UDSECU { let session_type_t = session_type.clone(); // Enter extended diagnostic session (Full features) - let s_id = cfg.send_id; + let s_id = diag_cfg.send_id; std::thread::spawn(move || { println!("UDS Diag server start!"); let mut timer = Instant::now(); while should_run_t.load(Relaxed) { if let Ok(data) = channel_tx_receiver.try_recv() { - let res = Self::run_command_iso_tp( - comm_server.as_ref(), + let res = Self::run_command_resp( + &mut interface, + &tx_flags, s_id, data.0, &data.1, @@ -488,8 +489,9 @@ impl ProtocolServer for UDSECU { if timer.elapsed().as_millis() >= 2000 && *session_type_t.read().unwrap() != DiagSession::Default { - if Self::run_command_iso_tp( - comm_server.as_ref(), + if Self::run_command_resp( + &mut interface, + &tx_flags, s_id, UDSCommand::TesterPresent.into(), &[0x00], @@ -504,16 +506,15 @@ impl ProtocolServer for UDSECU { std::thread::sleep(std::time::Duration::from_micros(100)) } println!("UDS Diag server stop!"); - comm_server.close_iso15765_interface(); + interface.close(); }); let mut ecu = UDSECU { - iso_tp_settings: *cfg, should_run, last_error, cmd_tx: channel_tx_sender, cmd_rx: Arc::new(channel_rx_receiver), - send_id: cfg.send_id, + send_id: diag_cfg.send_id, curr_session_type: session_type, // Assumed, cmd_mutex: Arc::new(Mutex::new(())), }; @@ -557,58 +558,4 @@ impl ProtocolServer for UDSECU { None => None, } } - - fn run_command_iso_tp( - server: &dyn ComServer, - send_id: u32, - cmd: u8, - args: &[u8], - receive_require: bool, - ) -> Result, ProtocolError> { - let mut data = crate::commapi::comm_api::ISO15765Data { - id: send_id, - data: vec![cmd], - pad_frame: false, - ext_addressing: false, - }; - data.data.extend_from_slice(args); - if !receive_require { - server - .send_iso15765_data(&[data], 0) - .map(|_| vec![]) - .map_err(ProtocolError::CommError) - } else { - // Await max 1 second for response - let res = server.send_receive_iso15765(data, 1000, 1)?; - if res.is_empty() { - return Err(ProtocolError::Timeout); - } - let mut tmp_res = res[0].data.clone(); - if tmp_res[0] == 0x7F && tmp_res[2] == 0x78 { - // ResponsePending - println!("UDS - ECU is processing request - Waiting!"); - let start = Instant::now(); - while start.elapsed().as_millis() < 1000 { - // ECU is sending a response, but its busy right now. just gotta wait for the ECU to give us its response! - if let Some(msg) = server.read_iso15765_packets(0, 1)?.get(0) { - tmp_res = msg.data.clone(); - } - } - } - if tmp_res[0] == 0x7F { - // Still error :( - Err(ProtocolError::ProtocolError(Box::new( - Self::Error::from_byte(tmp_res[2]), - ))) - } else if tmp_res[0] == (cmd + 0x40) { - Ok(tmp_res) - } else { - eprintln!( - "UDS - Command response did not match request? Send: {:02X} - Recv: {:02X}", - cmd, tmp_res[0] - ); - Err(ProtocolError::Timeout) - } - } - } } diff --git a/app_rust/src/commapi/socket_can_api.rs b/app_rust/src/commapi/socket_can_api.rs index 636e16d..aef7998 100644 --- a/app_rust/src/commapi/socket_can_api.rs +++ b/app_rust/src/commapi/socket_can_api.rs @@ -344,11 +344,26 @@ impl ComServer for SocketCanAPI { fn add_can_filter( &mut self, - filter: FilterType, - id: u32, - mask: u32, - ) -> Result { - let f = CANFilter::new(id, mask)?; + f: FilterType) -> Result { + + // SocketCAN + let mut apply_mask = 0; + let mut apply_id = 0; + match f { + FilterType::Block{id, mask} => { + + }, + FilterType::Pass{id, mask} => { + apply_id = id; + apply_mask = mask; + }, + FilterType::IsoTP{ id, mask, fc } => { + return Err(ComServerError{ err_code: 99, err_desc: "Cannot apply a FlowControl filter to CAN".into()}) + } + } + + + let f = CANFilter::new(apply_id, apply_mask)?; // Find a free ID let mut pos = 99; for x in 0..10usize { @@ -379,7 +394,7 @@ impl ComServer for SocketCanAPI { self.write_filters() } - fn add_iso15765_filter(&mut self, id: u32, mask: u32, resp_id: u32) -> Result { + fn add_iso15765_filter(&mut self, f: FilterType) -> Result { if self.isotp_iface.read().unwrap().is_some() { // Socket CAN only allows for 1 ISO-TP filter! return Err(ComServerError { @@ -388,11 +403,15 @@ impl ComServer for SocketCanAPI { }) } - // Now try to setup the ISO-TP interface - let iface = IsoTpSocket::open_with_opts(&self.iface, resp_id, id & mask, None, None, None)?; - iface.set_nonblocking(true)?; // Request non blocking! - *self.isotp_iface.write().unwrap() = Some(iface); - Ok(1) + if let FilterType::IsoTP{id, mask, fc} = f { + // Now try to setup the ISO-TP interface + let iface = IsoTpSocket::open_with_opts(&self.iface, fc, id & mask, None, None, None)?; + iface.set_nonblocking(true)?; // Request non blocking! + *self.isotp_iface.write().unwrap() = Some(iface); + Ok(1) + } else { + Err(ComServerError{ err_code: 99, err_desc: "Cannot apply a pass/block filter to ISOTP".into()}) + } } fn rem_iso15765_filter(&mut self, filter_idx: u32) -> Result<(), ComServerError> { diff --git a/app_rust/src/main_ba.rs b/app_rust/src/main_ba.rs new file mode 100644 index 0000000..3117b91 --- /dev/null +++ b/app_rust/src/main_ba.rs @@ -0,0 +1,202 @@ + +use std::cmp::min; + +use image::{GenericImageView, ImageFormat}; +use std::io::Write; +use std::io::Read; +use std::io::{self, BufReader}; + +mod commapi; +mod passthru; + +use crate::{ + commapi::{comm_api::ComServer, passthru_api::PassthruApi, protocols::kwp2000::KWP2000ECU}, + commapi::{comm_api::ISO15765Config, protocols::ProtocolServer}, + passthru::{PassthruDevice, PassthruDrv}, +}; + +#[derive(Debug, Copy, Clone)] +struct Line { + start_x: u8, + start_y: u8, + end_x: u8, + end_y: u8, +} + +fn main() { + + let args: Vec = std::env::args().collect(); + + const LCD_WIDTH: u32 = 60; + const LCD_HEIGHT: u32 = 100; + + let dev = PassthruDevice::find_all().expect("Couldn't find any passthru adapters for test") + [0] + .clone(); + let drv = PassthruDrv::load_lib(dev.drv_path.clone()).expect("Couldn't load library"); + + // Open a Comm server link with my custom Passthru adapter + let mut api = PassthruApi::new(dev, drv).clone_box(); + api.open_device().expect("Could not open device!"); + + // Start ISO-TP KWP2000 session with IC + let mut server = KWP2000ECU::start_diag_session( + api, + &ISO15765Config { + baud: 83_333, + send_id: 1460, + recv_id: 1268, + block_size: 8, + sep_time: 20, + use_ext_isotp: false, + use_ext_can: false + }, + Some(0x001C), + ) + .expect("Error opening connection with IC ECU"); + + let input_file = std::fs::File::open(args[1].clone()).unwrap(); + let mut output_file = std::fs::File::create(args[2].clone()).unwrap(); + + // W203 IC is 56 pixels wide, ~100 tall for the top zone + let img = image::load(BufReader::new(input_file), ImageFormat::Png) + .expect("Error loading image"); + + // get scale bounds for the image + let sf = (img.width() as f32 / LCD_WIDTH as f32) as f32; + + let mut lines: Vec = Vec::new(); + // Drawing a large vertical line seems to clear the LCD in test mode + + let mut lines2: Vec = Vec::new(); + // Drawing a large vertical line seems to clear the LCD in test mode + + let max_height = std::cmp::min((img.height() as f32 / sf) as u32, LCD_HEIGHT); + + let y_offset: u8 = 60; + + for y in 0..max_height { + let mut new_line = true; + for x in 0..LCD_WIDTH { + let x_coord = min((x as f32 * sf) as u32, img.width() - 1); + let y_coord = min((y as f32 * sf) as u32, img.height() - 1); + let px_colour = img.get_pixel(x_coord, y_coord); + let rgb = px_colour.0; + if rgb[0] > 128 || rgb[1] > 128 || rgb[2] > 128 { + if new_line { + // Create new line + lines.push(Line { + start_x: x as u8, + start_y: y as u8 + y_offset, + end_x: x as u8, + end_y: y as u8 + y_offset, + }); + new_line = false; + } else { + // Append to last line in the matrix + if let Some(line) = lines.last_mut() { + line.end_x = x as u8; + line.end_y = y as u8 + y_offset; + } + } + } else { + new_line = true; // We need to draw a new line + } + } + } + + for x in 0..LCD_WIDTH { + let mut new_line = true; + for y in 0..max_height { + let x_coord = min((x as f32 * sf) as u32, img.width() - 1); + let y_coord = min((y as f32 * sf) as u32, img.height() - 1); + let px_colour = img.get_pixel(x_coord, y_coord); + let rgb = px_colour.0; + if rgb[0] > 128 || rgb[1] > 128 || rgb[2] > 128 { + if new_line { + // Create new line + lines2.push(Line { + start_x: x as u8, + start_y: y as u8 + y_offset, + end_x: x as u8, + end_y: y as u8 + y_offset, + }); + new_line = false; + } else { + // Append to last line in the matrix + if let Some(line) = lines2.last_mut() { + line.end_x = x as u8; + line.end_y = y as u8 + y_offset; + } + } + } else { + new_line = true; // We need to draw a new line + } + } + } + + let lines_ref = if lines.len() > lines2.len() { + &lines2 + } else { + &lines + }; + + let mut pic_idx: u16 = 0; + + server.set_p2_timeout_interval(150); + + for l in lines_ref { + // Send draw line command to LCD + server.run_command(0x31, &[0x03, 0x06, l.start_x, l.start_y, l.end_x, l.end_y]); + } + + // Get time-lapse photo from phone camera + std::thread::spawn(move || { + use curl::easy::Easy; + use std::sync::Arc; + + let mut handle = Easy::new(); + handle.url("http://192.168.1.204:8080/ptz?zoom=15").unwrap(); + + if let Err(e) = handle.perform() { + eprintln!("Error setting Zoom level!"); + std::process::exit(1) + } + + handle.url("http://192.168.1.204:8080/focus_distance?set=2.31").unwrap(); + + if let Err(e) = handle.perform() { + eprintln!("Error setting focus level!"); + std::process::exit(1) + } + + let mut data: Vec = Vec::new(); + { + let mut handle2 = Easy::new(); + handle2.url("http://192.168.1.204:8080/photo.jpg").unwrap(); + let mut transfer = handle2.transfer(); + transfer.write_function(|bytes| { + data.extend_from_slice(bytes); + Ok(bytes.len()) + }).unwrap(); + + if let Err(e) = transfer.perform() { + eprintln!("Error getting photo! {}", e); + std::process::exit(2) + } + } + + // Write to file + if let Err(e) = output_file.write_all(&data) { + eprintln!("Error writing file!"); + std::process::exit(3) + } + + // We are done. Exit thread! + std::process::exit(0) + }); + + loop { + server.run_command(0x31, &[03, 06, 00, 00, 00, 00]); // Keep the test active (Stops LCD from clearing after test) + } +} diff --git a/app_rust/src/windows/cantracer.rs b/app_rust/src/windows/cantracer.rs index c62a560..f2dc9d4 100644 --- a/app_rust/src/windows/cantracer.rs +++ b/app_rust/src/windows/cantracer.rs @@ -1,9 +1,9 @@ -use crate::{commapi::comm_api::{CanFrame, ComServer, FilterType}, themes::checkbox}; +use crate::{commapi::{comm_api::{CanFrame, ComServer, FilterType}, iface::{CanbusInterface, IFACE_CFG, Interface, InterfaceConfig, InterfacePayload}}, themes::checkbox}; use crate::themes::{button_coloured, ButtonType}; use crate::windows::window::WindowMessage; use iced::time; use iced::{button, Color, Column, Element, Length, Row, Scrollable, Subscription, Text}; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; use std::time::Instant; #[derive(Debug, Clone)] @@ -15,10 +15,10 @@ pub enum TracerMessage { #[derive(Debug, Clone)] pub struct CanTracer { - server: Box, + can_interface: CanbusInterface, btn_state: button::State, - can_queue: HashMap, - can_prev: HashMap, + can_queue: HashMap, + can_prev: HashMap, is_connected: bool, is_binary_fmt: bool, status_text: String, @@ -28,7 +28,7 @@ pub struct CanTracer { impl<'a> CanTracer { pub(crate) fn new(server: Box) -> Self { Self { - server, + can_interface: CanbusInterface::new_raw(server), btn_state: Default::default(), can_queue: HashMap::new(), can_prev: HashMap::new(), @@ -39,7 +39,7 @@ impl<'a> CanTracer { } } - pub fn insert_frames_to_map(&mut self, frames: Vec) { + pub fn insert_frames_to_map(&mut self, frames: Vec) { for f in frames { self.can_queue.insert(f.id, f); } @@ -48,34 +48,38 @@ impl<'a> CanTracer { pub fn update(&mut self, msg: &TracerMessage) -> Option { match msg { TracerMessage::NewData(_) => { - if let Ok(m) = self.server.as_ref().read_can_packets(0, 100) { + if let Ok(m) = self.can_interface.recv_data(100, 0) { self.insert_frames_to_map(m) } } TracerMessage::ToggleCan => { if self.is_connected { - if let Err(e) = self.server.as_mut().close_can_interface() { + if let Err(e) = self.can_interface.close() { self.status_text = format!("Error closing CAN Interface {}", e) } else { self.is_connected = false; self.can_queue.clear(); } - } else if let Err(e) = self.server.as_mut().open_can_interface(500_000, false) { + } else if let Err(e) = { + let mut cfg = InterfaceConfig::new(); + cfg.add_param(IFACE_CFG::BAUDRATE, 500_000); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, 0); + self.can_interface.setup(&cfg) + }{ self.status_text = format!("Error opening CAN Interface {}", e) } else { self.is_connected = true; if let Err(e) = - self.server - .as_mut() - .add_can_filter(FilterType::Pass, 0x0000, 0x0000) + self.can_interface.add_filter(FilterType::Pass{id: 0x0000, mask: 0x0000}) { self.status_text = format!("Error setting CAN Filter {}", e) - } else if let Err(e) = self.server.send_can_packets( - &[CanFrame::new( - 0x07DF, - &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - )], - 0, + } else if let Err(e) = self.can_interface.send_data( + &[InterfacePayload { + id: 0x07DF, + data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + flags: vec![], + }], + 0 ) { self.status_text = format!("Error sending wake-up packet {}", e) } @@ -125,8 +129,8 @@ impl<'a> CanTracer { pub fn build_can_list( binary: &bool, - curr_data: &HashMap, - old_data: &mut HashMap, + curr_data: &HashMap, + old_data: &mut HashMap, ) -> Element<'a, TracerMessage> { let mut col = Column::new(); let mut x: Vec = curr_data.keys().into_iter().copied().collect(); @@ -141,8 +145,8 @@ impl<'a> CanTracer { ); if let Some(old_frame) = old_data.get(&cid) { // Old frame exists, try to work out what changed - let old_data = old_frame.get_data(); - for (i, byte) in i.get_data().iter().enumerate() { + let old_data = &old_frame.data; + for (i, byte) in i.data.iter().enumerate() { container = if *byte == old_data[i] { // Same as old data @@ -175,7 +179,7 @@ impl<'a> CanTracer { col = col.push(container) } else { // New frame, just add it - for byte in i.get_data() { + for byte in &i.data { container = match binary { true => container.push(Row::new().push(Text::new(format!("{:08b}", byte)))), // Cram all binary bits together false => container.push( @@ -185,7 +189,7 @@ impl<'a> CanTracer { } } } - old_data.insert(cid, *i); // Update the old table + old_data.insert(cid, i.clone()); // Update the old table } col.into() } diff --git a/app_rust/src/windows/diag_scanner.rs b/app_rust/src/windows/diag_scanner.rs index 4fbe3ce..ddf69b9 100644 --- a/app_rust/src/windows/diag_scanner.rs +++ b/app_rust/src/windows/diag_scanner.rs @@ -6,7 +6,7 @@ use commapi::{ }; use iced::{Align, Column, Element, Length, Row, Space}; -use crate::{commapi::{self, comm_api::{CanFrame, ComServer}, protocols::kwp2000::read_ecu_identification::read_dcx_mmc_id}, themes::{ +use crate::{commapi::{self, comm_api::{CanFrame, ComServer, FilterType}, iface::{BufferType, DynamicInterface, IFACE_CFG, Interface, InterfaceConfig, InterfacePayload, InterfaceType, PayloadFlag}, protocols::{DiagCfg, kwp2000::read_ecu_identification::read_dcx_mmc_id}}, themes::{ button_coloured, button_outlined, progress_bar, text, title_text, ButtonType, TextType, }}; @@ -21,7 +21,8 @@ pub enum DiagScannerMessage { #[derive(Debug, Clone)] pub struct DiagScanner { - server: Box, + adapter: Box, + activate_interface: DynamicInterface, curr_stage: usize, btn: iced::button::State, status: String, @@ -39,7 +40,8 @@ pub struct DiagScanner { impl DiagScanner { pub(crate) fn new(server: Box) -> Self { Self { - server, + adapter: server, + activate_interface: DynamicInterface::blank(), curr_stage: 0, btn: Default::default(), status: String::new(), @@ -58,49 +60,49 @@ impl DiagScanner { fn increment_stage(&mut self) -> Option { match self.curr_stage { 0 => { - if self.server.read_battery_voltage().unwrap_or(0.0) < 11.7 { + if self.adapter.read_battery_voltage().unwrap_or(0.0) < 11.7 { self.status = "Battery voltage too low / Could not read battery voltage".into(); return None; } + // Try to setup CAN Iface with open filter - if let Err(e) = self.server.open_can_interface(500_000, false) { - self.status = format!("Could open CAN Interface ({})", e) - } else { - // Opening interface was OK - match self.server.add_can_filter( - commapi::comm_api::FilterType::Pass, - 0x00000000, - 0x00000000, - ) { - Ok(f_idx) => { - // Send the OBD-II get VIN request via CAN. This should wake up the OBD-II port's CAN Iface - // on most cars - if self - .server - .send_can_packets(&[CanFrame::new(0x07DF, &[0x09, 0x02])], 0) - .is_err() - { - self.status = "Could not send wake up test packet".into(); - self.server - .close_can_interface() - .expect("What a terrible failure. Closing CAN Iface failed!?"); - } else { - std::thread::sleep(std::time::Duration::from_millis(500)); - self.filter_idx = f_idx; - self.curr_stage += 1; // We can progress to the next stage! - self.can_traffic_id_list.clear(); - self.clock = Instant::now(); // Begin polling clock - return Some(DiagScannerMessage::ScanPoll); // Begin the polling! - } - } - Err(e) => { - // STOP THE CAN INTERFACE - self.server - .close_can_interface() - .expect("What a terrible failure. Closing CAN Iface failed!?"); - self.status = format!("Could not set CAN filter ({})", e) + let mut cfg = InterfaceConfig::new(); + cfg.add_param(IFACE_CFG::BAUDRATE, 500_000); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, 0); + match DynamicInterface::new( &self.adapter, InterfaceType::Can, &cfg) { + Ok(dyn_iface) => self.activate_interface = dyn_iface, + Err(e) => { + self.status = format!("Could open CAN Interface ({})", e); + return None; + } + } + + // Opening interface was OK + match self.activate_interface.exec(|can| { + can.add_filter(FilterType::Pass{id: 0x0000, mask: 0x0000}) + }) { + Ok(f_idx) => { + // Send the OBD-II get VIN request via CAN. This should wake up the OBD-II port's CAN Iface + // on most cars + if let Err(e) = self.activate_interface.send_data( + &[InterfacePayload::new(0x07Df, &[0x09, 0x02])], 0 + ) { + self.status = "Could not send wake up test packet".into(); + self.activate_interface.close().expect("WTF. Could not close CAN Interface!?"); + } else { + std::thread::sleep(std::time::Duration::from_millis(500)); + self.filter_idx = f_idx; + self.curr_stage += 1; // We can progress to the next stage! + self.can_traffic_id_list.clear(); + self.clock = Instant::now(); // Begin polling clock + return Some(DiagScannerMessage::ScanPoll); // Begin the polling! } } + Err(e) => { + // STOP THE CAN INTERFACE + self.activate_interface.close().expect("WTF. Could not close CAN Interface!?"); + self.status = format!("Could not set CAN filter ({})", e) + } } } 1 => { @@ -108,25 +110,34 @@ impl DiagScanner { self.can_traffic_id_list.insert(0x07DF, false); // Add OBD-II CAN ID so we don't scan that self.curr_stage += 1; // We can progress to the next stage! self.curr_scan_id = 0x0; // Set start ID to 0 - self.server.clear_can_rx_buffer(); + self.activate_interface.clearBuffer(BufferType::BOTH); return Some(DiagScannerMessage::ScanPoll); // Begin the CAN interrogation (Stage 1) } 2 => { - if let Err(e) = self.server.close_can_interface() { + if let Err(e) = self.activate_interface.close() { self.status = format!("Error closing old CAN Interface!: {}", e.err_desc); return None; } self.curr_stage += 1; self.curr_scan_id = 0; // First entry in our array - if let Err(e) = self.server.open_can_interface(500_000, false) { - self.status = format!("Error opening new CAN Interface!: {}", e.err_desc); - return None; + + let mut cfg = InterfaceConfig::new(); + cfg.add_param(IFACE_CFG::BAUDRATE, 500_000); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, 0); + match DynamicInterface::new(&self.adapter, InterfaceType::Can, &cfg) { + Ok(iface) => { + self.activate_interface = iface; + return Some(DiagScannerMessage::ScanPoll) // Begin the CAN interrogation (Stage 2) + } + Err(e) => { + self.status = format!("Error opening new CAN Interface!: {}", e.err_desc); + return None; + } } - return Some(DiagScannerMessage::ScanPoll); // Begin the CAN interrogation (Stage 2) } 3 => { // network cool down - if let Err(e) = self.server.close_can_interface() { + if let Err(e) = self.activate_interface.close() { self.status = format!("Error closing old CAN Interface!: {}", e.err_desc); return None; } @@ -175,7 +186,7 @@ impl DiagScanner { if self.clock.elapsed().as_millis() >= 10000 { Some(DiagScannerMessage::IncrementStage) } else { - for frame in &self.server.read_can_packets(0, 10000).unwrap_or_default() { + for frame in &self.activate_interface.recv_data(10000, 0).unwrap_or_default() { self.can_traffic_id_list.insert(frame.id, true); } Some(DiagScannerMessage::ScanPoll) // Keep polling @@ -188,25 +199,22 @@ impl DiagScanner { } else if self.clock.elapsed().as_millis() >= 100 { // Timeout waiting for response self.get_next_canid(); - self.server.clear_can_rx_buffer(); + self.activate_interface.clearBuffer(BufferType::RX); // Send a fake ISO-TP first frame. Tell the potential ECU we are sending 16 bytes to it. If it uses ISO-TP, it'll send back a // flow control message back to OVD - self.server.send_can_packets( - &[CanFrame::new( - self.curr_scan_id, - &[0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - )], - 0, + self.activate_interface.send_data( + &[InterfacePayload::new(self.curr_scan_id, &[0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])], + 0 ); // Reset the timer self.clock = Instant::now(); Some(DiagScannerMessage::ScanPoll) } else { // Keep scanning for response messages - for frame in &self.server.read_can_packets(0, 10000).unwrap_or_default() { + for frame in &self.activate_interface.recv_data(10000, 0).unwrap_or_default() { if self.can_traffic_id_list.get(&frame.id).is_none() { // Its a new frame we haven't seen before! - let payload = frame.get_data(); + let payload = &frame.data; if payload[0] == 0x30 && payload.len() == 8 { // Possible recv ID? - It might pick up multiple IDs during the scan, we filter it later on if let Some(r) = self.stage2_results.get_mut(&self.curr_scan_id) { @@ -223,11 +231,10 @@ impl DiagScanner { } 3 => { if self.clock.elapsed().as_millis() > 100 { - println!("REM FILTER"); - self.server.rem_can_filter(self.filter_idx); - self.server.clear_can_rx_buffer(); + self.activate_interface.rem_filter(self.filter_idx); + self.activate_interface.clearBuffer(BufferType::BOTH); if self.curr_scan_id as usize >= self.stage2_results.len() { - self.server.close_can_interface(); // We don't need CAN anymore + self.activate_interface.close(); // We don't need CAN anymore return Some(DiagScannerMessage::IncrementStage); // Done with stage3 scan } let keys: Vec = self.stage2_results.keys().copied().collect(); @@ -235,16 +242,15 @@ impl DiagScanner { .stage2_results .get(&keys[self.curr_scan_id as usize]) .unwrap(); - self.filter_idx = self - .server - .add_can_filter(commapi::comm_api::FilterType::Pass, filter_id[0], 0xFFFF) - .unwrap(); - self.server.send_can_packets( - &[CanFrame::new( - keys[self.curr_scan_id as usize], - &[0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - )], - 0, + + match self.activate_interface.add_filter(FilterType::Pass{id: filter_id[0], mask: 0xFFFF}) { + Ok(f_id) => self.filter_idx = f_id, + Err(e) => {todo!("CAN Filter setup failure handling in stage 3: {}", e)} + } + + self.activate_interface.send_data( + &[InterfacePayload::new(keys[self.curr_scan_id as usize], &[0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])], + 0 ); self.clock = Instant::now(); self.curr_scan_id += 1; // For next time @@ -252,8 +258,8 @@ impl DiagScanner { } else { let keys: Vec = self.stage2_results.keys().copied().collect(); // Scanning current CAN ID entries - for frame in &self.server.read_can_packets(0, 10000).unwrap_or_default() { - let payload = frame.get_data(); + for frame in &self.activate_interface.recv_data(10000, 0).unwrap_or_default() { + let payload = &frame.data; if payload[0] == 0x30 && payload.len() == 8 { // Not a false positive! We can add the Config to list! self.stage3_results.push(ISO15765Config { @@ -284,6 +290,20 @@ impl DiagScanner { } let ecu = self.stage3_results[self.curr_scan_id as usize]; + let mut cfg = InterfaceConfig::new(); + cfg.add_param(IFACE_CFG::BAUDRATE, 500_000); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, 0); + cfg.add_param(IFACE_CFG::EXT_ISOTP_ADDR, 0); + cfg.add_param(IFACE_CFG::ISOTP_BS, ecu.block_size); + cfg.add_param(IFACE_CFG::ISOTP_ST_MIN, ecu.sep_time); + + let diag_cfg = DiagCfg { + send_id: ecu.send_id, + recv_id: ecu.recv_id, + global_id: None, + + }; + let mut ecu_res = ECUDiagSettings { name: "Unknown ECU name".into(), send_id: ecu.send_id, @@ -294,8 +314,14 @@ impl DiagScanner { kwp_support: false, }; - // Interrogate the ECU with extended diagnostic session - match KWP2000ECU::start_diag_session(self.server.clone(), &ecu, None) { + match KWP2000ECU::start_diag_session( + &self.adapter, + InterfaceType::IsoTp, + cfg, + Some(vec![PayloadFlag::ISOTP_PAD_FRAME]), + diag_cfg + + ) { Ok(mut s) => { if let Ok(id) = read_dcx_mmc_id(&s) { ecu_res.name = format!("ECU Part number: {}",id.part_number); @@ -318,8 +344,30 @@ impl DiagScanner { return Some(DiagScannerMessage::IncrementStage); } let ecu = self.stage3_results[self.curr_scan_id as usize]; + + let mut cfg = InterfaceConfig::new(); + cfg.add_param(IFACE_CFG::BAUDRATE, 500_000); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, 0); + cfg.add_param(IFACE_CFG::EXT_ISOTP_ADDR, 0); + cfg.add_param(IFACE_CFG::ISOTP_BS, ecu.block_size); + cfg.add_param(IFACE_CFG::ISOTP_ST_MIN, ecu.sep_time); + + let diag_cfg = DiagCfg { + send_id: ecu.send_id, + recv_id: ecu.recv_id, + global_id: None, + + }; + + // Interrogate the ECU with extended diagnostic session - match UDSECU::start_diag_session(self.server.clone(), &ecu, None) { + match UDSECU::start_diag_session( + &self.adapter, + InterfaceType::IsoTp, + cfg, + Some(vec![PayloadFlag::ISOTP_PAD_FRAME]), + diag_cfg + ) { Ok(mut s) => { // TODO find a UDS only CMD to test with println!("ECU 0x{:04X} supports UDS!", ecu.send_id); diff --git a/app_rust/src/windows/diag_session/json_session.rs b/app_rust/src/windows/diag_session/json_session.rs index 2bc54f3..9dfac64 100644 --- a/app_rust/src/windows/diag_session/json_session.rs +++ b/app_rust/src/windows/diag_session/json_session.rs @@ -1,6 +1,6 @@ use core::panic; use std::{borrow::Borrow, cell::RefCell, sync::Arc, time::Instant, vec}; -use crate::commapi::protocols::kwp2000::read_ecu_identification; +use crate::commapi::{iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, kwp2000::read_ecu_identification}}; use common::schema::{ConType, Connection, OvdECU, diag::{TableData, dtc::ECUDTC, service::{Service}}, variant::{ECUVariantDefinition, ECUVariantPattern}}; use iced::{Align, Column, Length, Row, Scrollable, Subscription, time}; use lazy_static::__Deref; @@ -108,16 +108,21 @@ impl JsonDiagSession { // For now, Diag server ONLY supports ISO-TP, not LIN! let create_server = match connection_settings.connection_type { ConType::ISOTP { blocksize, st_min, ext_isotp_addr, ext_can_addr } => { - let cfg = ISO15765Config { - baud: connection_settings.baud, + let mut cfg = InterfaceConfig::new(); + cfg.add_param(IFACE_CFG::BAUDRATE, connection_settings.baud); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, ext_can_addr as u32); + cfg.add_param(IFACE_CFG::EXT_ISOTP_ADDR, ext_isotp_addr as u32); + cfg.add_param(IFACE_CFG::ISOTP_BS, blocksize); + cfg.add_param(IFACE_CFG::ISOTP_ST_MIN, st_min); + + let diag_cfg = DiagCfg { send_id: connection_settings.send_id, recv_id: connection_settings.recv_id, - block_size: blocksize, - sep_time: st_min, - use_ext_can: ext_can_addr, - use_ext_isotp: ext_isotp_addr + global_id: connection_settings.global_send_id, }; - DiagServer::new(comm_server, &cfg, connection_settings.global_send_id, diag_server_type) + + let tx_flags = vec![PayloadFlag::ISOTP_PAD_FRAME]; + DiagServer::new(diag_server_type, &comm_server, InterfaceType::IsoTp, cfg, Some(tx_flags), diag_cfg) }, ConType::LIN { .. } => return Err(SessionError::Other("K-Line is not implemented at this time".into())) }; diff --git a/app_rust/src/windows/diag_session/kwp2000_session.rs b/app_rust/src/windows/diag_session/kwp2000_session.rs index def7f77..248aa40 100644 --- a/app_rust/src/windows/diag_session/kwp2000_session.rs +++ b/app_rust/src/windows/diag_session/kwp2000_session.rs @@ -6,14 +6,7 @@ use std::{ use iced::{time, Column, Container, Length, Row, Space, Subscription}; use log_view::{LogType, LogView}; -use crate::{ - commapi::{ - comm_api::{ComServer, ISO15765Config}, - protocols::{kwp2000::KWP2000ECU, ProtocolServer, DTCState}, - }, - themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, - windows::window, -}; +use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, iface::{InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DTCState, DiagCfg, ProtocolServer, kwp2000::KWP2000ECU}}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::window}; use super::{log_view, DiagMessageTrait, SessionResult, SessionTrait}; @@ -167,7 +160,21 @@ impl SessionTrait for KWP2000DiagSession { fn update(&mut self, msg: &Self::msg) -> Option { match msg { KWP2000DiagSessionMsg::ConnectECU => { - match KWP2000ECU::start_diag_session(self.server.clone(), &self.ecu, None) { + let cfg = InterfaceConfig::new(); + + let diag_cfg = DiagCfg { + send_id: self.ecu.send_id, + recv_id: self.ecu.recv_id, + global_id: None, + }; + + match KWP2000ECU::start_diag_session( + &self.server, + InterfaceType::IsoTp, + cfg, + Some(vec![PayloadFlag::ISOTP_PAD_FRAME]), + diag_cfg + ) { Ok(server) => { window::disable_home(); self.diag_server = Some(server); diff --git a/app_rust/src/windows/diag_session/uds_session.rs b/app_rust/src/windows/diag_session/uds_session.rs index ce32601..fd85e57 100644 --- a/app_rust/src/windows/diag_session/uds_session.rs +++ b/app_rust/src/windows/diag_session/uds_session.rs @@ -9,7 +9,7 @@ use std::{ use iced::{time, Column, Container, Length, Row, Space, Subscription}; use log_view::{LogType, LogView}; -use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, protocols::uds::UDSECU, protocols::ProtocolServer}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::{diag_manual::DiagManualMessage, window}}; +use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, iface::{InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, ProtocolServer}, protocols::uds::UDSECU}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::{diag_manual::DiagManualMessage, window}}; use super::{log_view, DiagMessageTrait, SessionMsg, SessionResult, SessionTrait}; @@ -163,7 +163,21 @@ impl SessionTrait for UDSDiagSession { fn update(&mut self, msg: &Self::msg) -> Option { match msg { UDSDiagSessionMsg::ConnectECU => { - match UDSECU::start_diag_session(self.server.clone(), &self.ecu, None) { + let cfg = InterfaceConfig::new(); + + let diag_cfg = DiagCfg { + send_id: self.ecu.send_id, + recv_id: self.ecu.recv_id, + global_id: None, + }; + + match UDSECU::start_diag_session( + &self.server, + InterfaceType::IsoTp, + cfg, + Some(vec![PayloadFlag::ISOTP_PAD_FRAME]), + diag_cfg + ) { Ok(server) => { window::disable_home(); self.diag_server = Some(server); @@ -268,7 +282,9 @@ impl SessionTrait for UDSDiagSession { } } } - _ => {} + + UDSDiagSessionMsg::Back => {} + UDSDiagSessionMsg::LoadErrorDefinition => {} } None } diff --git a/app_rust/src/windows/obd.rs b/app_rust/src/windows/obd.rs index 007cb31..4cbe70e 100644 --- a/app_rust/src/windows/obd.rs +++ b/app_rust/src/windows/obd.rs @@ -1,4 +1,4 @@ -use crate::{commapi::{comm_api::{Capability, ComServer, ISO15765Config}, protocols::{ProtocolServer, obd2::{self, ObdServer, service09::Service09Data}}}, themes::button_coloured}; +use crate::{commapi::{comm_api::{Capability, ComServer, ISO15765Config}, iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, ProtocolServer, obd2::{self, ObdServer, service09::Service09Data}}}, themes::button_coloured}; use crate::commapi::protocols::vin::Vin; use crate::themes::{button_outlined, text, title_text, ButtonType, TextType, TitleSize}; use iced::{button, Align, Button, Column, Element, Length, Row, Space, Text}; @@ -42,16 +42,25 @@ impl OBDHome { OBDMessage::InitOBD_IsoTP => { // Try all the CAN IDs for test_id in [0x07E8, 0x07E9, 0x07E0].iter() { - let cfg = ISO15765Config { - baud: 500_000, + let mut cfg = InterfaceConfig::new(); + cfg.add_param(IFACE_CFG::BAUDRATE, 500_000); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, 0); + cfg.add_param(IFACE_CFG::EXT_ISOTP_ADDR, 0); + + let diag_cfg = DiagCfg { send_id: 0x07DF, recv_id: *test_id, - block_size: 8, - sep_time: 20, - use_ext_isotp: false, - use_ext_can: false + global_id: None, + }; - if let Ok(server) = ObdServer::start_diag_session(self.server.clone(), &cfg, None) { + if let Ok(server) = ObdServer::start_diag_session( + &self.server, + InterfaceType::IsoTp, + cfg, + Some(vec![PayloadFlag::ISOTP_PAD_FRAME]), + diag_cfg + + ) { if let Ok(r) = server.req_service09(|x| Ok(x.get_everything(&server))) { self.s09_data = r; } @@ -78,6 +87,7 @@ impl OBDHome { } self.curr_service = sid; // What service UI should we be in? } + OBDMessage::ChooseService(_) => {} } None } From 05d31b5f8c7f9e843eac650b7e88640610ac5542 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 14 May 2021 21:32:01 +0100 Subject: [PATCH 2/8] Add IfaceCFG to diag sessions --- app_rust/src/commapi/iface.rs | 7 ++++++- app_rust/src/windows/cantracer.rs | 2 +- app_rust/src/windows/diag_session/kwp2000_session.rs | 9 +++++++-- app_rust/src/windows/diag_session/uds_session.rs | 10 ++++++++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app_rust/src/commapi/iface.rs b/app_rust/src/commapi/iface.rs index c08033b..fafd539 100644 --- a/app_rust/src/commapi/iface.rs +++ b/app_rust/src/commapi/iface.rs @@ -101,7 +101,12 @@ pub trait Interface: Send + Sync + Debug { fn send_recv_data(&mut self, request: InterfacePayload, write_timeout: u32, read_timeout: u32) -> InterfaceResult { self.clearBuffer(BufferType::RX); self.send_data(&[request], write_timeout)?; - self.recv_data(1, read_timeout).map(|p| p[0].clone()) + let res = self.recv_data(1, read_timeout)?; + if res.is_empty() { + return Err(ComServerError{err_code: 2, err_desc: "Timeout waiting".into()}); + } else { + return Ok(res[0].clone()) + } } fn get_server(&self) -> Box; fn clone_box(&self) -> Box; diff --git a/app_rust/src/windows/cantracer.rs b/app_rust/src/windows/cantracer.rs index f2dc9d4..58df77b 100644 --- a/app_rust/src/windows/cantracer.rs +++ b/app_rust/src/windows/cantracer.rs @@ -63,7 +63,7 @@ impl<'a> CanTracer { } else if let Err(e) = { let mut cfg = InterfaceConfig::new(); cfg.add_param(IFACE_CFG::BAUDRATE, 500_000); - cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, 0); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, 1); self.can_interface.setup(&cfg) }{ self.status_text = format!("Error opening CAN Interface {}", e) diff --git a/app_rust/src/windows/diag_session/kwp2000_session.rs b/app_rust/src/windows/diag_session/kwp2000_session.rs index 248aa40..0a64295 100644 --- a/app_rust/src/windows/diag_session/kwp2000_session.rs +++ b/app_rust/src/windows/diag_session/kwp2000_session.rs @@ -6,7 +6,7 @@ use std::{ use iced::{time, Column, Container, Length, Row, Space, Subscription}; use log_view::{LogType, LogView}; -use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, iface::{InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DTCState, DiagCfg, ProtocolServer, kwp2000::KWP2000ECU}}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::window}; +use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DTCState, DiagCfg, ProtocolServer, kwp2000::KWP2000ECU}}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::window}; use super::{log_view, DiagMessageTrait, SessionResult, SessionTrait}; @@ -160,7 +160,12 @@ impl SessionTrait for KWP2000DiagSession { fn update(&mut self, msg: &Self::msg) -> Option { match msg { KWP2000DiagSessionMsg::ConnectECU => { - let cfg = InterfaceConfig::new(); + let mut cfg = InterfaceConfig::new(); + cfg.add_param(IFACE_CFG::BAUDRATE, self.ecu.baud); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, self.ecu.use_ext_can as u32); + cfg.add_param(IFACE_CFG::EXT_ISOTP_ADDR, self.ecu.use_ext_isotp as u32); + cfg.add_param(IFACE_CFG::ISOTP_BS, self.ecu.block_size); + cfg.add_param(IFACE_CFG::ISOTP_ST_MIN, self.ecu.sep_time); let diag_cfg = DiagCfg { send_id: self.ecu.send_id, diff --git a/app_rust/src/windows/diag_session/uds_session.rs b/app_rust/src/windows/diag_session/uds_session.rs index fd85e57..1c9288d 100644 --- a/app_rust/src/windows/diag_session/uds_session.rs +++ b/app_rust/src/windows/diag_session/uds_session.rs @@ -9,7 +9,7 @@ use std::{ use iced::{time, Column, Container, Length, Row, Space, Subscription}; use log_view::{LogType, LogView}; -use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, iface::{InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, ProtocolServer}, protocols::uds::UDSECU}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::{diag_manual::DiagManualMessage, window}}; +use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, ProtocolServer}, protocols::uds::UDSECU}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::{diag_manual::DiagManualMessage, window}}; use super::{log_view, DiagMessageTrait, SessionMsg, SessionResult, SessionTrait}; @@ -163,7 +163,13 @@ impl SessionTrait for UDSDiagSession { fn update(&mut self, msg: &Self::msg) -> Option { match msg { UDSDiagSessionMsg::ConnectECU => { - let cfg = InterfaceConfig::new(); + let mut cfg = InterfaceConfig::new(); + cfg.add_param(IFACE_CFG::BAUDRATE, self.ecu.baud); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, self.ecu.use_ext_can as u32); + cfg.add_param(IFACE_CFG::EXT_ISOTP_ADDR, self.ecu.use_ext_isotp as u32); + cfg.add_param(IFACE_CFG::ISOTP_BS, self.ecu.block_size); + cfg.add_param(IFACE_CFG::ISOTP_ST_MIN, self.ecu.sep_time); + let diag_cfg = DiagCfg { send_id: self.ecu.send_id, From a7154031e52e9cadf79362765dc5b2bff137499e Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 14 May 2021 21:36:59 +0100 Subject: [PATCH 3/8] Adjust timeouts --- app_rust/src/commapi/protocols/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app_rust/src/commapi/protocols/mod.rs b/app_rust/src/commapi/protocols/mod.rs index a2b3e46..a286046 100644 --- a/app_rust/src/commapi/protocols/mod.rs +++ b/app_rust/src/commapi/protocols/mod.rs @@ -253,7 +253,7 @@ pub trait ProtocolServer: Sized { .map_err(ProtocolError::CommError) } else { // Await max 1 second for response - let mut res = interface.send_recv_data(tx, 1000, 1000)?; + let mut res = interface.send_recv_data(tx, 0, 2000)?; if res.data[0] == 0x7F && res.data[2] == 0x78 { // ResponsePending println!("DIAG - ECU is processing request - Waiting!"); From 1f7d71bc0e3f133c13eee5faf642688ec7219de3 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Sat, 15 May 2021 17:31:06 +0100 Subject: [PATCH 4/8] Improve CAN Analyzer --- app_rust/src/windows/cantracer.rs | 137 +++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 32 deletions(-) diff --git a/app_rust/src/windows/cantracer.rs b/app_rust/src/windows/cantracer.rs index 58df77b..f94d2d9 100644 --- a/app_rust/src/windows/cantracer.rs +++ b/app_rust/src/windows/cantracer.rs @@ -1,39 +1,77 @@ -use crate::{commapi::{comm_api::{CanFrame, ComServer, FilterType}, iface::{CanbusInterface, IFACE_CFG, Interface, InterfaceConfig, InterfacePayload}}, themes::checkbox}; +use crate::{commapi::{comm_api::{CanFrame, ComServer, FilterType}, iface::{CanbusInterface, IFACE_CFG, Interface, InterfaceConfig, InterfacePayload}}, themes::{TextType, checkbox, picklist, text}}; use crate::themes::{button_coloured, ButtonType}; use crate::windows::window::WindowMessage; -use iced::time; +use iced::{pick_list, time}; use iced::{button, Color, Column, Element, Length, Row, Scrollable, Subscription, Text}; -use std::{collections::HashMap, sync::Arc}; +use iced_native::Widget; +use std::{borrow::Cow, collections::HashMap, sync::Arc}; use std::time::Instant; #[derive(Debug, Clone)] pub enum TracerMessage { NewData(Instant), ToggleCan, + ToggleExt(bool), + SelectBaud(CanSpeed), ToggleBinaryMode(bool), } #[derive(Debug, Clone)] pub struct CanTracer { + can_spd_state: pick_list::State, + can_spd: CanSpeed, can_interface: CanbusInterface, btn_state: button::State, can_queue: HashMap, can_prev: HashMap, is_connected: bool, is_binary_fmt: bool, + use_ext_can: bool, status_text: String, scroll_state: iced::scrollable::State, } +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct CanSpeed { + pub baud: u32, + text: &'static str +} + +impl std::fmt::Display for CanSpeed { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{}", self.text)) + } +} + +const CAN_SPEEDS: &[CanSpeed] = &[ + CanSpeed {baud: 5000, text: "5 kbit/s" }, + CanSpeed {baud: 10000, text: "10 kbit/s" }, + CanSpeed {baud: 20000, text: "20 kbit/s" }, + CanSpeed {baud: 25000, text: "25 kbit/s" }, + CanSpeed {baud: 33333, text: "33.3 kbit/s" }, + CanSpeed {baud: 50000, text: "50 kbit/s" }, + CanSpeed {baud: 80000, text: "80 kbit/s" }, + CanSpeed {baud: 83333, text: "83.3 kbit/s" }, + CanSpeed {baud: 100000, text: "100 kbit/s" }, + CanSpeed {baud: 125000, text: "125 kbit/s" }, + CanSpeed {baud: 200000, text: "200 kbit/s" }, + CanSpeed {baud: 250000, text: "250 kbit/s" }, + CanSpeed {baud: 500000, text: "500 kbit/s" }, + CanSpeed {baud: 1000000, text: "1 mbit/s" } +]; + impl<'a> CanTracer { pub(crate) fn new(server: Box) -> Self { Self { + can_spd_state: Default::default(), + can_spd: CAN_SPEEDS.iter().find(|x| x.baud == 500000).unwrap().clone(), // 500kbps can_interface: CanbusInterface::new_raw(server), btn_state: Default::default(), can_queue: HashMap::new(), can_prev: HashMap::new(), is_connected: false, is_binary_fmt: false, + use_ext_can: false, status_text: "".into(), scroll_state: Default::default(), } @@ -45,6 +83,42 @@ impl<'a> CanTracer { } } + fn close_can(&mut self) { + if let Err(e) = self.can_interface.close() { + self.status_text = format!("Error closing CAN Interface {}", e) + } else { + self.is_connected = false; + self.can_queue.clear(); + } + } + + fn open_can(&mut self) { + if let Err(e) = { + let mut cfg = InterfaceConfig::new(); + cfg.add_param(IFACE_CFG::BAUDRATE, self.can_spd.baud); + cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, self.use_ext_can as u32); + self.can_interface.setup(&cfg) + }{ + self.status_text = format!("Error opening CAN Interface {}", e) + } else { + self.is_connected = true; + if let Err(e) = + self.can_interface.add_filter(FilterType::Pass{id: 0x0000, mask: 0x0000}) + { + self.status_text = format!("Error setting CAN Filter {}", e) + } else if let Err(e) = self.can_interface.send_data( + &[InterfacePayload { + id: 0x07DF, + data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + flags: vec![], + }], + 0 + ) { + self.status_text = format!("Error sending wake-up packet {}", e) + } + } + } + pub fn update(&mut self, msg: &TracerMessage) -> Option { match msg { TracerMessage::NewData(_) => { @@ -54,38 +128,23 @@ impl<'a> CanTracer { } TracerMessage::ToggleCan => { if self.is_connected { - if let Err(e) = self.can_interface.close() { - self.status_text = format!("Error closing CAN Interface {}", e) - } else { - self.is_connected = false; - self.can_queue.clear(); - } - } else if let Err(e) = { - let mut cfg = InterfaceConfig::new(); - cfg.add_param(IFACE_CFG::BAUDRATE, 500_000); - cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, 1); - self.can_interface.setup(&cfg) - }{ - self.status_text = format!("Error opening CAN Interface {}", e) + self.close_can(); } else { - self.is_connected = true; - if let Err(e) = - self.can_interface.add_filter(FilterType::Pass{id: 0x0000, mask: 0x0000}) - { - self.status_text = format!("Error setting CAN Filter {}", e) - } else if let Err(e) = self.can_interface.send_data( - &[InterfacePayload { - id: 0x07DF, - data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - flags: vec![], - }], - 0 - ) { - self.status_text = format!("Error sending wake-up packet {}", e) - } + self.open_can(); } } TracerMessage::ToggleBinaryMode(b) => self.is_binary_fmt = *b, + TracerMessage::ToggleExt(use_ext) => { + self.use_ext_can = *use_ext; + // User is in session, reconnect + if self.is_connected { + self.close_can(); + self.open_can(); + } + }, + TracerMessage::SelectBaud(b) => { + self.can_spd = *b + } } None } @@ -103,13 +162,27 @@ impl<'a> CanTracer { true => button_coloured(&mut self.btn_state, "Disconnect", ButtonType::Info), } .on_press(TracerMessage::ToggleCan); + + let speed_selector = picklist(&mut self.can_spd_state, CAN_SPEEDS, Some(self.can_spd), TracerMessage::SelectBaud); + let check = self.is_binary_fmt; + let ext_toggle = checkbox(self.use_ext_can, "Use Extended CAN", TracerMessage::ToggleExt); + + let mut r = Row::new(); + if !self.is_connected { + r = r.push(text("CAN Speed: ", TextType::Normal)) + .push(speed_selector) + + } + Column::new() .padding(10) .spacing(10) .push(Text::new("CAN Tracer")) + .push(r) .push(btn) + .push(ext_toggle) .push(checkbox( check, "View CAN in Binary", @@ -141,7 +214,7 @@ impl<'a> CanTracer { container = container.push( Row::new() .push(Text::new(format!("CID: {:04X}", i.id))) - .width(Length::Units(100)), + .width(Length::Units(200)), ); if let Some(old_frame) = old_data.get(&cid) { // Old frame exists, try to work out what changed From 6270867304af97798221a725f143658287d4623c Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Sat, 15 May 2021 20:12:37 +0100 Subject: [PATCH 5/8] Make compiler happier --- app_rust/Cargo.toml | 1 + app_rust/src/commapi/iface.rs | 31 +-- app_rust/src/commapi/mod.rs | 2 + app_rust/src/commapi/protocols/kwp2000/mod.rs | 2 +- app_rust/src/commapi/protocols/mod.rs | 6 +- app_rust/src/commapi/protocols/obd2/mod.rs | 28 +-- app_rust/src/commapi/protocols/uds/mod.rs | 4 +- app_rust/src/commapi/socket_can_api.rs | 22 -- app_rust/src/main_ba.rs | 202 ------------------ app_rust/src/passthru.rs | 5 + app_rust/src/themes/mod.rs | 2 + app_rust/src/widgets/mod.rs | 121 +---------- app_rust/src/widgets/table.rs | 11 +- app_rust/src/windows/cantracer.rs | 5 +- app_rust/src/windows/diag_home.rs | 3 - app_rust/src/windows/diag_manual.rs | 5 +- app_rust/src/windows/diag_scanner.rs | 16 +- .../src/windows/diag_session/json_session.rs | 15 +- .../windows/diag_session/kwp2000_session.rs | 1 - app_rust/src/windows/diag_session/mod.rs | 4 - .../src/windows/diag_session/uds_session.rs | 7 +- app_rust/src/windows/home.rs | 1 + app_rust/src/windows/launcher.rs | 7 +- app_rust/src/windows/obd.rs | 10 +- app_rust/src/windows/window.rs | 9 +- 25 files changed, 79 insertions(+), 441 deletions(-) delete mode 100644 app_rust/src/main_ba.rs diff --git a/app_rust/Cargo.toml b/app_rust/Cargo.toml index 3097673..16f1efd 100644 --- a/app_rust/Cargo.toml +++ b/app_rust/Cargo.toml @@ -3,6 +3,7 @@ name = "openvehiclediag" version = "1.0.0" description = "pen Vehicle Diagnostics (OVD) is a Rust-based open source vehicle ECU diagnostic platform." edition = "2018" + [package.metadata.bundle] version = "0.6.0" authors = ["ashcon"] diff --git a/app_rust/src/commapi/iface.rs b/app_rust/src/commapi/iface.rs index fafd539..1cb27f8 100644 --- a/app_rust/src/commapi/iface.rs +++ b/app_rust/src/commapi/iface.rs @@ -1,14 +1,13 @@ -use std::{borrow::{Borrow, BorrowMut}, cell::{Cell, RefCell}, collections::HashMap, sync::{Arc, Mutex}}; +use std::{borrow::BorrowMut, collections::HashMap, sync::{Arc, Mutex}}; use std::fmt::Debug; -use lazy_static::__Deref; -use super::{comm_api::{Capability, ComServer, ComServerError, FilterType, CanFrame, ISO15765Data}, protocols::ProtocolResult}; +use super::{comm_api::{Capability, ComServer, ComServerError, FilterType, CanFrame, ISO15765Data}}; pub type InterfaceResult = std::result::Result; #[derive(Debug)] -#[allow(non_camel_case_types)] +#[allow(non_camel_case_types, dead_code)] pub enum IFACE_CFG { BAUDRATE, SEND_ID, @@ -84,6 +83,7 @@ impl InterfaceConfig { } #[derive(Debug, Copy, Clone, PartialEq)] +#[allow(dead_code)] pub enum BufferType { TX, RX, @@ -97,9 +97,9 @@ pub trait Interface: Send + Sync + Debug { fn add_filter(&mut self, f: FilterType) -> InterfaceResult; fn rem_filter(&mut self, f_id: u32) -> InterfaceResult<()>; fn close(&mut self) -> InterfaceResult<()>; - fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()>; + fn clear_buffer(&mut self, buffer_type: BufferType) -> InterfaceResult<()>; fn send_recv_data(&mut self, request: InterfacePayload, write_timeout: u32, read_timeout: u32) -> InterfaceResult { - self.clearBuffer(BufferType::RX); + self.clear_buffer(BufferType::RX)?; self.send_data(&[request], write_timeout)?; let res = self.recv_data(1, read_timeout)?; if res.is_empty() { @@ -133,8 +133,8 @@ impl CanbusInterface { impl Interface for CanbusInterface { - fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()> { - match bufType { + fn clear_buffer(&mut self, buffer_type: BufferType) -> InterfaceResult<()> { + match buffer_type { BufferType::TX => self.dev.clear_can_tx_buffer(), BufferType::RX => self.dev.clear_can_rx_buffer(), BufferType::BOTH => { @@ -210,8 +210,8 @@ impl IsoTPInterface { impl Interface for IsoTPInterface { - fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()> { - match bufType { + fn clear_buffer(&mut self, buffer_type: BufferType) -> InterfaceResult<()> { + match buffer_type { BufferType::TX => self.dev.clear_iso15765_tx_buffer(), BufferType::RX => self.dev.clear_iso15765_rx_buffer(), BufferType::BOTH => { @@ -295,8 +295,9 @@ impl Iso14230Interface { } } +#[allow(unused_variables)] impl Interface for Iso14230Interface { - fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()> { + fn clear_buffer(&mut self, buffer_type: BufferType) -> InterfaceResult<()> { todo!() } @@ -349,8 +350,9 @@ impl Iso9141Interface { } } +#[allow(unused_variables)] impl Interface for Iso9141Interface { - fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()> { + fn clear_buffer(&mut self, buffer_type: BufferType) -> InterfaceResult<()> { todo!() } @@ -425,6 +427,7 @@ impl DynamicInterface { } } + #[allow(dead_code)] pub fn get_name(&self) -> &str { if let Some(s) = self.iface_type { match &s { @@ -468,8 +471,8 @@ impl Interface for DynamicInterface { self.exec(|iface| iface.close()) } - fn clearBuffer(&mut self, bufType: BufferType) -> InterfaceResult<()> { - self.exec(|iface| iface.clearBuffer(bufType)) + fn clear_buffer(&mut self, buffer_type: BufferType) -> InterfaceResult<()> { + self.exec(|iface| iface.clear_buffer(buffer_type)) } fn send_recv_data(&mut self, request: InterfacePayload, write_timeout: u32, read_timeout: u32) -> InterfaceResult { diff --git a/app_rust/src/commapi/mod.rs b/app_rust/src/commapi/mod.rs index f3016a3..bad28aa 100644 --- a/app_rust/src/commapi/mod.rs +++ b/app_rust/src/commapi/mod.rs @@ -1,3 +1,5 @@ + +#[allow(dead_code)] pub mod comm_api; pub mod passthru_api; pub mod pdu_api; diff --git a/app_rust/src/commapi/protocols/kwp2000/mod.rs b/app_rust/src/commapi/protocols/kwp2000/mod.rs index 2f4bda5..70ca770 100644 --- a/app_rust/src/commapi/protocols/kwp2000/mod.rs +++ b/app_rust/src/commapi/protocols/kwp2000/mod.rs @@ -504,7 +504,7 @@ impl ProtocolServer for KWP2000ECU { std::thread::sleep(std::time::Duration::from_micros(100)) } println!("Diag server stop!"); - dyn_interface.close(); + let _res = dyn_interface.close(); }); let mut ecu = KWP2000ECU { diff --git a/app_rust/src/commapi/protocols/mod.rs b/app_rust/src/commapi/protocols/mod.rs index a286046..16a3301 100644 --- a/app_rust/src/commapi/protocols/mod.rs +++ b/app_rust/src/commapi/protocols/mod.rs @@ -1,12 +1,12 @@ -use std::{fmt::Display, time::Instant}; +use std::{fmt::Display}; -use comm_api::{ComServerError, ISO15765Config}; +use comm_api::{ComServerError}; use kwp2000::KWP2000ECU; use uds::UDSECU; use self::{kwp2000::read_ecu_identification, uds::read_data}; -use super::{comm_api::{self, ComServer, ISO15765Data}, iface::{Interface, InterfaceConfig, InterfacePayload, InterfaceType, PayloadFlag}}; +use super::{comm_api::{self, ComServer}, iface::{Interface, InterfaceConfig, InterfacePayload, InterfaceType, PayloadFlag}}; pub mod kwp2000; pub mod obd2; diff --git a/app_rust/src/commapi/protocols/obd2/mod.rs b/app_rust/src/commapi/protocols/obd2/mod.rs index 8cab4b0..0d22abe 100644 --- a/app_rust/src/commapi/protocols/obd2/mod.rs +++ b/app_rust/src/commapi/protocols/obd2/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::{Borrow, BorrowMut}, sync::{Arc, Mutex, RwLock, atomic::{AtomicBool, Ordering}, mpsc::{self, Receiver, Sender}}, vec}; +use std::{sync::{Arc, Mutex, RwLock, atomic::{AtomicBool, Ordering}, mpsc::{self, Receiver, Sender}}, vec}; use commapi::protocols::ProtocolError; @@ -55,7 +55,7 @@ impl CommandError for ObdError { Some("OBD Command not supported".into()) } - fn from_byte(b: u8) -> Self + fn from_byte(_b: u8) -> Self where Self: Sized { Self::CmdNotSupported @@ -150,7 +150,7 @@ pub struct ObdServer { s07: Option, s08: Option, s09: Option, - s0A: Option, + s10: Option, } impl ObdServer { @@ -177,7 +177,7 @@ impl ObdServer { res.push((self.s07.is_some(), 0x07, "Show pending DTCs".into())); res.push((self.s08.is_some(), 0x08, "Control operation".into())); res.push((self.s09.is_some(), 0x09, "Vehicle Info".into())); - res.push((self.s0A.is_some(), 0x0A, "Permanent DTCs".into())); + res.push((self.s10.is_some(), 0x0A, "Permanent DTCs".into())); return res; } @@ -189,30 +189,30 @@ impl ObdServer { return } for idx in 0..num_dtcs as usize { - let A = bytes[idx*2+1]; - let B = bytes[idx*2+2]; - let char = match (A & 0b11000000) >> 6 { + let a = bytes[idx*2+1]; + let b = bytes[idx*2+2]; + let char = match (a & 0b11000000) >> 6 { 0 => 'P', 1 => 'C', 2 => 'B', _ => 'U' }; - let second = match (A & 0b00001100) >> 4 { + let second = match (a & 0b00001100) >> 4 { 0 => '0', 1 => '1', 2 => '2', _ => '3', }; // STD Hex representation - let third = format!("{:1X}", A & 0b00001111); - let fourth = format!("{:02X}", B); + let third = format!("{:1X}", a & 0b00001111); + let fourth = format!("{:02X}", b); res.push(DTC { error: format!("{}{}{}{}", char, second, third, fourth), state: state, check_engine_on: state == DTCState::Stored || state == DTCState::Permanent, - id: (A as u32) << 8 | B as u32, + id: (a as u32) << 8 | b as u32, }) } } @@ -282,7 +282,7 @@ impl ProtocolServer for ObdServer { std::thread::sleep(std::time::Duration::from_micros(100)) } println!("OBD2 Server stop!"); - dyn_interface.close(); + let _res = dyn_interface.close(); }); let mut server = ObdServer { @@ -299,7 +299,7 @@ impl ProtocolServer for ObdServer { s07: None, s08: None, s09: None, - s0A: None, + s10: None, }; std::thread::sleep(std::time::Duration::from_millis(100)); // Wait for diag server to start @@ -322,7 +322,7 @@ impl ProtocolServer for ObdServer { if self.cmd_tx.send((cmd, Vec::from(args), true)).is_err() { return Err(ProtocolError::CustomError("Channel Tx failed".into())); } - let mut resp = self.cmd_rx.recv().unwrap()?; + let resp = self.cmd_rx.recv().unwrap()?; if resp[0] == 0x7F { Err(ProtocolError::ProtocolError(Box::new(ObdError::from_byte(0)))) } else { diff --git a/app_rust/src/commapi/protocols/uds/mod.rs b/app_rust/src/commapi/protocols/uds/mod.rs index d370ba4..dca5280 100644 --- a/app_rust/src/commapi/protocols/uds/mod.rs +++ b/app_rust/src/commapi/protocols/uds/mod.rs @@ -1,6 +1,6 @@ use self::diag_session_control::DiagSession; use super::{CautionLevel, CommandError, DTC, DiagCfg, ECUCommand, ProtocolError, ProtocolResult, ProtocolServer, Selectable}; -use crate::commapi::{comm_api::{ComServer, FilterType, ISO15765Config}, iface::{Interface, InterfaceConfig, InterfaceType, IsoTPInterface, PayloadFlag}}; +use crate::commapi::{comm_api::{ComServer, FilterType}, iface::{InterfaceConfig, InterfaceType, IsoTPInterface, PayloadFlag}}; use std::sync::atomic::Ordering::Relaxed; use std::{ sync::{ @@ -506,7 +506,7 @@ impl ProtocolServer for UDSECU { std::thread::sleep(std::time::Duration::from_micros(100)) } println!("UDS Diag server stop!"); - interface.close(); + let _res = interface.close(); }); let mut ecu = UDSECU { diff --git a/app_rust/src/commapi/socket_can_api.rs b/app_rust/src/commapi/socket_can_api.rs index aef7998..a8add2f 100644 --- a/app_rust/src/commapi/socket_can_api.rs +++ b/app_rust/src/commapi/socket_can_api.rs @@ -99,28 +99,6 @@ impl SocketCanAPI { Ok(()) } - fn run_can_iface_mut Result>(&mut self, func: F) -> Result { - match self.sockcan_iface.write() { - Ok(mut r) => { - match r.as_mut() { - Some(x) => func(x), - None => Err(ComServerError { - err_code: 99, - err_desc: "Can Socket was null!".into(), - - }) - } - }, - Err(_) => { - Err(ComServerError { - err_code: 99, - err_desc: "Write guard failed on CAN Socket".into(), - - }) - } - } - } - fn run_can_iface Result>(&self, func: F) -> Result { match self.sockcan_iface.read() { Ok(r) => { diff --git a/app_rust/src/main_ba.rs b/app_rust/src/main_ba.rs deleted file mode 100644 index 3117b91..0000000 --- a/app_rust/src/main_ba.rs +++ /dev/null @@ -1,202 +0,0 @@ - -use std::cmp::min; - -use image::{GenericImageView, ImageFormat}; -use std::io::Write; -use std::io::Read; -use std::io::{self, BufReader}; - -mod commapi; -mod passthru; - -use crate::{ - commapi::{comm_api::ComServer, passthru_api::PassthruApi, protocols::kwp2000::KWP2000ECU}, - commapi::{comm_api::ISO15765Config, protocols::ProtocolServer}, - passthru::{PassthruDevice, PassthruDrv}, -}; - -#[derive(Debug, Copy, Clone)] -struct Line { - start_x: u8, - start_y: u8, - end_x: u8, - end_y: u8, -} - -fn main() { - - let args: Vec = std::env::args().collect(); - - const LCD_WIDTH: u32 = 60; - const LCD_HEIGHT: u32 = 100; - - let dev = PassthruDevice::find_all().expect("Couldn't find any passthru adapters for test") - [0] - .clone(); - let drv = PassthruDrv::load_lib(dev.drv_path.clone()).expect("Couldn't load library"); - - // Open a Comm server link with my custom Passthru adapter - let mut api = PassthruApi::new(dev, drv).clone_box(); - api.open_device().expect("Could not open device!"); - - // Start ISO-TP KWP2000 session with IC - let mut server = KWP2000ECU::start_diag_session( - api, - &ISO15765Config { - baud: 83_333, - send_id: 1460, - recv_id: 1268, - block_size: 8, - sep_time: 20, - use_ext_isotp: false, - use_ext_can: false - }, - Some(0x001C), - ) - .expect("Error opening connection with IC ECU"); - - let input_file = std::fs::File::open(args[1].clone()).unwrap(); - let mut output_file = std::fs::File::create(args[2].clone()).unwrap(); - - // W203 IC is 56 pixels wide, ~100 tall for the top zone - let img = image::load(BufReader::new(input_file), ImageFormat::Png) - .expect("Error loading image"); - - // get scale bounds for the image - let sf = (img.width() as f32 / LCD_WIDTH as f32) as f32; - - let mut lines: Vec = Vec::new(); - // Drawing a large vertical line seems to clear the LCD in test mode - - let mut lines2: Vec = Vec::new(); - // Drawing a large vertical line seems to clear the LCD in test mode - - let max_height = std::cmp::min((img.height() as f32 / sf) as u32, LCD_HEIGHT); - - let y_offset: u8 = 60; - - for y in 0..max_height { - let mut new_line = true; - for x in 0..LCD_WIDTH { - let x_coord = min((x as f32 * sf) as u32, img.width() - 1); - let y_coord = min((y as f32 * sf) as u32, img.height() - 1); - let px_colour = img.get_pixel(x_coord, y_coord); - let rgb = px_colour.0; - if rgb[0] > 128 || rgb[1] > 128 || rgb[2] > 128 { - if new_line { - // Create new line - lines.push(Line { - start_x: x as u8, - start_y: y as u8 + y_offset, - end_x: x as u8, - end_y: y as u8 + y_offset, - }); - new_line = false; - } else { - // Append to last line in the matrix - if let Some(line) = lines.last_mut() { - line.end_x = x as u8; - line.end_y = y as u8 + y_offset; - } - } - } else { - new_line = true; // We need to draw a new line - } - } - } - - for x in 0..LCD_WIDTH { - let mut new_line = true; - for y in 0..max_height { - let x_coord = min((x as f32 * sf) as u32, img.width() - 1); - let y_coord = min((y as f32 * sf) as u32, img.height() - 1); - let px_colour = img.get_pixel(x_coord, y_coord); - let rgb = px_colour.0; - if rgb[0] > 128 || rgb[1] > 128 || rgb[2] > 128 { - if new_line { - // Create new line - lines2.push(Line { - start_x: x as u8, - start_y: y as u8 + y_offset, - end_x: x as u8, - end_y: y as u8 + y_offset, - }); - new_line = false; - } else { - // Append to last line in the matrix - if let Some(line) = lines2.last_mut() { - line.end_x = x as u8; - line.end_y = y as u8 + y_offset; - } - } - } else { - new_line = true; // We need to draw a new line - } - } - } - - let lines_ref = if lines.len() > lines2.len() { - &lines2 - } else { - &lines - }; - - let mut pic_idx: u16 = 0; - - server.set_p2_timeout_interval(150); - - for l in lines_ref { - // Send draw line command to LCD - server.run_command(0x31, &[0x03, 0x06, l.start_x, l.start_y, l.end_x, l.end_y]); - } - - // Get time-lapse photo from phone camera - std::thread::spawn(move || { - use curl::easy::Easy; - use std::sync::Arc; - - let mut handle = Easy::new(); - handle.url("http://192.168.1.204:8080/ptz?zoom=15").unwrap(); - - if let Err(e) = handle.perform() { - eprintln!("Error setting Zoom level!"); - std::process::exit(1) - } - - handle.url("http://192.168.1.204:8080/focus_distance?set=2.31").unwrap(); - - if let Err(e) = handle.perform() { - eprintln!("Error setting focus level!"); - std::process::exit(1) - } - - let mut data: Vec = Vec::new(); - { - let mut handle2 = Easy::new(); - handle2.url("http://192.168.1.204:8080/photo.jpg").unwrap(); - let mut transfer = handle2.transfer(); - transfer.write_function(|bytes| { - data.extend_from_slice(bytes); - Ok(bytes.len()) - }).unwrap(); - - if let Err(e) = transfer.perform() { - eprintln!("Error getting photo! {}", e); - std::process::exit(2) - } - } - - // Write to file - if let Err(e) = output_file.write_all(&data) { - eprintln!("Error writing file!"); - std::process::exit(3) - } - - // We are done. Exit thread! - std::process::exit(0) - }); - - loop { - server.run_command(0x31, &[03, 06, 00, 00, 00, 00]); // Keep the test active (Stops LCD from clearing after test) - } -} diff --git a/app_rust/src/passthru.rs b/app_rust/src/passthru.rs index 08d2c49..5fdee4c 100644 --- a/app_rust/src/passthru.rs +++ b/app_rust/src/passthru.rs @@ -201,6 +201,7 @@ impl PassthruDrv { } } + #[allow(dead_code)] pub fn is_connected(&self) -> bool { self.is_connected } @@ -366,6 +367,7 @@ impl PassthruDrv { //type PassThruStartPeriodicMsgFn = unsafe extern "stdcall" fn(channel_id: u32, msg: *const PASSTHRU_MSG, msg_id: *mut u32, time_interval: u32) -> i32; /// Returns message ID + #[allow(dead_code)] pub fn start_periodic_msg( &self, channel_id: u32, @@ -385,6 +387,7 @@ impl PassthruDrv { } //type PassThruStopPeriodicMsgFn = unsafe extern "stdcall" fn(channel_id: u32, msg_id: u32) -> i32; + #[allow(dead_code)] pub fn stop_periodic_msg(&self, channel_id: u32, msg_id: u32) -> Result<()> { ret_res(unsafe { (&self.stop_periodic_fn)(channel_id, msg_id) }, ()) } @@ -444,6 +447,7 @@ impl PassthruDrv { } //type PassThruSetProgrammingVoltageFn = unsafe extern "stdcall" fn(device_id: u32, pin_number: u32, voltage: u32) -> i32; + #[allow(dead_code)] pub fn set_programming_voltage(&self, dev_id: u32, pin: u32, voltage: u32) -> Result<()> { ret_res(unsafe { (&self.set_prog_v_fn)(dev_id, pin, voltage) }, ()) } @@ -506,6 +510,7 @@ pub enum LoadDeviceError { } impl LoadDeviceError { + #[allow(dead_code)] pub fn get_err_desc(&self) -> String { match &self { LoadDeviceError::NoName => "No device name attribute".to_string(), diff --git a/app_rust/src/themes/mod.rs b/app_rust/src/themes/mod.rs index 869dbc4..fd82dd6 100644 --- a/app_rust/src/themes/mod.rs +++ b/app_rust/src/themes/mod.rs @@ -70,6 +70,7 @@ pub(crate) fn get_theme<'a>() -> &'a Style { unsafe { &CURR_THEME } } +#[allow(dead_code)] pub enum ButtonType { Primary, Secondary, @@ -81,6 +82,7 @@ pub enum ButtonType { Dark, } +#[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub enum TextType { Success, diff --git a/app_rust/src/widgets/mod.rs b/app_rust/src/widgets/mod.rs index ad99569..790b2c4 100644 --- a/app_rust/src/widgets/mod.rs +++ b/app_rust/src/widgets/mod.rs @@ -1,120 +1 @@ -use std::{collections::VecDeque, time::Instant}; -pub mod table; - -pub trait Graphable { - fn draw(&self); -} - -#[derive(Debug, Default)] -pub struct Graph { - cache: iced::canvas::Cache, -} - -pub struct LineTimeGraph<'a> { - frame: &'a iced::canvas::Frame, - points: VecDeque<(u128, f32)>, - y_bounds: (f32, f32), // Min, Max - x_label: String, - y_label: String, - max_time_ms: u128, -} - -impl<'a> LineTimeGraph<'a> { - pub fn new(frame: &'a iced::canvas::Frame, x_label: String, y_label: String) -> Self { - Self { - frame, - points: VecDeque::new(), - y_bounds: (0.0, 1.0), - x_label, - y_label, - max_time_ms: 30000, // 30 seconds - } - } - - pub fn add_data_point(&mut self, y: f32) { - // Adjust our bounds - if y > self.y_bounds.1 { - self.y_bounds.1 = y - } else if y < self.y_bounds.0 { - self.y_bounds.0 = y - } - // Push the data point - let time = Instant::now().elapsed().as_millis(); - self.points.push_back((time, y)) - } -} - -impl<'a> Graphable for LineTimeGraph<'a> { - fn draw(&self) { - let max_height = self.frame.height(); - let resolution_y = max_height / (self.y_bounds.1 + self.y_bounds.0); // y per px - - let curr_time = Instant::now().elapsed().as_millis(); // Time at x=0 - - let resolution_x = self.max_time_ms as f32 / self.frame.width(); // ms per px - - todo!() - } -} - -#[cfg_attr(not(feature = "graph_tests"), ignore)] -pub mod graph_test { - use iced::{Application, Canvas, Clipboard, Column, Rectangle, Settings, canvas::{self, Cursor, Frame, Geometry}, executor}; - - use crate::themes::elements::Container; - - use super::*; - - #[test] - fn test_plot() { - let mut settings = Settings::default(); - settings.window.resizable = false; - settings.window.size = (1366, 768); - settings.window.min_size = Some((1366, 768)); - TestApp::run(settings); - } - - #[derive(Debug, Clone, Copy)] - enum TestMsg {} - - struct TestApp<'a> { - frame: iced::canvas::Frame, - graph: LineTimeGraph<'a>, - } - - impl<'a> iced::Application for TestApp<'a> { - type Executor = executor::Default; - type Message = TestMsg; - type Flags = (); - - fn new(flags: Self::Flags) -> (Self, iced::Command) { - todo!() - /* - let f = Frame::new(iced::Size::new(1366.0, 768.0)); - (Self { - frame: f, - graph: LineTimeGraph::new(&f, "X axis".into(), "Y axis".into()) - }, iced::Command::none()) - */ - } - - fn title(&self) -> String { - "Test graph library".into() - } - - fn update(&mut self, message: Self::Message, _clipboard: &mut Clipboard) -> iced::Command { - todo!() - } - - fn view(&mut self) -> iced::Element<'_, Self::Message> { - todo!() - //Container::new(Canvas::new(&mut self.graph).into() - } - } - - impl canvas::Program for Graph { - fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec { - vec![] - } - } -} +pub mod table; \ No newline at end of file diff --git a/app_rust/src/widgets/table.rs b/app_rust/src/widgets/table.rs index 38f8e0b..99ffb86 100644 --- a/app_rust/src/widgets/table.rs +++ b/app_rust/src/widgets/table.rs @@ -1,9 +1,6 @@ -use std::marker::PhantomData; - use iced::{Column, Element, Length, Row, Scrollable, scrollable}; -use iced_native::Widget; -use crate::themes::{ButtonType, TextType, button_coloured, button_outlined, button_table, text}; +use crate::themes::{ButtonType, TextType, button_table, text}; #[derive(Debug, Copy, Clone)] pub struct TableMsg(pub usize, pub usize); @@ -88,10 +85,6 @@ impl Table { } } - pub fn set_default_text(&mut self, default: String) { - self.default_text = default; - } - pub fn view(&mut self) -> Element{ if self.header_row.is_empty() || self.text_matrix.is_empty() { @@ -100,7 +93,7 @@ impl Table { let mut row = Row::new(); - let mut scroll = Scrollable::new(&mut self.scroll_sate); + let scroll = Scrollable::new(&mut self.scroll_sate); let mut column = Column::new(); diff --git a/app_rust/src/windows/cantracer.rs b/app_rust/src/windows/cantracer.rs index f94d2d9..bd91279 100644 --- a/app_rust/src/windows/cantracer.rs +++ b/app_rust/src/windows/cantracer.rs @@ -1,10 +1,9 @@ -use crate::{commapi::{comm_api::{CanFrame, ComServer, FilterType}, iface::{CanbusInterface, IFACE_CFG, Interface, InterfaceConfig, InterfacePayload}}, themes::{TextType, checkbox, picklist, text}}; +use crate::{commapi::{comm_api::{ComServer, FilterType}, iface::{CanbusInterface, IFACE_CFG, Interface, InterfaceConfig, InterfacePayload}}, themes::{TextType, checkbox, picklist, text}}; use crate::themes::{button_coloured, ButtonType}; use crate::windows::window::WindowMessage; use iced::{pick_list, time}; use iced::{button, Color, Column, Element, Length, Row, Scrollable, Subscription, Text}; -use iced_native::Widget; -use std::{borrow::Cow, collections::HashMap, sync::Arc}; +use std::collections::HashMap; use std::time::Instant; #[derive(Debug, Clone)] diff --git a/app_rust/src/windows/diag_home.rs b/app_rust/src/windows/diag_home.rs index 292099c..c346659 100644 --- a/app_rust/src/windows/diag_home.rs +++ b/app_rust/src/windows/diag_home.rs @@ -88,9 +88,6 @@ impl DiagHome { pub fn subscription(&self) -> Subscription { if let Some(ref manual) = self.manual_mode { manual.subscription().map(DiagHomeMessage::ManualSession) - } else if let Some(ref scan) = self.scan_mode { - //todo!() - Subscription::none() } else { Subscription::none() } diff --git a/app_rust/src/windows/diag_manual.rs b/app_rust/src/windows/diag_manual.rs index 09c3e7f..1cf6757 100644 --- a/app_rust/src/windows/diag_manual.rs +++ b/app_rust/src/windows/diag_manual.rs @@ -19,7 +19,6 @@ use super::{ #[derive(Debug, Clone)] pub enum DiagManualMessage { LaunchFileBrowser, - LoadFile(String), PickECU(ECUDiagSettings), LaunchKWP, LaunchKWPCustom, @@ -27,7 +26,6 @@ pub enum DiagManualMessage { LaunchUDSCustom, LaunchCustom, LaunchCustomCustom, - Back, Session(SessionMsg), //User input queues @@ -116,7 +114,6 @@ impl DiagManual { } } match msg { - DiagManualMessage::Back => {} DiagManualMessage::LaunchFileBrowser => { if let nfd::Response::Okay(f_path) = nfd::open_file_dialog(Some("json"), None).unwrap_or(nfd::Response::Cancel) @@ -267,7 +264,7 @@ impl DiagManual { return; } - if let SessionType::JSON(ecu, connection) = &session_type { + if let SessionType::JSON(_,_) = &session_type { match DiagSession::new(&session_type, self.server.clone(), None) { Ok(session) => self.session = Some(session), Err(e) => self.status = format!("Error init diag session: {}", e.get_description()), diff --git a/app_rust/src/windows/diag_scanner.rs b/app_rust/src/windows/diag_scanner.rs index ddf69b9..2283bc8 100644 --- a/app_rust/src/windows/diag_scanner.rs +++ b/app_rust/src/windows/diag_scanner.rs @@ -6,7 +6,7 @@ use commapi::{ }; use iced::{Align, Column, Element, Length, Row, Space}; -use crate::{commapi::{self, comm_api::{CanFrame, ComServer, FilterType}, iface::{BufferType, DynamicInterface, IFACE_CFG, Interface, InterfaceConfig, InterfacePayload, InterfaceType, PayloadFlag}, protocols::{DiagCfg, kwp2000::read_ecu_identification::read_dcx_mmc_id}}, themes::{ +use crate::{commapi::{self, comm_api::{ComServer, FilterType}, iface::{BufferType, DynamicInterface, IFACE_CFG, Interface, InterfaceConfig, InterfacePayload, InterfaceType, PayloadFlag}, protocols::{DiagCfg, kwp2000::read_ecu_identification::read_dcx_mmc_id}}, themes::{ button_coloured, button_outlined, progress_bar, text, title_text, ButtonType, TextType, }}; @@ -87,7 +87,7 @@ impl DiagScanner { if let Err(e) = self.activate_interface.send_data( &[InterfacePayload::new(0x07Df, &[0x09, 0x02])], 0 ) { - self.status = "Could not send wake up test packet".into(); + self.status = format!("Could not send wake up test packet: {}", e); self.activate_interface.close().expect("WTF. Could not close CAN Interface!?"); } else { std::thread::sleep(std::time::Duration::from_millis(500)); @@ -110,7 +110,10 @@ impl DiagScanner { self.can_traffic_id_list.insert(0x07DF, false); // Add OBD-II CAN ID so we don't scan that self.curr_stage += 1; // We can progress to the next stage! self.curr_scan_id = 0x0; // Set start ID to 0 - self.activate_interface.clearBuffer(BufferType::BOTH); + if let Err(e) = self.activate_interface.clear_buffer(BufferType::BOTH) { + self.status = format!("Could not set Clear CAN Tx/Rx buffers: {}", e); + return None; + } return Some(DiagScannerMessage::ScanPoll); // Begin the CAN interrogation (Stage 1) } 2 => { @@ -199,7 +202,7 @@ impl DiagScanner { } else if self.clock.elapsed().as_millis() >= 100 { // Timeout waiting for response self.get_next_canid(); - self.activate_interface.clearBuffer(BufferType::RX); + self.activate_interface.clear_buffer(BufferType::RX); // Send a fake ISO-TP first frame. Tell the potential ECU we are sending 16 bytes to it. If it uses ISO-TP, it'll send back a // flow control message back to OVD self.activate_interface.send_data( @@ -232,7 +235,7 @@ impl DiagScanner { 3 => { if self.clock.elapsed().as_millis() > 100 { self.activate_interface.rem_filter(self.filter_idx); - self.activate_interface.clearBuffer(BufferType::BOTH); + self.activate_interface.clear_buffer(BufferType::BOTH); if self.curr_scan_id as usize >= self.stage2_results.len() { self.activate_interface.close(); // We don't need CAN anymore return Some(DiagScannerMessage::IncrementStage); // Done with stage3 scan @@ -432,7 +435,6 @@ impl DiagScanner { } None } - _ => None, } } @@ -645,7 +647,7 @@ impl DiagScanner { } fn draw_stage_7(&mut self) -> Element { - let mut c = Column::new() + let c = Column::new() .padding(10) .spacing(10) .align_items(Align::Center) diff --git a/app_rust/src/windows/diag_session/json_session.rs b/app_rust/src/windows/diag_session/json_session.rs index 9dfac64..26970fd 100644 --- a/app_rust/src/windows/diag_session/json_session.rs +++ b/app_rust/src/windows/diag_session/json_session.rs @@ -1,14 +1,13 @@ use core::panic; -use std::{borrow::Borrow, cell::RefCell, sync::Arc, time::Instant, vec}; +use std::{cell::RefCell, time::Instant, vec}; use crate::commapi::{iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, kwp2000::read_ecu_identification}}; -use common::schema::{ConType, Connection, OvdECU, diag::{TableData, dtc::ECUDTC, service::{Service}}, variant::{ECUVariantDefinition, ECUVariantPattern}}; -use iced::{Align, Column, Length, Row, Scrollable, Subscription, time}; -use lazy_static::__Deref; +use common::schema::{ConType, Connection, OvdECU, diag::{dtc::ECUDTC, service::{Service}}, variant::{ECUVariantDefinition, ECUVariantPattern}}; +use iced::{Align, Column, Length, Row, Subscription, time}; -use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, protocols::{DTC, DTCState, DiagProtocol, DiagServer, ProtocolResult, ProtocolServer, kwp2000::KWP2000ECU}}, themes::{ +use crate::{commapi::{comm_api::{ComServer}, protocols::{DTCState, DiagProtocol, DiagServer, ProtocolResult}}, themes::{ button_coloured, button_outlined, picklist, text, text_input, title_text, ButtonType, TextType, - }, widgets::{self, table::{Table, TableMsg}}}; + }, widgets::table::{Table, TableMsg}}; use super::{ log_view::{LogType, LogView}, @@ -26,7 +25,6 @@ pub enum JsonDiagSessionMsg { ReadErrors, ClearErrors, ReadInfo, - RunService, ExecuteService(ServiceRef, Vec), ClearLogs, Selector(SelectorMsg), @@ -133,7 +131,7 @@ impl JsonDiagSession { println!("Server started"); let variant = server.get_variant_id()? as u32; let mut unknown_variant = false; - let mut ecu_varient = ecu_data.variants.clone().into_iter().find(|x| { + let ecu_varient = ecu_data.variants.clone().into_iter().find(|x| { x.clone() .patterns .into_iter() @@ -514,7 +512,6 @@ impl SessionTrait for JsonDiagSession { } } - _ => todo!(), } None } diff --git a/app_rust/src/windows/diag_session/kwp2000_session.rs b/app_rust/src/windows/diag_session/kwp2000_session.rs index 0a64295..004ad92 100644 --- a/app_rust/src/windows/diag_session/kwp2000_session.rs +++ b/app_rust/src/windows/diag_session/kwp2000_session.rs @@ -16,7 +16,6 @@ pub enum KWP2000DiagSessionMsg { DisconnectECU, Back, PollServer(Instant), - LoadErrorDefinition, ClearLogs, ClearErrors, ReadCodes, diff --git a/app_rust/src/windows/diag_session/mod.rs b/app_rust/src/windows/diag_session/mod.rs index 264ddbc..3bec2f4 100644 --- a/app_rust/src/windows/diag_session/mod.rs +++ b/app_rust/src/windows/diag_session/mod.rs @@ -12,8 +12,6 @@ use crate::commapi::{ use self::{json_session::JsonDiagSessionMsg, kwp2000_session::KWP2000DiagSessionMsg}; -use super::diag_manual::DiagManualMessage; - pub mod custom_session; pub mod json_session; pub mod kwp2000_session; @@ -33,7 +31,6 @@ pub enum SessionMsg { UDS(UDSDiagSessionMsg), JSON(JsonDiagSessionMsg), Custom(CustomDiagSessionMsg), - ExitSession, } impl DiagMessageTrait for SessionMsg { @@ -43,7 +40,6 @@ impl DiagMessageTrait for SessionMsg { SessionMsg::UDS(u) => u.is_back(), SessionMsg::JSON(j) => j.is_back(), SessionMsg::Custom(c) => c.is_back(), - SessionMsg::ExitSession => true, } } } diff --git a/app_rust/src/windows/diag_session/uds_session.rs b/app_rust/src/windows/diag_session/uds_session.rs index 1c9288d..feac7d8 100644 --- a/app_rust/src/windows/diag_session/uds_session.rs +++ b/app_rust/src/windows/diag_session/uds_session.rs @@ -9,9 +9,9 @@ use std::{ use iced::{time, Column, Container, Length, Row, Space, Subscription}; use log_view::{LogType, LogView}; -use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, ProtocolServer}, protocols::uds::UDSECU}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::{diag_manual::DiagManualMessage, window}}; +use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, ProtocolServer}, protocols::uds::UDSECU}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::window}; -use super::{log_view, DiagMessageTrait, SessionMsg, SessionResult, SessionTrait}; +use super::{log_view, DiagMessageTrait, SessionResult, SessionTrait}; #[derive(Debug, Clone, PartialEq)] pub enum UDSDiagSessionMsg { @@ -19,7 +19,6 @@ pub enum UDSDiagSessionMsg { DisconnectECU, Back, PollServer(Instant), - LoadErrorDefinition, ClearLogs, ClearErrors, ReadCodes, @@ -288,9 +287,7 @@ impl SessionTrait for UDSDiagSession { } } } - UDSDiagSessionMsg::Back => {} - UDSDiagSessionMsg::LoadErrorDefinition => {} } None } diff --git a/app_rust/src/windows/home.rs b/app_rust/src/windows/home.rs index ddbd118..3928313 100644 --- a/app_rust/src/windows/home.rs +++ b/app_rust/src/windows/home.rs @@ -32,6 +32,7 @@ impl Home { ret } + #[allow(dead_code)] pub fn update(&mut self, _msg: &HomeMessage) -> Option { None } diff --git a/app_rust/src/windows/launcher.rs b/app_rust/src/windows/launcher.rs index 366cf8d..a753a34 100644 --- a/app_rust/src/windows/launcher.rs +++ b/app_rust/src/windows/launcher.rs @@ -6,11 +6,8 @@ use crate::themes::{button_coloured, container, picklist, radio_btn, text, Butto use crate::windows::launcher::LauncherMessage::LaunchRequested; use crate::windows::window::ApplicationError::DriverError; use crate::windows::window::{ApplicationError, WindowMessage}; -use crate::{ - passthru::{PassthruDevice, PassthruDrv}, - themes::images::{pix_to_iced_image}, -}; -use iced::{button, pick_list, Align, Column, Element, Image, Length, Row, Text}; +use crate::passthru::{PassthruDevice, PassthruDrv}; +use iced::{button, pick_list, Align, Column, Element, Length, Row, Text}; #[cfg(target_os = "linux")] use crate::commapi::socket_can_api::SocketCanAPI; diff --git a/app_rust/src/windows/obd.rs b/app_rust/src/windows/obd.rs index 4cbe70e..81167be 100644 --- a/app_rust/src/windows/obd.rs +++ b/app_rust/src/windows/obd.rs @@ -1,11 +1,10 @@ -use crate::{commapi::{comm_api::{Capability, ComServer, ISO15765Config}, iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, ProtocolServer, obd2::{self, ObdServer, service09::Service09Data}}}, themes::button_coloured}; -use crate::commapi::protocols::vin::Vin; +use crate::{commapi::{comm_api::{Capability, ComServer}, iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, ProtocolServer, obd2::{ObdServer, service09::Service09Data}}}, themes::button_coloured}; use crate::themes::{button_outlined, text, title_text, ButtonType, TextType, TitleSize}; use iced::{button, Align, Button, Column, Element, Length, Row, Space, Text}; #[derive(Debug, Clone)] pub enum OBDMessage { - InitOBD_IsoTP, + InitIsoTP, Disconnect, ChooseService(u8), } @@ -39,7 +38,7 @@ impl OBDHome { pub fn update(&mut self, msg: &OBDMessage) -> Option { match msg { - OBDMessage::InitOBD_IsoTP => { + OBDMessage::InitIsoTP => { // Try all the CAN IDs for test_id in [0x07E8, 0x07E9, 0x07E0].iter() { let mut cfg = InterfaceConfig::new(); @@ -87,7 +86,6 @@ impl OBDHome { } self.curr_service = sid; // What service UI should we be in? } - OBDMessage::ChooseService(_) => {} } None } @@ -143,7 +141,7 @@ impl OBDHome { let can_btn = match self.server.get_capabilities().supports_iso15765() { Capability::Yes => { button_outlined(&mut self.can_state, "OBD over CANBUS", ButtonType::Danger) - .on_press(OBDMessage::InitOBD_IsoTP) + .on_press(OBDMessage::InitIsoTP) } _ => Button::new( &mut self.can_state, diff --git a/app_rust/src/windows/window.rs b/app_rust/src/windows/window.rs index 359622b..e5b6050 100644 --- a/app_rust/src/windows/window.rs +++ b/app_rust/src/windows/window.rs @@ -71,11 +71,6 @@ impl<'a> WindowState { return launcher.update(x); } } - Self::Home(home) => { - if let WindowMessage::Home(x) = msg { - return home.update(x); - } - } Self::CanTracer(tracer) => { if let WindowMessage::CanTracer(x) = msg { return tracer.update(x); @@ -90,7 +85,8 @@ impl<'a> WindowState { if let WindowMessage::OBDTools(x) = msg { return o.update(x).map(WindowMessage::OBDTools); } - } + }, + _ => return None } None } @@ -111,7 +107,6 @@ impl WindowState { #[derive(Debug, Clone)] pub enum WindowMessage { Launcher(LauncherMessage), - Home(HomeMessage), CanTracer(TracerMessage), DiagHome(DiagHomeMessage), OBDTools(OBDMessage), From cd41b651be372fd6b772051b1c686d9b2d8658f1 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Sat, 15 May 2021 20:12:51 +0100 Subject: [PATCH 6/8] Cargo fmt --- app_rust/src/cli_tests/mod.rs | 2 +- app_rust/src/commapi/comm_api.rs | 12 +- app_rust/src/commapi/iface.rs | 188 ++-- app_rust/src/commapi/mod.rs | 3 +- app_rust/src/commapi/passthru_api.rs | 47 +- app_rust/src/commapi/pdu_api.rs | 4 +- app_rust/src/commapi/protocols/kwp2000/mod.rs | 63 +- .../protocols/kwp2000/read_status_dtc.rs | 2 +- app_rust/src/commapi/protocols/mod.rs | 52 +- app_rust/src/commapi/protocols/obd2/codes.rs | 4 +- app_rust/src/commapi/protocols/obd2/mod.rs | 104 ++- .../src/commapi/protocols/obd2/service01.rs | 850 +++++++++++++++--- .../src/commapi/protocols/obd2/service02.rs | 5 +- .../src/commapi/protocols/obd2/service03.rs | 13 +- .../src/commapi/protocols/obd2/service04.rs | 3 +- .../src/commapi/protocols/obd2/service05.rs | 4 +- .../src/commapi/protocols/obd2/service06.rs | 4 +- .../src/commapi/protocols/obd2/service07.rs | 4 +- .../src/commapi/protocols/obd2/service08.rs | 4 +- .../src/commapi/protocols/obd2/service09.rs | 53 +- .../src/commapi/protocols/obd2/service10.rs | 4 +- app_rust/src/commapi/protocols/uds/mod.rs | 20 +- .../src/commapi/protocols/uds/read_data.rs | 5 +- app_rust/src/commapi/socket_can_api.rs | 156 ++-- app_rust/src/main.rs | 2 +- app_rust/src/passthru.rs | 48 +- app_rust/src/themes/elements.rs | 21 +- app_rust/src/themes/images.rs | 1 - app_rust/src/themes/mod.rs | 26 +- app_rust/src/widgets/mod.rs | 2 +- app_rust/src/widgets/table.rs | 59 +- app_rust/src/windows/cantracer.rs | 122 ++- app_rust/src/windows/diag_home.rs | 2 +- app_rust/src/windows/diag_manual.rs | 23 +- app_rust/src/windows/diag_scanner.rs | 94 +- .../src/windows/diag_session/json_session.rs | 350 +++++--- .../windows/diag_session/kwp2000_session.rs | 61 +- app_rust/src/windows/diag_session/mod.rs | 29 +- .../src/windows/diag_session/uds_session.rs | 21 +- app_rust/src/windows/launcher.rs | 23 +- app_rust/src/windows/obd.rs | 106 ++- app_rust/src/windows/window.rs | 15 +- 42 files changed, 1796 insertions(+), 815 deletions(-) diff --git a/app_rust/src/cli_tests/mod.rs b/app_rust/src/cli_tests/mod.rs index f0e5c70..78e0bad 100644 --- a/app_rust/src/cli_tests/mod.rs +++ b/app_rust/src/cli_tests/mod.rs @@ -44,7 +44,7 @@ pub mod draw_routine { block_size: 8, sep_time: 20, use_ext_isotp: false, - use_ext_can: false + use_ext_can: false, }, None, ) diff --git a/app_rust/src/commapi/comm_api.rs b/app_rust/src/commapi/comm_api.rs index e919ff5..e1a6d4f 100644 --- a/app_rust/src/commapi/comm_api.rs +++ b/app_rust/src/commapi/comm_api.rs @@ -102,7 +102,7 @@ unsafe impl Sync for ISO15765Config {} pub enum FilterType { Pass { id: u32, mask: u32 }, Block { id: u32, mask: u32 }, - IsoTP { id: u32, mask: u32, fc: u32 } + IsoTP { id: u32, mask: u32, fc: u32 }, } #[derive(Debug, Clone)] @@ -225,8 +225,11 @@ pub trait ComServer: Send + Sync + Debug { /// ## Returns /// The number of CAN Frames successfully written to the vehicle, if Timeout is 0, this /// number will always be equal to the number of frames that were provided. - fn send_can_packets(&mut self, data: &[CanFrame], timeout_ms: u32) - -> Result; + fn send_can_packets( + &mut self, + data: &[CanFrame], + timeout_ms: u32, + ) -> Result; /// Returns a boolean indicating if there is at least 1 channel communicating with the car fn is_connected(&self) -> bool; @@ -327,8 +330,7 @@ pub trait ComServer: Send + Sync + Debug { /// /// ## Returns /// The filter ID provided by the adapter. Use this when destroying the filter - fn add_can_filter(&mut self, f: FilterType) - -> Result; + fn add_can_filter(&mut self, f: FilterType) -> Result; /// Tells the adapter to remove an active filter on an open CAN channel /// # Params diff --git a/app_rust/src/commapi/iface.rs b/app_rust/src/commapi/iface.rs index 1cb27f8..f2cb39b 100644 --- a/app_rust/src/commapi/iface.rs +++ b/app_rust/src/commapi/iface.rs @@ -1,8 +1,11 @@ -use std::{borrow::BorrowMut, collections::HashMap, sync::{Arc, Mutex}}; use std::fmt::Debug; +use std::{ + borrow::BorrowMut, + collections::HashMap, + sync::{Arc, Mutex}, +}; -use super::{comm_api::{Capability, ComServer, ComServerError, FilterType, CanFrame, ISO15765Data}}; - +use super::comm_api::{CanFrame, Capability, ComServer, ComServerError, FilterType, ISO15765Data}; pub type InterfaceResult = std::result::Result; @@ -25,19 +28,18 @@ impl ToString for IFACE_CFG { } } - #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq)] #[allow(non_camel_case_types)] pub enum PayloadFlag { ISOTP_PAD_FRAME, - ISOTP_EXT_ADDR + ISOTP_EXT_ADDR, } #[derive(Debug, Clone)] pub struct InterfacePayload { pub id: u32, pub data: Vec, - pub flags: Vec + pub flags: Vec, } impl InterfacePayload { @@ -56,13 +58,13 @@ impl InterfacePayload { #[derive(Debug, Clone)] pub struct InterfaceConfig { - params: HashMap + params: HashMap, } impl InterfaceConfig { pub fn new() -> Self { Self { - params: HashMap::new() + params: HashMap::new(), } } @@ -77,7 +79,13 @@ impl InterfaceConfig { pub fn get_param(&self, param_name: IFACE_CFG) -> InterfaceResult { match self.params.get(¶m_name.to_string()) { Some(x) => Ok(*x), - None => Err(ComServerError{ err_code: 999, err_desc: format!("Interface configuration has missing parameter {:?}", param_name)}) + None => Err(ComServerError { + err_code: 999, + err_desc: format!( + "Interface configuration has missing parameter {:?}", + param_name + ), + }), } } } @@ -87,7 +95,7 @@ impl InterfaceConfig { pub enum BufferType { TX, RX, - BOTH + BOTH, } pub trait Interface: Send + Sync + Debug { @@ -98,14 +106,22 @@ pub trait Interface: Send + Sync + Debug { fn rem_filter(&mut self, f_id: u32) -> InterfaceResult<()>; fn close(&mut self) -> InterfaceResult<()>; fn clear_buffer(&mut self, buffer_type: BufferType) -> InterfaceResult<()>; - fn send_recv_data(&mut self, request: InterfacePayload, write_timeout: u32, read_timeout: u32) -> InterfaceResult { + fn send_recv_data( + &mut self, + request: InterfacePayload, + write_timeout: u32, + read_timeout: u32, + ) -> InterfaceResult { self.clear_buffer(BufferType::RX)?; self.send_data(&[request], write_timeout)?; let res = self.recv_data(1, read_timeout)?; if res.is_empty() { - return Err(ComServerError{err_code: 2, err_desc: "Timeout waiting".into()}); + return Err(ComServerError { + err_code: 2, + err_desc: "Timeout waiting".into(), + }); } else { - return Ok(res[0].clone()) + return Ok(res[0].clone()); } } fn get_server(&self) -> Box; @@ -114,25 +130,31 @@ pub trait Interface: Send + Sync + Debug { #[derive(Debug, Clone)] pub struct CanbusInterface { - dev: Box + dev: Box, } impl CanbusInterface { pub fn new(dev: Box) -> InterfaceResult> { if dev.get_capabilities().support_can_fd() != Capability::Yes { - Err(ComServerError { err_code: 1, err_desc: "Device does not support CAN".into() }) + Err(ComServerError { + err_code: 1, + err_desc: "Device does not support CAN".into(), + }) } else { - Ok(Box::new(CanbusInterface{dev : dev.clone_box()})) + Ok(Box::new(CanbusInterface { + dev: dev.clone_box(), + })) } } pub fn new_raw(dev: Box) -> Self { - CanbusInterface{dev : dev.clone_box()} + CanbusInterface { + dev: dev.clone_box(), + } } } impl Interface for CanbusInterface { - fn clear_buffer(&mut self, buffer_type: BufferType) -> InterfaceResult<()> { match buffer_type { BufferType::TX => self.dev.clear_can_tx_buffer(), @@ -146,27 +168,26 @@ impl Interface for CanbusInterface { fn setup(&mut self, cfg: &InterfaceConfig) -> InterfaceResult<()> { self.dev.open_can_interface( - cfg.get_param(IFACE_CFG::BAUDRATE)?, - cfg.get_param_or_default(IFACE_CFG::EXT_CAN_ADDR, 0) > 0 + cfg.get_param(IFACE_CFG::BAUDRATE)?, + cfg.get_param_or_default(IFACE_CFG::EXT_CAN_ADDR, 0) > 0, ) } fn send_data(&mut self, data: &[InterfacePayload], timeout: u32) -> InterfaceResult { - let can_packets: Vec = data.iter().map(|f|{ - CanFrame::new(f.id, &f.data) - }).collect(); + let can_packets: Vec = + data.iter().map(|f| CanFrame::new(f.id, &f.data)).collect(); self.dev.send_can_packets(&can_packets, timeout) } fn recv_data(&mut self, max: usize, timeout: u32) -> InterfaceResult> { self.dev.read_can_packets(timeout, max).map(|v| { - v.iter().map(|f| { - InterfacePayload { + v.iter() + .map(|f| InterfacePayload { id: f.id, data: Vec::from(f.get_data()), - flags: vec![] - } - }).collect() + flags: vec![], + }) + .collect() }) } @@ -188,28 +209,32 @@ impl Interface for CanbusInterface { fn clone_box(&self) -> Box { Box::new(Self { - dev: self.dev.clone() + dev: self.dev.clone(), }) } } #[derive(Debug, Clone)] pub struct IsoTPInterface { - dev: Box + dev: Box, } impl IsoTPInterface { pub fn new(dev: Box) -> InterfaceResult> { if dev.get_capabilities().supports_iso15765() != Capability::Yes { - Err(ComServerError { err_code: 1, err_desc: "Device does not support IsoTP".into() }) + Err(ComServerError { + err_code: 1, + err_desc: "Device does not support IsoTP".into(), + }) } else { - Ok(Box::new(IsoTPInterface{dev : dev.clone_box()})) + Ok(Box::new(IsoTPInterface { + dev: dev.clone_box(), + })) } } } impl Interface for IsoTPInterface { - fn clear_buffer(&mut self, buffer_type: BufferType) -> InterfaceResult<()> { match buffer_type { BufferType::TX => self.dev.clear_iso15765_tx_buffer(), @@ -223,38 +248,39 @@ impl Interface for IsoTPInterface { fn setup(&mut self, cfg: &InterfaceConfig) -> InterfaceResult<()> { self.dev.open_iso15765_interface( - cfg.get_param(IFACE_CFG::BAUDRATE)?, - cfg.get_param_or_default(IFACE_CFG::EXT_CAN_ADDR, 0) > 0, - cfg.get_param_or_default(IFACE_CFG::EXT_ISOTP_ADDR, 0) > 0 + cfg.get_param(IFACE_CFG::BAUDRATE)?, + cfg.get_param_or_default(IFACE_CFG::EXT_CAN_ADDR, 0) > 0, + cfg.get_param_or_default(IFACE_CFG::EXT_ISOTP_ADDR, 0) > 0, )?; // Use default if not specified self.dev.set_iso15765_params( cfg.get_param_or_default(IFACE_CFG::ISOTP_ST_MIN, 20), - cfg.get_param_or_default(IFACE_CFG::ISOTP_BS, 8) + cfg.get_param_or_default(IFACE_CFG::ISOTP_BS, 8), ) } fn send_data(&mut self, data: &[InterfacePayload], timeout: u32) -> InterfaceResult { - let isotp_data: Vec = data.iter().map(|t|{ - ISO15765Data { + let isotp_data: Vec = data + .iter() + .map(|t| ISO15765Data { id: t.id, data: t.data.clone(), pad_frame: t.is_flag_set(PayloadFlag::ISOTP_PAD_FRAME), ext_addressing: t.is_flag_set(PayloadFlag::ISOTP_EXT_ADDR), - } - }).collect(); + }) + .collect(); self.dev.send_iso15765_data(&isotp_data, timeout) } fn recv_data(&mut self, max: usize, timeout: u32) -> InterfaceResult> { - self.dev.read_iso15765_packets(timeout, max).map(|v|{ - v.iter().map(|f| { - InterfacePayload { + self.dev.read_iso15765_packets(timeout, max).map(|v| { + v.iter() + .map(|f| InterfacePayload { id: f.id, data: f.data.clone(), flags: vec![], - } - }).collect() + }) + .collect() }) } @@ -276,21 +302,26 @@ impl Interface for IsoTPInterface { fn clone_box(&self) -> Box { Box::new(Self { - dev: self.dev.clone() + dev: self.dev.clone(), }) } } #[derive(Debug, Clone)] pub struct Iso14230Interface { - dev: Box + dev: Box, } impl Iso14230Interface { pub fn new(dev: Box) -> InterfaceResult> { if dev.get_capabilities().supports_iso14230() != Capability::Yes { - Err(ComServerError { err_code: 1, err_desc: "Device does not support ISO14230".into() }) + Err(ComServerError { + err_code: 1, + err_desc: "Device does not support ISO14230".into(), + }) } else { - Ok(Box::new(Iso14230Interface{dev : dev.clone_box()})) + Ok(Box::new(Iso14230Interface { + dev: dev.clone_box(), + })) } } } @@ -331,21 +362,26 @@ impl Interface for Iso14230Interface { fn clone_box(&self) -> Box { Box::new(Self { - dev: self.dev.clone() + dev: self.dev.clone(), }) } } #[derive(Debug, Clone)] pub struct Iso9141Interface { - dev: Box + dev: Box, } impl Iso9141Interface { pub fn new(dev: Box) -> InterfaceResult> { if dev.get_capabilities().supports_iso9141() != Capability::Yes { - Err(ComServerError { err_code: 1, err_desc: "Device does not support ISO9141".into() }) + Err(ComServerError { + err_code: 1, + err_desc: "Device does not support ISO9141".into(), + }) } else { - Ok(Box::new(Iso9141Interface{dev : dev.clone_box()})) + Ok(Box::new(Iso9141Interface { + dev: dev.clone_box(), + })) } } } @@ -386,7 +422,7 @@ impl Interface for Iso9141Interface { fn clone_box(&self) -> Box { Box::new(Self { - dev: self.dev.clone() + dev: self.dev.clone(), }) } } @@ -396,34 +432,38 @@ pub enum InterfaceType { Can, IsoTp, Iso14230, - Iso9141 + Iso9141, } #[derive(Debug, Clone)] pub struct DynamicInterface { iface_type: Option, - iface: Option>>> + iface: Option>>>, } impl DynamicInterface { - pub fn new(server: &Box, iface_type: InterfaceType, cfg: &InterfaceConfig) -> InterfaceResult { + pub fn new( + server: &Box, + iface_type: InterfaceType, + cfg: &InterfaceConfig, + ) -> InterfaceResult { let mut iface = match iface_type { InterfaceType::Can => CanbusInterface::new(server.clone_box())?, InterfaceType::IsoTp => IsoTPInterface::new(server.clone_box())?, InterfaceType::Iso14230 => Iso14230Interface::new(server.clone_box())?, - InterfaceType::Iso9141 => Iso9141Interface::new(server.clone_box())? + InterfaceType::Iso9141 => Iso9141Interface::new(server.clone_box())?, }; iface.setup(cfg)?; Ok(Self { iface_type: Some(iface_type), - iface: Some(Arc::new(Mutex::new(iface))) + iface: Some(Arc::new(Mutex::new(iface))), }) } pub fn blank() -> Self { Self { iface_type: None, - iface: None + iface: None, } } @@ -434,23 +474,28 @@ impl DynamicInterface { InterfaceType::Can => "CAN", InterfaceType::IsoTp => "ISO15765 (ISO-TP)", InterfaceType::Iso14230 => "ISO14230 (KWP2000 over LIN)", - InterfaceType::Iso9141 => "ISO9141 (OBD-II)" + InterfaceType::Iso9141 => "ISO9141 (OBD-II)", } } else { "Not configured" } } - pub fn exec) -> InterfaceResult>(&mut self, func: F) -> InterfaceResult { + pub fn exec) -> InterfaceResult>( + &mut self, + func: F, + ) -> InterfaceResult { match self.iface.as_mut() { Some(x) => func(x.lock().unwrap().borrow_mut()), - None => Err(ComServerError{ err_code: 98, err_desc: "Dynamic interface not configured!".into() }) + None => Err(ComServerError { + err_code: 98, + err_desc: "Dynamic interface not configured!".into(), + }), } } } impl Interface for DynamicInterface { - fn send_data(&mut self, data: &[InterfacePayload], timeout: u32) -> InterfaceResult { self.exec(|iface| iface.send_data(data, timeout)) } @@ -475,7 +520,12 @@ impl Interface for DynamicInterface { self.exec(|iface| iface.clear_buffer(buffer_type)) } - fn send_recv_data(&mut self, request: InterfacePayload, write_timeout: u32, read_timeout: u32) -> InterfaceResult { + fn send_recv_data( + &mut self, + request: InterfacePayload, + write_timeout: u32, + read_timeout: u32, + ) -> InterfaceResult { self.exec(|iface| iface.send_recv_data(request.clone(), write_timeout, read_timeout)) } @@ -486,14 +536,14 @@ impl Interface for DynamicInterface { fn get_server(&self) -> Box { match &self.iface { Some(x) => x.lock().unwrap().get_server(), - None => panic!("Illegal access of null interface") + None => panic!("Illegal access of null interface"), } } fn clone_box(&self) -> Box { match &self.iface { Some(x) => x.lock().unwrap().clone_box(), - None => panic!("Illegal access of null interface") + None => panic!("Illegal access of null interface"), } } -} \ No newline at end of file +} diff --git a/app_rust/src/commapi/mod.rs b/app_rust/src/commapi/mod.rs index bad28aa..4457334 100644 --- a/app_rust/src/commapi/mod.rs +++ b/app_rust/src/commapi/mod.rs @@ -1,10 +1,9 @@ - #[allow(dead_code)] pub mod comm_api; +pub mod iface; pub mod passthru_api; pub mod pdu_api; pub mod protocols; -pub mod iface; #[cfg(target_os = "linux")] pub mod socket_can_api; diff --git a/app_rust/src/commapi/passthru_api.rs b/app_rust/src/commapi/passthru_api.rs index b29ea28..fb5490f 100644 --- a/app_rust/src/commapi/passthru_api.rs +++ b/app_rust/src/commapi/passthru_api.rs @@ -2,8 +2,6 @@ use crate::commapi::comm_api::{ CanFrame, Capability, ComServer, ComServerError, DeviceCapabilities, FilterType, ISO15765Data, }; use crate::passthru::{self, DrvVersion, PassthruDevice, PassthruDrv}; -use std::sync::{Arc, Mutex, RwLock}; -use std::{os::raw::c_void, time::Instant}; use j2534_rust::FilterType::{BLOCK_FILTER, FLOW_CONTROL_FILTER, PASS_FILTER}; use j2534_rust::IoctlID::READ_VBATT; use j2534_rust::PassthruError::{ERR_FAILED, ERR_INVALID_CHANNEL_ID}; @@ -11,6 +9,8 @@ use j2534_rust::{ ConnectFlags, IoctlID, IoctlParam, Loggable, PassthruError, Protocol, SConfig, SConfigList, TxFlag, PASSTHRU_MSG, }; +use std::sync::{Arc, Mutex, RwLock}; +use std::{os::raw::c_void, time::Instant}; #[derive(Debug, Clone)] pub struct PassthruApi { @@ -241,26 +241,29 @@ impl ComServer for PassthruApi { Ok(()) } - fn add_can_filter( - &mut self, - f: FilterType) -> Result { + fn add_can_filter(&mut self, f: FilterType) -> Result { match *self.can_channel_idx.read().unwrap() { None => Err(self.convert_error(ERR_INVALID_CHANNEL_ID)), Some(idx) => { let mut apply_mask = 0; let mut apply_id = 0; let f_type = match f { - FilterType::Pass {id, mask} => { + FilterType::Pass { id, mask } => { apply_mask = mask; apply_id = id; PASS_FILTER - }, - FilterType::Block {id, mask} => { + } + FilterType::Block { id, mask } => { apply_mask = mask; apply_id = id; BLOCK_FILTER - }, - _ => return Err(ComServerError{ err_code: 99, err_desc: "Cannot apply a flow control filter to CAN".into()}) + } + _ => { + return Err(ComServerError { + err_code: 99, + err_desc: "Cannot apply a flow control filter to CAN".into(), + }) + } }; let mut mask_msg = PASSTHRU_MSG { @@ -297,28 +300,25 @@ impl ComServer for PassthruApi { } } - fn add_iso15765_filter( - &mut self, - f: FilterType - ) -> Result { + fn add_iso15765_filter(&mut self, f: FilterType) -> Result { match *self.iso15765_channel_idx.read().unwrap() { None => Err(self.convert_error(ERR_INVALID_CHANNEL_ID)), Some(idx) => { - if let FilterType::IsoTP{id, mask, fc} = f { + if let FilterType::IsoTP { id, mask, fc } = f { let mut mask_msg = PASSTHRU_MSG { protocol_id: Protocol::ISO15765 as u32, data_size: 4, ..Default::default() }; PassthruApi::u32_to_msg_id(mask, &mut mask_msg); - + let mut ptn_msg = PASSTHRU_MSG { protocol_id: Protocol::ISO15765 as u32, data_size: 4, ..Default::default() }; PassthruApi::u32_to_msg_id(id, &mut ptn_msg); - + let mut fc_msg = PASSTHRU_MSG { protocol_id: Protocol::ISO15765 as u32, data_size: 4, @@ -328,11 +328,20 @@ impl ComServer for PassthruApi { self.driver .lock() .unwrap() - .start_msg_filter(idx, FLOW_CONTROL_FILTER, &mask_msg, &ptn_msg, Some(fc_msg)) + .start_msg_filter( + idx, + FLOW_CONTROL_FILTER, + &mask_msg, + &ptn_msg, + Some(fc_msg), + ) .map_err(|e| self.convert_error(e)) } else { // Error out - Err(ComServerError{ err_code: 99, err_desc: "Cannot apply a pass/block filter to ISOTP".into()}) + Err(ComServerError { + err_code: 99, + err_desc: "Cannot apply a pass/block filter to ISOTP".into(), + }) } } } diff --git a/app_rust/src/commapi/pdu_api.rs b/app_rust/src/commapi/pdu_api.rs index 71a129d..7585002 100644 --- a/app_rust/src/commapi/pdu_api.rs +++ b/app_rust/src/commapi/pdu_api.rs @@ -76,9 +76,7 @@ impl ComServer for DpduAPI { unimplemented!() } - fn add_can_filter( - &mut self, - f: FilterType) -> Result { + fn add_can_filter(&mut self, f: FilterType) -> Result { unimplemented!() } diff --git a/app_rust/src/commapi/protocols/kwp2000/mod.rs b/app_rust/src/commapi/protocols/kwp2000/mod.rs index 70ca770..ad322fd 100644 --- a/app_rust/src/commapi/protocols/kwp2000/mod.rs +++ b/app_rust/src/commapi/protocols/kwp2000/mod.rs @@ -1,4 +1,7 @@ -use commapi::{comm_api::{ComServer, ISO15765Config}, protocols::DTCState}; +use commapi::{ + comm_api::{ComServer, ISO15765Config}, + protocols::DTCState, +}; use std::sync::atomic::Ordering::Relaxed; use std::{ sync::{ @@ -10,9 +13,18 @@ use std::{ }; use self::start_diag_session::DiagSession; -use crate::{commapi::{self, comm_api::FilterType, iface::{DynamicInterface, Interface, InterfaceConfig, InterfaceType, IsoTPInterface, PayloadFlag}}}; +use crate::commapi::{ + self, + comm_api::FilterType, + iface::{ + DynamicInterface, Interface, InterfaceConfig, InterfaceType, IsoTPInterface, PayloadFlag, + }, +}; -use super::{CautionLevel, CommandError, DTC, DiagCfg, ECUCommand, ProtocolError, ProtocolResult, ProtocolServer, Selectable}; +use super::{ + CautionLevel, CommandError, DiagCfg, ECUCommand, ProtocolError, ProtocolResult, ProtocolServer, + Selectable, DTC, +}; pub mod clear_diag_information; pub mod ecu_reset; @@ -417,14 +429,23 @@ impl ProtocolServer for KWP2000ECU { diag_cfg: DiagCfg, ) -> ProtocolResult { if interface_type != InterfaceType::IsoTp && interface_type != InterfaceType::Iso14230 { - return Err(ProtocolError::CustomError("KWP2000 Can only be executed over ISO-TP or ISO14230".into())) + return Err(ProtocolError::CustomError( + "KWP2000 Can only be executed over ISO-TP or ISO14230".into(), + )); } - let mut dyn_interface = DynamicInterface::new(comm_server, interface_type, &interface_cfg)?.clone_box(); + let mut dyn_interface = + DynamicInterface::new(comm_server, interface_type, &interface_cfg)?.clone_box(); if interface_type == InterfaceType::IsoTp { - dyn_interface.add_filter(FilterType::IsoTP{id: diag_cfg.recv_id, mask: 0xFFFF, fc: diag_cfg.send_id})?; + dyn_interface.add_filter(FilterType::IsoTP { + id: diag_cfg.recv_id, + mask: 0xFFFF, + fc: diag_cfg.send_id, + })?; } else { - return Err(ProtocolError::CustomError("KWP2000 over ISO14230 is a WIP".into())) + return Err(ProtocolError::CustomError( + "KWP2000 over ISO14230 is a WIP".into(), + )); } let should_run = Arc::new(AtomicBool::new(true)); @@ -471,18 +492,32 @@ impl ProtocolServer for KWP2000ECU { { timer = Instant::now(); //if let Err(e) = Self::run_command_iso_tp(comm_server.as_ref(), 0x001C, Service::TesterPresent.into(), &[0x02], false) { - + let tp_cmd = match diag_cfg.global_id { // Global tester present - No response from ECU - Some(x) => Self::run_command_resp(&mut dyn_interface, &tx_flags, x, Service::TesterPresent.into(), &[0x02], false), - None => Self::run_command_resp(&mut dyn_interface, &tx_flags, s_id, Service::TesterPresent.into(), &[0x01], true) + Some(x) => Self::run_command_resp( + &mut dyn_interface, + &tx_flags, + x, + Service::TesterPresent.into(), + &[0x02], + false, + ), + None => Self::run_command_resp( + &mut dyn_interface, + &tx_flags, + s_id, + Service::TesterPresent.into(), + &[0x01], + true, + ), }; if let Err(e) = tp_cmd { if e.is_timeout() { println!("Lost connection with ECU! - {:?}", e); // Try to regain connection if Self::run_command_resp( - &mut dyn_interface, + &mut dyn_interface, &tx_flags, s_id, Service::StartDiagSession.into(), @@ -558,12 +593,12 @@ impl ProtocolServer for KWP2000ECU { //let flag = (status >> 4 & 0b00000001) > 0; //0b011 let storage_state = (status >> 5) & 0b0000011; - + let state = match storage_state { 1 => DTCState::Stored, 2 => DTCState::Pending, 3 => DTCState::Permanent, - _ => DTCState::None + _ => DTCState::None, }; let mil = (status >> 7 & 0b00000001) > 0; @@ -572,7 +607,7 @@ impl ProtocolServer for KWP2000ECU { error: name, state, check_engine_on: mil, - id: ((bytes[0] as u32) << 8) | bytes[1] as u32 + id: ((bytes[0] as u32) << 8) | bytes[1] as u32, }); bytes.drain(0..3); // DTC is 3 bytes (1 for status, 2 for the ID) } diff --git a/app_rust/src/commapi/protocols/kwp2000/read_status_dtc.rs b/app_rust/src/commapi/protocols/kwp2000/read_status_dtc.rs index 1f39374..e90f48f 100644 --- a/app_rust/src/commapi/protocols/kwp2000/read_status_dtc.rs +++ b/app_rust/src/commapi/protocols/kwp2000/read_status_dtc.rs @@ -1,4 +1,4 @@ -use crate::commapi::protocols::{DTC, ProtocolResult, ProtocolServer}; +use crate::commapi::protocols::{ProtocolResult, ProtocolServer, DTC}; use super::KWP2000ECU; diff --git a/app_rust/src/commapi/protocols/mod.rs b/app_rust/src/commapi/protocols/mod.rs index 16a3301..1e13a1a 100644 --- a/app_rust/src/commapi/protocols/mod.rs +++ b/app_rust/src/commapi/protocols/mod.rs @@ -1,12 +1,15 @@ -use std::{fmt::Display}; +use std::fmt::Display; -use comm_api::{ComServerError}; +use comm_api::ComServerError; use kwp2000::KWP2000ECU; use uds::UDSECU; use self::{kwp2000::read_ecu_identification, uds::read_data}; -use super::{comm_api::{self, ComServer}, iface::{Interface, InterfaceConfig, InterfacePayload, InterfaceType, PayloadFlag}}; +use super::{ + comm_api::{self, ComServer}, + iface::{Interface, InterfaceConfig, InterfacePayload, InterfaceType, PayloadFlag}, +}; pub mod kwp2000; pub mod obd2; @@ -98,7 +101,7 @@ pub enum DTCState { None, Stored, Pending, - Permanent + Permanent, } #[derive(Debug, Clone)] @@ -145,11 +148,23 @@ impl DiagServer { interface_type: InterfaceType, interface_cfg: InterfaceConfig, tx_flags: Option>, - diag_cfg: DiagCfg + diag_cfg: DiagCfg, ) -> ProtocolResult { Ok(match protocol { - DiagProtocol::KWP2000 => Self::KWP2000(KWP2000ECU::start_diag_session(comm_server, interface_type, interface_cfg, tx_flags, diag_cfg)?), - DiagProtocol::UDS => Self::UDS(UDSECU::start_diag_session(comm_server, interface_type, interface_cfg, tx_flags, diag_cfg)?), + DiagProtocol::KWP2000 => Self::KWP2000(KWP2000ECU::start_diag_session( + comm_server, + interface_type, + interface_cfg, + tx_flags, + diag_cfg, + )?), + DiagProtocol::UDS => Self::UDS(UDSECU::start_diag_session( + comm_server, + interface_type, + interface_cfg, + tx_flags, + diag_cfg, + )?), }) } @@ -177,7 +192,7 @@ impl DiagServer { pub fn into_kwp(&mut self) -> Option<&mut KWP2000ECU> { match self { Self::KWP2000(s) => Some(s), - Self::UDS(s) => None + Self::UDS(s) => None, } } @@ -197,15 +212,19 @@ impl DiagServer { pub fn get_variant_id(&self) -> ProtocolResult { match self { - Self::KWP2000(s) => read_ecu_identification::read_dcx_mmc_id(&s).map(|x| x.diag_information as u32), - Self::UDS(s) => read_data::read_variant_id(s) + Self::KWP2000(s) => { + read_ecu_identification::read_dcx_mmc_id(&s).map(|x| x.diag_information as u32) + } + Self::UDS(s) => read_data::read_variant_id(s), } } pub fn get_dtc_env_data(&self, dtc: &DTC) -> ProtocolResult> { match self { Self::KWP2000(s) => kwp2000::read_status_dtc::read_status_dtc(s, dtc), - Self::UDS(s) => Err(ProtocolError::CustomError("Not implemented (get_dtc_env_data)".into())), // TODO + Self::UDS(s) => Err(ProtocolError::CustomError( + "Not implemented (get_dtc_env_data)".into(), + )), // TODO } } } @@ -225,7 +244,7 @@ pub trait ProtocolServer: Sized { interface_type: InterfaceType, interface_cfg: InterfaceConfig, tx_flags: Option>, - diag_cfg: DiagCfg + diag_cfg: DiagCfg, ) -> ProtocolResult; fn exit_diag_session(&mut self); fn run_command(&self, cmd: u8, args: &[u8]) -> ProtocolResult>; @@ -248,7 +267,8 @@ pub trait ProtocolServer: Sized { tx.flags = f.clone(); } if !receive_require { - interface.send_data(&[tx], 0) + interface + .send_data(&[tx], 0) .map(|_| vec![]) .map_err(ProtocolError::CommError) } else { @@ -264,12 +284,10 @@ pub trait ProtocolServer: Sized { } else { return Err(ProtocolError::ProtocolError(Box::new( Self::Error::from_byte(res.data[2]), - ))) + ))); } } - Err(e) => { - return Err(ProtocolError::CommError(e)) - } + Err(e) => return Err(ProtocolError::CommError(e)), } } if res.data[0] == 0x7F { diff --git a/app_rust/src/commapi/protocols/obd2/codes.rs b/app_rust/src/commapi/protocols/obd2/codes.rs index 02bcef8..2d5e9a2 100644 --- a/app_rust/src/commapi/protocols/obd2/codes.rs +++ b/app_rust/src/commapi/protocols/obd2/codes.rs @@ -1,6 +1,6 @@ use crate::commapi::protocols::DTC; -pub (crate) fn get_dtc_desc(dtc: &DTC) -> String { +pub(crate) fn get_dtc_desc(dtc: &DTC) -> String { match dtc.error.as_str() { "P0100" => "Mass or Volume Air Flow Circuit Malfunction", "P0101" => "Mass or Volume Air Flow Circuit Range/Performance Problem", @@ -3075,4 +3075,4 @@ pub (crate) fn get_dtc_desc(dtc: &DTC) -> String { "U2500" => "(CAN) Lack of Acknowledgement From Engine Management", _ => "Unknown Error", }.into() -} \ No newline at end of file +} diff --git a/app_rust/src/commapi/protocols/obd2/mod.rs b/app_rust/src/commapi/protocols/obd2/mod.rs index 0d22abe..0e5c131 100644 --- a/app_rust/src/commapi/protocols/obd2/mod.rs +++ b/app_rust/src/commapi/protocols/obd2/mod.rs @@ -1,13 +1,31 @@ -use std::{sync::{Arc, Mutex, RwLock, atomic::{AtomicBool, Ordering}, mpsc::{self, Receiver, Sender}}, vec}; +use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::{self, Receiver, Sender}, + Arc, Mutex, RwLock, + }, + vec, +}; use commapi::protocols::ProtocolError; -use crate::commapi::{self, comm_api::{ComServer, FilterType}, iface::{DynamicInterface, Interface, InterfaceConfig, InterfaceType, PayloadFlag}}; +use crate::commapi::{ + self, + comm_api::{ComServer, FilterType}, + iface::{DynamicInterface, Interface, InterfaceConfig, InterfaceType, PayloadFlag}, +}; -use self::{service01::Service01, service02::Service02, service03::Service03, service04::Service04, service05::Service05, service06::Service06, service07::Service07, service08::Service08, service09::Service09, service10::Service0A}; +use self::{ + service01::Service01, service02::Service02, service03::Service03, service04::Service04, + service05::Service05, service06::Service06, service07::Service07, service08::Service08, + service09::Service09, service10::Service0A, +}; -use super::{CommandError, DTC, DTCState, DiagCfg, ECUCommand, ProtocolResult, ProtocolServer, Selectable}; +use super::{ + CommandError, DTCState, DiagCfg, ECUCommand, ProtocolResult, ProtocolServer, Selectable, DTC, +}; +pub mod codes; pub mod service01; pub mod service02; pub mod service03; @@ -18,12 +36,11 @@ pub mod service07; pub mod service08; pub mod service09; pub mod service10; -pub mod codes; pub type OBDError = ProtocolResult; // Helper function to get bits from byte array, in order MSB to LSB -pub (crate) fn get_obd_bits(src: &[u8]) -> Vec { +pub(crate) fn get_obd_bits(src: &[u8]) -> Vec { let mut res = Vec::new(); for byte in src { let mut mask: u8 = 0b10000000; @@ -35,15 +52,16 @@ pub (crate) fn get_obd_bits(src: &[u8]) -> Vec { res } -trait ObdService where Self: Sized { +trait ObdService +where + Self: Sized, +{ fn init(s: &ObdServer) -> Option; } - - #[derive(Debug, Clone, Copy)] pub enum ObdError { - CmdNotSupported + CmdNotSupported, } impl CommandError for ObdError { @@ -57,13 +75,14 @@ impl CommandError for ObdError { fn from_byte(_b: u8) -> Self where - Self: Sized { + Self: Sized, + { Self::CmdNotSupported } } #[derive(Debug, Copy, Clone)] -pub enum OBDCmd{ +pub enum OBDCmd { Service01, Service02, Service03, @@ -89,7 +108,8 @@ impl Selectable for OBDCmd { OBDCmd::Service08 => "Control operation of on-board systems", OBDCmd::Service09 => "Request vehicle information", OBDCmd::Service0A => "Permanent DTCs", - }.into() + } + .into() } fn get_name(&self) -> String { @@ -154,11 +174,16 @@ pub struct ObdServer { } impl ObdServer { - pub fn req_service09 ProtocolResult>(&self, func: F) -> ProtocolResult { + pub fn req_service09 ProtocolResult>( + &self, + func: F, + ) -> ProtocolResult { if let Some(s) = &self.s09 { func(s) } else { - Err(ProtocolError::CustomError("Service not supported by ECU".into())) + Err(ProtocolError::CustomError( + "Service not supported by ECU".into(), + )) } } @@ -172,7 +197,11 @@ impl ObdServer { res.push((self.s02.is_some(), 0x02, "Freeze frame data".into())); res.push((self.s03.is_some(), 0x03, "Read DTCs".into())); res.push((self.s04.is_some(), 0x04, "Clear DTCs".into())); - res.push((self.s05.is_some(), 0x05, "O2 monitoring test results".into())); + res.push(( + self.s05.is_some(), + 0x05, + "O2 monitoring test results".into(), + )); res.push((self.s06.is_some(), 0x06, "test results (other)".into())); res.push((self.s07.is_some(), 0x07, "Show pending DTCs".into())); res.push((self.s08.is_some(), 0x08, "Control operation".into())); @@ -181,21 +210,20 @@ impl ObdServer { return res; } - // Used for services 03, 07 and 0A fn decode_dtc_resp(&self, bytes: &[u8], state: DTCState, res: &mut Vec) { let num_dtcs = bytes[0]; if num_dtcs == 0 { - return + return; } for idx in 0..num_dtcs as usize { - let a = bytes[idx*2+1]; - let b = bytes[idx*2+2]; + let a = bytes[idx * 2 + 1]; + let b = bytes[idx * 2 + 2]; let char = match (a & 0b11000000) >> 6 { 0 => 'P', 1 => 'C', 2 => 'B', - _ => 'U' + _ => 'U', }; let second = match (a & 0b00001100) >> 4 { @@ -235,16 +263,25 @@ impl ProtocolServer for ObdServer { diag_cfg: DiagCfg, ) -> super::ProtocolResult { if interface_type != InterfaceType::IsoTp && interface_type != InterfaceType::Iso9141 { - return Err(ProtocolError::CustomError("OBD-II Can only be executed over ISO-TP or ISO9141".into())) + return Err(ProtocolError::CustomError( + "OBD-II Can only be executed over ISO-TP or ISO9141".into(), + )); } - let mut dyn_interface = DynamicInterface::new(comm_server, interface_type, &interface_cfg)?.clone_box(); + let mut dyn_interface = + DynamicInterface::new(comm_server, interface_type, &interface_cfg)?.clone_box(); if interface_type == InterfaceType::IsoTp { - dyn_interface.add_filter(FilterType::IsoTP{id: diag_cfg.recv_id, mask: 0xFFFF, fc: diag_cfg.send_id})?; + dyn_interface.add_filter(FilterType::IsoTP { + id: diag_cfg.recv_id, + mask: 0xFFFF, + fc: diag_cfg.send_id, + })?; } else { - return Err(ProtocolError::CustomError("OBD-II over ISO9141 is a WIP".into())) + return Err(ProtocolError::CustomError( + "OBD-II over ISO9141 is a WIP".into(), + )); } - + let should_run = Arc::new(AtomicBool::new(true)); let should_run_t = should_run.clone(); @@ -324,7 +361,9 @@ impl ProtocolServer for ObdServer { } let resp = self.cmd_rx.recv().unwrap()?; if resp[0] == 0x7F { - Err(ProtocolError::ProtocolError(Box::new(ObdError::from_byte(0)))) + Err(ProtocolError::ProtocolError(Box::new(ObdError::from_byte( + 0, + )))) } else { Ok(resp) } @@ -332,15 +371,18 @@ impl ProtocolServer for ObdServer { fn read_errors(&self) -> super::ProtocolResult> { let mut res: Vec = Vec::new(); - if let Ok(resp) = self.run_command(0x03, &[]) { // Stored DTCs + if let Ok(resp) = self.run_command(0x03, &[]) { + // Stored DTCs println!("S03: {:02X?}", resp); self.decode_dtc_resp(&resp[1..], DTCState::Stored, &mut res); } - if let Ok(resp) = self.run_command(0x07, &[]) { // Pending DTCs + if let Ok(resp) = self.run_command(0x07, &[]) { + // Pending DTCs println!("S07: {:02X?}", resp); self.decode_dtc_resp(&resp[1..], DTCState::Pending, &mut res); } - if let Ok(resp) = self.run_command(0x0A, &[]) { // Permanent DTCs + if let Ok(resp) = self.run_command(0x0A, &[]) { + // Permanent DTCs println!("S0A: {:02X?}", resp); self.decode_dtc_resp(&resp[1..], DTCState::Permanent, &mut res); } @@ -360,4 +402,4 @@ impl Drop for ObdServer { fn drop(&mut self) { self.exit_diag_session(); } -} \ No newline at end of file +} diff --git a/app_rust/src/commapi/protocols/obd2/service01.rs b/app_rust/src/commapi/protocols/obd2/service01.rs index 6da18c3..ff56e6a 100644 --- a/app_rust/src/commapi/protocols/obd2/service01.rs +++ b/app_rust/src/commapi/protocols/obd2/service01.rs @@ -4,7 +4,7 @@ use lazy_static::lazy_static; use crate::commapi::protocols::{ProtocolError, ProtocolServer}; -use super::{OBDError, ObdError, ObdServer, ObdService, get_obd_bits}; +use super::{get_obd_bits, OBDError, ObdError, ObdServer, ObdService}; lazy_static! { static ref PID_LIST: PidList = PidList::init_list(); @@ -21,7 +21,7 @@ enum OBDDataType { pub enum PidReturnType<'a> { Number(PidResult<'a>), MultiNumber(Vec>), - String(String) // Enums + String(String), // Enums } #[derive(Clone)] @@ -32,38 +32,48 @@ struct PidConvert { fun1: Arc f32>>, fun2: Arc Vec>>, fun3: Arc String>>, - bounds: Vec<(f32, f32)> + bounds: Vec<(f32, f32)>, } -unsafe impl Send for PidConvert{} -unsafe impl Sync for PidConvert{} +unsafe impl Send for PidConvert {} +unsafe impl Sync for PidConvert {} impl PidConvert { fn parse(&self, args: [u8; 4]) -> PidReturnType { match self.data_type { - OBDDataType::Number => PidReturnType::Number(PidResult{ + OBDDataType::Number => PidReturnType::Number(PidResult { desc: self.desc[0], unit: self.unit[0], bounds: self.bounds[0], - res: (self.fun1)(args[0] as f32, args[1] as f32, args[2] as f32, args[3] as f32) + res: (self.fun1)( + args[0] as f32, + args[1] as f32, + args[2] as f32, + args[3] as f32, + ), }), OBDDataType::MultiNumber => { - let t = (self.fun2)(args[0] as f32, args[1] as f32, args[2] as f32, args[3] as f32); + let t = (self.fun2)( + args[0] as f32, + args[1] as f32, + args[2] as f32, + args[3] as f32, + ); let mut res = Vec::new(); for (idx, x) in t.iter().enumerate() { - res.push( - PidResult{ - desc: self.desc[idx], - unit: self.unit[idx], - bounds: self.bounds[idx], - res: *x - } - ) + res.push(PidResult { + desc: self.desc[idx], + unit: self.unit[idx], + bounds: self.bounds[idx], + res: *x, + }) } PidReturnType::MultiNumber(res) - }, - OBDDataType::String => PidReturnType::String((self.fun3)(args[0], args[1], args[2], args[3])) + } + OBDDataType::String => { + PidReturnType::String((self.fun3)(args[0], args[1], args[2], args[3])) + } } } } @@ -73,155 +83,709 @@ pub struct PidResult<'a> { desc: &'a str, unit: &'a str, res: f32, - bounds: (f32, f32) + bounds: (f32, f32), } pub struct PidList { - pids: Vec> + pids: Vec>, } impl PidList { - pub (crate) fn init_list() -> Self { - let mut res = PidList { pids: vec![None; 0xFF] }; + pub(crate) fn init_list() -> Self { + let mut res = PidList { + pids: vec![None; 0xFF], + }; // Add new PIDS here! - res.add_func_str(0x03, "Fuel system status", &|a, _, _, _| { PidList::get_fuel_status(a) }); - res.add_func_num(0x04, "Calculated engine load", "%", (0.0, 100.0), &|a, _, _, _|{ a/2.55 }); - res.add_func_num(0x05, "Engine coolant tempurature", "\u{00B0}C", (-40.0, 215.0), &|a, _, _, _|{ a - 40.0 }); - res.add_func_num(0x06, "Short term fuel trim - Bank 1", "%", (-100.0, 99.2), &|a, _, _, _|{ a/1.28 - 100.0 }); - res.add_func_num(0x07, "Long term fuel trim - Bank 1", "%", (-100.0, 99.2), &|a, _, _, _|{ a/1.28 - 100.0 }); - res.add_func_num(0x08, "Short term fuel trim - Bank 2", "%", (-100.0, 99.2), &|a, _, _, _|{ a/1.28 - 100.0 }); - res.add_func_num(0x09, "Long term fuel trim - Bank 2", "%", (-100.0, 99.2), &|a, _, _, _|{ a/1.28 - 100.0 }); - res.add_func_num(0x0A, "Fuel pressure", "kPa", (0.0, 765.0), &|a, _, _, _|{ a * 3.0 }); - res.add_func_num(0x0B, "Intake manifold absolute pressure", "kPa", (0.0, 255.0), &|a, _, _, _|{ a }); - res.add_func_num(0x0C, "Engine speed", "rpm", (0.0, 16383.75), &|a, b, _, _|{ ((256.0 * a) + b)/4.0 }); - res.add_func_num(0x0D, "Vehicle speed", "km/h", (0.0, 255.0), &|a, _, _, _|{ a }); - res.add_func_num(0x0E, "Timing advance", "\u{00B0} before TDC", (-64.0, 63.5), &|a, _, _, _|{ a/2.0 - 64.0 }); - res.add_func_num(0x0F, "Intake air temperature", "\u{00B0}C", (-40.0, 215.0), &|a, _, _, _|{ a - 40.0 }); - res.add_func_num(0x10, "Mass air flow sensor (MAF)", "grams/sec", (0.0, 655.35), &|a, b, _, _|{ ((256.0 * a) + b) / 100.0 }); - res.add_func_num(0x11, "Throttle position", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_str(0x12, "Commanded secondary air status", &|a, _, _, _| { PidList::get_secondary_air_status(a) }); - res.add_func_mult(0x14, &["O2 sensor 1 voltage", "O2 sensor 1 short fuel trim"], &["V", "%"], &[(0.0, 1.275), (-100.0, 99.2)], &|a, b, _, _| { vec![a/200.0, (1.28*b) - 100.0] }); - res.add_func_mult(0x15, &["O2 sensor 2 voltage", "O2 sensor 2 short fuel trim"], &["V", "%"], &[(0.0, 1.275), (-100.0, 99.2)], &|a, b, _, _| { vec![a/200.0, (1.28*b) - 100.0] }); - res.add_func_mult(0x16, &["O2 sensor 3 voltage", "O2 sensor 3 short fuel trim"], &["V", "%"], &[(0.0, 1.275), (-100.0, 99.2)], &|a, b, _, _| { vec![a/200.0, (1.28*b) - 100.0] }); - res.add_func_mult(0x17, &["O2 sensor 4 voltage", "O2 sensor 4 short fuel trim"], &["V", "%"], &[(0.0, 1.275), (-100.0, 99.2)], &|a, b, _, _| { vec![a/200.0, (1.28*b) - 100.0] }); - res.add_func_mult(0x18, &["O2 sensor 5 voltage", "O2 sensor 5 short fuel trim"], &["V", "%"], &[(0.0, 1.275), (-100.0, 99.2)], &|a, b, _, _| { vec![a/200.0, (1.28*b) - 100.0] }); - res.add_func_mult(0x19, &["O2 sensor 6 voltage", "O2 sensor 6 short fuel trim"], &["V", "%"], &[(0.0, 1.275), (-100.0, 99.2)], &|a, b, _, _| { vec![a/200.0, (1.28*b) - 100.0] }); - res.add_func_mult(0x1A, &["O2 sensor 7 voltage", "O2 sensor 7 short fuel trim"], &["V", "%"], &[(0.0, 1.275), (-100.0, 99.2)], &|a, b, _, _| { vec![a/200.0, (1.28*b) - 100.0] }); - res.add_func_mult(0x1B, &["O2 sensor 8 voltage", "O2 sensor 8 short fuel trim"], &["V", "%"], &[(0.0, 1.275), (-100.0, 99.2)], &|a, b, _, _| { vec![a/200.0, (1.28*b) - 100.0] }); - res.add_func_str(0x1C, "OBD standards supported", &|a, _, _, _| { PidList::get_obd_standard(a) }); - res.add_func_str(0x1E, "AUX input status", &|a, _, _, _| {if a == 0 {"Power take off inactive"} else {"Power take off active"}.into() }); - res.add_func_num(0x1F, "Run time since engine start", "seconds", (0.0, 65535.0), &|a, b, _, _|{ (a * 256.0) + b }); - res.add_func_num(0x21, "Distance traveled with MIL on", "km", (0.0, 65535.0), &|a, b, _, _|{ (a * 256.0) + b }); - res.add_func_num(0x22, "Fuel rail pressure", "kPa", (0.0, 5177.265), &|a, b, _, _|{ 0.079 * ((a * 256.0) + b) }); - res.add_func_num(0x23, "Fuel rail gauge pressure", "kPa", (0.0, 655350.0), &|a, b, _, _|{ 10.0 * ((a * 256.0) + b) }); - res.add_func_mult(0x24, &["O2 sensor 1 Air-fuel ratio", "O2 sensor 1 voltage"], &["", "V"], &[(0.0, 2.0), (-100.0, 8.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), (8.0/65536.0)*((256.0*c)+d)] }); - res.add_func_mult(0x25, &["O2 sensor 2 Air-fuel ratio", "O2 sensor 2 voltage"], &["", "V"], &[(0.0, 2.0), (-100.0, 8.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), (8.0/65536.0)*((256.0*c)+d)] }); - res.add_func_mult(0x26, &["O2 sensor 3 Air-fuel ratio", "O2 sensor 3 voltage"], &["", "V"], &[(0.0, 2.0), (-100.0, 8.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), (8.0/65536.0)*((256.0*c)+d)] }); - res.add_func_mult(0x27, &["O2 sensor 4 Air-fuel ratio", "O2 sensor 4 voltage"], &["", "V"], &[(0.0, 2.0), (-100.0, 8.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), (8.0/65536.0)*((256.0*c)+d)] }); - res.add_func_mult(0x28, &["O2 sensor 5 Air-fuel ratio", "O2 sensor 5 voltage"], &["", "V"], &[(0.0, 2.0), (-100.0, 8.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), (8.0/65536.0)*((256.0*c)+d)] }); - res.add_func_mult(0x29, &["O2 sensor 6 Air-fuel ratio", "O2 sensor 6 voltage"], &["", "V"], &[(0.0, 2.0), (-100.0, 8.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), (8.0/65536.0)*((256.0*c)+d)] }); - res.add_func_mult(0x2A, &["O2 sensor 7 Air-fuel ratio", "O2 sensor 7 voltage"], &["", "V"], &[(0.0, 2.0), (-100.0, 8.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), (8.0/65536.0)*((256.0*c)+d)] }); - res.add_func_mult(0x2B, &["O2 sensor 8 Air-fuel ratio", "O2 sensor 8 voltage"], &["", "V"], &[(0.0, 2.0), (-100.0, 8.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), (8.0/65536.0)*((256.0*c)+d)] }); - res.add_func_num(0x2C, "Commanded EGR", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x2D, "EGR Error", "%", (-100.0, 99.2), &|a, _, _, _|{ (a / 1.28) - 100.0 }); - res.add_func_num(0x2E, "Commanded evaporative purge", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x2F, "Fuel tank level input", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x31, "Distance traveled since codes cleared", "km", (0.0, 65535.0), &|a, b, _, _|{ 0.079 * ((a * 256.0) + b) }); - res.add_func_num(0x32, "Evap. System vapor pressure", "Pa", (-8192.0, 8191.75), &|a, b, _, _|{ 0.25 * ((a * 256.0) + b) }); - res.add_func_num(0x33, "Absolute barometric pressure", "kPa", (0.0, 255.0), &|a, _, _, _|{ a }); - res.add_func_mult(0x34, &["O2 sensor 1 Air-fuel ratio", "O2 sensor 1 current"], &["", "mA"], &[(0.0, 2.0), (0.0, 128.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), c + (d / 256.0) - 128.0] }); - res.add_func_mult(0x35, &["O2 sensor 2 Air-fuel ratio", "O2 sensor 2 current"], &["", "mA"], &[(0.0, 2.0), (0.0, 128.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), c + (d / 256.0) - 128.0] }); - res.add_func_mult(0x36, &["O2 sensor 3 Air-fuel ratio", "O2 sensor 3 current"], &["", "mA"], &[(0.0, 2.0), (0.0, 128.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), c + (d / 256.0) - 128.0] }); - res.add_func_mult(0x37, &["O2 sensor 4 Air-fuel ratio", "O2 sensor 4 current"], &["", "mA"], &[(0.0, 2.0), (0.0, 128.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), c + (d / 256.0) - 128.0] }); - res.add_func_mult(0x38, &["O2 sensor 5 Air-fuel ratio", "O2 sensor 5 current"], &["", "mA"], &[(0.0, 2.0), (0.0, 128.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), c + (d / 256.0) - 128.0] }); - res.add_func_mult(0x39, &["O2 sensor 6 Air-fuel ratio", "O2 sensor 6 current"], &["", "mA"], &[(0.0, 2.0), (0.0, 128.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), c + (d / 256.0) - 128.0] }); - res.add_func_mult(0x3A, &["O2 sensor 7 Air-fuel ratio", "O2 sensor 7 current"], &["", "mA"], &[(0.0, 2.0), (0.0, 128.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), c + (d / 256.0) - 128.0] }); - res.add_func_mult(0x3B, &["O2 sensor 8 Air-fuel ratio", "O2 sensor 8 current"], &["", "mA"], &[(0.0, 2.0), (0.0, 128.0)], &|a, b, c, d| { vec![(2.0/65536.0)*((256.0*a)+b), c + (d / 256.0) - 128.0] }); - res.add_func_num(0x3C, "Catalyst temperature. Bank 1, Sensor 1", "\u{00B0}C", (-40.0, 6513.5), &|a, b, _, _|{ 0.1 * ((a * 256.0) + b) }); - res.add_func_num(0x3D, "Catalyst temperature. Bank 2, Sensor 1", "\u{00B0}C", (-40.0, 6513.5), &|a, b, _, _|{ 0.1 * ((a * 256.0) + b) }); - res.add_func_num(0x3E, "Catalyst temperature. Bank 1, Sensor 2", "\u{00B0}C", (-40.0, 6513.5), &|a, b, _, _|{ 0.1 * ((a * 256.0) + b) }); - res.add_func_num(0x3F, "Catalyst temperature. Bank 2, Sensor 2", "\u{00B0}C", (-40.0, 6513.5), &|a, b, _, _|{ 0.1 * ((a * 256.0) + b) }); - res.add_func_num(0x42, "Control module voltage", "V", (0.0, 65.535), &|a, b, _, _|{ 0.001 * ((a * 256.0) + b) }); - res.add_func_num(0x43, "Absolute load value", "%", (0.0, 25700.0), &|a, b, _, _|{ ((a * 256.0) + b) * (100.0/255.0) }); - res.add_func_num(0x44, "Commanded air-fuel ratio", "", (0.0, 2.0), &|a, b, _, _|{ ((a * 256.0) + b) * (2.0/65536.0) }); - res.add_func_num(0x45, "Relative throttle position", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x46, "Ambient air temperature", "\u{00B0}C", (-40.0, 215.0), &|a, _, _, _|{ a -40.0 }); - res.add_func_num(0x47, "Absolute throttle position B", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x48, "Absolute throttle position C", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x49, "Accelerator pedal position D", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x4A, "Accelerator pedal position E", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x4B, "Accelerator pedal position F", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x4C, "Commanded throttle actuator", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x4D, "Time with MIL on", "Minutes", (0.0, 65535.0), &|a, b, _, _|{ (a*256.0) + b }); - res.add_func_num(0x4E, "Time since trouble code cleared", "Minutes", (0.0, 65535.0), &|a, b, _, _|{ (a*256.0) + b }); - res.add_func_str(0x51, "Fuel type", &|a, _, _, _| { PidList::get_fuel_type(a) }); - res.add_func_num(0x52, "Ethanol fuel percentage", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x53, "Absolute Evap system vapor pressure", "kPa", (0.0, 327.675), &|a, b, _, _|{ ((256.0 * a) + b) / 200.0 }); - res.add_func_num(0x54, "Evap system vapor pressure", "Pa", (-32767.0, 32768.0), &|a, b, _, _|{ ((256.0 * a) + b) - 32767.0 }); + res.add_func_str(0x03, "Fuel system status", &|a, _, _, _| { + PidList::get_fuel_status(a) + }); + res.add_func_num( + 0x04, + "Calculated engine load", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x05, + "Engine coolant tempurature", + "\u{00B0}C", + (-40.0, 215.0), + &|a, _, _, _| a - 40.0, + ); + res.add_func_num( + 0x06, + "Short term fuel trim - Bank 1", + "%", + (-100.0, 99.2), + &|a, _, _, _| a / 1.28 - 100.0, + ); + res.add_func_num( + 0x07, + "Long term fuel trim - Bank 1", + "%", + (-100.0, 99.2), + &|a, _, _, _| a / 1.28 - 100.0, + ); + res.add_func_num( + 0x08, + "Short term fuel trim - Bank 2", + "%", + (-100.0, 99.2), + &|a, _, _, _| a / 1.28 - 100.0, + ); + res.add_func_num( + 0x09, + "Long term fuel trim - Bank 2", + "%", + (-100.0, 99.2), + &|a, _, _, _| a / 1.28 - 100.0, + ); + res.add_func_num(0x0A, "Fuel pressure", "kPa", (0.0, 765.0), &|a, _, _, _| { + a * 3.0 + }); + res.add_func_num( + 0x0B, + "Intake manifold absolute pressure", + "kPa", + (0.0, 255.0), + &|a, _, _, _| a, + ); + res.add_func_num( + 0x0C, + "Engine speed", + "rpm", + (0.0, 16383.75), + &|a, b, _, _| ((256.0 * a) + b) / 4.0, + ); + res.add_func_num(0x0D, "Vehicle speed", "km/h", (0.0, 255.0), &|a, _, _, _| a); + res.add_func_num( + 0x0E, + "Timing advance", + "\u{00B0} before TDC", + (-64.0, 63.5), + &|a, _, _, _| a / 2.0 - 64.0, + ); + res.add_func_num( + 0x0F, + "Intake air temperature", + "\u{00B0}C", + (-40.0, 215.0), + &|a, _, _, _| a - 40.0, + ); + res.add_func_num( + 0x10, + "Mass air flow sensor (MAF)", + "grams/sec", + (0.0, 655.35), + &|a, b, _, _| ((256.0 * a) + b) / 100.0, + ); + res.add_func_num( + 0x11, + "Throttle position", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_str(0x12, "Commanded secondary air status", &|a, _, _, _| { + PidList::get_secondary_air_status(a) + }); + res.add_func_mult( + 0x14, + &["O2 sensor 1 voltage", "O2 sensor 1 short fuel trim"], + &["V", "%"], + &[(0.0, 1.275), (-100.0, 99.2)], + &|a, b, _, _| vec![a / 200.0, (1.28 * b) - 100.0], + ); + res.add_func_mult( + 0x15, + &["O2 sensor 2 voltage", "O2 sensor 2 short fuel trim"], + &["V", "%"], + &[(0.0, 1.275), (-100.0, 99.2)], + &|a, b, _, _| vec![a / 200.0, (1.28 * b) - 100.0], + ); + res.add_func_mult( + 0x16, + &["O2 sensor 3 voltage", "O2 sensor 3 short fuel trim"], + &["V", "%"], + &[(0.0, 1.275), (-100.0, 99.2)], + &|a, b, _, _| vec![a / 200.0, (1.28 * b) - 100.0], + ); + res.add_func_mult( + 0x17, + &["O2 sensor 4 voltage", "O2 sensor 4 short fuel trim"], + &["V", "%"], + &[(0.0, 1.275), (-100.0, 99.2)], + &|a, b, _, _| vec![a / 200.0, (1.28 * b) - 100.0], + ); + res.add_func_mult( + 0x18, + &["O2 sensor 5 voltage", "O2 sensor 5 short fuel trim"], + &["V", "%"], + &[(0.0, 1.275), (-100.0, 99.2)], + &|a, b, _, _| vec![a / 200.0, (1.28 * b) - 100.0], + ); + res.add_func_mult( + 0x19, + &["O2 sensor 6 voltage", "O2 sensor 6 short fuel trim"], + &["V", "%"], + &[(0.0, 1.275), (-100.0, 99.2)], + &|a, b, _, _| vec![a / 200.0, (1.28 * b) - 100.0], + ); + res.add_func_mult( + 0x1A, + &["O2 sensor 7 voltage", "O2 sensor 7 short fuel trim"], + &["V", "%"], + &[(0.0, 1.275), (-100.0, 99.2)], + &|a, b, _, _| vec![a / 200.0, (1.28 * b) - 100.0], + ); + res.add_func_mult( + 0x1B, + &["O2 sensor 8 voltage", "O2 sensor 8 short fuel trim"], + &["V", "%"], + &[(0.0, 1.275), (-100.0, 99.2)], + &|a, b, _, _| vec![a / 200.0, (1.28 * b) - 100.0], + ); + res.add_func_str(0x1C, "OBD standards supported", &|a, _, _, _| { + PidList::get_obd_standard(a) + }); + res.add_func_str(0x1E, "AUX input status", &|a, _, _, _| { + if a == 0 { + "Power take off inactive" + } else { + "Power take off active" + } + .into() + }); + res.add_func_num( + 0x1F, + "Run time since engine start", + "seconds", + (0.0, 65535.0), + &|a, b, _, _| (a * 256.0) + b, + ); + res.add_func_num( + 0x21, + "Distance traveled with MIL on", + "km", + (0.0, 65535.0), + &|a, b, _, _| (a * 256.0) + b, + ); + res.add_func_num( + 0x22, + "Fuel rail pressure", + "kPa", + (0.0, 5177.265), + &|a, b, _, _| 0.079 * ((a * 256.0) + b), + ); + res.add_func_num( + 0x23, + "Fuel rail gauge pressure", + "kPa", + (0.0, 655350.0), + &|a, b, _, _| 10.0 * ((a * 256.0) + b), + ); + res.add_func_mult( + 0x24, + &["O2 sensor 1 Air-fuel ratio", "O2 sensor 1 voltage"], + &["", "V"], + &[(0.0, 2.0), (-100.0, 8.0)], + &|a, b, c, d| { + vec![ + (2.0 / 65536.0) * ((256.0 * a) + b), + (8.0 / 65536.0) * ((256.0 * c) + d), + ] + }, + ); + res.add_func_mult( + 0x25, + &["O2 sensor 2 Air-fuel ratio", "O2 sensor 2 voltage"], + &["", "V"], + &[(0.0, 2.0), (-100.0, 8.0)], + &|a, b, c, d| { + vec![ + (2.0 / 65536.0) * ((256.0 * a) + b), + (8.0 / 65536.0) * ((256.0 * c) + d), + ] + }, + ); + res.add_func_mult( + 0x26, + &["O2 sensor 3 Air-fuel ratio", "O2 sensor 3 voltage"], + &["", "V"], + &[(0.0, 2.0), (-100.0, 8.0)], + &|a, b, c, d| { + vec![ + (2.0 / 65536.0) * ((256.0 * a) + b), + (8.0 / 65536.0) * ((256.0 * c) + d), + ] + }, + ); + res.add_func_mult( + 0x27, + &["O2 sensor 4 Air-fuel ratio", "O2 sensor 4 voltage"], + &["", "V"], + &[(0.0, 2.0), (-100.0, 8.0)], + &|a, b, c, d| { + vec![ + (2.0 / 65536.0) * ((256.0 * a) + b), + (8.0 / 65536.0) * ((256.0 * c) + d), + ] + }, + ); + res.add_func_mult( + 0x28, + &["O2 sensor 5 Air-fuel ratio", "O2 sensor 5 voltage"], + &["", "V"], + &[(0.0, 2.0), (-100.0, 8.0)], + &|a, b, c, d| { + vec![ + (2.0 / 65536.0) * ((256.0 * a) + b), + (8.0 / 65536.0) * ((256.0 * c) + d), + ] + }, + ); + res.add_func_mult( + 0x29, + &["O2 sensor 6 Air-fuel ratio", "O2 sensor 6 voltage"], + &["", "V"], + &[(0.0, 2.0), (-100.0, 8.0)], + &|a, b, c, d| { + vec![ + (2.0 / 65536.0) * ((256.0 * a) + b), + (8.0 / 65536.0) * ((256.0 * c) + d), + ] + }, + ); + res.add_func_mult( + 0x2A, + &["O2 sensor 7 Air-fuel ratio", "O2 sensor 7 voltage"], + &["", "V"], + &[(0.0, 2.0), (-100.0, 8.0)], + &|a, b, c, d| { + vec![ + (2.0 / 65536.0) * ((256.0 * a) + b), + (8.0 / 65536.0) * ((256.0 * c) + d), + ] + }, + ); + res.add_func_mult( + 0x2B, + &["O2 sensor 8 Air-fuel ratio", "O2 sensor 8 voltage"], + &["", "V"], + &[(0.0, 2.0), (-100.0, 8.0)], + &|a, b, c, d| { + vec![ + (2.0 / 65536.0) * ((256.0 * a) + b), + (8.0 / 65536.0) * ((256.0 * c) + d), + ] + }, + ); + res.add_func_num(0x2C, "Commanded EGR", "%", (0.0, 100.0), &|a, _, _, _| { + a / 2.55 + }); + res.add_func_num(0x2D, "EGR Error", "%", (-100.0, 99.2), &|a, _, _, _| { + (a / 1.28) - 100.0 + }); + res.add_func_num( + 0x2E, + "Commanded evaporative purge", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x2F, + "Fuel tank level input", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x31, + "Distance traveled since codes cleared", + "km", + (0.0, 65535.0), + &|a, b, _, _| 0.079 * ((a * 256.0) + b), + ); + res.add_func_num( + 0x32, + "Evap. System vapor pressure", + "Pa", + (-8192.0, 8191.75), + &|a, b, _, _| 0.25 * ((a * 256.0) + b), + ); + res.add_func_num( + 0x33, + "Absolute barometric pressure", + "kPa", + (0.0, 255.0), + &|a, _, _, _| a, + ); + res.add_func_mult( + 0x34, + &["O2 sensor 1 Air-fuel ratio", "O2 sensor 1 current"], + &["", "mA"], + &[(0.0, 2.0), (0.0, 128.0)], + &|a, b, c, d| vec![(2.0 / 65536.0) * ((256.0 * a) + b), c + (d / 256.0) - 128.0], + ); + res.add_func_mult( + 0x35, + &["O2 sensor 2 Air-fuel ratio", "O2 sensor 2 current"], + &["", "mA"], + &[(0.0, 2.0), (0.0, 128.0)], + &|a, b, c, d| vec![(2.0 / 65536.0) * ((256.0 * a) + b), c + (d / 256.0) - 128.0], + ); + res.add_func_mult( + 0x36, + &["O2 sensor 3 Air-fuel ratio", "O2 sensor 3 current"], + &["", "mA"], + &[(0.0, 2.0), (0.0, 128.0)], + &|a, b, c, d| vec![(2.0 / 65536.0) * ((256.0 * a) + b), c + (d / 256.0) - 128.0], + ); + res.add_func_mult( + 0x37, + &["O2 sensor 4 Air-fuel ratio", "O2 sensor 4 current"], + &["", "mA"], + &[(0.0, 2.0), (0.0, 128.0)], + &|a, b, c, d| vec![(2.0 / 65536.0) * ((256.0 * a) + b), c + (d / 256.0) - 128.0], + ); + res.add_func_mult( + 0x38, + &["O2 sensor 5 Air-fuel ratio", "O2 sensor 5 current"], + &["", "mA"], + &[(0.0, 2.0), (0.0, 128.0)], + &|a, b, c, d| vec![(2.0 / 65536.0) * ((256.0 * a) + b), c + (d / 256.0) - 128.0], + ); + res.add_func_mult( + 0x39, + &["O2 sensor 6 Air-fuel ratio", "O2 sensor 6 current"], + &["", "mA"], + &[(0.0, 2.0), (0.0, 128.0)], + &|a, b, c, d| vec![(2.0 / 65536.0) * ((256.0 * a) + b), c + (d / 256.0) - 128.0], + ); + res.add_func_mult( + 0x3A, + &["O2 sensor 7 Air-fuel ratio", "O2 sensor 7 current"], + &["", "mA"], + &[(0.0, 2.0), (0.0, 128.0)], + &|a, b, c, d| vec![(2.0 / 65536.0) * ((256.0 * a) + b), c + (d / 256.0) - 128.0], + ); + res.add_func_mult( + 0x3B, + &["O2 sensor 8 Air-fuel ratio", "O2 sensor 8 current"], + &["", "mA"], + &[(0.0, 2.0), (0.0, 128.0)], + &|a, b, c, d| vec![(2.0 / 65536.0) * ((256.0 * a) + b), c + (d / 256.0) - 128.0], + ); + res.add_func_num( + 0x3C, + "Catalyst temperature. Bank 1, Sensor 1", + "\u{00B0}C", + (-40.0, 6513.5), + &|a, b, _, _| 0.1 * ((a * 256.0) + b), + ); + res.add_func_num( + 0x3D, + "Catalyst temperature. Bank 2, Sensor 1", + "\u{00B0}C", + (-40.0, 6513.5), + &|a, b, _, _| 0.1 * ((a * 256.0) + b), + ); + res.add_func_num( + 0x3E, + "Catalyst temperature. Bank 1, Sensor 2", + "\u{00B0}C", + (-40.0, 6513.5), + &|a, b, _, _| 0.1 * ((a * 256.0) + b), + ); + res.add_func_num( + 0x3F, + "Catalyst temperature. Bank 2, Sensor 2", + "\u{00B0}C", + (-40.0, 6513.5), + &|a, b, _, _| 0.1 * ((a * 256.0) + b), + ); + res.add_func_num( + 0x42, + "Control module voltage", + "V", + (0.0, 65.535), + &|a, b, _, _| 0.001 * ((a * 256.0) + b), + ); + res.add_func_num( + 0x43, + "Absolute load value", + "%", + (0.0, 25700.0), + &|a, b, _, _| ((a * 256.0) + b) * (100.0 / 255.0), + ); + res.add_func_num( + 0x44, + "Commanded air-fuel ratio", + "", + (0.0, 2.0), + &|a, b, _, _| ((a * 256.0) + b) * (2.0 / 65536.0), + ); + res.add_func_num( + 0x45, + "Relative throttle position", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x46, + "Ambient air temperature", + "\u{00B0}C", + (-40.0, 215.0), + &|a, _, _, _| a - 40.0, + ); + res.add_func_num( + 0x47, + "Absolute throttle position B", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x48, + "Absolute throttle position C", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x49, + "Accelerator pedal position D", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x4A, + "Accelerator pedal position E", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x4B, + "Accelerator pedal position F", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x4C, + "Commanded throttle actuator", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x4D, + "Time with MIL on", + "Minutes", + (0.0, 65535.0), + &|a, b, _, _| (a * 256.0) + b, + ); + res.add_func_num( + 0x4E, + "Time since trouble code cleared", + "Minutes", + (0.0, 65535.0), + &|a, b, _, _| (a * 256.0) + b, + ); + res.add_func_str(0x51, "Fuel type", &|a, _, _, _| PidList::get_fuel_type(a)); + res.add_func_num( + 0x52, + "Ethanol fuel percentage", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x53, + "Absolute Evap system vapor pressure", + "kPa", + (0.0, 327.675), + &|a, b, _, _| ((256.0 * a) + b) / 200.0, + ); + res.add_func_num( + 0x54, + "Evap system vapor pressure", + "Pa", + (-32767.0, 32768.0), + &|a, b, _, _| ((256.0 * a) + b) - 32767.0, + ); - res.add_func_mult(0x55, &["Short term secondary oxygen trim bank 1", "Short term secondary oxygen trim bank 3"], &["%", "%"], &[(-100.0, 99.2), (-100.0, 99.2)], &|a, b, _, _| { vec![(a * 1.28)-100.0, (b * 1.28)-100.0] }); - res.add_func_mult(0x56, &["Long term secondary oxygen trim bank 1", "Long term secondary oxygen trim bank 3"], &["%", "%"], &[(-100.0, 99.2), (-100.0, 99.2)], &|a, b, _, _| { vec![(a * 1.28)-100.0, (b * 1.28)-100.0] }); - res.add_func_mult(0x57, &["Short term secondary oxygen trim bank 2", "Short term secondary oxygen trim bank 4"], &["%", "%"], &[(-100.0, 99.2), (-100.0, 99.2)], &|a, b, _, _| { vec![(a * 1.28)-100.0, (b * 1.28)-100.0] }); - res.add_func_mult(0x58, &["Long term secondary oxygen trim bank 2", "Long term secondary oxygen trim bank 4"], &["%", "%"], &[(-100.0, 99.2), (-100.0, 99.2)], &|a, b, _, _| { vec![(a * 1.28)-100.0, (b * 1.28)-100.0] }); - res.add_func_num(0x59, "Fuel rail absolute pressure", "kPa", (0.0, 655350.0), &|a, b, _, _|{ 10.0*(a*256.0 + b) }); - res.add_func_num(0x5A, "Relative accelerator pedal position", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x5B, "Hybrid battery pack remaining life", "%", (0.0, 100.0), &|a, _, _, _|{ a / 2.55 }); - res.add_func_num(0x5C, "Engine oil temperature", "\u{00B0}C", (-40.0, 210.0), &|a, _, _, _|{ a - 40.0 }); - res.add_func_num(0x5D, "Fuel injection timing", "\u{00B0}", (0.0, 100.0), &|a, b, _, _|{ (((256.0 * a) + b) / 128.0) - 210.0 }); - res.add_func_num(0x5E, "Engine fuel rate", "L/h", (0.0, 3212.75), &|a, b, _, _|{ ((256.0 * a) + b) / 20.0 }); + res.add_func_mult( + 0x55, + &[ + "Short term secondary oxygen trim bank 1", + "Short term secondary oxygen trim bank 3", + ], + &["%", "%"], + &[(-100.0, 99.2), (-100.0, 99.2)], + &|a, b, _, _| vec![(a * 1.28) - 100.0, (b * 1.28) - 100.0], + ); + res.add_func_mult( + 0x56, + &[ + "Long term secondary oxygen trim bank 1", + "Long term secondary oxygen trim bank 3", + ], + &["%", "%"], + &[(-100.0, 99.2), (-100.0, 99.2)], + &|a, b, _, _| vec![(a * 1.28) - 100.0, (b * 1.28) - 100.0], + ); + res.add_func_mult( + 0x57, + &[ + "Short term secondary oxygen trim bank 2", + "Short term secondary oxygen trim bank 4", + ], + &["%", "%"], + &[(-100.0, 99.2), (-100.0, 99.2)], + &|a, b, _, _| vec![(a * 1.28) - 100.0, (b * 1.28) - 100.0], + ); + res.add_func_mult( + 0x58, + &[ + "Long term secondary oxygen trim bank 2", + "Long term secondary oxygen trim bank 4", + ], + &["%", "%"], + &[(-100.0, 99.2), (-100.0, 99.2)], + &|a, b, _, _| vec![(a * 1.28) - 100.0, (b * 1.28) - 100.0], + ); + res.add_func_num( + 0x59, + "Fuel rail absolute pressure", + "kPa", + (0.0, 655350.0), + &|a, b, _, _| 10.0 * (a * 256.0 + b), + ); + res.add_func_num( + 0x5A, + "Relative accelerator pedal position", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x5B, + "Hybrid battery pack remaining life", + "%", + (0.0, 100.0), + &|a, _, _, _| a / 2.55, + ); + res.add_func_num( + 0x5C, + "Engine oil temperature", + "\u{00B0}C", + (-40.0, 210.0), + &|a, _, _, _| a - 40.0, + ); + res.add_func_num( + 0x5D, + "Fuel injection timing", + "\u{00B0}", + (0.0, 100.0), + &|a, b, _, _| (((256.0 * a) + b) / 128.0) - 210.0, + ); + res.add_func_num( + 0x5E, + "Engine fuel rate", + "L/h", + (0.0, 3212.75), + &|a, b, _, _| ((256.0 * a) + b) / 20.0, + ); - res.add_func_num(0xC0, "Odometer reading", "km", (0.0, 429496729.5), &|a, b, c, d|{ ( (a * 2i32.pow(24) as f32)+(b * 2i32.pow(16) as f32)+(c * 2i32.pow(8) as f32) +d)/10.0 }); - res + res.add_func_num( + 0xC0, + "Odometer reading", + "km", + (0.0, 429496729.5), + &|a, b, c, d| { + ((a * 2i32.pow(24) as f32) + + (b * 2i32.pow(16) as f32) + + (c * 2i32.pow(8) as f32) + + d) + / 10.0 + }, + ); + res } pub fn get_desc_pid(&self, pid: u8) -> Option<(u8, Vec<&'static str>)> { - self.pids[pid as usize].as_ref().map(|x| (pid, x.desc.clone())) + self.pids[pid as usize] + .as_ref() + .map(|x| (pid, x.desc.clone())) } - - fn add_func_num(&mut self, pid: u8, desc: &'static str, unit: &'static str, bounds: (f32, f32), f: &'static dyn Fn(f32,f32,f32,f32) -> f32) { + fn add_func_num( + &mut self, + pid: u8, + desc: &'static str, + unit: &'static str, + bounds: (f32, f32), + f: &'static dyn Fn(f32, f32, f32, f32) -> f32, + ) { self.pids[pid as usize] = Some(PidConvert { data_type: OBDDataType::Number, desc: vec![desc], unit: vec![unit], fun1: Arc::new(Box::new(f)), - fun2: Arc::new(Box::new(|_, _, _, _| { vec![] })), - fun3: Arc::new(Box::new(|_, _, _, _| "".into() )), + fun2: Arc::new(Box::new(|_, _, _, _| vec![])), + fun3: Arc::new(Box::new(|_, _, _, _| "".into())), bounds: vec![bounds], }); } - fn add_func_mult(&mut self, pid: u8, desc: &[&'static str], unit: &[&'static str], bounds: &[(f32, f32)], f: &'static dyn Fn(f32,f32,f32,f32) -> Vec) { + fn add_func_mult( + &mut self, + pid: u8, + desc: &[&'static str], + unit: &[&'static str], + bounds: &[(f32, f32)], + f: &'static dyn Fn(f32, f32, f32, f32) -> Vec, + ) { assert!(desc.len() == unit.len() && unit.len() == bounds.len()); // TODO - Const generic when that becomes stable self.pids[pid as usize] = Some(PidConvert { data_type: OBDDataType::MultiNumber, desc: Vec::from(desc), unit: Vec::from(unit), - fun1: Arc::new(Box::new(|_, _, _, _| {0.0f32 })), + fun1: Arc::new(Box::new(|_, _, _, _| 0.0f32)), fun2: Arc::new(Box::new(f)), fun3: Arc::new(Box::new(|_, _, _, _| "".into())), - bounds: Vec::from(bounds) - + bounds: Vec::from(bounds), }) } - fn add_func_str(&mut self, pid: u8, desc: &'static str, f: &'static dyn Fn(u8,u8,u8,u8) -> String) { + fn add_func_str( + &mut self, + pid: u8, + desc: &'static str, + f: &'static dyn Fn(u8, u8, u8, u8) -> String, + ) { self.pids[pid as usize] = Some(PidConvert { data_type: OBDDataType::String, desc: vec![desc], unit: vec![], - fun1: Arc::new(Box::new(|_, _, _, _| {0.0f32 })), - fun2: Arc::new(Box::new(|_, _, _, _| { vec![] })), + fun1: Arc::new(Box::new(|_, _, _, _| 0.0f32)), + fun2: Arc::new(Box::new(|_, _, _, _| vec![])), fun3: Arc::new(Box::new(f)), - bounds: vec![] + bounds: vec![], }) } pub fn parse_pid(&self, pid: u8, args: &[u8]) -> Option { let parser = self.pids[pid as usize].as_ref()?; let len = min(4, args.len()); - let mut n : [u8; 4] = [0x00; 4]; + let mut n: [u8; 4] = [0x00; 4]; n[0..len].copy_from_slice(&args[0..len]); Some(parser.parse(n)) } @@ -234,18 +798,20 @@ impl PidList { 4 => "Open loop due to engine load OR fuel cut due to deceleration", 8 => "Open loop due to system failure", 16 => "Closed loop, using at least 1 O2 sensor but fault detected", - _ => return format!("Unknown. Raw: 0x{:02X}", a) - }.into() + _ => return format!("Unknown. Raw: 0x{:02X}", a), + } + .into() } - + fn get_secondary_air_status(a: u8) -> String { match a { 1 => "Upstream", 2 => "Downstream of catalytic converter", 4 => "From the outside atmosphere or off", 8 => "Pump commanded on for diagnostics", - _ => return format!("Unknown. Raw: 0x{:02X}", a) - }.into() + _ => return format!("Unknown. Raw: 0x{:02X}", a), + } + .into() } fn get_obd_standard(a: u8) -> String { @@ -279,8 +845,9 @@ impl PidList { 32 => "India OBD-II (IOBD-II)", 33 => "heavy Duty Euro OBD Stage IV (HD EOBD-IV)", 251..=255 => "Not avaliable for assignment (SAE J1939 special meaning)", - _ => return format!("Unknown. Raw: 0x{:02X}", a) - }.into() + _ => return format!("Unknown. Raw: 0x{:02X}", a), + } + .into() } fn get_fuel_type(a: u8) -> String { @@ -309,14 +876,15 @@ impl PidList { 21 => "Hybrid running electric and combustion engine", 22 => "Hybrid Regenerative", 23 => "Bifuel running Diesel", - _ => return format!("Unknown. Raw: 0x{:02X}", a) - }.into() + _ => return format!("Unknown. Raw: 0x{:02X}", a), + } + .into() } } #[derive(Debug, Clone)] pub struct Service01 { - supported_pids: Vec + supported_pids: Vec, } impl ObdService for Service01 { @@ -324,7 +892,9 @@ impl ObdService for Service01 { println!("Attempt init service 01!"); println!("Check PIDS 01-20"); let mut res = s.run_command(0x01, &[0x00]).ok()?; - let mut s01 = Service01 { supported_pids: get_obd_bits(&res[2..]) }; + let mut s01 = Service01 { + supported_pids: get_obd_bits(&res[2..]), + }; // Now query extra 01 pids if s01.check_service_supported(0x20).is_ok() { @@ -362,7 +932,6 @@ impl ObdService for Service01 { // Check C1-E0 res = s.run_command(0x01, &[0xC0]).ok()?; s01.supported_pids.append(&mut get_obd_bits(&res[2..])); - } println!("Supported PIDS:"); for x in s01.get_supported_chartable_pids() { @@ -374,13 +943,18 @@ impl ObdService for Service01 { impl Service01 { fn check_service_supported(&self, pid: u8) -> OBDError<()> { - if let Some(r) = self.supported_pids.get(pid as usize - 1) { // -1 as pid 0x00 is not here + if let Some(r) = self.supported_pids.get(pid as usize - 1) { + // -1 as pid 0x00 is not here match r { true => Ok(()), - false => Err(ProtocolError::ProtocolError(Box::new(ObdError::CmdNotSupported))) + false => Err(ProtocolError::ProtocolError(Box::new( + ObdError::CmdNotSupported, + ))), } } else { - Err(ProtocolError::ProtocolError(Box::new(ObdError::CmdNotSupported))) + Err(ProtocolError::ProtocolError(Box::new( + ObdError::CmdNotSupported, + ))) } } @@ -398,4 +972,4 @@ impl Service01 { .map(|x| x.unwrap()) .collect() } -} \ No newline at end of file +} diff --git a/app_rust/src/commapi/protocols/obd2/service02.rs b/app_rust/src/commapi/protocols/obd2/service02.rs index 73cdddf..9d363f0 100644 --- a/app_rust/src/commapi/protocols/obd2/service02.rs +++ b/app_rust/src/commapi/protocols/obd2/service02.rs @@ -1,5 +1,2 @@ - - - #[derive(Debug, Clone)] -pub struct Service02; \ No newline at end of file +pub struct Service02; diff --git a/app_rust/src/commapi/protocols/obd2/service03.rs b/app_rust/src/commapi/protocols/obd2/service03.rs index 15b9df9..15bb8eb 100644 --- a/app_rust/src/commapi/protocols/obd2/service03.rs +++ b/app_rust/src/commapi/protocols/obd2/service03.rs @@ -1,9 +1,7 @@ -use crate::commapi::protocols::{DTC, ProtocolServer, DTCState}; +use crate::commapi::protocols::{DTCState, ProtocolServer, DTC}; use super::{OBDError, ObdServer}; - - #[derive(Debug, Clone)] pub struct Service03; @@ -18,7 +16,7 @@ impl Service03 { 1 => 'C', // Chassis 2 => 'B', // Body 3 => 'U', // Network - _ => 'P' // Powertrain + _ => 'P', // Powertrain }; let n1 = (bytes[0] >> 4) & 0b0000011; let n2 = (bytes[0] >> 2) & 0b0000011; @@ -26,14 +24,13 @@ impl Service03 { error: format!("{}{:1X}{:1X}{:2X}", prefix, n1, n2, bytes[1]), state: DTCState::Stored, // TODO Fix this check_engine_on: true, - id: bytes[1] as u32 - + id: bytes[1] as u32, }; bytes.drain(0..2); res.push(dtc); } // TODO - println!("DTC BYTES: {:02X?}", res); + println!("DTC BYTES: {:02X?}", res); Ok(res) } -} \ No newline at end of file +} diff --git a/app_rust/src/commapi/protocols/obd2/service04.rs b/app_rust/src/commapi/protocols/obd2/service04.rs index 612c649..1397c97 100644 --- a/app_rust/src/commapi/protocols/obd2/service04.rs +++ b/app_rust/src/commapi/protocols/obd2/service04.rs @@ -2,7 +2,6 @@ use crate::commapi::protocols::ProtocolServer; use super::{OBDError, ObdServer}; - #[derive(Debug, Clone)] pub struct Service04; @@ -10,4 +9,4 @@ impl Service04 { pub fn clear_dtcs(s: &ObdServer) -> OBDError<()> { s.run_command(0x04, &[]).map(|_| ()) } -} \ No newline at end of file +} diff --git a/app_rust/src/commapi/protocols/obd2/service05.rs b/app_rust/src/commapi/protocols/obd2/service05.rs index c22e179..4a93f20 100644 --- a/app_rust/src/commapi/protocols/obd2/service05.rs +++ b/app_rust/src/commapi/protocols/obd2/service05.rs @@ -1,4 +1,2 @@ - - #[derive(Debug, Clone)] -pub struct Service05; \ No newline at end of file +pub struct Service05; diff --git a/app_rust/src/commapi/protocols/obd2/service06.rs b/app_rust/src/commapi/protocols/obd2/service06.rs index fa3f608..1b54ae5 100644 --- a/app_rust/src/commapi/protocols/obd2/service06.rs +++ b/app_rust/src/commapi/protocols/obd2/service06.rs @@ -1,4 +1,2 @@ - - #[derive(Debug, Clone)] -pub struct Service06; \ No newline at end of file +pub struct Service06; diff --git a/app_rust/src/commapi/protocols/obd2/service07.rs b/app_rust/src/commapi/protocols/obd2/service07.rs index 054ab34..90768c0 100644 --- a/app_rust/src/commapi/protocols/obd2/service07.rs +++ b/app_rust/src/commapi/protocols/obd2/service07.rs @@ -1,4 +1,2 @@ - - #[derive(Debug, Clone)] -pub struct Service07; \ No newline at end of file +pub struct Service07; diff --git a/app_rust/src/commapi/protocols/obd2/service08.rs b/app_rust/src/commapi/protocols/obd2/service08.rs index 7d67903..725b934 100644 --- a/app_rust/src/commapi/protocols/obd2/service08.rs +++ b/app_rust/src/commapi/protocols/obd2/service08.rs @@ -1,4 +1,2 @@ - - #[derive(Debug, Clone)] -pub struct Service08; \ No newline at end of file +pub struct Service08; diff --git a/app_rust/src/commapi/protocols/obd2/service09.rs b/app_rust/src/commapi/protocols/obd2/service09.rs index 697b399..a85bca0 100644 --- a/app_rust/src/commapi/protocols/obd2/service09.rs +++ b/app_rust/src/commapi/protocols/obd2/service09.rs @@ -1,11 +1,10 @@ use crate::commapi::protocols::{ProtocolError, ProtocolServer}; -use super::{OBDError, ObdError, ObdServer, ObdService, get_obd_bits}; - +use super::{get_obd_bits, OBDError, ObdError, ObdServer, ObdService}; #[derive(Debug, Clone)] -pub struct Service09{ - supported_pids: Vec +pub struct Service09 { + supported_pids: Vec, } #[derive(Debug, Clone, Default)] @@ -13,7 +12,7 @@ pub struct Service09Data { pub vin: String, pub calibration_id: String, pub cvns: Vec, - pub ecu_name: String + pub ecu_name: String, } impl ObdService for Service09 { @@ -22,8 +21,8 @@ impl ObdService for Service09 { let res = s.run_command(0x09, &[0x00]).ok()?; println!("Service 09 init bytes: {:02X?}", &res[2..]); // Drop SID and CID let bits = get_obd_bits(&res[2..]); - let s09 = Service09{ - supported_pids: bits + let s09 = Service09 { + supported_pids: bits, }; Some(s09) } @@ -31,14 +30,19 @@ impl ObdService for Service09 { impl Service09 { fn check_service_supported(&self, pid: u8) -> OBDError<()> { - if let Some(r) = self.supported_pids.get(pid as usize - 1) { // -1 as pid 0x00 is not here + if let Some(r) = self.supported_pids.get(pid as usize - 1) { + // -1 as pid 0x00 is not here match r { true => Ok(()), - false => Err(ProtocolError::ProtocolError(Box::new(ObdError::CmdNotSupported))) + false => Err(ProtocolError::ProtocolError(Box::new( + ObdError::CmdNotSupported, + ))), } } else { eprintln!("Warning. Out of range PID requested {:02X}", pid); - Err(ProtocolError::ProtocolError(Box::new(ObdError::CmdNotSupported))) + Err(ProtocolError::ProtocolError(Box::new( + ObdError::CmdNotSupported, + ))) } } @@ -46,13 +50,13 @@ impl Service09 { Service09Data { vin: self.get_vin(s).unwrap_or("Not Supported".into()), calibration_id: self.get_calibration_id(s).unwrap_or("Not Supported".into()), - cvns: self.get_calibration_verification_numbers(s).unwrap_or_default(), + cvns: self + .get_calibration_verification_numbers(s) + .unwrap_or_default(), ecu_name: self.get_ecu_name(s).unwrap_or("Not Supported".into()), - } } - pub fn get_vin_msg_count(&self, s: &ObdServer) -> OBDError { self.check_service_supported(0x01)?; s.run_command(0x09, &[0x01]).map(|s| s[2]) @@ -60,17 +64,20 @@ impl Service09 { pub fn get_vin(&self, s: &ObdServer) -> OBDError { self.check_service_supported(0x02)?; - s.run_command(0x09, &[0x02]).map(|s| String::from_utf8_lossy(&s[2..]).to_string()) + s.run_command(0x09, &[0x02]) + .map(|s| String::from_utf8_lossy(&s[2..]).to_string()) } pub fn get_ecu_name(&self, s: &ObdServer) -> OBDError { self.check_service_supported(0x0A)?; - s.run_command(0x09, &[0x0A]).map(|s| String::from_utf8_lossy(&s[2..]).to_string()) + s.run_command(0x09, &[0x0A]) + .map(|s| String::from_utf8_lossy(&s[2..]).to_string()) } pub fn get_calibration_id(&self, s: &ObdServer) -> OBDError { self.check_service_supported(0x04)?; - s.run_command(0x09, &[0x04]).map(|s| String::from_utf8_lossy(&s[2..]).to_string()) + s.run_command(0x09, &[0x04]) + .map(|s| String::from_utf8_lossy(&s[2..]).to_string()) } pub fn get_calibration_verification_numbers(&self, s: &ObdServer) -> OBDError> { @@ -80,17 +87,23 @@ impl Service09 { let count = raw[0]; // Number of CVNs raw.drain(0..1); let mut res = Vec::new(); - if raw.len() == (count*4) as usize { + if raw.len() == (count * 4) as usize { // Valid number of bytes for _ in 0..count { // CVN format - res.push(format!("{:02X}{:02X}{:02X}{:02X}", raw[0], raw[1], raw[2], raw[3])); + res.push(format!( + "{:02X}{:02X}{:02X}{:02X}", + raw[0], raw[1], raw[2], raw[3] + )); raw.drain(0..4); } Ok(res) } else { eprintln!("Invaliud count!"); - Err(ProtocolError::InvalidResponseSize{ expect: 3 + (count*4) as usize, actual: raw.len() + 2 }) + Err(ProtocolError::InvalidResponseSize { + expect: 3 + (count * 4) as usize, + actual: raw.len() + 2, + }) } } -} \ No newline at end of file +} diff --git a/app_rust/src/commapi/protocols/obd2/service10.rs b/app_rust/src/commapi/protocols/obd2/service10.rs index 4af8a1b..a501389 100644 --- a/app_rust/src/commapi/protocols/obd2/service10.rs +++ b/app_rust/src/commapi/protocols/obd2/service10.rs @@ -1,4 +1,2 @@ - - #[derive(Debug, Clone)] -pub struct Service0A; \ No newline at end of file +pub struct Service0A; diff --git a/app_rust/src/commapi/protocols/uds/mod.rs b/app_rust/src/commapi/protocols/uds/mod.rs index dca5280..be69b96 100644 --- a/app_rust/src/commapi/protocols/uds/mod.rs +++ b/app_rust/src/commapi/protocols/uds/mod.rs @@ -1,6 +1,12 @@ use self::diag_session_control::DiagSession; -use super::{CautionLevel, CommandError, DTC, DiagCfg, ECUCommand, ProtocolError, ProtocolResult, ProtocolServer, Selectable}; -use crate::commapi::{comm_api::{ComServer, FilterType}, iface::{InterfaceConfig, InterfaceType, IsoTPInterface, PayloadFlag}}; +use super::{ + CautionLevel, CommandError, DiagCfg, ECUCommand, ProtocolError, ProtocolResult, ProtocolServer, + Selectable, DTC, +}; +use crate::commapi::{ + comm_api::{ComServer, FilterType}, + iface::{InterfaceConfig, InterfaceType, IsoTPInterface, PayloadFlag}, +}; use std::sync::atomic::Ordering::Relaxed; use std::{ sync::{ @@ -439,13 +445,19 @@ impl ProtocolServer for UDSECU { diag_cfg: DiagCfg, ) -> ProtocolResult { if interface_type != InterfaceType::IsoTp { - return Err(ProtocolError::CustomError("UDS Can only be executed over ISO-TP".into())) + return Err(ProtocolError::CustomError( + "UDS Can only be executed over ISO-TP".into(), + )); } let mut interface = IsoTPInterface::new(comm_server.clone_box())?; interface.setup(&interface_cfg)?; - interface.add_filter(FilterType::IsoTP{id: diag_cfg.recv_id, mask: 0xFFFF, fc: diag_cfg.send_id})?; + interface.add_filter(FilterType::IsoTP { + id: diag_cfg.recv_id, + mask: 0xFFFF, + fc: diag_cfg.send_id, + })?; let should_run = Arc::new(AtomicBool::new(true)); let should_run_t = should_run.clone(); diff --git a/app_rust/src/commapi/protocols/uds/read_data.rs b/app_rust/src/commapi/protocols/uds/read_data.rs index d45595a..3d46c60 100644 --- a/app_rust/src/commapi/protocols/uds/read_data.rs +++ b/app_rust/src/commapi/protocols/uds/read_data.rs @@ -2,12 +2,9 @@ use crate::commapi::protocols::{ProtocolResult, ProtocolServer}; use super::UDSECU; - - - pub fn read_variant_id(ecu: &UDSECU) -> ProtocolResult { let mut res = ecu.run_command(super::UDSCommand::ReadDataByID.into(), &[0xF1, 0x00])?; println!("{:02X?}", res); res.drain(0..2); Ok((res[0] as u32) << 24 | (res[1] as u32) << 16 | (res[2] as u32) << 8 | res[3] as u32) -} \ No newline at end of file +} diff --git a/app_rust/src/commapi/socket_can_api.rs b/app_rust/src/commapi/socket_can_api.rs index a8add2f..01fbeeb 100644 --- a/app_rust/src/commapi/socket_can_api.rs +++ b/app_rust/src/commapi/socket_can_api.rs @@ -1,9 +1,13 @@ -use std::{borrow::Borrow, sync::{Arc, PoisonError, RwLock}, time::Instant}; +use std::{ + borrow::Borrow, + sync::{Arc, PoisonError, RwLock}, + time::Instant, +}; -use crate::{commapi, main}; use crate::commapi::comm_api::{ CanFrame, ComServerError, DeviceCapabilities, FilterType, ISO15765Data, }; +use crate::{commapi, main}; use commapi::comm_api::ComServer; use socketcan::{CANError, CANFilter, CANSocket, ConstructionError}; use socketcan_isotp::{IsoTpOptions, IsoTpSocket}; @@ -20,8 +24,8 @@ pub struct SocketCanAPI { isotp_iface: Arc>>, can_filters: [Option; 10], isotp_in_use: bool, - req_iso_tp_settings: (u32, bool, bool) // Baud, ext CAN, ext Addressing - // TODO SocketCAN + req_iso_tp_settings: (u32, bool, bool), // Baud, ext CAN, ext Addressing + // TODO SocketCAN } impl std::fmt::Debug for SocketCanAPI { @@ -38,7 +42,6 @@ impl From for ComServerError { Self { err_code: 0xFF, err_desc: x.to_string(), - } } } @@ -48,7 +51,6 @@ impl From for ComServerError { Self { err_code: x.raw_os_error().unwrap_or_default() as u32, err_desc: x.to_string(), - } } } @@ -56,18 +58,14 @@ impl From for ComServerError { impl From for ComServerError { fn from(x: socketcan_isotp::Error) -> Self { match x { - socketcan_isotp::Error::LookupError { source } => { - ComServerError { - err_code: 2, - err_desc: source.to_string(), - } - } - socketcan_isotp::Error::IOError { source } => { - ComServerError { - err_code: 3, - err_desc: source.to_string(), - } - } + socketcan_isotp::Error::LookupError { source } => ComServerError { + err_code: 2, + err_desc: source.to_string(), + }, + socketcan_isotp::Error::IOError { source } => ComServerError { + err_code: 3, + err_desc: source.to_string(), + }, } } } @@ -80,44 +78,41 @@ impl SocketCanAPI { isotp_iface: Arc::new(RwLock::new(None)), can_filters: [None; 10], isotp_in_use: false, - req_iso_tp_settings: (0, false, false) + req_iso_tp_settings: (0, false, false), } } } impl SocketCanAPI { fn write_filters(&mut self) -> Result<(), ComServerError> { - let filters: Vec = self.can_filters.iter() + let filters: Vec = self + .can_filters + .iter() .filter(|x| x.is_some()) .map(|x| x.unwrap()) .collect(); - - self.run_can_iface(|s| { - s.set_filter(&filters).map_err(|x| x.into()) - })?; + + self.run_can_iface(|s| s.set_filter(&filters).map_err(|x| x.into()))?; Ok(()) } - fn run_can_iface Result>(&self, func: F) -> Result { + fn run_can_iface Result>( + &self, + func: F, + ) -> Result { match self.sockcan_iface.read() { - Ok(r) => { - match r.as_ref() { - Some(x) => func(x), - None => Err(ComServerError { - err_code: 99, - err_desc: "Can Socket was null!".into(), - - }) - } - }, - Err(_) => { - Err(ComServerError { + Ok(r) => match r.as_ref() { + Some(x) => func(x), + None => Err(ComServerError { err_code: 99, - err_desc: "Read guard failed on CAN Socket".into(), - - }) - } + err_desc: "Can Socket was null!".into(), + }), + }, + Err(_) => Err(ComServerError { + err_code: 99, + err_desc: "Read guard failed on CAN Socket".into(), + }), } } } @@ -171,27 +166,23 @@ impl ComServer for SocketCanAPI { if timeout_ms == 0 { let v_timeout = 10; match &self.run_can_iface(|x| x.read_frame().map_err(|x| x.into())) { - Ok(cf) => { - res.push(CanFrame::from(*cf)) - }, + Ok(cf) => res.push(CanFrame::from(*cf)), Err(e) => { - return Ok(res) // Return what we have + return Ok(res); // Return what we have } } if res.len() == max_msgs { - return Ok(res) + return Ok(res); } } else { let start = Instant::now(); while start.elapsed().as_millis() <= timeout_ms as u128 { match &self.run_can_iface(|x| x.read_frame().map_err(|x| x.into())) { - Ok(cf) => { - res.push(CanFrame::from(*cf)) - }, + Ok(cf) => res.push(CanFrame::from(*cf)), Err(_) => {} // Ignore error when using timeout } if res.len() == max_msgs { - return Ok(res) + return Ok(res); } } } @@ -209,13 +200,11 @@ impl ComServer for SocketCanAPI { i.write(&x.data)?; } Ok(data.len()) - }, - None => { - Err(ComServerError { - err_code: 4, - err_desc: "Socket CAN Interface null!".into(), - }) } + None => Err(ComServerError { + err_code: 4, + err_desc: "Socket CAN Interface null!".into(), + }), } } @@ -235,10 +224,9 @@ impl ComServer for SocketCanAPI { data: Vec::from(buf), pad_frame: false, ext_addressing: false, - }); if res.len() == max_msgs { - return Ok(res) + return Ok(res); } } Ok(res) @@ -251,19 +239,16 @@ impl ComServer for SocketCanAPI { data: Vec::from(buf), pad_frame: false, ext_addressing: false, - }) } } Ok(res) } - }, - None => { - Err(ComServerError { - err_code: 4, - err_desc: "Socket CAN Interface null!".into(), - }) } + None => Err(ComServerError { + err_code: 4, + err_desc: "Socket CAN Interface null!".into(), + }), } } @@ -320,27 +305,24 @@ impl ComServer for SocketCanAPI { Ok(()) } - fn add_can_filter( - &mut self, - f: FilterType) -> Result { - - // SocketCAN - let mut apply_mask = 0; + fn add_can_filter(&mut self, f: FilterType) -> Result { + // SocketCAN + let mut apply_mask = 0; let mut apply_id = 0; match f { - FilterType::Block{id, mask} => { - - }, - FilterType::Pass{id, mask} => { + FilterType::Block { id, mask } => {} + FilterType::Pass { id, mask } => { apply_id = id; apply_mask = mask; - }, - FilterType::IsoTP{ id, mask, fc } => { - return Err(ComServerError{ err_code: 99, err_desc: "Cannot apply a FlowControl filter to CAN".into()}) + } + FilterType::IsoTP { id, mask, fc } => { + return Err(ComServerError { + err_code: 99, + err_desc: "Cannot apply a FlowControl filter to CAN".into(), + }) } } - let f = CANFilter::new(apply_id, apply_mask)?; // Find a free ID let mut pos = 99; @@ -350,18 +332,19 @@ impl ComServer for SocketCanAPI { break; } } - if pos == 99 { // No free filters + if pos == 99 { + // No free filters return Err(ComServerError { err_code: 98, err_desc: "No free CAN Filters were found".into(), - }) + }); } self.can_filters[pos] = Some(f); // Now write the filters if let Err(e) = self.write_filters() { self.can_filters[pos] = None; // Unset if filter set failed! - return Err(ComServerError::from(e)) + return Err(ComServerError::from(e)); } // Set was OK! return result Ok(pos as u32) @@ -378,17 +361,20 @@ impl ComServer for SocketCanAPI { return Err(ComServerError { err_code: 1, err_desc: "Socket CAN only allows for 1 ISO-TP filter!".into(), - }) + }); } - if let FilterType::IsoTP{id, mask, fc} = f { + if let FilterType::IsoTP { id, mask, fc } = f { // Now try to setup the ISO-TP interface let iface = IsoTpSocket::open_with_opts(&self.iface, fc, id & mask, None, None, None)?; iface.set_nonblocking(true)?; // Request non blocking! *self.isotp_iface.write().unwrap() = Some(iface); Ok(1) } else { - Err(ComServerError{ err_code: 99, err_desc: "Cannot apply a pass/block filter to ISOTP".into()}) + Err(ComServerError { + err_code: 99, + err_desc: "Cannot apply a pass/block filter to ISOTP".into(), + }) } } diff --git a/app_rust/src/main.rs b/app_rust/src/main.rs index c656f4a..f7ca3d6 100644 --- a/app_rust/src/main.rs +++ b/app_rust/src/main.rs @@ -1,9 +1,9 @@ use iced::{Application, Settings}; mod cli_tests; mod commapi; -mod widgets; mod passthru; mod themes; +mod widgets; mod windows; use iced::window::Icon; diff --git a/app_rust/src/passthru.rs b/app_rust/src/passthru.rs index 5fdee4c..aee9fed 100644 --- a/app_rust/src/passthru.rs +++ b/app_rust/src/passthru.rs @@ -1,10 +1,10 @@ +use j2534_rust::FilterType::FLOW_CONTROL_FILTER; +use j2534_rust::*; use lazy_static::lazy_static; use libloading::Library; use serde::{Deserialize, Serialize}; use std::sync::{Arc, RwLock}; use std::{ffi::*, fmt}; -use j2534_rust::FilterType::FLOW_CONTROL_FILTER; -use j2534_rust::*; lazy_static! { pub static ref DRIVER: Arc>> = Arc::new(RwLock::new(None)); @@ -141,7 +141,7 @@ fn ret_res(res: i32, ret: T) -> Result { impl PassthruDrv { pub fn load_lib(path: String) -> std::result::Result { - let lib = unsafe {Library::new(path)? }; + let lib = unsafe { Library::new(path)? }; unsafe { let open_fn = *lib.get::(b"PassThruOpen\0")?.into_raw(); let close_fn = *lib.get::(b"PassThruClose\0")?.into_raw(); @@ -409,30 +409,26 @@ impl PassthruDrv { let mut filter_id: u32 = 0; let res = match flow_control.as_ref() { - None => { - unsafe { - (&self.start_filter_fn)( - channel_id, - tmp, - mask as *const PASSTHRU_MSG, - pattern as *const PASSTHRU_MSG, - std::ptr::null() as *const PASSTHRU_MSG, - &mut filter_id as *mut u32, - ) - } + None => unsafe { + (&self.start_filter_fn)( + channel_id, + tmp, + mask as *const PASSTHRU_MSG, + pattern as *const PASSTHRU_MSG, + std::ptr::null() as *const PASSTHRU_MSG, + &mut filter_id as *mut u32, + ) + }, + Some(fc) => unsafe { + (&self.start_filter_fn)( + channel_id, + tmp, + mask as *const PASSTHRU_MSG, + pattern as *const PASSTHRU_MSG, + fc as *const PASSTHRU_MSG, + &mut filter_id as *mut u32, + ) }, - Some(fc) => { - unsafe { - (&self.start_filter_fn)( - channel_id, - tmp, - mask as *const PASSTHRU_MSG, - pattern as *const PASSTHRU_MSG, - fc as *const PASSTHRU_MSG, - &mut filter_id as *mut u32, - ) - } - } }; ret_res(res, filter_id) } diff --git a/app_rust/src/themes/elements.rs b/app_rust/src/themes/elements.rs index a2117f1..a571838 100644 --- a/app_rust/src/themes/elements.rs +++ b/app_rust/src/themes/elements.rs @@ -89,7 +89,6 @@ impl button::StyleSheet for ButtonStyle { } } - pub struct ButtonTableStyle { color: Color, selected: bool, @@ -112,7 +111,6 @@ impl button::StyleSheet for ButtonTableStyle { border_width: 0f32, border_color: WHITE, text_color: self.color, - }, super::Style::Dark => button::Style { shadow_offset: Default::default(), @@ -132,7 +130,6 @@ impl button::StyleSheet for ButtonTableStyle { border_width: 0f32, border_color: WHITE, text_color: self.color, - }, super::Style::Dark => button::Style { shadow_offset: Default::default(), @@ -150,16 +147,23 @@ impl button::StyleSheet for ButtonTableStyle { match super::get_theme() { super::Style::Light => button::Style { shadow_offset: Default::default(), - background: if self.selected { GREY.into() } else { WHITE.into() }, + background: if self.selected { + GREY.into() + } else { + WHITE.into() + }, border_radius: 0f32, border_width: 0f32, border_color: WHITE, text_color: DARK_BG, - }, super::Style::Dark => button::Style { shadow_offset: Default::default(), - background: if self.selected { GREY.into() } else { DARK_BG.into() }, + background: if self.selected { + GREY.into() + } else { + DARK_BG.into() + }, border_radius: 0f32, border_width: 0f32, border_color: DARK_BG, @@ -181,7 +185,6 @@ impl button::StyleSheet for ButtonTableStyle { border_width: 0f32, border_color: WHITE, text_color: GREY, - }, super::Style::Dark => button::Style { shadow_offset: Default::default(), @@ -418,7 +421,7 @@ impl iced::checkbox::StyleSheet for CheckBox { border_radius: BUTTON_RADIUS, border_width: BUTTON_BORDER_WIDTH, border_color: super::WHITE, - } + }, } } @@ -437,7 +440,7 @@ impl iced::checkbox::StyleSheet for CheckBox { border_radius: BUTTON_RADIUS, border_width: BUTTON_BORDER_WIDTH, border_color: super::WHITE, - } + }, } } } diff --git a/app_rust/src/themes/images.rs b/app_rust/src/themes/images.rs index f0d5dbf..0e25650 100644 --- a/app_rust/src/themes/images.rs +++ b/app_rust/src/themes/images.rs @@ -20,7 +20,6 @@ pub const TRAY_ICON_DARK: &[u8] = include_bytes!("../../img/launcher_dark.png"); #[cfg(windows)] pub const TRAY_ICON_DARK: &[u8] = include_bytes!("..\\..\\img\\launcher_dark.png"); - pub fn get_launcher_image() -> iced::Image { match super::get_theme() { super::Style::Light => pix_to_iced_image(LAUNCHER_IMG_LIGHT), diff --git a/app_rust/src/themes/mod.rs b/app_rust/src/themes/mod.rs index fd82dd6..e69258c 100644 --- a/app_rust/src/themes/mod.rs +++ b/app_rust/src/themes/mod.rs @@ -1,9 +1,12 @@ pub mod elements; pub mod images; use crate::themes::elements::{ButtonStyle, DropDown, PBar}; -use iced::{Button, Checkbox, Color, Container, Element, PickList, ProgressBar, Radio, Text, button, pick_list}; -use std::ops::RangeInclusive; +use iced::{ + button, pick_list, Button, Checkbox, Color, Container, Element, PickList, ProgressBar, Radio, + Text, +}; use std::borrow::Cow; +use std::ops::RangeInclusive; use self::elements::{ButtonTableStyle, CheckBox, TextInput}; @@ -32,8 +35,6 @@ const WHITE: Color = Color { static mut CURR_THEME: Style = Style::Dark; - - static mut DEBUG: bool = false; pub fn set_debug(state: bool) { @@ -151,22 +152,17 @@ pub fn button_table<'a, T: Clone>( state: &'a mut button::State, text: &str, btn_type: ButtonType, - is_selected: bool + is_selected: bool, ) -> Button<'a, T> { let color = btn_type.get_colour(); - Button::new(state, Text::new(text)) - .style(ButtonTableStyle::new(color, is_selected)) + Button::new(state, Text::new(text)).style(ButtonTableStyle::new(color, is_selected)) } -pub fn checkbox( - is_checked: bool, - label: &str, - on_click: F -) -> Checkbox +pub fn checkbox(is_checked: bool, label: &str, on_click: F) -> Checkbox where -F: 'static + Fn(bool) -> T { - Checkbox::new(is_checked, label, on_click) - .style(CheckBox) + F: 'static + Fn(bool) -> T, +{ + Checkbox::new(is_checked, label, on_click).style(CheckBox) } pub fn picklist<'a, T, Msg>( diff --git a/app_rust/src/widgets/mod.rs b/app_rust/src/widgets/mod.rs index 790b2c4..13971b0 100644 --- a/app_rust/src/widgets/mod.rs +++ b/app_rust/src/widgets/mod.rs @@ -1 +1 @@ -pub mod table; \ No newline at end of file +pub mod table; diff --git a/app_rust/src/widgets/table.rs b/app_rust/src/widgets/table.rs index 99ffb86..ec828df 100644 --- a/app_rust/src/widgets/table.rs +++ b/app_rust/src/widgets/table.rs @@ -1,6 +1,6 @@ -use iced::{Column, Element, Length, Row, Scrollable, scrollable}; +use iced::{scrollable, Column, Element, Length, Row, Scrollable}; -use crate::themes::{ButtonType, TextType, button_table, text}; +use crate::themes::{button_table, text, ButtonType, TextType}; #[derive(Debug, Copy, Clone)] pub struct TableMsg(pub usize, pub usize); @@ -22,11 +22,17 @@ pub struct Table { scroll_sate: scrollable::State, default_text: String, selected_row: usize, - widths: Vec + widths: Vec, } impl Table { - pub fn new(header: Vec, matrix: Vec>, widths: Vec,selectable: bool, max_height: usize) -> Self { + pub fn new( + header: Vec, + matrix: Vec>, + widths: Vec, + selectable: bool, + max_height: usize, + ) -> Self { let mut tmp_matrix: Vec> = Vec::new(); if matrix.len() == 0 { return Self { @@ -38,8 +44,8 @@ impl Table { scroll_sate: scrollable::State::default(), default_text: "No data".into(), selected_row: 0, - widths - } + widths, + }; } assert!(header.len() == matrix[0].len()); @@ -54,15 +60,13 @@ impl Table { } } - for (x,s) in tmp_matrix.iter().enumerate() { + for (x, s) in tmp_matrix.iter().enumerate() { for (y, _) in s.iter().enumerate() { - states.push( - Position { - x, - y, - state: iced::button::State::default() - } - ) + states.push(Position { + x, + y, + state: iced::button::State::default(), + }) } } @@ -75,7 +79,7 @@ impl Table { scroll_sate: scrollable::State::default(), default_text: "No data".into(), selected_row: 0, - widths + widths, } } @@ -85,10 +89,9 @@ impl Table { } } - pub fn view(&mut self) -> Element{ - + pub fn view(&mut self) -> Element { if self.header_row.is_empty() || self.text_matrix.is_empty() { - return text(&self.default_text, TextType::Normal).into() + return text(&self.default_text, TextType::Normal).into(); } let mut row = Row::new(); @@ -108,9 +111,14 @@ impl Table { } let selected = self.selectable && self.selected_row == position.y; - let mut btn = button_table(&mut position.state, &self.text_matrix[position.x][position.y], ButtonType::Danger, selected) - .width(Length::Units(self.widths[position.x])); - + let mut btn = button_table( + &mut position.state, + &self.text_matrix[position.x][position.y], + ButtonType::Danger, + selected, + ) + .width(Length::Units(self.widths[position.x])); + if self.selectable { btn = btn.on_press(TableMsg(position.x, position.y)); } @@ -118,6 +126,11 @@ impl Table { } row = row.push(column); // Last column - scroll.push(row).padding(4).spacing(4).max_height(self.max_height as u32).into() + scroll + .push(row) + .padding(4) + .spacing(4) + .max_height(self.max_height as u32) + .into() } -} \ No newline at end of file +} diff --git a/app_rust/src/windows/cantracer.rs b/app_rust/src/windows/cantracer.rs index bd91279..8f7c978 100644 --- a/app_rust/src/windows/cantracer.rs +++ b/app_rust/src/windows/cantracer.rs @@ -1,8 +1,14 @@ -use crate::{commapi::{comm_api::{ComServer, FilterType}, iface::{CanbusInterface, IFACE_CFG, Interface, InterfaceConfig, InterfacePayload}}, themes::{TextType, checkbox, picklist, text}}; use crate::themes::{button_coloured, ButtonType}; use crate::windows::window::WindowMessage; -use iced::{pick_list, time}; +use crate::{ + commapi::{ + comm_api::{ComServer, FilterType}, + iface::{CanbusInterface, Interface, InterfaceConfig, InterfacePayload, IFACE_CFG}, + }, + themes::{checkbox, picklist, text, TextType}, +}; use iced::{button, Color, Column, Element, Length, Row, Scrollable, Subscription, Text}; +use iced::{pick_list, time}; use std::collections::HashMap; use std::time::Instant; @@ -33,7 +39,7 @@ pub struct CanTracer { #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct CanSpeed { pub baud: u32, - text: &'static str + text: &'static str, } impl std::fmt::Display for CanSpeed { @@ -43,27 +49,73 @@ impl std::fmt::Display for CanSpeed { } const CAN_SPEEDS: &[CanSpeed] = &[ - CanSpeed {baud: 5000, text: "5 kbit/s" }, - CanSpeed {baud: 10000, text: "10 kbit/s" }, - CanSpeed {baud: 20000, text: "20 kbit/s" }, - CanSpeed {baud: 25000, text: "25 kbit/s" }, - CanSpeed {baud: 33333, text: "33.3 kbit/s" }, - CanSpeed {baud: 50000, text: "50 kbit/s" }, - CanSpeed {baud: 80000, text: "80 kbit/s" }, - CanSpeed {baud: 83333, text: "83.3 kbit/s" }, - CanSpeed {baud: 100000, text: "100 kbit/s" }, - CanSpeed {baud: 125000, text: "125 kbit/s" }, - CanSpeed {baud: 200000, text: "200 kbit/s" }, - CanSpeed {baud: 250000, text: "250 kbit/s" }, - CanSpeed {baud: 500000, text: "500 kbit/s" }, - CanSpeed {baud: 1000000, text: "1 mbit/s" } + CanSpeed { + baud: 5000, + text: "5 kbit/s", + }, + CanSpeed { + baud: 10000, + text: "10 kbit/s", + }, + CanSpeed { + baud: 20000, + text: "20 kbit/s", + }, + CanSpeed { + baud: 25000, + text: "25 kbit/s", + }, + CanSpeed { + baud: 33333, + text: "33.3 kbit/s", + }, + CanSpeed { + baud: 50000, + text: "50 kbit/s", + }, + CanSpeed { + baud: 80000, + text: "80 kbit/s", + }, + CanSpeed { + baud: 83333, + text: "83.3 kbit/s", + }, + CanSpeed { + baud: 100000, + text: "100 kbit/s", + }, + CanSpeed { + baud: 125000, + text: "125 kbit/s", + }, + CanSpeed { + baud: 200000, + text: "200 kbit/s", + }, + CanSpeed { + baud: 250000, + text: "250 kbit/s", + }, + CanSpeed { + baud: 500000, + text: "500 kbit/s", + }, + CanSpeed { + baud: 1000000, + text: "1 mbit/s", + }, ]; impl<'a> CanTracer { pub(crate) fn new(server: Box) -> Self { Self { can_spd_state: Default::default(), - can_spd: CAN_SPEEDS.iter().find(|x| x.baud == 500000).unwrap().clone(), // 500kbps + can_spd: CAN_SPEEDS + .iter() + .find(|x| x.baud == 500000) + .unwrap() + .clone(), // 500kbps can_interface: CanbusInterface::new_raw(server), btn_state: Default::default(), can_queue: HashMap::new(), @@ -97,21 +149,22 @@ impl<'a> CanTracer { cfg.add_param(IFACE_CFG::BAUDRATE, self.can_spd.baud); cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, self.use_ext_can as u32); self.can_interface.setup(&cfg) - }{ + } { self.status_text = format!("Error opening CAN Interface {}", e) } else { self.is_connected = true; - if let Err(e) = - self.can_interface.add_filter(FilterType::Pass{id: 0x0000, mask: 0x0000}) - { + if let Err(e) = self.can_interface.add_filter(FilterType::Pass { + id: 0x0000, + mask: 0x0000, + }) { self.status_text = format!("Error setting CAN Filter {}", e) } else if let Err(e) = self.can_interface.send_data( - &[InterfacePayload { + &[InterfacePayload { id: 0x07DF, data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], flags: vec![], }], - 0 + 0, ) { self.status_text = format!("Error sending wake-up packet {}", e) } @@ -140,10 +193,8 @@ impl<'a> CanTracer { self.close_can(); self.open_can(); } - }, - TracerMessage::SelectBaud(b) => { - self.can_spd = *b } + TracerMessage::SelectBaud(b) => self.can_spd = *b, } None } @@ -162,17 +213,26 @@ impl<'a> CanTracer { } .on_press(TracerMessage::ToggleCan); - let speed_selector = picklist(&mut self.can_spd_state, CAN_SPEEDS, Some(self.can_spd), TracerMessage::SelectBaud); + let speed_selector = picklist( + &mut self.can_spd_state, + CAN_SPEEDS, + Some(self.can_spd), + TracerMessage::SelectBaud, + ); let check = self.is_binary_fmt; - let ext_toggle = checkbox(self.use_ext_can, "Use Extended CAN", TracerMessage::ToggleExt); + let ext_toggle = checkbox( + self.use_ext_can, + "Use Extended CAN", + TracerMessage::ToggleExt, + ); let mut r = Row::new(); if !self.is_connected { - r = r.push(text("CAN Speed: ", TextType::Normal)) + r = r + .push(text("CAN Speed: ", TextType::Normal)) .push(speed_selector) - } Column::new() diff --git a/app_rust/src/windows/diag_home.rs b/app_rust/src/windows/diag_home.rs index c346659..21f26f8 100644 --- a/app_rust/src/windows/diag_home.rs +++ b/app_rust/src/windows/diag_home.rs @@ -2,7 +2,7 @@ use super::{ diag_manual::{DiagManual, DiagManualMessage}, diag_scanner::{DiagScanner, DiagScannerMessage}, }; -use crate::commapi::comm_api::{ComServer}; +use crate::commapi::comm_api::ComServer; use crate::themes::{button_outlined, text, title_text, ButtonType, TextType, TitleSize}; use iced::{Align, Column, Element, Length, Row, Space, Subscription}; use serde::{Deserialize, Serialize}; diff --git a/app_rust/src/windows/diag_manual.rs b/app_rust/src/windows/diag_manual.rs index 1cf6757..2051e49 100644 --- a/app_rust/src/windows/diag_manual.rs +++ b/app_rust/src/windows/diag_manual.rs @@ -6,8 +6,7 @@ use iced::{Align, Column, Element, Length, Row, Subscription}; use crate::{ commapi::comm_api::{ComServer, ISO15765Config}, themes::{ - button_outlined, picklist, text, text_input, title_text, ButtonType, - TextType, TitleSize, + button_outlined, picklist, text, text_input, title_text, ButtonType, TextType, TitleSize, }, }; @@ -121,7 +120,6 @@ impl DiagManual { if let Ok(mut file) = File::open(f_path) { let mut str = "".into(); if file.read_to_string(&mut str).is_ok() { - if let Ok(car) = serde_json::from_str::(&str) { println!("Car save opened!"); self.curr_ecu = None; @@ -264,7 +262,7 @@ impl DiagManual { return; } - if let SessionType::JSON(_,_) = &session_type { + if let SessionType::JSON(_, _) = &session_type { match DiagSession::new(&session_type, self.server.clone(), None) { Ok(session) => self.session = Some(session), Err(e) => self.status = format!("Error init diag session: {}", e.get_description()), @@ -277,7 +275,7 @@ impl DiagManual { block_size: Self::decode_string_int(&self.str_bs).unwrap(), sep_time: Self::decode_string_int(&self.str_sep).unwrap(), use_ext_isotp: false, - use_ext_can: false + use_ext_can: false, }; match DiagSession::new(&session_type, self.server.clone(), Some(cfg)) { Ok(session) => self.session = Some(session), @@ -291,7 +289,7 @@ impl DiagManual { block_size: ecu.block_size, sep_time: ecu.sep_time_ms, use_ext_isotp: false, - use_ext_can: false + use_ext_can: false, }; match DiagSession::new(&session_type, self.server.clone(), Some(cfg)) { Ok(session) => self.session = Some(session), @@ -311,11 +309,18 @@ impl DiagManual { .spacing(20) .align_items(Align::Center) .width(Length::Fill) - .push(title_text("Load a save file or ECU JSON file to get started", TitleSize::P3)); + .push(title_text( + "Load a save file or ECU JSON file to get started", + TitleSize::P3, + )); view = view.push( - button_outlined(&mut self.btn_state, "Load save / ECU JSON file", ButtonType::Success) - .on_press(DiagManualMessage::LaunchFileBrowser), + button_outlined( + &mut self.btn_state, + "Load save / ECU JSON file", + ButtonType::Success, + ) + .on_press(DiagManualMessage::LaunchFileBrowser), ); if let Some(car) = &self.car { diff --git a/app_rust/src/windows/diag_scanner.rs b/app_rust/src/windows/diag_scanner.rs index 2283bc8..abf8017 100644 --- a/app_rust/src/windows/diag_scanner.rs +++ b/app_rust/src/windows/diag_scanner.rs @@ -6,9 +6,20 @@ use commapi::{ }; use iced::{Align, Column, Element, Length, Row, Space}; -use crate::{commapi::{self, comm_api::{ComServer, FilterType}, iface::{BufferType, DynamicInterface, IFACE_CFG, Interface, InterfaceConfig, InterfacePayload, InterfaceType, PayloadFlag}, protocols::{DiagCfg, kwp2000::read_ecu_identification::read_dcx_mmc_id}}, themes::{ +use crate::{ + commapi::{ + self, + comm_api::{ComServer, FilterType}, + iface::{ + BufferType, DynamicInterface, Interface, InterfaceConfig, InterfacePayload, + InterfaceType, PayloadFlag, IFACE_CFG, + }, + protocols::{kwp2000::read_ecu_identification::read_dcx_mmc_id, DiagCfg}, + }, + themes::{ button_coloured, button_outlined, progress_bar, text, title_text, ButtonType, TextType, - }}; + }, +}; use super::diag_home::{ECUDiagSettings, VehicleECUList}; @@ -69,7 +80,7 @@ impl DiagScanner { let mut cfg = InterfaceConfig::new(); cfg.add_param(IFACE_CFG::BAUDRATE, 500_000); cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, 0); - match DynamicInterface::new( &self.adapter, InterfaceType::Can, &cfg) { + match DynamicInterface::new(&self.adapter, InterfaceType::Can, &cfg) { Ok(dyn_iface) => self.activate_interface = dyn_iface, Err(e) => { self.status = format!("Could open CAN Interface ({})", e); @@ -79,16 +90,22 @@ impl DiagScanner { // Opening interface was OK match self.activate_interface.exec(|can| { - can.add_filter(FilterType::Pass{id: 0x0000, mask: 0x0000}) + can.add_filter(FilterType::Pass { + id: 0x0000, + mask: 0x0000, + }) }) { Ok(f_idx) => { // Send the OBD-II get VIN request via CAN. This should wake up the OBD-II port's CAN Iface // on most cars - if let Err(e) = self.activate_interface.send_data( - &[InterfacePayload::new(0x07Df, &[0x09, 0x02])], 0 - ) { + if let Err(e) = self + .activate_interface + .send_data(&[InterfacePayload::new(0x07Df, &[0x09, 0x02])], 0) + { self.status = format!("Could not send wake up test packet: {}", e); - self.activate_interface.close().expect("WTF. Could not close CAN Interface!?"); + self.activate_interface + .close() + .expect("WTF. Could not close CAN Interface!?"); } else { std::thread::sleep(std::time::Duration::from_millis(500)); self.filter_idx = f_idx; @@ -100,7 +117,9 @@ impl DiagScanner { } Err(e) => { // STOP THE CAN INTERFACE - self.activate_interface.close().expect("WTF. Could not close CAN Interface!?"); + self.activate_interface + .close() + .expect("WTF. Could not close CAN Interface!?"); self.status = format!("Could not set CAN filter ({})", e) } } @@ -130,7 +149,7 @@ impl DiagScanner { match DynamicInterface::new(&self.adapter, InterfaceType::Can, &cfg) { Ok(iface) => { self.activate_interface = iface; - return Some(DiagScannerMessage::ScanPoll) // Begin the CAN interrogation (Stage 2) + return Some(DiagScannerMessage::ScanPoll); // Begin the CAN interrogation (Stage 2) } Err(e) => { self.status = format!("Error opening new CAN Interface!: {}", e.err_desc); @@ -189,7 +208,11 @@ impl DiagScanner { if self.clock.elapsed().as_millis() >= 10000 { Some(DiagScannerMessage::IncrementStage) } else { - for frame in &self.activate_interface.recv_data(10000, 0).unwrap_or_default() { + for frame in &self + .activate_interface + .recv_data(10000, 0) + .unwrap_or_default() + { self.can_traffic_id_list.insert(frame.id, true); } Some(DiagScannerMessage::ScanPoll) // Keep polling @@ -206,15 +229,22 @@ impl DiagScanner { // Send a fake ISO-TP first frame. Tell the potential ECU we are sending 16 bytes to it. If it uses ISO-TP, it'll send back a // flow control message back to OVD self.activate_interface.send_data( - &[InterfacePayload::new(self.curr_scan_id, &[0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])], - 0 + &[InterfacePayload::new( + self.curr_scan_id, + &[0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + )], + 0, ); // Reset the timer self.clock = Instant::now(); Some(DiagScannerMessage::ScanPoll) } else { // Keep scanning for response messages - for frame in &self.activate_interface.recv_data(10000, 0).unwrap_or_default() { + for frame in &self + .activate_interface + .recv_data(10000, 0) + .unwrap_or_default() + { if self.can_traffic_id_list.get(&frame.id).is_none() { // Its a new frame we haven't seen before! let payload = &frame.data; @@ -245,15 +275,23 @@ impl DiagScanner { .stage2_results .get(&keys[self.curr_scan_id as usize]) .unwrap(); - - match self.activate_interface.add_filter(FilterType::Pass{id: filter_id[0], mask: 0xFFFF}) { + + match self.activate_interface.add_filter(FilterType::Pass { + id: filter_id[0], + mask: 0xFFFF, + }) { Ok(f_id) => self.filter_idx = f_id, - Err(e) => {todo!("CAN Filter setup failure handling in stage 3: {}", e)} + Err(e) => { + todo!("CAN Filter setup failure handling in stage 3: {}", e) + } } self.activate_interface.send_data( - &[InterfacePayload::new(keys[self.curr_scan_id as usize], &[0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])], - 0 + &[InterfacePayload::new( + keys[self.curr_scan_id as usize], + &[0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + )], + 0, ); self.clock = Instant::now(); self.curr_scan_id += 1; // For next time @@ -261,7 +299,11 @@ impl DiagScanner { } else { let keys: Vec = self.stage2_results.keys().copied().collect(); // Scanning current CAN ID entries - for frame in &self.activate_interface.recv_data(10000, 0).unwrap_or_default() { + for frame in &self + .activate_interface + .recv_data(10000, 0) + .unwrap_or_default() + { let payload = &frame.data; if payload[0] == 0x30 && payload.len() == 8 { // Not a false positive! We can add the Config to list! @@ -272,7 +314,7 @@ impl DiagScanner { block_size: payload[1] as u32, sep_time: payload[2] as u32, use_ext_isotp: false, - use_ext_can: false + use_ext_can: false, }) } } @@ -304,7 +346,6 @@ impl DiagScanner { send_id: ecu.send_id, recv_id: ecu.recv_id, global_id: None, - }; let mut ecu_res = ECUDiagSettings { @@ -322,12 +363,11 @@ impl DiagScanner { InterfaceType::IsoTp, cfg, Some(vec![PayloadFlag::ISOTP_PAD_FRAME]), - diag_cfg - + diag_cfg, ) { Ok(mut s) => { if let Ok(id) = read_dcx_mmc_id(&s) { - ecu_res.name = format!("ECU Part number: {}",id.part_number); + ecu_res.name = format!("ECU Part number: {}", id.part_number); println!("ECU 0x{:04X} supports KWP2000!", ecu.send_id); ecu_res.kwp_support = true; } @@ -359,17 +399,15 @@ impl DiagScanner { send_id: ecu.send_id, recv_id: ecu.recv_id, global_id: None, - }; - // Interrogate the ECU with extended diagnostic session match UDSECU::start_diag_session( &self.adapter, InterfaceType::IsoTp, cfg, Some(vec![PayloadFlag::ISOTP_PAD_FRAME]), - diag_cfg + diag_cfg, ) { Ok(mut s) => { // TODO find a UDS only CMD to test with diff --git a/app_rust/src/windows/diag_session/json_session.rs b/app_rust/src/windows/diag_session/json_session.rs index 26970fd..01052d8 100644 --- a/app_rust/src/windows/diag_session/json_session.rs +++ b/app_rust/src/windows/diag_session/json_session.rs @@ -1,13 +1,27 @@ +use crate::commapi::{ + iface::{InterfaceConfig, InterfaceType, PayloadFlag, IFACE_CFG}, + protocols::{kwp2000::read_ecu_identification, DiagCfg}, +}; +use common::schema::{ + diag::{dtc::ECUDTC, service::Service}, + variant::{ECUVariantDefinition, ECUVariantPattern}, + ConType, Connection, OvdECU, +}; use core::panic; +use iced::{time, Align, Column, Length, Row, Subscription}; use std::{cell::RefCell, time::Instant, vec}; -use crate::commapi::{iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, kwp2000::read_ecu_identification}}; -use common::schema::{ConType, Connection, OvdECU, diag::{dtc::ECUDTC, service::{Service}}, variant::{ECUVariantDefinition, ECUVariantPattern}}; -use iced::{Align, Column, Length, Row, Subscription, time}; -use crate::{commapi::{comm_api::{ComServer}, protocols::{DTCState, DiagProtocol, DiagServer, ProtocolResult}}, themes::{ - button_coloured, button_outlined, picklist, text, text_input, - title_text, ButtonType, TextType, - }, widgets::table::{Table, TableMsg}}; +use crate::{ + commapi::{ + comm_api::ComServer, + protocols::{DTCState, DiagProtocol, DiagServer, ProtocolResult}, + }, + themes::{ + button_coloured, button_outlined, picklist, text, text_input, title_text, ButtonType, + TextType, + }, + widgets::table::{Table, TableMsg}, +}; use super::{ log_view::{LogType, LogView}, @@ -30,7 +44,7 @@ pub enum JsonDiagSessionMsg { Selector(SelectorMsg), LoopRead(Instant), Navigate(TargetPage), - Select(usize, usize, usize) + Select(usize, usize, usize), } impl From for JsonDiagSessionMsg { @@ -42,10 +56,8 @@ impl From for JsonDiagSessionMsg { impl DiagMessageTrait for JsonDiagSessionMsg { fn is_back(&self) -> bool { match self { - JsonDiagSessionMsg::Navigate(s) => { - s == &TargetPage::Home - } - _ => false + JsonDiagSessionMsg::Navigate(s) => s == &TargetPage::Home, + _ => false, } } } @@ -57,7 +69,7 @@ pub struct DisplayableDTC { desc: String, state: DTCState, mil_on: bool, - envs: Vec<(String, String)> // Key, Value + envs: Vec<(String, String)>, // Key, Value } #[derive(Debug, Clone, PartialEq, Eq, Copy)] @@ -65,7 +77,7 @@ pub enum TargetPage { Home, Main, ECUInfo, - Error + Error, } #[derive(Debug, Clone)] @@ -80,32 +92,37 @@ pub struct JsonDiagSession { service_selector: ServiceSelector, looping_text: String, looping_service: Option, // Allow only read-only services to be loop read - logged_dtcs: Vec, // DTCs stored on ECU, + logged_dtcs: Vec, // DTCs stored on ECU, btn1: iced::button::State, btn2: iced::button::State, btn3: iced::button::State, page_state: TargetPage, scroll_state1: iced::scrollable::State, scroll_state2: iced::scrollable::State, - tables: Vec + tables: Vec
, } impl JsonDiagSession { pub fn new( comm_server: Box, ecu_data: OvdECU, - connection_settings: Connection + connection_settings: Connection, ) -> SessionResult { let diag_server_type = match connection_settings.server_type { common::schema::ServerType::UDS => DiagProtocol::UDS, - common::schema::ServerType::KWP2000 => DiagProtocol::KWP2000 + common::schema::ServerType::KWP2000 => DiagProtocol::KWP2000, }; println!("Detect. ECU uses {:?}", diag_server_type); // TODO K-Line KWP2000 // For now, Diag server ONLY supports ISO-TP, not LIN! let create_server = match connection_settings.connection_type { - ConType::ISOTP { blocksize, st_min, ext_isotp_addr, ext_can_addr } => { + ConType::ISOTP { + blocksize, + st_min, + ext_isotp_addr, + ext_can_addr, + } => { let mut cfg = InterfaceConfig::new(); cfg.add_param(IFACE_CFG::BAUDRATE, connection_settings.baud); cfg.add_param(IFACE_CFG::EXT_CAN_ADDR, ext_can_addr as u32); @@ -120,40 +137,59 @@ impl JsonDiagSession { }; let tx_flags = vec![PayloadFlag::ISOTP_PAD_FRAME]; - DiagServer::new(diag_server_type, &comm_server, InterfaceType::IsoTp, cfg, Some(tx_flags), diag_cfg) - }, - ConType::LIN { .. } => return Err(SessionError::Other("K-Line is not implemented at this time".into())) + DiagServer::new( + diag_server_type, + &comm_server, + InterfaceType::IsoTp, + cfg, + Some(tx_flags), + diag_cfg, + ) + } + ConType::LIN { .. } => { + return Err(SessionError::Other( + "K-Line is not implemented at this time".into(), + )) + } }; - match create_server { Ok(server) => { println!("Server started"); let variant = server.get_variant_id()? as u32; let mut unknown_variant = false; - let ecu_varient = ecu_data.variants.clone().into_iter().find(|x| { - x.clone() - .patterns - .into_iter() - .any(|p| p.vendor_id == variant) - }).unwrap_or_else(|| { - eprintln!("WARNING. Unknown ECU Variant!"); - unknown_variant = true; - ecu_data.variants[0].clone() - }); + let ecu_varient = ecu_data + .variants + .clone() + .into_iter() + .find(|x| { + x.clone() + .patterns + .into_iter() + .any(|p| p.vendor_id == variant) + }) + .unwrap_or_else(|| { + eprintln!("WARNING. Unknown ECU Variant!"); + unknown_variant = true; + ecu_data.variants[0].clone() + }); let pattern = &ecu_varient .patterns .iter() .find(|x| x.vendor_id == variant) .unwrap() .clone(); - println!("ECU Variant: {} (Vendor: {})", ecu_varient.name, pattern.vendor); + println!( + "ECU Variant: {} (Vendor: {})", + ecu_varient.name, pattern.vendor + ); - let read_functions: Vec = ecu_varient.downloads + let read_functions: Vec = ecu_varient + .downloads .iter() .clone() .map(|s| ServiceRef { - inner: RefCell::new(s.clone()) + inner: RefCell::new(s.clone()), }) .collect(); @@ -182,7 +218,7 @@ impl JsonDiagSession { page_state: TargetPage::Main, scroll_state1: iced::scrollable::State::default(), scroll_state2: iced::scrollable::State::default(), - tables: vec![Table::default(); MAX_TABLES] + tables: vec![Table::default(); MAX_TABLES], }) } Err(e) => { @@ -199,11 +235,13 @@ impl JsonDiagSession { .push( button_outlined(&mut self.btn1, "ECU Information", ButtonType::Primary) .on_press(JsonDiagSessionMsg::ReadInfo), - ).width(Length::FillPortion(1)) + ) + .width(Length::FillPortion(1)) .push( button_outlined(&mut self.btn2, "Read errors", ButtonType::Primary) .on_press(JsonDiagSessionMsg::ReadErrors), - ).width(Length::FillPortion(1)); + ) + .width(Length::FillPortion(1)); btn_view = btn_view.push( self.service_selector .view() @@ -256,22 +294,26 @@ impl JsonDiagSession { .align_items(Align::Center) .push(title_text("ECU Error view", crate::themes::TitleSize::P3)); - // Clear btn - let read_btn = button_outlined(&mut self.btn1, "Read errors", ButtonType::Primary).on_press(JsonDiagSessionMsg::ReadErrors); - + let read_btn = button_outlined(&mut self.btn1, "Read errors", ButtonType::Primary) + .on_press(JsonDiagSessionMsg::ReadErrors); let mut clear_btn = button_outlined(&mut self.btn2, "Clear errors", ButtonType::Primary); if self.logged_dtcs.len() > 0 { clear_btn = clear_btn.on_press(JsonDiagSessionMsg::ClearErrors); }; - let btn_row = Row::new().padding(8).spacing(8) - .push(read_btn) + let btn_row = Row::new() + .padding(8) + .spacing(8) + .push(read_btn) .push(clear_btn) .push(iced::Space::with_width(Length::Fill)) - .push(button_outlined(&mut self.btn3, "Back", ButtonType::Primary).on_press(JsonDiagSessionMsg::Navigate(TargetPage::Main))); - + .push( + button_outlined(&mut self.btn3, "Back", ButtonType::Primary) + .on_press(JsonDiagSessionMsg::Navigate(TargetPage::Main)), + ); + // Like Vediamo. 2 tables. 1 with error list, 1 with env data for current selected DTC content = content.push(header); @@ -283,15 +325,22 @@ impl JsonDiagSession { for (id, table) in self.tables.iter_mut().enumerate() { match id { TABLE_DTC => { - content = content.push(table.view().map(|x| { - JsonDiagSessionMsg::Select(TABLE_DTC, x.0, x.1) - })); - }, + content = content.push( + table + .view() + .map(|x| JsonDiagSessionMsg::Select(TABLE_DTC, x.0, x.1)), + ); + } ENV_TABLE => { - content = content.push(title_text("Freeze frame data", crate::themes::TitleSize::P4)); - content = content.push(table.view().map(|x| { - JsonDiagSessionMsg::Select(ENV_TABLE, x.0, x.1) - })); + content = content.push(title_text( + "Freeze frame data", + crate::themes::TitleSize::P4, + )); + content = content.push( + table + .view() + .map(|x| JsonDiagSessionMsg::Select(ENV_TABLE, x.0, x.1)), + ); } _ => {} } @@ -303,7 +352,6 @@ impl JsonDiagSession { } } - pub fn draw_info_ui(&mut self) -> iced::Element { let content = Column::new() .padding(8) @@ -316,20 +364,27 @@ impl JsonDiagSession { .align_items(Align::Center) .push(title_text("ECU Info view", crate::themes::TitleSize::P3)); - let btn_row = Row::new().padding(8).spacing(8) + let btn_row = Row::new() + .padding(8) + .spacing(8) .push(iced::Space::with_width(Length::Fill)) - .push(button_outlined(&mut self.btn1, "Back", ButtonType::Primary).on_press(JsonDiagSessionMsg::Navigate(TargetPage::Main))); - - + .push( + button_outlined(&mut self.btn1, "Back", ButtonType::Primary) + .on_press(JsonDiagSessionMsg::Navigate(TargetPage::Main)), + ); + content - .push(header) - .push(btn_row) - .push(self.tables[INFO_TABLE_ID].view().map(|_| JsonDiagSessionMsg::Select(INFO_TABLE_ID, 0, 0))) - .into() + .push(header) + .push(btn_row) + .push( + self.tables[INFO_TABLE_ID] + .view() + .map(|_| JsonDiagSessionMsg::Select(INFO_TABLE_ID, 0, 0)), + ) + .into() } } - impl SessionTrait for JsonDiagSession { type msg = JsonDiagSessionMsg; fn view(&mut self) -> iced::Element { @@ -337,16 +392,15 @@ impl SessionTrait for JsonDiagSession { TargetPage::Main => self.draw_main_ui(), TargetPage::Error => self.draw_error_ui(), TargetPage::ECUInfo => self.draw_info_ui(), - _ => panic!("????") - }.into() + _ => panic!("????"), + } + .into() } fn update(&mut self, msg: &Self::msg) -> Option { //self.log_view.clear_logs(); match msg { - JsonDiagSessionMsg::Navigate(target) => { - self.page_state = *target - } + JsonDiagSessionMsg::Navigate(target) => self.page_state = *target, JsonDiagSessionMsg::ReadInfo => { let header: Vec = vec!["".into(), "".into()]; let mut params: Vec> = Vec::new(); @@ -355,12 +409,16 @@ impl SessionTrait for JsonDiagSession { params.push(vec!["Software".into(), self.ecu_data.name.clone()]); params.push(vec!["Manufacture".into(), self.pattern.vendor.clone()]); if let Some(kwp) = self.server.into_kwp() { - - if let Ok(res) = read_ecu_identification::read_dcx_mmc_id(kwp) { params.push(vec!["Part number".into(), res.part_number.clone()]); - params.push(vec!["Hardware version".into(), res.hardware_version.clone()]); - params.push(vec!["Software version".into(), res.software_version.clone()]); + params.push(vec![ + "Hardware version".into(), + res.hardware_version.clone(), + ]); + params.push(vec![ + "Software version".into(), + res.software_version.clone(), + ]); } else { params.push(vec!["Part number".into(), "Unknown".into()]); params.push(vec!["Hardware version".into(), "Unknown".into()]); @@ -368,74 +426,105 @@ impl SessionTrait for JsonDiagSession { } if let Ok(res) = read_ecu_identification::read_dcs_id(kwp) { - params.push(vec!["Hardware build date (WW/YY)".into(), res.hardware_build_date.clone()]); - params.push(vec!["Software build date (WW/YY)".into(), res.software_written_date.clone()]); - params.push(vec!["Production date (DD/MM/YY)".into(), res.production_date.clone()]); + params.push(vec![ + "Hardware build date (WW/YY)".into(), + res.hardware_build_date.clone(), + ]); + params.push(vec![ + "Software build date (WW/YY)".into(), + res.software_written_date.clone(), + ]); + params.push(vec![ + "Production date (DD/MM/YY)".into(), + res.production_date.clone(), + ]); } else { params.push(vec!["Hardware build date (WW/YY)".into(), "Unknown".into()]); params.push(vec!["Software build date (WW/YY)".into(), "Unknown".into()]); params.push(vec!["Production date (DD/MM/YY)".into(), "Unknown".into()]); } } - self.tables[INFO_TABLE_ID] = Table::new(header, params, vec![400, 400], false, 900); - return Some(JsonDiagSessionMsg::Navigate(TargetPage::ECUInfo)) - - - - + return Some(JsonDiagSessionMsg::Navigate(TargetPage::ECUInfo)); } JsonDiagSessionMsg::ReadErrors => match self.server.read_errors() { Ok(res) => { let dtc_list = self.ecu_data.errors.clone(); - self.logged_dtcs = res.iter().map(|raw_dtc| { - let ecu_dtc = dtc_list.clone().into_iter() - .find(|x| x.error_name.ends_with(&raw_dtc.error)) - .unwrap_or(ECUDTC { - error_name: raw_dtc.error.clone(), - summary: "UNKNOWN ERROR".into(), - description: "UNKNOWN DTC".into(), + self.logged_dtcs = res + .iter() + .map(|raw_dtc| { + let ecu_dtc = dtc_list + .clone() + .into_iter() + .find(|x| x.error_name.ends_with(&raw_dtc.error)) + .unwrap_or(ECUDTC { + error_name: raw_dtc.error.clone(), + summary: "UNKNOWN ERROR".into(), + description: "UNKNOWN DTC".into(), + envs: Vec::new(), + }); + let mut res = DisplayableDTC { + code: ecu_dtc.error_name.clone(), + summary: ecu_dtc.summary.clone(), + desc: ecu_dtc.description.clone(), + state: raw_dtc.state, + mil_on: raw_dtc.check_engine_on, envs: Vec::new(), - }); - let mut res = DisplayableDTC { - code: ecu_dtc.error_name.clone(), - summary: ecu_dtc.summary.clone(), - desc: ecu_dtc.description.clone(), - state: raw_dtc.state, - mil_on: raw_dtc.check_engine_on, - envs: Vec::new(), - }; - - if ecu_dtc.envs.len() > 0 { // If parsable freeze frame data exists, then read it - if let Ok(args) = self.server.get_dtc_env_data(raw_dtc) { - for e in &ecu_dtc.envs { - match e.decode_value_to_string(&args) { - Ok(s) => res.envs.push((e.name.clone(), s)), - Err(err) => { - eprintln!("Warning could not decode param: {:?}", err) + }; + + if ecu_dtc.envs.len() > 0 { + // If parsable freeze frame data exists, then read it + if let Ok(args) = self.server.get_dtc_env_data(raw_dtc) { + for e in &ecu_dtc.envs { + match e.decode_value_to_string(&args) { + Ok(s) => res.envs.push((e.name.clone(), s)), + Err(err) => { + eprintln!( + "Warning could not decode param: {:?}", + err + ) + } } } } } - } - res - }).collect(); - let entries: Vec> = self.logged_dtcs.iter().map(|dtc| { - vec![dtc.code.clone(), dtc.desc.clone(), format!("{:?}", dtc.state), if dtc.mil_on { "YES".into() } else {"NO ".into()}] - }).collect(); - + res + }) + .collect(); + let entries: Vec> = self + .logged_dtcs + .iter() + .map(|dtc| { + vec![ + dtc.code.clone(), + dtc.desc.clone(), + format!("{:?}", dtc.state), + if dtc.mil_on { + "YES".into() + } else { + "NO ".into() + }, + ] + }) + .collect(); + let table = Table::new( - vec!["Error".into(), "Description".into(), "State".into(), "MIL on".into()], - entries, - vec![200, 600, 150, 100], - true, - 400 + vec![ + "Error".into(), + "Description".into(), + "State".into(), + "MIL on".into(), + ], + entries, + vec![200, 600, 150, 100], + true, + 400, ); self.tables[TABLE_DTC] = table; if self.page_state == TargetPage::Main { // Goto DTC View! - return Some(JsonDiagSessionMsg::Navigate(TargetPage::Error)) + return Some(JsonDiagSessionMsg::Navigate(TargetPage::Error)); } } Err(e) => { @@ -446,18 +535,16 @@ impl SessionTrait for JsonDiagSession { self.logged_dtcs.clear(); } }, - JsonDiagSessionMsg::ClearErrors => { - match self.server.clear_errors() { - Ok(_) => { - self.log_view.add_msg("Clear ECU Errors OK!", LogType::Info); - self.logged_dtcs.clear(); - }, - Err(e) => self.log_view.add_msg( - format!("Error clearing ECU Errors: {}", e.get_text()), - LogType::Error, - ), + JsonDiagSessionMsg::ClearErrors => match self.server.clear_errors() { + Ok(_) => { + self.log_view.add_msg("Clear ECU Errors OK!", LogType::Info); + self.logged_dtcs.clear(); } - } + Err(e) => self.log_view.add_msg( + format!("Error clearing ECU Errors: {}", e.get_text()), + LogType::Error, + ), + }, JsonDiagSessionMsg::Selector(s) => match s { SelectorMsg::PickLoopService(l) => self.looping_service = Some(l.clone()), @@ -508,8 +595,7 @@ impl SessionTrait for JsonDiagSession { for (name, v) in &self.logged_dtcs[*y].envs { values.push(vec![name.clone(), v.clone()]); } - self.tables[ENV_TABLE] = Table::new(header, values, vec![400, 200],false, 300); - + self.tables[ENV_TABLE] = Table::new(header, values, vec![400, 200], false, 300); } } } diff --git a/app_rust/src/windows/diag_session/kwp2000_session.rs b/app_rust/src/windows/diag_session/kwp2000_session.rs index 004ad92..7fc8199 100644 --- a/app_rust/src/windows/diag_session/kwp2000_session.rs +++ b/app_rust/src/windows/diag_session/kwp2000_session.rs @@ -1,12 +1,17 @@ -use std::{ - borrow::BorrowMut, - time::Instant, -}; +use std::{borrow::BorrowMut, time::Instant}; use iced::{time, Column, Container, Length, Row, Space, Subscription}; use log_view::{LogType, LogView}; -use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DTCState, DiagCfg, ProtocolServer, kwp2000::KWP2000ECU}}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::window}; +use crate::{ + commapi::{ + comm_api::{ComServer, ISO15765Config}, + iface::{InterfaceConfig, InterfaceType, PayloadFlag, IFACE_CFG}, + protocols::{kwp2000::KWP2000ECU, DTCState, DiagCfg, ProtocolServer}, + }, + themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, + windows::window, +}; use super::{log_view, DiagMessageTrait, SessionResult, SessionTrait}; @@ -173,11 +178,11 @@ impl SessionTrait for KWP2000DiagSession { }; match KWP2000ECU::start_diag_session( - &self.server, - InterfaceType::IsoTp, + &self.server, + InterfaceType::IsoTp, cfg, Some(vec![PayloadFlag::ISOTP_PAD_FRAME]), - diag_cfg + diag_cfg, ) { Ok(server) => { window::disable_home(); @@ -248,26 +253,38 @@ impl SessionTrait for KWP2000DiagSession { ); self.can_clear_codes = true; for x in &errors { - let status = if !x.check_engine_on { - " MIL ON " - } else { - " " - }; + let status = if !x.check_engine_on { " MIL ON " } else { " " }; match &x.state { &DTCState::Permanent => { - self.logview.add_msg(format!("{} {} - Permanent DTC", x.error, status).as_str(), LogType::Error); - }, + self.logview.add_msg( + format!("{} {} - Permanent DTC", x.error, status) + .as_str(), + LogType::Error, + ); + } &DTCState::Stored => { - self.logview.add_msg(format!("{} {} - Stored DTC", x.error, status).as_str(), LogType::Error); - }, + self.logview.add_msg( + format!("{} {} - Stored DTC", x.error, status) + .as_str(), + LogType::Error, + ); + } &DTCState::Pending => { - self.logview.add_msg(format!("{} {} - Pending DTC", x.error, status).as_str(), LogType::Warn); - }, + self.logview.add_msg( + format!("{} {} - Pending DTC", x.error, status) + .as_str(), + LogType::Warn, + ); + } &DTCState::None => { - self.logview.add_msg(format!("{} {} - New DTC", x.error, status).as_str(), LogType::Info); - }, + self.logview.add_msg( + format!("{} {} - New DTC", x.error, status) + .as_str(), + LogType::Info, + ); + } } - + println!("{}", x); } } diff --git a/app_rust/src/windows/diag_session/mod.rs b/app_rust/src/windows/diag_session/mod.rs index 3bec2f4..2ad9832 100644 --- a/app_rust/src/windows/diag_session/mod.rs +++ b/app_rust/src/windows/diag_session/mod.rs @@ -82,18 +82,23 @@ impl DiagSession { ecu: Option, ) -> SessionResult { Ok(match session_type { - SessionType::UDS => { - Self::UDS(UDSDiagSession::new(comm_server, ecu.ok_or(SessionError::Other("Null ECU configuration!?".into()))?)?) - }, - SessionType::KWP => { - Self::KWP(KWP2000DiagSession::new(comm_server, ecu.ok_or(SessionError::Other("Null ECU configuration!?".into()))?)?) - }, - SessionType::Custom => { - Self::Custom(CustomDiagSession::new(comm_server, ecu.ok_or(SessionError::Other("Null ECU configuration!?".into()))?)?) - }, - SessionType::JSON(ecu_data, settings) => { - Self::JSON(JsonDiagSession::new(comm_server, ecu_data.clone(), settings.clone())?) - } + SessionType::UDS => Self::UDS(UDSDiagSession::new( + comm_server, + ecu.ok_or(SessionError::Other("Null ECU configuration!?".into()))?, + )?), + SessionType::KWP => Self::KWP(KWP2000DiagSession::new( + comm_server, + ecu.ok_or(SessionError::Other("Null ECU configuration!?".into()))?, + )?), + SessionType::Custom => Self::Custom(CustomDiagSession::new( + comm_server, + ecu.ok_or(SessionError::Other("Null ECU configuration!?".into()))?, + )?), + SessionType::JSON(ecu_data, settings) => Self::JSON(JsonDiagSession::new( + comm_server, + ecu_data.clone(), + settings.clone(), + )?), }) } diff --git a/app_rust/src/windows/diag_session/uds_session.rs b/app_rust/src/windows/diag_session/uds_session.rs index feac7d8..8bebcc4 100644 --- a/app_rust/src/windows/diag_session/uds_session.rs +++ b/app_rust/src/windows/diag_session/uds_session.rs @@ -9,7 +9,16 @@ use std::{ use iced::{time, Column, Container, Length, Row, Space, Subscription}; use log_view::{LogType, LogView}; -use crate::{commapi::{comm_api::{ComServer, ISO15765Config}, iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, ProtocolServer}, protocols::uds::UDSECU}, themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, windows::window}; +use crate::{ + commapi::{ + comm_api::{ComServer, ISO15765Config}, + iface::{InterfaceConfig, InterfaceType, PayloadFlag, IFACE_CFG}, + protocols::uds::UDSECU, + protocols::{DiagCfg, ProtocolServer}, + }, + themes::{button_outlined, text, text_input, title_text, ButtonType, TextType, TitleSize}, + windows::window, +}; use super::{log_view, DiagMessageTrait, SessionResult, SessionTrait}; @@ -169,7 +178,6 @@ impl SessionTrait for UDSDiagSession { cfg.add_param(IFACE_CFG::ISOTP_BS, self.ecu.block_size); cfg.add_param(IFACE_CFG::ISOTP_ST_MIN, self.ecu.sep_time); - let diag_cfg = DiagCfg { send_id: self.ecu.send_id, recv_id: self.ecu.recv_id, @@ -177,11 +185,11 @@ impl SessionTrait for UDSDiagSession { }; match UDSECU::start_diag_session( - &self.server, - InterfaceType::IsoTp, + &self.server, + InterfaceType::IsoTp, cfg, Some(vec![PayloadFlag::ISOTP_PAD_FRAME]), - diag_cfg + diag_cfg, ) { Ok(server) => { window::disable_home(); @@ -294,8 +302,7 @@ impl SessionTrait for UDSDiagSession { fn subscription(&self) -> iced::Subscription { if self.diag_server.is_some() { - time::every(std::time::Duration::from_millis(250)) - .map(UDSDiagSessionMsg::PollServer) + time::every(std::time::Duration::from_millis(250)).map(UDSDiagSessionMsg::PollServer) } else { Subscription::none() } diff --git a/app_rust/src/windows/launcher.rs b/app_rust/src/windows/launcher.rs index a753a34..ada9f7d 100644 --- a/app_rust/src/windows/launcher.rs +++ b/app_rust/src/windows/launcher.rs @@ -1,12 +1,15 @@ use std::process::Command; -use crate::{commapi::comm_api::{ComServer, ComServerError}, themes::images::get_launcher_image}; use crate::commapi::passthru_api::PassthruApi; +use crate::passthru::{PassthruDevice, PassthruDrv}; use crate::themes::{button_coloured, container, picklist, radio_btn, text, ButtonType, TextType}; use crate::windows::launcher::LauncherMessage::LaunchRequested; use crate::windows::window::ApplicationError::DriverError; use crate::windows::window::{ApplicationError, WindowMessage}; -use crate::passthru::{PassthruDevice, PassthruDrv}; +use crate::{ + commapi::comm_api::{ComServer, ComServerError}, + themes::images::get_launcher_image, +}; use iced::{button, pick_list, Align, Column, Element, Length, Row, Text}; #[cfg(target_os = "linux")] @@ -158,14 +161,14 @@ impl Launcher { #[cfg(target_os = "linux")] // Only available on Linux { - selection = selection.push(radio_btn( - API::SocketCAN, - "Socket CAN", - Some(self.api_selection), - LauncherMessage::SwitchAPI, - ButtonType::Primary - )); - } + selection = selection.push(radio_btn( + API::SocketCAN, + "Socket CAN", + Some(self.api_selection), + LauncherMessage::SwitchAPI, + ButtonType::Primary, + )); + } let mut contents = if self.api_selection == API::DPdu { Column::new() diff --git a/app_rust/src/windows/obd.rs b/app_rust/src/windows/obd.rs index 81167be..362e72d 100644 --- a/app_rust/src/windows/obd.rs +++ b/app_rust/src/windows/obd.rs @@ -1,5 +1,15 @@ -use crate::{commapi::{comm_api::{Capability, ComServer}, iface::{IFACE_CFG, InterfaceConfig, InterfaceType, PayloadFlag}, protocols::{DiagCfg, ProtocolServer, obd2::{ObdServer, service09::Service09Data}}}, themes::button_coloured}; use crate::themes::{button_outlined, text, title_text, ButtonType, TextType, TitleSize}; +use crate::{ + commapi::{ + comm_api::{Capability, ComServer}, + iface::{InterfaceConfig, InterfaceType, PayloadFlag, IFACE_CFG}, + protocols::{ + obd2::{service09::Service09Data, ObdServer}, + DiagCfg, ProtocolServer, + }, + }, + themes::button_coloured, +}; use iced::{button, Align, Button, Column, Element, Length, Row, Space, Text}; #[derive(Debug, Clone)] @@ -18,7 +28,7 @@ pub struct OBDHome { in_session: bool, s09_data: Service09Data, curr_service: u8, - service_btn_states: [button::State; 10] + service_btn_states: [button::State; 10], } impl OBDHome { @@ -31,13 +41,12 @@ impl OBDHome { in_session: false, s09_data: Default::default(), curr_service: 0, - service_btn_states: [button::State::default(); 10] + service_btn_states: [button::State::default(); 10], } } pub fn update(&mut self, msg: &OBDMessage) -> Option { match msg { - OBDMessage::InitIsoTP => { // Try all the CAN IDs for test_id in [0x07E8, 0x07E9, 0x07E0].iter() { @@ -50,15 +59,13 @@ impl OBDHome { send_id: 0x07DF, recv_id: *test_id, global_id: None, - }; if let Ok(server) = ObdServer::start_diag_session( &self.server, InterfaceType::IsoTp, cfg, Some(vec![PayloadFlag::ISOTP_PAD_FRAME]), - diag_cfg - + diag_cfg, ) { if let Ok(r) = server.req_service09(|x| Ok(x.get_everything(&server))) { self.s09_data = r; @@ -70,19 +77,24 @@ impl OBDHome { break; } } - }, + } OBDMessage::Disconnect => { if self.obd_server.is_some() { self.obd_server.take(); // Take and destroy self.in_session = false; } - }, + } &OBDMessage::ChooseService(sid) => { if sid == 0x03 { for dtc in &self.obd_server.as_ref().unwrap().read_errors().unwrap() { - println!("Code: {} State: {:?} Desc: {}", dtc.error, dtc.state, ObdServer::get_dtc_desc(dtc)) + println!( + "Code: {} State: {:?} Desc: {}", + dtc.error, + dtc.state, + ObdServer::get_dtc_desc(dtc) + ) } - return None + return None; } self.curr_service = sid; // What service UI should we be in? } @@ -94,7 +106,7 @@ impl OBDHome { if self.in_session { match self.curr_service { 0x09 => self.create_s09_ui(), - _ => self.create_main_ui() + _ => self.create_main_ui(), } } else { self.create_connect_ui() @@ -102,7 +114,6 @@ impl OBDHome { } pub fn create_main_ui(&mut self) -> Element { - let mut row = Column::new() .padding(10) .spacing(10) @@ -120,18 +131,18 @@ impl OBDHome { row = row.push(btn) } - Column::new() - .padding(10) - .spacing(10) - .push(title_text("OBD Diagnostics", TitleSize::P2)) - .push(button_outlined(&mut self.can_state, "Disconnect", ButtonType::Primary).on_press(OBDMessage::Disconnect)) - .push(row) - - .into() + .padding(10) + .spacing(10) + .push(title_text("OBD Diagnostics", TitleSize::P2)) + .push( + button_outlined(&mut self.can_state, "Disconnect", ButtonType::Primary) + .on_press(OBDMessage::Disconnect), + ) + .push(row) + .into() } - pub fn create_connect_ui(&mut self) -> Element { let obd_btn = button_outlined( &mut self.kline_state, @@ -149,9 +160,7 @@ impl OBDHome { ), }; - let mut btn_row = Row::new() - .padding(10) - .spacing(10); + let mut btn_row = Row::new().padding(10).spacing(10); let mut connect_shown = false; if self.server.get_capabilities().supports_iso9141() == Capability::Yes { @@ -166,16 +175,18 @@ impl OBDHome { // No way to talk to OBD!! if !connect_shown { return Column::new() - .padding(10) - .spacing(10) - .push(title_text("OBD Diagnostics", TitleSize::P2)) - .push(text("Unfortunately, your adapter does not support ISO9141 or ISO15765.", TextType::Warning)) - .push(btn_row) - .align_items(Align::Center) - .into() + .padding(10) + .spacing(10) + .push(title_text("OBD Diagnostics", TitleSize::P2)) + .push(text( + "Unfortunately, your adapter does not support ISO9141 or ISO15765.", + TextType::Warning, + )) + .push(btn_row) + .align_items(Align::Center) + .into(); } - Column::new() .padding(10) .spacing(10) @@ -189,16 +200,33 @@ impl OBDHome { pub fn create_s09_ui(&mut self) -> Element { Column::new() .push(title_text("Vehicle information", TitleSize::P3)) - .push(text(format!("VIN: {}", self.s09_data.vin).as_str(), TextType::Normal)) - .push(text(format!("ECU Name: {}", self.s09_data.ecu_name).as_str(), TextType::Normal)) - .push(text(format!("Calibration ID: {}", self.s09_data.calibration_id).as_str(), TextType::Normal)) - .push(text(format!("CVNs: {:?}", self.s09_data.cvns).as_str(), TextType::Normal)) + .push(text( + format!("VIN: {}", self.s09_data.vin).as_str(), + TextType::Normal, + )) + .push(text( + format!("ECU Name: {}", self.s09_data.ecu_name).as_str(), + TextType::Normal, + )) + .push(text( + format!("Calibration ID: {}", self.s09_data.calibration_id).as_str(), + TextType::Normal, + )) + .push(text( + format!("CVNs: {:?}", self.s09_data.cvns).as_str(), + TextType::Normal, + )) .push(self.add_back_button()) .into() - } pub fn add_back_button(&mut self) -> Element { - button_coloured(&mut self.service_btn_states[0], "Go back", ButtonType::Primary).on_press(OBDMessage::ChooseService(0)).into() + button_coloured( + &mut self.service_btn_states[0], + "Go back", + ButtonType::Primary, + ) + .on_press(OBDMessage::ChooseService(0)) + .into() } } diff --git a/app_rust/src/windows/window.rs b/app_rust/src/windows/window.rs index e5b6050..744cca4 100644 --- a/app_rust/src/windows/window.rs +++ b/app_rust/src/windows/window.rs @@ -8,7 +8,10 @@ use crate::{ commapi::comm_api::{Capability, ComServer, ComServerError}, themes, WIN_HEIGHT, }; -use iced::{Align, Application, Clipboard, Column, Command, Container, Element, Length, Row, Rule, Space, Subscription, Text, button, executor, time}; +use iced::{ + button, executor, time, Align, Application, Clipboard, Column, Command, Container, Element, + Length, Row, Rule, Space, Subscription, Text, +}; use std::fmt::Debug; use std::time::Instant; @@ -85,8 +88,8 @@ impl<'a> WindowState { if let WindowMessage::OBDTools(x) = msg { return o.update(x).map(WindowMessage::OBDTools); } - }, - _ => return None + } + _ => return None, } None } @@ -163,7 +166,11 @@ impl Application for MainWindow { } } - fn update(&mut self, message: Self::Message, _clipboard: &mut Clipboard) -> Command { + fn update( + &mut self, + message: Self::Message, + _clipboard: &mut Clipboard, + ) -> Command { match message { WindowMessage::StatusUpdate(_) => { // On request for battery voltage reading, try to read from the adapter, but it might timeout From 43165cb235fd610bb59322ac608ff26eb3274384 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Sat, 15 May 2021 20:57:06 +0100 Subject: [PATCH 7/8] Capture application crashes to log file --- app_rust/Cargo.toml | 2 ++ app_rust/src/main.rs | 25 +++++++++++++++++++++++++ app_rust/src/windows/home.rs | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/app_rust/Cargo.toml b/app_rust/Cargo.toml index 16f1efd..ca8a4ef 100644 --- a/app_rust/Cargo.toml +++ b/app_rust/Cargo.toml @@ -34,6 +34,8 @@ hex-serde = "0.1.0" chrono = "0.4.19" hex = "0.4.2" image = "0.23.12" +dialog = "0.3.0" +backtrace = "0.3.59" [target.'cfg(windows)'.dependencies] winreg = "0.8" diff --git a/app_rust/src/main.rs b/app_rust/src/main.rs index f7ca3d6..173b850 100644 --- a/app_rust/src/main.rs +++ b/app_rust/src/main.rs @@ -1,3 +1,7 @@ +use std::{fs::File, io::Write, panic, time}; +use backtrace; + +use dialog::DialogBox; use iced::{Application, Settings}; mod cli_tests; mod commapi; @@ -31,5 +35,26 @@ fn main() -> iced::Result { themes::set_debug(true) } } + + panic::set_hook(Box::new(|info|{ + let backtrace = backtrace::Backtrace::new(); + + let mut report = String::from("!!! Report crash to https://github.com/rnd-ash/OpenVehicleDiag/issues/ !!!\n"); + report.push_str("\n----\n"); + report.push_str(format!("Reason: {}\nBacktrace:\n{:?}", info, backtrace).as_str()); + report.push_str("\n----\n"); + let time = chrono::Utc::now(); + let path = std::env::current_dir().unwrap().join(format!("ovd_crash-{}.txt", time.format("%F-%H_%M_%S"))); + let write_res = File::create(&path).unwrap().write_all(report.as_bytes()); + + let mut summary = format!("Reason: {}\n", info); + summary.push_str(format!("Crash report written to {}\n", &path.as_os_str().to_str().unwrap()).as_str()); + summary.push_str("See crash report for more info on how to report"); + + let _res = dialog::Message::new(summary) + .title("Oh no! OpenVehicleDiag crashed") + .show(); + })); + MainWindow::run(launcher_settings) } diff --git a/app_rust/src/windows/home.rs b/app_rust/src/windows/home.rs index 3928313..25b86c5 100644 --- a/app_rust/src/windows/home.rs +++ b/app_rust/src/windows/home.rs @@ -129,7 +129,7 @@ impl Home { .push( button_outlined(&mut self.obd_state, "OBD Tools", ButtonType::Primary) .on_press(WindowMessage::GoOBD), - ), + ) ); contents.into() } From c5ecc571255c2a0d4c39c94e0d4357c87ae0df61 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Sat, 15 May 2021 20:59:46 +0100 Subject: [PATCH 8/8] Bump version to 1.0.5 --- README.md | 2 +- app_rust/Cargo.toml | 2 +- app_rust/README.md | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ca49867..5201b30 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The idea is to make diagnosing and exploring your cars diagnostics functions pos This is for my University FYP for my degree at the University of Reading ### Latest release -[Version 1.0.0 (21/04/21)](https://github.com/rnd-ash/OpenVehicleDiag/releases/tag/v1.0.0) +[Version 1.0.5 (15/05/21)](https://github.com/rnd-ash/OpenVehicleDiag/releases/tag/v1.0.0) ## Youtube video playlist Videos showing OpenVehicleDiag in use and its development progress can be found [here](https://youtube.com/playlist?list=PLxrw-4Vt7xtty50LmMoLXN2iKiUknbMng) diff --git a/app_rust/Cargo.toml b/app_rust/Cargo.toml index ca8a4ef..18db4f3 100644 --- a/app_rust/Cargo.toml +++ b/app_rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openvehiclediag" -version = "1.0.0" +version = "1.0.5" description = "pen Vehicle Diagnostics (OVD) is a Rust-based open source vehicle ECU diagnostic platform." edition = "2018" diff --git a/app_rust/README.md b/app_rust/README.md index 4214b61..7640402 100644 --- a/app_rust/README.md +++ b/app_rust/README.md @@ -1,7 +1,5 @@ # Open vehicle diagnostics app - - ## Features (Current) * CAN Tracer * OBD Toolbox