satrs_core/pus/
scheduler_srv.rs

1use super::scheduler::PusSchedulerProvider;
2use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
3use crate::pool::PoolProvider;
4use crate::pus::{PusPacketHandlerResult, PusPacketHandlingError};
5use alloc::string::ToString;
6use spacepackets::ecss::{scheduling, PusPacket};
7use spacepackets::time::cds::TimeProvider;
8
9/// This is a helper class for [std] environments to handle generic PUS 11 (scheduling service)
10/// packets. This handler is able to handle the most important PUS requests for a scheduling
11/// service which provides the [PusSchedulerInterface].
12///
13/// Please note that this class does not do the regular periodic handling like releasing any
14/// telecommands inside the scheduler. The user can retrieve the wrapped scheduler via the
15/// [Self::scheduler] and [Self::scheduler_mut] function and then use the scheduler API to release
16/// telecommands when applicable.
17pub struct PusService11SchedHandler<
18    TcInMemConverter: EcssTcInMemConverter,
19    PusScheduler: PusSchedulerProvider,
20> {
21    pub service_helper: PusServiceHelper<TcInMemConverter>,
22    scheduler: PusScheduler,
23}
24
25impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
26    PusService11SchedHandler<TcInMemConverter, Scheduler>
27{
28    pub fn new(service_helper: PusServiceHelper<TcInMemConverter>, scheduler: Scheduler) -> Self {
29        Self {
30            service_helper,
31            scheduler,
32        }
33    }
34
35    pub fn scheduler_mut(&mut self) -> &mut Scheduler {
36        &mut self.scheduler
37    }
38
39    pub fn scheduler(&self) -> &Scheduler {
40        &self.scheduler
41    }
42
43    pub fn handle_one_tc(
44        &mut self,
45        sched_tc_pool: &mut (impl PoolProvider + ?Sized),
46    ) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
47        let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
48        if possible_packet.is_none() {
49            return Ok(PusPacketHandlerResult::Empty);
50        }
51        let ecss_tc_and_token = possible_packet.unwrap();
52        let tc = self
53            .service_helper
54            .tc_in_mem_converter
55            .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
56        let subservice = PusPacket::subservice(&tc);
57        let standard_subservice = scheduling::Subservice::try_from(subservice);
58        if standard_subservice.is_err() {
59            return Ok(PusPacketHandlerResult::CustomSubservice(
60                subservice,
61                ecss_tc_and_token.token,
62            ));
63        }
64        let mut partial_error = None;
65        let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
66        match standard_subservice.unwrap() {
67            scheduling::Subservice::TcEnableScheduling => {
68                let start_token = self
69                    .service_helper
70                    .common
71                    .verification_handler
72                    .get_mut()
73                    .start_success(ecss_tc_and_token.token, Some(&time_stamp))
74                    .expect("Error sending start success");
75
76                self.scheduler.enable();
77                if self.scheduler.is_enabled() {
78                    self.service_helper
79                        .common
80                        .verification_handler
81                        .get_mut()
82                        .completion_success(start_token, Some(&time_stamp))
83                        .expect("Error sending completion success");
84                } else {
85                    return Err(PusPacketHandlingError::Other(
86                        "failed to enabled scheduler".to_string(),
87                    ));
88                }
89            }
90            scheduling::Subservice::TcDisableScheduling => {
91                let start_token = self
92                    .service_helper
93                    .common
94                    .verification_handler
95                    .get_mut()
96                    .start_success(ecss_tc_and_token.token, Some(&time_stamp))
97                    .expect("Error sending start success");
98
99                self.scheduler.disable();
100                if !self.scheduler.is_enabled() {
101                    self.service_helper
102                        .common
103                        .verification_handler
104                        .get_mut()
105                        .completion_success(start_token, Some(&time_stamp))
106                        .expect("Error sending completion success");
107                } else {
108                    return Err(PusPacketHandlingError::Other(
109                        "failed to disable scheduler".to_string(),
110                    ));
111                }
112            }
113            scheduling::Subservice::TcResetScheduling => {
114                let start_token = self
115                    .service_helper
116                    .common
117                    .verification_handler
118                    .get_mut()
119                    .start_success(ecss_tc_and_token.token, Some(&time_stamp))
120                    .expect("Error sending start success");
121
122                self.scheduler
123                    .reset(sched_tc_pool)
124                    .expect("Error resetting TC Pool");
125
126                self.service_helper
127                    .common
128                    .verification_handler
129                    .get_mut()
130                    .completion_success(start_token, Some(&time_stamp))
131                    .expect("Error sending completion success");
132            }
133            scheduling::Subservice::TcInsertActivity => {
134                let start_token = self
135                    .service_helper
136                    .common
137                    .verification_handler
138                    .get_mut()
139                    .start_success(ecss_tc_and_token.token, Some(&time_stamp))
140                    .expect("error sending start success");
141
142                // let mut pool = self.sched_tc_pool.write().expect("locking pool failed");
143                self.scheduler
144                    .insert_wrapped_tc::<TimeProvider>(&tc, sched_tc_pool)
145                    .expect("insertion of activity into pool failed");
146
147                self.service_helper
148                    .common
149                    .verification_handler
150                    .get_mut()
151                    .completion_success(start_token, Some(&time_stamp))
152                    .expect("sending completion success failed");
153            }
154            _ => {
155                // Treat unhandled standard subservices as custom subservices for now.
156                return Ok(PusPacketHandlerResult::CustomSubservice(
157                    subservice,
158                    ecss_tc_and_token.token,
159                ));
160            }
161        }
162        if let Some(partial_error) = partial_error {
163            return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess(
164                partial_error,
165            ));
166        }
167        Ok(PusPacketHandlerResult::RequestHandled)
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use crate::pool::{StaticMemoryPool, StaticPoolConfig};
174    use crate::pus::tests::TEST_APID;
175    use crate::pus::{
176        scheduler::{self, PusSchedulerProvider, TcInfo},
177        tests::{PusServiceHandlerWithSharedStoreCommon, PusTestHarness},
178        verification::{RequestId, TcStateAccepted, VerificationToken},
179        EcssTcInSharedStoreConverter,
180    };
181    use alloc::collections::VecDeque;
182    use delegate::delegate;
183    use spacepackets::ecss::scheduling::Subservice;
184    use spacepackets::ecss::tc::PusTcSecondaryHeader;
185    use spacepackets::ecss::WritablePusPacket;
186    use spacepackets::time::TimeWriter;
187    use spacepackets::SpHeader;
188    use spacepackets::{
189        ecss::{tc::PusTcCreator, tm::PusTmReader},
190        time::cds,
191    };
192
193    use super::PusService11SchedHandler;
194
195    struct Pus11HandlerWithStoreTester {
196        common: PusServiceHandlerWithSharedStoreCommon,
197        handler: PusService11SchedHandler<EcssTcInSharedStoreConverter, TestScheduler>,
198        sched_tc_pool: StaticMemoryPool,
199    }
200
201    impl Pus11HandlerWithStoreTester {
202        pub fn new() -> Self {
203            let test_scheduler = TestScheduler::default();
204            let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false);
205            let sched_tc_pool = StaticMemoryPool::new(pool_cfg.clone());
206            let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new();
207            Self {
208                common,
209                handler: PusService11SchedHandler::new(srv_handler, test_scheduler),
210                sched_tc_pool,
211            }
212        }
213    }
214
215    impl PusTestHarness for Pus11HandlerWithStoreTester {
216        delegate! {
217            to self.common {
218                fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
219                fn read_next_tm(&mut self) -> PusTmReader<'_>;
220                fn check_no_tm_available(&self) -> bool;
221                fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId);
222            }
223        }
224    }
225
226    #[derive(Default)]
227    pub struct TestScheduler {
228        reset_count: u32,
229        enabled: bool,
230        enabled_count: u32,
231        disabled_count: u32,
232        inserted_tcs: VecDeque<TcInfo>,
233    }
234
235    impl PusSchedulerProvider for TestScheduler {
236        type TimeProvider = cds::TimeProvider;
237
238        fn reset(
239            &mut self,
240            _store: &mut (impl crate::pool::PoolProvider + ?Sized),
241        ) -> Result<(), crate::pool::StoreError> {
242            self.reset_count += 1;
243            Ok(())
244        }
245
246        fn is_enabled(&self) -> bool {
247            self.enabled
248        }
249
250        fn enable(&mut self) {
251            self.enabled_count += 1;
252            self.enabled = true;
253        }
254
255        fn disable(&mut self) {
256            self.disabled_count += 1;
257            self.enabled = false;
258        }
259
260        fn insert_unwrapped_and_stored_tc(
261            &mut self,
262            _time_stamp: spacepackets::time::UnixTimestamp,
263            info: crate::pus::scheduler::TcInfo,
264        ) -> Result<(), crate::pus::scheduler::ScheduleError> {
265            self.inserted_tcs.push_back(info);
266            Ok(())
267        }
268    }
269
270    fn generic_subservice_send(
271        test_harness: &mut Pus11HandlerWithStoreTester,
272        subservice: Subservice,
273    ) {
274        let mut reply_header = SpHeader::tm_unseg(TEST_APID, 0, 0).unwrap();
275        let tc_header = PusTcSecondaryHeader::new_simple(11, subservice as u8);
276        let enable_scheduling = PusTcCreator::new(&mut reply_header, tc_header, &[0; 7], true);
277        let token = test_harness.send_tc(&enable_scheduling);
278
279        let request_id = token.req_id();
280        test_harness
281            .handler
282            .handle_one_tc(&mut test_harness.sched_tc_pool)
283            .unwrap();
284        test_harness.check_next_verification_tm(1, request_id);
285        test_harness.check_next_verification_tm(3, request_id);
286        test_harness.check_next_verification_tm(7, request_id);
287    }
288
289    #[test]
290    fn test_scheduling_enabling_tc() {
291        let mut test_harness = Pus11HandlerWithStoreTester::new();
292        test_harness.handler.scheduler_mut().disable();
293        assert!(!test_harness.handler.scheduler().is_enabled());
294        generic_subservice_send(&mut test_harness, Subservice::TcEnableScheduling);
295        assert!(test_harness.handler.scheduler().is_enabled());
296        assert_eq!(test_harness.handler.scheduler().enabled_count, 1);
297    }
298
299    #[test]
300    fn test_scheduling_disabling_tc() {
301        let mut test_harness = Pus11HandlerWithStoreTester::new();
302        test_harness.handler.scheduler_mut().enable();
303        assert!(test_harness.handler.scheduler().is_enabled());
304        generic_subservice_send(&mut test_harness, Subservice::TcDisableScheduling);
305        assert!(!test_harness.handler.scheduler().is_enabled());
306        assert_eq!(test_harness.handler.scheduler().disabled_count, 1);
307    }
308
309    #[test]
310    fn test_reset_scheduler_tc() {
311        let mut test_harness = Pus11HandlerWithStoreTester::new();
312        generic_subservice_send(&mut test_harness, Subservice::TcResetScheduling);
313        assert_eq!(test_harness.handler.scheduler().reset_count, 1);
314    }
315
316    #[test]
317    fn test_insert_activity_tc() {
318        let mut test_harness = Pus11HandlerWithStoreTester::new();
319        let mut reply_header = SpHeader::tm_unseg(TEST_APID, 0, 0).unwrap();
320        let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
321        let ping_tc = PusTcCreator::new(&mut reply_header, sec_header, &[], true);
322        let req_id_ping_tc = scheduler::RequestId::from_tc(&ping_tc);
323        let stamper = cds::TimeProvider::from_now_with_u16_days().expect("time provider failed");
324        let mut sched_app_data: [u8; 64] = [0; 64];
325        let mut written_len = stamper.write_to_bytes(&mut sched_app_data).unwrap();
326        let ping_raw = ping_tc.to_vec().expect("generating raw tc failed");
327        sched_app_data[written_len..written_len + ping_raw.len()].copy_from_slice(&ping_raw);
328        written_len += ping_raw.len();
329        reply_header = SpHeader::tm_unseg(TEST_APID, 1, 0).unwrap();
330        sec_header = PusTcSecondaryHeader::new_simple(11, Subservice::TcInsertActivity as u8);
331        let enable_scheduling = PusTcCreator::new(
332            &mut reply_header,
333            sec_header,
334            &sched_app_data[..written_len],
335            true,
336        );
337        let token = test_harness.send_tc(&enable_scheduling);
338
339        let request_id = token.req_id();
340        test_harness
341            .handler
342            .handle_one_tc(&mut test_harness.sched_tc_pool)
343            .unwrap();
344        test_harness.check_next_verification_tm(1, request_id);
345        test_harness.check_next_verification_tm(3, request_id);
346        test_harness.check_next_verification_tm(7, request_id);
347        let tc_info = test_harness
348            .handler
349            .scheduler_mut()
350            .inserted_tcs
351            .pop_front()
352            .unwrap();
353        assert_eq!(tc_info.request_id(), req_id_ping_tc);
354    }
355}