feat(mavlink): adopt the kaptan-native dialect

This commit is contained in:
Onur Özkan committed 2026-08-24 00:56:06 +03:00
1 parent 1c90870634
commit 5f91465e66
87 files changed
+7733 -6850

No files matched your search

+39 -36
View File
@@ -7,13 +7,16 @@
#![no_std]
#[cfg(test)]
extern crate std;
use kaptan_config::{ConfigManager, ConfigStore, StartupError};
use kaptan_flight_config::{CandidateProfile, VehicleCapabilities};
use kaptan_log::{LogRecord, LogSinkQueue};
/// Default bounded allocation for the MAVLink diagnostic sink. Error records
/// are partitioned from optional detail.
pub type RuntimeMavlinkLogQueue = LogSinkQueue<8, 16>;
type RuntimeMavlinkLogQueue = LogSinkQueue<8, 16>;
/// One boot-active vehicle runtime. It deliberately has no MAVLink dependency.
pub struct FlightRuntime<S> {
@@ -45,20 +48,9 @@ where
&self.configuration
}
/// Mutably borrows the sole configuration manager for protocol dispatch.
pub fn configuration_mut(&mut self) -> &mut ConfigManager<CandidateProfile, S> {
&mut self.configuration
}
/// Composition-owned fan-out boundary for one preformatted event.
/// A future blackbox adds its independent sink admission here.
/// Composition-owned admission boundary for one preformatted event.
pub fn publish_log_event(&mut self, record: LogRecord) {
let _ = self.mavlink_logs.publish_record(record);
}
/// Peeks the highest-priority pending text log for the sole telemetry link.
pub fn pending_mavlink_report(&self) -> Option<LogRecord> {
self.mavlink_logs.peek()
self.mavlink_logs.publish_record(record);
}
/// Submits the next record and removes it only after transport admission.
@@ -68,25 +60,26 @@ where
) -> Option<R> {
self.mavlink_logs.submit_next(submit)
}
/// Immutable boot-active MAVLink log policy copied from the validated profile.
pub const fn mavlink_log_filter(&self) -> kaptan_log::LevelFilter {
self.mavlink_logs.filter()
}
}
#[cfg(test)]
mod tests {
use kaptan_config::{ConfigModel, ConfigStore, ConfigValue};
use kaptan_log::{Level, LevelFilter, LogFlags, LogRecord, LogText};
use kaptan_log::TransportAdmission;
use std::sync::Mutex;
use super::FlightRuntime;
static LOG_TEST: Mutex<()> = Mutex::new(());
struct Store;
impl ConfigStore<kaptan_flight_config::CandidateProfile> for Store {
type Error = ();
fn load(&mut self) -> Result<Option<kaptan_flight_config::CandidateProfile>, Self::Error> {
fn load(
&mut self,
_: &kaptan_flight_config::CandidateProfile,
) -> Result<Option<kaptan_flight_config::CandidateProfile>, Self::Error> {
Ok(None)
}
@@ -100,6 +93,8 @@ mod tests {
#[test]
fn runtime_uses_the_active_mavlink_policy_and_owns_one_sink_queue() {
let _guard = LOG_TEST.lock().unwrap();
while kaptan_log::drain_one(|_| ()).is_some() {}
let mut runtime = FlightRuntime::recover(
kaptan_flight_config::CandidateProfile::factory(),
kaptan_flight_config::VehicleCapabilities::quad_x(),
@@ -108,24 +103,29 @@ mod tests {
.expect("factory configuration validates");
assert_eq!(
runtime.configuration().active().mavlink_logging(),
runtime.mavlink_log_filter()
kaptan_log::LevelFilter::Info
);
kaptan_log::info!("runtime ready");
runtime.publish_log_event(kaptan_log::drain_one(|record| *record).unwrap());
assert_eq!(
runtime.submit_next_mavlink(|record| {
assert_eq!(record.level(), kaptan_log::Level::Info);
(TransportAdmission::Accepted, ())
}),
Some(())
);
runtime.publish_log_event(LogRecord::new(
Level::Info,
LogFlags::NONE,
LogText::from_message("runtime ready"),
));
assert!(runtime.pending_mavlink_report().is_some());
}
#[test]
fn disabled_mavlink_sink_filters_every_level() {
let _guard = LOG_TEST.lock().unwrap();
while kaptan_log::drain_one(|_| ()).is_some() {}
let mut factory = kaptan_flight_config::CandidateProfile::factory();
let level_filter = kaptan_flight_config::CandidateProfile::descriptors()
.find(|descriptor| descriptor.display_key == "mavlink.logging.level_filter")
.expect("MAVLink log-level descriptor");
factory
.set_field(level_filter.id, ConfigValue::U32(0))
.set_field(level_filter.id, ConfigValue::U8(0))
.unwrap();
let mut runtime = FlightRuntime::recover(
factory,
@@ -134,12 +134,15 @@ mod tests {
)
.expect("disabled MAVLink sink is valid");
assert_eq!(runtime.mavlink_log_filter(), LevelFilter::Off);
runtime.publish_log_event(LogRecord::new(
Level::Error,
LogFlags::NONE,
LogText::from_message("not routed to MAVLink"),
));
assert!(runtime.pending_mavlink_report().is_none());
assert_eq!(
runtime.configuration().active().mavlink_logging(),
kaptan_log::LevelFilter::Off
);
kaptan_log::error!("not routed to MAVLink");
runtime.publish_log_event(kaptan_log::drain_one(|record| *record).unwrap());
assert_eq!(
runtime.submit_next_mavlink(|_| (TransportAdmission::Accepted, ())),
None
);
}
}