refactor: move firmware workspace under firmware
This commit is contained in:
1 parent
153b0bf262
commit
f03fc119a4
222 files changed
+56
-13
No files matched your search
@@ -0,0 +1,260 @@
|
||||
//! Bounded human-readable diagnostic records.
|
||||
|
||||
use crate::{ErrorAdditionalInformation, ErrorPath, LogText, Severity};
|
||||
|
||||
/// 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.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
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.
|
||||
pub const TRUNCATED: Self = Self(1 << 1);
|
||||
/// Adjacent equivalent records were coalesced.
|
||||
pub const COALESCED: Self = Self(1 << 2);
|
||||
|
||||
/// Raw MAVLink bitmask.
|
||||
pub const fn bits(self) -> u8 {
|
||||
self.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.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum DeliveryClass {
|
||||
/// Error logs retained independently from optional log verbosity.
|
||||
Critical,
|
||||
/// Warning/info/debug/trace logs subject to the configured filter.
|
||||
BestEffort,
|
||||
}
|
||||
|
||||
/// A fully formatted bounded diagnostic waiting for MAVLink admission.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct LogRecord {
|
||||
severity: Severity,
|
||||
flags: LogFlags,
|
||||
body: RecordBody,
|
||||
/// Adjacent equivalent diagnostics retained as one record.
|
||||
pub occurrences: u32,
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
Self {
|
||||
severity,
|
||||
flags: with_truncation(flags, message),
|
||||
body: RecordBody::Log { message },
|
||||
occurrences: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
occurrences: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wire-compatible severity.
|
||||
pub const fn severity(&self) -> Severity {
|
||||
self.severity
|
||||
}
|
||||
|
||||
/// Wire 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 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.
|
||||
pub const fn delivery(&self) -> DeliveryClass {
|
||||
match self.body {
|
||||
RecordBody::Log { .. } => DeliveryClass::BestEffort,
|
||||
RecordBody::Error { .. } => DeliveryClass::Critical,
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests whether two pending records can coalesce without hiding a new
|
||||
/// payload, severity, or delivery class.
|
||||
pub fn is_equivalent_to(&self, other: &Self) -> bool {
|
||||
self.severity == other.severity && self.flags == other.flags && self.body == other.body
|
||||
}
|
||||
|
||||
pub(crate) fn increment_occurrences(&mut self) {
|
||||
self.occurrences = self.occurrences.saturating_add(1);
|
||||
self.flags = self.flags.with(LogFlags::COALESCED);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
Reference in new issue
Block a user