refactor: move firmware workspace under firmware
This commit is contained in:
1 parent
153b0bf262
commit
f03fc119a4
222 files changed
+56
-13
No files matched your search
@@ -0,0 +1,200 @@
|
||||
//! Transport-neutral typed configuration fields and descriptors.
|
||||
|
||||
use crate::{ConfigViolation, ValidationRule};
|
||||
use kaptan_log::KaptanError;
|
||||
|
||||
/// A stable, schema-local identity for one editable configuration field.
|
||||
///
|
||||
/// IDs are never reused within a schema generation. They are compact wire
|
||||
/// identities; display strings belong to metadata and diagnostics only.
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct FieldId(u16);
|
||||
|
||||
impl FieldId {
|
||||
/// Creates a stable field identity.
|
||||
pub const fn new(value: u16) -> Self {
|
||||
assert!(
|
||||
value != 0 && value != u16::MAX,
|
||||
"reserved configuration field id"
|
||||
);
|
||||
Self(value)
|
||||
}
|
||||
|
||||
/// Parses a field identity received from an untrusted transport.
|
||||
pub const fn try_new(value: u16) -> Option<Self> {
|
||||
if value == 0 || value == u16::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(Self(value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the transport representation.
|
||||
pub const fn get(self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// The scalar representation of a configuration value.
|
||||
///
|
||||
/// The variants deliberately preserve native widths: durations, for example,
|
||||
/// remain `u64` microseconds instead of being lossy display-unit conversions.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ConfigValue {
|
||||
/// Boolean value.
|
||||
Bool(bool),
|
||||
/// Signed 32-bit integer.
|
||||
I32(i32),
|
||||
/// Unsigned 32-bit integer.
|
||||
U32(u32),
|
||||
/// Unsigned 64-bit integer.
|
||||
U64(u64),
|
||||
/// IEEE-754 single-precision value.
|
||||
F32(f32),
|
||||
/// IEEE-754 double-precision value.
|
||||
F64(f64),
|
||||
}
|
||||
|
||||
impl ConfigValue {
|
||||
/// The declared value kind.
|
||||
pub const fn kind(self) -> ValueKind {
|
||||
match self {
|
||||
Self::Bool(_) => ValueKind::Bool,
|
||||
Self::I32(_) => ValueKind::I32,
|
||||
Self::U32(_) => ValueKind::U32,
|
||||
Self::U64(_) => ValueKind::U64,
|
||||
Self::F32(_) => ValueKind::F32,
|
||||
Self::F64(_) => ValueKind::F64,
|
||||
}
|
||||
}
|
||||
|
||||
/// Canonical scalar bits for compact transport encodings.
|
||||
pub const fn raw_bits(self) -> u64 {
|
||||
match self {
|
||||
Self::Bool(value) => value as u64,
|
||||
Self::I32(value) => value as u32 as u64,
|
||||
Self::U32(value) => value as u64,
|
||||
Self::U64(value) => value,
|
||||
Self::F32(value) => value.to_bits() as u64,
|
||||
Self::F64(value) => value.to_bits(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The scalar kind carried in a descriptor or typed protocol message.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum ValueKind {
|
||||
/// Boolean.
|
||||
Bool = 1,
|
||||
/// Signed 32-bit integer.
|
||||
I32 = 2,
|
||||
/// Unsigned 32-bit integer.
|
||||
U32 = 3,
|
||||
/// Unsigned 64-bit integer.
|
||||
U64 = 4,
|
||||
/// Single-precision float.
|
||||
F32 = 5,
|
||||
/// Double-precision float.
|
||||
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 {
|
||||
/// Stable schema-local identity.
|
||||
pub id: FieldId,
|
||||
/// GCS display key; never used as a wire identity.
|
||||
pub display_key: &'static str,
|
||||
/// Scalar storage type.
|
||||
pub kind: ValueKind,
|
||||
/// Inclusive lower bound when the value is ordered.
|
||||
pub minimum: Option<ConfigValue>,
|
||||
/// Inclusive upper bound when the value is ordered.
|
||||
pub maximum: Option<ConfigValue>,
|
||||
/// Firmware-build default.
|
||||
pub default: ConfigValue,
|
||||
/// 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.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, KaptanError)]
|
||||
pub enum FieldWriteError {
|
||||
/// No descriptor exposes the requested identity.
|
||||
UnknownField(FieldId),
|
||||
/// The supplied scalar type differs from the descriptor type.
|
||||
WrongType {
|
||||
/// Requested field.
|
||||
field: FieldId,
|
||||
/// Expected descriptor kind.
|
||||
expected: ValueKind,
|
||||
/// Supplied scalar kind.
|
||||
actual: ValueKind,
|
||||
},
|
||||
/// A locally validated scalar violated a stable rule.
|
||||
Violation(ConfigViolation),
|
||||
}
|
||||
|
||||
impl FieldWriteError {
|
||||
/// Returns the typed local validation issue, if this was one.
|
||||
pub const fn violation(self) -> Option<ConfigViolation> {
|
||||
match self {
|
||||
Self::Violation(violation) => Some(violation),
|
||||
Self::UnknownField(_) | Self::WrongType { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a field-local non-finite error.
|
||||
pub const fn non_finite(field: FieldId) -> Self {
|
||||
Self::Violation(ConfigViolation::field(field, ValidationRule::NonFinite))
|
||||
}
|
||||
}
|
||||
|
||||
/// Vehicle-specific editable candidate model.
|
||||
///
|
||||
/// Implementations expose every operator-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 {
|
||||
/// Immutable configuration consumed by the runtime at boot.
|
||||
type Active;
|
||||
/// Immutable compiled board/vehicle capability contract.
|
||||
type Context;
|
||||
|
||||
/// Exact identifier for the current externally transferable schema.
|
||||
const SCHEMA_ID: [u8; 16];
|
||||
|
||||
/// Returns the descriptor at a stable zero-based schema index.
|
||||
fn descriptor(index: u16) -> Option<&'static FieldDescriptor>;
|
||||
|
||||
/// Number of fields exposed by the compiled schema.
|
||||
fn field_count() -> u16;
|
||||
|
||||
/// Returns a candidate value by stable ID.
|
||||
fn field_value(&self, id: FieldId) -> Option<ConfigValue>;
|
||||
|
||||
/// Returns one value from the immutable boot-active view.
|
||||
fn active_field_value(active: &Self::Active, id: FieldId) -> Option<ConfigValue>;
|
||||
|
||||
/// Applies exactly one locally valid scalar to this candidate.
|
||||
fn set_field(&mut self, id: FieldId, value: ConfigValue) -> Result<(), FieldWriteError>;
|
||||
|
||||
/// Fully validates and converts the candidate to an immutable active view.
|
||||
fn validate(&self, context: &Self::Context) -> Result<Self::Active, ConfigViolation>;
|
||||
}
|
||||
Reference in new issue
Block a user