Skip to content

Commit

Permalink
add first end-to-end test / example app
Browse files Browse the repository at this point in the history
  • Loading branch information
robamu committed Aug 22, 2024
1 parent b5196a9 commit de4509f
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 98 deletions.
4 changes: 3 additions & 1 deletion src/dest.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::user::TransactionFinishedParams;
use crate::{user::TransactionFinishedParams, GenericSendError};
use core::str::{from_utf8, Utf8Error};
use std::path::{Path, PathBuf};

Expand Down Expand Up @@ -184,6 +184,8 @@ pub enum DestError {
PathConcat,
#[error("no remote entity configuration found for {0:?}")]
NoRemoteCfgFound(UnsignedByteField),
#[error("issue sending PDU: {0}")]
SendError(#[from] GenericSendError),
#[error("cfdp feature not implemented")]
NotImplemented,
}
Expand Down
281 changes: 187 additions & 94 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ use core::{cell::RefCell, fmt::Debug, hash::Hash};
use crc::{Crc, CRC_32_CKSUM};
#[cfg(feature = "std")]
use hashbrown::HashMap;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use spacepackets::{
cfdp::{
pdu::{FileDirectiveType, PduError, PduHeader},
Expand All @@ -31,10 +34,6 @@ use spacepackets::{
};
#[cfg(feature = "std")]
use std::time::Duration;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "std")]
pub use std_mod::*;

Expand Down Expand Up @@ -101,84 +100,6 @@ pub trait CheckTimerProviderCreator {
fn create_check_timer_provider(&self, timer_context: TimerContext) -> Self::CheckTimer;
}

#[cfg(feature = "std")]
pub mod std_mod {
use super::*;

/// Simple implementation of the [CheckTimerCreator] trait assuming a standard runtime.
/// It also assumes that a second accuracy of the check timer period is sufficient.
#[derive(Debug)]
pub struct StdCheckTimer {
expiry_time_seconds: u64,
start_time: std::time::Instant,
}

impl StdCheckTimer {
pub fn new(expiry_time_seconds: u64) -> Self {
Self {
expiry_time_seconds,
start_time: std::time::Instant::now(),
}
}

pub fn expiry_time_seconds(&self) -> u64 {
self.expiry_time_seconds
}
}

impl CountdownProvider for StdCheckTimer {
fn has_expired(&self) -> bool {
let elapsed_time = self.start_time.elapsed();
if elapsed_time.as_nanos() > self.expiry_time_seconds as u128 * 1_000_000_000 {
return true;
}
false
}

fn reset(&mut self) {
self.start_time = std::time::Instant::now();
}
}

pub struct StdCheckTimerCreator {
pub check_limit_timeout_secs: u64,
}

impl StdCheckTimerCreator {
pub const fn new(check_limit_timeout_secs: u64) -> Self {
Self {
check_limit_timeout_secs,
}
}
}

impl Default for StdCheckTimerCreator {
fn default() -> Self {
Self::new(5)
}
}

impl CheckTimerProviderCreator for StdCheckTimerCreator {
type CheckTimer = StdCheckTimer;

fn create_check_timer_provider(&self, timer_context: TimerContext) -> Self::CheckTimer {
match timer_context {
TimerContext::CheckLimit {
local_id: _,
remote_id: _,
entity_type: _,
} => StdCheckTimer::new(self.check_limit_timeout_secs),
TimerContext::NakActivity {
expiry_time_seconds,
} => StdCheckTimer::new(Duration::from_secs_f32(expiry_time_seconds).as_secs()),
TimerContext::PositiveAck {
expiry_time_seconds,
} => StdCheckTimer::new(Duration::from_secs_f32(expiry_time_seconds).as_secs()),
}
}
}
}

/// This structure models the remote entity configuration information as specified in chapter 8.3
/// of the CFDP standard.
Expand Down Expand Up @@ -298,25 +219,70 @@ pub trait RemoteEntityConfigProvider {

#[cfg(feature = "std")]
#[derive(Default)]
pub struct StdRemoteEntityConfigProvider {
remote_cfg_table: HashMap<u64, RemoteEntityConfig>,
}
pub struct StdRemoteEntityConfigProvider(pub HashMap<u64, RemoteEntityConfig>);

#[cfg(feature = "std")]
impl RemoteEntityConfigProvider for StdRemoteEntityConfigProvider {
fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
self.remote_cfg_table.get(&remote_id)
self.0.get(&remote_id)
}
fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
self.remote_cfg_table.get_mut(&remote_id)
self.0.get_mut(&remote_id)
}
fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool {
self.remote_cfg_table
.insert(cfg.entity_id.value(), *cfg)
.is_some()
self.0.insert(cfg.entity_id.value(), *cfg).is_some()
}
fn remove_config(&mut self, remote_id: u64) -> bool {
self.remote_cfg_table.remove(&remote_id).is_some()
self.0.remove(&remote_id).is_some()
}
}

#[cfg(feature = "alloc")]
#[derive(Default)]
pub struct VecRemoteEntityConfigProvider(pub alloc::vec::Vec<RemoteEntityConfig>);

#[cfg(feature = "alloc")]
impl RemoteEntityConfigProvider for alloc::vec::Vec<RemoteEntityConfig> {
fn get(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
self.iter().find(|&cfg| cfg.entity_id.value() == remote_id)
}

fn get_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
self.iter_mut()
.find(|cfg| cfg.entity_id.value() == remote_id)
}

fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool {
self.push(*cfg);
true
}

fn remove_config(&mut self, remote_id: u64) -> bool {
for (idx, cfg) in self.iter().enumerate() {
if cfg.entity_id.value() == remote_id {
self.remove(idx);
return true;
}
}
false
}
}

