Compare commits
1 Commits
v0.3.0
...
impl-acked
| Author | SHA1 | Date | |
|---|---|---|---|
| c6df24b947 |
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Install nextest
|
||||
uses: taiki-e/install-action@nextest
|
||||
- run: cargo nextest run --features "serde, defmt"
|
||||
- run: cargo nextest run --all-features
|
||||
- run: cargo test --doc
|
||||
|
||||
msrv:
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@1.86.0
|
||||
- uses: dtolnay/rust-toolchain@1.81.0
|
||||
- run: cargo check --release
|
||||
|
||||
cross-check:
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf"
|
||||
- run: cargo check --release --target=${{matrix.target}} --no-default-features --features "packet-buf-1k, defmt"
|
||||
- run: cargo check --release --target=${{matrix.target}} --no-default-features
|
||||
|
||||
fmt:
|
||||
name: Check formatting
|
||||
@@ -53,8 +53,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- run: cargo fmt --all -- --check
|
||||
|
||||
docs:
|
||||
@@ -63,7 +61,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --features "serde, defmt"
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features
|
||||
|
||||
clippy:
|
||||
name: Clippy
|
||||
@@ -71,6 +69,4 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
- run: cargo clippy -- -D warnings
|
||||
|
||||
13
CHANGELOG.md
13
CHANGELOG.md
@@ -8,16 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [unreleased]
|
||||
|
||||
# [v0.3.0] 2025-09-25
|
||||
|
||||
- Bumped `spacepackets` to v0.16
|
||||
- Bumped `spacepackets` to v0.15
|
||||
- Bumped `defmt` to v1
|
||||
|
||||
## Added
|
||||
|
||||
- Acknowledged mode support for both source and destination handler.
|
||||
- `FaultInfo` structure which is passed to user fault callbacks.
|
||||
|
||||
# [v0.2.0] 2024-11-26
|
||||
|
||||
- Bumped `thiserror` to v2
|
||||
@@ -28,7 +21,3 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
# [v0.1.0] 2024-09-11
|
||||
|
||||
Initial release
|
||||
|
||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/cfdp/compare/v0.3.0...HEAD
|
||||
[v0.3.0]: https://egit.irs.uni-stuttgart.de/rust/cfdp/compare/v0.2.0...v0.3.0
|
||||
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/cfdp/compare/v0.1.0...v0.2.0
|
||||
|
||||
27
Cargo.toml
27
Cargo.toml
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "cfdp-rs"
|
||||
version = "0.3.0"
|
||||
edition = "2024"
|
||||
rust-version = "1.86.0"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.81.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
description = "High level CCSDS File Delivery Protocol components"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/cfdp"
|
||||
@@ -20,14 +20,13 @@ crc = "3"
|
||||
smallvec = "1"
|
||||
derive-new = ">=0.6, <=0.7"
|
||||
hashbrown = { version = ">=0.14, <=0.15", optional = true }
|
||||
spacepackets = { version = "0.16", default-features = false }
|
||||
spacepackets = { version = "0.15", default-features = false }
|
||||
thiserror = { version = "2", default-features = false }
|
||||
heapless = "0.9"
|
||||
serde = { version = "1", optional = true }
|
||||
defmt = { version = "1", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["std", "packet-buf-2k"]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"alloc",
|
||||
"thiserror/std",
|
||||
@@ -37,21 +36,9 @@ alloc = [
|
||||
"hashbrown",
|
||||
"spacepackets/alloc"
|
||||
]
|
||||
serde = ["dep:serde", "spacepackets/serde", "hashbrown/serde", "heapless/serde"]
|
||||
serde = ["dep:serde", "spacepackets/serde", "hashbrown/serde"]
|
||||
defmt = ["dep:defmt", "spacepackets/defmt"]
|
||||
|
||||
# Available packet buffer sizes. Only one should be enabled.
|
||||
# 256 bytes
|
||||
packet-buf-256 = []
|
||||
# 512 bytes
|
||||
packet-buf-512 = []
|
||||
# 1024 bytes
|
||||
packet-buf-1k = []
|
||||
# 2048 bytes
|
||||
packet-buf-2k = []
|
||||
# 4096 bytes
|
||||
packet-buf-4k = []
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
rand = "0.9"
|
||||
@@ -61,5 +48,5 @@ chrono = "0.4"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["serde", "defmt"]
|
||||
all-features = true
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
|
||||
39
README.md
39
README.md
@@ -1,8 +1,7 @@
|
||||
[](https://crates.io/crates/cfdp-rs)
|
||||
[](https://docs.rs/cfdp-rs)
|
||||
[](https://github.com/us-irs/cfdp-rs/actions/workflows/ci.yml)
|
||||
[](https://matrix.to/#/#sat-rs:matrix.org)
|
||||
<!-- Does not work right now, I'd need to host that myself. [](https://absatsw.irs.uni-stuttgart.de/projects/cfdp/coverage-rs/latest/index.html) -->
|
||||
[](https://absatsw.irs.uni-stuttgart.de/projects/cfdp/coverage-rs/latest/index.html)
|
||||
|
||||
cfdp-rs - High level Rust crate for CFDP components
|
||||
======================
|
||||
@@ -17,17 +16,37 @@ The underlying base packet library used to generate the packets to be sent is th
|
||||
`cfdp-rs` currently supports following high-level features:
|
||||
|
||||
- Unacknowledged (class 1) file transfers for both source and destination side.
|
||||
- Acknowledged (class 2) 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
|
||||
|
||||
Check out the [documentation](https://docs.rs/cfdp-rs) for more information on available
|
||||
Rust features.
|
||||
## Rust features
|
||||
|
||||
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
|
||||
as possible, but also has sufficient abstraction to allow for integration on`no_std` environments
|
||||
and can be used on these systems as well as long as the `alloc` feature is activated.
|
||||
|
||||
Please note that even though the `alloc` feature is required for the core handlers, these
|
||||
components will only allocate memory at initialization time and thus are still viable for systems
|
||||
where run-time allocation is prohibited.
|
||||
|
||||
### Default features
|
||||
|
||||
- [`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.
|
||||
Enabled by the `std` feature.
|
||||
|
||||
### Optional Features
|
||||
|
||||
- [`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::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
|
||||
|
||||
# Examples
|
||||
|
||||
@@ -36,11 +55,13 @@ examples.
|
||||
|
||||
# Coverage
|
||||
|
||||
Coverage can be generated using [`llvm-cov`](https://github.com/taiki-e/cargo-llvm-cov). If you have not done so
|
||||
already, install the tool:
|
||||
Coverage was generated using [`grcov`](https://github.com/mozilla/grcov). If you have not done so
|
||||
already, install the `llvm-tools-preview`:
|
||||
|
||||
```sh
|
||||
cargo +stable install cargo-llvm-cov --locked
|
||||
rustup component add llvm-tools-preview
|
||||
cargo install grcov --locked
|
||||
```
|
||||
|
||||
After this, you can run `cargo llvm-cov nextest` to run all the tests and display coverage.
|
||||
After that, you can simply run `coverage.py` to test the project with coverage. You can optionally
|
||||
supply the `--open` flag to open the coverage report in your webbrowser.
|
||||
|
||||
3
docs.sh
Executable file
3
docs.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --all-features --open
|
||||
@@ -12,13 +12,13 @@ You can run both applications with `-h` to get more information about the availa
|
||||
## Running the Python App
|
||||
|
||||
It is recommended to run the Python App in a dedicated virtual environment. For example, on a
|
||||
Unix system you can use `uv venv` and then `source .venv/bin/activate` to create
|
||||
Unix system you can use `python3 -m venv venv` and then `source venv/bin/activate` to create
|
||||
and activate a virtual environment.
|
||||
|
||||
After that, you can install the required dependencies using
|
||||
|
||||
```sh
|
||||
uv pip install -r requirements.txt
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
and then run the application using `./main.py` or `python3 main.py`.
|
||||
|
||||
@@ -16,11 +16,11 @@ from typing import Any, Dict, List, Tuple, Optional
|
||||
from multiprocessing import Queue
|
||||
from queue import Empty
|
||||
|
||||
from cfdppy.handler import DestHandler, RemoteEntityConfigTable, SourceHandler
|
||||
from cfdppy.handler import DestHandler, RemoteEntityCfgTable, SourceHandler
|
||||
from cfdppy.exceptions import InvalidDestinationId, SourceFileDoesNotExist
|
||||
from cfdppy import (
|
||||
CfdpUserBase,
|
||||
LocalEntityConfig,
|
||||
LocalEntityCfg,
|
||||
PacketDestination,
|
||||
PutRequest,
|
||||
TransactionId,
|
||||
@@ -31,8 +31,8 @@ from cfdppy.mib import (
|
||||
CheckTimerProvider,
|
||||
DefaultFaultHandlerBase,
|
||||
EntityType,
|
||||
IndicationConfig,
|
||||
RemoteEntityConfig,
|
||||
IndicationCfg,
|
||||
RemoteEntityCfg,
|
||||
)
|
||||
from cfdppy.user import (
|
||||
FileSegmentRecvdParams,
|
||||
@@ -58,7 +58,7 @@ from spacepackets.util import ByteFieldU16, UnsignedByteField
|
||||
PYTHON_ENTITY_ID = ByteFieldU16(1)
|
||||
RUST_ENTITY_ID = ByteFieldU16(2)
|
||||
# Enable all indications for both local and remote entity.
|
||||
INDICATION_CFG = IndicationConfig()
|
||||
INDICATION_CFG = IndicationCfg()
|
||||
|
||||
BASE_STR_SRC = "PY SRC"
|
||||
BASE_STR_DEST = "PY DEST"
|
||||
@@ -79,7 +79,7 @@ DEST_ENTITY_QUEUE = Queue()
|
||||
# be sent by the UDP server.
|
||||
TM_QUEUE = Queue()
|
||||
|
||||
REMOTE_CFG_OF_PY_ENTITY = RemoteEntityConfig(
|
||||
REMOTE_CFG_OF_PY_ENTITY = RemoteEntityCfg(
|
||||
entity_id=PYTHON_ENTITY_ID,
|
||||
max_packet_len=MAX_PACKET_LEN,
|
||||
max_file_segment_len=FILE_SEGMENT_SIZE,
|
||||
@@ -585,7 +585,7 @@ def main():
|
||||
|
||||
logging.basicConfig(level=logging_level)
|
||||
|
||||
remote_cfg_table = RemoteEntityConfigTable()
|
||||
remote_cfg_table = RemoteEntityCfgTable()
|
||||
remote_cfg_table.add_config(REMOTE_CFG_OF_REMOTE_ENTITY)
|
||||
|
||||
src_fault_handler = CfdpFaultHandler(BASE_STR_SRC)
|
||||
@@ -594,7 +594,7 @@ def main():
|
||||
src_user = CfdpUser(BASE_STR_SRC, PUT_REQ_QUEUE)
|
||||
check_timer_provider = CustomCheckTimerProvider()
|
||||
source_handler = SourceHandler(
|
||||
cfg=LocalEntityConfig(PYTHON_ENTITY_ID, INDICATION_CFG, src_fault_handler),
|
||||
cfg=LocalEntityCfg(PYTHON_ENTITY_ID, INDICATION_CFG, src_fault_handler),
|
||||
seq_num_provider=src_seq_count_provider,
|
||||
remote_cfg_table=remote_cfg_table,
|
||||
user=src_user,
|
||||
@@ -614,7 +614,7 @@ def main():
|
||||
dest_fault_handler = CfdpFaultHandler(BASE_STR_DEST)
|
||||
dest_user = CfdpUser(BASE_STR_DEST, PUT_REQ_QUEUE)
|
||||
dest_handler = DestHandler(
|
||||
cfg=LocalEntityConfig(PYTHON_ENTITY_ID, INDICATION_CFG, dest_fault_handler),
|
||||
cfg=LocalEntityCfg(PYTHON_ENTITY_ID, INDICATION_CFG, dest_fault_handler),
|
||||
user=dest_user,
|
||||
remote_cfg_table=remote_cfg_table,
|
||||
check_timer_provider=check_timer_provider,
|
||||
|
||||
@@ -3,36 +3,31 @@ use std::{
|
||||
fs::OpenOptions,
|
||||
io::{self, ErrorKind, Write},
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket},
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU16},
|
||||
mpsc,
|
||||
},
|
||||
sync::mpsc,
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use cfdp::{
|
||||
EntityType, FaultInfo, IndicationConfig, LocalEntityConfig, PduOwnedWithInfo, PduProvider,
|
||||
RemoteEntityConfig, StdTimerCreator, TransactionId, UserFaultHook,
|
||||
dest::DestinationHandler,
|
||||
filestore::NativeFilestore,
|
||||
lost_segments::LostSegmentsList,
|
||||
request::PutRequestOwned,
|
||||
request::{PutRequestOwned, StaticPutRequestCacher},
|
||||
source::SourceHandler,
|
||||
user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams, TransactionFinishedParams},
|
||||
EntityType, IndicationConfig, LocalEntityConfig, PduOwnedWithInfo, PduProvider,
|
||||
RemoteEntityConfig, StdTimerCreator, TransactionId, UserFaultHookProvider,
|
||||
};
|
||||
use clap::Parser;
|
||||
use log::{debug, info, warn};
|
||||
use spacepackets::{
|
||||
cfdp::{
|
||||
pdu::{file_data::FileDataPdu, metadata::MetadataPduReader, PduError},
|
||||
ChecksumType, ConditionCode, TransmissionMode,
|
||||
pdu::{PduError, file_data::FileDataPdu, metadata::MetadataPduReader},
|
||||
},
|
||||
seq_count::SeqCountProviderSyncU16,
|
||||
util::{UnsignedByteFieldU16, UnsignedEnum},
|
||||
};
|
||||
|
||||
static KILL_APP: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
const PYTHON_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(1);
|
||||
const RUST_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(2);
|
||||
|
||||
@@ -63,21 +58,43 @@ pub struct Cli {
|
||||
#[derive(Default)]
|
||||
pub struct ExampleFaultHandler {}
|
||||
|
||||
impl UserFaultHook for ExampleFaultHandler {
|
||||
fn notice_of_suspension_cb(&mut self, fault_info: FaultInfo) {
|
||||
panic!("unexpected suspension, {:?}", fault_info);
|
||||
impl UserFaultHookProvider for ExampleFaultHandler {
|
||||
fn notice_of_suspension_cb(
|
||||
&mut self,
|
||||
transaction_id: TransactionId,
|
||||
cond: ConditionCode,
|
||||
progress: u64,
|
||||
) {
|
||||
panic!(
|
||||
"unexpected suspension of transaction {:?}, condition code {:?}, progress {}",
|
||||
transaction_id, cond, progress
|
||||
);
|
||||
}
|
||||
|
||||
fn notice_of_cancellation_cb(&mut self, fault_info: FaultInfo) {
|
||||
panic!("unexpected cancellation, {:?}", fault_info);
|
||||
fn notice_of_cancellation_cb(
|
||||
&mut self,
|
||||
transaction_id: TransactionId,
|
||||
cond: ConditionCode,
|
||||
progress: u64,
|
||||
) {
|
||||
panic!(
|
||||
"unexpected cancellation of transaction {:?}, condition code {:?}, progress {}",
|
||||
transaction_id, cond, progress
|
||||
);
|
||||
}
|
||||
|
||||
fn abandoned_cb(&mut self, fault_info: FaultInfo) {
|
||||
panic!("unexpected abandonment, {:?}", fault_info);
|
||||
fn abandoned_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64) {
|
||||
panic!(
|
||||
"unexpected abandonment of transaction {:?}, condition code {:?}, progress {}",
|
||||
transaction_id, cond, progress
|
||||
);
|
||||
}
|
||||
|
||||
fn ignore_cb(&mut self, fault_info: FaultInfo) {
|
||||
panic!("unexpected ignore, {:?}", fault_info);
|
||||
fn ignore_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64) {
|
||||
panic!(
|
||||
"ignoring unexpected error in transaction {:?}, condition code {:?}, progress {}",
|
||||
transaction_id, cond, progress
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +231,7 @@ impl UdpServer {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(e.into())
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
let (_, from) = res;
|
||||
@@ -240,7 +257,7 @@ impl UdpServer {
|
||||
while let Ok(tm) = receiver.try_recv() {
|
||||
debug!("Sending PDU: {:?}", tm);
|
||||
pdu_printout(&tm);
|
||||
let result = self.socket.send_to(tm.raw_pdu(), self.remote_addr());
|
||||
let result = self.socket.send_to(tm.pdu(), self.remote_addr());
|
||||
if let Err(e) = result {
|
||||
warn!("Sending TM with UDP socket failed: {e}")
|
||||
}
|
||||
@@ -263,7 +280,7 @@ fn pdu_printout(pdu: &PduOwnedWithInfo) {
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::AckPdu => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::MetadataPdu => {
|
||||
let meta_pdu =
|
||||
MetadataPduReader::new(pdu.raw_pdu()).expect("creating metadata pdu failed");
|
||||
MetadataPduReader::new(pdu.pdu()).expect("creating metadata pdu failed");
|
||||
debug!("Metadata PDU: {:?}", meta_pdu)
|
||||
}
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::NakPdu => (),
|
||||
@@ -271,8 +288,7 @@ fn pdu_printout(pdu: &PduOwnedWithInfo) {
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::KeepAlivePdu => (),
|
||||
},
|
||||
spacepackets::cfdp::PduType::FileData => {
|
||||
let fd_pdu =
|
||||
FileDataPdu::from_bytes(pdu.raw_pdu()).expect("creating file data pdu failed");
|
||||
let fd_pdu = FileDataPdu::from_bytes(pdu.pdu()).expect("creating file data pdu failed");
|
||||
debug!("File data PDU: {:?}", fd_pdu);
|
||||
}
|
||||
}
|
||||
@@ -313,6 +329,7 @@ fn main() {
|
||||
);
|
||||
let (source_tm_tx, source_tm_rx) = mpsc::channel::<PduOwnedWithInfo>();
|
||||
let (dest_tm_tx, dest_tm_rx) = mpsc::channel::<PduOwnedWithInfo>();
|
||||
let put_request_cacher = StaticPutRequestCacher::new(2048);
|
||||
let remote_cfg_python = RemoteEntityConfig::new_with_default_values(
|
||||
PYTHON_ID.into(),
|
||||
1024,
|
||||
@@ -321,11 +338,13 @@ fn main() {
|
||||
spacepackets::cfdp::TransmissionMode::Unacknowledged,
|
||||
ChecksumType::Crc32C,
|
||||
);
|
||||
let seq_count_provider = AtomicU16::default();
|
||||
let seq_count_provider = SeqCountProviderSyncU16::default();
|
||||
let mut source_handler = SourceHandler::new(
|
||||
local_cfg_source,
|
||||
source_tm_tx,
|
||||
NativeFilestore::default(),
|
||||
put_request_cacher,
|
||||
2048,
|
||||
remote_cfg_python,
|
||||
StdTimerCreator::default(),
|
||||
seq_count_provider,
|
||||
@@ -339,11 +358,11 @@ fn main() {
|
||||
);
|
||||
let mut dest_handler = DestinationHandler::new(
|
||||
local_cfg_dest,
|
||||
1024,
|
||||
dest_tm_tx,
|
||||
NativeFilestore::default(),
|
||||
remote_cfg_python,
|
||||
StdTimerCreator::default(),
|
||||
LostSegmentsList::default(),
|
||||
);
|
||||
let mut cfdp_user_dest = ExampleCfdpUser::new(EntityType::Receiving);
|
||||
|
||||
@@ -392,9 +411,6 @@ fn main() {
|
||||
.expect("put request failed");
|
||||
}
|
||||
loop {
|
||||
if KILL_APP.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
let mut next_delay = None;
|
||||
let mut undelayed_call_count = 0;
|
||||
let packet_info = match source_tc_rx.try_recv() {
|
||||
@@ -437,9 +453,6 @@ fn main() {
|
||||
loop {
|
||||
let mut next_delay = None;
|
||||
let mut undelayed_call_count = 0;
|
||||
if KILL_APP.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
let packet_info = match dest_tc_rx.try_recv() {
|
||||
Ok(pdu_with_info) => Some(pdu_with_info),
|
||||
Err(e) => match e {
|
||||
@@ -481,9 +494,6 @@ fn main() {
|
||||
info!("Starting UDP server on {}", remote_addr);
|
||||
loop {
|
||||
loop {
|
||||
if KILL_APP.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
match udp_server.try_recv_tc() {
|
||||
Ok(result) => match result {
|
||||
Some((pdu, _addr)) => {
|
||||
|
||||
34
justfile
34
justfile
@@ -1,34 +0,0 @@
|
||||
all: check build embedded clippy fmt docs test coverage
|
||||
|
||||
clippy:
|
||||
cargo clippy -- -D warnings
|
||||
|
||||
fmt:
|
||||
cargo fmt --all -- --check
|
||||
|
||||
check:
|
||||
cargo check --features "serde, defmt"
|
||||
|
||||
test:
|
||||
cargo nextest r --features "serde, defmt"
|
||||
cargo test --doc
|
||||
|
||||
build:
|
||||
cargo build --features "serde, defmt"
|
||||
|
||||
embedded:
|
||||
cargo build --target thumbv7em-none-eabihf --no-default-features --features "defmt, packet-buf-1k"
|
||||
|
||||
docs:
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --features "serde, defmt"
|
||||
|
||||
docs-html:
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --features "serde, defmt" --open
|
||||
|
||||
coverage:
|
||||
cargo llvm-cov nextest
|
||||
|
||||
coverage-html:
|
||||
cargo llvm-cov nextest --html --open
|
||||
@@ -1,21 +0,0 @@
|
||||
#[cfg(not(any(
|
||||
feature = "packet-buf-256",
|
||||
feature = "packet-buf-512",
|
||||
feature = "packet-buf-1k",
|
||||
feature = "packet-buf-2k",
|
||||
feature = "packet-buf-4k"
|
||||
)))]
|
||||
compile_error!(
|
||||
"One of the features `packet-buf-256`, `packet-buf-512`, `packet-buf-1k`, `packet-buf-2k`, or `packet-buf-4k` must be enabled."
|
||||
);
|
||||
|
||||
#[cfg(feature = "packet-buf-256")]
|
||||
pub const PACKET_BUF_LEN: usize = 256;
|
||||
#[cfg(feature = "packet-buf-512")]
|
||||
pub const PACKET_BUF_LEN: usize = 512;
|
||||
#[cfg(feature = "packet-buf-1k")]
|
||||
pub const PACKET_BUF_LEN: usize = 1024;
|
||||
#[cfg(feature = "packet-buf-2k")]
|
||||
pub const PACKET_BUF_LEN: usize = 2048;
|
||||
#[cfg(feature = "packet-buf-4k")]
|
||||
pub const PACKET_BUF_LEN: usize = 4096;
|
||||
3304
src/dest.rs
3304
src/dest.rs
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
use spacepackets::ByteConversionError;
|
||||
use spacepackets::cfdp::ChecksumType;
|
||||
use spacepackets::ByteConversionError;
|
||||
#[cfg(feature = "std")]
|
||||
pub use std_mod::*;
|
||||
|
||||
@@ -375,11 +375,9 @@ mod tests {
|
||||
.create_dir(dir_path.to_str().expect("getting str for file failed"))
|
||||
.unwrap();
|
||||
assert!(NATIVE_FS.exists(dir_path.to_str().unwrap()).unwrap());
|
||||
assert!(
|
||||
NATIVE_FS
|
||||
.is_dir(dir_path.as_path().to_str().unwrap())
|
||||
.unwrap()
|
||||
);
|
||||
assert!(NATIVE_FS
|
||||
.is_dir(dir_path.as_path().to_str().unwrap())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
705
src/lib.rs
705
src/lib.rs
File diff suppressed because it is too large
Load Diff
1361
src/lost_segments.rs
1361
src/lost_segments.rs
File diff suppressed because it is too large
Load Diff
333
src/request.rs
333
src/request.rs
@@ -1,10 +1,7 @@
|
||||
use core::str::Utf8Error;
|
||||
|
||||
use spacepackets::{
|
||||
ByteConversionError,
|
||||
cfdp::{
|
||||
tlv::{GenericTlv, Tlv, TlvType},
|
||||
SegmentationControl, TransmissionMode,
|
||||
tlv::{GenericTlv, ReadableTlv as _, Tlv, TlvType, WritableTlv as _},
|
||||
},
|
||||
util::UnsignedByteField,
|
||||
};
|
||||
@@ -229,171 +226,16 @@ pub fn generic_tlv_list_type_check<TlvProvider: GenericTlv>(
|
||||
true
|
||||
}
|
||||
|
||||
pub struct StaticPutRequestFields {
|
||||
pub destination_id: UnsignedByteField,
|
||||
/// Static buffer to store source file path.
|
||||
pub source_file_buf: [u8; u8::MAX as usize],
|
||||
/// Current source path length.
|
||||
pub source_file_len: usize,
|
||||
/// Static buffer to store dest file path.
|
||||
pub dest_file_buf: [u8; u8::MAX as usize],
|
||||
/// Current destination path length.
|
||||
pub dest_file_len: usize,
|
||||
pub trans_mode: Option<TransmissionMode>,
|
||||
pub closure_requested: Option<bool>,
|
||||
pub seg_ctrl: Option<SegmentationControl>,
|
||||
}
|
||||
|
||||
impl Default for StaticPutRequestFields {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
destination_id: UnsignedByteField::new(0, 0),
|
||||
source_file_buf: [0; u8::MAX as usize],
|
||||
source_file_len: Default::default(),
|
||||
dest_file_buf: [0; u8::MAX as usize],
|
||||
dest_file_len: Default::default(),
|
||||
trans_mode: Default::default(),
|
||||
closure_requested: Default::default(),
|
||||
seg_ctrl: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticPutRequestFields {
|
||||
pub fn clear(&mut self) {
|
||||
self.destination_id = UnsignedByteField::new(0, 0);
|
||||
self.source_file_len = 0;
|
||||
self.dest_file_len = 0;
|
||||
self.trans_mode = None;
|
||||
self.closure_requested = None;
|
||||
self.seg_ctrl = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a put request cache structure which can be used to cache [ReadablePutRequest]s
|
||||
/// without requiring run-time allocation. The user must specify the static buffer sizes used
|
||||
/// to store TLVs or list of TLVs.
|
||||
pub struct StaticPutRequestCacher<const BUF_SIZE: usize> {
|
||||
pub static_fields: StaticPutRequestFields,
|
||||
opts_buf: [u8; BUF_SIZE],
|
||||
opts_len: usize,
|
||||
}
|
||||
|
||||
impl<const BUF_SIZE: usize> Default for StaticPutRequestCacher<BUF_SIZE> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const BUF_SIZE: usize> StaticPutRequestCacher<BUF_SIZE> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
static_fields: StaticPutRequestFields::default(),
|
||||
opts_buf: [0; BUF_SIZE],
|
||||
opts_len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(
|
||||
&mut self,
|
||||
put_request: &impl ReadablePutRequest,
|
||||
) -> Result<(), ByteConversionError> {
|
||||
self.static_fields.destination_id = put_request.destination_id();
|
||||
if let Some(source_file) = put_request.source_file() {
|
||||
if source_file.len() > u8::MAX as usize {
|
||||
return Err(ByteConversionError::ToSliceTooSmall {
|
||||
found: self.static_fields.source_file_buf.len(),
|
||||
expected: source_file.len(),
|
||||
});
|
||||
}
|
||||
self.static_fields.source_file_buf[..source_file.len()]
|
||||
.copy_from_slice(source_file.as_bytes());
|
||||
self.static_fields.source_file_len = source_file.len();
|
||||
}
|
||||
if let Some(dest_file) = put_request.dest_file() {
|
||||
if dest_file.len() > u8::MAX as usize {
|
||||
return Err(ByteConversionError::ToSliceTooSmall {
|
||||
found: self.static_fields.source_file_buf.len(),
|
||||
expected: dest_file.len(),
|
||||
});
|
||||
}
|
||||
self.static_fields.dest_file_buf[..dest_file.len()]
|
||||
.copy_from_slice(dest_file.as_bytes());
|
||||
self.static_fields.dest_file_len = dest_file.len();
|
||||
}
|
||||
self.static_fields.trans_mode = put_request.trans_mode();
|
||||
self.static_fields.closure_requested = put_request.closure_requested();
|
||||
self.static_fields.seg_ctrl = put_request.seg_ctrl();
|
||||
let mut current_idx = 0;
|
||||
let mut store_tlv = |tlv: &Tlv| {
|
||||
if current_idx + tlv.len_full() > self.opts_buf.len() {
|
||||
return Err(ByteConversionError::ToSliceTooSmall {
|
||||
found: self.opts_buf.len(),
|
||||
expected: current_idx + tlv.len_full(),
|
||||
});
|
||||
}
|
||||
// We checked the buffer lengths, so this should never fail.
|
||||
tlv.write_to_bytes(&mut self.opts_buf[current_idx..current_idx + tlv.len_full()])
|
||||
.unwrap();
|
||||
current_idx += tlv.len_full();
|
||||
Ok(())
|
||||
};
|
||||
if let Some(fs_req) = put_request.fs_requests() {
|
||||
for fs_req in fs_req {
|
||||
store_tlv(&fs_req)?;
|
||||
}
|
||||
}
|
||||
if let Some(msgs_to_user) = put_request.msgs_to_user() {
|
||||
for msg_to_user in msgs_to_user {
|
||||
store_tlv(&msg_to_user)?;
|
||||
}
|
||||
}
|
||||
self.opts_len = current_idx;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn has_source_file(&self) -> bool {
|
||||
self.static_fields.source_file_len > 0
|
||||
}
|
||||
|
||||
pub fn has_dest_file(&self) -> bool {
|
||||
self.static_fields.dest_file_len > 0
|
||||
}
|
||||
|
||||
pub fn source_file(&self) -> Result<&str, Utf8Error> {
|
||||
core::str::from_utf8(
|
||||
&self.static_fields.source_file_buf[0..self.static_fields.source_file_len],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dest_file(&self) -> Result<&str, Utf8Error> {
|
||||
core::str::from_utf8(&self.static_fields.dest_file_buf[0..self.static_fields.dest_file_len])
|
||||
}
|
||||
|
||||
pub fn opts_len(&self) -> usize {
|
||||
self.opts_len
|
||||
}
|
||||
|
||||
pub fn opts_slice(&self) -> &[u8] {
|
||||
&self.opts_buf[0..self.opts_len]
|
||||
}
|
||||
|
||||
/// This clears the cacher structure. This is a cheap operation because it only
|
||||
/// sets [Option]al values to [None] and the length of stores TLVs to 0.
|
||||
///
|
||||
/// Please note that this method will not set the values in the buffer to 0.
|
||||
pub fn clear(&mut self) {
|
||||
self.static_fields.clear();
|
||||
self.opts_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod alloc_mod {
|
||||
use core::str::Utf8Error;
|
||||
|
||||
use super::*;
|
||||
use alloc::string::ToString;
|
||||
use spacepackets::cfdp::tlv::{TlvOwned, msg_to_user::MsgToUserTlv};
|
||||
use spacepackets::{
|
||||
cfdp::tlv::{msg_to_user::MsgToUserTlv, ReadableTlv, TlvOwned, WritableTlv},
|
||||
ByteConversionError,
|
||||
};
|
||||
|
||||
/// Owned variant of [PutRequest] with no lifetimes which is also [Clone]able.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -553,6 +395,161 @@ pub mod alloc_mod {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StaticPutRequestFields {
|
||||
pub destination_id: UnsignedByteField,
|
||||
/// Static buffer to store source file path.
|
||||
pub source_file_buf: [u8; u8::MAX as usize],
|
||||
/// Current source path length.
|
||||
pub source_file_len: usize,
|
||||
/// Static buffer to store dest file path.
|
||||
pub dest_file_buf: [u8; u8::MAX as usize],
|
||||
/// Current destination path length.
|
||||
pub dest_file_len: usize,
|
||||
pub trans_mode: Option<TransmissionMode>,
|
||||
pub closure_requested: Option<bool>,
|
||||
pub seg_ctrl: Option<SegmentationControl>,
|
||||
}
|
||||
|
||||
impl Default for StaticPutRequestFields {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
destination_id: UnsignedByteField::new(0, 0),
|
||||
source_file_buf: [0; u8::MAX as usize],
|
||||
source_file_len: Default::default(),
|
||||
dest_file_buf: [0; u8::MAX as usize],
|
||||
dest_file_len: Default::default(),
|
||||
trans_mode: Default::default(),
|
||||
closure_requested: Default::default(),
|
||||
seg_ctrl: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticPutRequestFields {
|
||||
pub fn clear(&mut self) {
|
||||
self.destination_id = UnsignedByteField::new(0, 0);
|
||||
self.source_file_len = 0;
|
||||
self.dest_file_len = 0;
|
||||
self.trans_mode = None;
|
||||
self.closure_requested = None;
|
||||
self.seg_ctrl = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a put request cache structure which can be used to cache [ReadablePutRequest]s
|
||||
/// without requiring run-time allocation. The user must specify the static buffer sizes used
|
||||
/// to store TLVs or list of TLVs.
|
||||
pub struct StaticPutRequestCacher {
|
||||
pub static_fields: StaticPutRequestFields,
|
||||
opts_buf: alloc::vec::Vec<u8>,
|
||||
opts_len: usize, // fs_request_start_end_pos: Option<(usize, usize)>
|
||||
}
|
||||
|
||||
impl StaticPutRequestCacher {
|
||||
pub fn new(max_len_opts_buf: usize) -> Self {
|
||||
Self {
|
||||
static_fields: StaticPutRequestFields::default(),
|
||||
opts_buf: alloc::vec![0; max_len_opts_buf],
|
||||
opts_len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(
|
||||
&mut self,
|
||||
put_request: &impl ReadablePutRequest,
|
||||
) -> Result<(), ByteConversionError> {
|
||||
self.static_fields.destination_id = put_request.destination_id();
|
||||
if let Some(source_file) = put_request.source_file() {
|
||||
if source_file.len() > u8::MAX as usize {
|
||||
return Err(ByteConversionError::ToSliceTooSmall {
|
||||
found: self.static_fields.source_file_buf.len(),
|
||||
expected: source_file.len(),
|
||||
});
|
||||
}
|
||||
self.static_fields.source_file_buf[..source_file.len()]
|
||||
.copy_from_slice(source_file.as_bytes());
|
||||
self.static_fields.source_file_len = source_file.len();
|
||||
}
|
||||
if let Some(dest_file) = put_request.dest_file() {
|
||||
if dest_file.len() > u8::MAX as usize {
|
||||
return Err(ByteConversionError::ToSliceTooSmall {
|
||||
found: self.static_fields.source_file_buf.len(),
|
||||
expected: dest_file.len(),
|
||||
});
|
||||
}
|
||||
self.static_fields.dest_file_buf[..dest_file.len()]
|
||||
.copy_from_slice(dest_file.as_bytes());
|
||||
self.static_fields.dest_file_len = dest_file.len();
|
||||
}
|
||||
self.static_fields.trans_mode = put_request.trans_mode();
|
||||
self.static_fields.closure_requested = put_request.closure_requested();
|
||||
self.static_fields.seg_ctrl = put_request.seg_ctrl();
|
||||
let mut current_idx = 0;
|
||||
let mut store_tlv = |tlv: &Tlv| {
|
||||
if current_idx + tlv.len_full() > self.opts_buf.len() {
|
||||
return Err(ByteConversionError::ToSliceTooSmall {
|
||||
found: self.opts_buf.len(),
|
||||
expected: current_idx + tlv.len_full(),
|
||||
});
|
||||
}
|
||||
// We checked the buffer lengths, so this should never fail.
|
||||
tlv.write_to_bytes(&mut self.opts_buf[current_idx..current_idx + tlv.len_full()])
|
||||
.unwrap();
|
||||
current_idx += tlv.len_full();
|
||||
Ok(())
|
||||
};
|
||||
if let Some(fs_req) = put_request.fs_requests() {
|
||||
for fs_req in fs_req {
|
||||
store_tlv(&fs_req)?;
|
||||
}
|
||||
}
|
||||
if let Some(msgs_to_user) = put_request.msgs_to_user() {
|
||||
for msg_to_user in msgs_to_user {
|
||||
store_tlv(&msg_to_user)?;
|
||||
}
|
||||
}
|
||||
self.opts_len = current_idx;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn has_source_file(&self) -> bool {
|
||||
self.static_fields.source_file_len > 0
|
||||
}
|
||||
|
||||
pub fn has_dest_file(&self) -> bool {
|
||||
self.static_fields.dest_file_len > 0
|
||||
}
|
||||
|
||||
pub fn source_file(&self) -> Result<&str, Utf8Error> {
|
||||
core::str::from_utf8(
|
||||
&self.static_fields.source_file_buf[0..self.static_fields.source_file_len],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dest_file(&self) -> Result<&str, Utf8Error> {
|
||||
core::str::from_utf8(
|
||||
&self.static_fields.dest_file_buf[0..self.static_fields.dest_file_len],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn opts_len(&self) -> usize {
|
||||
self.opts_len
|
||||
}
|
||||
|
||||
pub fn opts_slice(&self) -> &[u8] {
|
||||
&self.opts_buf[0..self.opts_len]
|
||||
}
|
||||
|
||||
/// This clears the cacher structure. This is a cheap operation because it only
|
||||
/// sets [Option]al values to [None] and the length of stores TLVs to 0.
|
||||
///
|
||||
/// Please note that this method will not set the values in the buffer to 0.
|
||||
pub fn clear(&mut self) {
|
||||
self.static_fields.clear();
|
||||
self.opts_len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -560,7 +557,7 @@ mod tests {
|
||||
use std::string::String;
|
||||
|
||||
use spacepackets::{
|
||||
cfdp::tlv::{ReadableTlv, msg_to_user::MsgToUserTlv},
|
||||
cfdp::tlv::{msg_to_user::MsgToUserTlv, ReadableTlv},
|
||||
util::UbfU16,
|
||||
};
|
||||
|
||||
@@ -692,7 +689,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_put_request_cacher_basic() {
|
||||
let put_request_cached = StaticPutRequestCacher::<128>::new();
|
||||
let put_request_cached = StaticPutRequestCacher::new(128);
|
||||
assert_eq!(put_request_cached.static_fields.source_file_len, 0);
|
||||
assert_eq!(put_request_cached.static_fields.dest_file_len, 0);
|
||||
assert_eq!(put_request_cached.opts_len(), 0);
|
||||
@@ -701,7 +698,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_put_request_cacher_set() {
|
||||
let mut put_request_cached = StaticPutRequestCacher::<128>::new();
|
||||
let mut put_request_cached = StaticPutRequestCacher::new(128);
|
||||
let src_file = "/tmp/hello.txt";
|
||||
let dest_file = "/tmp/hello2.txt";
|
||||
let put_request =
|
||||
@@ -723,7 +720,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_put_request_cacher_set_and_clear() {
|
||||
let mut put_request_cached = StaticPutRequestCacher::<128>::new();
|
||||
let mut put_request_cached = StaticPutRequestCacher::new(128);
|
||||
let src_file = "/tmp/hello.txt";
|
||||
let dest_file = "/tmp/hello2.txt";
|
||||
let put_request =
|
||||
|
||||
1896
src/source.rs
1896
src/source.rs
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
use core::fmt::Debug;
|
||||
|
||||
/// Generic abstraction for a check/countdown timer. Should also be cheap to copy and clone.
|
||||
pub trait Countdown: Debug {
|
||||
/// Generic abstraction for a check/countdown timer.
|
||||
pub trait CountdownProvider: Debug {
|
||||
fn has_expired(&self) -> bool;
|
||||
fn reset(&mut self);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
use spacepackets::cfdp::tlv::WritableTlv;
|
||||
use spacepackets::{
|
||||
cfdp::{
|
||||
ConditionCode,
|
||||
pdu::{
|
||||
file_data::SegmentMetadata,
|
||||
finished::{DeliveryCode, FileStatus},
|
||||
},
|
||||
tlv::msg_to_user::MsgToUserTlv,
|
||||
ConditionCode,
|
||||
},
|
||||
util::UnsignedByteField,
|
||||
};
|
||||
|
||||
@@ -2,27 +2,23 @@
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::Write,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, AtomicU16},
|
||||
mpsc,
|
||||
},
|
||||
sync::{atomic::AtomicBool, mpsc, Arc},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use cfdp::{
|
||||
EntityType, FaultInfo, IndicationConfig, LocalEntityConfig, PduOwnedWithInfo,
|
||||
RemoteEntityConfig, StdTimerCreator, TransactionId, UserFaultHook,
|
||||
dest::DestinationHandler,
|
||||
filestore::NativeFilestore,
|
||||
lost_segments::LostSegmentsList,
|
||||
request::PutRequestOwned,
|
||||
request::{PutRequestOwned, StaticPutRequestCacher},
|
||||
source::SourceHandler,
|
||||
user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams, TransactionFinishedParams},
|
||||
EntityType, IndicationConfig, LocalEntityConfig, PduOwnedWithInfo, RemoteEntityConfig,
|
||||
StdTimerCreator, TransactionId, UserFaultHookProvider,
|
||||
};
|
||||
use spacepackets::{
|
||||
cfdp::{ChecksumType, ConditionCode, TransmissionMode},
|
||||
seq_count::SeqCountProviderSyncU16,
|
||||
util::UnsignedByteFieldU16,
|
||||
};
|
||||
|
||||
@@ -34,21 +30,43 @@ const FILE_DATA: &str = "Hello World!";
|
||||
#[derive(Default)]
|
||||
pub struct ExampleFaultHandler {}
|
||||
|
||||
impl UserFaultHook for ExampleFaultHandler {
|
||||
fn notice_of_suspension_cb(&mut self, fault_info: FaultInfo) {
|
||||
panic!("unexpected suspension, {:?}", fault_info);
|
||||
impl UserFaultHookProvider for ExampleFaultHandler {
|
||||
fn notice_of_suspension_cb(
|
||||
&mut self,
|
||||
transaction_id: TransactionId,
|
||||
cond: ConditionCode,
|
||||
progress: u64,
|
||||
) {
|
||||
panic!(
|
||||
"unexpected suspension of transaction {:?}, condition code {:?}, progress {}",
|
||||
transaction_id, cond, progress
|
||||
);
|
||||
}
|
||||
|
||||
fn notice_of_cancellation_cb(&mut self, fault_info: FaultInfo) {
|
||||
panic!("unexpected cancellation, {:?}", fault_info);
|
||||
fn notice_of_cancellation_cb(
|
||||
&mut self,
|
||||
transaction_id: TransactionId,
|
||||
cond: ConditionCode,
|
||||
progress: u64,
|
||||
) {
|
||||
panic!(
|
||||
"unexpected cancellation of transaction {:?}, condition code {:?}, progress {}",
|
||||
transaction_id, cond, progress
|
||||
);
|
||||
}
|
||||
|
||||
fn abandoned_cb(&mut self, fault_info: FaultInfo) {
|
||||
panic!("unexpected abandonment, {:?}", fault_info);
|
||||
fn abandoned_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64) {
|
||||
panic!(
|
||||
"unexpected abandonment of transaction {:?}, condition code {:?}, progress {}",
|
||||
transaction_id, cond, progress
|
||||
);
|
||||
}
|
||||
|
||||
fn ignore_cb(&mut self, fault_info: FaultInfo) {
|
||||
panic!("unexpected ignore, {:?}", fault_info);
|
||||
fn ignore_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64) {
|
||||
panic!(
|
||||
"ignoring unexpected error in transaction {:?}, condition code {:?}, progress {}",
|
||||
transaction_id, cond, progress
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +156,7 @@ impl CfdpUser for ExampleCfdpUser {
|
||||
}
|
||||
}
|
||||
|
||||
fn end_to_end_test(transmission_mode: TransmissionMode, with_closure: bool) {
|
||||
fn end_to_end_test(with_closure: bool) {
|
||||
// Simplified event handling using atomic signals.
|
||||
let stop_signal_source = Arc::new(AtomicBool::new(false));
|
||||
let stop_signal_dest = stop_signal_source.clone();
|
||||
@@ -167,19 +185,22 @@ fn end_to_end_test(transmission_mode: TransmissionMode, with_closure: bool) {
|
||||
);
|
||||
let (source_tx, source_rx) = mpsc::channel::<PduOwnedWithInfo>();
|
||||
let (dest_tx, dest_rx) = mpsc::channel::<PduOwnedWithInfo>();
|
||||
let put_request_cacher = StaticPutRequestCacher::new(2048);
|
||||
let remote_cfg_of_dest = RemoteEntityConfig::new_with_default_values(
|
||||
REMOTE_ID.into(),
|
||||
1024,
|
||||
with_closure,
|
||||
false,
|
||||
transmission_mode,
|
||||
spacepackets::cfdp::TransmissionMode::Unacknowledged,
|
||||
ChecksumType::Crc32,
|
||||
);
|
||||
let seq_count_provider = AtomicU16::default();
|
||||
let seq_count_provider = SeqCountProviderSyncU16::default();
|
||||
let mut source_handler = SourceHandler::new(
|
||||
local_cfg_source,
|
||||
source_tx,
|
||||
NativeFilestore::default(),
|
||||
put_request_cacher,
|
||||
2048,
|
||||
remote_cfg_of_dest,
|
||||
StdTimerCreator::default(),
|
||||
seq_count_provider,
|
||||
@@ -196,16 +217,16 @@ fn end_to_end_test(transmission_mode: TransmissionMode, with_closure: bool) {
|
||||
1024,
|
||||
true,
|
||||
false,
|
||||
transmission_mode,
|
||||
spacepackets::cfdp::TransmissionMode::Unacknowledged,
|
||||
ChecksumType::Crc32,
|
||||
);
|
||||
let mut dest_handler = DestinationHandler::new(
|
||||
local_cfg_dest,
|
||||
1024,
|
||||
dest_tx,
|
||||
NativeFilestore::default(),
|
||||
remote_cfg_of_source,
|
||||
StdTimerCreator::default(),
|
||||
LostSegmentsList::default(),
|
||||
);
|
||||
let mut cfdp_user_dest = ExampleCfdpUser::new(EntityType::Receiving, completion_signal_dest);
|
||||
|
||||
@@ -213,7 +234,7 @@ fn end_to_end_test(transmission_mode: TransmissionMode, with_closure: bool) {
|
||||
REMOTE_ID.into(),
|
||||
srcfile.to_str().expect("invaid path string"),
|
||||
destfile.to_str().expect("invaid path string"),
|
||||
Some(transmission_mode),
|
||||
Some(TransmissionMode::Unacknowledged),
|
||||
Some(with_closure),
|
||||
)
|
||||
.expect("put request creation failed");
|
||||
@@ -322,16 +343,11 @@ fn end_to_end_test(transmission_mode: TransmissionMode, with_closure: bool) {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end_to_end_unacknowledged_no_closure() {
|
||||
end_to_end_test(TransmissionMode::Unacknowledged, false);
|
||||
fn end_to_end_test_no_closure() {
|
||||
end_to_end_test(false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end_to_end_unacknowledged_with_closure() {
|
||||
end_to_end_test(TransmissionMode::Unacknowledged, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end_to_end_acknowledged() {
|
||||
end_to_end_test(TransmissionMode::Acknowledged, true);
|
||||
fn end_to_end_test_with_closure() {
|
||||
end_to_end_test(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user