continue mode tree

This commit is contained in:
Robin Müller 2025-01-16 16:49:35 +01:00
parent 4f37abac96
commit 42ad2ce9f5
Signed by: muellerr
GPG Key ID: A649FB78196E3849
3 changed files with 243 additions and 139 deletions

View File

@ -176,15 +176,10 @@ impl<R: MessageReceiverProvider<ModeRequest>> ModeRequestReceiver
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, thiserror::Error)]
pub enum ModeError { pub enum ModeError {
Messaging(GenericTargetedMessagingError), #[error("Messaging error: {0}")]
} Messaging(#[from] GenericTargetedMessagingError),
impl From<GenericTargetedMessagingError> for ModeError {
fn from(value: GenericTargetedMessagingError) -> Self {
Self::Messaging(value)
}
} }
pub trait ModeProvider { pub trait ModeProvider {

View File

@ -40,7 +40,7 @@ pub struct ModeDoesNotExistError(Mode);
#[derive(Debug)] #[derive(Debug)]
pub struct SequenceExecutionHelper { pub struct SequenceExecutionHelper {
target_mode: Mode, target_mode: Option<Mode>,
state: SequenceExecutionHelperStates, state: SequenceExecutionHelperStates,
request_id: RequestId, request_id: RequestId,
current_sequence_index: Option<usize>, current_sequence_index: Option<usize>,
@ -49,7 +49,7 @@ pub struct SequenceExecutionHelper {
impl Default for SequenceExecutionHelper { impl Default for SequenceExecutionHelper {
fn default() -> Self { fn default() -> Self {
Self { Self {
target_mode: 0, target_mode: None,
state: SequenceExecutionHelperStates::Idle, state: SequenceExecutionHelperStates::Idle,
request_id: 0, request_id: 0,
current_sequence_index: None, current_sequence_index: None,
@ -67,12 +67,16 @@ impl SequenceExecutionHelper {
if !sequence_tables.0.contains_key(&mode) { if !sequence_tables.0.contains_key(&mode) {
return Err(ModeDoesNotExistError(mode)); return Err(ModeDoesNotExistError(mode));
} }
self.target_mode = mode; self.target_mode = Some(mode);
self.request_id = request_id; self.request_id = request_id;
self.current_sequence_index = None; self.current_sequence_index = None;
Ok(()) Ok(())
} }
pub fn target_mode(&self) -> Option<Mode> {
self.target_mode
}
pub fn confirm_sequence_done(&mut self) { pub fn confirm_sequence_done(&mut self) {
if let SequenceExecutionHelperStates::AwaitingCheckSuccess = self.state { if let SequenceExecutionHelperStates::AwaitingCheckSuccess = self.state {
self.state = SequenceExecutionHelperStates::Idle; self.state = SequenceExecutionHelperStates::Idle;
@ -103,10 +107,13 @@ impl SequenceExecutionHelper {
if self.state == SequenceExecutionHelperStates::AwaitingCheckSuccess { if self.state == SequenceExecutionHelperStates::AwaitingCheckSuccess {
return Ok(SequenceHandlerResult::AwaitingSuccessCheck); return Ok(SequenceHandlerResult::AwaitingSuccessCheck);
} }
if self.target_mode.is_none() {
return Ok(SequenceHandlerResult::SequenceDone);
}
match self.current_sequence_index { match self.current_sequence_index {
Some(idx) => { Some(idx) => {
// Execute the sequence. // Execute the sequence.
let seq_table_value = table.0.get(&self.target_mode).unwrap(); let seq_table_value = table.0.get(&self.target_mode.unwrap()).unwrap();
self.execute_sequence_and_map_to_result( self.execute_sequence_and_map_to_result(
seq_table_value, seq_table_value,
idx, idx,
@ -116,7 +123,7 @@ impl SequenceExecutionHelper {
} }
None => { None => {
// Find the first sequence // Find the first sequence
let seq_table_value = table.0.get(&self.target_mode).unwrap(); let seq_table_value = table.0.get(&self.target_mode.unwrap()).unwrap();
if seq_table_value.entries.is_empty() { if seq_table_value.entries.is_empty() {
Ok(SequenceHandlerResult::SequenceDone) Ok(SequenceHandlerResult::SequenceDone)
} else { } else {

View File

@ -14,6 +14,7 @@ use satrs::mode_tree::{
TargetTablesMapValue, TargetTablesMapValue,
}; };
use satrs::request::{MessageMetadata, RequestId}; use satrs::request::{MessageMetadata, RequestId};
use satrs::res_code::ResultU16;
use satrs::subsystem::{ use satrs::subsystem::{
ModeDoesNotExistError, SequenceExecutionHelper, SequenceHandlerResult, TargetKeepingResult, ModeDoesNotExistError, SequenceExecutionHelper, SequenceHandlerResult, TargetKeepingResult,
}; };
@ -23,6 +24,7 @@ use satrs::{
request::GenericMessage, request::GenericMessage,
ComponentId, ComponentId,
}; };
use std::borrow::Borrow;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::convert::Infallible; use std::convert::Infallible;
@ -58,7 +60,7 @@ pub enum TestComponentId {
pub type RequestSenderType = mpsc::SyncSender<GenericMessage<ModeRequest>>; pub type RequestSenderType = mpsc::SyncSender<GenericMessage<ModeRequest>>;
pub type ReplySenderType = mpsc::SyncSender<GenericMessage<ModeReply>>; pub type ReplySenderType = mpsc::SyncSender<GenericMessage<ModeReply>>;
#[derive(Debug, Default)] #[derive(Debug, Default, PartialEq, Eq)]
pub enum ModeTreeHelperState { pub enum ModeTreeHelperState {
#[default] #[default]
Idle, Idle,
@ -93,11 +95,7 @@ pub enum ModeTreeHelperError {
CurrentModeNotInTargetTable(Mode), CurrentModeNotInTargetTable(Mode),
} }
// TODO: pub struct SubsystemCommandingHelper {
//
// 1. Fallback mode? Needs to be a part of the target mode table..
// 2. State to determine whether we are in sequence execution mode or in target keeping mode.
pub struct ModeTreeCommandingHelper {
pub current_mode: ModeAndSubmode, pub current_mode: ModeAndSubmode,
pub state: ModeTreeHelperState, pub state: ModeTreeHelperState,
pub children_mode_store: ModeStoreVec, pub children_mode_store: ModeStoreVec,
@ -106,7 +104,7 @@ pub struct ModeTreeCommandingHelper {
pub helper: SequenceExecutionHelper, pub helper: SequenceExecutionHelper,
} }
impl Default for ModeTreeCommandingHelper { impl Default for SubsystemCommandingHelper {
fn default() -> Self { fn default() -> Self {
Self { Self {
current_mode: UNKNOWN_MODE, current_mode: UNKNOWN_MODE,
@ -119,7 +117,7 @@ impl Default for ModeTreeCommandingHelper {
} }
} }
impl ModeTreeCommandingHelper { impl SubsystemCommandingHelper {
pub fn new( pub fn new(
children_mode_store: ModeStoreVec, children_mode_store: ModeStoreVec,
target_tables: TargetModeTables, target_tables: TargetModeTables,
@ -135,6 +133,75 @@ impl ModeTreeCommandingHelper {
} }
} }
pub fn start_command_sequence(
&mut self,
mode: Mode,
request_id: RequestId,
) -> Result<(), ModeDoesNotExistError> {
self.helper.load(mode, request_id, &self.sequence_tables)?;
self.state = ModeTreeHelperState::SequenceCommanding;
Ok(())
}
pub fn state_machine(
&mut self,
opt_reply: Option<GenericMessage<ModeReply>>,
req_sender: &impl ModeRequestSender,
) -> Result<ModeTreeHelperResult, ModeTreeHelperError> {
if let Some(reply) = opt_reply {
self.handle_mode_reply(&reply);
}
match self.state {
ModeTreeHelperState::Idle => Ok(ModeTreeHelperResult::Idle),
ModeTreeHelperState::TargetKeeping => {
// We check whether the current mode is modelled by a target table first.
if let Some(target_table) = self.target_tables.0.get(&self.current_mode.mode()) {
for entry in &target_table.entries {
if !entry.monitor_state {
continue;
}
let mut target_mode_violated = false;
self.children_mode_store.0.iter().for_each(|val| {
if val.id() == entry.common.target_id {
target_mode_violated = if let Some(allowed_submode_mask) =
entry.allowed_submode_mask()
{
let fixed_bits = !allowed_submode_mask;
(val.mode_and_submode().mode()
!= entry.common.mode_submode.mode())
&& (val.mode_and_submode().submode() & fixed_bits
!= entry.common.mode_submode.submode() & fixed_bits)
} else {
val.mode_and_submode() != entry.common.mode_submode
};
}
})
}
// Target keeping violated. Report violation and fallback mode to user.
return Ok(TargetKeepingResult::Violated {
fallback_mode: None,
}
.into());
}
Ok(ModeTreeHelperResult::TargetKeeping(TargetKeepingResult::Ok))
}
ModeTreeHelperState::SequenceCommanding => {
let result = self.helper.run(
&self.sequence_tables,
req_sender,
&mut self.children_mode_store,
)?;
// By default, the helper will automatically transition into the target keeping
// mode after an executed sequence.
if let SequenceHandlerResult::SequenceDone = result {
self.state = ModeTreeHelperState::TargetKeeping;
self.current_mode = ModeAndSubmode::new(self.helper.target_mode().unwrap(), 0);
}
Ok(result.into())
}
}
}
pub fn add_target_and_sequence_table( pub fn add_target_and_sequence_table(
&mut self, &mut self,
mode: Mode, mode: Mode,
@ -192,74 +259,11 @@ impl ModeTreeCommandingHelper {
} }
}; };
} }
pub fn start_command_sequence(
&mut self,
mode: Mode,
request_id: RequestId,
) -> Result<(), ModeDoesNotExistError> {
self.helper.load(mode, request_id, &self.sequence_tables)?;
self.state = ModeTreeHelperState::SequenceCommanding;
Ok(())
}
pub fn state_machine(
&mut self,
opt_reply: Option<GenericMessage<ModeReply>>,
req_sender: &impl ModeRequestSender,
) -> Result<ModeTreeHelperResult, ModeTreeHelperError> {
if let Some(reply) = opt_reply {
self.handle_mode_reply(&reply);
}
match self.state {
ModeTreeHelperState::Idle => Ok(ModeTreeHelperResult::Idle),
ModeTreeHelperState::TargetKeeping => {
// We check whether the current mode is modelled by a target table first.
if let Some(target_table) = self.target_tables.0.get(&self.current_mode.mode()) {
for entry in &target_table.entries {
if !entry.monitor_state {
continue;
}
let mut target_mode_violated = false;
self.children_mode_store.0.iter().for_each(|val| {
if val.id() == entry.common.target_id {
target_mode_violated = if let Some(allowed_submode_mask) =
entry.allowed_submode_mask()
{
let fixed_bits = !allowed_submode_mask;
(val.mode_and_submode().mode()
!= entry.common.mode_submode.mode())
&& (val.mode_and_submode().submode() & fixed_bits
!= entry.common.mode_submode.submode() & fixed_bits)
} else {
val.mode_and_submode() != entry.common.mode_submode
};
}
})
}
// Target keeping violated. Report violation and fallback mode to user.
return Ok(TargetKeepingResult::Violated {
fallback_mode: None,
}
.into());
}
Ok(ModeTreeHelperResult::TargetKeeping(TargetKeepingResult::Ok))
}
ModeTreeHelperState::SequenceCommanding => Ok(self
.helper
.run(
&self.sequence_tables,
req_sender,
&mut self.children_mode_store,
)?
.into()),
}
}
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct ModeRequestHandlerMock { pub struct ModeRequestHandlerMock {
get_mode_calls: RefCell<u32>, get_mode_calls: RefCell<usize>,
start_transition_calls: VecDeque<(MessageMetadata, ModeAndSubmode)>, start_transition_calls: VecDeque<(MessageMetadata, ModeAndSubmode)>,
announce_mode_calls: RefCell<VecDeque<AnnounceModeInfo>>, announce_mode_calls: RefCell<VecDeque<AnnounceModeInfo>>,
handle_mode_info_calls: VecDeque<(MessageMetadata, ModeAndSubmode)>, handle_mode_info_calls: VecDeque<(MessageMetadata, ModeAndSubmode)>,
@ -276,6 +280,14 @@ impl ModeRequestHandlerMock {
self.handle_mode_info_calls.clear(); self.handle_mode_info_calls.clear();
self.send_mode_reply_calls.borrow_mut().clear(); self.send_mode_reply_calls.borrow_mut().clear();
} }
pub fn mode_messages_received(&self) -> usize {
*self.get_mode_calls.borrow()
+ self.start_transition_calls.borrow().len()
+ self.announce_mode_calls.borrow().len()
+ self.handle_mode_info_calls.borrow().len()
+ self.handle_mode_reached_calls.borrow().len()
+ self.send_mode_reply_calls.borrow().len()
}
} }
impl ModeProvider for ModeRequestHandlerMock { impl ModeProvider for ModeRequestHandlerMock {
@ -341,7 +353,35 @@ impl ModeRequestHandler for ModeRequestHandlerMock {
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct ModeReplyHandlerMock {} pub struct ModeReplyHandlerMock {
mode_info_messages: VecDeque<(MessageMetadata, ModeAndSubmode)>,
mode_reply_messages: VecDeque<(MessageMetadata, ModeAndSubmode)>,
cant_reach_mode_messages: VecDeque<(MessageMetadata, ResultU16)>,
wrong_mode_messages: VecDeque<(MessageMetadata, ModeAndSubmode, ModeAndSubmode)>,
}
impl ModeReplyHandlerMock {
pub fn handle_mode_reply(&mut self, request: &GenericMessage<ModeReply>) {
match request.message {
ModeReply::ModeInfo(mode_and_submode) => {
self.mode_info_messages
.push_back((request.requestor_info, mode_and_submode));
}
ModeReply::ModeReply(mode_and_submode) => {
self.mode_reply_messages
.push_back((request.requestor_info, mode_and_submode));
}
ModeReply::CantReachMode(result_u16) => {
self.cant_reach_mode_messages
.push_back((request.requestor_info, result_u16));
}
ModeReply::WrongMode { expected, reached } => {
self.wrong_mode_messages
.push_back((request.requestor_info, expected, reached));
}
}
}
}
struct PusModeService { struct PusModeService {
pub request_id_counter: Cell<u32>, pub request_id_counter: Cell<u32>,
@ -394,9 +434,8 @@ impl ModeParent for PusModeService {
struct AcsSubsystem { struct AcsSubsystem {
pub mode_node: ModeRequestorAndHandlerMpscBounded, pub mode_node: ModeRequestorAndHandlerMpscBounded,
pub mode_requestor_info: Option<MessageMetadata>, pub mode_requestor_info: Option<MessageMetadata>,
pub mode_and_submode: ModeAndSubmode,
pub target_mode_and_submode: Option<ModeAndSubmode>, pub target_mode_and_submode: Option<ModeAndSubmode>,
pub subsystem_helper: ModeTreeCommandingHelper, pub subsystem_helper: SubsystemCommandingHelper,
pub mode_req_handler_mock: ModeRequestHandlerMock, pub mode_req_handler_mock: ModeRequestHandlerMock,
pub mode_req_recvd: u32, pub mode_req_recvd: u32,
} }
@ -406,9 +445,8 @@ impl AcsSubsystem {
Self { Self {
mode_node, mode_node,
mode_requestor_info: None, mode_requestor_info: None,
mode_and_submode: UNKNOWN_MODE,
target_mode_and_submode: None, target_mode_and_submode: None,
subsystem_helper: ModeTreeCommandingHelper::default(), subsystem_helper: SubsystemCommandingHelper::default(),
mode_req_handler_mock: Default::default(), mode_req_handler_mock: Default::default(),
mode_req_recvd: 0, mode_req_recvd: 0,
} }
@ -435,13 +473,32 @@ impl AcsSubsystem {
.state_machine(mode_reply, &self.mode_node) .state_machine(mode_reply, &self.mode_node)
{ {
Ok(result) => match result { Ok(result) => match result {
ModeTreeHelperResult::Idle => todo!(), ModeTreeHelperResult::Idle => (),
ModeTreeHelperResult::TargetKeeping(target_keeping_result) => todo!(), ModeTreeHelperResult::TargetKeeping(target_keeping_result) => {
ModeTreeHelperResult::SequenceCommanding(sequence_handler_result) => todo!(), match target_keeping_result {
TargetKeepingResult::Ok => todo!(),
TargetKeepingResult::Violated { fallback_mode } => {
if let Some(fallback_mode) = fallback_mode {
self.subsystem_helper
.start_command_sequence(fallback_mode, 0)
.unwrap();
}
}
}
}
ModeTreeHelperResult::SequenceCommanding(sequence_handler_result) => {
match sequence_handler_result {
SequenceHandlerResult::SequenceDone => (),
SequenceHandlerResult::SequenceStepDone => (),
SequenceHandlerResult::AwaitingSuccessCheck => (),
}
}
}, },
Err(error) => match error { Err(error) => match error {
ModeTreeHelperError::Message(generic_targeted_messaging_error) => todo!(), ModeTreeHelperError::Message(_generic_targeted_messaging_error) => {
ModeTreeHelperError::CurrentModeNotInTargetTable(_) => todo!(), panic!("messaging error")
}
ModeTreeHelperError::CurrentModeNotInTargetTable(_) => panic!("mode not found"),
}, },
} }
} }
@ -486,12 +543,22 @@ impl ModeChild for AcsSubsystem {
impl ModeProvider for AcsSubsystem { impl ModeProvider for AcsSubsystem {
fn mode_and_submode(&self) -> ModeAndSubmode { fn mode_and_submode(&self) -> ModeAndSubmode {
self.mode_and_submode self.subsystem_helper.current_mode
} }
} }
#[derive(Debug, thiserror::Error)]
pub enum SubsytemModeError {
#[error("mode error: {0:?}")]
Mode(#[from] ModeError),
#[error("mode does not exist: {0}")]
ModeDoesNotExist(#[from] ModeDoesNotExistError),
#[error("busy with mode transition")]
Busy,
}
impl ModeRequestHandler for AcsSubsystem { impl ModeRequestHandler for AcsSubsystem {
type Error = ModeError; type Error = SubsytemModeError;
fn start_transition( fn start_transition(
&mut self, &mut self,
@ -499,26 +566,23 @@ impl ModeRequestHandler for AcsSubsystem {
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
forced: bool, forced: bool,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
if !forced && self.subsystem_helper.state == ModeTreeHelperState::SequenceCommanding {
return Err(SubsytemModeError::Busy);
}
self.mode_requestor_info = Some(requestor); self.mode_requestor_info = Some(requestor);
self.target_mode_and_submode = Some(mode_and_submode); self.target_mode_and_submode = Some(mode_and_submode);
self.mode_req_handler_mock self.mode_req_handler_mock
.start_transition(requestor, mode_and_submode, forced) .start_transition(requestor, mode_and_submode, forced)
.unwrap(); .unwrap();
// TODO: CHeck if a transition is already active. For now, we do not allow a new transition
// if one is already active.
// Execute mode map by executing the transition table(s).
// TODO: How to deal with error handling? Add this error to generic ModeError, or create
// new error type?
self.subsystem_helper self.subsystem_helper
.start_command_sequence(mode_and_submode.mode(), requestor.request_id()) .start_command_sequence(mode_and_submode.mode(), requestor.request_id())?;
.unwrap();
Ok(()) Ok(())
} }
fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool) { fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool) {
println!( println!(
"TestAssembly: Announcing mode (recursively: {}): {:?}", "TestAssembly: Announcing mode (recursively: {}): {:?}",
recursive, self.mode_and_submode recursive, self.subsystem_helper.current_mode
); );
let mut mode_request = ModeRequest::AnnounceMode; let mut mode_request = ModeRequest::AnnounceMode;
if recursive { if recursive {
@ -546,7 +610,10 @@ impl ModeRequestHandler for AcsSubsystem {
requestor_info: Option<MessageMetadata>, requestor_info: Option<MessageMetadata>,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
if let Some(requestor) = requestor_info { if let Some(requestor) = requestor_info {
self.send_mode_reply(requestor, ModeReply::ModeReply(self.mode_and_submode))?; self.send_mode_reply(
requestor,
ModeReply::ModeReply(self.subsystem_helper.current_mode),
)?;
} }
self.mode_req_handler_mock self.mode_req_handler_mock
.handle_mode_reached(requestor_info) .handle_mode_reached(requestor_info)
@ -576,18 +643,22 @@ impl ModeRequestHandler for AcsSubsystem {
self.mode_req_handler_mock self.mode_req_handler_mock
.send_mode_reply(requestor_info, reply) .send_mode_reply(requestor_info, reply)
.unwrap(); .unwrap();
self.mode_node.send_mode_reply(requestor_info, reply)?; self.mode_node
.send_mode_reply(requestor_info, reply)
.map_err(ModeError::Messaging)?;
Ok(()) Ok(())
} }
} }
// TODO: This assembly requires some helper component to process commands.. Maybe implement it
// manually first?
struct MgmAssembly { struct MgmAssembly {
pub mode_node: ModeRequestorAndHandlerMpscBounded, pub mode_node: ModeRequestorAndHandlerMpscBounded,
pub mode_requestor_info: Option<MessageMetadata>, pub mode_requestor_info: Option<MessageMetadata>,
pub mode_and_submode: ModeAndSubmode, pub mode_and_submode: ModeAndSubmode,
pub target_mode_and_submode: Option<ModeAndSubmode>, pub target_mode_and_submode: Option<ModeAndSubmode>,
pub mode_req_mock: ModeRequestHandlerMock, pub mode_req_mock: ModeRequestHandlerMock,
pub mode_msgs_recvd: u32, pub mode_reply_mock: ModeReplyHandlerMock,
} }
impl MgmAssembly { impl MgmAssembly {
@ -598,7 +669,7 @@ impl MgmAssembly {
mode_and_submode: UNKNOWN_MODE, mode_and_submode: UNKNOWN_MODE,
target_mode_and_submode: None, target_mode_and_submode: None,
mode_req_mock: Default::default(), mode_req_mock: Default::default(),
mode_msgs_recvd: 0, mode_reply_mock: Default::default(),
} }
} }
@ -606,23 +677,23 @@ impl MgmAssembly {
self.check_mode_requests().expect("mode messaging error"); self.check_mode_requests().expect("mode messaging error");
self.check_mode_replies().expect("mode messaging error"); self.check_mode_replies().expect("mode messaging error");
} }
pub fn get_and_clear_num_mode_msgs(&mut self) -> u32 { pub fn get_num_mode_requests(&mut self) -> usize {
let tmp = self.mode_msgs_recvd; self.mode_req_mock.mode_messages_received()
self.mode_msgs_recvd = 0;
tmp
} }
pub fn check_mode_requests(&mut self) -> Result<(), GenericTargetedMessagingError> { pub fn check_mode_requests(&mut self) -> Result<(), GenericTargetedMessagingError> {
if let Some(request) = self.mode_node.try_recv_mode_request()? { if let Some(request) = self.mode_node.try_recv_mode_request()? {
self.mode_msgs_recvd += 1;
self.handle_mode_request(request).unwrap(); self.handle_mode_request(request).unwrap();
} }
Ok(()) Ok(())
} }
pub fn check_mode_replies(&mut self) -> Result<(), GenericTargetedMessagingError> { pub fn check_mode_replies(&mut self) -> Result<(), GenericTargetedMessagingError> {
// TODO: Call mode reply handler mock. // TODO: If a transition is active, we need to check whether all children have replied
// and have the correct mode. So we probably need some children list / map similarly to the
// subsystem, which also tracks where a reply is still awaited.
if let Some(reply_and_id) = self.mode_node.try_recv_mode_reply()? { if let Some(reply_and_id) = self.mode_node.try_recv_mode_reply()? {
self.mode_reply_mock.handle_mode_reply(&reply_and_id);
match reply_and_id.message { match reply_and_id.message {
ModeReply::ModeReply(reply) => { ModeReply::ModeReply(reply) => {
println!( println!(
@ -687,6 +758,25 @@ impl ModeRequestHandler for MgmAssembly {
self.mode_req_mock self.mode_req_mock
.start_transition(requestor, mode_and_submode, forced) .start_transition(requestor, mode_and_submode, forced)
.unwrap(); .unwrap();
// TODO: Is it correct to simply forward the mode?
self.mode_node
.request_sender_store
.0
.iter()
.for_each(|(_, sender)| {
sender
.send(GenericMessage::new(
MessageMetadata::new(
requestor.request_id(),
self.mode_node.local_channel_id_generic(),
),
ModeRequest::SetMode {
mode_and_submode,
forced: false,
},
))
.expect("sending mode request failed");
});
Ok(()) Ok(())
} }
@ -1001,6 +1091,7 @@ impl ModeRequestHandler for CommonDevice {
self.mode_req_mock.handle_mode_reached(requestor).unwrap(); self.mode_req_mock.handle_mode_reached(requestor).unwrap();
Ok(()) Ok(())
} }
fn handle_mode_info( fn handle_mode_info(
&mut self, &mut self,
requestor_info: MessageMetadata, requestor_info: MessageMetadata,
@ -1046,6 +1137,14 @@ pub struct AcsController {
} }
impl AcsController { impl AcsController {
pub fn new(mode_node: ModeRequestHandlerMpscBounded) -> Self {
Self {
mode_node,
mode_and_submode: UNKNOWN_MODE,
announce_mode_queue: Default::default(),
mode_req_mock: Default::default(),
}
}
pub fn run(&mut self) { pub fn run(&mut self) {
self.check_mode_requests().expect("mode messaging error"); self.check_mode_requests().expect("mode messaging error");
} }
@ -1245,12 +1344,7 @@ impl TreeTestbench {
); );
let mut mgm_assy = MgmAssembly::new(mgm_assy_node); let mut mgm_assy = MgmAssembly::new(mgm_assy_node);
let mut acs_subsystem = AcsSubsystem::new(acs_subsystem_node); let mut acs_subsystem = AcsSubsystem::new(acs_subsystem_node);
let mut acs_ctrl = AcsController { let mut acs_ctrl = AcsController::new(acs_ctrl_node);
mode_node: acs_ctrl_node,
mode_and_submode: UNKNOWN_MODE,
announce_mode_queue: RefCell::new(Default::default()),
mode_req_mock: Default::default(),
};
let mut pus_service = PusModeService { let mut pus_service = PusModeService {
request_id_counter: Cell::new(0), request_id_counter: Cell::new(0),
mode_node: mode_node_pus, mode_node: mode_node_pus,
@ -1292,7 +1386,7 @@ impl TreeTestbench {
sequence_tbl_safe_1.add_entry(SequenceTableEntry::new( sequence_tbl_safe_1.add_entry(SequenceTableEntry::new(
"SAFE_SEQ_1_ACS_CTRL", "SAFE_SEQ_1_ACS_CTRL",
TestComponentId::AcsController as u64, TestComponentId::AcsController as u64,
ModeAndSubmode::new(AcsMode::IDLE as Mode, 0), ModeAndSubmode::new(AcsMode::SAFE as Mode, 0),
false, false,
)); ));
let mut sequence_tbl_safe = SequenceTablesMapValue::new("SAFE_SEQ_TBL"); let mut sequence_tbl_safe = SequenceTablesMapValue::new("SAFE_SEQ_TBL");
@ -1397,6 +1491,16 @@ impl TreeTestbench {
mgm_devs: [mgm_dev_0, mgm_dev_1], mgm_devs: [mgm_dev_0, mgm_dev_1],
} }
} }
pub fn run(&mut self) {
self.subsystem.run();
self.ctrl.run();
self.mgt_manager.run();
self.mgm_assy.run();
self.mgm_devs[0].run();
self.mgm_devs[1].run();
self.mgt_dev.run();
}
} }
#[test] #[test]
@ -1404,15 +1508,8 @@ fn announce_recursively() {
let mut tb = TreeTestbench::new(); let mut tb = TreeTestbench::new();
tb.pus.announce_modes_recursively(); tb.pus.announce_modes_recursively();
// Run everything twice so the order does not matter. // Run everything twice so the order does not matter.
for _ in 0..2 { tb.run();
tb.subsystem.run(); tb.run();
tb.ctrl.run();
tb.mgt_manager.run();
tb.mgm_assy.run();
tb.mgm_devs[0].run();
tb.mgm_devs[1].run();
tb.mgt_dev.run();
}
assert_eq!(tb.subsystem.get_and_clear_num_mode_requests(), 1); assert_eq!(tb.subsystem.get_and_clear_num_mode_requests(), 1);
let mut announces = tb let mut announces = tb
.subsystem .subsystem
@ -1424,7 +1521,7 @@ fn announce_recursively() {
assert_eq!(tb.ctrl.mode_req_mock.start_transition_calls.len(), 0); assert_eq!(tb.ctrl.mode_req_mock.start_transition_calls.len(), 0);
assert_eq!(tb.ctrl.mode_and_submode(), UNKNOWN_MODE); assert_eq!(tb.ctrl.mode_and_submode(), UNKNOWN_MODE);
assert_eq!(announces.len(), 1); assert_eq!(announces.len(), 1);
assert_eq!(tb.mgm_assy.get_and_clear_num_mode_msgs(), 1); assert_eq!(tb.mgm_assy.get_num_mode_requests(), 1);
announces = tb.mgm_assy.mode_req_mock.announce_mode_calls.borrow_mut(); announces = tb.mgm_assy.mode_req_mock.announce_mode_calls.borrow_mut();
assert_eq!(tb.mgm_assy.mode_req_mock.start_transition_calls.len(), 0); assert_eq!(tb.mgm_assy.mode_req_mock.start_transition_calls.len(), 0);
assert_eq!(tb.mgm_assy.mode_and_submode(), UNKNOWN_MODE); assert_eq!(tb.mgm_assy.mode_and_submode(), UNKNOWN_MODE);
@ -1456,12 +1553,17 @@ fn announce_recursively() {
#[test] #[test]
fn command_safe_mode() { fn command_safe_mode() {
let mut tb = TreeTestbench::new(); let mut tb = TreeTestbench::new();
tb.subsystem.run(); tb.run();
tb.ctrl.run();
tb.mgm_assy.run();
tb.mgt_dev.run();
tb.mgm_devs[0].run();
tb.mgm_devs[1].run();
tb.pus tb.pus
.send_mode_cmd(ModeAndSubmode::new(AcsMode::IDLE as u32, 0)); .send_mode_cmd(ModeAndSubmode::new(AcsMode::SAFE as u32, 0));
tb.run();
tb.run();
assert_eq!(
tb.ctrl.mode_and_submode(),
ModeAndSubmode::new(AcsMode::SAFE as u32, 0)
);
assert_eq!(
tb.mgm_assy.mode_and_submode(),
ModeAndSubmode::new(DefaultMode::NORMAL as u32, 0)
);
} }