diff --git a/satrs-example/client/Cargo.toml b/satrs-example/client/Cargo.toml index 5876e73..bd28404 100644 --- a/satrs-example/client/Cargo.toml +++ b/satrs-example/client/Cargo.toml @@ -10,6 +10,7 @@ fern = "0.7" humantime = "2" serde = { version = "1" } serde_json = "1" +satrs = { path = "../../satrs" } satrs-example = { path = ".." } satrs-minisim = { path = "../minisim" } types = { path = "../types" } diff --git a/satrs-example/client/src/main.rs b/satrs-example/client/src/main.rs index ed90001..f6f39ab 100644 --- a/satrs-example/client/src/main.rs +++ b/satrs-example/client/src/main.rs @@ -53,6 +53,27 @@ impl From for SpiFaultMode { } } +#[derive(Debug, PartialEq, Eq, Clone, Copy, clap::ValueEnum)] +enum HealthStateSelect { + Healthy, + Faulty, + PermanentFaulty, + ExternalControl, + NeedsRecovery, +} + +impl From for satrs::health::HealthState { + fn from(state: HealthStateSelect) -> Self { + match state { + HealthStateSelect::Healthy => satrs::health::HealthState::Healthy, + HealthStateSelect::Faulty => satrs::health::HealthState::Faulty, + HealthStateSelect::PermanentFaulty => satrs::health::HealthState::PermanentFaulty, + HealthStateSelect::ExternalControl => satrs::health::HealthState::ExternalControl, + HealthStateSelect::NeedsRecovery => satrs::health::HealthState::NeedsRecovery, + } + } +} + #[derive(Debug, PartialEq, Eq, Clone, Copy, clap::Parser)] struct MgmArgs { #[arg(short, long)] @@ -67,6 +88,10 @@ struct MgmArgs { /// regardless of which MGM the request names (a pre-existing minisim limitation). #[arg(long, value_enum)] spi_fault: Option, + /// Override the device's FDIR health state, for example to clear a `Faulty` state set by + /// the handler after the underlying issue has been fixed or worked around. + #[arg(long, value_enum)] + health: Option, } #[derive(Debug, PartialEq, Eq, Clone, Copy, clap::Parser)] @@ -173,6 +198,23 @@ fn handle_mgm_command( let request_packet = request.to_vec(); client.send_to(&request_packet, addr).unwrap(); } + if let Some(health) = args.health { + let request = types::ccsds::CcsdsTcPacketOwned::new_with_request( + SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)), + TcHeader::new(target_id, types::MessageType::Health), + types::acs::mgm::request::Request::Health( + types::acs::mgm::request::HealthRequest::SetHealth(health.into()), + ), + ); + let sent_tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&request.sp_header); + log::info!( + "sending {:?} set-health request with TC ID {:#010x}", + target_id, + sent_tc_id.raw() + ); + let request_packet = request.to_vec(); + client.send_to(&request_packet, addr).unwrap(); + } Ok(()) } diff --git a/satrs-example/src/acs/mgm.rs b/satrs-example/src/acs/mgm.rs index ba3e0c0..387622a 100644 --- a/satrs-example/src/acs/mgm.rs +++ b/satrs-example/src/acs/mgm.rs @@ -271,6 +271,25 @@ impl MgmHandlerLis3Mdl { )), ), }, + mgm::request::Request::Health(health_request) => { + match health_request { + mgm::request::HealthRequest::SetHealth(health_state) => { + log::info!( + "{}: setting health to {:?} via ground command", + self.id.str(), + health_state + ); + self.health_table.set_health( + self.id.component_id().into(), + health_state, + ); + self.send_telemetry( + Some(tc_id), + mgm::response::Response::Ok, + ); + } + } + } } } Err(e) => { diff --git a/satrs-example/types/Cargo.toml b/satrs-example/types/Cargo.toml index 99b7a90..1911db2 100644 --- a/satrs-example/types/Cargo.toml +++ b/satrs-example/types/Cargo.toml @@ -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" } diff --git a/satrs-example/types/src/acs/mgm.rs b/satrs-example/types/src/acs/mgm.rs index 8868f30..1f6c147 100644 --- a/satrs-example/types/src/acs/mgm.rs +++ b/satrs-example/types/src/acs/mgm.rs @@ -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, } } } diff --git a/satrs-example/types/src/lib.rs b/satrs-example/types/src/lib.rs index d79afd7..44c370f 100644 --- a/satrs-example/types/src/lib.rs +++ b/satrs-example/types/src/lib.rs @@ -150,6 +150,7 @@ pub enum MessageType { Action, Event, Verification, + Health, } pub trait Message { diff --git a/satrs/src/health.rs b/satrs/src/health.rs index 9992b27..20e563b 100644 --- a/satrs/src/health.rs +++ b/satrs/src/health.rs @@ -1,6 +1,7 @@ use crate::ComponentId; #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum HealthState { Healthy = 1, Faulty = 2,