1#![no_std]
111#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
113#[cfg(feature = "alloc")]
114extern crate alloc;
115#[cfg(any(feature = "std", test))]
116extern crate std;
117
118pub mod buf_len;
119pub mod dest;
120pub mod filestore;
121pub mod lost_segments;
122pub mod request;
123pub mod source;
124pub mod time;
125pub mod user;
126
127use crate::time::Countdown;
128use core::{cell::RefCell, fmt::Debug, hash::Hash};
129use crc::{CRC_32_ISCSI, CRC_32_ISO_HDLC, Crc};
130
131#[cfg(feature = "alloc")]
132pub use alloc_mod::*;
133use core::time::Duration;
134#[cfg(feature = "serde")]
135use serde::{Deserialize, Serialize};
136use spacepackets::{
137 cfdp::{
138 ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
139 pdu::{FileDirectiveType, PduError, PduHeader},
140 },
141 util::{UnsignedByteField, UnsignedEnum},
142};
143#[cfg(feature = "std")]
144pub use std_mod::*;
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147#[cfg_attr(feature = "defmt", derive(defmt::Format))]
148#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
149pub enum EntityType {
150 Sending,
151 Receiving,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155#[cfg_attr(feature = "defmt", derive(defmt::Format))]
156#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
157pub enum TimerContext {
158 CheckLimit {
159 local_id: UnsignedByteField,
160 remote_id: UnsignedByteField,
161 entity_type: EntityType,
162 },
163 NakActivity {
164 expiry_time: Duration,
165 },
166 PositiveAck {
167 expiry_time: Duration,
168 },
169}
170
171pub trait TimerCreator {
209 type Countdown: Countdown;
210
211 fn create_countdown(&self, timer_context: TimerContext) -> Self::Countdown;
212}
213
214#[derive(Debug, Copy, Clone)]
274#[cfg_attr(feature = "defmt", derive(defmt::Format))]
275#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
276pub struct RemoteEntityConfig {
277 pub entity_id: UnsignedByteField,
278 pub max_packet_len: usize,
279 pub max_file_segment_len: Option<usize>,
280 pub closure_requested_by_default: bool,
281 pub crc_on_transmission_by_default: bool,
282 pub default_transmission_mode: TransmissionMode,
283 pub default_crc_type: ChecksumType,
284 pub positive_ack_timer_interval: Duration,
285 pub positive_ack_timer_expiration_limit: u32,
286 pub check_limit: u32,
287 pub disposition_on_cancellation: bool,
288 pub immediate_nak_mode: bool,
289 pub nak_timer_interval: Duration,
290 pub nak_timer_expiration_limit: u32,
291}
292
293impl RemoteEntityConfig {
294 pub fn new_with_default_values(
295 entity_id: UnsignedByteField,
296 max_packet_len: usize,
297 closure_requested_by_default: bool,
298 crc_on_transmission_by_default: bool,
299 default_transmission_mode: TransmissionMode,
300 default_crc_type: ChecksumType,
301 ) -> Self {
302 Self {
303 entity_id,
304 max_file_segment_len: None,
305 max_packet_len,
306 closure_requested_by_default,
307 crc_on_transmission_by_default,
308 default_transmission_mode,
309 default_crc_type,
310 check_limit: 2,
311 positive_ack_timer_interval: Duration::from_secs(10),
312 positive_ack_timer_expiration_limit: 2,
313 disposition_on_cancellation: false,
314 immediate_nak_mode: true,
315 nak_timer_interval: Duration::from_secs(10),
316 nak_timer_expiration_limit: 2,
317 }
318 }
319}
320
321#[derive(Debug, PartialEq, Eq, thiserror::Error)]
322#[cfg_attr(feature = "defmt", derive(defmt::Format))]
323#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
324pub enum RemoteConfigStoreError {
325 #[error("store is full")]
326 Full,
327}
328
329pub trait RemoteConfigStore {
330 fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig>;
332
333 fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig>;
334
335 fn add_config(&mut self, cfg: &RemoteEntityConfig) -> Result<bool, RemoteConfigStoreError>;
338}
339
340#[cfg(feature = "alloc")]
343#[derive(Default, Debug)]
344#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
345pub struct RemoteConfigStoreStd(pub hashbrown::HashMap<u64, RemoteEntityConfig>);
346
347#[cfg(feature = "std")]
348impl RemoteConfigStore for RemoteConfigStoreStd {
349 fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
350 self.0.get(&remote_id)
351 }
352
353 fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
354 self.0.get_mut(&remote_id)
355 }
356
357 fn add_config(&mut self, cfg: &RemoteEntityConfig) -> Result<bool, RemoteConfigStoreError> {
358 Ok(self.0.insert(cfg.entity_id.value(), *cfg).is_some())
359 }
360}
361
362#[cfg(feature = "std")]
363impl RemoteConfigStoreStd {
364 pub fn remove_config(&mut self, remote_id: u64) -> bool {
365 self.0.remove(&remote_id).is_some()
366 }
367}
368
369#[cfg(feature = "alloc")]
372#[derive(Default, Debug)]
373#[cfg_attr(feature = "defmt", derive(defmt::Format))]
374#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
375pub struct RemoteConfigList(pub alloc::vec::Vec<RemoteEntityConfig>);
376
377#[cfg(feature = "alloc")]
378impl RemoteConfigStore for RemoteConfigList {
379 fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
380 self.0
381 .iter()
382 .find(|&cfg| cfg.entity_id.value() == remote_id)
383 }
384
385 fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
386 self.0
387 .iter_mut()
388 .find(|cfg| cfg.entity_id.value() == remote_id)
389 }
390
391 fn add_config(&mut self, cfg: &RemoteEntityConfig) -> Result<bool, RemoteConfigStoreError> {
392 for other_cfg in self.0.iter() {
393 if cfg.entity_id.value() == other_cfg.entity_id.value() {
394 return Ok(false);
395 }
396 }
397 self.0.push(*cfg);
398 Ok(true)
399 }
400}
401
402#[cfg(feature = "alloc")]
403impl RemoteConfigList {
404 pub fn remove_config(&mut self, remote_id: u64) -> bool {
405 for (idx, cfg) in self.0.iter().enumerate() {
406 if cfg.entity_id.value() == remote_id {
407 self.0.remove(idx);
408 return true;
409 }
410 }
411 false
412 }
413}
414
415#[derive(Default, Debug)]
418#[cfg_attr(feature = "defmt", derive(defmt::Format))]
419#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
420pub struct RemoteConfigListHeapless<const N: usize>(pub heapless::vec::Vec<RemoteEntityConfig, N>);
421
422impl<const N: usize> RemoteConfigStore for RemoteConfigListHeapless<N> {
423 fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
424 self.0
425 .iter()
426 .find(|&cfg| cfg.entity_id.value() == remote_id)
427 }
428
429 fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
430 self.0
431 .iter_mut()
432 .find(|cfg| cfg.entity_id.value() == remote_id)
433 }
434
435 fn add_config(&mut self, cfg: &RemoteEntityConfig) -> Result<bool, RemoteConfigStoreError> {
436 if self.0.is_full() {
437 return Err(RemoteConfigStoreError::Full);
438 }
439 for other_cfg in self.0.iter() {
440 if cfg.entity_id.value() == other_cfg.entity_id.value() {
441 return Ok(false);
442 }
443 }
444 self.0.push(*cfg).unwrap();
445 Ok(true)
446 }
447}
448
449impl<const N: usize> RemoteConfigListHeapless<N> {
450 pub fn remove_config(&mut self, remote_id: u64) -> bool {
451 for (idx, cfg) in self.0.iter().enumerate() {
452 if cfg.entity_id.value() == remote_id {
453 self.0.remove(idx);
454 return true;
455 }
456 }
457 false
458 }
459}
460
461impl RemoteConfigStore for RemoteEntityConfig {
464 fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
465 if remote_id == self.entity_id.value() {
466 return Some(self);
467 }
468 None
469 }
470
471 fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
472 if remote_id == self.entity_id.value() {
473 return Some(self);
474 }
475 None
476 }
477
478 fn add_config(&mut self, _cfg: &RemoteEntityConfig) -> Result<bool, RemoteConfigStoreError> {
479 Err(RemoteConfigStoreError::Full)
480 }
481}
482
483pub trait UserFaultHook {
494 fn notice_of_suspension_cb(&mut self, fault_info: FaultInfo);
495
496 fn notice_of_cancellation_cb(&mut self, fault_info: FaultInfo);
497
498 fn abandoned_cb(&mut self, fault_info: FaultInfo);
499
500 fn ignore_cb(&mut self, fault_info: FaultInfo);
501}
502
503#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
506pub struct DummyFaultHook {}
507
508impl UserFaultHook for DummyFaultHook {
509 fn notice_of_suspension_cb(&mut self, _fault_info: FaultInfo) {}
510
511 fn notice_of_cancellation_cb(&mut self, _fault_info: FaultInfo) {}
512
513 fn abandoned_cb(&mut self, _fault_info: FaultInfo) {}
514
515 fn ignore_cb(&mut self, _fault_info: FaultInfo) {}
516}
517
518pub struct FaultHandler<UserHandler: UserFaultHook> {
539 handler_array: [FaultHandlerCode; 10],
540 pub user_hook: RefCell<UserHandler>,
543}
544
545#[derive(Debug, Copy, Clone, PartialEq, Eq)]
546#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
547pub struct FaultInfo {
548 transaction_id: TransactionId,
549 condition_code: ConditionCode,
550 progress: u64,
551}
552
553impl FaultInfo {
554 pub const fn new(
555 transaction_id: TransactionId,
556 condition_code: ConditionCode,
557 progress: u64,
558 ) -> Self {
559 Self {
560 transaction_id,
561 condition_code,
562 progress,
563 }
564 }
565
566 #[inline]
567 pub const fn transaction_id(&self) -> TransactionId {
568 self.transaction_id
569 }
570
571 #[inline]
572 pub const fn condition_code(&self) -> ConditionCode {
573 self.condition_code
574 }
575
576 #[inline]
577 pub const fn progress(&self) -> u64 {
578 self.progress
579 }
580}
581
582impl<UserHandler: UserFaultHook> FaultHandler<UserHandler> {
583 fn condition_code_to_array_index(conditon_code: ConditionCode) -> Option<usize> {
584 Some(match conditon_code {
585 ConditionCode::PositiveAckLimitReached => 0,
586 ConditionCode::KeepAliveLimitReached => 1,
587 ConditionCode::InvalidTransmissionMode => 2,
588 ConditionCode::FilestoreRejection => 3,
589 ConditionCode::FileChecksumFailure => 4,
590 ConditionCode::FileSizeError => 5,
591 ConditionCode::NakLimitReached => 6,
592 ConditionCode::InactivityDetected => 7,
593 ConditionCode::CheckLimitReached => 8,
594 ConditionCode::UnsupportedChecksumType => 9,
595 _ => return None,
596 })
597 }
598
599 pub fn set_fault_handler(
600 &mut self,
601 condition_code: ConditionCode,
602 fault_handler: FaultHandlerCode,
603 ) {
604 let array_idx = Self::condition_code_to_array_index(condition_code);
605 if array_idx.is_none() {
606 return;
607 }
608 self.handler_array[array_idx.unwrap()] = fault_handler;
609 }
610
611 pub fn new(user_fault_handler: UserHandler) -> Self {
612 let mut init_array = [FaultHandlerCode::NoticeOfCancellation; 10];
613 init_array
614 [Self::condition_code_to_array_index(ConditionCode::FileChecksumFailure).unwrap()] =
615 FaultHandlerCode::IgnoreError;
616 init_array[Self::condition_code_to_array_index(ConditionCode::UnsupportedChecksumType)
617 .unwrap()] = FaultHandlerCode::IgnoreError;
618 Self {
619 handler_array: init_array,
620 user_hook: RefCell::new(user_fault_handler),
621 }
622 }
623
624 pub fn get_fault_handler(&self, condition_code: ConditionCode) -> FaultHandlerCode {
625 let array_idx = Self::condition_code_to_array_index(condition_code);
626 if array_idx.is_none() {
627 return FaultHandlerCode::IgnoreError;
628 }
629 self.handler_array[array_idx.unwrap()]
630 }
631
632 pub fn report_fault(&self, code: FaultHandlerCode, fault_info: FaultInfo) -> FaultHandlerCode {
633 let mut handler_mut = self.user_hook.borrow_mut();
634 match code {
635 FaultHandlerCode::NoticeOfCancellation => {
636 handler_mut.notice_of_cancellation_cb(fault_info);
637 }
638 FaultHandlerCode::NoticeOfSuspension => {
639 handler_mut.notice_of_suspension_cb(fault_info);
640 }
641 FaultHandlerCode::IgnoreError => {
642 handler_mut.ignore_cb(fault_info);
643 }
644 FaultHandlerCode::AbandonTransaction => {
645 handler_mut.abandoned_cb(fault_info);
646 }
647 }
648 code
649 }
650}
651
652#[derive(Debug, Clone, Copy, PartialEq, Eq)]
653#[cfg_attr(feature = "defmt", derive(defmt::Format))]
654#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
655pub struct IndicationConfig {
656 pub eof_sent: bool,
657 pub eof_recv: bool,
658 pub file_segment_recv: bool,
659 pub transaction_finished: bool,
660 pub suspended: bool,
661 pub resumed: bool,
662}
663
664impl Default for IndicationConfig {
665 fn default() -> Self {
666 Self {
667 eof_sent: true,
668 eof_recv: true,
669 file_segment_recv: true,
670 transaction_finished: true,
671 suspended: true,
672 resumed: true,
673 }
674 }
675}
676
677pub struct LocalEntityConfig<UserFaultHookInstance: UserFaultHook> {
679 pub id: UnsignedByteField,
680 pub indication_cfg: IndicationConfig,
681 pub fault_handler: FaultHandler<UserFaultHookInstance>,
682}
683
684impl<UserFaultHookInstance: UserFaultHook> LocalEntityConfig<UserFaultHookInstance> {
685 pub fn new(
686 id: UnsignedByteField,
687 indication_cfg: IndicationConfig,
688 hook: UserFaultHookInstance,
689 ) -> Self {
690 Self {
691 id,
692 indication_cfg,
693 fault_handler: FaultHandler::new(hook),
694 }
695 }
696}
697
698impl<UserFaultHookInstance: UserFaultHook> LocalEntityConfig<UserFaultHookInstance> {
699 pub fn user_fault_hook_mut(&mut self) -> &mut RefCell<UserFaultHookInstance> {
700 &mut self.fault_handler.user_hook
701 }
702
703 pub fn user_fault_hook(&self) -> &RefCell<UserFaultHookInstance> {
704 &self.fault_handler.user_hook
705 }
706}
707
708#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
710#[non_exhaustive]
711pub enum GenericSendError {
712 #[error("RX disconnected")]
713 RxDisconnected,
714 #[error("queue is full, fill count {0:?}")]
715 QueueFull(Option<u32>),
716 #[error("other send error")]
717 Other,
718}
719
720pub trait PduSender {
721 fn send_pdu(
722 &self,
723 pdu_type: PduType,
724 file_directive_type: Option<FileDirectiveType>,
725 raw_pdu: &[u8],
726 ) -> Result<(), GenericSendError>;
727
728 fn send_file_directive_pdu(
729 &self,
730 file_directive_type: FileDirectiveType,
731 raw_pdu: &[u8],
732 ) -> Result<(), GenericSendError> {
733 self.send_pdu(PduType::FileDirective, Some(file_directive_type), raw_pdu)
734 }
735}
736
737#[cfg(feature = "std")]
738pub mod std_mod {
739 use std::sync::mpsc;
740
741 use super::*;
742
743 impl PduSender for mpsc::Sender<PduOwnedWithInfo> {
744 fn send_pdu(
745 &self,
746 pdu_type: PduType,
747 file_directive_type: Option<FileDirectiveType>,
748 raw_pdu: &[u8],
749 ) -> Result<(), GenericSendError> {
750 self.send(PduOwnedWithInfo::new(
751 pdu_type,
752 file_directive_type,
753 raw_pdu.to_vec(),
754 ))
755 .map_err(|_| GenericSendError::RxDisconnected)?;
756 Ok(())
757 }
758 }
759
760 #[derive(Debug)]
762 pub struct StdCountdown {
763 expiry_time: Duration,
764 start_time: std::time::Instant,
765 }
766
767 impl StdCountdown {
768 pub fn new(expiry_time: Duration) -> Self {
769 Self {
770 expiry_time,
771 start_time: std::time::Instant::now(),
772 }
773 }
774
775 pub fn expiry_time_seconds(&self) -> u64 {
776 self.expiry_time.as_secs()
777 }
778 }
779
780 impl Countdown for StdCountdown {
781 fn has_expired(&self) -> bool {
782 if self.start_time.elapsed() > self.expiry_time {
783 return true;
784 }
785 false
786 }
787
788 fn reset(&mut self) {
789 self.start_time = std::time::Instant::now();
790 }
791 }
792
793 pub struct StdTimerCreator {
794 pub check_limit_timeout: Duration,
795 }
796
797 impl StdTimerCreator {
798 pub const fn new(check_limit_timeout: Duration) -> Self {
799 Self {
800 check_limit_timeout,
801 }
802 }
803 }
804
805 impl Default for StdTimerCreator {
806 fn default() -> Self {
807 Self::new(Duration::from_secs(5))
808 }
809 }
810
811 impl TimerCreator for StdTimerCreator {
812 type Countdown = StdCountdown;
813
814 fn create_countdown(&self, timer_context: TimerContext) -> Self::Countdown {
815 match timer_context {
816 TimerContext::CheckLimit {
817 local_id: _,
818 remote_id: _,
819 entity_type: _,
820 } => StdCountdown::new(self.check_limit_timeout),
821 TimerContext::NakActivity { expiry_time } => StdCountdown::new(expiry_time),
822 TimerContext::PositiveAck { expiry_time } => StdCountdown::new(expiry_time),
823 }
824 }
825 }
826}
827
828#[derive(Debug, Eq, Copy, Clone)]
831#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
832#[cfg_attr(feature = "defmt", derive(defmt::Format))]
833pub struct TransactionId {
834 source_id: UnsignedByteField,
835 seq_num: UnsignedByteField,
836}
837
838impl TransactionId {
839 pub fn new(source_id: UnsignedByteField, seq_num: UnsignedByteField) -> Self {
840 Self { source_id, seq_num }
841 }
842
843 pub fn source_id(&self) -> &UnsignedByteField {
844 &self.source_id
845 }
846
847 pub fn seq_num(&self) -> &UnsignedByteField {
848 &self.seq_num
849 }
850}
851
852impl Hash for TransactionId {
853 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
854 self.source_id.value().hash(state);
855 self.seq_num.value().hash(state);
856 }
857}
858
859impl PartialEq for TransactionId {
860 fn eq(&self, other: &Self) -> bool {
861 self.source_id.value() == other.source_id.value()
862 && self.seq_num.value() == other.seq_num.value()
863 }
864}
865
866#[derive(Debug, Copy, Clone, PartialEq, Eq)]
867#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
868pub enum State {
869 Idle = 0,
870 Busy = 1,
871 Suspended = 2,
872}
873
874pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
879pub const CRC_32C: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
884
885#[derive(Debug, PartialEq, Eq, Copy, Clone)]
886#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
887pub enum PacketTarget {
888 SourceEntity,
889 DestEntity,
890}
891
892pub trait PduProvider {
895 fn pdu_type(&self) -> PduType;
896 fn file_directive_type(&self) -> Option<FileDirectiveType>;
897 fn raw_pdu(&self) -> &[u8];
898 fn packet_target(&self) -> Result<PacketTarget, PduError>;
899}
900
901pub struct DummyPduProvider(());
902
903impl PduProvider for DummyPduProvider {
904 fn pdu_type(&self) -> PduType {
905 PduType::FileData
906 }
907
908 fn file_directive_type(&self) -> Option<FileDirectiveType> {
909 None
910 }
911
912 fn raw_pdu(&self) -> &[u8] {
913 &[]
914 }
915
916 fn packet_target(&self) -> Result<PacketTarget, PduError> {
917 Ok(PacketTarget::SourceEntity)
918 }
919}
920
921pub struct PduRawWithInfo<'raw_packet> {
925 pdu_type: PduType,
926 file_directive_type: Option<FileDirectiveType>,
927 packet_len: usize,
928 raw_packet: &'raw_packet [u8],
929}
930
931pub fn determine_packet_target(raw_pdu: &[u8]) -> Result<PacketTarget, PduError> {
932 let (header, header_len) = PduHeader::from_bytes(raw_pdu)?;
933 if header.pdu_type() == PduType::FileData {
934 return Ok(PacketTarget::DestEntity);
935 }
936 let file_directive_type = FileDirectiveType::try_from(raw_pdu[header_len]).map_err(|_| {
937 PduError::InvalidDirectiveType {
938 found: raw_pdu[header_len],
939 expected: None,
940 }
941 })?;
942 let packet_target = match file_directive_type {
943 FileDirectiveType::NakPdu
946 | FileDirectiveType::FinishedPdu
947 | FileDirectiveType::KeepAlivePdu => PacketTarget::SourceEntity,
948 FileDirectiveType::MetadataPdu
951 | FileDirectiveType::EofPdu
952 | FileDirectiveType::PromptPdu => PacketTarget::DestEntity,
953 FileDirectiveType::AckPdu => {
957 let acked_directive = FileDirectiveType::try_from(raw_pdu[header_len + 1] >> 4)
958 .map_err(|_| PduError::InvalidDirectiveType {
959 found: (raw_pdu[header_len + 1] >> 4),
960 expected: None,
961 })?;
962 if acked_directive == FileDirectiveType::EofPdu {
963 PacketTarget::SourceEntity
964 } else if acked_directive == FileDirectiveType::FinishedPdu {
965 PacketTarget::DestEntity
966 } else {
967 return Err(PduError::InvalidDirectiveType {
969 found: raw_pdu[header_len + 1],
970 expected: None,
971 });
972 }
973 }
974 };
975 Ok(packet_target)
976}
977
978impl<'raw> PduRawWithInfo<'raw> {
979 pub fn new(raw_packet: &'raw [u8]) -> Result<Self, PduError> {
980 let (pdu_header, header_len) = PduHeader::from_bytes(raw_packet)?;
981 if pdu_header.pdu_type() == PduType::FileData {
982 return Ok(Self {
983 pdu_type: pdu_header.pdu_type(),
984 file_directive_type: None,
985 packet_len: pdu_header.pdu_len(),
986 raw_packet,
987 });
988 }
989 if pdu_header.pdu_datafield_len() < 1 {
990 return Err(PduError::Format);
991 }
992 let directive = FileDirectiveType::try_from(raw_packet[header_len]).map_err(|_| {
997 PduError::InvalidDirectiveType {
998 found: raw_packet[header_len],
999 expected: None,
1000 }
1001 })?;
1002 Ok(Self {
1003 pdu_type: pdu_header.pdu_type(),
1004 file_directive_type: Some(directive),
1005 packet_len: pdu_header.pdu_len(),
1006 raw_packet,
1007 })
1008 }
1009
1010 pub fn raw_packet(&self) -> &[u8] {
1011 &self.raw_packet[0..self.packet_len]
1012 }
1013}
1014
1015impl PduProvider for PduRawWithInfo<'_> {
1016 fn pdu_type(&self) -> PduType {
1017 self.pdu_type
1018 }
1019
1020 fn file_directive_type(&self) -> Option<FileDirectiveType> {
1021 self.file_directive_type
1022 }
1023
1024 fn raw_pdu(&self) -> &[u8] {
1025 self.raw_packet
1026 }
1027
1028 fn packet_target(&self) -> Result<PacketTarget, PduError> {
1029 determine_packet_target(self.raw_packet)
1030 }
1031}
1032
1033#[cfg(feature = "alloc")]
1034pub mod alloc_mod {
1035 use spacepackets::cfdp::{
1036 PduType,
1037 pdu::{FileDirectiveType, PduError},
1038 };
1039
1040 use crate::{PacketTarget, PduProvider, PduRawWithInfo, determine_packet_target};
1041
1042 #[derive(Debug, PartialEq, Eq, Clone)]
1043 pub struct PduOwnedWithInfo {
1044 pub pdu_type: PduType,
1045 pub file_directive_type: Option<FileDirectiveType>,
1046 pub pdu: alloc::vec::Vec<u8>,
1047 }
1048
1049 impl PduOwnedWithInfo {
1050 pub fn new_from_raw_packet(raw_packet: &[u8]) -> Result<Self, PduError> {
1051 Ok(PduRawWithInfo::new(raw_packet)?.into())
1052 }
1053
1054 pub fn new(
1055 pdu_type: PduType,
1056 file_directive_type: Option<FileDirectiveType>,
1057 pdu: alloc::vec::Vec<u8>,
1058 ) -> Self {
1059 Self {
1060 pdu_type,
1061 file_directive_type,
1062 pdu,
1063 }
1064 }
1065 }
1066
1067 impl From<PduRawWithInfo<'_>> for PduOwnedWithInfo {
1068 fn from(value: PduRawWithInfo) -> Self {
1069 Self::new(
1070 value.pdu_type(),
1071 value.file_directive_type(),
1072 value.raw_packet().to_vec(),
1073 )
1074 }
1075 }
1076
1077 impl PduProvider for PduOwnedWithInfo {
1078 fn pdu_type(&self) -> PduType {
1079 self.pdu_type
1080 }
1081
1082 fn file_directive_type(&self) -> Option<FileDirectiveType> {
1083 self.file_directive_type
1084 }
1085
1086 fn raw_pdu(&self) -> &[u8] {
1087 &self.pdu
1088 }
1089
1090 fn packet_target(&self) -> Result<PacketTarget, PduError> {
1091 determine_packet_target(&self.pdu)
1092 }
1093 }
1094}
1095
1096#[derive(Debug, Clone, Copy)]
1097struct PositiveAckParams {
1098 ack_counter: u32,
1099 positive_ack_of_cancellation: bool,
1100}
1101
1102#[cfg(test)]
1103pub(crate) mod tests {
1104 use core::{
1105 cell::{Cell, RefCell},
1106 sync::atomic::AtomicBool,
1107 };
1108 use std::{println, sync::Arc};
1109
1110 use alloc::{collections::VecDeque, string::String, vec::Vec};
1111 use spacepackets::{
1112 cfdp::{
1113 ChecksumType, ConditionCode, PduType, TransmissionMode,
1114 lv::Lv,
1115 pdu::{
1116 CommonPduConfig, FileDirectiveType, PduHeader,
1117 eof::EofPdu,
1118 file_data::FileDataPdu,
1119 finished::{DeliveryCode, FileStatus},
1120 metadata::{MetadataGenericParams, MetadataPduCreator},
1121 },
1122 },
1123 util::{UnsignedByteField, UnsignedByteFieldU8, UnsignedByteFieldU16, UnsignedEnum},
1124 };
1125 use user::{CfdpUser, OwnedMetadataRecvdParams, TransactionFinishedParams};
1126
1127 use crate::{PacketTarget, StdCountdown};
1128
1129 use super::*;
1130
1131 pub const LOCAL_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(1);
1132 pub const REMOTE_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(2);
1133
1134 #[derive(Debug, Default, Clone)]
1136 pub(crate) struct TimerExpiryControl {
1137 pub(crate) check_limit: Arc<AtomicBool>,
1138 pub(crate) positive_ack: Arc<AtomicBool>,
1139 pub(crate) nak_activity: Arc<AtomicBool>,
1140 }
1141
1142 impl TimerExpiryControl {
1143 pub fn set_check_limit_expired(&mut self) {
1144 self.check_limit
1145 .store(true, core::sync::atomic::Ordering::Release);
1146 }
1147
1148 pub fn set_positive_ack_expired(&mut self) {
1149 self.positive_ack
1150 .store(true, core::sync::atomic::Ordering::Release);
1151 }
1152
1153 pub fn set_nak_activity_expired(&mut self) {
1154 self.nak_activity
1155 .store(true, core::sync::atomic::Ordering::Release);
1156 }
1157 }
1158
1159 #[derive(Debug)]
1160 pub(crate) struct TestCheckTimer {
1161 counter: Cell<u32>,
1162 context: TimerContext,
1163 expiry_control: TimerExpiryControl,
1164 }
1165
1166 impl Countdown for TestCheckTimer {
1167 fn has_expired(&self) -> bool {
1168 match self.context {
1169 TimerContext::CheckLimit {
1170 local_id: _,
1171 remote_id: _,
1172 entity_type: _,
1173 } => self
1174 .expiry_control
1175 .check_limit
1176 .load(core::sync::atomic::Ordering::Acquire),
1177 TimerContext::PositiveAck { expiry_time: _ } => self
1178 .expiry_control
1179 .positive_ack
1180 .load(core::sync::atomic::Ordering::Acquire),
1181 TimerContext::NakActivity { expiry_time: _ } => self
1182 .expiry_control
1183 .nak_activity
1184 .load(core::sync::atomic::Ordering::Acquire),
1185 }
1186 }
1187 fn reset(&mut self) {
1188 match self.context {
1189 TimerContext::CheckLimit {
1190 local_id: _,
1191 remote_id: _,
1192 entity_type: _,
1193 } => self
1194 .expiry_control
1195 .check_limit
1196 .store(false, core::sync::atomic::Ordering::Release),
1197 TimerContext::NakActivity { expiry_time: _ } => self
1198 .expiry_control
1199 .nak_activity
1200 .store(false, core::sync::atomic::Ordering::Release),
1201 TimerContext::PositiveAck { expiry_time: _ } => self
1202 .expiry_control
1203 .positive_ack
1204 .store(false, core::sync::atomic::Ordering::Release),
1205 }
1206 self.counter.set(0);
1207 }
1208 }
1209
1210 impl TestCheckTimer {
1211 pub fn new(context: TimerContext, expiry_control: &TimerExpiryControl) -> Self {
1212 Self {
1213 counter: Cell::new(0),
1214 context,
1215 expiry_control: expiry_control.clone(),
1216 }
1217 }
1218 }
1219
1220 pub(crate) struct TestCheckTimerCreator {
1221 expiry_control: TimerExpiryControl,
1222 }
1223
1224 impl TestCheckTimerCreator {
1225 pub fn new(expiry_control: &TimerExpiryControl) -> Self {
1226 Self {
1227 expiry_control: expiry_control.clone(),
1228 }
1229 }
1230 }
1231
1232 impl TimerCreator for TestCheckTimerCreator {
1233 type Countdown = TestCheckTimer;
1234
1235 fn create_countdown(&self, timer_context: TimerContext) -> Self::Countdown {
1236 match timer_context {
1237 TimerContext::CheckLimit { .. } => {
1238 TestCheckTimer::new(timer_context, &self.expiry_control)
1239 }
1240 TimerContext::PositiveAck { expiry_time: _ } => {
1241 TestCheckTimer::new(timer_context, &self.expiry_control)
1242 }
1243 TimerContext::NakActivity { expiry_time: _ } => {
1244 TestCheckTimer::new(timer_context, &self.expiry_control)
1245 }
1246 }
1247 }
1248 }
1249
1250 #[derive(Debug)]
1251 pub struct FileSegmentRecvdParamsNoSegMetadata {
1252 #[allow(dead_code)]
1253 pub id: TransactionId,
1254 pub offset: u64,
1255 pub length: usize,
1256 }
1257
1258 #[derive(Default, Debug)]
1259 pub struct TestCfdpUser {
1260 pub check_queues_empty_on_drop: bool,
1261 pub next_expected_seq_num: u64,
1262 pub expected_full_src_name: String,
1263 pub expected_full_dest_name: String,
1264 pub expected_file_size: u64,
1265 pub transaction_indication_call_count: u32,
1266 pub eof_sent_call_count: u32,
1267 pub eof_recvd_call_count: u32,
1268 pub finished_indic_queue: VecDeque<TransactionFinishedParams>,
1269 pub metadata_recv_queue: VecDeque<OwnedMetadataRecvdParams>,
1270 pub file_seg_recvd_queue: VecDeque<FileSegmentRecvdParamsNoSegMetadata>,
1271 }
1272
1273 impl TestCfdpUser {
1274 pub fn new(
1275 next_expected_seq_num: u64,
1276 expected_full_src_name: String,
1277 expected_full_dest_name: String,
1278 expected_file_size: u64,
1279 ) -> Self {
1280 Self {
1281 check_queues_empty_on_drop: true,
1282 next_expected_seq_num,
1283 expected_full_src_name,
1284 expected_full_dest_name,
1285 expected_file_size,
1286 transaction_indication_call_count: 0,
1287 eof_recvd_call_count: 0,
1288 eof_sent_call_count: 0,
1289 finished_indic_queue: VecDeque::new(),
1290 metadata_recv_queue: VecDeque::new(),
1291 file_seg_recvd_queue: VecDeque::new(),
1292 }
1293 }
1294
1295 pub fn generic_id_check(&self, id: &crate::TransactionId) {
1296 assert_eq!(id.source_id, LOCAL_ID.into());
1297 assert_eq!(id.seq_num().value(), self.next_expected_seq_num);
1298 }
1299
1300 pub fn indication_queues_empty(&self) -> bool {
1301 self.finished_indic_queue.is_empty()
1302 && self.metadata_recv_queue.is_empty()
1303 && self.file_seg_recvd_queue.is_empty()
1304 }
1305
1306 pub fn verify_finished_indication_retained(
1307 &mut self,
1308 delivery_code: DeliveryCode,
1309 cond_code: ConditionCode,
1310 id: TransactionId,
1311 ) {
1312 self.verify_finished_indication(delivery_code, cond_code, id, FileStatus::Retained);
1313 }
1314
1315 pub fn verify_finished_indication(
1316 &mut self,
1317 delivery_code: DeliveryCode,
1318 cond_code: ConditionCode,
1319 id: TransactionId,
1320 file_status: FileStatus,
1321 ) {
1322 assert_eq!(self.finished_indic_queue.len(), 1);
1323 let finished_indication = self.finished_indic_queue.pop_front().unwrap();
1324 assert_eq!(finished_indication.id, id);
1325 assert_eq!(finished_indication.condition_code, cond_code);
1326 assert_eq!(finished_indication.delivery_code, delivery_code);
1327 assert_eq!(finished_indication.file_status, file_status);
1328 }
1329 }
1330
1331 impl CfdpUser for TestCfdpUser {
1332 fn transaction_indication(&mut self, id: &crate::TransactionId) {
1333 self.generic_id_check(id);
1334 self.transaction_indication_call_count += 1;
1335 }
1336
1337 fn eof_sent_indication(&mut self, id: &crate::TransactionId) {
1338 self.generic_id_check(id);
1339 self.eof_sent_call_count += 1;
1340 }
1341
1342 fn transaction_finished_indication(
1343 &mut self,
1344 finished_params: &crate::user::TransactionFinishedParams,
1345 ) {
1346 self.generic_id_check(&finished_params.id);
1347 self.finished_indic_queue.push_back(*finished_params);
1348 }
1349
1350 fn metadata_recvd_indication(
1351 &mut self,
1352 md_recvd_params: &crate::user::MetadataReceivedParams,
1353 ) {
1354 self.generic_id_check(&md_recvd_params.id);
1355 assert_eq!(
1356 String::from(md_recvd_params.src_file_name),
1357 self.expected_full_src_name
1358 );
1359 assert_eq!(
1360 String::from(md_recvd_params.dest_file_name),
1361 self.expected_full_dest_name
1362 );
1363 assert_eq!(md_recvd_params.msgs_to_user.len(), 0);
1364 assert_eq!(md_recvd_params.source_id, LOCAL_ID.into());
1365 assert_eq!(md_recvd_params.file_size, self.expected_file_size);
1366 self.metadata_recv_queue.push_back(md_recvd_params.into());
1367 }
1368
1369 fn file_segment_recvd_indication(
1370 &mut self,
1371 segment_recvd_params: &crate::user::FileSegmentRecvdParams,
1372 ) {
1373 self.generic_id_check(&segment_recvd_params.id);
1374 self.file_seg_recvd_queue
1375 .push_back(FileSegmentRecvdParamsNoSegMetadata {
1376 id: segment_recvd_params.id,
1377 offset: segment_recvd_params.offset,
1378 length: segment_recvd_params.length,
1379 })
1380 }
1381
1382 fn report_indication(&mut self, _id: &crate::TransactionId) {}
1383
1384 fn suspended_indication(
1385 &mut self,
1386 _id: &crate::TransactionId,
1387 _condition_code: ConditionCode,
1388 ) {
1389 panic!("unexpected suspended indication");
1390 }
1391
1392 fn resumed_indication(&mut self, _id: &crate::TransactionId, _progresss: u64) {}
1393
1394 fn fault_indication(
1395 &mut self,
1396 _id: &crate::TransactionId,
1397 _condition_code: ConditionCode,
1398 _progress: u64,
1399 ) {
1400 panic!("unexpected fault indication");
1401 }
1402
1403 fn abandoned_indication(
1404 &mut self,
1405 _id: &crate::TransactionId,
1406 _condition_code: ConditionCode,
1407 _progress: u64,
1408 ) {
1409 panic!("unexpected abandoned indication");
1410 }
1411
1412 fn eof_recvd_indication(&mut self, id: &crate::TransactionId) {
1413 self.generic_id_check(id);
1414 self.eof_recvd_call_count += 1;
1415 }
1416 }
1417
1418 impl Drop for TestCfdpUser {
1419 fn drop(&mut self) {
1420 if self.check_queues_empty_on_drop {
1421 assert!(
1422 self.indication_queues_empty(),
1423 "indication queues not empty on drop: finished: {}, metadata: {}, file seg: {}",
1424 self.finished_indic_queue.len(),
1425 self.metadata_recv_queue.len(),
1426 self.file_seg_recvd_queue.len()
1427 );
1428 }
1429 }
1430 }
1431
1432 #[derive(Default, Debug)]
1433 pub(crate) struct TestFaultHandler {
1434 pub notice_of_suspension_queue: VecDeque<FaultInfo>,
1435 pub notice_of_cancellation_queue: VecDeque<FaultInfo>,
1436 pub abandoned_queue: VecDeque<FaultInfo>,
1437 pub ignored_queue: VecDeque<FaultInfo>,
1438 }
1439
1440 impl UserFaultHook for TestFaultHandler {
1441 fn notice_of_suspension_cb(&mut self, fault_info: FaultInfo) {
1442 self.notice_of_suspension_queue.push_back(fault_info)
1443 }
1444
1445 fn notice_of_cancellation_cb(&mut self, fault_info: FaultInfo) {
1446 self.notice_of_cancellation_queue.push_back(fault_info)
1447 }
1448
1449 fn abandoned_cb(&mut self, fault_info: FaultInfo) {
1450 self.abandoned_queue.push_back(fault_info)
1451 }
1452
1453 fn ignore_cb(&mut self, fault_info: FaultInfo) {
1454 self.ignored_queue.push_back(fault_info)
1455 }
1456 }
1457
1458 impl TestFaultHandler {
1459 pub(crate) fn suspension_queue_empty(&self) -> bool {
1460 self.notice_of_suspension_queue.is_empty()
1461 }
1462 pub(crate) fn cancellation_queue_empty(&self) -> bool {
1463 self.notice_of_cancellation_queue.is_empty()
1464 }
1465 pub(crate) fn ignored_queue_empty(&self) -> bool {
1466 self.ignored_queue.is_empty()
1467 }
1468 pub(crate) fn abandoned_queue_empty(&self) -> bool {
1469 self.abandoned_queue.is_empty()
1470 }
1471 pub(crate) fn all_queues_empty(&self) -> bool {
1472 self.suspension_queue_empty()
1473 && self.cancellation_queue_empty()
1474 && self.ignored_queue_empty()
1475 && self.abandoned_queue_empty()
1476 }
1477 }
1478
1479 pub struct SentPdu {
1480 pub pdu_type: PduType,
1481 pub file_directive_type: Option<FileDirectiveType>,
1482 pub raw_pdu: Vec<u8>,
1483 }
1484
1485 #[derive(Default)]
1486 pub struct TestCfdpSender {
1487 pub packet_queue: RefCell<VecDeque<SentPdu>>,
1488 }
1489
1490 impl PduSender for TestCfdpSender {
1491 fn send_pdu(
1492 &self,
1493 pdu_type: PduType,
1494 file_directive_type: Option<FileDirectiveType>,
1495 raw_pdu: &[u8],
1496 ) -> Result<(), GenericSendError> {
1497 println!(
1498 "sent pdu: {:?}, directive: {:?}, len: {}",
1499 pdu_type,
1500 file_directive_type,
1501 raw_pdu.len()
1502 );
1503 self.packet_queue.borrow_mut().push_back(SentPdu {
1504 pdu_type,
1505 file_directive_type,
1506 raw_pdu: raw_pdu.to_vec(),
1507 });
1508 Ok(())
1509 }
1510 }
1511
1512 impl TestCfdpSender {
1513 pub fn queue_len(&self) -> usize {
1514 self.packet_queue.borrow_mut().len()
1515 }
1516
1517 pub fn retrieve_next_pdu(&self) -> Option<SentPdu> {
1518 self.packet_queue.borrow_mut().pop_front()
1519 }
1520
1521 pub fn queue_empty(&self) -> bool {
1522 self.packet_queue.borrow_mut().is_empty()
1523 }
1524 }
1525
1526 pub fn basic_remote_cfg_table(
1527 dest_id: impl Into<UnsignedByteField>,
1528 max_packet_len: usize,
1529 crc_on_transmission_by_default: bool,
1530 ) -> RemoteConfigStoreStd {
1531 let mut table = RemoteConfigStoreStd::default();
1532 let remote_entity_cfg = RemoteEntityConfig::new_with_default_values(
1533 dest_id.into(),
1534 max_packet_len,
1535 true,
1536 crc_on_transmission_by_default,
1537 TransmissionMode::Unacknowledged,
1538 ChecksumType::Crc32,
1539 );
1540 table.add_config(&remote_entity_cfg).unwrap();
1541 table
1542 }
1543
1544 fn generic_pdu_header() -> PduHeader {
1545 let pdu_conf = CommonPduConfig::default();
1546 PduHeader::new_for_file_directive(pdu_conf, 0)
1547 }
1548
1549 #[test]
1550 fn test_transaction_id() {
1551 let transaction_id = TransactionId::new(
1552 UnsignedByteFieldU16::new(1).into(),
1553 UnsignedByteFieldU16::new(2).into(),
1554 );
1555 assert_eq!(transaction_id.source_id().value(), 1);
1556 assert_eq!(transaction_id.seq_num().value(), 2);
1557 }
1558
1559 #[test]
1560 fn test_metadata_pdu_info() {
1561 let mut buf: [u8; 128] = [0; 128];
1562 let pdu_header = generic_pdu_header();
1563 let metadata_params = MetadataGenericParams::default();
1564 let src_file_name = "hello.txt";
1565 let dest_file_name = "hello-dest.txt";
1566 let src_lv = Lv::new_from_str(src_file_name).unwrap();
1567 let dest_lv = Lv::new_from_str(dest_file_name).unwrap();
1568 let metadata_pdu =
1569 MetadataPduCreator::new_no_opts(pdu_header, metadata_params, src_lv, dest_lv);
1570 metadata_pdu
1571 .write_to_bytes(&mut buf)
1572 .expect("writing metadata PDU failed");
1573
1574 let packet_info = PduRawWithInfo::new(&buf).expect("creating packet info failed");
1575 assert_eq!(packet_info.pdu_type(), PduType::FileDirective);
1576 assert!(packet_info.file_directive_type().is_some());
1577 assert_eq!(
1578 packet_info.file_directive_type().unwrap(),
1579 FileDirectiveType::MetadataPdu
1580 );
1581 assert_eq!(
1582 packet_info.raw_packet(),
1583 &buf[0..metadata_pdu.len_written()]
1584 );
1585 assert_eq!(
1586 packet_info.packet_target().unwrap(),
1587 PacketTarget::DestEntity
1588 );
1589 }
1590
1591 #[test]
1592 fn test_filedata_pdu_info() {
1593 let mut buf: [u8; 128] = [0; 128];
1594 let pdu_header = generic_pdu_header();
1595 let file_data_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 0, &[]);
1596 file_data_pdu
1597 .write_to_bytes(&mut buf)
1598 .expect("writing file data PDU failed");
1599 let packet_info = PduRawWithInfo::new(&buf).expect("creating packet info failed");
1600 assert_eq!(
1601 packet_info.raw_packet(),
1602 &buf[0..file_data_pdu.len_written()]
1603 );
1604 assert_eq!(packet_info.pdu_type(), PduType::FileData);
1605 assert!(packet_info.file_directive_type().is_none());
1606 assert_eq!(
1607 packet_info.packet_target().unwrap(),
1608 PacketTarget::DestEntity
1609 );
1610 }
1611
1612 #[test]
1613 fn test_eof_pdu_info() {
1614 let mut buf: [u8; 128] = [0; 128];
1615 let pdu_header = generic_pdu_header();
1616 let eof_pdu = EofPdu::new_no_error(pdu_header, 0, 0);
1617 eof_pdu
1618 .write_to_bytes(&mut buf)
1619 .expect("writing file data PDU failed");
1620 let packet_info = PduRawWithInfo::new(&buf).expect("creating packet info failed");
1621 assert_eq!(packet_info.pdu_type(), PduType::FileDirective);
1622 assert!(packet_info.file_directive_type().is_some());
1623 assert_eq!(packet_info.raw_packet(), &buf[0..eof_pdu.len_written()]);
1624 assert_eq!(
1625 packet_info.file_directive_type().unwrap(),
1626 FileDirectiveType::EofPdu
1627 );
1628 }
1629
1630 #[test]
1631 fn test_std_check_timer() {
1632 let mut std_check_timer = StdCountdown::new(Duration::from_secs(1));
1633 assert!(!std_check_timer.has_expired());
1634 assert_eq!(std_check_timer.expiry_time_seconds(), 1);
1635 std::thread::sleep(Duration::from_millis(800));
1636 assert!(!std_check_timer.has_expired());
1637 std::thread::sleep(Duration::from_millis(205));
1638 assert!(std_check_timer.has_expired());
1639 std_check_timer.reset();
1640 assert!(!std_check_timer.has_expired());
1641 }
1642
1643 #[test]
1644 fn test_std_check_timer_creator() {
1645 let std_check_timer_creator = StdTimerCreator::new(Duration::from_secs(1));
1646 let check_timer = std_check_timer_creator.create_countdown(TimerContext::NakActivity {
1647 expiry_time: Duration::from_secs(1),
1648 });
1649 assert_eq!(check_timer.expiry_time_seconds(), 1);
1650 }
1651
1652 #[test]
1653 fn test_remote_cfg_provider_single() {
1654 let mut remote_entity_cfg = RemoteEntityConfig::new_with_default_values(
1655 REMOTE_ID.into(),
1656 1024,
1657 true,
1658 false,
1659 TransmissionMode::Unacknowledged,
1660 ChecksumType::Crc32,
1661 );
1662 let remote_entity_retrieved = remote_entity_cfg.get(REMOTE_ID.value()).unwrap();
1663 assert_eq!(remote_entity_retrieved.entity_id, REMOTE_ID.into());
1664 assert_eq!(remote_entity_retrieved.max_packet_len, 1024);
1665 assert!(remote_entity_retrieved.closure_requested_by_default);
1666 assert!(!remote_entity_retrieved.crc_on_transmission_by_default);
1667 assert_eq!(
1668 remote_entity_retrieved.default_crc_type,
1669 ChecksumType::Crc32
1670 );
1671 let remote_entity_mut = remote_entity_cfg.get_mut(REMOTE_ID.value()).unwrap();
1672 assert_eq!(remote_entity_mut.entity_id, REMOTE_ID.into());
1673 let dummy = RemoteEntityConfig::new_with_default_values(
1674 LOCAL_ID.into(),
1675 1024,
1676 true,
1677 false,
1678 TransmissionMode::Unacknowledged,
1679 ChecksumType::Crc32,
1680 );
1681 assert_eq!(
1682 remote_entity_cfg.add_config(&dummy).unwrap_err(),
1683 RemoteConfigStoreError::Full
1684 );
1685 let remote_entity_retrieved = remote_entity_cfg.get(REMOTE_ID.value()).unwrap();
1686 assert_eq!(remote_entity_retrieved.entity_id, REMOTE_ID.into());
1687 assert!(remote_entity_cfg.get(LOCAL_ID.value()).is_none());
1689 assert!(remote_entity_cfg.get_mut(LOCAL_ID.value()).is_none());
1690 }
1691
1692 #[test]
1693 fn test_remote_cfg_provider_std() {
1694 let remote_entity_cfg = RemoteEntityConfig::new_with_default_values(
1695 REMOTE_ID.into(),
1696 1024,
1697 true,
1698 false,
1699 TransmissionMode::Unacknowledged,
1700 ChecksumType::Crc32,
1701 );
1702 let mut remote_cfg_provider = RemoteConfigStoreStd::default();
1703 assert!(remote_cfg_provider.0.is_empty());
1704 remote_cfg_provider.add_config(&remote_entity_cfg).unwrap();
1705 assert_eq!(remote_cfg_provider.0.len(), 1);
1706 let remote_entity_cfg_2 = RemoteEntityConfig::new_with_default_values(
1707 LOCAL_ID.into(),
1708 1024,
1709 true,
1710 false,
1711 TransmissionMode::Unacknowledged,
1712 ChecksumType::Crc32,
1713 );
1714 let cfg_0 = remote_cfg_provider.get(REMOTE_ID.value()).unwrap();
1715 assert_eq!(cfg_0.entity_id, REMOTE_ID.into());
1716 remote_cfg_provider
1717 .add_config(&remote_entity_cfg_2)
1718 .unwrap();
1719 assert_eq!(remote_cfg_provider.0.len(), 2);
1720 let cfg_1 = remote_cfg_provider.get(LOCAL_ID.value()).unwrap();
1721 assert_eq!(cfg_1.entity_id, LOCAL_ID.into());
1722 assert!(remote_cfg_provider.remove_config(REMOTE_ID.value()));
1723 assert_eq!(remote_cfg_provider.0.len(), 1);
1724 let cfg_1_mut = remote_cfg_provider.get_mut(LOCAL_ID.value()).unwrap();
1725 cfg_1_mut.default_crc_type = ChecksumType::Crc32C;
1726 assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value()));
1727 assert!(remote_cfg_provider.get_mut(REMOTE_ID.value()).is_none());
1728 }
1729
1730 #[test]
1731 fn test_remote_cfg_provider_vector() {
1732 let mut remote_cfg_provider = RemoteConfigList::default();
1733 let remote_entity_cfg = RemoteEntityConfig::new_with_default_values(
1734 REMOTE_ID.into(),
1735 1024,
1736 true,
1737 false,
1738 TransmissionMode::Unacknowledged,
1739 ChecksumType::Crc32,
1740 );
1741 assert!(remote_cfg_provider.0.is_empty());
1742 remote_cfg_provider.add_config(&remote_entity_cfg).unwrap();
1743 assert_eq!(remote_cfg_provider.0.len(), 1);
1744 let remote_entity_cfg_2 = RemoteEntityConfig::new_with_default_values(
1745 LOCAL_ID.into(),
1746 1024,
1747 true,
1748 false,
1749 TransmissionMode::Unacknowledged,
1750 ChecksumType::Crc32,
1751 );
1752 let cfg_0 = remote_cfg_provider.get(REMOTE_ID.value()).unwrap();
1753 assert_eq!(cfg_0.entity_id, REMOTE_ID.into());
1754 assert!(
1755 remote_cfg_provider
1756 .add_config(&remote_entity_cfg_2)
1757 .unwrap()
1758 );
1759 assert_eq!(remote_cfg_provider.0.len(), 2);
1760 let cfg_1 = remote_cfg_provider.get(LOCAL_ID.value()).unwrap();
1761 assert_eq!(cfg_1.entity_id, LOCAL_ID.into());
1762 assert!(remote_cfg_provider.remove_config(REMOTE_ID.value()));
1763 assert_eq!(remote_cfg_provider.0.len(), 1);
1764 let cfg_1_mut = remote_cfg_provider.get_mut(LOCAL_ID.value()).unwrap();
1765 cfg_1_mut.default_crc_type = ChecksumType::Crc32C;
1766 assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value()));
1767 assert!(remote_cfg_provider.get_mut(REMOTE_ID.value()).is_none());
1768 }
1769
1770 #[test]
1771 fn dummy_fault_hook_test() {
1772 let mut user_hook_dummy = DummyFaultHook::default();
1773 let transaction_id = TransactionId::new(
1774 UnsignedByteFieldU8::new(0).into(),
1775 UnsignedByteFieldU8::new(0).into(),
1776 );
1777 user_hook_dummy.notice_of_cancellation_cb(FaultInfo::new(
1778 transaction_id,
1779 ConditionCode::NoError,
1780 0,
1781 ));
1782 user_hook_dummy.notice_of_suspension_cb(FaultInfo::new(
1783 transaction_id,
1784 ConditionCode::NoError,
1785 0,
1786 ));
1787 user_hook_dummy.abandoned_cb(FaultInfo::new(transaction_id, ConditionCode::NoError, 0));
1788 user_hook_dummy.ignore_cb(FaultInfo::new(transaction_id, ConditionCode::NoError, 0));
1789 }
1790
1791 #[test]
1792 fn dummy_pdu_provider_test() {
1793 let dummy_pdu_provider = DummyPduProvider(());
1794 assert_eq!(dummy_pdu_provider.pdu_type(), PduType::FileData);
1795 assert!(dummy_pdu_provider.file_directive_type().is_none());
1796 assert_eq!(dummy_pdu_provider.raw_pdu(), &[]);
1797 assert_eq!(
1798 dummy_pdu_provider.packet_target(),
1799 Ok(PacketTarget::SourceEntity)
1800 );
1801 }
1802
1803 #[test]
1804 fn test_fault_handler_checksum_error_ignored_by_default() {
1805 let fault_handler = FaultHandler::new(TestFaultHandler::default());
1806 assert_eq!(
1807 fault_handler.get_fault_handler(ConditionCode::FileChecksumFailure),
1808 FaultHandlerCode::IgnoreError
1809 );
1810 }
1811
1812 #[test]
1813 fn test_fault_handler_unsupported_checksum_ignored_by_default() {
1814 let fault_handler = FaultHandler::new(TestFaultHandler::default());
1815 assert_eq!(
1816 fault_handler.get_fault_handler(ConditionCode::UnsupportedChecksumType),
1817 FaultHandlerCode::IgnoreError
1818 );
1819 }
1820
1821 #[test]
1822 fn test_fault_handler_basic() {
1823 let mut fault_handler = FaultHandler::new(TestFaultHandler::default());
1824 assert_eq!(
1825 fault_handler.get_fault_handler(ConditionCode::FileChecksumFailure),
1826 FaultHandlerCode::IgnoreError
1827 );
1828 fault_handler.set_fault_handler(
1829 ConditionCode::FileChecksumFailure,
1830 FaultHandlerCode::NoticeOfCancellation,
1831 );
1832 assert_eq!(
1833 fault_handler.get_fault_handler(ConditionCode::FileChecksumFailure),
1834 FaultHandlerCode::NoticeOfCancellation
1835 );
1836 }
1837
1838 #[test]
1839 fn transaction_id_hashable_usable_as_map_key() {
1840 let mut map = hashbrown::HashMap::new();
1841 let transaction_id_0 = TransactionId::new(
1842 UnsignedByteFieldU8::new(1).into(),
1843 UnsignedByteFieldU8::new(2).into(),
1844 );
1845 map.insert(transaction_id_0, 5_u32);
1846 }
1847}