if_alloc! {
use crate::alloc::{string::String, fmt::format};
}
use bitfield_struct::bitfield;
use crate::{
conversion::Conversion,
identifier::{Id, IsProtocol},
};
use super::address::SourceAddr;
#[bitfield(u32, order = Msb)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct J1939 {
#[bits(3)]
__: u8,
#[bits(3)]
priority_bits: u8,
#[bits(1)]
reserved_bits: bool,
#[bits(1)]
data_page_bits: bool,
#[bits(8)]
pdu_format_bits: u8,
#[bits(8)]
pdu_specific_bits: u8,
#[bits(8)]
source_address_bits: u8,
}
impl IsProtocol for J1939 {}
pub type IdJ1939 = Id<J1939>;
impl Conversion<u32> for IdJ1939 {
type Error = anyhow::Error;
#[inline]
fn from_bits(bits: u32) -> Self {
let bitfield = J1939(bits);
Self(bitfield)
}
#[inline]
fn from_hex(hex_str: &str) -> Self {
let bits = u32::from_str_radix(hex_str, 16).unwrap_or_default();
let bitfield = J1939(bits);
Self(bitfield)
}
#[inline]
fn try_from_bits(bits: u32) -> Result<Self, Self::Error> {
if bits > 0x1FFF_FFFF {
return Err(anyhow::anyhow!(
"Identifier bits out of range! Valid range is 0..536870911 - got {}",
bits
));
}
let bitfield = J1939(bits);
Ok(Self(bitfield))
}
#[inline]
fn try_from_hex(hex_str: &str) -> Result<Self, Self::Error> {
let bits = u32::from_str_radix(hex_str, 16).map_err(anyhow::Error::msg)?;
if bits > 0x1FFF_FFFF {
return Err(anyhow::anyhow!(
"Identifier bits out of range! Valid range is 0x00000000..0x1FFFFFFF - got {:#08X}",
bits
));
}
let bitfield = J1939(bits);
Ok(Self(bitfield))
}
#[inline]
fn into_bits(self) -> u32 {
self.0.into_bits()
}
#[inline]
#[cfg(feature = "alloc")]
fn into_hex(self) -> String {
format(format_args!("{:08X}", self.0.into_bits()))
}
}
impl IdJ1939 {
#[must_use]
pub const fn into_raw_parts(self) -> (u8, bool, bool, u8, u8, u8) {
let p = self.0.priority_bits();
let r = self.0.reserved_bits();
let dp = self.0.data_page_bits();
let pf = self.0.pdu_format_bits();
let ps = self.0.pdu_specific_bits();
let sa = self.0.source_address_bits();
(p, r, dp, pf, ps, sa)
}
pub fn from_raw_parts(
priority: u8,
reserved: bool,
data_page: bool,
pdu_format: u8,
pdu_specific: u8,
source_addr: u8,
) -> Result<Self, anyhow::Error> {
if priority > 0x7 {
return Err(anyhow::anyhow!(
"Invalid priority! The priority value must be between 0 and 7 inclusive - got {}.",
priority
));
}
let bitfield = J1939::new()
.with_priority_bits(priority)
.with_reserved_bits(reserved)
.with_data_page_bits(data_page)
.with_pdu_format_bits(pdu_format)
.with_pdu_specific_bits(pdu_specific)
.with_source_address_bits(source_addr);
Ok(Self(bitfield))
}
#[inline]
#[must_use]
pub const fn priority(&self) -> u8 {
self.0.priority_bits()
}
#[inline]
#[must_use]
pub const fn reserved(&self) -> bool {
self.0.reserved_bits()
}
#[inline]
#[must_use]
pub const fn data_page(&self) -> bool {
self.0.data_page_bits()
}
#[inline]
#[must_use]
pub const fn pdu_format(&self) -> u8 {
self.0.pdu_format_bits()
}
#[inline]
#[must_use]
pub const fn pdu_specific(&self) -> u8 {
self.0.pdu_specific_bits()
}
#[inline]
#[must_use]
pub fn source_address(&self) -> SourceAddr {
SourceAddr::Some(self.0.source_address_bits())
}
}
#[cfg(test)]
mod j1939_tests {
use super::*;
#[test]
fn test_from_bits() {
let id_a = IdJ1939::from_bits(16711935);
assert_eq!(0b000_0_0_11111111_00000000_11111111, id_a.0 .0)
}
#[test]
fn test_from_hex() {
let id_a = IdJ1939::from_hex("00FF00FF");
assert_eq!(0b000_0_0_11111111_00000000_11111111, id_a.0 .0)
}
#[test]
fn test_try_from_bits() {
let id_a = IdJ1939::try_from_bits(16711935).unwrap();
let id_b = IdJ1939::try_from_bits(536870912);
assert_eq!(0b000_0_0_11111111_00000000_11111111, id_a.0 .0);
assert!(id_b.is_err())
}
#[test]
fn test_try_from_hex() {
let id_a = IdJ1939::try_from_hex("00FF00FF").unwrap();
let id_b = IdJ1939::try_from_hex("20000000");
assert_eq!(0b000_0_0_11111111_00000000_11111111, id_a.0 .0);
assert!(id_b.is_err())
}
#[test]
fn test_into_bits() {
let id_a = IdJ1939::from_bits(16711935);
assert_eq!(16711935, id_a.into_bits())
}
#[cfg(feature = "alloc")]
#[test]
fn test_into_hex() {
let id_a = IdJ1939::from_bits(16711935);
assert_eq!("00FF00FF", id_a.into_hex())
}
}