add more error handling
Some checks failed
Rust/cfdp/pipeline/head There was a failure building this commit

This commit is contained in:
2024-09-05 14:44:56 +02:00
parent 69eed4a46d
commit 0766a0e6c9
6 changed files with 300 additions and 98 deletions

View File

@@ -15,7 +15,7 @@ use cfdp::{
source::SourceHandler, source::SourceHandler,
user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams, TransactionFinishedParams}, user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams, TransactionFinishedParams},
EntityType, IndicationConfig, LocalEntityConfig, PduOwnedWithInfo, PduProvider, EntityType, IndicationConfig, LocalEntityConfig, PduOwnedWithInfo, PduProvider,
RemoteEntityConfig, StdCheckTimerCreator, TransactionId, UserFaultHookProvider, RemoteEntityConfig, StdTimerCreator, TransactionId, UserFaultHookProvider,
}; };
use clap::Parser; use clap::Parser;
use log::{debug, info, warn}; use log::{debug, info, warn};
@@ -346,6 +346,7 @@ fn main() {
put_request_cacher, put_request_cacher,
2048, 2048,
remote_cfg_python, remote_cfg_python,
StdTimerCreator::default(),
seq_count_provider, seq_count_provider,
); );
let mut cfdp_user_source = ExampleCfdpUser::new(EntityType::Sending); let mut cfdp_user_source = ExampleCfdpUser::new(EntityType::Sending);
@@ -361,7 +362,7 @@ fn main() {
dest_tm_tx, dest_tm_tx,
NativeFilestore::default(), NativeFilestore::default(),
remote_cfg_python, remote_cfg_python,
StdCheckTimerCreator::default(), StdTimerCreator::default(),
); );
let mut cfdp_user_dest = ExampleCfdpUser::new(EntityType::Receiving); let mut cfdp_user_dest = ExampleCfdpUser::new(EntityType::Receiving);

View File

