Compare commits

...

29 Commits

Author SHA1 Message Date
68d20b222a fix no_std build 2025-01-23 17:05:19 +01:00
f202ba59d0 basic test suite for exec helper done 2025-01-23 17:05:19 +01:00
2b01537cfd add more tests 2025-01-23 17:05:19 +01:00
bda47b067d continue seq exec helper tests 2025-01-23 17:05:19 +01:00
4d1dd0aa19 continue tests 2025-01-23 17:05:19 +01:00
245cd6a2c2 continue mode tree 2025-01-23 17:05:19 +01:00
7b1efeb150 more generic components to library 2025-01-23 17:05:19 +01:00
b46c06f1af more tests 2025-01-23 17:05:19 +01:00
b948f9fe2d add more tests 2025-01-23 17:05:19 +01:00
4a2262dbe2 extend tests 2025-01-23 17:05:19 +01:00
fabe52ee0d some docs and helper methods 2025-01-23 17:05:19 +01:00
803f626d51 device manager works 2025-01-23 17:05:19 +01:00
559e040ea9 command propagation works 2025-01-23 17:05:19 +01:00
43f67876c1 continue mode tree 2025-01-23 17:05:19 +01:00
b7662636f5 got an assembly helper now 2025-01-23 17:05:19 +01:00
a326da6ed6 continue mode tree 2025-01-23 17:05:19 +01:00
da6ed5233d introduce forced flag for set mode cmd 2025-01-23 17:05:19 +01:00
7532dd78de continue mode tree execution helper 2025-01-23 17:05:19 +01:00
d5d3577af8 continue mode tree helper 2025-01-23 17:05:19 +01:00
1a211b073f save 2025-01-23 17:05:19 +01:00
3ec5c76733 continue subsystem helper 2025-01-23 17:05:19 +01:00
51e723a45f continue mode tree feature 2025-01-23 17:05:19 +01:00
a27e1e308d generic shananigans 2025-01-23 17:05:19 +01:00
31074dc1aa move generic components to library 2025-01-23 17:05:19 +01:00
d97ae401e5 add testbench 2025-01-23 17:05:19 +01:00
f4624afd41 continue mode tree 2025-01-23 17:05:19 +01:00
d9a20e2d7d continue 2025-01-23 17:05:19 +01:00
c18fbb59ad Merge pull request 're-run formatter' (#212) from run-afmt into main
Reviewed-on: #212
2025-01-23 17:04:58 +01:00
c91ddcd658
re-run formatter 2025-01-23 17:02:16 +01:00
19 changed files with 3714 additions and 553 deletions

3
docs.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --all-features --open

View File

@ -386,6 +386,7 @@ impl<
&mut self,
requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode,
_forced: bool,
) -> Result<(), satrs::mode::ModeError> {
log::info!(
"{}: transitioning to mode {:?}",
@ -575,7 +576,10 @@ mod tests {
.mode_request_tx
.send(GenericMessage::new(
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");
testbench.handler.periodic_operation();
@ -633,7 +637,10 @@ mod tests {
.mode_request_tx
.send(GenericMessage::new(
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");
testbench.handler.periodic_operation();

View File

@ -412,6 +412,7 @@ impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeRequestHandler
&mut self,
requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode,
_forced: bool,
) -> Result<(), satrs::mode::ModeError> {
log::info!(
"{}: transitioning to mode {:?}",
@ -660,7 +661,10 @@ mod tests {
.mode_request_tx
.send(GenericMessage::new(
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");
let switch_map_shared = testbench.handler.shared_switch_map.lock().unwrap();
@ -692,7 +696,10 @@ mod tests {
.mode_request_tx
.send(GenericMessage::new(
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");
testbench

View File

@ -291,7 +291,10 @@ fn static_tmtc_pool_main() {
pcdu_handler_mode_tx
.send(GenericMessage::new(
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");
@ -598,7 +601,10 @@ fn dyn_tmtc_pool_main() {
pcdu_handler_mode_tx
.send(GenericMessage::new(
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");

View File

@ -110,6 +110,7 @@ impl PusReplyHandler<ActivePusRequestStd, ModeReply> for ModeReplyHandler {
),
)?;
}
ModeReply::ModeInfo(_mode_and_submode) => (),
};
Ok(true)
}
@ -190,7 +191,13 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
}
let mode_and_submode = ModeAndSubmode::from_be_bytes(&tc.user_data()[4..])
.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::TcAnnounceMode => Ok((active_request, ModeRequest::AnnounceMode)),
@ -346,7 +353,13 @@ mod tests {
let (_active_req, req) = testbench
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
.expect("conversion has failed");
assert_eq!(req, ModeRequest::SetMode(mode_and_submode));
assert_eq!(
req,
ModeRequest::SetMode {
mode_and_submode,
forced: false
}
);
}
#[test]

156
satrs/src/dev_mgmt.rs Normal file
View File

@ -0,0 +1,156 @@
use crate::{
mode::{ModeAndSubmode, ModeReply, ModeRequest, ModeRequestSender},
mode_tree::{ModeStoreProvider, ModeStoreVec},
queue::GenericTargetedMessagingError,
request::{GenericMessage, RequestId},
subsystem::ModeTreeHelperState,
ComponentId,
};
#[derive(Debug, Default)]
pub enum AssemblyHelperResult {
#[default]
Idle,
TargetKeepingViolation(ComponentId),
ModeCommandingDone,
}
/// A generic helper for manager components which manage child components in a mode tree.
///
/// Mode commands are usually forwarded to all children components transparently.
/// For example, this could be used in an Assembly component which manages multiple redundant
/// child components. It can also be used inside a manager component which only manages one device.
#[derive(Debug, Default)]
pub struct AssemblyCommandingHelper {
/// The IDs, modes and reply awaition status of all children are tracked in this data
/// structure.
pub children_mode_store: ModeStoreVec,
/// Target mode used for mode commanding.
pub target_mode: Option<ModeAndSubmode>,
/// Request ID of active mode commanding request.
pub active_request_id: Option<RequestId>,
pub state: ModeTreeHelperState,
}
pub type DevManagerCommandingHelper = AssemblyCommandingHelper;
impl AssemblyCommandingHelper {
pub fn send_mode_cmd_to_all_children_with_reply_awaition(
&mut self,
request_id: RequestId,
mode_and_submode: ModeAndSubmode,
forced: bool,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericTargetedMessagingError> {
self.target_mode = Some(mode_and_submode);
for child in self.children_mode_store.0.iter_mut() {
mode_req_sender.send_mode_request(
request_id,
child.id(),
ModeRequest::SetMode {
mode_and_submode,
forced,
},
)?;
child.awaiting_reply = true;
}
self.active_request_id = Some(request_id);
self.state = ModeTreeHelperState::ModeCommanding;
Ok(())
}
pub fn send_announce_mode_cmd_to_children(
&self,
request_id: RequestId,
mode_req_sender: &impl ModeRequestSender,
recursive: bool,
) -> Result<(), GenericTargetedMessagingError> {
let mut request = ModeRequest::AnnounceMode;
if recursive {
request = ModeRequest::AnnounceModeRecursive;
}
for child in self.children_mode_store.0.iter() {
mode_req_sender.send_mode_request(request_id, child.id(), request)?;
}
Ok(())
}
pub fn add_mode_child(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
self.children_mode_store.add_component(target_id, mode);
}
pub fn count_number_of_children_with_target_mode(&self) -> Option<usize> {
self.target_mode?;
let target_mode = self.target_mode.unwrap();
let mut children_in_target_mode = 0;
for child in self.children_mode_store.0.iter() {
if child.mode_and_submode() == target_mode {
children_in_target_mode += 1;
}
}
Some(children_in_target_mode)
}
pub fn handle_mode_reply(
&mut self,
mode_reply: &GenericMessage<ModeReply>,
) -> AssemblyHelperResult {
if !self
.children_mode_store
.has_component(mode_reply.sender_id())
{
return AssemblyHelperResult::Idle;
}
let mut generic_mode_reply_handler = |mode_and_submode: Option<ModeAndSubmode>| {
// Tying the reply awaition to the request ID ensures that something like replies
// belonging to older requests do not interfere with the completion handling of
// the mode commanding. This is important for forced mode commands.
let mut handle_awaition = false;
if self.state == ModeTreeHelperState::ModeCommanding
&& self.active_request_id.is_some()
&& mode_reply.request_id() == self.active_request_id.unwrap()
{
handle_awaition = true;
}
let still_awating_replies = self.children_mode_store.mode_reply_handler(
mode_reply.sender_id(),
mode_and_submode,
handle_awaition,
);
if self.state == ModeTreeHelperState::TargetKeeping
&& mode_and_submode.is_some()
&& self.target_mode.is_some()
&& mode_and_submode.unwrap() != self.target_mode.unwrap()
{
return AssemblyHelperResult::TargetKeepingViolation(mode_reply.sender_id());
}
// It is okay to unwrap: If awaition should be handled, the returned value should
// always be some valid value.
if self.state == ModeTreeHelperState::ModeCommanding
&& handle_awaition
&& !still_awating_replies.unwrap()
{
self.state = ModeTreeHelperState::TargetKeeping;
self.active_request_id = None;
return AssemblyHelperResult::ModeCommandingDone;
}
AssemblyHelperResult::Idle
};
match mode_reply.message {
ModeReply::ModeInfo(mode_and_submode) | ModeReply::ModeReply(mode_and_submode) => {
generic_mode_reply_handler(Some(mode_and_submode))
}
ModeReply::CantReachMode(_result_u16) => generic_mode_reply_handler(None),
ModeReply::WrongMode {
expected: _,
reached,
} => generic_mode_reply_handler(Some(reached)),
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_basic() {}
}

View File

@ -22,29 +22,32 @@ extern crate downcast_rs;
#[cfg(any(feature = "std", test))]
extern crate std;
pub mod action;
#[cfg(feature = "alloc")]
pub mod dev_mgmt;
pub mod encoding;
pub mod event_man;
pub mod events;
#[cfg(feature = "std")]
pub mod executable;
pub mod hal;
pub mod hk;
pub mod mode;
#[cfg(feature = "std")]
pub mod mode_tree;
pub mod params;
pub mod pool;
pub mod power;
pub mod pus;
pub mod queue;
pub mod request;
pub mod res_code;
pub mod time;
pub mod tmtc;
#[cfg(feature = "alloc")]
pub mod scheduling;
pub mod action;
pub mod hk;
pub mod mode;
pub mod params;
#[cfg(feature = "alloc")]
pub mod subsystem;
pub mod time;
pub mod tmtc;
pub use spacepackets;

View File

@ -12,7 +12,9 @@ pub use std_mod::*;
use crate::{
queue::GenericTargetedMessagingError,
request::{GenericMessage, MessageMetadata, MessageReceiver, MessageReceiverWithId, RequestId},
request::{
GenericMessage, MessageMetadata, MessageReceiverProvider, MessageReceiverWithId, RequestId,
},
ComponentId,
};
@ -26,6 +28,9 @@ pub struct ModeAndSubmode {
submode: Submode,
}
pub const INVALID_MODE: ModeAndSubmode = ModeAndSubmode::new(u32::MAX, 0);
pub const UNKNOWN_MODE: ModeAndSubmode = ModeAndSubmode::new(u32::MAX - 1, 0);
impl ModeAndSubmode {
pub const RAW_LEN: usize = size_of::<Mode>() + size_of::<Submode>();
@ -111,7 +116,10 @@ impl TargetedModeCommand {
pub enum ModeRequest {
/// Mode information. Can be used to notify other components of changed modes.
ModeInfo(ModeAndSubmode),
SetMode(ModeAndSubmode),
SetMode {
mode_and_submode: ModeAndSubmode,
forced: bool,
},
ReadMode,
AnnounceMode,
AnnounceModeRecursive,
@ -127,6 +135,8 @@ pub struct TargetedModeRequest {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ModeReply {
/// 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),
// Can not reach the commanded mode. Contains a reason as a [ResultU16].
@ -156,7 +166,7 @@ pub trait ModeRequestReceiver {
) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError>;
}
impl<R: MessageReceiver<ModeRequest>> ModeRequestReceiver
impl<R: MessageReceiverProvider<ModeRequest>> ModeRequestReceiver
for MessageReceiverWithId<ModeRequest, R>
{
fn try_recv_mode_request(
@ -166,15 +176,12 @@ impl<R: MessageReceiver<ModeRequest>> ModeRequestReceiver
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, thiserror::Error)]
pub enum ModeError {
Messaging(GenericTargetedMessagingError),
}
impl From<GenericTargetedMessagingError> for ModeError {
fn from(value: GenericTargetedMessagingError) -> Self {
Self::Messaging(value)
}
#[error("Messaging error: {0}")]
Messaging(#[from] GenericTargetedMessagingError),
#[error("busy with other mode request")]
Busy,
}
pub trait ModeProvider {
@ -196,6 +203,7 @@ pub trait ModeRequestHandler: ModeProvider {
&mut self,
requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode,
forced: bool,
) -> Result<(), Self::Error>;
fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool);
@ -222,9 +230,10 @@ pub trait ModeRequestHandler: ModeProvider {
request: GenericMessage<ModeRequest>,
) -> Result<(), Self::Error> {
match request.message {
ModeRequest::SetMode(mode_and_submode) => {
self.start_transition(request.requestor_info, mode_and_submode)
}
ModeRequest::SetMode {
mode_and_submode,
forced,
} => self.start_transition(request.requestor_info, mode_and_submode, forced),
ModeRequest::ReadMode => self.send_mode_reply(
request.requestor_info,
ModeReply::ModeReply(self.mode_and_submode()),
@ -248,7 +257,9 @@ pub trait ModeReplyReceiver {
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError>;
}
impl<R: MessageReceiver<ModeReply>> ModeReplyReceiver for MessageReceiverWithId<ModeReply, R> {
impl<R: MessageReceiverProvider<ModeReply>> ModeReplyReceiver
for MessageReceiverWithId<ModeReply, R>
{
fn try_recv_mode_reply(
&self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
@ -270,12 +281,13 @@ pub trait ModeReplySender {
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use crate::request::{
MessageSender, MessageSenderAndReceiver, MessageSenderMap, RequestAndReplySenderAndReceiver,
MessageSenderAndReceiver, MessageSenderMap, MessageSenderProvider,
MessageSenderStoreProvider, RequestAndReplySenderAndReceiver,
};
use super::*;
impl<S: MessageSender<ModeReply>> MessageSenderMap<ModeReply, S> {
impl<S: MessageSenderProvider<ModeReply>> MessageSenderMap<ModeReply, S> {
pub fn send_mode_reply(
&self,
requestor_info: MessageMetadata,
@ -290,8 +302,13 @@ pub mod alloc_mod {
}
}
impl<FROM, S: MessageSender<ModeReply>, R: MessageReceiver<FROM>> ModeReplySender
for MessageSenderAndReceiver<ModeReply, FROM, S, R>
impl<
From,
Sender: MessageSenderProvider<ModeReply>,
Receiver: MessageReceiverProvider<From>,
SenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
> ModeReplySender
for MessageSenderAndReceiver<ModeReply, From, Sender, Receiver, SenderStore>
{
fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic()
@ -302,7 +319,7 @@ pub mod alloc_mod {
requestor_info: MessageMetadata,
request: ModeReply,
) -> Result<(), GenericTargetedMessagingError> {
self.message_sender_map.send_mode_reply(
self.message_sender_store.send_message(
MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()),
requestor_info.sender_id(),
request,
@ -310,8 +327,13 @@ pub mod alloc_mod {
}
}
impl<TO, S: MessageSender<TO>, R: MessageReceiver<ModeReply>> ModeReplyReceiver
for MessageSenderAndReceiver<TO, ModeReply, S, R>
impl<
To,
Sender: MessageSenderProvider<To>,
Receiver: MessageReceiverProvider<ModeReply>,
SenderStore: MessageSenderStoreProvider<To, Sender>,
> ModeReplyReceiver
for MessageSenderAndReceiver<To, ModeReply, Sender, Receiver, SenderStore>
{
fn try_recv_mode_reply(
&self,
@ -321,26 +343,51 @@ pub mod alloc_mod {
}
impl<
REQUEST,
S0: MessageSender<REQUEST>,
R0: MessageReceiver<ModeReply>,
S1: MessageSender<ModeReply>,
R1: MessageReceiver<REQUEST>,
> RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
Request,
ReqSender: MessageSenderProvider<Request>,
ReqReceiver: MessageReceiverProvider<Request>,
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
Reply,
ReplySender: MessageSenderProvider<Reply>,
ReplyReceiver: MessageReceiverProvider<Reply>,
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
>
RequestAndReplySenderAndReceiver<
Request,
ReqSender,
ReqReceiver,
ReqSenderStore,
Reply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{
pub fn add_reply_target(&mut self, target_id: ComponentId, reply_sender: S1) {
self.reply_sender_map
pub fn add_reply_target(&mut self, target_id: ComponentId, reply_sender: ReplySender) {
self.reply_sender_store
.add_message_target(target_id, reply_sender)
}
}
impl<
REQUEST,
S0: MessageSender<REQUEST>,
R0: MessageReceiver<ModeReply>,
S1: MessageSender<ModeReply>,
R1: MessageReceiver<REQUEST>,
> ModeReplySender for RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
Request,
ReqSender: MessageSenderProvider<Request>,
ReqReceiver: MessageReceiverProvider<Request>,
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
ReplySender: MessageSenderProvider<ModeReply>,
ReplyReceiver: MessageReceiverProvider<ModeReply>,
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
> ModeReplySender
for RequestAndReplySenderAndReceiver<
Request,
ReqSender,
ReqReceiver,
ReqSenderStore,
ModeReply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{
fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic()
@ -349,24 +396,35 @@ pub mod alloc_mod {
fn send_mode_reply(
&self,
requestor_info: MessageMetadata,
request: ModeReply,
reply: ModeReply,
) -> Result<(), GenericTargetedMessagingError> {
self.reply_sender_map.send_mode_reply(
self.reply_sender_store.send_message(
MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()),
requestor_info.sender_id(),
request,
reply,
)
}
}
impl<
REQUEST,
S0: MessageSender<REQUEST>,
R0: MessageReceiver<ModeReply>,
S1: MessageSender<ModeReply>,
R1: MessageReceiver<REQUEST>,
Request,
ReqSender: MessageSenderProvider<Request>,
ReqReceiver: MessageReceiverProvider<Request>,
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
ReplySender: MessageSenderProvider<ModeReply>,
ReplyReceiver: MessageReceiverProvider<ModeReply>,
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
> ModeReplyReceiver
for RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
for RequestAndReplySenderAndReceiver<
Request,
ReqSender,
ReqReceiver,
ReqSenderStore,
ModeReply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{
fn try_recv_mode_reply(
&self,
@ -376,11 +434,14 @@ pub mod alloc_mod {
}
/// Helper type definition for a mode handler which can handle mode requests.
pub type ModeRequestHandlerInterface<S, R> =
MessageSenderAndReceiver<ModeReply, ModeRequest, S, R>;
pub type ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore> =
MessageSenderAndReceiver<ModeReply, ModeRequest, Sender, Receiver, ReplySenderStore>;
impl<S: MessageSender<ModeReply>, R: MessageReceiver<ModeRequest>>
ModeRequestHandlerInterface<S, R>
impl<
Sender: MessageSenderProvider<ModeReply>,
Receiver: MessageReceiverProvider<ModeRequest>,
ReplySenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
> ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore>
{
pub fn try_recv_mode_request(
&self,
@ -403,9 +464,15 @@ pub mod alloc_mod {
/// Helper type defintion for a mode handler object which can send mode requests and receive
/// mode replies.
pub type ModeRequestorInterface<S, R> = MessageSenderAndReceiver<ModeRequest, ModeReply, S, R>;
pub type ModeRequestorInterface<Sender, Receiver, RequestSenderStore> =
MessageSenderAndReceiver<ModeRequest, ModeReply, Sender, Receiver, RequestSenderStore>;
impl<S: MessageSender<ModeRequest>, R: MessageReceiver<ModeReply>> ModeRequestorInterface<S, R> {
impl<
Sender: MessageSenderProvider<ModeRequest>,
Receiver: MessageReceiverProvider<ModeReply>,
RequestSenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
> ModeRequestorInterface<Sender, Receiver, RequestSenderStore>
{
pub fn try_recv_mode_reply(
&self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
@ -424,10 +491,25 @@ pub mod alloc_mod {
/// Helper type defintion for a mode handler object which can both send mode requests and
/// process mode requests.
pub type ModeInterface<S0, R0, S1, R1> =
RequestAndReplySenderAndReceiver<ModeRequest, ModeReply, S0, R0, S1, R1>;
pub type ModeInterface<
ReqSender,
ReqReceiver,
ReqSenderStore,
ReplySender,
ReplyReceiver,
ReplySenderStore,
> = RequestAndReplySenderAndReceiver<
ModeRequest,
ReqSender,
ReqReceiver,
ReqSenderStore,
ModeReply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>;
impl<S: MessageSender<ModeRequest>> MessageSenderMap<ModeRequest, S> {
impl<S: MessageSenderProvider<ModeRequest>> MessageSenderMap<ModeRequest, S> {
pub fn send_mode_request(
&self,
requestor_info: MessageMetadata,
@ -442,25 +524,13 @@ pub mod alloc_mod {
}
}
/*
impl<S: MessageSender<ModeRequest>> ModeRequestSender for MessageSenderMapWithId<ModeRequest, S> {
fn local_channel_id(&self) -> ComponentId {
self.local_channel_id
}
fn send_mode_request(
&self,
request_id: RequestId,
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.send_message(request_id, target_id, request)
}
}
*/
impl<TO, S: MessageSender<TO>, R: MessageReceiver<ModeRequest>> ModeRequestReceiver
for MessageSenderAndReceiver<TO, ModeRequest, S, R>
impl<
To,
Sender: MessageSenderProvider<To>,
Receiver: MessageReceiverProvider<ModeRequest>,
SenderStore: MessageSenderStoreProvider<To, Sender>,
> ModeRequestReceiver
for MessageSenderAndReceiver<To, ModeRequest, Sender, Receiver, SenderStore>
{
fn try_recv_mode_request(
&self,
@ -469,8 +539,13 @@ pub mod alloc_mod {
}
}
impl<FROM, S: MessageSender<ModeRequest>, R: MessageReceiver<FROM>> ModeRequestSender
for MessageSenderAndReceiver<ModeRequest, FROM, S, R>
impl<
From,
Sender: MessageSenderProvider<ModeRequest>,
Receiver: MessageReceiverProvider<From>,
SenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
> ModeRequestSender
for MessageSenderAndReceiver<ModeRequest, From, Sender, Receiver, SenderStore>
{
fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic()
@ -482,7 +557,7 @@ pub mod alloc_mod {
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.message_sender_map.send_mode_request(
self.message_sender_store.send_message(
MessageMetadata::new(request_id, self.local_channel_id()),
target_id,
request,
@ -491,27 +566,50 @@ pub mod alloc_mod {
}
impl<
REPLY,
S0: MessageSender<ModeRequest>,
R0: MessageReceiver<REPLY>,
S1: MessageSender<REPLY>,
R1: MessageReceiver<ModeRequest>,
> RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
ReqSender: MessageSenderProvider<ModeRequest>,
ReqReceiver: MessageReceiverProvider<ModeRequest>,
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
Reply,
ReplySender: MessageSenderProvider<Reply>,
ReplyReceiver: MessageReceiverProvider<Reply>,
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
>
RequestAndReplySenderAndReceiver<
ModeRequest,
ReqSender,
ReqReceiver,
ReqSenderStore,
Reply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: S0) {
self.request_sender_map
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: ReqSender) {
self.request_sender_store
.add_message_target(target_id, request_sender)
}
}
impl<
REPLY,
S0: MessageSender<ModeRequest>,
R0: MessageReceiver<REPLY>,
S1: MessageSender<REPLY>,
R1: MessageReceiver<ModeRequest>,
ReqSender: MessageSenderProvider<ModeRequest>,
ReqReceiver: MessageReceiverProvider<ModeRequest>,
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
Reply,
ReplySender: MessageSenderProvider<Reply>,
ReplyReceiver: MessageReceiverProvider<Reply>,
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
> ModeRequestSender
for RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
for RequestAndReplySenderAndReceiver<
ModeRequest,
ReqSender,
ReqReceiver,
ReqSenderStore,
Reply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{
fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic()
@ -523,7 +621,7 @@ pub mod alloc_mod {
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.request_sender_map.send_mode_request(
self.request_sender_store.send_message(
MessageMetadata::new(request_id, self.local_channel_id()),
target_id,
request,
@ -532,13 +630,24 @@ pub mod alloc_mod {
}
impl<
REPLY,
S0: MessageSender<ModeRequest>,
R0: MessageReceiver<REPLY>,
S1: MessageSender<REPLY>,
R1: MessageReceiver<ModeRequest>,
ReqSender: MessageSenderProvider<ModeRequest>,
ReqReceiver: MessageReceiverProvider<ModeRequest>,
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
Reply,
ReplySender: MessageSenderProvider<Reply>,
ReplyReceiver: MessageReceiverProvider<Reply>,
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
> ModeRequestReceiver
for RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
for RequestAndReplySenderAndReceiver<
ModeRequest,
ReqSender,
ReqReceiver,
ReqSenderStore,
Reply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{
fn try_recv_mode_request(
&self,
@ -552,37 +661,57 @@ pub mod alloc_mod {
pub mod std_mod {
use std::sync::mpsc;
use crate::request::{MessageSenderList, OneMessageSender};
use super::*;
pub type ModeRequestHandlerMpsc = ModeRequestHandlerInterface<
mpsc::Sender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
MessageSenderList<ModeReply, mpsc::Sender<GenericMessage<ModeReply>>>,
>;
pub type ModeRequestHandlerMpscBounded = ModeRequestHandlerInterface<
mpsc::SyncSender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
MessageSenderList<ModeReply, mpsc::SyncSender<GenericMessage<ModeReply>>>,
>;
pub type ModeRequestorMpsc = ModeRequestorInterface<
pub type ModeRequestorOneChildMpsc = ModeRequestorInterface<
mpsc::Sender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
OneMessageSender<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
>;
pub type ModeRequestorBoundedMpsc = ModeRequestorInterface<
pub type ModeRequestorOneChildBoundedMpsc = ModeRequestorInterface<
mpsc::SyncSender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
OneMessageSender<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
>;
pub type ModeRequestorChildListMpsc = ModeRequestorInterface<
mpsc::Sender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
MessageSenderList<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
>;
pub type ModeRequestorChildListBoundedMpsc = ModeRequestorInterface<
mpsc::SyncSender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
MessageSenderList<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
>;
pub type ModeRequestorAndHandlerMpsc = ModeInterface<
mpsc::Sender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
mpsc::Sender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
MessageSenderList<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
mpsc::Sender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
MessageSenderList<ModeReply, mpsc::Sender<GenericMessage<ModeReply>>>,
>;
pub type ModeRequestorAndHandlerMpscBounded = ModeInterface<
mpsc::SyncSender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
mpsc::SyncSender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
MessageSenderList<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
mpsc::SyncSender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
MessageSenderList<ModeReply, mpsc::SyncSender<GenericMessage<ModeReply>>>,
>;
}

View File

@ -2,10 +2,57 @@ use alloc::vec::Vec;
use hashbrown::HashMap;
use crate::{
mode::{Mode, ModeAndSubmode, Submode},
mode::{Mode, ModeAndSubmode, ModeReply, ModeRequest, Submode},
request::MessageSenderProvider,
ComponentId,
};
#[cfg(feature = "alloc")]
pub use alloc_mod::*;
/// Common trait for node modes which can have mode parents or mode children.
pub trait ModeNode {
fn id(&self) -> ComponentId;
}
/// Trait which denotes that an object is a parent in a mode tree.
///
/// A mode parent is capable of sending mode requests to child objects and has a unique component
/// ID.
pub trait ModeParent: ModeNode {
type Sender: MessageSenderProvider<ModeRequest>;
fn add_mode_child(&mut self, id: ComponentId, request_sender: Self::Sender);
}
/// Trait which denotes that an object is a child in a mode tree.
///
/// A child is capable of sending mode replies to parent objects and has a unique component ID.
pub trait ModeChild: ModeNode {
type Sender: MessageSenderProvider<ModeReply>;
fn add_mode_parent(&mut self, id: ComponentId, reply_sender: Self::Sender);
}
/// Utility method which connects a mode tree parent object to a child object by calling
/// [ModeParent::add_mode_child] on the [parent][ModeParent] and calling
/// [ModeChild::add_mode_parent] on the [child][ModeChild].
///
/// # Arguments
///
/// * `parent` - The parent object which implements [ModeParent].
/// * `request_sender` - Sender object to send mode requests to the child.
/// * `child` - The child object which implements [ModeChild].
/// * `reply_sender` - Sender object to send mode replies to the parent.
pub fn connect_mode_nodes<ReqSender, ReplySender>(
parent: &mut impl ModeParent<Sender = ReqSender>,
request_sender: ReqSender,
child: &mut impl ModeChild<Sender = ReplySender>,
reply_sender: ReplySender,
) {
parent.add_mode_child(child.id(), request_sender);
child.add_mode_parent(parent.id(), reply_sender);
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TableEntryType {
/// Target table containing information of the expected children modes for given mode.
@ -15,23 +62,508 @@ pub enum TableEntryType {
Sequence,
}
pub struct ModeTableEntry {
/// Common fields required for both target and sequence table entries.
///
/// The most important parameters here are the target ID which this entry belongs to, and the mode
/// and submode the entry either will be commanded to for sequence table entries or which will be
/// monitored for target table entries.
#[derive(Debug, Copy, Clone)]
pub struct ModeTableEntryCommon {
/// Name of respective table entry.
pub name: &'static str,
/// Target channel ID.
pub channel_id: ComponentId,
/// Target component ID.
pub target_id: ComponentId,
/// Has a different meaning depending on whether this is a sequence table or a target table.
///
/// - For sequence tables, this denotes the mode which will be commanded
/// - For target tables, this is the mode which the target children should have and which
/// might be monitored depending on configuration.
pub mode_submode: ModeAndSubmode,
/// This mask allows to specify multiple allowed submodes for a given mode.
pub allowed_submode_mask: Option<Submode>,
}
impl ModeTableEntryCommon {
pub fn set_allowed_submode_mask(&mut self, mask: Submode) {
self.allowed_submode_mask = Some(mask);
}
pub fn allowed_submode_mask(&self) -> Option<Submode> {
self.allowed_submode_mask
}
}
/// An entry for the target tables.
#[derive(Debug)]
pub struct TargetTableEntry {
pub common: ModeTableEntryCommon,
pub monitor_state: bool,
}
impl TargetTableEntry {
pub fn new(
name: &'static str,
target_id: ComponentId,
mode_submode: ModeAndSubmode,
allowed_submode_mask: Option<Submode>,
) -> Self {
Self {
common: ModeTableEntryCommon {
name,
target_id,
mode_submode,
allowed_submode_mask,
},
monitor_state: true,
}
}
pub fn new_with_precise_submode(
name: &'static str,
target_id: ComponentId,
mode_submode: ModeAndSubmode,
) -> Self {
Self {
common: ModeTableEntryCommon {
name,
target_id,
mode_submode,
allowed_submode_mask: None,
},
monitor_state: true,
}
}
delegate::delegate! {
to self.common {
pub fn set_allowed_submode_mask(&mut self, mask: Submode);
pub fn allowed_submode_mask(&self) -> Option<Submode>;
}
}
}
/// An entry for the sequence tables.
///
/// The [Self::check_success] field specifies that a mode sequence executor should check that the
/// target mode was actually reached before executing the next sequence.
#[derive(Debug)]
pub struct SequenceTableEntry {
pub common: ModeTableEntryCommon,
pub check_success: bool,
}
pub struct ModeTableMapValue {
/// Name for a given mode table entry.
pub name: &'static str,
pub entries: Vec<ModeTableEntry>,
impl SequenceTableEntry {
pub fn new(
name: &'static str,
target_id: ComponentId,
mode_submode: ModeAndSubmode,
check_success: bool,
) -> Self {
Self {
common: ModeTableEntryCommon {
name,
target_id,
mode_submode,
allowed_submode_mask: None,
},
check_success,
}
}
delegate::delegate! {
to self.common {
pub fn set_allowed_submode_mask(&mut self, mask: Submode);
pub fn allowed_submode_mask(&self) -> Option<Submode>;
}
}
}
pub type ModeTable = HashMap<Mode, ModeTableMapValue>;
#[derive(Debug, thiserror::Error)]
#[error("target {0} not in mode store")]
pub struct TargetNotInModeStoreError(pub ComponentId);
/// Mode store value type.
#[derive(Debug, Copy, Clone)]
pub struct ModeStoreValue {
/// ID of the mode component.
id: ComponentId,
/// Current mode and submode of the component.
pub mode_and_submode: ModeAndSubmode,
/// State information to track whether a reply should be awaited for the mode component.
pub awaiting_reply: bool,
}
impl ModeStoreValue {
pub fn new(id: ComponentId, mode_and_submode: ModeAndSubmode) -> Self {
Self {
id,
mode_and_submode,
awaiting_reply: false,
}
}
pub fn id(&self) -> ComponentId {
self.id
}
pub fn mode_and_submode(&self) -> ModeAndSubmode {
self.mode_and_submode
}
}
pub trait ModeStoreProvider {
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode);
fn has_component(&self, target_id: ComponentId) -> bool;
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue>;
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue>;
/// Generic handler for mode replies received from child components.
///
/// Implementation should clear the awaition flag if the `handle_reply_awaition` argument is
/// true and returns whether any children are still awaiting replies. If the flag is not set
fn mode_reply_handler_with_reply_awaition(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
) -> bool {
self.mode_reply_handler(sender_id, reported_mode_and_submode, true)
.unwrap_or(false)
}
fn mode_reply_handler_without_reply_awaition(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
) {
self.mode_reply_handler(sender_id, reported_mode_and_submode, false);
}
fn mode_reply_handler(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
with_reply_awaition: bool,
) -> Option<bool>;
}
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use super::*;
#[derive(Debug)]
pub struct TargetTablesMapValue {
/// Name for a given mode table entry.
pub name: &'static str,
/// Optional fallback mode if the target mode can not be kept.
pub fallback_mode: Option<Mode>,
/// These are the rows of the a target table.
pub entries: Vec<TargetTableEntry>,
}
impl TargetTablesMapValue {
pub fn new(name: &'static str, fallback_mode: Option<Mode>) -> Self {
Self {
name,
fallback_mode,
entries: Default::default(),
}
}
pub fn add_entry(&mut self, entry: TargetTableEntry) {
self.entries.push(entry);
}
}
/// One sequence of a [SequenceTablesMapValue] in a [SequenceModeTables].
///
/// It contains all mode requests which need to be executed for a sequence step and it also
/// associates a [Self::name] with the sequence.
#[derive(Debug)]
pub struct SequenceTableMapTable {
/// Name for a given mode sequence.
pub name: &'static str,
/// These are the rows of the a sequence table.
pub entries: Vec<SequenceTableEntry>,
}
impl SequenceTableMapTable {
pub fn new(name: &'static str) -> Self {
Self {
name,
entries: Default::default(),
}
}
pub fn add_entry(&mut self, entry: SequenceTableEntry) {
self.entries.push(entry);
}
}
/// A sequence table entry.
///
/// This is simply a list of [SequenceTableMapTable]s which also associates a [Self::name]
/// with the sequence. The order of sub-tables in the list also specifies the execution order
/// in the mode sequence.
#[derive(Debug)]
pub struct SequenceTablesMapValue {
/// Name for a given mode sequence.
pub name: &'static str,
/// Each sequence can consists of multiple sequences that are executed consecutively.
pub entries: Vec<SequenceTableMapTable>,
}
impl SequenceTablesMapValue {
pub fn new(name: &'static str) -> Self {
Self {
name,
entries: Default::default(),
}
}
pub fn add_sequence_table(&mut self, entry: SequenceTableMapTable) {
self.entries.push(entry);
}
}
#[derive(Debug, Default)]
pub struct TargetModeTables(pub HashMap<Mode, TargetTablesMapValue>);
/// This is the core data structure used to store mode sequence tables.
///
/// A mode sequence table specifies which commands have to be sent in which order
/// to reach a certain [Mode]. Therefore, it simply maps a [Mode] to a [SequenceTablesMapValue].
#[derive(Debug, Default)]
pub struct SequenceModeTables(pub HashMap<Mode, SequenceTablesMapValue>);
/// Mode store which tracks the [mode information][ModeStoreValue] inside a [Vec]
#[derive(Debug, Default)]
pub struct ModeStoreVec(pub alloc::vec::Vec<ModeStoreValue>);
/// Mode store which tracks the mode information inside a [hashbrown::HashMap]
#[derive(Debug, Default)]
pub struct ModeStoreMap(pub hashbrown::HashMap<ComponentId, ModeStoreValue>);
impl ModeStoreProvider for ModeStoreVec {
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
self.0.push(ModeStoreValue::new(target_id, mode));
}
fn has_component(&self, target_id: ComponentId) -> bool {
self.0.iter().any(|val| val.id == target_id)
}
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue> {
self.0.iter().find(|val| val.id == target_id)
}
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue> {
self.0.iter_mut().find(|val| val.id == target_id)
}
fn mode_reply_handler(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
handle_reply_awaition: bool,
) -> Option<bool> {
let mut still_awating_replies = None;
if handle_reply_awaition {
still_awating_replies = Some(false);
}
self.0.iter_mut().for_each(|val| {
if val.id() == sender_id {
if let Some(mode_and_submode) = reported_mode_and_submode {
val.mode_and_submode = mode_and_submode;
}
if handle_reply_awaition {
val.awaiting_reply = false;
}
}
if handle_reply_awaition && val.awaiting_reply {
still_awating_replies = Some(true);
}
});
still_awating_replies
}
}
impl ModeStoreProvider for ModeStoreMap {
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
self.0
.insert(target_id, ModeStoreValue::new(target_id, mode));
}
fn has_component(&self, target_id: ComponentId) -> bool {
self.0.contains_key(&target_id)
}
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue> {
self.0.get(&target_id)
}
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue> {
self.0.get_mut(&target_id)
}
fn mode_reply_handler(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
handle_reply_awaition: bool,
) -> Option<bool> {
let mut still_awating_replies = None;
if handle_reply_awaition {
still_awating_replies = Some(false);
}
for val in self.0.values_mut() {
if val.id() == sender_id {
if let Some(mode_and_submode) = reported_mode_and_submode {
val.mode_and_submode = mode_and_submode;
}
if handle_reply_awaition {
val.awaiting_reply = false;
}
}
if handle_reply_awaition && val.awaiting_reply {
still_awating_replies = Some(true);
}
}
still_awating_replies
}
}
}
#[cfg(test)]
mod tests {}
mod tests {
use super::*;
fn generic_test(mode_store: &mut impl ModeStoreProvider) {
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
assert!(mode_store.has_component(1));
assert!(mode_store.has_component(2));
assert_eq!(
mode_store.get(1).unwrap().mode_and_submode(),
ModeAndSubmode::new(0, 0)
);
assert!(!mode_store.get(1).unwrap().awaiting_reply);
assert!(!mode_store.get(2).unwrap().awaiting_reply);
assert_eq!(mode_store.get(1).unwrap().id, 1);
assert_eq!(mode_store.get(2).unwrap().id, 2);
assert!(mode_store.get(3).is_none());
assert!(mode_store.get_mut(3).is_none());
}
fn generic_reply_handling_with_reply_awaition(mode_store: &mut impl ModeStoreProvider) {
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
mode_store.get_mut(1).unwrap().awaiting_reply = true;
mode_store.get_mut(2).unwrap().awaiting_reply = true;
let mut reply_awation_pending =
mode_store.mode_reply_handler_with_reply_awaition(1, Some(ModeAndSubmode::new(2, 0)));
assert!(reply_awation_pending);
reply_awation_pending = mode_store.mode_reply_handler_with_reply_awaition(2, None);
assert!(!reply_awation_pending);
assert!(!mode_store.get(1).unwrap().awaiting_reply);
assert!(!mode_store.get(2).unwrap().awaiting_reply);
assert_eq!(
mode_store.get(1).unwrap().mode_and_submode(),
ModeAndSubmode::new(2, 0)
);
assert_eq!(
mode_store.get(2).unwrap().mode_and_submode(),
ModeAndSubmode::new(1, 0)
);
}
fn generic_reply_handling_test_no_reply_awaition(mode_store: &mut impl ModeStoreProvider) {
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
mode_store.get_mut(1).unwrap().awaiting_reply = true;
mode_store.get_mut(2).unwrap().awaiting_reply = true;
mode_store.mode_reply_handler_without_reply_awaition(1, Some(ModeAndSubmode::new(2, 0)));
mode_store.mode_reply_handler_without_reply_awaition(2, None);
assert!(mode_store.get(1).unwrap().awaiting_reply);
assert!(mode_store.get(2).unwrap().awaiting_reply);
assert_eq!(
mode_store.get(1).unwrap().mode_and_submode(),
ModeAndSubmode::new(2, 0)
);
assert_eq!(
mode_store.get(2).unwrap().mode_and_submode(),
ModeAndSubmode::new(1, 0)
);
}
fn generic_reply_handling_with_reply_awaition_2(mode_store: &mut impl ModeStoreProvider) {
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
mode_store.get_mut(1).unwrap().awaiting_reply = true;
mode_store.get_mut(2).unwrap().awaiting_reply = true;
let mut reply_awation_pending =
mode_store.mode_reply_handler(1, Some(ModeAndSubmode::new(2, 0)), true);
assert!(reply_awation_pending.unwrap());
reply_awation_pending = mode_store.mode_reply_handler(2, None, true);
assert!(!reply_awation_pending.unwrap());
assert!(!mode_store.get(1).unwrap().awaiting_reply);
assert!(!mode_store.get(2).unwrap().awaiting_reply);
assert_eq!(
mode_store.get(1).unwrap().mode_and_submode(),
ModeAndSubmode::new(2, 0)
);
assert_eq!(
mode_store.get(2).unwrap().mode_and_submode(),
ModeAndSubmode::new(1, 0)
);
}
#[test]
fn test_vec_mode_store() {
let mut mode_store = ModeStoreVec::default();
generic_test(&mut mode_store);
}
#[test]
fn test_map_mode_store() {
let mut mode_store = ModeStoreMap::default();
generic_test(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_vec_with_reply_awaition() {
let mut mode_store = ModeStoreVec::default();
generic_reply_handling_with_reply_awaition(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_vec_with_reply_awaition_2() {
let mut mode_store = ModeStoreVec::default();
generic_reply_handling_with_reply_awaition_2(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_map_with_reply_awaition() {
let mut mode_store = ModeStoreMap::default();
generic_reply_handling_with_reply_awaition(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_map_with_reply_awaition_2() {
let mut mode_store = ModeStoreMap::default();
generic_reply_handling_with_reply_awaition_2(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_vec_no_reply_awaition() {
let mut mode_store = ModeStoreVec::default();
generic_reply_handling_test_no_reply_awaition(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_map_no_reply_awaition() {
let mut mode_store = ModeStoreMap::default();
generic_reply_handling_test_no_reply_awaition(&mut mode_store);
}
}

View File

@ -68,7 +68,8 @@ pub mod alloc_mod {
action::ActionRequest,
queue::GenericTargetedMessagingError,
request::{
GenericMessage, MessageReceiver, MessageSender, MessageSenderAndReceiver, RequestId,
GenericMessage, MessageReceiverProvider, MessageSenderAndReceiver,
MessageSenderProvider, MessageSenderStoreProvider, RequestId,
},
ComponentId,
};
@ -76,11 +77,14 @@ pub mod alloc_mod {
use super::ActionReplyPus;
/// Helper type definition for a mode handler which can handle mode requests.
pub type ActionRequestHandlerInterface<S, R> =
MessageSenderAndReceiver<ActionReplyPus, ActionRequest, S, R>;
pub type ActionRequestHandlerInterface<Sender, Receiver, ReplySenderStore> =
MessageSenderAndReceiver<ActionReplyPus, ActionRequest, Sender, Receiver, ReplySenderStore>;
impl<S: MessageSender<ActionReplyPus>, R: MessageReceiver<ActionRequest>>
ActionRequestHandlerInterface<S, R>
impl<
Sender: MessageSenderProvider<ActionReplyPus>,
Receiver: MessageReceiverProvider<ActionRequest>,
ReplySender: MessageSenderStoreProvider<ActionReplyPus, Sender>,
> ActionRequestHandlerInterface<Sender, Receiver, ReplySender>
{
pub fn try_recv_action_request(
&self,
@ -100,11 +104,20 @@ pub mod alloc_mod {
/// Helper type defintion for a mode handler object which can send mode requests and receive
/// mode replies.
pub type ActionRequestorInterface<S, R> =
MessageSenderAndReceiver<ActionRequest, ActionReplyPus, S, R>;
pub type ActionRequestorInterface<Sender, Receiver, RequestSenderStore> =
MessageSenderAndReceiver<
ActionRequest,
ActionReplyPus,
Sender,
Receiver,
RequestSenderStore,
>;
impl<S: MessageSender<ActionRequest>, R: MessageReceiver<ActionReplyPus>>
ActionRequestorInterface<S, R>
impl<
Sender: MessageSenderProvider<ActionRequest>,
Receiver: MessageReceiverProvider<ActionReplyPus>,
RequestSenderStore: MessageSenderStoreProvider<ActionRequest, Sender>,
> ActionRequestorInterface<Sender, Receiver, RequestSenderStore>
{
pub fn try_recv_action_reply(
&self,
@ -132,6 +145,7 @@ pub mod std_mod {
verification::{self, TcStateToken},
ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap,
},
request::{MessageSenderMap, OneMessageSender},
ComponentId,
};
@ -174,22 +188,38 @@ pub mod std_mod {
}
pub type DefaultActiveActionRequestMap = DefaultActiveRequestMap<ActivePusActionRequestStd>;
pub type ActionRequestHandlerMpsc = ActionRequestHandlerInterface<
pub type ActionRequestHandlerOneSenderMpsc = ActionRequestHandlerInterface<
mpsc::Sender<GenericMessage<ActionReplyPus>>,
mpsc::Receiver<GenericMessage<ActionRequest>>,
OneMessageSender<
GenericMessage<ActionReplyPus>,
mpsc::Sender<GenericMessage<ActionReplyPus>>,
>,
>;
pub type ActionRequestHandlerMpscBounded = ActionRequestHandlerInterface<
pub type ActionRequestHandlerOneSenderMpscBounded = ActionRequestHandlerInterface<
mpsc::SyncSender<GenericMessage<ActionReplyPus>>,
mpsc::Receiver<GenericMessage<ActionRequest>>,
OneMessageSender<
GenericMessage<ActionReplyPus>,
mpsc::SyncSender<GenericMessage<ActionReplyPus>>,
>,
>;
pub type ActionRequestorMpsc = ActionRequestorInterface<
pub type ActionRequestorWithSenderMapMpsc = ActionRequestorInterface<
mpsc::Sender<GenericMessage<ActionRequest>>,
mpsc::Receiver<GenericMessage<ActionReplyPus>>,
MessageSenderMap<
GenericMessage<ActionRequest>,
mpsc::Sender<GenericMessage<ActionRequest>>,
>,
>;
pub type ActionRequestorBoundedMpsc = ActionRequestorInterface<
pub type ActionRequestorWithSenderMapBoundedMpsc = ActionRequestorInterface<
mpsc::SyncSender<GenericMessage<ActionRequest>>,
mpsc::Receiver<GenericMessage<ActionReplyPus>>,
MessageSenderMap<
GenericMessage<ActionRequest>,
mpsc::SyncSender<GenericMessage<ActionRequest>>,
>,
>;
}

View File

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

View File

@ -39,7 +39,7 @@ mod tests {
use crate::{
mode::{
ModeAndSubmode, ModeReply, ModeReplySender, ModeRequest, ModeRequestSender,
ModeRequestorAndHandlerMpsc, ModeRequestorMpsc,
ModeRequestorAndHandlerMpsc, ModeRequestorOneChildMpsc,
},
request::{GenericMessage, MessageMetadata},
};
@ -52,7 +52,8 @@ mod tests {
fn test_simple_mode_requestor() {
let (reply_sender, reply_receiver) = mpsc::channel();
let (request_sender, request_receiver) = mpsc::channel();
let mut mode_requestor = ModeRequestorMpsc::new(TEST_COMPONENT_ID_0, reply_receiver);
let mut mode_requestor =
ModeRequestorOneChildMpsc::new(TEST_COMPONENT_ID_0, reply_receiver);
mode_requestor.add_message_target(TEST_COMPONENT_ID_1, request_sender);
// Send a request and verify it arrives at the receiver.

View File

@ -1,6 +1,3 @@
use core::fmt::{Display, Formatter};
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "std")]
use std::sync::mpsc;
@ -10,89 +7,31 @@ use crate::ComponentId;
pub type ChannelId = u32;
/// Generic error type for sending something via a message queue.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
pub enum GenericSendError {
#[error("rx side has disconnected")]
RxDisconnected,
#[error("queue with max capacity of {0:?} is full")]
QueueFull(Option<u32>),
#[error("target queue with ID {0} does not exist")]
TargetDoesNotExist(ComponentId),
}
impl Display for GenericSendError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
GenericSendError::RxDisconnected => {
write!(f, "rx side has disconnected")
}
GenericSendError::QueueFull(max_cap) => {
write!(f, "queue with max capacity of {max_cap:?} is full")
}
GenericSendError::TargetDoesNotExist(target) => {
write!(f, "target queue with ID {target} does not exist")
}
}
}
}
#[cfg(feature = "std")]
impl Error for GenericSendError {}
/// Generic error type for sending something via a message queue.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
pub enum GenericReceiveError {
#[error("nothing to receive")]
Empty,
#[error("tx side with id {0:?} has disconnected")]
TxDisconnected(Option<ComponentId>),
}
impl Display for GenericReceiveError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::TxDisconnected(channel_id) => {
write!(f, "tx side with id {channel_id:?} has disconnected")
}
Self::Empty => {
write!(f, "nothing to receive")
}
}
}
}
#[cfg(feature = "std")]
impl Error for GenericReceiveError {}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, thiserror::Error)]
pub enum GenericTargetedMessagingError {
Send(GenericSendError),
Receive(GenericReceiveError),
}
impl From<GenericSendError> for GenericTargetedMessagingError {
fn from(value: GenericSendError) -> Self {
Self::Send(value)
}
}
impl From<GenericReceiveError> for GenericTargetedMessagingError {
fn from(value: GenericReceiveError) -> Self {
Self::Receive(value)
}
}
impl Display for GenericTargetedMessagingError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::Send(err) => write!(f, "generic targeted messaging error: {}", err),
Self::Receive(err) => write!(f, "generic targeted messaging error: {}", err),
}
}
}
#[cfg(feature = "std")]
impl Error for GenericTargetedMessagingError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
GenericTargetedMessagingError::Send(send) => Some(send),
GenericTargetedMessagingError::Receive(receive) => Some(receive),
}
}
#[error("generic targeted messaging send error: {0}")]
Send(#[from] GenericSendError),
#[error("generic targeted messaging receive error: {0}")]
Receive(#[from] GenericReceiveError),
}
#[cfg(feature = "std")]

View File

@ -140,24 +140,27 @@ impl<Message> GenericMessage<Message> {
}
/// Generic trait for objects which can send targeted messages.
pub trait MessageSender<MSG>: Send {
pub trait MessageSenderProvider<MSG>: Send {
fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericTargetedMessagingError>;
}
// Generic trait for objects which can receive targeted messages.
pub trait MessageReceiver<MSG> {
pub trait MessageReceiverProvider<MSG> {
fn try_recv(&self) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError>;
}
pub struct MessageWithSenderIdReceiver<MSG, R: MessageReceiver<MSG>>(pub R, PhantomData<MSG>);
pub struct MessageWithSenderIdReceiver<Msg, Receiver: MessageReceiverProvider<Msg>>(
pub Receiver,
PhantomData<Msg>,
);
impl<MSG, R: MessageReceiver<MSG>> From<R> for MessageWithSenderIdReceiver<MSG, R> {
impl<MSG, R: MessageReceiverProvider<MSG>> From<R> for MessageWithSenderIdReceiver<MSG, R> {
fn from(receiver: R) -> Self {
MessageWithSenderIdReceiver(receiver, PhantomData)
}
}
impl<MSG, R: MessageReceiver<MSG>> MessageWithSenderIdReceiver<MSG, R> {
impl<MSG, R: MessageReceiverProvider<MSG>> MessageWithSenderIdReceiver<MSG, R> {
pub fn try_recv_message(
&self,
) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError> {
@ -165,12 +168,12 @@ impl<MSG, R: MessageReceiver<MSG>> MessageWithSenderIdReceiver<MSG, R> {
}
}
pub struct MessageReceiverWithId<MSG, R: MessageReceiver<MSG>> {
pub struct MessageReceiverWithId<MSG, R: MessageReceiverProvider<MSG>> {
local_channel_id: ComponentId,
reply_receiver: MessageWithSenderIdReceiver<MSG, R>,
}
impl<MSG, R: MessageReceiver<MSG>> MessageReceiverWithId<MSG, R> {
impl<MSG, R: MessageReceiverProvider<MSG>> MessageReceiverWithId<MSG, R> {
pub fn new(local_channel_id: ComponentId, reply_receiver: R) -> Self {
Self {
local_channel_id,
@ -183,7 +186,7 @@ impl<MSG, R: MessageReceiver<MSG>> MessageReceiverWithId<MSG, R> {
}
}
impl<MSG, R: MessageReceiver<MSG>> MessageReceiverWithId<MSG, R> {
impl<MSG, R: MessageReceiverProvider<MSG>> MessageReceiverWithId<MSG, R> {
pub fn try_recv_message(
&self,
) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError> {
@ -191,34 +194,122 @@ impl<MSG, R: MessageReceiver<MSG>> MessageReceiverWithId<MSG, R> {
}
}
pub trait MessageSenderStoreProvider<Message, Sender>: Default {
fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender);
fn send_message(
&self,
requestor_info: MessageMetadata,
target_channel_id: ComponentId,
message: Message,
) -> Result<(), GenericTargetedMessagingError>;
}
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use crate::queue::GenericSendError;
use std::convert::From;
use super::*;
use hashbrown::HashMap;
pub struct MessageSenderMap<MSG, S: MessageSender<MSG>>(
pub HashMap<ComponentId, S>,
pub struct OneMessageSender<Msg, S: MessageSenderProvider<Msg>> {
pub id_and_sender: Option<(ComponentId, S)>,
pub(crate) phantom: PhantomData<Msg>,
}
impl<Msg, S: MessageSenderProvider<Msg>> Default for OneMessageSender<Msg, S> {
fn default() -> Self {
Self {
id_and_sender: Default::default(),
phantom: Default::default(),
}
}
}
impl<Msg, Sender: MessageSenderProvider<Msg>> MessageSenderStoreProvider<Msg, Sender>
for OneMessageSender<Msg, Sender>
{
fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender) {
if self.id_and_sender.is_some() {
return;
}
self.id_and_sender = Some((target_id, message_sender));
}
fn send_message(
&self,
requestor_info: MessageMetadata,
target_channel_id: ComponentId,
message: Msg,
) -> Result<(), GenericTargetedMessagingError> {
if let Some((current_id, sender)) = &self.id_and_sender {
if *current_id == target_channel_id {
sender.send(GenericMessage::new(requestor_info, message))?;
return Ok(());
}
}
Err(GenericSendError::TargetDoesNotExist(target_channel_id).into())
}
}
pub struct MessageSenderList<MSG, S: MessageSenderProvider<MSG>>(
pub alloc::vec::Vec<(ComponentId, S)>,
pub(crate) PhantomData<MSG>,
);
impl<MSG, S: MessageSender<MSG>> Default for MessageSenderMap<MSG, S> {
impl<MSG, S: MessageSenderProvider<MSG>> Default for MessageSenderList<MSG, S> {
fn default() -> Self {
Self(Default::default(), PhantomData)
}
}
impl<MSG, S: MessageSender<MSG>> MessageSenderMap<MSG, S> {
pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: S) {
self.0.insert(target_id, message_sender);
impl<Msg, Sender: MessageSenderProvider<Msg>> MessageSenderStoreProvider<Msg, Sender>
for MessageSenderList<Msg, Sender>
{
fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender) {
self.0.push((target_id, message_sender));
}
pub fn send_message(
fn send_message(
&self,
requestor_info: MessageMetadata,
target_channel_id: ComponentId,
message: MSG,
message: Msg,
) -> Result<(), GenericTargetedMessagingError> {
for (current_id, sender) in &self.0 {
if *current_id == target_channel_id {
sender.send(GenericMessage::new(requestor_info, message))?;
return Ok(());
}
}
Err(GenericSendError::TargetDoesNotExist(target_channel_id).into())
}
}
pub struct MessageSenderMap<MSG, S: MessageSenderProvider<MSG>>(
pub HashMap<ComponentId, S>,
pub(crate) PhantomData<MSG>,
);
impl<MSG, S: MessageSenderProvider<MSG>> Default for MessageSenderMap<MSG, S> {
fn default() -> Self {
Self(Default::default(), PhantomData)
}
}
impl<Msg, Sender: MessageSenderProvider<Msg>> MessageSenderStoreProvider<Msg, Sender>
for MessageSenderMap<Msg, Sender>
{
fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender) {
self.0.insert(target_id, message_sender);
}
fn send_message(
&self,
requestor_info: MessageMetadata,
target_channel_id: ComponentId,
message: Msg,
) -> Result<(), GenericTargetedMessagingError> {
if self.0.contains_key(&target_channel_id) {
return self
@ -231,25 +322,38 @@ pub mod alloc_mod {
}
}
pub struct MessageSenderAndReceiver<TO, FROM, S: MessageSender<TO>, R: MessageReceiver<FROM>> {
pub struct MessageSenderAndReceiver<
To,
From,
Sender: MessageSenderProvider<To>,
Receiver: MessageReceiverProvider<From>,
SenderStore: MessageSenderStoreProvider<To, Sender>,
> {
pub local_channel_id: ComponentId,
pub message_sender_map: MessageSenderMap<TO, S>,
pub message_receiver: MessageWithSenderIdReceiver<FROM, R>,
pub message_sender_store: SenderStore,
pub message_receiver: MessageWithSenderIdReceiver<From, Receiver>,
pub(crate) phantom: PhantomData<(To, Sender)>,
}
impl<TO, FROM, S: MessageSender<TO>, R: MessageReceiver<FROM>>
MessageSenderAndReceiver<TO, FROM, S, R>
impl<
To,
From,
Sender: MessageSenderProvider<To>,
Receiver: MessageReceiverProvider<From>,
SenderStore: MessageSenderStoreProvider<To, Sender>,
> MessageSenderAndReceiver<To, From, Sender, Receiver, SenderStore>
{
pub fn new(local_channel_id: ComponentId, message_receiver: R) -> Self {
pub fn new(local_channel_id: ComponentId, message_receiver: Receiver) -> Self {
Self {
local_channel_id,
message_sender_map: Default::default(),
message_sender_store: Default::default(),
message_receiver: MessageWithSenderIdReceiver::from(message_receiver),
phantom: PhantomData,
}
}
pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: S) {
self.message_sender_map
pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender) {
self.message_sender_store
.add_message_target(target_id, message_sender)
}
@ -262,9 +366,9 @@ pub mod alloc_mod {
&self,
request_id: RequestId,
target_id: ComponentId,
message: TO,
message: To,
) -> Result<(), GenericTargetedMessagingError> {
self.message_sender_map.send_message(
self.message_sender_store.send_message(
MessageMetadata::new(request_id, self.local_channel_id_generic()),
target_id,
message,
@ -274,48 +378,64 @@ pub mod alloc_mod {
/// Try to receive a message, which can be a reply or a request, depending on the generics.
pub fn try_recv_message(
&self,
) -> Result<Option<GenericMessage<FROM>>, GenericTargetedMessagingError> {
) -> Result<Option<GenericMessage<From>>, GenericTargetedMessagingError> {
self.message_receiver.try_recv_message()
}
}
pub struct RequestAndReplySenderAndReceiver<
REQUEST,
REPLY,
S0: MessageSender<REQUEST>,
R0: MessageReceiver<REPLY>,
S1: MessageSender<REPLY>,
R1: MessageReceiver<REQUEST>,
Request,
ReqSender: MessageSenderProvider<Request>,
ReqReceiver: MessageReceiverProvider<Request>,
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
Reply,
ReplySender: MessageSenderProvider<Reply>,
ReplyReceiver: MessageReceiverProvider<Reply>,
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
> {
pub local_channel_id: ComponentId,
// These 2 are a functional group.
pub request_sender_map: MessageSenderMap<REQUEST, S0>,
pub reply_receiver: MessageWithSenderIdReceiver<REPLY, R0>,
pub request_sender_store: ReqSenderStore,
pub reply_receiver: MessageWithSenderIdReceiver<Reply, ReplyReceiver>,
// These 2 are a functional group.
pub request_receiver: MessageWithSenderIdReceiver<REQUEST, R1>,
pub reply_sender_map: MessageSenderMap<REPLY, S1>,
pub request_receiver: MessageWithSenderIdReceiver<Request, ReqReceiver>,
pub reply_sender_store: ReplySenderStore,
phantom: PhantomData<(ReqSender, ReplySender)>,
}
impl<
REQUEST,
REPLY,
S0: MessageSender<REQUEST>,
R0: MessageReceiver<REPLY>,
S1: MessageSender<REPLY>,
R1: MessageReceiver<REQUEST>,
> RequestAndReplySenderAndReceiver<REQUEST, REPLY, S0, R0, S1, R1>
Request,
ReqSender: MessageSenderProvider<Request>,
ReqReceiver: MessageReceiverProvider<Request>,
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
Reply,
ReplySender: MessageSenderProvider<Reply>,
ReplyReceiver: MessageReceiverProvider<Reply>,
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
>
RequestAndReplySenderAndReceiver<
Request,
ReqSender,
ReqReceiver,
ReqSenderStore,
Reply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{
pub fn new(
local_channel_id: ComponentId,
request_receiver: R1,
reply_receiver: R0,
request_receiver: ReqReceiver,
reply_receiver: ReplyReceiver,
) -> Self {
Self {
local_channel_id,
request_receiver: request_receiver.into(),
reply_receiver: reply_receiver.into(),
request_sender_map: Default::default(),
reply_sender_map: Default::default(),
request_sender_store: Default::default(),
reply_sender_store: Default::default(),
phantom: PhantomData,
}
}
@ -333,14 +453,14 @@ pub mod std_mod {
use crate::queue::{GenericReceiveError, GenericSendError};
impl<MSG: Send> MessageSender<MSG> for mpsc::Sender<GenericMessage<MSG>> {
impl<MSG: Send> MessageSenderProvider<MSG> for mpsc::Sender<GenericMessage<MSG>> {
fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericTargetedMessagingError> {
self.send(message)
.map_err(|_| GenericSendError::RxDisconnected)?;
Ok(())
}
}
impl<MSG: Send> MessageSender<MSG> for mpsc::SyncSender<GenericMessage<MSG>> {
impl<MSG: Send> MessageSenderProvider<MSG> for mpsc::SyncSender<GenericMessage<MSG>> {
fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericTargetedMessagingError> {
if let Err(e) = self.try_send(message) {
return match e {
@ -357,7 +477,7 @@ pub mod std_mod {
pub type MessageSenderMapMpsc<MSG> = MessageReceiverWithId<MSG, mpsc::Sender<MSG>>;
pub type MessageSenderMapBoundedMpsc<MSG> = MessageReceiverWithId<MSG, mpsc::SyncSender<MSG>>;
impl<MSG> MessageReceiver<MSG> for mpsc::Receiver<GenericMessage<MSG>> {
impl<MSG> MessageReceiverProvider<MSG> for mpsc::Receiver<GenericMessage<MSG>> {
fn try_recv(&self) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError> {
match self.try_recv() {
Ok(msg) => Ok(Some(msg)),
@ -386,7 +506,7 @@ mod tests {
use crate::{
queue::{GenericReceiveError, GenericSendError, GenericTargetedMessagingError},
request::{MessageMetadata, MessageSenderMap},
request::{MessageMetadata, MessageSenderMap, MessageSenderStoreProvider},
};
use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId};

View File

@ -154,7 +154,7 @@ pub mod std_mod {
}
/// Can be used to set the start of the slot to the current time. This is useful if a custom
/// runner implementation is used instead of the [Self::start] method.
/// runner implementation is used instead of the [Self::run_one_task_cycle] method.
pub fn init_start_of_slot(&mut self) {
self.start_of_slot = Instant::now();
}

998
satrs/src/subsystem.rs Normal file
View File

@ -0,0 +1,998 @@
use crate::{
mode::{Mode, ModeAndSubmode, ModeReply, ModeRequest, ModeRequestSender, UNKNOWN_MODE},
mode_tree::{
ModeStoreProvider, ModeStoreVec, SequenceModeTables, SequenceTableMapTable,
SequenceTablesMapValue, TargetModeTables, TargetNotInModeStoreError, TargetTablesMapValue,
},
queue::GenericTargetedMessagingError,
request::{GenericMessage, RequestId},
ComponentId,
};
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum SequenceExecutionHelperState {
/// The sequence execution is IDLE, no command is loaded or the sequence exection has
/// finished
Idle,
/// The sequence helper is executing a sequence and no replies need to be awaited.
Busy,
/// The sequence helper is still awaiting a reply from a mode children. The reply awaition
/// is a property of a mode commanding sequence
AwaitingSuccessCheck,
}
#[derive(Debug)]
pub enum TargetKeepingResult {
Ok,
Violated { fallback_mode: Option<Mode> },
}
#[derive(Debug, PartialEq, Eq)]
pub enum ModeCommandingResult {
/// The commanding of all children is finished
Done,
/// One step of a commanding chain is finished
StepDone,
/// Reply awaition is required for some children
AwaitingSuccessCheck,
}
#[derive(Debug, thiserror::Error)]
#[error("Mode {0} does not exist")]
pub struct ModeDoesNotExistError(Mode);
/// This sequence execution helper includes some boilerplate logic to
/// execute [SequenceModeTables].
///
/// It takes care of commanding the [ModeRequest]s specified in those tables and also includes the
/// states required to track the current progress of a sequence execution and take care of
/// reply and success awaition.
#[derive(Debug)]
pub struct SequenceExecutionHelper {
target_mode: Option<Mode>,
state: SequenceExecutionHelperState,
request_id: Option<RequestId>,
current_sequence_index: Option<usize>,
last_sequence_index: Option<usize>,
}
impl Default for SequenceExecutionHelper {
fn default() -> Self {
Self {
target_mode: None,
state: SequenceExecutionHelperState::Idle,
request_id: None,
current_sequence_index: None,
last_sequence_index: None,
}
}
}
impl SequenceExecutionHelper {
pub fn new() -> Self {
Default::default()
}
/// Load a new mode sequence to be executed
pub fn load(
&mut self,
mode: Mode,
request_id: RequestId,
sequence_tables: &SequenceModeTables,
) -> Result<(), ModeDoesNotExistError> {
if !sequence_tables.0.contains_key(&mode) {
return Err(ModeDoesNotExistError(mode));
}
self.target_mode = Some(mode);
self.request_id = Some(request_id);
self.state = SequenceExecutionHelperState::Busy;
self.current_sequence_index = None;
Ok(())
}
/// Run the sequence execution helper.
///
/// This function will execute the sequence in the given [SequenceModeTables] based on the
/// mode loaded in [Self::load]. It calls [Self::execute_sequence_and_map_to_result] and
/// automatically takes care of state management, including increments of the sequence table
/// index.
///
/// The returnvalues of the helper have the following meaning.
///
/// * [ModeCommandingResult::AwaitingSuccessCheck] - The sequence is still awaiting a success.
/// The user should check whether all children have reached the commanded target mode, for
/// example by checking [mode replies][ModeReply] received by the children components, and
/// then calling [Self::confirm_sequence_done] to advance to the sequence or complete the
/// sequence.
/// * [ModeCommandingResult::CommandingDone] - The sequence is done. The user can load a new
/// sequence now without overwriting the last one. The sequence executor is in
/// [SequenceExecutionHelperState::Idle] again.
/// * [ModeCommandingResult::CommandingStepDone] - The sequence has advanced one step. The user
/// can now call [Self::run] again to immediately execute the next step in the sequence.
///
/// Generally, periodic execution of the [Self::run] method should be performed while
/// [Self::state] is not [SequenceExecutionHelperState::Idle].
///
/// # 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
/// * `children_mode_store` - The mode store vector to keep track of the mode states of
/// children components
pub fn run(
&mut self,
table: &SequenceModeTables,
sender: &impl ModeRequestSender,
children_mode_store: &mut ModeStoreVec,
) -> Result<ModeCommandingResult, GenericTargetedMessagingError> {
if self.state == SequenceExecutionHelperState::Idle {
return Ok(ModeCommandingResult::Done);
}
if self.state == SequenceExecutionHelperState::AwaitingSuccessCheck {
return Ok(ModeCommandingResult::AwaitingSuccessCheck);
}
if self.target_mode.is_none() {
return Ok(ModeCommandingResult::Done);
}
match self.current_sequence_index {
Some(idx) => {
// Execute the sequence.
let seq_table_value = table.0.get(&self.target_mode.unwrap()).unwrap();
self.execute_sequence_and_map_to_result(
seq_table_value,
idx,
sender,
children_mode_store,
)
}
None => {
// Find the first sequence
let seq_table_value = table.0.get(&self.target_mode.unwrap()).unwrap();
self.last_sequence_index = Some(seq_table_value.entries.len() - 1);
if seq_table_value.entries.is_empty() {
Ok(ModeCommandingResult::Done)
} else {
self.current_sequence_index = Some(0);
self.execute_sequence_and_map_to_result(
seq_table_value,
0,
sender,
children_mode_store,
)
}
}
}
}
/// Retrieve the currently loaded target mode
pub fn target_mode(&self) -> Option<Mode> {
self.target_mode
}
/// Confirm that a sequence which is awaiting a success check is done
pub fn confirm_sequence_done(&mut self) {
if let SequenceExecutionHelperState::AwaitingSuccessCheck = self.state {
self.state = SequenceExecutionHelperState::Busy;
if let (Some(last_sequence_index), Some(current_sequence_index)) =
(self.last_sequence_index, self.current_sequence_index)
{
if current_sequence_index == last_sequence_index {
self.state = SequenceExecutionHelperState::Idle;
}
}
self.current_sequence_index = Some(self.current_sequence_index.unwrap() + 1);
}
}
/// Internal state of the execution helper.
pub fn state(&self) -> SequenceExecutionHelperState {
self.state
}
pub fn awaiting_success_check(&self) -> bool {
self.state == SequenceExecutionHelperState::AwaitingSuccessCheck
}
pub fn current_sequence_index(&self) -> Option<usize> {
self.current_sequence_index
}
/// Execute a sequence at the given sequence index for a given [SequenceTablesMapValue].
///
/// This method calls [Self::execute_sequence] and maps the result to a [ModeCommandingResult].
/// It is also called by the [Self::run] method of this helper.
pub fn execute_sequence_and_map_to_result(
&mut self,
seq_table_value: &SequenceTablesMapValue,
sequence_idx: usize,
sender: &impl ModeRequestSender,
mode_store_vec: &mut ModeStoreVec,
) -> Result<ModeCommandingResult, GenericTargetedMessagingError> {
if self.state() == SequenceExecutionHelperState::Idle || self.request_id.is_none() {
return Ok(ModeCommandingResult::Done);
}
if Self::execute_sequence(
self.request_id.unwrap(),
&seq_table_value.entries[sequence_idx],
sender,
mode_store_vec,
)? {
self.state = SequenceExecutionHelperState::AwaitingSuccessCheck;
Ok(ModeCommandingResult::AwaitingSuccessCheck)
} else if seq_table_value.entries.len() - 1 == sequence_idx {
self.state = SequenceExecutionHelperState::Idle;
return Ok(ModeCommandingResult::Done);
} else {
self.current_sequence_index = Some(sequence_idx + 1);
return Ok(ModeCommandingResult::StepDone);
}
}
/// Generic stateless execution helper method.
///
/// The [RequestId] and the [SequenceTableMapTable] to be executed are passed explicitely
/// here. This method is called by [Self::execute_sequence_and_map_to_result].
///
/// This method itereates through the entries of the given sequence table and sends out
/// [ModeRequest]s to set the modes of the children according to the table entries.
/// It also sets the reply awaition field in the children mode store where a success
/// check is required to true.
///
/// It returns whether any commanding success check is required by any entry in the table.
pub fn execute_sequence(
request_id: RequestId,
map_table: &SequenceTableMapTable,
sender: &impl ModeRequestSender,
children_mode_store: &mut ModeStoreVec,
) -> Result<bool, GenericTargetedMessagingError> {
let mut some_succes_check_required = false;
for entry in &map_table.entries {
sender.send_mode_request(
request_id,
entry.common.target_id,
ModeRequest::SetMode {
mode_and_submode: entry.common.mode_submode,
forced: false,
},
)?;
if entry.check_success {
children_mode_store.0.iter_mut().for_each(|val| {
if val.id() == entry.common.target_id {
val.awaiting_reply = true;
}
});
some_succes_check_required = true;
}
}
Ok(some_succes_check_required)
}
}
#[derive(Debug, Default, PartialEq, Eq)]
pub enum ModeTreeHelperState {
#[default]
Idle,
/// The helper is currently trying to keep a target mode.
TargetKeeping,
/// The helper is currently busy to command a mode.
ModeCommanding,
}
#[derive(Debug, Default)]
pub enum SubsystemHelperResult {
#[default]
Idle,
/// Result of a target keeping operation
TargetKeeping(TargetKeepingResult),
/// Result of a mode commanding operation
ModeCommanding(ModeCommandingResult),
}
impl From<TargetKeepingResult> for SubsystemHelperResult {
fn from(value: TargetKeepingResult) -> Self {
Self::TargetKeeping(value)
}
}
impl From<ModeCommandingResult> for SubsystemHelperResult {
fn from(value: ModeCommandingResult) -> Self {
Self::ModeCommanding(value)
}
}
#[derive(Debug, thiserror::Error)]
pub enum ModeTreeHelperError {
#[error("generic targeted messaging error: {0}")]
Message(#[from] GenericTargetedMessagingError),
#[error("current mode {0} is not contained in target table")]
CurrentModeNotInTargetTable(Mode),
}
/// This is a helper object which can be used by a subsystem component to execute mode sequences
/// and perform target keeping.
///
/// This helper object tries to compose as much data and state information as possible which is
/// required for this process.
pub struct SubsystemCommandingHelper {
/// Current mode of the owner subsystem.
pub current_mode: ModeAndSubmode,
/// State of the helper.
pub state: ModeTreeHelperState,
/// 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.
pub active_request_id: Option<RequestId>,
/// The primary data structure to keep the target state information for subsystem
/// [modes][Mode]. it specifies the mode each child should have for a certain subsystem mode
/// and is relevant for target keeping.
pub target_tables: TargetModeTables,
/// The primary data structure to keep the sequence commanding information for commanded
/// subsystem [modes][Mode]. It specifies the actual commands and the order they should be
/// sent in to reach a certain [mode][Mode].
pub sequence_tables: SequenceModeTables,
/// The sequence execution helper is used to execute sequences in the [Self::sequence_tables].
pub seq_exec_helper: SequenceExecutionHelper,
}
impl Default for SubsystemCommandingHelper {
fn default() -> Self {
Self {
current_mode: UNKNOWN_MODE,
state: Default::default(),
children_mode_store: Default::default(),
active_request_id: None,
target_tables: Default::default(),
sequence_tables: Default::default(),
seq_exec_helper: Default::default(),
}
}
}
impl SubsystemCommandingHelper {
/// Create a new substem commanding helper with an intial [ModeTreeHelperState::Idle] state,
/// an empty mode children store and empty target and sequence mode tables.
pub fn new(
children_mode_store: ModeStoreVec,
target_tables: TargetModeTables,
sequence_tables: SequenceModeTables,
) -> Self {
Self {
current_mode: UNKNOWN_MODE,
state: ModeTreeHelperState::Idle,
children_mode_store,
active_request_id: None,
target_tables,
sequence_tables,
seq_exec_helper: Default::default(),
}
}
/// Add a mode child to the internal [Self::children_mode_store].
pub fn add_mode_child(&mut self, child: ComponentId, mode: ModeAndSubmode) {
self.children_mode_store.add_component(child, mode);
}
/// Add a target mode table and an associated sequence mode table.
pub fn add_target_and_sequence_table(
&mut self,
mode: Mode,
target_table_val: TargetTablesMapValue,
sequence_table_val: SequenceTablesMapValue,
) {
self.target_tables.0.insert(mode, target_table_val);
self.sequence_tables.0.insert(mode, sequence_table_val);
}
/// Starts a command sequence for a given [mode][Mode].
pub fn start_command_sequence(
&mut self,
mode: Mode,
request_id: RequestId,
) -> Result<(), ModeDoesNotExistError> {
self.seq_exec_helper
.load(mode, request_id, &self.sequence_tables)?;
self.state = ModeTreeHelperState::ModeCommanding;
Ok(())
}
pub fn send_announce_mode_cmd_to_children(
&self,
request_id: RequestId,
req_sender: &impl ModeRequestSender,
recursive: bool,
) -> Result<(), GenericTargetedMessagingError> {
let mut request = ModeRequest::AnnounceMode;
if recursive {
request = ModeRequest::AnnounceModeRecursive;
}
for child in &self.children_mode_store.0 {
req_sender.send_mode_request(request_id, child.id(), request)?;
}
Ok(())
}
pub fn state_machine(
&mut self,
opt_reply: Option<GenericMessage<ModeReply>>,
req_sender: &impl ModeRequestSender,
) -> Result<SubsystemHelperResult, ModeTreeHelperError> {
if let Some(reply) = opt_reply {
self.handle_mode_reply(&reply);
}
match self.state {
ModeTreeHelperState::Idle => Ok(SubsystemHelperResult::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()) {
return Ok(self.perform_target_keeping(target_table).into());
}
Ok(TargetKeepingResult::Ok.into())
}
ModeTreeHelperState::ModeCommanding => {
let result = self.seq_exec_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 ModeCommandingResult::Done = result {
self.state = ModeTreeHelperState::TargetKeeping;
self.active_request_id = None;
self.current_mode =
ModeAndSubmode::new(self.seq_exec_helper.target_mode().unwrap(), 0);
}
Ok(result.into())
}
}
}
pub fn perform_target_keeping(
&self,
target_table: &TargetTablesMapValue,
) -> TargetKeepingResult {
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
};
}
});
if target_mode_violated {
// Target keeping violated. Report violation and fallback mode to user.
return TargetKeepingResult::Violated {
fallback_mode: target_table.fallback_mode,
};
}
}
TargetKeepingResult::Ok
}
pub fn handle_mode_reply(&mut self, reply: &GenericMessage<ModeReply>) {
if !self.children_mode_store.has_component(reply.sender_id()) {
return;
}
let mut generic_mode_reply_handler =
|sender_id, mode_and_submode: Option<ModeAndSubmode>| {
// Tying the reply awaition to the request ID ensures that something like replies
// belonging to older requests do not interfere with the completion handling of
// the mode commanding. This is important for forced mode commands.
let mut handle_awaition = false;
if self.state == ModeTreeHelperState::ModeCommanding
&& self.active_request_id.is_some()
&& reply.request_id() == self.active_request_id.unwrap()
{
handle_awaition = true;
}
let still_awating_replies = self.children_mode_store.mode_reply_handler(
sender_id,
mode_and_submode,
handle_awaition,
);
if self.state == ModeTreeHelperState::ModeCommanding
&& !still_awating_replies.unwrap_or(false)
{
self.seq_exec_helper.confirm_sequence_done();
}
};
match reply.message {
ModeReply::ModeInfo(mode_and_submode) => {
generic_mode_reply_handler(reply.sender_id(), Some(mode_and_submode));
}
ModeReply::ModeReply(mode_and_submode) => {
generic_mode_reply_handler(reply.sender_id(), Some(mode_and_submode));
}
ModeReply::CantReachMode(_) => {
generic_mode_reply_handler(reply.sender_id(), None);
}
ModeReply::WrongMode { reached, .. } => {
generic_mode_reply_handler(reply.sender_id(), Some(reached));
}
};
}
pub fn update_child_mode(
&mut self,
child: ComponentId,
mode: ModeAndSubmode,
) -> Result<(), TargetNotInModeStoreError> {
let val_mut = self
.children_mode_store
.get_mut(child)
.ok_or(TargetNotInModeStoreError(child))?;
val_mut.mode_and_submode = mode;
Ok(())
}
}
#[cfg(test)]
mod tests {
use core::cell::RefCell;
use std::collections::VecDeque;
use crate::{
mode::{Mode, ModeAndSubmode, ModeRequest, ModeRequestSender, UNKNOWN_MODE},
mode_tree::{
ModeStoreProvider, ModeStoreVec, SequenceModeTables, SequenceTableEntry,
SequenceTableMapTable, SequenceTablesMapValue,
},
queue::GenericTargetedMessagingError,
request::RequestId,
subsystem::{ModeCommandingResult, SequenceExecutionHelperState},
ComponentId,
};
use super::SequenceExecutionHelper;
#[derive(Debug)]
pub enum ExampleTargetId {
Target0 = 1,
Target1 = 2,
Target2 = 3,
}
#[derive(Debug)]
pub enum ExampleMode {
Mode0 = 1,
Mode1 = 2,
Mode2 = 3,
}
pub struct ModeReqWrapper {
pub request_id: RequestId,
pub target_id: ComponentId,
pub request: ModeRequest,
}
#[derive(Default)]
pub struct ModeReqSenderMock {
pub requests: RefCell<VecDeque<ModeReqWrapper>>,
}
impl ModeRequestSender for ModeReqSenderMock {
fn local_channel_id(&self) -> crate::ComponentId {
0
}
fn send_mode_request(
&self,
request_id: RequestId,
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.requests.borrow_mut().push_back(ModeReqWrapper {
request_id,
target_id,
request,
});
Ok(())
}
}
pub struct SequenceExecutorTestbench {
pub sender: ModeReqSenderMock,
pub mode_store: ModeStoreVec,
pub seq_table: SequenceModeTables,
pub execution_helper: SequenceExecutionHelper,
}
impl SequenceExecutorTestbench {
pub fn new() -> Self {
let mode_store = create_default_mode_store();
let seq_table = create_simple_sample_seq_table(false, false);
Self {
sender: ModeReqSenderMock::default(),
mode_store,
seq_table,
execution_helper: SequenceExecutionHelper::new(),
}
}
pub fn get_mode_table(&mut self, mode: ExampleMode) -> &mut SequenceTablesMapValue {
self.seq_table.0.get_mut(&(mode as Mode)).unwrap()
}
pub fn run(&mut self) -> Result<ModeCommandingResult, GenericTargetedMessagingError> {
self.execution_helper
.run(&self.seq_table, &self.sender, &mut self.mode_store)
}
fn check_run_is_no_op(&mut self) {
// Assure that no unexpected behaviour occurs.
assert_eq!(
self.execution_helper
.run(&self.seq_table, &self.sender, &mut self.mode_store)
.unwrap(),
ModeCommandingResult::Done
);
assert_eq!(
self.execution_helper.state(),
SequenceExecutionHelperState::Idle
);
assert!(self.sender.requests.borrow().is_empty());
}
fn generic_checks_subsystem_md1_step0(&mut self, expected_req_id: RequestId) {
assert_eq!(
self.execution_helper.target_mode().unwrap(),
ExampleMode::Mode1 as Mode
);
assert_eq!(self.sender.requests.borrow().len(), 2);
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
assert_eq!(req_0.target_id, ExampleTargetId::Target0 as u64);
assert_eq!(req_0.request_id, expected_req_id);
assert_eq!(
req_0.request,
ModeRequest::SetMode {
mode_and_submode: SUBSYSTEM_MD1_ST0_TGT0_MODE,
forced: false
}
);
let req_1 = self.sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req_1.target_id, ExampleTargetId::Target1 as u64);
assert_eq!(
req_1.request,
ModeRequest::SetMode {
mode_and_submode: SUBSYSTEM_MD1_ST0_TGT1_MODE,
forced: false
}
);
}
fn generic_checks_subsystem_md1_step1(&mut self, expected_req_id: RequestId) {
assert_eq!(
self.execution_helper.target_mode().unwrap(),
ExampleMode::Mode1 as Mode
);
assert_eq!(self.sender.requests.borrow().len(), 1);
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
assert_eq!(req_0.target_id, ExampleTargetId::Target2 as u64);
assert_eq!(req_0.request_id, expected_req_id);
assert_eq!(
req_0.request,
ModeRequest::SetMode {
mode_and_submode: SUBSYSTEM_MD1_ST1_TGT2_MODE,
forced: false
}
);
}
fn generic_checks_subsystem_md0(&mut self, expected_req_id: RequestId) {
assert_eq!(
self.execution_helper.target_mode().unwrap(),
ExampleMode::Mode0 as Mode
);
assert_eq!(self.execution_helper.current_sequence_index().unwrap(), 0);
assert_eq!(self.sender.requests.borrow().len(), 2);
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
assert_eq!(req_0.target_id, ExampleTargetId::Target0 as u64);
assert_eq!(req_0.request_id, expected_req_id);
assert_eq!(
req_0.request,
ModeRequest::SetMode {
mode_and_submode: SUBSYSTEM_MD0_TGT_0_MODE,
forced: false
}
);
let req_1 = self.sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req_1.target_id, ExampleTargetId::Target1 as u64);
assert_eq!(
req_1.request,
ModeRequest::SetMode {
mode_and_submode: SUBSYSTEM_MD0_TGT_1_MODE,
forced: false
}
);
}
}
fn create_default_mode_store() -> ModeStoreVec {
let mut mode_store = ModeStoreVec::default();
mode_store.add_component(ExampleTargetId::Target0 as u64, UNKNOWN_MODE);
mode_store.add_component(ExampleTargetId::Target1 as u64, UNKNOWN_MODE);
mode_store.add_component(ExampleTargetId::Target2 as u64, UNKNOWN_MODE);
mode_store
}
fn create_simple_sample_seq_table(
success_check_tgt0: bool,
success_check_tgt1: bool,
) -> SequenceModeTables {
let mut table = SequenceModeTables::default();
// Mode 0 - One step command
let mut table_val = SequenceTablesMapValue::new("MODE_0");
let mut table_seq_0 = SequenceTableMapTable::new("MODE_0_SEQ_0");
table_seq_0.add_entry(SequenceTableEntry::new(
"TARGET_0",
ExampleTargetId::Target0 as u64,
SUBSYSTEM_MD0_TGT_0_MODE,
success_check_tgt0,
));
table_seq_0.add_entry(SequenceTableEntry::new(
"TARGET_1",
ExampleTargetId::Target1 as u64,
SUBSYSTEM_MD0_TGT_1_MODE,
success_check_tgt1,
));
table_val.add_sequence_table(table_seq_0);
table.0.insert(ExampleMode::Mode0 as u32, table_val);
// Mode 1 - Multi Step command
let mut table_val = SequenceTablesMapValue::new("MODE_1");
let mut table_seq_0 = SequenceTableMapTable::new("MODE_1_SEQ_0");
table_seq_0.add_entry(SequenceTableEntry::new(
"MD1_SEQ0_TGT0",
ExampleTargetId::Target0 as u64,
SUBSYSTEM_MD1_ST0_TGT0_MODE,
false,
));
table_seq_0.add_entry(SequenceTableEntry::new(
"MD1_SEQ0_TGT1",
ExampleTargetId::Target1 as u64,
SUBSYSTEM_MD1_ST0_TGT1_MODE,
false,
));
table_val.add_sequence_table(table_seq_0);
let mut table_seq_1 = SequenceTableMapTable::new("MODE_1_SEQ_1");
table_seq_1.add_entry(SequenceTableEntry::new(
"MD1_SEQ1_TGT2",
ExampleTargetId::Target2 as u64,
SUBSYSTEM_MD1_ST1_TGT2_MODE,
false,
));
table_val.add_sequence_table(table_seq_1);
table.0.insert(ExampleMode::Mode1 as u32, table_val);
table
}
const SUBSYSTEM_MD0_TGT_0_MODE: ModeAndSubmode =
ModeAndSubmode::new(ExampleMode::Mode0 as u32, 0);
const SUBSYSTEM_MD0_TGT_1_MODE: ModeAndSubmode =
ModeAndSubmode::new(ExampleMode::Mode1 as u32, 0);
const SUBSYSTEM_MD1_ST0_TGT0_MODE: ModeAndSubmode =
ModeAndSubmode::new(ExampleMode::Mode2 as u32, 0);
const SUBSYSTEM_MD1_ST0_TGT1_MODE: ModeAndSubmode =
ModeAndSubmode::new(ExampleMode::Mode0 as u32, 0);
const SUBSYSTEM_MD1_ST1_TGT2_MODE: ModeAndSubmode =
ModeAndSubmode::new(ExampleMode::Mode1 as u32, 0);
#[test]
fn test_init_state() {
let execution_helper = SequenceExecutionHelper::new();
assert_eq!(execution_helper.state(), SequenceExecutionHelperState::Idle);
assert!(!execution_helper.awaiting_success_check());
assert!(execution_helper.target_mode().is_none());
assert!(execution_helper.current_sequence_index().is_none());
}
#[test]
fn test_sequence_execution_helper_no_success_check() {
let mut tb = SequenceExecutorTestbench::new();
let expected_req_id = 1;
tb.execution_helper
.load(ExampleMode::Mode0 as u32, expected_req_id, &tb.seq_table)
.unwrap();
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::Busy
);
assert!(!tb.execution_helper.awaiting_success_check());
assert_eq!(
tb.execution_helper.target_mode().unwrap(),
ExampleMode::Mode0 as Mode
);
assert_eq!(
tb.execution_helper
.run(&tb.seq_table, &tb.sender, &mut tb.mode_store)
.expect("sequence exeecution helper run failure"),
ModeCommandingResult::Done
);
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::Idle
);
assert!(!tb.execution_helper.awaiting_success_check());
tb.generic_checks_subsystem_md0(expected_req_id);
tb.check_run_is_no_op();
}
#[test]
fn test_sequence_execution_helper_with_success_check() {
let mut tb = SequenceExecutorTestbench::new();
let mode0_table = tb.get_mode_table(ExampleMode::Mode0);
mode0_table.entries[0].entries[0].check_success = true;
mode0_table.entries[0].entries[1].check_success = true;
let expected_req_id = 1;
tb.execution_helper
.load(ExampleMode::Mode0 as u32, expected_req_id, &tb.seq_table)
.unwrap();
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::Busy
);
assert!(!tb.execution_helper.awaiting_success_check());
assert_eq!(
tb.execution_helper.target_mode().unwrap(),
ExampleMode::Mode0 as Mode
);
assert_eq!(
tb.run().expect("sequence exeecution helper run failure"),
ModeCommandingResult::AwaitingSuccessCheck
);
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::AwaitingSuccessCheck
);
// These are not cleared, even if the execution helper is already IDLE. This is okay for
// now.
assert!(tb.execution_helper.awaiting_success_check());
tb.generic_checks_subsystem_md0(expected_req_id);
tb.execution_helper.confirm_sequence_done();
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::Idle
);
tb.check_run_is_no_op();
}
#[test]
fn test_sequence_execution_helper_with_partial_check() {
let mut tb = SequenceExecutorTestbench::new();
let mode0_table = tb.get_mode_table(ExampleMode::Mode0);
mode0_table.entries[0].entries[0].check_success = true;
let expected_req_id = 1;
tb.execution_helper
.load(ExampleMode::Mode0 as u32, expected_req_id, &tb.seq_table)
.unwrap();
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::Busy
);
assert!(!tb.execution_helper.awaiting_success_check());
assert_eq!(
tb.run().expect("sequence execution helper run failure"),
ModeCommandingResult::AwaitingSuccessCheck
);
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::AwaitingSuccessCheck
);
// These are not cleared, even if the execution helper is already IDLE. This is okay for
// now.
assert!(tb.execution_helper.awaiting_success_check());
tb.generic_checks_subsystem_md0(expected_req_id);
tb.execution_helper.confirm_sequence_done();
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::Idle
);
tb.check_run_is_no_op();
}
#[test]
fn test_sequence_execution_helper_multi_step_no_success_check() {
let mut tb = SequenceExecutorTestbench::new();
let expected_req_id = 1;
tb.execution_helper
.load(ExampleMode::Mode1 as u32, expected_req_id, &tb.seq_table)
.unwrap();
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::Busy
);
assert!(!tb.execution_helper.awaiting_success_check());
assert_eq!(
tb.execution_helper.target_mode().unwrap(),
ExampleMode::Mode1 as Mode
);
assert_eq!(
tb.run().expect("sequence execution helper run failure"),
ModeCommandingResult::StepDone
);
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::Busy
);
assert!(!tb.execution_helper.awaiting_success_check());
tb.generic_checks_subsystem_md1_step0(expected_req_id);
assert_eq!(tb.execution_helper.current_sequence_index().unwrap(), 1);
assert_eq!(
tb.run().expect("sequence execution helper run failure"),
ModeCommandingResult::Done
);
tb.generic_checks_subsystem_md1_step1(expected_req_id);
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::Idle
);
tb.check_run_is_no_op();
}
#[test]
fn test_sequence_execution_helper_multi_step_full_success_check() {
let mut tb = SequenceExecutorTestbench::new();
let expected_req_id = 1;
tb.execution_helper
.load(ExampleMode::Mode1 as u32, expected_req_id, &tb.seq_table)
.unwrap();
let mode1_table = tb.get_mode_table(ExampleMode::Mode1);
mode1_table.entries[0].entries[0].check_success = true;
mode1_table.entries[0].entries[1].check_success = true;
mode1_table.entries[1].entries[0].check_success = true;
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::Busy
);
assert!(!tb.execution_helper.awaiting_success_check());
assert_eq!(
tb.execution_helper.target_mode().unwrap(),
ExampleMode::Mode1 as Mode
);
assert_eq!(
tb.run().expect("sequence execution helper run failure"),
ModeCommandingResult::AwaitingSuccessCheck
);
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::AwaitingSuccessCheck
);
assert!(tb.execution_helper.awaiting_success_check());
tb.generic_checks_subsystem_md1_step0(expected_req_id);
assert_eq!(tb.execution_helper.current_sequence_index().unwrap(), 0);
tb.execution_helper.confirm_sequence_done();
assert_eq!(
tb.run().expect("sequence execution helper run failure"),
ModeCommandingResult::AwaitingSuccessCheck
);
assert_eq!(
tb.execution_helper.state(),
SequenceExecutionHelperState::AwaitingSuccessCheck
);
assert!(tb.execution_helper.awaiting_success_check());
assert_eq!(tb.execution_helper.current_sequence_index().unwrap(), 1);
tb.generic_checks_subsystem_md1_step1(expected_req_id);
tb.execution_helper.confirm_sequence_done();
tb.check_run_is_no_op();
}
// TODO: Test subsystem commanding helper
}

View File

@ -17,37 +17,37 @@ impl PusTmWithCdsShortHelper {
}
#[cfg(feature = "std")]
pub fn create_pus_tm_timestamp_now<'a>(
&'a mut self,
pub fn create_pus_tm_timestamp_now<'time, 'src_data>(
&'time mut self,
service: u8,
subservice: u8,
source_data: &'a [u8],
source_data: &'src_data [u8],
seq_count: u16,
) -> PusTmCreator {
) -> PusTmCreator<'time, 'src_data> {
let time_stamp = CdsTime::now_with_u16_days().unwrap();
time_stamp.write_to_bytes(&mut self.cds_short_buf).unwrap();
self.create_pus_tm_common(service, subservice, source_data, seq_count)
}
pub fn create_pus_tm_with_stamper<'a>(
&'a mut self,
pub fn create_pus_tm_with_stamper<'time, 'src_data>(
&'time mut self,
service: u8,
subservice: u8,
source_data: &'a [u8],
source_data: &'src_data [u8],
stamper: &CdsTime,
seq_count: u16,
) -> PusTmCreator {
) -> PusTmCreator<'time, 'src_data> {
stamper.write_to_bytes(&mut self.cds_short_buf).unwrap();
self.create_pus_tm_common(service, subservice, source_data, seq_count)
}
fn create_pus_tm_common<'a>(
&'a self,
fn create_pus_tm_common<'time, 'src_data>(
&'time self,
service: u8,
subservice: u8,
source_data: &'a [u8],
source_data: &'src_data [u8],
seq_count: u16,
) -> PusTmCreator {
) -> PusTmCreator<'time, 'src_data> {
let reply_header = SpHeader::new_for_unseg_tm(self.apid, seq_count, 0);
let tc_header = PusTmSecondaryHeader::new_simple(service, subservice, &self.cds_short_buf);
PusTmCreator::new(reply_header, tc_header, source_data, true)

File diff suppressed because it is too large Load Diff

View File

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