diff --git a/docs.sh b/docs.sh new file mode 100755 index 0000000..37563d2 --- /dev/null +++ b/docs.sh @@ -0,0 +1,3 @@ +#!/bin/sh +export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" +cargo +nightly doc --all-features --open diff --git a/satrs/src/mode_tree.rs b/satrs/src/mode_tree.rs index 152ba0e..57ba712 100644 --- a/satrs/src/mode_tree.rs +++ b/satrs/src/mode_tree.rs @@ -144,7 +144,7 @@ impl TargetTableEntry { /// An entry for the sequence tables. /// -/// The `check_success` field instructs the mode sequence executor to verify that the +/// The [Self::check_success] field specifies that a mode sequence executor should check that the /// target mode was actually reached before executing the next sequence. #[derive(Debug)] pub struct SequenceTableEntry { @@ -232,6 +232,10 @@ pub mod alloc_mod { } } + /// One sequence of a [SequenceTablesMapValue] in a [SequenceModeTables]. + /// + /// It contains all mode requests which need to be executed for a sequence step and it also + /// associates a [Self::name] with the sequence. #[derive(Debug)] pub struct SequenceTableMapTable { /// Name for a given mode sequence. @@ -253,6 +257,11 @@ pub mod alloc_mod { } } + /// A sequence table entry. + /// + /// This is simply a list of [SequenceTableMapTable]s which also associates a [Self::name] + /// with the sequence. The order of sub-tables in the list also specifies the execution order + /// in the mode sequence. #[derive(Debug)] pub struct SequenceTablesMapValue { /// Name for a given mode sequence. @@ -276,17 +285,26 @@ pub mod alloc_mod { #[derive(Debug, Default)] pub struct TargetModeTables(pub HashMap); + + /// This is the core data structure used to store mode sequence tables. + /// + /// A mode sequence table specifies which commands have to be sent in which order + /// to reach a certain [Mode]. Therefore, it simply maps a [Mode] to a [SequenceTablesMapValue]. #[derive(Debug, Default)] pub struct SequenceModeTables(pub HashMap); - #[derive(Debug)] - pub struct ModeStoreVecValue { + /// Mode store value type. + #[derive(Debug, Copy, Clone)] + pub struct ModeStoreValue { + /// ID of the mode component. id: ComponentId, + /// Current mode and submode of the component. pub mode_and_submode: ModeAndSubmode, + /// State information to track whether a reply should be awaited for the mode component. pub awaiting_reply: bool, } - impl ModeStoreVecValue { + impl ModeStoreValue { pub fn new(id: ComponentId, mode_and_submode: ModeAndSubmode) -> Self { Self { id, @@ -304,10 +322,13 @@ pub mod alloc_mod { } } + /// Mode store which tracks the [mode information][ModeStoreValue] inside a [Vec] #[derive(Debug, Default)] - pub struct ModeStoreVec(pub alloc::vec::Vec); + pub struct ModeStoreVec(pub alloc::vec::Vec); + + /// Mode store which tracks the mode information inside a [hashbrown::HashMap] #[derive(Debug, Default)] - pub struct ModeStoreMap(pub hashbrown::HashMap); + pub struct ModeStoreMap(pub hashbrown::HashMap); impl ModeStoreVec { /// Generic handler for mode replies received from child components. @@ -338,7 +359,7 @@ pub mod alloc_mod { } impl ModeStoreProvider for ModeStoreVec { fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) { - self.0.push(ModeStoreVecValue::new(target_id, mode)); + self.0.push(ModeStoreValue::new(target_id, mode)); } fn has_component(&self, target_id: ComponentId) -> bool { @@ -369,7 +390,8 @@ pub mod alloc_mod { impl ModeStoreProvider for ModeStoreMap { fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) { - self.0.insert(target_id, mode); + self.0 + .insert(target_id, ModeStoreValue::new(target_id, mode)); } fn has_component(&self, target_id: ComponentId) -> bool { @@ -377,14 +399,15 @@ pub mod alloc_mod { } fn get_mode(&self, target_id: ComponentId) -> Option { - self.0.get(&target_id).copied() + self.0.get(&target_id).map(|v| v.mode_and_submode()) } + fn set_mode_for_contained_component( &mut self, target_id: ComponentId, mode_to_set: ModeAndSubmode, ) { - self.0.insert(target_id, mode_to_set); + self.0.get_mut(&target_id).unwrap().mode_and_submode = mode_to_set; } } } diff --git a/satrs/src/scheduling.rs b/satrs/src/scheduling.rs index 5df7033..ff2c0a1 100644 --- a/satrs/src/scheduling.rs +++ b/satrs/src/scheduling.rs @@ -154,7 +154,7 @@ pub mod std_mod { } /// Can be used to set the start of the slot to the current time. This is useful if a custom - /// runner implementation is used instead of the [Self::start] method. + /// runner implementation is used instead of the [Self::run_one_task_cycle] method. pub fn init_start_of_slot(&mut self) { self.start_of_slot = Instant::now(); } diff --git a/satrs/src/subsystem.rs b/satrs/src/subsystem.rs index 9a1ec1a..6ba4d5c 100644 --- a/satrs/src/subsystem.rs +++ b/satrs/src/subsystem.rs @@ -10,18 +10,15 @@ use crate::{ }; #[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum SequenceExecutionHelperStates { +pub enum SequenceExecutionHelperState { + /// The sequence execution is IDLE, no command is loaded or the sequence exection has + /// finished Idle, + /// The sequence helper is executing a sequence and no replies need to be awaited. + Busy, + /// The sequence helper is still awaiting a reply from a mode children. The reply awaition + /// is a property of a mode commanding sequence AwaitingCheckSuccess, - Done, -} - -pub trait CheckSuccessProvider { - fn mode_request_requires_success_check( - &mut self, - target_id: ComponentId, - target_mode: ModeAndSubmode, - ); } #[derive(Debug)] @@ -32,8 +29,11 @@ pub enum TargetKeepingResult { #[derive(Debug)] pub enum ModeCommandingResult { + /// The commanding of all children is finished CommandingDone, + /// One step of a commanding chain is finished CommandingStepDone, + /// Reply awaition is required for some children AwaitingSuccessCheck, } @@ -41,11 +41,17 @@ pub enum ModeCommandingResult { #[error("Mode {0} does not exist")] pub struct ModeDoesNotExistError(Mode); +/// This sequence execution helper includes some boilerplate logic to +/// execute [SequenceModeTables]. +/// +/// It takes care of commanding the [ModeRequest]s specified in those tables and also includes the +/// states required to track the current progress of a sequence execution and take care of +/// reply and success awaition. #[derive(Debug)] pub struct SequenceExecutionHelper { target_mode: Option, - state: SequenceExecutionHelperStates, - request_id: RequestId, + state: SequenceExecutionHelperState, + request_id: Option, current_sequence_index: Option, } @@ -53,14 +59,19 @@ impl Default for SequenceExecutionHelper { fn default() -> Self { Self { target_mode: None, - state: SequenceExecutionHelperStates::Idle, - request_id: 0, + state: SequenceExecutionHelperState::Idle, + request_id: None, current_sequence_index: None, } } } impl SequenceExecutionHelper { + pub fn new() -> Self { + Default::default() + } + + /// Load a new mode sequence to be executed pub fn load( &mut self, mode: Mode, @@ -71,43 +82,52 @@ impl SequenceExecutionHelper { return Err(ModeDoesNotExistError(mode)); } self.target_mode = Some(mode); - self.request_id = request_id; + self.request_id = Some(request_id); + self.state = SequenceExecutionHelperState::Busy; self.current_sequence_index = None; Ok(()) } - pub fn target_mode(&self) -> Option { - self.target_mode - } - - pub fn confirm_sequence_done(&mut self) { - if let SequenceExecutionHelperStates::AwaitingCheckSuccess = self.state { - self.state = SequenceExecutionHelperStates::Idle; - } - } - - pub fn state(&self) -> SequenceExecutionHelperStates { - self.state - } - - pub fn awaiting_check_success(&self) -> bool { - matches!( - self.state, - SequenceExecutionHelperStates::AwaitingCheckSuccess - ) - } - - pub fn current_sequence_index(&self) -> Option { - self.current_sequence_index - } - + /// Run the sequence execution helper. + /// + /// This function will execute the sequence in the given [SequenceModeTables] based on the + /// mode loaded in [Self::load]. It calls [Self::execute_sequence_and_map_to_result] and + /// automatically takes care of state management, including increments of the sequence table + /// index. + /// + /// The returnvalues of the helper have the following meaning. + /// + /// * [ModeCommandingResult::AwaitingSuccessCheck] - The sequence is still awaiting a success. + /// The user should check whether all children have reached the commanded target mode, for + /// example by checking [mode replies][ModeReply] received by the children components, and + /// then calling [Self::confirm_sequence_done] to advance to the sequence or complete the + /// sequence. + /// * [ModeCommandingResult::CommandingDone] - The sequence is done. The user can load a new + /// sequence now without overwriting the last one. The sequence executor is in + /// [SequenceExecutionHelperState::Idle] again. + /// * [ModeCommandingResult::CommandingStepDone] - The sequence has advanced one step. The user + /// can now call [Self::run] again to immediately execute the next step in the sequence. + /// + /// Generally, periodic execution of the [Self::run] method should be performed while + /// [Self::state] is not [SequenceExecutionHelperState::Idle]. + /// + /// # Arguments + /// + /// * `table` - This table contains the sequence tables to reach the mode previously loaded + /// with [Self::load] + /// * `sender` - The sender to send mode requests to the components + /// * `children_mode_store` - The mode store vector to keep track of the mode states of + /// children components pub fn run( &mut self, table: &SequenceModeTables, sender: &impl ModeRequestSender, - mode_store_vec: &mut ModeStoreVec, + children_mode_store: &mut ModeStoreVec, ) -> Result { - if self.state == SequenceExecutionHelperStates::AwaitingCheckSuccess { + if self.state == SequenceExecutionHelperState::Idle { + return Ok(ModeCommandingResult::CommandingDone); + } + if self.state == SequenceExecutionHelperState::AwaitingCheckSuccess { return Ok(ModeCommandingResult::AwaitingSuccessCheck); } if self.target_mode.is_none() { @@ -121,7 +141,7 @@ impl SequenceExecutionHelper { seq_table_value, idx, sender, - mode_store_vec, + children_mode_store, ) } None => { @@ -135,13 +155,42 @@ impl SequenceExecutionHelper { seq_table_value, 0, sender, - mode_store_vec, + children_mode_store, ) } } } } + /// Retrieve the currently loaded target mode + pub fn target_mode(&self) -> Option { + self.target_mode + } + + /// Confirm that a sequence which is awaiting a success check is done + pub fn confirm_sequence_done(&mut self) { + if let SequenceExecutionHelperState::AwaitingCheckSuccess = self.state { + self.state = SequenceExecutionHelperState::Idle; + } + } + + /// Internal state of the execution helper. + pub fn state(&self) -> SequenceExecutionHelperState { + self.state + } + + pub fn awaiting_check_success(&self) -> bool { + self.state == SequenceExecutionHelperState::AwaitingCheckSuccess + } + + pub fn current_sequence_index(&self) -> Option { + self.current_sequence_index + } + + /// Execute a sequence at the given sequence index for a given [SequenceTablesMapValue]. + /// + /// This method calls [Self::execute_sequence] and maps the result to a [ModeCommandingResult]. + /// It is also called by the [Self::run] method of this helper. pub fn execute_sequence_and_map_to_result( &mut self, seq_table_value: &SequenceTablesMapValue, @@ -149,15 +198,19 @@ impl SequenceExecutionHelper { sender: &impl ModeRequestSender, mode_store_vec: &mut ModeStoreVec, ) -> Result { + if self.state() == SequenceExecutionHelperState::Idle || self.request_id.is_none() { + return Ok(ModeCommandingResult::CommandingDone); + } if Self::execute_sequence( - self.request_id, + self.request_id.unwrap(), &seq_table_value.entries[sequence_idx], sender, mode_store_vec, )? { - self.state = SequenceExecutionHelperStates::AwaitingCheckSuccess; + self.state = SequenceExecutionHelperState::AwaitingCheckSuccess; Ok(ModeCommandingResult::AwaitingSuccessCheck) } else if seq_table_value.entries.len() - 1 == sequence_idx { + self.state = SequenceExecutionHelperState::Idle; return Ok(ModeCommandingResult::CommandingDone); } else { self.current_sequence_index = Some(sequence_idx + 1); @@ -165,11 +218,22 @@ impl SequenceExecutionHelper { } } + /// Generic stateless execution helper method. + /// + /// The [RequestId] and the [SequenceTableMapTable] to be executed are passed explicitely + /// here. This method is called by [Self::execute_sequence_and_map_to_result]. + /// + /// This method itereates through the entries of the given sequence table and sends out + /// [ModeRequest]s to set the modes of the children according to the table entries. + /// It also sets the reply awaition field in the children mode store where a success + /// check is required to true. + /// + /// It returns whether any commanding success check is required by any entry in the table. pub fn execute_sequence( request_id: RequestId, map_table: &SequenceTableMapTable, sender: &impl ModeRequestSender, - mode_store_vec: &mut ModeStoreVec, + children_mode_store: &mut ModeStoreVec, ) -> Result { let mut some_succes_check_required = false; for entry in &map_table.entries { @@ -181,12 +245,12 @@ impl SequenceExecutionHelper { forced: false, }, )?; - mode_store_vec.0.iter_mut().for_each(|val| { - if val.id() == entry.common.target_id { - val.awaiting_reply = true; - } - }); if entry.check_success { + children_mode_store.0.iter_mut().for_each(|val| { + if val.id() == entry.common.target_id { + val.awaiting_reply = true; + } + }); some_succes_check_required = true; } } @@ -198,15 +262,19 @@ impl SequenceExecutionHelper { pub enum ModeTreeHelperState { #[default] Idle, - TargetKeeping = 1, - ModeCommanding = 2, + /// The helper is currently trying to keep a target mode. + TargetKeeping, + /// The helper is currently busy to command a mode. + ModeCommanding, } #[derive(Debug, Default)] pub enum SubsystemHelperResult { #[default] Idle, + /// Result of a target keeping operation TargetKeeping(TargetKeepingResult), + /// Result of a mode commanding operation ModeCommanding(ModeCommandingResult), } @@ -230,13 +298,30 @@ pub enum ModeTreeHelperError { CurrentModeNotInTargetTable(Mode), } +/// This is a helper object which can be used by a subsystem component to execute mode sequences +/// and perform target keeping. +/// +/// This helper object tries to compose as much data and state information as possible which is +/// required for this process. pub struct SubsystemCommandingHelper { + /// Current mode of the owner subsystem. pub current_mode: ModeAndSubmode, + /// State of the helper. pub state: ModeTreeHelperState, + /// This data structure is used to track all mode children. pub children_mode_store: ModeStoreVec, + /// This field is set when a mode sequence is executed. It is used to determine whether mode + /// replies are relevant for reply awaition logic. pub active_request_id: Option, + /// The primary data structure to keep the target state information for subsystem + /// [modes][Mode]. it specifies the mode each child should have for a certain subsystem mode + /// and is relevant for target keeping. pub target_tables: TargetModeTables, + /// The primary data structure to keep the sequence commanding information for commanded + /// subsystem [modes][Mode]. It specifies the actual commands and the order they should be + /// sent in to reach a certain [mode][Mode]. pub sequence_tables: SequenceModeTables, + /// The sequence execution helper is used to execute sequences in the [Self::sequence_tables]. pub seq_exec_helper: SequenceExecutionHelper, } @@ -255,6 +340,8 @@ impl Default for SubsystemCommandingHelper { } impl SubsystemCommandingHelper { + /// Create a new substem commanding helper with an intial [ModeTreeHelperState::Idle] state, + /// an empty mode children store and empty target and sequence mode tables. pub fn new( children_mode_store: ModeStoreVec, target_tables: TargetModeTables, @@ -271,10 +358,12 @@ impl SubsystemCommandingHelper { } } + /// Add a mode child to the internal [Self::children_mode_store]. pub fn add_mode_child(&mut self, child: ComponentId, mode: ModeAndSubmode) { self.children_mode_store.add_component(child, mode); } + /// Add a target mode table and an associated sequence mode table. pub fn add_target_and_sequence_table( &mut self, mode: Mode, @@ -285,12 +374,14 @@ impl SubsystemCommandingHelper { self.sequence_tables.0.insert(mode, sequence_table_val); } + /// Starts a command sequence for a given [mode][Mode]. pub fn start_command_sequence( &mut self, mode: Mode, request_id: RequestId, ) -> Result<(), ModeDoesNotExistError> { - self.seq_exec_helper.load(mode, request_id, &self.sequence_tables)?; + self.seq_exec_helper + .load(mode, request_id, &self.sequence_tables)?; self.state = ModeTreeHelperState::ModeCommanding; Ok(()) } @@ -339,7 +430,8 @@ impl SubsystemCommandingHelper { if let ModeCommandingResult::CommandingDone = result { self.state = ModeTreeHelperState::TargetKeeping; self.active_request_id = None; - self.current_mode = ModeAndSubmode::new(self.seq_exec_helper.target_mode().unwrap(), 0); + self.current_mode = + ModeAndSubmode::new(self.seq_exec_helper.target_mode().unwrap(), 0); } Ok(result.into()) } diff --git a/satrs/tests/mode_tree.rs b/satrs/tests/mode_tree.rs index 8dde240..9d83d80 100644 --- a/satrs/tests/mode_tree.rs +++ b/satrs/tests/mode_tree.rs @@ -981,12 +981,15 @@ pub struct AcsController { } impl AcsController { + pub fn id() -> ComponentId { + TestComponentId::AcsController as u64 + } pub fn new(mode_node: ModeRequestHandlerMpscBounded) -> Self { Self { mode_node, mode_and_submode: UNKNOWN_MODE, announce_mode_queue: Default::default(), - mode_req_mock: ModeRequestHandlerMock::new(TestComponentId::AcsController as u64), + mode_req_mock: ModeRequestHandlerMock::new(Self::id()), } } pub fn run(&mut self) {