1use core::{cell::RefCell, fmt::Debug, hash::Hash};
4
5use crc::{Crc, CRC_32_CKSUM};
6use hashbrown::HashMap;
7use spacepackets::{
8    cfdp::{
9        pdu::{FileDirectiveType, PduError, PduHeader},
10        ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
11    },
12    util::UnsignedByteField,
13};
14
15#[cfg(feature = "alloc")]
16use alloc::boxed::Box;
17#[cfg(feature = "serde")]
18use serde::{Deserialize, Serialize};
19
20#[cfg(feature = "std")]
21pub mod dest;
22#[cfg(feature = "alloc")]
23pub mod filestore;
24#[cfg(feature = "std")]
25pub mod source;
26pub mod user;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum EntityType {
30    Sending,
31    Receiving,
32}
33
34pub enum TimerContext {
35    CheckLimit {
36        local_id: UnsignedByteField,
37        remote_id: UnsignedByteField,
38        entity_type: EntityType,
39    },
40    NakActivity {
41        expiry_time_seconds: f32,
42    },
43    PositiveAck {
44        expiry_time_seconds: f32,
45    },
46}
47
48pub trait CheckTimer: Debug {
78    fn has_expired(&self) -> bool;
79    fn reset(&mut self);
80}
81
82#[cfg(feature = "alloc")]
91pub trait CheckTimerCreator {
92    fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box<dyn CheckTimer>;
93}
94
95#[cfg(feature = "std")]
98#[derive(Debug)]
99pub struct StdCheckTimer {
100    expiry_time_seconds: u64,
101    start_time: std::time::Instant,
102}
103
104#[cfg(feature = "std")]
105impl StdCheckTimer {
106    pub fn new(expiry_time_seconds: u64) -> Self {
107        Self {
108            expiry_time_seconds,
109            start_time: std::time::Instant::now(),
110        }
111    }
112}
113
114#[cfg(feature = "std")]
115impl CheckTimer for StdCheckTimer {
116    fn has_expired(&self) -> bool {
117        let elapsed_time = self.start_time.elapsed();
118        if elapsed_time.as_secs() > self.expiry_time_seconds {
119            return true;
120        }
121        false
122    }
123
124    fn reset(&mut self) {
125        self.start_time = std::time::Instant::now();
126    }
127}
128
129#[derive(Debug, Copy, Clone)]
189pub struct RemoteEntityConfig {
190    pub entity_id: UnsignedByteField,
191    pub max_packet_len: usize,
192    pub max_file_segment_len: usize,
193    pub closure_requested_by_default: bool,
194    pub crc_on_transmission_by_default: bool,
195    pub default_transmission_mode: TransmissionMode,
196    pub default_crc_type: ChecksumType,
197    pub positive_ack_timer_interval_seconds: f32,
198    pub positive_ack_timer_expiration_limit: u32,
199    pub check_limit: u32,
200    pub disposition_on_cancellation: bool,
201    pub immediate_nak_mode: bool,
202    pub nak_timer_interval_seconds: f32,
203    pub nak_timer_expiration_limit: u32,
204}
205
206impl RemoteEntityConfig {
207    pub fn new_with_default_values(
208        entity_id: UnsignedByteField,
209        max_file_segment_len: usize,
210        max_packet_len: usize,
211        closure_requested_by_default: bool,
212        crc_on_transmission_by_default: bool,
213        default_transmission_mode: TransmissionMode,
214        default_crc_type: ChecksumType,
215    ) -> Self {
216        Self {
217            entity_id,
218            max_file_segment_len,
219            max_packet_len,
220            closure_requested_by_default,
221            crc_on_transmission_by_default,
222            default_transmission_mode,
223            default_crc_type,
224            check_limit: 2,
225            positive_ack_timer_interval_seconds: 10.0,
226            positive_ack_timer_expiration_limit: 2,
227            disposition_on_cancellation: false,
228            immediate_nak_mode: true,
229            nak_timer_interval_seconds: 10.0,
230            nak_timer_expiration_limit: 2,
231        }
232    }
233}
234
235pub trait RemoteEntityConfigProvider {
236    fn get_remote_config(&self, remote_id: u64) -> Option<&RemoteEntityConfig>;
238    fn get_remote_config_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig>;
239    fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool;
242    fn remove_config(&mut self, remote_id: u64) -> bool;
245}
246
247#[cfg(feature = "std")]
248#[derive(Default)]
249pub struct StdRemoteEntityConfigProvider {
250    remote_cfg_table: HashMap<u64, RemoteEntityConfig>,
251}
252
253#[cfg(feature = "std")]
254impl RemoteEntityConfigProvider for StdRemoteEntityConfigProvider {
255    fn get_remote_config(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
256        self.remote_cfg_table.get(&remote_id)
257    }
258    fn get_remote_config_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
259        self.remote_cfg_table.get_mut(&remote_id)
260    }
261    fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool {
262        self.remote_cfg_table
263            .insert(cfg.entity_id.value(), *cfg)
264            .is_some()
265    }
266    fn remove_config(&mut self, remote_id: u64) -> bool {
267        self.remote_cfg_table.remove(&remote_id).is_some()
268    }
269}
270
271pub trait UserFaultHandler {
282    fn notice_of_suspension_cb(
283        &mut self,
284        transaction_id: TransactionId,
285        cond: ConditionCode,
286        progress: u64,
287    );
288
289    fn notice_of_cancellation_cb(
290        &mut self,
291        transaction_id: TransactionId,
292        cond: ConditionCode,
293        progress: u64,
294    );
295
296    fn abandoned_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64);
297
298    fn ignore_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64);
299}
300
301pub struct DefaultFaultHandler {
322    handler_array: [FaultHandlerCode; 10],
323    user_fault_handler: RefCell<Box<dyn UserFaultHandler + Send>>,
326}
327
328impl DefaultFaultHandler {
329    fn condition_code_to_array_index(conditon_code: ConditionCode) -> Option<usize> {
330        Some(match conditon_code {
331            ConditionCode::PositiveAckLimitReached => 0,
332            ConditionCode::KeepAliveLimitReached => 1,
333            ConditionCode::InvalidTransmissionMode => 2,
334            ConditionCode::FilestoreRejection => 3,
335            ConditionCode::FileChecksumFailure => 4,
336            ConditionCode::FileSizeError => 5,
337            ConditionCode::NakLimitReached => 6,
338            ConditionCode::InactivityDetected => 7,
339            ConditionCode::CheckLimitReached => 8,
340            ConditionCode::UnsupportedChecksumType => 9,
341            _ => return None,
342        })
343    }
344
345    pub fn set_fault_handler(
346        &mut self,
347        condition_code: ConditionCode,
348        fault_handler: FaultHandlerCode,
349    ) {
350        let array_idx = Self::condition_code_to_array_index(condition_code);
351        if array_idx.is_none() {
352            return;
353        }
354        self.handler_array[array_idx.unwrap()] = fault_handler;
355    }
356
357    pub fn new(user_fault_handler: Box<dyn UserFaultHandler + Send>) -> Self {
358        let mut init_array = [FaultHandlerCode::NoticeOfCancellation; 10];
359        init_array
360            [Self::condition_code_to_array_index(ConditionCode::FileChecksumFailure).unwrap()] =
361            FaultHandlerCode::IgnoreError;
362        init_array[Self::condition_code_to_array_index(ConditionCode::UnsupportedChecksumType)
363            .unwrap()] = FaultHandlerCode::IgnoreError;
364        Self {
365            handler_array: init_array,
366            user_fault_handler: RefCell::new(user_fault_handler),
367        }
368    }
369
370    pub fn get_fault_handler(&self, condition_code: ConditionCode) -> FaultHandlerCode {
371        let array_idx = Self::condition_code_to_array_index(condition_code);
372        if array_idx.is_none() {
373            return FaultHandlerCode::IgnoreError;
374        }
375        self.handler_array[array_idx.unwrap()]
376    }
377
378    pub fn report_fault(
379        &self,
380        transaction_id: TransactionId,
381        condition: ConditionCode,
382        progress: u64,
383    ) -> FaultHandlerCode {
384        let array_idx = Self::condition_code_to_array_index(condition);
385        if array_idx.is_none() {
386            return FaultHandlerCode::IgnoreError;
387        }
388        let fh_code = self.handler_array[array_idx.unwrap()];
389        let mut handler_mut = self.user_fault_handler.borrow_mut();
390        match fh_code {
391            FaultHandlerCode::NoticeOfCancellation => {
392                handler_mut.notice_of_cancellation_cb(transaction_id, condition, progress);
393            }
394            FaultHandlerCode::NoticeOfSuspension => {
395                handler_mut.notice_of_suspension_cb(transaction_id, condition, progress);
396            }
397            FaultHandlerCode::IgnoreError => {
398                handler_mut.ignore_cb(transaction_id, condition, progress);
399            }
400            FaultHandlerCode::AbandonTransaction => {
401                handler_mut.abandoned_cb(transaction_id, condition, progress);
402            }
403        }
404        fh_code
405    }
406}
407
408pub struct IndicationConfig {
409    pub eof_sent: bool,
410    pub eof_recv: bool,
411    pub file_segment_recv: bool,
412    pub transaction_finished: bool,
413    pub suspended: bool,
414    pub resumed: bool,
415}
416
417impl Default for IndicationConfig {
418    fn default() -> Self {
419        Self {
420            eof_sent: true,
421            eof_recv: true,
422            file_segment_recv: true,
423            transaction_finished: true,
424            suspended: true,
425            resumed: true,
426        }
427    }
428}
429
430pub struct LocalEntityConfig {
431    pub id: UnsignedByteField,
432    pub indication_cfg: IndicationConfig,
433    pub default_fault_handler: DefaultFaultHandler,
434}
435
436#[derive(Debug, Eq, Copy, Clone)]
439#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
440pub struct TransactionId {
441    source_id: UnsignedByteField,
442    seq_num: UnsignedByteField,
443}
444
445impl TransactionId {
446    pub fn new(source_id: UnsignedByteField, seq_num: UnsignedByteField) -> Self {
447        Self { source_id, seq_num }
448    }
449
450    pub fn source_id(&self) -> &UnsignedByteField {
451        &self.source_id
452    }
453
454    pub fn seq_num(&self) -> &UnsignedByteField {
455        &self.seq_num
456    }
457}
458
459impl Hash for TransactionId {
460    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
461        self.source_id.value().hash(state);
462        self.seq_num.value().hash(state);
463    }
464}
465
466impl PartialEq for TransactionId {
467    fn eq(&self, other: &Self) -> bool {
468        self.source_id.value() == other.source_id.value()
469            && self.seq_num.value() == other.seq_num.value()
470    }
471}
472
473#[derive(Debug, Copy, Clone, PartialEq, Eq)]
474#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
475pub enum TransactionStep {
476    Idle = 0,
477    TransactionStart = 1,
478    ReceivingFileDataPdus = 2,
479    ReceivingFileDataPdusWithCheckLimitHandling = 3,
480    SendingAckPdu = 4,
481    TransferCompletion = 5,
482    SendingFinishedPdu = 6,
483}
484
485#[derive(Debug, Copy, Clone, PartialEq, Eq)]
486#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
487pub enum State {
488    Idle = 0,
489    Busy = 1,
490    Suspended = 2,
491}
492
493pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_CKSUM);
494
495#[derive(Debug, PartialEq, Eq, Copy, Clone)]
496#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
497pub enum PacketTarget {
498    SourceEntity,
499    DestEntity,
500}
501
502pub struct PacketInfo<'raw_packet> {
506    pdu_type: PduType,
507    pdu_directive: Option<FileDirectiveType>,
508    target: PacketTarget,
509    raw_packet: &'raw_packet [u8],
510}
511
512impl<'raw> PacketInfo<'raw> {
513    pub fn new(raw_packet: &'raw [u8]) -> Result<Self, PduError> {
514        let (pdu_header, header_len) = PduHeader::from_bytes(raw_packet)?;
515        if pdu_header.pdu_type() == PduType::FileData {
516            return Ok(Self {
517                pdu_type: pdu_header.pdu_type(),
518                pdu_directive: None,
519                target: PacketTarget::DestEntity,
520                raw_packet,
521            });
522        }
523        if pdu_header.pdu_datafield_len() < 1 {
524            return Err(PduError::FormatError);
525        }
526        let directive = FileDirectiveType::try_from(raw_packet[header_len]).map_err(|_| {
531            PduError::InvalidDirectiveType {
532                found: raw_packet[header_len],
533                expected: None,
534            }
535        })?;
536        let packet_target = match directive {
537            FileDirectiveType::NakPdu
540            | FileDirectiveType::FinishedPdu
541            | FileDirectiveType::KeepAlivePdu => PacketTarget::SourceEntity,
542            FileDirectiveType::MetadataPdu
545            | FileDirectiveType::EofPdu
546            | FileDirectiveType::PromptPdu => PacketTarget::DestEntity,
547            FileDirectiveType::AckPdu => {
551                let acked_directive = FileDirectiveType::try_from(raw_packet[header_len + 1])
552                    .map_err(|_| PduError::InvalidDirectiveType {
553                        found: raw_packet[header_len],
554                        expected: None,
555                    })?;
556                if acked_directive == FileDirectiveType::EofPdu {
557                    PacketTarget::SourceEntity
558                } else if acked_directive == FileDirectiveType::FinishedPdu {
559                    PacketTarget::DestEntity
560                } else {
561                    return Err(PduError::InvalidDirectiveType {
563                        found: raw_packet[header_len + 1],
564                        expected: None,
565                    });
566                }
567            }
568        };
569        Ok(Self {
570            pdu_type: pdu_header.pdu_type(),
571            pdu_directive: Some(directive),
572            target: packet_target,
573            raw_packet,
574        })
575    }
576
577    pub fn pdu_type(&self) -> PduType {
578        self.pdu_type
579    }
580
581    pub fn pdu_directive(&self) -> Option<FileDirectiveType> {
582        self.pdu_directive
583    }
584
585    pub fn target(&self) -> PacketTarget {
586        self.target
587    }
588
589    pub fn raw_packet(&self) -> &[u8] {
590        self.raw_packet
591    }
592}
593
594#[cfg(test)]
595mod tests {
596    use spacepackets::cfdp::{
597        lv::Lv,
598        pdu::{
599            eof::EofPdu,
600            file_data::FileDataPdu,
601            metadata::{MetadataGenericParams, MetadataPduCreator},
602            CommonPduConfig, FileDirectiveType, PduHeader, WritablePduPacket,
603        },
604        PduType,
605    };
606
607    use crate::cfdp::PacketTarget;
608
609    use super::PacketInfo;
610
611    fn generic_pdu_header() -> PduHeader {
612        let pdu_conf = CommonPduConfig::default();
613        PduHeader::new_no_file_data(pdu_conf, 0)
614    }
615
616    #[test]
617    fn test_metadata_pdu_info() {
618        let mut buf: [u8; 128] = [0; 128];
619        let pdu_header = generic_pdu_header();
620        let metadata_params = MetadataGenericParams::default();
621        let src_file_name = "hello.txt";
622        let dest_file_name = "hello-dest.txt";
623        let src_lv = Lv::new_from_str(src_file_name).unwrap();
624        let dest_lv = Lv::new_from_str(dest_file_name).unwrap();
625        let metadata_pdu =
626            MetadataPduCreator::new_no_opts(pdu_header, metadata_params, src_lv, dest_lv);
627        metadata_pdu
628            .write_to_bytes(&mut buf)
629            .expect("writing metadata PDU failed");
630
631        let packet_info = PacketInfo::new(&buf).expect("creating packet info failed");
632        assert_eq!(packet_info.pdu_type(), PduType::FileDirective);
633        assert!(packet_info.pdu_directive().is_some());
634        assert_eq!(
635            packet_info.pdu_directive().unwrap(),
636            FileDirectiveType::MetadataPdu
637        );
638        assert_eq!(packet_info.target(), PacketTarget::DestEntity);
639    }
640
641    #[test]
642    fn test_filedata_pdu_info() {
643        let mut buf: [u8; 128] = [0; 128];
644        let pdu_header = generic_pdu_header();
645        let file_data_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 0, &[]);
646        file_data_pdu
647            .write_to_bytes(&mut buf)
648            .expect("writing file data PDU failed");
649        let packet_info = PacketInfo::new(&buf).expect("creating packet info failed");
650        assert_eq!(packet_info.pdu_type(), PduType::FileData);
651        assert!(packet_info.pdu_directive().is_none());
652        assert_eq!(packet_info.target(), PacketTarget::DestEntity);
653    }
654
655    #[test]
656    fn test_eof_pdu_info() {
657        let mut buf: [u8; 128] = [0; 128];
658        let pdu_header = generic_pdu_header();
659        let eof_pdu = EofPdu::new_no_error(pdu_header, 0, 0);
660        eof_pdu
661            .write_to_bytes(&mut buf)
662            .expect("writing file data PDU failed");
663        let packet_info = PacketInfo::new(&buf).expect("creating packet info failed");
664        assert_eq!(packet_info.pdu_type(), PduType::FileDirective);
665        assert!(packet_info.pdu_directive().is_some());
666        assert_eq!(
667            packet_info.pdu_directive().unwrap(),
668            FileDirectiveType::EofPdu
669        );
670    }
671}