@@ -24,7 +24,7 @@
//! //!
//! 3. Finished PDU has been sent back to the remote side. //! 3. Finished PDU has been sent back to the remote side.
//! //!
//! ### Acknowledged mode //! ### Acknowledged mode (*not implemented yet*)
//! //!
//! 3. An EOF ACK PDU has been sent back to the remote side. //! 3. An EOF ACK PDU has been sent back to the remote side.
//! 4. A Finished PDU has been sent back to the remote side. //! 4. A Finished PDU has been sent back to the remote side.
@@ -35,10 +35,10 @@ use core::str::{from_utf8, from_utf8_unchecked, Utf8Error};
use super::{ use super::{
filestore::{FilestoreError, NativeFilestore, VirtualFilestore}, filestore::{FilestoreError, NativeFilestore, VirtualFilestore},
user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams}, user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams},
CheckTimerProviderCreator, CountdownProvider, EntityType, LocalEntityConfig, PacketTarget, CountdownProvider, EntityType, LocalEntityConfig, PacketTarget, PduSendProvider,
PduSendProvider, RemoteEntityConfig, RemoteEntityConfigProvider, State, StdCheckTimer, RemoteEntityConfig, RemoteEntityConfigProvider, State, StdCountdown,
StdCheckTimerCreator, StdRemoteEntityConfigProvider, TimerContext, TransactionId, StdRemoteEntityConfigProvider, StdTimerCreator, TimerContext, TimerCreatorProvider,
UserFaultHookProvider, TransactionId, UserFaultHookProvider,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use spacepackets::{ use spacepackets::{
@@ -249,7 +249,7 @@ pub struct DestinationHandler<
UserFaultHook: UserFaultHookProvider, UserFaultHook: UserFaultHookProvider,
Vfs: VirtualFilestore, Vfs: VirtualFilestore,
RemoteCfgTable: RemoteEntityConfigProvider, RemoteCfgTable: RemoteEntityConfigProvider,
CheckTimerCreator: CheckTimerProviderCreator<CheckTimer = CheckTimerProvider>, CheckTimerCreator: TimerCreatorProvider<Countdown = CheckTimerProvider>,
CheckTimerProvider: CountdownProvider, CheckTimerProvider: CountdownProvider,
> { > {
local_cfg: LocalEntityConfig<UserFaultHook>, local_cfg: LocalEntityConfig<UserFaultHook>,
@@ -269,8 +269,8 @@ pub type StdDestinationHandler<PduSender, UserFaultHook> = DestinationHandler<
UserFaultHook, UserFaultHook,
NativeFilestore, NativeFilestore,
StdRemoteEntityConfigProvider, StdRemoteEntityConfigProvider,
StdCheckTimerCreator, StdTimerCreator,
StdCheckTimer, StdCountdown,
>; >;
impl< impl<
@@ -278,7 +278,7 @@ impl<
UserFaultHook: UserFaultHookProvider, UserFaultHook: UserFaultHookProvider,
Vfs: VirtualFilestore, Vfs: VirtualFilestore,
RemoteCfgTable: RemoteEntityConfigProvider, RemoteCfgTable: RemoteEntityConfigProvider,
CheckTimerCreator: CheckTimerProviderCreator<CheckTimer = CheckTimerProvider>, CheckTimerCreator: TimerCreatorProvider<Countdown = CheckTimerProvider>,
CheckTimerProvider: CountdownProvider, CheckTimerProvider: CountdownProvider,
> >
DestinationHandler< DestinationHandler<
@@ -601,6 +601,7 @@ impl<
file_delivery_complete = true; file_delivery_complete = true;
} else { } else {
match self.vfs.checksum_verify( match self.vfs.checksum_verify(
checksum,
// Safety: It was already verified that the path is valid during the transaction start. // Safety: It was already verified that the path is valid during the transaction start.
unsafe { unsafe {
from_utf8_unchecked( from_utf8_unchecked(
@@ -609,7 +610,7 @@ impl<
) )
}, },
self.tparams.metadata_params().checksum_type, self.tparams.metadata_params().checksum_type,
checksum, self.tparams.tstate.progress,
&mut self.tparams.cksum_buf, &mut self.tparams.cksum_buf,
) { ) {
Ok(checksum_success) => { Ok(checksum_success) => {
@@ -642,14 +643,13 @@ impl<
fn start_check_limit_handling(&mut self) { fn start_check_limit_handling(&mut self) {
self.step = TransactionStep::ReceivingFileDataPdusWithCheckLimitHandling; self.step = TransactionStep::ReceivingFileDataPdusWithCheckLimitHandling;
self.tparams.tstate.current_check_timer = Some( self.tparams.tstate.current_check_timer = Some(self.check_timer_creator.create_countdown(
self.check_timer_creator TimerContext::CheckLimit {
.create_check_timer_provider(TimerContext::CheckLimit {
local_id: self.local_cfg.id, local_id: self.local_cfg.id,
remote_id: self.tparams.remote_cfg.unwrap().entity_id, remote_id: self.tparams.remote_cfg.unwrap().entity_id,
entity_type: EntityType::Receiving, entity_type: EntityType::Receiving,
}), },
); ));
self.tparams.tstate.current_check_count = 0; self.tparams.tstate.current_check_count = 0;
} }
@@ -951,8 +951,8 @@ mod tests {
basic_remote_cfg_table, SentPdu, TestCfdpSender, TestCfdpUser, TestFaultHandler, basic_remote_cfg_table, SentPdu, TestCfdpSender, TestCfdpUser, TestFaultHandler,
LOCAL_ID, LOCAL_ID,
}, },
CheckTimerProviderCreator, CountdownProvider, FaultHandler, IndicationConfig, CountdownProvider, FaultHandler, IndicationConfig, PduRawWithInfo,
PduRawWithInfo, StdRemoteEntityConfigProvider, CRC_32, StdRemoteEntityConfigProvider, TimerCreatorProvider, CRC_32,
}; };
use super::*; use super::*;
@@ -995,10 +995,10 @@ mod tests {
} }
} }
impl CheckTimerProviderCreator for TestCheckTimerCreator { impl TimerCreatorProvider for TestCheckTimerCreator {
type CheckTimer = TestCheckTimer; type Countdown = TestCheckTimer;
fn create_check_timer_provider(&self, timer_context: TimerContext) -> Self::CheckTimer { fn create_countdown(&self, timer_context: TimerContext) -> Self::Countdown {
match timer_context { match timer_context {
TimerContext::CheckLimit { .. } => { TimerContext::CheckLimit { .. } => {
TestCheckTimer::new(self.check_limit_expired_flag.clone()) TestCheckTimer::new(self.check_limit_expired_flag.clone())

View File

@@ -155,6 +155,7 @@ pub trait VirtualFilestore {
&self, &self,
file_path: &str, file_path: &str,
checksum_type: ChecksumType, checksum_type: ChecksumType,
size_to_verify: u64,
verification_buf: &mut [u8], verification_buf: &mut [u8],
) -> Result<u32, FilestoreError>; ) -> Result<u32, FilestoreError>;
@@ -167,13 +168,14 @@ pub trait VirtualFilestore {
/// 4096 or 8192 bytes. /// 4096 or 8192 bytes.
fn checksum_verify( fn checksum_verify(
&self, &self,
expected_checksum: u32,
file_path: &str, file_path: &str,
checksum_type: ChecksumType, checksum_type: ChecksumType,
expected_checksum: u32, size_to_verify: u64,
verification_buf: &mut [u8], verification_buf: &mut [u8],
) -> Result<bool, FilestoreError> { ) -> Result<bool, FilestoreError> {
Ok( Ok(
self.calculate_checksum(file_path, checksum_type, verification_buf)? self.calculate_checksum(file_path, checksum_type, size_to_verify, verification_buf)?
== expected_checksum, == expected_checksum,
) )
} }
@@ -326,18 +328,23 @@ pub mod std_mod {
&self, &self,
file_path: &str, file_path: &str,
checksum_type: ChecksumType, checksum_type: ChecksumType,
size_to_verify: u64,
verification_buf: &mut [u8], verification_buf: &mut [u8],
) -> Result<u32, FilestoreError> { ) -> Result<u32, FilestoreError> {
let mut calc_with_crc_lib = |crc: Crc<u32>| -> Result<u32, FilestoreError> { let mut calc_with_crc_lib = |crc: Crc<u32>| -> Result<u32, FilestoreError> {
let mut digest = crc.digest(); let mut digest = crc.digest();
let file_to_check = File::open(file_path)?; let mut buf_reader = BufReader::new(File::open(file_path)?);
let mut buf_reader = BufReader::new(file_to_check); let mut remaining_bytes = size_to_verify;
loop { while remaining_bytes > 0 {
let bytes_read = buf_reader.read(verification_buf)?; // Read the smaller of the remaining bytes or the buffer size
let bytes_to_read = remaining_bytes.min(verification_buf.len() as u64) as usize;
let bytes_read = buf_reader.read(&mut verification_buf[0..bytes_to_read])?;
if bytes_read == 0 { if bytes_read == 0 {
break; break; // Reached end of file
} }
digest.update(&verification_buf[0..bytes_read]); digest.update(&verification_buf[0..bytes_read]);
remaining_bytes -= bytes_read as u64;
} }
Ok(digest.finalize()) Ok(digest.finalize())
}; };
@@ -776,9 +783,10 @@ mod tests {
checksum = checksum.wrapping_add(u32::from_be_bytes(buffer)); checksum = checksum.wrapping_add(u32::from_be_bytes(buffer));
let mut verif_buf: [u8; 32] = [0; 32]; let mut verif_buf: [u8; 32] = [0; 32];
let result = NATIVE_FS.checksum_verify( let result = NATIVE_FS.checksum_verify(
checksum,
file_path.to_str().unwrap(), file_path.to_str().unwrap(),
ChecksumType::Modular, ChecksumType::Modular,
checksum, EXAMPLE_DATA_CFDP.len() as u64,
&mut verif_buf, &mut verif_buf,
); );
assert!(result.is_ok()); assert!(result.is_ok());
@@ -791,6 +799,7 @@ mod tests {
// The file to check does not even need to exist, and the verification buffer can be // The file to check does not even need to exist, and the verification buffer can be
// empty: the null checksum is always yields the same result. // empty: the null checksum is always yields the same result.
let result = NATIVE_FS.checksum_verify( let result = NATIVE_FS.checksum_verify(
0,
file_path.to_str().unwrap(), file_path.to_str().unwrap(),
ChecksumType::NullChecksum, ChecksumType::NullChecksum,
0, 0,
@@ -807,6 +816,7 @@ mod tests {
// The file to check does not even need to exist, and the verification buffer can be // The file to check does not even need to exist, and the verification buffer can be
// empty: the null checksum is always yields the same result. // empty: the null checksum is always yields the same result.
let result = NATIVE_FS.checksum_verify( let result = NATIVE_FS.checksum_verify(
0,
file_path.to_str().unwrap(), file_path.to_str().unwrap(),
ChecksumType::Crc32Proximity1, ChecksumType::Crc32Proximity1,
0, 0,

View File

@@ -111,10 +111,10 @@ pub enum TimerContext {
/// The timer will be used to perform the Positive Acknowledgement Procedures as specified in /// The timer will be used to perform the Positive Acknowledgement Procedures as specified in
/// 4.7. 1of the CFDP standard. The expiration period will be provided by the Positive ACK timer /// 4.7. 1of the CFDP standard. The expiration period will be provided by the Positive ACK timer
/// interval of the remote entity configuration. /// interval of the remote entity configuration.
pub trait CheckTimerProviderCreator { pub trait TimerCreatorProvider {
type CheckTimer: CountdownProvider; type Countdown: CountdownProvider;
fn create_check_timer_provider(&self, timer_context: TimerContext) -> Self::CheckTimer; fn create_countdown(&self, timer_context: TimerContext) -> Self::Countdown;
} }
/// This structure models the remote entity configuration information as specified in chapter 8.3 /// This structure models the remote entity configuration information as specified in chapter 8.3
@@ -591,12 +591,12 @@ pub mod std_mod {
/// Simple implementation of the [CountdownProvider] trait assuming a standard runtime. /// Simple implementation of the [CountdownProvider] trait assuming a standard runtime.
/// It also assumes that a second accuracy of the check timer period is sufficient. /// It also assumes that a second accuracy of the check timer period is sufficient.
#[derive(Debug)] #[derive(Debug)]
pub struct StdCheckTimer { pub struct StdCountdown {
expiry_time_seconds: u64, expiry_time_seconds: u64,
start_time: std::time::Instant, start_time: std::time::Instant,
} }
impl StdCheckTimer { impl StdCountdown {
pub fn new(expiry_time_seconds: u64) -> Self { pub fn new(expiry_time_seconds: u64) -> Self {
Self { Self {
expiry_time_seconds, expiry_time_seconds,
@@ -609,7 +609,7 @@ pub mod std_mod {
} }
} }
impl CountdownProvider for StdCheckTimer { impl CountdownProvider for StdCountdown {
fn has_expired(&self) -> bool { fn has_expired(&self) -> bool {
let elapsed_time = self.start_time.elapsed(); let elapsed_time = self.start_time.elapsed();
if elapsed_time.as_nanos() > self.expiry_time_seconds as u128 * 1_000_000_000 { if elapsed_time.as_nanos() > self.expiry_time_seconds as u128 * 1_000_000_000 {
@@ -623,11 +623,11 @@ pub mod std_mod {
} }
} }
pub struct StdCheckTimerCreator { pub struct StdTimerCreator {
pub check_limit_timeout_secs: u64, pub check_limit_timeout_secs: u64,
} }
impl StdCheckTimerCreator { impl StdTimerCreator {
pub const fn new(check_limit_timeout_secs: u64) -> Self { pub const fn new(check_limit_timeout_secs: u64) -> Self {
Self { Self {
check_limit_timeout_secs, check_limit_timeout_secs,
@@ -635,28 +635,28 @@ pub mod std_mod {
} }
} }
impl Default for StdCheckTimerCreator { impl Default for StdTimerCreator {
fn default() -> Self { fn default() -> Self {
Self::new(5) Self::new(5)
} }
} }
impl CheckTimerProviderCreator for StdCheckTimerCreator { impl TimerCreatorProvider for StdTimerCreator {
type CheckTimer = StdCheckTimer; type Countdown = StdCountdown;
fn create_check_timer_provider(&self, timer_context: TimerContext) -> Self::CheckTimer { fn create_countdown(&self, timer_context: TimerContext) -> Self::Countdown {
match timer_context { match timer_context {
TimerContext::CheckLimit { TimerContext::CheckLimit {
local_id: _, local_id: _,
remote_id: _, remote_id: _,
entity_type: _, entity_type: _,
} => StdCheckTimer::new(self.check_limit_timeout_secs), } => StdCountdown::new(self.check_limit_timeout_secs),
TimerContext::NakActivity { TimerContext::NakActivity {
expiry_time_seconds, expiry_time_seconds,
} => StdCheckTimer::new(Duration::from_secs_f32(expiry_time_seconds).as_secs()), } => StdCountdown::new(Duration::from_secs_f32(expiry_time_seconds).as_secs()),
TimerContext::PositiveAck { TimerContext::PositiveAck {
expiry_time_seconds, expiry_time_seconds,
} => StdCheckTimer::new(Duration::from_secs_f32(expiry_time_seconds).as_secs()), } => StdCountdown::new(Duration::from_secs_f32(expiry_time_seconds).as_secs()),
} }
} }
} }
@@ -725,6 +725,8 @@ pub enum PacketTarget {
DestEntity, DestEntity,
} }
/// Generic trait which models a raw CFDP packet data unit (PDU) block with some additional context
/// information.
pub trait PduProvider { pub trait PduProvider {
fn pdu_type(&self) -> PduType; fn pdu_type(&self) -> PduType;
fn file_directive_type(&self) -> Option<FileDirectiveType>; fn file_directive_type(&self) -> Option<FileDirectiveType>;
@@ -948,7 +950,7 @@ pub(crate) mod tests {
}; };
use user::{CfdpUser, OwnedMetadataRecvdParams, TransactionFinishedParams}; use user::{CfdpUser, OwnedMetadataRecvdParams, TransactionFinishedParams};
use crate::{PacketTarget, StdCheckTimer}; use crate::{PacketTarget, StdCountdown};
use super::*; use super::*;
@@ -1298,7 +1300,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_std_check_timer() { fn test_std_check_timer() {
let mut std_check_timer = StdCheckTimer::new(1); let mut std_check_timer = StdCountdown::new(1);
assert!(!std_check_timer.has_expired()); assert!(!std_check_timer.has_expired());
assert_eq!(std_check_timer.expiry_time_seconds(), 1); assert_eq!(std_check_timer.expiry_time_seconds(), 1);
std::thread::sleep(Duration::from_millis(800)); std::thread::sleep(Duration::from_millis(800));
@@ -1311,9 +1313,8 @@ pub(crate) mod tests {
#[test] #[test]
fn test_std_check_timer_creator() { fn test_std_check_timer_creator() {
let std_check_timer_creator = StdCheckTimerCreator::new(1); let std_check_timer_creator = StdTimerCreator::new(1);
let check_timer = let check_timer = std_check_timer_creator.create_countdown(TimerContext::NakActivity {
std_check_timer_creator.create_check_timer_provider(TimerContext::NakActivity {
expiry_time_seconds: 1.0, expiry_time_seconds: 1.0,
}); });
assert_eq!(check_timer.expiry_time_seconds(), 1); assert_eq!(check_timer.expiry_time_seconds(), 1);

View File

@@ -60,7 +60,10 @@ use spacepackets::{
use spacepackets::seq_count::SequenceCountProvider; use spacepackets::seq_count::SequenceCountProvider;
use crate::{DummyPduProvider, GenericSendError, PduProvider}; use crate::{
time::CountdownProvider, DummyPduProvider, EntityType, GenericSendError, PduProvider,
TimerCreatorProvider,
};
use super::{ use super::{
filestore::{FilestoreError, VirtualFilestore}, filestore::{FilestoreError, VirtualFilestore},
@@ -208,6 +211,8 @@ pub struct SourceHandler<
UserFaultHook: UserFaultHookProvider, UserFaultHook: UserFaultHookProvider,
Vfs: VirtualFilestore, Vfs: VirtualFilestore,
RemoteCfgTable: RemoteEntityConfigProvider, RemoteCfgTable: RemoteEntityConfigProvider,
TimerCreator: TimerCreatorProvider<Countdown = Countdown>,
Countdown: CountdownProvider,
SeqCountProvider: SequenceCountProvider, SeqCountProvider: SequenceCountProvider,
> { > {
local_cfg: LocalEntityConfig<UserFaultHook>, local_cfg: LocalEntityConfig<UserFaultHook>,
@@ -223,6 +228,8 @@ pub struct SourceHandler<
fparams: FileParams, fparams: FileParams,
// PDU configuration is cached so it can be re-used for all PDUs generated for file transfers. // PDU configuration is cached so it can be re-used for all PDUs generated for file transfers.
pdu_conf: CommonPduConfig, pdu_conf: CommonPduConfig,
countdown: Option<Countdown>,
timer_creator: TimerCreator,
seq_count_provider: SeqCountProvider, seq_count_provider: SeqCountProvider,
} }
@@ -231,8 +238,19 @@ impl<
UserFaultHook: UserFaultHookProvider, UserFaultHook: UserFaultHookProvider,
Vfs: VirtualFilestore, Vfs: VirtualFilestore,
RemoteCfgTable: RemoteEntityConfigProvider, RemoteCfgTable: RemoteEntityConfigProvider,
CheckTimerCreator: TimerCreatorProvider<Countdown = CheckTimerProvider>,
CheckTimerProvider: CountdownProvider,
SeqCountProvider: SequenceCountProvider, SeqCountProvider: SequenceCountProvider,
> SourceHandler<PduSender, UserFaultHook, Vfs, RemoteCfgTable, SeqCountProvider> >
SourceHandler<
PduSender,
UserFaultHook,
Vfs,
RemoteCfgTable,
CheckTimerCreator,
CheckTimerProvider,
SeqCountProvider,
>
{ {
/// Creates a new instance of a source handler. /// Creates a new instance of a source handler.
/// ///
@@ -251,8 +269,12 @@ impl<
/// example 2048 or 4096 bytes. /// example 2048 or 4096 bytes.
/// * `remote_cfg_table` - The [RemoteEntityConfigProvider] used to look up remote /// * `remote_cfg_table` - The [RemoteEntityConfigProvider] used to look up remote
/// entities and target specific configuration for file copy operations. /// entities and target specific configuration for file copy operations.
/// * `check_timer_creator` - [CheckTimerProviderCreator] used by the CFDP handler to generate
/// timers required by various tasks. This allows to use this handler for embedded systems
/// where the standard time APIs might not be available.
/// * `seq_count_provider` - The [SequenceCountProvider] used to generate the [TransactionId] /// * `seq_count_provider` - The [SequenceCountProvider] used to generate the [TransactionId]
/// which contains an incrementing counter. /// which contains an incrementing counter.
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
cfg: LocalEntityConfig<UserFaultHook>, cfg: LocalEntityConfig<UserFaultHook>,
pdu_sender: PduSender, pdu_sender: PduSender,
@@ -260,6 +282,7 @@ impl<
put_request_cacher: StaticPutRequestCacher, put_request_cacher: StaticPutRequestCacher,
pdu_and_cksum_buf_size: usize, pdu_and_cksum_buf_size: usize,
remote_cfg_table: RemoteCfgTable, remote_cfg_table: RemoteCfgTable,
check_timer_creator: CheckTimerCreator,
seq_count_provider: SeqCountProvider, seq_count_provider: SeqCountProvider,
) -> Self { ) -> Self {
Self { Self {
@@ -273,6 +296,8 @@ impl<
tstate: Default::default(), tstate: Default::default(),
fparams: Default::default(), fparams: Default::default(),
pdu_conf: Default::default(), pdu_conf: Default::default(),
countdown: None,
timer_creator: check_timer_creator,
seq_count_provider, seq_count_provider,
} }
} }
@@ -296,9 +321,9 @@ impl<
pub fn state_machine( pub fn state_machine(
&mut self, &mut self,
cfdp_user: &mut impl CfdpUser, cfdp_user: &mut impl CfdpUser,
packet_to_insert: Option<&impl PduProvider>, pdu: Option<&impl PduProvider>,
) -> Result<u32, SourceError> { ) -> Result<u32, SourceError> {
if let Some(packet) = packet_to_insert { if let Some(packet) = pdu {
self.insert_packet(cfdp_user, packet)?; self.insert_packet(cfdp_user, packet)?;
} }
match self.state_helper.state { match self.state_helper.state {
@@ -306,7 +331,7 @@ impl<
// TODO: In acknowledged mode, add timer handling. // TODO: In acknowledged mode, add timer handling.
Ok(0) Ok(0)
} }
super::State::Busy => self.fsm_busy(cfdp_user), super::State::Busy => self.fsm_busy(cfdp_user, pdu),
super::State::Suspended => { super::State::Suspended => {
// There is now way to suspend the handler currently anyway. // There is now way to suspend the handler currently anyway.
Ok(0) Ok(0)
@@ -426,6 +451,34 @@ impl<
Ok(()) Ok(())
} }
/// This functions models the Cancel.request CFDP primitive and is the recommended way to
/// cancel a transaction.
///
/// This method will cause a Notice of Cancellation at this entity if a transaction is active
/// and the passed transaction ID matches the currently active transaction ID. Please note
/// that the state machine might still be active because a cancelled transfer might still
/// require some packets to be sent to the remote receiver entity.
///
/// If not unexpected errors occur, this method returns [true] if the transfer was cancelled
/// propery and [false] if there is no transaction active or the passed transaction ID and the
/// active ID do not match.
pub fn cancel_request(&mut self, transaction_id: &TransactionId) -> Result<bool, SourceError> {
if self.state_helper.state == super::State::Idle {
return Ok(false);
}
if let Some(active_id) = self.transaction_id() {
if active_id == *transaction_id {
self.declare_fault(ConditionCode::CancelRequestReceived)?;
return Ok(true);
}
}
Ok(false)
}
pub fn transaction_id(&self) -> Option<TransactionId> {
self.tstate.as_ref().map(|v| v.transaction_id)
}
fn calculate_max_file_seg_len(&self, remote_cfg: &RemoteEntityConfig) -> u64 { fn calculate_max_file_seg_len(&self, remote_cfg: &RemoteEntityConfig) -> u64 {
let mut derived_max_seg_len = calculate_max_file_seg_len_for_max_packet_len_and_pdu_header( let mut derived_max_seg_len = calculate_max_file_seg_len_for_max_packet_len_and_pdu_header(
&PduHeader::new_no_file_data(self.pdu_conf, 0), &PduHeader::new_no_file_data(self.pdu_conf, 0),
@@ -447,7 +500,11 @@ impl<
self.tstate.as_ref().map(|v| v.transmission_mode) self.tstate.as_ref().map(|v| v.transmission_mode)
} }
fn fsm_busy(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<u32, SourceError> { fn fsm_busy(
&mut self,
cfdp_user: &mut impl CfdpUser,
pdu: Option<&impl PduProvider>,
) -> Result<u32, SourceError> {
let mut sent_packets = 0; let mut sent_packets = 0;
if self.state_helper.step == TransactionStep::Idle { if self.state_helper.step == TransactionStep::Idle {
self.state_helper.step = TransactionStep::TransactionStart; self.state_helper.step = TransactionStep::TransactionStart;
@@ -473,6 +530,40 @@ impl<
sent_packets += 1; sent_packets += 1;
} }
if self.state_helper.step == TransactionStep::WaitingForFinished { if self.state_helper.step == TransactionStep::WaitingForFinished {
self.handle_wait_for_finished_pdu(pdu)?;
}
if self.state_helper.step == TransactionStep::NoticeOfCompletion {
self.notice_of_completion(cfdp_user);
self.reset();
}
Ok(sent_packets)
}
fn handle_wait_for_finished_pdu(
&mut self,
packet: Option<&impl PduProvider>,
) -> Result<(), SourceError> {
if let Some(packet) = packet {
if let Some(FileDirectiveType::FinishedPdu) = packet.file_directive_type() {
let finished_pdu = FinishedPduReader::new(packet.pdu())?;
self.tstate.as_mut().unwrap().finished_params = Some(FinishedParams {
condition_code: finished_pdu.condition_code(),
delivery_code: finished_pdu.delivery_code(),
file_status: finished_pdu.file_status(),
});
if self.transmission_mode().unwrap() == TransmissionMode::Acknowledged {
// TODO: Ack packet handling
self.state_helper.step = TransactionStep::NoticeOfCompletion;
} else {
self.state_helper.step = TransactionStep::NoticeOfCompletion;
}
return Ok(());
}
}
// If we reach this state, countdown is definitely valid instance.
if self.countdown.as_ref().unwrap().has_expired() {
self.declare_fault(ConditionCode::CheckLimitReached)?;
}
/* /*
def _handle_wait_for_finish(self): def _handle_wait_for_finish(self):
if ( if (
@@ -498,12 +589,7 @@ impl<
else: else:
self.states.step = TransactionStep.NOTICE_OF_COMPLETION self.states.step = TransactionStep.NOTICE_OF_COMPLETION
*/ */
} Ok(())
if self.state_helper.step == TransactionStep::NoticeOfCompletion {
self.notice_of_completion(cfdp_user);
self.reset();
}
Ok(sent_packets)
} }
fn eof_fsm(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<(), SourceError> { fn eof_fsm(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<(), SourceError> {
@@ -511,6 +597,7 @@ impl<
let checksum = self.vfs.calculate_checksum( let checksum = self.vfs.calculate_checksum(
self.put_request_cacher.source_file().unwrap(), self.put_request_cacher.source_file().unwrap(),
tstate.remote_cfg.default_crc_type, tstate.remote_cfg.default_crc_type,
self.fparams.file_size,
self.pdu_and_cksum_buffer.get_mut(), self.pdu_and_cksum_buffer.get_mut(),
)?; )?;
self.prepare_and_send_eof_pdu(checksum)?; self.prepare_and_send_eof_pdu(checksum)?;
@@ -520,7 +607,13 @@ impl<
} }
if tstate.transmission_mode == TransmissionMode::Unacknowledged { if tstate.transmission_mode == TransmissionMode::Unacknowledged {
if tstate.closure_requested { if tstate.closure_requested {
// TODO: Check timer handling. self.countdown = Some(self.timer_creator.create_countdown(
crate::TimerContext::CheckLimit {
local_id: self.local_cfg.id,
remote_id: tstate.remote_cfg.entity_id,
entity_type: EntityType::Sending,
},
));
self.state_helper.step = TransactionStep::WaitingForFinished; self.state_helper.step = TransactionStep::WaitingForFinished;
} else { } else {
self.state_helper.step = TransactionStep::NoticeOfCompletion; self.state_helper.step = TransactionStep::NoticeOfCompletion;
@@ -874,6 +967,97 @@ impl<
&self.local_cfg &self.local_cfg
} }
fn declare_fault(&mut self, cond: ConditionCode) -> Result<(), SourceError> {
let fh = self.local_cfg.fault_handler.get_fault_handler(cond);
match fh {
spacepackets::cfdp::FaultHandlerCode::NoticeOfCancellation => {
if let ControlFlow::Break(_) = self.notice_of_cancellation(cond)? {
return Ok(());
}
}
spacepackets::cfdp::FaultHandlerCode::NoticeOfSuspension => {
self.notice_of_suspension();
}
spacepackets::cfdp::FaultHandlerCode::IgnoreError => (),
spacepackets::cfdp::FaultHandlerCode::AbandonTransaction => self.abandon_transaction(),
}
let tstate = self.tstate.as_ref().unwrap();
self.local_cfg.fault_handler.report_fault(
tstate.transaction_id,
cond,
self.fparams.progress,
);
Ok(())
}
fn notice_of_cancellation(
&mut self,
condition_code: ConditionCode,
) -> Result<ControlFlow<()>, SourceError> {
// CFDP standard 4.11.2.2.3: Any fault declared in the course of transferring
// the EOF (cancel) PDU must result in abandonment of the transaction.
if let Some(cond_code_eof) = self.tstate.as_ref().unwrap().cond_code_eof {
if cond_code_eof != ConditionCode::NoError {
let tstate = self.tstate.as_ref().unwrap();
// Still call the abandonment callback to ensure the fault is logged.
self.local_cfg
.fault_handler
.user_hook
.get_mut()
.abandoned_cb(tstate.transaction_id, cond_code_eof, self.fparams.progress);
self.abandon_transaction();
return Ok(ControlFlow::Break(()));
}
}
let tstate = self.tstate.as_mut().unwrap();
tstate.cond_code_eof = Some(condition_code);
// As specified in 4.11.2.2, prepare an EOF PDU to be sent to the remote entity. Supply
// the checksum for the file copy progress sent so far.
let checksum = self.vfs.calculate_checksum(
self.put_request_cacher.source_file().unwrap(),
tstate.remote_cfg.default_crc_type,
self.fparams.progress,
self.pdu_and_cksum_buffer.get_mut(),
)?;
self.prepare_and_send_eof_pdu(checksum)?;
Ok(ControlFlow::Continue(()))
}
fn notice_of_suspension(&mut self) {}
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.
self.reset();
}
/*
def _notice_of_cancellation(self, condition_code: ConditionCode) -> bool:
"""Returns whether the fault declaration handler can returns prematurely."""
# CFDP standard 4.11.2.2.3: Any fault declared in the course of transferring
# the EOF (cancel) PDU must result in abandonment of the transaction.
if (
self._params.cond_code_eof is not None
and self._params.cond_code_eof != ConditionCode.NO_ERROR
):
assert self._params.transaction_id is not None
# We still call the abandonment callback to ensure the fault is logged.
self.cfg.default_fault_handlers.abandoned_cb(
self._params.transaction_id,
self._params.cond_code_eof,
self._params.fp.progress,
)
self._abandon_transaction()
return False
self._params.cond_code_eof = condition_code
# As specified in 4.11.2.2, prepare an EOF PDU to be sent to the remote entity. Supply
# the checksum for the file copy progress sent so far.
self._prepare_eof_pdu(self._checksum_calculation(self._params.fp.progress))
self.states.step = TransactionStep.SENDING_EOF
return True
*/
/// This function is public to allow completely resetting the handler, but it is explicitely /// This function is public to allow completely resetting the handler, but it is explicitely
/// discouraged to do this. CFDP has mechanism to detect issues and errors on itself. /// discouraged to do this. CFDP has mechanism to detect issues and errors on itself.
/// Resetting the handler might interfere with these mechanisms and lead to unexpected /// Resetting the handler might interfere with these mechanisms and lead to unexpected
@@ -882,6 +1066,7 @@ impl<
self.state_helper = Default::default(); self.state_helper = Default::default();
self.tstate = None; self.tstate = None;
self.fparams = Default::default(); self.fparams = Default::default();
self.countdown = None;
} }
} }
@@ -907,7 +1092,8 @@ mod tests {
filestore::NativeFilestore, filestore::NativeFilestore,
request::PutRequestOwned, request::PutRequestOwned,
tests::{basic_remote_cfg_table, SentPdu, TestCfdpSender, TestCfdpUser, TestFaultHandler}, tests::{basic_remote_cfg_table, SentPdu, TestCfdpSender, TestCfdpUser, TestFaultHandler},
FaultHandler, IndicationConfig, PduRawWithInfo, StdRemoteEntityConfigProvider, CRC_32, FaultHandler, IndicationConfig, PduRawWithInfo, StdCountdown,
StdRemoteEntityConfigProvider, StdTimerCreator, CRC_32,
}; };
use spacepackets::seq_count::SeqCountProviderSimple; use spacepackets::seq_count::SeqCountProviderSimple;
@@ -927,6 +1113,8 @@ mod tests {
TestFaultHandler, TestFaultHandler,
NativeFilestore, NativeFilestore,
StdRemoteEntityConfigProvider, StdRemoteEntityConfigProvider,
StdTimerCreator,
StdCountdown,
SeqCountProviderSimple<u16>, SeqCountProviderSimple<u16>,
>; >;
@@ -967,6 +1155,7 @@ mod tests {
max_packet_len, max_packet_len,
crc_on_transmission_by_default, crc_on_transmission_by_default,
), ),
StdTimerCreator::new(1),
SeqCountProviderSimple::default(), SeqCountProviderSimple::default(),
), ),
srcfile_handle, srcfile_handle,

View File

@@ -14,7 +14,7 @@ use cfdp::{
source::SourceHandler, source::SourceHandler,
user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams, TransactionFinishedParams}, user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams, TransactionFinishedParams},
EntityType, IndicationConfig, LocalEntityConfig, PduOwnedWithInfo, RemoteEntityConfig, EntityType, IndicationConfig, LocalEntityConfig, PduOwnedWithInfo, RemoteEntityConfig,
StdCheckTimerCreator, TransactionId, UserFaultHookProvider, StdTimerCreator, TransactionId, UserFaultHookProvider,
}; };
use spacepackets::{ use spacepackets::{
cfdp::{ChecksumType, ConditionCode, TransmissionMode}, cfdp::{ChecksumType, ConditionCode, TransmissionMode},
@@ -202,6 +202,7 @@ fn end_to_end_test(with_closure: bool) {
put_request_cacher, put_request_cacher,
2048, 2048,
remote_cfg_of_dest, remote_cfg_of_dest,
StdTimerCreator::default(),
seq_count_provider, seq_count_provider,
); );
let mut cfdp_user_source = ExampleCfdpUser::new(EntityType::Sending, completion_signal_source); let mut cfdp_user_source = ExampleCfdpUser::new(EntityType::Sending, completion_signal_source);
@@ -225,7 +226,7 @@ fn end_to_end_test(with_closure: bool) {
dest_tx, dest_tx,
NativeFilestore::default(), NativeFilestore::default(),
remote_cfg_of_source, remote_cfg_of_source,
StdCheckTimerCreator::default(), StdTimerCreator::default(),
); );
let mut cfdp_user_dest = ExampleCfdpUser::new(EntityType::Receiving, completion_signal_dest); let mut cfdp_user_dest = ExampleCfdpUser::new(EntityType::Receiving, completion_signal_dest);