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
9pub 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 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 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}