1#[cfg(not(feature = "std"))]
16use alloc::vec::Vec;
17
18use crate::error::DecodeError;
19use core::fmt;
20
21pub const CHECKSUM_SIZE: usize = 4;
23
24#[derive(Debug, Clone, PartialEq)]
26pub struct RawData {
27 pub source_id: u32,
29 pub timestamp: u64,
31 pub value: f64,
33}
34
35impl RawData {
36 pub fn new(value: f64, timestamp: u64) -> Self {
38 Self {
39 source_id: 0,
40 timestamp,
41 value,
42 }
43 }
44
45 pub fn with_source(source_id: u32, value: f64, timestamp: u64) -> Self {
47 Self {
48 source_id,
49 timestamp,
50 value,
51 }
52 }
53
54 pub fn raw_size(&self) -> usize {
56 20
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
63#[repr(u8)]
64pub enum Priority {
65 P1Critical = 0,
67 P2Important = 1,
69 #[default]
71 P3Normal = 2,
72 P4Deferred = 3,
74 P5Disposable = 4,
76}
77
78impl Priority {
79 pub fn should_transmit(&self) -> bool {
81 matches!(
82 self,
83 Priority::P1Critical | Priority::P2Important | Priority::P3Normal
84 )
85 }
86
87 pub fn requires_ack(&self) -> bool {
89 matches!(self, Priority::P1Critical)
90 }
91
92 pub fn from_u8(value: u8) -> Option<Self> {
94 match value {
95 0 => Some(Priority::P1Critical),
96 1 => Some(Priority::P2Important),
97 2 => Some(Priority::P3Normal),
98 3 => Some(Priority::P4Deferred),
99 4 => Some(Priority::P5Disposable),
100 _ => None,
101 }
102 }
103}
104
105impl fmt::Display for Priority {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 match self {
108 Priority::P1Critical => write!(f, "P1-CRITICAL"),
109 Priority::P2Important => write!(f, "P2-IMPORTANT"),
110 Priority::P3Normal => write!(f, "P3-NORMAL"),
111 Priority::P4Deferred => write!(f, "P4-DEFERRED"),
112 Priority::P5Disposable => write!(f, "P5-DISPOSABLE"),
113 }
114 }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
119#[repr(u8)]
120pub enum MessageType {
121 #[default]
123 Data = 0,
124 Sync = 1,
126 Request = 2,
128 Response = 3,
130 Ack = 4,
132 Nack = 5,
134 Heartbeat = 6,
136 Reserved = 7,
138}
139
140impl MessageType {
141 pub fn from_u8(value: u8) -> Option<Self> {
143 match value {
144 0 => Some(MessageType::Data),
145 1 => Some(MessageType::Sync),
146 2 => Some(MessageType::Request),
147 3 => Some(MessageType::Response),
148 4 => Some(MessageType::Ack),
149 5 => Some(MessageType::Nack),
150 6 => Some(MessageType::Heartbeat),
151 7 => Some(MessageType::Reserved),
152 _ => None,
153 }
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
159#[repr(u8)]
160pub enum EncodingType {
161 #[default]
163 Raw64 = 0x00,
164 Raw32 = 0x01,
166 Delta8 = 0x10,
168 Delta16 = 0x11,
170 Delta32 = 0x12,
172 Pattern = 0x20,
174 PatternDelta = 0x21,
176 Repeated = 0x30,
178 Interpolated = 0x31,
180 Multi = 0x40,
182}
183
184impl EncodingType {
185 pub fn from_u8(value: u8) -> Option<Self> {
187 match value {
188 0x00 => Some(EncodingType::Raw64),
189 0x01 => Some(EncodingType::Raw32),
190 0x10 => Some(EncodingType::Delta8),
191 0x11 => Some(EncodingType::Delta16),
192 0x12 => Some(EncodingType::Delta32),
193 0x20 => Some(EncodingType::Pattern),
194 0x21 => Some(EncodingType::PatternDelta),
195 0x30 => Some(EncodingType::Repeated),
196 0x31 => Some(EncodingType::Interpolated),
197 0x40 => Some(EncodingType::Multi),
198 _ => None,
199 }
200 }
201
202 pub fn typical_size(&self) -> usize {
204 match self {
205 EncodingType::Raw64 => 8,
206 EncodingType::Raw32 => 4,
207 EncodingType::Delta8 => 1,
208 EncodingType::Delta16 => 2,
209 EncodingType::Delta32 => 4,
210 EncodingType::Pattern => 2, EncodingType::PatternDelta => 3, EncodingType::Repeated => 0,
213 EncodingType::Interpolated => 0,
214 EncodingType::Multi => 0, }
216 }
217}
218
219#[derive(Debug, Clone, PartialEq, Eq)]
221pub struct MessageHeader {
222 pub version: u8,
224 pub message_type: MessageType,
226 pub priority: Priority,
228 pub sequence: u32,
230 pub timestamp: u32,
232 pub context_version: u32,
234}
235
236impl MessageHeader {
237 pub fn new(message_type: MessageType, priority: Priority) -> Self {
239 Self {
240 version: crate::PROTOCOL_VERSION,
241 message_type,
242 priority,
243 sequence: 0,
244 timestamp: 0,
245 context_version: 0,
246 }
247 }
248
249 pub const SIZE: usize = 13;
251
252 pub fn encode_header_byte(&self) -> u8 {
254 let version_bits = (self.version & 0x03) << 6;
255 let type_bits = (self.message_type as u8 & 0x07) << 3;
256 let priority_bits = self.priority as u8 & 0x07;
257 version_bits | type_bits | priority_bits
258 }
259
260 pub fn decode_header_byte(byte: u8) -> (u8, Option<MessageType>, Option<Priority>) {
262 let version = (byte >> 6) & 0x03;
263 let msg_type = MessageType::from_u8((byte >> 3) & 0x07);
264 let priority = Priority::from_u8(byte & 0x07);
265 (version, msg_type, priority)
266 }
267
268 pub fn to_bytes(&self) -> [u8; Self::SIZE] {
270 let mut bytes = [0u8; Self::SIZE];
271 bytes[0] = self.encode_header_byte();
272 bytes[1..5].copy_from_slice(&self.sequence.to_be_bytes());
273 bytes[5..9].copy_from_slice(&self.timestamp.to_be_bytes());
274 bytes[9..13].copy_from_slice(&self.context_version.to_be_bytes());
275 bytes
276 }
277
278 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
280 if bytes.len() < Self::SIZE {
281 return None;
282 }
283
284 let (version, msg_type, priority) = Self::decode_header_byte(bytes[0]);
285 let msg_type = msg_type?;
286 let priority = priority?;
287
288 let sequence = u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
289 let timestamp = u32::from_be_bytes([bytes[5], bytes[6], bytes[7], bytes[8]]);
290 let context_version = u32::from_be_bytes([bytes[9], bytes[10], bytes[11], bytes[12]]);
291
292 Some(Self {
293 version,
294 message_type: msg_type,
295 priority,
296 sequence,
297 timestamp,
298 context_version,
299 })
300 }
301}
302
303impl Default for MessageHeader {
304 fn default() -> Self {
305 Self::new(MessageType::Data, Priority::P3Normal)
306 }
307}
308
309#[derive(Debug, Clone, PartialEq)]
311pub struct EncodedMessage {
312 pub header: MessageHeader,
314 pub payload: Vec<u8>,
316}
317
318impl EncodedMessage {
319 pub fn new(header: MessageHeader, payload: Vec<u8>) -> Self {
321 Self { header, payload }
322 }
323
324 pub fn len(&self) -> usize {
326 MessageHeader::SIZE + self.payload.len()
327 }
328
329 pub fn is_empty(&self) -> bool {
331 self.payload.is_empty()
332 }
333
334 pub fn encoding_type(&self) -> Option<EncodingType> {
336 if self.payload.len() >= 2 {
339 EncodingType::from_u8(self.payload[1])
340 } else {
341 None
342 }
343 }
344
345 pub fn to_bytes(&self) -> Vec<u8> {
347 let mut bytes = Vec::with_capacity(self.len());
348 bytes.extend_from_slice(&self.header.to_bytes());
349 bytes.extend_from_slice(&self.payload);
350 bytes
351 }
352
353 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
355 if bytes.len() < MessageHeader::SIZE {
356 return None;
357 }
358
359 let header = MessageHeader::from_bytes(&bytes[..MessageHeader::SIZE])?;
360 let payload = bytes[MessageHeader::SIZE..].to_vec();
361
362 Some(Self { header, payload })
363 }
364
365 pub fn compute_checksum(&self) -> u32 {
367 use xxhash_rust::xxh32::xxh32;
368
369 let mut data = Vec::with_capacity(MessageHeader::SIZE + self.payload.len());
370 data.extend_from_slice(&self.header.to_bytes());
371 data.extend_from_slice(&self.payload);
372
373 xxh32(&data, 0) }
375
376 pub fn to_bytes_with_checksum(&self) -> Vec<u8> {
378 let mut bytes = self.to_bytes();
379 let checksum = self.compute_checksum();
380 bytes.extend_from_slice(&checksum.to_be_bytes());
381 bytes
382 }
383
384 pub fn from_bytes_with_checksum(bytes: &[u8]) -> Result<Self, DecodeError> {
386 if bytes.len() < MessageHeader::SIZE + CHECKSUM_SIZE {
387 return Err(DecodeError::BufferTooShort {
388 needed: MessageHeader::SIZE + CHECKSUM_SIZE,
389 available: bytes.len(),
390 });
391 }
392
393 let checksum_offset = bytes.len() - CHECKSUM_SIZE;
394 let expected = u32::from_be_bytes(bytes[checksum_offset..].try_into().unwrap());
395
396 let message =
397 Self::from_bytes(&bytes[..checksum_offset]).ok_or(DecodeError::InvalidHeader)?;
398
399 let actual = message.compute_checksum();
400
401 if actual != expected {
402 return Err(DecodeError::InvalidChecksum { expected, actual });
403 }
404
405 Ok(message)
406 }
407}
408
409#[derive(Debug, Clone, PartialEq)]
411pub struct DecodedData {
412 pub source_id: u32,
414 pub timestamp: u64,
416 pub value: f64,
418 pub priority: Priority,
420 pub deferred_available: bool,
422}
423
424impl DecodedData {
425 pub fn new(source_id: u32, timestamp: u64, value: f64, priority: Priority) -> Self {
427 Self {
428 source_id,
429 timestamp,
430 value,
431 priority,
432 deferred_available: false,
433 }
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440
441 #[test]
442 fn test_priority_ordering() {
443 assert!(Priority::P1Critical < Priority::P2Important);
444 assert!(Priority::P2Important < Priority::P3Normal);
445 assert!(Priority::P3Normal < Priority::P4Deferred);
446 assert!(Priority::P4Deferred < Priority::P5Disposable);
447 }
448
449 #[test]
450 fn test_priority_should_transmit() {
451 assert!(Priority::P1Critical.should_transmit());
452 assert!(Priority::P2Important.should_transmit());
453 assert!(Priority::P3Normal.should_transmit());
454 assert!(!Priority::P4Deferred.should_transmit());
455 assert!(!Priority::P5Disposable.should_transmit());
456 }
457
458 #[test]
459 fn test_header_byte_roundtrip() {
460 let header = MessageHeader {
461 version: 1,
462 message_type: MessageType::Data,
463 priority: Priority::P2Important,
464 sequence: 0,
465 timestamp: 0,
466 context_version: 0,
467 };
468
469 let byte = header.encode_header_byte();
470 let (version, msg_type, priority) = MessageHeader::decode_header_byte(byte);
471
472 assert_eq!(version, 1);
473 assert_eq!(msg_type, Some(MessageType::Data));
474 assert_eq!(priority, Some(Priority::P2Important));
475 }
476
477 #[test]
478 fn test_header_serialization() {
479 let header = MessageHeader {
480 version: 1,
481 message_type: MessageType::Sync,
482 priority: Priority::P1Critical,
483 sequence: 12345,
484 timestamp: 67890,
485 context_version: 42,
486 };
487
488 let bytes = header.to_bytes();
489 let restored = MessageHeader::from_bytes(&bytes).unwrap();
490
491 assert_eq!(header.version, restored.version);
492 assert_eq!(header.message_type, restored.message_type);
493 assert_eq!(header.priority, restored.priority);
494 assert_eq!(header.sequence, restored.sequence);
495 assert_eq!(header.timestamp, restored.timestamp);
496 assert_eq!(header.context_version, restored.context_version);
497 }
498
499 #[test]
500 fn test_message_serialization() {
501 let message = EncodedMessage {
502 header: MessageHeader::default(),
503 payload: vec![0x00, 0x10, 0x42],
504 };
505
506 let bytes = message.to_bytes();
507 let restored = EncodedMessage::from_bytes(&bytes).unwrap();
508
509 assert_eq!(message.header.message_type, restored.header.message_type);
510 assert_eq!(message.payload, restored.payload);
511 }
512
513 #[test]
514 fn test_raw_data() {
515 let data = RawData::new(42.5, 12345);
516 assert_eq!(data.source_id, 0);
517 assert_eq!(data.value, 42.5);
518 assert_eq!(data.timestamp, 12345);
519 assert_eq!(data.raw_size(), 20);
520 }
521
522 #[test]
523 fn test_checksum_computation() {
524 let message = EncodedMessage {
525 header: MessageHeader::default(),
526 payload: vec![0x00, 0x10, 0x42],
527 };
528
529 let checksum1 = message.compute_checksum();
530 let checksum2 = message.compute_checksum();
531
532 assert_eq!(checksum1, checksum2);
534
535 let message2 = EncodedMessage {
537 header: MessageHeader::default(),
538 payload: vec![0x00, 0x10, 0x43],
539 };
540 let checksum3 = message2.compute_checksum();
541 assert_ne!(checksum1, checksum3);
542 }
543
544 #[test]
545 fn test_checksum_roundtrip() {
546 let message = EncodedMessage {
547 header: MessageHeader {
548 version: 1,
549 message_type: MessageType::Data,
550 priority: Priority::P2Important,
551 sequence: 42,
552 timestamp: 12345,
553 context_version: 7,
554 },
555 payload: vec![0x00, 0x10, 0x42, 0x55, 0xAA],
556 };
557
558 let bytes = message.to_bytes_with_checksum();
559 let restored = EncodedMessage::from_bytes_with_checksum(&bytes).unwrap();
560
561 assert_eq!(message.header.sequence, restored.header.sequence);
562 assert_eq!(message.header.timestamp, restored.header.timestamp);
563 assert_eq!(message.payload, restored.payload);
564 }
565
566 #[test]
567 fn test_checksum_corruption_detected() {
568 let message = EncodedMessage {
569 header: MessageHeader::default(),
570 payload: vec![0x00, 0x10, 0x42],
571 };
572
573 let mut bytes = message.to_bytes_with_checksum();
574
575 bytes[MessageHeader::SIZE] ^= 0xFF;
577
578 let result = EncodedMessage::from_bytes_with_checksum(&bytes);
579 assert!(matches!(result, Err(DecodeError::InvalidChecksum { .. })));
580 }
581
582 #[test]
583 fn test_checksum_buffer_too_short() {
584 let short_bytes = vec![0u8; MessageHeader::SIZE]; let result = EncodedMessage::from_bytes_with_checksum(&short_bytes);
587 assert!(matches!(result, Err(DecodeError::BufferTooShort { .. })));
588 }
589}