Skip to content

Commit

Permalink
add health module
Browse files Browse the repository at this point in the history
  • Loading branch information
robamu committed Jan 31, 2025
1 parent 9325707 commit f2648fb
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 105 deletions.
157 changes: 114 additions & 43 deletions satrs/src/dev_mgmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,122 @@ use crate::{
subsystem::ModeTreeHelperState,
ComponentId,
};
use core::fmt::Debug;

#[derive(Debug, Default)]
pub enum AssemblyHelperResult {
pub enum DevManagerHelperResult {
#[default]
Idle,
TargetKeepingViolation(ComponentId),
Busy,
ModeCommandingDone,
}

#[derive(Debug)]
pub enum DevManagerHelperError {
ChildNotInStore,
}

pub trait DevManagerUserHook: Debug {
fn send_mode_cmds_to_children(
&self,
parent_mode: ModeAndSubmode,
forced: bool,
children_mode_store: &mut ModeStoreVec,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericTargetedMessagingError>;
}

#[derive(Debug, Default)]
pub struct TransparentDevManagerHook {}

impl DevManagerUserHook for TransparentDevManagerHook {
fn send_mode_cmds_to_children(
&self,
commanded_parent_mode: ModeAndSubmode,
forced: bool,
children_mode_store: &mut ModeStoreVec,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericTargetedMessagingError> {
for child in children_mode_store {
mode_req_sender.send_mode_request(
0,
child.id(),
ModeRequest::SetMode {
mode_and_submode: commanded_parent_mode,
forced,
},
)?;
child.awaiting_reply = true;
}
Ok(())
}
}

/// A generic helper for manager components which manage child components in a mode tree.
///
/// Mode commands are usually forwarded to all children components transparently.
/// For example, this could be used in an Assembly component which manages multiple redundant
/// child components. It can also be used inside a manager component which only manages one device.
#[derive(Debug, Default)]
pub struct AssemblyCommandingHelper {
#[derive(Debug)]
pub struct DevManagerCommandingHelper<UserHook: DevManagerUserHook> {
/// The IDs, modes and reply awaition status of all children are tracked in this data
/// structure.
pub children_mode_store: ModeStoreVec,
pub user_hook: UserHook,
/// Target mode used for mode commanding.
target_mode: Option<ModeAndSubmode>,
/// Request ID of active mode commanding request.
active_request_id: Option<RequestId>,
state: ModeTreeHelperState,
}

pub type DevManagerCommandingHelper = AssemblyCommandingHelper;
impl<UserHook: DevManagerUserHook> DevManagerCommandingHelper<UserHook> {
pub fn new(user_hook: UserHook) -> Self {
Self {
children_mode_store: Default::default(),
target_mode: None,
user_hook,
active_request_id: None,
state: Default::default(),
}
}

impl AssemblyCommandingHelper {
pub fn send_mode_cmd_to_all_children_with_reply_awaition(
pub fn send_mode_cmd_to_one_child(
&mut self,
request_id: RequestId,
target_id: ComponentId,
mode_and_submode: ModeAndSubmode,
forced: bool,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericTargetedMessagingError> {
self.target_mode = Some(mode_and_submode);
for child in self.children_mode_store.0.iter_mut() {
mode_req_sender.send_mode_request(
request_id,
child.id(),
ModeRequest::SetMode {
mode_and_submode,
forced,
},
)?;
child.awaiting_reply = true;
}
mode_req_sender.send_mode_request(
request_id,
target_id,
ModeRequest::SetMode {
mode_and_submode,
forced,
},
)?;
self.active_request_id = Some(request_id);
self.state = ModeTreeHelperState::ModeCommanding;
Ok(())
}

pub fn send_mode_cmd_to_all_children(
&mut self,
request_id: RequestId,
mode_and_submode: ModeAndSubmode,
forced: bool,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericTargetedMessagingError> {
self.target_mode = Some(mode_and_submode);
self.user_hook.send_mode_cmds_to_children(
mode_and_submode,
forced,
&mut self.children_mode_store,
mode_req_sender,
)?;
self.active_request_id = Some(request_id);
self.state = ModeTreeHelperState::ModeCommanding;
Ok(())
Expand Down Expand Up @@ -91,27 +159,38 @@ impl AssemblyCommandingHelper {
self.children_mode_store.add_component(target_id, mode);
}

pub fn count_number_of_children_with_target_mode(&self) -> Option<usize> {
self.target_mode?;
let target_mode = self.target_mode.unwrap();
/// Helper method which counts the number of children which have a certain mode.
pub fn count_number_of_children_with_mode(&self, mode_and_submode: ModeAndSubmode) -> usize {
let mut children_in_target_mode = 0;
for child in self.children_mode_store.0.iter() {
if child.mode_and_submode() == target_mode {
for child in &self.children_mode_store {
if child.mode_and_submode() == mode_and_submode {
children_in_target_mode += 1;
}
}
Some(children_in_target_mode)
children_in_target_mode
}

/// Helper method which counts the number of children which have the mode of the assembly
/// itself.
///
/// This is useful for device managers where the child or the children devices should have the
/// same mode as the device manager.
pub fn count_number_of_children_with_target_mode(&self) -> Option<usize> {
Some(self.count_number_of_children_with_mode(self.target_mode?))
}

pub fn handle_mode_reply(
&mut self,
mode_reply: &GenericMessage<ModeReply>,
) -> AssemblyHelperResult {
) -> Result<DevManagerHelperResult, DevManagerHelperError> {
if self.target_mode().is_none() || self.active_request_id().is_none() {
return Ok(DevManagerHelperResult::Idle);
}
if !self
.children_mode_store
.has_component(mode_reply.sender_id())
{
return AssemblyHelperResult::Idle;
return Err(DevManagerHelperError::ChildNotInStore);
}
let mut generic_mode_reply_handler = |mode_and_submode: Option<ModeAndSubmode>| {
// Tying the reply awaition to the request ID ensures that something like replies
Expand All @@ -129,13 +208,6 @@ impl AssemblyCommandingHelper {
mode_and_submode,
handle_awaition,
);
if self.state == ModeTreeHelperState::TargetKeeping
&& mode_and_submode.is_some()
&& self.target_mode.is_some()
&& mode_and_submode.unwrap() != self.target_mode.unwrap()
{
return AssemblyHelperResult::TargetKeepingViolation(mode_reply.sender_id());
}
// It is okay to unwrap: If awaition should be handled, the returned value should
// always be some valid value.
if self.state == ModeTreeHelperState::ModeCommanding
Expand All @@ -144,9 +216,9 @@ impl AssemblyCommandingHelper {
{
self.state = ModeTreeHelperState::TargetKeeping;
self.active_request_id = None;
return AssemblyHelperResult::ModeCommandingDone;
return Ok(DevManagerHelperResult::ModeCommandingDone);
}
AssemblyHelperResult::Idle
Ok(DevManagerHelperResult::Busy)
};
match mode_reply.message {
ModeReply::ModeInfo(mode_and_submode) | ModeReply::ModeReply(mode_and_submode) => {
Expand Down Expand Up @@ -174,15 +246,15 @@ mod tests {

#[test]
fn test_basic() {
let assy_helper = AssemblyCommandingHelper::default();
let assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
assert_eq!(assy_helper.state(), ModeTreeHelperState::Idle);
assert!(assy_helper.active_request_id().is_none());
assert!(assy_helper.target_mode().is_none());
}

#[test]
fn test_mode_announce() {
let mut assy_helper = AssemblyCommandingHelper::default();
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
assy_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
assy_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
Expand All @@ -199,10 +271,10 @@ mod tests {
assert_eq!(req.request_id, 1);
assert_eq!(req.request, ModeRequest::AnnounceMode);
}

#[test]
fn test_mode_announce_recursive() {
let mut assy_helper = AssemblyCommandingHelper::default();
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
assy_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
assy_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
Expand All @@ -221,10 +293,9 @@ mod tests {
}

#[test]
fn test_mode_commanding() {
let mut assy_helper = AssemblyCommandingHelper::default();
fn test_mode_commanding_one_child() {
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
assy_helper.send_mode_cmd_to_all_children_with_reply_awaition(1, , forced, mode_req_sender)

//assy_helper.send_mode_cmd_to_all_children_with_reply_awaition(1, , forced, mode_req_sender)
}
}
39 changes: 39 additions & 0 deletions satrs/src/health.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use crate::ComponentId;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum HealthState {
Healthy = 1,
Faulty = 2,
PermanentFaulty = 3,
ExternalControl = 4,
NeedsRecovery = 5,
}

pub trait HealthTableProvider {
fn health(&self, id: ComponentId) -> Option<HealthState>;
fn set_health(&mut self, id: ComponentId, health: HealthState);
}

#[cfg(feature = "std")]
#[derive(Debug, Clone)]
pub struct HealthTableMapSync(
std::sync::Arc<std::sync::Mutex<hashbrown::HashMap<ComponentId, HealthState>>>,
);

#[cfg(feature = "std")]
impl HealthTableMapSync {
pub fn new(health_table: hashbrown::HashMap<ComponentId, HealthState>) -> Self {
Self(std::sync::Arc::new(std::sync::Mutex::new(health_table)))
}
}

#[cfg(feature = "std")]
impl HealthTableProvider for HealthTableMapSync {
fn health(&self, id: ComponentId) -> Option<HealthState> {
self.0.lock().unwrap().get(&id).copied()
}

fn set_health(&mut self, id: ComponentId, health: HealthState) {
self.0.lock().unwrap().insert(id, health);
}
}
1 change: 1 addition & 0 deletions satrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub mod events;
#[cfg(feature = "std")]
pub mod executable;
pub mod hal;
pub mod health;
pub mod hk;
pub mod mode;
#[cfg(feature = "std")]
Expand Down
27 changes: 27 additions & 0 deletions satrs/src/mode_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,37 @@ pub mod alloc_mod {
#[derive(Debug, Default)]
pub struct ModeStoreVec(pub alloc::vec::Vec<ModeStoreValue>);

impl<'a> IntoIterator for &'a ModeStoreVec {
type Item = &'a ModeStoreValue;
type IntoIter = std::slice::Iter<'a, ModeStoreValue>;

fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}

impl<'a> IntoIterator for &'a mut ModeStoreVec {
type Item = &'a mut ModeStoreValue;
type IntoIter = std::slice::IterMut<'a, ModeStoreValue>;

fn into_iter(self) -> Self::IntoIter {
self.0.iter_mut()
}
}

/// Mode store which tracks the mode information inside a [hashbrown::HashMap]
#[derive(Debug, Default)]
pub struct ModeStoreMap(pub hashbrown::HashMap<ComponentId, ModeStoreValue>);

impl<'a> IntoIterator for &'a ModeStoreMap {
type Item = (&'a ComponentId, &'a ModeStoreValue);
type IntoIter = hashbrown::hash_map::Iter<'a, ComponentId, ModeStoreValue>;

fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}

impl ModeStoreProvider for ModeStoreVec {
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
self.0.push(ModeStoreValue::new(target_id, mode));
Expand Down
Loading

0 comments on commit f2648fb

Please sign in to comment.