come on, neotest..
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit

This commit is contained in:
Robin Müller 2023-12-13 15:15:22 +01:00
parent 51d3c9b6e8
commit c766ab2d71
Signed by: muellerr
GPG Key ID: A649FB78196E3849
4 changed files with 201 additions and 13 deletions

View File

@ -8,9 +8,10 @@ use std::{
use crate::cfdp::user::TransactionFinishedParams; use crate::cfdp::user::TransactionFinishedParams;
use super::{ use super::{
filestore::{NativeFilestore, VirtualFilestore},
user::{CfdpUser, MetadataReceivedParams}, user::{CfdpUser, MetadataReceivedParams},
CheckTimerCreator, PacketInfo, PacketTarget, RemoteEntityConfigProvider, State, TransactionId, CheckTimerCreator, DefaultFaultHandler, PacketInfo, PacketTarget, RemoteEntityConfig,
TransactionStep, CRC_32, RemoteEntityConfigProvider, State, TransactionId, TransactionStep, CRC_32,
}; };
use alloc::boxed::Box; use alloc::boxed::Box;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -21,10 +22,10 @@ use spacepackets::{
file_data::FileDataPdu, file_data::FileDataPdu,
finished::{DeliveryCode, FileStatus, FinishedPduCreator}, finished::{DeliveryCode, FileStatus, FinishedPduCreator},
metadata::{MetadataGenericParams, MetadataPduReader}, metadata::{MetadataGenericParams, MetadataPduReader},
CommonPduConfig, FileDirectiveType, PduError, PduHeader, WritablePduPacket, CfdpPdu, CfdpPdu, CommonPduConfig, FileDirectiveType, PduError, PduHeader, WritablePduPacket,
}, },
tlv::{msg_to_user::MsgToUserTlv, EntityIdTlv, TlvType, GenericTlv}, tlv::{msg_to_user::MsgToUserTlv, EntityIdTlv, GenericTlv, TlvType},
ConditionCode, PduType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
}, },
util::UnsignedByteField, util::UnsignedByteField,
}; };
@ -36,6 +37,8 @@ pub struct DestinationHandler {
state: State, state: State,
tparams: TransactionParams, tparams: TransactionParams,
packets_to_send_ctx: PacketsToSendContext, packets_to_send_ctx: PacketsToSendContext,
vfs: Box<dyn VirtualFilestore>,
default_fault_handler: DefaultFaultHandler,
remote_cfg_table: Box<dyn RemoteEntityConfigProvider>, remote_cfg_table: Box<dyn RemoteEntityConfigProvider>,
check_timer_creator: Box<dyn CheckTimerCreator>, check_timer_creator: Box<dyn CheckTimerCreator>,
} }
@ -77,6 +80,7 @@ impl Default for TransferState {
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
struct TransactionParams { struct TransactionParams {
tstate: TransferState, tstate: TransferState,
@ -85,6 +89,13 @@ struct TransactionParams {
cksum_buf: [u8; 1024], cksum_buf: [u8; 1024],
msgs_to_user_size: usize, msgs_to_user_size: usize,
msgs_to_user_buf: [u8; 1024], msgs_to_user_buf: [u8; 1024],
remote_cfg: Option<RemoteEntityConfig>,
}
impl TransactionParams {
fn transmission_mode(&self) -> TransmissionMode {
self.pdu_conf.trans_mode
}
} }
impl Default for FileProperties { impl Default for FileProperties {
@ -118,6 +129,7 @@ impl Default for TransactionParams {
msgs_to_user_buf: [0; 1024], msgs_to_user_buf: [0; 1024],
tstate: Default::default(), tstate: Default::default(),
file_properties: Default::default(), file_properties: Default::default(),
remote_cfg: None,
} }
} }
} }
@ -153,11 +165,15 @@ pub enum DestError {
PathConversion(#[from] Utf8Error), PathConversion(#[from] Utf8Error),
#[error("error building dest path from source file name and dest folder")] #[error("error building dest path from source file name and dest folder")]
PathConcatError, PathConcatError,
#[error("no remote entity configuration found for {0:?}")]
NoRemoteCfgFound(UnsignedByteField),
} }
impl DestinationHandler { impl DestinationHandler {
pub fn new( pub fn new(
entity_id: impl Into<UnsignedByteField>, entity_id: impl Into<UnsignedByteField>,
vfs: Box<dyn VirtualFilestore>,
default_fault_handler: DefaultFaultHandler,
remote_cfg_table: Box<dyn RemoteEntityConfigProvider>, remote_cfg_table: Box<dyn RemoteEntityConfigProvider>,
check_timer_creator: Box<dyn CheckTimerCreator>, check_timer_creator: Box<dyn CheckTimerCreator>,
) -> Self { ) -> Self {
@ -167,6 +183,8 @@ impl DestinationHandler {
state: State::Idle, state: State::Idle,
tparams: Default::default(), tparams: Default::default(),
packets_to_send_ctx: Default::default(), packets_to_send_ctx: Default::default(),
vfs,
default_fault_handler,
remote_cfg_table, remote_cfg_table,
check_timer_creator, check_timer_creator,
} }
@ -286,6 +304,15 @@ impl DestinationHandler {
let metadata_pdu = MetadataPduReader::from_bytes(raw_packet)?; let metadata_pdu = MetadataPduReader::from_bytes(raw_packet)?;
self.tparams.reset(); self.tparams.reset();
self.tparams.tstate.metadata_params = *metadata_pdu.metadata_params(); self.tparams.tstate.metadata_params = *metadata_pdu.metadata_params();
let remote_cfg = self
.remote_cfg_table
.get_remote_config(metadata_pdu.dest_id().value());
if remote_cfg.is_none() {
return Err(DestError::NoRemoteCfgFound(metadata_pdu.dest_id()));
}
self.tparams.remote_cfg = Some(*remote_cfg.unwrap());
// TODO: Support for metadata only PDUs.
let src_name = metadata_pdu.src_file_name(); let src_name = metadata_pdu.src_file_name();
if src_name.is_empty() { if src_name.is_empty() {
return Err(DestError::EmptySrcFileField); return Err(DestError::EmptySrcFileField);
@ -332,17 +359,24 @@ impl DestinationHandler {
Ok(()) Ok(())
} }
#[allow(clippy::needless_if)]
pub fn handle_eof_pdu(&mut self, raw_packet: &[u8]) -> Result<(), DestError> { pub fn handle_eof_pdu(&mut self, raw_packet: &[u8]) -> Result<(), DestError> {
if self.state == State::Idle || self.step != TransactionStep::ReceivingFileDataPdus { if self.state == State::Idle || self.step != TransactionStep::ReceivingFileDataPdus {
return Err(DestError::WrongStateForFileDataAndEof); return Err(DestError::WrongStateForFileDataAndEof);
} }
let eof_pdu = EofPdu::from_bytes(raw_packet)?; let eof_pdu = EofPdu::from_bytes(raw_packet)?;
let checksum = eof_pdu.file_checksum(); if eof_pdu.condition_code() == ConditionCode::NoError {
self.handle_no_error_eof_pdu(&eof_pdu)?;
} else {
todo!("implement cancel request handling");
}
Ok(())
}
fn handle_no_error_eof_pdu(&mut self, eof_pdu: &EofPdu) -> Result<(), DestError> {
// For a standard disk based file system, which is assumed to be used for now, the file // For a standard disk based file system, which is assumed to be used for now, the file
// will always be retained. This might change in the future. // will always be retained. This might change in the future.
self.tparams.tstate.file_status = FileStatus::Retained; self.tparams.tstate.file_status = FileStatus::Retained;
if self.checksum_check(checksum)? { if self.checksum_check(eof_pdu.file_checksum())? {
self.tparams.tstate.condition_code = ConditionCode::NoError; self.tparams.tstate.condition_code = ConditionCode::NoError;
self.tparams.tstate.delivery_code = DeliveryCode::Complete; self.tparams.tstate.delivery_code = DeliveryCode::Complete;
} else { } else {
@ -351,7 +385,7 @@ impl DestinationHandler {
// TODO: Check progress, and implement transfer completion timer as specified in the // TODO: Check progress, and implement transfer completion timer as specified in the
// standard. This timer protects against out of order arrival of packets. // standard. This timer protects against out of order arrival of packets.
if self.tparams.tstate.progress != self.tparams.file_size() {} if self.tparams.tstate.progress != self.tparams.file_size() {}
if self.state == State::Busy { if self.tparams.transmission_mode() == TransmissionMode::Unacknowledged {
self.step = TransactionStep::TransferCompletion; self.step = TransactionStep::TransferCompletion;
} else { } else {
self.step = TransactionStep::SendingAckPdu; self.step = TransactionStep::SendingAckPdu;
@ -364,6 +398,7 @@ impl DestinationHandler {
} }
fn checksum_check(&mut self, expected_checksum: u32) -> Result<bool, DestError> { fn checksum_check(&mut self, expected_checksum: u32) -> Result<bool, DestError> {
// TODO: Implement this using the new virtual filestore abstraction.
let mut digest = CRC_32.digest(); let mut digest = CRC_32.digest();
let file_to_check = File::open(&self.tparams.file_properties.dest_path_buf)?; let file_to_check = File::open(&self.tparams.file_properties.dest_path_buf)?;
let mut buf_reader = BufReader::new(file_to_check); let mut buf_reader = BufReader::new(file_to_check);
@ -492,6 +527,11 @@ impl DestinationHandler {
Ok(()) Ok(())
} }
fn declare_fault(&mut self, condition_code: ConditionCode) -> FaultHandlerCode {
todo!("implement this. requires cached fault handler abstraction");
FaultHandlerCode::IgnoreError
}
fn reset(&mut self) { fn reset(&mut self) {
self.step = TransactionStep::Idle; self.step = TransactionStep::Idle;
self.state = State::Idle; self.state = State::Idle;
@ -520,12 +560,17 @@ mod tests {
use alloc::{format, string::String}; use alloc::{format, string::String};
use rand::Rng; use rand::Rng;
use spacepackets::{ use spacepackets::{
cfdp::{lv::Lv, pdu::{WritablePduPacket, metadata::MetadataPduCreator}, ChecksumType, TransmissionMode}, cfdp::{
lv::Lv,
pdu::{metadata::MetadataPduCreator, WritablePduPacket},
ChecksumType, TransmissionMode,
},
util::{UbfU16, UnsignedByteFieldU16}, util::{UbfU16, UnsignedByteFieldU16},
}; };
use crate::cfdp::{ use crate::cfdp::{
CheckTimer, CheckTimerCreator, RemoteEntityConfig, StdRemoteEntityConfigProvider, CheckTimer, CheckTimerCreator, RemoteEntityConfig, StdRemoteEntityConfigProvider,
UserFaultHandler,
}; };
use super::*; use super::*;
@ -553,6 +598,47 @@ mod tests {
} }
} }
#[derive(Default)]
struct TestFaultHandler {
notice_of_suspension_count: u32,
notice_of_cancellation_count: u32,
abandoned_count: u32,
ignored_count: u32,
}
impl UserFaultHandler for TestFaultHandler {
fn notice_of_suspension_cb(
&mut self,
transaction_id: TransactionId,
cond: ConditionCode,
progress: u64,
) {
self.notice_of_suspension_count += 1;
}
fn notice_of_cancellation_cb(
&mut self,
transaction_id: TransactionId,
cond: ConditionCode,
progress: u64,
) {
self.notice_of_cancellation_count += 1;
}
fn abandoned_cb(
&mut self,
transaction_id: TransactionId,
cond: ConditionCode,
progress: u64,
) {
self.abandoned_count += 1;
}
fn ignore_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64) {
self.ignored_count += 1;
}
}
impl CfdpUser for TestCfdpUser { impl CfdpUser for TestCfdpUser {
fn transaction_indication(&mut self, id: &crate::cfdp::TransactionId) { fn transaction_indication(&mut self, id: &crate::cfdp::TransactionId) {
self.generic_id_check(id); self.generic_id_check(id);
@ -719,8 +805,11 @@ mod tests {
} }
fn default_dest_handler() -> DestinationHandler { fn default_dest_handler() -> DestinationHandler {
let test_fault_handler = TestFaultHandler::default();
DestinationHandler::new( DestinationHandler::new(
REMOTE_ID, REMOTE_ID,
Box::new(NativeFilestore::default()),
DefaultFaultHandler::new(Box::new(test_fault_handler)),
Box::new(basic_remote_cfg_table()), Box::new(basic_remote_cfg_table()),
Box::new(TestCheckTimerCreator::new(2, 2)), Box::new(TestCheckTimerCreator::new(2, 2)),
) )

View File

@ -141,6 +141,7 @@ pub mod stdmod {
path::Path, path::Path,
}; };
#[derive(Default)]
pub struct NativeFilestore {} pub struct NativeFilestore {}
impl VirtualFilestore for NativeFilestore { impl VirtualFilestore for NativeFilestore {

View File

@ -5,7 +5,7 @@ use hashbrown::HashMap;
use spacepackets::{ use spacepackets::{
cfdp::{ cfdp::{
pdu::{FileDirectiveType, PduError, PduHeader}, pdu::{FileDirectiveType, PduError, PduHeader},
ChecksumType, PduType, TransmissionMode, ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
}, },
util::UnsignedByteField, util::UnsignedByteField,
}; };
@ -161,6 +161,103 @@ impl RemoteEntityConfigProvider for StdRemoteEntityConfigProvider {
} }
} }
/// This trait introduces some callbacks which will be called when a particular CFDP fault
/// handler is called. This allows to implement some CFDP features like fault handler logging,
/// which would not be possible generically otherwise.
pub trait UserFaultHandler {
fn notice_of_suspension_cb(
&mut self,
transaction_id: TransactionId,
cond: ConditionCode,
progress: u64,
);
fn notice_of_cancellation_cb(
&mut self,
transaction_id: TransactionId,
cond: ConditionCode,
progress: u64,
);
fn abandoned_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64);
fn ignore_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64);
}
pub struct DefaultFaultHandler {
handler_array: [FaultHandlerCode; 10],
user_fault_handler: Box<dyn UserFaultHandler + Send>,
}
impl DefaultFaultHandler {
fn condition_code_to_array_index(conditon_code: ConditionCode) -> Option<usize> {
Some(match conditon_code {
ConditionCode::PositiveAckLimitReached => 0,
ConditionCode::KeepAliveLimitReached => 1,
ConditionCode::InvalidTransmissionMode => 2,
ConditionCode::FilestoreRejection => 3,
ConditionCode::FileChecksumFailure => 4,
ConditionCode::FileSizeError => 5,
ConditionCode::NakLimitReached => 6,
ConditionCode::InactivityDetected => 7,
ConditionCode::CheckLimitReached => 8,
ConditionCode::UnsupportedChecksumType => 9,
_ => return None,
})
}
pub fn new(user_fault_handler: Box<dyn UserFaultHandler + Send>) -> Self {
let mut init_array = [FaultHandlerCode::NoticeOfCancellation; 10];
init_array
[Self::condition_code_to_array_index(ConditionCode::FileChecksumFailure).unwrap()] =
FaultHandlerCode::IgnoreError;
init_array[Self::condition_code_to_array_index(ConditionCode::UnsupportedChecksumType)
.unwrap()] = FaultHandlerCode::IgnoreError;
Self {
handler_array: init_array,
user_fault_handler,
}
}
fn report_fault(
&mut self,
transaction_id: TransactionId,
condition: ConditionCode,
progress: u64,
) -> FaultHandlerCode {
let array_idx = Self::condition_code_to_array_index(condition);
if array_idx.is_none() {
return FaultHandlerCode::IgnoreError;
}
let fh_code = self.handler_array[array_idx.unwrap()];
match fh_code {
FaultHandlerCode::NoticeOfCancellation => {
self.user_fault_handler.notice_of_cancellation_cb(
transaction_id,
condition,
progress,
);
}
FaultHandlerCode::NoticeOfSuspension => {
self.user_fault_handler.notice_of_suspension_cb(
transaction_id,
condition,
progress,
);
}
FaultHandlerCode::IgnoreError => {
self.user_fault_handler
.ignore_cb(transaction_id, condition, progress);
}
FaultHandlerCode::AbandonTransaction => {
self.user_fault_handler
.abandoned_cb(transaction_id, condition, progress);
}
}
fh_code
}
}
#[derive(Debug, Eq, Copy, Clone)] #[derive(Debug, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TransactionId { pub struct TransactionId {
@ -347,7 +444,8 @@ mod tests {
let dest_file_name = "hello-dest.txt"; let dest_file_name = "hello-dest.txt";
let src_lv = Lv::new_from_str(src_file_name).unwrap(); let src_lv = Lv::new_from_str(src_file_name).unwrap();
let dest_lv = Lv::new_from_str(dest_file_name).unwrap(); let dest_lv = Lv::new_from_str(dest_file_name).unwrap();
let metadata_pdu = MetadataPduCreator::new_no_opts(pdu_header, metadata_params, src_lv, dest_lv); let metadata_pdu =
MetadataPduCreator::new_no_opts(pdu_header, metadata_params, src_lv, dest_lv);
metadata_pdu metadata_pdu
.write_to_bytes(&mut buf) .write_to_bytes(&mut buf)
.expect("writing metadata PDU failed"); .expect("writing metadata PDU failed");

View File

@ -1,7 +1,7 @@
#![allow(dead_code)] #![allow(dead_code)]
use core::mem::size_of; use core::mem::size_of;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use spacepackets::ecss::{Ptc, PfcReal, PfcUnsigned}; use spacepackets::ecss::{PfcReal, PfcUnsigned, Ptc};
use spacepackets::time::cds::TimeProvider; use spacepackets::time::cds::TimeProvider;
use spacepackets::time::{CcsdsTimeProvider, TimeWriter}; use spacepackets::time::{CcsdsTimeProvider, TimeWriter};