First PUS handler abstractions with request mapping #121
@ -1,5 +1,8 @@
|
||||
<p align="center"> <img src="misc/satrs-logo.png" width="40%"> </p>
|
||||
|
||||
[![Crates.io](https://img.shields.io/crates/v/satrs)](https://crates.io/crates/satrs)
|
||||
[![docs.rs](https://img.shields.io/docsrs/satrs)](https://docs.rs/satrs)
|
||||
|
||||
sat-rs
|
||||
=========
|
||||
|
||||
|
@ -43,8 +43,8 @@ def main():
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--package",
|
||||
choices=["satrs-core"],
|
||||
default="satrs-core",
|
||||
choices=["satrs"],
|
||||
default="satrs",
|
||||
help="Choose project to generate coverage for",
|
||||
)
|
||||
parser.add_argument(
|
||||
|
@ -20,12 +20,12 @@ thiserror = "1"
|
||||
derive-new = "0.5"
|
||||
|
||||
[dependencies.satrs]
|
||||
version = "0.1.1"
|
||||
# path = "../satrs"
|
||||
# version = "0.1.1"
|
||||
path = "../satrs"
|
||||
|
||||
[dependencies.satrs-mib]
|
||||
version = "0.1.0"
|
||||
# path = "../satrs-mib"
|
||||
# version = "0.1.0"
|
||||
path = "../satrs-mib"
|
||||
|
||||
[features]
|
||||
dyn_tmtc = []
|
||||
|
@ -1,8 +1,9 @@
|
||||
use std::sync::mpsc::{self, TryRecvError};
|
||||
|
||||
use log::{info, warn};
|
||||
use satrs::pus::verification::VerificationReporterWithSender;
|
||||
use satrs::pus::verification::{VerificationReporterWithSender, VerificationReportingProvider};
|
||||
use satrs::pus::{EcssTmSender, PusTmWrapper};
|
||||
use satrs::request::TargetAndApidId;
|
||||
use satrs::spacepackets::ecss::hk::Subservice as HkSubservice;
|
||||
use satrs::{
|
||||
hk::HkRequest,
|
||||
@ -70,12 +71,12 @@ impl AcsTask {
|
||||
"ACS thread: Received HK request {:?}",
|
||||
request.targeted_request
|
||||
);
|
||||
let target_and_apid_id = TargetAndApidId::from(request.targeted_request.target_id);
|
||||
match request.targeted_request.request {
|
||||
Request::Hk(hk_req) => match hk_req {
|
||||
HkRequest::OneShot(unique_id) => self.handle_hk_request(
|
||||
request.targeted_request.target_id_with_apid.target_id(),
|
||||
unique_id,
|
||||
),
|
||||
HkRequest::OneShot(unique_id) => {
|
||||
self.handle_hk_request(target_and_apid_id.target(), unique_id)
|
||||
}
|
||||
HkRequest::Enable(_) => {}
|
||||
HkRequest::Disable(_) => {}
|
||||
HkRequest::ModifyCollectionInterval(_, _) => {}
|
||||
@ -89,10 +90,10 @@ impl AcsTask {
|
||||
}
|
||||
let started_token = self
|
||||
.verif_reporter
|
||||
.start_success(request.token, Some(&self.timestamp))
|
||||
.start_success(request.token, &self.timestamp)
|
||||
.expect("Sending start success failed");
|
||||
self.verif_reporter
|
||||
.completion_success(started_token, Some(&self.timestamp))
|
||||
.completion_success(started_token, &self.timestamp)
|
||||
.expect("Sending completion success failed");
|
||||
true
|
||||
}
|
||||
|
@ -48,7 +48,11 @@ pub mod tmtc_err {
|
||||
#[resultcode]
|
||||
pub const PUS_SERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 2);
|
||||
#[resultcode]
|
||||
pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 3);
|
||||
pub const PUS_SUBSERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 3);
|
||||
#[resultcode]
|
||||
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 = "Not enough data inside the TC application data field. Optionally includes: \
|
||||
@ -60,6 +64,9 @@ pub mod tmtc_err {
|
||||
pub const TMTC_RESULTS: &[ResultU16Info] = &[
|
||||
INVALID_PUS_SERVICE_EXT,
|
||||
INVALID_PUS_SUBSERVICE_EXT,
|
||||
PUS_SERVICE_NOT_IMPLEMENTED_EXT,
|
||||
UNKNOWN_TARGET_ID_EXT,
|
||||
ROUTING_ERROR_EXT,
|
||||
NOT_ENOUGH_APP_DATA_EXT,
|
||||
];
|
||||
}
|
||||
@ -76,6 +83,13 @@ pub mod hk_err {
|
||||
pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Hk as u8, 2);
|
||||
#[resultcode]
|
||||
pub const COLLECTION_INTERVAL_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 3);
|
||||
|
||||
pub const HK_ERR_RESULTS: &[ResultU16Info] = &[
|
||||
TARGET_ID_MISSING_EXT,
|
||||
UNKNOWN_TARGET_ID_EXT,
|
||||
UNKNOWN_TARGET_ID_EXT,
|
||||
COLLECTION_INTERVAL_MISSING_EXT,
|
||||
];
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
|
@ -12,7 +12,10 @@ use satrs::{
|
||||
DefaultPusMgmtBackendProvider, EventReporter, EventRequest, EventRequestWithToken,
|
||||
PusEventDispatcher,
|
||||
},
|
||||
verification::{TcStateStarted, VerificationReporterWithSender, VerificationToken},
|
||||
verification::{
|
||||
TcStateStarted, VerificationReporterWithSender, VerificationReportingProvider,
|
||||
VerificationToken,
|
||||
},
|
||||
EcssTmSender,
|
||||
},
|
||||
spacepackets::time::cds::{self, TimeProvider},
|
||||
@ -73,7 +76,7 @@ impl PusEventHandler {
|
||||
.try_into()
|
||||
.expect("expected start verification token");
|
||||
self.verif_handler
|
||||
.completion_success(started_token, Some(timestamp))
|
||||
.completion_success(started_token, timestamp)
|
||||
.expect("Sending completion success failed");
|
||||
};
|
||||
// handle event requests
|
||||
|
@ -1,59 +1 @@
|
||||
use derive_new::new;
|
||||
use satrs::spacepackets::ecss::tc::IsPusTelecommand;
|
||||
use satrs::spacepackets::ecss::PusPacket;
|
||||
use satrs::spacepackets::{ByteConversionError, CcsdsPacket};
|
||||
use satrs::tmtc::TargetId;
|
||||
use std::fmt;
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod config;
|
||||
|
||||
pub type Apid = u16;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TargetIdCreationError {
|
||||
#[error("byte conversion")]
|
||||
ByteConversion(#[from] ByteConversionError),
|
||||
#[error("not enough app data to generate target ID")]
|
||||
NotEnoughAppData(usize),
|
||||
}
|
||||
|
||||
// TODO: can these stay pub?
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, new)]
|
||||
pub struct TargetIdWithApid {
|
||||
pub apid: Apid,
|
||||
pub target: TargetId,
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetIdWithApid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}, {}", self.apid, self.target)
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetIdWithApid {
|
||||
pub fn apid(&self) -> Apid {
|
||||
self.apid
|
||||
}
|
||||
pub fn target_id(&self) -> TargetId {
|
||||
self.target
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetIdWithApid {
|
||||
pub fn from_tc(
|
||||
tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand),
|
||||
) -> Result<Self, TargetIdCreationError> {
|
||||
if tc.user_data().len() < 4 {
|
||||
return Err(ByteConversionError::FromSliceTooSmall {
|
||||
found: tc.user_data().len(),
|
||||
expected: 8,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
Ok(Self {
|
||||
apid: tc.apid(),
|
||||
target: u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ use log::info;
|
||||
use pus::test::create_test_service_dynamic;
|
||||
use satrs::hal::std::tcp_server::ServerConfig;
|
||||
use satrs::hal::std::udp_server::UdpTcServer;
|
||||
use satrs::request::TargetAndApidId;
|
||||
use satrs::tmtc::tm_helper::SharedTmPool;
|
||||
use satrs_example::config::pool::{create_sched_tc_pool, create_static_pools};
|
||||
use satrs_example::config::tasks::{
|
||||
@ -35,7 +36,7 @@ use crate::pus::hk::{create_hk_service_dynamic, create_hk_service_static};
|
||||
use crate::pus::scheduler::{create_scheduler_service_dynamic, create_scheduler_service_static};
|
||||
use crate::pus::test::create_test_service_static;
|
||||
use crate::pus::{PusReceiver, PusTcMpscRouter};
|
||||
use crate::requests::RequestWithToken;
|
||||
use crate::requests::{GenericRequestRouter, RequestWithToken};
|
||||
use crate::tcp::{SyncTcpTmSource, TcpTask};
|
||||
use crate::tmtc::{
|
||||
PusTcSourceProviderSharedPool, SharedTcPool, TcSourceTaskDynamic, TcSourceTaskStatic,
|
||||
@ -45,10 +46,8 @@ use satrs::pus::event_man::EventRequestWithToken;
|
||||
use satrs::pus::verification::{VerificationReporterCfg, VerificationReporterWithSender};
|
||||
use satrs::pus::{EcssTmSender, MpscTmAsVecSender, MpscTmInSharedPoolSender};
|
||||
use satrs::spacepackets::{time::cds::TimeProvider, time::TimeWriter};
|
||||
use satrs::tmtc::{CcsdsDistributor, TargetId};
|
||||
use satrs::tmtc::CcsdsDistributor;
|
||||
use satrs::ChannelId;
|
||||
use satrs_example::TargetIdWithApid;
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::mpsc::{self, channel};
|
||||
use std::sync::{Arc, RwLock};
|
||||
@ -82,11 +81,11 @@ fn static_tmtc_pool_main() {
|
||||
tm_funnel_tx.clone(),
|
||||
));
|
||||
|
||||
let acs_target_id = TargetIdWithApid::new(PUS_APID, RequestTargetId::AcsSubsystem as TargetId);
|
||||
let acs_target_id = TargetAndApidId::new(PUS_APID, RequestTargetId::AcsSubsystem as u32);
|
||||
let (acs_thread_tx, acs_thread_rx) = channel::<RequestWithToken>();
|
||||
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
||||
let mut request_map = HashMap::new();
|
||||
request_map.insert(acs_target_id, acs_thread_tx);
|
||||
let mut request_map = GenericRequestRouter::default();
|
||||
request_map.0.insert(acs_target_id.into(), acs_thread_tx);
|
||||
|
||||
// This helper structure is used by all telecommand providers which need to send telecommands
|
||||
// to the TC source.
|
||||
@ -310,11 +309,11 @@ fn dyn_tmtc_pool_main() {
|
||||
tm_funnel_tx.clone(),
|
||||
));
|
||||
|
||||
let acs_target_id = TargetIdWithApid::new(PUS_APID, RequestTargetId::AcsSubsystem as TargetId);
|
||||
let acs_target_id = TargetAndApidId::new(PUS_APID, RequestTargetId::AcsSubsystem as u32);
|
||||
let (acs_thread_tx, acs_thread_rx) = channel::<RequestWithToken>();
|
||||
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
||||
let mut request_map = HashMap::new();
|
||||
request_map.insert(acs_target_id, acs_thread_tx);
|
||||
let mut request_map = GenericRequestRouter::default();
|
||||
request_map.0.insert(acs_target_id.into(), acs_thread_tx);
|
||||
|
||||
let tc_source = PusTcSourceProviderDynamic(tc_source_tx);
|
||||
|
||||
|
@ -1,22 +1,76 @@
|
||||
use crate::requests::{ActionRequest, Request, RequestWithToken};
|
||||
use log::{error, warn};
|
||||
use satrs::action::ActionRequest;
|
||||
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
|
||||
use satrs::pus::action::{PusActionToRequestConverter, PusService8ActionHandler};
|
||||
use satrs::pus::verification::{
|
||||
FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationToken,
|
||||
FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationReportingProvider,
|
||||
VerificationToken,
|
||||
};
|
||||
use satrs::pus::{
|
||||
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
|
||||
EcssTcReceiver, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender,
|
||||
PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHelper,
|
||||
MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
|
||||
PusPacketHandlingError, PusServiceHelper,
|
||||
};
|
||||
use satrs::request::TargetAndApidId;
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::PusPacket;
|
||||
use satrs::tmtc::tm_helper::SharedTmPool;
|
||||
use satrs::ChannelId;
|
||||
use satrs::{ChannelId, TargetId};
|
||||
use satrs_example::config::{tmtc_err, TcReceiverId, TmSenderId, PUS_APID};
|
||||
use satrs_example::TargetIdWithApid;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::{self, Sender};
|
||||
use std::sync::mpsc::{self};
|
||||
|
||||
use crate::requests::GenericRequestRouter;
|
||||
|
||||
use super::GenericRoutingErrorHandler;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ExampleActionRequestConverter {}
|
||||
|
||||
impl PusActionToRequestConverter for ExampleActionRequestConverter {
|
||||
type Error = PusPacketHandlingError;
|
||||
|
||||
fn convert(
|
||||
&mut self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
tc: &PusTcReader,
|
||||
time_stamp: &[u8],
|
||||
verif_reporter: &impl VerificationReportingProvider,
|
||||
) -> Result<(TargetId, ActionRequest), Self::Error> {
|
||||
let subservice = tc.subservice();
|
||||
let user_data = tc.user_data();
|
||||
if user_data.len() < 8 {
|
||||
verif_reporter
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new_no_fail_data(time_stamp, &tmtc_err::NOT_ENOUGH_APP_DATA),
|
||||
)
|
||||
.expect("Sending start failure failed");
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData {
|
||||
expected: 8,
|
||||
found: user_data.len(),
|
||||
});
|
||||
}
|
||||
let target_id = TargetAndApidId::from_pus_tc(tc).unwrap();
|
||||
let action_id = u32::from_be_bytes(user_data[4..8].try_into().unwrap());
|
||||
if subservice == 128 {
|
||||
Ok((
|
||||
target_id.raw(),
|
||||
ActionRequest::UnsignedIdAndVecData {
|
||||
action_id,
|
||||
data: user_data[8..].to_vec(),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
verif_reporter
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new_no_fail_data(time_stamp, &tmtc_err::INVALID_PUS_SUBSERVICE),
|
||||
)
|
||||
.expect("Sending start failure failed");
|
||||
Err(PusPacketHandlingError::InvalidSubservice(subservice))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_action_service_static(
|
||||
shared_tm_store: SharedTmPool,
|
||||
@ -24,7 +78,7 @@ pub fn create_action_service_static(
|
||||
verif_reporter: VerificationReporterWithSender,
|
||||
tc_pool: SharedStaticMemoryPool,
|
||||
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
|
||||
action_router: GenericRequestRouter,
|
||||
) -> Pus8Wrapper<EcssTcInSharedStoreConverter> {
|
||||
let action_srv_tm_sender = MpscTmInSharedPoolSender::new(
|
||||
TmSenderId::PusAction as ChannelId,
|
||||
@ -38,12 +92,16 @@ pub fn create_action_service_static(
|
||||
pus_action_rx,
|
||||
);
|
||||
let pus_8_handler = PusService8ActionHandler::new(
|
||||
PusServiceHelper::new(
|
||||
Box::new(action_srv_receiver),
|
||||
Box::new(action_srv_tm_sender),
|
||||
PUS_APID,
|
||||
verif_reporter.clone(),
|
||||
EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048),
|
||||
request_map.clone(),
|
||||
),
|
||||
ExampleActionRequestConverter::default(),
|
||||
action_router,
|
||||
GenericRoutingErrorHandler::<8>::default(),
|
||||
);
|
||||
Pus8Wrapper { pus_8_handler }
|
||||
}
|
||||
@ -52,7 +110,7 @@ pub fn create_action_service_dynamic(
|
||||
tm_funnel_tx: mpsc::Sender<Vec<u8>>,
|
||||
verif_reporter: VerificationReporterWithSender,
|
||||
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
|
||||
action_router: GenericRequestRouter,
|
||||
) -> Pus8Wrapper<EcssTcInVecConverter> {
|
||||
let action_srv_tm_sender = MpscTmAsVecSender::new(
|
||||
TmSenderId::PusAction as ChannelId,
|
||||
@ -65,146 +123,28 @@ pub fn create_action_service_dynamic(
|
||||
pus_action_rx,
|
||||
);
|
||||
let pus_8_handler = PusService8ActionHandler::new(
|
||||
PusServiceHelper::new(
|
||||
Box::new(action_srv_receiver),
|
||||
Box::new(action_srv_tm_sender),
|
||||
PUS_APID,
|
||||
verif_reporter.clone(),
|
||||
EcssTcInVecConverter::default(),
|
||||
request_map.clone(),
|
||||
),
|
||||
ExampleActionRequestConverter::default(),
|
||||
action_router,
|
||||
GenericRoutingErrorHandler::<8>::default(),
|
||||
);
|
||||
Pus8Wrapper { pus_8_handler }
|
||||
}
|
||||
|
||||
pub struct PusService8ActionHandler<TcInMemConverter: EcssTcInMemConverter> {
|
||||
service_helper: PusServiceHelper<TcInMemConverter>,
|
||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> PusService8ActionHandler<TcInMemConverter> {
|
||||
pub fn new(
|
||||
tc_receiver: Box<dyn EcssTcReceiver>,
|
||||
tm_sender: Box<dyn EcssTmSender>,
|
||||
tm_apid: u16,
|
||||
verification_handler: VerificationReporterWithSender,
|
||||
tc_in_mem_converter: TcInMemConverter,
|
||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
service_helper: PusServiceHelper::new(
|
||||
tc_receiver,
|
||||
tm_sender,
|
||||
tm_apid,
|
||||
verification_handler,
|
||||
tc_in_mem_converter,
|
||||
),
|
||||
request_handlers,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_action_request_with_id(
|
||||
&self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
tc: &PusTcReader,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<(), PusPacketHandlingError> {
|
||||
let user_data = tc.user_data();
|
||||
if user_data.len() < 8 {
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.borrow_mut()
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new(Some(time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None),
|
||||
)
|
||||
.expect("Sending start failure failed");
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData(
|
||||
"Expected at least 4 bytes".into(),
|
||||
));
|
||||
}
|
||||
//let target_id = u32::from_be_bytes(user_data[0..4].try_into().unwrap());
|
||||
let target_id = TargetIdWithApid::from_tc(tc).unwrap();
|
||||
let action_id = u32::from_be_bytes(user_data[4..8].try_into().unwrap());
|
||||
if let Some(sender) = self.request_handlers.get(&target_id) {
|
||||
sender
|
||||
.send(RequestWithToken::new(
|
||||
target_id,
|
||||
Request::Action(ActionRequest::CmdWithU32Id((
|
||||
action_id,
|
||||
Vec::from(&user_data[8..]),
|
||||
))),
|
||||
token,
|
||||
))
|
||||
.expect("Forwarding action request failed");
|
||||
} else {
|
||||
let mut fail_data: [u8; 4] = [0; 4];
|
||||
fail_data.copy_from_slice(&target_id.target.to_be_bytes());
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.borrow_mut()
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new(
|
||||
Some(time_stamp),
|
||||
&tmtc_err::UNKNOWN_TARGET_ID,
|
||||
Some(&fail_data),
|
||||
),
|
||||
)
|
||||
.expect("Sending start failure failed");
|
||||
return Err(PusPacketHandlingError::Other(format!(
|
||||
"Unknown target ID {target_id}"
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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();
|
||||
self.service_helper
|
||||
.tc_in_mem_converter
|
||||
.cache_ecss_tc_in_memory(&ecss_tc_and_token.tc_in_memory)?;
|
||||
let tc = PusTcReader::new(self.service_helper.tc_in_mem_converter.tc_slice_raw())?.0;
|
||||
let subservice = tc.subservice();
|
||||
let mut partial_error = None;
|
||||
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
|
||||
match subservice {
|
||||
128 => {
|
||||
self.handle_action_request_with_id(ecss_tc_and_token.token, &tc, &time_stamp)?;
|
||||
}
|
||||
_ => {
|
||||
let fail_data = [subservice];
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_failure(
|
||||
ecss_tc_and_token.token,
|
||||
FailParams::new(
|
||||
Some(&time_stamp),
|
||||
&tmtc_err::INVALID_PUS_SUBSERVICE,
|
||||
Some(&fail_data),
|
||||
),
|
||||
)
|
||||
.expect("Sending start failure failed");
|
||||
return Err(PusPacketHandlingError::InvalidSubservice(subservice));
|
||||
}
|
||||
}
|
||||
if let Some(partial_error) = partial_error {
|
||||
return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess(
|
||||
partial_error,
|
||||
));
|
||||
}
|
||||
Ok(PusPacketHandlerResult::RequestHandled)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pus8Wrapper<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub(crate) pus_8_handler: PusService8ActionHandler<TcInMemConverter>,
|
||||
pub(crate) pus_8_handler: PusService8ActionHandler<
|
||||
TcInMemConverter,
|
||||
VerificationReporterWithSender,
|
||||
ExampleActionRequestConverter,
|
||||
GenericRequestRouter,
|
||||
GenericRoutingErrorHandler<8>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> Pus8Wrapper<TcInMemConverter> {
|
||||
|
@ -76,7 +76,7 @@ pub fn create_event_service_dynamic(
|
||||
}
|
||||
|
||||
pub struct Pus5Wrapper<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub pus_5_handler: PusService5EventHandler<TcInMemConverter>,
|
||||
pub pus_5_handler: PusService5EventHandler<TcInMemConverter, VerificationReporterWithSender>,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> Pus5Wrapper<TcInMemConverter> {
|
||||
|
@ -1,22 +1,145 @@
|
||||
use crate::requests::{Request, RequestWithToken};
|
||||
use log::{error, warn};
|
||||
use satrs::hk::{CollectionIntervalFactor, HkRequest};
|
||||
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
|
||||
use satrs::pus::hk::{PusHkToRequestConverter, PusService3HkHandler};
|
||||
use satrs::pus::verification::{
|
||||
FailParams, StdVerifReporterWithSender, VerificationReporterWithSender,
|
||||
FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationReportingProvider,
|
||||
VerificationToken,
|
||||
};
|
||||
use satrs::pus::{
|
||||
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
|
||||
EcssTcReceiver, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender,
|
||||
PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHelper,
|
||||
MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
|
||||
PusPacketHandlingError, PusServiceHelper,
|
||||
};
|
||||
use satrs::request::TargetAndApidId;
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::{hk, PusPacket};
|
||||
use satrs::tmtc::tm_helper::SharedTmPool;
|
||||
use satrs::ChannelId;
|
||||
use satrs::{ChannelId, TargetId};
|
||||
use satrs_example::config::{hk_err, tmtc_err, TcReceiverId, TmSenderId, PUS_APID};
|
||||
use satrs_example::TargetIdWithApid;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::{self, Sender};
|
||||
use std::sync::mpsc::{self};
|
||||
|
||||
use crate::requests::GenericRequestRouter;
|
||||
|
||||
use super::GenericRoutingErrorHandler;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ExampleHkRequestConverter {}
|
||||
|
||||
impl PusHkToRequestConverter for ExampleHkRequestConverter {
|
||||
type Error = PusPacketHandlingError;
|
||||
|
||||
fn convert(
|
||||
&mut self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
tc: &PusTcReader,
|
||||
time_stamp: &[u8],
|
||||
verif_reporter: &impl VerificationReportingProvider,
|
||||
) -> Result<(TargetId, HkRequest), Self::Error> {
|
||||
let user_data = tc.user_data();
|
||||
if user_data.is_empty() {
|
||||
let user_data_len = user_data.len() as u32;
|
||||
let user_data_len_raw = user_data_len.to_be_bytes();
|
||||
verif_reporter
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new(
|
||||
time_stamp,
|
||||
&tmtc_err::NOT_ENOUGH_APP_DATA,
|
||||
&user_data_len_raw,
|
||||
),
|
||||
)
|
||||
.expect("Sending start failure TM failed");
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData {
|
||||
expected: 4,
|
||||
found: 0,
|
||||
});
|
||||
}
|
||||
if user_data.len() < 8 {
|
||||
let err = if user_data.len() < 4 {
|
||||
&hk_err::TARGET_ID_MISSING
|
||||
} else {
|
||||
&hk_err::UNIQUE_ID_MISSING
|
||||
};
|
||||
let user_data_len = user_data.len() as u32;
|
||||
let user_data_len_raw = user_data_len.to_be_bytes();
|
||||
verif_reporter
|
||||
.start_failure(token, FailParams::new(time_stamp, err, &user_data_len_raw))
|
||||
.expect("Sending start failure TM failed");
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData {
|
||||
expected: 8,
|
||||
found: 4,
|
||||
});
|
||||
}
|
||||
let subservice = tc.subservice();
|
||||
let target_id = TargetAndApidId::from_pus_tc(tc).expect("invalid tc format");
|
||||
let unique_id = u32::from_be_bytes(tc.user_data()[4..8].try_into().unwrap());
|
||||
|
||||
let standard_subservice = hk::Subservice::try_from(subservice);
|
||||
if standard_subservice.is_err() {
|
||||
verif_reporter
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new(time_stamp, &tmtc_err::INVALID_PUS_SUBSERVICE, &[subservice]),
|
||||
)
|
||||
.expect("Sending start failure TM failed");
|
||||
return Err(PusPacketHandlingError::InvalidSubservice(subservice));
|
||||
}
|
||||
Ok((
|
||||
target_id.into(),
|
||||
match standard_subservice.unwrap() {
|
||||
hk::Subservice::TcEnableHkGeneration | hk::Subservice::TcEnableDiagGeneration => {
|
||||
HkRequest::Enable(unique_id)
|
||||
}
|
||||
hk::Subservice::TcDisableHkGeneration | hk::Subservice::TcDisableDiagGeneration => {
|
||||
HkRequest::Disable(unique_id)
|
||||
}
|
||||
hk::Subservice::TcReportHkReportStructures => todo!(),
|
||||
hk::Subservice::TmHkPacket => todo!(),
|
||||
hk::Subservice::TcGenerateOneShotHk | hk::Subservice::TcGenerateOneShotDiag => {
|
||||
HkRequest::OneShot(unique_id)
|
||||
}
|
||||
hk::Subservice::TcModifyDiagCollectionInterval
|
||||
| hk::Subservice::TcModifyHkCollectionInterval => {
|
||||
if user_data.len() < 12 {
|
||||
verif_reporter
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new_no_fail_data(
|
||||
time_stamp,
|
||||
&tmtc_err::NOT_ENOUGH_APP_DATA,
|
||||
),
|
||||
)
|
||||
.expect("Sending start failure TM failed");
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData {
|
||||
expected: 12,
|
||||
found: user_data.len(),
|
||||
});
|
||||
}
|
||||
HkRequest::ModifyCollectionInterval(
|
||||
unique_id,
|
||||
CollectionIntervalFactor::from_be_bytes(
|
||||
user_data[8..12].try_into().unwrap(),
|
||||
),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
verif_reporter
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new(
|
||||
time_stamp,
|
||||
&tmtc_err::PUS_SUBSERVICE_NOT_IMPLEMENTED,
|
||||
&[subservice],
|
||||
),
|
||||
)
|
||||
.expect("Sending start failure TM failed");
|
||||
return Err(PusPacketHandlingError::InvalidSubservice(subservice));
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_hk_service_static(
|
||||
shared_tm_store: SharedTmPool,
|
||||
@ -24,7 +147,7 @@ pub fn create_hk_service_static(
|
||||
verif_reporter: VerificationReporterWithSender,
|
||||
tc_pool: SharedStaticMemoryPool,
|
||||
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
|
||||
request_router: GenericRequestRouter,
|
||||
) -> Pus3Wrapper<EcssTcInSharedStoreConverter> {
|
||||
let hk_srv_tm_sender = MpscTmInSharedPoolSender::new(
|
||||
TmSenderId::PusHk as ChannelId,
|
||||
@ -35,12 +158,16 @@ pub fn create_hk_service_static(
|
||||
let hk_srv_receiver =
|
||||
MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx);
|
||||
let pus_3_handler = PusService3HkHandler::new(
|
||||
PusServiceHelper::new(
|
||||
Box::new(hk_srv_receiver),
|
||||
Box::new(hk_srv_tm_sender),
|
||||
PUS_APID,
|
||||
verif_reporter.clone(),
|
||||
EcssTcInSharedStoreConverter::new(tc_pool, 2048),
|
||||
request_map,
|
||||
),
|
||||
ExampleHkRequestConverter::default(),
|
||||
request_router,
|
||||
GenericRoutingErrorHandler::default(),
|
||||
);
|
||||
Pus3Wrapper { pus_3_handler }
|
||||
}
|
||||
@ -49,7 +176,7 @@ pub fn create_hk_service_dynamic(
|
||||
tm_funnel_tx: mpsc::Sender<Vec<u8>>,
|
||||
verif_reporter: VerificationReporterWithSender,
|
||||
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
|
||||
request_router: GenericRequestRouter,
|
||||
) -> Pus3Wrapper<EcssTcInVecConverter> {
|
||||
let hk_srv_tm_sender = MpscTmAsVecSender::new(
|
||||
TmSenderId::PusHk as ChannelId,
|
||||
@ -59,154 +186,28 @@ pub fn create_hk_service_dynamic(
|
||||
let hk_srv_receiver =
|
||||
MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx);
|
||||
let pus_3_handler = PusService3HkHandler::new(
|
||||
PusServiceHelper::new(
|
||||
Box::new(hk_srv_receiver),
|
||||
Box::new(hk_srv_tm_sender),
|
||||
PUS_APID,
|
||||
verif_reporter.clone(),
|
||||
EcssTcInVecConverter::default(),
|
||||
request_map,
|
||||
),
|
||||
ExampleHkRequestConverter::default(),
|
||||
request_router,
|
||||
GenericRoutingErrorHandler::default(),
|
||||
);
|
||||
Pus3Wrapper { pus_3_handler }
|
||||
}
|
||||
|
||||
pub struct PusService3HkHandler<TcInMemConverter: EcssTcInMemConverter> {
|
||||
psb: PusServiceHelper<TcInMemConverter>,
|
||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> PusService3HkHandler<TcInMemConverter> {
|
||||
pub fn new(
|
||||
tc_receiver: Box<dyn EcssTcReceiver>,
|
||||
tm_sender: Box<dyn EcssTmSender>,
|
||||
tm_apid: u16,
|
||||
verification_handler: StdVerifReporterWithSender,
|
||||
tc_in_mem_converter: TcInMemConverter,
|
||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
psb: PusServiceHelper::new(
|
||||
tc_receiver,
|
||||
tm_sender,
|
||||
tm_apid,
|
||||
verification_handler,
|
||||
tc_in_mem_converter,
|
||||
),
|
||||
request_handlers,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
|
||||
let possible_packet = self.psb.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
|
||||
.psb
|
||||
.tc_in_mem_converter
|
||||
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
|
||||
let subservice = tc.subservice();
|
||||
let mut partial_error = None;
|
||||
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
|
||||
let user_data = tc.user_data();
|
||||
if user_data.is_empty() {
|
||||
self.psb
|
||||
.common
|
||||
.verification_handler
|
||||
.borrow_mut()
|
||||
.start_failure(
|
||||
ecss_tc_and_token.token,
|
||||
FailParams::new(Some(&time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None),
|
||||
)
|
||||
.expect("Sending start failure TM failed");
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData(
|
||||
"Expected at least 8 bytes of app data".into(),
|
||||
));
|
||||
}
|
||||
if user_data.len() < 8 {
|
||||
let err = if user_data.len() < 4 {
|
||||
&hk_err::TARGET_ID_MISSING
|
||||
} else {
|
||||
&hk_err::UNIQUE_ID_MISSING
|
||||
};
|
||||
self.psb
|
||||
.common
|
||||
.verification_handler
|
||||
.borrow_mut()
|
||||
.start_failure(
|
||||
ecss_tc_and_token.token,
|
||||
FailParams::new(Some(&time_stamp), err, None),
|
||||
)
|
||||
.expect("Sending start failure TM failed");
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData(
|
||||
"Expected at least 8 bytes of app data".into(),
|
||||
));
|
||||
}
|
||||
let target_id = TargetIdWithApid::from_tc(&tc).expect("invalid tc format");
|
||||
let unique_id = u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap());
|
||||
if !self.request_handlers.contains_key(&target_id) {
|
||||
self.psb
|
||||
.common
|
||||
.verification_handler
|
||||
.borrow_mut()
|
||||
.start_failure(
|
||||
ecss_tc_and_token.token,
|
||||
FailParams::new(Some(&time_stamp), &hk_err::UNKNOWN_TARGET_ID, None),
|
||||
)
|
||||
.expect("Sending start failure TM failed");
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData(format!(
|
||||
"Unknown target ID {target_id}"
|
||||
)));
|
||||
}
|
||||
let send_request = |target: TargetIdWithApid, request: HkRequest| {
|
||||
let sender = self.request_handlers.get(&target).unwrap();
|
||||
sender
|
||||
.send(RequestWithToken::new(
|
||||
target,
|
||||
Request::Hk(request),
|
||||
ecss_tc_and_token.token,
|
||||
))
|
||||
.unwrap_or_else(|_| panic!("Sending HK request {request:?} failed"));
|
||||
};
|
||||
if subservice == hk::Subservice::TcEnableHkGeneration as u8 {
|
||||
send_request(target_id, HkRequest::Enable(unique_id));
|
||||
} else if subservice == hk::Subservice::TcDisableHkGeneration as u8 {
|
||||
send_request(target_id, HkRequest::Disable(unique_id));
|
||||
} else if subservice == hk::Subservice::TcGenerateOneShotHk as u8 {
|
||||
send_request(target_id, HkRequest::OneShot(unique_id));
|
||||
} else if subservice == hk::Subservice::TcModifyHkCollectionInterval as u8 {
|
||||
if user_data.len() < 12 {
|
||||
self.psb
|
||||
.common
|
||||
.verification_handler
|
||||
.borrow_mut()
|
||||
.start_failure(
|
||||
ecss_tc_and_token.token,
|
||||
FailParams::new(
|
||||
Some(&time_stamp),
|
||||
&hk_err::COLLECTION_INTERVAL_MISSING,
|
||||
None,
|
||||
),
|
||||
)
|
||||
.expect("Sending start failure TM failed");
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData(
|
||||
"Collection interval missing".into(),
|
||||
));
|
||||
}
|
||||
send_request(
|
||||
target_id,
|
||||
HkRequest::ModifyCollectionInterval(
|
||||
unique_id,
|
||||
CollectionIntervalFactor::from_be_bytes(user_data[8..12].try_into().unwrap()),
|
||||
),
|
||||
);
|
||||
}
|
||||
Ok(PusPacketHandlerResult::RequestHandled)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pus3Wrapper<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub(crate) pus_3_handler: PusService3HkHandler<TcInMemConverter>,
|
||||
pub(crate) pus_3_handler: PusService3HkHandler<
|
||||
TcInMemConverter,
|
||||
VerificationReporterWithSender,
|
||||
ExampleHkRequestConverter,
|
||||
GenericRequestRouter,
|
||||
GenericRoutingErrorHandler<3>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> Pus3Wrapper<TcInMemConverter> {
|
||||
|
@ -1,7 +1,11 @@
|
||||
use crate::tmtc::MpscStoreAndSendError;
|
||||
use log::warn;
|
||||
use satrs::pus::verification::{FailParams, StdVerifReporterWithSender};
|
||||
use satrs::pus::{EcssTcAndToken, PusPacketHandlerResult, TcInMemory};
|
||||
use satrs::pus::verification::{
|
||||
FailParams, StdVerifReporterWithSender, VerificationReportingProvider,
|
||||
};
|
||||
use satrs::pus::{
|
||||
EcssTcAndToken, GenericRoutingError, PusPacketHandlerResult, PusRoutingErrorHandler, TcInMemory,
|
||||
};
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::PusServiceId;
|
||||
use satrs::spacepackets::time::cds::TimeProvider;
|
||||
@ -78,7 +82,7 @@ impl PusReceiver {
|
||||
self.stamp_helper.update_from_now();
|
||||
let accepted_token = self
|
||||
.verif_reporter
|
||||
.acceptance_success(init_token, Some(self.stamp_helper.stamp()))
|
||||
.acceptance_success(init_token, self.stamp_helper.stamp())
|
||||
.expect("Acceptance success failure");
|
||||
let service = PusServiceId::try_from(service);
|
||||
match service {
|
||||
@ -115,9 +119,9 @@ impl PusReceiver {
|
||||
let result = self.verif_reporter.start_failure(
|
||||
accepted_token,
|
||||
FailParams::new(
|
||||
Some(self.stamp_helper.stamp()),
|
||||
self.stamp_helper.stamp(),
|
||||
&tmtc_err::PUS_SERVICE_NOT_IMPLEMENTED,
|
||||
Some(&[standard_service as u8]),
|
||||
&[standard_service as u8],
|
||||
),
|
||||
);
|
||||
if result.is_err() {
|
||||
@ -139,9 +143,9 @@ impl PusReceiver {
|
||||
.start_failure(
|
||||
accepted_token,
|
||||
FailParams::new(
|
||||
Some(self.stamp_helper.stamp()),
|
||||
self.stamp_helper.stamp(),
|
||||
&tmtc_err::INVALID_PUS_SUBSERVICE,
|
||||
Some(&[e.number]),
|
||||
&[e.number],
|
||||
),
|
||||
)
|
||||
.expect("Start failure verification failed")
|
||||
@ -151,3 +155,56 @@ impl PusReceiver {
|
||||
Ok(PusPacketHandlerResult::RequestHandled)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GenericRoutingErrorHandler<const SERVICE_ID: u8> {}
|
||||
|
||||
impl<const SERVICE_ID: u8> PusRoutingErrorHandler for GenericRoutingErrorHandler<SERVICE_ID> {
|
||||
type Error = satrs::pus::GenericRoutingError;
|
||||
|
||||
fn handle_error(
|
||||
&self,
|
||||
target_id: satrs::TargetId,
|
||||
token: satrs::pus::verification::VerificationToken<
|
||||
satrs::pus::verification::TcStateAccepted,
|
||||
>,
|
||||
_tc: &PusTcReader,
|
||||
error: Self::Error,
|
||||
time_stamp: &[u8],
|
||||
verif_reporter: &impl VerificationReportingProvider,
|
||||
) {
|
||||
warn!("Routing request for service {SERVICE_ID} failed: {error:?}");
|
||||
match error {
|
||||
GenericRoutingError::UnknownTargetId(id) => {
|
||||
let mut fail_data: [u8; 8] = [0; 8];
|
||||
fail_data.copy_from_slice(&id.to_be_bytes());
|
||||
verif_reporter
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new(time_stamp, &tmtc_err::UNKNOWN_TARGET_ID, &fail_data),
|
||||
)
|
||||
.expect("Sending start failure failed");
|
||||
}
|
||||
GenericRoutingError::SendError(_) => {
|
||||
let mut fail_data: [u8; 8] = [0; 8];
|
||||
fail_data.copy_from_slice(&target_id.to_be_bytes());
|
||||
verif_reporter
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new(time_stamp, &tmtc_err::ROUTING_ERROR, &fail_data),
|
||||
)
|
||||
.expect("Sending start failure failed");
|
||||
}
|
||||
GenericRoutingError::NotEnoughAppData { expected, found } => {
|
||||
let mut context_info = (found as u32).to_be_bytes().to_vec();
|
||||
context_info.extend_from_slice(&(expected as u32).to_be_bytes());
|
||||
verif_reporter
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new(time_stamp, &tmtc_err::NOT_ENOUGH_APP_DATA, &context_info),
|
||||
)
|
||||
.expect("Sending start failure failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,8 @@ impl TcReleaser for mpsc::Sender<Vec<u8>> {
|
||||
}
|
||||
|
||||
pub struct Pus11Wrapper<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub pus_11_handler: PusService11SchedHandler<TcInMemConverter, PusScheduler>,
|
||||
pub pus_11_handler:
|
||||
PusService11SchedHandler<TcInMemConverter, VerificationReporterWithSender, PusScheduler>,
|
||||
pub sched_tc_pool: StaticMemoryPool,
|
||||
pub releaser_buf: [u8; 4096],
|
||||
pub tc_releaser: Box<dyn TcReleaser + Send>,
|
||||
|
@ -2,7 +2,9 @@ use log::{info, warn};
|
||||
use satrs::params::Params;
|
||||
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
|
||||
use satrs::pus::test::PusService17TestHandler;
|
||||
use satrs::pus::verification::{FailParams, VerificationReporterWithSender};
|
||||
use satrs::pus::verification::{
|
||||
FailParams, VerificationReporterWithSender, VerificationReportingProvider,
|
||||
};
|
||||
use satrs::pus::{
|
||||
EcssTcAndToken, EcssTcInMemConverter, EcssTcInVecConverter, MpscTcReceiver, MpscTmAsVecSender,
|
||||
MpscTmInSharedPoolSender, PusPacketHandlerResult, PusServiceHelper,
|
||||
@ -79,7 +81,7 @@ pub fn create_test_service_dynamic(
|
||||
}
|
||||
|
||||
pub struct Service17CustomWrapper<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub pus17_handler: PusService17TestHandler<TcInMemConverter>,
|
||||
pub pus17_handler: PusService17TestHandler<TcInMemConverter, VerificationReporterWithSender>,
|
||||
pub test_srv_event_sender: Sender<(EventU32, Option<Params>)>,
|
||||
}
|
||||
|
||||
@ -125,15 +127,13 @@ impl<TcInMemConverter: EcssTcInMemConverter> Service17CustomWrapper<TcInMemConve
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_success(token, Some(&stamp_buf))
|
||||
.start_success(token, &stamp_buf)
|
||||
.expect("Error sending start success");
|
||||
self.pus17_handler
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.completion_success(start_token, Some(&stamp_buf))
|
||||
.completion_success(start_token, &stamp_buf)
|
||||
.expect("Error sending completion success");
|
||||
} else {
|
||||
let fail_data = [tc.subservice()];
|
||||
@ -141,13 +141,12 @@ impl<TcInMemConverter: EcssTcInMemConverter> Service17CustomWrapper<TcInMemConve
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new(
|
||||
Some(&stamp_buf),
|
||||
&stamp_buf,
|
||||
&tmtc_err::INVALID_PUS_SUBSERVICE,
|
||||
Some(&fail_data),
|
||||
&fail_data,
|
||||
),
|
||||
)
|
||||
.expect("Sending start failure verification failed");
|
||||
|
45
satrs-example/src/queue.rs
Normal file
45
satrs-example/src/queue.rs
Normal file
@ -0,0 +1,45 @@
|
||||
/// Generic error type for sending something via a message queue.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum GenericSendError {
|
||||
RxDisconnected,
|
||||
QueueFull(Option<u32>),
|
||||
}
|
||||
|
||||
impl Display for GenericSendError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
GenericSendError::RxDisconnected => {
|
||||
write!(f, "rx side has disconnected")
|
||||
}
|
||||
GenericSendError::QueueFull(max_cap) => {
|
||||
write!(f, "queue with max capacity of {max_cap:?} is full")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for GenericSendError {}
|
||||
|
||||
/// Generic error type for sending something via a message queue.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum GenericRecvError {
|
||||
Empty,
|
||||
TxDisconnected,
|
||||
}
|
||||
|
||||
impl Display for GenericRecvError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::TxDisconnected => {
|
||||
write!(f, "tx side has disconnected")
|
||||
}
|
||||
Self::Empty => {
|
||||
write!(f, "nothing to receive")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for GenericRecvError {}
|
@ -1,15 +1,16 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc;
|
||||
|
||||
use derive_new::new;
|
||||
use satrs::action::ActionRequest;
|
||||
use satrs::hk::HkRequest;
|
||||
use satrs::mode::ModeRequest;
|
||||
use satrs::pus::action::PusActionRequestRouter;
|
||||
use satrs::pus::hk::PusHkRequestRouter;
|
||||
use satrs::pus::verification::{TcStateAccepted, VerificationToken};
|
||||
use satrs_example::TargetIdWithApid;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub enum ActionRequest {
|
||||
CmdWithU32Id((u32, Vec<u8>)),
|
||||
CmdWithStringId((String, Vec<u8>)),
|
||||
}
|
||||
use satrs::pus::GenericRoutingError;
|
||||
use satrs::queue::GenericSendError;
|
||||
use satrs::TargetId;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
@ -22,7 +23,7 @@ pub enum Request {
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, new)]
|
||||
pub struct TargetedRequest {
|
||||
pub(crate) target_id_with_apid: TargetIdWithApid,
|
||||
pub(crate) target_id: TargetId,
|
||||
pub(crate) request: Request,
|
||||
}
|
||||
|
||||
@ -34,7 +35,7 @@ pub struct RequestWithToken {
|
||||
|
||||
impl RequestWithToken {
|
||||
pub fn new(
|
||||
target_id: TargetIdWithApid,
|
||||
target_id: TargetId,
|
||||
request: Request,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
) -> Self {
|
||||
@ -44,3 +45,50 @@ impl RequestWithToken {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct GenericRequestRouter(pub HashMap<TargetId, mpsc::Sender<RequestWithToken>>);
|
||||
|
||||
impl PusHkRequestRouter for GenericRequestRouter {
|
||||
type Error = GenericRoutingError;
|
||||
|
||||
fn route(
|
||||
&self,
|
||||
target_id: TargetId,
|
||||
hk_request: HkRequest,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
) -> Result<(), Self::Error> {
|
||||
if let Some(sender) = self.0.get(&target_id) {
|
||||
sender
|
||||
.send(RequestWithToken::new(
|
||||
target_id,
|
||||
Request::Hk(hk_request),
|
||||
token,
|
||||
))
|
||||
.map_err(|_| GenericRoutingError::SendError(GenericSendError::RxDisconnected))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PusActionRequestRouter for GenericRequestRouter {
|
||||
type Error = GenericRoutingError;
|
||||
|
||||
fn route(
|
||||
&self,
|
||||
target_id: TargetId,
|
||||
action_request: ActionRequest,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
) -> Result<(), Self::Error> {
|
||||
if let Some(sender) = self.0.get(&target_id) {
|
||||
sender
|
||||
.send(RequestWithToken::new(
|
||||
target_id,
|
||||
Request::Action(action_request),
|
||||
token,
|
||||
))
|
||||
.map_err(|_| GenericRoutingError::SendError(GenericSendError::RxDisconnected))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [unreleased]
|
||||
|
||||
# [v0.1.1] 2024-02-17
|
||||
|
||||
- Bumped `spacepackets` to v0.10.0
|
||||
|
||||
# [v0.1.0] 2024-02-12
|
||||
|
||||
Initial release containing the `resultcode` macro.
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "satrs-mib"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.61"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
@ -23,12 +23,12 @@ version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.satrs-shared]
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
features = ["serde"]
|
||||
|
||||
[dependencies.satrs-mib-codegen]
|
||||
path = "codegen"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "satrs-mib-codegen"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
description = "satrs-mib proc macro implementation"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
|
||||
@ -26,9 +26,7 @@ features = ["full"]
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = { version = "1", features = ["diff"] }
|
||||
satrs-shared = "0.1.2"
|
||||
|
||||
[dev-dependencies.satrs-mib]
|
||||
path = ".."
|
||||
|
||||
[dev-dependencies.satrs-shared]
|
||||
version = "0.1.1"
|
||||
|
@ -3,10 +3,12 @@ use syn::{parse_macro_input, ItemConst, LitStr};
|
||||
|
||||
/// This macro can be used to automatically generate introspection information for return codes.
|
||||
///
|
||||
/// For example, it can be applied to types like the [satrs_shared::res_code::ResultU16] type
|
||||
/// to automatically generate [satrs_mib::res_code::ResultU16Info] instances. These instances
|
||||
/// can then be used for tasks like generating CSVs or YAML files with the list of all result
|
||||
/// codes. This information is valuable for both operators and developers.
|
||||
/// For example, it can be applied to types like the
|
||||
/// [`satrs_mib::res_code::ResultU16`](https://docs.rs/satrs-mib/latest/satrs_mib/res_code/struct.ResultU16.html#) type
|
||||
/// to automatically generate
|
||||
/// [`satrs_mib::res_code::ResultU16Info`](https://docs.rs/satrs-mib/latest/satrs_mib/res_code/struct.ResultU16Info.html)
|
||||
/// instances. These instances can then be used for tasks like generating CSVs or YAML files with
|
||||
/// the list of all result codes. This information is valuable for both operators and developers.
|
||||
#[proc_macro_attribute]
|
||||
pub fn resultcode(
|
||||
args: proc_macro::TokenStream,
|
||||
|
@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [unreleased]
|
||||
|
||||
# [v0.1.2] 2024-02-17
|
||||
|
||||
- Bumped `spacepackets` to v0.10.0 for `UnsignedEnum` trait change.
|
||||
|
||||
# [v0.1.1] 2024-02-12
|
||||
|
||||
- Added missing `#![no_std]` attribute for library
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "satrs-shared"
|
||||
description = "Components shared by multiple sat-rs crates"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
edition = "2021"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
|
||||
@ -18,7 +18,7 @@ default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.spacepackets]
|
||||
version = "0.9"
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
|
@ -52,6 +52,10 @@ impl UnsignedEnum for ResultU16 {
|
||||
buf[1] = self.unique_id;
|
||||
Ok(self.size())
|
||||
}
|
||||
|
||||
fn value(&self) -> u64 {
|
||||
self.raw() as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssEnumeration for ResultU16 {
|
||||
|
@ -8,6 +8,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [unreleased]
|
||||
|
||||
# [v0.2.0]
|
||||
|
||||
## Added
|
||||
|
||||
- New PUS service abstractions for HK (PUS 3) and actions (PUS 8). Introducing new abstractions
|
||||
allows to move some boilerplate code into the framework.
|
||||
- New `VerificationReportingProvider` abstraction to avoid relying on a concrete verification
|
||||
reporting provider.
|
||||
|
||||
## Changed
|
||||
|
||||
- Verification reporter API timestamp arguments are not `Option`al anymore. Empty timestamps
|
||||
can be passed by simply specifying the `&[]` empty slice argument.
|
||||
|
||||
# [v0.1.1] 2024-02-12
|
||||
|
||||
- Minor fixes for crate config `homepage` entries and links in documentation.
|
||||
|
@ -17,7 +17,7 @@ delegate = ">0.7, <=0.10"
|
||||
paste = "1"
|
||||
smallvec = "1"
|
||||
crc = "3"
|
||||
satrs-shared = "0.1.1"
|
||||
satrs-shared = "0.1.2"
|
||||
|
||||
[dependencies.num_enum]
|
||||
version = ">0.5, <=0.7"
|
||||
@ -68,7 +68,7 @@ features = ["all"]
|
||||
optional = true
|
||||
|
||||
[dependencies.spacepackets]
|
||||
version = "0.9"
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
|
||||
[dependencies.cobs]
|
||||
|
42
satrs/src/action.rs
Normal file
42
satrs/src/action.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use crate::{pool::StoreAddr, TargetId};
|
||||
|
||||
pub type ActionId = u32;
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub enum ActionRequest {
|
||||
UnsignedIdAndStoreData {
|
||||
action_id: ActionId,
|
||||
data_addr: StoreAddr,
|
||||
},
|
||||
#[cfg(feature = "alloc")]
|
||||
UnsignedIdAndVecData {
|
||||
action_id: ActionId,
|
||||
data: alloc::vec::Vec<u8>,
|
||||
},
|
||||
#[cfg(feature = "alloc")]
|
||||
StringIdAndVecData {
|
||||
action_id: alloc::string::String,
|
||||
data: alloc::vec::Vec<u8>,
|
||||
},
|
||||
#[cfg(feature = "alloc")]
|
||||
StringIdAndStoreData {
|
||||
action_id: alloc::string::String,
|
||||
data: StoreAddr,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TargetedActionRequest {
|
||||
target: TargetId,
|
||||
action_request: ActionRequest,
|
||||
}
|
||||
|
||||
impl TargetedActionRequest {
|
||||
pub fn new(target: TargetId, action_request: ActionRequest) -> Self {
|
||||
Self {
|
||||
target,
|
||||
action_request,
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ use spacepackets::{
|
||||
tlv::{msg_to_user::MsgToUserTlv, EntityIdTlv, GenericTlv, TlvType},
|
||||
ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
|
||||
},
|
||||
util::UnsignedByteField,
|
||||
util::{UnsignedByteField, UnsignedEnum},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -9,7 +9,7 @@ use spacepackets::{
|
||||
pdu::{FileDirectiveType, PduError, PduHeader},
|
||||
ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
|
||||
},
|
||||
util::UnsignedByteField,
|
||||
util::{UnsignedByteField, UnsignedEnum},
|
||||
};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
|
@ -412,6 +412,10 @@ impl UnsignedEnum for EventU32 {
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||
self.base.write_to_bytes(self.raw(), buf, self.size())
|
||||
}
|
||||
|
||||
fn value(&self) -> u64 {
|
||||
self.raw().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssEnumeration for EventU32 {
|
||||
@ -425,6 +429,7 @@ impl<SEVERITY: HasSeverity> UnsignedEnum for EventU32TypedSev<SEVERITY> {
|
||||
delegate!(to self.event {
|
||||
fn size(&self) -> usize;
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
|
||||
fn value(&self) -> u64;
|
||||
});
|
||||
}
|
||||
|
||||
@ -560,6 +565,10 @@ impl UnsignedEnum for EventU16 {
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||
self.base.write_to_bytes(self.raw(), buf, self.size())
|
||||
}
|
||||
|
||||
fn value(&self) -> u64 {
|
||||
self.raw().into()
|
||||
}
|
||||
}
|
||||
impl EcssEnumeration for EventU16 {
|
||||
#[inline]
|
||||
@ -573,6 +582,7 @@ impl<SEVERITY: HasSeverity> UnsignedEnum for EventU16TypedSev<SEVERITY> {
|
||||
delegate!(to self.event {
|
||||
fn size(&self) -> usize;
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
|
||||
fn value(&self) -> u64;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,8 @@
|
||||
use crate::{
|
||||
pus::verification::{TcStateAccepted, VerificationToken},
|
||||
TargetId,
|
||||
};
|
||||
|
||||
pub type CollectionIntervalFactor = u32;
|
||||
pub type UniqueId = u32;
|
||||
|
||||
@ -11,6 +16,25 @@ pub enum HkRequest {
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct TargetedHkRequest {
|
||||
target: u32,
|
||||
hk_request: HkRequest,
|
||||
pub target_id: TargetId,
|
||||
pub hk_request: HkRequest,
|
||||
}
|
||||
|
||||
impl TargetedHkRequest {
|
||||
pub fn new(target_id: TargetId, hk_request: HkRequest) -> Self {
|
||||
Self {
|
||||
target_id,
|
||||
hk_request,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PusHkRequestRouter {
|
||||
type Error;
|
||||
fn route(
|
||||
&self,
|
||||
target_id: TargetId,
|
||||
hk_request: HkRequest,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
@ -34,19 +34,25 @@ pub mod events;
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||
pub mod executable;
|
||||
pub mod hal;
|
||||
pub mod hk;
|
||||
pub mod mode;
|
||||
pub mod objects;
|
||||
pub mod params;
|
||||
pub mod pool;
|
||||
pub mod power;
|
||||
pub mod pus;
|
||||
pub mod queue;
|
||||
pub mod request;
|
||||
pub mod res_code;
|
||||
pub mod seq_count;
|
||||
pub mod tmtc;
|
||||
|
||||
pub mod action;
|
||||
pub mod hk;
|
||||
pub mod mode;
|
||||
pub mod params;
|
||||
|
||||
pub use spacepackets;
|
||||
|
||||
// Generic channel ID type.
|
||||
/// Generic channel ID type.
|
||||
pub type ChannelId = u32;
|
||||
|
||||
/// Generic target ID type.
|
||||
pub type TargetId = u64;
|
||||
|
@ -1,9 +1,10 @@
|
||||
use crate::tmtc::TargetId;
|
||||
use core::mem::size_of;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use spacepackets::ByteConversionError;
|
||||
|
||||
use crate::TargetId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ModeAndSubmode {
|
||||
@ -47,12 +48,12 @@ impl ModeAndSubmode {
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ModeCommand {
|
||||
pub struct TargetedModeCommand {
|
||||
pub address: TargetId,
|
||||
pub mode_submode: ModeAndSubmode,
|
||||
}
|
||||
|
||||
impl ModeCommand {
|
||||
impl TargetedModeCommand {
|
||||
pub const fn new(address: TargetId, mode_submode: ModeAndSubmode) -> Self {
|
||||
Self {
|
||||
address,
|
||||
|
@ -51,7 +51,6 @@
|
||||
//! assert_eq!(example_obj.id, obj_id);
|
||||
//! assert_eq!(example_obj.dummy, 42);
|
||||
//! ```
|
||||
use crate::tmtc::TargetId;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(feature = "alloc")]
|
||||
@ -63,6 +62,8 @@ use hashbrown::HashMap;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
use crate::TargetId;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
pub struct ObjectId {
|
||||
pub id: TargetId,
|
||||
|
385
satrs/src/pus/action.rs
Normal file
385
satrs/src/pus/action.rs
Normal file
@ -0,0 +1,385 @@
|
||||
use crate::{action::ActionRequest, TargetId};
|
||||
|
||||
use super::verification::{TcStateAccepted, VerificationToken};
|
||||
|
||||
#[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")))]
|
||||
pub use alloc_mod::*;
|
||||
|
||||
/// 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>;
|
||||
}
|
||||
|
||||
#[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 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 [VerificationReporterWithSender] 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_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||
pub mod std_mod {
|
||||
use crate::pus::{
|
||||
verification::VerificationReportingProvider, EcssTcInMemConverter, GenericRoutingError,
|
||||
PusPacketHandlerResult, PusPacketHandlingError, PusRoutingErrorHandler, PusServiceBase,
|
||||
PusServiceHelper,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// 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 PusService8ActionHandler<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
RequestConverter: PusActionToRequestConverter,
|
||||
RequestRouter: PusActionRequestRouter<Error = RoutingError>,
|
||||
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
|
||||
RoutingError = GenericRoutingError,
|
||||
> {
|
||||
service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
|
||||
pub request_converter: RequestConverter,
|
||||
pub request_router: RequestRouter,
|
||||
pub routing_error_handler: RoutingErrorHandler,
|
||||
}
|
||||
|
||||
impl<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
RequestConverter: PusActionToRequestConverter<Error = PusPacketHandlingError>,
|
||||
RequestRouter: PusActionRequestRouter<Error = RoutingError>,
|
||||
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
|
||||
RoutingError: Clone,
|
||||
>
|
||||
PusService8ActionHandler<
|
||||
TcInMemConverter,
|
||||
VerificationReporter,
|
||||
RequestConverter,
|
||||
RequestRouter,
|
||||
RoutingErrorHandler,
|
||||
RoutingError,
|
||||
>
|
||||
where
|
||||
PusPacketHandlingError: From<RoutingError>,
|
||||
{
|
||||
pub fn new(
|
||||
service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
|
||||
request_converter: RequestConverter,
|
||||
request_router: RequestRouter,
|
||||
routing_error_handler: RoutingErrorHandler,
|
||||
) -> Self {
|
||||
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 =
|
||||
PusServiceBase::<VerificationReporter>::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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use delegate::delegate;
|
||||
|
||||
use spacepackets::{
|
||||
ecss::{
|
||||
tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader},
|
||||
tm::PusTmReader,
|
||||
PusPacket,
|
||||
},
|
||||
CcsdsPacket, SequenceFlags, SpHeader,
|
||||
};
|
||||
|
||||
use crate::pus::{
|
||||
tests::{
|
||||
PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler, TestConverter,
|
||||
TestRouter, TestRoutingErrorHandler, APP_DATA_TOO_SHORT, TEST_APID,
|
||||
},
|
||||
verification::{
|
||||
tests::TestVerificationReporter, FailParams, RequestId, VerificationReportingProvider,
|
||||
},
|
||||
EcssTcInVecConverter, GenericRoutingError, PusPacketHandlerResult, PusPacketHandlingError,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl PusActionRequestRouter for TestRouter<ActionRequest> {
|
||||
type Error = GenericRoutingError;
|
||||
|
||||
fn route(
|
||||
&self,
|
||||
target_id: TargetId,
|
||||
hk_request: ActionRequest,
|
||||
_token: VerificationToken<TcStateAccepted>,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.routing_requests
|
||||
.borrow_mut()
|
||||
.push_back((target_id, hk_request));
|
||||
self.check_for_injected_error()
|
||||
}
|
||||
}
|
||||
|
||||
impl PusActionToRequestConverter for TestConverter<8> {
|
||||
type Error = PusPacketHandlingError;
|
||||
fn convert(
|
||||
&mut self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
tc: &PusTcReader,
|
||||
time_stamp: &[u8],
|
||||
verif_reporter: &impl VerificationReportingProvider,
|
||||
) -> Result<(TargetId, ActionRequest), 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() == 1 {
|
||||
verif_reporter
|
||||
.start_success(token, time_stamp)
|
||||
.expect("start success failure");
|
||||
return Ok((
|
||||
target_id.into(),
|
||||
ActionRequest::UnsignedIdAndVecData {
|
||||
action_id: u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()),
|
||||
data: tc.user_data()[4..].to_vec(),
|
||||
},
|
||||
));
|
||||
}
|
||||
Err(PusPacketHandlingError::InvalidAppData(
|
||||
"unexpected app data".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
struct Pus8HandlerWithVecTester {
|
||||
common: PusServiceHandlerWithVecCommon<TestVerificationReporter>,
|
||||
handler: PusService8ActionHandler<
|
||||
EcssTcInVecConverter,
|
||||
TestVerificationReporter,
|
||||
TestConverter<8>,
|
||||
TestRouter<ActionRequest>,
|
||||
TestRoutingErrorHandler,
|
||||
>,
|
||||
}
|
||||
|
||||
impl Pus8HandlerWithVecTester {
|
||||
pub fn new() -> Self {
|
||||
let (common, srv_handler) =
|
||||
PusServiceHandlerWithVecCommon::new_with_test_verif_sender();
|
||||
Self {
|
||||
common,
|
||||
handler: PusService8ActionHandler::new(
|
||||
srv_handler,
|
||||
TestConverter::default(),
|
||||
TestRouter::default(),
|
||||
TestRoutingErrorHandler::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, ActionRequest);
|
||||
}
|
||||
}
|
||||
delegate! {
|
||||
to self.handler.routing_error_handler {
|
||||
pub fn retrieve_next_error(&mut self) -> (TargetId, GenericRoutingError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PusTestHarness for Pus8HandlerWithVecTester {
|
||||
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 Pus8HandlerWithVecTester {
|
||||
delegate! {
|
||||
to self.handler {
|
||||
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_test() {
|
||||
let mut action_handler = Pus8HandlerWithVecTester::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());
|
||||
if let ActionRequest::UnsignedIdAndVecData { action_id, data } = action_req {
|
||||
assert_eq!(action_id, 1);
|
||||
assert_eq!(data, &[]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_routing_error() {
|
||||
let mut action_handler = Pus8HandlerWithVecTester::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());
|
||||
if let ActionRequest::UnsignedIdAndVecData { action_id, data } = action_req {
|
||||
assert_eq!(action_id, 1);
|
||||
assert_eq!(data, &[]);
|
||||
}
|
||||
|
||||
let (target_id, found_error) = action_handler.retrieve_next_error();
|
||||
assert_eq!(target_id, TEST_APID.into());
|
||||
check_error(found_error);
|
||||
}
|
||||
}
|
@ -6,16 +6,24 @@ use spacepackets::ecss::event::Subservice;
|
||||
use spacepackets::ecss::PusPacket;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use super::verification::VerificationReportingProvider;
|
||||
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
|
||||
|
||||
pub struct PusService5EventHandler<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub service_helper: PusServiceHelper<TcInMemConverter>,
|
||||
pub struct PusService5EventHandler<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
> {
|
||||
pub service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
|
||||
event_request_tx: Sender<EventRequestWithToken>,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> PusService5EventHandler<TcInMemConverter> {
|
||||
impl<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
> PusService5EventHandler<TcInMemConverter, VerificationReporter>
|
||||
{
|
||||
pub fn new(
|
||||
service_handler: PusServiceHelper<TcInMemConverter>,
|
||||
service_handler: PusServiceHelper<TcInMemConverter, VerificationReporter>,
|
||||
event_request_tx: Sender<EventRequestWithToken>,
|
||||
) -> Self {
|
||||
Self {
|
||||
@ -44,9 +52,10 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService5EventHandler<TcInMemConv
|
||||
}
|
||||
let handle_enable_disable_request = |enable: bool, stamp: [u8; 7]| {
|
||||
if tc.user_data().len() < 4 {
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData(
|
||||
"at least 4 bytes event ID expected".into(),
|
||||
));
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData {
|
||||
expected: 4,
|
||||
found: tc.user_data().len(),
|
||||
});
|
||||
}
|
||||
let user_data = tc.user_data();
|
||||
let event_u32 = EventU32::from(u32::from_be_bytes(user_data[0..4].try_into().unwrap()));
|
||||
@ -54,8 +63,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService5EventHandler<TcInMemConv
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.borrow_mut()
|
||||
.start_success(ecss_tc_and_token.token, Some(&stamp))
|
||||
.start_success(ecss_tc_and_token.token, &stamp)
|
||||
.map_err(|_| PartialPusHandlingError::Verification);
|
||||
let partial_error = start_token.clone().err();
|
||||
let mut token: TcStateToken = ecss_tc_and_token.token.into();
|
||||
@ -86,7 +94,9 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService5EventHandler<TcInMemConv
|
||||
Ok(PusPacketHandlerResult::RequestHandled)
|
||||
};
|
||||
let mut partial_error = None;
|
||||
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
|
||||
let time_stamp = PusServiceBase::<VerificationReporter>::get_current_cds_short_timestamp(
|
||||
&mut partial_error,
|
||||
);
|
||||
match srv.unwrap() {
|
||||
Subservice::TmInfoReport
|
||||
| Subservice::TmLowSeverityReport
|
||||
@ -128,7 +138,7 @@ mod tests {
|
||||
|
||||
use crate::pus::event_man::EventRequest;
|
||||
use crate::pus::tests::SimplePusPacketHandler;
|
||||
use crate::pus::verification::RequestId;
|
||||
use crate::pus::verification::{RequestId, VerificationReporterWithSender};
|
||||
use crate::{
|
||||
events::EventU32,
|
||||
pus::{
|
||||
@ -145,7 +155,8 @@ mod tests {
|
||||
|
||||
struct Pus5HandlerWithStoreTester {
|
||||
common: PusServiceHandlerWithSharedStoreCommon,
|
||||
handler: PusService5EventHandler<EcssTcInSharedStoreConverter>,
|
||||
handler:
|
||||
PusService5EventHandler<EcssTcInSharedStoreConverter, VerificationReporterWithSender>,
|
||||
}
|
||||
|
||||
impl Pus5HandlerWithStoreTester {
|
||||
@ -271,8 +282,9 @@ mod tests {
|
||||
let result = test_harness.handle_one_tc();
|
||||
assert!(result.is_err());
|
||||
let result = result.unwrap_err();
|
||||
if let PusPacketHandlingError::NotEnoughAppData(string) = result {
|
||||
assert_eq!(string, "at least 4 bytes event ID expected");
|
||||
if let PusPacketHandlingError::NotEnoughAppData { expected, found } = result {
|
||||
assert_eq!(expected, 4);
|
||||
assert_eq!(found, 3);
|
||||
} else {
|
||||
panic!("unexpected result type {result:?}")
|
||||
}
|
||||
|
@ -1 +1,394 @@
|
||||
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")))]
|
||||
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 {
|
||||
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
|
||||
/// a [HkRequest].
|
||||
///
|
||||
/// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard.
|
||||
/// The only requirement is that a valid [TargetId] and a [HkRequest] 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 [VerificationReporterWithSender] instance is passed to the user to also allow handling
|
||||
/// of the verification process as part of the PUS standard requirements.
|
||||
pub trait PusHkToRequestConverter {
|
||||
type Error;
|
||||
fn convert(
|
||||
&mut self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
tc: &PusTcReader,
|
||||
time_stamp: &[u8],
|
||||
verif_reporter: &impl VerificationReportingProvider,
|
||||
) -> Result<(TargetId, HkRequest), Self::Error>;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||
pub mod std_mod {
|
||||
use crate::pus::{
|
||||
verification::VerificationReportingProvider, EcssTcInMemConverter, GenericRoutingError,
|
||||
PusPacketHandlerResult, PusPacketHandlingError, PusRoutingErrorHandler, PusServiceBase,
|
||||
PusServiceHelper,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// This is a generic high-level handler for the PUS service 3 housekeeping 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
|
||||
/// [PusPacketHandlerResult] for the concrete implementation which offers a packet handler.
|
||||
/// 3. Route the action request using the provided [PusActionRequestRouter]. The generic error
|
||||
/// type is constrained to the [GenericRoutingError] for the concrete implementation.
|
||||
/// 4. Handle all routing errors using the provided [PusRoutingErrorHandler]. The generic error
|
||||
/// type is constrained to the [GenericRoutingError] for the concrete implementation.
|
||||
pub struct PusService3HkHandler<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
RequestConverter: PusHkToRequestConverter,
|
||||
RequestRouter: PusHkRequestRouter<Error = RoutingError>,
|
||||
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
|
||||
RoutingError = GenericRoutingError,
|
||||
> {
|
||||
service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
|
||||
pub request_converter: RequestConverter,
|
||||
pub request_router: RequestRouter,
|
||||
pub routing_error_handler: RoutingErrorHandler,
|
||||
}
|
||||
|
||||
impl<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
RequestConverter: PusHkToRequestConverter<Error = PusPacketHandlingError>,
|
||||
RequestRouter: PusHkRequestRouter<Error = RoutingError>,
|
||||
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
|
||||
RoutingError: Clone,
|
||||
>
|
||||
PusService3HkHandler<
|
||||
TcInMemConverter,
|
||||
VerificationReporter,
|
||||
RequestConverter,
|
||||
RequestRouter,
|
||||
RoutingErrorHandler,
|
||||
RoutingError,
|
||||
>
|
||||
where
|
||||
PusPacketHandlingError: From<RoutingError>,
|
||||
{
|
||||
pub fn new(
|
||||
service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
|
||||
request_converter: RequestConverter,
|
||||
request_router: RequestRouter,
|
||||
routing_error_handler: RoutingErrorHandler,
|
||||
) -> Self {
|
||||
Self {
|
||||
service_helper,
|
||||
request_converter,
|
||||
request_router,
|
||||
routing_error_handler,
|
||||
}
|
||||
}
|
||||
|
||||
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 =
|
||||
PusServiceBase::<VerificationReporter>::get_current_cds_short_timestamp(
|
||||
&mut partial_error,
|
||||
);
|
||||
let (target_id, hk_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, hk_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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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::{
|
||||
hk::HkRequest,
|
||||
pus::{
|
||||
tests::{
|
||||
PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler,
|
||||
TestConverter, TestRouter, TestRoutingErrorHandler, APP_DATA_TOO_SHORT, TEST_APID,
|
||||
},
|
||||
verification::{
|
||||
tests::TestVerificationReporter, FailParams, RequestId, TcStateAccepted,
|
||||
VerificationReportingProvider, VerificationToken,
|
||||
},
|
||||
EcssTcInVecConverter, GenericRoutingError, PusPacketHandlerResult,
|
||||
PusPacketHandlingError,
|
||||
},
|
||||
TargetId,
|
||||
};
|
||||
|
||||
use super::{PusHkRequestRouter, PusHkToRequestConverter, PusService3HkHandler};
|
||||
|
||||
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 PusHkToRequestConverter 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: PusService3HkHandler<
|
||||
EcssTcInVecConverter,
|
||||
TestVerificationReporter,
|
||||
TestConverter<3>,
|
||||
TestRouter<HkRequest>,
|
||||
TestRoutingErrorHandler,
|
||||
>,
|
||||
}
|
||||
|
||||
impl Pus3HandlerWithVecTester {
|
||||
pub fn new() -> Self {
|
||||
let (common, srv_handler) =
|
||||
PusServiceHandlerWithVecCommon::new_with_test_verif_sender();
|
||||
Self {
|
||||
common,
|
||||
handler: PusService3HkHandler::new(
|
||||
srv_handler,
|
||||
TestConverter::default(),
|
||||
TestRouter::default(),
|
||||
TestRoutingErrorHandler::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.routing_error_handler {
|
||||
pub fn retrieve_next_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_error();
|
||||
assert_eq!(target_id, TEST_APID.into());
|
||||
check_error(found_error);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
//!
|
||||
//! This module contains structures to make working with the PUS C standard easier.
|
||||
//! The satrs-example application contains various usage examples of these components.
|
||||
use crate::queue::{GenericRecvError, GenericSendError};
|
||||
use crate::ChannelId;
|
||||
use core::fmt::{Display, Formatter};
|
||||
#[cfg(feature = "alloc")]
|
||||
@ -16,6 +17,7 @@ use spacepackets::ecss::tm::PusTmCreator;
|
||||
use spacepackets::ecss::PusError;
|
||||
use spacepackets::{ByteConversionError, SpHeader};
|
||||
|
||||
pub mod action;
|
||||
pub mod event;
|
||||
pub mod event_man;
|
||||
#[cfg(feature = "std")]
|
||||
@ -55,52 +57,6 @@ impl<'tm> From<PusTmCreator<'tm>> for PusTmWrapper<'tm> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic error type for sending something via a message queue.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum GenericSendError {
|
||||
RxDisconnected,
|
||||
QueueFull(Option<u32>),
|
||||
}
|
||||
|
||||
impl Display for GenericSendError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
GenericSendError::RxDisconnected => {
|
||||
write!(f, "rx side has disconnected")
|
||||
}
|
||||
GenericSendError::QueueFull(max_cap) => {
|
||||
write!(f, "queue with max capacity of {max_cap:?} is full")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for GenericSendError {}
|
||||
|
||||
/// Generic error type for sending something via a message queue.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum GenericRecvError {
|
||||
Empty,
|
||||
TxDisconnected,
|
||||
}
|
||||
|
||||
impl Display for GenericRecvError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::TxDisconnected => {
|
||||
write!(f, "tx side has disconnected")
|
||||
}
|
||||
Self::Empty => {
|
||||
write!(f, "nothing to receive")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for GenericRecvError {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EcssTmtcError {
|
||||
StoreLock,
|
||||
@ -304,8 +260,12 @@ pub trait ReceivesEcssPusTc {
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
mod alloc_mod {
|
||||
use crate::TargetId;
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::pus::verification::VerificationReportingProvider;
|
||||
|
||||
/// Extension trait for [EcssTmSenderCore].
|
||||
///
|
||||
/// It provides additional functionality, for example by implementing the [Downcast] trait
|
||||
@ -385,22 +345,33 @@ mod alloc_mod {
|
||||
impl<T> EcssTcReceiver for T where T: EcssTcReceiverCore + 'static {}
|
||||
|
||||
impl_downcast!(EcssTcReceiver);
|
||||
|
||||
pub trait PusRoutingErrorHandler {
|
||||
type Error;
|
||||
fn handle_error(
|
||||
&self,
|
||||
target_id: TargetId,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
tc: &PusTcReader,
|
||||
error: Self::Error,
|
||||
time_stamp: &[u8],
|
||||
verif_reporter: &impl VerificationReportingProvider,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||
pub mod std_mod {
|
||||
use crate::pool::{PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool, StoreAddr};
|
||||
use crate::pus::verification::{
|
||||
StdVerifReporterWithSender, TcStateAccepted, VerificationToken,
|
||||
};
|
||||
use crate::pus::verification::{TcStateAccepted, VerificationToken};
|
||||
use crate::pus::{
|
||||
EcssChannel, EcssTcAndToken, EcssTcReceiver, EcssTcReceiverCore, EcssTmSender,
|
||||
EcssTmSenderCore, EcssTmtcError, GenericRecvError, GenericSendError, PusTmWrapper,
|
||||
TryRecvTmtcError,
|
||||
};
|
||||
use crate::tmtc::tm_helper::SharedTmPool;
|
||||
use crate::ChannelId;
|
||||
use crate::{ChannelId, TargetId};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use crossbeam_channel as cb;
|
||||
@ -410,13 +381,12 @@ pub mod std_mod {
|
||||
use spacepackets::time::cds::TimeProvider;
|
||||
use spacepackets::time::StdTimestampError;
|
||||
use spacepackets::time::TimeWriter;
|
||||
use std::cell::RefCell;
|
||||
use std::string::String;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::TryRecvError;
|
||||
use thiserror::Error;
|
||||
|
||||
use super::verification::VerificationReporterWithSender;
|
||||
use super::verification::VerificationReportingProvider;
|
||||
use super::{AcceptedEcssTcAndToken, TcInMemory};
|
||||
|
||||
impl From<mpsc::SendError<StoreAddr>> for EcssTmtcError {
|
||||
@ -662,6 +632,20 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 .
|
||||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
pub enum GenericRoutingError {
|
||||
#[error("not enough application data, expected at least {expected}, found {found}")]
|
||||
NotEnoughAppData { expected: usize, found: usize },
|
||||
#[error("Unknown target ID {0}")]
|
||||
UnknownTargetId(TargetId),
|
||||
#[error("Sending action request failed: {0}")]
|
||||
SendError(GenericSendError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
pub enum PusPacketHandlingError {
|
||||
#[error("generic PUS error: {0}")]
|
||||
@ -670,8 +654,8 @@ pub mod std_mod {
|
||||
WrongService(u8),
|
||||
#[error("invalid subservice {0}")]
|
||||
InvalidSubservice(u8),
|
||||
#[error("not enough application data available: {0}")]
|
||||
NotEnoughAppData(String),
|
||||
#[error("not enough application data, expected at least {expected}, found {found}")]
|
||||
NotEnoughAppData { expected: usize, found: usize },
|
||||
#[error("PUS packet too large, does not fit in buffer: {0}")]
|
||||
PusPacketTooLarge(usize),
|
||||
#[error("invalid application data")]
|
||||
@ -682,6 +666,8 @@ pub mod std_mod {
|
||||
EcssTmtc(#[from] EcssTmtcError),
|
||||
#[error("invalid verification token")]
|
||||
InvalidVerificationToken,
|
||||
#[error("request routing error: {0}")]
|
||||
RequestRoutingError(#[from] GenericRoutingError),
|
||||
#[error("other error {0}")]
|
||||
Other(String),
|
||||
}
|
||||
@ -825,18 +811,18 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PusServiceBase {
|
||||
pub struct PusServiceBase<VerificationReporter: VerificationReportingProvider> {
|
||||
pub tc_receiver: Box<dyn EcssTcReceiver>,
|
||||
pub tm_sender: Box<dyn EcssTmSender>,
|
||||
pub tm_apid: u16,
|
||||
/// The verification handler is wrapped in a [RefCell] to allow the interior mutability
|
||||
/// pattern. This makes writing methods which are not mutable a lot easier.
|
||||
pub verification_handler: RefCell<StdVerifReporterWithSender>,
|
||||
pub verification_handler: VerificationReporter,
|
||||
}
|
||||
|
||||
impl PusServiceBase {
|
||||
impl<VerificationReporter: VerificationReportingProvider> PusServiceBase<VerificationReporter> {
|
||||
#[cfg(feature = "std")]
|
||||
pub fn get_current_timestamp(
|
||||
pub fn get_current_cds_short_timestamp(
|
||||
partial_error: &mut Option<PartialPusHandlingError>,
|
||||
) -> [u8; 7] {
|
||||
let mut time_stamp: [u8; 7] = [0; 7];
|
||||
@ -850,11 +836,10 @@ pub mod std_mod {
|
||||
}
|
||||
time_stamp
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn get_current_timestamp_ignore_error() -> [u8; 7] {
|
||||
let mut dummy = None;
|
||||
Self::get_current_timestamp(&mut dummy)
|
||||
Self::get_current_cds_short_timestamp(&mut dummy)
|
||||
}
|
||||
}
|
||||
|
||||
@ -867,17 +852,24 @@ pub mod std_mod {
|
||||
/// This base class can handle PUS telecommands backed by different memory storage machanisms
|
||||
/// by using the [EcssTcInMemConverter] abstraction. This object provides some convenience
|
||||
/// methods to make the generic parts of TC handling easier.
|
||||
pub struct PusServiceHelper<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub common: PusServiceBase,
|
||||
pub struct PusServiceHelper<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
> {
|
||||
pub common: PusServiceBase<VerificationReporter>,
|
||||
pub tc_in_mem_converter: TcInMemConverter,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> PusServiceHelper<TcInMemConverter> {
|
||||
impl<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
> PusServiceHelper<TcInMemConverter, VerificationReporter>
|
||||
{
|
||||
pub fn new(
|
||||
tc_receiver: Box<dyn EcssTcReceiver>,
|
||||
tm_sender: Box<dyn EcssTmSender>,
|
||||
tm_apid: u16,
|
||||
verification_handler: VerificationReporterWithSender,
|
||||
verification_handler: VerificationReporter,
|
||||
tc_in_mem_converter: TcInMemConverter,
|
||||
) -> Self {
|
||||
Self {
|
||||
@ -885,7 +877,7 @@ pub mod std_mod {
|
||||
tc_receiver,
|
||||
tm_sender,
|
||||
tm_apid,
|
||||
verification_handler: RefCell::new(verification_handler),
|
||||
verification_handler,
|
||||
},
|
||||
tc_in_mem_converter,
|
||||
}
|
||||
@ -939,12 +931,15 @@ pub(crate) fn source_buffer_large_enough(cap: usize, len: usize) -> Result<(), E
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use core::cell::RefCell;
|
||||
use std::sync::mpsc::TryRecvError;
|
||||
use std::sync::{mpsc, RwLock};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec;
|
||||
use spacepackets::ecss::tc::PusTcCreator;
|
||||
use alloc::collections::VecDeque;
|
||||
use alloc::vec::Vec;
|
||||
use satrs_shared::res_code::ResultU16;
|
||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
|
||||
use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader};
|
||||
use spacepackets::ecss::{PusPacket, WritablePusPacket};
|
||||
use spacepackets::CcsdsPacket;
|
||||
@ -954,14 +949,17 @@ pub mod tests {
|
||||
};
|
||||
use crate::pus::verification::RequestId;
|
||||
use crate::tmtc::tm_helper::SharedTmPool;
|
||||
use crate::TargetId;
|
||||
|
||||
use super::verification::tests::{SharedVerificationMap, TestVerificationReporter};
|
||||
use super::verification::{
|
||||
TcStateAccepted, VerificationReporterCfg, VerificationReporterWithSender, VerificationToken,
|
||||
TcStateAccepted, VerificationReporterCfg, VerificationReporterWithSender,
|
||||
VerificationReportingProvider, VerificationToken,
|
||||
};
|
||||
use super::{
|
||||
EcssTcAndToken, EcssTcInSharedStoreConverter, EcssTcInVecConverter, MpscTcReceiver,
|
||||
MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
|
||||
PusPacketHandlingError, PusServiceHelper, TcInMemory,
|
||||
EcssTcAndToken, EcssTcInSharedStoreConverter, EcssTcInVecConverter, GenericRoutingError,
|
||||
MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
|
||||
PusPacketHandlingError, PusRoutingErrorHandler, PusServiceHelper, TcInMemory,
|
||||
};
|
||||
|
||||
pub const TEST_APID: u16 = 0x101;
|
||||
@ -1016,8 +1014,11 @@ pub mod tests {
|
||||
/// [PusServiceHandler] which might be required for a specific PUS service handler.
|
||||
///
|
||||
/// The PUS service handler is instantiated with a [EcssTcInStoreConverter].
|
||||
pub fn new() -> (Self, PusServiceHelper<EcssTcInSharedStoreConverter>) {
|
||||
let pool_cfg = StaticPoolConfig::new(vec![(16, 16), (8, 32), (4, 64)], false);
|
||||
pub fn new() -> (
|
||||
Self,
|
||||
PusServiceHelper<EcssTcInSharedStoreConverter, VerificationReporterWithSender>,
|
||||
) {
|
||||
let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false);
|
||||
let tc_pool = StaticMemoryPool::new(pool_cfg.clone());
|
||||
let tm_pool = StaticMemoryPool::new(pool_cfg);
|
||||
let shared_tc_pool = SharedStaticMemoryPool::new(RwLock::new(tc_pool));
|
||||
@ -1062,7 +1063,7 @@ pub mod tests {
|
||||
let token = self.verification_handler.add_tc(tc);
|
||||
let token = self
|
||||
.verification_handler
|
||||
.acceptance_success(token, Some(&[0; 7]))
|
||||
.acceptance_success(token, &[0; 7])
|
||||
.unwrap();
|
||||
let tc_size = tc.write_to_bytes(&mut self.pus_buf).unwrap();
|
||||
let mut tc_pool = self.tc_pool.write().unwrap();
|
||||
@ -1109,15 +1110,18 @@ pub mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PusServiceHandlerWithVecCommon {
|
||||
pub struct PusServiceHandlerWithVecCommon<VerificationReporter: VerificationReportingProvider> {
|
||||
current_tm: Option<alloc::vec::Vec<u8>>,
|
||||
tc_sender: mpsc::Sender<EcssTcAndToken>,
|
||||
tm_receiver: mpsc::Receiver<alloc::vec::Vec<u8>>,
|
||||
verification_handler: VerificationReporterWithSender,
|
||||
pub verification_handler: VerificationReporter,
|
||||
}
|
||||
|
||||
impl PusServiceHandlerWithVecCommon {
|
||||
pub fn new() -> (Self, PusServiceHelper<EcssTcInVecConverter>) {
|
||||
impl PusServiceHandlerWithVecCommon<VerificationReporterWithSender> {
|
||||
pub fn new_with_standard_verif_reporter() -> (
|
||||
Self,
|
||||
PusServiceHelper<EcssTcInVecConverter, VerificationReporterWithSender>,
|
||||
) {
|
||||
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
|
||||
let (tm_tx, tm_rx) = mpsc::channel();
|
||||
|
||||
@ -1125,6 +1129,7 @@ pub mod tests {
|
||||
let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
|
||||
let verification_handler =
|
||||
VerificationReporterWithSender::new(&verif_cfg, Box::new(verif_sender));
|
||||
|
||||
let test_srv_tm_sender = MpscTmAsVecSender::new(0, "test-sender", tm_tx);
|
||||
let test_srv_tc_receiver = MpscTcReceiver::new(0, "test-receiver", test_srv_tc_rx);
|
||||
let in_store_converter = EcssTcInVecConverter::default();
|
||||
@ -1144,12 +1149,47 @@ pub mod tests {
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PusServiceHandlerWithVecCommon<TestVerificationReporter> {
|
||||
pub fn new_with_test_verif_sender() -> (
|
||||
Self,
|
||||
PusServiceHelper<EcssTcInVecConverter, TestVerificationReporter>,
|
||||
) {
|
||||
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
|
||||
let (tm_tx, tm_rx) = mpsc::channel();
|
||||
|
||||
let test_srv_tm_sender = MpscTmAsVecSender::new(0, "test-sender", tm_tx);
|
||||
let test_srv_tc_receiver = MpscTcReceiver::new(0, "test-receiver", test_srv_tc_rx);
|
||||
let in_store_converter = EcssTcInVecConverter::default();
|
||||
let shared_verif_map = SharedVerificationMap::default();
|
||||
let verification_handler = TestVerificationReporter::new(shared_verif_map);
|
||||
(
|
||||
Self {
|
||||
current_tm: None,
|
||||
tc_sender: test_srv_tc_tx,
|
||||
tm_receiver: tm_rx,
|
||||
verification_handler: verification_handler.clone(),
|
||||
},
|
||||
PusServiceHelper::new(
|
||||
Box::new(test_srv_tc_receiver),
|
||||
Box::new(test_srv_tm_sender),
|
||||
TEST_APID,
|
||||
verification_handler,
|
||||
in_store_converter,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<VerificationReporter: VerificationReportingProvider>
|
||||
PusServiceHandlerWithVecCommon<VerificationReporter>
|
||||
{
|
||||
pub fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted> {
|
||||
let token = self.verification_handler.add_tc(tc);
|
||||
let token = self
|
||||
.verification_handler
|
||||
.acceptance_success(token, Some(&[0; 7]))
|
||||
.acceptance_success(token, &[0; 7])
|
||||
.unwrap();
|
||||
// Send accepted TC to test service handler.
|
||||
self.tc_sender
|
||||
@ -1191,4 +1231,106 @@ pub mod tests {
|
||||
assert_eq!(req_id, expected_request_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub const APP_DATA_TOO_SHORT: ResultU16 = ResultU16::new(1, 1);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestConverter<const SERVICE: u8> {
|
||||
pub conversion_request: VecDeque<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<const SERVICE: u8> TestConverter<SERVICE> {
|
||||
pub fn check_service(&self, tc: &PusTcReader) -> Result<(), PusPacketHandlingError> {
|
||||
if tc.service() != SERVICE {
|
||||
return Err(PusPacketHandlingError::WrongService(tc.service()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) {
|
||||
self.conversion_request.is_empty();
|
||||
}
|
||||
|
||||
pub fn check_next_conversion(&mut self, tc: &PusTcCreator) {
|
||||
assert!(!self.conversion_request.is_empty());
|
||||
assert_eq!(
|
||||
self.conversion_request.pop_front().unwrap(),
|
||||
tc.to_vec().unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestRoutingErrorHandler {
|
||||
pub routing_errors: RefCell<VecDeque<(TargetId, GenericRoutingError)>>,
|
||||
}
|
||||
|
||||
impl PusRoutingErrorHandler for TestRoutingErrorHandler {
|
||||
type Error = GenericRoutingError;
|
||||
|
||||
fn handle_error(
|
||||
&self,
|
||||
target_id: TargetId,
|
||||
_token: VerificationToken<TcStateAccepted>,
|
||||
_tc: &PusTcReader,
|
||||
error: Self::Error,
|
||||
_time_stamp: &[u8],
|
||||
_verif_reporter: &impl VerificationReportingProvider,
|
||||
) {
|
||||
self.routing_errors
|
||||
.borrow_mut()
|
||||
.push_back((target_id, error));
|
||||
}
|
||||
}
|
||||
|
||||
impl TestRoutingErrorHandler {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.routing_errors.borrow().is_empty()
|
||||
}
|
||||
|
||||
pub fn retrieve_next_error(&mut self) -> (TargetId, GenericRoutingError) {
|
||||
if self.routing_errors.borrow().is_empty() {
|
||||
panic!("no routing request available");
|
||||
}
|
||||
self.routing_errors.borrow_mut().pop_front().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestRouter<REQUEST> {
|
||||
pub routing_requests: RefCell<VecDeque<(TargetId, REQUEST)>>,
|
||||
pub injected_routing_failure: RefCell<Option<GenericRoutingError>>,
|
||||
}
|
||||
|
||||
impl<REQUEST> Default for TestRouter<REQUEST> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
routing_requests: Default::default(),
|
||||
injected_routing_failure: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<REQUEST> TestRouter<REQUEST> {
|
||||
pub fn check_for_injected_error(&self) -> Result<(), GenericRoutingError> {
|
||||
if self.injected_routing_failure.borrow().is_some() {
|
||||
return Err(self.injected_routing_failure.borrow_mut().take().unwrap());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn inject_routing_error(&mut self, error: GenericRoutingError) {
|
||||
*self.injected_routing_failure.borrow_mut() = Some(error);
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.routing_requests.borrow().is_empty()
|
||||
}
|
||||
|
||||
pub fn retrieve_next_request(&mut self) -> (TargetId, REQUEST) {
|
||||
if self.routing_requests.borrow().is_empty() {
|
||||
panic!("no routing request available");
|
||||
}
|
||||
self.routing_requests.borrow_mut().pop_front().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::scheduler::PusSchedulerProvider;
|
||||
use super::verification::VerificationReportingProvider;
|
||||
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
|
||||
use crate::pool::PoolProvider;
|
||||
use crate::pus::{PusPacketHandlerResult, PusPacketHandlingError};
|
||||
@ -16,16 +17,23 @@ use spacepackets::time::cds::TimeProvider;
|
||||
/// telecommands when applicable.
|
||||
pub struct PusService11SchedHandler<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
PusScheduler: PusSchedulerProvider,
|
||||
> {
|
||||
pub service_helper: PusServiceHelper<TcInMemConverter>,
|
||||
pub service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
|
||||
scheduler: PusScheduler,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
||||
PusService11SchedHandler<TcInMemConverter, Scheduler>
|
||||
impl<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
Scheduler: PusSchedulerProvider,
|
||||
> PusService11SchedHandler<TcInMemConverter, VerificationReporter, Scheduler>
|
||||
{
|
||||
pub fn new(service_helper: PusServiceHelper<TcInMemConverter>, scheduler: Scheduler) -> Self {
|
||||
pub fn new(
|
||||
service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
|
||||
scheduler: Scheduler,
|
||||
) -> Self {
|
||||
Self {
|
||||
service_helper,
|
||||
scheduler,
|
||||
@ -62,15 +70,16 @@ impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
||||
));
|
||||
}
|
||||
let mut partial_error = None;
|
||||
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
|
||||
let time_stamp = PusServiceBase::<VerificationReporter>::get_current_cds_short_timestamp(
|
||||
&mut partial_error,
|
||||
);
|
||||
match standard_subservice.unwrap() {
|
||||
scheduling::Subservice::TcEnableScheduling => {
|
||||
let start_token = self
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
|
||||
.start_success(ecss_tc_and_token.token, &time_stamp)
|
||||
.expect("Error sending start success");
|
||||
|
||||
self.scheduler.enable();
|
||||
@ -78,8 +87,7 @@ impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.completion_success(start_token, Some(&time_stamp))
|
||||
.completion_success(start_token, &time_stamp)
|
||||
.expect("Error sending completion success");
|
||||
} else {
|
||||
return Err(PusPacketHandlingError::Other(
|
||||
@ -92,8 +100,7 @@ impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
|
||||
.start_success(ecss_tc_and_token.token, &time_stamp)
|
||||
.expect("Error sending start success");
|
||||
|
||||
self.scheduler.disable();
|
||||
@ -101,8 +108,7 @@ impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.completion_success(start_token, Some(&time_stamp))
|
||||
.completion_success(start_token, &time_stamp)
|
||||
.expect("Error sending completion success");
|
||||
} else {
|
||||
return Err(PusPacketHandlingError::Other(
|
||||
@ -115,8 +121,7 @@ impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
|
||||
.start_success(ecss_tc_and_token.token, &time_stamp)
|
||||
.expect("Error sending start success");
|
||||
|
||||
self.scheduler
|
||||
@ -126,8 +131,7 @@ impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.completion_success(start_token, Some(&time_stamp))
|
||||
.completion_success(start_token, &time_stamp)
|
||||
.expect("Error sending completion success");
|
||||
}
|
||||
scheduling::Subservice::TcInsertActivity => {
|
||||
@ -135,8 +139,7 @@ impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_success(ecss_tc_and_token.token, Some(&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");
|
||||
@ -147,8 +150,7 @@ impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.completion_success(start_token, Some(&time_stamp))
|
||||
.completion_success(start_token, &time_stamp)
|
||||
.expect("sending completion success failed");
|
||||
}
|
||||
_ => {
|
||||
@ -172,6 +174,7 @@ impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
||||
mod tests {
|
||||
use crate::pool::{StaticMemoryPool, StaticPoolConfig};
|
||||
use crate::pus::tests::TEST_APID;
|
||||
use crate::pus::verification::VerificationReporterWithSender;
|
||||
use crate::pus::{
|
||||
scheduler::{self, PusSchedulerProvider, TcInfo},
|
||||
tests::{PusServiceHandlerWithSharedStoreCommon, PusTestHarness},
|
||||
@ -194,7 +197,11 @@ mod tests {
|
||||
|
||||
struct Pus11HandlerWithStoreTester {
|
||||
common: PusServiceHandlerWithSharedStoreCommon,
|
||||
handler: PusService11SchedHandler<EcssTcInSharedStoreConverter, TestScheduler>,
|
||||
handler: PusService11SchedHandler<
|
||||
EcssTcInSharedStoreConverter,
|
||||
VerificationReporterWithSender,
|
||||
TestScheduler,
|
||||
>,
|
||||
sched_tc_pool: StaticMemoryPool,
|
||||
}
|
||||
|
||||
|
@ -5,16 +5,24 @@ use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||
use spacepackets::ecss::PusPacket;
|
||||
use spacepackets::SpHeader;
|
||||
|
||||
use super::verification::VerificationReportingProvider;
|
||||
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
|
||||
|
||||
/// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets.
|
||||
/// This handler only processes ping requests and generates a ping reply for them accordingly.
|
||||
pub struct PusService17TestHandler<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub service_helper: PusServiceHelper<TcInMemConverter>,
|
||||
pub struct PusService17TestHandler<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
> {
|
||||
pub service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> PusService17TestHandler<TcInMemConverter> {
|
||||
pub fn new(service_helper: PusServiceHelper<TcInMemConverter>) -> Self {
|
||||
impl<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
> PusService17TestHandler<TcInMemConverter, VerificationReporter>
|
||||
{
|
||||
pub fn new(service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>) -> Self {
|
||||
Self { service_helper }
|
||||
}
|
||||
|
||||
@ -33,13 +41,15 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService17TestHandler<TcInMemConv
|
||||
}
|
||||
if tc.subservice() == 1 {
|
||||
let mut partial_error = None;
|
||||
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
|
||||
let time_stamp =
|
||||
PusServiceBase::<VerificationReporter>::get_current_cds_short_timestamp(
|
||||
&mut partial_error,
|
||||
);
|
||||
let result = self
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_success(ecss_tc_and_token.token, Some(&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)
|
||||
@ -67,8 +77,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService17TestHandler<TcInMemConv
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.completion_success(start_token, Some(&time_stamp))
|
||||
.completion_success(start_token, &time_stamp)
|
||||
.is_err()
|
||||
{
|
||||
partial_error = Some(PartialPusHandlingError::Verification)
|
||||
@ -95,7 +104,7 @@ mod tests {
|
||||
PusServiceHandlerWithSharedStoreCommon, PusServiceHandlerWithVecCommon, PusTestHarness,
|
||||
SimplePusPacketHandler, TEST_APID,
|
||||
};
|
||||
use crate::pus::verification::RequestId;
|
||||
use crate::pus::verification::{RequestId, VerificationReporterWithSender};
|
||||
use crate::pus::verification::{TcStateAccepted, VerificationToken};
|
||||
use crate::pus::{
|
||||
EcssTcInSharedStoreConverter, EcssTcInVecConverter, PusPacketHandlerResult,
|
||||
@ -111,7 +120,8 @@ mod tests {
|
||||
|
||||
struct Pus17HandlerWithStoreTester {
|
||||
common: PusServiceHandlerWithSharedStoreCommon,
|
||||
handler: PusService17TestHandler<EcssTcInSharedStoreConverter>,
|
||||
handler:
|
||||
PusService17TestHandler<EcssTcInSharedStoreConverter, VerificationReporterWithSender>,
|
||||
}
|
||||
|
||||
impl Pus17HandlerWithStoreTester {
|
||||
@ -148,13 +158,14 @@ mod tests {
|
||||
}
|
||||
|
||||
struct Pus17HandlerWithVecTester {
|
||||
common: PusServiceHandlerWithVecCommon,
|
||||
handler: PusService17TestHandler<EcssTcInVecConverter>,
|
||||
common: PusServiceHandlerWithVecCommon<VerificationReporterWithSender>,
|
||||
handler: PusService17TestHandler<EcssTcInVecConverter, VerificationReporterWithSender>,
|
||||
}
|
||||
|
||||
impl Pus17HandlerWithVecTester {
|
||||
pub fn new() -> Self {
|
||||
let (common, srv_handler) = PusServiceHandlerWithVecCommon::new();
|
||||
let (common, srv_handler) =
|
||||
PusServiceHandlerWithVecCommon::new_with_standard_verif_reporter();
|
||||
Self {
|
||||
common,
|
||||
handler: PusService17TestHandler::new(srv_handler),
|
||||
|
@ -16,7 +16,9 @@
|
||||
//! use std::sync::{Arc, mpsc, RwLock};
|
||||
//! use std::time::Duration;
|
||||
//! use satrs::pool::{PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
|
||||
//! use satrs::pus::verification::{VerificationReporterCfg, VerificationReporterWithSender};
|
||||
//! use satrs::pus::verification::{
|
||||
//! VerificationReportingProvider, VerificationReporterCfg, VerificationReporterWithSender
|
||||
//! };
|
||||
//! use satrs::seq_count::SeqCountProviderSimple;
|
||||
//! use satrs::pus::MpscTmInSharedPoolSender;
|
||||
//! use satrs::tmtc::tm_helper::SharedTmPool;
|
||||
@ -43,9 +45,9 @@
|
||||
//! let init_token = reporter.add_tc(&pus_tc_0);
|
||||
//!
|
||||
//! // Complete success sequence for a telecommand
|
||||
//! let accepted_token = reporter.acceptance_success(init_token, Some(&EMPTY_STAMP)).unwrap();
|
||||
//! let started_token = reporter.start_success(accepted_token, Some(&EMPTY_STAMP)).unwrap();
|
||||
//! reporter.completion_success(started_token, Some(&EMPTY_STAMP)).unwrap();
|
||||
//! let accepted_token = reporter.acceptance_success(init_token, &EMPTY_STAMP).unwrap();
|
||||
//! let started_token = reporter.start_success(accepted_token, &EMPTY_STAMP).unwrap();
|
||||
//! reporter.completion_success(started_token, &EMPTY_STAMP).unwrap();
|
||||
//!
|
||||
//! // Verify it arrives correctly on receiver end
|
||||
//! let mut tm_buf: [u8; 1024] = [0; 1024];
|
||||
@ -279,16 +281,16 @@ impl<STATE> VerificationToken<STATE> {
|
||||
|
||||
/// Composite helper struct to pass failure parameters to the [VerificationReporter]
|
||||
pub struct FailParams<'stamp, 'fargs> {
|
||||
time_stamp: Option<&'stamp [u8]>,
|
||||
time_stamp: &'stamp [u8],
|
||||
failure_code: &'fargs dyn EcssEnumeration,
|
||||
failure_data: Option<&'fargs [u8]>,
|
||||
failure_data: &'fargs [u8],
|
||||
}
|
||||
|
||||
impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> {
|
||||
pub fn new(
|
||||
time_stamp: Option<&'stamp [u8]>,
|
||||
time_stamp: &'stamp [u8],
|
||||
failure_code: &'fargs impl EcssEnumeration,
|
||||
failure_data: Option<&'fargs [u8]>,
|
||||
failure_data: &'fargs [u8],
|
||||
) -> Self {
|
||||
Self {
|
||||
time_stamp,
|
||||
@ -296,6 +298,13 @@ impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> {
|
||||
failure_data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_no_fail_data(
|
||||
time_stamp: &'stamp [u8],
|
||||
failure_code: &'fargs impl EcssEnumeration,
|
||||
) -> Self {
|
||||
Self::new(time_stamp, failure_code, &[])
|
||||
}
|
||||
}
|
||||
|
||||
/// Composite helper struct to pass step failure parameters to the [VerificationReporter]
|
||||
@ -306,10 +315,10 @@ pub struct FailParamsWithStep<'stamp, 'fargs> {
|
||||
|
||||
impl<'stamp, 'fargs> FailParamsWithStep<'stamp, 'fargs> {
|
||||
pub fn new(
|
||||
time_stamp: Option<&'stamp [u8]>,
|
||||
time_stamp: &'stamp [u8],
|
||||
step: &'fargs impl EcssEnumeration,
|
||||
failure_code: &'fargs impl EcssEnumeration,
|
||||
failure_data: Option<&'fargs [u8]>,
|
||||
failure_data: &'fargs [u8],
|
||||
) -> Self {
|
||||
Self {
|
||||
bp: FailParams::new(time_stamp, failure_code, failure_data),
|
||||
@ -399,6 +408,66 @@ impl<'src_data, TcState: WasAtLeastAccepted + Copy>
|
||||
pub fn send_success_step_or_completion_success(self) {}
|
||||
}
|
||||
|
||||
pub trait VerificationReportingProvider {
|
||||
fn add_tc(
|
||||
&mut self,
|
||||
pus_tc: &(impl CcsdsPacket + IsPusTelecommand),
|
||||
) -> VerificationToken<TcStateNone> {
|
||||
self.add_tc_with_req_id(RequestId::new(pus_tc))
|
||||
}
|
||||
|
||||
fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<TcStateNone>;
|
||||
|
||||
fn acceptance_success(
|
||||
&self,
|
||||
token: VerificationToken<TcStateNone>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<VerificationToken<TcStateAccepted>, VerificationOrSendErrorWithToken<TcStateNone>>;
|
||||
|
||||
fn acceptance_failure(
|
||||
&self,
|
||||
token: VerificationToken<TcStateNone>,
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationOrSendErrorWithToken<TcStateNone>>;
|
||||
|
||||
fn start_success(
|
||||
&self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<VerificationToken<TcStateStarted>, VerificationOrSendErrorWithToken<TcStateAccepted>>;
|
||||
|
||||
fn start_failure(
|
||||
&self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationOrSendErrorWithToken<TcStateAccepted>>;
|
||||
|
||||
fn step_success(
|
||||
&self,
|
||||
token: &VerificationToken<TcStateStarted>,
|
||||
time_stamp: &[u8],
|
||||
step: impl EcssEnumeration,
|
||||
) -> Result<(), EcssTmtcError>;
|
||||
|
||||
fn step_failure(
|
||||
&self,
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
params: FailParamsWithStep,
|
||||
) -> Result<(), VerificationOrSendErrorWithToken<TcStateStarted>>;
|
||||
|
||||
fn completion_success<TcState: WasAtLeastAccepted + Copy>(
|
||||
&self,
|
||||
token: VerificationToken<TcState>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<(), VerificationOrSendErrorWithToken<TcState>>;
|
||||
|
||||
fn completion_failure<TcState: WasAtLeastAccepted + Copy>(
|
||||
&self,
|
||||
token: VerificationToken<TcState>,
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationOrSendErrorWithToken<TcState>>;
|
||||
}
|
||||
|
||||
/// Primary verification handler. It provides an API to send PUS 1 verification telemetry packets
|
||||
/// and verify the various steps of telecommand handling as specified in the PUS standard.
|
||||
///
|
||||
@ -456,7 +525,7 @@ impl VerificationReporterCore {
|
||||
token: VerificationToken<State>,
|
||||
seq_count: u16,
|
||||
msg_count: u16,
|
||||
time_stamp: Option<&'src_data [u8]>,
|
||||
time_stamp: &'src_data [u8],
|
||||
) -> Result<
|
||||
VerificationSendable<'src_data, State, VerifSuccess>,
|
||||
VerificationErrorWithToken<State>,
|
||||
@ -513,7 +582,7 @@ impl VerificationReporterCore {
|
||||
token: VerificationToken<TcStateNone>,
|
||||
seq_count: u16,
|
||||
msg_count: u16,
|
||||
time_stamp: Option<&'src_data [u8]>,
|
||||
time_stamp: &'src_data [u8],
|
||||
) -> Result<
|
||||
VerificationSendable<'src_data, TcStateNone, VerifSuccess>,
|
||||
VerificationErrorWithToken<TcStateNone>,
|
||||
@ -584,7 +653,7 @@ impl VerificationReporterCore {
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
seq_count: u16,
|
||||
msg_count: u16,
|
||||
time_stamp: Option<&'src_data [u8]>,
|
||||
time_stamp: &'src_data [u8],
|
||||
) -> Result<
|
||||
VerificationSendable<'src_data, TcStateAccepted, VerifSuccess>,
|
||||
VerificationErrorWithToken<TcStateAccepted>,
|
||||
@ -658,7 +727,7 @@ impl VerificationReporterCore {
|
||||
token: &VerificationToken<TcStateStarted>,
|
||||
seq_count: u16,
|
||||
msg_count: u16,
|
||||
time_stamp: Option<&'src_data [u8]>,
|
||||
time_stamp: &'src_data [u8],
|
||||
step: impl EcssEnumeration,
|
||||
) -> Result<VerificationSendable<'src_data, TcStateStarted, VerifSuccess>, EcssTmtcError> {
|
||||
Ok(VerificationSendable::new_no_token(
|
||||
@ -714,7 +783,7 @@ impl VerificationReporterCore {
|
||||
token: VerificationToken<TcState>,
|
||||
seq_counter: u16,
|
||||
msg_counter: u16,
|
||||
time_stamp: Option<&'src_data [u8]>,
|
||||
time_stamp: &'src_data [u8],
|
||||
) -> Result<
|
||||
VerificationSendable<'src_data, TcState, VerifSuccess>,
|
||||
VerificationErrorWithToken<TcState>,
|
||||
@ -788,7 +857,7 @@ impl VerificationReporterCore {
|
||||
seq_count: u16,
|
||||
msg_counter: u16,
|
||||
req_id: &RequestId,
|
||||
time_stamp: Option<&'src_data [u8]>,
|
||||
time_stamp: &'src_data [u8],
|
||||
step: Option<&(impl EcssEnumeration + ?Sized)>,
|
||||
) -> Result<PusTmCreator<'src_data>, EcssTmtcError> {
|
||||
let mut source_data_len = size_of::<u32>();
|
||||
@ -832,9 +901,7 @@ impl VerificationReporterCore {
|
||||
if let Some(step) = step {
|
||||
source_data_len += step.size();
|
||||
}
|
||||
if let Some(failure_data) = params.failure_data {
|
||||
source_data_len += failure_data.len();
|
||||
}
|
||||
source_data_len += params.failure_data.len();
|
||||
source_buffer_large_enough(src_data_buf.len(), source_data_len)?;
|
||||
req_id.to_bytes(&mut src_data_buf[0..RequestId::SIZE_AS_BYTES]);
|
||||
idx += RequestId::SIZE_AS_BYTES;
|
||||
@ -849,9 +916,7 @@ impl VerificationReporterCore {
|
||||
.write_to_be_bytes(&mut src_data_buf[idx..idx + params.failure_code.size()])
|
||||
.map_err(PusError::ByteConversion)?;
|
||||
idx += params.failure_code.size();
|
||||
if let Some(failure_data) = params.failure_data {
|
||||
src_data_buf[idx..idx + failure_data.len()].copy_from_slice(failure_data);
|
||||
}
|
||||
src_data_buf[idx..idx + params.failure_data.len()].copy_from_slice(params.failure_data);
|
||||
let mut sp_header = SpHeader::tm_unseg(self.apid(), seq_count, 0).unwrap();
|
||||
Ok(self.create_pus_verif_tm_base(
|
||||
src_data_buf,
|
||||
@ -869,11 +934,11 @@ impl VerificationReporterCore {
|
||||
subservice: u8,
|
||||
msg_counter: u16,
|
||||
sp_header: &mut SpHeader,
|
||||
time_stamp: Option<&'src_data [u8]>,
|
||||
time_stamp: &'src_data [u8],
|
||||
source_data_len: usize,
|
||||
) -> PusTmCreator<'src_data> {
|
||||
let tm_sec_header =
|
||||
PusTmSecondaryHeader::new(1, subservice, msg_counter, self.dest_id, time_stamp);
|
||||
PusTmSecondaryHeader::new(1, subservice, msg_counter, self.dest_id, Some(time_stamp));
|
||||
PusTmCreator::new(
|
||||
sp_header,
|
||||
tm_sec_header,
|
||||
@ -970,7 +1035,7 @@ mod alloc_mod {
|
||||
&self,
|
||||
token: VerificationToken<TcStateNone>,
|
||||
sender: &(impl EcssTmSenderCore + ?Sized),
|
||||
time_stamp: Option<&[u8]>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<VerificationToken<TcStateAccepted>, VerificationOrSendErrorWithToken<TcStateNone>>
|
||||
{
|
||||
let seq_count = self
|
||||
@ -1025,7 +1090,7 @@ mod alloc_mod {
|
||||
&self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
sender: &(impl EcssTmSenderCore + ?Sized),
|
||||
time_stamp: Option<&[u8]>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<
|
||||
VerificationToken<TcStateStarted>,
|
||||
VerificationOrSendErrorWithToken<TcStateAccepted>,
|
||||
@ -1085,7 +1150,7 @@ mod alloc_mod {
|
||||
&self,
|
||||
token: &VerificationToken<TcStateStarted>,
|
||||
sender: &(impl EcssTmSenderCore + ?Sized),
|
||||
time_stamp: Option<&[u8]>,
|
||||
time_stamp: &[u8],
|
||||
step: impl EcssEnumeration,
|
||||
) -> Result<(), EcssTmtcError> {
|
||||
let seq_count = self
|
||||
@ -1148,7 +1213,7 @@ mod alloc_mod {
|
||||
&self,
|
||||
token: VerificationToken<TcState>,
|
||||
sender: &(impl EcssTmSenderCore + ?Sized),
|
||||
time_stamp: Option<&[u8]>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<(), VerificationOrSendErrorWithToken<TcState>> {
|
||||
let seq_count = self
|
||||
.seq_count_provider
|
||||
@ -1226,24 +1291,34 @@ mod alloc_mod {
|
||||
to self.reporter {
|
||||
pub fn set_apid(&mut self, apid: u16) -> bool;
|
||||
pub fn apid(&self) -> u16;
|
||||
pub fn add_tc(&mut self, pus_tc: &(impl CcsdsPacket + IsPusTelecommand)) -> VerificationToken<TcStateNone>;
|
||||
pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<TcStateNone>;
|
||||
pub fn dest_id(&self) -> u16;
|
||||
pub fn set_dest_id(&mut self, dest_id: u16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acceptance_success(
|
||||
impl VerificationReportingProvider for VerificationReporterWithSender {
|
||||
delegate! {
|
||||
to self.reporter {
|
||||
fn add_tc(
|
||||
&mut self,
|
||||
pus_tc: &(impl CcsdsPacket + IsPusTelecommand),
|
||||
) -> VerificationToken<TcStateNone>;
|
||||
fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<TcStateNone>;
|
||||
}
|
||||
}
|
||||
|
||||
fn acceptance_success(
|
||||
&self,
|
||||
token: VerificationToken<TcStateNone>,
|
||||
time_stamp: Option<&[u8]>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<VerificationToken<TcStateAccepted>, VerificationOrSendErrorWithToken<TcStateNone>>
|
||||
{
|
||||
self.reporter
|
||||
.acceptance_success(token, self.sender.as_ref(), time_stamp)
|
||||
}
|
||||
|
||||
pub fn acceptance_failure(
|
||||
fn acceptance_failure(
|
||||
&self,
|
||||
token: VerificationToken<TcStateNone>,
|
||||
params: FailParams,
|
||||
@ -1252,10 +1327,10 @@ mod alloc_mod {
|
||||
.acceptance_failure(token, self.sender.as_ref(), params)
|
||||
}
|
||||
|
||||
pub fn start_success(
|
||||
fn start_success(
|
||||
&self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
time_stamp: Option<&[u8]>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<
|
||||
VerificationToken<TcStateStarted>,
|
||||
VerificationOrSendErrorWithToken<TcStateAccepted>,
|
||||
@ -1264,7 +1339,7 @@ mod alloc_mod {
|
||||
.start_success(token, self.sender.as_ref(), time_stamp)
|
||||
}
|
||||
|
||||
pub fn start_failure(
|
||||
fn start_failure(
|
||||
&self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
params: FailParams,
|
||||
@ -1273,17 +1348,17 @@ mod alloc_mod {
|
||||
.start_failure(token, self.sender.as_ref(), params)
|
||||
}
|
||||
|
||||
pub fn step_success(
|
||||
fn step_success(
|
||||
&self,
|
||||
token: &VerificationToken<TcStateStarted>,
|
||||
time_stamp: Option<&[u8]>,
|
||||
time_stamp: &[u8],
|
||||
step: impl EcssEnumeration,
|
||||
) -> Result<(), EcssTmtcError> {
|
||||
self.reporter
|
||||
.step_success(token, self.sender.as_ref(), time_stamp, step)
|
||||
}
|
||||
|
||||
pub fn step_failure(
|
||||
fn step_failure(
|
||||
&self,
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
params: FailParamsWithStep,
|
||||
@ -1292,16 +1367,16 @@ mod alloc_mod {
|
||||
.step_failure(token, self.sender.as_ref(), params)
|
||||
}
|
||||
|
||||
pub fn completion_success<TcState: WasAtLeastAccepted + Copy>(
|
||||
fn completion_success<TcState: WasAtLeastAccepted + Copy>(
|
||||
&self,
|
||||
token: VerificationToken<TcState>,
|
||||
time_stamp: Option<&[u8]>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<(), VerificationOrSendErrorWithToken<TcState>> {
|
||||
self.reporter
|
||||
.completion_success(token, self.sender.as_ref(), time_stamp)
|
||||
}
|
||||
|
||||
pub fn completion_failure<TcState: WasAtLeastAccepted + Copy>(
|
||||
fn completion_failure<TcState: WasAtLeastAccepted + Copy>(
|
||||
&self,
|
||||
token: VerificationToken<TcState>,
|
||||
params: FailParams,
|
||||
@ -1322,7 +1397,7 @@ mod std_mod {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub mod tests {
|
||||
use crate::pool::{PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
|
||||
use crate::pus::tests::CommonTmInfo;
|
||||
use crate::pus::verification::{
|
||||
@ -1335,6 +1410,8 @@ mod tests {
|
||||
use crate::ChannelId;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::format;
|
||||
use alloc::sync::Arc;
|
||||
use hashbrown::HashMap;
|
||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
|
||||
use spacepackets::ecss::tm::PusTmReader;
|
||||
use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, PusError, PusPacket};
|
||||
@ -1342,15 +1419,208 @@ mod tests {
|
||||
use spacepackets::{ByteConversionError, CcsdsPacket, SpHeader};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::{mpsc, Mutex};
|
||||
use std::time::Duration;
|
||||
use std::vec;
|
||||
use std::vec::Vec;
|
||||
|
||||
use super::VerificationReportingProvider;
|
||||
|
||||
fn is_send<T: Send>(_: &T) {}
|
||||
#[allow(dead_code)]
|
||||
fn is_sync<T: Sync>(_: &T) {}
|
||||
|
||||
pub struct VerificationStatus {
|
||||
pub accepted: Option<bool>,
|
||||
pub started: Option<bool>,
|
||||
pub step: u64,
|
||||
pub step_status: Option<bool>,
|
||||
pub completed: Option<bool>,
|
||||
pub failure_data: Option<Vec<u8>>,
|
||||
pub fail_enum: Option<u64>,
|
||||
}
|
||||
|
||||
pub type SharedVerificationMap = Arc<Mutex<RefCell<HashMap<RequestId, VerificationStatus>>>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestVerificationReporter {
|
||||
pub verification_map: SharedVerificationMap,
|
||||
}
|
||||
|
||||
impl TestVerificationReporter {
|
||||
pub fn new(verification_map: SharedVerificationMap) -> Self {
|
||||
Self { verification_map }
|
||||
}
|
||||
}
|
||||
|
||||
impl VerificationReportingProvider for TestVerificationReporter {
|
||||
fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<TcStateNone> {
|
||||
let verif_map = self.verification_map.lock().unwrap();
|
||||
verif_map.borrow_mut().insert(
|
||||
req_id,
|
||||
VerificationStatus {
|
||||
accepted: None,
|
||||
started: None,
|
||||
step: 0,
|
||||
step_status: None,
|
||||
completed: None,
|
||||
failure_data: None,
|
||||
fail_enum: None,
|
||||
},
|
||||
);
|
||||
VerificationToken {
|
||||
state: core::marker::PhantomData,
|
||||
req_id,
|
||||
}
|
||||
}
|
||||
|
||||
fn acceptance_success(
|
||||
&self,
|
||||
token: VerificationToken<TcStateNone>,
|
||||
_time_stamp: &[u8],
|
||||
) -> Result<
|
||||
VerificationToken<super::TcStateAccepted>,
|
||||
super::VerificationOrSendErrorWithToken<TcStateNone>,
|
||||
> {
|
||||
let verif_map = self.verification_map.lock().unwrap();
|
||||
match verif_map.borrow_mut().get_mut(&token.req_id) {
|
||||
Some(entry) => entry.accepted = Some(true),
|
||||
None => panic!(
|
||||
"unexpected acceptance success for request ID {}",
|
||||
token.req_id()
|
||||
),
|
||||
};
|
||||
Ok(VerificationToken {
|
||||
state: core::marker::PhantomData,
|
||||
req_id: token.req_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn acceptance_failure(
|
||||
&self,
|
||||
token: VerificationToken<TcStateNone>,
|
||||
params: FailParams,
|
||||
) -> Result<(), super::VerificationOrSendErrorWithToken<TcStateNone>> {
|
||||
let verif_map = self.verification_map.lock().unwrap();
|
||||
match verif_map.borrow_mut().get_mut(&token.req_id) {
|
||||
Some(entry) => {
|
||||
entry.accepted = Some(false);
|
||||
entry.failure_data = Some(params.failure_data.to_vec());
|
||||
entry.fail_enum = Some(params.failure_code.value());
|
||||
}
|
||||
None => panic!(
|
||||
"unexpected acceptance failure for request ID {}",
|
||||
token.req_id()
|
||||
),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_success(
|
||||
&self,
|
||||
token: VerificationToken<super::TcStateAccepted>,
|
||||
_time_stamp: &[u8],
|
||||
) -> Result<
|
||||
VerificationToken<super::TcStateStarted>,
|
||||
super::VerificationOrSendErrorWithToken<super::TcStateAccepted>,
|
||||
> {
|
||||
let verif_map = self.verification_map.lock().unwrap();
|
||||
match verif_map.borrow_mut().get_mut(&token.req_id) {
|
||||
Some(entry) => entry.started = Some(true),
|
||||
None => panic!("unexpected start success for request ID {}", token.req_id()),
|
||||
};
|
||||
Ok(VerificationToken {
|
||||
state: core::marker::PhantomData,
|
||||
req_id: token.req_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn start_failure(
|
||||
&self,
|
||||
token: VerificationToken<super::TcStateAccepted>,
|
||||
params: FailParams,
|
||||
) -> Result<(), super::VerificationOrSendErrorWithToken<super::TcStateAccepted>> {
|
||||
let verif_map = self.verification_map.lock().unwrap();
|
||||
match verif_map.borrow_mut().get_mut(&token.req_id) {
|
||||
Some(entry) => {
|
||||
entry.started = Some(false);
|
||||
entry.failure_data = Some(params.failure_data.to_vec());
|
||||
entry.fail_enum = Some(params.failure_code.value());
|
||||
}
|
||||
None => panic!("unexpected start failure for request ID {}", token.req_id()),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn step_success(
|
||||
&self,
|
||||
token: &VerificationToken<super::TcStateStarted>,
|
||||
_time_stamp: &[u8],
|
||||
step: impl spacepackets::ecss::EcssEnumeration,
|
||||
) -> Result<(), EcssTmtcError> {
|
||||
let verif_map = self.verification_map.lock().unwrap();
|
||||
match verif_map.borrow_mut().get_mut(&token.req_id) {
|
||||
Some(entry) => {
|
||||
entry.step = step.value();
|
||||
entry.step_status = Some(true);
|
||||
}
|
||||
None => panic!("unexpected start success for request ID {}", token.req_id()),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn step_failure(
|
||||
&self,
|
||||
token: VerificationToken<super::TcStateStarted>,
|
||||
_params: FailParamsWithStep,
|
||||
) -> Result<(), super::VerificationOrSendErrorWithToken<super::TcStateStarted>> {
|
||||
let verif_map = self.verification_map.lock().unwrap();
|
||||
match verif_map.borrow_mut().get_mut(&token.req_id) {
|
||||
Some(entry) => {
|
||||
entry.step_status = Some(false);
|
||||
}
|
||||
None => panic!("unexpected start success for request ID {}", token.req_id()),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn completion_success<TcState: super::WasAtLeastAccepted + Copy>(
|
||||
&self,
|
||||
token: VerificationToken<TcState>,
|
||||
_time_stamp: &[u8],
|
||||
) -> Result<(), super::VerificationOrSendErrorWithToken<TcState>> {
|
||||
let verif_map = self.verification_map.lock().unwrap();
|
||||
match verif_map.borrow_mut().get_mut(&token.req_id) {
|
||||
Some(entry) => entry.completed = Some(true),
|
||||
None => panic!(
|
||||
"unexpected acceptance success for request ID {}",
|
||||
token.req_id()
|
||||
),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn completion_failure<TcState: super::WasAtLeastAccepted + Copy>(
|
||||
&self,
|
||||
token: VerificationToken<TcState>,
|
||||
params: FailParams,
|
||||
) -> Result<(), super::VerificationOrSendErrorWithToken<TcState>> {
|
||||
let verif_map = self.verification_map.lock().unwrap();
|
||||
match verif_map.borrow_mut().get_mut(&token.req_id) {
|
||||
Some(entry) => {
|
||||
entry.completed = Some(false);
|
||||
entry.failure_data = Some(params.failure_data.to_vec());
|
||||
entry.fail_enum = Some(params.failure_code.value());
|
||||
}
|
||||
None => panic!(
|
||||
"unexpected acceptance success for request ID {}",
|
||||
token.req_id()
|
||||
),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const TEST_APID: u16 = 0x02;
|
||||
const EMPTY_STAMP: [u8; 7] = [0; 7];
|
||||
|
||||
@ -1504,7 +1774,7 @@ mod tests {
|
||||
fn test_basic_acceptance_success() {
|
||||
let (b, tok) = base_init(false);
|
||||
let mut sender = TestSender::default();
|
||||
b.vr.acceptance_success(tok, &sender, Some(&EMPTY_STAMP))
|
||||
b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
acceptance_check(&mut sender, &tok.req_id);
|
||||
}
|
||||
@ -1513,7 +1783,7 @@ mod tests {
|
||||
fn test_basic_acceptance_success_with_helper() {
|
||||
let (mut b, tok) = base_with_helper_init();
|
||||
b.helper
|
||||
.acceptance_success(tok, Some(&EMPTY_STAMP))
|
||||
.acceptance_success(tok, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
|
||||
acceptance_check(sender, &tok.req_id);
|
||||
@ -1544,7 +1814,7 @@ mod tests {
|
||||
let stamp_buf = [1, 2, 3, 4, 5, 6, 7];
|
||||
let mut sender = TestSender::default();
|
||||
let fail_code = EcssEnumU16::new(2);
|
||||
let fail_params = FailParams::new(Some(stamp_buf.as_slice()), &fail_code, None);
|
||||
let fail_params = FailParams::new_no_fail_data(stamp_buf.as_slice(), &fail_code);
|
||||
b.vr.acceptance_failure(tok, &sender, fail_params)
|
||||
.expect("Sending acceptance success failed");
|
||||
acceptance_fail_check(&mut sender, tok.req_id, stamp_buf);
|
||||
@ -1556,7 +1826,7 @@ mod tests {
|
||||
b.rep().reporter.dest_id = 5;
|
||||
let stamp_buf = [1, 2, 3, 4, 5, 6, 7];
|
||||
let fail_code = EcssEnumU16::new(2);
|
||||
let fail_params = FailParams::new(Some(stamp_buf.as_slice()), &fail_code, None);
|
||||
let fail_params = FailParams::new_no_fail_data(stamp_buf.as_slice(), &fail_code);
|
||||
b.helper
|
||||
.acceptance_failure(tok, fail_params)
|
||||
.expect("Sending acceptance success failed");
|
||||
@ -1573,11 +1843,7 @@ mod tests {
|
||||
let fail_data: [u8; 16] = [0; 16];
|
||||
// 4 req ID + 1 byte step + 2 byte error code + 8 byte fail data
|
||||
assert_eq!(b.rep().allowed_source_data_len(), 15);
|
||||
let fail_params = FailParams::new(
|
||||
Some(stamp_buf.as_slice()),
|
||||
&fail_code,
|
||||
Some(fail_data.as_slice()),
|
||||
);
|
||||
let fail_params = FailParams::new(stamp_buf.as_slice(), &fail_code, fail_data.as_slice());
|
||||
let res = b.helper.acceptance_failure(tok, fail_params);
|
||||
assert!(res.is_err());
|
||||
let err_with_token = res.unwrap_err();
|
||||
@ -1609,11 +1875,7 @@ mod tests {
|
||||
let fail_data = EcssEnumU32::new(12);
|
||||
let mut fail_data_raw = [0; 4];
|
||||
fail_data.write_to_be_bytes(&mut fail_data_raw).unwrap();
|
||||
let fail_params = FailParams::new(
|
||||
Some(&EMPTY_STAMP),
|
||||
&fail_code,
|
||||
Some(fail_data_raw.as_slice()),
|
||||
);
|
||||
let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, fail_data_raw.as_slice());
|
||||
b.vr.acceptance_failure(tok, &sender, fail_params)
|
||||
.expect("Sending acceptance success failed");
|
||||
let cmp_info = TmInfo {
|
||||
@ -1673,16 +1935,12 @@ mod tests {
|
||||
let fail_data: i32 = -12;
|
||||
let mut fail_data_raw = [0; 4];
|
||||
fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice());
|
||||
let fail_params = FailParams::new(
|
||||
Some(&EMPTY_STAMP),
|
||||
&fail_code,
|
||||
Some(fail_data_raw.as_slice()),
|
||||
);
|
||||
let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, fail_data_raw.as_slice());
|
||||
|
||||
let accepted_token =
|
||||
b.vr.acceptance_success(tok, &sender, Some(&EMPTY_STAMP))
|
||||
b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
b.vr.start_failure(accepted_token, &mut sender, fail_params)
|
||||
b.vr.start_failure(accepted_token, &sender, fail_params)
|
||||
.expect("Start failure failure");
|
||||
start_fail_check(&mut sender, tok.req_id, fail_data_raw);
|
||||
}
|
||||
@ -1694,21 +1952,15 @@ mod tests {
|
||||
let fail_data: i32 = -12;
|
||||
let mut fail_data_raw = [0; 4];
|
||||
fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice());
|
||||
let fail_params = FailParams::new(
|
||||
Some(&EMPTY_STAMP),
|
||||
&fail_code,
|
||||
Some(fail_data_raw.as_slice()),
|
||||
);
|
||||
let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, fail_data_raw.as_slice());
|
||||
|
||||
let accepted_token = b
|
||||
.helper
|
||||
.acceptance_success(tok, Some(&EMPTY_STAMP))
|
||||
.acceptance_success(tok, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
let empty = b
|
||||
.helper
|
||||
b.helper
|
||||
.start_failure(accepted_token, fail_params)
|
||||
.expect("Start failure failure");
|
||||
assert_eq!(empty, ());
|
||||
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
|
||||
start_fail_check(sender, tok.req_id, fail_data_raw);
|
||||
}
|
||||
@ -1775,26 +2027,16 @@ mod tests {
|
||||
let mut sender = TestSender::default();
|
||||
let accepted_token = b
|
||||
.rep()
|
||||
.acceptance_success(tok, &sender, Some(&EMPTY_STAMP))
|
||||
.acceptance_success(tok, &sender, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
let started_token = b
|
||||
.rep()
|
||||
.start_success(accepted_token, &sender, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0])
|
||||
.expect("Sending start success failed");
|
||||
b.rep()
|
||||
.step_success(
|
||||
&started_token,
|
||||
&sender,
|
||||
Some(&EMPTY_STAMP),
|
||||
EcssEnumU8::new(0),
|
||||
)
|
||||
.step_success(&started_token, &sender, &EMPTY_STAMP, EcssEnumU8::new(0))
|
||||
.expect("Sending step 0 success failed");
|
||||
b.vr.step_success(
|
||||
&started_token,
|
||||
&sender,
|
||||
Some(&EMPTY_STAMP),
|
||||
EcssEnumU8::new(1),
|
||||
)
|
||||
b.vr.step_success(&started_token, &sender, &EMPTY_STAMP, EcssEnumU8::new(1))
|
||||
.expect("Sending step 1 success failed");
|
||||
assert_eq!(sender.service_queue.borrow().len(), 4);
|
||||
step_success_check(&mut sender, tok.req_id);
|
||||
@ -1805,17 +2047,17 @@ mod tests {
|
||||
let (mut b, tok) = base_with_helper_init();
|
||||
let accepted_token = b
|
||||
.helper
|
||||
.acceptance_success(tok, Some(&EMPTY_STAMP))
|
||||
.acceptance_success(tok, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
let started_token = b
|
||||
.helper
|
||||
.start_success(accepted_token, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0])
|
||||
.expect("Sending start success failed");
|
||||
b.helper
|
||||
.step_success(&started_token, Some(&EMPTY_STAMP), EcssEnumU8::new(0))
|
||||
.step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0))
|
||||
.expect("Sending step 0 success failed");
|
||||
b.helper
|
||||
.step_success(&started_token, Some(&EMPTY_STAMP), EcssEnumU8::new(1))
|
||||
.step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(1))
|
||||
.expect("Sending step 1 success failed");
|
||||
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
|
||||
assert_eq!(sender.service_queue.borrow().len(), 4);
|
||||
@ -1900,31 +2142,22 @@ mod tests {
|
||||
fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice());
|
||||
let fail_step = EcssEnumU8::new(1);
|
||||
let fail_params = FailParamsWithStep::new(
|
||||
Some(&EMPTY_STAMP),
|
||||
&EMPTY_STAMP,
|
||||
&fail_step,
|
||||
&fail_code,
|
||||
Some(fail_data_raw.as_slice()),
|
||||
fail_data_raw.as_slice(),
|
||||
);
|
||||
|
||||
let accepted_token =
|
||||
b.vr.acceptance_success(tok, &mut sender, Some(&EMPTY_STAMP))
|
||||
b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
let started_token =
|
||||
b.vr.start_success(accepted_token, &mut sender, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
b.vr.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0])
|
||||
.expect("Sending start success failed");
|
||||
let mut empty =
|
||||
b.vr.step_success(
|
||||
&started_token,
|
||||
&mut sender,
|
||||
Some(&EMPTY_STAMP),
|
||||
EcssEnumU8::new(0),
|
||||
)
|
||||
b.vr.step_success(&started_token, &sender, &EMPTY_STAMP, EcssEnumU8::new(0))
|
||||
.expect("Sending completion success failed");
|
||||
assert_eq!(empty, ());
|
||||
empty =
|
||||
b.vr.step_failure(started_token, &mut sender, fail_params)
|
||||
b.vr.step_failure(started_token, &sender, fail_params)
|
||||
.expect("Step failure failed");
|
||||
assert_eq!(empty, ());
|
||||
check_step_failure(&mut sender, req_id, fail_data_raw);
|
||||
}
|
||||
|
||||
@ -1938,30 +2171,26 @@ mod tests {
|
||||
fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice());
|
||||
let fail_step = EcssEnumU8::new(1);
|
||||
let fail_params = FailParamsWithStep::new(
|
||||
Some(&EMPTY_STAMP),
|
||||
&EMPTY_STAMP,
|
||||
&fail_step,
|
||||
&fail_code,
|
||||
Some(fail_data_raw.as_slice()),
|
||||
fail_data_raw.as_slice(),
|
||||
);
|
||||
|
||||
let accepted_token = b
|
||||
.helper
|
||||
.acceptance_success(tok, Some(&EMPTY_STAMP))
|
||||
.acceptance_success(tok, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
let started_token = b
|
||||
.helper
|
||||
.start_success(accepted_token, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0])
|
||||
.expect("Sending start success failed");
|
||||
let mut empty = b
|
||||
.helper
|
||||
.step_success(&started_token, Some(&EMPTY_STAMP), EcssEnumU8::new(0))
|
||||
b.helper
|
||||
.step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0))
|
||||
.expect("Sending completion success failed");
|
||||
assert_eq!(empty, ());
|
||||
empty = b
|
||||
.helper
|
||||
b.helper
|
||||
.step_failure(started_token, fail_params)
|
||||
.expect("Step failure failed");
|
||||
assert_eq!(empty, ());
|
||||
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
|
||||
check_step_failure(sender, req_id, fail_data_raw);
|
||||
}
|
||||
@ -2018,18 +2247,16 @@ mod tests {
|
||||
let mut sender = TestSender::default();
|
||||
let req_id = tok.req_id;
|
||||
let fail_code = EcssEnumU32::new(0x1020);
|
||||
let fail_params = FailParams::new(Some(&EMPTY_STAMP), &fail_code, None);
|
||||
let fail_params = FailParams::new_no_fail_data(&EMPTY_STAMP, &fail_code);
|
||||
|
||||
let accepted_token =
|
||||
b.vr.acceptance_success(tok, &mut sender, Some(&EMPTY_STAMP))
|
||||
b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
let started_token =
|
||||
b.vr.start_success(accepted_token, &mut sender, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
b.vr.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0])
|
||||
.expect("Sending start success failed");
|
||||
let empty =
|
||||
b.vr.completion_failure(started_token, &mut sender, fail_params)
|
||||
b.vr.completion_failure(started_token, &sender, fail_params)
|
||||
.expect("Completion failure");
|
||||
assert_eq!(empty, ());
|
||||
completion_fail_check(&mut sender, req_id);
|
||||
}
|
||||
|
||||
@ -2038,21 +2265,19 @@ mod tests {
|
||||
let (mut b, tok) = base_with_helper_init();
|
||||
let req_id = tok.req_id;
|
||||
let fail_code = EcssEnumU32::new(0x1020);
|
||||
let fail_params = FailParams::new(Some(&EMPTY_STAMP), &fail_code, None);
|
||||
let fail_params = FailParams::new_no_fail_data(&EMPTY_STAMP, &fail_code);
|
||||
|
||||
let accepted_token = b
|
||||
.helper
|
||||
.acceptance_success(tok, Some(&EMPTY_STAMP))
|
||||
.acceptance_success(tok, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
let started_token = b
|
||||
.helper
|
||||
.start_success(accepted_token, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0])
|
||||
.expect("Sending start success failed");
|
||||
let empty = b
|
||||
.helper
|
||||
b.helper
|
||||
.completion_failure(started_token, fail_params)
|
||||
.expect("Completion failure");
|
||||
assert_eq!(empty, ());
|
||||
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
|
||||
completion_fail_check(sender, req_id);
|
||||
}
|
||||
@ -2106,12 +2331,12 @@ mod tests {
|
||||
let (b, tok) = base_init(false);
|
||||
let mut sender = TestSender::default();
|
||||
let accepted_token =
|
||||
b.vr.acceptance_success(tok, &mut sender, Some(&EMPTY_STAMP))
|
||||
b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
let started_token =
|
||||
b.vr.start_success(accepted_token, &mut sender, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
b.vr.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0])
|
||||
.expect("Sending start success failed");
|
||||
b.vr.completion_success(started_token, &mut sender, Some(&EMPTY_STAMP))
|
||||
b.vr.completion_success(started_token, &sender, &EMPTY_STAMP)
|
||||
.expect("Sending completion success failed");
|
||||
completion_success_check(&mut sender, tok.req_id);
|
||||
}
|
||||
@ -2121,14 +2346,14 @@ mod tests {
|
||||
let (mut b, tok) = base_with_helper_init();
|
||||
let accepted_token = b
|
||||
.helper
|
||||
.acceptance_success(tok, Some(&EMPTY_STAMP))
|
||||
.acceptance_success(tok, &EMPTY_STAMP)
|
||||
.expect("Sending acceptance success failed");
|
||||
let started_token = b
|
||||
.helper
|
||||
.start_success(accepted_token, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0])
|
||||
.expect("Sending start success failed");
|
||||
b.helper
|
||||
.completion_success(started_token, Some(&EMPTY_STAMP))
|
||||
.completion_success(started_token, &EMPTY_STAMP)
|
||||
.expect("Sending completion success failed");
|
||||
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
|
||||
completion_success_check(sender, tok.req_id);
|
||||
@ -2154,13 +2379,13 @@ mod tests {
|
||||
|
||||
// Complete success sequence for a telecommand
|
||||
let accepted_token = reporter
|
||||
.acceptance_success(init_token, Some(&EMPTY_STAMP))
|
||||
.acceptance_success(init_token, &EMPTY_STAMP)
|
||||
.unwrap();
|
||||
let started_token = reporter
|
||||
.start_success(accepted_token, Some(&EMPTY_STAMP))
|
||||
.start_success(accepted_token, &EMPTY_STAMP)
|
||||
.unwrap();
|
||||
reporter
|
||||
.completion_success(started_token, Some(&EMPTY_STAMP))
|
||||
.completion_success(started_token, &EMPTY_STAMP)
|
||||
.unwrap();
|
||||
|
||||
// Verify it arrives correctly on receiver end
|
||||
|
49
satrs/src/queue.rs
Normal file
49
satrs/src/queue.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use core::fmt::{Display, Formatter};
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
/// Generic error type for sending something via a message queue.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum GenericSendError {
|
||||
RxDisconnected,
|
||||
QueueFull(Option<u32>),
|
||||
}
|
||||
|
||||
impl Display for GenericSendError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
GenericSendError::RxDisconnected => {
|
||||
write!(f, "rx side has disconnected")
|
||||
}
|
||||
GenericSendError::QueueFull(max_cap) => {
|
||||
write!(f, "queue with max capacity of {max_cap:?} is full")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for GenericSendError {}
|
||||
|
||||
/// Generic error type for sending something via a message queue.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum GenericRecvError {
|
||||
Empty,
|
||||
TxDisconnected,
|
||||
}
|
||||
|
||||
impl Display for GenericRecvError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::TxDisconnected => {
|
||||
write!(f, "tx side has disconnected")
|
||||
}
|
||||
Self::Empty => {
|
||||
write!(f, "nothing to receive")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for GenericRecvError {}
|
@ -1 +1,110 @@
|
||||
use core::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
use spacepackets::{
|
||||
ecss::{tc::IsPusTelecommand, PusPacket},
|
||||
ByteConversionError, CcsdsPacket,
|
||||
};
|
||||
|
||||
use crate::TargetId;
|
||||
|
||||
pub type Apid = u16;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum TargetIdCreationError {
|
||||
ByteConversion(ByteConversionError),
|
||||
NotEnoughAppData(usize),
|
||||
}
|
||||
|
||||
impl From<ByteConversionError> for TargetIdCreationError {
|
||||
fn from(e: ByteConversionError) -> Self {
|
||||
Self::ByteConversion(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetIdCreationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::ByteConversion(e) => write!(f, "target ID creation: {}", e),
|
||||
Self::NotEnoughAppData(len) => {
|
||||
write!(f, "not enough app data to generate target ID: {}", len)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for TargetIdCreationError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
if let Self::ByteConversion(e) = self {
|
||||
return Some(e);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct TargetAndApidId {
|
||||
pub apid: Apid,
|
||||
pub target: u32,
|
||||
}
|
||||
|
||||
impl TargetAndApidId {
|
||||
pub fn new(apid: Apid, target: u32) -> Self {
|
||||
Self { apid, target }
|
||||
}
|
||||
|
||||
pub fn apid(&self) -> Apid {
|
||||
self.apid
|
||||
}
|
||||
|
||||
pub fn target(&self) -> u32 {
|
||||
self.target
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> TargetId {
|
||||
((self.apid as u64) << 32) | (self.target as u64)
|
||||
}
|
||||
|
||||
pub fn target_id(&self) -> TargetId {
|
||||
self.raw()
|
||||
}
|
||||
|
||||
pub fn from_pus_tc(
|
||||
tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand),
|
||||
) -> Result<Self, TargetIdCreationError> {
|
||||
if tc.user_data().len() < 4 {
|
||||
return Err(ByteConversionError::FromSliceTooSmall {
|
||||
found: tc.user_data().len(),
|
||||
expected: 8,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
Ok(Self {
|
||||
apid: tc.apid(),
|
||||
target: u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for TargetAndApidId {
|
||||
fn from(raw: u64) -> Self {
|
||||
Self {
|
||||
apid: (raw >> 32) as u16,
|
||||
target: raw as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TargetAndApidId> for u64 {
|
||||
fn from(target_and_apid_id: TargetAndApidId) -> Self {
|
||||
target_and_apid_id.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetAndApidId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}, {}", self.apid, self.target)
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,6 @@ pub use ccsds_distrib::{CcsdsDistributor, CcsdsError, CcsdsPacketHandler};
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use pus_distrib::{PusDistributor, PusServiceProvider};
|
||||
|
||||
pub type TargetId = u32;
|
||||
|
||||
/// Generic trait for object which can receive any telecommands in form of a raw bytestream, with
|
||||
/// no assumptions about the received protocol.
|
||||
///
|
||||
|
@ -1,9 +1,10 @@
|
||||
//#[cfg(feature = "crossbeam")]
|
||||
#[cfg(feature = "crossbeam")]
|
||||
pub mod crossbeam_test {
|
||||
use hashbrown::HashMap;
|
||||
use satrs::pool::{PoolProvider, PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
|
||||
use satrs::pus::verification::{
|
||||
FailParams, RequestId, VerificationReporterCfg, VerificationReporterWithSender,
|
||||
VerificationReportingProvider,
|
||||
};
|
||||
use satrs::pus::CrossbeamTmInStoreSender;
|
||||
use satrs::tmtc::tm_helper::SharedTmPool;
|
||||
@ -89,24 +90,24 @@ pub mod crossbeam_test {
|
||||
|
||||
let token = reporter_with_sender_0.add_tc_with_req_id(req_id_0);
|
||||
let accepted_token = reporter_with_sender_0
|
||||
.acceptance_success(token, Some(&FIXED_STAMP))
|
||||
.acceptance_success(token, &FIXED_STAMP)
|
||||
.expect("Acceptance success failed");
|
||||
|
||||
// Do some start handling here
|
||||
let started_token = reporter_with_sender_0
|
||||
.start_success(accepted_token, Some(&FIXED_STAMP))
|
||||
.start_success(accepted_token, &FIXED_STAMP)
|
||||
.expect("Start success failed");
|
||||
// Do some step handling here
|
||||
reporter_with_sender_0
|
||||
.step_success(&started_token, Some(&FIXED_STAMP), EcssEnumU8::new(0))
|
||||
.step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(0))
|
||||
.expect("Start success failed");
|
||||
|
||||
// Finish up
|
||||
reporter_with_sender_0
|
||||
.step_success(&started_token, Some(&FIXED_STAMP), EcssEnumU8::new(1))
|
||||
.step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(1))
|
||||
.expect("Start success failed");
|
||||
reporter_with_sender_0
|
||||
.completion_success(started_token, Some(&FIXED_STAMP))
|
||||
.completion_success(started_token, &FIXED_STAMP)
|
||||
.expect("Completion success failed");
|
||||
});
|
||||
|
||||
@ -124,13 +125,13 @@ pub mod crossbeam_test {
|
||||
let (tc, _) = PusTcReader::new(&tc_buf[0..tc_len]).unwrap();
|
||||
let token = reporter_with_sender_1.add_tc(&tc);
|
||||
let accepted_token = reporter_with_sender_1
|
||||
.acceptance_success(token, Some(&FIXED_STAMP))
|
||||
.acceptance_success(token, &FIXED_STAMP)
|
||||
.expect("Acceptance success failed");
|
||||
let started_token = reporter_with_sender_1
|
||||
.start_success(accepted_token, Some(&FIXED_STAMP))
|
||||
.start_success(accepted_token, &FIXED_STAMP)
|
||||
.expect("Start success failed");
|
||||
let fail_code = EcssEnumU16::new(2);
|
||||
let params = FailParams::new(Some(&FIXED_STAMP), &fail_code, None);
|
||||
let params = FailParams::new_no_fail_data(&FIXED_STAMP, &fail_code);
|
||||
reporter_with_sender_1
|
||||
.completion_failure(started_token, params)
|
||||
.expect("Completion success failed");
|
||||
|
Loading…
Reference in New Issue
Block a user