From 2700e03c162136830cf2f0c034befb819f4ee240 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 14 Jul 2026 12:57:05 +0200 Subject: [PATCH] start integrating new susbystem commanding helper --- satrs-example/models/src/acs/ctrl.rs | 3 +- satrs-example/models/src/acs/mgm_assembly.rs | 29 ++++++++ satrs-example/models/src/acs/mgt.rs | 3 +- satrs-example/models/src/lib.rs | 5 +- satrs-example/src/acs/subsystem.rs | 31 +++++++- satrs/src/mode_tree.rs | 2 +- satrs/src/subsystem.rs | 76 +++++++++----------- 7 files changed, 102 insertions(+), 47 deletions(-) diff --git a/satrs-example/models/src/acs/ctrl.rs b/satrs-example/models/src/acs/ctrl.rs index 9b901cb..0835756 100644 --- a/satrs-example/models/src/acs/ctrl.rs +++ b/satrs-example/models/src/acs/ctrl.rs @@ -1,4 +1,5 @@ -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq, num_enum::IntoPrimitive, num_enum::TryFromPrimitive)] +#[repr(u32)] pub enum Mode { Passive, Safe, diff --git a/satrs-example/models/src/acs/mgm_assembly.rs b/satrs-example/models/src/acs/mgm_assembly.rs index 261c041..eb1f1fa 100644 --- a/satrs-example/models/src/acs/mgm_assembly.rs +++ b/satrs-example/models/src/acs/mgm_assembly.rs @@ -1,5 +1,8 @@ use core::str::FromStr; +use num_enum::TryFromPrimitive as _; +use satrs::mode::ModeRaw; + use crate::DeviceMode; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)] @@ -11,6 +14,32 @@ pub enum Mode { NoModeKeeping, } +impl From for ModeRaw { + fn from(value: Mode) -> Self { + match value { + Mode::Device(device_mode) => device_mode.into(), + Mode::NoModeKeeping => 5, + } + } +} + +impl TryFrom for Mode { + type Error = (); + + fn try_from(value: ModeRaw) -> Result { + match DeviceMode::try_from_primitive(value) { + Ok(val) => Ok(Mode::Device(val)), + Err(_) => { + if value == 5 { + Ok(Mode::NoModeKeeping) + } else { + Err(()) + } + } + } + } +} + impl FromStr for Mode { type Err = (); diff --git a/satrs-example/models/src/acs/mgt.rs b/satrs-example/models/src/acs/mgt.rs index 1450214..ca7a7b9 100644 --- a/satrs-example/models/src/acs/mgt.rs +++ b/satrs-example/models/src/acs/mgt.rs @@ -1,4 +1,5 @@ -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq, num_enum::IntoPrimitive, num_enum::TryFromPrimitive)] +#[repr(u32)] pub enum Mode { Off, Normal, diff --git a/satrs-example/models/src/lib.rs b/satrs-example/models/src/lib.rs index ac4cbc5..e39a592 100644 --- a/satrs-example/models/src/lib.rs +++ b/satrs-example/models/src/lib.rs @@ -29,8 +29,10 @@ pub enum ComponentId { AcsSubsystem, AcsMgmAssembly, + AcsController, AcsMgm0, AcsMgm1, + AcsMgt, EpsSubsystem, EpsPcdu, @@ -159,7 +161,8 @@ pub trait Message { /// The states are related both to the physical and the logical state of the device. Some /// device handlers control the power supply of their own device and an off state might also /// mean that the device is physically off. -#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Copy, Clone)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Copy, Clone, num_enum::IntoPrimitive, num_enum::TryFromPrimitive)] +#[repr(u32)] pub enum DeviceMode { Off = 0, On = 1, diff --git a/satrs-example/src/acs/subsystem.rs b/satrs-example/src/acs/subsystem.rs index 36341aa..ca2bc7d 100644 --- a/satrs-example/src/acs/subsystem.rs +++ b/satrs-example/src/acs/subsystem.rs @@ -7,7 +7,12 @@ use models::{ ComponentId, acs::subsystem::{Mode, response}, }; -use satrs::spacepackets::CcsdsPacketIdAndPsc; +use satrs::{ + mode::ModeRaw, + mode_tree::{ModeStoreProvider, ModeStoreVec, SequenceModeTables, TargetModeTables}, + spacepackets::CcsdsPacketIdAndPsc, + subsystem::SubsystemCommandingHelper, +}; use satrs_example::{ModeHelper, TmtcQueues}; #[derive(Debug)] @@ -114,6 +119,7 @@ pub struct Subsystem { mode_request_senders: ModeRequestSenders, mode_report_receivers: ModeReportReceivers, tmtc_queues: TmtcQueues, + subsystem_helper: SubsystemCommandingHelper, } impl Subsystem { @@ -124,6 +130,24 @@ impl Subsystem { mode_report_receivers: ModeReportReceivers, tmtc_queues: TmtcQueues, ) -> Self { + let mut mode_store_vec = ModeStoreVec::default(); + mode_store_vec.add_component( + ComponentId::AcsMgmAssembly as satrs::ComponentId, + models::acs::mgm_assembly::Mode::NoModeKeeping.into(), + ); + mode_store_vec.add_component( + ComponentId::AcsController as satrs::ComponentId, + models::acs::ctrl::Mode::Passive.into(), + ); + mode_store_vec.add_component( + ComponentId::AcsMgt as satrs::ComponentId, + models::acs::mgt::Mode::Off.into(), + ); + + let mut target_tables = TargetModeTables::default(); + + let mut sequence_tables = SequenceModeTables::default(); + Self { mode_helper: ModeHelper::new( models::acs::subsystem::Mode::Off, @@ -134,6 +158,11 @@ impl Subsystem { mode_report_receivers, tmtc_queues, transition_step: 0, + subsystem_helper: SubsystemCommandingHelper::new( + mode_store_vec, + target_tables, + sequence_tables, + ), } } diff --git a/satrs/src/mode_tree.rs b/satrs/src/mode_tree.rs index 3842a79..2843ed7 100644 --- a/satrs/src/mode_tree.rs +++ b/satrs/src/mode_tree.rs @@ -168,7 +168,7 @@ impl ModeStoreValue { } pub trait SequenceTableProvider { - fn sequence_at_index(&self, index: usize) -> Option<&[SequenceTableEntry]>; + fn sequence_at_index(&self, index: u8) -> Option<&[SequenceTableEntry]>; } #[derive(Debug, thiserror::Error)] diff --git a/satrs/src/subsystem.rs b/satrs/src/subsystem.rs index 45c1c25..3e88225 100644 --- a/satrs/src/subsystem.rs +++ b/satrs/src/subsystem.rs @@ -1,3 +1,5 @@ +use arbitrary_int::{traits::Integer as _, u24}; + use crate::{ ComponentId, mode_tree::{ @@ -40,8 +42,6 @@ pub struct ModeDoesNotExistError(ModeRaw); pub enum StartSequenceError { #[error("mode {0} does not exist")] ModeDoesNotExist(#[from] ModeDoesNotExistError), - #[error("invalid request ID")] - InvalidRequestId(RequestId), } #[derive(Debug, thiserror::Error)] @@ -60,8 +60,8 @@ pub struct SequenceExecutionHelper { target_mode: Option, state: SequenceExecutionHelperState, request_id: Option, - current_sequence_index: Option, - last_sequence_index: Option, + current_sequence_index: Option, + last_sequence_index: Option, } impl Default for SequenceExecutionHelper { @@ -123,17 +123,20 @@ impl SequenceExecutionHelper { /// /// # 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 + /// * `sequence_table_provider` - This table contains the sequence tables to reach the mode + /// previously loaded with [Self::load] /// * `children_mode_store` - The mode store vector to keep track of the mode states of /// children components + /// * `mode_request_handler` - A function which is called to send out mode requests to the + /// children or cache the required requests so they can be sent after the function call. pub fn run( &mut self, sequence_table_provider: &impl SequenceTableProvider, children_mode_store: &mut impl ModeStoreProvider, mode_request_handler: F, ) -> Result { + // TODO: Check whether sequence table length is larger than 255. Improbable, but let's + // solve this cleanly. if self.state == SequenceExecutionHelperState::Idle { return Ok(ModeCommandingResult::Done); } @@ -211,7 +214,7 @@ impl SequenceExecutionHelper { self.state == SequenceExecutionHelperState::AwaitingSuccessCheck } - pub fn current_sequence_index(&self) -> Option { + pub fn current_sequence_index(&self) -> Option { self.current_sequence_index } @@ -224,7 +227,7 @@ impl SequenceExecutionHelper { /// It is also called by the [Self::run] method of this helper. pub fn execute_sequence_and_map_to_result( &mut self, - sequence_index: usize, + sequence_index: u8, table_entries: &[SequenceTableEntry], children_mode_store: &mut impl ModeStoreProvider, mode_request_handler: impl FnMut(ComponentId, ModeRaw), @@ -235,11 +238,11 @@ impl SequenceExecutionHelper { if Self::execute_sequence(table_entries, children_mode_store, mode_request_handler) { self.state = SequenceExecutionHelperState::AwaitingSuccessCheck; ModeCommandingResult::AwaitingSuccessCheck - } else if table_entries.len() - 1 == sequence_index { + } else if table_entries.len() - 1 == sequence_index as usize { self.state = SequenceExecutionHelperState::Idle; ModeCommandingResult::Done } else { - self.current_sequence_index = Some(sequence_index + 1); + self.current_sequence_index = Some(sequence_index as u8 + 1); ModeCommandingResult::StepDone } } @@ -310,7 +313,7 @@ pub enum ModeTreeHelperError { #[error("mode command failed")] ModeCommmandFailure { /// Table index of the sequence table entry which failed. - seq_table_index: Option, + seq_table_index: Option, }, /// Target mode keeping violation. #[error("target keeping violation")] @@ -323,8 +326,12 @@ pub enum ModeTreeHelperError { /// This is a helper object which can be used by a subsystem component to execute mode sequences /// and perform target keeping. /// +/// It currently only works on systems with allocation support and it will also allocate at +/// run-time. +/// /// This helper object tries to compose as much data and state information as possible which is /// required for this process. +#[derive(Debug)] pub struct SubsystemCommandingHelper { /// State of the helper. state: ModeTreeHelperState, @@ -332,8 +339,9 @@ pub struct SubsystemCommandingHelper { current_mode: ModeRaw, /// 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. + /// Sequence counter used to generate unique request IDs. + sequence_counter: u24, + /// Active internal request ID, whic his built from the sequence counter and sequence index. active_internal_request_id: Option, /// The primary data structure to keep the target state information for subsystem /// [modes][ModeRaw]. it specifies the mode each child should have for a certain subsystem mode @@ -351,6 +359,7 @@ impl Default for SubsystemCommandingHelper { fn default() -> Self { Self { current_mode: u32::MAX, + sequence_counter: u24::ZERO, state: Default::default(), children_mode_store: Default::default(), active_internal_request_id: None, @@ -382,6 +391,7 @@ impl SubsystemCommandingHelper { state: ModeTreeHelperState::Idle, children_mode_store, active_internal_request_id: None, + sequence_counter: u24::ZERO, target_tables, sequence_tables, seq_exec_helper: Default::default(), @@ -396,18 +406,14 @@ impl SubsystemCommandingHelper { self.current_mode } - pub fn request_id(&self) -> Option { - self.active_internal_request_id.map(|v| v >> 8) - } - /// This returns the internal request ID, which is the regular [Self::request_id] specified - /// by the user shifter 8 to the right and then increment with the current sequence commanding + /// by the user shifted 8 to the right and then increment with the current sequence commanding /// step. The value can still be retrieved because it might be required for reply verification. /// /// The state machine specifies this request ID for all mode commands related to the /// current step of sequence commanding. - pub fn internal_request_id(&self) -> Option { - self.active_internal_request_id + fn internal_request_id(&self, sequence_index: u8) -> RequestId { + (self.sequence_counter.as_u32() << 8) | sequence_index as u32 } /// Retrieve the fallback mode for the current mode of the subsystem by trying to retrieve @@ -448,23 +454,10 @@ impl SubsystemCommandingHelper { /// # Arguments /// /// - `mode` - The mode to command - /// - `request_id` - Request ID associated with the command sequence. The value of this value - /// should not be larger than the maximum possible value for 24 bits: (2 ^ 24) - 1 = 16777215 - /// because 8 bits are reserved for internal sequence index tracking. - pub fn start_command_sequence( - &mut self, - mode: ModeRaw, - request_id: RequestId, - ) -> Result<(), StartSequenceError> { - if request_id > 2_u32.pow(24) - 1 { - return Err(StartSequenceError::InvalidRequestId(request_id)); - } - self.active_internal_request_id = Some(request_id << 8); - self.seq_exec_helper.load( - mode, - self.active_internal_request_id.unwrap(), - &self.sequence_tables, - )?; + pub fn start_command_sequence(&mut self, mode: ModeRaw) -> Result<(), StartSequenceError> { + self.sequence_counter = self.sequence_counter.wrapping_add(u24::new(1)); + self.seq_exec_helper + .load(mode, self.internal_request_id(0), &self.sequence_tables)?; self.state = ModeTreeHelperState::ModeCommanding; Ok(()) } @@ -574,10 +567,9 @@ impl SubsystemCommandingHelper { } fn update_internal_req_id(&mut self) { - let new_internal_req_id = (self.request_id().unwrap() << 8) - | self.seq_exec_helper.current_sequence_index().unwrap() as u32; - self.seq_exec_helper.set_request_id(new_internal_req_id); - self.active_internal_request_id = Some(new_internal_req_id); + self.seq_exec_helper.set_request_id( + self.internal_request_id(self.seq_exec_helper.current_sequence_index().unwrap() as u8), + ); } // Handles a mode reply message and returns whether the reply completes a step of sequence