//! Per-kind parse-error counters surfaced in `MavlinkLayer::health()`. use std::sync::atomic::{AtomicU64, Ordering}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ParseErrorKind { /// Frame failed CRC verification. Crc, /// Frame payload was shorter than the header advertised. Truncated, /// Frame's message id is outside the ยง7.7 surface. UnknownId, /// Per-link sequence number jumped (logged but not fatal). SequenceGap, /// Message-specific payload decode failed (e.g. enum out of range). InvalidPayload, } #[derive(Debug, Default)] pub struct ParseErrors { crc: AtomicU64, truncated: AtomicU64, unknown_id: AtomicU64, sequence_gap: AtomicU64, invalid_payload: AtomicU64, } impl ParseErrors { pub fn new() -> Self { Self::default() } pub fn record(&self, kind: ParseErrorKind) { let cell = match kind { ParseErrorKind::Crc => &self.crc, ParseErrorKind::Truncated => &self.truncated, ParseErrorKind::UnknownId => &self.unknown_id, ParseErrorKind::SequenceGap => &self.sequence_gap, ParseErrorKind::InvalidPayload => &self.invalid_payload, }; cell.fetch_add(1, Ordering::Relaxed); } pub fn snapshot(&self) -> ParseErrorsSnapshot { ParseErrorsSnapshot { crc: self.crc.load(Ordering::Relaxed), truncated: self.truncated.load(Ordering::Relaxed), unknown_id: self.unknown_id.load(Ordering::Relaxed), sequence_gap: self.sequence_gap.load(Ordering::Relaxed), invalid_payload: self.invalid_payload.load(Ordering::Relaxed), } } } #[derive(Debug, Clone, Copy, Default)] pub struct ParseErrorsSnapshot { pub crc: u64, pub truncated: u64, pub unknown_id: u64, pub sequence_gap: u64, pub invalid_payload: u64, } impl ParseErrorsSnapshot { pub fn total(&self) -> u64 { self.crc + self.truncated + self.unknown_id + self.sequence_gap + self.invalid_payload } } #[cfg(test)] mod tests { use super::*; #[test] fn records_increment_independently() { // Arrange let pe = ParseErrors::new(); // Act pe.record(ParseErrorKind::Crc); pe.record(ParseErrorKind::UnknownId); pe.record(ParseErrorKind::UnknownId); // Assert let snap = pe.snapshot(); assert_eq!(snap.crc, 1); assert_eq!(snap.unknown_id, 2); assert_eq!(snap.total(), 3); } }