made request handler more generic
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good

This commit is contained in:
Robin Müller 2024-03-13 16:06:12 +01:00
parent 41e57a7942
commit 8bdc9686fb
Signed by: muellerr
GPG Key ID: A649FB78196E3849
6 changed files with 290 additions and 188 deletions

View File

@ -1,18 +1,16 @@
use log::{error, warn}; use log::{error, warn};
use satrs::action::{ActionRequest, ActionRequestVariant}; use satrs::action::{ActionRequest, ActionRequestVariant};
use satrs::pool::{SharedStaticMemoryPool, StoreAddr}; use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
use satrs::pus::action::{PusActionToRequestConverter, PusService8ActionHandler}; use satrs::pus::action::PusService8ActionRequestHandler;
use satrs::pus::verification::std_mod::{
VerificationReporterWithSharedPoolMpscBoundedSender, VerificationReporterWithVecMpscSender,
};
use satrs::pus::verification::{ use satrs::pus::verification::{
FailParams, TcStateAccepted, VerificationReportingProvider, VerificationToken, FailParams, TcStateAccepted, VerificationReporterWithSharedPoolMpscBoundedSender,
VerificationReporterWithVecMpscSender, VerificationReportingProvider, VerificationToken,
}; };
use satrs::pus::{ use satrs::pus::{
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusPacketHandlerResult, EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusPacketHandlerResult,
PusPacketHandlingError, PusServiceHelper, TmAsVecSenderWithId, TmAsVecSenderWithMpsc, PusPacketHandlingError, PusServiceHelper, PusTcToRequestConverter, TmAsVecSenderWithId,
TmInSharedPoolSenderWithBoundedMpsc, TmInSharedPoolSenderWithId, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc, TmInSharedPoolSenderWithId,
}; };
use satrs::request::TargetAndApidId; use satrs::request::TargetAndApidId;
use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::tc::PusTcReader;
@ -29,7 +27,7 @@ use super::GenericRoutingErrorHandler;
#[derive(Default)] #[derive(Default)]
pub struct ExampleActionRequestConverter {} pub struct ExampleActionRequestConverter {}
impl PusActionToRequestConverter for ExampleActionRequestConverter { impl PusTcToRequestConverter<ActionRequest> for ExampleActionRequestConverter {
type Error = PusPacketHandlingError; type Error = PusPacketHandlingError;
fn convert( fn convert(
@ -99,7 +97,7 @@ pub fn create_action_service_static(
"PUS_8_TC_RECV", "PUS_8_TC_RECV",
pus_action_rx, pus_action_rx,
); );
let pus_8_handler = PusService8ActionHandler::new( let pus_8_handler = PusService8ActionRequestHandler::new(
PusServiceHelper::new( PusServiceHelper::new(
action_srv_receiver, action_srv_receiver,
action_srv_tm_sender, action_srv_tm_sender,
@ -135,7 +133,7 @@ pub fn create_action_service_dynamic(
"PUS_8_TC_RECV", "PUS_8_TC_RECV",
pus_action_rx, pus_action_rx,
); );
let pus_8_handler = PusService8ActionHandler::new( let pus_8_handler = PusService8ActionRequestHandler::new(
PusServiceHelper::new( PusServiceHelper::new(
action_srv_receiver, action_srv_receiver,
action_srv_tm_sender, action_srv_tm_sender,
@ -156,7 +154,7 @@ pub struct Pus8Wrapper<
TcInMemConverter: EcssTcInMemConverter, TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
> { > {
pub(crate) pus_8_handler: PusService8ActionHandler< pub(crate) pus_8_handler: PusService8ActionRequestHandler<
TcReceiver, TcReceiver,
TmSender, TmSender,
TcInMemConverter, TcInMemConverter,

View File

@ -5,10 +5,9 @@ use derive_new::new;
use satrs::action::ActionRequest; use satrs::action::ActionRequest;
use satrs::hk::HkRequest; use satrs::hk::HkRequest;
use satrs::mode::ModeRequest; use satrs::mode::ModeRequest;
use satrs::pus::action::PusActionRequestRouter;
use satrs::pus::hk::PusHkRequestRouter; use satrs::pus::hk::PusHkRequestRouter;
use satrs::pus::verification::{TcStateAccepted, VerificationToken}; use satrs::pus::verification::{TcStateAccepted, VerificationToken};
use satrs::pus::GenericRoutingError; use satrs::pus::{GenericRoutingError, PusRequestRouter};
use satrs::queue::GenericSendError; use satrs::queue::GenericSendError;
use satrs::TargetId; use satrs::TargetId;
@ -71,7 +70,7 @@ impl PusHkRequestRouter for GenericRequestRouter {
} }
} }
impl PusActionRequestRouter for GenericRequestRouter { impl PusRequestRouter<ActionRequest> for GenericRequestRouter {
type Error = GenericRoutingError; type Error = GenericRoutingError;
fn route( fn route(

View File

@ -5,10 +5,7 @@ use crate::{
TargetId, TargetId,
}; };
use super::{ use super::{verification::VerificationToken, ActiveRequest, ActiveRequestProvider};
verification::{TcStateAccepted, VerificationToken},
ActiveRequest, ActiveRequestProvider,
};
use delegate::delegate; use delegate::delegate;
use satrs_shared::res_code::ResultU16; use satrs_shared::res_code::ResultU16;
@ -20,6 +17,7 @@ pub use std_mod::*;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[allow(unused_imports)]
pub use alloc_mod::*; pub use alloc_mod::*;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -54,18 +52,6 @@ pub enum ActionReplyPus {
}, },
} }
/// This trait is an abstraction for the routing of PUS service 8 action requests to a dedicated
/// recipient using the generic [TargetId].
pub trait PusActionRequestRouter {
type Error;
fn route(
&self,
target_id: TargetId,
hk_request: ActionRequest,
token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error>;
}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ActiveActionRequest { pub struct ActiveActionRequest {
pub action_id: ActionId, pub action_id: ActionId,
@ -85,39 +71,7 @@ impl ActiveRequestProvider for ActiveActionRequest {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub mod alloc_mod { pub mod alloc_mod {}
use spacepackets::ecss::tc::PusTcReader;
use crate::pus::verification::VerificationReportingProvider;
use super::*;
/// This trait is an abstraction for the conversion of a PUS service 8 action telecommand into
/// an [ActionRequest].
///
/// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard.
/// The only requirement is that a valid [TargetId] and an [ActionRequest] are returned by the
/// core conversion function.
///
/// The user should take care of performing the error handling as well. Some of the following
/// aspects might be relevant:
///
/// - Checking the validity of the APID, service ID, subservice ID.
/// - Checking the validity of the user data.
///
/// A [VerificationReportingProvider] instance is passed to the user to also allow handling
/// of the verification process as part of the PUS standard requirements.
pub trait PusActionToRequestConverter {
type Error;
fn convert(
&mut self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, ActionRequest), Self::Error>;
}
}
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
@ -125,14 +79,12 @@ pub mod std_mod {
use crate::{ use crate::{
params::WritableToBeBytes, params::WritableToBeBytes,
pus::{ pus::{
get_current_cds_short_timestamp,
verification::{ verification::{
self, FailParams, FailParamsWithStep, TcStateStarted, VerificationReportingProvider, self, FailParams, FailParamsWithStep, TcStateStarted, VerificationReportingProvider,
}, },
ActiveRequestMapProvider, DefaultActiveRequestMap, EcssTcInMemConverter, ActiveRequestMapProvider, DefaultActiveRequestMap, EcssTmSenderCore, EcssTmtcError,
EcssTcReceiverCore, EcssTmSenderCore, EcssTmtcError, GenericRoutingError, GenericRoutingError, PusServiceReplyHandler, PusTargetedRequestHandler,
PusPacketHandlerResult, PusPacketHandlingError, PusRoutingErrorHandler, ReplyHandlerHook,
PusServiceHelper, PusServiceReplyHandler, ReplyHandlerHook,
}, },
}; };
use core::time::Duration; use core::time::Duration;
@ -141,45 +93,7 @@ pub mod std_mod {
use super::*; use super::*;
/// This is a high-level handler for the PUS service 8 action service. pub type PusService8ActionRequestHandler<
///
/// It performs the following handling steps:
///
/// 1. Retrieve the next TC packet from the [PusServiceHelper]. The [EcssTcInMemConverter]
/// allows to configure the used telecommand memory backend.
/// 2. Convert the TC to a targeted action request using the provided
/// [PusActionToRequestConverter]. The generic error type is constrained to the
/// [PusPacketHandlingError] for the concrete implementation which offers a packet handler.
/// 3. Route the action request using the provided [PusActionRequestRouter].
/// 4. Handle all routing errors using the provided [PusRoutingErrorHandler].
pub struct PusService8ActionHandler<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusActionToRequestConverter,
RequestRouter: PusActionRequestRouter<Error = RoutingError>,
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
RoutingError = GenericRoutingError,
> {
service_helper:
PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
pub request_converter: RequestConverter,
pub request_router: RequestRouter,
pub routing_error_handler: RoutingErrorHandler,
}
impl<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusActionToRequestConverter<Error = PusPacketHandlingError>,
RequestRouter: PusActionRequestRouter<Error = RoutingError>,
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
RoutingError: Clone,
>
PusService8ActionHandler<
TcReceiver, TcReceiver,
TmSender, TmSender,
TcInMemConverter, TcInMemConverter,
@ -187,77 +101,30 @@ pub mod std_mod {
RequestConverter, RequestConverter,
RequestRouter, RequestRouter,
RoutingErrorHandler, RoutingErrorHandler,
RoutingError, RoutingError = GenericRoutingError,
> > = PusTargetedRequestHandler<
where
PusPacketHandlingError: From<RoutingError>,
{
pub fn new(
service_helper: PusServiceHelper<
TcReceiver, TcReceiver,
TmSender, TmSender,
TcInMemConverter, TcInMemConverter,
VerificationReporter, VerificationReporter,
>, RequestConverter,
request_converter: RequestConverter, RequestRouter,
request_router: RequestRouter, RoutingErrorHandler,
routing_error_handler: RoutingErrorHandler, ActionRequest,
) -> Self { RoutingError,
Self { >;
service_helper,
request_converter,
request_router,
routing_error_handler,
}
}
/// Core function to poll the next TC packet and try to handle it.
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
if possible_packet.is_none() {
return Ok(PusPacketHandlerResult::Empty);
}
let ecss_tc_and_token = possible_packet.unwrap();
let tc = self
.service_helper
.tc_in_mem_converter
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
let mut partial_error = None;
let time_stamp = get_current_cds_short_timestamp(&mut partial_error);
let (target_id, action_request) = self.request_converter.convert(
ecss_tc_and_token.token,
&tc,
&time_stamp,
&self.service_helper.common.verification_handler,
)?;
if let Err(e) =
self.request_router
.route(target_id, action_request, ecss_tc_and_token.token)
{
self.routing_error_handler.handle_error(
target_id,
ecss_tc_and_token.token,
&tc,
e.clone(),
&time_stamp,
&self.service_helper.common.verification_handler,
);
return Err(e.into());
}
Ok(PusPacketHandlerResult::RequestHandled)
}
}
pub type DefaultActiveActionRequestMap = DefaultActiveRequestMap<ActiveActionRequest>; pub type DefaultActiveActionRequestMap = DefaultActiveRequestMap<ActiveActionRequest>;
/// Type definition for a PUS 8 action service reply handler which constrains the /// Type definition for a PUS 8 action service reply handler which constrains the
/// [PusServiceReplyHandler] active request and reply generics to the [ActiveActionRequest] and /// [PusServiceReplyHandler] active request and reply generics to the [ActiveActionRequest] and
/// [ActionReplyPusWithIds] type. /// [ActionReplyPusWithIds] type.
pub type PusService8ReplyHandler<VerificationReporter, ActiveRequestMap, UserHook> = pub type PusService8ReplyHandler<VerificationReporter, ActiveRequestMap, UserHook, TmSender> =
PusServiceReplyHandler< PusServiceReplyHandler<
VerificationReporter, VerificationReporter,
ActiveRequestMap, ActiveRequestMap,
UserHook, UserHook,
TmSender,
ActiveActionRequest, ActiveActionRequest,
ActionReplyPusWithIds, ActionReplyPusWithIds,
>; >;
@ -266,7 +133,8 @@ pub mod std_mod {
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
ActiveRequestMap: ActiveRequestMapProvider<ActiveActionRequest>, ActiveRequestMap: ActiveRequestMapProvider<ActiveActionRequest>,
UserHook: ReplyHandlerHook<ActiveActionRequest, ActionReplyPusWithIds>, UserHook: ReplyHandlerHook<ActiveActionRequest, ActionReplyPusWithIds>,
> PusService8ReplyHandler<VerificationReporter, ActiveRequestMap, UserHook> TmSender: EcssTmSenderCore,
> PusService8ReplyHandler<VerificationReporter, ActiveRequestMap, UserHook, TmSender>
{ {
/// Helper method to register a recently routed action request. /// Helper method to register a recently routed action request.
pub fn add_routed_action_request( pub fn add_routed_action_request(
@ -366,7 +234,14 @@ pub mod std_mod {
impl< impl<
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
UserHook: ReplyHandlerHook<ActiveActionRequest, ActionReplyPusWithIds>, UserHook: ReplyHandlerHook<ActiveActionRequest, ActionReplyPusWithIds>,
> PusService8ReplyHandler<VerificationReporter, DefaultActiveActionRequestMap, UserHook> TmSender: EcssTmSenderCore,
>
PusService8ReplyHandler<
VerificationReporter,
DefaultActiveActionRequestMap,
UserHook,
TmSender,
>
{ {
/// Create a new PUS Service 8 reply handler with the [ActiveRequestMap] generic /// Create a new PUS Service 8 reply handler with the [ActiveRequestMap] generic
/// constrained to the [DefaultActiveActionRequestMap] object and with the current time /// constrained to the [DefaultActiveActionRequestMap] object and with the current time
@ -377,12 +252,14 @@ pub mod std_mod {
verification_reporter: VerificationReporter, verification_reporter: VerificationReporter,
fail_data_buf_size: usize, fail_data_buf_size: usize,
user_hook: UserHook, user_hook: UserHook,
tm_sender: TmSender,
) -> Result<Self, SystemTimeError> { ) -> Result<Self, SystemTimeError> {
let current_time = UnixTimestamp::from_now()?; let current_time = UnixTimestamp::from_now()?;
Ok(Self::new_with_default_map( Ok(Self::new_with_default_map(
verification_reporter, verification_reporter,
fail_data_buf_size, fail_data_buf_size,
user_hook, user_hook,
tm_sender,
current_time, current_time,
)) ))
} }
@ -393,6 +270,7 @@ pub mod std_mod {
verification_reporter: VerificationReporter, verification_reporter: VerificationReporter,
fail_data_buf_size: usize, fail_data_buf_size: usize,
user_hook: UserHook, user_hook: UserHook,
tm_sender: TmSender,
init_time: UnixTimestamp, init_time: UnixTimestamp,
) -> Self { ) -> Self {
Self::new( Self::new(
@ -400,6 +278,7 @@ pub mod std_mod {
DefaultActiveActionRequestMap::default(), DefaultActiveActionRequestMap::default(),
fail_data_buf_size, fail_data_buf_size,
user_hook, user_hook,
tm_sender,
init_time, init_time,
) )
} }
@ -409,9 +288,9 @@ pub mod std_mod {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use core::{cell::RefCell, time::Duration}; use core::{cell::RefCell, time::Duration};
use std::time::SystemTimeError; use std::{sync::mpsc, time::SystemTimeError};
use alloc::collections::VecDeque; use alloc::{collections::VecDeque, vec::Vec};
use delegate::delegate; use delegate::delegate;
use spacepackets::{ use spacepackets::{
@ -434,33 +313,34 @@ mod tests {
verification::{ verification::{
self, self,
tests::{SharedVerificationMap, TestVerificationReporter, VerificationStatus}, tests::{SharedVerificationMap, TestVerificationReporter, VerificationStatus},
FailParams, TcStateNone, TcStateStarted, VerificationReportingProvider, FailParams, TcStateAccepted, TcStateNone, TcStateStarted,
VerificationReportingProvider,
}, },
EcssTcInVecConverter, EcssTmtcError, GenericRoutingError, MpscTcReceiver, EcssTcInVecConverter, EcssTmtcError, GenericRoutingError, MpscTcReceiver,
PusPacketHandlerResult, PusPacketHandlingError, ReplyHandlerHook, PusPacketHandlerResult, PusPacketHandlingError, PusRequestRouter,
TmAsVecSenderWithMpsc, PusTcToRequestConverter, ReplyHandlerHook, TmAsVecSenderWithMpsc,
}, },
}; };
use super::*; use super::*;
impl PusActionRequestRouter for TestRouter<ActionRequest> { impl<Request> PusRequestRouter<Request> for TestRouter<Request> {
type Error = GenericRoutingError; type Error = GenericRoutingError;
fn route( fn route(
&self, &self,
target_id: TargetId, target_id: TargetId,
action_request: ActionRequest, request: Request,
_token: VerificationToken<TcStateAccepted>, _token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
self.routing_requests self.routing_requests
.borrow_mut() .borrow_mut()
.push_back((target_id, action_request)); .push_back((target_id, request));
self.check_for_injected_error() self.check_for_injected_error()
} }
} }
impl PusActionToRequestConverter for TestConverter<8> { impl PusTcToRequestConverter<ActionRequest> for TestConverter<8> {
type Error = PusPacketHandlingError; type Error = PusPacketHandlingError;
fn convert( fn convert(
&mut self, &mut self,
@ -508,7 +388,7 @@ mod tests {
struct Pus8RequestTestbenchWithVec { struct Pus8RequestTestbenchWithVec {
common: PusServiceHandlerWithVecCommon<TestVerificationReporter>, common: PusServiceHandlerWithVecCommon<TestVerificationReporter>,
handler: PusService8ActionHandler< handler: PusService8ActionRequestHandler<
MpscTcReceiver, MpscTcReceiver,
TmAsVecSenderWithMpsc, TmAsVecSenderWithMpsc,
EcssTcInVecConverter, EcssTcInVecConverter,
@ -525,7 +405,7 @@ mod tests {
PusServiceHandlerWithVecCommon::new_with_test_verif_sender(); PusServiceHandlerWithVecCommon::new_with_test_verif_sender();
Self { Self {
common, common,
handler: PusService8ActionHandler::new( handler: PusService8ActionRequestHandler::new(
srv_handler, srv_handler,
TestConverter::default(), TestConverter::default(),
TestRouter::default(), TestRouter::default(),
@ -599,10 +479,13 @@ mod tests {
pub struct Pus8ReplyTestbench { pub struct Pus8ReplyTestbench {
verif_reporter: TestVerificationReporter, verif_reporter: TestVerificationReporter,
#[allow(dead_code)]
ecss_tm_receiver: mpsc::Receiver<Vec<u8>>,
handler: PusService8ReplyHandler< handler: PusService8ReplyHandler<
TestVerificationReporter, TestVerificationReporter,
DefaultActiveActionRequestMap, DefaultActiveActionRequestMap,
TestReplyHandlerHook, TestReplyHandlerHook,
mpsc::Sender<Vec<u8>>,
>, >,
} }
@ -611,11 +494,13 @@ mod tests {
let reply_handler_hook = TestReplyHandlerHook::default(); let reply_handler_hook = TestReplyHandlerHook::default();
let shared_verif_map = SharedVerificationMap::default(); let shared_verif_map = SharedVerificationMap::default();
let test_verif_reporter = TestVerificationReporter::new(shared_verif_map.clone()); let test_verif_reporter = TestVerificationReporter::new(shared_verif_map.clone());
let (ecss_tm_sender, ecss_tm_receiver) = mpsc::channel();
let reply_handler = if normal_ctor { let reply_handler = if normal_ctor {
PusService8ReplyHandler::new_from_now_with_default_map( PusService8ReplyHandler::new_from_now_with_default_map(
test_verif_reporter.clone(), test_verif_reporter.clone(),
128, 128,
reply_handler_hook, reply_handler_hook,
ecss_tm_sender,
) )
.expect("creating reply handler failed") .expect("creating reply handler failed")
} else { } else {
@ -624,11 +509,13 @@ mod tests {
DefaultActiveActionRequestMap::default(), DefaultActiveActionRequestMap::default(),
128, 128,
reply_handler_hook, reply_handler_hook,
ecss_tm_sender,
) )
.expect("creating reply handler failed") .expect("creating reply handler failed")
}; };
Self { Self {
verif_reporter: test_verif_reporter, verif_reporter: test_verif_reporter,
ecss_tm_receiver,
handler: reply_handler, handler: reply_handler,
} }
} }

View File

@ -337,6 +337,18 @@ pub trait ReplyHandlerHook<ActiveRequestType, ReplyType> {
fn timeout_error_code(&self) -> ResultU16; fn timeout_error_code(&self) -> ResultU16;
} }
/// This trait is an abstraction for the routing of PUS request to a dedicated
/// recipient using the generic [TargetId].
pub trait PusRequestRouter<Request> {
type Error;
fn route(
&self,
target_id: TargetId,
hk_request: Request,
token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error>;
}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod alloc_mod { pub mod alloc_mod {
use hashbrown::HashMap; use hashbrown::HashMap;
@ -427,6 +439,32 @@ pub mod alloc_mod {
impl_downcast!(EcssTcReceiver); impl_downcast!(EcssTcReceiver);
/// This trait is an abstraction for the conversion of a PUS telecommand into a generic request
/// type.
///
/// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard.
/// The only requirement is that a valid [TargetId] and a request instance are returned by the
/// core conversion function.
///
/// The user should take care of performing the error handling as well. Some of the following
/// aspects might be relevant:
///
/// - Checking the validity of the APID, service ID, subservice ID.
/// - Checking the validity of the user data.
///
/// A [VerificationReportingProvider] instance is passed to the user to also allow handling
/// of the verification process as part of the PUS standard requirements.
pub trait PusTcToRequestConverter<Request> {
type Error;
fn convert(
&mut self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, Request), Self::Error>;
}
pub trait PusRoutingErrorHandler { pub trait PusRoutingErrorHandler {
type Error; type Error;
fn handle_error( fn handle_error(
@ -500,6 +538,7 @@ pub mod alloc_mod {
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestType>, ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestType>,
UserHook: ReplyHandlerHook<ActiveRequestType, ReplyType>, UserHook: ReplyHandlerHook<ActiveRequestType, ReplyType>,
TmSender: EcssTmSenderCore,
ActiveRequestType: ActiveRequestProvider, ActiveRequestType: ActiveRequestProvider,
ReplyType, ReplyType,
> { > {
@ -508,6 +547,7 @@ pub mod alloc_mod {
pub fail_data_buf: alloc::vec::Vec<u8>, pub fail_data_buf: alloc::vec::Vec<u8>,
pub current_time: UnixTimestamp, pub current_time: UnixTimestamp,
pub user_hook: UserHook, pub user_hook: UserHook,
pub tm_sender: TmSender,
phantom: PhantomData<(ActiveRequestType, ReplyType)>, phantom: PhantomData<(ActiveRequestType, ReplyType)>,
} }
@ -515,6 +555,7 @@ pub mod alloc_mod {
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestType>, ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestType>,
UserHook: ReplyHandlerHook<ActiveRequestType, ReplyType>, UserHook: ReplyHandlerHook<ActiveRequestType, ReplyType>,
TmSender: EcssTmSenderCore,
ActiveRequestType: ActiveRequestProvider, ActiveRequestType: ActiveRequestProvider,
ReplyType, ReplyType,
> >
@ -522,6 +563,7 @@ pub mod alloc_mod {
VerificationReporter, VerificationReporter,
ActiveRequestMap, ActiveRequestMap,
UserHook, UserHook,
TmSender,
ActiveRequestType, ActiveRequestType,
ReplyType, ReplyType,
> >
@ -533,6 +575,7 @@ pub mod alloc_mod {
active_request_map: ActiveRequestMap, active_request_map: ActiveRequestMap,
fail_data_buf_size: usize, fail_data_buf_size: usize,
user_hook: UserHook, user_hook: UserHook,
tm_sender: TmSender,
) -> Result<Self, std::time::SystemTimeError> { ) -> Result<Self, std::time::SystemTimeError> {
let current_time = UnixTimestamp::from_now()?; let current_time = UnixTimestamp::from_now()?;
Ok(Self::new( Ok(Self::new(
@ -540,6 +583,7 @@ pub mod alloc_mod {
active_request_map, active_request_map,
fail_data_buf_size, fail_data_buf_size,
user_hook, user_hook,
tm_sender,
current_time, current_time,
)) ))
} }
@ -549,6 +593,7 @@ pub mod alloc_mod {
active_request_map: ActiveRequestMap, active_request_map: ActiveRequestMap,
fail_data_buf_size: usize, fail_data_buf_size: usize,
user_hook: UserHook, user_hook: UserHook,
tm_sender: TmSender,
init_time: UnixTimestamp, init_time: UnixTimestamp,
) -> Self { ) -> Self {
Self { Self {
@ -557,6 +602,7 @@ pub mod alloc_mod {
fail_data_buf: alloc::vec![0; fail_data_buf_size], fail_data_buf: alloc::vec![0; fail_data_buf_size],
current_time: init_time, current_time: init_time,
user_hook, user_hook,
tm_sender,
phantom: PhantomData, phantom: PhantomData,
} }
} }
@ -642,6 +688,7 @@ pub mod std_mod {
use crate::tmtc::tm_helper::SharedTmPool; use crate::tmtc::tm_helper::SharedTmPool;
use crate::{ChannelId, TargetId}; use crate::{ChannelId, TargetId};
use alloc::vec::Vec; use alloc::vec::Vec;
use core::marker::PhantomData;
use spacepackets::ecss::tc::PusTcReader; use spacepackets::ecss::tc::PusTcReader;
use spacepackets::ecss::tm::PusTmCreator; use spacepackets::ecss::tm::PusTmCreator;
use spacepackets::ecss::{PusError, WritablePusPacket}; use spacepackets::ecss::{PusError, WritablePusPacket};
@ -657,7 +704,10 @@ pub mod std_mod {
pub use cb_mod::*; pub use cb_mod::*;
use super::verification::VerificationReportingProvider; use super::verification::VerificationReportingProvider;
use super::{AcceptedEcssTcAndToken, TcInMemory}; use super::{
AcceptedEcssTcAndToken, PusRequestRouter, PusRoutingErrorHandler, PusTcToRequestConverter,
TcInMemory,
};
impl From<mpsc::SendError<StoreAddr>> for EcssTmtcError { impl From<mpsc::SendError<StoreAddr>> for EcssTmtcError {
fn from(_: mpsc::SendError<StoreAddr>) -> Self { fn from(_: mpsc::SendError<StoreAddr>) -> Self {
@ -931,6 +981,118 @@ pub mod std_mod {
} }
} }
/// This is a high-level handler for the PUS service 8 action service.
///
/// It performs the following handling steps:
///
/// 1. Retrieve the next TC packet from the [PusServiceHelper]. The [EcssTcInMemConverter]
/// allows to configure the used telecommand memory backend.
/// 2. Convert the TC to a targeted action request using the provided
/// [PusActionToRequestConverter]. The generic error type is constrained to the
/// [PusPacketHandlingError] for the concrete implementation which offers a packet handler.
/// 3. Route the action request using the provided [PusActionRequestRouter].
/// 4. Handle all routing errors using the provided [PusRoutingErrorHandler].
pub struct PusTargetedRequestHandler<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusTcToRequestConverter<Request>,
RequestRouter: PusRequestRouter<Request, Error = RoutingError>,
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
Request,
RoutingError = GenericRoutingError,
> {
service_helper:
PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
pub request_converter: RequestConverter,
pub request_router: RequestRouter,
pub routing_error_handler: RoutingErrorHandler,
phantom: PhantomData<Request>,
}
impl<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusTcToRequestConverter<Request, Error = PusPacketHandlingError>,
RequestRouter: PusRequestRouter<Request, Error = RoutingError>,
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
Request,
RoutingError: Clone,
>
PusTargetedRequestHandler<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
RequestConverter,
RequestRouter,
RoutingErrorHandler,
Request,
RoutingError,
>
where
PusPacketHandlingError: From<RoutingError>,
{
pub fn new(
service_helper: PusServiceHelper<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
>,
request_converter: RequestConverter,
request_router: RequestRouter,
routing_error_handler: RoutingErrorHandler,
) -> Self {
Self {
service_helper,
request_converter,
request_router,
routing_error_handler,
phantom: PhantomData,
}
}
/// Core function to poll the next TC packet and try to handle it.
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
if possible_packet.is_none() {
return Ok(PusPacketHandlerResult::Empty);
}
let ecss_tc_and_token = possible_packet.unwrap();
let tc = self
.service_helper
.tc_in_mem_converter
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
let mut partial_error = None;
let time_stamp = get_current_cds_short_timestamp(&mut partial_error);
let (target_id, action_request) = self.request_converter.convert(
ecss_tc_and_token.token,
&tc,
&time_stamp,
&self.service_helper.common.verification_handler,
)?;
if let Err(e) =
self.request_router
.route(target_id, action_request, ecss_tc_and_token.token)
{
self.routing_error_handler.handle_error(
target_id,
ecss_tc_and_token.token,
&tc,
e.clone(),
&time_stamp,
&self.service_helper.common.verification_handler,
);
return Err(e.into());
}
Ok(PusPacketHandlerResult::RequestHandled)
}
}
// TODO: All these types could probably be no_std if we implemented error handling ourselves.. // TODO: All these types could probably be no_std if we implemented error handling ourselves..
// but thiserror is really nice, so keep it like this for simplicity for now. Maybe thiserror // but thiserror is really nice, so keep it like this for simplicity for now. Maybe thiserror
// will be no_std soon, see https://github.com/rust-lang/rust/issues/103765 . // will be no_std soon, see https://github.com/rust-lang/rust/issues/103765 .

View File

@ -2,6 +2,10 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{mode::ModeRequest, TargetId};
use super::verification::{TcStateAccepted, VerificationToken};
#[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)] #[repr(u8)]
@ -14,3 +18,51 @@ pub enum Subservice {
TmCantReachMode = 7, TmCantReachMode = 7,
TmWrongModeReply = 8, TmWrongModeReply = 8,
} }
/// This trait is an abstraction for the routing of PUS service 8 action requests to a dedicated
/// recipient using the generic [TargetId].
pub trait PusModeRequestRouter {
type Error;
fn route(
&self,
target_id: TargetId,
mode_request: ModeRequest,
token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error>;
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub mod alloc_mod {
use spacepackets::ecss::tc::PusTcReader;
use crate::pus::verification::VerificationReportingProvider;
use super::*;
/// This trait is an abstraction for the conversion of a PUS mode service telecommand into
/// an [ModeRequest].
///
/// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard.
/// The only requirement is that a valid [TargetId] and an [ActionRequest] are returned by the
/// core conversion function.
///
/// The user should take care of performing the error handling as well. Some of the following
/// aspects might be relevant:
///
/// - Checking the validity of the APID, service ID, subservice ID.
/// - Checking the validity of the user data.
///
/// A [VerificationReportingProvider] instance is passed to the user to also allow handling
/// of the verification process as part of the PUS standard requirements.
pub trait PusModeToRequestConverter {
type Error;
fn convert(
&mut self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, ModeRequest), Self::Error>;
}
}

View File

@ -48,6 +48,10 @@ struct TestDevice {
pub mode_requestor_info: Option<(RequestId, ChannelId)>, pub mode_requestor_info: Option<(RequestId, ChannelId)>,
} }
pub struct ModeLeafDeviceHelper {
// pub
}
impl TestDevice { impl TestDevice {
pub fn run(&mut self) { pub fn run(&mut self) {
self.check_mode_requests().expect("mode messaging error"); self.check_mode_requests().expect("mode messaging error");