From 14410b39aae7906375661d005b2a73891632bef6 Mon Sep 17 00:00:00 2001 From: marcelbuesing Date: Thu, 25 Mar 2021 07:26:25 +0100 Subject: [PATCH] Start multiplexed signal implementation --- src/lib.rs | 173 ++++++++++++++++++++++- testing/can-messages/src/messages.rs | 199 +++++++++++++++++++++++++++ testing/dbc-examples/example.dbc | 11 ++ 3 files changed, 378 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6849bda..c03d2c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,10 +7,13 @@ #![deny(missing_docs, clippy::integer_arithmetic)] use anyhow::{anyhow, ensure, Context, Result}; -use can_dbc::{Message, Signal, ValDescription, ValueDescription, DBC}; +use can_dbc::{Message, MultiplexIndicator, Signal, ValDescription, ValueDescription, DBC}; use heck::{CamelCase, SnakeCase}; use pad::PadAdapter; -use std::io::{BufWriter, Write}; +use std::{ + collections::{HashMap, HashSet}, + io::{BufWriter, Write}, +}; mod includes; mod keywords; @@ -215,10 +218,77 @@ fn render_message(mut w: impl Write, msg: &Message, dbc: &DBC) -> Result<()> { writeln!(w)?; for signal in msg.signals().iter() { - render_signal(&mut w, signal, dbc, msg) - .with_context(|| format!("write signal impl `{}`", signal.name()))?; + match signal.multiplexer_indicator() { + MultiplexIndicator::Plain => render_plain_signal(&mut w, signal, dbc, msg) + .with_context(|| format!("write signal impl `{}`", signal.name()))?, + MultiplexIndicator::Multiplexor => { + writeln!(w, "/// Get raw value of {}", signal.name())?; + writeln!(w, "///")?; + writeln!(w, "/// - Start bit: {}", signal.start_bit)?; + writeln!(w, "/// - Signal size: {} bits", signal.signal_size)?; + writeln!(w, "/// - Factor: {}", signal.factor)?; + writeln!(w, "/// - Offset: {}", signal.offset)?; + writeln!(w, "/// - Byte order: {:?}", signal.byte_order())?; + writeln!(w, "/// - Value type: {:?}", signal.value_type())?; + writeln!(w, "#[inline(always)]")?; + writeln!( + w, + "pub fn {}_raw(&self) -> {} {{", + field_name(signal.name()), + signal_to_rust_type(&signal) + )?; + { + let mut w = PadAdapter::wrap(&mut w); + signal_from_payload(&mut w, signal, msg).context("signal from payload")?; + } + writeln!(&mut w, "}}")?; + writeln!(w)?; + + writeln!( + w, + "pub fn {}(&self) -> {} {{", + field_name(signal.name()), + multiplex_enum_name(msg, signal)? + )?; + { + let mut w = PadAdapter::wrap(&mut w); + writeln!( + &mut w, + "match self.{}_raw() {{", + field_name(signal.name()) + )?; + + let multiplexer_indexes: HashSet = msg + .signals() + .iter() + .filter_map(|s| { + if let MultiplexIndicator::MultiplexedSignal(index) = + s.multiplexer_indicator() + { + Some(index) + } else { + None + } + }) + .cloned() + .collect(); + + { + let mut w = PadAdapter::wrap(&mut w); + for multiplexer_index in multiplexer_indexes { + writeln!(&mut w, "{idx} => {signal_name}M{idx}{{ raw: &self.raw }},", signal_name = signal.name().to_camel_case(), idx = multiplexer_index)?; + } + } + + writeln!(w, "}}")?; + } + writeln!(w, "}}")?; + } + MultiplexIndicator::MultiplexedSignal(_) => {} + } } } + writeln!(w, "}}")?; writeln!(w)?; @@ -280,10 +350,19 @@ fn render_message(mut w: impl Write, msg: &Message, dbc: &DBC) -> Result<()> { write_enum(&mut w, signal, msg, variants.as_slice())?; } + let multiplexor_signal = msg + .signals() + .iter() + .find(|s| *s.multiplexer_indicator() == MultiplexIndicator::Multiplexor); + + if let Some(multiplexor_signal) = multiplexor_signal { + render_multiplexor_enums(w, msg, multiplexor_signal)?; + } + Ok(()) } -fn render_signal(mut w: impl Write, signal: &Signal, dbc: &DBC, msg: &Message) -> Result<()> { +fn render_plain_signal(mut w: impl Write, signal: &Signal, dbc: &DBC, msg: &Message) -> Result<()> { writeln!(w, "/// {}", signal.name())?; if let Some(comment) = dbc.signal_comment(*msg.message_id(), &signal.name()) { writeln!(w, "///")?; @@ -703,6 +782,26 @@ fn enum_variant_name(x: &str) -> String { } } +fn multiplex_enum_name(msg: &Message, multiplexor: &Signal) -> Result { + ensure!( + matches!( + multiplexor.multiplexer_indicator(), + MultiplexIndicator::Multiplexor + ), + "signal {:?} is not the multiplexor", + multiplexor + ); + Ok(format!( + "{}{}", + msg.message_name().to_camel_case(), + multiplexor.name().to_camel_case() + )) +} + +fn multiplex_enum_variant_name(switch_index: u64) -> String { + format!("M{}", switch_index) +} + fn render_debug_impl(mut w: impl Write, msg: &Message) -> Result<()> { let typ = type_name(msg.message_name()); writeln!(w, r##"#[cfg(feature = "debug")]"##)?; @@ -745,6 +844,70 @@ fn render_debug_impl(mut w: impl Write, msg: &Message) -> Result<()> { Ok(()) } +fn render_multiplexor_enums( + mut w: impl Write, + msg: &Message, + multiplexor_signal: &Signal, +) -> Result<()> { + ensure!( + *multiplexor_signal.multiplexer_indicator() == MultiplexIndicator::Multiplexor, + "signal {} is not the multiplexor", + multiplexor_signal.name(), + ); + + let mut multiplexed_signals = HashMap::new(); + for signal in msg.signals() { + if let MultiplexIndicator::MultiplexedSignal(switch_index) = signal.multiplexer_indicator() + { + multiplexed_signals + .entry(switch_index) + .and_modify(|v: &mut Vec<&Signal>| v.push(&signal)) + .or_insert(vec![&signal]); + } + } + + writeln!( + w, + "/// Defined values for multiplexed signal {}", + msg.message_name() + )?; + writeln!(w, "#[derive(Clone, Copy, PartialEq)]")?; + writeln!(w, r##"#[cfg_attr(feature = "debug", derive(Debug))]"##)?; + + writeln!( + w, + "pub enum {} {{", + multiplex_enum_name(msg, multiplexor_signal)? + )?; + + { + let mut w = PadAdapter::wrap(&mut w); + for (switch_index, _multiplexed_signals) in multiplexed_signals.iter() { + let multiplexed_name = multiplexor_signal.name().to_camel_case(); + writeln!( + w, + "{multiplexed_name}M{idx}({multiplexed_name}M{idx}),", + idx = switch_index, + multiplexed_name = multiplexed_name + )?; + } + } + writeln!(w, "}}")?; + + for (switch_index, _multiplexed_signals) in multiplexed_signals.iter() { + let multiplexed_name = multiplexor_signal.name().to_camel_case(); + writeln!(w, "#[derive(Clone, Copy)]")?; + writeln!( + w, + "struct {multiplexed_name}M{idx} {{ raw: &[u8] }}", + idx = switch_index, + multiplexed_name = multiplexed_name + )?; + } + + Ok(()) +} + fn render_arbitrary(mut w: impl Write, msg: &Message) -> Result<()> { writeln!(w, r##"#[cfg(feature = "arb")]"##)?; writeln!( diff --git a/testing/can-messages/src/messages.rs b/testing/can-messages/src/messages.rs index 6b5a7cb..49bb064 100644 --- a/testing/can-messages/src/messages.rs +++ b/testing/can-messages/src/messages.rs @@ -29,6 +29,8 @@ pub enum Messages { Amet(Amet), /// Dolor Dolor(Dolor), + /// SENSOR_SONARS + SensorSonars(SensorSonars), } impl Messages { @@ -42,6 +44,7 @@ impl Messages { 512 => Messages::Bar(Bar::try_from(payload)?), 1024 => Messages::Amet(Amet::try_from(payload)?), 1028 => Messages::Dolor(Dolor::try_from(payload)?), + 200 => Messages::SensorSonars(SensorSonars::try_from(payload)?), n => return Err(CanError::UnknownMessageId(n)), }; Ok(res) @@ -952,6 +955,202 @@ impl Into for DolorOneFloat { } } +/// SENSOR_SONARS +/// +/// - ID: 200 (0xc8) +/// - Size: 8 bytes +/// - Transmitter: SENSOR +#[derive(Clone, Copy)] +pub struct SensorSonars { + raw: [u8; 8], +} + +impl SensorSonars { + pub const MESSAGE_ID: u32 = 200; + + /// Construct new SENSOR_SONARS from values + pub fn new( + sensor_sonars_mux: u8, + sensor_sonars_err_count: u16, + sensor_sonars_left: f32, + sensor_sonars_middle: f32, + sensor_sonars_right: f32, + sensor_sonars_rear: f32, + sensor_sonars_no_filt_left: f32, + sensor_sonars_no_filt_middle: f32, + sensor_sonars_no_filt_right: f32, + sensor_sonars_no_filt_rear: f32, + ) -> Result { + let mut res = Self { raw: [0u8; 8] }; + res.set_sensor_sonars_mux(sensor_sonars_mux)?; + res.set_sensor_sonars_err_count(sensor_sonars_err_count)?; + res.set_sensor_sonars_left(sensor_sonars_left)?; + res.set_sensor_sonars_middle(sensor_sonars_middle)?; + res.set_sensor_sonars_right(sensor_sonars_right)?; + res.set_sensor_sonars_rear(sensor_sonars_rear)?; + res.set_sensor_sonars_no_filt_left(sensor_sonars_no_filt_left)?; + res.set_sensor_sonars_no_filt_middle(sensor_sonars_no_filt_middle)?; + res.set_sensor_sonars_no_filt_right(sensor_sonars_no_filt_right)?; + res.set_sensor_sonars_no_filt_rear(sensor_sonars_no_filt_rear)?; + Ok(res) + } + + /// Access message payload raw value + pub fn raw(&self) -> &[u8] { + &self.raw + } + + /// Get raw value of SENSOR_SONARS_mux + /// + /// - Start bit: 0 + /// - Signal size: 4 bits + /// - Factor: 1 + /// - Offset: 0 + /// - Byte order: LittleEndian + /// - Value type: Unsigned + #[inline(always)] + pub fn sensor_sonars_mux_raw(&self) -> u8 { + let signal = self.raw.view_bits::()[0..4].load_le::(); + + signal + } + + pub fn sensor_sonars_mux(&self) -> SensorSonarsSensorSonarsMux { + match self.sensor_sonars_mux_raw() { + 0 => SensorSonarsMuxM0 { raw: &self.raw }, + 1 => SensorSonarsMuxM1 { raw: &self.raw }, + } + } + /// SENSOR_SONARS_err_count + /// + /// - Min: 0 + /// - Max: 0 + /// - Unit: "" + /// - Receivers: DRIVER, IO + #[inline(always)] + pub fn sensor_sonars_err_count(&self) -> u16 { + self.sensor_sonars_err_count_raw() + } + + /// Get raw value of SENSOR_SONARS_err_count + /// + /// - Start bit: 4 + /// - Signal size: 12 bits + /// - Factor: 1 + /// - Offset: 0 + /// - Byte order: LittleEndian + /// - Value type: Unsigned + #[inline(always)] + pub fn sensor_sonars_err_count_raw(&self) -> u16 { + let signal = self.raw.view_bits::()[4..16].load_le::(); + + signal + } + + /// Set value of SENSOR_SONARS_err_count + #[inline(always)] + pub fn set_sensor_sonars_err_count(&mut self, value: u16) -> Result<(), CanError> { + #[cfg(feature = "range_checked")] + if value < 0_u16 || 0_u16 < value { + return Err(CanError::ParameterOutOfRange { message_id: 200 }); + } + self.raw.view_bits_mut::()[4..16].store_le(value); + Ok(()) + } +} + +impl core::convert::TryFrom<&[u8]> for SensorSonars { + type Error = CanError; + + #[inline(always)] + fn try_from(payload: &[u8]) -> Result { + if payload.len() != 8 { + return Err(CanError::InvalidPayloadSize); + } + let mut raw = [0u8; 8]; + raw.copy_from_slice(&payload[..8]); + Ok(Self { raw }) + } +} + +#[cfg(feature = "debug")] +impl core::fmt::Debug for SensorSonars { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + if f.alternate() { + f.debug_struct("SensorSonars") + .field("sensor_sonars_mux", &self.sensor_sonars_mux()) + .field("sensor_sonars_err_count", &self.sensor_sonars_err_count()) + .field("sensor_sonars_left", &self.sensor_sonars_left()) + .field("sensor_sonars_middle", &self.sensor_sonars_middle()) + .field("sensor_sonars_right", &self.sensor_sonars_right()) + .field("sensor_sonars_rear", &self.sensor_sonars_rear()) + .field( + "sensor_sonars_no_filt_left", + &self.sensor_sonars_no_filt_left(), + ) + .field( + "sensor_sonars_no_filt_middle", + &self.sensor_sonars_no_filt_middle(), + ) + .field( + "sensor_sonars_no_filt_right", + &self.sensor_sonars_no_filt_right(), + ) + .field( + "sensor_sonars_no_filt_rear", + &self.sensor_sonars_no_filt_rear(), + ) + .finish() + } else { + f.debug_tuple("SensorSonars").field(&self.raw).finish() + } + } +} + +#[cfg(feature = "arb")] +impl<'a> Arbitrary<'a> for SensorSonars { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + let sensor_sonars_mux = u.int_in_range(0..=0)?; + let sensor_sonars_err_count = u.int_in_range(0..=0)?; + let sensor_sonars_left = u.float_in_range(0_f32..=0_f32)?; + let sensor_sonars_middle = u.float_in_range(0_f32..=0_f32)?; + let sensor_sonars_right = u.float_in_range(0_f32..=0_f32)?; + let sensor_sonars_rear = u.float_in_range(0_f32..=0_f32)?; + let sensor_sonars_no_filt_left = u.float_in_range(0_f32..=0_f32)?; + let sensor_sonars_no_filt_middle = u.float_in_range(0_f32..=0_f32)?; + let sensor_sonars_no_filt_right = u.float_in_range(0_f32..=0_f32)?; + let sensor_sonars_no_filt_rear = u.float_in_range(0_f32..=0_f32)?; + SensorSonars::new( + sensor_sonars_mux, + sensor_sonars_err_count, + sensor_sonars_left, + sensor_sonars_middle, + sensor_sonars_right, + sensor_sonars_rear, + sensor_sonars_no_filt_left, + sensor_sonars_no_filt_middle, + sensor_sonars_no_filt_right, + sensor_sonars_no_filt_rear, + ) + .map_err(|_| arbitrary::Error::IncorrectFormat) + } +} +/// Defined values for multiplexed signal SENSOR_SONARS +#[derive(Clone, Copy, PartialEq)] +#[cfg_attr(feature = "debug", derive(Debug))] +pub enum SensorSonarsSensorSonarsMux { + SensorSonarsMuxM1(SensorSonarsMuxM1), + SensorSonarsMuxM0(SensorSonarsMuxM0), +} +#[derive(Clone, Copy)] +struct SensorSonarsMuxM1 { + raw: &[u8], +} +#[derive(Clone, Copy)] +struct SensorSonarsMuxM0 { + raw: &[u8], +} + /// This is just to make testing easier #[allow(dead_code)] fn main() {} diff --git a/testing/dbc-examples/example.dbc b/testing/dbc-examples/example.dbc index 3a1773e..0d6a16d 100644 --- a/testing/dbc-examples/example.dbc +++ b/testing/dbc-examples/example.dbc @@ -29,6 +29,17 @@ BO_ 1024 Amet: 8 Sit BO_ 1028 Dolor: 8 Sit SG_ OneFloat : 0|12@0+ (0.5,0) [0.00|130.00] "" Vector__XXX +BO_ 200 SENSOR_SONARS: 8 SENSOR + SG_ SENSOR_SONARS_mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_err_count : 4|12@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_left m0 : 16|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_middle m0 : 28|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_right m0 : 40|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_rear m0 : 52|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ SENSOR_SONARS_no_filt_left m1 : 16|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_middle m1 : 28|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_right m1 : 40|12@1+ (0.1,0) [0|0] "" DBG + SG_ SENSOR_SONARS_no_filt_rear m1 : 52|12@1+ (0.1,0) [0|0] "" DBG VAL_ 512 Three 0 "OFF" 1 "ON" 2 "ONER" 3 "ONEST"; VAL_ 512 Four 0 "Off" 1 "On" 2 "Oner" 3 "Onest";