source handler test
Some checks failed
Rust/cfdp/pipeline/head There was a failure building this commit
Some checks failed
Rust/cfdp/pipeline/head There was a failure building this commit
This commit is contained in:
155
src/source.rs
155
src/source.rs
@@ -462,47 +462,22 @@ impl<
|
|||||||
/// If not unexpected errors occur, this method returns [true] if the transfer was cancelled
|
/// 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
|
/// propery and [false] if there is no transaction active or the passed transaction ID and the
|
||||||
/// active ID do not match.
|
/// active ID do not match.
|
||||||
pub fn cancel_request(&mut self, transaction_id: &TransactionId) -> Result<bool, SourceError> {
|
pub fn cancel_request(&mut self, user: &mut impl CfdpUser, transaction_id: &TransactionId) -> Result<bool, SourceError> {
|
||||||
if self.state_helper.state == super::State::Idle {
|
if self.state_helper.state == super::State::Idle {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
if let Some(active_id) = self.transaction_id() {
|
if let Some(active_id) = self.transaction_id() {
|
||||||
if active_id == *transaction_id {
|
if active_id == *transaction_id {
|
||||||
self.declare_fault(ConditionCode::CancelRequestReceived)?;
|
self.notice_of_cancellation(user, ConditionCode::CancelRequestReceived)?;
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(false)
|
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 {
|
|
||||||
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),
|
|
||||||
remote_cfg.max_packet_len,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
if remote_cfg.max_file_segment_len.is_some() {
|
|
||||||
derived_max_seg_len = core::cmp::min(
|
|
||||||
remote_cfg.max_file_segment_len.unwrap(),
|
|
||||||
derived_max_seg_len,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
derived_max_seg_len as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the [TransmissionMode] for the active file operation.
|
|
||||||
#[inline]
|
|
||||||
pub fn transmission_mode(&self) -> Option<super::TransmissionMode> {
|
|
||||||
self.tstate.as_ref().map(|v| v.transmission_mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fsm_busy(
|
fn fsm_busy(
|
||||||
&mut self,
|
&mut self,
|
||||||
cfdp_user: &mut impl CfdpUser,
|
user: &mut impl CfdpUser,
|
||||||
pdu: Option<&impl PduProvider>,
|
pdu: Option<&impl PduProvider>,
|
||||||
) -> Result<u32, SourceError> {
|
) -> Result<u32, SourceError> {
|
||||||
let mut sent_packets = 0;
|
let mut sent_packets = 0;
|
||||||
@@ -510,7 +485,7 @@ impl<
|
|||||||
self.state_helper.step = TransactionStep::TransactionStart;
|
self.state_helper.step = TransactionStep::TransactionStart;
|
||||||
}
|
}
|
||||||
if self.state_helper.step == TransactionStep::TransactionStart {
|
if self.state_helper.step == TransactionStep::TransactionStart {
|
||||||
self.handle_transaction_start(cfdp_user)?;
|
self.handle_transaction_start(user)?;
|
||||||
self.state_helper.step = TransactionStep::SendingMetadata;
|
self.state_helper.step = TransactionStep::SendingMetadata;
|
||||||
}
|
}
|
||||||
if self.state_helper.step == TransactionStep::SendingMetadata {
|
if self.state_helper.step == TransactionStep::SendingMetadata {
|
||||||
@@ -526,14 +501,14 @@ impl<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.state_helper.step == TransactionStep::SendingEof {
|
if self.state_helper.step == TransactionStep::SendingEof {
|
||||||
self.eof_fsm(cfdp_user)?;
|
self.eof_fsm(user)?;
|
||||||
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)?;
|
self.handle_wait_for_finished_pdu(user, pdu)?;
|
||||||
}
|
}
|
||||||
if self.state_helper.step == TransactionStep::NoticeOfCompletion {
|
if self.state_helper.step == TransactionStep::NoticeOfCompletion {
|
||||||
self.notice_of_completion(cfdp_user);
|
self.notice_of_completion(user);
|
||||||
self.reset();
|
self.reset();
|
||||||
}
|
}
|
||||||
Ok(sent_packets)
|
Ok(sent_packets)
|
||||||
@@ -541,6 +516,7 @@ impl<
|
|||||||
|
|
||||||
fn handle_wait_for_finished_pdu(
|
fn handle_wait_for_finished_pdu(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
user: &mut impl CfdpUser,
|
||||||
packet: Option<&impl PduProvider>,
|
packet: Option<&impl PduProvider>,
|
||||||
) -> Result<(), SourceError> {
|
) -> Result<(), SourceError> {
|
||||||
if let Some(packet) = packet {
|
if let Some(packet) = packet {
|
||||||
@@ -562,7 +538,7 @@ impl<
|
|||||||
}
|
}
|
||||||
// If we reach this state, countdown is definitely valid instance.
|
// If we reach this state, countdown is definitely valid instance.
|
||||||
if self.countdown.as_ref().unwrap().has_expired() {
|
if self.countdown.as_ref().unwrap().has_expired() {
|
||||||
self.declare_fault(ConditionCode::CheckLimitReached)?;
|
self.declare_fault(user, ConditionCode::CheckLimitReached)?;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
def _handle_wait_for_finish(self):
|
def _handle_wait_for_finish(self):
|
||||||
@@ -592,7 +568,7 @@ impl<
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eof_fsm(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<(), SourceError> {
|
fn eof_fsm(&mut self, user: &mut impl CfdpUser) -> Result<(), SourceError> {
|
||||||
let tstate = self.tstate.as_ref().unwrap();
|
let tstate = self.tstate.as_ref().unwrap();
|
||||||
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(),
|
||||||
@@ -600,11 +576,8 @@ impl<
|
|||||||
self.fparams.file_size,
|
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(user, checksum)?;
|
||||||
let tstate = self.tstate.as_ref().unwrap();
|
let tstate = self.tstate.as_ref().unwrap();
|
||||||
if self.local_cfg.indication_cfg.eof_sent {
|
|
||||||
cfdp_user.eof_sent_indication(&tstate.transaction_id);
|
|
||||||
}
|
|
||||||
if tstate.transmission_mode == TransmissionMode::Unacknowledged {
|
if tstate.transmission_mode == TransmissionMode::Unacknowledged {
|
||||||
if tstate.closure_requested {
|
if tstate.closure_requested {
|
||||||
self.countdown = Some(self.timer_creator.create_countdown(
|
self.countdown = Some(self.timer_creator.create_countdown(
|
||||||
@@ -806,6 +779,21 @@ impl<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
&PduHeader::new_no_file_data(self.pdu_conf, 0),
|
||||||
|
remote_cfg.max_packet_len,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
if remote_cfg.max_file_segment_len.is_some() {
|
||||||
|
derived_max_seg_len = core::cmp::min(
|
||||||
|
remote_cfg.max_file_segment_len.unwrap(),
|
||||||
|
derived_max_seg_len,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
derived_max_seg_len as u64
|
||||||
|
}
|
||||||
|
|
||||||
fn send_progressing_file_data_pdu(&mut self) -> Result<bool, SourceError> {
|
fn send_progressing_file_data_pdu(&mut self) -> Result<bool, SourceError> {
|
||||||
// Should never be called, but use defensive programming here.
|
// Should never be called, but use defensive programming here.
|
||||||
if self.fparams.progress >= self.fparams.file_size {
|
if self.fparams.progress >= self.fparams.file_size {
|
||||||
@@ -887,7 +875,7 @@ impl<
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_and_send_eof_pdu(&mut self, checksum: u32) -> Result<(), SourceError> {
|
fn prepare_and_send_eof_pdu(&mut self, cfdp_user: &mut impl CfdpUser, checksum: u32) -> Result<(), SourceError> {
|
||||||
let tstate = self
|
let tstate = self
|
||||||
.tstate
|
.tstate
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -900,6 +888,9 @@ impl<
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
self.pdu_send_helper(&eof_pdu)?;
|
self.pdu_send_helper(&eof_pdu)?;
|
||||||
|
if self.local_cfg.indication_cfg.eof_sent {
|
||||||
|
cfdp_user.eof_sent_indication(&tstate.transaction_id);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -953,6 +944,16 @@ impl<
|
|||||||
|
|
||||||
fn handle_keep_alive_pdu(&mut self) {}
|
fn handle_keep_alive_pdu(&mut self) {}
|
||||||
|
|
||||||
|
pub fn transaction_id(&self) -> Option<TransactionId> {
|
||||||
|
self.tstate.as_ref().map(|v| v.transaction_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [TransmissionMode] for the active file operation.
|
||||||
|
#[inline]
|
||||||
|
pub fn transmission_mode(&self) -> Option<super::TransmissionMode> {
|
||||||
|
self.tstate.as_ref().map(|v| v.transmission_mode)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the [TransactionStep], which denotes the exact step of a pending CFDP transaction when
|
/// Get the [TransactionStep], which denotes the exact step of a pending CFDP transaction when
|
||||||
/// applicable.
|
/// applicable.
|
||||||
pub fn step(&self) -> TransactionStep {
|
pub fn step(&self) -> TransactionStep {
|
||||||
@@ -967,11 +968,18 @@ impl<
|
|||||||
&self.local_cfg
|
&self.local_cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_fault(&mut self, cond: ConditionCode) -> Result<(), SourceError> {
|
fn declare_fault(
|
||||||
|
&mut self,
|
||||||
|
user: &mut impl CfdpUser,
|
||||||
|
cond: ConditionCode,
|
||||||
|
) -> Result<(), SourceError> {
|
||||||
|
// Need to cache those in advance, because a notice of cancellation can reset the handler.
|
||||||
|
let transaction_id = self.tstate.as_ref().unwrap().transaction_id;
|
||||||
|
let progress = self.fparams.progress;
|
||||||
let fh = self.local_cfg.fault_handler.get_fault_handler(cond);
|
let fh = self.local_cfg.fault_handler.get_fault_handler(cond);
|
||||||
match fh {
|
match fh {
|
||||||
spacepackets::cfdp::FaultHandlerCode::NoticeOfCancellation => {
|
spacepackets::cfdp::FaultHandlerCode::NoticeOfCancellation => {
|
||||||
if let ControlFlow::Break(_) = self.notice_of_cancellation(cond)? {
|
if let ControlFlow::Break(_) = self.notice_of_cancellation(user, cond)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -981,30 +989,30 @@ impl<
|
|||||||
spacepackets::cfdp::FaultHandlerCode::IgnoreError => (),
|
spacepackets::cfdp::FaultHandlerCode::IgnoreError => (),
|
||||||
spacepackets::cfdp::FaultHandlerCode::AbandonTransaction => self.abandon_transaction(),
|
spacepackets::cfdp::FaultHandlerCode::AbandonTransaction => self.abandon_transaction(),
|
||||||
}
|
}
|
||||||
let tstate = self.tstate.as_ref().unwrap();
|
|
||||||
self.local_cfg.fault_handler.report_fault(
|
self.local_cfg.fault_handler.report_fault(
|
||||||
tstate.transaction_id,
|
transaction_id,
|
||||||
cond,
|
cond,
|
||||||
self.fparams.progress,
|
progress,
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notice_of_cancellation(
|
fn notice_of_cancellation(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
user: &mut impl CfdpUser,
|
||||||
condition_code: ConditionCode,
|
condition_code: ConditionCode,
|
||||||
) -> Result<ControlFlow<()>, SourceError> {
|
) -> Result<ControlFlow<()>, SourceError> {
|
||||||
|
let transaction_id = self.tstate.as_ref().unwrap().transaction_id;
|
||||||
// CFDP standard 4.11.2.2.3: Any fault declared in the course of transferring
|
// 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.
|
// 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 let Some(cond_code_eof) = self.tstate.as_ref().unwrap().cond_code_eof {
|
||||||
if cond_code_eof != ConditionCode::NoError {
|
if cond_code_eof != ConditionCode::NoError {
|
||||||
let tstate = self.tstate.as_ref().unwrap();
|
|
||||||
// Still call the abandonment callback to ensure the fault is logged.
|
// Still call the abandonment callback to ensure the fault is logged.
|
||||||
self.local_cfg
|
self.local_cfg
|
||||||
.fault_handler
|
.fault_handler
|
||||||
.user_hook
|
.user_hook
|
||||||
.get_mut()
|
.get_mut()
|
||||||
.abandoned_cb(tstate.transaction_id, cond_code_eof, self.fparams.progress);
|
.abandoned_cb(transaction_id, cond_code_eof, self.fparams.progress);
|
||||||
self.abandon_transaction();
|
self.abandon_transaction();
|
||||||
return Ok(ControlFlow::Break(()));
|
return Ok(ControlFlow::Break(()));
|
||||||
}
|
}
|
||||||
@@ -1020,7 +1028,13 @@ impl<
|
|||||||
self.fparams.progress,
|
self.fparams.progress,
|
||||||
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(user, checksum)?;
|
||||||
|
if self.transmission_mode().unwrap() == TransmissionMode::Unacknowledged {
|
||||||
|
// We are done.
|
||||||
|
self.reset();
|
||||||
|
} else {
|
||||||
|
self.state_helper.step = TransactionStep::WaitingForEofAck;
|
||||||
|
}
|
||||||
Ok(ControlFlow::Continue(()))
|
Ok(ControlFlow::Continue(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1072,7 +1086,8 @@ impl<
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{fs::OpenOptions, io::Write, path::PathBuf, vec::Vec};
|
use core::time::Duration;
|
||||||
|
use std::{fs::OpenOptions, io::Write, path::PathBuf, thread, vec::Vec};
|
||||||
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@@ -1175,6 +1190,10 @@ mod tests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_check_limit_timeout(&mut self, timeout: Duration) {
|
||||||
|
self.handler.timer_creator.check_limit_timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
fn put_request(
|
fn put_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
put_request: &impl ReadablePutRequest,
|
put_request: &impl ReadablePutRequest,
|
||||||
@@ -1601,14 +1620,48 @@ mod tests {
|
|||||||
let error = tb.put_request(&put_request);
|
let error = tb.put_request(&put_request);
|
||||||
assert!(error.is_err());
|
assert!(error.is_err());
|
||||||
let error = error.unwrap_err();
|
let error = error.unwrap_err();
|
||||||
if let PutRequestError::FileDoesNotExist = error {
|
if !matches!(error, PutRequestError::FileDoesNotExist) {
|
||||||
} else {
|
|
||||||
panic!("unexpected error type: {:?}", error);
|
panic!("unexpected error type: {:?}", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_finished_pdu_check_timeout() {
|
fn test_finished_pdu_check_timeout() {
|
||||||
// TODO: Implement.
|
let fault_handler = TestFaultHandler::default();
|
||||||
|
let test_sender = TestCfdpSender::default();
|
||||||
|
let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512);
|
||||||
|
tb.set_check_limit_timeout(Duration::from_millis(45));
|
||||||
|
let filesize = 0;
|
||||||
|
let put_request = PutRequestOwned::new_regular_request(
|
||||||
|
REMOTE_ID.into(),
|
||||||
|
&tb.srcfile,
|
||||||
|
&tb.destfile,
|
||||||
|
Some(TransmissionMode::Unacknowledged),
|
||||||
|
Some(true),
|
||||||
|
)
|
||||||
|
.expect("creating put request failed");
|
||||||
|
let mut cfdp_user = tb.create_user(0, filesize);
|
||||||
|
let (closure_requested, _pdu_header) =
|
||||||
|
tb.common_no_acked_file_transfer(&mut cfdp_user, put_request, filesize);
|
||||||
|
tb.common_eof_pdu_check(
|
||||||
|
&mut cfdp_user,
|
||||||
|
closure_requested,
|
||||||
|
filesize,
|
||||||
|
CRC_32.digest().finalize(),
|
||||||
|
);
|
||||||
|
// After 50 ms delay, we run into a timeout, which leads to a check limit error
|
||||||
|
// declaration -> leads to a notice of cancellation -> leads to an EOF PDU with the
|
||||||
|
// appropriate error code.
|
||||||
|
thread::sleep(Duration::from_millis(50));
|
||||||
|
assert_eq!(
|
||||||
|
tb.handler.state_machine_no_packet(&mut cfdp_user).unwrap(),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
let next_pdu = tb.get_next_sent_pdu().unwrap();
|
||||||
|
let eof_pdu = EofPdu::from_bytes(&next_pdu.raw_pdu).expect("invalid EOF PDU format");
|
||||||
|
tb.common_pdu_check_for_file_transfer(eof_pdu.pdu_header(), CrcFlag::NoCrc);
|
||||||
|
assert_eq!(eof_pdu.condition_code(), ConditionCode::CheckLimitReached);
|
||||||
|
assert_eq!(eof_pdu.file_size(), 0);
|
||||||
|
assert_eq!(eof_pdu.file_checksum(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user