use crate::events::EventU32; use crate::pus::event_man::{EventRequest, EventRequestWithToken}; use crate::pus::verification::TcStateToken; use crate::pus::{PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError}; use crate::queue::GenericSendError; use spacepackets::ecss::event::Subservice; use spacepackets::ecss::PusPacket; use std::sync::mpsc::Sender; use super::verification::VerificationReportingProvider; use super::{ EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore, GenericConversionError, GenericRoutingError, PusServiceHelper, }; pub struct PusEventServiceHandler< TcReceiver: EcssTcReceiverCore, TmSender: EcssTmSenderCore, TcInMemConverter: EcssTcInMemConverter, VerificationReporter: VerificationReportingProvider, > { pub service_helper: PusServiceHelper, event_request_tx: Sender, } impl< TcReceiver: EcssTcReceiverCore, TmSender: EcssTmSenderCore, TcInMemConverter: EcssTcInMemConverter, VerificationReporter: VerificationReportingProvider, > PusEventServiceHandler { pub fn new( service_helper: PusServiceHelper< TcReceiver, TmSender, TcInMemConverter, VerificationReporter, >, event_request_tx: Sender, ) -> Self { Self { service_helper, event_request_tx, } } pub fn poll_and_handle_next_tc( &mut self, time_stamp: &[u8], ) -> Result { 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_mut() .cache(&ecss_tc_and_token.tc_in_memory)?; let tc = self.service_helper.tc_in_mem_converter().convert()?; let subservice = tc.subservice(); let srv = Subservice::try_from(subservice); if srv.is_err() { return Ok(PusPacketHandlerResult::CustomSubservice( tc.subservice(), ecss_tc_and_token.token, )); } let handle_enable_disable_request = |enable: bool| -> Result { if tc.user_data().len() < 4 { return Err(GenericConversionError::NotEnoughAppData { expected: 4, found: tc.user_data().len(), } .into()); } let user_data = tc.user_data(); let event_u32 = EventU32::from(u32::from_be_bytes(user_data[0..4].try_into().unwrap())); let start_token = self .service_helper .common .verif_reporter .start_success( &self.service_helper.common.tm_sender, ecss_tc_and_token.token, time_stamp, ) .map_err(|_| PartialPusHandlingError::Verification); let partial_error = start_token.clone().err(); let mut token: TcStateToken = ecss_tc_and_token.token.into(); if let Ok(start_token) = start_token { token = start_token.into(); } let event_req_with_token = if enable { EventRequestWithToken { request: EventRequest::Enable(event_u32), token, } } else { EventRequestWithToken { request: EventRequest::Disable(event_u32), token, } }; self.event_request_tx .send(event_req_with_token) .map_err(|_| { PusPacketHandlingError::RequestRouting(GenericRoutingError::Send( GenericSendError::RxDisconnected, )) })?; if let Some(partial_error) = partial_error { return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess( partial_error, )); } Ok(PusPacketHandlerResult::RequestHandled) }; match srv.unwrap() { Subservice::TmInfoReport | Subservice::TmLowSeverityReport | Subservice::TmMediumSeverityReport | Subservice::TmHighSeverityReport => { return Err(PusPacketHandlingError::RequestConversion( GenericConversionError::WrongService(tc.subservice()), )) } Subservice::TcEnableEventGeneration => { handle_enable_disable_request(true)?; } Subservice::TcDisableEventGeneration => { handle_enable_disable_request(false)?; } Subservice::TcReportDisabledList | Subservice::TmDisabledEventsReport => { return Ok(PusPacketHandlerResult::SubserviceNotImplemented( subservice, ecss_tc_and_token.token, )); } } Ok(PusPacketHandlerResult::RequestHandled) } } #[cfg(test)] mod tests { use delegate::delegate; use spacepackets::ecss::event::Subservice; use spacepackets::time::{cds, TimeWriter}; use spacepackets::util::UnsignedEnum; use spacepackets::{ ecss::{ tc::{PusTcCreator, PusTcSecondaryHeader}, tm::PusTmReader, }, SpHeader, }; use std::sync::mpsc::{self, Sender}; use crate::pus::event_man::EventRequest; use crate::pus::test_util::{PusTestHarness, SimplePusPacketHandler, TEST_APID}; use crate::pus::verification::{ RequestId, VerificationReporter, VerificationReportingProvider, }; use crate::pus::{GenericConversionError, MpscTcReceiver, MpscTmInSharedPoolSenderBounded}; use crate::{ events::EventU32, pus::{ event_man::EventRequestWithToken, tests::PusServiceHandlerWithSharedStoreCommon, verification::{TcStateAccepted, VerificationToken}, EcssTcInSharedStoreConverter, PusPacketHandlerResult, PusPacketHandlingError, }, }; use super::PusEventServiceHandler; const TEST_EVENT_0: EventU32 = EventU32::const_new(crate::events::Severity::INFO, 5, 25); struct Pus5HandlerWithStoreTester { common: PusServiceHandlerWithSharedStoreCommon, handler: PusEventServiceHandler< MpscTcReceiver, MpscTmInSharedPoolSenderBounded, EcssTcInSharedStoreConverter, VerificationReporter, >, } impl Pus5HandlerWithStoreTester { pub fn new(event_request_tx: Sender) -> Self { let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(0); Self { common, handler: PusEventServiceHandler::new(srv_handler, event_request_tx), } } } impl PusTestHarness for Pus5HandlerWithStoreTester { fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken { let init_token = self.handler.service_helper.verif_reporter_mut().add_tc(tc); self.handler .service_helper .verif_reporter() .acceptance_success(self.handler.service_helper.tm_sender(), init_token, &[0; 7]) .expect("acceptance success failure") } delegate! { to self.common { fn send_tc(&self, token: &VerificationToken, tc: &PusTcCreator); 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 Pus5HandlerWithStoreTester { fn handle_one_tc(&mut self) -> Result { let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); self.handler.poll_and_handle_next_tc(&time_stamp) } } fn event_test( test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler), subservice: Subservice, expected_event_req: EventRequest, event_req_receiver: mpsc::Receiver, ) { let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let sec_header = PusTcSecondaryHeader::new_simple(5, subservice as u8); let mut app_data = [0; 4]; TEST_EVENT_0 .write_to_be_bytes(&mut app_data) .expect("writing test event failed"); let ping_tc = PusTcCreator::new(sp_header, sec_header, &app_data, true); let token = test_harness.init_verification(&ping_tc); test_harness.send_tc(&token, &ping_tc); let request_id = token.request_id(); test_harness.handle_one_tc().unwrap(); test_harness.check_next_verification_tm(1, request_id); test_harness.check_next_verification_tm(3, request_id); // Completion TM is not generated for us. assert!(test_harness.check_no_tm_available()); let event_request = event_req_receiver .try_recv() .expect("no event request received"); assert_eq!(expected_event_req, event_request.request); } #[test] fn test_enabling_event_reporting() { let (event_request_tx, event_request_rx) = mpsc::channel(); let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx); event_test( &mut test_harness, Subservice::TcEnableEventGeneration, EventRequest::Enable(TEST_EVENT_0), event_request_rx, ); } #[test] fn test_disabling_event_reporting() { let (event_request_tx, event_request_rx) = mpsc::channel(); let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx); event_test( &mut test_harness, Subservice::TcDisableEventGeneration, EventRequest::Disable(TEST_EVENT_0), event_request_rx, ); } #[test] fn test_empty_tc_queue() { let (event_request_tx, _) = mpsc::channel(); let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx); let result = test_harness.handle_one_tc(); assert!(result.is_ok()); let result = result.unwrap(); if let PusPacketHandlerResult::Empty = result { } else { panic!("unexpected result type {result:?}") } } #[test] fn test_sending_custom_subservice() { let (event_request_tx, _) = mpsc::channel(); let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx); let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let sec_header = PusTcSecondaryHeader::new_simple(5, 200); let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true); let token = test_harness.init_verification(&ping_tc); test_harness.send_tc(&token, &ping_tc); let result = test_harness.handle_one_tc(); assert!(result.is_ok()); let result = result.unwrap(); if let PusPacketHandlerResult::CustomSubservice(subservice, _) = result { assert_eq!(subservice, 200); } else { panic!("unexpected result type {result:?}") } } #[test] fn test_sending_invalid_app_data() { let (event_request_tx, _) = mpsc::channel(); let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx); let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let sec_header = PusTcSecondaryHeader::new_simple(5, Subservice::TcEnableEventGeneration as u8); let ping_tc = PusTcCreator::new(sp_header, sec_header, &[0, 1, 2], true); let token = test_harness.init_verification(&ping_tc); test_harness.send_tc(&token, &ping_tc); let result = test_harness.handle_one_tc(); assert!(result.is_err()); let result = result.unwrap_err(); if let PusPacketHandlingError::RequestConversion( GenericConversionError::NotEnoughAppData { expected, found }, ) = result { assert_eq!(expected, 4); assert_eq!(found, 3); } else { panic!("unexpected result type {result:?}") } } }