refactor(firmware): simplify configuration and diagnostics

This commit is contained in:
Onur Özkan committed 2026-08-23 00:40:36 +03:00
1 parent ba9c97ed3b
commit fdf82a5114
96 files changed
+1612 -2772

No files matched your search

@@ -3,7 +3,7 @@
use kaptan_config::{
ConfigModel, ConfigValue, ConfigViolation, FieldWriteError, ValidationRule, ValueKind,
};
use kaptan_log::KaptanError;
use kaptan_err_propagate::KaptanError;
use minicbor::{Decoder, Encoder, encode::Write};
use crate::{CandidateProfile, FORMAT_VERSION, SCHEMA_ID, VehicleCapabilities};
@@ -17,12 +17,16 @@ const SCHEMA_KEY: u16 = u16::MAX;
#[derive(Clone, Copy, Debug, Eq, PartialEq, KaptanError)]
pub enum CborError {
/// The supplied payload exceeded the bounded configuration storage budget.
#[kaptan_error(id = 3001)]
TooLarge,
/// CBOR was malformed or did not match a supported versioned wire record.
#[kaptan_error(id = 3002)]
Malformed,
/// A second CBOR item followed the candidate snapshot.
#[kaptan_error(id = 3003)]
TrailingData,
/// The decoded candidate violated complete schema/capability validation.
#[kaptan_error(id = 3004)]
InvalidProfile(ConfigViolation),
}
@@ -201,7 +201,7 @@ pub struct VehicleProfile {
pub(crate) thrust: ThrustSettings,
pub(crate) rate_pid: PidSettings,
pub(crate) actuator_limits: ActuatorLimits,
pub(crate) mavlink_logging: super::MavlinkLoggingSettings,
pub(crate) mavlink_logging: kaptan_log::LevelFilter,
pub(crate) mixer: [[f32; 4]; 4],
}
@@ -228,7 +228,7 @@ impl VehicleProfile {
self.actuator_limits
}
/// Immutable boot-active policy for the MAVLink diagnostic sink.
pub const fn mavlink_logging(self) -> super::MavlinkLoggingSettings {
pub const fn mavlink_logging(self) -> kaptan_log::LevelFilter {
self.mavlink_logging
}
pub const fn mixer(self) -> [[f32; 4]; 4] {
@@ -4,11 +4,11 @@ use kaptan_config::{
Config, ConfigModel, ConfigValue, ConfigViolation, FieldDescriptor, FieldId, FieldWriteError,
ValidationRule,
};
use kaptan_log::Severity;
use kaptan_log::LevelFilter;
use super::{
ActuatorLimits, AidingSettings, ControlSettings, MavlinkLoggingSettings, PidSettings,
SafetySettings, ThrustSettings, TimingSettings, VehicleCapabilities, VehicleProfile,
ActuatorLimits, AidingSettings, ControlSettings, PidSettings, SafetySettings, ThrustSettings,
TimingSettings, VehicleCapabilities, VehicleProfile,
control::{
ActuatorLimitsCandidate, AidingCandidate, ControlCandidate, PidCandidate, ThrustCandidate,
},
@@ -18,7 +18,7 @@ use super::{
};
/// Current persisted CBOR schema for the vehicle candidate.
pub const FORMAT_VERSION: u16 = 2;
pub const FORMAT_VERSION: u16 = 3;
/// Complete editable vehicle candidate.
///
@@ -83,8 +83,7 @@ impl CandidateProfile {
slew_per_second: 8.0,
},
mavlink_logging: MavlinkLoggingCandidate {
enabled: true,
maximum_severity: Severity::Info,
level_filter: LevelFilter::Info,
},
}
}
@@ -147,8 +146,7 @@ impl CandidateProfile {
slew_per_second: actuator_limits.slew_per_second(),
},
mavlink_logging: MavlinkLoggingCandidate {
enabled: mavlink_logging.enabled(),
maximum_severity: mavlink_logging.maximum_severity(),
level_filter: mavlink_logging,
},
}
}
@@ -203,10 +201,7 @@ impl CandidateProfile {
maximum: self.actuator_limits.maximum,
slew_per_second: self.actuator_limits.slew_per_second,
},
mavlink_logging: MavlinkLoggingSettings::new(
self.mavlink_logging.enabled,
self.mavlink_logging.maximum_severity,
),
mavlink_logging: self.mavlink_logging.level_filter,
mixer: capabilities.mixer(),
})
}
@@ -258,7 +253,7 @@ impl ConfigModel for CandidateProfile {
mod tests {
use kaptan_config::{ConfigModel, ConfigValue, FieldId, FieldWriteError, ValidationRule};
use super::{CandidateProfile, Severity, VehicleCapabilities};
use super::{CandidateProfile, LevelFilter, VehicleCapabilities};
use crate::profile::ids;
const CAPABILITIES: VehicleCapabilities = VehicleCapabilities::quad_x();
@@ -268,8 +263,8 @@ mod tests {
assert_eq!(
crate::SCHEMA_ID,
[
0xc4, 0xf6, 0x36, 0xc0, 0x29, 0xd2, 0x3e, 0x29, 0xa3, 0x85, 0x5b, 0x36, 0xb7, 0xb3,
0x59, 0x9c,
0xa1, 0x4c, 0x28, 0xba, 0x08, 0xc0, 0xfd, 0xc3, 0xe2, 0x54, 0x7d, 0x77, 0x40, 0x3c,
0xed, 0x94,
]
);
}
@@ -350,39 +345,35 @@ mod tests {
#[test]
fn mavlink_logging_policy_is_persisted_and_restart_only() {
let mut candidate = CandidateProfile::factory();
let severity_descriptor = CandidateProfile::descriptors()
.find(|descriptor| descriptor.display_key == "mavlink.logging.maximum_severity")
let level_descriptor = CandidateProfile::descriptors()
.find(|descriptor| descriptor.display_key == "mavlink.logging.level_filter")
.unwrap();
assert_eq!(
severity_descriptor.enum_options,
Severity::CONFIG_ENUM_OPTIONS
level_descriptor.enum_options,
LevelFilter::CONFIG_ENUM_OPTIONS
);
candidate
.set_field(
descriptor_id("mavlink.logging.enabled"),
ConfigValue::Bool(false),
descriptor_id("mavlink.logging.level_filter"),
ConfigValue::U32(LevelFilter::Off.wire_value().into()),
)
.unwrap();
let active = candidate.validate(&CAPABILITIES).unwrap();
assert!(!active.mavlink_logging().enabled());
assert_eq!(active.mavlink_logging(), LevelFilter::Off);
candidate
.set_field(
descriptor_id("mavlink.logging.maximum_severity"),
ConfigValue::U32(5),
descriptor_id("mavlink.logging.level_filter"),
ConfigValue::U32(LevelFilter::Trace.wire_value().into()),
)
.unwrap();
assert_eq!(
candidate
.validate(&CAPABILITIES)
.unwrap()
.mavlink_logging()
.maximum_severity(),
Severity::Notice
candidate.validate(&CAPABILITIES).unwrap().mavlink_logging(),
LevelFilter::Trace
);
assert!(matches!(
candidate.set_field(
descriptor_id("mavlink.logging.maximum_severity"),
ConfigValue::U32(8),
descriptor_id("mavlink.logging.level_filter"),
ConfigValue::U32(6),
),
Err(FieldWriteError::Violation(issue))
if issue.rule() == ValidationRule::InvalidEnumValue
@@ -390,22 +381,21 @@ mod tests {
}
#[test]
fn every_persisted_log_severity_is_retained_exactly() {
for raw in 0..=7 {
fn every_persisted_log_filter_is_retained_exactly() {
for raw in 0..=5 {
let mut candidate = CandidateProfile::factory();
candidate
.set_field(
descriptor_id("mavlink.logging.maximum_severity"),
descriptor_id("mavlink.logging.level_filter"),
ConfigValue::U32(raw),
)
.expect("descriptor range accepts the stable severity");
.expect("descriptor accepts the stable level filter");
assert_eq!(
candidate
.validate(&CAPABILITIES)
.expect("valid complete profile")
.mavlink_logging()
.maximum_severity(),
Severity::try_from_wire(u8::try_from(raw).unwrap()).unwrap()
.mavlink_logging(),
LevelFilter::try_from_wire(u8::try_from(raw).unwrap()).unwrap()
);
}
}
@@ -1,44 +1,16 @@
//! Persisted, restart-only MAVLink diagnostic-sink policy.
use kaptan_config::Config;
use kaptan_log::Severity;
use kaptan_log::LevelFilter;
/// Candidate policy for the MAVLink diagnostic sink.
#[derive(Clone, Copy, Config)]
pub(super) struct MavlinkLoggingCandidate {
#[config(id = 24, key = "mavlink.logging.enabled", unit = "bool")]
pub(super) enabled: bool,
#[config(
enum,
id = 25,
key = "mavlink.logging.maximum_severity",
unit = "mav_severity"
key = "mavlink.logging.level_filter",
unit = "log_level"
)]
pub(super) maximum_severity: Severity,
}
/// Immutable boot-active policy for the MAVLink diagnostic sink.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct MavlinkLoggingSettings {
enabled: bool,
maximum_severity: Severity,
}
impl MavlinkLoggingSettings {
pub(crate) const fn new(enabled: bool, maximum_severity: Severity) -> Self {
Self {
enabled,
maximum_severity,
}
}
/// Whether the MAVLink diagnostic sink accepts records.
pub const fn enabled(self) -> bool {
self.enabled
}
/// Maximum accepted MAVLink severity, from Emergency (0) through Debug (7).
pub const fn maximum_severity(self) -> Severity {
self.maximum_severity
}
pub(super) level_filter: LevelFilter,
}
@@ -13,4 +13,3 @@ pub use active::{
TimingSettings, VehicleCapabilities, VehicleProfile,
};
pub use candidate::{CandidateProfile, FORMAT_VERSION};
pub use mavlink_logging::MavlinkLoggingSettings;