diff --git a/Cargo.toml b/Cargo.toml index 9fd514b..265d502 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ with-serde = ["serde"] [dependencies] can-dbc-pest = { version = "0.6.0" } encoding_rs = { version = "0.8", optional = true } +num = { version = "0.4.3", default-features = false } serde = { version = "1.0", features = ["derive"], optional = true } thiserror = "2.0.17" diff --git a/src/extend/mod.rs b/src/extend/mod.rs new file mode 100644 index 0000000..c15db8b --- /dev/null +++ b/src/extend/mod.rs @@ -0,0 +1,35 @@ +mod signal; + +/// DBC value +#[derive(Debug, Clone)] +pub struct Value { + pub name: String, + pub raw: u64, + pub offset: f64, + pub factor: f64, + pub unit: String, +} + +// TODO: decide if these are really needed +// if added, the entire crate cannot use unsafe_code = "forbid" in Cargo.toml +// unsafe impl Send for Value {} +// unsafe impl Sync for Value {} + +impl Value { + /// Get the value of the signal + #[inline] + pub fn value(&self) -> T + where + T: num::NumCast + Default, + { + T::from(self.raw as f64 * self.factor + self.offset).unwrap_or_default() + } + /// Get the value and unit of the signal as a string + #[inline] + pub fn value_string(&self) -> String + where + T: num::NumCast + Default + std::fmt::Display, + { + format!("{} {}", self.value::(), self.unit) + } +} diff --git a/src/extend/signal.rs b/src/extend/signal.rs new file mode 100644 index 0000000..fbc088c --- /dev/null +++ b/src/extend/signal.rs @@ -0,0 +1,156 @@ +use super::Value; +use crate::{ByteOrder, Signal, ValueType}; + +impl Signal { + /// Decodes/Extracts a signal from the message data as a [`Value`] + /// + /// # Params + /// + /// * data: source data + /// + /// # Return + /// + /// [`Value`] if the size of signal is not zero + pub fn decode_value(&self, data: &[u8]) -> Option { + self.decode(data).map(|v| Value { + name: self.name.clone(), + raw: v, + offset: self.offset, + factor: self.factor, + unit: self.unit.clone(), + }) + } + + /// Decodes/Extracts a signal from the message data + /// + /// # Params + /// + /// * data: source data + /// + /// # Return + /// + /// Raw signal value if the size of signal is not zero + pub fn decode(&self, data: &[u8]) -> Option { + if self.size == 0 { + return None; + } + + let len = data.len(); + let mut result = 0; + match self.byte_order { + ByteOrder::LittleEndian => { + let mut src_bit = self.start_bit as usize; + let mut dst_bit = 0; + for _ in 0..self.size { + /* copy bit */ + let index = src_bit / 8; + if index >= len { + return None; + } + if (data[index] & (1 << (src_bit % 8))) > 0 { + result |= 1 << dst_bit; + } + + /* calculate next position */ + src_bit += 1; + dst_bit += 1; + } + } + ByteOrder::BigEndian => { + let mut src_bit = self.start_bit as usize; + let mut dst_bit = self.size - 1; + for _ in 0..self.size { + /* copy bit */ + let index = src_bit / 8; + if index >= len { + return None; + } + if (data[index] & (1 << (src_bit % 8))) > 0 { + result |= 1 << dst_bit; + } + + /* calculate next position */ + if (src_bit % 8) == 0 { + src_bit += 15; + } else { + src_bit -= 1; + } + dst_bit -= 1; + } + } + } + + match self.value_type { + ValueType::Signed => { + if (result & (1 << (self.size - 1))) > 0 { + for i in self.size..64 { + result |= 1 << i; + } + } + } + ValueType::Unsigned => {} + } + + Some(result) + } + + /// Encodes a signal into the message data + /// + /// # Params + /// + /// * data: source data + /// + /// * value: Raw signal value + pub fn encode(&self, data: &mut Vec, value: u64) { + if self.size == 0 { + return; + } + + match self.byte_order { + ByteOrder::LittleEndian => { + let mut src_bit = self.start_bit as usize; + let mut dst_bit = 0; + for _ in 0..self.size { + /* copy bit */ + let index = src_bit / 8; + if index >= data.len() { + data.resize(index + 1, 0); + } + if (value & 1 << dst_bit) > 0 { + data[index] |= 1 << (src_bit % 8); + } else { + data[index] &= !(1 << (src_bit % 8)); + } + + /* calculate next position */ + src_bit += 1; + dst_bit += 1; + } + } + ByteOrder::BigEndian => { + let mut src_bit = self.start_bit as usize; + let mut dst_bit = self.size - 1; + for _ in 0..self.size { + /* copy bit */ + let index = src_bit / 8; + if index >= data.len() { + data.resize(index + 1, 0); + } + if (value & 1 << dst_bit) > 0 { + data[index] |= 1 << (dst_bit % 8); + } else { + data[index] &= !(1 << (src_bit % 8)); + } + + /* calculate next position */ + if (src_bit % 8) == 0 { + src_bit += 15; + } else { + src_bit -= 1; + } + dst_bit -= 1; + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 702130f..e1e3ab5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,9 @@ #![doc = include_str!("../README.md")] mod ast; +mod extend; mod parser; + #[cfg(test)] mod parser_tests; @@ -10,6 +12,7 @@ pub use ast::*; // Re-export of `encoding_rs` as encodings to simplify usage #[cfg(feature = "encodings")] pub use encoding_rs as encodings; +pub use extend::*; #[cfg(feature = "encodings")] pub use parser::decode_cp1252; pub use parser::{DbcError, DbcResult};