start integrating new susbystem commanding helper

This commit is contained in:
Robin Mueller
2026-07-14 12:57:05 +02:00
parent 818f7d19eb
commit 2700e03c16
7 changed files with 102 additions and 47 deletions
+2 -1
View File
@@ -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,
@@ -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<Mode> for ModeRaw {
fn from(value: Mode) -> Self {
match value {
Mode::Device(device_mode) => device_mode.into(),
Mode::NoModeKeeping => 5,
}
}
}
impl TryFrom<ModeRaw> for Mode {
type Error = ();
fn try_from(value: ModeRaw) -> Result<Self, Self::Error> {
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 = ();
+2 -1
View File
@@ -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,
+4 -1
View File
@@ -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,
+30 -1
View File
@@ -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,
),
}
}
+1 -1
View File
@@ -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)]
+34 -42
View File
@@ -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<ModeRaw>,
state: SequenceExecutionHelperState,
request_id: Option<RequestId>,
current_sequence_index: Option<usize>,
last_sequence_index: Option<usize>,
current_sequence_index: Option<u8>,
last_sequence_index: Option<u8>,
}
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<F: FnMut(ComponentId, ModeRaw)>(
&mut self,
sequence_table_provider: &impl SequenceTableProvider,
children_mode_store: &mut impl ModeStoreProvider,
mode_request_handler: F,
) -> Result<ModeCommandingResult, InvalidSequenceIndexError> {
// 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<usize> {
pub fn current_sequence_index(&self) -> Option<u8> {
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<usize>,
seq_table_index: Option<u8>,
},
/// 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<RequestId>,
/// 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<RequestId> {
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<RequestId> {
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