feat(config): add validated profile core
This commit is contained in:
1 parent
0a75860841
commit
6c5e061359
36 files changed
+3343
No files matched your search
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user