refactor: move firmware workspace under firmware

This commit is contained in:
Onur Özkan committed 2026-08-10 16:58:55 +03:00
1 parent 153b0bf262
commit f03fc119a4
222 files changed
+56 -13

No files matched your search

+260
View File
@@ -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
}