introduce forced flag for set mode cmd

This commit is contained in:
Robin Müller 2025-01-16 13:56:49 +01:00
parent a42aefff87
commit df24f50e8e
9 changed files with 127 additions and 33 deletions

View File

@ -386,6 +386,7 @@ impl<
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
_forced: bool,
) -> Result<(), satrs::mode::ModeError> { ) -> Result<(), satrs::mode::ModeError> {
log::info!( log::info!(
"{}: transitioning to mode {:?}", "{}: transitioning to mode {:?}",
@ -575,7 +576,10 @@ mod tests {
.mode_request_tx .mode_request_tx
.send(GenericMessage::new( .send(GenericMessage::new(
MessageMetadata::new(0, PUS_MODE_SERVICE.id()), MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
ModeRequest::SetMode(ModeAndSubmode::new(DeviceMode::Normal as u32, 0)), ModeRequest::SetMode {
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
forced: false,
},
)) ))
.expect("failed to send mode request"); .expect("failed to send mode request");
testbench.handler.periodic_operation(); testbench.handler.periodic_operation();
@ -633,7 +637,10 @@ mod tests {
.mode_request_tx .mode_request_tx
.send(GenericMessage::new( .send(GenericMessage::new(
MessageMetadata::new(0, PUS_MODE_SERVICE.id()), MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
ModeRequest::SetMode(ModeAndSubmode::new(DeviceMode::Normal as u32, 0)), ModeRequest::SetMode {
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
forced: false,
},
)) ))
.expect("failed to send mode request"); .expect("failed to send mode request");
testbench.handler.periodic_operation(); testbench.handler.periodic_operation();

View File

@ -412,6 +412,7 @@ impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeRequestHandler
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
_forced: bool,
) -> Result<(), satrs::mode::ModeError> { ) -> Result<(), satrs::mode::ModeError> {
log::info!( log::info!(
"{}: transitioning to mode {:?}", "{}: transitioning to mode {:?}",
@ -660,7 +661,10 @@ mod tests {
.mode_request_tx .mode_request_tx
.send(GenericMessage::new( .send(GenericMessage::new(
MessageMetadata::new(0, PUS_MODE_SERVICE.id()), MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
ModeRequest::SetMode(ModeAndSubmode::new(DeviceMode::Normal as u32, 0)), ModeRequest::SetMode {
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
forced: false,
},
)) ))
.expect("failed to send mode request"); .expect("failed to send mode request");
let switch_map_shared = testbench.handler.shared_switch_map.lock().unwrap(); let switch_map_shared = testbench.handler.shared_switch_map.lock().unwrap();
@ -692,7 +696,10 @@ mod tests {
.mode_request_tx .mode_request_tx
.send(GenericMessage::new( .send(GenericMessage::new(
MessageMetadata::new(0, PUS_MODE_SERVICE.id()), MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
ModeRequest::SetMode(ModeAndSubmode::new(DeviceMode::Normal as u32, 0)), ModeRequest::SetMode {
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
forced: false,
},
)) ))
.expect("failed to send mode request"); .expect("failed to send mode request");
testbench testbench

View File

@ -291,7 +291,10 @@ fn static_tmtc_pool_main() {
pcdu_handler_mode_tx pcdu_handler_mode_tx
.send(GenericMessage::new( .send(GenericMessage::new(
MessageMetadata::new(0, NO_SENDER), MessageMetadata::new(0, NO_SENDER),
ModeRequest::SetMode(ModeAndSubmode::new(DeviceMode::Normal as Mode, 0)), ModeRequest::SetMode {
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as Mode, 0),
forced: false,
},
)) ))
.expect("sending initial mode request failed"); .expect("sending initial mode request failed");
@ -598,7 +601,10 @@ fn dyn_tmtc_pool_main() {
pcdu_handler_mode_tx pcdu_handler_mode_tx
.send(GenericMessage::new( .send(GenericMessage::new(
MessageMetadata::new(0, NO_SENDER), MessageMetadata::new(0, NO_SENDER),
ModeRequest::SetMode(ModeAndSubmode::new(DeviceMode::Normal as Mode, 0)), ModeRequest::SetMode {
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as Mode, 0),
forced: false,
},
)) ))
.expect("sending initial mode request failed"); .expect("sending initial mode request failed");

View File

@ -191,7 +191,13 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
} }
let mode_and_submode = ModeAndSubmode::from_be_bytes(&tc.user_data()[4..]) let mode_and_submode = ModeAndSubmode::from_be_bytes(&tc.user_data()[4..])
.expect("mode and submode extraction failed"); .expect("mode and submode extraction failed");
Ok((active_request, ModeRequest::SetMode(mode_and_submode))) Ok((
active_request,
ModeRequest::SetMode {
mode_and_submode,
forced: false,
},
))
} }
Subservice::TcReadMode => Ok((active_request, ModeRequest::ReadMode)), Subservice::TcReadMode => Ok((active_request, ModeRequest::ReadMode)),
Subservice::TcAnnounceMode => Ok((active_request, ModeRequest::AnnounceMode)), Subservice::TcAnnounceMode => Ok((active_request, ModeRequest::AnnounceMode)),
@ -347,7 +353,13 @@ mod tests {
let (_active_req, req) = testbench let (_active_req, req) = testbench
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0) .convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
.expect("conversion has failed"); .expect("conversion has failed");
assert_eq!(req, ModeRequest::SetMode(mode_and_submode)); assert_eq!(
req,
ModeRequest::SetMode {
mode_and_submode,
forced: false
}
);
} }
#[test] #[test]

View File

@ -116,7 +116,10 @@ impl TargetedModeCommand {
pub enum ModeRequest { pub enum ModeRequest {
/// Mode information. Can be used to notify other components of changed modes. /// Mode information. Can be used to notify other components of changed modes.
ModeInfo(ModeAndSubmode), ModeInfo(ModeAndSubmode),
SetMode(ModeAndSubmode), SetMode {
mode_and_submode: ModeAndSubmode,
forced: bool,
},
ReadMode, ReadMode,
AnnounceMode, AnnounceMode,
AnnounceModeRecursive, AnnounceModeRecursive,
@ -203,6 +206,7 @@ pub trait ModeRequestHandler: ModeProvider {
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
forced: bool,
) -> Result<(), Self::Error>; ) -> Result<(), Self::Error>;
fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool); fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool);
@ -229,9 +233,10 @@ pub trait ModeRequestHandler: ModeProvider {
request: GenericMessage<ModeRequest>, request: GenericMessage<ModeRequest>,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
match request.message { match request.message {
ModeRequest::SetMode(mode_and_submode) => { ModeRequest::SetMode {
self.start_transition(request.requestor_info, mode_and_submode) mode_and_submode,
} forced,
} => self.start_transition(request.requestor_info, mode_and_submode, forced),
ModeRequest::ReadMode => self.send_mode_reply( ModeRequest::ReadMode => self.send_mode_reply(
request.requestor_info, request.requestor_info,
ModeReply::ModeReply(self.mode_and_submode()), ModeReply::ModeReply(self.mode_and_submode()),

View File

@ -1221,9 +1221,10 @@ pub(crate) fn source_buffer_large_enough(
#[cfg(any(feature = "test_util", test))] #[cfg(any(feature = "test_util", test))]
pub mod test_util { pub mod test_util {
use crate::request::UniqueApidTargetId;
use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader}; use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader};
use crate::request::UniqueApidTargetId;
use super::{ use super::{
verification::{self, TcStateAccepted, VerificationToken}, verification::{self, TcStateAccepted, VerificationToken},
DirectPusPacketHandlerResult, PusPacketHandlingError, DirectPusPacketHandlerResult, PusPacketHandlingError,
@ -1232,6 +1233,7 @@ pub mod test_util {
pub const TEST_APID: u16 = 0x101; pub const TEST_APID: u16 = 0x101;
pub const TEST_UNIQUE_ID_0: u32 = 0x05; pub const TEST_UNIQUE_ID_0: u32 = 0x05;
pub const TEST_UNIQUE_ID_1: u32 = 0x06; pub const TEST_UNIQUE_ID_1: u32 = 0x06;
pub const TEST_COMPONENT_ID_0: UniqueApidTargetId = pub const TEST_COMPONENT_ID_0: UniqueApidTargetId =
UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0); UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0);
pub const TEST_COMPONENT_ID_1: UniqueApidTargetId = pub const TEST_COMPONENT_ID_1: UniqueApidTargetId =
@ -1268,14 +1270,13 @@ pub mod tests {
use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader}; use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader};
use spacepackets::ecss::{PusPacket, WritablePusPacket}; use spacepackets::ecss::{PusPacket, WritablePusPacket};
use spacepackets::CcsdsPacket; use spacepackets::CcsdsPacket;
use test_util::{TEST_APID, TEST_COMPONENT_ID_0};
use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig}; use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig};
use crate::pus::verification::{RequestId, VerificationReporter}; use crate::pus::verification::{RequestId, VerificationReporter};
use crate::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool}; use crate::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool};
use crate::ComponentId; use crate::ComponentId;
use super::test_util::{TEST_APID, TEST_COMPONENT_ID_0};
use super::verification::test_util::TestVerificationReporter; use super::verification::test_util::TestVerificationReporter;
use super::verification::{ use super::verification::{
TcStateAccepted, VerificationReporterCfg, VerificationReportingProvider, VerificationToken, TcStateAccepted, VerificationReporterCfg, VerificationReportingProvider, VerificationToken,

View File

@ -166,7 +166,10 @@ impl SequenceExecutionHelper {
sender.send_mode_request( sender.send_mode_request(
request_id, request_id,
entry.common.target_id, entry.common.target_id,
ModeRequest::SetMode(entry.common.mode_submode), ModeRequest::SetMode {
mode_and_submode: entry.common.mode_submode,
forced: false,
},
)?; )?;
mode_store_vec.0.iter_mut().for_each(|val| { mode_store_vec.0.iter_mut().for_each(|val| {
if val.id() == entry.common.target_id { if val.id() == entry.common.target_id {

View File

@ -13,8 +13,10 @@ use satrs::mode_tree::{
ModeStoreVec, SequenceModeTables, SequenceTablesMapValue, TargetModeTables, ModeStoreVec, SequenceModeTables, SequenceTablesMapValue, TargetModeTables,
TargetTablesMapValue, TargetTablesMapValue,
}; };
use satrs::request::MessageMetadata; use satrs::request::{MessageMetadata, RequestId};
use satrs::subsystem::{SequenceExecutionHelper, SequenceHandlerResult, TargetKeepingResult}; use satrs::subsystem::{
ModeDoesNotExistError, SequenceExecutionHelper, SequenceHandlerResult, TargetKeepingResult,
};
use satrs::{ use satrs::{
mode::{ModeAndSubmode, ModeReply, ModeRequest}, mode::{ModeAndSubmode, ModeReply, ModeRequest},
queue::GenericTargetedMessagingError, queue::GenericTargetedMessagingError,
@ -191,13 +193,23 @@ 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( pub fn state_machine(
&mut self, &mut self,
opt_reply: Option<&GenericMessage<ModeReply>>, opt_reply: Option<GenericMessage<ModeReply>>,
req_sender: &impl ModeRequestSender, req_sender: &impl ModeRequestSender,
) -> Result<ModeTreeHelperResult, ModeTreeHelperError> { ) -> Result<ModeTreeHelperResult, ModeTreeHelperError> {
if let Some(reply) = opt_reply { if let Some(reply) = opt_reply {
self.handle_mode_reply(reply); self.handle_mode_reply(&reply);
} }
match self.state { match self.state {
ModeTreeHelperState::Idle => Ok(ModeTreeHelperResult::Idle), ModeTreeHelperState::Idle => Ok(ModeTreeHelperResult::Idle),
@ -280,6 +292,7 @@ impl ModeRequestHandler for ModeRequestHandlerMock {
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
_forced: bool,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
self.start_transition_calls self.start_transition_calls
.push_back((requestor, mode_and_submode)); .push_back((requestor, mode_and_submode));
@ -347,6 +360,21 @@ impl PusModeService {
self.request_id_counter self.request_id_counter
.replace(self.request_id_counter.get() + 1); .replace(self.request_id_counter.get() + 1);
} }
pub fn send_mode_cmd(&self, mode: ModeAndSubmode) {
self.mode_node
.send_mode_request(
self.request_id_counter.get(),
TestComponentId::AcsSubsystem as ComponentId,
ModeRequest::SetMode {
mode_and_submode: mode,
forced: false,
},
)
.unwrap();
self.request_id_counter
.replace(self.request_id_counter.get() + 1);
}
} }
impl ModeNode for PusModeService { impl ModeNode for PusModeService {
@ -398,8 +426,23 @@ impl AcsSubsystem {
self.handle_mode_request(request) self.handle_mode_request(request)
.expect("mode messaging error"); .expect("mode messaging error");
} }
if let Some(_reply) = self.mode_node.try_recv_mode_reply().unwrap() { let mut mode_reply = None;
// TODO: Implementation. if let Some(reply) = self.mode_node.try_recv_mode_reply().unwrap() {
mode_reply = Some(reply);
}
match self
.subsystem_helper
.state_machine(mode_reply, &self.mode_node)
{
Ok(result) => match result {
ModeTreeHelperResult::Idle => todo!(),
ModeTreeHelperResult::TargetKeeping(target_keeping_result) => todo!(),
ModeTreeHelperResult::SequenceCommanding(sequence_handler_result) => todo!(),
},
Err(error) => match error {
ModeTreeHelperError::Message(generic_targeted_messaging_error) => todo!(),
ModeTreeHelperError::CurrentModeNotInTargetTable(_) => todo!(),
},
} }
} }
pub fn add_target_and_sequence_table( pub fn add_target_and_sequence_table(
@ -454,13 +497,21 @@ impl ModeRequestHandler for AcsSubsystem {
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
forced: bool,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
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) .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). // 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
.start_command_sequence(mode_and_submode.mode(), requestor.request_id())
.unwrap();
Ok(()) Ok(())
} }
@ -629,11 +680,12 @@ impl ModeRequestHandler for MgmAssembly {
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
forced: bool,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
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_mock self.mode_req_mock
.start_transition(requestor, mode_and_submode) .start_transition(requestor, mode_and_submode, forced)
.unwrap(); .unwrap();
Ok(()) Ok(())
} }
@ -780,11 +832,12 @@ impl ModeRequestHandler for DeviceManager {
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
forced: bool,
) -> Result<(), ModeError> { ) -> Result<(), ModeError> {
self.mode_and_submode = mode_and_submode; self.mode_and_submode = mode_and_submode;
self.handle_mode_reached(Some(requestor))?; self.handle_mode_reached(Some(requestor))?;
self.mode_req_mock self.mode_req_mock
.start_transition(requestor, mode_and_submode) .start_transition(requestor, mode_and_submode, forced)
.unwrap(); .unwrap();
Ok(()) Ok(())
} }
@ -923,11 +976,12 @@ impl ModeRequestHandler for CommonDevice {
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
forced: bool,
) -> Result<(), ModeError> { ) -> Result<(), ModeError> {
self.mode_and_submode = mode_and_submode; self.mode_and_submode = mode_and_submode;
self.handle_mode_reached(Some(requestor))?; self.handle_mode_reached(Some(requestor))?;
self.mode_req_mock self.mode_req_mock
.start_transition(requestor, mode_and_submode) .start_transition(requestor, mode_and_submode, forced)
.unwrap(); .unwrap();
Ok(()) Ok(())
} }
@ -1031,11 +1085,12 @@ impl ModeRequestHandler for AcsController {
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
forced: bool,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
self.mode_and_submode = mode_and_submode; self.mode_and_submode = mode_and_submode;
self.handle_mode_reached(Some(requestor))?; self.handle_mode_reached(Some(requestor))?;
self.mode_req_mock self.mode_req_mock
.start_transition(requestor, mode_and_submode) .start_transition(requestor, mode_and_submode, forced)
.unwrap(); .unwrap();
Ok(()) Ok(())
} }
@ -1407,4 +1462,6 @@ fn command_safe_mode() {
tb.mgt_dev.run(); tb.mgt_dev.run();
tb.mgm_devs[0].run(); tb.mgm_devs[0].run();
tb.mgm_devs[1].run(); tb.mgm_devs[1].run();
tb.pus
.send_mode_cmd(ModeAndSubmode::new(AcsMode::IDLE as u32, 0));
} }

View File

@ -6,7 +6,6 @@ use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
use satrs::params::U32Pair; use satrs::params::U32Pair;
use satrs::params::{Params, ParamsHeapless, WritableToBeBytes}; use satrs::params::{Params, ParamsHeapless, WritableToBeBytes};
use satrs::pus::event_man::{DefaultPusEventReportingMap, EventReporter, PusEventTmCreatorWithMap}; use satrs::pus::event_man::{DefaultPusEventReportingMap, EventReporter, PusEventTmCreatorWithMap};
use satrs::pus::test_util::TEST_COMPONENT_ID_0;
use satrs::request::UniqueApidTargetId; use satrs::request::UniqueApidTargetId;
use satrs::tmtc::PacketAsVec; use satrs::tmtc::PacketAsVec;
use spacepackets::ecss::tm::PusTmReader; use spacepackets::ecss::tm::PusTmReader;
@ -100,10 +99,7 @@ fn test_threaded_usage() {
// Event sender and TM checker thread // Event sender and TM checker thread
let jh1 = thread::spawn(move || { let jh1 = thread::spawn(move || {
event_tx event_tx
.send(EventMessage::new( .send(EventMessage::new(TEST_ID.id(), INFO_EVENT.into()))
TEST_COMPONENT_ID_0.id(),
INFO_EVENT.into(),
))
.expect("Sending info event failed"); .expect("Sending info event failed");
loop { loop {
match event_packet_rx.try_recv() { match event_packet_rx.try_recv() {
@ -130,7 +126,7 @@ fn test_threaded_usage() {
} }
event_tx event_tx
.send(EventMessage::new_with_params( .send(EventMessage::new_with_params(
TEST_COMPONENT_ID_0.id(), TEST_ID.id(),
LOW_SEV_EVENT, LOW_SEV_EVENT,
&Params::Heapless((2_u32, 3_u32).into()), &Params::Heapless((2_u32, 3_u32).into()),
)) ))