need to re-work all of this..
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit

This commit is contained in:
Robin Müller 2024-03-18 19:57:37 +01:00
parent d2c4cd7428
commit 0f2a700ef1
Signed by: muellerr
GPG Key ID: A649FB78196E3849
19 changed files with 561 additions and 618 deletions

View File

@ -53,6 +53,8 @@ pub mod tmtc_err {
pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 4);
#[resultcode]
pub const ROUTING_ERROR: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 5);
#[resultcode(info = "Request timeout for targeted PUS request. P1: Request ID. P2: Target ID")]
pub const REQUEST_TIMEOUT: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 6);
#[resultcode(
info = "Not enough data inside the TC application data field. Optionally includes: \

View File

@ -2,8 +2,8 @@ use log::{error, warn};
use satrs::action::{ActionRequest, ActionRequestVariant};
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
use satrs::pus::action::{
ActionReplyPusWithActionId, ActiveActionRequest, DefaultActiveActionRequestMap,
PusService8ActionRequestHandler, PusService8ReplyHandler,
ActionReplyPus, ActionReplyPusWithActionId, ActiveActionRequest, DefaultActiveActionRequestMap,
PusService8ReplyHandler,
};
use satrs::pus::verification::{
FailParams, TcStateAccepted, VerificationReporterWithSharedPoolMpscBoundedSender,
@ -12,20 +12,56 @@ use satrs::pus::verification::{
use satrs::pus::{
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusPacketHandlerResult,
PusPacketHandlingError, PusServiceHelper, PusTcToRequestConverter, ReplyHandlerHook,
TmAsVecSenderWithId, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc,
TmInSharedPoolSenderWithId,
PusPacketHandlingError, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter,
ReplyHandlerHook, TmAsVecSenderWithId, TmAsVecSenderWithMpsc,
TmInSharedPoolSenderWithBoundedMpsc, TmInSharedPoolSenderWithId,
};
use satrs::request::TargetAndApidId;
use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::PusPacket;
use satrs::tmtc::tm_helper::SharedTmPool;
use satrs::{ChannelId, TargetId};
use satrs_example::config::tmtc_err::REQUEST_TIMEOUT;
use satrs_example::config::{tmtc_err, TcReceiverId, TmSenderId, PUS_APID};
use std::sync::mpsc::{self};
use crate::requests::GenericRequestRouter;
use super::PusTargetedRequestService;
#[derive(Default)]
pub struct ActionReplyHandler {}
impl PusReplyHandler<ActiveActionRequest, ActionReplyPusWithActionId> for ActionReplyHandler {
type Error = ();
fn handle_unexpected_reply(
&mut self,
reply: &satrs::request::GenericMessage<ActionReplyPusWithActionId>,
) {
log::warn!("received unexpected reply for service {SERVICE}: {reply}");
}
fn handle_reply(
&mut self,
reply: &satrs::request::GenericMessage<ActionReplyPusWithActionId>,
verification_handler: &impl VerificationReportingProvider,
tm_sender: &impl EcssTmSenderCore,
) -> Result<bool, Self::Error> {
todo!()
}
/*
fn timeout_callback(&self, active_request: &ActiveRequestType) {
log::warn!("timeout for active request {active_request} on service {SERVICE}");
}
fn timeout_error_code(&self) -> satrs::res_code::ResultU16 {
REQUEST_TIMEOUT
}
*/
}
#[derive(Default)]
pub struct ExampleActionRequestConverter {}
@ -99,7 +135,7 @@ pub fn create_action_service_static(
"PUS_8_TC_RECV",
pus_action_rx,
);
let action_request_handler = PusService8ActionRequestHandler::new(
let action_request_handler = PusTargetedRequestService::new(
PusServiceHelper::new(
action_srv_receiver,
action_srv_tm_sender.clone(),
@ -109,18 +145,13 @@ pub fn create_action_service_static(
),
ExampleActionRequestConverter::default(),
action_router,
);
let action_reply_handler = PusService8ReplyHandler::new_from_now(
verif_reporter.clone(),
// TODO: Implementation which does not use run-time allocation? Maybe something like
// a bounded wrapper which pre-allocates using [HashMap::with_capacity]..
DefaultActiveActionRequestMap::default(),
1024,
PusActionReplyHook::default(),
action_srv_tm_sender,
)
.expect("Failed to create PUS 8 reply handler");
ActionReplyHandler::<8>::default(),
);
Pus8Wrapper {
action_request_handler,
action_reply_handler,
}
}
@ -145,7 +176,7 @@ pub fn create_action_service_dynamic(
"PUS_8_TC_RECV",
pus_action_rx,
);
let action_request_handler = PusService8ActionRequestHandler::new(
let action_request_handler = PusTargetedRequestService::new(
PusServiceHelper::new(
action_srv_receiver,
action_srv_tm_sender.clone(),
@ -155,18 +186,11 @@ pub fn create_action_service_dynamic(
),
ExampleActionRequestConverter::default(),
action_router,
);
let action_reply_handler = PusService8ReplyHandler::new_from_now(
verif_reporter.clone(),
DefaultActiveActionRequestMap::default(),
1024,
PusActionReplyHook::default(),
action_srv_tm_sender,
)
.expect("Failed to create PUS 8 reply handler");
ActionReplyHandler::<8>::default(),
);
Pus8Wrapper {
action_request_handler,
action_reply_handler,
}
}
@ -195,19 +219,13 @@ pub struct Pus8Wrapper<
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
> {
pub(crate) action_request_handler: PusService8ActionRequestHandler<
pub(crate) action_request_handler: PusTargetedRequestService<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
ExampleActionRequestConverter,
GenericRequestRouter,
>,
pub(crate) action_reply_handler: PusService8ReplyHandler<
VerificationReporter,
DefaultActiveActionRequestMap,
PusActionReplyHook,
TmSender,
ActionRequest,
>,
}
@ -239,7 +257,6 @@ impl<
error!("PUS packet handling error: {error:?}")
}
}
// self.action_reply_handler.handle_replies();
false
}
}

View File

