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