1use core::fmt::{Debug, Display, Formatter};
6use core::time::Duration;
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9use spacepackets::ecss::scheduling::TimeWindowType;
10use spacepackets::ecss::tc::{GenericPusTcSecondaryHeader, IsPusTelecommand, PusTcReader};
11use spacepackets::ecss::{PusError, PusPacket, WritablePusPacket};
12use spacepackets::time::{
13 CcsdsTimeProvider, TimeReader, TimeWriter, TimestampError, UnixTimestamp,
14};
15use spacepackets::{ByteConversionError, CcsdsPacket};
16#[cfg(feature = "std")]
17use std::error::Error;
18
19use crate::pool::{PoolProvider, StoreError};
20#[cfg(feature = "alloc")]
21pub use alloc_mod::*;
22
23#[derive(Debug, Copy, Clone, PartialEq, Eq)]
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29pub struct RequestId {
30 pub(crate) source_id: u16,
31 pub(crate) apid: u16,
32 pub(crate) seq_count: u16,
33}
34
35impl RequestId {
36 pub fn source_id(&self) -> u16 {
37 self.source_id
38 }
39
40 pub fn apid(&self) -> u16 {
41 self.apid
42 }
43
44 pub fn seq_count(&self) -> u16 {
45 self.seq_count
46 }
47
48 pub fn from_tc(
49 tc: &(impl CcsdsPacket + GenericPusTcSecondaryHeader + IsPusTelecommand),
50 ) -> Self {
51 RequestId {
52 source_id: tc.source_id(),
53 apid: tc.apid(),
54 seq_count: tc.seq_count(),
55 }
56 }
57
58 pub fn as_u64(&self) -> u64 {
59 ((self.source_id as u64) << 32) | ((self.apid as u64) << 16) | self.seq_count as u64
60 }
61}
62
63pub type AddrInStore = u64;
64
65#[derive(Debug, Copy, Clone, PartialEq, Eq)]
68#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
69pub struct TcInfo {
70 addr: AddrInStore,
71 request_id: RequestId,
72}
73
74impl TcInfo {
75 pub fn addr(&self) -> AddrInStore {
76 self.addr
77 }
78
79 pub fn request_id(&self) -> RequestId {
80 self.request_id
81 }
82
83 pub fn new(addr: u64, request_id: RequestId) -> Self {
84 TcInfo { addr, request_id }
85 }
86}
87
88pub struct TimeWindow<TimeProvder> {
89 time_window_type: TimeWindowType,
90 start_time: Option<TimeProvder>,
91 end_time: Option<TimeProvder>,
92}
93
94impl<TimeProvider> TimeWindow<TimeProvider> {
95 pub fn new_select_all() -> Self {
96 Self {
97 time_window_type: TimeWindowType::SelectAll,
98 start_time: None,
99 end_time: None,
100 }
101 }
102
103 pub fn time_window_type(&self) -> TimeWindowType {
104 self.time_window_type
105 }
106
107 pub fn start_time(&self) -> Option<&TimeProvider> {
108 self.start_time.as_ref()
109 }
110
111 pub fn end_time(&self) -> Option<&TimeProvider> {
112 self.end_time.as_ref()
113 }
114}
115
116impl<TimeProvider: CcsdsTimeProvider + Clone> TimeWindow<TimeProvider> {
117 pub fn new_from_time_to_time(start_time: &TimeProvider, end_time: &TimeProvider) -> Self {
118 Self {
119 time_window_type: TimeWindowType::TimeTagToTimeTag,
120 start_time: Some(start_time.clone()),
121 end_time: Some(end_time.clone()),
122 }
123 }
124
125 pub fn new_from_time(start_time: &TimeProvider) -> Self {
126 Self {
127 time_window_type: TimeWindowType::FromTimeTag,
128 start_time: Some(start_time.clone()),
129 end_time: None,
130 }
131 }
132
133 pub fn new_to_time(end_time: &TimeProvider) -> Self {
134 Self {
135 time_window_type: TimeWindowType::ToTimeTag,
136 start_time: None,
137 end_time: Some(end_time.clone()),
138 }
139 }
140}
141
142#[derive(Debug, Clone, PartialEq, Eq)]
143#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
144pub enum ScheduleError {
145 PusError(PusError),
146 ReleaseTimeInTimeMargin {
150 current_time: UnixTimestamp,
151 time_margin: Duration,
152 release_time: UnixTimestamp,
153 },
154 NestedScheduledTc,
156 StoreError(StoreError),
157 TcDataEmpty,
158 TimestampError(TimestampError),
159 WrongSubservice(u8),
160 WrongService(u8),
161 ByteConversionError(ByteConversionError),
162}
163
164impl Display for ScheduleError {
165 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
166 match self {
167 ScheduleError::PusError(e) => {
168 write!(f, "Pus Error: {e}")
169 }
170 ScheduleError::ReleaseTimeInTimeMargin {
171 current_time,
172 time_margin,
173 release_time,
174 } => {
175 write!(
176 f,
177 "time margin too short, current time: {current_time:?}, time margin: {time_margin:?}, release time: {release_time:?}"
178 )
179 }
180 ScheduleError::NestedScheduledTc => {
181 write!(f, "nested scheduling is not allowed")
182 }
183 ScheduleError::StoreError(e) => {
184 write!(f, "pus scheduling: {e}")
185 }
186 ScheduleError::TcDataEmpty => {
187 write!(f, "empty TC data field")
188 }
189 ScheduleError::TimestampError(e) => {
190 write!(f, "pus scheduling: {e}")
191 }
192 ScheduleError::WrongService(srv) => {
193 write!(f, "pus scheduling: wrong service number {srv}")
194 }
195 ScheduleError::WrongSubservice(subsrv) => {
196 write!(f, "pus scheduling: wrong subservice number {subsrv}")
197 }
198 ScheduleError::ByteConversionError(e) => {
199 write!(f, "pus scheduling: {e}")
200 }
201 }
202 }
203}
204
205impl From<PusError> for ScheduleError {
206 fn from(e: PusError) -> Self {
207 Self::PusError(e)
208 }
209}
210
211impl From<StoreError> for ScheduleError {
212 fn from(e: StoreError) -> Self {
213 Self::StoreError(e)
214 }
215}
216
217impl From<TimestampError> for ScheduleError {
218 fn from(e: TimestampError) -> Self {
219 Self::TimestampError(e)
220 }
221}
222impl From<ByteConversionError> for ScheduleError {
223 fn from(e: ByteConversionError) -> Self {
224 Self::ByteConversionError(e)
225 }
226}
227
228#[cfg(feature = "std")]
229impl Error for ScheduleError {
230 fn source(&self) -> Option<&(dyn Error + 'static)> {
231 match self {
232 ScheduleError::PusError(e) => Some(e),
233 ScheduleError::StoreError(e) => Some(e),
234 ScheduleError::TimestampError(e) => Some(e),
235 ScheduleError::ByteConversionError(e) => Some(e),
236 _ => None,
237 }
238 }
239}
240
241pub trait PusSchedulerProvider {
243 type TimeProvider: CcsdsTimeProvider + TimeReader;
244
245 fn reset(&mut self, store: &mut (impl PoolProvider + ?Sized)) -> Result<(), StoreError>;
246
247 fn is_enabled(&self) -> bool;
248
249 fn enable(&mut self);
250
251 fn disable(&mut self);
254
255 fn insert_unwrapped_and_stored_tc(
258 &mut self,
259 time_stamp: UnixTimestamp,
260 info: TcInfo,
261 ) -> Result<(), ScheduleError>;
262
263 fn insert_wrapped_tc<TimeProvider>(
266 &mut self,
267 pus_tc: &(impl IsPusTelecommand + PusPacket + GenericPusTcSecondaryHeader),
268 pool: &mut (impl PoolProvider + ?Sized),
269 ) -> Result<TcInfo, ScheduleError> {
270 if PusPacket::service(pus_tc) != 11 {
271 return Err(ScheduleError::WrongService(PusPacket::service(pus_tc)));
272 }
273 if PusPacket::subservice(pus_tc) != 4 {
274 return Err(ScheduleError::WrongSubservice(PusPacket::subservice(
275 pus_tc,
276 )));
277 }
278 if pus_tc.user_data().is_empty() {
279 return Err(ScheduleError::TcDataEmpty);
280 }
281 let user_data = pus_tc.user_data();
282 let stamp: Self::TimeProvider = TimeReader::from_bytes(user_data)?;
283 let unix_stamp = stamp.unix_stamp();
284 let stamp_len = stamp.len_as_bytes();
285 self.insert_unwrapped_tc(unix_stamp, &user_data[stamp_len..], pool)
286 }
287
288 fn insert_unwrapped_tc(
291 &mut self,
292 time_stamp: UnixTimestamp,
293 tc: &[u8],
294 pool: &mut (impl PoolProvider + ?Sized),
295 ) -> Result<TcInfo, ScheduleError> {
296 let check_tc = PusTcReader::new(tc)?;
297 if PusPacket::service(&check_tc.0) == 11 && PusPacket::subservice(&check_tc.0) == 4 {
298 return Err(ScheduleError::NestedScheduledTc);
299 }
300 let req_id = RequestId::from_tc(&check_tc.0);
301
302 match pool.add(tc) {
303 Ok(addr) => {
304 let info = TcInfo::new(addr, req_id);
305 self.insert_unwrapped_and_stored_tc(time_stamp, info)?;
306 Ok(info)
307 }
308 Err(err) => Err(err.into()),
309 }
310 }
311}
312
313pub fn generate_insert_telecommand_app_data(
318 buf: &mut [u8],
319 release_time: &impl TimeWriter,
320 request: &impl WritablePusPacket,
321) -> Result<usize, ScheduleError> {
322 let required_len = 2 + release_time.len_written() + request.len_written();
323 if required_len > buf.len() {
324 return Err(ByteConversionError::ToSliceTooSmall {
325 found: buf.len(),
326 expected: required_len,
327 }
328 .into());
329 }
330 let mut current_len = 0;
331 let n = 1_u16;
332 buf[current_len..current_len + 2].copy_from_slice(&n.to_be_bytes());
333 current_len += 2;
334 current_len += release_time
335 .write_to_bytes(&mut buf[current_len..current_len + release_time.len_written()])?;
336 current_len +=
337 request.write_to_bytes(&mut buf[current_len..current_len + request.len_written()])?;
338 Ok(current_len)
339}
340
341#[cfg(feature = "alloc")]
342pub mod alloc_mod {
343 use super::*;
344 use crate::pool::{PoolProvider, StoreAddr, StoreError};
345 use alloc::collections::btree_map::{Entry, Range};
346 use alloc::collections::BTreeMap;
347 use alloc::vec;
348 use alloc::vec::Vec;
349 use core::time::Duration;
350 use spacepackets::ecss::scheduling::TimeWindowType;
351 use spacepackets::ecss::tc::{PusTc, PusTcReader};
352 use spacepackets::ecss::PusPacket;
353 use spacepackets::time::cds::DaysLen24Bits;
354 use spacepackets::time::{cds, CcsdsTimeProvider, UnixTimestamp};
355
356 #[cfg(feature = "std")]
357 use std::time::SystemTimeError;
358
359 pub fn generate_insert_telecommand_app_data_as_vec(
362 release_time: &impl TimeWriter,
363 request: &impl WritablePusPacket,
364 ) -> Result<alloc::vec::Vec<u8>, ScheduleError> {
365 let mut vec = alloc::vec::Vec::new();
366 vec.extend_from_slice(&1_u16.to_be_bytes());
367 vec.append(&mut release_time.to_vec()?);
368 vec.append(&mut request.to_vec()?);
369 Ok(vec)
370 }
371
372 enum DeletionResult {
373 WithoutStoreDeletion(Option<StoreAddr>),
374 WithStoreDeletion(Result<bool, StoreError>),
375 }
376
377 #[derive(Debug)]
399 pub struct PusScheduler {
400 tc_map: BTreeMap<UnixTimestamp, Vec<TcInfo>>,
401 pub(crate) current_time: UnixTimestamp,
402 time_margin: Duration,
403 enabled: bool,
404 }
405 impl PusScheduler {
406 pub fn new(init_current_time: UnixTimestamp, time_margin: Duration) -> Self {
417 PusScheduler {
418 tc_map: Default::default(),
419 current_time: init_current_time,
420 time_margin,
421 enabled: true,
422 }
423 }
424
425 #[cfg(feature = "std")]
427 #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
428 pub fn new_with_current_init_time(time_margin: Duration) -> Result<Self, SystemTimeError> {
429 Ok(Self::new(UnixTimestamp::from_now()?, time_margin))
430 }
431
432 pub fn num_scheduled_telecommands(&self) -> u64 {
433 let mut num_entries = 0;
434 for entries in &self.tc_map {
435 num_entries += entries.1.len() as u64;
436 }
437 num_entries
438 }
439
440 pub fn update_time(&mut self, current_time: UnixTimestamp) {
441 self.current_time = current_time;
442 }
443
444 pub fn current_time(&self) -> &UnixTimestamp {
445 &self.current_time
446 }
447
448 pub fn insert_unwrapped_and_stored_tc(
451 &mut self,
452 time_stamp: UnixTimestamp,
453 info: TcInfo,
454 ) -> Result<(), ScheduleError> {
455 if time_stamp < self.current_time + self.time_margin {
456 return Err(ScheduleError::ReleaseTimeInTimeMargin {
457 current_time: self.current_time,
458 time_margin: self.time_margin,
459 release_time: time_stamp,
460 });
461 }
462 match self.tc_map.entry(time_stamp) {
463 Entry::Vacant(e) => {
464 e.insert(vec![info]);
465 }
466 Entry::Occupied(mut v) => {
467 v.get_mut().push(info);
468 }
469 }
470 Ok(())
471 }
472
473 pub fn insert_unwrapped_tc(
476 &mut self,
477 time_stamp: UnixTimestamp,
478 tc: &[u8],
479 pool: &mut (impl PoolProvider + ?Sized),
480 ) -> Result<TcInfo, ScheduleError> {
481 let check_tc = PusTcReader::new(tc)?;
482 if PusPacket::service(&check_tc.0) == 11 && PusPacket::subservice(&check_tc.0) == 4 {
483 return Err(ScheduleError::NestedScheduledTc);
484 }
485 let req_id = RequestId::from_tc(&check_tc.0);
486
487 match pool.add(tc) {
488 Ok(addr) => {
489 let info = TcInfo::new(addr, req_id);
490 self.insert_unwrapped_and_stored_tc(time_stamp, info)?;
491 Ok(info)
492 }
493 Err(err) => Err(err.into()),
494 }
495 }
496
497 pub fn insert_wrapped_tc_cds_short(
500 &mut self,
501 pus_tc: &PusTc,
502 pool: &mut (impl PoolProvider + ?Sized),
503 ) -> Result<TcInfo, ScheduleError> {
504 self.insert_wrapped_tc::<cds::TimeProvider>(pus_tc, pool)
505 }
506
507 pub fn insert_wrapped_tc_cds_long(
510 &mut self,
511 pus_tc: &PusTc,
512 pool: &mut (impl PoolProvider + ?Sized),
513 ) -> Result<TcInfo, ScheduleError> {
514 self.insert_wrapped_tc::<cds::TimeProvider<DaysLen24Bits>>(pus_tc, pool)
515 }
516
517 pub fn delete_by_time_filter<TimeProvider: CcsdsTimeProvider + Clone>(
526 &mut self,
527 time_window: TimeWindow<TimeProvider>,
528 pool: &mut (impl PoolProvider + ?Sized),
529 ) -> Result<u64, (u64, StoreError)> {
530 let range = self.retrieve_by_time_filter(time_window);
531 let mut del_packets = 0;
532 let mut res_if_fails = None;
533 let mut keys_to_delete = Vec::new();
534 for time_bucket in range {
535 for tc in time_bucket.1 {
536 match pool.delete(tc.addr) {
537 Ok(_) => del_packets += 1,
538 Err(e) => res_if_fails = Some(e),
539 }
540 }
541 keys_to_delete.push(*time_bucket.0);
542 }
543 for key in keys_to_delete {
544 self.tc_map.remove(&key);
545 }
546 if let Some(err) = res_if_fails {
547 return Err((del_packets, err));
548 }
549 Ok(del_packets)
550 }
551
552 pub fn delete_all(
557 &mut self,
558 pool: &mut (impl PoolProvider + ?Sized),
559 ) -> Result<u64, (u64, StoreError)> {
560 self.delete_by_time_filter(TimeWindow::<cds::TimeProvider>::new_select_all(), pool)
561 }
562
563 pub fn retrieve_all(&mut self) -> Range<'_, UnixTimestamp, Vec<TcInfo>> {
565 self.tc_map.range(..)
566 }
567
568 pub fn retrieve_by_time_filter<TimeProvider: CcsdsTimeProvider>(
573 &mut self,
574 time_window: TimeWindow<TimeProvider>,
575 ) -> Range<'_, UnixTimestamp, Vec<TcInfo>> {
576 match time_window.time_window_type() {
577 TimeWindowType::SelectAll => self.tc_map.range(..),
578 TimeWindowType::TimeTagToTimeTag => {
579 let start_time = time_window.start_time().unwrap().unix_stamp();
581 let end_time = time_window.end_time().unwrap().unix_stamp();
582 self.tc_map.range(start_time..=end_time)
583 }
584 TimeWindowType::FromTimeTag => {
585 let start_time = time_window.start_time().unwrap().unix_stamp();
587 self.tc_map.range(start_time..)
588 }
589 TimeWindowType::ToTimeTag => {
590 let end_time = time_window.end_time().unwrap().unix_stamp();
592 self.tc_map.range(..=end_time)
593 }
594 }
595 }
596
597 pub fn delete_by_request_id(&mut self, req_id: &RequestId) -> Option<StoreAddr> {
604 if let DeletionResult::WithoutStoreDeletion(v) =
605 self.delete_by_request_id_internal_without_store_deletion(req_id)
606 {
607 return v;
608 }
609 panic!("unexpected deletion result");
610 }
611
612 pub fn delete_by_request_id_and_from_pool(
614 &mut self,
615 req_id: &RequestId,
616 pool: &mut (impl PoolProvider + ?Sized),
617 ) -> Result<bool, StoreError> {
618 if let DeletionResult::WithStoreDeletion(v) =
619 self.delete_by_request_id_internal_with_store_deletion(req_id, pool)
620 {
621 return v;
622 }
623 panic!("unexpected deletion result");
624 }
625
626 fn delete_by_request_id_internal_without_store_deletion(
627 &mut self,
628 req_id: &RequestId,
629 ) -> DeletionResult {
630 let mut idx_found = None;
631 for time_bucket in &mut self.tc_map {
632 for (idx, tc_info) in time_bucket.1.iter().enumerate() {
633 if &tc_info.request_id == req_id {
634 idx_found = Some(idx);
635 }
636 }
637 if let Some(idx) = idx_found {
638 let addr = time_bucket.1.remove(idx).addr;
639 return DeletionResult::WithoutStoreDeletion(Some(addr));
640 }
641 }
642 DeletionResult::WithoutStoreDeletion(None)
643 }
644
645 fn delete_by_request_id_internal_with_store_deletion(
646 &mut self,
647 req_id: &RequestId,
648 pool: &mut (impl PoolProvider + ?Sized),
649 ) -> DeletionResult {
650 let mut idx_found = None;
651 for time_bucket in &mut self.tc_map {
652 for (idx, tc_info) in time_bucket.1.iter().enumerate() {
653 if &tc_info.request_id == req_id {
654 idx_found = Some(idx);
655 }
656 }
657 if let Some(idx) = idx_found {
658 let addr = time_bucket.1.remove(idx).addr;
659 return match pool.delete(addr) {
660 Ok(_) => DeletionResult::WithStoreDeletion(Ok(true)),
661 Err(e) => DeletionResult::WithStoreDeletion(Err(e)),
662 };
663 }
664 }
665 DeletionResult::WithStoreDeletion(Ok(false))
666 }
667
668 #[cfg(feature = "std")]
669 #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
670 pub fn update_time_from_now(&mut self) -> Result<(), SystemTimeError> {
671 self.current_time = UnixTimestamp::from_now()?;
672 Ok(())
673 }
674
675 pub fn release_telecommands_with_buffer<R: FnMut(bool, &TcInfo, &[u8]) -> bool>(
691 &mut self,
692 releaser: R,
693 tc_store: &mut (impl PoolProvider + ?Sized),
694 tc_buf: &mut [u8],
695 ) -> Result<u64, (u64, StoreError)> {
696 self.release_telecommands_internal(releaser, tc_store, Some(tc_buf))
697 }
698
699 pub fn release_telecommands<R: FnMut(bool, &TcInfo, &[u8]) -> bool>(
706 &mut self,
707 releaser: R,
708 tc_store: &mut (impl PoolProvider + ?Sized),
709 ) -> Result<u64, (u64, StoreError)> {
710 self.release_telecommands_internal(releaser, tc_store, None)
711 }
712
713 fn release_telecommands_internal<R: FnMut(bool, &TcInfo, &[u8]) -> bool>(
714 &mut self,
715 mut releaser: R,
716 tc_store: &mut (impl PoolProvider + ?Sized),
717 mut tc_buf: Option<&mut [u8]>,
718 ) -> Result<u64, (u64, StoreError)> {
719 let tcs_to_release = self.telecommands_to_release();
720 let mut released_tcs = 0;
721 let mut store_error = Ok(());
722 for tc in tcs_to_release {
723 for info in tc.1 {
724 let should_delete = match tc_buf.as_mut() {
725 Some(buf) => {
726 tc_store
727 .read(&info.addr, buf)
728 .map_err(|e| (released_tcs, e))?;
729 releaser(self.enabled, info, buf)
730 }
731 None => {
732 let tc = tc_store
733 .read_as_vec(&info.addr)
734 .map_err(|e| (released_tcs, e))?;
735 releaser(self.enabled, info, &tc)
736 }
737 };
738 released_tcs += 1;
739 if should_delete {
740 let res = tc_store.delete(info.addr);
741 if res.is_err() {
742 store_error = res;
743 }
744 }
745 }
746 }
747 self.tc_map.retain(|k, _| k > &self.current_time);
748 store_error
749 .map(|_| released_tcs)
750 .map_err(|e| (released_tcs, e))
751 }
752
753 pub fn release_telecommands_no_deletion<R: FnMut(bool, &TcInfo, &[u8])>(
760 &mut self,
761 mut releaser: R,
762 tc_store: &(impl PoolProvider + ?Sized),
763 tc_buf: &mut [u8],
764 ) -> Result<Vec<TcInfo>, (Vec<TcInfo>, StoreError)> {
765 let tcs_to_release = self.telecommands_to_release();
766 let mut released_tcs = Vec::new();
767 for tc in tcs_to_release {
768 for info in tc.1 {
769 tc_store
770 .read(&info.addr, tc_buf)
771 .map_err(|e| (released_tcs.clone(), e))?;
772 releaser(self.is_enabled(), info, tc_buf);
773 released_tcs.push(*info);
774 }
775 }
776 self.tc_map.retain(|k, _| k > &self.current_time);
777 Ok(released_tcs)
778 }
779
780 pub fn telecommands_to_release(&self) -> Range<'_, UnixTimestamp, Vec<TcInfo>> {
782 self.tc_map.range(..=self.current_time)
783 }
784 }
785
786 impl PusSchedulerProvider for PusScheduler {
787 type TimeProvider = cds::TimeProvider;
788
789 fn reset(&mut self, store: &mut (impl PoolProvider + ?Sized)) -> Result<(), StoreError> {
796 self.enabled = false;
797 let mut deletion_ok = Ok(());
798 for tc_lists in &mut self.tc_map {
799 for tc in tc_lists.1 {
800 let res = store.delete(tc.addr);
801 if res.is_err() {
802 deletion_ok = res;
803 }
804 }
805 }
806 self.tc_map.clear();
807 deletion_ok
808 }
809
810 fn is_enabled(&self) -> bool {
811 self.enabled
812 }
813
814 fn enable(&mut self) {
815 self.enabled = true;
816 }
817
818 fn disable(&mut self) {
821 self.enabled = false;
822 }
823
824 fn insert_unwrapped_and_stored_tc(
825 &mut self,
826 time_stamp: UnixTimestamp,
827 info: TcInfo,
828 ) -> Result<(), ScheduleError> {
829 if time_stamp < self.current_time + self.time_margin {
830 return Err(ScheduleError::ReleaseTimeInTimeMargin {
831 current_time: self.current_time,
832 time_margin: self.time_margin,
833 release_time: time_stamp,
834 });
835 }
836 match self.tc_map.entry(time_stamp) {
837 Entry::Vacant(e) => {
838 e.insert(vec![info]);
839 }
840 Entry::Occupied(mut v) => {
841 v.get_mut().push(info);
842 }
843 }
844 Ok(())
845 }
846 }
847}
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852 use crate::pool::{
853 PoolProvider, StaticMemoryPool, StaticPoolAddr, StaticPoolConfig, StoreAddr, StoreError,
854 };
855 use alloc::collections::btree_map::Range;
856 use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
857 use spacepackets::ecss::WritablePusPacket;
858 use spacepackets::time::{cds, TimeWriter, UnixTimestamp};
859 use spacepackets::{PacketId, PacketSequenceCtrl, PacketType, SequenceFlags, SpHeader};
860 use std::time::Duration;
861 use std::vec::Vec;
862 #[allow(unused_imports)]
863 use std::{println, vec};
864
865 fn pus_tc_base(timestamp: UnixTimestamp, buf: &mut [u8]) -> (SpHeader, usize) {
866 let cds_time = cds::TimeProvider::from_unix_secs_with_u16_days(×tamp).unwrap();
867 let len_time_stamp = cds_time.write_to_bytes(buf).unwrap();
868 let len_packet = base_ping_tc_simple_ctor(0, None)
869 .write_to_bytes(&mut buf[len_time_stamp..])
870 .unwrap();
871 (
872 SpHeader::tc_unseg(0x02, 0x34, len_packet as u16).unwrap(),
873 len_packet + len_time_stamp,
874 )
875 }
876
877 fn scheduled_tc(timestamp: UnixTimestamp, buf: &mut [u8]) -> PusTcCreator {
878 let (mut sph, len_app_data) = pus_tc_base(timestamp, buf);
879 PusTcCreator::new_simple(&mut sph, 11, 4, Some(&buf[..len_app_data]), true)
880 }
881
882 fn wrong_tc_service(timestamp: UnixTimestamp, buf: &mut [u8]) -> PusTcCreator {
883 let (mut sph, len_app_data) = pus_tc_base(timestamp, buf);
884 PusTcCreator::new_simple(&mut sph, 12, 4, Some(&buf[..len_app_data]), true)
885 }
886
887 fn wrong_tc_subservice(timestamp: UnixTimestamp, buf: &mut [u8]) -> PusTcCreator {
888 let (mut sph, len_app_data) = pus_tc_base(timestamp, buf);
889 PusTcCreator::new_simple(&mut sph, 11, 5, Some(&buf[..len_app_data]), true)
890 }
891
892 fn double_wrapped_time_tagged_tc(timestamp: UnixTimestamp, buf: &mut [u8]) -> PusTcCreator {
893 let cds_time = cds::TimeProvider::from_unix_secs_with_u16_days(×tamp).unwrap();
894 let len_time_stamp = cds_time.write_to_bytes(buf).unwrap();
895 let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap();
896 let inner_time_tagged_tc = PusTcCreator::new_simple(&mut sph, 11, 4, None, true);
899 let packet_len = inner_time_tagged_tc
900 .write_to_bytes(&mut buf[len_time_stamp..])
901 .expect("writing inner time tagged tc failed");
902 PusTcCreator::new_simple(
903 &mut sph,
904 11,
905 4,
906 Some(&buf[..len_time_stamp + packet_len]),
907 true,
908 )
909 }
910
911 fn invalid_time_tagged_cmd() -> PusTcCreator<'static> {
912 let mut sph = SpHeader::tc_unseg(0x02, 0x34, 1).unwrap();
913 PusTcCreator::new_simple(&mut sph, 11, 4, None, true)
914 }
915
916 fn base_ping_tc_simple_ctor(
917 seq_count: u16,
918 app_data: Option<&'static [u8]>,
919 ) -> PusTcCreator<'static> {
920 let mut sph = SpHeader::tc_unseg(0x02, seq_count, 0).unwrap();
921 PusTcCreator::new_simple(&mut sph, 17, 1, app_data, true)
922 }
923
924 fn ping_tc_to_store(
925 pool: &mut StaticMemoryPool,
926 buf: &mut [u8],
927 seq_count: u16,
928 app_data: Option<&'static [u8]>,
929 ) -> TcInfo {
930 let ping_tc = base_ping_tc_simple_ctor(seq_count, app_data);
931 let ping_size = ping_tc.write_to_bytes(buf).expect("writing ping TC failed");
932 let first_addr = pool.add(&buf[0..ping_size]).unwrap();
933 TcInfo::new(first_addr, RequestId::from_tc(&ping_tc))
934 }
935
936 #[test]
937 fn test_enable_api() {
938 let mut scheduler =
939 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
940 assert!(scheduler.is_enabled());
941 scheduler.disable();
942 assert!(!scheduler.is_enabled());
943 scheduler.enable();
944 assert!(scheduler.is_enabled());
945 }
946
947 #[test]
948 fn test_reset() {
949 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
950 let mut scheduler =
951 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
952
953 let mut buf: [u8; 32] = [0; 32];
954 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
955
956 scheduler
957 .insert_unwrapped_and_stored_tc(
958 UnixTimestamp::new_only_seconds(100),
959 TcInfo::new(tc_info_0.addr, tc_info_0.request_id),
960 )
961 .unwrap();
962
963 let app_data = &[0, 1, 2];
964 let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, Some(app_data));
965 scheduler
966 .insert_unwrapped_and_stored_tc(
967 UnixTimestamp::new_only_seconds(200),
968 TcInfo::new(tc_info_1.addr, tc_info_1.request_id),
969 )
970 .unwrap();
971
972 let app_data = &[0, 1, 2];
973 let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, Some(app_data));
974 scheduler
975 .insert_unwrapped_and_stored_tc(
976 UnixTimestamp::new_only_seconds(300),
977 TcInfo::new(tc_info_2.addr(), tc_info_2.request_id()),
978 )
979 .unwrap();
980
981 assert_eq!(scheduler.num_scheduled_telecommands(), 3);
982 assert!(scheduler.is_enabled());
983 scheduler.reset(&mut pool).expect("deletion of TCs failed");
984 assert!(!scheduler.is_enabled());
985 assert_eq!(scheduler.num_scheduled_telecommands(), 0);
986 assert!(!pool.has_element_at(&tc_info_0.addr()).unwrap());
987 assert!(!pool.has_element_at(&tc_info_1.addr()).unwrap());
988 assert!(!pool.has_element_at(&tc_info_2.addr()).unwrap());
989 }
990
991 #[test]
992 fn insert_multi_with_same_time() {
993 let mut scheduler =
994 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
995
996 scheduler
997 .insert_unwrapped_and_stored_tc(
998 UnixTimestamp::new_only_seconds(100),
999 TcInfo::new(
1000 StoreAddr::from(StaticPoolAddr {
1001 pool_idx: 0,
1002 packet_idx: 1,
1003 }),
1004 RequestId {
1005 seq_count: 1,
1006 apid: 0,
1007 source_id: 0,
1008 },
1009 ),
1010 )
1011 .unwrap();
1012
1013 scheduler
1014 .insert_unwrapped_and_stored_tc(
1015 UnixTimestamp::new_only_seconds(100),
1016 TcInfo::new(
1017 StoreAddr::from(StaticPoolAddr {
1018 pool_idx: 0,
1019 packet_idx: 2,
1020 }),
1021 RequestId {
1022 seq_count: 2,
1023 apid: 1,
1024 source_id: 5,
1025 },
1026 ),
1027 )
1028 .unwrap();
1029
1030 scheduler
1031 .insert_unwrapped_and_stored_tc(
1032 UnixTimestamp::new_only_seconds(300),
1033 TcInfo::new(
1034 StaticPoolAddr {
1035 pool_idx: 0,
1036 packet_idx: 2,
1037 }
1038 .into(),
1039 RequestId {
1040 source_id: 10,
1041 seq_count: 20,
1042 apid: 23,
1043 },
1044 ),
1045 )
1046 .unwrap();
1047
1048 assert_eq!(scheduler.num_scheduled_telecommands(), 3);
1049 }
1050
1051 #[test]
1052 fn test_time_update() {
1053 let mut scheduler =
1054 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1055 let time = UnixTimestamp::new(1, 2).unwrap();
1056 scheduler.update_time(time);
1057 assert_eq!(scheduler.current_time(), &time);
1058 }
1059
1060 fn common_check(
1061 enabled: bool,
1062 store_addr: &StoreAddr,
1063 expected_store_addrs: Vec<StoreAddr>,
1064 counter: &mut usize,
1065 ) {
1066 assert!(enabled);
1067 assert!(expected_store_addrs.contains(store_addr));
1068 *counter += 1;
1069 }
1070 fn common_check_disabled(
1071 enabled: bool,
1072 store_addr: &StoreAddr,
1073 expected_store_addrs: Vec<StoreAddr>,
1074 counter: &mut usize,
1075 ) {
1076 assert!(!enabled);
1077 assert!(expected_store_addrs.contains(store_addr));
1078 *counter += 1;
1079 }
1080
1081 #[test]
1082 fn test_request_id() {
1083 let src_id_to_set = 12;
1084 let apid_to_set = 0x22;
1085 let seq_count = 105;
1086 let mut sp_header = SpHeader::tc_unseg(apid_to_set, 105, 0).unwrap();
1087 let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
1088 sec_header.source_id = src_id_to_set;
1089 let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
1090 let req_id = RequestId::from_tc(&ping_tc);
1091 assert_eq!(req_id.source_id(), src_id_to_set);
1092 assert_eq!(req_id.apid(), apid_to_set);
1093 assert_eq!(req_id.seq_count(), seq_count);
1094 assert_eq!(
1095 req_id.as_u64(),
1096 ((src_id_to_set as u64) << 32) | (apid_to_set as u64) << 16 | seq_count as u64
1097 );
1098 }
1099 #[test]
1100 fn test_release_telecommands() {
1101 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1102 let mut scheduler =
1103 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1104
1105 let mut buf: [u8; 32] = [0; 32];
1106 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
1107
1108 scheduler
1109 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0)
1110 .expect("insertion failed");
1111
1112 let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None);
1113 scheduler
1114 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(200), tc_info_1)
1115 .expect("insertion failed");
1116
1117 let mut i = 0;
1118 let mut test_closure_1 = |boolvar: bool, tc_info: &TcInfo, _tc: &[u8]| {
1119 common_check(boolvar, &tc_info.addr, vec![tc_info_0.addr()], &mut i);
1120 true
1121 };
1122
1123 scheduler.update_time(UnixTimestamp::new_only_seconds(99));
1125
1126 let mut tc_buf: [u8; 128] = [0; 128];
1127 scheduler
1128 .release_telecommands_with_buffer(&mut test_closure_1, &mut pool, &mut tc_buf)
1129 .expect("deletion failed");
1130
1131 scheduler.update_time(UnixTimestamp::new_only_seconds(100));
1133
1134 let mut released = scheduler
1135 .release_telecommands(&mut test_closure_1, &mut pool)
1136 .expect("deletion failed");
1137 assert_eq!(released, 1);
1138 assert!(!pool.has_element_at(&tc_info_0.addr()).unwrap());
1140
1141 let mut test_closure_2 = |boolvar: bool, tc_info: &TcInfo, _tc: &[u8]| {
1143 common_check(boolvar, &tc_info.addr, vec![tc_info_1.addr()], &mut i);
1144 true
1145 };
1146
1147 scheduler.update_time(UnixTimestamp::new_only_seconds(206));
1148
1149 released = scheduler
1150 .release_telecommands_with_buffer(&mut test_closure_2, &mut pool, &mut tc_buf)
1151 .expect("deletion failed");
1152 assert_eq!(released, 1);
1153 assert!(!pool.has_element_at(&tc_info_1.addr()).unwrap());
1155
1156 scheduler
1158 .release_telecommands(&mut test_closure_2, &mut pool)
1159 .expect("deletion failed");
1160
1161 assert_eq!(i, 2);
1163 }
1164
1165 #[test]
1166 fn release_multi_with_same_time() {
1167 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1168 let mut scheduler =
1169 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1170
1171 let mut buf: [u8; 32] = [0; 32];
1172 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
1173
1174 scheduler
1175 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0)
1176 .expect("insertion failed");
1177
1178 let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None);
1179 scheduler
1180 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_1)
1181 .expect("insertion failed");
1182
1183 let mut i = 0;
1184 let mut test_closure = |boolvar: bool, store_addr: &TcInfo, _tc: &[u8]| {
1185 common_check(
1186 boolvar,
1187 &store_addr.addr,
1188 vec![tc_info_0.addr(), tc_info_1.addr()],
1189 &mut i,
1190 );
1191 true
1192 };
1193
1194 scheduler.update_time(UnixTimestamp::new_only_seconds(99));
1196 let mut tc_buf: [u8; 128] = [0; 128];
1197
1198 let mut released = scheduler
1199 .release_telecommands_with_buffer(&mut test_closure, &mut pool, &mut tc_buf)
1200 .expect("deletion failed");
1201 assert_eq!(released, 0);
1202
1203 scheduler.update_time(UnixTimestamp::new_only_seconds(100));
1205
1206 released = scheduler
1207 .release_telecommands(&mut test_closure, &mut pool)
1208 .expect("deletion failed");
1209 assert_eq!(released, 2);
1210 assert!(!pool.has_element_at(&tc_info_0.addr()).unwrap());
1211 assert!(!pool.has_element_at(&tc_info_1.addr()).unwrap());
1212
1213 released = scheduler
1215 .release_telecommands(&mut test_closure, &mut pool)
1216 .expect("deletion failed");
1217 assert_eq!(released, 0);
1218
1219 assert_eq!(i, 2);
1221 }
1222
1223 #[test]
1224 fn release_with_scheduler_disabled() {
1225 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1226 let mut scheduler =
1227 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1228
1229 scheduler.disable();
1230
1231 let mut buf: [u8; 32] = [0; 32];
1232 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
1233
1234 scheduler
1235 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0)
1236 .expect("insertion failed");
1237
1238 let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None);
1239 scheduler
1240 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(200), tc_info_1)
1241 .expect("insertion failed");
1242
1243 let mut i = 0;
1244 let mut test_closure_1 = |boolvar: bool, tc_info: &TcInfo, _tc: &[u8]| {
1245 common_check_disabled(boolvar, &tc_info.addr, vec![tc_info_0.addr()], &mut i);
1246 true
1247 };
1248
1249 let mut tc_buf: [u8; 128] = [0; 128];
1250
1251 scheduler.update_time(UnixTimestamp::new_only_seconds(99));
1253
1254 scheduler
1255 .release_telecommands_with_buffer(&mut test_closure_1, &mut pool, &mut tc_buf)
1256 .expect("deletion failed");
1257
1258 scheduler.update_time(UnixTimestamp::new_only_seconds(100));
1260
1261 let mut released = scheduler
1262 .release_telecommands(&mut test_closure_1, &mut pool)
1263 .expect("deletion failed");
1264 assert_eq!(released, 1);
1265 assert!(!pool.has_element_at(&tc_info_0.addr()).unwrap());
1266
1267 let mut test_closure_2 = |boolvar: bool, tc_info: &TcInfo, _tc: &[u8]| {
1269 common_check_disabled(boolvar, &tc_info.addr, vec![tc_info_1.addr()], &mut i);
1270 true
1271 };
1272
1273 scheduler.update_time(UnixTimestamp::new_only_seconds(206));
1274
1275 released = scheduler
1276 .release_telecommands(&mut test_closure_2, &mut pool)
1277 .expect("deletion failed");
1278 assert_eq!(released, 1);
1279 assert!(!pool.has_element_at(&tc_info_1.addr()).unwrap());
1280
1281 scheduler
1283 .release_telecommands(&mut test_closure_2, &mut pool)
1284 .expect("deletion failed");
1285
1286 assert_eq!(i, 2);
1288 }
1289
1290 #[test]
1291 fn insert_unwrapped_tc() {
1292 let mut scheduler =
1293 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1294
1295 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1296 let mut buf: [u8; 32] = [0; 32];
1297 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
1298
1299 let info = scheduler
1300 .insert_unwrapped_tc(
1301 UnixTimestamp::new_only_seconds(100),
1302 &buf[..pool.len_of_data(&tc_info_0.addr()).unwrap()],
1303 &mut pool,
1304 )
1305 .unwrap();
1306
1307 assert!(pool.has_element_at(&tc_info_0.addr()).unwrap());
1308
1309 let mut read_buf: [u8; 64] = [0; 64];
1310 pool.read(&tc_info_0.addr(), &mut read_buf).unwrap();
1311 let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
1312 assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None));
1313
1314 assert_eq!(scheduler.num_scheduled_telecommands(), 1);
1315
1316 scheduler.update_time(UnixTimestamp::new_only_seconds(101));
1317
1318 let mut addr_vec = Vec::new();
1319
1320 let mut i = 0;
1321 let mut test_closure = |boolvar: bool, tc_info: &TcInfo, _tc: &[u8]| {
1322 common_check(boolvar, &tc_info.addr, vec![info.addr], &mut i);
1323 addr_vec.push(tc_info.addr);
1325 false
1326 };
1327
1328 scheduler
1329 .release_telecommands(&mut test_closure, &mut pool)
1330 .unwrap();
1331
1332 let read_len = pool.read(&addr_vec[0], &mut read_buf).unwrap();
1333 let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
1334 assert_eq!(read_len, check_tc.1);
1335 assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None));
1336 }
1337
1338 #[test]
1339 fn insert_wrapped_tc() {
1340 let mut scheduler =
1341 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1342
1343 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1344
1345 let mut buf: [u8; 32] = [0; 32];
1346 let tc = scheduled_tc(UnixTimestamp::new_only_seconds(100), &mut buf);
1347
1348 let info = match scheduler.insert_wrapped_tc::<cds::TimeProvider>(&tc, &mut pool) {
1349 Ok(addr) => addr,
1350 Err(e) => {
1351 panic!("unexpected error {e}");
1352 }
1353 };
1354
1355 assert!(pool.has_element_at(&info.addr).unwrap());
1356
1357 let read_len = pool.read(&info.addr, &mut buf).unwrap();
1358 let check_tc = PusTcReader::new(&buf).expect("incorrect Pus tc raw data");
1359 assert_eq!(read_len, check_tc.1);
1360 assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None));
1361
1362 assert_eq!(scheduler.num_scheduled_telecommands(), 1);
1363
1364 scheduler.update_time(UnixTimestamp::new_only_seconds(101));
1365
1366 let mut addr_vec = Vec::new();
1367
1368 let mut i = 0;
1369 let mut test_closure = |boolvar: bool, tc_info: &TcInfo, _tc: &[u8]| {
1370 common_check(boolvar, &tc_info.addr, vec![info.addr], &mut i);
1371 addr_vec.push(tc_info.addr);
1373 false
1374 };
1375
1376 let mut tc_buf: [u8; 64] = [0; 64];
1377
1378 scheduler
1379 .release_telecommands_with_buffer(&mut test_closure, &mut pool, &mut tc_buf)
1380 .unwrap();
1381
1382 let read_len = pool.read(&addr_vec[0], &mut buf).unwrap();
1383 let check_tc = PusTcReader::new(&buf).expect("incorrect PUS tc raw data");
1384 assert_eq!(read_len, check_tc.1);
1385 assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None));
1386 }
1387
1388 #[test]
1389 fn insert_wrong_service() {
1390 let mut scheduler =
1391 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1392
1393 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1394
1395 let mut buf: [u8; 32] = [0; 32];
1396 let tc = wrong_tc_service(UnixTimestamp::new_only_seconds(100), &mut buf);
1397
1398 let err = scheduler.insert_wrapped_tc::<cds::TimeProvider>(&tc, &mut pool);
1399 assert!(err.is_err());
1400 let err = err.unwrap_err();
1401 match err {
1402 ScheduleError::WrongService(wrong_service) => {
1403 assert_eq!(wrong_service, 12);
1404 }
1405 _ => {
1406 panic!("unexpected error")
1407 }
1408 }
1409 }
1410
1411 #[test]
1412 fn insert_wrong_subservice() {
1413 let mut scheduler =
1414 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1415
1416 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1417
1418 let mut buf: [u8; 32] = [0; 32];
1419 let tc = wrong_tc_subservice(UnixTimestamp::new_only_seconds(100), &mut buf);
1420
1421 let err = scheduler.insert_wrapped_tc::<cds::TimeProvider>(&tc, &mut pool);
1422 assert!(err.is_err());
1423 let err = err.unwrap_err();
1424 match err {
1425 ScheduleError::WrongSubservice(wrong_subsrv) => {
1426 assert_eq!(wrong_subsrv, 5);
1427 }
1428 _ => {
1429 panic!("unexpected error")
1430 }
1431 }
1432 }
1433
1434 #[test]
1435 fn insert_wrapped_tc_faulty_app_data() {
1436 let mut scheduler =
1437 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1438 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1439 let tc = invalid_time_tagged_cmd();
1440 let insert_res = scheduler.insert_wrapped_tc::<cds::TimeProvider>(&tc, &mut pool);
1441 assert!(insert_res.is_err());
1442 let err = insert_res.unwrap_err();
1443 match err {
1444 ScheduleError::TcDataEmpty => {}
1445 _ => panic!("unexpected error {err}"),
1446 }
1447 }
1448
1449 #[test]
1450 fn insert_doubly_wrapped_time_tagged_cmd() {
1451 let mut scheduler =
1452 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1453 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1454 let mut buf: [u8; 64] = [0; 64];
1455 let tc = double_wrapped_time_tagged_tc(UnixTimestamp::new_only_seconds(50), &mut buf);
1456 let insert_res = scheduler.insert_wrapped_tc::<cds::TimeProvider>(&tc, &mut pool);
1457 assert!(insert_res.is_err());
1458 let err = insert_res.unwrap_err();
1459 match err {
1460 ScheduleError::NestedScheduledTc => {}
1461 _ => panic!("unexpected error {err}"),
1462 }
1463 }
1464
1465 #[test]
1466 fn test_ctor_from_current() {
1467 let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
1468 .expect("creation from current time failed");
1469 let current_time = scheduler.current_time;
1470 assert!(current_time.unix_seconds > 0);
1471 }
1472
1473 #[test]
1474 fn test_update_from_current() {
1475 let mut scheduler =
1476 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1477 assert_eq!(scheduler.current_time.unix_seconds, 0);
1478 scheduler
1479 .update_time_from_now()
1480 .expect("updating scheduler time from now failed");
1481 assert!(scheduler.current_time.unix_seconds > 0);
1482 }
1483
1484 #[test]
1485 fn release_time_within_time_margin() {
1486 let mut scheduler =
1487 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1488
1489 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1490
1491 let mut buf: [u8; 32] = [0; 32];
1492
1493 let tc = scheduled_tc(UnixTimestamp::new_only_seconds(4), &mut buf);
1494 let insert_res = scheduler.insert_wrapped_tc::<cds::TimeProvider>(&tc, &mut pool);
1495 assert!(insert_res.is_err());
1496 let err = insert_res.unwrap_err();
1497 match err {
1498 ScheduleError::ReleaseTimeInTimeMargin {
1499 current_time,
1500 time_margin,
1501 release_time,
1502 } => {
1503 assert_eq!(current_time, UnixTimestamp::new_only_seconds(0));
1504 assert_eq!(time_margin, Duration::from_secs(5));
1505 assert_eq!(release_time, UnixTimestamp::new_only_seconds(4));
1506 }
1507 _ => panic!("unexepcted error {err}"),
1508 }
1509 }
1510
1511 #[test]
1512 fn test_store_error_propagation_release() {
1513 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1514 let mut scheduler =
1515 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1516 let mut buf: [u8; 32] = [0; 32];
1517 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
1518 scheduler
1519 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0)
1520 .expect("insertion failed");
1521
1522 let mut i = 0;
1523 let test_closure_1 = |boolvar: bool, tc_info: &TcInfo, _tc: &[u8]| {
1524 common_check_disabled(boolvar, &tc_info.addr, vec![tc_info_0.addr()], &mut i);
1525 true
1526 };
1527
1528 pool.delete(tc_info_0.addr()).expect("deletion failed");
1530 scheduler.disable();
1532 scheduler.update_time(UnixTimestamp::new_only_seconds(100));
1533 let release_res = scheduler.release_telecommands(test_closure_1, &mut pool);
1534 assert!(release_res.is_err());
1535 let err = release_res.unwrap_err();
1536 assert_eq!(err.0, 0);
1538 match err.1 {
1539 StoreError::DataDoesNotExist(addr) => {
1540 assert_eq!(tc_info_0.addr(), addr);
1541 }
1542 _ => panic!("unexpected error {}", err.1),
1543 }
1544 }
1545
1546 #[test]
1547 fn test_store_error_propagation_reset() {
1548 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1549 let mut scheduler =
1550 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1551 let mut buf: [u8; 32] = [0; 32];
1552 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
1553 scheduler
1554 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0)
1555 .expect("insertion failed");
1556
1557 pool.delete(tc_info_0.addr()).expect("deletion failed");
1559 let reset_res = scheduler.reset(&mut pool);
1560 assert!(reset_res.is_err());
1561 let err = reset_res.unwrap_err();
1562 match err {
1563 StoreError::DataDoesNotExist(addr) => {
1564 assert_eq!(addr, tc_info_0.addr());
1565 }
1566 _ => panic!("unexpected error {err}"),
1567 }
1568 }
1569
1570 #[test]
1571 fn test_delete_by_req_id_simple_retrieve_addr() {
1572 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1573 let mut scheduler =
1574 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1575 let mut buf: [u8; 32] = [0; 32];
1576 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
1577 scheduler
1578 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0)
1579 .expect("inserting tc failed");
1580 assert_eq!(scheduler.num_scheduled_telecommands(), 1);
1581 let addr = scheduler
1582 .delete_by_request_id(&tc_info_0.request_id())
1583 .unwrap();
1584 assert!(pool.has_element_at(&tc_info_0.addr()).unwrap());
1585 assert_eq!(tc_info_0.addr(), addr);
1586 assert_eq!(scheduler.num_scheduled_telecommands(), 0);
1587 }
1588
1589 #[test]
1590 fn test_delete_by_req_id_simple_delete_all() {
1591 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1592 let mut scheduler =
1593 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1594 let mut buf: [u8; 32] = [0; 32];
1595 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
1596 scheduler
1597 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0)
1598 .expect("inserting tc failed");
1599 assert_eq!(scheduler.num_scheduled_telecommands(), 1);
1600 let del_res =
1601 scheduler.delete_by_request_id_and_from_pool(&tc_info_0.request_id(), &mut pool);
1602 assert!(del_res.is_ok());
1603 assert!(del_res.unwrap());
1604 assert!(!pool.has_element_at(&tc_info_0.addr()).unwrap());
1605 assert_eq!(scheduler.num_scheduled_telecommands(), 0);
1606 }
1607
1608 #[test]
1609 fn test_delete_by_req_id_complex() {
1610 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1611 let mut scheduler =
1612 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1613 let mut buf: [u8; 32] = [0; 32];
1614 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
1615 scheduler
1616 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0)
1617 .expect("inserting tc failed");
1618 let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None);
1619 scheduler
1620 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_1)
1621 .expect("inserting tc failed");
1622 let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, None);
1623 scheduler
1624 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_2)
1625 .expect("inserting tc failed");
1626 assert_eq!(scheduler.num_scheduled_telecommands(), 3);
1627
1628 let addr_0 = scheduler.delete_by_request_id(&tc_info_0.request_id());
1630 assert!(addr_0.is_some());
1631 assert_eq!(addr_0.unwrap(), tc_info_0.addr());
1632 assert!(pool.has_element_at(&tc_info_0.addr()).unwrap());
1633 assert_eq!(scheduler.num_scheduled_telecommands(), 2);
1634
1635 let del_res =
1637 scheduler.delete_by_request_id_and_from_pool(&tc_info_2.request_id(), &mut pool);
1638 assert!(del_res.is_ok());
1639 assert!(del_res.unwrap());
1640 assert!(!pool.has_element_at(&tc_info_2.addr()).unwrap());
1641 assert_eq!(scheduler.num_scheduled_telecommands(), 1);
1642
1643 let addr_1 =
1645 scheduler.delete_by_request_id_and_from_pool(&tc_info_1.request_id(), &mut pool);
1646 assert!(addr_1.is_ok());
1647 assert!(addr_1.unwrap());
1648 assert!(!pool.has_element_at(&tc_info_1.addr()).unwrap());
1649 assert_eq!(scheduler.num_scheduled_telecommands(), 0);
1650 }
1651
1652 #[test]
1653 fn insert_full_store_test() {
1654 let mut scheduler =
1655 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1656
1657 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(1, 64)], false));
1658
1659 let mut buf: [u8; 32] = [0; 32];
1660 pool.add(&[0, 1, 2]).unwrap();
1662 let tc = scheduled_tc(UnixTimestamp::new_only_seconds(100), &mut buf);
1663
1664 let insert_res = scheduler.insert_wrapped_tc::<cds::TimeProvider>(&tc, &mut pool);
1665 assert!(insert_res.is_err());
1666 let err = insert_res.unwrap_err();
1667 match err {
1668 ScheduleError::StoreError(e) => match e {
1669 StoreError::StoreFull(_) => {}
1670 _ => panic!("unexpected store error {e}"),
1671 },
1672 _ => panic!("unexpected error {err}"),
1673 }
1674 }
1675
1676 fn insert_command_with_release_time(
1677 pool: &mut StaticMemoryPool,
1678 scheduler: &mut PusScheduler,
1679 seq_count: u16,
1680 release_secs: u64,
1681 ) -> TcInfo {
1682 let mut buf: [u8; 32] = [0; 32];
1683 let tc_info = ping_tc_to_store(pool, &mut buf, seq_count, None);
1684
1685 scheduler
1686 .insert_unwrapped_and_stored_tc(
1687 UnixTimestamp::new_only_seconds(release_secs as i64),
1688 tc_info,
1689 )
1690 .expect("inserting tc failed");
1691 tc_info
1692 }
1693
1694 #[test]
1695 fn test_time_window_retrieval_select_all() {
1696 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1697 let mut scheduler =
1698 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1699 let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
1700 let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
1701 assert_eq!(scheduler.num_scheduled_telecommands(), 2);
1702 let check_range = |range: Range<UnixTimestamp, Vec<TcInfo>>| {
1703 let mut tcs_in_range = 0;
1704 for (idx, time_bucket) in range.enumerate() {
1705 tcs_in_range += 1;
1706 if idx == 0 {
1707 assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(50));
1708 assert_eq!(time_bucket.1.len(), 1);
1709 assert_eq!(time_bucket.1[0].request_id, tc_info_0.request_id);
1710 } else if idx == 1 {
1711 assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(100));
1712 assert_eq!(time_bucket.1.len(), 1);
1713 assert_eq!(time_bucket.1[0].request_id, tc_info_1.request_id);
1714 }
1715 }
1716 assert_eq!(tcs_in_range, 2);
1717 };
1718 let range = scheduler.retrieve_all();
1719 check_range(range);
1720 let range =
1721 scheduler.retrieve_by_time_filter(TimeWindow::<cds::TimeProvider>::new_select_all());
1722 check_range(range);
1723 }
1724
1725 #[test]
1726 fn test_time_window_retrieval_select_from_stamp() {
1727 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1728 let mut scheduler =
1729 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1730 let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
1731 let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
1732 let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
1733 let start_stamp =
1734 cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100))
1735 .expect("creating start stamp failed");
1736 let time_window = TimeWindow::new_from_time(&start_stamp);
1737 assert_eq!(scheduler.num_scheduled_telecommands(), 3);
1738
1739 let range = scheduler.retrieve_by_time_filter(time_window);
1740 let mut tcs_in_range = 0;
1741 for (idx, time_bucket) in range.enumerate() {
1742 tcs_in_range += 1;
1743 if idx == 0 {
1744 assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(100));
1745 assert_eq!(time_bucket.1.len(), 1);
1746 assert_eq!(time_bucket.1[0].request_id, tc_info_1.request_id());
1747 } else if idx == 1 {
1748 assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(150));
1749 assert_eq!(time_bucket.1.len(), 1);
1750 assert_eq!(time_bucket.1[0].request_id, tc_info_2.request_id());
1751 }
1752 }
1753 assert_eq!(tcs_in_range, 2);
1754 }
1755
1756 #[test]
1757 fn test_time_window_retrieval_select_to_time() {
1758 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1759 let mut scheduler =
1760 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1761 let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
1762 let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
1763 let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
1764 assert_eq!(scheduler.num_scheduled_telecommands(), 3);
1765
1766 let end_stamp =
1767 cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100))
1768 .expect("creating start stamp failed");
1769 let time_window = TimeWindow::new_to_time(&end_stamp);
1770 let range = scheduler.retrieve_by_time_filter(time_window);
1771 let mut tcs_in_range = 0;
1772 for (idx, time_bucket) in range.enumerate() {
1773 tcs_in_range += 1;
1774 if idx == 0 {
1775 assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(50));
1776 assert_eq!(time_bucket.1.len(), 1);
1777 assert_eq!(time_bucket.1[0].request_id, tc_info_0.request_id());
1778 } else if idx == 1 {
1779 assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(100));
1780 assert_eq!(time_bucket.1.len(), 1);
1781 assert_eq!(time_bucket.1[0].request_id, tc_info_1.request_id());
1782 }
1783 }
1784 assert_eq!(tcs_in_range, 2);
1785 }
1786
1787 #[test]
1788 fn test_time_window_retrieval_select_from_time_to_time() {
1789 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1790 let mut scheduler =
1791 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1792 let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
1793 let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
1794 let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
1795 let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 200);
1796 assert_eq!(scheduler.num_scheduled_telecommands(), 4);
1797
1798 let start_stamp =
1799 cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100))
1800 .expect("creating start stamp failed");
1801 let end_stamp =
1802 cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(150))
1803 .expect("creating end stamp failed");
1804 let time_window = TimeWindow::new_from_time_to_time(&start_stamp, &end_stamp);
1805 let range = scheduler.retrieve_by_time_filter(time_window);
1806 let mut tcs_in_range = 0;
1807 for (idx, time_bucket) in range.enumerate() {
1808 tcs_in_range += 1;
1809 if idx == 0 {
1810 assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(100));
1811 assert_eq!(time_bucket.1.len(), 1);
1812 assert_eq!(time_bucket.1[0].request_id, tc_info_1.request_id());
1813 } else if idx == 1 {
1814 assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(150));
1815 assert_eq!(time_bucket.1.len(), 1);
1816 assert_eq!(time_bucket.1[0].request_id, tc_info_2.request_id());
1817 }
1818 }
1819 assert_eq!(tcs_in_range, 2);
1820 }
1821
1822 #[test]
1823 fn test_deletion_all() {
1824 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1825 let mut scheduler =
1826 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1827 insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
1828 insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
1829 assert_eq!(scheduler.num_scheduled_telecommands(), 2);
1830 let del_res = scheduler.delete_all(&mut pool);
1831 assert!(del_res.is_ok());
1832 assert_eq!(del_res.unwrap(), 2);
1833 assert_eq!(scheduler.num_scheduled_telecommands(), 0);
1834 assert!(scheduler.is_enabled());
1836
1837 insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
1838 insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
1839 assert_eq!(scheduler.num_scheduled_telecommands(), 2);
1840 let del_res = scheduler
1841 .delete_by_time_filter(TimeWindow::<cds::TimeProvider>::new_select_all(), &mut pool);
1842 assert!(del_res.is_ok());
1843 assert_eq!(del_res.unwrap(), 2);
1844 assert_eq!(scheduler.num_scheduled_telecommands(), 0);
1845 assert!(scheduler.is_enabled());
1847 }
1848
1849 #[test]
1850 fn test_deletion_from_start_time() {
1851 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1852 let mut scheduler =
1853 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1854 insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
1855 let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
1856 let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
1857 assert_eq!(scheduler.num_scheduled_telecommands(), 3);
1858 let start_stamp =
1859 cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100))
1860 .expect("creating start stamp failed");
1861 let time_window = TimeWindow::new_from_time(&start_stamp);
1862 let del_res = scheduler.delete_by_time_filter(time_window, &mut pool);
1863 assert!(del_res.is_ok());
1864 assert_eq!(del_res.unwrap(), 2);
1865 assert_eq!(scheduler.num_scheduled_telecommands(), 1);
1866 assert!(!pool.has_element_at(&cmd_0_to_delete.addr()).unwrap());
1867 assert!(!pool.has_element_at(&cmd_1_to_delete.addr()).unwrap());
1868 }
1869
1870 #[test]
1871 fn test_deletion_to_end_time() {
1872 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1873 let mut scheduler =
1874 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1875 let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
1876 let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
1877 insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
1878 assert_eq!(scheduler.num_scheduled_telecommands(), 3);
1879
1880 let end_stamp =
1881 cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100))
1882 .expect("creating start stamp failed");
1883 let time_window = TimeWindow::new_to_time(&end_stamp);
1884 let del_res = scheduler.delete_by_time_filter(time_window, &mut pool);
1885 assert!(del_res.is_ok());
1886 assert_eq!(del_res.unwrap(), 2);
1887 assert_eq!(scheduler.num_scheduled_telecommands(), 1);
1888 assert!(!pool.has_element_at(&cmd_0_to_delete.addr()).unwrap());
1889 assert!(!pool.has_element_at(&cmd_1_to_delete.addr()).unwrap());
1890 }
1891
1892 #[test]
1893 fn test_deletion_from_start_time_to_end_time() {
1894 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1895 let mut scheduler =
1896 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1897 let cmd_out_of_range_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
1898 let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
1899 let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
1900 let cmd_out_of_range_1 =
1901 insert_command_with_release_time(&mut pool, &mut scheduler, 0, 200);
1902 assert_eq!(scheduler.num_scheduled_telecommands(), 4);
1903
1904 let start_stamp =
1905 cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100))
1906 .expect("creating start stamp failed");
1907 let end_stamp =
1908 cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(150))
1909 .expect("creating end stamp failed");
1910 let time_window = TimeWindow::new_from_time_to_time(&start_stamp, &end_stamp);
1911 let del_res = scheduler.delete_by_time_filter(time_window, &mut pool);
1912 assert!(del_res.is_ok());
1913 assert_eq!(del_res.unwrap(), 2);
1914 assert_eq!(scheduler.num_scheduled_telecommands(), 2);
1915 assert!(pool.has_element_at(&cmd_out_of_range_0.addr()).unwrap());
1916 assert!(!pool.has_element_at(&cmd_0_to_delete.addr()).unwrap());
1917 assert!(!pool.has_element_at(&cmd_1_to_delete.addr()).unwrap());
1918 assert!(pool.has_element_at(&cmd_out_of_range_1.addr()).unwrap());
1919 }
1920
1921 #[test]
1922 fn test_release_without_deletion() {
1923 let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
1924 let mut scheduler =
1925 PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
1926
1927 let mut buf: [u8; 32] = [0; 32];
1928 let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
1929
1930 scheduler
1931 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0)
1932 .expect("insertion failed");
1933
1934 let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None);
1935 scheduler
1936 .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(200), tc_info_1)
1937 .expect("insertion failed");
1938
1939 let mut i = 0;
1940 let mut test_closure_1 = |boolvar: bool, tc_info: &TcInfo, _tc: &[u8]| {
1941 common_check(
1942 boolvar,
1943 &tc_info.addr,
1944 vec![tc_info_0.addr(), tc_info_1.addr()],
1945 &mut i,
1946 );
1947 };
1948
1949 scheduler.update_time(UnixTimestamp::new_only_seconds(205));
1950
1951 let mut tc_buf: [u8; 64] = [0; 64];
1952 let tc_info_vec = scheduler
1953 .release_telecommands_no_deletion(&mut test_closure_1, &pool, &mut tc_buf)
1954 .expect("deletion failed");
1955 assert_eq!(tc_info_vec[0], tc_info_0);
1956 assert_eq!(tc_info_vec[1], tc_info_1);
1957 }
1958
1959 #[test]
1960 fn test_generic_insert_app_data_test() {
1961 let time_writer = cds::TimeProvider::new_with_u16_days(1, 1);
1962 let mut sph = SpHeader::new(
1963 PacketId::const_new(PacketType::Tc, true, 0x002),
1964 PacketSequenceCtrl::const_new(SequenceFlags::Unsegmented, 5),
1965 0,
1966 );
1967 let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
1968 let ping_tc = PusTcCreator::new_no_app_data(&mut sph, sec_header, true);
1969 let mut buf: [u8; 64] = [0; 64];
1970 let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
1971 assert!(result.is_ok());
1972 assert_eq!(result.unwrap(), 2 + 7 + ping_tc.len_written());
1973 let n = u16::from_be_bytes(buf[0..2].try_into().unwrap());
1974 assert_eq!(n, 1);
1975 let time_reader = cds::TimeProvider::from_bytes_with_u16_days(&buf[2..2 + 7]).unwrap();
1976 assert_eq!(time_reader, time_writer);
1977 let pus_tc_reader = PusTcReader::new(&buf[9..]).unwrap().0;
1978 assert_eq!(pus_tc_reader, ping_tc);
1979 }
1980
1981 #[test]
1982 fn test_generic_insert_app_data_test_byte_conv_error() {
1983 let time_writer = cds::TimeProvider::new_with_u16_days(1, 1);
1984 let mut sph = SpHeader::new(
1985 PacketId::const_new(PacketType::Tc, true, 0x002),
1986 PacketSequenceCtrl::const_new(SequenceFlags::Unsegmented, 5),
1987 0,
1988 );
1989 let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
1990 let ping_tc = PusTcCreator::new_no_app_data(&mut sph, sec_header, true);
1991 let mut buf: [u8; 16] = [0; 16];
1992 let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
1993 assert!(result.is_err());
1994 let error = result.unwrap_err();
1995 if let ScheduleError::ByteConversionError(ByteConversionError::ToSliceTooSmall {
1996 found,
1997 expected,
1998 }) = error
1999 {
2000 assert_eq!(found, 16);
2001 assert_eq!(
2002 expected,
2003 2 + time_writer.len_written() + ping_tc.len_written()
2004 );
2005 } else {
2006 panic!("unexpected error {error}")
2007 }
2008 }
2009
2010 #[test]
2011 fn test_generic_insert_app_data_test_as_vec() {
2012 let time_writer = cds::TimeProvider::new_with_u16_days(1, 1);
2013 let mut sph = SpHeader::new(
2014 PacketId::const_new(PacketType::Tc, true, 0x002),
2015 PacketSequenceCtrl::const_new(SequenceFlags::Unsegmented, 5),
2016 0,
2017 );
2018 let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
2019 let ping_tc = PusTcCreator::new_no_app_data(&mut sph, sec_header, true);
2020 let mut buf: [u8; 64] = [0; 64];
2021 generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc).unwrap();
2022 let vec = generate_insert_telecommand_app_data_as_vec(&time_writer, &ping_tc)
2023 .expect("vec generation failed");
2024 assert_eq!(&buf[..vec.len()], vec);
2025 }
2026}