refactor(firmware): centralize configuration and diagnostics

This commit is contained in:
Onur Özkan committed 2026-08-22 18:37:18 +03:00
1 parent 7048ed89d0
commit 387fdaad23
140 files changed
+6891 -4541

No files matched your search

+115 -18
View File
@@ -3,9 +3,18 @@
use crate::{ConfigViolation, ValidationRule};
use kaptan_log::KaptanError;
/// Maximum choices representable by one enum-backed field.
///
/// Enum values and indexed discovery use their native `u8` domain. Options
/// are immutable flash metadata, so no smaller runtime resource limit is
/// necessary.
pub const MAX_ENUM_OPTIONS_PER_FIELD: usize = u8::MAX as usize;
/// Maximum UTF-8 bytes advertised for one enum variant name.
pub const MAX_ENUM_OPTION_LABEL_BYTES: usize = 32;
/// A stable, schema-local identity for one editable configuration field.
///
/// IDs are never reused within a schema generation. They are compact wire
/// IDs are never reused within a schema version. They are compact wire
/// identities; display strings belong to metadata and diagnostics only.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
#[repr(transparent)]
@@ -56,6 +65,88 @@ pub enum ConfigValue {
F64(f64),
}
/// One named value advertised for an enum-backed configuration field.
///
/// Enum metadata belongs to the compiled schema. Generic transports can
/// therefore render choices without knowing field keys or firmware-specific
/// enum types. The tuple is `(stable u8 representation, display label)`.
pub type EnumOption = (u8, &'static str);
/// Failure to project a generic configuration scalar into its native type.
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ScalarValueError {
/// The generic scalar kind does not match the native field type.
WrongKind(ValueKind),
/// The representation has the correct scalar kind but no native value.
OutOfDomain,
}
/// Native representation used by generated configuration fields.
///
/// The generic manager and transports retain their compact fixed scalar
/// protocol while board and runtime structures keep the narrow or enum type
/// that actually models the value.
#[doc(hidden)]
pub trait ConfigScalar: Copy {
/// Generic scalar kind used at schema and transport boundaries.
const KIND: ValueKind;
/// Projects the native value into the generic scalar boundary.
fn into_config_value(self) -> ConfigValue;
/// Validates and projects one generic scalar into the native type.
fn try_from_config_value(value: ConfigValue) -> Result<Self, ScalarValueError>;
}
macro_rules! direct_config_scalar {
($type:ty, $kind:ident, $variant:ident) => {
impl ConfigScalar for $type {
const KIND: ValueKind = ValueKind::$kind;
fn into_config_value(self) -> ConfigValue {
ConfigValue::$variant(self)
}
fn try_from_config_value(value: ConfigValue) -> Result<Self, ScalarValueError> {
match value {
ConfigValue::$variant(value) => Ok(value),
value => Err(ScalarValueError::WrongKind(value.kind())),
}
}
}
};
}
direct_config_scalar!(bool, Bool, Bool);
direct_config_scalar!(i32, I32, I32);
direct_config_scalar!(u32, U32, U32);
direct_config_scalar!(u64, U64, U64);
direct_config_scalar!(f32, F32, F32);
direct_config_scalar!(f64, F64, F64);
macro_rules! narrow_unsigned_config_scalar {
($type:ty) => {
impl ConfigScalar for $type {
const KIND: ValueKind = ValueKind::U32;
fn into_config_value(self) -> ConfigValue {
ConfigValue::U32(u32::from(self))
}
fn try_from_config_value(value: ConfigValue) -> Result<Self, ScalarValueError> {
match value {
ConfigValue::U32(value) => {
Self::try_from(value).map_err(|_| ScalarValueError::OutOfDomain)
}
value => Err(ScalarValueError::WrongKind(value.kind())),
}
}
}
};
}
narrow_unsigned_config_scalar!(u8);
narrow_unsigned_config_scalar!(u16);
impl ConfigValue {
/// The declared value kind.
pub const fn kind(self) -> ValueKind {
@@ -80,6 +171,26 @@ impl ConfigValue {
Self::F64(value) => value.to_bits(),
}
}
/// Decodes the canonical scalar representation used by snapshots and the
/// configuration wire protocol.
pub const fn try_from_raw_bits(kind: ValueKind, bits: u64) -> Option<Self> {
match kind {
ValueKind::Bool => match bits {
0 => Some(Self::Bool(false)),
1 => Some(Self::Bool(true)),
_ => None,
},
ValueKind::I32 if bits <= u32::MAX as u64 => Some(Self::I32(bits as u32 as i32)),
ValueKind::U32 if bits <= u32::MAX as u64 => Some(Self::U32(bits as u32)),
ValueKind::U64 => Some(Self::U64(bits)),
ValueKind::F32 if bits <= u32::MAX as u64 => {
Some(Self::F32(f32::from_bits(bits as u32)))
}
ValueKind::F64 => Some(Self::F64(f64::from_bits(bits))),
ValueKind::I32 | ValueKind::U32 | ValueKind::F32 => None,
}
}
}
/// The scalar kind carried in a descriptor or typed protocol message.
@@ -100,18 +211,6 @@ pub enum ValueKind {
F64 = 6,
}
/// When a committed field may affect the active runtime.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum ApplyPolicy {
/// Reserved for individually proven live-safe changes.
Live = 1,
/// Reserved for controlled output-inhibited reconfiguration.
DisarmedReset = 2,
/// P1 policy: the field takes effect only at the next boot.
RestartRequired = 3,
}
/// Static metadata for one configuration field.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FieldDescriptor {
@@ -125,12 +224,10 @@ pub struct FieldDescriptor {
pub minimum: Option<ConfigValue>,
/// Inclusive upper bound when the value is ordered.
pub maximum: Option<ConfigValue>,
/// Firmware-build default.
pub default: ConfigValue,
/// Named choices for an enum-backed field, otherwise empty.
pub enum_options: &'static [EnumOption],
/// Human-readable unit for schema metadata.
pub unit: &'static str,
/// Activation policy advertised to a control station.
pub apply_policy: ApplyPolicy,
}
/// Failure while changing one transaction field.
@@ -168,7 +265,7 @@ impl FieldWriteError {
/// Vehicle-specific editable candidate model.
///
/// Implementations expose every operator-editable scalar through the static
/// Implementations expose every user-editable scalar through the static
/// descriptor registry. A field write performs only local validation; complete
/// relational and capability validation happens through [`Self::validate`].
pub trait ConfigModel: Clone {