Skip to content

Commit

Permalink
Merge pull request #79 from Dillonmcewan/DM-fix-signed-truncated-signals
Browse files Browse the repository at this point in the history
Fix issue with decoding signed values of non-standard length
  • Loading branch information
Pascal Hertleif authored Aug 20, 2024
2 parents a3fec55 + 874f360 commit b3fcfb7
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 14 deletions.
12 changes: 2 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ fn signal_from_payload(mut w: impl Write, signal: &Signal, msg: &Message) -> Res

format!(
"self.raw.view_bits::<Lsb0>()[{start}..{end}].load_le::<{typ}>()",
typ = signal_to_rust_uint(signal),
typ = signal_to_rust_int(signal),
start = start_bit,
end = end_bit,
)
Expand All @@ -842,7 +842,7 @@ fn signal_from_payload(mut w: impl Write, signal: &Signal, msg: &Message) -> Res

format!(
"self.raw.view_bits::<Msb0>()[{start}..{end}].load_be::<{typ}>()",
typ = signal_to_rust_uint(signal),
typ = signal_to_rust_int(signal),
start = start_bit,
end = end_bit
)
Expand All @@ -852,14 +852,6 @@ fn signal_from_payload(mut w: impl Write, signal: &Signal, msg: &Message) -> Res
writeln!(&mut w, r#"let signal = {};"#, read_fn)?;
writeln!(&mut w)?;

if *signal.value_type() == can_dbc::ValueType::Signed {
writeln!(
&mut w,
"let signal = {}::from_ne_bytes(signal.to_ne_bytes());",
signal_to_rust_int(signal)
)?;
};

if signal.signal_size == 1 {
writeln!(&mut w, "signal == 1")?;
} else if signal_is_float_in_rust(signal) {
Expand Down
228 changes: 224 additions & 4 deletions testing/can-messages/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ pub enum Messages {
LargerIntsWithOffsets(LargerIntsWithOffsets),
/// MsgWithoutSignals
MsgWithoutSignals(MsgWithoutSignals),
/// TruncatedBeSignal
TruncatedBeSignal(TruncatedBeSignal),
/// TruncatedLeSignal
TruncatedLeSignal(TruncatedLeSignal),
}

impl Messages {
Expand All @@ -59,6 +63,8 @@ impl Messages {
1344 => Messages::NegativeFactorTest(NegativeFactorTest::try_from(payload)?),
1338 => Messages::LargerIntsWithOffsets(LargerIntsWithOffsets::try_from(payload)?),
513 => Messages::MsgWithoutSignals(MsgWithoutSignals::try_from(payload)?),
9001 => Messages::TruncatedBeSignal(TruncatedBeSignal::try_from(payload)?),
9002 => Messages::TruncatedLeSignal(TruncatedLeSignal::try_from(payload)?),
n => return Err(CanError::UnknownMessageId(n)),
};
Ok(res)
Expand Down Expand Up @@ -159,9 +165,8 @@ impl Foo {
/// - Value type: Signed
#[inline(always)]
pub fn current_raw(&self) -> f32 {
let signal = self.raw.view_bits::<Lsb0>()[0..16].load_le::<u16>();
let signal = self.raw.view_bits::<Lsb0>()[0..16].load_le::<i16>();

let signal = i16::from_ne_bytes(signal.to_ne_bytes());
let factor = 0.0625_f32;
let offset = 0_f32;
(signal as f32) * factor + offset
Expand Down Expand Up @@ -1930,9 +1935,8 @@ impl NegativeFactorTest {
/// - Value type: Signed
#[inline(always)]
pub fn width_more_than_min_max_raw(&self) -> i16 {
let signal = self.raw.view_bits::<Lsb0>()[16..26].load_le::<u16>();
let signal = self.raw.view_bits::<Lsb0>()[16..26].load_le::<i16>();

let signal = i16::from_ne_bytes(signal.to_ne_bytes());
let factor = 1;
let signal = signal as i16;
i16::from(signal).saturating_mul(factor).saturating_add(0)
Expand Down Expand Up @@ -2215,6 +2219,222 @@ impl<'a> Arbitrary<'a> for MsgWithoutSignals {
}
}

/// TruncatedBeSignal
///
/// - ID: 9001 (0x2329)
/// - Size: 8 bytes
/// - Transmitter: Ipsum
#[derive(Clone, Copy)]
pub struct TruncatedBeSignal {
raw: [u8; 8],
}

impl TruncatedBeSignal {
pub const MESSAGE_ID: u32 = 9001;

pub const FOO_MIN: i16 = -100_i16;
pub const FOO_MAX: i16 = 100_i16;

/// Construct new TruncatedBeSignal from values
pub fn new(foo: i16) -> Result<Self, CanError> {
let mut res = Self { raw: [0u8; 8] };
res.set_foo(foo)?;
Ok(res)
}

/// Access message payload raw value
pub fn raw(&self) -> &[u8; 8] {
&self.raw
}

/// Foo
///
/// - Min: -100
/// - Max: 100
/// - Unit: ""
/// - Receivers: Vector__XXX
#[inline(always)]
pub fn foo(&self) -> i16 {
self.foo_raw()
}

/// Get raw value of Foo
///
/// - Start bit: 0
/// - Signal size: 12 bits
/// - Factor: 1
/// - Offset: 0
/// - Byte order: BigEndian
/// - Value type: Signed
#[inline(always)]
pub fn foo_raw(&self) -> i16 {
let signal = self.raw.view_bits::<Msb0>()[7..19].load_be::<i16>();

let factor = 1;
let signal = signal as i16;
i16::from(signal).saturating_mul(factor).saturating_add(0)
}

/// Set value of Foo
#[inline(always)]
pub fn set_foo(&mut self, value: i16) -> Result<(), CanError> {
if value < -100_i16 || 100_i16 < value {
return Err(CanError::ParameterOutOfRange { message_id: 9001 });
}
let factor = 1;
let value = value
.checked_sub(0)
.ok_or(CanError::ParameterOutOfRange { message_id: 9001 })?;
let value = (value / factor) as i16;

let value = u16::from_ne_bytes(value.to_ne_bytes());
self.raw.view_bits_mut::<Msb0>()[7..19].store_be(value);
Ok(())
}
}

impl core::convert::TryFrom<&[u8]> for TruncatedBeSignal {
type Error = CanError;

#[inline(always)]
fn try_from(payload: &[u8]) -> Result<Self, Self::Error> {
if payload.len() != 8 {
return Err(CanError::InvalidPayloadSize);
}
let mut raw = [0u8; 8];
raw.copy_from_slice(&payload[..8]);
Ok(Self { raw })
}
}

impl core::fmt::Debug for TruncatedBeSignal {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if f.alternate() {
f.debug_struct("TruncatedBeSignal")
.field("foo", &self.foo())
.finish()
} else {
f.debug_tuple("TruncatedBeSignal").field(&self.raw).finish()
}
}
}

#[cfg(feature = "arb")]
impl<'a> Arbitrary<'a> for TruncatedBeSignal {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self, arbitrary::Error> {
let foo = u.int_in_range(-100..=100)?;
TruncatedBeSignal::new(foo).map_err(|_| arbitrary::Error::IncorrectFormat)
}
}

/// TruncatedLeSignal
///
/// - ID: 9002 (0x232a)
/// - Size: 8 bytes
/// - Transmitter: Ipsum
#[derive(Clone, Copy)]
pub struct TruncatedLeSignal {
raw: [u8; 8],
}

impl TruncatedLeSignal {
pub const MESSAGE_ID: u32 = 9002;

pub const FOO_MIN: i16 = -100_i16;
pub const FOO_MAX: i16 = 100_i16;

/// Construct new TruncatedLeSignal from values
pub fn new(foo: i16) -> Result<Self, CanError> {
let mut res = Self { raw: [0u8; 8] };
res.set_foo(foo)?;
Ok(res)
}

/// Access message payload raw value
pub fn raw(&self) -> &[u8; 8] {
&self.raw
}

/// Foo
///
/// - Min: -100
/// - Max: 100
/// - Unit: ""
/// - Receivers: Vector__XXX
#[inline(always)]
pub fn foo(&self) -> i16 {
self.foo_raw()
}

/// Get raw value of Foo
///
/// - Start bit: 0
/// - Signal size: 12 bits
/// - Factor: 1
/// - Offset: 0
/// - Byte order: LittleEndian
/// - Value type: Signed
#[inline(always)]
pub fn foo_raw(&self) -> i16 {
let signal = self.raw.view_bits::<Lsb0>()[0..12].load_le::<i16>();

let factor = 1;
let signal = signal as i16;
i16::from(signal).saturating_mul(factor).saturating_add(0)
}

/// Set value of Foo
#[inline(always)]
pub fn set_foo(&mut self, value: i16) -> Result<(), CanError> {
if value < -100_i16 || 100_i16 < value {
return Err(CanError::ParameterOutOfRange { message_id: 9002 });
}
let factor = 1;
let value = value
.checked_sub(0)
.ok_or(CanError::ParameterOutOfRange { message_id: 9002 })?;
let value = (value / factor) as i16;

let value = u16::from_ne_bytes(value.to_ne_bytes());
self.raw.view_bits_mut::<Lsb0>()[0..12].store_le(value);
Ok(())
}
}

impl core::convert::TryFrom<&[u8]> for TruncatedLeSignal {
type Error = CanError;

#[inline(always)]
fn try_from(payload: &[u8]) -> Result<Self, Self::Error> {
if payload.len() != 8 {
return Err(CanError::InvalidPayloadSize);
}
let mut raw = [0u8; 8];
raw.copy_from_slice(&payload[..8]);
Ok(Self { raw })
}
}

impl core::fmt::Debug for TruncatedLeSignal {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if f.alternate() {
f.debug_struct("TruncatedLeSignal")
.field("foo", &self.foo())
.finish()
} else {
f.debug_tuple("TruncatedLeSignal").field(&self.raw).finish()
}
}
}

#[cfg(feature = "arb")]
impl<'a> Arbitrary<'a> for TruncatedLeSignal {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self, arbitrary::Error> {
let foo = u.int_in_range(-100..=100)?;
TruncatedLeSignal::new(foo).map_err(|_| arbitrary::Error::IncorrectFormat)
}
}

/// This is just to make testing easier
#[allow(dead_code)]
fn main() {}
Expand Down
19 changes: 19 additions & 0 deletions testing/can-messages/tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use can_messages::{
Amet, Bar, BarThree, CanError, Foo, LargerIntsWithOffsets, MultiplexTest,
MultiplexTestMultiplexorIndex, MultiplexTestMultiplexorM0, NegativeFactorTest,
TruncatedBeSignal, TruncatedLeSignal,
};

#[test]
Expand Down Expand Up @@ -75,6 +76,24 @@ fn pack_unpack_message_containing_multiplexed_signals() {
}
}

#[test]
fn pack_unpack_signed_truncated_signal() {
let result = TruncatedBeSignal::new(42).unwrap();
assert_eq!(result.foo_raw(), 42);

let result = TruncatedLeSignal::new(42).unwrap();
assert_eq!(result.foo_raw(), 42);
}

#[test]
fn pack_unpack_signed_truncated_signal_negative() {
let result = TruncatedBeSignal::new(-42).unwrap();
assert_eq!(result.foo_raw(), -42);

let result = TruncatedLeSignal::new(-42).unwrap();
assert_eq!(result.foo_raw(), -42);
}

#[test]
fn offset_integers() {
let mut m = LargerIntsWithOffsets::new(100, 30000).unwrap();
Expand Down
6 changes: 6 additions & 0 deletions testing/dbc-examples/example.dbc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ BO_ 1338 LargerIntsWithOffsets: 8 Sit

BO_ 513 MsgWithoutSignals: 8 Ipsum

BO_ 9001 TruncatedBeSignal: 8 Ipsum
SG_ Foo : 0|12@0- (1,0) [-100|100] "" Vector__XXX

BO_ 9002 TruncatedLeSignal: 8 Ipsum
SG_ Foo : 0|12@1- (1,0) [-100|100] "" Vector__XXX

VAL_ 512 Three 0 "OFF" 1 "ON" 2 "ONER" 3 "ONEST";
VAL_ 512 Four 0 "Off" 1 "On" 2 "Oner" 3 "Onest";
VAL_ 512 Type 0 "0Off" 1 "1On";
Expand Down

0 comments on commit b3fcfb7

Please sign in to comment.