if_alloc! {
use crate::alloc::{string::String, fmt::format};
}
use bitfield_struct::bitfield;
use crate::{conversion::Conversion, identifier::Id, protocol::j1939::identifier::J1939};
use super::address::DestinationAddr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PduAssignment {
Sae(u32),
Manufacturer(u32),
Unknown(u32),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PduFormat {
Pdu1(u8),
Pdu2(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CommunicationMode {
P2P,
Broadcast,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GroupExtension {
None,
Some(u8),
}
#[bitfield(u32, order = Msb, conversion = false)]
#[derive(PartialEq, Eq)]
pub struct Pgn {
#[bits(14)]
__: u16,
#[bits(1)]
reserved_bits: bool,
#[bits(1)]
data_page_bits: bool,
#[bits(8)]
pdu_format_bits: u8,
#[bits(8)]
pdu_specific_bits: u8,
}
impl Conversion<u32> for Pgn {
type Error = anyhow::Error;
#[inline]
fn from_bits(bits: u32) -> Self {
Self(bits)
}
#[inline]
fn from_hex(hex_str: &str) -> Self {
let bits = u32::from_str_radix(hex_str, 16).unwrap_or_default();
Self(bits)
}
#[inline]
fn try_from_bits(bits: u32) -> Result<Self, Self::Error> {
if bits > 0x3FFFF {
return Err(anyhow::anyhow!(
"PGN bits out of range! Valid range is 0x0000..0xFFFF - got {bits:#04X}"
));
}
Ok(Self(bits))
}
#[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 > 0x3FFFF {
return Err(anyhow::anyhow!(
"PGN bits out of range! Valid range is 0x0000..0xFFFF - got {bits:#04X}"
));
}
Ok(Self(bits))
}
#[inline]
fn into_bits(self) -> u32 {
self.0
}
#[inline]
#[cfg(feature = "alloc")]
fn into_hex(self) -> String {
format(format_args!("{:05X}", self.into_bits()))
}
}
impl Pgn {
#[inline]
#[must_use]
pub const fn pdu_format(&self) -> PduFormat {
match (self.pdu_format_bits() < 240, self.pdu_format_bits()) {
(true, a) => PduFormat::Pdu1(a),
(false, b) => PduFormat::Pdu2(b),
}
}
#[inline]
#[must_use]
pub const fn group_extension(&self) -> GroupExtension {
match self.pdu_format() {
PduFormat::Pdu1(_) => GroupExtension::None,
PduFormat::Pdu2(_) => GroupExtension::Some(self.pdu_specific_bits()),
}
}
#[inline]
#[must_use]
pub const fn destination_address(&self) -> DestinationAddr {
match self.pdu_format() {
PduFormat::Pdu1(_) => DestinationAddr::Some(self.pdu_specific_bits()),
PduFormat::Pdu2(_) => DestinationAddr::None,
}
}
#[inline]
#[must_use]
pub const fn communication_mode(&self) -> CommunicationMode {
match self.pdu_format() {
PduFormat::Pdu1(_) => CommunicationMode::P2P,
PduFormat::Pdu2(_) => CommunicationMode::Broadcast,
}
}
#[inline]
#[must_use]
pub const fn is_p2p(&self) -> bool {
match self.communication_mode() {
CommunicationMode::P2P => true,
CommunicationMode::Broadcast => false,
}
}
#[inline]
#[must_use]
pub const fn is_broadcast(&self) -> bool {
match self.communication_mode() {
CommunicationMode::P2P => false,
CommunicationMode::Broadcast => true,
}
}
#[must_use]
pub fn pdu_assignment(&self) -> PduAssignment {
match self.into_bits() {
0x0000_0000..=0x0000_EE00
| 0x0000_F000..=0x0000_FEFF
| 0x0001_0000..=0x0001_EE00
| 0x0001_F000..=0x0001_FEFF => PduAssignment::Sae(self.into_bits()),
0x0000_EF00 | 0x0000_FF00..=0x0000_FFFF | 0x0001_EF00 | 0x0001_FF00..=0x0001_FFFF => {
PduAssignment::Manufacturer(self.into_bits())
}
p => PduAssignment::Unknown(p),
}
}
}
impl Id<J1939> {
#[inline]
#[must_use]
pub const fn pgn_bits(&self) -> u32 {
let pgn_bitfield = Pgn::new()
.with_reserved_bits(self.reserved())
.with_data_page_bits(self.data_page())
.with_pdu_format_bits(self.pdu_format())
.with_pdu_specific_bits(self.pdu_specific());
pgn_bitfield.0
}
#[inline]
#[must_use]
pub const fn pgn(&self) -> Pgn {
Pgn::new()
.with_reserved_bits(self.reserved())
.with_data_page_bits(self.data_page())
.with_pdu_format_bits(self.pdu_format())
.with_pdu_specific_bits(self.pdu_specific())
}
}
#[cfg(test)]
mod pgn_tests {
use super::*;
use crate::protocol::j1939::address::Addr;
#[test]
fn test_pdu_assignment() -> Result<(), anyhow::Error> {
let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
let assignment_a = id_a.pgn().pdu_assignment();
let assignment_b = id_b.pgn().pdu_assignment();
let assignment_c = id_c.pgn().pdu_assignment();
let assignment_d = id_d.pgn().pdu_assignment();
assert_eq!(PduAssignment::Sae(65266), assignment_a);
assert_eq!(PduAssignment::Sae(65170), assignment_b);
assert_eq!(PduAssignment::Manufacturer(65313), assignment_c);
assert_eq!(PduAssignment::Sae(41), assignment_d);
Ok(())
}
#[test]
fn test_communication_mode() -> Result<(), anyhow::Error> {
let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
let comms_mode_a = id_a.pgn().communication_mode();
let comms_mode_b = id_b.pgn().communication_mode();
let comms_mode_c = id_c.pgn().communication_mode();
let comms_mode_d = id_d.pgn().communication_mode();
assert_eq!(CommunicationMode::Broadcast, comms_mode_a);
assert_eq!(CommunicationMode::Broadcast, comms_mode_b);
assert_eq!(CommunicationMode::Broadcast, comms_mode_c);
assert_eq!(CommunicationMode::P2P, comms_mode_d);
Ok(())
}
#[test]
fn test_destination_address() -> Result<(), anyhow::Error> {
let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
let dest_addr_a = id_a.pgn().destination_address();
let dest_addr_b = id_b.pgn().destination_address();
let dest_addr_c = id_c.pgn().destination_address();
let dest_addr_d = id_d.pgn().destination_address();
assert_eq!(DestinationAddr::None, dest_addr_a);
assert_eq!(DestinationAddr::None, dest_addr_b);
assert_eq!(DestinationAddr::None, dest_addr_c);
assert_eq!(DestinationAddr::Some(41), dest_addr_d);
assert_eq!(Some(Addr::RetarderExhaustEngine1), dest_addr_d.lookup());
Ok(())
}
#[test]
fn test_group_extension() -> Result<(), anyhow::Error> {
let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
let group_ext_a = id_a.pgn().group_extension();
let group_ext_b = id_b.pgn().group_extension();
let group_ext_c = id_c.pgn().group_extension();
let group_ext_d = id_d.pgn().group_extension();
assert_eq!(GroupExtension::Some(242), group_ext_a);
assert_eq!(GroupExtension::Some(146), group_ext_b);
assert_eq!(GroupExtension::Some(33), group_ext_c);
assert_eq!(GroupExtension::None, group_ext_d);
Ok(())
}
#[test]
fn test_pdu_format() -> Result<(), anyhow::Error> {
let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
let pdu_format_a = id_a.pgn().pdu_format();
let pdu_format_b = id_b.pgn().pdu_format();
let pdu_format_c = id_c.pgn().pdu_format();
let pdu_format_d = id_d.pgn().pdu_format();
assert_eq!(PduFormat::Pdu2(254), pdu_format_a);
assert_eq!(PduFormat::Pdu2(254), pdu_format_b);
assert_eq!(PduFormat::Pdu2(255), pdu_format_c);
assert_eq!(PduFormat::Pdu1(0), pdu_format_d);
Ok(())
}
#[test]
fn test_pgn_bits() -> Result<(), anyhow::Error> {
let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
let pgn_bits_a = id_a.pgn();
let pgn_bits_b = id_b.pgn();
let pgn_bits_c = id_c.pgn();
let pgn_bits_d = id_d.pgn();
assert_eq!(65266, pgn_bits_a.into_bits());
assert_eq!(65170, pgn_bits_b.into_bits());
assert_eq!(65313, pgn_bits_c.into_bits());
assert_eq!(41, pgn_bits_d.into_bits());
Ok(())
}
}