can_types/
payload.rs

1// Copyright (c) 2024 Nathan H. Keough
2//
3// This work is dual-licensed under MIT OR Apache 2.0 (or any later version).
4// You may choose between one of them if you use this work.
5//
6// For further detail, please refer to the individual licenses located at the root of this crate.
7
8//! Defines various PDU contents, including generic `Data` or the `Name` fields.
9
10if_alloc! {
11    use crate::alloc::{string::String, fmt::format};
12}
13
14use bitfield_struct::bitfield;
15
16use crate::conversion::Conversion;
17
18/// Marks a type, associating it with a protocol data unit (PDU)
19pub trait IsDataUnit {}
20
21/// Bitfield representing an 8-byte data field.
22///
23/// ### Repr `u64`
24///
25/// | Field  | Size (bits) |
26/// |--------|-------------|
27/// | byte 0 | 8           |
28/// | byte 1 | 8           |
29/// | byte 2 | 8           |
30/// | byte 3 | 8           |
31/// | byte 4 | 8           |
32/// | byte 5 | 8           |
33/// | byte 6 | 8           |
34/// | byte 7 | 8           |
35#[bitfield(u64, order = Msb, conversion = false)]
36#[derive(PartialEq, Eq, PartialOrd, Ord)]
37pub struct Data {
38    #[bits(8)]
39    byte_0_bits: u8,
40    #[bits(8)]
41    byte_1_bits: u8,
42    #[bits(8)]
43    byte_2_bits: u8,
44    #[bits(8)]
45    byte_3_bits: u8,
46    #[bits(8)]
47    byte_4_bits: u8,
48    #[bits(8)]
49    byte_5_bits: u8,
50    #[bits(8)]
51    byte_6_bits: u8,
52    #[bits(8)]
53    byte_7_bits: u8,
54}
55
56/// Represents a `Name` in the SAE J1939 protocol.
57///
58/// The `Name` structure is used in the SAE J1939 protocol to represent the identity of a device or
59/// component within a vehicle's network.
60///
61/// ### Repr: `u64`
62/// | Field                   | Size (bits) |
63/// |-------------------------|-------------|
64/// | Arbitrary Address       | 1           |
65/// | Industry Group          | 3           |
66/// | Vehicle System Instance | 4           |
67/// | Vehicle system          | 7           |
68/// | Reserved                | 1           |
69/// | Function                | 8           |
70/// | Function Instance       | 5           |
71/// | ECU Instance            | 3           |
72/// | Manufacturer Code       | 11          |
73/// | Identity Number         | 21          |
74#[bitfield(u64, order = Msb, conversion = false)]
75#[derive(PartialEq, Eq, PartialOrd, Ord)]
76pub struct Name {
77    #[bits(1)]
78    arbitrary_address_bits: bool,
79    #[bits(3)]
80    industry_group_bits: u8,
81    #[bits(4)]
82    vehicle_system_instance_bits: u8,
83    #[bits(7)]
84    vehicle_system_bits: u8,
85    #[bits(1)]
86    reserved_bits: bool,
87    #[bits(8)]
88    function_bits: u8,
89    #[bits(5)]
90    function_instance_bits: u8,
91    #[bits(3)]
92    ecu_instance_bits: u8,
93    #[bits(11)]
94    manufacturer_code_bits: u16,
95    #[bits(21)]
96    identity_number_bits: u32,
97}
98
99impl IsDataUnit for Data {}
100impl IsDataUnit for Name {}
101
102/// Represents a Protocol Data Unit (PDU) in the context of Controller Area Network (CAN).
103#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
104pub struct Pdu<U: IsDataUnit>(pub(crate) U);
105
106impl Conversion<u64> for Pdu<Data> {
107    type Error = anyhow::Error;
108
109    /// Creates a new [`Data`] bitfield from a 64-bit integer.
110    fn from_bits(bits: u64) -> Self {
111        Self(Data(bits))
112    }
113
114    /// Creates a new [`Data`] bitfield from a base-16 (hex) string slice.
115    fn from_hex(hex_str: &str) -> Self {
116        let bits = u64::from_str_radix(hex_str, 16).unwrap_or_default();
117
118        Self(Data(bits))
119    }
120
121    /// Creates a new [`Data`] bitfield from a 64-bit integer.
122    /// # Errors
123    /// - Never (conversion is trivial)
124    fn try_from_bits(bits: u64) -> Result<Self, Self::Error> {
125        Ok(Self(Data(bits)))
126    }
127
128    /// Creates a new [`Data`] bitfield from a base-16 (hex) string slice.
129    /// # Errors
130    /// - If failed to parse input hexadecimal string slice.
131    fn try_from_hex(hex_str: &str) -> Result<Self, Self::Error> {
132        let bits = u64::from_str_radix(hex_str, 16).map_err(anyhow::Error::msg)?;
133
134        Ok(Self(Data(bits)))
135    }
136
137    /// Creates a new 64-bit integer from the [`Data`] bitfield.
138    fn into_bits(self) -> u64 {
139        self.0 .0
140    }
141
142    /// Creates a new base-16 (hex) [`String`] from the [`Data`] bitfield.
143    /// # Requires
144    /// - `alloc`
145    #[cfg(feature = "alloc")]
146    fn into_hex(self) -> String {
147        format(format_args!("{:016X}", self.0 .0))
148    }
149}
150
151impl Pdu<Data> {
152    /// Retrieve byte 0.
153    #[must_use]
154    pub const fn byte_0(self) -> u8 {
155        self.0.byte_0_bits()
156    }
157
158    /// Retrieve byte 1.
159    #[must_use]
160    pub const fn byte_1(self) -> u8 {
161        self.0.byte_1_bits()
162    }
163
164    /// Retrieve byte 2.
165    #[must_use]
166    pub const fn byte_2(self) -> u8 {
167        self.0.byte_2_bits()
168    }
169
170    /// Retrieve byte 3.
171    #[must_use]
172    pub const fn byte_3(self) -> u8 {
173        self.0.byte_3_bits()
174    }
175
176    /// Retrieve byte 4.
177    #[must_use]
178    pub const fn byte_4(self) -> u8 {
179        self.0.byte_4_bits()
180    }
181
182    /// Retrieve byte 5.
183    #[must_use]
184    pub const fn byte_5(self) -> u8 {
185        self.0.byte_5_bits()
186    }
187
188    /// Retrieve byte 6.
189    #[must_use]
190    pub const fn byte_6(self) -> u8 {
191        self.0.byte_6_bits()
192    }
193
194    /// Retrieve byte 7.
195    #[must_use]
196    pub const fn byte_7(self) -> u8 {
197        self.0.byte_7_bits()
198    }
199
200    /// Return the 64-bit [`Data`] bitfield as little-endian bytes.
201    #[must_use]
202    pub const fn to_le_bytes(&self) -> [u8; 8] {
203        self.0 .0.to_le_bytes()
204    }
205
206    /// Return the 64-bit [`Data`] bitfield as big-endian bytes.
207    #[must_use]
208    pub const fn to_be_bytes(&self) -> [u8; 8] {
209        self.0 .0.to_be_bytes()
210    }
211
212    /// Return the 64-bit [`Data`] bitfield as native-endian bytes.
213    #[must_use]
214    pub const fn to_ne_bytes(&self) -> [u8; 8] {
215        self.0 .0.to_ne_bytes()
216    }
217
218    /// Convert the [`Data`] bitfield to little-endian byte format.
219    #[must_use]
220    pub const fn to_le(&self) -> Self {
221        Self(Data(self.0 .0.to_le()))
222    }
223
224    /// Convert the [`Data`] bitfield to big-endian byte format.
225    #[must_use]
226    pub const fn to_be(&self) -> Self {
227        Self(Data(self.0 .0.to_be()))
228    }
229}
230
231impl Conversion<u64> for Pdu<Name> {
232    type Error = anyhow::Error;
233
234    /// Creates a new [`Name`] bitfield from a 64-bit integer.
235    fn from_bits(bits: u64) -> Self {
236        Self(Name(bits))
237    }
238
239    /// Creates a new [`Name`] bitfield from a base-16 (hex) string slice.
240    fn from_hex(hex_str: &str) -> Self {
241        let bits = u64::from_str_radix(hex_str, 16).unwrap_or_default();
242
243        Self(Name(bits))
244    }
245
246    /// Creates a new [`Name`] bitfield from a 64-bit integer.
247    /// # Errors
248    /// - Never (conversion is trivial)
249    fn try_from_bits(bits: u64) -> Result<Self, Self::Error> {
250        Ok(Self(Name(bits)))
251    }
252
253    /// Creates a new [`Name`] bitfield from a base-16 (hex) string slice.
254    /// # Errors
255    /// - If invalid encoding of provided Base16 string
256    /// - If insufficient output buffer length
257    fn try_from_hex(hex_str: &str) -> Result<Self, Self::Error> {
258        let bits = u64::from_str_radix(hex_str, 16).map_err(anyhow::Error::msg)?;
259
260        Ok(Self(Name(bits)))
261    }
262
263    /// Creates a new 64-bit integer from the [`Name`] bitfield.
264    fn into_bits(self) -> u64 {
265        self.0 .0
266    }
267
268    /// Creates a new base-16 (hex) [`String`] from the [`Name`] bitfield.
269    /// # Requires
270    /// - `alloc`
271    #[cfg(feature = "alloc")]
272    fn into_hex(self) -> String {
273        format(format_args!("{:016X}", self.0 .0))
274    }
275}
276
277impl Pdu<Name> {
278    /// Indicates whether or not the ECU/CA can negotiate an address (true = yes; false = no).
279    #[must_use]
280    pub const fn arbitrary_address(&self) -> bool {
281        self.0.arbitrary_address_bits()
282    }
283
284    /// These codes are associated with particular industries such as on-highway equipment,
285    /// agricultural equipment, and more.
286    #[must_use]
287    pub const fn industry_group(&self) -> u8 {
288        self.0.industry_group_bits()
289    }
290
291    /// Assigns a number to each instance on the Vehicle System (in case you connect several
292    /// networks – e.g. connecting cars on a train).
293    #[must_use]
294    pub const fn vehicle_system_instance(&self) -> u8 {
295        self.0.vehicle_system_instance_bits()
296    }
297
298    /// Vehicle systems are associated with the Industry Group and they can be, for instance,
299    /// “tractor” in the “Common” industry or “trailer” in the “On-Highway” industry group.
300    #[must_use]
301    pub const fn vehicle_system(&self) -> u8 {
302        self.0.vehicle_system_bits()
303    }
304
305    /// Always zero(false).
306    #[must_use]
307    pub const fn reserved(&self) -> bool {
308        self.0.reserved_bits()
309    }
310
311    /// This code, in a range between 128 and 255, is assigned according to the Industry Group. A
312    /// value between 0 and 127 is not associated with any other parameter.
313    #[must_use]
314    pub const fn function(&self) -> u8 {
315        self.0.function_bits()
316    }
317
318    /// Returns the function instance.
319    #[must_use]
320    pub const fn function_instance(&self) -> u8 {
321        self.0.function_instance_bits()
322    }
323
324    /// A J1939 network may accommodate several ECUs of the same kind (i.e. same functionality).
325    /// The Instance code separates them.
326    #[must_use]
327    pub const fn ecu_instance(&self) -> u8 {
328        self.0.ecu_instance_bits()
329    }
330
331    /// The 11-Bit Manufacturer Code is assigned by the SAE.
332    #[must_use]
333    pub const fn manufacturer_code(&self) -> u16 {
334        self.0.manufacturer_code_bits()
335    }
336
337    /// This field is assigned by the manufacturer, similar to a serial number, i.e. the code must
338    /// be uniquely assigned to the unit.
339    #[must_use]
340    pub const fn identity_number(&self) -> u32 {
341        self.0.identity_number_bits()
342    }
343}
344
345#[cfg(test)]
346mod data_tests {
347    use super::*;
348
349    #[test]
350    fn test_data_bitfield() -> Result<(), anyhow::Error> {
351        let data_a = Pdu::<Data>::from_hex("FFFF82DF1AFFFFFF");
352        let be_bytes_a: [u8; 8] = [0xFF, 0xFF, 0x82, 0xDF, 0x1A, 0xFF, 0xFF, 0xFF];
353        let le_bytes_a: [u8; 8] = [0xFF, 0xFF, 0xFF, 0x1A, 0xDF, 0x82, 0xFF, 0xFF];
354
355        assert_eq!(be_bytes_a, data_a.to_be_bytes());
356        assert_eq!(le_bytes_a, data_a.to_le_bytes());
357
358        assert_eq!(18446606493475143679, data_a.into_bits());
359
360        assert_eq!(Pdu::<Data>(Data(18446743089616977919)), data_a.to_be());
361        assert_eq!(Pdu::<Data>(Data(18446606493475143679)), data_a.to_le());
362
363        Ok(())
364    }
365
366    #[test]
367    fn test_name_bitfield() {
368        let name_a = Name::new()
369            .with_arbitrary_address_bits(true)
370            .with_industry_group_bits(0)
371            .with_vehicle_system_instance_bits(0x5)
372            .with_vehicle_system_bits(0x6)
373            .with_reserved_bits(false)
374            .with_function_bits(0x5)
375            .with_function_instance_bits(0x2)
376            .with_ecu_instance_bits(0x1)
377            .with_manufacturer_code_bits(0x122)
378            .with_identity_number_bits(0xB0309);
379
380        let pdu_name = Pdu::<Name>(name_a);
381
382        let bytes_a: [u8; 8] = [0x09, 0x03, 0x4B, 0x24, 0x11, 0x05, 0x0C, 0x85];
383        let name_a_bytes = pdu_name.into_bits().to_le_bytes();
384
385        assert_eq!(bytes_a, name_a_bytes);
386    }
387}