feat(firmware): validate configuration and migrate sensor-driven flight APIs
Validate configuration through its owning schemas and keep board declarations separate from shared F722 resource ownership. Bound USB transitions, isolate transport failures and retain restart-only activation. Migrate navigation and flight consumers with the sensor and schema contracts. Require explicit installation data and synchronous output authority; board motor outputs remain inhibited. Validation: assembled-prefix host tests, strict Clippy, platform/USB tests and both ARM release builds. Final-source coverage and scoped Miri/Kani evidence are retained separately from hardware qualification.
This commit is contained in:
1 parent
fb1b8af2d1
commit
f9a61a086f
211 files changed
+45657
-12740
No files matched your search
@@ -290,3 +290,91 @@ fn recursive_tree_rejects_duplicate_display_keys() {
|
||||
Err(issue) if issue.rule() == ValidationRule::Inconsistent
|
||||
));
|
||||
}
|
||||
|
||||
#[derive(Config)]
|
||||
struct FloatRules {
|
||||
#[config(id = 1, key = "finite", unit = "", finite)]
|
||||
finite: f32,
|
||||
#[config(id = 2, key = "nonzero", unit = "", nonzero)]
|
||||
nonzero: f64,
|
||||
#[config(id = 3, key = "lower", unit = "", range(min = 0.0))]
|
||||
lower: f32,
|
||||
#[config(id = 4, key = "upper", unit = "", range(max = 1.0))]
|
||||
upper: f64,
|
||||
#[config(id = 5, key = "free", unit = "")]
|
||||
free: f64,
|
||||
#[config(id = 6, key = "nonzero32", unit = "", nonzero)]
|
||||
nonzero32: f32,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scalar_rules_preserve_local_repair_and_float_domains() {
|
||||
let mut values = FloatRules {
|
||||
finite: f32::NAN,
|
||||
nonzero: 0.0,
|
||||
lower: f32::NAN,
|
||||
upper: f64::NAN,
|
||||
free: f64::NAN,
|
||||
nonzero32: 0.0,
|
||||
};
|
||||
// Every other field is invalid. A local repair must still succeed.
|
||||
values
|
||||
.set_field(FieldId::new(1), ConfigValue::F32(1.0))
|
||||
.unwrap();
|
||||
for (id, value, rule) in [
|
||||
(
|
||||
1,
|
||||
ConfigValue::F32(f32::INFINITY),
|
||||
ValidationRule::NonFinite,
|
||||
),
|
||||
(2, ConfigValue::F64(0.0), ValidationRule::Zero),
|
||||
(2, ConfigValue::F64(-0.0), ValidationRule::Zero),
|
||||
(3, ConfigValue::F32(f32::NAN), ValidationRule::Unordered),
|
||||
(4, ConfigValue::F64(f64::NAN), ValidationRule::Unordered),
|
||||
(6, ConfigValue::F32(0.0), ValidationRule::Zero),
|
||||
(6, ConfigValue::F32(-0.0), ValidationRule::Zero),
|
||||
] {
|
||||
let id = FieldId::new(id);
|
||||
let before = values.field_value(id);
|
||||
assert_eq!(
|
||||
values.set_field(id, value),
|
||||
Err(FieldWriteError::Violation(ConfigViolation::field(id, rule)))
|
||||
);
|
||||
assert_eq!(values.field_value(id), before);
|
||||
}
|
||||
// Neither nonzero nor a one-sided range implies finite.
|
||||
for (id, value) in [
|
||||
(2, ConfigValue::F64(f64::from_bits(0xfff0_0000_0000_0042))),
|
||||
(3, ConfigValue::F32(f32::INFINITY)),
|
||||
(4, ConfigValue::F64(f64::NEG_INFINITY)),
|
||||
(5, ConfigValue::F64(f64::from_bits(0x7ff8_0000_0000_1234))),
|
||||
(6, ConfigValue::F32(f32::INFINITY)),
|
||||
] {
|
||||
values.set_field(FieldId::new(id), value).unwrap();
|
||||
assert_eq!(values.field_value(FieldId::new(id)), Some(value));
|
||||
}
|
||||
values.validate().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rule_metadata_changes_fingerprints() {
|
||||
let descriptor = FloatRules::DESCRIPTORS[0];
|
||||
assert!(descriptor.finite);
|
||||
assert!(!descriptor.nonzero);
|
||||
assert!(FloatRules::DESCRIPTORS[1].nonzero);
|
||||
let original = kaptan_config::validation::__schema_id(&[descriptor]);
|
||||
assert_ne!(
|
||||
original,
|
||||
kaptan_config::validation::__schema_id(&[kaptan_config::FieldDescriptor {
|
||||
finite: false,
|
||||
..descriptor
|
||||
}])
|
||||
);
|
||||
assert_ne!(
|
||||
original,
|
||||
kaptan_config::validation::__schema_id(&[kaptan_config::FieldDescriptor {
|
||||
nonzero: true,
|
||||
..descriptor
|
||||
}])
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use kaptan_config_derive::Config;
|
||||
|
||||
#[derive(Config)]
|
||||
struct InvertedNativeBounds {
|
||||
#[config(id = 1, key = "value", unit = "", range(min = 1.0000000596046448, max = 1.0))]
|
||||
value: f32,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: configuration range minimum exceeds maximum
|
||||
--> tests/ui/f32_double_rounded_bounds.rs:6:5
|
||||
|
|
||||
6 | value: f32,
|
||||
| ^^^^^
|
||||
@@ -0,0 +1,24 @@
|
||||
use kaptan_config_derive::Config;
|
||||
|
||||
#[derive(Config)]
|
||||
struct NonzeroBool {
|
||||
#[config(id = 1, key = "value", unit = "")]
|
||||
#[config(nonzero)]
|
||||
value: bool,
|
||||
}
|
||||
|
||||
#[derive(Config)]
|
||||
struct RangedBool {
|
||||
#[config(id = 1, key = "value", unit = "")]
|
||||
#[config(range(min = 0, max = 1))]
|
||||
value: bool,
|
||||
}
|
||||
|
||||
#[derive(Config)]
|
||||
struct FiniteBool {
|
||||
#[config(id = 1, key = "value", unit = "")]
|
||||
#[config(finite)]
|
||||
value: bool,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,17 @@
|
||||
error: nonzero and range require a supported primitive numeric field
|
||||
--> tests/ui/numeric_rules_on_bool.rs:6:14
|
||||
|
|
||||
6 | #[config(nonzero)]
|
||||
| ^^^^^^^
|
||||
|
||||
error: nonzero and range require a supported primitive numeric field
|
||||
--> tests/ui/numeric_rules_on_bool.rs:13:14
|
||||
|
|
||||
13 | #[config(range(min = 0, max = 1))]
|
||||
| ^^^^^
|
||||
|
||||
error: finite requires an f32 or f64 field
|
||||
--> tests/ui/numeric_rules_on_bool.rs:20:14
|
||||
|
|
||||
20 | #[config(finite)]
|
||||
| ^^^^^^
|
||||
@@ -0,0 +1,31 @@
|
||||
use kaptan_config_derive::{Config, ConfigEnum};
|
||||
|
||||
#[derive(Clone, Copy, ConfigEnum)]
|
||||
#[repr(u8)]
|
||||
enum Mode {
|
||||
Off = 0,
|
||||
On = 1,
|
||||
}
|
||||
|
||||
#[derive(Config)]
|
||||
struct NonzeroEnum {
|
||||
#[config(enum, id = 1, key = "value", unit = "")]
|
||||
#[config(nonzero)]
|
||||
value: Mode,
|
||||
}
|
||||
|
||||
#[derive(Config)]
|
||||
struct RangedEnum {
|
||||
#[config(enum, id = 1, key = "value", unit = "")]
|
||||
#[config(range(min = 0, max = 1))]
|
||||
value: Mode,
|
||||
}
|
||||
|
||||
#[derive(Config)]
|
||||
struct FiniteEnum {
|
||||
#[config(enum, id = 1, key = "value", unit = "")]
|
||||
#[config(finite)]
|
||||
value: Mode,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,17 @@
|
||||
error: nonzero and range require a supported primitive numeric field
|
||||
--> tests/ui/numeric_rules_on_enum.rs:13:14
|
||||
|
|
||||
13 | #[config(nonzero)]
|
||||
| ^^^^^^^
|
||||
|
||||
error: nonzero and range require a supported primitive numeric field
|
||||
--> tests/ui/numeric_rules_on_enum.rs:20:14
|
||||
|
|
||||
20 | #[config(range(min = 0, max = 1))]
|
||||
| ^^^^^
|
||||
|
||||
error: finite requires an f32 or f64 field
|
||||
--> tests/ui/numeric_rules_on_enum.rs:27:14
|
||||
|
|
||||
27 | #[config(finite)]
|
||||
| ^^^^^^
|
||||
@@ -0,0 +1,15 @@
|
||||
use kaptan_config_derive::Config;
|
||||
|
||||
#[derive(Config)]
|
||||
struct UnsupportedScalar {
|
||||
#[config(id = 1, key = "wide", unit = "")]
|
||||
wide: u128,
|
||||
}
|
||||
|
||||
#[derive(Config)]
|
||||
struct UnsupportedCheckedScalar {
|
||||
#[config(id = 2, key = "signed", unit = "", nonzero)]
|
||||
signed: i64,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: non-primitive configuration fields require config(enum)
|
||||
--> tests/ui/unsupported_scalar.rs:6:11
|
||||
|
|
||||
6 | wide: u128,
|
||||
| ^^^^
|
||||
|
||||
error: nonzero and range require a supported primitive numeric field
|
||||
--> tests/ui/unsupported_scalar.rs:11:49
|
||||
|
|
||||
11 | #[config(id = 2, key = "signed", unit = "", nonzero)]
|
||||
| ^^^^^^^
|
||||
Reference in new issue
Block a user