diff --git a/satrs/src/lib.rs b/satrs/src/lib.rs index 8c9fab8..43b1d17 100644 --- a/satrs/src/lib.rs +++ b/satrs/src/lib.rs @@ -36,15 +36,16 @@ pub mod pus; pub mod queue; pub mod request; pub mod res_code; -pub mod time; -pub mod tmtc; #[cfg(feature = "alloc")] pub mod scheduling; +pub mod time; +pub mod tmtc; pub mod action; pub mod hk; pub mod mode; pub mod params; +pub mod subsystem; pub use spacepackets; diff --git a/satrs/src/mode_tree.rs b/satrs/src/mode_tree.rs index 1cddd32..e0d99ed 100644 --- a/satrs/src/mode_tree.rs +++ b/satrs/src/mode_tree.rs @@ -15,23 +15,168 @@ pub enum TableEntryType { Sequence, } -pub struct ModeTableEntry { +/// Common fields required for both target and sequence table entries. +/// +/// The most important parameters here are the target ID which this entry belongs to, and the mode +/// and submode the entry either will be commanded to for sequence table entries or which will be +/// monitored for target table entries. +#[derive(Debug, Copy, Clone)] +pub struct ModeTableEntryCommon { /// Name of respective table entry. pub name: &'static str, - /// Target channel ID. - pub channel_id: ComponentId, + /// Target component ID. + pub target_id: ComponentId, + /// Has a different meaning depending on whether this is a sequence table or a target table. + /// + /// - For sequence tables, this denotes the mode which will be commanded + /// - For target tables, this is the mode which the target children should have and which + /// might be monitored depending on configuration. pub mode_submode: ModeAndSubmode, + /// This mask allows to specify multiple allowed submodes for a given mode. pub allowed_submode_mask: Option, +} + +impl ModeTableEntryCommon { + pub fn set_allowed_submode_mask(&mut self, mask: Submode) { + self.allowed_submode_mask = Some(mask); + } + + pub fn allowed_submode_mask(&self) -> Option { + self.allowed_submode_mask + } +} + +/// An entry for the target tables. +#[derive(Debug)] +pub struct TargetTableEntry { + pub common: ModeTableEntryCommon, + pub monitor_state: bool, +} + +impl TargetTableEntry { + pub fn new( + name: &'static str, + target_id: ComponentId, + mode_submode: ModeAndSubmode, + monitor_state: bool, + ) -> Self { + Self { + common: ModeTableEntryCommon { + name, + target_id, + mode_submode, + allowed_submode_mask: None, + }, + monitor_state, + } + } + + delegate::delegate! { + to self.common { + pub fn set_allowed_submode_mask(&mut self, mask: Submode); + pub fn allowed_submode_mask(&self) -> Option; + } + } +} + +#[derive(Debug)] +pub struct TargetTableMapValue { + /// Name for a given mode table entry. + pub name: &'static str, + /// These are the rows of the a target table. + pub entries: Vec, +} + +impl TargetTableMapValue { + pub fn new(name: &'static str) -> Self { + Self { + name, + entries: Default::default(), + } + } + + pub fn add_entry(&mut self, entry: TargetTableEntry) { + self.entries.push(entry); + } +} + +/// An entry for the sequence tables. +/// +/// The `check_success` field instructs the mode sequence executor to verify that the +/// target mode was actually reached before executing the next sequence. +pub struct SequenceTableEntry { + pub common: ModeTableEntryCommon, pub check_success: bool, } -pub struct ModeTableMapValue { - /// Name for a given mode table entry. - pub name: &'static str, - pub entries: Vec, +impl SequenceTableEntry { + pub fn new( + name: &'static str, + target_id: ComponentId, + mode_submode: ModeAndSubmode, + check_success: bool, + ) -> Self { + Self { + common: ModeTableEntryCommon { + name, + target_id, + mode_submode, + allowed_submode_mask: None, + }, + check_success, + } + } + + delegate::delegate! { + to self.common { + pub fn set_allowed_submode_mask(&mut self, mask: Submode); + pub fn allowed_submode_mask(&self) -> Option; + } + } } -pub type ModeTable = HashMap; +pub struct SequenceTableMapTable { + /// Name for a given mode sequence. + pub name: &'static str, + /// These are the rows of the a sequence table. + pub entries: Vec, +} + +impl SequenceTableMapTable { + pub fn new(name: &'static str) -> Self { + Self { + name, + entries: Default::default(), + } + } + + pub fn add_entry(&mut self, entry: SequenceTableEntry) { + self.entries.push(entry); + } +} + +pub struct SequenceTableMapValue { + /// Name for a given mode sequence. + pub name: &'static str, + /// Each sequence can consists of multiple sequences that are executed consecutively. + pub entries: Vec, +} + +impl SequenceTableMapValue { + pub fn new(name: &'static str) -> Self { + Self { + name, + entries: Default::default(), + } + } + + pub fn add_sequence_table(&mut self, entry: SequenceTableMapTable) { + self.entries.push(entry); + } +} + +pub struct TargetModeTable(pub HashMap); +pub struct SequenceModeTable(pub HashMap); #[cfg(test)] mod tests {} diff --git a/satrs/src/subsystem.rs b/satrs/src/subsystem.rs new file mode 100644 index 0000000..033821f --- /dev/null +++ b/satrs/src/subsystem.rs @@ -0,0 +1,134 @@ +use crate::{ + mode::{Mode, ModeAndSubmode, ModeRequest, ModeRequestSender}, + mode_tree::{SequenceModeTable, SequenceTableMapTable, SequenceTableMapValue}, + queue::GenericTargetedMessagingError, + request::RequestId, + ComponentId, +}; + +#[derive(Debug, PartialEq, Eq)] +pub enum SequenceExecutionHelperStates { + Idle, + AwaitingCheckSuccess, + Done, +} + +#[derive(Debug)] +pub struct SequenceExecutionHelper { + target_mode: Mode, + state: SequenceExecutionHelperStates, + request_id: RequestId, + current_sequence_index: Option, +} + +pub trait CheckSuccessProvider { + fn mode_request_requires_success_check( + &mut self, + target_id: ComponentId, + target_mode: ModeAndSubmode, + ); +} + +#[derive(Debug)] +pub enum SequenceHandlerResult { + SequenceDone, + SequenceStepDone, + AwaitingSuccessCheck, +} + +impl SequenceExecutionHelper { + pub fn new( + mode: Mode, + request_id: RequestId, + sequence_table: &SequenceModeTable, + ) -> Option { + if !sequence_table.0.contains_key(&mode) { + return None; + } + Some(Self { + target_mode: mode, + state: SequenceExecutionHelperStates::Idle, + request_id, + current_sequence_index: None, + }) + } + + pub fn confirm_sequence_done(&mut self) { + if let SequenceExecutionHelperStates::AwaitingCheckSuccess = self.state { + self.state = SequenceExecutionHelperStates::Idle; + } + } + + pub fn run( + &mut self, + table: &SequenceModeTable, + sender: &impl ModeRequestSender, + ) -> Result { + if self.state == SequenceExecutionHelperStates::AwaitingCheckSuccess { + return Ok(SequenceHandlerResult::AwaitingSuccessCheck); + } + match self.current_sequence_index { + Some(idx) => { + // Execute the sequence. + let seq_table_value = table.0.get(&self.target_mode).unwrap(); + self.execute_sequence_and_map_to_result(seq_table_value, idx, sender) + } + None => { + // Find the first sequence + let seq_table_value = table.0.get(&self.target_mode).unwrap(); + if seq_table_value.entries.is_empty() { + Ok(SequenceHandlerResult::SequenceDone) + } else { + self.current_sequence_index = Some(0); + self.execute_sequence_and_map_to_result(seq_table_value, 0, sender) + } + } + } + } + + pub fn execute_sequence_and_map_to_result( + &mut self, + seq_table_value: &SequenceTableMapValue, + sequence_idx: usize, + sender: &impl ModeRequestSender, + ) -> Result { + if Self::execute_sequence( + self.request_id, + &seq_table_value.entries[sequence_idx], + sender, + )? { + self.state = SequenceExecutionHelperStates::AwaitingCheckSuccess; + Ok(SequenceHandlerResult::AwaitingSuccessCheck) + } else if seq_table_value.entries.len() - 1 == sequence_idx { + return Ok(SequenceHandlerResult::SequenceDone); + } else { + self.current_sequence_index = Some(sequence_idx + 1); + return Ok(SequenceHandlerResult::SequenceStepDone); + } + } + + pub fn execute_sequence( + request_id: RequestId, + map_table: &SequenceTableMapTable, + //sequence_idx: usize, + sender: &impl ModeRequestSender, + ) -> Result { + let mut some_succes_check_required = false; + for entry in &map_table.entries { + sender.send_mode_request( + request_id, + entry.common.target_id, + ModeRequest::SetMode(entry.common.mode_submode), + )?; + if entry.check_success { + some_succes_check_required = true; + } + } + Ok(some_succes_check_required) + } +} + +#[cfg(test)] +mod tests { + pub struct SatSystem {} +} diff --git a/satrs/tests/mode_tree.rs b/satrs/tests/mode_tree.rs index 17f9836..d8773c4 100644 --- a/satrs/tests/mode_tree.rs +++ b/satrs/tests/mode_tree.rs @@ -19,7 +19,8 @@ pub enum TestComponentId { Device1 = 1, Device2 = 2, Assembly = 3, - PusModeService = 4, + Subsystem = 4, + PusModeService = 5, } struct PusModeService { @@ -41,78 +42,86 @@ impl PusModeService { } } -struct TestDevice { +struct TestSubsystem { pub name: String, - pub mode_node: ModeRequestHandlerMpscBounded, + pub mode_node: ModeRequestorAndHandlerMpscBounded, + pub mode_requestor_info: Option, pub mode_and_submode: ModeAndSubmode, + pub target_mode_and_submode: Option, } -impl TestDevice { - pub fn run(&mut self) { - self.check_mode_requests().expect("mode messaging error"); - } - - pub fn check_mode_requests(&mut self) -> Result<(), ModeError> { - if let Some(request) = self.mode_node.try_recv_mode_request()? { - self.handle_mode_request(request)? - } - Ok(()) - } -} - -impl ModeProvider for TestDevice { +impl ModeProvider for TestSubsystem { fn mode_and_submode(&self) -> ModeAndSubmode { self.mode_and_submode } } -impl ModeRequestHandler for TestDevice { +impl ModeRequestHandler for TestSubsystem { type Error = ModeError; fn start_transition( &mut self, requestor: MessageMetadata, mode_and_submode: ModeAndSubmode, - ) -> Result<(), ModeError> { - self.mode_and_submode = mode_and_submode; - self.handle_mode_reached(Some(requestor))?; + ) -> Result<(), Self::Error> { + self.mode_requestor_info = Some(requestor); + self.target_mode_and_submode = Some(mode_and_submode); + // Execute mode map by executing the transition table(s). Ok(()) } - fn announce_mode(&self, _requestor_info: Option, _recursive: bool) { + fn announce_mode(&self, requestor_info: Option, recursive: bool) { println!( - "{}: announcing mode: {:?}", - self.name, self.mode_and_submode + "TestAssembly: Announcing mode (recursively: {}): {:?}", + recursive, self.mode_and_submode ); + // self.mode_requestor_info = Some((request_id, sender_id)); + let mut mode_request = ModeRequest::AnnounceMode; + if recursive { + mode_request = ModeRequest::AnnounceModeRecursive; + } + let request_id = requestor_info.map_or(0, |info| info.request_id()); + self.mode_node + .request_sender_map + .0 + .iter() + .for_each(|(_, sender)| { + sender + .send(GenericMessage::new( + MessageMetadata::new(request_id, self.mode_node.local_channel_id_generic()), + mode_request, + )) + .expect("sending mode request failed"); + }); } - fn handle_mode_reached(&mut self, requestor: Option) -> Result<(), ModeError> { - if let Some(requestor) = requestor { + fn handle_mode_reached( + &mut self, + requestor_info: Option, + ) -> Result<(), Self::Error> { + if let Some(requestor) = requestor_info { self.send_mode_reply(requestor, ModeReply::ModeReply(self.mode_and_submode))?; } Ok(()) } - fn send_mode_reply( - &self, - requestor_info: MessageMetadata, - reply: ModeReply, - ) -> Result<(), ModeError> { - self.mode_node.send_mode_reply(requestor_info, reply)?; - Ok(()) - } fn handle_mode_info( &mut self, requestor_info: MessageMetadata, info: ModeAndSubmode, - ) -> Result<(), ModeError> { - // A device is a leaf in the tree.. so this really should not happen - println!( - "{}: unexpected mode info from {:?} with mode: {:?}", - self.name, - requestor_info.sender_id(), - info - ); + ) -> Result<(), Self::Error> { + // TODO: Need to check whether mode table execution is finished. + // This works by checking the children modes received through replies against the + // mode table after all transition tables were executed. + Ok(()) + } + + fn send_mode_reply( + &self, + requestor_info: MessageMetadata, + reply: ModeReply, + ) -> Result<(), Self::Error> { + self.mode_node.send_mode_reply(requestor_info, reply)?; Ok(()) } } @@ -253,11 +262,88 @@ impl ModeRequestHandler for TestAssembly { } } +struct TestDevice { + pub name: String, + pub mode_node: ModeRequestHandlerMpscBounded, + pub mode_and_submode: ModeAndSubmode, +} + +impl TestDevice { + pub fn run(&mut self) { + self.check_mode_requests().expect("mode messaging error"); + } + + pub fn check_mode_requests(&mut self) -> Result<(), ModeError> { + if let Some(request) = self.mode_node.try_recv_mode_request()? { + self.handle_mode_request(request)? + } + Ok(()) + } +} + +impl ModeProvider for TestDevice { + fn mode_and_submode(&self) -> ModeAndSubmode { + self.mode_and_submode + } +} + +impl ModeRequestHandler for TestDevice { + type Error = ModeError; + + fn start_transition( + &mut self, + requestor: MessageMetadata, + mode_and_submode: ModeAndSubmode, + ) -> Result<(), ModeError> { + self.mode_and_submode = mode_and_submode; + self.handle_mode_reached(Some(requestor))?; + Ok(()) + } + + fn announce_mode(&self, _requestor_info: Option, _recursive: bool) { + println!( + "{}: announcing mode: {:?}", + self.name, self.mode_and_submode + ); + } + + fn handle_mode_reached(&mut self, requestor: Option) -> Result<(), ModeError> { + if let Some(requestor) = requestor { + self.send_mode_reply(requestor, ModeReply::ModeReply(self.mode_and_submode))?; + } + Ok(()) + } + fn send_mode_reply( + &self, + requestor_info: MessageMetadata, + reply: ModeReply, + ) -> Result<(), ModeError> { + self.mode_node.send_mode_reply(requestor_info, reply)?; + Ok(()) + } + + fn handle_mode_info( + &mut self, + requestor_info: MessageMetadata, + info: ModeAndSubmode, + ) -> Result<(), ModeError> { + // A device is a leaf in the tree.. so this really should not happen + println!( + "{}: unexpected mode info from {:?} with mode: {:?}", + self.name, + requestor_info.sender_id(), + info + ); + Ok(()) + } +} + fn main() { // All request channel handles. let (request_sender_to_dev1, request_receiver_dev1) = mpsc::sync_channel(10); let (request_sender_to_dev2, request_receiver_dev2) = mpsc::sync_channel(10); let (request_sender_to_assy, request_receiver_assy) = mpsc::sync_channel(10); + let (request_sender_to_subsystem, request_receiver_subsystem) = mpsc::sync_channel(10); // All reply channel handles. let (reply_sender_to_assy, reply_receiver_assy) = mpsc::sync_channel(10); @@ -298,6 +384,11 @@ fn main() { TestComponentId::Device2 as ComponentId, request_sender_to_dev2.clone(), ); + mode_node_pus.add_message_target( + TestComponentId::Subsystem as ComponentId, + request_sender_to_subsystem.clone(), + ); + mode_node_assy.add_request_target( TestComponentId::Device1 as ComponentId, request_sender_to_dev1,