@ -106,8 +106,8 @@ impl<
VerificationReporter: VerificationReportingProvider,
> Pus5Wrapper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
{
pub fn handle_next_packet(&mut self) -> bool {
match self.pus_5_handler.handle_one_tc() {
pub fn handle_next_packet(&mut self, time_stamp: &[u8]) -> bool {
match self.pus_5_handler.handle_one_tc(time_stamp) {
Ok(result) => match result {
PusPacketHandlerResult::RequestHandled => {}
PusPacketHandlerResult::RequestHandledPartialSuccess(e) => {

View File

@ -1,7 +1,18 @@
use crate::requests::GenericRequestRouter;
use crate::tmtc::MpscStoreAndSendError;
use log::warn;
use satrs::pus::verification::{FailParams, VerificationReportingProvider};
use satrs::pus::{EcssTcAndToken, PusPacketHandlerResult, TcInMemory};
use satrs::pus::verification::{
self, FailParams, VerificationReporter, VerificationReporterWithSharedPoolMpscBoundedSender,
VerificationReporterWithVecMpscSender, VerificationReportingProvider,
};
use satrs::pus::{
ActiveRequestMapProvider, ActiveRequestProvider, DefaultActiveRequestMap, EcssTcAndToken,
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiverCore,
EcssTmSenderCore, GenericRoutingError, MpscTcReceiver, PusPacketHandlerResult, PusReplyHandler,
PusServiceHelper, PusServiceReplyHandler, PusTcToRequestConverter, ReplyHandlerHook,
TcInMemory, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc,
};
use satrs::request::GenericMessage;
use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::PusServiceId;
use satrs::spacepackets::time::cds::TimeProvider;
@ -36,13 +47,6 @@ struct TimeStampHelper {
}
impl TimeStampHelper {
pub fn new() -> Self {
Self {
stamper: TimeProvider::new_with_u16_days(0, 0),
time_stamp: [0; 7],
}
}
pub fn stamp(&self) -> &[u8] {
&self.time_stamp
}
@ -57,16 +61,137 @@ impl TimeStampHelper {
}
}
impl Default for TimeStampHelper {
fn default() -> Self {
Self {
stamper: TimeProvider::from_now_with_u16_days().expect("creating time stamper failed"),
time_stamp: Default::default(),
}
}
}
impl<VerificationReporter: VerificationReportingProvider> PusReceiver<VerificationReporter> {
pub fn new(verif_reporter: VerificationReporter, pus_router: PusTcMpscRouter) -> Self {
Self {
verif_reporter,
pus_router,
stamp_helper: TimeStampHelper::new(),
stamp_helper: TimeStampHelper::default(),
}
}
}
pub struct PusTargetedRequestService<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusTcToRequestConverter<RequestType>,
ReplyHook: ReplyHandlerHook<RequestType, ReplyType>,
ActiveRequestMap: ActiveRequestMapProvider<RequestType>,
ActiveRequestType: ActiveRequestProvider,
RequestType,
ReplyType,
> {
pub service_helper:
PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
pub request_router: GenericRequestRouter,
pub request_converter: RequestConverter,
pub active_request_map: ActiveRequestMap,
pub reply_hook: ReplyHook,
phantom: std::marker::PhantomData<RequestType>,
}
impl<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusTcToRequestConverter<RequestType>,
ReplyHandler: PusReplyHandler<ActiveRequestType, ReplyType>,
ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestType>,
ActiveRequestType: ActiveRequestProvider,
RequestType,
ReplyType,
>
PusTargetedRequestService<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
RequestConverter,
ReplyHandler,
ActiveRequestMap,
ActiveRequestType,
RequestType,
ReplyType,
>
{
pub fn new(
service_helper: PusServiceHelper<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
>,
request_converter: RequestConverter,
request_router: GenericRequestRouter,
active_request_map: ActiveRequestMap,
reply_hook: ReplyHandler,
) -> Self {
Self {
service_helper,
request_router,
request_converter,
active_request_map,
reply_hook,
phantom: std::marker::PhantomData,
}
}
pub fn handle_one_tc(&mut self) {
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 (active_request, 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(
active_request.target_id(),
action_request,
ecss_tc_and_token.token,
) {
let verif_request_id = verification::RequestId::new(&tc);
self.active_request_map
.insert(verif_request_id.into(), active_request);
self.request_router.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 fn insert_reply(&mut self, reply: &GenericMessage<ReplyType>) {
// self.reply_hook.insert_reply(reply, &self.active_request_map);
}
}
impl<VerificationReporter: VerificationReportingProvider> PusReceiver<VerificationReporter> {
pub fn handle_tc_packet(
&mut self,

View File

@ -103,8 +103,11 @@ impl<
}
}
pub fn handle_next_packet(&mut self) -> bool {
match self.pus_11_handler.handle_one_tc(&mut self.sched_tc_pool) {
pub fn handle_next_packet(&mut self, time_stamp: &[u8]) -> bool {
match self
.pus_11_handler
.handle_one_tc(time_stamp, &mut self.sched_tc_pool)
{
Ok(result) => match result {
PusPacketHandlerResult::RequestHandled => {}
PusPacketHandlerResult::RequestHandledPartialSuccess(e) => {

View File

@ -1,6 +1,9 @@
use satrs::pus::{
use satrs::{
pus::{
verification::VerificationReportingProvider, EcssTcInMemConverter, EcssTcReceiverCore,
EcssTmSenderCore,
},
spacepackets::time::{cds, TimeWriter},
};
use super::{
@ -51,6 +54,10 @@ impl<
pub fn periodic_operation(&mut self) {
self.schedule_srv.release_tcs();
let time_stamp = cds::TimeProvider::from_now_with_u16_days()
.expect("time stamp generation error")
.to_vec()
.unwrap();
loop {
let mut all_queues_empty = true;
let mut is_srv_finished = |srv_handler_finished: bool| {
@ -58,9 +65,9 @@ impl<
all_queues_empty = false;
}
};
is_srv_finished(self.test_srv.handle_next_packet());
is_srv_finished(self.schedule_srv.handle_next_packet());
is_srv_finished(self.event_srv.handle_next_packet());
is_srv_finished(self.test_srv.handle_next_packet(&time_stamp));
is_srv_finished(self.schedule_srv.handle_next_packet(&time_stamp));
is_srv_finished(self.event_srv.handle_next_packet(&time_stamp));
is_srv_finished(self.action_srv.handle_next_packet());
is_srv_finished(self.hk_srv.handle_next_packet());
if all_queues_empty {

View File

@ -111,8 +111,8 @@ impl<
VerificationReporter: VerificationReportingProvider,
> Service17CustomWrapper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
{
pub fn handle_next_packet(&mut self) -> bool {
let res = self.pus17_handler.handle_one_tc();
pub fn handle_next_packet(&mut self, time_stamp: &[u8]) -> bool {
let res = self.pus17_handler.handle_one_tc(time_stamp);
if res.is_err() {
warn!("PUS17 handler failed with error {:?}", res.unwrap_err());
return true;

View File

@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
use super::{
filestore::{FilestoreError, VirtualFilestore},
user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams},
CheckTimer, CheckTimerCreator, EntityType, LocalEntityConfig, PacketInfo, PacketTarget,
CheckTimerCreator, CountdownProvider, EntityType, LocalEntityConfig, PacketInfo, PacketTarget,
RemoteEntityConfig, RemoteEntityConfigProvider, State, TimerContext, TransactionId,
TransactionStep,
};
@ -54,7 +54,7 @@ struct TransferState {
completion_disposition: CompletionDisposition,
checksum: u32,
current_check_count: u32,
current_check_timer: Option<Box<dyn CheckTimer>>,
current_check_timer: Option<Box<dyn CountdownProvider>>,
}
impl Default for TransferState {
@ -799,9 +799,9 @@ mod tests {
};
use crate::cfdp::{
filestore::NativeFilestore, user::OwnedMetadataRecvdParams, CheckTimer, CheckTimerCreator,
DefaultFaultHandler, IndicationConfig, RemoteEntityConfig, StdRemoteEntityConfigProvider,
UserFaultHandler, CRC_32,
filestore::NativeFilestore, user::OwnedMetadataRecvdParams, CheckTimerCreator,
CountdownProvider, DefaultFaultHandler, IndicationConfig, RemoteEntityConfig,
StdRemoteEntityConfigProvider, UserFaultHandler, CRC_32,
};
use super::*;
@ -1057,7 +1057,7 @@ mod tests {
expired: Arc<AtomicBool>,
}
impl CheckTimer for TestCheckTimer {
impl CountdownProvider for TestCheckTimer {
fn has_expired(&self) -> bool {
self.expired.load(core::sync::atomic::Ordering::Relaxed)
}
@ -1088,7 +1088,10 @@ mod tests {
}
impl CheckTimerCreator for TestCheckTimerCreator {
fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box<dyn CheckTimer> {
fn get_check_timer_provider(
&self,
timer_context: TimerContext,
) -> Box<dyn CountdownProvider> {
match timer_context {
TimerContext::CheckLimit { .. } => {
Box::new(TestCheckTimer::new(self.check_limit_expired_flag.clone()))

View File

@ -17,6 +17,8 @@ use alloc::boxed::Box;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::time::CountdownProvider;
#[cfg(feature = "std")]
pub mod dest;
#[cfg(feature = "alloc")]
@ -45,7 +47,15 @@ pub enum TimerContext {
},
}
/// Generic abstraction for a check timer which is used by 3 mechanisms of the CFDP protocol.
/// A generic trait which allows CFDP entities to create check timers which are required to
/// implement special procedures in unacknowledged transmission mode, as specified in 4.6.3.2
/// and 4.6.3.3.
///
/// This trait also allows the creation of different check timers depending on context and purpose
/// of the timer, the runtime environment (e.g. standard clock timer vs. timer using a RTC) or
/// other factors.
///
/// The countdown timer is used by 3 mechanisms of the CFDP protocol.
///
/// ## 1. Check limit handling
///
@ -74,22 +84,9 @@ pub enum TimerContext {
/// The timer will be used to perform the Positive Acknowledgement Procedures as specified in
/// 4.7. 1of the CFDP standard. The expiration period will be provided by the Positive ACK timer
/// interval of the remote entity configuration.
pub trait CheckTimer: Debug {
fn has_expired(&self) -> bool;
fn reset(&mut self);
}
/// A generic trait which allows CFDP entities to create check timers which are required to
/// implement special procedures in unacknowledged transmission mode, as specified in 4.6.3.2
/// and 4.6.3.3. The [CheckTimer] documentation provides more information about the purpose of the
/// check timer in the context of CFDP.
///
/// This trait also allows the creation of different check timers depending on context and purpose
/// of the timer, the runtime environment (e.g. standard clock timer vs. timer using a RTC) or
/// other factors.
#[cfg(feature = "alloc")]
pub trait CheckTimerCreator {
fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box<dyn CheckTimer>;
fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box<dyn CountdownProvider>;
}
/// Simple implementation of the [CheckTimerCreator] trait assuming a standard runtime.
@ -112,7 +109,7 @@ impl StdCheckTimer {
}
#[cfg(feature = "std")]
impl CheckTimer for StdCheckTimer {
impl CountdownProvider for StdCheckTimer {
fn has_expired(&self) -> bool {
let elapsed_time = self.start_time.elapsed();
if elapsed_time.as_secs() > self.expiry_time_seconds {

View File

@ -43,6 +43,7 @@ pub mod queue;
pub mod request;
pub mod res_code;
pub mod seq_count;
pub mod time;
pub mod tmtc;
pub mod action;

View File

@ -105,7 +105,6 @@ pub mod std_mod {
self, FailParams, FailParamsWithStep, TcStateStarted, VerificationReportingProvider,
},
ActiveRequestMapProvider, DefaultActiveRequestMap, EcssTmSenderCore, EcssTmtcError,
GenericRoutingError, PusServiceReplyHandler, PusTargetedRequestHandler,
ReplyHandlerHook,
},
};
@ -115,27 +114,9 @@ pub mod std_mod {
use super::*;
pub type PusService8ActionRequestHandler<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
RequestConverter,
RequestRouter,
RoutingError = GenericRoutingError,
> = PusTargetedRequestHandler<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
RequestConverter,
RequestRouter,
ActionRequest,
RoutingError,
>;
pub type DefaultActiveActionRequestMap = DefaultActiveRequestMap<ActiveActionRequest>;
/*
/// Type definition for a PUS 8 action service reply handler which constrains the
/// [PusServiceReplyHandler] active request and reply generics to the [ActiveActionRequest] and
/// [ActionReplyPusWithIds] type.
@ -299,6 +280,7 @@ pub mod std_mod {
)
}
}
*/
}
#[cfg(test)]
@ -311,11 +293,12 @@ mod tests {
use spacepackets::{
ecss::{
tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader},
tc::{PusTcCreator, PusTcReader},
tm::PusTmReader,
PusPacket,
},
CcsdsPacket, SequenceFlags, SpHeader,
time::{cds, TimeWriter},
CcsdsPacket,
};
use crate::{
@ -324,7 +307,7 @@ mod tests {
pus::{
tests::{
PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler,
TestConverter, TestRouter, APP_DATA_TOO_SHORT, TEST_APID,
TestConverter, TestRouter, APP_DATA_TOO_SHORT,
},
verification::{
self,
@ -332,9 +315,9 @@ mod tests {
FailParams, TcStateAccepted, TcStateNone, TcStateStarted,
VerificationReportingProvider,
},
EcssTcInVecConverter, EcssTmtcError, GenericRoutingError, MpscTcReceiver,
PusPacketHandlerResult, PusPacketHandlingError, PusRequestRouter,
PusTcToRequestConverter, ReplyHandlerHook, TmAsVecSenderWithMpsc,
EcssTcInMemConverter, EcssTcInVecConverter, EcssTmtcError, GenericRoutingError,
MpscTcReceiver, PusPacketHandlerResult, PusPacketHandlingError, PusRequestRouter,
PusServiceHelper, PusTcToRequestConverter, ReplyHandlerHook, TmAsVecSenderWithMpsc,
},
};
@ -416,29 +399,32 @@ mod tests {
}
}
struct Pus8RequestTestbenchWithVec {
common: PusServiceHandlerWithVecCommon<TestVerificationReporter>,
handler: PusService8ActionRequestHandler<
pub struct PusDynRequestHandler<const SERVICE: u8, Request> {
srv_helper: PusServiceHelper<
MpscTcReceiver,
TmAsVecSenderWithMpsc,
EcssTcInVecConverter,
TestVerificationReporter,
TestConverter<8>,
TestRouter<ActionRequest>,
>,
request_converter: TestConverter<SERVICE>,
request_router: TestRouter<Request>,
}
struct Pus8RequestTestbenchWithVec {
common: PusServiceHandlerWithVecCommon<TestVerificationReporter>,
handler: PusDynRequestHandler<8, ActionRequest>,
}
impl Pus8RequestTestbenchWithVec {
pub fn new() -> Self {
let (common, srv_handler) =
PusServiceHandlerWithVecCommon::new_with_test_verif_sender();
let (common, srv_helper) = PusServiceHandlerWithVecCommon::new_with_test_verif_sender();
Self {
common,
handler: PusService8ActionRequestHandler::new(
srv_handler,
TestConverter::default(),
TestRouter::default(),
),
handler: PusDynRequestHandler {
srv_helper,
request_converter: TestConverter::default(),
request_router: TestRouter::default(),
},
}
}
@ -474,10 +460,43 @@ mod tests {
}
}
impl SimplePusPacketHandler for Pus8RequestTestbenchWithVec {
delegate! {
to self.handler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.handler.srv_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
.handler
.srv_helper
.tc_in_mem_converter
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
let time_stamp = cds::TimeProvider::from_now_with_u16_days()
.expect("timestamp generation failed")
.to_vec()
.unwrap();
let (target_id, action_request) = self.handler.request_converter.convert(
ecss_tc_and_token.token,
&tc,
&time_stamp,
&self.handler.srv_helper.common.verification_handler,
)?;
if let Err(e) = self.handler.request_router.route(
target_id,
action_request,
ecss_tc_and_token.token,
) {
self.handler.request_router.handle_error(
target_id,
ecss_tc_and_token.token,
&tc,
e.clone(),
&time_stamp,
&self.handler.srv_helper.common.verification_handler,
);
return Err(e.into());
}
Ok(PusPacketHandlerResult::RequestHandled)
}
}
@ -667,68 +686,6 @@ mod tests {
}
}
#[test]
fn basic_test() {
let mut action_handler = Pus8RequestTestbenchWithVec::new();
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
let sec_header = PusTcSecondaryHeader::new_simple(8, 1);
let action_id: u32 = 1;
let action_id_raw = action_id.to_be_bytes();
let tc = PusTcCreator::new(&mut sp_header, sec_header, action_id_raw.as_ref(), true);
action_handler.send_tc(&tc);
let result = action_handler.handle_one_tc();
assert!(result.is_ok());
action_handler.check_next_conversion(&tc);
let (target_id, action_req) = action_handler.retrieve_next_request();
assert_eq!(target_id, TEST_APID.into());
assert_eq!(action_req.action_id, 1);
if let ActionRequestVariant::VecData(data) = action_req.variant {
assert_eq!(data, &[]);
}
}
#[test]
fn test_routing_error() {
let mut action_handler = Pus8RequestTestbenchWithVec::new();
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
let sec_header = PusTcSecondaryHeader::new_simple(8, 1);
let action_id: u32 = 1;
let action_id_raw = action_id.to_be_bytes();
let tc = PusTcCreator::new(&mut sp_header, sec_header, action_id_raw.as_ref(), true);
let error = GenericRoutingError::UnknownTargetId(25);
action_handler
.handler
.request_router
.inject_routing_error(error);
action_handler.send_tc(&tc);
let result = action_handler.handle_one_tc();
assert!(result.is_err());
let check_error = |routing_error: GenericRoutingError| {
if let GenericRoutingError::UnknownTargetId(id) = routing_error {
assert_eq!(id, 25);
} else {
panic!("unexpected error type");
}
};
if let PusPacketHandlingError::RequestRoutingError(routing_error) = result.unwrap_err() {
check_error(routing_error);
} else {
panic!("unexpected error type");
}
action_handler.check_next_conversion(&tc);
let (target_id, action_req) = action_handler.retrieve_next_request();
assert_eq!(target_id, TEST_APID.into());
assert_eq!(action_req.action_id, 1);
if let ActionRequestVariant::VecData(data) = action_req.variant {
assert_eq!(data, &[]);
}
let (target_id, found_error) = action_handler.retrieve_next_routing_error();
assert_eq!(target_id, TEST_APID.into());
check_error(found_error);
}
#[test]
fn test_reply_handler_completion_success() {
let mut reply_testbench = Pus8ReplyTestbench::new(true);

View File

@ -7,10 +7,7 @@ use spacepackets::ecss::PusPacket;
use std::sync::mpsc::Sender;
use super::verification::VerificationReportingProvider;
use super::{
get_current_cds_short_timestamp, EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore,
PusServiceHelper,
};
use super::{EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore, PusServiceHelper};
pub struct PusService5EventHandler<
TcReceiver: EcssTcReceiverCore,
@ -45,7 +42,10 @@ impl<
}
}
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
pub fn handle_one_tc(
&mut self,
time_stamp: &[u8],
) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
if possible_packet.is_none() {
return Ok(PusPacketHandlerResult::Empty);
@ -63,7 +63,7 @@ impl<
ecss_tc_and_token.token,
));
}
let handle_enable_disable_request = |enable: bool, stamp: [u8; 7]| {
let handle_enable_disable_request = |enable: bool| {
if tc.user_data().len() < 4 {
return Err(PusPacketHandlingError::NotEnoughAppData {
expected: 4,
@ -76,7 +76,7 @@ impl<
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &stamp)
.start_success(ecss_tc_and_token.token, time_stamp)
.map_err(|_| PartialPusHandlingError::Verification);
let partial_error = start_token.clone().err();
let mut token: TcStateToken = ecss_tc_and_token.token.into();
@ -106,8 +106,6 @@ impl<
}
Ok(PusPacketHandlerResult::RequestHandled)
};
let mut partial_error = None;
let time_stamp = get_current_cds_short_timestamp(&mut partial_error);
match srv.unwrap() {
Subservice::TmInfoReport
| Subservice::TmLowSeverityReport
@ -116,10 +114,10 @@ impl<
return Err(PusPacketHandlingError::InvalidSubservice(tc.subservice()))
}
Subservice::TcEnableEventGeneration => {
handle_enable_disable_request(true, time_stamp)?;
handle_enable_disable_request(true)?;
}
Subservice::TcDisableEventGeneration => {
handle_enable_disable_request(false, time_stamp)?;
handle_enable_disable_request(false)?;
}
Subservice::TcReportDisabledList | Subservice::TmDisabledEventsReport => {
return Ok(PusPacketHandlerResult::SubserviceNotImplemented(
@ -137,6 +135,7 @@ impl<
mod tests {
use delegate::delegate;
use spacepackets::ecss::event::Subservice;
use spacepackets::time::{cds, TimeWriter};
use spacepackets::util::UnsignedEnum;
use spacepackets::{
ecss::{
@ -200,10 +199,9 @@ mod tests {
}
impl SimplePusPacketHandler for Pus5HandlerWithStoreTester {
delegate! {
to self.handler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
}
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::TimeProvider::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler.handle_one_tc(&time_stamp)
}
}

View File

@ -1,280 +0,0 @@
pub use spacepackets::ecss::hk::*;
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub use std_mod::*;
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[allow(unused_imports)]
pub use alloc_mod::*;
use crate::{hk::HkRequest, TargetId};
use super::verification::{TcStateAccepted, VerificationToken};
/// This trait is an abstraction for the routing of PUS service 3 housekeeping requests to a
/// dedicated recipient using the generic [TargetId].
pub trait PusHkRequestRouter {
type Error;
fn route(
&self,
target_id: TargetId,
hk_request: HkRequest,
token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error>;
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub mod alloc_mod {}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub mod std_mod {
use crate::pus::{GenericRoutingError, PusTargetedRequestHandler};
use super::*;
pub type PusService3HkRequestHandler<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
RequestConverter,
RequestRouter,
RoutingError = GenericRoutingError,
> = PusTargetedRequestHandler<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
RequestConverter,
RequestRouter,
HkRequest,
RoutingError,
>;
}
#[cfg(test)]
mod tests {
use delegate::delegate;
use spacepackets::ecss::hk::Subservice;
use spacepackets::{
ecss::{
tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader},
tm::PusTmReader,
PusPacket,
},
CcsdsPacket, SequenceFlags, SpHeader,
};
use crate::pus::{MpscTcReceiver, PusTcToRequestConverter, TmAsVecSenderWithMpsc};
use crate::{
hk::HkRequest,
pus::{
tests::{
PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler,
TestConverter, TestRouter, APP_DATA_TOO_SHORT, TEST_APID,
},
verification::{
tests::TestVerificationReporter, FailParams, RequestId, TcStateAccepted,
VerificationReportingProvider, VerificationToken,
},
EcssTcInVecConverter, GenericRoutingError, PusPacketHandlerResult,
PusPacketHandlingError,
},
TargetId,
};
use super::{PusHkRequestRouter, PusService3HkRequestHandler};
impl PusHkRequestRouter for TestRouter<HkRequest> {
type Error = GenericRoutingError;
fn route(
&self,
target_id: TargetId,
hk_request: HkRequest,
_token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error> {
self.routing_requests
.borrow_mut()
.push_back((target_id, hk_request));
self.check_for_injected_error()
}
}
impl PusTcToRequestConverter<HkRequest> for TestConverter<3> {
type Error = PusPacketHandlingError;
fn convert(
&mut self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, HkRequest), Self::Error> {
self.conversion_request.push_back(tc.raw_data().to_vec());
self.check_service(tc)?;
let target_id = tc.apid();
if tc.user_data().len() < 4 {
verif_reporter
.start_failure(
token,
FailParams::new(
time_stamp,
&APP_DATA_TOO_SHORT,
(tc.user_data().len() as u32).to_be_bytes().as_ref(),
),
)
.expect("start success failure");
return Err(PusPacketHandlingError::NotEnoughAppData {
expected: 4,
found: tc.user_data().len(),
});
}
if tc.subservice() == Subservice::TcGenerateOneShotHk as u8 {
verif_reporter
.start_success(token, time_stamp)
.expect("start success failure");
return Ok((
target_id.into(),
HkRequest::OneShot(u32::from_be_bytes(
tc.user_data()[0..4].try_into().unwrap(),
)),
));
}
Err(PusPacketHandlingError::InvalidAppData(
"unexpected app data".into(),
))
}
}
struct Pus3HandlerWithVecTester {
common: PusServiceHandlerWithVecCommon<TestVerificationReporter>,
handler: PusService3HkRequestHandler<
MpscTcReceiver,
TmAsVecSenderWithMpsc,
EcssTcInVecConverter,
TestVerificationReporter,
TestConverter<3>,
TestRouter<HkRequest>,
>,
}
impl Pus3HandlerWithVecTester {
pub fn new() -> Self {
let (common, srv_handler) =
PusServiceHandlerWithVecCommon::new_with_test_verif_sender();
Self {
common,
handler: PusService3HkRequestHandler::new(
srv_handler,
TestConverter::default(),
TestRouter::default(),
),
}
}
delegate! {
to self.handler.request_converter {
pub fn check_next_conversion(&mut self, tc: &PusTcCreator);
}
}
delegate! {
to self.handler.request_router {
pub fn retrieve_next_request(&mut self) -> (TargetId, HkRequest);
}
}
delegate! {
to self.handler.request_router {
pub fn retrieve_next_routing_error(&mut self) -> (TargetId, GenericRoutingError);
}
}
}
impl PusTestHarness for Pus3HandlerWithVecTester {
delegate! {
to self.common {
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
fn read_next_tm(&mut self) -> PusTmReader<'_>;
fn check_no_tm_available(&self) -> bool;
fn check_next_verification_tm(
&self,
subservice: u8,
expected_request_id: RequestId,
);
}
}
}
impl SimplePusPacketHandler for Pus3HandlerWithVecTester {
delegate! {
to self.handler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
}
}
}
#[test]
fn basic_test() {
let mut hk_handler = Pus3HandlerWithVecTester::new();
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
let sec_header = PusTcSecondaryHeader::new_simple(3, Subservice::TcGenerateOneShotHk as u8);
let unique_id: u32 = 1;
let unique_id_raw = unique_id.to_be_bytes();
let tc = PusTcCreator::new(&mut sp_header, sec_header, unique_id_raw.as_ref(), true);
hk_handler.send_tc(&tc);
let result = hk_handler.handle_one_tc();
assert!(result.is_ok());
hk_handler.check_next_conversion(&tc);
let (target_id, hk_request) = hk_handler.retrieve_next_request();
assert_eq!(target_id, TEST_APID.into());
if let HkRequest::OneShot(id) = hk_request {
assert_eq!(id, unique_id);
} else {
panic!("unexpected request");
}
}
#[test]
fn test_routing_error() {
let mut hk_handler = Pus3HandlerWithVecTester::new();
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
let sec_header = PusTcSecondaryHeader::new_simple(3, Subservice::TcGenerateOneShotHk as u8);
let unique_id: u32 = 1;
let unique_id_raw = unique_id.to_be_bytes();
let tc = PusTcCreator::new(&mut sp_header, sec_header, unique_id_raw.as_ref(), true);
let error = GenericRoutingError::UnknownTargetId(25);
hk_handler
.handler
.request_router
.inject_routing_error(error);
hk_handler.send_tc(&tc);
let result = hk_handler.handle_one_tc();
assert!(result.is_err());
let check_error = |routing_error: GenericRoutingError| {
if let GenericRoutingError::UnknownTargetId(id) = routing_error {
assert_eq!(id, 25);
} else {
panic!("unexpected error type");
}
};
if let PusPacketHandlingError::RequestRoutingError(routing_error) = result.unwrap_err() {
check_error(routing_error);
} else {
panic!("unexpected error type");
}
hk_handler.check_next_conversion(&tc);
let (target_id, hk_req) = hk_handler.retrieve_next_request();
assert_eq!(target_id, TEST_APID.into());
if let HkRequest::OneShot(unique_id) = hk_req {
assert_eq!(unique_id, 1);
}
let (target_id, found_error) = hk_handler.retrieve_next_routing_error();
assert_eq!(target_id, TEST_APID.into());
check_error(found_error);
}
}

View File

@ -29,7 +29,6 @@ pub mod event;
pub mod event_man;
#[cfg(feature = "std")]
pub mod event_srv;
pub mod hk;
pub mod mode;
pub mod scheduler;
#[cfg(feature = "std")]
@ -44,7 +43,7 @@ pub use alloc_mod::*;
#[cfg(feature = "std")]
pub use std_mod::*;
use self::verification::{FailParams, TcStateStarted, VerificationReportingProvider};
use self::verification::{TcStateStarted, VerificationReportingProvider};
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PusTmWrapper<'tm> {
@ -298,14 +297,14 @@ pub trait ActiveRequestProvider {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ActiveRequest {
pub struct ActivePusRequest {
target_id: TargetId,
token: VerificationToken<TcStateStarted>,
start_time: UnixTimestamp,
timeout: Duration,
}
impl ActiveRequestProvider for ActiveRequest {
impl ActiveRequestProvider for ActivePusRequest {
fn target_id(&self) -> TargetId {
self.target_id
}
@ -323,30 +322,18 @@ impl ActiveRequestProvider for ActiveRequest {
}
}
/// Generic user hook method.
///
/// This hook method currently serves the following tasks:
///
/// 1. Pass specific information to the reply handlers which can not be kept inside the
/// framework. This includes information like the error codes used for packet verification.
/// 2. It exposes callback methods which can be useful to perform custom user operations like
/// logging.
pub trait ReplyHandlerHook<ActiveRequestType, ReplyType> {
fn handle_unexpected_reply(&mut self, reply: &GenericMessage<ReplyType>);
fn timeout_callback(&self, active_request: &ActiveRequestType);
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>;
fn handle_error(
&self,
target_id: TargetId,
@ -358,6 +345,23 @@ pub trait PusRequestRouter<Request> {
);
}
pub trait PusReplyHandler<ActiveRequestInfo: ActiveRequestProvider, ReplyType> {
type Error;
fn handle_reply(
&mut self,
reply: &GenericMessage<ReplyType>,
verification_handler: &impl VerificationReportingProvider,
tm_sender: &impl EcssTmSenderCore,
) -> Result<bool, Self::Error>;
fn handle_unexpected_reply(
&mut self,
reply: &GenericMessage<ReplyType>,
tm_sender: &impl EcssTmSenderCore,
);
}
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use hashbrown::HashMap;
@ -463,7 +467,7 @@ pub mod alloc_mod {
///
/// 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> {
pub trait PusTcToRequestConverter<ActiveRequestInfo, Request> {
type Error;
fn convert(
&mut self,
@ -471,7 +475,7 @@ pub mod alloc_mod {
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, Request), Self::Error>;
) -> Result<(ActiveRequestInfo, Request), Self::Error>;
}
#[derive(Clone, Debug)]
@ -513,7 +517,9 @@ pub mod alloc_mod {
}
}
/// Generic reply handler structure which can be used to handle replies for a specific PUS service.
/*
/// Generic reply handler structure which can be used to handle replies for a specific PUS
/// service.
///
/// This is done by keeping track of active requests using an internal map structure. An API
/// to register new active requests is exposed as well.
@ -524,42 +530,35 @@ pub mod alloc_mod {
/// PUS reply handlers. Concrete PUS handlers should constrain the [ActiveRequestProvider] and
/// the `ReplyType` generics to specific types tailored towards PUS services in addition to
/// providing an API which can process received replies and convert them into verification
/// completions or other operation like user hook calls. The framework also provides some concrete
/// PUS handlers for common PUS services like the mode, action and housekeeping service.
/// completions or other operation like user hook calls. The framework also provides some
/// concrete PUS handlers for common PUS services like the mode, action and housekeeping
/// service.
///
/// This object does not automatically update its internal time information used to check for
/// timeouts. The user should call the [Self::update_time] and [Self::update_time_from_now] methods
/// to do this.
/// timeouts. The user should call the [Self::update_time] and [Self::update_time_from_now]
/// methods to do this.
pub struct PusServiceReplyHandler<
VerificationReporter: VerificationReportingProvider,
ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestType>,
UserHook: ReplyHandlerHook<ActiveRequestType, ReplyType>,
TmSender: EcssTmSenderCore,
ReplyHook: ReplyHandlerHook<ActiveRequestType, ReplyType>,
ActiveRequestType: ActiveRequestProvider,
ReplyType,
> {
pub active_request_map: ActiveRequestMap,
pub verification_reporter: VerificationReporter,
pub tm_buf: alloc::vec::Vec<u8>,
pub current_time: UnixTimestamp,
pub user_hook: UserHook,
pub tm_sender: TmSender,
pub user_hook: ReplyHook,
phantom: PhantomData<(ActiveRequestType, ReplyType)>,
}
impl<
VerificationReporter: VerificationReportingProvider,
ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestType>,
UserHook: ReplyHandlerHook<ActiveRequestType, ReplyType>,
TmSender: EcssTmSenderCore,
ReplyHook: ReplyHandlerHook<ActiveRequestType, ReplyType>,
ActiveRequestType: ActiveRequestProvider,
ReplyType,
>
PusServiceReplyHandler<
VerificationReporter,
ActiveRequestMap,
UserHook,
TmSender,
ReplyHook,
ActiveRequestType,
ReplyType,
>
@ -567,15 +566,12 @@ pub mod alloc_mod {
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn new_from_now(
verification_reporter: VerificationReporter,
active_request_map: ActiveRequestMap,
fail_data_buf_size: usize,
user_hook: UserHook,
tm_sender: TmSender,
user_hook: ReplyHook,
) -> Result<Self, std::time::SystemTimeError> {
let current_time = UnixTimestamp::from_now()?;
Ok(Self::new(
verification_reporter,
active_request_map,
fail_data_buf_size,
user_hook,
@ -585,16 +581,14 @@ pub mod alloc_mod {
}
pub fn new(
verification_reporter: VerificationReporter,
active_request_map: ActiveRequestMap,
fail_data_buf_size: usize,
user_hook: UserHook,
user_hook: ReplyHook,
tm_sender: TmSender,
init_time: UnixTimestamp,
) -> Self {
Self {
active_request_map,
verification_reporter,
tm_buf: alloc::vec![0; fail_data_buf_size],
current_time: init_time,
user_hook,
@ -668,6 +662,7 @@ pub mod alloc_mod {
self.current_time = time;
}
}
*/
}
#[cfg(feature = "std")]
@ -684,13 +679,10 @@ pub mod std_mod {
use crate::tmtc::tm_helper::SharedTmPool;
use crate::{ChannelId, TargetId};
use alloc::vec::Vec;
use core::marker::PhantomData;
use spacepackets::ecss::tc::PusTcReader;
use spacepackets::ecss::tm::PusTmCreator;
use spacepackets::ecss::{PusError, WritablePusPacket};
use spacepackets::time::cds::TimeProvider;
use spacepackets::time::StdTimestampError;
use spacepackets::time::TimeWriter;
use std::string::String;
use std::sync::mpsc;
use std::sync::mpsc::TryRecvError;
@ -700,7 +692,7 @@ pub mod std_mod {
pub use cb_mod::*;
use super::verification::VerificationReportingProvider;
use super::{AcceptedEcssTcAndToken, PusRequestRouter, PusTcToRequestConverter, TcInMemory};
use super::{AcceptedEcssTcAndToken, TcInMemory};
impl From<mpsc::SendError<StoreAddr>> for EcssTmtcError {
fn from(_: mpsc::SendError<StoreAddr>) -> Self {
@ -974,17 +966,18 @@ pub mod std_mod {
}
}
/// This is a high-level handler for the PUS service 8 action service.
/// This is a high-level handler for the generic PUS services which need to convert PUS
/// commands into a request/reply pattern.
///
/// 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
/// [PusTcToRequestConverter]. 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].
/// 3. Route the action request using the provided [PusRequestRouter].
/*
pub struct PusTargetedRequestHandler<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
@ -999,10 +992,12 @@ pub mod std_mod {
PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
pub request_converter: RequestConverter,
pub request_router: RequestRouter,
// pub routing_error_handler: RoutingErrorHandler,
phantom: PhantomData<Request>,
}
// pub trait PusReplyHandlerProvider {
// fn add_routed_request(&mut self, request_id: RequestId, active_request: ActiveRequest);
// }
impl<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
@ -1010,7 +1005,6 @@ pub mod std_mod {
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusTcToRequestConverter<Request, Error = PusPacketHandlingError>,
RequestRouter: PusRequestRouter<Request, Error = RoutingError>,
// RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
Request,
RoutingError: Clone,
>
@ -1021,7 +1015,6 @@ pub mod std_mod {
VerificationReporter,
RequestConverter,
RequestRouter,
// RoutingErrorHandler,
Request,
RoutingError,
>
@ -1042,7 +1035,6 @@ pub mod std_mod {
service_helper,
request_converter,
request_router,
// routing_error_handler,
phantom: PhantomData,
}
}
@ -1083,7 +1075,7 @@ pub mod std_mod {
Ok(PusPacketHandlerResult::RequestHandled)
}
}
*/
// 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
// will be no_std soon, see https://github.com/rust-lang/rust/issues/103765 .
@ -1272,26 +1264,6 @@ pub mod std_mod {
pub tm_apid: u16,
pub verification_handler: VerificationReporter,
}
#[cfg(feature = "std")]
pub fn get_current_cds_short_timestamp(
partial_error: &mut Option<PartialPusHandlingError>,
) -> [u8; 7] {
let mut time_stamp: [u8; 7] = [0; 7];
let time_provider =
TimeProvider::from_now_with_u16_days().map_err(PartialPusHandlingError::Time);
if let Ok(time_provider) = time_provider {
// Can't fail, we have a buffer with the exact required size.
time_provider.write_to_bytes(&mut time_stamp).unwrap();
} else {
*partial_error = Some(time_provider.unwrap_err());
}
time_stamp
}
#[cfg(feature = "std")]
pub fn get_current_timestamp_ignore_error() -> [u8; 7] {
let mut dummy = None;
get_current_cds_short_timestamp(&mut dummy)
}
/// This is a high-level PUS packet handler helper.
///

View File

@ -43,45 +43,25 @@ pub mod std_mod {
};
use crate::{
mode::{GenericModeReply, ModeRequest},
mode::GenericModeReply,
pus::{
mode::Subservice,
verification::{
self, FailParams, TcStateStarted, VerificationReportingProvider, VerificationToken,
},
ActiveRequest, ActiveRequestMapProvider, EcssTmSenderCore, EcssTmtcError,
GenericRoutingError, PusServiceReplyHandler, PusTargetedRequestHandler, PusTmWrapper,
ReplyHandlerHook,
ActivePusRequest, ActiveRequestMapProvider, EcssTmSenderCore, EcssTmtcError,
PusServiceReplyHandler, PusTmWrapper, ReplyHandlerHook,
},
TargetId,
};
pub trait ModeReplyHook: ReplyHandlerHook<ActiveRequest, ModeReply> {
pub trait ModeReplyHook: ReplyHandlerHook<ActivePusRequest, ModeReply> {
fn wrong_mode_result_code(&self) -> ResultU16;
fn can_not_reach_mode_result_code(&self) -> ResultU16;
}
use super::{ModeReply, MODE_SERVICE_ID};
pub type PusModeServiceRequestHandler<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
RequestConverter,
RequestRouter,
RoutingError = GenericRoutingError,
> = PusTargetedRequestHandler<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
RequestConverter,
RequestRouter,
ModeRequest,
RoutingError,
>;
/// Type definition for a PUS mode servicd reply handler which constrains the
/// [PusServiceReplyHandler] active request and reply generics to the [ActiveActionRequest] and
/// [ActionReplyPusWithIds] type.
@ -95,13 +75,13 @@ pub mod std_mod {
ActiveRequestMap,
UserHook,
TmSender,
ActiveRequest,
ActivePusRequest,
ModeReply,
>;
impl<
VerificationReporter: VerificationReportingProvider,
ActiveRequestMap: ActiveRequestMapProvider<ActiveRequest>,
ActiveRequestMap: ActiveRequestMapProvider<ActivePusRequest>,
UserHook: ModeReplyHook,
TmSender: EcssTmSenderCore,
> PusModeServiceReplyHandler<VerificationReporter, ActiveRequestMap, UserHook, TmSender>
@ -116,7 +96,7 @@ pub mod std_mod {
) {
self.active_request_map.insert(
&request_id.into(),
ActiveRequest {
ActivePusRequest {
target_id,
token,
start_time: self.current_time,

View File

@ -5,10 +5,9 @@ use super::verification::{
VerificationReporterWithVecMpscSender, VerificationReportingProvider,
};
use super::{
get_current_cds_short_timestamp, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
EcssTcInVecConverter, EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusServiceHelper,
TmAsVecSenderWithBoundedMpsc, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc,
TmInSharedPoolSenderWithMpsc,
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiverCore,
EcssTmSenderCore, MpscTcReceiver, PusServiceHelper, TmAsVecSenderWithBoundedMpsc,
TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc, TmInSharedPoolSenderWithMpsc,
};
use crate::pool::PoolProvider;
use crate::pus::{PusPacketHandlerResult, PusPacketHandlingError};
@ -76,6 +75,7 @@ impl<
pub fn handle_one_tc(
&mut self,
time_stamp: &[u8],
sched_tc_pool: &mut (impl PoolProvider + ?Sized),
) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
@ -95,15 +95,14 @@ impl<
ecss_tc_and_token.token,
));
}
let mut partial_error = None;
let time_stamp = get_current_cds_short_timestamp(&mut partial_error);
let partial_error = None;
match standard_subservice.unwrap() {
scheduling::Subservice::TcEnableScheduling => {
let start_token = self
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.start_success(ecss_tc_and_token.token, time_stamp)
.expect("Error sending start success");
self.scheduler.enable();
@ -111,7 +110,7 @@ impl<
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.completion_success(start_token, time_stamp)
.expect("Error sending completion success");
} else {
return Err(PusPacketHandlingError::Other(
@ -124,7 +123,7 @@ impl<
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.start_success(ecss_tc_and_token.token, time_stamp)
.expect("Error sending start success");
self.scheduler.disable();
@ -132,7 +131,7 @@ impl<
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.completion_success(start_token, time_stamp)
.expect("Error sending completion success");
} else {
return Err(PusPacketHandlingError::Other(
@ -145,7 +144,7 @@ impl<
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.start_success(ecss_tc_and_token.token, time_stamp)
.expect("Error sending start success");
self.scheduler
@ -155,7 +154,7 @@ impl<
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.completion_success(start_token, time_stamp)
.expect("Error sending completion success");
}
scheduling::Subservice::TcInsertActivity => {
@ -163,7 +162,7 @@ impl<
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.start_success(ecss_tc_and_token.token, time_stamp)
.expect("error sending start success");
// let mut pool = self.sched_tc_pool.write().expect("locking pool failed");
@ -174,7 +173,7 @@ impl<
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.completion_success(start_token, time_stamp)
.expect("sending completion success failed");
}
_ => {
@ -241,7 +240,10 @@ mod tests {
verification::{RequestId, TcStateAccepted, VerificationToken},
EcssTcInSharedStoreConverter,
};
use crate::pus::{MpscTcReceiver, TmInSharedPoolSenderWithBoundedMpsc};
use crate::pus::{
MpscTcReceiver, PusPacketHandlerResult, PusPacketHandlingError,
TmInSharedPoolSenderWithBoundedMpsc,
};
use alloc::collections::VecDeque;
use delegate::delegate;
use spacepackets::ecss::scheduling::Subservice;
@ -280,6 +282,12 @@ mod tests {
sched_tc_pool,
}
}
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::TimeProvider::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler
.handle_one_tc(&time_stamp, &mut self.sched_tc_pool)
}
}
impl PusTestHarness for Pus11HandlerWithStoreTester {
@ -347,9 +355,10 @@ mod tests {
let token = test_harness.send_tc(&enable_scheduling);
let request_id = token.req_id();
let time_stamp = cds::TimeProvider::new_with_u16_days(0, 0).to_vec().unwrap();
test_harness
.handler
.handle_one_tc(&mut test_harness.sched_tc_pool)
.handle_one_tc(&time_stamp, &mut test_harness.sched_tc_pool)
.unwrap();
test_harness.check_next_verification_tm(1, request_id);
test_harness.check_next_verification_tm(3, request_id);
@ -407,10 +416,7 @@ mod tests {
let token = test_harness.send_tc(&enable_scheduling);
let request_id = token.req_id();
test_harness
.handler
.handle_one_tc(&mut test_harness.sched_tc_pool)
.unwrap();
test_harness.handle_one_tc().unwrap();
test_harness.check_next_verification_tm(1, request_id);
test_harness.check_next_verification_tm(3, request_id);
test_harness.check_next_verification_tm(7, request_id);

View File

@ -11,10 +11,9 @@ use super::verification::{
VerificationReporterWithVecMpscSender, VerificationReportingProvider,
};
use super::{
get_current_cds_short_timestamp, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
EcssTcInVecConverter, EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusServiceHelper,
TmAsVecSenderWithBoundedMpsc, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc,
TmInSharedPoolSenderWithMpsc,
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiverCore,
EcssTmSenderCore, MpscTcReceiver, PusServiceHelper, TmAsVecSenderWithBoundedMpsc,
TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc, TmInSharedPoolSenderWithMpsc,
};
/// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets.
@ -47,7 +46,10 @@ impl<
Self { service_helper }
}
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
pub fn handle_one_tc(
&mut self,
time_stamp: &[u8],
) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
if possible_packet.is_none() {
return Ok(PusPacketHandlerResult::Empty);
@ -62,12 +64,11 @@ impl<
}
if tc.subservice() == 1 {
let mut partial_error = None;
let time_stamp = get_current_cds_short_timestamp(&mut partial_error);
let result = self
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.start_success(ecss_tc_and_token.token, time_stamp)
.map_err(|_| PartialPusHandlingError::Verification);
let start_token = if let Ok(result) = result {
Some(result)
@ -78,7 +79,7 @@ impl<
// Sequence count will be handled centrally in TM funnel.
let mut reply_header =
SpHeader::tm_unseg(self.service_helper.common.tm_apid, 0, 0).unwrap();
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &time_stamp);
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, time_stamp);
let ping_reply = PusTmCreator::new(&mut reply_header, tc_header, &[], true);
let result = self
.service_helper
@ -95,7 +96,7 @@ impl<
.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.completion_success(start_token, time_stamp)
.is_err()
{
partial_error = Some(PartialPusHandlingError::Verification)
@ -168,6 +169,7 @@ mod tests {
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
use spacepackets::ecss::tm::PusTmReader;
use spacepackets::ecss::PusPacket;
use spacepackets::time::{cds, TimeWriter};
use spacepackets::{SequenceFlags, SpHeader};
use super::PusService17TestHandler;
@ -208,10 +210,9 @@ mod tests {
}
}
impl SimplePusPacketHandler for Pus17HandlerWithStoreTester {
delegate! {
to self.handler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
}
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::TimeProvider::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler.handle_one_tc(&time_stamp)
}
}
@ -251,10 +252,9 @@ mod tests {
}
}
impl SimplePusPacketHandler for Pus17HandlerWithVecTester {
delegate! {
to self.handler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
}
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::TimeProvider::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler.handle_one_tc(&time_stamp)
}
}

7
satrs/src/time.rs Normal file
View File

@ -0,0 +1,7 @@
use core::fmt::Debug;
/// Generic abstraction for a check/countdown timer.
pub trait CountdownProvider: Debug {
fn has_expired(&self) -> bool;
fn reset(&mut self);
}

View File

@ -0,0 +1,148 @@
use std::sync::mpsc;
use satrs::{
pus::{
verification::{
TcStateAccepted, VerificationReporterCfg, VerificationReporterWithVecMpscSender,
VerificationReportingProvider, VerificationToken,
},
ActivePusRequest, DefaultActiveRequestMap, EcssTcInVecConverter, PusRequestRouter,
PusServiceReplyHandler, PusTargetedRequestHandler, PusTcToRequestConverter,
ReplyHandlerHook, TmAsVecSenderWithId,
},
TargetId,
};
use spacepackets::{
ecss::{tc::PusTcReader, PusPacket},
CcsdsPacket,
};
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum DummyRequest {
Ping,
WithParam(u32),
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum DummyReply {
Pong,
}
pub struct DummyRequestConverter {}
impl PusTcToRequestConverter<DummyRequest> for DummyRequestRouter {
type Error = ();
fn convert(
&mut self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, DummyRequest), Self::Error> {
if tc.service() == 205 && tc.subservice() == 1 {
return Ok((tc.apid().into(), DummyRequest::Ping));
}
Err(())
}
}
pub struct DummyRequestRouter {
dummy_1_sender: mpsc::Sender<DummyRequest>,
dummy_2_sender: mpsc::Sender<DummyRequest>,
}
impl PusRequestRouter<DummyRequest> for DummyRequestRouter {
type Error = ();
fn route(
&self,
target_id: TargetId,
request: DummyRequest,
token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error> {
if target_id == DummyTargetId::Object1 as u64 {
self.dummy_1_sender.send(request).ok();
} else {
self.dummy_2_sender.send(request).ok();
}
Ok(())
}
fn handle_error(
&self,
target_id: TargetId,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
error: Self::Error,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) {
panic!("routing error");
}
}
#[derive(Default)]
pub struct DummyReplyUserHook {}
impl ReplyHandlerHook<ActivePusRequest, DummyReply> for DummyReplyUserHook {
fn handle_unexpected_reply(&mut self, reply: &satrs::request::GenericMessage<DummyReply>) {
todo!()
}
fn timeout_callback(&self, active_request: &ActivePusRequest) {
todo!()
}
fn timeout_error_code(&self) -> satrs_shared::res_code::ResultU16 {
todo!()
}
}
pub type PusDummyRequestHandler = PusTargetedRequestHandler<
mpsc::Sender<Vec<u8>>,
mpsc::Sender<Vec<u8>>,
EcssTcInVecConverter,
VerificationReporterWithVecMpscSender,
DummyRequestConverter,
DummyRequestRouter,
DummyRequest,
>;
pub type PusDummyReplyHandler = PusServiceReplyHandler<
VerificationReporterWithVecMpscSender,
DefaultActiveRequestMap<ActivePusRequest>,
DummyReplyUserHook,
mpsc::Sender<Vec<u8>>,
ActivePusRequest,
DummyReply,
>;
const TEST_APID: u16 = 5;
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum DummyTargetId {
Object1 = 1,
Object2 = 2,
}
pub enum DummyChannelId {
Router = 1,
Object1 = 2,
Object2 = 3,
}
fn main() {
let reporter_cfg = VerificationReporterCfg::new(TEST_APID, 2, 2, 256).unwrap();
let (tm_sender, tm_receiver) = mpsc::channel();
let tm_sender_with_wrapper =
TmAsVecSenderWithId::new(DummyChannelId::Router as u32, "ROUTER", tm_sender.clone());
let verification_handler =
VerificationReporterWithVecMpscSender::new(&reporter_cfg, tm_sender_with_wrapper);
// let dummy_request_handler = PusDummyRequestHandler::new()
let dummy_reply_handler = PusDummyReplyHandler::new_from_now(
verification_handler,
DefaultActiveRequestMap::default(),
256,
DummyReplyUserHook::default(),
tm_sender.clone(),
);
}