feat: add FDIR fault counter and wire it into the MGM device handler

Add satrs::fdir::FaultCounter, an FSFW-style error threshold counter:
counts faults, decrements over time when faults stop, and reports
when a threshold is exceeded. Two variants for now, mirroring the
hk.rs helper pattern:
- FaultCounterStd, backed by std::time::Instant
- FaultCounterEmbassy, backed by embassy_time::Instant (embassy-time
  feature), with an optional defmt::Format impl gated on the defmt
  feature

Add satrs::health::HealthTableMapSync::default() for easy construction
of a shared, global health table.

Wire both into the example app's MGM device handler as the first real
FDIR use case:
- the minisim MGM model gains SpiFaultMode (None/AllZeros/AllOnes) and
  a SetSpiFault request, so a stuck SPI bus can be injected for testing,
  independent of switch state
- MgmHandlerLis3Mdl::poll_sensor checks the SPI transfer result: a
  comm timeout or an all-1s stuck-bus reply (the same pattern the sim
  already uses for "device off") counts as a fault. Above threshold,
  the component is marked Faulty in a HealthTableMapSync shared from
  main.rs. This logic lives in the device handler, not the SPI comm
  layer, since deciding what a failed transfer means for FDIR is a
  handler concern.
- an all-0s reply is deliberately not treated as a fault, since it
  collides with a legitimate zero-field reading

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BaKjBjnxaHJ6vzficJcjN4
This commit is contained in:
Robin MuellerandClaude Sonnet 5 committed 2026-09-16 13:15:00 +02:00
1 parent 9de9ce3131
commit 1256c6b07f
14 files changed
+872 -100

No files matched your search

+1 -1
View File
@@ -6,7 +6,7 @@ edition = "2024"
[dependencies]
serde = { version = "1", features = ["derive"] }
spacepackets = { version = "0.18", default-features = false }
satrs = { path = "../../satrs" }
satrs = { path = "../../satrs", features = ["serde"] }
num_enum = { version = "0.7" }
strum = { version = "0.28", features = ["derive"] }
postcard = { version = "1" }
+9
View File
@@ -7,6 +7,13 @@ pub mod request {
ReadMode,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum HealthRequest {
/// Overrides the device's autonomous FDIR health state, for example to clear a `Faulty`
/// state set by the handler after ground has fixed or worked around the underlying issue.
SetHealth(satrs::health::HealthState),
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum HkId {
Sensor,
@@ -23,6 +30,7 @@ pub mod request {
Ping,
Hk(HkRequest),
Mode(ModeRequest),
Health(HealthRequest),
}
impl Request {
@@ -31,6 +39,7 @@ pub mod request {
Request::Ping => crate::MessageType::Verification,
Request::Hk(_hk_request) => crate::MessageType::Hk,
Request::Mode(_mode) => crate::MessageType::Mode,
Request::Health(_health) => crate::MessageType::Health,
}
}
}
+1
View File
@@ -150,6 +150,7 @@ pub enum MessageType {
Action,
Event,
Verification,
Health,
}
pub trait Message {