can_types/protocol/j1939/
pgn.rs1if_alloc! {
32 use crate::alloc::{string::String, fmt::format};
33}
34
35use bitfield_struct::bitfield;
36
37use crate::{conversion::Conversion, identifier::Id, protocol::j1939::identifier::J1939};
38
39use super::address::DestinationAddr;
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum PduAssignment {
44 Sae(u32),
47 Manufacturer(u32),
50 Unknown(u32),
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum PduFormat {
58 Pdu1(u8),
61 Pdu2(u8),
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum CommunicationMode {
69 P2P,
72 Broadcast,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum GroupExtension {
79 None,
81 Some(u8),
83}
84
85#[bitfield(u32, order = Msb, conversion = false)]
97#[derive(PartialEq, Eq)]
98pub struct Pgn {
99 #[bits(14)]
100 __: u16,
101 #[bits(1)]
102 reserved_bits: bool,
103 #[bits(1)]
104 data_page_bits: bool,
105 #[bits(8)]
106 pdu_format_bits: u8,
107 #[bits(8)]
108 pdu_specific_bits: u8,
109}
110
111impl Conversion<u32> for Pgn {
112 type Error = anyhow::Error;
113
114 #[inline]
116 fn from_bits(bits: u32) -> Self {
117 Self(bits)
118 }
119
120 #[inline]
122 fn from_hex(hex_str: &str) -> Self {
123 let bits = u32::from_str_radix(hex_str, 16).unwrap_or_default();
124
125 Self(bits)
126 }
127
128 #[inline]
132 fn try_from_bits(bits: u32) -> Result<Self, Self::Error> {
133 if bits > 0x3FFFF {
134 return Err(anyhow::anyhow!(
135 "PGN bits out of range! Valid range is 0x0000..0xFFFF - got {bits:#04X}"
136 ));
137 }
138 Ok(Self(bits))
139 }
140
141 #[inline]
146 fn try_from_hex(hex_str: &str) -> Result<Self, Self::Error> {
147 let bits = u32::from_str_radix(hex_str, 16).map_err(anyhow::Error::msg)?;
148 if bits > 0x3FFFF {
149 return Err(anyhow::anyhow!(
150 "PGN bits out of range! Valid range is 0x0000..0xFFFF - got {bits:#04X}"
151 ));
152 }
153 Ok(Self(bits))
154 }
155
156 #[inline]
158 fn into_bits(self) -> u32 {
159 self.0
160 }
161
162 #[inline]
166 #[cfg(feature = "alloc")]
167 fn into_hex(self) -> String {
168 format(format_args!("{:05X}", self.into_bits()))
169 }
170}
171
172impl Pgn {
173 #[inline]
179 #[must_use]
180 pub const fn pdu_format(&self) -> PduFormat {
181 match (self.pdu_format_bits() < 240, self.pdu_format_bits()) {
182 (true, a) => PduFormat::Pdu1(a),
183 (false, b) => PduFormat::Pdu2(b),
184 }
185 }
186
187 #[inline]
193 #[must_use]
194 pub const fn group_extension(&self) -> GroupExtension {
195 match self.pdu_format() {
196 PduFormat::Pdu1(_) => GroupExtension::None,
197 PduFormat::Pdu2(_) => GroupExtension::Some(self.pdu_specific_bits()),
198 }
199 }
200
201 #[inline]
207 #[must_use]
208 pub const fn destination_address(&self) -> DestinationAddr {
209 match self.pdu_format() {
210 PduFormat::Pdu1(_) => DestinationAddr::Some(self.pdu_specific_bits()),
211 PduFormat::Pdu2(_) => DestinationAddr::None,
212 }
213 }
214
215 #[inline]
221 #[must_use]
222 pub const fn communication_mode(&self) -> CommunicationMode {
223 match self.pdu_format() {
224 PduFormat::Pdu1(_) => CommunicationMode::P2P,
225 PduFormat::Pdu2(_) => CommunicationMode::Broadcast,
226 }
227 }
228
229 #[inline]
235 #[must_use]
236 pub const fn is_p2p(&self) -> bool {
237 match self.communication_mode() {
238 CommunicationMode::P2P => true,
239 CommunicationMode::Broadcast => false,
240 }
241 }
242
243 #[inline]
249 #[must_use]
250 pub const fn is_broadcast(&self) -> bool {
251 match self.communication_mode() {
252 CommunicationMode::P2P => false,
253 CommunicationMode::Broadcast => true,
254 }
255 }
256
257 #[must_use]
264 pub fn pdu_assignment(&self) -> PduAssignment {
265 match self.into_bits() {
266 0x0000_0000..=0x0000_EE00
267 | 0x0000_F000..=0x0000_FEFF
268 | 0x0001_0000..=0x0001_EE00
269 | 0x0001_F000..=0x0001_FEFF => PduAssignment::Sae(self.into_bits()),
270
271 0x0000_EF00 | 0x0000_FF00..=0x0000_FFFF | 0x0001_EF00 | 0x0001_FF00..=0x0001_FFFF => {
272 PduAssignment::Manufacturer(self.into_bits())
273 }
274 p => PduAssignment::Unknown(p),
275 }
276 }
277}
278
279impl Id<J1939> {
280 #[inline]
285 #[must_use]
286 pub const fn pgn_bits(&self) -> u32 {
287 let pgn_bitfield = Pgn::new()
288 .with_reserved_bits(self.reserved())
289 .with_data_page_bits(self.data_page())
290 .with_pdu_format_bits(self.pdu_format())
291 .with_pdu_specific_bits(self.pdu_specific());
292
293 pgn_bitfield.0
294 }
295
296 #[inline]
301 #[must_use]
302 pub const fn pgn(&self) -> Pgn {
303 Pgn::new()
304 .with_reserved_bits(self.reserved())
305 .with_data_page_bits(self.data_page())
306 .with_pdu_format_bits(self.pdu_format())
307 .with_pdu_specific_bits(self.pdu_specific())
308 }
309}
310
311#[cfg(test)]
312mod pgn_tests {
313 use super::*;
320 use crate::protocol::j1939::address::Addr;
321
322 #[test]
323 fn test_pdu_assignment() -> Result<(), anyhow::Error> {
324 let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
325 let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
326 let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
327 let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
328
329 let assignment_a = id_a.pgn().pdu_assignment();
330 let assignment_b = id_b.pgn().pdu_assignment();
331 let assignment_c = id_c.pgn().pdu_assignment();
332 let assignment_d = id_d.pgn().pdu_assignment();
333
334 assert_eq!(PduAssignment::Sae(65266), assignment_a);
335 assert_eq!(PduAssignment::Sae(65170), assignment_b);
336 assert_eq!(PduAssignment::Manufacturer(65313), assignment_c);
337 assert_eq!(PduAssignment::Sae(41), assignment_d);
338
339 Ok(())
340 }
341
342 #[test]
343 fn test_communication_mode() -> Result<(), anyhow::Error> {
344 let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
345 let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
346 let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
347 let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
348
349 let comms_mode_a = id_a.pgn().communication_mode();
350 let comms_mode_b = id_b.pgn().communication_mode();
351 let comms_mode_c = id_c.pgn().communication_mode();
352 let comms_mode_d = id_d.pgn().communication_mode();
353
354 assert_eq!(CommunicationMode::Broadcast, comms_mode_a);
355 assert_eq!(CommunicationMode::Broadcast, comms_mode_b);
356 assert_eq!(CommunicationMode::Broadcast, comms_mode_c);
357 assert_eq!(CommunicationMode::P2P, comms_mode_d);
358
359 Ok(())
360 }
361
362 #[test]
363 fn test_destination_address() -> Result<(), anyhow::Error> {
364 let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
365 let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
366 let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
367 let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
368
369 let dest_addr_a = id_a.pgn().destination_address();
370 let dest_addr_b = id_b.pgn().destination_address();
371 let dest_addr_c = id_c.pgn().destination_address();
372 let dest_addr_d = id_d.pgn().destination_address();
373
374 assert_eq!(DestinationAddr::None, dest_addr_a);
375 assert_eq!(DestinationAddr::None, dest_addr_b);
376 assert_eq!(DestinationAddr::None, dest_addr_c);
377 assert_eq!(DestinationAddr::Some(41), dest_addr_d);
378 assert_eq!(Some(Addr::RetarderExhaustEngine1), dest_addr_d.lookup());
379
380 Ok(())
381 }
382
383 #[test]
384 fn test_group_extension() -> Result<(), anyhow::Error> {
385 let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
386 let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
387 let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
388 let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
389
390 let group_ext_a = id_a.pgn().group_extension();
391 let group_ext_b = id_b.pgn().group_extension();
392 let group_ext_c = id_c.pgn().group_extension();
393 let group_ext_d = id_d.pgn().group_extension();
394
395 assert_eq!(GroupExtension::Some(242), group_ext_a);
396 assert_eq!(GroupExtension::Some(146), group_ext_b);
397 assert_eq!(GroupExtension::Some(33), group_ext_c);
398 assert_eq!(GroupExtension::None, group_ext_d);
399
400 Ok(())
401 }
402
403 #[test]
404 fn test_pdu_format() -> Result<(), anyhow::Error> {
405 let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
406 let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
407 let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
408 let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
409
410 let pdu_format_a = id_a.pgn().pdu_format();
411 let pdu_format_b = id_b.pgn().pdu_format();
412 let pdu_format_c = id_c.pgn().pdu_format();
413 let pdu_format_d = id_d.pgn().pdu_format();
414
415 assert_eq!(PduFormat::Pdu2(254), pdu_format_a);
416 assert_eq!(PduFormat::Pdu2(254), pdu_format_b);
417 assert_eq!(PduFormat::Pdu2(255), pdu_format_c);
418 assert_eq!(PduFormat::Pdu1(0), pdu_format_d);
419
420 Ok(())
421 }
422
423 #[test]
424 fn test_pgn_bits() -> Result<(), anyhow::Error> {
425 let id_a = Id::<J1939>::try_from_hex("18FEF200")?;
426 let id_b = Id::<J1939>::try_from_hex("1CFE9201")?;
427 let id_c = Id::<J1939>::try_from_hex("10FF2121")?;
428 let id_d = Id::<J1939>::try_from_hex("0C00290B")?;
429
430 let pgn_bits_a = id_a.pgn();
431 let pgn_bits_b = id_b.pgn();
432 let pgn_bits_c = id_c.pgn();
433 let pgn_bits_d = id_d.pgn();
434
435 assert_eq!(65266, pgn_bits_a.into_bits());
436 assert_eq!(65170, pgn_bits_b.into_bits());
437 assert_eq!(65313, pgn_bits_c.into_bits());
438 assert_eq!(41, pgn_bits_d.into_bits());
439
440 Ok(())
441 }
442}