satrs_core/pus/
test.rs

1use crate::pus::{
2    PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError, PusTmWrapper,
3};
4use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
5use spacepackets::ecss::PusPacket;
6use spacepackets::SpHeader;
7
8use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
9
10/// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets.
11/// This handler only processes ping requests and generates a ping reply for them accordingly.
12pub struct PusService17TestHandler<TcInMemConverter: EcssTcInMemConverter> {
13    pub service_helper: PusServiceHelper<TcInMemConverter>,
14}
15
16impl<TcInMemConverter: EcssTcInMemConverter> PusService17TestHandler<TcInMemConverter> {
17    pub fn new(service_helper: PusServiceHelper<TcInMemConverter>) -> Self {
18        Self { service_helper }
19    }
20
21    pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
22        let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
23        if possible_packet.is_none() {
24            return Ok(PusPacketHandlerResult::Empty);
25        }
26        let ecss_tc_and_token = possible_packet.unwrap();
27        let tc = self
28            .service_helper
29            .tc_in_mem_converter
30            .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
31        if tc.service() != 17 {
32            return Err(PusPacketHandlingError::WrongService(tc.service()));
33        }
34        if tc.subservice() == 1 {
35            let mut partial_error = None;
36            let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
37            let result = self
38                .service_helper
39                .common
40                .verification_handler
41                .get_mut()
42                .start_success(ecss_tc_and_token.token, Some(&time_stamp))
43                .map_err(|_| PartialPusHandlingError::Verification);
44            let start_token = if let Ok(result) = result {
45                Some(result)
46            } else {
47                partial_error = Some(result.unwrap_err());
48                None
49            };
50            // Sequence count will be handled centrally in TM funnel.
51            let mut reply_header =
52                SpHeader::tm_unseg(self.service_helper.common.tm_apid, 0, 0).unwrap();
53            let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &time_stamp);
54            let ping_reply = PusTmCreator::new(&mut reply_header, tc_header, &[], true);
55            let result = self
56                .service_helper
57                .common
58                .tm_sender
59                .send_tm(PusTmWrapper::Direct(ping_reply))
60                .map_err(PartialPusHandlingError::TmSend);
61            if let Err(err) = result {
62                partial_error = Some(err);
63            }
64
65            if let Some(start_token) = start_token {
66                if self
67                    .service_helper
68                    .common
69                    .verification_handler
70                    .get_mut()
71                    .completion_success(start_token, Some(&time_stamp))
72                    .is_err()
73                {
74                    partial_error = Some(PartialPusHandlingError::Verification)
75                }
76            }
77            if let Some(partial_error) = partial_error {
78                return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess(
79                    partial_error,
80                ));
81            };
82        } else {
83            return Ok(PusPacketHandlerResult::CustomSubservice(
84                tc.subservice(),
85                ecss_tc_and_token.token,
86            ));
87        }
88        Ok(PusPacketHandlerResult::RequestHandled)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use crate::pus::tests::{
95        PusServiceHandlerWithSharedStoreCommon, PusServiceHandlerWithVecCommon, PusTestHarness,
96        SimplePusPacketHandler, TEST_APID,
97    };
98    use crate::pus::verification::RequestId;
99    use crate::pus::verification::{TcStateAccepted, VerificationToken};
100    use crate::pus::{
101        EcssTcInSharedStoreConverter, EcssTcInVecConverter, PusPacketHandlerResult,
102        PusPacketHandlingError,
103    };
104    use delegate::delegate;
105    use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
106    use spacepackets::ecss::tm::PusTmReader;
107    use spacepackets::ecss::PusPacket;
108    use spacepackets::{SequenceFlags, SpHeader};
109
110    use super::PusService17TestHandler;
111
112    struct Pus17HandlerWithStoreTester {
113        common: PusServiceHandlerWithSharedStoreCommon,
114        handler: PusService17TestHandler<EcssTcInSharedStoreConverter>,
115    }
116
117    impl Pus17HandlerWithStoreTester {
118        pub fn new() -> Self {
119            let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new();
120            let pus_17_handler = PusService17TestHandler::new(srv_handler);
121            Self {
122                common,
123                handler: pus_17_handler,
124            }
125        }
126    }
127
128    impl PusTestHarness for Pus17HandlerWithStoreTester {
129        delegate! {
130            to self.common {
131                fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
132                fn read_next_tm(&mut self) -> PusTmReader<'_>;
133                fn check_no_tm_available(&self) -> bool;
134                fn check_next_verification_tm(
135                    &self,
136                    subservice: u8,
137                    expected_request_id: RequestId
138                );
139            }
140        }
141    }
142    impl SimplePusPacketHandler for Pus17HandlerWithStoreTester {
143        delegate! {
144            to self.handler {
145                fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
146            }
147        }
148    }
149
150    struct Pus17HandlerWithVecTester {
151        common: PusServiceHandlerWithVecCommon,
152        handler: PusService17TestHandler<EcssTcInVecConverter>,
153    }
154
155    impl Pus17HandlerWithVecTester {
156        pub fn new() -> Self {
157            let (common, srv_handler) = PusServiceHandlerWithVecCommon::new();
158            Self {
159                common,
160                handler: PusService17TestHandler::new(srv_handler),
161            }
162        }
163    }
164
165    impl PusTestHarness for Pus17HandlerWithVecTester {
166        delegate! {
167            to self.common {
168                fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
169                fn read_next_tm(&mut self) -> PusTmReader<'_>;
170                fn check_no_tm_available(&self) -> bool;
171                fn check_next_verification_tm(
172                    &self,
173                    subservice: u8,
174                    expected_request_id: RequestId,
175                );
176            }
177        }
178    }
179    impl SimplePusPacketHandler for Pus17HandlerWithVecTester {
180        delegate! {
181            to self.handler {
182                fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
183            }
184        }
185    }
186
187    fn ping_test(test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler)) {
188        // Create a ping TC, verify acceptance.
189        let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
190        let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
191        let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
192        let token = test_harness.send_tc(&ping_tc);
193        let request_id = token.req_id();
194        let result = test_harness.handle_one_tc();
195        assert!(result.is_ok());
196        // We should see 4 replies in the TM queue now: Acceptance TM, Start TM, ping reply and
197        // Completion TM
198
199        // Acceptance TM
200        test_harness.check_next_verification_tm(1, request_id);
201
202        // Start TM
203        test_harness.check_next_verification_tm(3, request_id);
204
205        // Ping reply
206        let tm = test_harness.read_next_tm();
207        assert_eq!(tm.service(), 17);
208        assert_eq!(tm.subservice(), 2);
209        assert!(tm.user_data().is_empty());
210
211        // TM completion
212        test_harness.check_next_verification_tm(7, request_id);
213    }
214
215    #[test]
216    fn test_basic_ping_processing_using_store() {
217        let mut test_harness = Pus17HandlerWithStoreTester::new();
218        ping_test(&mut test_harness);
219    }
220
221    #[test]
222    fn test_basic_ping_processing_using_vec() {
223        let mut test_harness = Pus17HandlerWithVecTester::new();
224        ping_test(&mut test_harness);
225    }
226
227    #[test]
228    fn test_empty_tc_queue() {
229        let mut test_harness = Pus17HandlerWithStoreTester::new();
230        let result = test_harness.handle_one_tc();
231        assert!(result.is_ok());
232        let result = result.unwrap();
233        if let PusPacketHandlerResult::Empty = result {
234        } else {
235            panic!("unexpected result type {result:?}")
236        }
237    }
238
239    #[test]
240    fn test_sending_unsupported_service() {
241        let mut test_harness = Pus17HandlerWithStoreTester::new();
242        let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
243        let sec_header = PusTcSecondaryHeader::new_simple(3, 1);
244        let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
245        test_harness.send_tc(&ping_tc);
246        let result = test_harness.handle_one_tc();
247        assert!(result.is_err());
248        let error = result.unwrap_err();
249        if let PusPacketHandlingError::WrongService(num) = error {
250            assert_eq!(num, 3);
251        } else {
252            panic!("unexpected error type {error}")
253        }
254    }
255
256    #[test]
257    fn test_sending_custom_subservice() {
258        let mut test_harness = Pus17HandlerWithStoreTester::new();
259        let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
260        let sec_header = PusTcSecondaryHeader::new_simple(17, 200);
261        let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
262        test_harness.send_tc(&ping_tc);
263        let result = test_harness.handle_one_tc();
264        assert!(result.is_ok());
265        let result = result.unwrap();
266        if let PusPacketHandlerResult::CustomSubservice(subservice, _) = result {
267            assert_eq!(subservice, 200);
268        } else {
269            panic!("unexpected result type {result:?}")
270        }
271    }
272}