refactor(firmware): simplify configuration and diagnostics
This commit is contained in:
1 parent
ba9c97ed3b
commit
fdf82a5114
96 files changed
+1612
-2772
No files matched your search
@@ -2,71 +2,42 @@
|
||||
|
||||
use std::{collections::VecDeque, time::SystemTime};
|
||||
|
||||
use kaptan_log::Level;
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
const MAX_LOG_ENTRIES: usize = 512;
|
||||
|
||||
/// Severity attached to one decoded diagnostic event.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum LogSeverity {
|
||||
Emergency,
|
||||
Alert,
|
||||
Critical,
|
||||
Error,
|
||||
Warning,
|
||||
Notice,
|
||||
Info,
|
||||
Debug,
|
||||
Trace,
|
||||
Unknown(u8),
|
||||
fn serialize_level<S>(level: &Level, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(level.as_str())
|
||||
}
|
||||
|
||||
impl LogSeverity {
|
||||
/// Decodes the stable MAV_SEVERITY numeric representation without
|
||||
/// accepting values that the protocol has not defined.
|
||||
pub const fn from_wire(level: u8, trace: bool) -> Self {
|
||||
match level {
|
||||
0 => Self::Emergency,
|
||||
1 => Self::Alert,
|
||||
2 => Self::Critical,
|
||||
3 => Self::Error,
|
||||
4 => Self::Warning,
|
||||
5 => Self::Notice,
|
||||
6 => Self::Info,
|
||||
7 if trace => Self::Trace,
|
||||
7 => Self::Debug,
|
||||
value => Self::Unknown(value),
|
||||
}
|
||||
/// Decodes the exact Kaptan log-level subset from its MAVLink representation.
|
||||
///
|
||||
/// Kaptan emits Error, Warn, Info, and Debug on the conventional MAVLink
|
||||
/// numbers 3, 4, 6, and 7. Trace is Debug plus the dedicated trace flag. The
|
||||
/// other MAVLink severity numbers are not Kaptan log levels.
|
||||
pub const fn level_from_wire(level: u8, trace: bool) -> Option<Level> {
|
||||
if trace && level != 7 {
|
||||
return None;
|
||||
}
|
||||
|
||||
const fn known_label(self) -> Option<&'static str> {
|
||||
match self {
|
||||
Self::Emergency => Some("EMERGENCY"),
|
||||
Self::Alert => Some("ALERT"),
|
||||
Self::Critical => Some("CRITICAL"),
|
||||
Self::Error => Some("ERROR"),
|
||||
Self::Warning => Some("WARNING"),
|
||||
Self::Notice => Some("NOTICE"),
|
||||
Self::Info => Some("INFO"),
|
||||
Self::Debug => Some("DEBUG"),
|
||||
Self::Trace => Some("TRACE"),
|
||||
Self::Unknown(_) => None,
|
||||
}
|
||||
match level {
|
||||
3 => Some(Level::Error),
|
||||
4 => Some(Level::Warn),
|
||||
6 => Some(Level::Info),
|
||||
7 if trace => Some(Level::Trace),
|
||||
7 => Some(Level::Debug),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for LogSeverity {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
if let Some(label) = self.known_label() {
|
||||
serializer.serialize_str(label)
|
||||
} else if let Self::Unknown(value) = self {
|
||||
serializer.serialize_str(&format!("UNKNOWN ({value})"))
|
||||
} else {
|
||||
unreachable!("every known severity has a static label")
|
||||
}
|
||||
/// Human-readable packet-view representation, including malformed values.
|
||||
pub fn wire_level_label(level: u8, trace: bool) -> String {
|
||||
match level_from_wire(level, trace) {
|
||||
Some(level) => level.as_str().into(),
|
||||
None => format!("UNKNOWN ({level})"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +46,8 @@ impl Serialize for LogSeverity {
|
||||
pub struct LogObservation {
|
||||
pub system_id: u8,
|
||||
pub component_id: u8,
|
||||
pub severity: LogSeverity,
|
||||
pub level: Level,
|
||||
pub truncated: bool,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
@@ -94,7 +66,9 @@ pub struct LogView {
|
||||
pub received_at_ms: u64,
|
||||
pub system_id: u8,
|
||||
pub component_id: u8,
|
||||
pub severity: LogSeverity,
|
||||
#[serde(serialize_with = "serialize_level")]
|
||||
pub level: Level,
|
||||
pub truncated: bool,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
@@ -131,7 +105,8 @@ impl LogMonitor {
|
||||
received_at_ms,
|
||||
system_id: observation.system_id,
|
||||
component_id: observation.component_id,
|
||||
severity: observation.severity,
|
||||
level: observation.level,
|
||||
truncated: observation.truncated,
|
||||
text: observation.text,
|
||||
});
|
||||
self.next_id += 1;
|
||||
@@ -164,7 +139,8 @@ mod tests {
|
||||
LogObservation {
|
||||
system_id: 1,
|
||||
component_id: 2,
|
||||
severity: LogSeverity::Info,
|
||||
level: Level::Info,
|
||||
truncated: false,
|
||||
text: format!("event {index}"),
|
||||
}
|
||||
}
|
||||
@@ -179,10 +155,15 @@ mod tests {
|
||||
assert_eq!(snapshot.entries.len(), 2);
|
||||
assert_eq!(snapshot.entries[0].id, 0);
|
||||
assert_eq!(snapshot.entries[0].received_at_ms, 1_234);
|
||||
assert!(!snapshot.entries[0].truncated);
|
||||
assert_eq!(snapshot.entries[0].text, "event 0");
|
||||
assert_eq!(snapshot.entries[1].id, 1);
|
||||
assert_eq!(snapshot.entries[1].text, "event 1");
|
||||
assert_eq!(snapshot.discarded, 0);
|
||||
assert_eq!(
|
||||
serde_json::to_value(&snapshot).unwrap()["entries"][0]["level"],
|
||||
"INFO"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -213,14 +194,41 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wire_severity_is_checked_and_unknown_values_remain_visible() {
|
||||
assert_eq!(LogSeverity::from_wire(3, false), LogSeverity::Error);
|
||||
assert_eq!(LogSeverity::from_wire(7, false), LogSeverity::Debug);
|
||||
assert_eq!(LogSeverity::from_wire(7, true), LogSeverity::Trace);
|
||||
assert_eq!(LogSeverity::from_wire(9, false), LogSeverity::Unknown(9));
|
||||
fn wire_level_accepts_only_the_exact_kaptan_contract() {
|
||||
for (wire, expected) in [
|
||||
(0, None),
|
||||
(1, None),
|
||||
(2, None),
|
||||
(3, Some(Level::Error)),
|
||||
(4, Some(Level::Warn)),
|
||||
(5, None),
|
||||
(6, Some(Level::Info)),
|
||||
(7, Some(Level::Debug)),
|
||||
(8, None),
|
||||
(u8::MAX, None),
|
||||
] {
|
||||
assert_eq!(level_from_wire(wire, false), expected, "wire {wire}");
|
||||
}
|
||||
assert_eq!(level_from_wire(7, true), Some(Level::Trace));
|
||||
for invalid_trace_level in [0, 1, 2, 3, 4, 5, 6, 8, u8::MAX] {
|
||||
assert_eq!(level_from_wire(invalid_trace_level, true), None);
|
||||
}
|
||||
assert_eq!(wire_level_label(2, false), "UNKNOWN (2)");
|
||||
assert_eq!(wire_level_label(9, false), "UNKNOWN (9)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truncation_state_survives_the_browser_projection() {
|
||||
let mut event = observation(0);
|
||||
event.truncated = true;
|
||||
let mut monitor = LogMonitor::default();
|
||||
monitor.observe([event], SystemTime::UNIX_EPOCH);
|
||||
|
||||
let snapshot = monitor.snapshot();
|
||||
assert!(snapshot.entries[0].truncated);
|
||||
assert_eq!(
|
||||
serde_json::to_value(LogSeverity::Unknown(9)).unwrap(),
|
||||
"UNKNOWN (9)"
|
||||
serde_json::to_value(&snapshot).unwrap()["entries"][0]["truncated"],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user