1#![no_std]
68#![cfg_attr(docsrs, feature(doc_auto_cfg))]
69#[cfg(feature = "alloc")]
70extern crate alloc;
71#[cfg(any(feature = "std", test))]
72extern crate std;
73
74#[cfg(feature = "alloc")]
75pub mod dest;
76pub mod filestore;
77pub mod request;
78#[cfg(feature = "alloc")]
79pub mod source;
80pub mod time;
81pub mod user;
82
83use crate::time::CountdownProvider;
84use core::{cell::RefCell, fmt::Debug, hash::Hash};
85use crc::{Crc, CRC_32_ISCSI, CRC_32_ISO_HDLC};
86
87#[cfg(feature = "alloc")]
88pub use alloc_mod::*;
89use core::time::Duration;
90#[cfg(feature = "serde")]
91use serde::{Deserialize, Serialize};
92use spacepackets::{
93 cfdp::{
94 pdu::{FileDirectiveType, PduError, PduHeader},
95 ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
96 },
97 util::{UnsignedByteField, UnsignedEnum},
98};
99#[cfg(feature = "std")]
100pub use std_mod::*;
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103#[cfg_attr(feature = "defmt", derive(defmt::Format))]
104#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
105pub enum EntityType {
106 Sending,
107 Receiving,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111#[cfg_attr(feature = "defmt", derive(defmt::Format))]
112#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
113pub enum TimerContext {
114 CheckLimit {
115 local_id: UnsignedByteField,
116 remote_id: UnsignedByteField,
117 entity_type: EntityType,
118 },
119 NakActivity {
120 expiry_time: Duration,
121 },
122 PositiveAck {
123 expiry_time: Duration,
124 },
125}
126
127pub trait TimerCreatorProvider {
165 type Countdown: CountdownProvider;
166
167 fn create_countdown(&self, timer_context: TimerContext) -> Self::Countdown;
168}
169
170#[derive(Debug, Copy, Clone)]
230#[cfg_attr(feature = "defmt", derive(defmt::Format))]
231#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
232pub struct RemoteEntityConfig {
233 pub entity_id: UnsignedByteField,
234 pub max_packet_len: usize,
235 pub max_file_segment_len: Option<usize>,
236 pub closure_requested_by_default: bool,
237 pub crc_on_transmission_by_default: bool,
238 pub default_transmission_mode: TransmissionMode,
239 pub default_crc_type: ChecksumType,
240 pub positive_ack_timer_interval_seconds: f32,
241 pub positive_ack_timer_expiration_limit: u32,
242 pub check_limit: u32,
243 pub disposition_on_cancellation: bool,
244 pub immediate_nak_mode: bool,
245 pub nak_timer_interval_seconds: f32,
246 pub nak_timer_expiration_limit: u32,
247}
248
249impl RemoteEntityConfig {
250 pub fn new_with_default_values(
251 entity_id: UnsignedByteField,
252 max_packet_len: usize,
253 closure_requested_by_default: bool,
254 crc_on_transmission_by_default: bool,
255 default_transmission_mode: TransmissionMode,
256 default_crc_type: ChecksumType,
257 ) -> Self {
258 Self {
259 entity_id,
260 max_file_segment_len: None,
261 max_packet_len,
262 closure_requested_by_default,
263 crc_on_transmission_by_default,
264 default_transmission_mode,
265 default_crc_type,
266 check_limit: 2,
267 positive_ack_timer_interval_seconds: 10.0,
268 positive_ack_timer_expiration_limit: 2,
269 disposition_on_cancellation: false,
270 immediate_nak_mode: true,
271 nak_timer_interval_seconds: 10.0,
272 nak_timer_expiration_limit: 2,
273 }
274 }
275}
276
277pub trait RemoteEntityConfigProvider {
278 fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig>;
280 fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig>;
281 fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool;
284 fn remove_config(&mut self, remote_id: u64) -> bool;
287}
288
289#[cfg(feature = "alloc")]
292#[derive(Default, Debug)]
293#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
294pub struct StdRemoteEntityConfigProvider(pub hashbrown::HashMap<u64, RemoteEntityConfig>);
295
296#[cfg(feature = "std")]
297impl RemoteEntityConfigProvider for StdRemoteEntityConfigProvider {
298 fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
299 self.0.get(&remote_id)
300 }
301 fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
302 self.0.get_mut(&remote_id)
303 }
304 fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool {
305 self.0.insert(cfg.entity_id.value(), *cfg).is_some()
306 }
307 fn remove_config(&mut self, remote_id: u64) -> bool {
308 self.0.remove(&remote_id).is_some()
309 }
310}
311
312#[cfg(feature = "alloc")]
315#[derive(Default, Debug)]
316#[cfg_attr(feature = "defmt", derive(defmt::Format))]
317#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
318pub struct VecRemoteEntityConfigProvider(pub alloc::vec::Vec<RemoteEntityConfig>);
319
320#[cfg(feature = "alloc")]
321impl RemoteEntityConfigProvider for VecRemoteEntityConfigProvider {
322 fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
323 self.0
324 .iter()
325 .find(|&cfg| cfg.entity_id.value() == remote_id)
326 }
327
328 fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
329 self.0
330 .iter_mut()
331 .find(|cfg| cfg.entity_id.value() == remote_id)
332 }
333
334 fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool {
335 self.0.push(*cfg);
336 true
337 }
338
339 fn remove_config(&mut self, remote_id: u64) -> bool {
340 for (idx, cfg) in self.0.iter().enumerate() {
341 if cfg.entity_id.value() == remote_id {
342 self.0.remove(idx);
343 return true;
344 }
345 }
346 false
347 }
348}
349
350impl RemoteEntityConfigProvider for RemoteEntityConfig {
354 fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
355 if remote_id == self.entity_id.value() {
356 return Some(self);
357 }
358 None
359 }
360
361 fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
362 if remote_id == self.entity_id.value() {
363 return Some(self);
364 }
365 None
366 }
367
368 fn add_config(&mut self, _cfg: &RemoteEntityConfig) -> bool {
369 false
370 }
371
372 fn remove_config(&mut self, _remote_id: u64) -> bool {
373 false
374 }
375}
376
377pub trait UserFaultHookProvider {
388 fn notice_of_suspension_cb(
389 &mut self,
390 transaction_id: TransactionId,
391 cond: ConditionCode,
392 progress: u64,
393 );
394
395 fn notice_of_cancellation_cb(
396 &mut self,
397 transaction_id: TransactionId,
398 cond: ConditionCode,
399 progress: u64,
400 );
401
402 fn abandoned_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64);
403
404 fn ignore_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64);
405}
406
407#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
410pub struct DummyFaultHook {}
411
412impl UserFaultHookProvider for DummyFaultHook {
413 fn notice_of_suspension_cb(
414 &mut self,
415 _transaction_id: TransactionId,
416 _cond: ConditionCode,
417 _progress: u64,
418 ) {
419 }
420
421 fn notice_of_cancellation_cb(
422 &mut self,
423 _transaction_id: TransactionId,
424 _cond: ConditionCode,
425 _progress: u64,
426 ) {
427 }
428
429 fn abandoned_cb(
430 &mut self,
431 _transaction_id: TransactionId,
432 _cond: ConditionCode,
433 _progress: u64,
434 ) {
435 }
436
437 fn ignore_cb(&mut self, _transaction_id: TransactionId, _cond: ConditionCode, _progress: u64) {}
438}
439
440pub struct FaultHandler<UserHandler: UserFaultHookProvider> {
461 handler_array: [FaultHandlerCode; 10],
462 pub user_hook: RefCell<UserHandler>,
465}
466
467impl<UserHandler: UserFaultHookProvider> FaultHandler<UserHandler> {
468 fn condition_code_to_array_index(conditon_code: ConditionCode) -> Option<usize> {
469 Some(match conditon_code {
470 ConditionCode::PositiveAckLimitReached => 0,
471 ConditionCode::KeepAliveLimitReached => 1,
472 ConditionCode::InvalidTransmissionMode => 2,
473 ConditionCode::FilestoreRejection => 3,
474 ConditionCode::FileChecksumFailure => 4,
475 ConditionCode::FileSizeError => 5,
476 ConditionCode::NakLimitReached => 6,
477 ConditionCode::InactivityDetected => 7,
478 ConditionCode::CheckLimitReached => 8,
479 ConditionCode::UnsupportedChecksumType => 9,
480 _ => return None,
481 })
482 }
483
484 pub fn set_fault_handler(
485 &mut self,
486 condition_code: ConditionCode,
487 fault_handler: FaultHandlerCode,
488 ) {
489 let array_idx = Self::condition_code_to_array_index(condition_code);
490 if array_idx.is_none() {
491 return;
492 }
493 self.handler_array[array_idx.unwrap()] = fault_handler;
494 }
495
496 pub fn new(user_fault_handler: UserHandler) -> Self {
497 let mut init_array = [FaultHandlerCode::NoticeOfCancellation; 10];
498 init_array
499 [Self::condition_code_to_array_index(ConditionCode::FileChecksumFailure).unwrap()] =
500 FaultHandlerCode::IgnoreError;
501 init_array[Self::condition_code_to_array_index(ConditionCode::UnsupportedChecksumType)
502 .unwrap()] = FaultHandlerCode::IgnoreError;
503 Self {
504 handler_array: init_array,
505 user_hook: RefCell::new(user_fault_handler),
506 }
507 }
508
509 pub fn get_fault_handler(&self, condition_code: ConditionCode) -> FaultHandlerCode {
510 let array_idx = Self::condition_code_to_array_index(condition_code);
511 if array_idx.is_none() {
512 return FaultHandlerCode::IgnoreError;
513 }
514 self.handler_array[array_idx.unwrap()]
515 }
516
517 pub fn report_fault(
518 &self,
519 transaction_id: TransactionId,
520 condition: ConditionCode,
521 progress: u64,
522 ) -> FaultHandlerCode {
523 let array_idx = Self::condition_code_to_array_index(condition);
524 if array_idx.is_none() {
525 return FaultHandlerCode::IgnoreError;
526 }
527 let fh_code = self.handler_array[array_idx.unwrap()];
528 let mut handler_mut = self.user_hook.borrow_mut();
529 match fh_code {
530 FaultHandlerCode::NoticeOfCancellation => {
531 handler_mut.notice_of_cancellation_cb(transaction_id, condition, progress);
532 }
533 FaultHandlerCode::NoticeOfSuspension => {
534 handler_mut.notice_of_suspension_cb(transaction_id, condition, progress);
535 }
536 FaultHandlerCode::IgnoreError => {
537 handler_mut.ignore_cb(transaction_id, condition, progress);
538 }
539 FaultHandlerCode::AbandonTransaction => {
540 handler_mut.abandoned_cb(transaction_id, condition, progress);
541 }
542 }
543 fh_code
544 }
545}
546
547#[derive(Debug, Clone, Copy, PartialEq, Eq)]
548#[cfg_attr(feature = "defmt", derive(defmt::Format))]
549#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
550pub struct IndicationConfig {
551 pub eof_sent: bool,
552 pub eof_recv: bool,
553 pub file_segment_recv: bool,
554 pub transaction_finished: bool,
555 pub suspended: bool,
556 pub resumed: bool,
557}
558
559impl Default for IndicationConfig {
560 fn default() -> Self {
561 Self {
562 eof_sent: true,
563 eof_recv: true,
564 file_segment_recv: true,
565 transaction_finished: true,
566 suspended: true,
567 resumed: true,
568 }
569 }
570}
571
572pub struct LocalEntityConfig<UserFaultHook: UserFaultHookProvider> {
574 pub id: UnsignedByteField,
575 pub indication_cfg: IndicationConfig,
576 pub fault_handler: FaultHandler<UserFaultHook>,
577}
578
579impl<UserFaultHook: UserFaultHookProvider> LocalEntityConfig<UserFaultHook> {
580 pub fn new(
581 id: UnsignedByteField,
582 indication_cfg: IndicationConfig,
583 hook: UserFaultHook,
584 ) -> Self {
585 Self {
586 id,
587 indication_cfg,
588 fault_handler: FaultHandler::new(hook),
589 }
590 }
591}
592
593impl<UserFaultHook: UserFaultHookProvider> LocalEntityConfig<UserFaultHook> {
594 pub fn user_fault_hook_mut(&mut self) -> &mut RefCell<UserFaultHook> {
595 &mut self.fault_handler.user_hook
596 }
597
598 pub fn user_fault_hook(&self) -> &RefCell<UserFaultHook> {
599 &self.fault_handler.user_hook
600 }
601}
602
603#[cfg(feature = "std")]
605#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
606#[non_exhaustive]
607pub enum GenericSendError {
608 #[error("RX disconnected")]
609 RxDisconnected,
610 #[error("queue is full, fill count {0:?}")]
611 QueueFull(Option<u32>),
612 #[error("other send error")]
613 Other,
614}
615
616#[cfg(feature = "std")]
617pub trait PduSendProvider {
618 fn send_pdu(
619 &self,
620 pdu_type: PduType,
621 file_directive_type: Option<FileDirectiveType>,
622 raw_pdu: &[u8],
623 ) -> Result<(), GenericSendError>;
624}
625
626#[cfg(feature = "std")]
627pub mod std_mod {
628 use std::sync::mpsc;
629
630 use super::*;
631
632 impl PduSendProvider for mpsc::Sender<PduOwnedWithInfo> {
633 fn send_pdu(
634 &self,
635 pdu_type: PduType,
636 file_directive_type: Option<FileDirectiveType>,
637 raw_pdu: &[u8],
638 ) -> Result<(), GenericSendError> {
639 self.send(PduOwnedWithInfo::new(
640 pdu_type,
641 file_directive_type,
642 raw_pdu.to_vec(),
643 ))
644 .map_err(|_| GenericSendError::RxDisconnected)?;
645 Ok(())
646 }
647 }
648
649 #[derive(Debug)]
651 pub struct StdCountdown {
652 expiry_time: Duration,
653 start_time: std::time::Instant,
654 }
655
656 impl StdCountdown {
657 pub fn new(expiry_time: Duration) -> Self {
658 Self {
659 expiry_time,
660 start_time: std::time::Instant::now(),
661 }
662 }
663
664 pub fn expiry_time_seconds(&self) -> u64 {
665 self.expiry_time.as_secs()
666 }
667 }
668
669 impl CountdownProvider for StdCountdown {
670 fn has_expired(&self) -> bool {
671 if self.start_time.elapsed() > self.expiry_time {
672 return true;
673 }
674 false
675 }
676
677 fn reset(&mut self) {
678 self.start_time = std::time::Instant::now();
679 }
680 }
681
682 pub struct StdTimerCreator {
683 pub check_limit_timeout: Duration,
684 }
685
686 impl StdTimerCreator {
687 pub const fn new(check_limit_timeout: Duration) -> Self {
688 Self {
689 check_limit_timeout,
690 }
691 }
692 }
693
694 impl Default for StdTimerCreator {
695 fn default() -> Self {
696 Self::new(Duration::from_secs(5))
697 }
698 }
699
700 impl TimerCreatorProvider for StdTimerCreator {
701 type Countdown = StdCountdown;
702
703 fn create_countdown(&self, timer_context: TimerContext) -> Self::Countdown {
704 match timer_context {
705 TimerContext::CheckLimit {
706 local_id: _,
707 remote_id: _,
708 entity_type: _,
709 } => StdCountdown::new(self.check_limit_timeout),
710 TimerContext::NakActivity { expiry_time } => StdCountdown::new(expiry_time),
711 TimerContext::PositiveAck { expiry_time } => StdCountdown::new(expiry_time),
712 }
713 }
714 }
715}
716
717#[derive(Debug, Eq, Copy, Clone)]
720#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
721#[cfg_attr(feature = "defmt", derive(defmt::Format))]
722pub struct TransactionId {
723 source_id: UnsignedByteField,
724 seq_num: UnsignedByteField,
725}
726
727impl TransactionId {
728 pub fn new(source_id: UnsignedByteField, seq_num: UnsignedByteField) -> Self {
729 Self { source_id, seq_num }
730 }
731
732 pub fn source_id(&self) -> &UnsignedByteField {
733 &self.source_id
734 }
735
736 pub fn seq_num(&self) -> &UnsignedByteField {
737 &self.seq_num
738 }
739}
740
741impl Hash for TransactionId {
742 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
743 self.source_id.value().hash(state);
744 self.seq_num.value().hash(state);
745 }
746}
747
748impl PartialEq for TransactionId {
749 fn eq(&self, other: &Self) -> bool {
750 self.source_id.value() == other.source_id.value()
751 && self.seq_num.value() == other.seq_num.value()
752 }
753}
754
755#[derive(Debug, Copy, Clone, PartialEq, Eq)]
756#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
757pub enum State {
758 Idle = 0,
759 Busy = 1,
760 Suspended = 2,
761}
762
763pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
768pub const CRC_32C: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
773
774#[derive(Debug, PartialEq, Eq, Copy, Clone)]
775#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
776pub enum PacketTarget {
777 SourceEntity,
778 DestEntity,
779}
780
781pub trait PduProvider {
784 fn pdu_type(&self) -> PduType;
785 fn file_directive_type(&self) -> Option<FileDirectiveType>;
786 fn pdu(&self) -> &[u8];
787 fn packet_target(&self) -> Result<PacketTarget, PduError>;
788}
789
790pub struct DummyPduProvider(());
791
792impl PduProvider for DummyPduProvider {
793 fn pdu_type(&self) -> PduType {
794 PduType::FileData
795 }
796
797 fn file_directive_type(&self) -> Option<FileDirectiveType> {
798 None
799 }
800
801 fn pdu(&self) -> &[u8] {
802 &[]
803 }
804
805 fn packet_target(&self) -> Result<PacketTarget, PduError> {
806 Ok(PacketTarget::SourceEntity)
807 }
808}
809
810pub struct PduRawWithInfo<'raw_packet> {
814 pdu_type: PduType,
815 file_directive_type: Option<FileDirectiveType>,
816 packet_len: usize,
817 raw_packet: &'raw_packet [u8],
818}
819
820pub fn determine_packet_target(raw_pdu: &[u8]) -> Result<PacketTarget, PduError> {
821 let (header, header_len) = PduHeader::from_bytes(raw_pdu)?;
822 if header.pdu_type() == PduType::FileData {
823 return Ok(PacketTarget::DestEntity);
824 }
825 let file_directive_type = FileDirectiveType::try_from(raw_pdu[header_len]).map_err(|_| {
826 PduError::InvalidDirectiveType {
827 found: raw_pdu[header_len],
828 expected: None,
829 }
830 })?;
831 let packet_target =
832 match file_directive_type {
833 FileDirectiveType::NakPdu
836 | FileDirectiveType::FinishedPdu
837 | FileDirectiveType::KeepAlivePdu => PacketTarget::SourceEntity,
838 FileDirectiveType::MetadataPdu
841 | FileDirectiveType::EofPdu
842 | FileDirectiveType::PromptPdu => PacketTarget::DestEntity,
843 FileDirectiveType::AckPdu => {
847 let acked_directive = FileDirectiveType::try_from(raw_pdu[header_len + 1])
848 .map_err(|_| PduError::InvalidDirectiveType {
849 found: raw_pdu[header_len],
850 expected: None,
851 })?;
852 if acked_directive == FileDirectiveType::EofPdu {
853 PacketTarget::SourceEntity
854 } else if acked_directive == FileDirectiveType::FinishedPdu {
855 PacketTarget::DestEntity
856 } else {
857 return Err(PduError::InvalidDirectiveType {
859 found: raw_pdu[header_len + 1],
860 expected: None,
861 });
862 }
863 }
864 };
865 Ok(packet_target)
866}
867
868impl<'raw> PduRawWithInfo<'raw> {
869 pub fn new(raw_packet: &'raw [u8]) -> Result<Self, PduError> {
870 let (pdu_header, header_len) = PduHeader::from_bytes(raw_packet)?;
871 if pdu_header.pdu_type() == PduType::FileData {
872 return Ok(Self {
873 pdu_type: pdu_header.pdu_type(),
874 file_directive_type: None,
875 packet_len: pdu_header.pdu_len(),
876 raw_packet,
877 });
878 }
879 if pdu_header.pdu_datafield_len() < 1 {
880 return Err(PduError::Format);
881 }
882 let directive = FileDirectiveType::try_from(raw_packet[header_len]).map_err(|_| {
887 PduError::InvalidDirectiveType {
888 found: raw_packet[header_len],
889 expected: None,
890 }
891 })?;
892 Ok(Self {
893 pdu_type: pdu_header.pdu_type(),
894 file_directive_type: Some(directive),
895 packet_len: pdu_header.pdu_len(),
896 raw_packet,
897 })
898 }
899
900 pub fn raw_packet(&self) -> &[u8] {
901 &self.raw_packet[0..self.packet_len]
902 }
903}
904
905impl PduProvider for PduRawWithInfo<'_> {
906 fn pdu_type(&self) -> PduType {
907 self.pdu_type
908 }
909
910 fn file_directive_type(&self) -> Option<FileDirectiveType> {
911 self.file_directive_type
912 }
913
914 fn pdu(&self) -> &[u8] {
915 self.raw_packet
916 }
917
918 fn packet_target(&self) -> Result<PacketTarget, PduError> {
919 determine_packet_target(self.raw_packet)
920 }
921}
922
923#[cfg(feature = "alloc")]
924pub mod alloc_mod {
925 use spacepackets::cfdp::{
926 pdu::{FileDirectiveType, PduError},
927 PduType,
928 };
929
930 use crate::{determine_packet_target, PacketTarget, PduProvider, PduRawWithInfo};
931
932 #[derive(Debug, PartialEq, Eq, Clone)]
933 pub struct PduOwnedWithInfo {
934 pub pdu_type: PduType,
935 pub file_directive_type: Option<FileDirectiveType>,
936 pub pdu: alloc::vec::Vec<u8>,
937 }
938
939 impl PduOwnedWithInfo {
940 pub fn new_from_raw_packet(raw_packet: &[u8]) -> Result<Self, PduError> {
941 Ok(PduRawWithInfo::new(raw_packet)?.into())
942 }
943
944 pub fn new(
945 pdu_type: PduType,
946 file_directive_type: Option<FileDirectiveType>,
947 pdu: alloc::vec::Vec<u8>,
948 ) -> Self {
949 Self {
950 pdu_type,
951 file_directive_type,
952 pdu,
953 }
954 }
955 }
956
957 impl From<PduRawWithInfo<'_>> for PduOwnedWithInfo {
958 fn from(value: PduRawWithInfo) -> Self {
959 Self::new(
960 value.pdu_type(),
961 value.file_directive_type(),
962 value.raw_packet().to_vec(),
963 )
964 }
965 }
966
967 impl PduProvider for PduOwnedWithInfo {
968 fn pdu_type(&self) -> PduType {
969 self.pdu_type
970 }
971
972 fn file_directive_type(&self) -> Option<FileDirectiveType> {
973 self.file_directive_type
974 }
975
976 fn pdu(&self) -> &[u8] {
977 &self.pdu
978 }
979
980 fn packet_target(&self) -> Result<PacketTarget, PduError> {
981 determine_packet_target(&self.pdu)
982 }
983 }
984}
985
986#[cfg(test)]
987pub(crate) mod tests {
988 use core::cell::RefCell;
989
990 use alloc::{collections::VecDeque, string::String, vec::Vec};
991 use spacepackets::{
992 cfdp::{
993 lv::Lv,
994 pdu::{
995 eof::EofPdu,
996 file_data::FileDataPdu,
997 metadata::{MetadataGenericParams, MetadataPduCreator},
998 CommonPduConfig, FileDirectiveType, PduHeader, WritablePduPacket,
999 },
1000 ChecksumType, ConditionCode, PduType, TransmissionMode,
1001 },
1002 util::{UnsignedByteField, UnsignedByteFieldU16, UnsignedByteFieldU8, UnsignedEnum},
1003 };
1004 use user::{CfdpUser, OwnedMetadataRecvdParams, TransactionFinishedParams};
1005
1006 use crate::{PacketTarget, StdCountdown};
1007
1008 use super::*;
1009
1010 pub const LOCAL_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(1);
1011 pub const REMOTE_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(2);
1012
1013 pub struct FileSegmentRecvdParamsNoSegMetadata {
1014 #[allow(dead_code)]
1015 pub id: TransactionId,
1016 pub offset: u64,
1017 pub length: usize,
1018 }
1019
1020 #[derive(Default)]
1021 pub struct TestCfdpUser {
1022 pub next_expected_seq_num: u64,
1023 pub expected_full_src_name: String,
1024 pub expected_full_dest_name: String,
1025 pub expected_file_size: u64,
1026 pub transaction_indication_call_count: u32,
1027 pub eof_sent_call_count: u32,
1028 pub eof_recvd_call_count: u32,
1029 pub finished_indic_queue: VecDeque<TransactionFinishedParams>,
1030 pub metadata_recv_queue: VecDeque<OwnedMetadataRecvdParams>,
1031 pub file_seg_recvd_queue: VecDeque<FileSegmentRecvdParamsNoSegMetadata>,
1032 }
1033
1034 impl TestCfdpUser {
1035 pub fn new(
1036 next_expected_seq_num: u64,
1037 expected_full_src_name: String,
1038 expected_full_dest_name: String,
1039 expected_file_size: u64,
1040 ) -> Self {
1041 Self {
1042 next_expected_seq_num,
1043 expected_full_src_name,
1044 expected_full_dest_name,
1045 expected_file_size,
1046 transaction_indication_call_count: 0,
1047 eof_recvd_call_count: 0,
1048 eof_sent_call_count: 0,
1049 finished_indic_queue: VecDeque::new(),
1050 metadata_recv_queue: VecDeque::new(),
1051 file_seg_recvd_queue: VecDeque::new(),
1052 }
1053 }
1054
1055 pub fn generic_id_check(&self, id: &crate::TransactionId) {
1056 assert_eq!(id.source_id, LOCAL_ID.into());
1057 assert_eq!(id.seq_num().value(), self.next_expected_seq_num);
1058 }
1059 }
1060
1061 impl CfdpUser for TestCfdpUser {
1062 fn transaction_indication(&mut self, id: &crate::TransactionId) {
1063 self.generic_id_check(id);
1064 self.transaction_indication_call_count += 1;
1065 }
1066
1067 fn eof_sent_indication(&mut self, id: &crate::TransactionId) {
1068 self.generic_id_check(id);
1069 self.eof_sent_call_count += 1;
1070 }
1071
1072 fn transaction_finished_indication(
1073 &mut self,
1074 finished_params: &crate::user::TransactionFinishedParams,
1075 ) {
1076 self.generic_id_check(&finished_params.id);
1077 self.finished_indic_queue.push_back(*finished_params);
1078 }
1079
1080 fn metadata_recvd_indication(
1081 &mut self,
1082 md_recvd_params: &crate::user::MetadataReceivedParams,
1083 ) {
1084 self.generic_id_check(&md_recvd_params.id);
1085 assert_eq!(
1086 String::from(md_recvd_params.src_file_name),
1087 self.expected_full_src_name
1088 );
1089 assert_eq!(
1090 String::from(md_recvd_params.dest_file_name),
1091 self.expected_full_dest_name
1092 );
1093 assert_eq!(md_recvd_params.msgs_to_user.len(), 0);
1094 assert_eq!(md_recvd_params.source_id, LOCAL_ID.into());
1095 assert_eq!(md_recvd_params.file_size, self.expected_file_size);
1096 self.metadata_recv_queue.push_back(md_recvd_params.into());
1097 }
1098
1099 fn file_segment_recvd_indication(
1100 &mut self,
1101 segment_recvd_params: &crate::user::FileSegmentRecvdParams,
1102 ) {
1103 self.generic_id_check(&segment_recvd_params.id);
1104 self.file_seg_recvd_queue
1105 .push_back(FileSegmentRecvdParamsNoSegMetadata {
1106 id: segment_recvd_params.id,
1107 offset: segment_recvd_params.offset,
1108 length: segment_recvd_params.length,
1109 })
1110 }
1111
1112 fn report_indication(&mut self, _id: &crate::TransactionId) {}
1113
1114 fn suspended_indication(
1115 &mut self,
1116 _id: &crate::TransactionId,
1117 _condition_code: ConditionCode,
1118 ) {
1119 panic!("unexpected suspended indication");
1120 }
1121
1122 fn resumed_indication(&mut self, _id: &crate::TransactionId, _progresss: u64) {}
1123
1124 fn fault_indication(
1125 &mut self,
1126 _id: &crate::TransactionId,
1127 _condition_code: ConditionCode,
1128 _progress: u64,
1129 ) {
1130 panic!("unexpected fault indication");
1131 }
1132
1133 fn abandoned_indication(
1134 &mut self,
1135 _id: &crate::TransactionId,
1136 _condition_code: ConditionCode,
1137 _progress: u64,
1138 ) {
1139 panic!("unexpected abandoned indication");
1140 }
1141
1142 fn eof_recvd_indication(&mut self, id: &crate::TransactionId) {
1143 self.generic_id_check(id);
1144 self.eof_recvd_call_count += 1;
1145 }
1146 }
1147
1148 #[derive(Default, Debug)]
1149 pub(crate) struct TestFaultHandler {
1150 pub notice_of_suspension_queue: VecDeque<(TransactionId, ConditionCode, u64)>,
1151 pub notice_of_cancellation_queue: VecDeque<(TransactionId, ConditionCode, u64)>,
1152 pub abandoned_queue: VecDeque<(TransactionId, ConditionCode, u64)>,
1153 pub ignored_queue: VecDeque<(TransactionId, ConditionCode, u64)>,
1154 }
1155
1156 impl UserFaultHookProvider for TestFaultHandler {
1157 fn notice_of_suspension_cb(
1158 &mut self,
1159 transaction_id: TransactionId,
1160 cond: ConditionCode,
1161 progress: u64,
1162 ) {
1163 self.notice_of_suspension_queue
1164 .push_back((transaction_id, cond, progress))
1165 }
1166
1167 fn notice_of_cancellation_cb(
1168 &mut self,
1169 transaction_id: TransactionId,
1170 cond: ConditionCode,
1171 progress: u64,
1172 ) {
1173 self.notice_of_cancellation_queue
1174 .push_back((transaction_id, cond, progress))
1175 }
1176
1177 fn abandoned_cb(
1178 &mut self,
1179 transaction_id: TransactionId,
1180 cond: ConditionCode,
1181 progress: u64,
1182 ) {
1183 self.abandoned_queue
1184 .push_back((transaction_id, cond, progress))
1185 }
1186
1187 fn ignore_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64) {
1188 self.ignored_queue
1189 .push_back((transaction_id, cond, progress))
1190 }
1191 }
1192
1193 impl TestFaultHandler {
1194 pub(crate) fn suspension_queue_empty(&self) -> bool {
1195 self.notice_of_suspension_queue.is_empty()
1196 }
1197 pub(crate) fn cancellation_queue_empty(&self) -> bool {
1198 self.notice_of_cancellation_queue.is_empty()
1199 }
1200 pub(crate) fn ignored_queue_empty(&self) -> bool {
1201 self.ignored_queue.is_empty()
1202 }
1203 pub(crate) fn abandoned_queue_empty(&self) -> bool {
1204 self.abandoned_queue.is_empty()
1205 }
1206 pub(crate) fn all_queues_empty(&self) -> bool {
1207 self.suspension_queue_empty()
1208 && self.cancellation_queue_empty()
1209 && self.ignored_queue_empty()
1210 && self.abandoned_queue_empty()
1211 }
1212 }
1213
1214 pub struct SentPdu {
1215 pub pdu_type: PduType,
1216 pub file_directive_type: Option<FileDirectiveType>,
1217 pub raw_pdu: Vec<u8>,
1218 }
1219
1220 #[derive(Default)]
1221 pub struct TestCfdpSender {
1222 pub packet_queue: RefCell<VecDeque<SentPdu>>,
1223 }
1224
1225 impl PduSendProvider for TestCfdpSender {
1226 fn send_pdu(
1227 &self,
1228 pdu_type: PduType,
1229 file_directive_type: Option<FileDirectiveType>,
1230 raw_pdu: &[u8],
1231 ) -> Result<(), GenericSendError> {
1232 self.packet_queue.borrow_mut().push_back(SentPdu {
1233 pdu_type,
1234 file_directive_type,
1235 raw_pdu: raw_pdu.to_vec(),
1236 });
1237 Ok(())
1238 }
1239 }
1240
1241 impl TestCfdpSender {
1242 pub fn retrieve_next_pdu(&self) -> Option<SentPdu> {
1243 self.packet_queue.borrow_mut().pop_front()
1244 }
1245 pub fn queue_empty(&self) -> bool {
1246 self.packet_queue.borrow_mut().is_empty()
1247 }
1248 }
1249
1250 pub fn basic_remote_cfg_table(
1251 dest_id: impl Into<UnsignedByteField>,
1252 max_packet_len: usize,
1253 crc_on_transmission_by_default: bool,
1254 ) -> StdRemoteEntityConfigProvider {
1255 let mut table = StdRemoteEntityConfigProvider::default();
1256 let remote_entity_cfg = RemoteEntityConfig::new_with_default_values(
1257 dest_id.into(),
1258 max_packet_len,
1259 true,
1260 crc_on_transmission_by_default,
1261 TransmissionMode::Unacknowledged,
1262 ChecksumType::Crc32,
1263 );
1264 table.add_config(&remote_entity_cfg);
1265 table
1266 }
1267
1268 fn generic_pdu_header() -> PduHeader {
1269 let pdu_conf = CommonPduConfig::default();
1270 PduHeader::new_no_file_data(pdu_conf, 0)
1271 }
1272
1273 #[test]
1274 fn test_transaction_id() {
1275 let transaction_id = TransactionId::new(
1276 UnsignedByteFieldU16::new(1).into(),
1277 UnsignedByteFieldU16::new(2).into(),
1278 );
1279 assert_eq!(transaction_id.source_id().value(), 1);
1280 assert_eq!(transaction_id.seq_num().value(), 2);
1281 }
1282
1283 #[test]
1284 fn test_metadata_pdu_info() {
1285 let mut buf: [u8; 128] = [0; 128];
1286 let pdu_header = generic_pdu_header();
1287 let metadata_params = MetadataGenericParams::default();
1288 let src_file_name = "hello.txt";
1289 let dest_file_name = "hello-dest.txt";
1290 let src_lv = Lv::new_from_str(src_file_name).unwrap();
1291 let dest_lv = Lv::new_from_str(dest_file_name).unwrap();
1292 let metadata_pdu =
1293 MetadataPduCreator::new_no_opts(pdu_header, metadata_params, src_lv, dest_lv);
1294 metadata_pdu
1295 .write_to_bytes(&mut buf)
1296 .expect("writing metadata PDU failed");
1297
1298 let packet_info = PduRawWithInfo::new(&buf).expect("creating packet info failed");
1299 assert_eq!(packet_info.pdu_type(), PduType::FileDirective);
1300 assert!(packet_info.file_directive_type().is_some());
1301 assert_eq!(
1302 packet_info.file_directive_type().unwrap(),
1303 FileDirectiveType::MetadataPdu
1304 );
1305 assert_eq!(
1306 packet_info.raw_packet(),
1307 &buf[0..metadata_pdu.len_written()]
1308 );
1309 assert_eq!(
1310 packet_info.packet_target().unwrap(),
1311 PacketTarget::DestEntity
1312 );
1313 }
1314
1315 #[test]
1316 fn test_filedata_pdu_info() {
1317 let mut buf: [u8; 128] = [0; 128];
1318 let pdu_header = generic_pdu_header();
1319 let file_data_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 0, &[]);
1320 file_data_pdu
1321 .write_to_bytes(&mut buf)
1322 .expect("writing file data PDU failed");
1323 let packet_info = PduRawWithInfo::new(&buf).expect("creating packet info failed");
1324 assert_eq!(
1325 packet_info.raw_packet(),
1326 &buf[0..file_data_pdu.len_written()]
1327 );
1328 assert_eq!(packet_info.pdu_type(), PduType::FileData);
1329 assert!(packet_info.file_directive_type().is_none());
1330 assert_eq!(
1331 packet_info.packet_target().unwrap(),
1332 PacketTarget::DestEntity
1333 );
1334 }
1335
1336 #[test]
1337 fn test_eof_pdu_info() {
1338 let mut buf: [u8; 128] = [0; 128];
1339 let pdu_header = generic_pdu_header();
1340 let eof_pdu = EofPdu::new_no_error(pdu_header, 0, 0);
1341 eof_pdu
1342 .write_to_bytes(&mut buf)
1343 .expect("writing file data PDU failed");
1344 let packet_info = PduRawWithInfo::new(&buf).expect("creating packet info failed");
1345 assert_eq!(packet_info.pdu_type(), PduType::FileDirective);
1346 assert!(packet_info.file_directive_type().is_some());
1347 assert_eq!(packet_info.raw_packet(), &buf[0..eof_pdu.len_written()]);
1348 assert_eq!(
1349 packet_info.file_directive_type().unwrap(),
1350 FileDirectiveType::EofPdu
1351 );
1352 }
1353
1354 #[test]
1355 fn test_std_check_timer() {
1356 let mut std_check_timer = StdCountdown::new(Duration::from_secs(1));
1357 assert!(!std_check_timer.has_expired());
1358 assert_eq!(std_check_timer.expiry_time_seconds(), 1);
1359 std::thread::sleep(Duration::from_millis(800));
1360 assert!(!std_check_timer.has_expired());
1361 std::thread::sleep(Duration::from_millis(205));
1362 assert!(std_check_timer.has_expired());
1363 std_check_timer.reset();
1364 assert!(!std_check_timer.has_expired());
1365 }
1366
1367 #[test]
1368 fn test_std_check_timer_creator() {
1369 let std_check_timer_creator = StdTimerCreator::new(Duration::from_secs(1));
1370 let check_timer = std_check_timer_creator.create_countdown(TimerContext::NakActivity {
1371 expiry_time: Duration::from_secs(1),
1372 });
1373 assert_eq!(check_timer.expiry_time_seconds(), 1);
1374 }
1375
1376 #[test]
1377 fn test_remote_cfg_provider_single() {
1378 let mut remote_entity_cfg = RemoteEntityConfig::new_with_default_values(
1379 REMOTE_ID.into(),
1380 1024,
1381 true,
1382 false,
1383 TransmissionMode::Unacknowledged,
1384 ChecksumType::Crc32,
1385 );
1386 let remote_entity_retrieved = remote_entity_cfg.get(REMOTE_ID.value()).unwrap();
1387 assert_eq!(remote_entity_retrieved.entity_id, REMOTE_ID.into());
1388 assert_eq!(remote_entity_retrieved.max_packet_len, 1024);
1389 assert!(remote_entity_retrieved.closure_requested_by_default);
1390 assert!(!remote_entity_retrieved.crc_on_transmission_by_default);
1391 assert_eq!(
1392 remote_entity_retrieved.default_crc_type,
1393 ChecksumType::Crc32
1394 );
1395 let remote_entity_mut = remote_entity_cfg.get_mut(REMOTE_ID.value()).unwrap();
1396 assert_eq!(remote_entity_mut.entity_id, REMOTE_ID.into());
1397 let dummy = RemoteEntityConfig::new_with_default_values(
1398 LOCAL_ID.into(),
1399 1024,
1400 true,
1401 false,
1402 TransmissionMode::Unacknowledged,
1403 ChecksumType::Crc32,
1404 );
1405 assert!(!remote_entity_cfg.add_config(&dummy));
1406 assert!(!remote_entity_cfg.remove_config(REMOTE_ID.value()));
1408 let remote_entity_retrieved = remote_entity_cfg.get(REMOTE_ID.value()).unwrap();
1409 assert_eq!(remote_entity_retrieved.entity_id, REMOTE_ID.into());
1410 assert!(remote_entity_cfg.get(LOCAL_ID.value()).is_none());
1412 assert!(remote_entity_cfg.get_mut(LOCAL_ID.value()).is_none());
1413 }
1414
1415 #[test]
1416 fn test_remote_cfg_provider_std() {
1417 let remote_entity_cfg = RemoteEntityConfig::new_with_default_values(
1418 REMOTE_ID.into(),
1419 1024,
1420 true,
1421 false,
1422 TransmissionMode::Unacknowledged,
1423 ChecksumType::Crc32,
1424 );
1425 let mut remote_cfg_provider = StdRemoteEntityConfigProvider::default();
1426 assert!(remote_cfg_provider.0.is_empty());
1427 remote_cfg_provider.add_config(&remote_entity_cfg);
1428 assert_eq!(remote_cfg_provider.0.len(), 1);
1429 let remote_entity_cfg_2 = RemoteEntityConfig::new_with_default_values(
1430 LOCAL_ID.into(),
1431 1024,
1432 true,
1433 false,
1434 TransmissionMode::Unacknowledged,
1435 ChecksumType::Crc32,
1436 );
1437 let cfg_0 = remote_cfg_provider.get(REMOTE_ID.value()).unwrap();
1438 assert_eq!(cfg_0.entity_id, REMOTE_ID.into());
1439 remote_cfg_provider.add_config(&remote_entity_cfg_2);
1440 assert_eq!(remote_cfg_provider.0.len(), 2);
1441 let cfg_1 = remote_cfg_provider.get(LOCAL_ID.value()).unwrap();
1442 assert_eq!(cfg_1.entity_id, LOCAL_ID.into());
1443 assert!(remote_cfg_provider.remove_config(REMOTE_ID.value()));
1444 assert_eq!(remote_cfg_provider.0.len(), 1);
1445 let cfg_1_mut = remote_cfg_provider.get_mut(LOCAL_ID.value()).unwrap();
1446 cfg_1_mut.default_crc_type = ChecksumType::Crc32C;
1447 assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value()));
1448 assert!(remote_cfg_provider.get_mut(REMOTE_ID.value()).is_none());
1449 }
1450
1451 #[test]
1452 fn test_remote_cfg_provider_vector() {
1453 let mut remote_cfg_provider = VecRemoteEntityConfigProvider::default();
1454 let remote_entity_cfg = RemoteEntityConfig::new_with_default_values(
1455 REMOTE_ID.into(),
1456 1024,
1457 true,
1458 false,
1459 TransmissionMode::Unacknowledged,
1460 ChecksumType::Crc32,
1461 );
1462 assert!(remote_cfg_provider.0.is_empty());
1463 remote_cfg_provider.add_config(&remote_entity_cfg);
1464 assert_eq!(remote_cfg_provider.0.len(), 1);
1465 let remote_entity_cfg_2 = RemoteEntityConfig::new_with_default_values(
1466 LOCAL_ID.into(),
1467 1024,
1468 true,
1469 false,
1470 TransmissionMode::Unacknowledged,
1471 ChecksumType::Crc32,
1472 );
1473 let cfg_0 = remote_cfg_provider.get(REMOTE_ID.value()).unwrap();
1474 assert_eq!(cfg_0.entity_id, REMOTE_ID.into());
1475 remote_cfg_provider.add_config(&remote_entity_cfg_2);
1476 assert_eq!(remote_cfg_provider.0.len(), 2);
1477 let cfg_1 = remote_cfg_provider.get(LOCAL_ID.value()).unwrap();
1478 assert_eq!(cfg_1.entity_id, LOCAL_ID.into());
1479 assert!(remote_cfg_provider.remove_config(REMOTE_ID.value()));
1480 assert_eq!(remote_cfg_provider.0.len(), 1);
1481 let cfg_1_mut = remote_cfg_provider.get_mut(LOCAL_ID.value()).unwrap();
1482 cfg_1_mut.default_crc_type = ChecksumType::Crc32C;
1483 assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value()));
1484 assert!(remote_cfg_provider.get_mut(REMOTE_ID.value()).is_none());
1485 }
1486
1487 #[test]
1488 fn dummy_fault_hook_test() {
1489 let mut user_hook_dummy = DummyFaultHook::default();
1490 let transaction_id = TransactionId::new(
1491 UnsignedByteFieldU8::new(0).into(),
1492 UnsignedByteFieldU8::new(0).into(),
1493 );
1494 user_hook_dummy.notice_of_cancellation_cb(transaction_id, ConditionCode::NoError, 0);
1495 user_hook_dummy.notice_of_suspension_cb(transaction_id, ConditionCode::NoError, 0);
1496 user_hook_dummy.abandoned_cb(transaction_id, ConditionCode::NoError, 0);
1497 user_hook_dummy.ignore_cb(transaction_id, ConditionCode::NoError, 0);
1498 }
1499
1500 #[test]
1501 fn dummy_pdu_provider_test() {
1502 let dummy_pdu_provider = DummyPduProvider(());
1503 assert_eq!(dummy_pdu_provider.pdu_type(), PduType::FileData);
1504 assert!(dummy_pdu_provider.file_directive_type().is_none());
1505 assert_eq!(dummy_pdu_provider.pdu(), &[]);
1506 assert_eq!(
1507 dummy_pdu_provider.packet_target(),
1508 Ok(PacketTarget::SourceEntity)
1509 );
1510 }
1511
1512 #[test]
1513 fn test_fault_handler_checksum_error_ignored_by_default() {
1514 let fault_handler = FaultHandler::new(TestFaultHandler::default());
1515 assert_eq!(
1516 fault_handler.get_fault_handler(ConditionCode::FileChecksumFailure),
1517 FaultHandlerCode::IgnoreError
1518 );
1519 }
1520
1521 #[test]
1522 fn test_fault_handler_unsupported_checksum_ignored_by_default() {
1523 let fault_handler = FaultHandler::new(TestFaultHandler::default());
1524 assert_eq!(
1525 fault_handler.get_fault_handler(ConditionCode::UnsupportedChecksumType),
1526 FaultHandlerCode::IgnoreError
1527 );
1528 }
1529
1530 #[test]
1531 fn test_fault_handler_basic() {
1532 let mut fault_handler = FaultHandler::new(TestFaultHandler::default());
1533 assert_eq!(
1534 fault_handler.get_fault_handler(ConditionCode::FileChecksumFailure),
1535 FaultHandlerCode::IgnoreError
1536 );
1537 fault_handler.set_fault_handler(
1538 ConditionCode::FileChecksumFailure,
1539 FaultHandlerCode::NoticeOfCancellation,
1540 );
1541 assert_eq!(
1542 fault_handler.get_fault_handler(ConditionCode::FileChecksumFailure),
1543 FaultHandlerCode::NoticeOfCancellation
1544 );
1545 }
1546
1547 #[test]
1548 fn transaction_id_hashable_usable_as_map_key() {
1549 let mut map = hashbrown::HashMap::new();
1550 let transaction_id_0 = TransactionId::new(
1551 UnsignedByteFieldU8::new(1).into(),
1552 UnsignedByteFieldU8::new(2).into(),
1553 );
1554 map.insert(transaction_id_0, 5_u32);
1555 }
1556}