diff --git a/.gitignore b/.gitignore index de358ff..7b06551 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ /target .vscode/ +.DS_Store + +# TODO: Remove +windows/ +bindings.rs diff --git a/Cargo.lock b/Cargo.lock index d838767..63e845b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -50,6 +59,7 @@ name = "automotive" version = "0.2.1" dependencies = [ "async-stream", + "bindgen", "bstr", "futures", "hex", @@ -82,6 +92,29 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bitflags" version = "2.5.0" @@ -111,12 +144,48 @@ version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "futures" version = "0.3.30" @@ -212,6 +281,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "heck" version = "0.4.1" @@ -230,18 +305,52 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +[[package]] +name = "libloading" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +dependencies = [ + "cfg-if", + "windows-targets 0.52.5", +] + [[package]] name = "libusb1-sys" version = "0.7.0" @@ -254,6 +363,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -276,6 +391,12 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -296,6 +417,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -378,6 +509,16 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.82" @@ -405,11 +546,34 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + [[package]] name = "regex-automata" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rusb" @@ -427,6 +591,25 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustversion" version = "1.0.16" @@ -508,6 +691,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -724,6 +913,18 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 609f031..0aeb519 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,9 @@ tokio = { version = "1.36.0", features = ["full"] } tokio-stream = "0.1.14" tracing = "0.1" +[build-dependencies] +bindgen = "0.69.4" + [target.'cfg(target_os = "linux")'.dependencies] socket2 = "0.5.7" libc = "0.2.154" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..9b9ff5f --- /dev/null +++ b/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + println!("cargo:rustc-link-search=windows/"); + println!("cargo:rustc-link-lib=vxlapi"); + + let bindings = bindgen::Builder::default() + .header("windows/wrapper.h") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/src/lib.rs b/src/lib.rs index 6a7ddc9..b059b7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,6 +75,7 @@ mod error; pub mod isotp; pub mod panda; pub mod uds; +pub mod vector; /// Re-export of relevant stream traits from `tokio_stream`. pub use tokio_stream::{Stream, StreamExt, Timeout}; diff --git a/src/vector/bit_timing.rs b/src/vector/bit_timing.rs new file mode 100644 index 0000000..e32c8e4 --- /dev/null +++ b/src/vector/bit_timing.rs @@ -0,0 +1,261 @@ +use crate::vector::error::Error; + +#[derive(Clone)] +pub enum BitTimingKind { + Standard(BitTiming), + Extended(BitTimingFd), +} + +#[derive(Clone)] +pub struct BitTiming { + pub f_clock: u32, + pub brp: u8, + pub tseg1: u8, + pub tseg2: u8, + pub sjw: u8, + pub nof_sample_points: u32, + strict: bool, +} + +impl BitTiming { + pub fn new( + f_clock: u32, + brp: u8, + tseg1: u8, + tseg2: u8, + sjw: u8, + nof_sample_points: u32, + strict: bool, + ) -> Result { + if brp < 1 || brp > 64 { + return Err(Error::BitTimingError("BRP must be between 1 and 64".to_string())); + } + + if tseg1 < 1 || tseg1 > 16 { + return Err(Error::BitTimingError("TSEG1 must be between 1 and 16".to_string())); + } + + if tseg2 < 1 || tseg2 > 8 { + return Err(Error::BitTimingError("TSEG2 must be between 1 and 8".to_string())); + } + + if sjw < 1 || sjw > 4 { + return Err(Error::BitTimingError("SJW must be between 1 and 4".to_string())); + } + + if sjw > tseg2 { + return Err(Error::BitTimingError( + "SJW must be less than or equal to TSEG2".to_string(), + )); + } + + if _sample_point(tseg1, tseg2) < 50.0 { + return Err(Error::BitTimingError( + "Sample point must be greater than or equal to 50%".to_string(), + )); + } + + match nof_sample_points { + 1 | 3 => (), + _ => { + return Err(Error::BitTimingError( + "Number of sample points must be 1 or 3".to_string(), + )) + } + } + + if strict { + let nbt = _nbt(tseg1, tseg2); + let bitrate = _bitrate(f_clock, brp, _nbt(tseg1, tseg2)); + + if nbt < 8 || nbt > 25 { + return Err(Error::BitTimingError("NBT must be between 8 and 25".to_string())); + } + + if brp < 1 || brp > 32 { + return Err(Error::BitTimingError("BRP must be between 1 and 32".to_string())); + } + + if bitrate < 5_000 || bitrate > 1_000_000 { + return Err(Error::BitTimingError( + "Bitrate must be between 5_000 and 1_000_000".to_string(), + )); + } + } + + Ok(Self { + f_clock, + brp, + tseg1, + tseg2, + sjw, + nof_sample_points, + strict, + }) + } + + /// Bit timing register 0 for SJA1000 + pub fn btr0(&self) -> u8 { + return (self.sjw - 1) << 6 | self.brp - 1; + } + + /// Bit timing register 1 for SJA1000 + pub fn btr1(&self) -> u8 { + let sam = match self.nof_sample_points { + 3 => 1, + _ => 0, + }; + + return sam << 7 | (self.tseg2 - 1) << 4 | self.tseg1 - 1; + } +} + +#[derive(Clone)] +pub struct BitTimingFd { + pub f_clock: u32, + pub nom_brp: u32, + pub nom_tseg1: u32, + pub nom_tseg2: u32, + pub nom_sjw: u32, + pub data_brp: u32, + pub data_tseg1: u32, + pub data_tseg2: u32, + pub data_sjw: u32, + strict: bool, +} + +impl BitTimingFd { + pub fn new( + f_clock: u32, + nom_brp: u32, + nom_tseg1: u32, + nom_tseg2: u32, + nom_sjw: u32, + data_brp: u32, + data_tseg1: u32, + data_tseg2: u32, + data_sjw: u32, + strict: bool, + ) -> Result { + if nom_brp < 1 { + return Err(Error::BitTimingError("Nominal BRP must be at least 1".to_string())); + } + + if data_brp < 1 { + return Err(Error::BitTimingError("Data BRP must be at least 1".to_string())); + } + + let nbt = _nbt_fd(nom_tseg1, nom_tseg2); + let dbt = Self::_dbt(data_tseg1, data_tseg2); + + if Self::_data_bitrate(f_clock, data_brp, dbt) < Self::_nom_bitrate(f_clock, nom_brp, nbt) { + return Err(Error::BitTimingError( + "Data bitrate must be greater than or equal to nominal bitrate".to_string(), + )); + } + + if nom_sjw > nom_tseg2 { + return Err(Error::BitTimingError( + "Nominal SJW must be less than or equal to Nominal TSEG2".to_string(), + )); + } + + if data_sjw > data_tseg2 { + return Err(Error::BitTimingError( + "Data SJW must be less than or equal to Data TSEG2".to_string(), + )); + } + + if _sample_point_fd(nom_tseg1, nom_tseg2) < 50.0 { + return Err(Error::BitTimingError( + "Nominal sample point must be greater than or equal to 50%".to_string(), + )); + } + + if Self::_data_sample_point(data_tseg1, data_tseg2) < 50.0 { + return Err(Error::BitTimingError( + "Data sample point must be greater than or equal to 50%".to_string(), + )); + } + + if strict { + if nbt < 8 || nbt > 80 { + return Err(Error::BitTimingError("NBT must be between 8 and 80".to_string())); + } + + if dbt < 5 || dbt > 25 { + return Err(Error::BitTimingError("DBT must be between 5 and 25".to_string())); + } + + // TODO: DO more checks based on: https://github.com/hardbyte/python-can/blob/4a41409de8e1eefaa1aa003da7e4f84f018c6791/can/bit_timing.py#L632 + } + + Ok(Self { + f_clock, + nom_brp, + nom_tseg1, + nom_tseg2, + nom_sjw, + data_brp, + data_tseg1, + data_tseg2, + data_sjw, + strict, + }) + } + + pub fn nom_bitrate(&self) -> u32 { + Self::_nom_bitrate(self.f_clock, self.nom_brp, _nbt_fd(self.nom_tseg1, self.nom_tseg2)) + } + + pub fn data_bitrate(&self) -> u32 { + Self::_data_bitrate( + self.f_clock, + self.data_brp, + Self::_dbt(self.data_tseg1, self.data_tseg2), + ) + } + + fn _nom_bitrate(f_clock: u32, nom_brp: u32, nom_nbt: u32) -> u32 { + _bitrate_fd(f_clock, nom_brp, nom_nbt) + } + + fn _data_bitrate(f_clock: u32, data_brp: u32, dbt: u32) -> u32 { + f_clock / (data_brp * dbt) + } + + fn _data_sample_point(data_tseg1: u32, data_tseg2: u32) -> f32 { + return 100.0 * (1 + data_tseg1) as f32 / (1 + data_tseg1 + data_tseg2) as f32; + } + + fn _dbt(data_tseg1: u32, data_tseg2: u32) -> u32 { + 1 + data_tseg1 + data_tseg2 + } +} + +/// Calculate the sample point in percent +fn _sample_point(tseg1: u8, tseg2: u8) -> f32 { + return 100.0 * (1 + tseg1) as f32 / (1 + tseg1 + tseg2) as f32; +} + +/// Calculate the sample point in percent +fn _sample_point_fd(tseg1: u32, tseg2: u32) -> f32 { + return 100.0 * (1 + tseg1) as f32 / (1 + tseg1 + tseg2) as f32; +} + +fn _nbt_fd(tseg1: u32, tseg2: u32) -> u32 { + return 1 + tseg1 + tseg2; +} + +/// Normal Bit Time +fn _nbt(tseg1: u8, tseg2: u8) -> u8 { + return 1 + tseg1 + tseg2; +} + +fn _bitrate_fd(f_clock: u32, brp: u32, nbt: u32) -> u32 { + return f_clock / (brp * nbt) as u32; +} + +fn _bitrate(f_clock: u32, brp: u8, nbt: u8) -> u32 { + return f_clock / (brp * nbt) as u32; +} diff --git a/src/vector/error.rs b/src/vector/error.rs new file mode 100644 index 0000000..8b4301d --- /dev/null +++ b/src/vector/error.rs @@ -0,0 +1,14 @@ +//! Error types for the Vector Client. +use thiserror::Error; + +#[derive(Error, Debug, PartialEq, Clone)] +pub enum Error { + #[error("Driver error: {0}")] + DriverError(String), + #[error("BitTimming error: {0}")] + BitTimingError(String), + #[error("Qeue is empty")] + EmptyQueue, + #[error("Qeue is full")] + FullQueue, +} diff --git a/src/vector/mod.rs b/src/vector/mod.rs new file mode 100644 index 0000000..c7aeca6 --- /dev/null +++ b/src/vector/mod.rs @@ -0,0 +1,395 @@ +pub mod bit_timing; +mod error; +pub mod types; +pub mod wrapper; + +pub use error::Error; + +use bit_timing::BitTimingKind; +use types::CanFilter; + +use crate::can::{CanAdapter, Frame, Identifier, AsyncCanAdapter}; +use crate::{ + XLevent, + XLaccess, + XLhandle, + XLportHandle, + XLcanTxEvent, + s_xl_tag_data, + s_xl_can_msg, + XL_CAN_EXT_MSG_ID, + XL_BUS_TYPE_CAN, + XL_INTERFACE_VERSION, + XL_INTERFACE_VERSION_V4, + e_XLevent_type_XL_TRANSMIT_MSG, + XL_CAN_TXMSG_FLAG_EDL, + XL_CAN_MSG_FLAG_TX_COMPLETED, + XLcanTxEvent__bindgen_ty_1, + XL_CAN_TX_MSG, + xlCanSetChannelMode, +}; +use std::collections::HashMap; +use std::collections::VecDeque; + +const XL_CAN_EV_TAG_TX_MSG: u16 = 1088; + +const CAN_FRAME_DATA_LENGTH: usize = 8; + + +#[derive(Clone)] +pub struct VectorCan { + pub channels: Vec, + pub can_filters: Option>, + pub poll_interval: f32, + pub receive_own_messages: bool, + pub timing: Option, + pub rx_queue_size: u32, + pub app_name: String, + pub serial: Option, + pub fd_mode: bool, + pub bit_rate: Option, + pub port_handle: XLportHandle, + // event_handle: XLhandle, + mask: XLaccess, + permission_mask: XLaccess, + loopback_queue: VecDeque, +} + +impl Default for VectorCan { + fn default() -> Self { + Self::new( + Vec::new(), + None, + 0.01, + false, + None, + //2_u32.pow(14), + 8192, + String::from("CANalyzer"), + None, + false, + None, + // -1, + // event_handle: std::ptr::null_mut(), + // mask: 0, + // permission_mask: 0, + ) + } +} + +impl VectorCan { + pub fn new( + channels: Vec, + can_filters: Option>, + poll_interval: f32, + receive_own_messages: bool, + timing: Option, + rx_queue_size: u32, + app_name: String, + serial: Option, + fd_mode: bool, + bit_rate: Option, + ) -> Self { + + /* + + let channel_configs = wrapper::get_channel_configs().unwrap(); + let mut mask: u64 = 0; + let mut channel_masks: HashMap = HashMap::new(); + let mut index_to_channel: HashMap = HashMap::new(); + + for channel in &channels { + let channel_index = + wrapper::find_global_channel_idx(*channel as u8, serial, Some(&app_name), channel_configs.clone()) + .unwrap(); + let channel_mask: u64 = 1 << channel_index; + channel_masks.insert(*channel, channel_mask); + index_to_channel.insert(channel_index as u32, *channel); + mask |= channel_mask; + } + + let mut permission_mask: Option = None; //XLaccess::default(); + if bit_rate.is_some() || fd_mode { + permission_mask = Some(mask); + } + + let inetface_version = match fd_mode { + true => XL_INTERFACE_VERSION_V4, + false => XL_INTERFACE_VERSION, + }; + + */ + let inetface_version = XL_INTERFACE_VERSION; + + //let mask: XLaccess = 0b0001100; // CAN bus is on channel 3 and 4 + let mask: XLaccess = 0b00001000; + let permission_mask: Option = Some(mask.clone()); //XLaccess::default(); + + let port_config = wrapper::open_port( + &app_name, + mask, + permission_mask, + rx_queue_size, + inetface_version, + XL_BUS_TYPE_CAN, + ) + .unwrap(); + + // TODO: Implement check_can_settings + let assert_timing = bit_rate.is_some() || timing.is_some(); + + if let Some(timing) = &timing { + match timing { + BitTimingKind::Standard(timing) => { + wrapper::set_bit_timing(port_config.port_handle, mask, port_config.permission_mask, &timing) + .unwrap(); + // let bit_rate = bit_rate.unwrap_or(500_000); + // let bit_timing = bit_timing::BitTiming::new(bit_rate, timing).unwrap(); + // wrapper::set_bit_timing(port_handle, channel_masks, bit_timing).unwrap(); + } + BitTimingKind::Extended(timing) => { + wrapper::set_bit_timing_fd(port_config.port_handle, mask, port_config.permission_mask, &timing) + .unwrap(); + } + } + } else if fd_mode { + // TODO: Implement https://github.com/hardbyte/python-can/blob/4a41409de8e1eefaa1aa003da7e4f84f018c6791/can/interfaces/vector/canlib.py#L288 + } else if let Some(bit_rate) = bit_rate { + wrapper::set_bit_rate(port_config.port_handle, mask, port_config.permission_mask, bit_rate).unwrap(); + } else { + println!("We are setting the to the default bit rate of 5000000"); + wrapper::set_bit_rate(port_config.port_handle, mask, port_config.permission_mask, 500_000).unwrap(); + } + + unsafe { + let status = xlCanSetChannelMode( + port_config.port_handle, + mask, + 0x0, + 0x0 + ); + + println!("Set channel status: {:?}", status); + }; + + let mut event_handle: XLhandle = std::ptr::null_mut(); + wrapper::set_notification(port_config.port_handle, &mut event_handle, 1).unwrap(); + + match wrapper::activate_channel(port_config.port_handle, mask, XL_BUS_TYPE_CAN, 0) { + Ok(_) => {} + Err(e) => { + wrapper::deactivate_channel(port_config.port_handle, mask).unwrap(); + wrapper::close_port(port_config.port_handle).unwrap(); + wrapper::close_driver().unwrap(); + panic!("Error activating channel: {:?}", e); + } + }; + + Self { + channels, + can_filters, + poll_interval, + receive_own_messages, + timing, + rx_queue_size, + app_name, + serial, + fd_mode, + bit_rate, + port_handle: port_config.port_handle, + // event_handle: event_handle, + mask, + permission_mask: port_config.permission_mask, + loopback_queue: VecDeque::new(), + } + } + + pub fn shutdown(&self) { + wrapper::deactivate_channel(self.port_handle, self.mask).unwrap(); + wrapper::close_port(self.port_handle).unwrap(); + wrapper::close_driver().unwrap(); + + } + + pub fn new_async(&self) -> AsyncCanAdapter { + // let socket = SocketCan::new(socket); + + AsyncCanAdapter::new(self.clone()) + } + + fn get_tx_channel_mask(&self, frames: &VecDeque) -> u64 { + //if frames.len() == 1 { + // self.channel_masks.get(&(frames[0].bus as u32)).unwrap_or(&self.mask).clone() + //} else { + // self.mask + //} + self.mask + } + + fn send_can(&self, frames: &VecDeque) -> Result { + let mask = self.get_tx_channel_mask(frames); + + // Change to send the frames one at a time so we can retry them + let mut events = vec![]; + for frame in frames { + events.push(build_xl_event(frame)); + } + + wrapper::send_can(self.port_handle, mask, events.len() as u32, events) + } + + fn receive_can(&self) -> Result { + let event = match wrapper::receive_can(self.port_handle) { + Ok(event) => event, + Err(err) => return Err(err), + }; + + // Ok(Frame::from(event)) + Ok(event.into()) + } + + fn send_can_fd(&self, frames: &VecDeque) { + let mask = self.get_tx_channel_mask(frames); + + let mut events = vec![]; + for frame in frames { + events.push(build_xl_can_tx_event(frame)); + } + + wrapper::send_can_fd(self.port_handle, mask, events.len(), events); + } + + +} + + +impl CanAdapter for VectorCan { + fn send(&mut self, frames: &mut VecDeque) -> Result<(), crate::error::Error> { + // let mask = self.get_tx_channel_mask(frames); + match self.fd_mode { + true => { + self.send_can_fd(frames); + }, + false => { + self.send_can(frames)?; + } + }; + + for frame in frames { + let mut frame = frame.clone(); + frame.loopback = true; + self.loopback_queue.push_back(frame); + } + + Ok(()) + } + + fn recv(&mut self) -> Result, crate::error::Error> { + let frame = match self.receive_can() { + Ok(frame) => frame, + Err(err) => match err { + Error::EmptyQueue => return Ok(vec![]), + _ => return Err(crate::error::Error::VectorError(err)), + } + }; + + let mut frames = vec![frame]; + + // Add fake loopback frames to the receive queue + frames.extend(self.loopback_queue.drain(..)); + + Ok(frames) + } +} + +fn is_loopback(event: &XLevent) -> bool { + unsafe { + (event.tagData.msg.flags & XL_CAN_MSG_FLAG_TX_COMPLETED as u16) == 0 + } +} + +fn build_xl_event(frame: &Frame) -> XLevent { + let id = match frame.id { + Identifier::Standard(id) => id, + Identifier::Extended(id) => id | XL_CAN_EXT_MSG_ID, + }; + + let data: [u8; CAN_FRAME_DATA_LENGTH] = { + let mut array = [0u8; CAN_FRAME_DATA_LENGTH]; + array[..CAN_FRAME_DATA_LENGTH].copy_from_slice(&frame.data[..CAN_FRAME_DATA_LENGTH]); + + array + }; + + let event = XLevent { + tag: e_XLevent_type_XL_TRANSMIT_MSG as u8, + chanIndex: 0,//frame.bus, + transId: 0, + portHandle: 0, + flags: 0, + reserved: 0, + timeStamp: 0, + tagData: s_xl_tag_data { + msg: s_xl_can_msg { + id, + flags: 0, + dlc: CAN_FRAME_DATA_LENGTH as u16, + res1: 0, + data, + res2: 0, + } + } + }; + + event +} + +fn build_xl_can_tx_event(frame: &Frame) -> XLcanTxEvent { + let id = match frame.id { + Identifier::Standard(id) => id, + Identifier::Extended(id) => id | XL_CAN_EXT_MSG_ID, + }; + + let mut flags = 0; + if frame.fd { + flags |= XL_CAN_TXMSG_FLAG_EDL; + } + + let event = XLcanTxEvent { + tag: XL_CAN_EV_TAG_TX_MSG, + transId: 0xFFFF, + channelIndex: frame.bus as u8, + reserved: [0, 0, 0], + tagData: XLcanTxEvent__bindgen_ty_1 { + canMsg: XL_CAN_TX_MSG { + canId: id, + msgFlags: flags, + dlc: 0, + reserved: [0, 0, 0, 0, 0, 0, 0], + data: [0; 64], + } + } + }; + + event +} + +impl From for Frame { + fn from(event: XLevent) -> Self { + let data = unsafe { + event.tagData.msg.data.to_vec() + }; + + let tx_id = unsafe { + event.tagData.msg.id + }; + + Frame { + bus: event.chanIndex, + id: Identifier::Standard(tx_id), + data, + loopback: is_loopback(&event), + fd: false, + } + } +} \ No newline at end of file diff --git a/src/vector/types.rs b/src/vector/types.rs new file mode 100644 index 0000000..27b91c9 --- /dev/null +++ b/src/vector/types.rs @@ -0,0 +1,18 @@ +use crate::{XLaccess, XLportHandle}; + +pub struct ApplicationConfig { + pub hw_type: i32, + pub hw_index: i32, + pub hw_channel: i32, +} + +#[derive(Clone)] +pub enum CanFilter { + Standard { id: u32, mask: u32 }, + Extended { id: u32, mask: u32, extended: bool }, +} + +pub struct PortConfig { + pub port_handle: XLportHandle, + pub permission_mask: XLaccess, +} diff --git a/src/vector/wrapper.rs b/src/vector/wrapper.rs new file mode 100644 index 0000000..4aeb365 --- /dev/null +++ b/src/vector/wrapper.rs @@ -0,0 +1,436 @@ +use crate::{ + xlActivateChannel, xlCanFdSetConfiguration, xlCanSetChannelBitrate, xlCanSetChannelMode, xlCanSetChannelParamsC200, + xlCloseDriver, xlClosePort, xlDeactivateChannel, xlGetApplConfig, xlGetChannelIndex, xlGetDriverConfig, + xlOpenDriver, xlOpenPort, xlSetNotification, xlCanTransmit, xlCanTransmitEx, xlReceive, XLevent, XLcanTxEvent, XLaccess, XLcanFdConf, XLchannelConfig, XLdriverConfig, XLhandle, + XLportHandle, XL_BUS_TYPE_CAN, XL_SUCCESS, XL_ERR_QUEUE_IS_EMPTY, XL_ERR_QUEUE_IS_FULL, +}; + +use crate::vector::types::{ApplicationConfig, PortConfig}; + +use crate::vector::bit_timing::BitTiming; + +use crate::vector::error::Error; + +use super::bit_timing::BitTimingFd; + +pub fn open_driver() -> Result<(), Error> { + let status = unsafe { xlOpenDriver() }; + + match status as u32 { + XL_SUCCESS => Ok(()), + _ => Err(Error::DriverError(format!( + "Failed to open driver with error code: {}", + status + ))), + } +} + +pub fn close_driver() -> Result<(), Error> { + let status = unsafe { xlCloseDriver() }; + match status as u32 { + XL_SUCCESS => Ok(()), + + _ => Err(Error::DriverError(format!( + "Failed to close driver with error code: {}", + status + ))), + } +} + +pub fn open_port( + app_name: &str, + mask: XLaccess, + permission_mask: Option, + rx_queue_size: u32, + interface_version: u32, + bus_type: u32, +) -> Result { + let mut port_handle: XLportHandle = 0; + let mut permission_mask = match permission_mask { + Some(mask) => mask, + None => 0, + }; + + let status = unsafe { + xlOpenPort( + &mut port_handle, + app_name.as_ptr() as *mut i8, + mask, + &mut permission_mask, + rx_queue_size, + interface_version, + bus_type, + ) + }; + + match status as u32 { + XL_SUCCESS => Ok(PortConfig { + port_handle, + permission_mask: permission_mask as XLaccess, + }), + + _ => Err(Error::DriverError(format!( + "Failed to open port with error code: {}", + status + ))), + } +} + +pub fn close_port(port_handle: XLportHandle) -> Result<(), Error> { + let status = unsafe { xlClosePort(port_handle) }; + + match status as u32 { + XL_SUCCESS => Ok(()), + _ => Err(Error::DriverError(format!( + "Failed to close port with error code: {}", + status + ))), + } +} + +pub fn send_can( + port_handle: XLportHandle, + access_mask: XLaccess, + events_count: u32, + events: Vec +) -> Result { + unsafe { + let mut count = events_count.clone(); + // let mut events_clone = events.clone(); + // println!("Events address: {:p}", &events_clone); + // println!("Count address: {:p}", &count); + let mut boxed = events.clone().into_boxed_slice(); + let mut array = boxed.as_mut_ptr(); + + // println!("BBBBBB: {:?}", array); + let status = xlCanTransmit( + port_handle, + access_mask, + &mut count as *mut u32, + array as *mut _ as *mut std::os::raw::c_void, + ); + + match status as u32 { + XL_SUCCESS => (), + XL_ERR_QUEUE_IS_FULL => return Err(Error::FullQueue), + _ => { + return Err(Error::DriverError(format!( + "Failed to send data to CAN with error: {}", + status + ))) + } + }; + + Ok(count) + } +} + +pub fn send_can_fd( + port_handle: XLportHandle, + access_mask: XLaccess, + events_count: usize, + events: Vec +) { + +} + +pub fn receive_can( + port_handle: XLportHandle, +) -> Result { + unsafe { + let mut event: XLevent = std::mem::zeroed(); //XLcanFdConf { + let mut out_count = 1u32; + let status = xlReceive( + port_handle, + &mut out_count as *mut u32, + &mut event as *mut XLevent + ); + + match status as u32 { + XL_SUCCESS => (), + XL_ERR_QUEUE_IS_EMPTY => return Err(Error::EmptyQueue), + _ => { + return Err(Error::DriverError(format!( + "Failed to receive data from CAN with error: {}", + status + ))) + } + }; + + Ok(event) + } +} + +pub fn set_bit_timing( + port_handle: XLportHandle, + mut channel_mask: u64, + permission_mask: u64, + timing: &BitTiming, +) -> Result<(), Error> { + channel_mask = channel_mask & permission_mask; + + if channel_mask == 0 { + return Ok(()); + } + + let status = unsafe { xlCanSetChannelParamsC200(port_handle, channel_mask, timing.btr0(), timing.btr1()) }; + + match status as u32 { + XL_SUCCESS => (), + _ => { + return Err(Error::DriverError(format!( + "Failed to set bit timing with error code: {}", + status + ))) + } + }; + + Ok(()) +} + +pub fn set_bit_timing_fd( + port_handle: XLportHandle, + mut channel_mask: u64, + permission_mask: u64, + timing: &BitTimingFd, +) -> Result<(), Error> { + channel_mask = channel_mask & permission_mask; + + if channel_mask == 0 { + return Ok(()); + } + + unsafe { + let mut conf: XLcanFdConf = std::mem::zeroed(); //XLcanFdConf { + conf.arbitrationBitRate = timing.nom_bitrate(); + conf.sjwAbr = timing.nom_sjw; + conf.tseg1Abr = timing.nom_tseg1; + conf.tseg2Abr = timing.nom_tseg2; + conf.dataBitRate = timing.data_bitrate(); + conf.sjwDbr = timing.data_sjw; + conf.tseg1Dbr = timing.data_tseg1; + conf.tseg2Dbr = timing.data_tseg2; + + let status = xlCanFdSetConfiguration(port_handle, channel_mask, &mut conf as *mut XLcanFdConf); + + match status as u32 { + XL_SUCCESS => (), + _ => { + return Err(Error::DriverError(format!( + "Failed to set bit timing with error code: {}", + status + ))) + } + }; + }; + + Ok(()) +} + +pub fn set_bit_rate( + port_handle: XLportHandle, + mut channel_mask: u64, + permission_mask: u64, + bit_rate: u32, +) -> Result<(), Error> { + channel_mask = channel_mask & permission_mask; + if channel_mask == 0 { + return Ok(()); + } + + let status = unsafe { xlCanSetChannelBitrate(port_handle, channel_mask, bit_rate) }; + match status as u32 { + XL_SUCCESS => (), + _ => { + return Err(Error::DriverError(format!( + "Failed to set bit rate with error code: {}", + status + ))) + } + }; + + // let mut timing = BitTiming::new(bit_rate)?; + + // set_bit_timing(port_handle, channel_mask, permission_mask, &timing) + //todo!() + Ok(()) +} + +pub fn set_channel_mode(port_handle: XLportHandle, channel_mask: XLaccess, tx: i32, txrq: i32) -> Result<(), Error> { + let status = unsafe { xlCanSetChannelMode(port_handle, channel_mask, tx, txrq) }; + + match status as u32 { + XL_SUCCESS => Ok(()), + _ => Err(Error::DriverError(format!( + "Failed to set channel mode with error code: {}", + status + ))), + } +} + +pub fn set_notification(port_handle: XLportHandle, event_handle: &mut XLhandle, queue_level: i32) -> Result<(), Error> { + let status = unsafe { xlSetNotification(port_handle, event_handle, queue_level) }; + + match status as u32 { + XL_SUCCESS => Ok(()), + _ => Err(Error::DriverError(format!( + "Failed to set notification with error code: {}", + status + ))), + } +} + +pub fn activate_channel( + port_handle: XLportHandle, + channel_mask: XLaccess, + bus_type: u32, + flags: u32, +) -> Result<(), Error> { + let status = unsafe { xlActivateChannel(port_handle, channel_mask, bus_type, flags) }; + + match status as u32 { + XL_SUCCESS => Ok(()), + _ => Err(Error::DriverError(format!( + "Failed to activate channel with error code: {}", + status + ))), + } +} + +pub fn deactivate_channel(port_handle: XLportHandle, channel_mask: XLaccess) -> Result<(), Error> { + let status = unsafe { xlDeactivateChannel(port_handle, channel_mask) }; + + match status as u32 { + XL_SUCCESS => Ok(()), + _ => Err(Error::DriverError(format!( + "Failed to deactivate channel with error code: {}", + status + ))), + } +} + +pub fn find_global_channel_idx( + channel: u8, + + serial: Option, + + app_name: Option<&str>, + + channel_configs: Vec, +) -> Result { + if let Some(serial) = serial { + let mut serial_found = false; + + for channel_config in channel_configs { + if channel_config.serialNumber == serial { + continue; + } + + serial_found = true; + + if channel_config.hwChannel == channel { + return Ok(channel_config.channelIndex); + } + } + + match serial_found { + true => { + return Err(Error::DriverError(format!( + "Channel {} not found on interface with serial: {}", + channel, serial + ))) + } + + false => return Err(Error::DriverError(format!("No interface with serial {} found", serial))), + }; + } + + if let Some(app_name) = app_name { + let app_config = get_application_config(app_name, channel as u32)?; + let idx = unsafe { xlGetChannelIndex(app_config.hw_type, app_config.hw_index, app_config.hw_channel) }; + + if idx < 0 { + // Undocumented behavior! See issue #353. + // If hardware is unavailable, this function returns -1. + // Raise an exception as if the driver + // would have signalled XL_ERR_HW_NOT_PRESENT. + return Err(Error::DriverError(format!( + "Failed to get channel id, due to undocumented behavior" + ))); + } + + return Ok(idx as u8); + } + + for channel_config in channel_configs { + if channel_config.hwChannel == channel { + return Ok(channel_config.channelIndex); + } + } + + Err(Error::DriverError(format!("Channel {} not found", channel))) +} + +pub fn get_channel_configs() -> Result, Error> { + let driver_config = get_driver_config()?; + let mut channels: Vec = Vec::new(); + + for i in 0..driver_config.channelCount { + channels.push(driver_config.channel[i as usize]); + + // let mut channel_config: XLchannelConfig = std::mem::zeroed(); + // let status = unsafe { xlGetChannelConfig(i, &mut channel_config) }; + // match status as u32 { + // XL_SUCCESS => channels.push(channel_config), + // _ => return Err(Error::DriverError(format!("Failed to get channel config with error code: {}", status))) + // } + } + + Ok(channels) +} + +pub fn get_driver_config() -> Result { + unsafe { + let mut driver_config: XLdriverConfig = std::mem::zeroed(); + + let status = xlGetDriverConfig(&mut driver_config); + match status as u32 { + XL_SUCCESS => Ok(driver_config), + + _ => Err(Error::DriverError(format!( + "Failed to get driver config with error code: {}", + status + ))), + } + } +} + +pub fn get_application_config(app_name: &str, app_channel: u32) -> Result { + unsafe { + let mut hw_type = std::mem::zeroed(); + let mut hw_index = std::mem::zeroed(); + let mut hw_channel = std::mem::zeroed(); + + let status = xlGetApplConfig( + app_name.as_ptr() as *mut i8, + app_channel, + &mut hw_type, + &mut hw_index, + &mut hw_channel, + XL_BUS_TYPE_CAN, + ); + + match status as u32 { + XL_SUCCESS => Ok(ApplicationConfig { + hw_type: hw_type as i32, + hw_index: hw_index as i32, + hw_channel: hw_channel as i32, + }), + + _ => Err(Error::DriverError(format!( + "Failed to get application config with error code: {}", + status + ))), + } + } +}