refactor(firmware): centralize configuration and diagnostics
This commit is contained in:
1 parent
7048ed89d0
commit
387fdaad23
140 files changed
+6891
-4541
No files matched your search
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user