refactor(firmware): simplify configuration and diagnostics

This commit is contained in:
Onur Özkan committed 2026-08-23 00:40:36 +03:00
1 parent ba9c97ed3b
commit fdf82a5114
96 files changed
+1612 -2772

No files matched your search

+28 -213
View File
@@ -1,55 +1,8 @@
//! Bounded human-readable diagnostic records.
//! Bounded human-readable log records.
use crate::{ErrorAdditionalInformation, ErrorPath, LogText, Severity};
use crate::{Level, LogText};
/// Static identity and optional detail for one error.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct StaticError {
path: &'static str,
additional_information: &'static str,
}
impl StaticError {
/// Creates one compile-time error identity without additional detail.
pub const fn new(path: &'static str) -> Self {
Self {
path,
additional_information: "",
}
}
/// Creates one compile-time error identity with static additional detail.
pub const fn with_additional_information(
path: &'static str,
additional_information: &'static str,
) -> Self {
Self {
path,
additional_information,
}
}
/// Stable human-readable error identity.
pub const fn path(self) -> &'static str {
self.path
}
/// Optional static human-facing detail.
pub const fn additional_information(self) -> &'static str {
self.additional_information
}
}
/// Implemented by [`crate::KaptanError`] derives.
///
/// This trait is deliberately pure: obtaining error identity never allocates,
/// publishes telemetry, changes safety state, or serializes generic sources.
pub trait ErrorReport {
/// Returns static identity and optional detail for this error value.
fn static_error(&self) -> &'static StaticError;
}
/// Bounded MAVLink log flags.
/// Flags belonging exclusively to a log record.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct LogFlags(u8);
@@ -57,208 +10,70 @@ pub struct LogFlags(u8);
impl LogFlags {
/// No optional flags.
pub const NONE: Self = Self(0);
/// Rust trace level, encoded as MAVLink DEBUG.
pub const TRACE: Self = Self(1 << 0);
/// Text did not fit the fixed record buffer.
/// Message text exceeded its fixed capacity.
pub const TRUNCATED: Self = Self(1 << 1);
/// Multiple ISR producer observations were folded into one mailbox event.
pub const COALESCED: Self = Self(1 << 2);
/// Raw MAVLink bitmask.
/// Raw transport bitmask.
pub const fn bits(self) -> u8 {
self.0
}
/// Bits valid for an ordinary `KAPTAN_LOG` record.
pub const fn log_bits(self) -> u8 {
self.0 & (Self::TRACE.0 | Self::TRUNCATED.0)
}
/// Bits valid for an error-only record. Trace is a property of ordinary
/// `KAPTAN_LOG` diagnostics and is intentionally never emitted on
/// `KAPTAN_ERROR`.
pub const fn error_bits(self) -> u8 {
self.0 & (Self::TRUNCATED.0 | Self::COALESCED.0)
}
/// Adds a bit.
pub const fn with(self, other: Self) -> Self {
Self(self.0 | other.0)
}
/// Tests whether a bit is present.
pub const fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
}
/// Wire family selected for one diagnostic record.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RecordChannel {
/// An ordinary severity-and-text `KAPTAN_LOG` record.
Log,
/// An error-only `KAPTAN_ERROR` record.
Error,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum RecordBody {
Log {
message: LogText,
},
Error {
path: ErrorPath,
additional_information: ErrorAdditionalInformation,
},
}
/// Priority selected internally from a diagnostic level.
/// Internal transport priority selected from a conventional log level.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DeliveryClass {
/// Error logs retained independently from optional log verbosity.
/// Error-level logs.
Critical,
/// Warning/info/debug/trace logs subject to the configured filter.
/// Warning, info, debug, and trace logs.
BestEffort,
}
/// A fully formatted bounded diagnostic waiting for MAVLink admission.
/// One bounded transport-neutral log event.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LogRecord {
severity: Severity,
level: Level,
flags: LogFlags,
body: RecordBody,
message: LogText,
}
impl LogRecord {
/// Creates one bounded diagnostic record. Error-or-higher severities use
/// their supplied text as the error path with no additional information;
/// ordinary severity records use `KAPTAN_LOG` message text.
pub fn new(severity: Severity, flags: LogFlags, message: LogText) -> Self {
if severity <= Severity::Error {
let flags = with_truncation(flags, message);
return Self::error_with_severity(
severity,
flags,
ErrorPath::from_message(message.as_str()),
ErrorAdditionalInformation::new(),
);
/// Creates one bounded log record at the supplied level.
pub fn new(level: Level, mut flags: LogFlags, message: LogText) -> Self {
if message.is_truncated() {
flags = flags.with(LogFlags::TRUNCATED);
}
Self {
severity,
flags: with_truncation(flags, message),
body: RecordBody::Log { message },
}
}
/// Creates an error-channel record with independently bounded identity
/// and optional additional information.
pub fn error(
flags: LogFlags,
path: ErrorPath,
additional_information: ErrorAdditionalInformation,
) -> Self {
Self::error_with_severity(Severity::Error, flags, path, additional_information)
}
/// Creates one record from a typed derived error without choosing a sink.
pub fn from_error<E: ErrorReport>(flags: LogFlags, error: &E) -> Self {
Self::from_static_error(flags, error.static_error())
}
/// Creates one record from an ISR-safe static error without choosing a sink.
pub fn from_static_error(flags: LogFlags, error: &'static StaticError) -> Self {
Self::error(
level,
flags,
ErrorPath::from_message(error.path()),
ErrorAdditionalInformation::from_message(error.additional_information()),
)
}
fn error_with_severity(
severity: Severity,
flags: LogFlags,
path: ErrorPath,
additional_information: ErrorAdditionalInformation,
) -> Self {
debug_assert!(severity <= Severity::Error);
Self {
severity,
flags: flags_for_error(flags, path, additional_information),
body: RecordBody::Error {
path,
additional_information,
},
message,
}
}
/// Wire-compatible severity.
pub const fn severity(&self) -> Severity {
self.severity
/// Conventional log level.
pub const fn level(&self) -> Level {
self.level
}
/// Wire flag bitmask.
/// Log-specific flag bitmask.
pub const fn flags(&self) -> LogFlags {
self.flags
}
/// Fixed text carried by an ordinary `KAPTAN_LOG` record.
pub const fn message(&self) -> Option<&LogText> {
match &self.body {
RecordBody::Log { message } => Some(message),
RecordBody::Error { .. } => None,
}
/// Fixed human-readable message.
pub const fn message(&self) -> &LogText {
&self.message
}
/// Fixed identity path carried by a `KAPTAN_ERROR` record.
pub const fn error_path(&self) -> Option<&ErrorPath> {
match &self.body {
RecordBody::Log { .. } => None,
RecordBody::Error { path, .. } => Some(path),
}
}
/// Fixed optional information carried by a `KAPTAN_ERROR` record.
pub const fn additional_information(&self) -> Option<&ErrorAdditionalInformation> {
match &self.body {
RecordBody::Log { .. } => None,
RecordBody::Error {
additional_information,
..
} => Some(additional_information),
}
}
/// Custom MAVLink diagnostic family selected for this record.
pub const fn channel(&self) -> RecordChannel {
match self.body {
RecordBody::Log { .. } => RecordChannel::Log,
RecordBody::Error { .. } => RecordChannel::Error,
}
}
/// Internal admission priority.
/// Internal transport priority.
pub const fn delivery(&self) -> DeliveryClass {
match self.body {
RecordBody::Log { .. } => DeliveryClass::BestEffort,
RecordBody::Error { .. } => DeliveryClass::Critical,
match self.level {
Level::Error => DeliveryClass::Critical,
Level::Warn | Level::Info | Level::Debug | Level::Trace => DeliveryClass::BestEffort,
}
}
}
fn with_truncation(mut flags: LogFlags, value: LogText) -> LogFlags {
if value.is_truncated() {
flags = flags.with(LogFlags::TRUNCATED);
}
flags
}
fn flags_for_error(
mut flags: LogFlags,
path: ErrorPath,
additional_information: ErrorAdditionalInformation,
) -> LogFlags {
if path.is_truncated() || additional_information.is_truncated() {
flags = flags.with(LogFlags::TRUNCATED);
}
flags
}