diff --git a/satrs/src/lib.rs b/satrs/src/lib.rs index c31df8d..ae9122c 100644 --- a/satrs/src/lib.rs +++ b/satrs/src/lib.rs @@ -34,6 +34,7 @@ pub mod events; #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] pub mod executable; pub mod hal; +pub mod mode_tree; pub mod objects; pub mod pool; pub mod power; @@ -51,8 +52,7 @@ pub mod params; pub use spacepackets; -/// Generic channel ID type. -pub type ChannelId = u32; +pub use queue::ChannelId; /// Generic target ID type. pub type TargetId = u64; diff --git a/satrs/src/mode.rs b/satrs/src/mode.rs index c5968b4..1346226 100644 --- a/satrs/src/mode.rs +++ b/satrs/src/mode.rs @@ -5,19 +5,22 @@ use spacepackets::ByteConversionError; use crate::TargetId; +pub type Mode = u32; +pub type Submode = u16; + #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ModeAndSubmode { - mode: u32, - submode: u16, + mode: Mode, + submode: Submode, } impl ModeAndSubmode { - pub const fn new_mode_only(mode: u32) -> Self { + pub const fn new_mode_only(mode: Mode) -> Self { Self { mode, submode: 0 } } - pub const fn new(mode: u32, submode: u16) -> Self { + pub const fn new(mode: Mode, submode: Submode) -> Self { Self { mode, submode } } @@ -33,16 +36,20 @@ impl ModeAndSubmode { }); } Ok(Self { - mode: u32::from_be_bytes(buf[0..4].try_into().unwrap()), - submode: u16::from_be_bytes(buf[4..6].try_into().unwrap()), + mode: Mode::from_be_bytes(buf[0..size_of::()].try_into().unwrap()), + submode: Submode::from_be_bytes( + buf[size_of::()..size_of::() + size_of::()] + .try_into() + .unwrap(), + ), }) } - pub fn mode(&self) -> u32 { + pub fn mode(&self) -> Mode { self.mode } - pub fn submode(&self) -> u16 { + pub fn submode(&self) -> Submode { self.submode } } @@ -87,6 +94,20 @@ pub enum ModeRequest { AnnounceModeRecursive, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ModeReply { + /// Unrequest mode information. Can be used to notify other components of changed modes. + ModeInfo(ModeAndSubmode), + /// Reply to a mode request to confirm the commanded mode was reached. + ModeReply(ModeAndSubmode), + CantReachMode(ModeAndSubmode), + WrongMode { + expected: ModeAndSubmode, + reached: ModeAndSubmode, + }, +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TargetedModeRequest { diff --git a/satrs/src/mode_tree.rs b/satrs/src/mode_tree.rs new file mode 100644 index 0000000..4eaecf6 --- /dev/null +++ b/satrs/src/mode_tree.rs @@ -0,0 +1,261 @@ +use std::sync::mpsc; + +use alloc::vec::Vec; +use hashbrown::HashMap; + +use crate::{ + mode::{Mode, ModeAndSubmode, ModeReply, ModeRequest, Submode}, + queue::GenericSendError, + ChannelId, +}; + +pub struct ModeRequestWithSenderId { + pub sender_id: ChannelId, + pub request: ModeRequest, +} + +impl ModeRequestWithSenderId { + pub fn new(sender_id: ChannelId, request: ModeRequest) -> Self { + Self { sender_id, request } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TableEntryType { + /// Target table containing information of the expected children modes for given mode. + Target, + /// Sequence table which contains information about how to reach a target table, including + /// the order of the sequences. + Sequence, +} + +pub struct ModeTableEntry { + /// Name of respective table entry. + pub name: &'static str, + /// Target channel ID. + pub channel_id: ChannelId, + pub mode_submode: ModeAndSubmode, + pub allowed_submode_mask: Option, + pub check_success: bool, +} + +pub struct ModeTableMapValue { + /// Name for a given mode table entry. + pub name: &'static str, + pub entries: Vec, +} + +pub type ModeTable = HashMap; + +#[derive(Debug, Clone)] +pub enum ModeMessagingError { + TargetDoesNotExist(ChannelId), + Send(GenericSendError), +} +impl From for ModeMessagingError { + fn from(value: GenericSendError) -> Self { + Self::Send(value) + } +} + +pub trait ModeReplyHandler { + fn local_channel_id(&self) -> ChannelId; + + fn send_mode_reply( + &mut self, + target_id: ChannelId, + reply: ModeReply, + ) -> Result<(), ModeMessagingError>; +} + +pub struct StandardModeReplyMap { + pub local_id: ChannelId, + pub reply_sender_map: HashMap>, +} + +impl ModeReplyHandler for StandardModeReplyMap { + fn send_mode_reply( + &mut self, + target_channel_id: ChannelId, + reply: ModeReply, + ) -> Result<(), ModeMessagingError> { + if self.reply_sender_map.contains_key(&target_channel_id) { + self.reply_sender_map + .get(&target_channel_id) + .unwrap() + .send(reply) + .map_err(|_| GenericSendError::RxDisconnected)?; + return Ok(()); + } + Err(ModeMessagingError::TargetDoesNotExist(target_channel_id)) + } + + fn local_channel_id(&self) -> ChannelId { + self.local_id + } +} + +pub trait ModeRequestSender { + fn local_channel_id(&self) -> Option; + fn send_mode_request( + &mut self, + target_id: ChannelId, + request: ModeRequest, + ) -> Result<(), ModeMessagingError>; +} + +pub struct StandardModeRequestMap { + pub local_channel_id: ChannelId, + pub request_sender_map: HashMap>, +} + +impl ModeRequestSender for StandardModeRequestMap { + fn send_mode_request( + &mut self, + target_id: ChannelId, + request: ModeRequest, + ) -> Result<(), ModeMessagingError> { + if self.request_sender_map.contains_key(&target_id) { + self.request_sender_map + .get(&target_id) + .unwrap() + .send(ModeRequestWithSenderId { + sender_id: target_id, + request, + }) + .map_err(|_| GenericSendError::RxDisconnected)?; + return Ok(()); + } + Err(ModeMessagingError::TargetDoesNotExist(target_id)) + } + + fn local_channel_id(&self) -> Option { + Some(self.local_channel_id) + } +} + +pub trait ModeProvider { + fn mode_and_submode(&self) -> ModeAndSubmode; +} + +#[derive(Debug, Clone)] +pub enum ModeError { + Messaging(ModeMessagingError), +} + +pub trait ModeRequestHandler: ModeProvider { + fn start_transition(&mut self, mode_and_submode: ModeAndSubmode) -> Result<(), ModeError>; + + fn announce_mode(&self, recursive: bool); + fn handle_mode_reached(&mut self) -> Result<(), ModeMessagingError>; +} + +#[cfg(test)] +mod tests { + use std::{println, sync::mpsc}; + + use crate::{ + mode::{ModeAndSubmode, ModeReply, ModeRequest}, + mode_tree::ModeRequestSender, + ChannelId, + }; + + use super::{ + ModeMessagingError, ModeProvider, ModeReplyHandler, ModeRequestHandler, + ModeRequestWithSenderId, StandardModeReplyMap, StandardModeRequestMap, + }; + + struct TestDevice { + /// One receiver handle for all mode requests. + pub mode_req_receiver: mpsc::Receiver, + /// This structure contains all handles to send mode replies. + pub mode_reply_sender: StandardModeReplyMap, + pub last_mode_sender: Option, + pub mode_and_submode: ModeAndSubmode, + pub target_mode_and_submode: Option, + } + + struct TestAssembly { + /// One receiver handle for all mode requests. + pub mode_req_receiver: mpsc::Receiver, + /// This structure contains all handles to send mode replies. + pub mode_reply_sender: StandardModeReplyMap, + /// This structure contains all handles to send mode requests to its children. + pub mode_children_map: StandardModeRequestMap, + pub last_mode_sender: Option, + pub mode_and_submode: ModeAndSubmode, + pub target_mode_and_submode: Option, + } + + impl ModeProvider for TestAssembly { + fn mode_and_submode(&self) -> ModeAndSubmode { + self.mode_and_submode + } + } + + impl TestAssembly { + pub fn check_mode_requests(&mut self) { + match self.mode_req_receiver.try_recv() { + Ok(ModeRequestWithSenderId { sender_id, request }) => { + match request { + ModeRequest::SetMode(mode_and_submode) => { + self.start_transition(mode_and_submode).unwrap(); + self.last_mode_sender = Some(sender_id); + } + ModeRequest::ReadMode => { + // self.handle_read_mode_request(0, self.mode_and_submode, &mut self.mode_reply_sender).unwrap() + self.mode_reply_sender + .send_mode_reply( + sender_id, + ModeReply::ModeReply(self.mode_and_submode), + ) + .unwrap() + } + ModeRequest::AnnounceMode => self.announce_mode(false), + ModeRequest::AnnounceModeRecursive => self.announce_mode(true), + } + } + Err(_) => todo!(), + }; + } + } + impl ModeRequestHandler for TestAssembly { + fn start_transition( + &mut self, + mode_and_submode: ModeAndSubmode, + ) -> Result<(), super::ModeError> { + self.target_mode_and_submode = Some(mode_and_submode); + Ok(()) + } + + fn announce_mode(&self, recursive: bool) { + println!( + "Announcing mode (recursively: {}): {:?}", + recursive, self.mode_and_submode + ); + let mut mode_request = ModeRequest::AnnounceMode; + if recursive { + mode_request = ModeRequest::AnnounceModeRecursive; + } + self.mode_children_map + .request_sender_map + .iter() + .for_each(|(_, sender)| { + sender + .send(ModeRequestWithSenderId::new( + self.mode_children_map.local_channel_id().unwrap(), + mode_request, + )) + .expect("sending mode request failed"); + }); + } + + fn handle_mode_reached(&mut self) -> Result<(), ModeMessagingError> { + self.mode_reply_sender.send_mode_reply( + self.last_mode_sender.unwrap(), + ModeReply::ModeReply(self.mode_and_submode), + )?; + Ok(()) + } + } +} diff --git a/satrs/src/queue.rs b/satrs/src/queue.rs index 25ff6c6..2d344e0 100644 --- a/satrs/src/queue.rs +++ b/satrs/src/queue.rs @@ -2,6 +2,9 @@ use core::fmt::{Display, Formatter}; #[cfg(feature = "std")] use std::error::Error; +/// Generic channel ID type. +pub type ChannelId = u32; + /// Generic error type for sending something via a message queue. #[derive(Debug, Copy, Clone)] pub enum GenericSendError {