Skip to main content

canlink_hal/
message.rs

1//! CAN message types and related structures.
2//!
3//! This module provides unified, hardware-independent representations of CAN messages,
4//! identifiers, timestamps, and message flags.
5
6use bitflags::bitflags;
7use serde::{Deserialize, Serialize};
8
9/// CAN identifier (standard or extended).
10///
11/// CAN supports two types of identifiers:
12/// - Standard: 11-bit identifier (0x000-0x7FF)
13/// - Extended: 29-bit identifier (0x00000000-0x1FFFFFFF)
14///
15/// # Examples
16///
17/// ```
18/// use canlink_hal::CanId;
19///
20/// let std_id = CanId::Standard(0x123);
21/// assert!(std_id.is_standard());
22/// assert_eq!(std_id.raw(), 0x123);
23///
24/// let ext_id = CanId::Extended(0x12345678);
25/// assert!(ext_id.is_extended());
26/// assert_eq!(ext_id.raw(), 0x12345678);
27/// ```
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
29pub enum CanId {
30    /// Standard 11-bit ID (0x000-0x7FF)
31    Standard(u16),
32
33    /// Extended 29-bit ID (0x00000000-0x1FFFFFFF)
34    Extended(u32),
35}
36
37impl CanId {
38    /// Get the raw ID value as u32.
39    ///
40    /// # Examples
41    ///
42    /// ```
43    /// use canlink_hal::CanId;
44    ///
45    /// assert_eq!(CanId::Standard(0x123).raw(), 0x123);
46    /// assert_eq!(CanId::Extended(0x12345678).raw(), 0x12345678);
47    /// ```
48    #[must_use]
49    pub fn raw(&self) -> u32 {
50        match self {
51            Self::Standard(id) => u32::from(*id),
52            Self::Extended(id) => *id,
53        }
54    }
55
56    /// Check if this is a standard frame ID.
57    ///
58    /// # Examples
59    ///
60    /// ```
61    /// use canlink_hal::CanId;
62    ///
63    /// assert!(CanId::Standard(0x123).is_standard());
64    /// assert!(!CanId::Extended(0x123).is_standard());
65    /// ```
66    #[must_use]
67    pub fn is_standard(&self) -> bool {
68        matches!(self, Self::Standard(_))
69    }
70
71    /// Check if this is an extended frame ID.
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// use canlink_hal::CanId;
77    ///
78    /// assert!(!CanId::Standard(0x123).is_extended());
79    /// assert!(CanId::Extended(0x123).is_extended());
80    /// ```
81    #[must_use]
82    pub fn is_extended(&self) -> bool {
83        matches!(self, Self::Extended(_))
84    }
85}
86
87bitflags! {
88    /// CAN message flags.
89    ///
90    /// These flags indicate various properties of a CAN message:
91    /// - RTR: Remote Transmission Request
92    /// - FD: CAN-FD format
93    /// - BRS: Bit Rate Switch (CAN-FD only)
94    /// - ESI: Error State Indicator (CAN-FD only)
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// use canlink_hal::MessageFlags;
100    ///
101    /// let flags = MessageFlags::FD | MessageFlags::BRS;
102    /// assert!(flags.contains(MessageFlags::FD));
103    /// assert!(flags.contains(MessageFlags::BRS));
104    /// assert!(!flags.contains(MessageFlags::RTR));
105    /// ```
106    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
107    pub struct MessageFlags: u8 {
108        /// Remote Transmission Request (RTR)
109        const RTR = 0b0000_0001;
110
111        /// CAN-FD format
112        const FD = 0b0000_0010;
113
114        /// Bit Rate Switch (BRS) - CAN-FD only
115        const BRS = 0b0000_0100;
116
117        /// Error State Indicator (ESI) - CAN-FD only
118        const ESI = 0b0000_1000;
119    }
120}
121
122impl Default for MessageFlags {
123    fn default() -> Self {
124        Self::empty()
125    }
126}
127
128// Implement Serialize and Deserialize for MessageFlags
129impl Serialize for MessageFlags {
130    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
131    where
132        S: serde::Serializer,
133    {
134        self.bits().serialize(serializer)
135    }
136}
137
138impl<'de> Deserialize<'de> for MessageFlags {
139    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
140    where
141        D: serde::Deserializer<'de>,
142    {
143        let bits = u8::deserialize(deserializer)?;
144        Ok(MessageFlags::from_bits_truncate(bits))
145    }
146}
147
148/// Microsecond-precision timestamp.
149///
150/// Represents a point in time with microsecond precision. The reference point
151/// is hardware-dependent (typically system boot time or epoch).
152///
153/// # Examples
154///
155/// ```
156/// use canlink_hal::Timestamp;
157///
158/// let ts = Timestamp::from_micros(1_000_000);
159/// assert_eq!(ts.as_micros(), 1_000_000);
160/// assert_eq!(ts.as_millis(), 1_000);
161/// ```
162#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
163pub struct Timestamp {
164    /// Microseconds since reference point
165    micros: u64,
166}
167
168impl Timestamp {
169    /// Create a timestamp from microseconds.
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// use canlink_hal::Timestamp;
175    ///
176    /// let ts = Timestamp::from_micros(1_500_000);
177    /// assert_eq!(ts.as_micros(), 1_500_000);
178    /// ```
179    #[must_use]
180    pub const fn from_micros(micros: u64) -> Self {
181        Self { micros }
182    }
183
184    /// Get the timestamp as microseconds.
185    ///
186    /// # Examples
187    ///
188    /// ```
189    /// use canlink_hal::Timestamp;
190    ///
191    /// let ts = Timestamp::from_micros(1_500_000);
192    /// assert_eq!(ts.as_micros(), 1_500_000);
193    /// ```
194    #[must_use]
195    pub const fn as_micros(&self) -> u64 {
196        self.micros
197    }
198
199    /// Get the timestamp as milliseconds.
200    ///
201    /// # Examples
202    ///
203    /// ```
204    /// use canlink_hal::Timestamp;
205    ///
206    /// let ts = Timestamp::from_micros(1_500_000);
207    /// assert_eq!(ts.as_millis(), 1_500);
208    /// ```
209    #[must_use]
210    pub const fn as_millis(&self) -> u64 {
211        self.micros / 1000
212    }
213
214    /// Get the timestamp as seconds.
215    ///
216    /// # Examples
217    ///
218    /// ```
219    /// use canlink_hal::Timestamp;
220    ///
221    /// let ts = Timestamp::from_micros(2_500_000);
222    /// assert_eq!(ts.as_secs(), 2);
223    /// ```
224    #[must_use]
225    pub const fn as_secs(&self) -> u64 {
226        self.micros / 1_000_000
227    }
228}
229
230/// Unified CAN message type.
231///
232/// This structure represents a CAN message in a hardware-independent way.
233/// It supports both CAN 2.0 and CAN-FD messages.
234///
235/// # Examples
236///
237/// ```
238/// use canlink_hal::{CanMessage, CanId};
239///
240/// // Create a standard CAN 2.0 message
241/// let msg = CanMessage::new_standard(0x123, &[1, 2, 3, 4]).unwrap();
242/// assert_eq!(msg.id(), CanId::Standard(0x123));
243/// assert_eq!(msg.data(), &[1, 2, 3, 4]);
244///
245/// // Create a CAN-FD message
246/// let fd_msg = CanMessage::new_fd(CanId::Standard(0x456), &[1; 64]).unwrap();
247/// assert_eq!(fd_msg.data().len(), 64);
248/// ```
249#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
250pub struct CanMessage {
251    /// CAN identifier (standard or extended)
252    id: CanId,
253
254    /// Message data (up to 64 bytes for CAN-FD)
255    data: Vec<u8>,
256
257    /// Timestamp (microsecond precision)
258    timestamp: Option<Timestamp>,
259
260    /// Message flags
261    flags: MessageFlags,
262}
263
264impl CanMessage {
265    /// Create a standard CAN 2.0 data frame.
266    ///
267    /// # Arguments
268    ///
269    /// * `id` - Standard 11-bit CAN ID (0x000-0x7FF)
270    /// * `data` - Data bytes (0-8 bytes)
271    ///
272    /// # Errors
273    ///
274    /// Returns `CanError::InvalidId` if the ID is out of range.
275    /// Returns `CanError::InvalidDataLength` if data length exceeds 8 bytes.
276    ///
277    /// # Examples
278    ///
279    /// ```
280    /// use canlink_hal::CanMessage;
281    ///
282    /// let msg = CanMessage::new_standard(0x123, &[1, 2, 3, 4]).unwrap();
283    /// assert_eq!(msg.data(), &[1, 2, 3, 4]);
284    /// ```
285    pub fn new_standard(id: u16, data: &[u8]) -> Result<Self, crate::error::CanError> {
286        if id > 0x7FF {
287            return Err(crate::error::CanError::InvalidId {
288                value: u32::from(id),
289                max: 0x7FF,
290            });
291        }
292        if data.len() > 8 {
293            return Err(crate::error::CanError::InvalidDataLength {
294                expected: 8,
295                actual: data.len(),
296            });
297        }
298        Ok(Self {
299            id: CanId::Standard(id),
300            data: data.to_vec(),
301            timestamp: None,
302            flags: MessageFlags::default(),
303        })
304    }
305
306    /// Create an extended CAN 2.0B data frame.
307    ///
308    /// # Arguments
309    ///
310    /// * `id` - Extended 29-bit CAN ID (0x00000000-0x1FFFFFFF)
311    /// * `data` - Data bytes (0-8 bytes)
312    ///
313    /// # Errors
314    ///
315    /// Returns `CanError::InvalidId` if the ID is out of range.
316    /// Returns `CanError::InvalidDataLength` if data length exceeds 8 bytes.
317    ///
318    /// # Examples
319    ///
320    /// ```
321    /// use canlink_hal::CanMessage;
322    ///
323    /// let msg = CanMessage::new_extended(0x12345678, &[1, 2, 3, 4]).unwrap();
324    /// assert_eq!(msg.data(), &[1, 2, 3, 4]);
325    /// ```
326    pub fn new_extended(id: u32, data: &[u8]) -> Result<Self, crate::error::CanError> {
327        if id > 0x1FFF_FFFF {
328            return Err(crate::error::CanError::InvalidId {
329                value: id,
330                max: 0x1FFF_FFFF,
331            });
332        }
333        if data.len() > 8 {
334            return Err(crate::error::CanError::InvalidDataLength {
335                expected: 8,
336                actual: data.len(),
337            });
338        }
339        Ok(Self {
340            id: CanId::Extended(id),
341            data: data.to_vec(),
342            timestamp: None,
343            flags: MessageFlags::default(),
344        })
345    }
346
347    /// Create a CAN-FD data frame.
348    ///
349    /// # Arguments
350    ///
351    /// * `id` - CAN identifier (standard or extended)
352    /// * `data` - Data bytes (0-64 bytes)
353    ///
354    /// # Errors
355    ///
356    /// Returns `CanError::InvalidDataLength` if data length exceeds 64 bytes.
357    ///
358    /// # Examples
359    ///
360    /// ```
361    /// use canlink_hal::{CanMessage, CanId};
362    ///
363    /// let msg = CanMessage::new_fd(CanId::Standard(0x123), &[1; 64]).unwrap();
364    /// assert_eq!(msg.data().len(), 64);
365    /// ```
366    pub fn new_fd(id: CanId, data: &[u8]) -> Result<Self, crate::error::CanError> {
367        if data.len() > 64 {
368            return Err(crate::error::CanError::InvalidDataLength {
369                expected: 64,
370                actual: data.len(),
371            });
372        }
373        Ok(Self {
374            id,
375            data: data.to_vec(),
376            timestamp: None,
377            flags: MessageFlags::FD | MessageFlags::BRS,
378        })
379    }
380
381    /// Create a remote frame (RTR).
382    ///
383    /// # Arguments
384    ///
385    /// * `id` - CAN identifier (standard or extended)
386    /// * `dlc` - Data Length Code (0-8)
387    ///
388    /// # Errors
389    ///
390    /// Returns `CanError::InvalidDataLength` if DLC exceeds 8.
391    ///
392    /// # Examples
393    ///
394    /// ```
395    /// use canlink_hal::{CanMessage, CanId};
396    ///
397    /// let msg = CanMessage::new_remote(CanId::Standard(0x123), 4).unwrap();
398    /// assert!(msg.is_remote());
399    /// ```
400    pub fn new_remote(id: CanId, dlc: u8) -> Result<Self, crate::error::CanError> {
401        if dlc > 8 {
402            return Err(crate::error::CanError::InvalidDataLength {
403                expected: 8,
404                actual: dlc as usize,
405            });
406        }
407        Ok(Self {
408            id,
409            data: vec![],
410            timestamp: None,
411            flags: MessageFlags::RTR,
412        })
413    }
414
415    /// Get the CAN identifier.
416    #[must_use]
417    pub const fn id(&self) -> CanId {
418        self.id
419    }
420
421    /// Get the message data.
422    #[must_use]
423    pub fn data(&self) -> &[u8] {
424        &self.data
425    }
426
427    /// Get the timestamp.
428    #[must_use]
429    pub const fn timestamp(&self) -> Option<Timestamp> {
430        self.timestamp
431    }
432
433    /// Get the message flags.
434    #[must_use]
435    pub const fn flags(&self) -> MessageFlags {
436        self.flags
437    }
438
439    /// Set the timestamp.
440    pub fn set_timestamp(&mut self, timestamp: Timestamp) {
441        self.timestamp = Some(timestamp);
442    }
443
444    /// Check if this is a remote frame.
445    #[must_use]
446    pub fn is_remote(&self) -> bool {
447        self.flags.contains(MessageFlags::RTR)
448    }
449
450    /// Check if this is a CAN-FD frame.
451    #[must_use]
452    pub fn is_fd(&self) -> bool {
453        self.flags.contains(MessageFlags::FD)
454    }
455
456    /// Check if this frame uses bit rate switching.
457    #[must_use]
458    pub fn is_brs(&self) -> bool {
459        self.flags.contains(MessageFlags::BRS)
460    }
461
462    /// Check if this frame has error state indicator set.
463    #[must_use]
464    pub fn is_esi(&self) -> bool {
465        self.flags.contains(MessageFlags::ESI)
466    }
467}
468
469#[cfg(test)]
470mod tests {
471    use super::*;
472
473    #[test]
474    fn test_can_id_standard() {
475        let id = CanId::Standard(0x123);
476        assert!(id.is_standard());
477        assert!(!id.is_extended());
478        assert_eq!(id.raw(), 0x123);
479    }
480
481    #[test]
482    fn test_can_id_extended() {
483        let id = CanId::Extended(0x1234_5678);
484        assert!(!id.is_standard());
485        assert!(id.is_extended());
486        assert_eq!(id.raw(), 0x1234_5678);
487    }
488
489    #[test]
490    fn test_message_flags() {
491        let flags = MessageFlags::FD | MessageFlags::BRS;
492        assert!(flags.contains(MessageFlags::FD));
493        assert!(flags.contains(MessageFlags::BRS));
494        assert!(!flags.contains(MessageFlags::RTR));
495    }
496
497    #[test]
498    fn test_timestamp() {
499        let ts = Timestamp::from_micros(1_500_000);
500        assert_eq!(ts.as_micros(), 1_500_000);
501        assert_eq!(ts.as_millis(), 1_500);
502        assert_eq!(ts.as_secs(), 1);
503    }
504
505    #[test]
506    fn test_can_message_standard() {
507        let msg = CanMessage::new_standard(0x123, &[1, 2, 3, 4]).unwrap();
508        assert_eq!(msg.id(), CanId::Standard(0x123));
509        assert_eq!(msg.data(), &[1, 2, 3, 4]);
510        assert!(!msg.is_remote());
511        assert!(!msg.is_fd());
512    }
513
514    #[test]
515    fn test_can_message_extended() {
516        let msg = CanMessage::new_extended(0x1234_5678, &[1, 2, 3, 4]).unwrap();
517        assert_eq!(msg.id(), CanId::Extended(0x1234_5678));
518        assert_eq!(msg.data(), &[1, 2, 3, 4]);
519    }
520
521    #[test]
522    fn test_can_message_fd() {
523        let msg = CanMessage::new_fd(CanId::Standard(0x123), &[1; 64]).unwrap();
524        assert_eq!(msg.data().len(), 64);
525        assert!(msg.is_fd());
526        assert!(msg.is_brs());
527    }
528
529    #[test]
530    fn test_can_message_remote() {
531        let msg = CanMessage::new_remote(CanId::Standard(0x123), 4).unwrap();
532        assert!(msg.is_remote());
533        assert_eq!(msg.data().len(), 0);
534    }
535
536    #[test]
537    fn test_invalid_standard_id() {
538        let result = CanMessage::new_standard(0x800, &[1, 2, 3]);
539        assert!(result.is_err());
540    }
541
542    #[test]
543    fn test_invalid_data_length() {
544        let result = CanMessage::new_standard(0x123, &[1; 9]);
545        assert!(result.is_err());
546    }
547
548    #[test]
549    fn test_invalid_fd_data_length() {
550        let result = CanMessage::new_fd(CanId::Standard(0x123), &[1; 65]);
551        assert!(result.is_err());
552    }
553}