continue mode tree
This commit is contained in:
parent
3233044266
commit
6ba8a410f4
3
docs.sh
Executable file
3
docs.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --all-features --open
|
@ -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<Mode, TargetTablesMapValue>);
|
||||
|
||||
/// 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<Mode, SequenceTablesMapValue>);
|
||||
|
||||
#[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<ModeStoreVecValue>);
|
||||
pub struct ModeStoreVec(pub alloc::vec::Vec<ModeStoreValue>);
|
||||
|
||||
/// Mode store which tracks the mode information inside a [hashbrown::HashMap]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ModeStoreMap(pub hashbrown::HashMap<ComponentId, ModeAndSubmode>);
|
||||
pub struct ModeStoreMap(pub hashbrown::HashMap<ComponentId, ModeStoreValue>);
|
||||
|
||||
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<ModeAndSubmode> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<Mode>,
|
||||
state: SequenceExecutionHelperStates,
|
||||
request_id: RequestId,
|
||||
state: SequenceExecutionHelperState,
|
||||
request_id: Option<RequestId>,
|
||||
current_sequence_index: Option<usize>,
|
||||
}
|
||||
|
||||
@ -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<Mode> {
|
||||
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<usize> {
|
||||
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<ModeCommandingResult, GenericTargetedMessagingError> {
|
||||
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<Mode> {
|
||||
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<usize> {
|
||||
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<ModeCommandingResult, GenericTargetedMessagingError> {
|
||||
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<bool, GenericTargetedMessagingError> {
|
||||
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<RequestId>,
|
||||
/// 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())
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user