diff --git a/src/request.rs b/src/request.rs index 0d812ce..f64e93e 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,3 +1,5 @@ +//! # Request module +#![deny(missing_docs)] use core::str::Utf8Error; use spacepackets::{ @@ -12,6 +14,9 @@ use spacepackets::{ #[cfg(feature = "alloc")] pub use alloc_mod::*; +/// File path is too large. +/// +/// The file path length is limited to 255 bytes. #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -20,36 +25,56 @@ pub struct FilePathTooLarge(pub usize); /// This trait is an abstraction for different Put Request structures which can be used /// by Put Request consumers. pub trait ReadablePutRequest { + /// Destination entity ID. fn destination_id(&self) -> UnsignedByteField; + /// Source file path. fn source_file(&self) -> Option<&str>; + /// Destination file path. fn dest_file(&self) -> Option<&str>; + /// Transmission mode if explicitely specified. fn trans_mode(&self) -> Option; + /// Closure is requested for unacknowledged file transfer. fn closure_requested(&self) -> Option; + /// Segmentation control. fn seg_ctrl(&self) -> Option; + /// Iterator over Messages to User TLVs, if any are supplied. fn msgs_to_user(&self) -> Option>>; + /// Iterator over fault handler override TLVs, if any are supplied. fn fault_handler_overrides(&self) -> Option>>; + /// Flow label TLV, if it is supplied. fn flow_label(&self) -> Option>; + /// Iterator over filestore request TLVs, if any are supplied. fn fs_requests(&self) -> Option>>; } +/// Put request structure. #[derive(Debug, PartialEq, Eq)] pub struct PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests> { + /// Destination entity ID. pub destination_id: UnsignedByteField, source_file: Option<&'src_file str>, dest_file: Option<&'dest_file str>, + /// Transmission mode. pub trans_mode: Option, + /// Closure requested flag for unacknowledged file transfer. pub closure_requested: Option, + /// Segmentation control. pub seg_ctrl: Option, + /// Messages to user TLVs. pub msgs_to_user: Option<&'msgs_to_user [Tlv<'msgs_to_user>]>, + /// Fault handler override TLVs. pub fault_handler_overrides: Option<&'fh_ovrds [Tlv<'fh_ovrds>]>, + /// Flow label TLV. pub flow_label: Option>, + /// Filestore request TLVs. pub fs_requests: Option<&'fs_requests [Tlv<'fs_requests>]>, } impl<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests> PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests> { + /// Create a new put request with all possible fields. #[allow(clippy::too_many_arguments)] pub fn new( destination_id: UnsignedByteField, @@ -130,6 +155,9 @@ impl ReadablePutRequest for PutRequest<'_, '_, '_, '_, '_, '_> { } } +/// Generic path checks. +/// +/// This only checks the length of the paths. pub fn generic_path_checks( source_file: Option<&str>, dest_file: Option<&str>, @@ -148,6 +176,7 @@ pub fn generic_path_checks( } impl<'src_file, 'dest_file> PutRequest<'src_file, 'dest_file, 'static, 'static, 'static, 'static> { + /// New regular put request with no additional TLVs. pub fn new_regular_request( dest_id: UnsignedByteField, source_file: &'src_file str, @@ -171,12 +200,14 @@ impl<'src_file, 'dest_file> PutRequest<'src_file, 'dest_file, 'static, 'static, } } +/// TLV has invalid type. #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TlvWithInvalidType(pub(crate) ()); impl<'msgs_to_user> PutRequest<'static, 'static, 'msgs_to_user, 'static, 'static, 'static> { + /// New put request which only contains messages to the user TLVs. pub fn new_msgs_to_user_only( dest_id: UnsignedByteField, msgs_to_user: &'msgs_to_user [Tlv<'msgs_to_user>], @@ -212,6 +243,7 @@ impl<'msgs_to_user> PutRequest<'static, 'static, 'msgs_to_user, 'static, 'static } } +/// Generic check of the TLV list type. pub fn generic_tlv_list_type_check( opt_tlvs: Option<&[TlvProvider]>, tlv_type: TlvType, @@ -229,7 +261,9 @@ pub fn generic_tlv_list_type_check( true } +/// Structure for all static put request fields. pub struct StaticPutRequestFields { + /// Destination entity ID. pub destination_id: UnsignedByteField, /// Static buffer to store source file path. pub source_file_buf: [u8; u8::MAX as usize], @@ -239,8 +273,11 @@ pub struct StaticPutRequestFields { pub dest_file_buf: [u8; u8::MAX as usize], /// Current destination path length. pub dest_file_len: usize, + /// Transmission mode. pub trans_mode: Option, + /// Closure requested flag for unacknowledged file transfer. pub closure_requested: Option, + /// Segmentation control. pub seg_ctrl: Option, } @@ -260,6 +297,7 @@ impl Default for StaticPutRequestFields { } impl StaticPutRequestFields { + /// Clears and resets the fields. pub fn clear(&mut self) { self.destination_id = UnsignedByteField::new(0, 0); self.source_file_len = 0; @@ -271,9 +309,11 @@ impl StaticPutRequestFields { } /// This is a put request cache structure which can be used to cache [ReadablePutRequest]s -/// without requiring run-time allocation. The user must specify the static buffer sizes used -/// to store TLVs or list of TLVs. +/// without requiring run-time allocation. +/// +/// The user must specify the static buffer sizes used to store TLVs or list of TLVs. pub struct StaticPutRequestCacher { + /// Static fields. pub static_fields: StaticPutRequestFields, opts_buf: [u8; BUF_SIZE], opts_len: usize, @@ -286,6 +326,7 @@ impl Default for StaticPutRequestCacher { } impl StaticPutRequestCacher { + /// Constructor. pub fn new() -> Self { Self { static_fields: StaticPutRequestFields::default(), @@ -294,6 +335,7 @@ impl StaticPutRequestCacher { } } + /// Set and update with using any generic [ReadablePutRequest]. pub fn set( &mut self, put_request: &impl ReadablePutRequest, @@ -352,28 +394,34 @@ impl StaticPutRequestCacher { Ok(()) } + /// Does the put request have a source file? pub fn has_source_file(&self) -> bool { self.static_fields.source_file_len > 0 } + /// Does the put request have a destination file? pub fn has_dest_file(&self) -> bool { self.static_fields.dest_file_len > 0 } + /// Source file path. pub fn source_file(&self) -> Result<&str, Utf8Error> { core::str::from_utf8( &self.static_fields.source_file_buf[0..self.static_fields.source_file_len], ) } + /// Destination file path. pub fn dest_file(&self) -> Result<&str, Utf8Error> { core::str::from_utf8(&self.static_fields.dest_file_buf[0..self.static_fields.dest_file_len]) } + /// Length of stored options TLVs. pub fn opts_len(&self) -> usize { self.opts_len } + /// Raw options slice. pub fn opts_slice(&self) -> &[u8] { &self.opts_buf[0..self.opts_len] } @@ -388,6 +436,7 @@ impl StaticPutRequestCacher { } } +/// [alloc] support module. #[cfg(feature = "alloc")] pub mod alloc_mod { @@ -400,19 +449,28 @@ pub mod alloc_mod { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PutRequestOwned { + /// Destination entity ID. pub destination_id: UnsignedByteField, source_file: Option, dest_file: Option, + /// Transmission mode. pub trans_mode: Option, + /// Closure requested flag for unacknowledged file transfer. pub closure_requested: Option, + /// Segmentation control. pub seg_ctrl: Option, + /// Messages to user TLVs. pub msgs_to_user: Option>, + /// Fault handler override TLVs. pub fault_handler_overrides: Option>, + /// Flow label TLV. pub flow_label: Option, + /// Filestore request TLVs. pub fs_requests: Option>, } impl PutRequestOwned { + /// New regular put request with no additional TLVs. pub fn new_regular_request( dest_id: UnsignedByteField, source_file: &str, @@ -440,6 +498,7 @@ pub mod alloc_mod { }) } + /// New put request which only contains messages to the user TLVs. pub fn new_msgs_to_user_only( dest_id: UnsignedByteField, msgs_to_user: &[MsgToUserTlv<'_>], diff --git a/src/source.rs b/src/source.rs index 57d5067..47bbc33 100644 --- a/src/source.rs +++ b/src/source.rs @@ -36,6 +36,7 @@ //! 6. A finished PDU ACK packet will be generated to be sent to the remote CFDP entity. //! The [spacepackets::cfdp::pdu::finished::FinishedPduReader] can be used to inspect the //! generated PDU. +#![deny(missing_docs)] use core::{ cell::{Cell, RefCell}, ops::ControlFlow, @@ -84,29 +85,42 @@ use super::{ #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TransactionStep { + /// Idle state, nothing to do. Idle = 0, + /// Transaction has started. TransactionStart = 1, + /// Sending Metadata PDU. SendingMetadata = 3, + /// Sending file data PDUs. SendingFileData = 4, /// Re-transmitting missing packets in acknowledged mode Retransmitting = 5, + /// Sending an EOF PDU. SendingEof = 6, + /// Waiting for the acknowledgement of the EOF PDU. WaitingForEofAck = 7, + /// Waiting for the Finished PDU from the receiver. WaitingForFinished = 8, + /// Performing the notice of completion. NoticeOfCompletion = 10, } +/// Parameter related to the file transfer. #[derive(Default, Debug, Copy, Clone)] -pub struct FileParams { - pub progress: u64, - pub segment_len: u64, - pub crc32: u32, - pub metadata_only: bool, - pub file_size: u64, - pub empty_file: bool, +struct FileParams { + /// Progress of the file transfer. + progress: u64, + /// Segment length for a single file segment which is limited by various factors. + segment_len: u64, + /// Metadata only flag. + metadata_only: bool, + /// File size. + file_size: u64, + /// Empty file flag. + empty_file: bool, /// The checksum is cached to avoid expensive re-calculation when the EOF PDU needs to be /// re-sent. - pub checksum_completed_file: Option, + checksum_completed_file: Option, } // Explicit choice to put all simple internal fields into Cells. @@ -137,6 +151,7 @@ impl StateHelper { } } +/// Parameters related to the Finished PDU. #[derive(Debug, Copy, Clone)] pub struct FinishedParams { condition_code: ConditionCode, @@ -144,68 +159,98 @@ pub struct FinishedParams { file_status: FileStatus, } +/// Source handler errors. #[derive(Debug, thiserror::Error)] pub enum SourceError { + /// Can not process the passed packet type. #[error("can not process packet type {pdu_type:?} with directive type {directive_type:?}")] CantProcessPacketType { + /// PDU type. pdu_type: PduType, + /// Directive type, if applicable. directive_type: Option, }, + /// Unexpected PDU for current state. #[error("unexpected PDU")] UnexpectedPdu { + /// PDU type. pdu_type: PduType, + /// Directive type, if applicable. directive_type: Option, }, + /// Put request is already active. #[error("source handler is already busy with put request")] PutRequestAlreadyActive, + /// Error during the caching process of a put request. #[error("error caching put request")] PutRequestCaching(ByteConversionError), + /// Generic filestore error. #[error("filestore error: {0}")] FilestoreError(#[from] FilestoreError), + /// Source file name is not valid UTF-8. #[error("source file does not have valid UTF8 format: {0}")] SourceFileNotValidUtf8(Utf8Error), + /// Destination file name is not valid UTF-8. #[error("destination file does not have valid UTF8 format: {0}")] DestFileNotValidUtf8(Utf8Error), + /// Invalid NAK PDU error. #[error("invalid NAK PDU received")] InvalidNakPdu, + /// PDU creation error. #[error("error related to PDU creation: {0}")] Pdu(#[from] PduError), + /// Feature not implemented error. #[error("cfdp feature not implemented")] NotImplemented, + /// Generic send error. #[error("issue sending PDU: {0}")] SendError(#[from] GenericSendError), } +/// Put request errors. #[derive(Debug, thiserror::Error)] pub enum PutRequestError { + /// Storage error. #[error("error caching put request: {0}")] Storage(#[from] ByteConversionError), + /// Already busy with a put request. #[error("already busy with put request")] AlreadyBusy, + /// No remote entity configuration was found for destination ID. #[error("no remote entity configuration found for {0:?}")] NoRemoteCfgFound(UnsignedByteField), + /// Source file name is not valid UTF-8. #[error("source file does not have valid UTF8 format: {0}")] SourceFileNotValidUtf8(#[from] Utf8Error), + /// File does not exist. #[error("source file does not exist")] FileDoesNotExist, + /// Generic filestore error. #[error("filestore error: {0}")] FilestoreError(#[from] FilestoreError), } +/// Anomaly tracker for the source handler. +/// +/// Anomalies are unexpected events which are not severe errors. #[derive(Debug, Default, Clone, Copy)] pub struct AnomalyTracker { invalid_ack_directive_code: u8, } +/// Finite state-machine context. #[derive(Debug, Default, PartialEq, Eq)] -pub enum FsmContext { +enum FsmContext { + /// None #[default] None, + /// The FSM should be reset when possible. ResetWhenPossible, } +/// Transaction parameters. #[derive(Debug)] -pub struct TransactionParams { +struct TransactionParams { transaction_id: Option, remote_cfg: Option, transmission_mode: Option, @@ -399,6 +444,9 @@ impl< } } + /// Transcation ID for the currently active transaction. + /// + /// Returns [None] if no transaction is active. #[inline] pub fn transaction_id(&self) -> Option { self.transaction_params.transaction_id @@ -417,11 +465,13 @@ impl< self.state_helper.step.get() } + /// Current state of the source handler. #[inline] pub fn state(&self) -> State { self.state_helper.state.get() } + /// Local configuration of the source handler. #[inline] pub fn local_cfg(&self) -> &LocalEntityConfig { &self.local_cfg @@ -1129,6 +1179,9 @@ impl< Ok(()) } + /// Manually trigger a notice of cancellation. + /// + /// This cancels any currently active transaction. pub fn notice_of_cancellation( &mut self, user: &mut impl CfdpUser, @@ -1174,12 +1227,18 @@ impl< } } + /// Manually trigger a notice of suspension. + /// + /// Please note that proper susopension handling is not implemented yet. pub fn notice_of_suspension(&mut self) { self.notice_of_suspension_internal(); } - fn notice_of_suspension_internal(&self) {} + fn notice_of_suspension_internal(&self) { + // TODO: Implement. + } + /// Manually abandon the currently active transaction. pub fn abandon_transaction(&mut self) { // I guess an abandoned transaction just stops whatever the handler is doing and resets // it to a clean state.. The implementation for this is quite easy. diff --git a/src/time.rs b/src/time.rs index 8a8f433..df5c8b9 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,7 +1,11 @@ +//! # Time support module. +#![deny(missing_docs)] use core::fmt::Debug; /// Generic abstraction for a check/countdown timer. Should also be cheap to copy and clone. pub trait Countdown: Debug { + /// The countdown has expired. fn has_expired(&self) -> bool; + /// Reset the countdown to its initial state. fn reset(&mut self); } diff --git a/src/user.rs b/src/user.rs index 6d058f4..aaa9e5f 100644 --- a/src/user.rs +++ b/src/user.rs @@ -1,3 +1,5 @@ +//! # User support and hooks module +#![deny(missing_docs)] #[cfg(feature = "alloc")] use spacepackets::cfdp::tlv::WritableTlv; use spacepackets::{ @@ -14,34 +16,53 @@ use spacepackets::{ use super::TransactionId; +/// Parameters related to a finished transfer. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TransactionFinishedParams { + /// ID of the transfer. pub id: TransactionId, + /// Condition code. pub condition_code: ConditionCode, + /// Delivery code. pub delivery_code: DeliveryCode, + /// File status. pub file_status: FileStatus, } +/// Parameters related to the reception of a metadata PDU, which might start file reception. #[derive(Debug)] pub struct MetadataReceivedParams<'src_file, 'dest_file, 'msgs_to_user> { + /// ID of the transfer. pub id: TransactionId, + /// Source entity ID. pub source_id: UnsignedByteField, + /// File size. pub file_size: u64, + /// Source file name. pub src_file_name: &'src_file str, + /// Destination file name. pub dest_file_name: &'dest_file str, + /// Messages to user TLVs. pub msgs_to_user: &'msgs_to_user [MsgToUserTlv<'msgs_to_user>], } +/// Owned variant of [MetadataReceivedParams]. #[cfg(feature = "alloc")] -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct OwnedMetadataRecvdParams { + /// ID of the transfer. pub id: TransactionId, + /// Source entity ID. pub source_id: UnsignedByteField, + /// File size. pub file_size: u64, + /// Source file name. pub src_file_name: alloc::string::String, + /// Destination file name. pub dest_file_name: alloc::string::String, + /// Messages to user TLVs. pub msgs_to_user: alloc::vec::Vec>, } @@ -66,35 +87,63 @@ impl From<&MetadataReceivedParams<'_, '_, '_>> for OwnedMetadataRecvdParams { } } +/// Parameters related to the reception of a file segment PDU. #[derive(Debug)] pub struct FileSegmentRecvdParams<'seg_meta> { + /// ID of the transfer. pub id: TransactionId, + /// Offset of the segment. pub offset: u64, + /// Length of the segment. pub length: usize, + /// Segment metadata, if present. pub segment_metadata: Option<&'seg_meta SegmentMetadata<'seg_meta>>, } +/// Generic CFDP user as specified in the CFDP standard. +/// +/// This trait declares all indications which are possible. pub trait CfdpUser { + /// Indication that a new transaction has started. fn transaction_indication(&mut self, id: &TransactionId); + + /// Indication that an EOF PDU has been sent. fn eof_sent_indication(&mut self, id: &TransactionId); + + /// Indication that a transaction has finished. fn transaction_finished_indication(&mut self, finished_params: &TransactionFinishedParams); + + /// Indication that metadata has been received. fn metadata_recvd_indication(&mut self, md_recvd_params: &MetadataReceivedParams); + + /// Indication that a file segment has been received. fn file_segment_recvd_indication(&mut self, segment_recvd_params: &FileSegmentRecvdParams); + // TODO: The standard does not strictly specify how the report information looks.. + /// Report information indication. fn report_indication(&mut self, id: &TransactionId); + + /// Indication that a transfer has been suspended. fn suspended_indication(&mut self, id: &TransactionId, condition_code: ConditionCode); + /// Indication that a transfer has been resumed. fn resumed_indication(&mut self, id: &TransactionId, progress: u64); + + /// Indication that a fault has occured. fn fault_indication( &mut self, id: &TransactionId, condition_code: ConditionCode, progress: u64, ); + + /// Indication that a transfer has been abandoned. fn abandoned_indication( &mut self, id: &TransactionId, condition_code: ConditionCode, progress: u64, ); + + /// Indication that an EOF PDU has been received. fn eof_recvd_indication(&mut self, id: &TransactionId); }