impl RemoteEntityConfigProvider for RemoteEntityConfig {
fn get(&self, _remote_id: u64) -> Option<&RemoteEntityConfig> {
Some(self)
}

fn get_mut(&mut self, _remote_id: u64) -> Option<&mut RemoteEntityConfig> {
Some(self)
}

fn add_config(&mut self, _cfg: &RemoteEntityConfig) -> bool {
false
}

fn remove_config(&mut self, _remote_id: u64) -> bool {
false
}
}

Expand Down Expand Up @@ -516,6 +482,20 @@ pub struct LocalEntityConfig<UserFaultHook: UserFaultHookProvider> {
pub fault_handler: FaultHandler<UserFaultHook>,
}

impl<UserFaultHook: UserFaultHookProvider> LocalEntityConfig<UserFaultHook> {
pub fn new(
id: UnsignedByteField,
indication_cfg: IndicationConfig,
hook: UserFaultHook,
) -> Self {
Self {
id,
indication_cfg,
fault_handler: FaultHandler::new(hook),
}
}
}

impl<UserFaultHook: UserFaultHookProvider> LocalEntityConfig<UserFaultHook> {
pub fn user_fault_hook_mut(&mut self) -> &mut RefCell<UserFaultHook> {
&mut self.fault_handler.user_hook
Expand All @@ -526,13 +506,126 @@ impl<UserFaultHook: UserFaultHookProvider> LocalEntityConfig<UserFaultHook> {
}
}

/// Generic error type for sending a PDU via a message queue.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum GenericSendError {
#[error("RX disconnected")]
RxDisconnected,
#[error("queue is full, fill count {0:?}")]
QueueFull(Option<u32>),
}

pub trait PduSendProvider {
fn send_pdu(
&self,
pdu_type: PduType,
file_directive_type: Option<FileDirectiveType>,
raw_pdu: &[u8],
) -> Result<(), PduError>;
) -> Result<(), GenericSendError>;
}

#[cfg(feature = "std")]
pub mod std_mod {
use std::{sync::mpsc, vec::Vec};

use super::*;

pub struct PduWithInfo {
pub pdu_type: PduType,
pub file_directive_type: Option<FileDirectiveType>,
pub pdu: Vec<u8>,
}

impl PduSendProvider for mpsc::Sender<PduWithInfo> {
fn send_pdu(
&self,
pdu_type: PduType,
file_directive_type: Option<FileDirectiveType>,
raw_pdu: &[u8],
) -> Result<(), GenericSendError> {
self.send(PduWithInfo {
pdu_type,
file_directive_type,
pdu: raw_pdu.to_vec(),
})
.map_err(|_| GenericSendError::RxDisconnected)?;
Ok(())
}
}

/// Simple implementation of the [CheckTimerCreator] trait assuming a standard runtime.
/// It also assumes that a second accuracy of the check timer period is sufficient.
#[derive(Debug)]
pub struct StdCheckTimer {
expiry_time_seconds: u64,
start_time: std::time::Instant,
}

impl StdCheckTimer {
pub fn new(expiry_time_seconds: u64) -> Self {
Self {
expiry_time_seconds,
start_time: std::time::Instant::now(),
}
}

pub fn expiry_time_seconds(&self) -> u64 {
self.expiry_time_seconds
}
}

impl CountdownProvider for StdCheckTimer {
fn has_expired(&self) -> bool {
let elapsed_time = self.start_time.elapsed();
if elapsed_time.as_nanos() > self.expiry_time_seconds as u128 * 1_000_000_000 {
return true;
}
false
}

fn reset(&mut self) {
self.start_time = std::time::Instant::now();
}
}

pub struct StdCheckTimerCreator {
pub check_limit_timeout_secs: u64,
}

impl StdCheckTimerCreator {
pub const fn new(check_limit_timeout_secs: u64) -> Self {
Self {
check_limit_timeout_secs,
}
}
}

impl Default for StdCheckTimerCreator {
fn default() -> Self {
Self::new(5)
}
}

impl CheckTimerProviderCreator for StdCheckTimerCreator {
type CheckTimer = StdCheckTimer;

fn create_check_timer_provider(&self, timer_context: TimerContext) -> Self::CheckTimer {
match timer_context {
TimerContext::CheckLimit {
local_id: _,
remote_id: _,
entity_type: _,
} => StdCheckTimer::new(self.check_limit_timeout_secs),
TimerContext::NakActivity {
expiry_time_seconds,
} => StdCheckTimer::new(Duration::from_secs_f32(expiry_time_seconds).as_secs()),
TimerContext::PositiveAck {
expiry_time_seconds,
} => StdCheckTimer::new(Duration::from_secs_f32(expiry_time_seconds).as_secs()),
}
}
}
}

/// The CFDP transaction ID of a CFDP transaction consists of the source entity ID and the sequence
Expand Down Expand Up @@ -693,7 +786,7 @@ pub(crate) mod tests {
eof::EofPdu,
file_data::FileDataPdu,
metadata::{MetadataGenericParams, MetadataPduCreator},
CommonPduConfig, FileDirectiveType, PduError, PduHeader, WritablePduPacket,
CommonPduConfig, FileDirectiveType, PduHeader, WritablePduPacket,
},
ChecksumType, ConditionCode, PduType, TransmissionMode,
},
Expand Down Expand Up @@ -925,7 +1018,7 @@ pub(crate) mod tests {
pdu_type: PduType,
file_directive_type: Option<FileDirectiveType>,
raw_pdu: &[u8],
) -> Result<(), PduError> {
) -> Result<(), GenericSendError> {
self.packet_queue.borrow_mut().push_back(SentPdu {
pdu_type,
file_directive_type,
Expand Down
Loading

0 comments on commit de4509f

Please sign in to comment.