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}