diff --git a/satrs-example/src/events.rs b/satrs-example/src/events.rs index cb0caf8..fe9ba82 100644 --- a/satrs-example/src/events.rs +++ b/satrs-example/src/events.rs @@ -2,7 +2,6 @@ use std::sync::mpsc::{self}; use crate::pus::create_verification_reporter; use satrs::event_man::{EventMessageU32, EventRoutingError}; -use satrs::params::WritableToBeBytes; use satrs::pus::event::EventTmHookProvider; use satrs::pus::verification::VerificationReporter; use satrs::pus::EcssTmSender; @@ -42,6 +41,7 @@ pub struct PusEventHandler { tm_sender: TmSender, time_provider: CdsTime, timestamp: [u8; 7], + small_data_buf: [u8; 64], verif_handler: VerificationReporter, } @@ -82,6 +82,7 @@ impl PusEventHandler { pus_event_man_rx, time_provider: CdsTime::new_with_u16_days(0, 0), timestamp: [0; 7], + small_data_buf: [0; 64], verif_handler, tm_sender, } @@ -132,19 +133,17 @@ impl PusEventHandler { // Perform the generation of PUS event packets match self.pus_event_man_rx.try_recv() { Ok(event_msg) => { - update_time(&mut self.time_provider, &mut self.timestamp); - let param_vec = event_msg.params().map_or(Vec::new(), |param| { - param.to_vec().expect("failed to convert params to vec") - }); // We use the TM modification hook to set the sender APID for each event. self.pus_event_tm_creator.reporter.tm_hook.next_apid = UniqueApidTargetId::from(event_msg.sender_id()).apid; + update_time(&mut self.time_provider, &mut self.timestamp); self.pus_event_tm_creator - .generate_pus_event_tm_generic( + .generate_pus_event_tm_generic_with_generic_params( &self.tm_sender, &self.timestamp, event_msg.event(), - Some(¶m_vec), + &mut self.small_data_buf, + event_msg.params(), ) .expect("Sending TM as event failed"); } diff --git a/satrs-example/src/pus/action.rs b/satrs-example/src/pus/action.rs index dcdb345..49cf44d 100644 --- a/satrs-example/src/pus/action.rs +++ b/satrs-example/src/pus/action.rs @@ -1,12 +1,12 @@ use log::{error, warn}; use satrs::action::{ActionRequest, ActionRequestVariant}; -use satrs::params::WritableToBeBytes; use satrs::pool::SharedStaticMemoryPool; use satrs::pus::action::{ ActionReplyPus, ActionReplyVariant, ActivePusActionRequestStd, DefaultActiveActionRequestMap, }; use satrs::pus::verification::{ - FailParams, FailParamsWithStep, TcStateAccepted, TcStateStarted, VerificationReporter, + handle_completion_failure_with_error_as_params, handle_step_failure_with_error_as_params, + FailParams, TcStateAccepted, TcStateStarted, VerificationReporter, VerificationReportingProvider, VerificationToken, }; use satrs::pus::{ @@ -61,7 +61,7 @@ impl PusReplyHandler for ActionReplyH active_request: &ActivePusActionRequestStd, tm_sender: &(impl EcssTmSender + ?Sized), verification_handler: &impl VerificationReportingProvider, - time_stamp: &[u8], + timestamp: &[u8], ) -> Result { let verif_token: VerificationToken = active_request .token() @@ -69,15 +69,21 @@ impl PusReplyHandler for ActionReplyH .expect("invalid token state"); let remove_entry = match &reply.message.variant { ActionReplyVariant::CompletionFailed { error_code, params } => { - let mut fail_data_len = 0; - if let Some(params) = params { - fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?; - } - verification_handler.completion_failure( + let error_propagated = handle_completion_failure_with_error_as_params( tm_sender, verif_token, - FailParams::new(time_stamp, error_code, &self.fail_data_buf[..fail_data_len]), + verification_handler, + timestamp, + error_code, + &mut self.fail_data_buf, + params.as_ref(), )?; + if !error_propagated { + log::warn!( + "error params for completion failure were not propated: {:?}", + params.as_ref() + ); + } true } ActionReplyVariant::StepFailed { @@ -85,31 +91,33 @@ impl PusReplyHandler for ActionReplyH step, params, } => { - let mut fail_data_len = 0; - if let Some(params) = params { - fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?; - } - verification_handler.step_failure( + let error_propagated = handle_step_failure_with_error_as_params( tm_sender, verif_token, - FailParamsWithStep::new( - time_stamp, - &EcssEnumU16::new(*step), - error_code, - &self.fail_data_buf[..fail_data_len], - ), + verification_handler, + timestamp, + &EcssEnumU16::new(*step), + error_code, + &mut self.fail_data_buf, + params.as_ref(), )?; + if !error_propagated { + log::warn!( + "error params for completion failure were not propated: {:?}", + params.as_ref() + ); + } true } ActionReplyVariant::Completed => { - verification_handler.completion_success(tm_sender, verif_token, time_stamp)?; + verification_handler.completion_success(tm_sender, verif_token, timestamp)?; true } ActionReplyVariant::StepSuccess { step } => { verification_handler.step_success( tm_sender, &verif_token, - time_stamp, + timestamp, EcssEnumU16::new(*step), )?; false diff --git a/satrs/src/pus/event_man.rs b/satrs/src/pus/event_man.rs index c8aec2d..8b01904 100644 --- a/satrs/src/pus/event_man.rs +++ b/satrs/src/pus/event_man.rs @@ -107,6 +107,7 @@ pub mod alloc_mod { use crate::{ events::EventU16, + params::{Params, WritableToBeBytes}, pus::event::{DummyEventHook, EventTmHookProvider}, }; @@ -147,6 +148,12 @@ pub mod alloc_mod { } } + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub struct EventGenerationResult { + pub event_was_enabled: bool, + pub params_were_propagated: bool, + } + pub struct PusEventTmCreatorWithMap< ReportingMap: PusEventReportingMapProvider, Event: GenericEvent, @@ -212,6 +219,53 @@ pub mod alloc_mod { .map_err(|e| e.into()), } } + + pub fn generate_pus_event_tm_generic_with_generic_params( + &self, + sender: &(impl EcssTmSender + ?Sized), + time_stamp: &[u8], + event: Event, + small_data_buf: &mut [u8], + params: Option<&Params>, + ) -> Result { + let mut result = EventGenerationResult { + event_was_enabled: false, + params_were_propagated: true, + }; + if params.is_none() { + result.event_was_enabled = + self.generate_pus_event_tm_generic(sender, time_stamp, event, None)?; + return Ok(result); + } + let params = params.unwrap(); + result.event_was_enabled = match params { + Params::Heapless(heapless_param) => { + heapless_param + .write_to_be_bytes(&mut small_data_buf[..heapless_param.written_len()]) + .map_err(EcssTmtcError::ByteConversion)?; + self.generate_pus_event_tm_generic( + sender, + time_stamp, + event, + Some(small_data_buf), + )? + } + Params::Vec(vec) => { + self.generate_pus_event_tm_generic(sender, time_stamp, event, Some(vec))? + } + Params::String(string) => self.generate_pus_event_tm_generic( + sender, + time_stamp, + event, + Some(string.as_bytes()), + )?, + _ => { + result.params_were_propagated = false; + self.generate_pus_event_tm_generic(sender, time_stamp, event, None)? + } + }; + Ok(result) + } } impl @@ -336,4 +390,9 @@ mod tests { assert!(event_sent); event_rx.try_recv().expect("No info event received"); } + + #[test] + fn test_event_with_generic_params() { + // TODO: Test this. + } } diff --git a/satrs/src/pus/verification.rs b/satrs/src/pus/verification.rs index 2f81e41..1d5f29c 100644 --- a/satrs/src/pus/verification.rs +++ b/satrs/src/pus/verification.rs @@ -79,6 +79,7 @@ //! The [integration test](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-core/tests/verification_test.rs) //! for the verification module contains examples how this module could be used in a more complex //! context involving multiple threads +use crate::params::{Params, WritableToBeBytes}; use crate::pus::{source_buffer_large_enough, EcssTmSender, EcssTmtcError}; use core::fmt::{Debug, Display, Formatter}; use core::hash::{Hash, Hasher}; @@ -1171,26 +1172,139 @@ pub mod alloc_mod { } } -/* -#[cfg(feature = "std")] -pub mod std_mod { - use std::sync::mpsc; - - use crate::pool::StoreAddr; - use crate::pus::verification::VerificationReporterWithSender; - - use super::alloc_mod::VerificationReporterWithSharedPoolSender; - - pub type VerificationReporterWithSharedPoolMpscSender = - VerificationReporterWithSharedPoolSender>; - pub type VerificationReporterWithSharedPoolMpscBoundedSender = - VerificationReporterWithSharedPoolSender>; - pub type VerificationReporterWithVecMpscSender = - VerificationReporterWithSender>>; - pub type VerificationReporterWithVecMpscBoundedSender = - VerificationReporterWithSender>>; +/// This helper function simplifies generating completion failures where the error data has +/// the generic [Params] type. +/// +/// A small data buffer needs to be supplied for the [Params::Heapless] type because all data +/// suplied as error data must be held in a slice. Passing a static buffer avoids dynamic memory +/// allocation for this case. +/// +/// Please note that this specific function can not propagate the [Params::Store] variant. +/// This function also might not be able to propagate other error variants which are added in +/// the future. The returned boolean on success denotes whether the error parameters were +/// propagated properly. +pub fn handle_completion_failure_with_error_as_params( + tm_sender: &(impl EcssTmSender + ?Sized), + verif_token: VerificationToken, + verif_reporter: &impl VerificationReportingProvider, + timestamp: &[u8], + error_code: &impl EcssEnumeration, + small_data_buf: &mut [u8], + error_params: Option<&Params>, +) -> Result { + let mut error_params_propagated = true; + if error_params.is_none() { + verif_reporter.completion_failure( + tm_sender, + verif_token, + FailParams::new(timestamp, error_code, &[]), + )?; + return Ok(true); + } + let error_params = error_params.unwrap(); + match error_params { + Params::Heapless(heapless_param) => { + heapless_param + .write_to_be_bytes(&mut small_data_buf[..heapless_param.written_len()])?; + verif_reporter.completion_failure( + tm_sender, + verif_token, + FailParams::new( + timestamp, + error_code, + &small_data_buf[..heapless_param.written_len()], + ), + )?; + } + Params::Vec(vec) => { + verif_reporter.completion_failure( + tm_sender, + verif_token, + FailParams::new(timestamp, error_code, vec), + )?; + } + Params::String(str) => { + verif_reporter.completion_failure( + tm_sender, + verif_token, + FailParams::new(timestamp, error_code, str.as_bytes()), + )?; + } + _ => { + verif_reporter.completion_failure( + tm_sender, + verif_token, + FailParams::new(timestamp, error_code, &[]), + )?; + error_params_propagated = false; + } + } + Ok(error_params_propagated) +} + +/// This function is similar to [handle_completion_failure_with_error_as_params] but handles the +/// step failure case. +#[allow(clippy::too_many_arguments)] +pub fn handle_step_failure_with_error_as_params( + tm_sender: &(impl EcssTmSender + ?Sized), + verif_token: VerificationToken, + verif_reporter: &impl VerificationReportingProvider, + timestamp: &[u8], + step: &impl EcssEnumeration, + error_code: &impl EcssEnumeration, + small_data_buf: &mut [u8], + error_params: Option<&Params>, +) -> Result { + if error_params.is_none() { + verif_reporter.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new(timestamp, step, error_code, &[]), + )?; + return Ok(true); + } + let error_params = error_params.unwrap(); + let mut error_params_propagated = true; + match error_params { + Params::Heapless(heapless_param) => { + heapless_param + .write_to_be_bytes(&mut small_data_buf[..heapless_param.written_len()])?; + verif_reporter.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new( + timestamp, + step, + error_code, + &small_data_buf[..heapless_param.written_len()], + ), + )?; + } + Params::Vec(vec) => { + verif_reporter.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new(timestamp, step, error_code, vec), + )?; + } + Params::String(str) => { + verif_reporter.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new(timestamp, step, error_code, str.as_bytes()), + )?; + } + _ => { + verif_reporter.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new(timestamp, step, error_code, &[]), + )?; + error_params_propagated = false; + } + } + Ok(error_params_propagated) } - */ #[cfg(any(feature = "test_util", test))] pub mod test_util { @@ -1566,62 +1680,6 @@ pub mod test_util { .pop_front() .expect("report queue is empty") } - /* - pub fn verification_info(&self, req_id: &RequestId) -> Option { - let verif_map = self.verification_map.lock().unwrap(); - let value = verif_map.borrow().get(req_id).cloned(); - value - } - - - pub fn check_started(&self, req_id: &RequestId) -> bool { - let verif_map = self.verification_map.lock().unwrap(); - if let Some(entry) = verif_map.borrow().get(req_id) { - return entry.started.unwrap_or(false); - } - false - } - - fn generic_completion_checks( - entry: &VerificationStatus, - step: Option, - completion_success: bool, - ) { - assert!(entry.accepted.unwrap()); - assert!(entry.started.unwrap()); - if let Some(step) = step { - assert!(entry.step_status.unwrap()); - assert_eq!(entry.step, step); - } else { - assert!(entry.step_status.is_none()); - } - assert_eq!(entry.completed.unwrap(), completion_success); - } - - - pub fn assert_completion_failure( - &self, - req_id: &RequestId, - step: Option, - error_code: u64, - ) { - let verif_map = self.verification_map.lock().unwrap(); - if let Some(entry) = verif_map.borrow().get(req_id) { - Self::generic_completion_checks(entry, step, false); - assert_eq!(entry.fail_enum.unwrap(), error_code); - return; - } - panic!("request not in verification map"); - } - - pub fn completion_status(&self, req_id: &RequestId) -> Option { - let verif_map = self.verification_map.lock().unwrap(); - if let Some(entry) = verif_map.borrow().get(req_id) { - return entry.completed; - } - panic!("request not in verification map"); - } - */ } } @@ -2369,4 +2427,14 @@ pub mod tests { .expect("Sending completion success failed"); testbench.completion_success_check(true); } + + #[test] + fn test_completion_failure_helper() { + // TODO: Add tests for this. + } + + #[test] + fn test_step_failure_helper() { + // TODO: Add tests for this. + } }