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}