Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6df24b947 | |||
| 732fac1d1c | |||
| 226a7494a0 | |||
| e401d5f1ac | |||
| 1b7d128a10 | |||
| fae27bdf93 | |||
| d5e2162f52 | |||
| e3fff18cd2 | |||
| 5c579209f0 |
@@ -29,7 +29,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@1.75.0
|
- uses: dtolnay/rust-toolchain@1.81.0
|
||||||
- run: cargo check --release
|
- run: cargo check --release
|
||||||
|
|
||||||
cross-check:
|
cross-check:
|
||||||
|
|||||||
@@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
- Bumped `spacepackets` to v0.15
|
||||||
|
- Bumped `defmt` to v1
|
||||||
|
|
||||||
|
# [v0.2.0] 2024-11-26
|
||||||
|
|
||||||
|
- Bumped `thiserror` to v2
|
||||||
|
- Bumped `spacepackets` to v0.13
|
||||||
|
- The source and destination handlers can now be used without the `std` feature and only require
|
||||||
|
the `alloc` feature.
|
||||||
|
|
||||||
# [v0.1.0] 2024-09-11
|
# [v0.1.0] 2024-09-11
|
||||||
|
|
||||||
Initial release
|
Initial release
|
||||||
|
|||||||
+11
-26
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cfdp-rs"
|
name = "cfdp-rs"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.75.0"
|
rust-version = "1.81.0"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
description = "High level CCSDS File Delivery Protocol components"
|
description = "High level CCSDS File Delivery Protocol components"
|
||||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/cfdp"
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/cfdp"
|
||||||
@@ -18,33 +18,18 @@ name = "cfdp"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
crc = "3"
|
crc = "3"
|
||||||
smallvec = "1"
|
smallvec = "1"
|
||||||
derive-new = "0.6"
|
derive-new = ">=0.6, <=0.7"
|
||||||
|
hashbrown = { version = ">=0.14, <=0.15", optional = true }
|
||||||
[dependencies.spacepackets]
|
spacepackets = { version = "0.15", default-features = false }
|
||||||
version = "0.12"
|
thiserror = { version = "2", default-features = false }
|
||||||
default-features = false
|
serde = { version = "1", optional = true }
|
||||||
|
defmt = { version = "1", optional = true }
|
||||||
[dependencies.thiserror]
|
|
||||||
version = "1"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.hashbrown]
|
|
||||||
version = "0.14"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.serde]
|
|
||||||
version = "1"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.defmt]
|
|
||||||
version = "0.3"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = [
|
std = [
|
||||||
"alloc",
|
"alloc",
|
||||||
"thiserror",
|
"thiserror/std",
|
||||||
"spacepackets/std"
|
"spacepackets/std"
|
||||||
]
|
]
|
||||||
alloc = [
|
alloc = [
|
||||||
@@ -56,9 +41,9 @@ defmt = ["dep:defmt", "spacepackets/defmt"]
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
rand = "0.8"
|
rand = "0.9"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
fern = "0.6"
|
fern = "0.7"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
|
|
||||||
|
|||||||
@@ -13,23 +13,36 @@ The underlying base packet library used to generate the packets to be sent is th
|
|||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
|
`cfdp-rs` currently supports following high-level features:
|
||||||
|
|
||||||
|
- Unacknowledged (class 1) file transfers for both source and destination side.
|
||||||
|
|
||||||
|
The following features have not been implemented yet. PRs or notifications for demand are welcome!
|
||||||
|
|
||||||
|
- Acknowledged (class 2) file transfers for both source and destination side.
|
||||||
|
- Suspending transfers
|
||||||
|
- Inactivity handling
|
||||||
|
- Start and end of transmission and reception opportunity handling
|
||||||
|
- Keep Alive and Prompt PDU handling
|
||||||
|
|
||||||
|
## Rust features
|
||||||
|
|
||||||
The goal of this library is to be flexible enough to support the use-cases of both on-board
|
The goal of this library is to be flexible enough to support the use-cases of both on-board
|
||||||
software and of ground software. It has support to make integration on [std] systems as simple
|
software and of ground software. It has support to make integration on `std` systems as simple
|
||||||
as possible, but also has sufficient abstraction to allow for integration on`no_std` environments.
|
as possible, but also has sufficient abstraction to allow for integration on`no_std` environments
|
||||||
Currently, the handlers still require the [std] feature until
|
and can be used on these systems as well as long as the `alloc` feature is activated.
|
||||||
[thiserror supports `error_in_core`](https://github.com/dtolnay/thiserror/pull/304).
|
|
||||||
|
|
||||||
It is recommended to activate the `alloc` feature at the very least to allow using the primary
|
Please note that even though the `alloc` feature is required for the core handlers, these
|
||||||
components provided by this crate. These components will only allocate memory at initialization
|
components will only allocate memory at initialization time and thus are still viable for systems
|
||||||
time and thus are still viable for systems where run-time allocation is prohibited.
|
where run-time allocation is prohibited.
|
||||||
|
|
||||||
## Default features
|
### Default features
|
||||||
|
|
||||||
- [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library.
|
- [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library.
|
||||||
- [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which require allocation support.
|
- [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which require allocation support.
|
||||||
Enabled by the `std` feature.
|
Enabled by the `std` feature.
|
||||||
|
|
||||||
## Optional Features
|
### Optional Features
|
||||||
|
|
||||||
- [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s
|
- [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s
|
||||||
- [`defmt`](https://defmt.ferrous-systems.com/): Add support for the `defmt` by adding the
|
- [`defmt`](https://defmt.ferrous-systems.com/): Add support for the `defmt` by adding the
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||||
|
cargo +nightly doc --all-features --open
|
||||||
+16
-15
@@ -55,7 +55,6 @@ use spacepackets::{
|
|||||||
},
|
},
|
||||||
util::{UnsignedByteField, UnsignedEnum},
|
util::{UnsignedByteField, UnsignedEnum},
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FileProperties {
|
struct FileProperties {
|
||||||
@@ -93,6 +92,7 @@ struct TransferState<Countdown: CountdownProvider> {
|
|||||||
transaction_id: Option<TransactionId>,
|
transaction_id: Option<TransactionId>,
|
||||||
metadata_params: MetadataGenericParams,
|
metadata_params: MetadataGenericParams,
|
||||||
progress: u64,
|
progress: u64,
|
||||||
|
// TODO: Can we delete this for good?
|
||||||
// file_size_eof: u64,
|
// file_size_eof: u64,
|
||||||
metadata_only: bool,
|
metadata_only: bool,
|
||||||
condition_code: ConditionCode,
|
condition_code: ConditionCode,
|
||||||
@@ -189,7 +189,7 @@ impl<CheckTimer: CountdownProvider> TransactionParams<CheckTimer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum DestError {
|
pub enum DestError {
|
||||||
/// File directive expected, but none specified
|
/// File directive expected, but none specified
|
||||||
#[error("expected file directive")]
|
#[error("expected file directive")]
|
||||||
@@ -215,6 +215,7 @@ pub enum DestError {
|
|||||||
#[error("pdu error {0}")]
|
#[error("pdu error {0}")]
|
||||||
Pdu(#[from] PduError),
|
Pdu(#[from] PduError),
|
||||||
#[error("io error {0}")]
|
#[error("io error {0}")]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
#[error("file store error {0}")]
|
#[error("file store error {0}")]
|
||||||
Filestore(#[from] FilestoreError),
|
Filestore(#[from] FilestoreError),
|
||||||
@@ -298,19 +299,19 @@ impl<
|
|||||||
///
|
///
|
||||||
/// * `local_cfg` - The local CFDP entity configuration.
|
/// * `local_cfg` - The local CFDP entity configuration.
|
||||||
/// * `max_packet_len` - The maximum expected generated packet size in bytes. Each time a
|
/// * `max_packet_len` - The maximum expected generated packet size in bytes. Each time a
|
||||||
/// packet is sent, it will be buffered inside an internal buffer. The length of this buffer
|
/// packet is sent, it will be buffered inside an internal buffer. The length of this buffer
|
||||||
/// will be determined by this parameter. This parameter can either be a known upper bound,
|
/// will be determined by this parameter. This parameter can either be a known upper bound,
|
||||||
/// or it can specifically be determined by the largest packet size parameter of all remote
|
/// or it can specifically be determined by the largest packet size parameter of all remote
|
||||||
/// entity configurations in the passed `remote_cfg_table`.
|
/// entity configurations in the passed `remote_cfg_table`.
|
||||||
/// * `pdu_sender` - [PduSendProvider] used to send generated PDU packets.
|
/// * `pdu_sender` - [PduSendProvider] used to send generated PDU packets.
|
||||||
/// * `vfs` - [VirtualFilestore] implementation used by the handler, which decouples the CFDP
|
/// * `vfs` - [VirtualFilestore] implementation used by the handler, which decouples the CFDP
|
||||||
/// implementation from the underlying filestore/filesystem. This allows to use this handler
|
/// implementation from the underlying filestore/filesystem. This allows to use this handler
|
||||||
/// for embedded systems where a standard runtime might not be available.
|
/// for embedded systems where a standard runtime might not be available.
|
||||||
/// * `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` - [TimerCreatorProvider] used by the CFDP handler to generate
|
/// * `check_timer_creator` - [TimerCreatorProvider] used by the CFDP handler to generate
|
||||||
/// timers required by various tasks. This allows to use this handler for embedded systems
|
/// timers required by various tasks. This allows to use this handler for embedded systems
|
||||||
/// where the standard time APIs might not be available.
|
/// where the standard time APIs might not be available.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
local_cfg: LocalEntityConfig<UserFaultHook>,
|
local_cfg: LocalEntityConfig<UserFaultHook>,
|
||||||
max_packet_len: usize,
|
max_packet_len: usize,
|
||||||
@@ -1429,7 +1430,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_segmented_file_transfer_not_acked() {
|
fn test_segmented_file_transfer_not_acked() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::rng();
|
||||||
let mut random_data = [0u8; 512];
|
let mut random_data = [0u8; 512];
|
||||||
rng.fill(&mut random_data);
|
rng.fill(&mut random_data);
|
||||||
let file_size = random_data.len() as u64;
|
let file_size = random_data.len() as u64;
|
||||||
@@ -1456,7 +1457,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_limit_handling_transfer_success() {
|
fn test_check_limit_handling_transfer_success() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::rng();
|
||||||
let mut random_data = [0u8; 512];
|
let mut random_data = [0u8; 512];
|
||||||
rng.fill(&mut random_data);
|
rng.fill(&mut random_data);
|
||||||
let file_size = random_data.len() as u64;
|
let file_size = random_data.len() as u64;
|
||||||
@@ -1502,7 +1503,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_limit_handling_limit_reached() {
|
fn test_check_limit_handling_limit_reached() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::rng();
|
||||||
let mut random_data = [0u8; 512];
|
let mut random_data = [0u8; 512];
|
||||||
rng.fill(&mut random_data);
|
rng.fill(&mut random_data);
|
||||||
let file_size = random_data.len() as u64;
|
let file_size = random_data.len() as u64;
|
||||||
@@ -1752,7 +1753,7 @@ mod tests {
|
|||||||
let fault_handler = TestFaultHandler::default();
|
let fault_handler = TestFaultHandler::default();
|
||||||
let src_path = tempfile::TempPath::from_path("/tmp/test.txt").to_path_buf();
|
let src_path = tempfile::TempPath::from_path("/tmp/test.txt").to_path_buf();
|
||||||
let dest_path = tempfile::TempDir::new().unwrap();
|
let dest_path = tempfile::TempDir::new().unwrap();
|
||||||
let mut dest_path_buf = dest_path.into_path();
|
let mut dest_path_buf = dest_path.keep();
|
||||||
let mut tb = DestHandlerTestbench::new(
|
let mut tb = DestHandlerTestbench::new(
|
||||||
fault_handler,
|
fault_handler,
|
||||||
false,
|
false,
|
||||||
|
|||||||
+33
-97
@@ -1,103 +1,37 @@
|
|||||||
use alloc::string::{String, ToString};
|
|
||||||
use core::fmt::Display;
|
|
||||||
use spacepackets::cfdp::ChecksumType;
|
use spacepackets::cfdp::ChecksumType;
|
||||||
use spacepackets::ByteConversionError;
|
use spacepackets::ByteConversionError;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::error::Error;
|
|
||||||
use std::path::Path;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use std_mod::*;
|
pub use std_mod::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(all(feature = "defmt", not(feature = "std")), derive(defmt::Format))]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum FilestoreError {
|
pub enum FilestoreError {
|
||||||
|
#[error("file does not exist")]
|
||||||
FileDoesNotExist,
|
FileDoesNotExist,
|
||||||
|
#[error("file already exists")]
|
||||||
FileAlreadyExists,
|
FileAlreadyExists,
|
||||||
|
#[error("directory does not exist")]
|
||||||
DirDoesNotExist,
|
DirDoesNotExist,
|
||||||
|
#[error("permission error")]
|
||||||
Permission,
|
Permission,
|
||||||
|
#[error("is not a file")]
|
||||||
IsNotFile,
|
IsNotFile,
|
||||||
|
#[error("is not a directory")]
|
||||||
IsNotDirectory,
|
IsNotDirectory,
|
||||||
ByteConversion(ByteConversionError),
|
#[error("byte conversion: {0}")]
|
||||||
Io {
|
ByteConversion(#[from] ByteConversionError),
|
||||||
raw_errno: Option<i32>,
|
#[error("IO error: {0})")]
|
||||||
string: String,
|
#[cfg(feature = "std")]
|
||||||
},
|
Io(#[from] std::io::Error),
|
||||||
|
#[error("checksum type not implemented: {0:?}")]
|
||||||
ChecksumTypeNotImplemented(ChecksumType),
|
ChecksumTypeNotImplemented(ChecksumType),
|
||||||
|
#[error("utf8 error")]
|
||||||
Utf8Error,
|
Utf8Error,
|
||||||
|
#[error("other error")]
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ByteConversionError> for FilestoreError {
|
|
||||||
fn from(value: ByteConversionError) -> Self {
|
|
||||||
Self::ByteConversion(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FilestoreError {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
match self {
|
|
||||||
FilestoreError::FileDoesNotExist => {
|
|
||||||
write!(f, "file does not exist")
|
|
||||||
}
|
|
||||||
FilestoreError::FileAlreadyExists => {
|
|
||||||
write!(f, "file already exists")
|
|
||||||
}
|
|
||||||
FilestoreError::DirDoesNotExist => {
|
|
||||||
write!(f, "directory does not exist")
|
|
||||||
}
|
|
||||||
FilestoreError::Permission => {
|
|
||||||
write!(f, "permission error")
|
|
||||||
}
|
|
||||||
FilestoreError::IsNotFile => {
|
|
||||||
write!(f, "is not a file")
|
|
||||||
}
|
|
||||||
FilestoreError::IsNotDirectory => {
|
|
||||||
write!(f, "is not a directory")
|
|
||||||
}
|
|
||||||
FilestoreError::ByteConversion(e) => {
|
|
||||||
write!(f, "filestore error: {e}")
|
|
||||||
}
|
|
||||||
FilestoreError::Io { raw_errno, string } => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"filestore generic IO error with raw errno {:?}: {}",
|
|
||||||
raw_errno, string
|
|
||||||
)
|
|
||||||
}
|
|
||||||
FilestoreError::ChecksumTypeNotImplemented(checksum_type) => {
|
|
||||||
write!(f, "checksum {:?} not implemented", checksum_type)
|
|
||||||
}
|
|
||||||
FilestoreError::Utf8Error => {
|
|
||||||
write!(f, "utf8 error")
|
|
||||||
}
|
|
||||||
FilestoreError::Other => {
|
|
||||||
write!(f, "some filestore error occured")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for FilestoreError {
|
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
||||||
match self {
|
|
||||||
FilestoreError::ByteConversion(e) => Some(e),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl From<std::io::Error> for FilestoreError {
|
|
||||||
fn from(value: std::io::Error) -> Self {
|
|
||||||
Self::Io {
|
|
||||||
raw_errno: value.raw_os_error(),
|
|
||||||
string: value.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait VirtualFilestore {
|
pub trait VirtualFilestore {
|
||||||
fn create_file(&self, file_path: &str) -> Result<(), FilestoreError>;
|
fn create_file(&self, file_path: &str) -> Result<(), FilestoreError>;
|
||||||
|
|
||||||
@@ -122,14 +56,7 @@ pub trait VirtualFilestore {
|
|||||||
|
|
||||||
fn filename_from_full_path(path: &str) -> Option<&str>
|
fn filename_from_full_path(path: &str) -> Option<&str>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized;
|
||||||
{
|
|
||||||
// Convert the path string to a Path
|
|
||||||
let path = Path::new(path);
|
|
||||||
|
|
||||||
// Extract the file name using the file_name() method
|
|
||||||
path.file_name().and_then(|name| name.to_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_file(&self, path: &str) -> Result<bool, FilestoreError>;
|
fn is_file(&self, path: &str) -> Result<bool, FilestoreError>;
|
||||||
|
|
||||||
@@ -194,6 +121,7 @@ pub mod std_mod {
|
|||||||
use std::{
|
use std::{
|
||||||
fs::{self, File, OpenOptions},
|
fs::{self, File, OpenOptions},
|
||||||
io::{BufReader, Read, Seek, SeekFrom, Write},
|
io::{BufReader, Read, Seek, SeekFrom, Write},
|
||||||
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -241,10 +169,7 @@ pub mod std_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_dir(&self, dir_path: &str) -> Result<(), FilestoreError> {
|
fn create_dir(&self, dir_path: &str) -> Result<(), FilestoreError> {
|
||||||
fs::create_dir(dir_path).map_err(|e| FilestoreError::Io {
|
fs::create_dir(dir_path)?;
|
||||||
raw_errno: e.raw_os_error(),
|
|
||||||
string: e.to_string(),
|
|
||||||
})?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,6 +283,17 @@ pub mod std_mod {
|
|||||||
_ => Err(FilestoreError::ChecksumTypeNotImplemented(checksum_type)),
|
_ => Err(FilestoreError::ChecksumTypeNotImplemented(checksum_type)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn filename_from_full_path(path: &str) -> Option<&str>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
// Convert the path string to a Path
|
||||||
|
let path = Path::new(path);
|
||||||
|
|
||||||
|
// Extract the file name using the file_name() method
|
||||||
|
path.file_name().and_then(|name| name.to_str())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NativeFilestore {
|
impl NativeFilestore {
|
||||||
@@ -393,7 +329,7 @@ pub mod std_mod {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{fs, path::Path, println};
|
use std::{fs, path::Path, println, string::ToString};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
@@ -706,7 +642,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
error.to_string(),
|
error.to_string(),
|
||||||
format!("filestore error: {}", byte_conv_error)
|
format!("byte conversion: {}", byte_conv_error)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected error");
|
panic!("unexpected error");
|
||||||
@@ -829,7 +765,7 @@ mod tests {
|
|||||||
if let FilestoreError::ChecksumTypeNotImplemented(cksum_type) = error {
|
if let FilestoreError::ChecksumTypeNotImplemented(cksum_type) = error {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
error.to_string(),
|
error.to_string(),
|
||||||
format!("checksum {:?} not implemented", cksum_type)
|
format!("checksum type not implemented: {:?}", cksum_type)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected error");
|
panic!("unexpected error");
|
||||||
|
|||||||
+43
-27
@@ -1,5 +1,23 @@
|
|||||||
//! This module contains the implementation of the CCSDS File Delivery Protocol (CFDP) high level
|
//! # `cfdp` - High-level support for implementing CFDP
|
||||||
//! abstractions as specified in CCSDS 727.0-B-5.
|
//!
|
||||||
|
//! This is a high-level support library for implementing the CCSDS File Delivery Protocol
|
||||||
|
//! according to the CCSDS 727.0-B-5 standard.
|
||||||
|
//!
|
||||||
|
//! # Features
|
||||||
|
//!
|
||||||
|
//! The crate currently supports following features:
|
||||||
|
//!
|
||||||
|
//! - Unacknowledged (class 1) file transfers for both source and destination side.
|
||||||
|
//!
|
||||||
|
//! The following features have not been implemented yet. PRs or notifications for demand are welcome!
|
||||||
|
//!
|
||||||
|
//! - Acknowledged (class 2) file transfers for both source and destination side.
|
||||||
|
//! - Suspending transfers
|
||||||
|
//! - Inactivity handling
|
||||||
|
//! - Start and end of transmission and reception opportunity handling
|
||||||
|
//! - Keep Alive and Prompt PDU handling
|
||||||
|
//!
|
||||||
|
//! # Overview
|
||||||
//!
|
//!
|
||||||
//! The basic idea of CFDP is to convert files of any size into a stream of packets called packet
|
//! The basic idea of CFDP is to convert files of any size into a stream of packets called packet
|
||||||
//! data units (PDU). CFPD has an unacknowledged and acknowledged mode, with the option to request
|
//! data units (PDU). CFPD has an unacknowledged and acknowledged mode, with the option to request
|
||||||
@@ -14,11 +32,12 @@
|
|||||||
//! The goal of this library is to be flexible enough to support the use-cases of both on-board
|
//! The goal of this library is to be flexible enough to support the use-cases of both on-board
|
||||||
//! software and of ground software. It has support to make integration on [std] systems as simple
|
//! software and of ground software. It has support to make integration on [std] systems as simple
|
||||||
//! as possible, but also has sufficient abstraction to allow for integration on `no_std`
|
//! as possible, but also has sufficient abstraction to allow for integration on `no_std`
|
||||||
//! environments. Currently, the handlers still require the [std] feature until
|
//! environments and can be used on these systems as well as long as the [alloc] feature is used
|
||||||
//! [thiserror supports `error_in_core`](https://github.com/dtolnay/thiserror/pull/304).
|
//! as well.
|
||||||
//! It is recommended to activate the `alloc` feature at the very least to allow using the primary
|
//!
|
||||||
//! components provided by this crate. These components will only allocate memory at initialization
|
//! Please note even though the [alloc] feature is required for the core handlers, these components
|
||||||
//! time and thus are still viable for systems where run-time allocation is prohibited.
|
//! will only allocate memory at initialization time and thus are still viable for systems where
|
||||||
|
//! run-time allocation is prohibited.
|
||||||
//!
|
//!
|
||||||
//! The core of this library are the [crate::dest::DestinationHandler] and the
|
//! The core of this library are the [crate::dest::DestinationHandler] and the
|
||||||
//! [crate::source::SourceHandler] components which model the CFDP destination and source entity
|
//! [crate::source::SourceHandler] components which model the CFDP destination and source entity
|
||||||
@@ -70,12 +89,11 @@ extern crate alloc;
|
|||||||
#[cfg(any(feature = "std", test))]
|
#[cfg(any(feature = "std", test))]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub mod dest;
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
|
pub mod dest;
|
||||||
pub mod filestore;
|
pub mod filestore;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "alloc")]
|
||||||
pub mod source;
|
pub mod source;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
@@ -83,8 +101,6 @@ pub mod user;
|
|||||||
use crate::time::CountdownProvider;
|
use crate::time::CountdownProvider;
|
||||||
use core::{cell::RefCell, fmt::Debug, hash::Hash};
|
use core::{cell::RefCell, fmt::Debug, hash::Hash};
|
||||||
use crc::{Crc, CRC_32_ISCSI, CRC_32_ISO_HDLC};
|
use crc::{Crc, CRC_32_ISCSI, CRC_32_ISO_HDLC};
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use hashbrown::HashMap;
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub use alloc_mod::*;
|
pub use alloc_mod::*;
|
||||||
@@ -171,7 +187,7 @@ pub trait TimerCreatorProvider {
|
|||||||
|
|
||||||
/// 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
|
||||||
/// of the CFDP standard.
|
/// of the CFDP standard.
|
||||||
|
///
|
||||||
/// Some of the fields which were not considered necessary for the Rust implementation
|
/// Some of the fields which were not considered necessary for the Rust implementation
|
||||||
/// were omitted. Some other fields which are not contained inside the standard but are considered
|
/// were omitted. Some other fields which are not contained inside the standard but are considered
|
||||||
/// necessary for the Rust implementation are included.
|
/// necessary for the Rust implementation are included.
|
||||||
@@ -197,16 +213,16 @@ pub trait TimerCreatorProvider {
|
|||||||
///
|
///
|
||||||
/// * `entity_id` - The ID of the remote entity.
|
/// * `entity_id` - The ID of the remote entity.
|
||||||
/// * `max_packet_len` - This determines of all PDUs generated for that remote entity in addition
|
/// * `max_packet_len` - This determines of all PDUs generated for that remote entity in addition
|
||||||
/// to the `max_file_segment_len` attribute which also determines the size of file data PDUs.
|
/// to the `max_file_segment_len` attribute which also determines the size of file data PDUs.
|
||||||
/// * `max_file_segment_len` The maximum file segment length which determines the maximum size
|
/// * `max_file_segment_len` The maximum file segment length which determines the maximum size
|
||||||
/// of file data PDUs in addition to the `max_packet_len` attribute. If this field is set
|
/// of file data PDUs in addition to the `max_packet_len` attribute. If this field is set
|
||||||
/// to None, the maximum file segment length will be derived from the maximum packet length.
|
/// to None, the maximum file segment length will be derived from the maximum packet length.
|
||||||
/// If this has some value which is smaller than the segment value derived from
|
/// If this has some value which is smaller than the segment value derived from
|
||||||
/// `max_packet_len`, this value will be picked.
|
/// `max_packet_len`, this value will be picked.
|
||||||
/// * `closure_requested_by_default` - If the closure requested field is not supplied as part of
|
/// * `closure_requested_by_default` - If the closure requested field is not supplied as part of
|
||||||
/// the Put Request, it will be determined from this field in the remote configuration.
|
/// the Put Request, it will be determined from this field in the remote configuration.
|
||||||
/// * `crc_on_transmission_by_default` - If the CRC option is not supplied as part of the Put
|
/// * `crc_on_transmission_by_default` - If the CRC option is not supplied as part of the Put
|
||||||
/// Request, it will be determined from this field in the remote configuration.
|
/// Request, it will be determined from this field in the remote configuration.
|
||||||
/// * `default_transmission_mode` - If the transmission mode is not supplied as part of the
|
/// * `default_transmission_mode` - If the transmission mode is not supplied as part of the
|
||||||
/// Put Request, it will be determined from this field in the remote configuration.
|
/// Put Request, it will be determined from this field in the remote configuration.
|
||||||
/// * `disposition_on_cancellation` - Determines whether an incomplete received file is discard on
|
/// * `disposition_on_cancellation` - Determines whether an incomplete received file is discard on
|
||||||
@@ -218,16 +234,16 @@ pub trait TimerCreatorProvider {
|
|||||||
/// reception of file data PDUs and EOF PDUs. Also see 4.6.3.3 of the CFDP standard. Defaults to
|
/// reception of file data PDUs and EOF PDUs. Also see 4.6.3.3 of the CFDP standard. Defaults to
|
||||||
/// 2, so the check limit timer may expire twice.
|
/// 2, so the check limit timer may expire twice.
|
||||||
/// * `positive_ack_timer_interval_seconds`- See the notes on the Positive Acknowledgment
|
/// * `positive_ack_timer_interval_seconds`- See the notes on the Positive Acknowledgment
|
||||||
/// Procedures inside the class documentation. Expected as floating point seconds. Defaults to
|
/// Procedures inside the class documentation. Expected as floating point seconds. Defaults to
|
||||||
/// 10 seconds.
|
/// 10 seconds.
|
||||||
/// * `positive_ack_timer_expiration_limit` - See the notes on the Positive Acknowledgment
|
/// * `positive_ack_timer_expiration_limit` - See the notes on the Positive Acknowledgment
|
||||||
/// Procedures inside the class documentation. Defaults to 2, so the timer may expire twice.
|
/// Procedures inside the class documentation. Defaults to 2, so the timer may expire twice.
|
||||||
/// * `immediate_nak_mode` - Specifies whether a NAK sequence should be issued immediately when a
|
/// * `immediate_nak_mode` - Specifies whether a NAK sequence should be issued immediately when a
|
||||||
/// file data gap or lost metadata is detected in the acknowledged mode. Defaults to True.
|
/// file data gap or lost metadata is detected in the acknowledged mode. Defaults to True.
|
||||||
/// * `nak_timer_interval_seconds` - See the notes on the Deferred Lost Segment Procedure inside
|
/// * `nak_timer_interval_seconds` - See the notes on the Deferred Lost Segment Procedure inside
|
||||||
/// the class documentation. Expected as floating point seconds. Defaults to 10 seconds.
|
/// the class documentation. Expected as floating point seconds. Defaults to 10 seconds.
|
||||||
/// * `nak_timer_expiration_limit` - See the notes on the Deferred Lost Segment Procedure inside
|
/// * `nak_timer_expiration_limit` - See the notes on the Deferred Lost Segment Procedure inside
|
||||||
/// the class documentation. Defaults to 2, so the timer may expire two times.
|
/// the class documentation. Defaults to 2, so the timer may expire two times.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
@@ -288,12 +304,12 @@ pub trait RemoteEntityConfigProvider {
|
|||||||
fn remove_config(&mut self, remote_id: u64) -> bool;
|
fn remove_config(&mut self, remote_id: u64) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a thin wrapper around a [HashMap] to store remote entity configurations.
|
/// This is a thin wrapper around a [hashbrown::HashMap] to store remote entity configurations.
|
||||||
/// It implements the full [RemoteEntityConfigProvider] trait.
|
/// It implements the full [RemoteEntityConfigProvider] trait.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "alloc")]
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct StdRemoteEntityConfigProvider(pub HashMap<u64, RemoteEntityConfig>);
|
pub struct StdRemoteEntityConfigProvider(pub hashbrown::HashMap<u64, RemoteEntityConfig>);
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl RemoteEntityConfigProvider for StdRemoteEntityConfigProvider {
|
impl RemoteEntityConfigProvider for StdRemoteEntityConfigProvider {
|
||||||
@@ -879,7 +895,7 @@ impl<'raw> PduRawWithInfo<'raw> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if pdu_header.pdu_datafield_len() < 1 {
|
if pdu_header.pdu_datafield_len() < 1 {
|
||||||
return Err(PduError::FormatError);
|
return Err(PduError::Format);
|
||||||
}
|
}
|
||||||
// Route depending on PDU type and directive type if applicable. Retrieve directive type
|
// Route depending on PDU type and directive type if applicable. Retrieve directive type
|
||||||
// from the raw stream for better performance (with sanity and directive code check).
|
// from the raw stream for better performance (with sanity and directive code check).
|
||||||
@@ -1548,7 +1564,7 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transaction_id_hashable_usable_as_map_key() {
|
fn transaction_id_hashable_usable_as_map_key() {
|
||||||
let mut map = HashMap::new();
|
let mut map = hashbrown::HashMap::new();
|
||||||
let transaction_id_0 = TransactionId::new(
|
let transaction_id_0 = TransactionId::new(
|
||||||
UnsignedByteFieldU8::new(1).into(),
|
UnsignedByteFieldU8::new(1).into(),
|
||||||
UnsignedByteFieldU8::new(2).into(),
|
UnsignedByteFieldU8::new(2).into(),
|
||||||
|
|||||||
+12
-12
@@ -24,10 +24,10 @@ pub trait ReadablePutRequest {
|
|||||||
fn closure_requested(&self) -> Option<bool>;
|
fn closure_requested(&self) -> Option<bool>;
|
||||||
fn seg_ctrl(&self) -> Option<SegmentationControl>;
|
fn seg_ctrl(&self) -> Option<SegmentationControl>;
|
||||||
|
|
||||||
fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv>>;
|
fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
|
||||||
fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv>>;
|
fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
|
||||||
fn flow_label(&self) -> Option<Tlv>;
|
fn flow_label(&self) -> Option<Tlv<'_>>;
|
||||||
fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv>>;
|
fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
@@ -101,25 +101,25 @@ impl ReadablePutRequest for PutRequest<'_, '_, '_, '_, '_, '_> {
|
|||||||
self.seg_ctrl
|
self.seg_ctrl
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv>> {
|
fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
|
||||||
if let Some(msgs_to_user) = self.msgs_to_user {
|
if let Some(msgs_to_user) = self.msgs_to_user {
|
||||||
return Some(msgs_to_user.iter().copied());
|
return Some(msgs_to_user.iter().copied());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv>> {
|
fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
|
||||||
if let Some(fh_overrides) = self.fault_handler_overrides {
|
if let Some(fh_overrides) = self.fault_handler_overrides {
|
||||||
return Some(fh_overrides.iter().copied());
|
return Some(fh_overrides.iter().copied());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flow_label(&self) -> Option<Tlv> {
|
fn flow_label(&self) -> Option<Tlv<'_>> {
|
||||||
self.flow_label
|
self.flow_label
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv>> {
|
fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
|
||||||
if let Some(fs_requests) = self.msgs_to_user {
|
if let Some(fs_requests) = self.msgs_to_user {
|
||||||
return Some(fs_requests.iter().copied());
|
return Some(fs_requests.iter().copied());
|
||||||
}
|
}
|
||||||
@@ -370,25 +370,25 @@ pub mod alloc_mod {
|
|||||||
self.seg_ctrl
|
self.seg_ctrl
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv>> {
|
fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
|
||||||
if let Some(msgs_to_user) = &self.msgs_to_user {
|
if let Some(msgs_to_user) = &self.msgs_to_user {
|
||||||
return Some(msgs_to_user.iter().map(|tlv_owned| tlv_owned.as_tlv()));
|
return Some(msgs_to_user.iter().map(|tlv_owned| tlv_owned.as_tlv()));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv>> {
|
fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
|
||||||
if let Some(fh_overrides) = &self.fault_handler_overrides {
|
if let Some(fh_overrides) = &self.fault_handler_overrides {
|
||||||
return Some(fh_overrides.iter().map(|tlv_owned| tlv_owned.as_tlv()));
|
return Some(fh_overrides.iter().map(|tlv_owned| tlv_owned.as_tlv()));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flow_label(&self) -> Option<Tlv> {
|
fn flow_label(&self) -> Option<Tlv<'_>> {
|
||||||
self.flow_label.as_ref().map(|tlv| tlv.as_tlv())
|
self.flow_label.as_ref().map(|tlv| tlv.as_tlv())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv>> {
|
fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv<'_>>> {
|
||||||
if let Some(requests) = &self.fs_requests {
|
if let Some(requests) = &self.fs_requests {
|
||||||
return Some(requests.iter().map(|tlv_owned| tlv_owned.as_tlv()));
|
return Some(requests.iter().map(|tlv_owned| tlv_owned.as_tlv()));
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-34
@@ -260,21 +260,21 @@ impl<
|
|||||||
/// * `cfg` - The local entity configuration for this source handler.
|
/// * `cfg` - The local entity configuration for this source handler.
|
||||||
/// * `pdu_sender` - [PduSendProvider] provider used to send CFDP PDUs generated by the handler.
|
/// * `pdu_sender` - [PduSendProvider] provider used to send CFDP PDUs generated by the handler.
|
||||||
/// * `vfs` - [VirtualFilestore] implementation used by the handler, which decouples the CFDP
|
/// * `vfs` - [VirtualFilestore] implementation used by the handler, which decouples the CFDP
|
||||||
/// implementation from the underlying filestore/filesystem. This allows to use this handler
|
/// implementation from the underlying filestore/filesystem. This allows to use this handler
|
||||||
/// for embedded systems where a standard runtime might not be available.
|
/// for embedded systems where a standard runtime might not be available.
|
||||||
/// * `put_request_cacher` - The put request cacher is used cache put requests without
|
/// * `put_request_cacher` - The put request cacher is used cache put requests without
|
||||||
/// requiring run-time allocation.
|
/// requiring run-time allocation.
|
||||||
/// * `pdu_and_cksum_buf_size` - The handler requires a buffer to generate PDUs and perform
|
/// * `pdu_and_cksum_buf_size` - The handler requires a buffer to generate PDUs and perform
|
||||||
/// checksum calculations. The user can specify the size of this buffer, so this should be
|
/// checksum calculations. The user can specify the size of this buffer, so this should be
|
||||||
/// set to the maximum expected PDU size or a conservative upper bound for this size, for
|
/// set to the maximum expected PDU size or a conservative upper bound for this size, for
|
||||||
/// 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.
|
||||||
/// * `timer_creator` - [TimerCreatorProvider] used by the CFDP handler to generate
|
/// * `timer_creator` - [TimerCreatorProvider] used by the CFDP handler to generate
|
||||||
/// timers required by various tasks. This allows to use this handler for embedded systems
|
/// timers required by various tasks. This allows to use this handler for embedded systems
|
||||||
/// where the standard time APIs might not be available.
|
/// 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)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
cfg: LocalEntityConfig<UserFaultHook>,
|
cfg: LocalEntityConfig<UserFaultHook>,
|
||||||
@@ -386,7 +386,7 @@ impl<
|
|||||||
/// This function is used to pass a put request to the source handler, which is
|
/// This function is used to pass a put request to the source handler, which is
|
||||||
/// also used to start a file copy operation. As such, this function models the Put.request
|
/// also used to start a file copy operation. As such, this function models the Put.request
|
||||||
/// CFDP primtiive.
|
/// CFDP primtiive.
|
||||||
|
///
|
||||||
/// Please note that the source handler can also process one put request at a time.
|
/// Please note that the source handler can also process one put request at a time.
|
||||||
/// The caller is responsible of creating a new source handler, one handler can only handle
|
/// The caller is responsible of creating a new source handler, one handler can only handle
|
||||||
/// one file copy request at a time.
|
/// one file copy request at a time.
|
||||||
@@ -505,7 +505,8 @@ impl<
|
|||||||
}
|
}
|
||||||
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.notice_of_cancellation(user, ConditionCode::CancelRequestReceived)?;
|
// Control flow result can be ignored here for the cancel request.
|
||||||
|
let _ = self.notice_of_cancellation(user, ConditionCode::CancelRequestReceived)?;
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -748,25 +749,6 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn notice_of_completion(&mut self, cfdp_user: &mut impl CfdpUser) {
|
fn notice_of_completion(&mut self, cfdp_user: &mut impl CfdpUser) {
|
||||||
/*
|
|
||||||
def _notice_of_completion(self):
|
|
||||||
if self.cfg.indication_cfg.transaction_finished_indication_required:
|
|
||||||
assert self._params.transaction_id is not None
|
|
||||||
# This happens for unacknowledged file copy operation with no closure.
|
|
||||||
if self._params.finished_params is None:
|
|
||||||
self._params.finished_params = FinishedParams(
|
|
||||||
condition_code=ConditionCode.NO_ERROR,
|
|
||||||
delivery_code=DeliveryCode.DATA_COMPLETE,
|
|
||||||
file_status=FileStatus.FILE_STATUS_UNREPORTED,
|
|
||||||
)
|
|
||||||
indication_params = TransactionFinishedParams(
|
|
||||||
transaction_id=self._params.transaction_id,
|
|
||||||
finished_params=self._params.finished_params,
|
|
||||||
)
|
|
||||||
self.user.transaction_finished_indication(indication_params)
|
|
||||||
# Transaction finished
|
|
||||||
self.reset()
|
|
||||||
*/
|
|
||||||
let tstate = self.tstate.as_ref().unwrap();
|
let tstate = self.tstate.as_ref().unwrap();
|
||||||
if self.local_cfg.indication_cfg.transaction_finished {
|
if self.local_cfg.indication_cfg.transaction_finished {
|
||||||
// The first case happens for unacknowledged file copy operation with no closure.
|
// The first case happens for unacknowledged file copy operation with no closure.
|
||||||
@@ -1552,7 +1534,7 @@ mod tests {
|
|||||||
.open(&tb.srcfile)
|
.open(&tb.srcfile)
|
||||||
.expect("opening file failed");
|
.expect("opening file failed");
|
||||||
let mut rand_data = [0u8; 140];
|
let mut rand_data = [0u8; 140];
|
||||||
rand::thread_rng().fill(&mut rand_data[..]);
|
rand::rng().fill(&mut rand_data[..]);
|
||||||
file.write_all(&rand_data)
|
file.write_all(&rand_data)
|
||||||
.expect("writing file content failed");
|
.expect("writing file content failed");
|
||||||
drop(file);
|
drop(file);
|
||||||
@@ -1571,7 +1553,7 @@ mod tests {
|
|||||||
.open(&tb.srcfile)
|
.open(&tb.srcfile)
|
||||||
.expect("opening file failed");
|
.expect("opening file failed");
|
||||||
let mut rand_data = [0u8; 140];
|
let mut rand_data = [0u8; 140];
|
||||||
rand::thread_rng().fill(&mut rand_data[..]);
|
rand::rng().fill(&mut rand_data[..]);
|
||||||
file.write_all(&rand_data)
|
file.write_all(&rand_data)
|
||||||
.expect("writing file content failed");
|
.expect("writing file content failed");
|
||||||
drop(file);
|
drop(file);
|
||||||
@@ -1767,7 +1749,7 @@ mod tests {
|
|||||||
.open(&tb.srcfile)
|
.open(&tb.srcfile)
|
||||||
.expect("opening file failed");
|
.expect("opening file failed");
|
||||||
let mut rand_data = [0u8; 140];
|
let mut rand_data = [0u8; 140];
|
||||||
rand::thread_rng().fill(&mut rand_data[..]);
|
rand::rng().fill(&mut rand_data[..]);
|
||||||
file.write_all(&rand_data)
|
file.write_all(&rand_data)
|
||||||
.expect("writing file content failed");
|
.expect("writing file content failed");
|
||||||
drop(file);
|
drop(file);
|
||||||
|
|||||||
Reference in New Issue
Block a user