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