use crate::pool::{SharedPool, StoreAddr}; use crate::pus::verification::{StdVerifReporterWithSender, TcStateAccepted, VerificationToken}; use crate::pus::{ EcssTcReceiver, EcssTmSender, PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHandler, PusTmWrapper, }; use spacepackets::ecss::PusPacket; use spacepackets::tc::PusTc; use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; use spacepackets::SpHeader; use std::boxed::Box; /// 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 { psb: PusServiceBase, } impl PusService17TestHandler { pub fn new( tc_receiver: Box<dyn EcssTcReceiver>, shared_tc_store: SharedPool, tm_sender: Box<dyn EcssTmSender>, tm_apid: u16, verification_handler: StdVerifReporterWithSender, ) -> Self { Self { psb: PusServiceBase::new( tc_receiver, shared_tc_store, tm_sender, tm_apid, verification_handler, ), } } } impl PusServiceHandler for PusService17TestHandler { fn psb_mut(&mut self) -> &mut PusServiceBase { &mut self.psb } fn psb(&self) -> &PusServiceBase { &self.psb } fn handle_one_tc( &mut self, addr: StoreAddr, token: VerificationToken<TcStateAccepted>, ) -> Result<PusPacketHandlerResult, PusPacketHandlingError> { self.copy_tc_to_buf(addr)?; let (tc, _) = PusTc::from_bytes(&self.psb.pus_buf)?; if tc.service() != 17 { return Err(PusPacketHandlingError::WrongService(tc.service())); } if tc.subservice() == 1 { let mut partial_error = None; let time_stamp = self.psb().get_current_timestamp(&mut partial_error); let result = self .psb .verification_handler .get_mut() .start_success(token, Some(&time_stamp)) .map_err(|_| PartialPusHandlingError::Verification); let start_token = if let Ok(result) = result { Some(result) } else { partial_error = Some(result.unwrap_err()); None }; // Sequence count will be handled centrally in TM funnel. let mut reply_header = SpHeader::tm_unseg(self.psb.tm_apid, 0, 0).unwrap(); let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &time_stamp); let ping_reply = PusTm::new(&mut reply_header, tc_header, None, true); let result = self .psb .tm_sender .send_tm(PusTmWrapper::Direct(ping_reply)) .map_err(PartialPusHandlingError::TmSend); if let Err(err) = result { partial_error = Some(err); } if let Some(start_token) = start_token { if self .psb .verification_handler .get_mut() .completion_success(start_token, Some(&time_stamp)) .is_err() { partial_error = Some(PartialPusHandlingError::Verification) } } if let Some(partial_error) = partial_error { return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess( partial_error, )); }; return Ok(PusPacketHandlerResult::RequestHandled); } Ok(PusPacketHandlerResult::CustomSubservice( tc.subservice(), token, )) } } #[cfg(test)] mod tests { use crate::pool::{LocalPool, PoolCfg, SharedPool}; use crate::pus::test::PusService17TestHandler; use crate::pus::verification::{ RequestId, StdVerifReporterWithSender, VerificationReporterCfg, }; use crate::pus::{MpscTcInStoreReceiver, MpscTmInStoreSender, PusServiceHandler}; use crate::tmtc::tm_helper::SharedTmStore; use spacepackets::ecss::{PusPacket, SerializablePusPacket}; use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; use spacepackets::tm::PusTm; use spacepackets::{SequenceFlags, SpHeader}; use std::boxed::Box; use std::sync::{mpsc, RwLock}; use std::vec; const TEST_APID: u16 = 0x101; #[test] fn test_basic_ping_processing() { let mut pus_buf: [u8; 64] = [0; 64]; let pool_cfg = PoolCfg::new(vec![(16, 16), (8, 32), (4, 64)]); let tc_pool = LocalPool::new(pool_cfg.clone()); let tm_pool = LocalPool::new(pool_cfg); let tc_pool_shared = SharedPool::new(RwLock::new(Box::new(tc_pool))); let shared_tm_store = SharedTmStore::new(Box::new(tm_pool)); let tm_pool_shared = shared_tm_store.clone_backing_pool(); let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel(); let (tm_tx, tm_rx) = mpsc::channel(); let verif_sender = MpscTmInStoreSender::new(0, "verif_sender", shared_tm_store.clone(), tm_tx.clone()); let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap(); let mut verification_handler = StdVerifReporterWithSender::new(&verif_cfg, Box::new(verif_sender)); let test_srv_tm_sender = MpscTmInStoreSender::new(0, "TEST_SENDER", shared_tm_store, tm_tx); let test_srv_tc_receiver = MpscTcInStoreReceiver::new(0, "TEST_RECEIVER", test_srv_tc_rx); let mut pus_17_handler = PusService17TestHandler::new( Box::new(test_srv_tc_receiver), tc_pool_shared.clone(), Box::new(test_srv_tm_sender), TEST_APID, verification_handler.clone(), ); // Create a ping TC, verify acceptance. let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); let sec_header = PusTcSecondaryHeader::new_simple(17, 1); let ping_tc = PusTc::new(&mut sp_header, sec_header, None, true); let token = verification_handler.add_tc(&ping_tc); let token = verification_handler .acceptance_success(token, None) .unwrap(); let tc_size = ping_tc.write_to_bytes(&mut pus_buf).unwrap(); let mut tc_pool = tc_pool_shared.write().unwrap(); let addr = tc_pool.add(&pus_buf[..tc_size]).unwrap(); drop(tc_pool); // Send accepted TC to test service handler. test_srv_tc_tx.send((addr, token.into())).unwrap(); let result = pus_17_handler.handle_next_packet(); assert!(result.is_ok()); // We should see 4 replies in the TM queue now: Acceptance TM, Start TM, ping reply and // Completion TM let mut next_msg = tm_rx.try_recv(); assert!(next_msg.is_ok()); let mut tm_addr = next_msg.unwrap(); let tm_pool = tm_pool_shared.read().unwrap(); let tm_raw = tm_pool.read(&tm_addr).unwrap(); let (tm, _) = PusTm::from_bytes(&tm_raw, 0).unwrap(); assert_eq!(tm.service(), 1); assert_eq!(tm.subservice(), 1); let req_id = RequestId::from_bytes(tm.user_data().unwrap()).unwrap(); assert_eq!(req_id, token.req_id()); // Acceptance TM next_msg = tm_rx.try_recv(); assert!(next_msg.is_ok()); tm_addr = next_msg.unwrap(); let tm_raw = tm_pool.read(&tm_addr).unwrap(); // Is generated with CDS short timestamp. let (tm, _) = PusTm::from_bytes(&tm_raw, 7).unwrap(); assert_eq!(tm.service(), 1); assert_eq!(tm.subservice(), 3); let req_id = RequestId::from_bytes(tm.user_data().unwrap()).unwrap(); assert_eq!(req_id, token.req_id()); // Ping reply next_msg = tm_rx.try_recv(); assert!(next_msg.is_ok()); tm_addr = next_msg.unwrap(); let tm_raw = tm_pool.read(&tm_addr).unwrap(); // Is generated with CDS short timestamp. let (tm, _) = PusTm::from_bytes(&tm_raw, 7).unwrap(); assert_eq!(tm.service(), 17); assert_eq!(tm.subservice(), 2); assert!(tm.user_data().is_none()); // TM completion next_msg = tm_rx.try_recv(); assert!(next_msg.is_ok()); tm_addr = next_msg.unwrap(); let tm_raw = tm_pool.read(&tm_addr).unwrap(); // Is generated with CDS short timestamp. let (tm, _) = PusTm::from_bytes(&tm_raw, 7).unwrap(); assert_eq!(tm.service(), 1); assert_eq!(tm.subservice(), 7); let req_id = RequestId::from_bytes(tm.user_data().unwrap()).unwrap(); assert_eq!(req_id, token.req_id()); } }