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

@@ -0,0 +1,14 @@
[package]
name = "kaptan-flight-runtime"
edition.workspace = true
license.workspace = true
rust-version.workspace = true
version.workspace = true
[lints]
workspace = true
[dependencies]
kaptan-config = { path = "../kaptan-config" }
kaptan-flight-config = { path = "../kaptan-flight-config" }
kaptan-log = { path = "../kaptan-log" }
@@ -0,0 +1,129 @@
//! Central, transport-neutral flight-runtime composition.
//!
//! This crate owns the one configuration manager and one bounded log hub for
//! an image. MAVLink adapters receive mutable borrows for bounded dispatch or
//! draining, but neither can own configuration lifecycle or link state.
#![no_std]
use kaptan_config::{ConfigManager, ProfileStore, StartupError};
use kaptan_flight_config::{CandidateProfile, VehicleCapabilities};
use kaptan_log::{EventHub, LogFlags, LogRecord, LogText, PublishOutcome, Severity};
/// Default bounded allocation for central runtime text diagnostics. Error
/// records are partitioned from optional detail.
pub type RuntimeLogHub = EventHub<8, 16>;
/// One boot-active vehicle runtime. It deliberately has no MAVLink dependency.
pub struct FlightRuntime<S> {
configuration: ConfigManager<CandidateProfile, S>,
logs: RuntimeLogHub,
}
impl<S> FlightRuntime<S>
where
S: ProfileStore<CandidateProfile>,
{
/// Recovers the central manager, validates its boot-active snapshot, and
/// copies the restart-only observability policy into the bounded hub.
pub fn recover(
capabilities: VehicleCapabilities,
store: S,
) -> Result<Self, StartupError<S::Error>> {
let configuration =
ConfigManager::recover(CandidateProfile::default(), capabilities, store)?;
let maximum_log_level = configuration.active().observability().maximum_log_level();
let logs = RuntimeLogHub::new(kaptan_log::LogFilter::new(
Severity::try_from_wire(maximum_log_level as u8)
.expect("validated flight profile has a MAVLink log severity"),
));
Ok(Self {
configuration,
logs,
})
}
/// The sole central configuration manager.
pub const fn configuration(&self) -> &ConfigManager<CandidateProfile, S> {
&self.configuration
}
/// Borrows the manager and hub together for a state-only protocol adapter.
/// This makes adapter ownership inversion unrepresentable in composition.
pub fn configuration_and_logs_mut(
&mut self,
) -> (&mut ConfigManager<CandidateProfile, S>, &mut RuntimeLogHub) {
(&mut self.configuration, &mut self.logs)
}
/// Publishes one preformatted bounded task record through the sole hub.
pub fn log(&mut self, severity: Severity, flags: LogFlags, message: LogText) -> PublishOutcome {
self.logs.publish_text(severity, flags, message)
}
/// Peeks the highest-priority pending text log for the sole telemetry link.
pub fn pending_report(&self) -> Option<LogRecord> {
self.logs.peek()
}
/// Submits the next record and removes it only after transport admission.
pub fn submit_next<R>(
&mut self,
submit: impl FnOnce(&LogRecord) -> (kaptan_log::TransportAdmission, R),
) -> Option<R> {
self.logs.submit_next(submit)
}
/// Immutable boot-active log policy copied from the validated profile.
pub const fn log_filter(&self) -> kaptan_log::LogFilter {
self.logs.filter()
}
}
#[cfg(test)]
mod tests {
use kaptan_config::{Generation, ProfileStore};
use kaptan_log::{LogFlags, LogText, Severity};
use super::FlightRuntime;
struct Store;
impl ProfileStore<kaptan_flight_config::CandidateProfile> for Store {
type Error = ();
fn load(
&mut self,
) -> Result<Option<(Generation, kaptan_flight_config::CandidateProfile)>, Self::Error>
{
Ok(None)
}
fn persist(
&mut self,
_: &kaptan_flight_config::CandidateProfile,
) -> Result<Generation, Self::Error> {
Ok(Generation::new(1))
}
}
#[test]
fn central_runtime_uses_the_active_profile_filter_and_owns_one_hub() {
let mut runtime =
FlightRuntime::recover(kaptan_flight_config::VehicleCapabilities::quad_x(), Store)
.expect("factory profile validates");
assert_eq!(
runtime
.configuration()
.active()
.observability()
.maximum_log_level(),
runtime.log_filter().maximum_severity() as u32
);
let _ = runtime.log(
Severity::Info,
LogFlags::NONE,
LogText::from_message("runtime ready"),
);
assert!(runtime.pending_report().is_some());
}
}