7 Commits
v0.3.0 ... HEAD

Author SHA1 Message Date
748142b0a4 Merge pull request 'more docs' (#16) from more-docs into main
Reviewed-on: #16
2025-11-27 15:35:37 +01:00
Robin Mueller
52b21db579 more docs 2025-11-27 15:35:05 +01:00
f4d66bb482 Merge pull request 'bump spacepackets' (#15) from bump-spacepackets into main
Reviewed-on: #15
2025-11-27 13:55:10 +01:00
Robin Mueller
98096fdea8 bump spacepackets 2025-11-27 13:54:01 +01:00
9494e29781 Merge pull request 'removed doc_auto_cfg feature' (#14) from remove-doc-auto-cfg-feature into main
Reviewed-on: #14
2025-10-02 09:47:33 +02:00
Robin Mueller
0d876c5525 removed doc_auto_cfg feature 2025-10-01 00:32:34 +02:00
44c31c935a use released cfdp-py in interop example 2025-09-25 14:06:08 +02:00
12 changed files with 297 additions and 139 deletions

View File

@@ -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.86.0 - uses: dtolnay/rust-toolchain@1.87
- run: cargo check --release - run: cargo check --release
cross-check: cross-check:
@@ -63,7 +63,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly - 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 --features "serde, defmt" --no-deps
clippy: clippy:
name: Clippy name: Clippy

View File

@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
- Bumped `spacepackets` to v0.17
# [v0.3.0] 2025-09-25 # [v0.3.0] 2025-09-25
- Bumped `spacepackets` to v0.16 - Bumped `spacepackets` to v0.16

View File

@@ -2,7 +2,7 @@
name = "cfdp-rs" name = "cfdp-rs"
version = "0.3.0" version = "0.3.0"
edition = "2024" edition = "2024"
rust-version = "1.86.0" rust-version = "1.87"
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"
@@ -20,7 +20,7 @@ crc = "3"
smallvec = "1" smallvec = "1"
derive-new = ">=0.6, <=0.7" derive-new = ">=0.6, <=0.7"
hashbrown = { version = ">=0.14, <=0.15", optional = true } hashbrown = { version = ">=0.14, <=0.15", optional = true }
spacepackets = { version = "0.16", default-features = false } spacepackets = { version = "0.17", default-features = false }
thiserror = { version = "2", default-features = false } thiserror = { version = "2", default-features = false }
heapless = "0.9" heapless = "0.9"
serde = { version = "1", optional = true } serde = { version = "1", optional = true }

View File

@@ -28,7 +28,7 @@ use spacepackets::{
ChecksumType, ConditionCode, TransmissionMode, ChecksumType, ConditionCode, TransmissionMode,
pdu::{PduError, file_data::FileDataPdu, metadata::MetadataPduReader}, pdu::{PduError, file_data::FileDataPdu, metadata::MetadataPduReader},
}, },
util::{UnsignedByteFieldU16, UnsignedEnum}, util::UnsignedByteFieldU16,
}; };
static KILL_APP: AtomicBool = AtomicBool::new(false); static KILL_APP: AtomicBool = AtomicBool::new(false);
@@ -258,17 +258,17 @@ impl UdpServer {
fn pdu_printout(pdu: &PduOwnedWithInfo) { fn pdu_printout(pdu: &PduOwnedWithInfo) {
match pdu.pdu_type() { match pdu.pdu_type() {
spacepackets::cfdp::PduType::FileDirective => match pdu.file_directive_type().unwrap() { spacepackets::cfdp::PduType::FileDirective => match pdu.file_directive_type().unwrap() {
spacepackets::cfdp::pdu::FileDirectiveType::EofPdu => (), spacepackets::cfdp::pdu::FileDirectiveType::Eof => (),
spacepackets::cfdp::pdu::FileDirectiveType::FinishedPdu => (), spacepackets::cfdp::pdu::FileDirectiveType::Finished => (),
spacepackets::cfdp::pdu::FileDirectiveType::AckPdu => (), spacepackets::cfdp::pdu::FileDirectiveType::Ack => (),
spacepackets::cfdp::pdu::FileDirectiveType::MetadataPdu => { spacepackets::cfdp::pdu::FileDirectiveType::Metadata => {
let meta_pdu = let meta_pdu =
MetadataPduReader::new(pdu.raw_pdu()).expect("creating metadata pdu failed"); MetadataPduReader::new(pdu.raw_pdu()).expect("creating metadata pdu failed");
debug!("Metadata PDU: {:?}", meta_pdu) debug!("Metadata PDU: {:?}", meta_pdu)
} }
spacepackets::cfdp::pdu::FileDirectiveType::NakPdu => (), spacepackets::cfdp::pdu::FileDirectiveType::Nak => (),
spacepackets::cfdp::pdu::FileDirectiveType::PromptPdu => (), spacepackets::cfdp::pdu::FileDirectiveType::Prompt => (),
spacepackets::cfdp::pdu::FileDirectiveType::KeepAlivePdu => (), spacepackets::cfdp::pdu::FileDirectiveType::KeepAlive => (),
}, },
spacepackets::cfdp::PduType::FileData => { spacepackets::cfdp::PduType::FileData => {
let fd_pdu = let fd_pdu =

View File

@@ -1 +1 @@
cfdp-py @ git+https://github.com/us-irs/cfdp-py.git@main cfdp-py == 0.6.0

View File

@@ -1,9 +1,12 @@
all: check build embedded clippy fmt docs test coverage all: check build embedded clippy check-fmt docs test coverage
clippy: clippy:
cargo clippy -- -D warnings cargo clippy -- -D warnings
fmt: fmt:
cargo fmt --all
check-fmt:
cargo fmt --all -- --check cargo fmt --all -- --check
check: check:
@@ -20,12 +23,10 @@ embedded:
cargo build --target thumbv7em-none-eabihf --no-default-features --features "defmt, packet-buf-1k" cargo build --target thumbv7em-none-eabihf --no-default-features --features "defmt, packet-buf-1k"
docs: docs:
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --features "serde, defmt" --no-deps
cargo +nightly doc --features "serde, defmt"
docs-html: docs-html:
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --features "serde, defmt" --open
cargo +nightly doc --features "serde, defmt" --open
coverage: coverage:
cargo llvm-cov nextest cargo llvm-cov nextest

View File

@@ -62,7 +62,7 @@ use spacepackets::{
}, },
tlv::{EntityIdTlv, GenericTlv, ReadableTlv, TlvType, msg_to_user::MsgToUserTlv}, tlv::{EntityIdTlv, GenericTlv, ReadableTlv, TlvType, msg_to_user::MsgToUserTlv},
}, },
util::{UnsignedByteField, UnsignedEnum}, util::UnsignedByteField,
}; };
#[derive(Debug)] #[derive(Debug)]
@@ -608,33 +608,31 @@ impl<
) -> Result<u32, DestError> { ) -> Result<u32, DestError> {
let mut sent_packets = 0; let mut sent_packets = 0;
match pdu_directive { match pdu_directive {
FileDirectiveType::EofPdu => { FileDirectiveType::Eof => {
let eof_pdu = EofPdu::from_bytes(raw_packet)?; let eof_pdu = EofPdu::from_bytes(raw_packet)?;
sent_packets += self.handle_eof_pdu(cfdp_user, eof_pdu)? sent_packets += self.handle_eof_pdu(cfdp_user, eof_pdu)?
} }
FileDirectiveType::FinishedPdu FileDirectiveType::Finished | FileDirectiveType::Nak | FileDirectiveType::KeepAlive => {
| FileDirectiveType::NakPdu
| FileDirectiveType::KeepAlivePdu => {
return Err(DestError::CantProcessPacketType { return Err(DestError::CantProcessPacketType {
pdu_type: PduType::FileDirective, pdu_type: PduType::FileDirective,
directive_type: Some(pdu_directive), directive_type: Some(pdu_directive),
}); });
} }
FileDirectiveType::AckPdu => { FileDirectiveType::Ack => {
let ack_pdu = AckPdu::from_bytes(raw_packet)?; let ack_pdu = AckPdu::from_bytes(raw_packet)?;
self.handle_ack_pdu(ack_pdu)?; self.handle_ack_pdu(ack_pdu)?;
} }
FileDirectiveType::MetadataPdu => { FileDirectiveType::Metadata => {
let metadata_pdu = MetadataPduReader::from_bytes(raw_packet)?; let metadata_pdu = MetadataPduReader::from_bytes(raw_packet)?;
self.handle_metadata_pdu(metadata_pdu)? self.handle_metadata_pdu(metadata_pdu)?
} }
FileDirectiveType::PromptPdu => self.handle_prompt_pdu(raw_packet)?, FileDirectiveType::Prompt => self.handle_prompt_pdu(raw_packet)?,
}; };
Ok(sent_packets) Ok(sent_packets)
} }
fn handle_ack_pdu(&mut self, ack_pdu: AckPdu) -> Result<(), DestError> { fn handle_ack_pdu(&mut self, ack_pdu: AckPdu) -> Result<(), DestError> {
if ack_pdu.directive_code_of_acked_pdu() != FileDirectiveType::FinishedPdu { if ack_pdu.directive_code_of_acked_pdu() != FileDirectiveType::Finished {
self.transaction_params self.transaction_params
.anomaly_tracker .anomaly_tracker
.increment_invalid_ack_directive_code(); .increment_invalid_ack_directive_code();
@@ -827,7 +825,7 @@ impl<
.finish(0, self.transaction_params.progress) .finish(0, self.transaction_params.progress)
.map_err(PduError::from)?; .map_err(PduError::from)?;
self.pdu_sender.send_file_directive_pdu( self.pdu_sender.send_file_directive_pdu(
FileDirectiveType::NakPdu, FileDirectiveType::Nak,
&self.pdu_and_cksum_buffer.borrow()[0..written_size], &self.pdu_and_cksum_buffer.borrow()[0..written_size],
)?; )?;
packets_sent += 1; packets_sent += 1;
@@ -954,7 +952,7 @@ impl<
}; };
let written_len = nak_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?; let written_len = nak_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?;
self.pdu_sender.send_file_directive_pdu( self.pdu_sender.send_file_directive_pdu(
FileDirectiveType::NakPdu, FileDirectiveType::Nak,
&self.pdu_and_cksum_buffer.borrow()[0..written_len], &self.pdu_and_cksum_buffer.borrow()[0..written_len],
)?; )?;
sent_packets += 1; sent_packets += 1;
@@ -998,7 +996,7 @@ impl<
if self.transmission_mode().unwrap() == TransmissionMode::Unacknowledged { if self.transmission_mode().unwrap() == TransmissionMode::Unacknowledged {
return Err(DestError::WrongStepForPdu { return Err(DestError::WrongStepForPdu {
pdu_type: PduType::FileDirective, pdu_type: PduType::FileDirective,
file_directive_type: Some(FileDirectiveType::EofPdu), file_directive_type: Some(FileDirectiveType::Eof),
step: self.step(), step: self.step(),
}); });
} }
@@ -1116,7 +1114,7 @@ impl<
); );
let written_len = ack_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?; let written_len = ack_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?;
self.pdu_sender.send_file_directive_pdu( self.pdu_sender.send_file_directive_pdu(
FileDirectiveType::AckPdu, FileDirectiveType::Ack,
&self.pdu_and_cksum_buffer.borrow()[0..written_len], &self.pdu_and_cksum_buffer.borrow()[0..written_len],
)?; )?;
Ok(()) Ok(())
@@ -1280,7 +1278,7 @@ impl<
.finish(0, self.transaction_params.file_size) .finish(0, self.transaction_params.file_size)
.map_err(PduError::from)?; .map_err(PduError::from)?;
self.pdu_sender.send_file_directive_pdu( self.pdu_sender.send_file_directive_pdu(
FileDirectiveType::NakPdu, FileDirectiveType::Nak,
&self.pdu_and_cksum_buffer.borrow()[0..written_len], &self.pdu_and_cksum_buffer.borrow()[0..written_len],
)?; )?;
sent_packets += 1; sent_packets += 1;
@@ -1336,7 +1334,7 @@ impl<
.finish(current_start_of_scope, current_end_of_scope) .finish(current_start_of_scope, current_end_of_scope)
.map_err(PduError::from)?; .map_err(PduError::from)?;
self.pdu_sender.send_file_directive_pdu( self.pdu_sender.send_file_directive_pdu(
FileDirectiveType::NakPdu, FileDirectiveType::Nak,
&self.pdu_and_cksum_buffer.borrow()[..written_len], &self.pdu_and_cksum_buffer.borrow()[..written_len],
)?; )?;
sent_packets += 1; sent_packets += 1;
@@ -1382,7 +1380,7 @@ impl<
.finish(current_start_of_scope, current_end_of_scope) .finish(current_start_of_scope, current_end_of_scope)
.map_err(PduError::from)?; .map_err(PduError::from)?;
self.pdu_sender.send_file_directive_pdu( self.pdu_sender.send_file_directive_pdu(
FileDirectiveType::NakPdu, FileDirectiveType::Nak,
&self.pdu_and_cksum_buffer.borrow()[..written_len], &self.pdu_and_cksum_buffer.borrow()[..written_len],
)?; )?;
sent_packets += 1; sent_packets += 1;
@@ -1898,7 +1896,7 @@ impl<
}; };
finished_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?; finished_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?;
self.pdu_sender.send_file_directive_pdu( self.pdu_sender.send_file_directive_pdu(
FileDirectiveType::FinishedPdu, FileDirectiveType::Finished,
&self.pdu_and_cksum_buffer.borrow()[0..finished_pdu.len_written()], &self.pdu_and_cksum_buffer.borrow()[0..finished_pdu.len_written()],
)?; )?;
Ok(1) Ok(1)
@@ -1926,7 +1924,7 @@ mod tests {
nak::NakPduReader, nak::NakPduReader,
}, },
}, },
util::UnsignedByteFieldU8, util::{UnsignedByteFieldU8, UnsignedEnum},
}; };
use crate::{ use crate::{
@@ -2053,7 +2051,7 @@ mod tests {
fn remote_cfg_mut(&mut self) -> &mut RemoteEntityConfig { fn remote_cfg_mut(&mut self) -> &mut RemoteEntityConfig {
self.handler self.handler
.remote_cfg_table .remote_cfg_table
.get_mut(LOCAL_ID.value()) .get_mut(LOCAL_ID.value_raw())
.unwrap() .unwrap()
} }
@@ -2214,13 +2212,13 @@ mod tests {
assert!(!self.pdu_queue_empty()); assert!(!self.pdu_queue_empty());
let pdu = self.get_next_pdu().unwrap(); let pdu = self.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::AckPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Ack);
let ack_pdu = AckPdu::from_bytes(&pdu.raw_pdu).unwrap(); let ack_pdu = AckPdu::from_bytes(&pdu.raw_pdu).unwrap();
assert_eq!(ack_pdu.condition_code(), cond_code); assert_eq!(ack_pdu.condition_code(), cond_code);
assert_eq!(ack_pdu.transaction_status(), TransactionStatus::Active); assert_eq!(ack_pdu.transaction_status(), TransactionStatus::Active);
assert_eq!( assert_eq!(
ack_pdu.directive_code_of_acked_pdu(), ack_pdu.directive_code_of_acked_pdu(),
FileDirectiveType::EofPdu FileDirectiveType::Eof
); );
} }
@@ -2229,7 +2227,7 @@ mod tests {
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(
pdu.file_directive_type.unwrap(), pdu.file_directive_type.unwrap(),
FileDirectiveType::FinishedPdu FileDirectiveType::Finished
); );
let finished_pdu = FinishedPduReader::from_bytes(&pdu.raw_pdu).unwrap(); let finished_pdu = FinishedPduReader::from_bytes(&pdu.raw_pdu).unwrap();
assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Complete); assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Complete);
@@ -2248,7 +2246,7 @@ mod tests {
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(
pdu.file_directive_type.unwrap(), pdu.file_directive_type.unwrap(),
FileDirectiveType::FinishedPdu FileDirectiveType::Finished
); );
let finished_pdu = FinishedPduReader::from_bytes(&pdu.raw_pdu).unwrap(); let finished_pdu = FinishedPduReader::from_bytes(&pdu.raw_pdu).unwrap();
assert_eq!(finished_pdu.delivery_code(), delivery_code); assert_eq!(finished_pdu.delivery_code(), delivery_code);
@@ -2764,7 +2762,7 @@ mod tests {
assert_eq!(sent_pdu.pdu_type, PduType::FileDirective); assert_eq!(sent_pdu.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(
sent_pdu.file_directive_type, sent_pdu.file_directive_type,
Some(FileDirectiveType::FinishedPdu) Some(FileDirectiveType::Finished)
); );
let finished_pdu = FinishedPduReader::from_bytes(&sent_pdu.raw_pdu).unwrap(); let finished_pdu = FinishedPduReader::from_bytes(&sent_pdu.raw_pdu).unwrap();
assert_eq!(finished_pdu.file_status(), FileStatus::Retained); assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
@@ -2826,7 +2824,7 @@ mod tests {
} = error } = error
{ {
assert_eq!(pdu_type, PduType::FileDirective); assert_eq!(pdu_type, PduType::FileDirective);
assert_eq!(directive_type, Some(FileDirectiveType::FinishedPdu)); assert_eq!(directive_type, Some(FileDirectiveType::Finished));
} }
} }
@@ -2938,7 +2936,7 @@ mod tests {
assert_eq!(sent_pdu.pdu_type, PduType::FileDirective); assert_eq!(sent_pdu.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(
sent_pdu.file_directive_type, sent_pdu.file_directive_type,
Some(FileDirectiveType::FinishedPdu) Some(FileDirectiveType::Finished)
); );
let finished_pdu = FinishedPduReader::from_bytes(&sent_pdu.raw_pdu).unwrap(); let finished_pdu = FinishedPduReader::from_bytes(&sent_pdu.raw_pdu).unwrap();
assert_eq!(finished_pdu.file_status(), FileStatus::Retained); assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
@@ -3083,7 +3081,7 @@ mod tests {
assert_eq!(finished_pdu.pdu_type, PduType::FileDirective); assert_eq!(finished_pdu.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(
finished_pdu.file_directive_type.unwrap(), finished_pdu.file_directive_type.unwrap(),
FileDirectiveType::FinishedPdu FileDirectiveType::Finished
); );
let finished_pdu = FinishedPduReader::from_bytes(&finished_pdu.raw_pdu).unwrap(); let finished_pdu = FinishedPduReader::from_bytes(&finished_pdu.raw_pdu).unwrap();
assert_eq!( assert_eq!(
@@ -3197,7 +3195,7 @@ mod tests {
assert_eq!(next_pdu.pdu_type, PduType::FileDirective); assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(
next_pdu.file_directive_type.unwrap(), next_pdu.file_directive_type.unwrap(),
FileDirectiveType::FinishedPdu FileDirectiveType::Finished
); );
let finished_pdu = let finished_pdu =
FinishedPduReader::new(&next_pdu.raw_pdu).expect("finished pdu read failed"); FinishedPduReader::new(&next_pdu.raw_pdu).expect("finished pdu read failed");
@@ -3249,7 +3247,7 @@ mod tests {
assert_eq!(next_pdu.pdu_type, PduType::FileDirective); assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(
next_pdu.file_directive_type.unwrap(), next_pdu.file_directive_type.unwrap(),
FileDirectiveType::FinishedPdu FileDirectiveType::Finished
); );
let finished_pdu = let finished_pdu =
FinishedPduReader::new(&next_pdu.raw_pdu).expect("finished pdu read failed"); FinishedPduReader::new(&next_pdu.raw_pdu).expect("finished pdu read failed");
@@ -3284,7 +3282,7 @@ mod tests {
let remote_cfg_mut = tb let remote_cfg_mut = tb
.handler .handler
.remote_cfg_table .remote_cfg_table
.get_mut(LOCAL_ID.value()) .get_mut(LOCAL_ID.value_raw())
.unwrap(); .unwrap();
remote_cfg_mut.disposition_on_cancellation = true; remote_cfg_mut.disposition_on_cancellation = true;
let mut user = tb.test_user_from_cached_paths(0); let mut user = tb.test_user_from_cached_paths(0);
@@ -3322,7 +3320,7 @@ mod tests {
let remote_cfg_mut = tb let remote_cfg_mut = tb
.handler .handler
.remote_cfg_table .remote_cfg_table
.get_mut(LOCAL_ID.value()) .get_mut(LOCAL_ID.value_raw())
.unwrap(); .unwrap();
remote_cfg_mut.disposition_on_cancellation = true; remote_cfg_mut.disposition_on_cancellation = true;
let mut user = tb.test_user_from_cached_paths(file_size); let mut user = tb.test_user_from_cached_paths(file_size);
@@ -3378,7 +3376,7 @@ mod tests {
assert_eq!(tb.pdu_queue_len(), 1); assert_eq!(tb.pdu_queue_len(), 1);
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.pdu_header().common_pdu_conf().file_flag, file_flag); assert_eq!(nak_pdu.pdu_header().common_pdu_conf().file_flag, file_flag);
assert_eq!(nak_pdu.start_of_scope(), 0); assert_eq!(nak_pdu.start_of_scope(), 0);
@@ -3444,7 +3442,7 @@ mod tests {
assert_eq!(tb.pdu_queue_len(), 1); assert_eq!(tb.pdu_queue_len(), 1);
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.start_of_scope(), 0); assert_eq!(nak_pdu.start_of_scope(), 0);
assert_eq!(nak_pdu.end_of_scope(), file_size); assert_eq!(nak_pdu.end_of_scope(), file_size);
@@ -3494,7 +3492,7 @@ mod tests {
assert_eq!(tb.pdu_queue_len(), 1); assert_eq!(tb.pdu_queue_len(), 1);
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.start_of_scope(), 0); assert_eq!(nak_pdu.start_of_scope(), 0);
assert_eq!(nak_pdu.end_of_scope(), file_size); assert_eq!(nak_pdu.end_of_scope(), file_size);
@@ -3552,7 +3550,7 @@ mod tests {
tb.check_eof_ack_pdu(ConditionCode::NoError); tb.check_eof_ack_pdu(ConditionCode::NoError);
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.start_of_scope(), 0); assert_eq!(nak_pdu.start_of_scope(), 0);
assert_eq!(nak_pdu.end_of_scope(), file_size); assert_eq!(nak_pdu.end_of_scope(), file_size);
@@ -3614,7 +3612,7 @@ mod tests {
assert_eq!(tb.pdu_queue_len(), 1); assert_eq!(tb.pdu_queue_len(), 1);
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.start_of_scope(), 0); assert_eq!(nak_pdu.start_of_scope(), 0);
assert_eq!(nak_pdu.end_of_scope(), file_size); assert_eq!(nak_pdu.end_of_scope(), file_size);
@@ -3628,7 +3626,7 @@ mod tests {
assert_eq!(tb.pdu_queue_len(), 1); assert_eq!(tb.pdu_queue_len(), 1);
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.start_of_scope(), 0); assert_eq!(nak_pdu.start_of_scope(), 0);
assert_eq!(nak_pdu.end_of_scope(), file_size); assert_eq!(nak_pdu.end_of_scope(), file_size);
@@ -3842,7 +3840,7 @@ mod tests {
assert_eq!(tb.pdu_queue_len(), 1); assert_eq!(tb.pdu_queue_len(), 1);
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.start_of_scope(), 0); assert_eq!(nak_pdu.start_of_scope(), 0);
assert_eq!(nak_pdu.end_of_scope(), file_size); assert_eq!(nak_pdu.end_of_scope(), file_size);
@@ -3926,7 +3924,7 @@ mod tests {
tb.check_eof_ack_pdu(ConditionCode::NoError); tb.check_eof_ack_pdu(ConditionCode::NoError);
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.start_of_scope(), 0); assert_eq!(nak_pdu.start_of_scope(), 0);
assert_eq!(nak_pdu.end_of_scope(), 10); assert_eq!(nak_pdu.end_of_scope(), 10);
@@ -3935,7 +3933,7 @@ mod tests {
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.start_of_scope(), 10); assert_eq!(nak_pdu.start_of_scope(), 10);
assert_eq!(nak_pdu.end_of_scope(), file_size); assert_eq!(nak_pdu.end_of_scope(), file_size);
@@ -4036,7 +4034,7 @@ mod tests {
tb.check_eof_ack_pdu(ConditionCode::NoError); tb.check_eof_ack_pdu(ConditionCode::NoError);
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.start_of_scope(), 0); assert_eq!(nak_pdu.start_of_scope(), 0);
assert_eq!(nak_pdu.end_of_scope(), 14); assert_eq!(nak_pdu.end_of_scope(), 14);
@@ -4045,7 +4043,7 @@ mod tests {
let pdu = tb.get_next_pdu().unwrap(); let pdu = tb.get_next_pdu().unwrap();
assert_eq!(pdu.pdu_type, PduType::FileDirective); assert_eq!(pdu.pdu_type, PduType::FileDirective);
assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::NakPdu); assert_eq!(pdu.file_directive_type.unwrap(), FileDirectiveType::Nak);
let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap(); let nak_pdu = NakPduReader::new(&pdu.raw_pdu).unwrap();
assert_eq!(nak_pdu.start_of_scope(), 14); assert_eq!(nak_pdu.start_of_scope(), 14);
assert_eq!(nak_pdu.end_of_scope(), file_size); assert_eq!(nak_pdu.end_of_scope(), file_size);

View File

@@ -109,7 +109,7 @@
//! [threadpool](https://docs.rs/threadpool/latest/threadpool/). //! [threadpool](https://docs.rs/threadpool/latest/threadpool/).
#![no_std] #![no_std]
// Show which crate feature enables conditionally compiled APIs in documentation. // Show which crate feature enables conditionally compiled APIs in documentation.
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
extern crate alloc; extern crate alloc;
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
@@ -138,7 +138,7 @@ use spacepackets::{
ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode, ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
pdu::{FileDirectiveType, PduError, PduHeader}, pdu::{FileDirectiveType, PduError, PduHeader},
}, },
util::{UnsignedByteField, UnsignedEnum}, util::UnsignedByteField,
}; };
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use std_mod::*; pub use std_mod::*;
@@ -942,26 +942,26 @@ pub fn determine_packet_target(raw_pdu: &[u8]) -> Result<PacketTarget, PduError>
let packet_target = match file_directive_type { let packet_target = match file_directive_type {
// Section c) of 4.5.3: These PDUs should always be targeted towards the file sender a.k.a. // Section c) of 4.5.3: These PDUs should always be targeted towards the file sender a.k.a.
// the source handler // the source handler
FileDirectiveType::NakPdu FileDirectiveType::Nak | FileDirectiveType::Finished | FileDirectiveType::KeepAlive => {
| FileDirectiveType::FinishedPdu PacketTarget::SourceEntity
| FileDirectiveType::KeepAlivePdu => PacketTarget::SourceEntity, }
// Section b) of 4.5.3: These PDUs should always be targeted towards the file receiver a.k.a. // Section b) of 4.5.3: These PDUs should always be targeted towards the file receiver a.k.a.
// the destination handler // the destination handler
FileDirectiveType::MetadataPdu FileDirectiveType::Metadata | FileDirectiveType::Eof | FileDirectiveType::Prompt => {
| FileDirectiveType::EofPdu PacketTarget::DestEntity
| FileDirectiveType::PromptPdu => PacketTarget::DestEntity, }
// Section a): Recipient depends of the type of PDU that is being acknowledged. We can simply // Section a): Recipient depends of the type of PDU that is being acknowledged. We can simply
// extract the PDU type from the raw stream. If it is an EOF PDU, this packet is passed to // extract the PDU type from the raw stream. If it is an EOF PDU, this packet is passed to
// the source handler, for a Finished PDU, it is passed to the destination handler. // the source handler, for a Finished PDU, it is passed to the destination handler.
FileDirectiveType::AckPdu => { FileDirectiveType::Ack => {
let acked_directive = FileDirectiveType::try_from(raw_pdu[header_len + 1] >> 4) let acked_directive = FileDirectiveType::try_from(raw_pdu[header_len + 1] >> 4)
.map_err(|_| PduError::InvalidDirectiveType { .map_err(|_| PduError::InvalidDirectiveType {
found: (raw_pdu[header_len + 1] >> 4), found: (raw_pdu[header_len + 1] >> 4),
expected: None, expected: None,
})?; })?;
if acked_directive == FileDirectiveType::EofPdu { if acked_directive == FileDirectiveType::Eof {
PacketTarget::SourceEntity PacketTarget::SourceEntity
} else if acked_directive == FileDirectiveType::FinishedPdu { } else if acked_directive == FileDirectiveType::Finished {
PacketTarget::DestEntity PacketTarget::DestEntity
} else { } else {
// TODO: Maybe a better error? This might be confusing.. // TODO: Maybe a better error? This might be confusing..
@@ -1576,7 +1576,7 @@ pub(crate) mod tests {
assert!(packet_info.file_directive_type().is_some()); assert!(packet_info.file_directive_type().is_some());
assert_eq!( assert_eq!(
packet_info.file_directive_type().unwrap(), packet_info.file_directive_type().unwrap(),
FileDirectiveType::MetadataPdu FileDirectiveType::Metadata
); );
assert_eq!( assert_eq!(
packet_info.raw_packet(), packet_info.raw_packet(),
@@ -1623,7 +1623,7 @@ pub(crate) mod tests {
assert_eq!(packet_info.raw_packet(), &buf[0..eof_pdu.len_written()]); assert_eq!(packet_info.raw_packet(), &buf[0..eof_pdu.len_written()]);
assert_eq!( assert_eq!(
packet_info.file_directive_type().unwrap(), packet_info.file_directive_type().unwrap(),
FileDirectiveType::EofPdu FileDirectiveType::Eof
); );
} }
@@ -1659,7 +1659,7 @@ pub(crate) mod tests {
TransmissionMode::Unacknowledged, TransmissionMode::Unacknowledged,
ChecksumType::Crc32, ChecksumType::Crc32,
); );
let remote_entity_retrieved = remote_entity_cfg.get(REMOTE_ID.value()).unwrap(); let remote_entity_retrieved = remote_entity_cfg.get(REMOTE_ID.value().into()).unwrap();
assert_eq!(remote_entity_retrieved.entity_id, REMOTE_ID.into()); assert_eq!(remote_entity_retrieved.entity_id, REMOTE_ID.into());
assert_eq!(remote_entity_retrieved.max_packet_len, 1024); assert_eq!(remote_entity_retrieved.max_packet_len, 1024);
assert!(remote_entity_retrieved.closure_requested_by_default); assert!(remote_entity_retrieved.closure_requested_by_default);
@@ -1668,7 +1668,7 @@ pub(crate) mod tests {
remote_entity_retrieved.default_crc_type, remote_entity_retrieved.default_crc_type,
ChecksumType::Crc32 ChecksumType::Crc32
); );
let remote_entity_mut = remote_entity_cfg.get_mut(REMOTE_ID.value()).unwrap(); let remote_entity_mut = remote_entity_cfg.get_mut(REMOTE_ID.value_raw()).unwrap();
assert_eq!(remote_entity_mut.entity_id, REMOTE_ID.into()); assert_eq!(remote_entity_mut.entity_id, REMOTE_ID.into());
let dummy = RemoteEntityConfig::new_with_default_values( let dummy = RemoteEntityConfig::new_with_default_values(
LOCAL_ID.into(), LOCAL_ID.into(),
@@ -1682,11 +1682,11 @@ pub(crate) mod tests {
remote_entity_cfg.add_config(&dummy).unwrap_err(), remote_entity_cfg.add_config(&dummy).unwrap_err(),
RemoteConfigStoreError::Full RemoteConfigStoreError::Full
); );
let remote_entity_retrieved = remote_entity_cfg.get(REMOTE_ID.value()).unwrap(); let remote_entity_retrieved = remote_entity_cfg.get(REMOTE_ID.value_raw()).unwrap();
assert_eq!(remote_entity_retrieved.entity_id, REMOTE_ID.into()); assert_eq!(remote_entity_retrieved.entity_id, REMOTE_ID.into());
// Does not exist. // Does not exist.
assert!(remote_entity_cfg.get(LOCAL_ID.value()).is_none()); assert!(remote_entity_cfg.get(LOCAL_ID.value_raw()).is_none());
assert!(remote_entity_cfg.get_mut(LOCAL_ID.value()).is_none()); assert!(remote_entity_cfg.get_mut(LOCAL_ID.value_raw()).is_none());
} }
#[test] #[test]
@@ -1711,20 +1711,20 @@ pub(crate) mod tests {
TransmissionMode::Unacknowledged, TransmissionMode::Unacknowledged,
ChecksumType::Crc32, ChecksumType::Crc32,
); );
let cfg_0 = remote_cfg_provider.get(REMOTE_ID.value()).unwrap(); let cfg_0 = remote_cfg_provider.get(REMOTE_ID.value_raw()).unwrap();
assert_eq!(cfg_0.entity_id, REMOTE_ID.into()); assert_eq!(cfg_0.entity_id, REMOTE_ID.into());
remote_cfg_provider remote_cfg_provider
.add_config(&remote_entity_cfg_2) .add_config(&remote_entity_cfg_2)
.unwrap(); .unwrap();
assert_eq!(remote_cfg_provider.0.len(), 2); assert_eq!(remote_cfg_provider.0.len(), 2);
let cfg_1 = remote_cfg_provider.get(LOCAL_ID.value()).unwrap(); let cfg_1 = remote_cfg_provider.get(LOCAL_ID.value_raw()).unwrap();
assert_eq!(cfg_1.entity_id, LOCAL_ID.into()); assert_eq!(cfg_1.entity_id, LOCAL_ID.into());
assert!(remote_cfg_provider.remove_config(REMOTE_ID.value())); assert!(remote_cfg_provider.remove_config(REMOTE_ID.value_raw()));
assert_eq!(remote_cfg_provider.0.len(), 1); assert_eq!(remote_cfg_provider.0.len(), 1);
let cfg_1_mut = remote_cfg_provider.get_mut(LOCAL_ID.value()).unwrap(); let cfg_1_mut = remote_cfg_provider.get_mut(LOCAL_ID.value_raw()).unwrap();
cfg_1_mut.default_crc_type = ChecksumType::Crc32C; cfg_1_mut.default_crc_type = ChecksumType::Crc32C;
assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value())); assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value_raw()));
assert!(remote_cfg_provider.get_mut(REMOTE_ID.value()).is_none()); assert!(remote_cfg_provider.get_mut(REMOTE_ID.value_raw()).is_none());
} }
#[test] #[test]
@@ -1749,7 +1749,7 @@ pub(crate) mod tests {
TransmissionMode::Unacknowledged, TransmissionMode::Unacknowledged,
ChecksumType::Crc32, ChecksumType::Crc32,
); );
let cfg_0 = remote_cfg_provider.get(REMOTE_ID.value()).unwrap(); let cfg_0 = remote_cfg_provider.get(REMOTE_ID.value_raw()).unwrap();
assert_eq!(cfg_0.entity_id, REMOTE_ID.into()); assert_eq!(cfg_0.entity_id, REMOTE_ID.into());
assert!( assert!(
remote_cfg_provider remote_cfg_provider
@@ -1757,14 +1757,14 @@ pub(crate) mod tests {
.unwrap() .unwrap()
); );
assert_eq!(remote_cfg_provider.0.len(), 2); assert_eq!(remote_cfg_provider.0.len(), 2);
let cfg_1 = remote_cfg_provider.get(LOCAL_ID.value()).unwrap(); let cfg_1 = remote_cfg_provider.get(LOCAL_ID.value_raw()).unwrap();
assert_eq!(cfg_1.entity_id, LOCAL_ID.into()); assert_eq!(cfg_1.entity_id, LOCAL_ID.into());
assert!(remote_cfg_provider.remove_config(REMOTE_ID.value())); assert!(remote_cfg_provider.remove_config(REMOTE_ID.value_raw()));
assert_eq!(remote_cfg_provider.0.len(), 1); assert_eq!(remote_cfg_provider.0.len(), 1);
let cfg_1_mut = remote_cfg_provider.get_mut(LOCAL_ID.value()).unwrap(); let cfg_1_mut = remote_cfg_provider.get_mut(LOCAL_ID.value_raw()).unwrap();
cfg_1_mut.default_crc_type = ChecksumType::Crc32C; cfg_1_mut.default_crc_type = ChecksumType::Crc32C;
assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value())); assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value_raw()));
assert!(remote_cfg_provider.get_mut(REMOTE_ID.value()).is_none()); assert!(remote_cfg_provider.get_mut(REMOTE_ID.value_raw()).is_none());
} }
#[test] #[test]

View File

@@ -1,3 +1,5 @@
//! # Request module
#![deny(missing_docs)]
use core::str::Utf8Error; use core::str::Utf8Error;
use spacepackets::{ use spacepackets::{
@@ -12,6 +14,9 @@ use spacepackets::{
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub use alloc_mod::*; pub use alloc_mod::*;
/// File path is too large.
///
/// The file path length is limited to 255 bytes.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@@ -20,36 +25,56 @@ pub struct FilePathTooLarge(pub usize);
/// This trait is an abstraction for different Put Request structures which can be used /// This trait is an abstraction for different Put Request structures which can be used
/// by Put Request consumers. /// by Put Request consumers.
pub trait ReadablePutRequest { pub trait ReadablePutRequest {
/// Destination entity ID.
fn destination_id(&self) -> UnsignedByteField; fn destination_id(&self) -> UnsignedByteField;
/// Source file path.
fn source_file(&self) -> Option<&str>; fn source_file(&self) -> Option<&str>;
/// Destination file path.
fn dest_file(&self) -> Option<&str>; fn dest_file(&self) -> Option<&str>;
/// Transmission mode if explicitely specified.
fn trans_mode(&self) -> Option<TransmissionMode>; fn trans_mode(&self) -> Option<TransmissionMode>;
/// Closure is requested for unacknowledged file transfer.
fn closure_requested(&self) -> Option<bool>; fn closure_requested(&self) -> Option<bool>;
/// Segmentation control.
fn seg_ctrl(&self) -> Option<SegmentationControl>; fn seg_ctrl(&self) -> Option<SegmentationControl>;
/// Iterator over Messages to User TLVs, if any are supplied.
fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv<'_>>>; fn msgs_to_user(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
/// Iterator over fault handler override TLVs, if any are supplied.
fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv<'_>>>; fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
/// Flow label TLV, if it is supplied.
fn flow_label(&self) -> Option<Tlv<'_>>; fn flow_label(&self) -> Option<Tlv<'_>>;
/// Iterator over filestore request TLVs, if any are supplied.
fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv<'_>>>; fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
} }
/// Put request structure.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests> { pub struct PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests> {
/// Destination entity ID.
pub destination_id: UnsignedByteField, pub destination_id: UnsignedByteField,
source_file: Option<&'src_file str>, source_file: Option<&'src_file str>,
dest_file: Option<&'dest_file str>, dest_file: Option<&'dest_file str>,
/// Transmission mode.
pub trans_mode: Option<TransmissionMode>, pub trans_mode: Option<TransmissionMode>,
/// Closure requested flag for unacknowledged file transfer.
pub closure_requested: Option<bool>, pub closure_requested: Option<bool>,
/// Segmentation control.
pub seg_ctrl: Option<SegmentationControl>, pub seg_ctrl: Option<SegmentationControl>,
/// Messages to user TLVs.
pub msgs_to_user: Option<&'msgs_to_user [Tlv<'msgs_to_user>]>, pub msgs_to_user: Option<&'msgs_to_user [Tlv<'msgs_to_user>]>,
/// Fault handler override TLVs.
pub fault_handler_overrides: Option<&'fh_ovrds [Tlv<'fh_ovrds>]>, pub fault_handler_overrides: Option<&'fh_ovrds [Tlv<'fh_ovrds>]>,
/// Flow label TLV.
pub flow_label: Option<Tlv<'flow_label>>, pub flow_label: Option<Tlv<'flow_label>>,
/// Filestore request TLVs.
pub fs_requests: Option<&'fs_requests [Tlv<'fs_requests>]>, pub fs_requests: Option<&'fs_requests [Tlv<'fs_requests>]>,
} }
impl<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests> impl<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests>
PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests> PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests>
{ {
/// Create a new put request with all possible fields.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
destination_id: UnsignedByteField, destination_id: UnsignedByteField,
@@ -130,6 +155,9 @@ impl ReadablePutRequest for PutRequest<'_, '_, '_, '_, '_, '_> {
} }
} }
/// Generic path checks.
///
/// This only checks the length of the paths.
pub fn generic_path_checks( pub fn generic_path_checks(
source_file: Option<&str>, source_file: Option<&str>,
dest_file: Option<&str>, dest_file: Option<&str>,
@@ -148,6 +176,7 @@ pub fn generic_path_checks(
} }
impl<'src_file, 'dest_file> PutRequest<'src_file, 'dest_file, 'static, 'static, 'static, 'static> { impl<'src_file, 'dest_file> PutRequest<'src_file, 'dest_file, 'static, 'static, 'static, 'static> {
/// New regular put request with no additional TLVs.
pub fn new_regular_request( pub fn new_regular_request(
dest_id: UnsignedByteField, dest_id: UnsignedByteField,
source_file: &'src_file str, source_file: &'src_file str,
@@ -171,12 +200,14 @@ impl<'src_file, 'dest_file> PutRequest<'src_file, 'dest_file, 'static, 'static,
} }
} }
/// TLV has invalid type.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TlvWithInvalidType(pub(crate) ()); pub struct TlvWithInvalidType(pub(crate) ());
impl<'msgs_to_user> PutRequest<'static, 'static, 'msgs_to_user, 'static, 'static, 'static> { impl<'msgs_to_user> PutRequest<'static, 'static, 'msgs_to_user, 'static, 'static, 'static> {
/// New put request which only contains messages to the user TLVs.
pub fn new_msgs_to_user_only( pub fn new_msgs_to_user_only(
dest_id: UnsignedByteField, dest_id: UnsignedByteField,
msgs_to_user: &'msgs_to_user [Tlv<'msgs_to_user>], msgs_to_user: &'msgs_to_user [Tlv<'msgs_to_user>],
@@ -212,6 +243,7 @@ impl<'msgs_to_user> PutRequest<'static, 'static, 'msgs_to_user, 'static, 'static
} }
} }
/// Generic check of the TLV list type.
pub fn generic_tlv_list_type_check<TlvProvider: GenericTlv>( pub fn generic_tlv_list_type_check<TlvProvider: GenericTlv>(
opt_tlvs: Option<&[TlvProvider]>, opt_tlvs: Option<&[TlvProvider]>,
tlv_type: TlvType, tlv_type: TlvType,
@@ -229,7 +261,9 @@ pub fn generic_tlv_list_type_check<TlvProvider: GenericTlv>(
true true
} }
/// Structure for all static put request fields.
pub struct StaticPutRequestFields { pub struct StaticPutRequestFields {
/// Destination entity ID.
pub destination_id: UnsignedByteField, pub destination_id: UnsignedByteField,
/// Static buffer to store source file path. /// Static buffer to store source file path.
pub source_file_buf: [u8; u8::MAX as usize], pub source_file_buf: [u8; u8::MAX as usize],
@@ -239,8 +273,11 @@ pub struct StaticPutRequestFields {
pub dest_file_buf: [u8; u8::MAX as usize], pub dest_file_buf: [u8; u8::MAX as usize],
/// Current destination path length. /// Current destination path length.
pub dest_file_len: usize, pub dest_file_len: usize,
/// Transmission mode.
pub trans_mode: Option<TransmissionMode>, pub trans_mode: Option<TransmissionMode>,
/// Closure requested flag for unacknowledged file transfer.
pub closure_requested: Option<bool>, pub closure_requested: Option<bool>,
/// Segmentation control.
pub seg_ctrl: Option<SegmentationControl>, pub seg_ctrl: Option<SegmentationControl>,
} }
@@ -260,6 +297,7 @@ impl Default for StaticPutRequestFields {
} }
impl StaticPutRequestFields { impl StaticPutRequestFields {
/// Clears and resets the fields.
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.destination_id = UnsignedByteField::new(0, 0); self.destination_id = UnsignedByteField::new(0, 0);
self.source_file_len = 0; self.source_file_len = 0;
@@ -271,9 +309,11 @@ impl StaticPutRequestFields {
} }
/// This is a put request cache structure which can be used to cache [ReadablePutRequest]s /// 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 /// without requiring run-time allocation.
/// to store TLVs or list of TLVs. ///
/// The user must specify the static buffer sizes used to store TLVs or list of TLVs.
pub struct StaticPutRequestCacher<const BUF_SIZE: usize> { pub struct StaticPutRequestCacher<const BUF_SIZE: usize> {
/// Static fields.
pub static_fields: StaticPutRequestFields, pub static_fields: StaticPutRequestFields,
opts_buf: [u8; BUF_SIZE], opts_buf: [u8; BUF_SIZE],
opts_len: usize, opts_len: usize,
@@ -286,6 +326,7 @@ impl<const BUF_SIZE: usize> Default for StaticPutRequestCacher<BUF_SIZE> {
} }
impl<const BUF_SIZE: usize> StaticPutRequestCacher<BUF_SIZE> { impl<const BUF_SIZE: usize> StaticPutRequestCacher<BUF_SIZE> {
/// Constructor.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
static_fields: StaticPutRequestFields::default(), static_fields: StaticPutRequestFields::default(),
@@ -294,6 +335,7 @@ impl<const BUF_SIZE: usize> StaticPutRequestCacher<BUF_SIZE> {
} }
} }
/// Set and update with using any generic [ReadablePutRequest].
pub fn set( pub fn set(
&mut self, &mut self,
put_request: &impl ReadablePutRequest, put_request: &impl ReadablePutRequest,
@@ -352,28 +394,34 @@ impl<const BUF_SIZE: usize> StaticPutRequestCacher<BUF_SIZE> {
Ok(()) Ok(())
} }
/// Does the put request have a source file?
pub fn has_source_file(&self) -> bool { pub fn has_source_file(&self) -> bool {
self.static_fields.source_file_len > 0 self.static_fields.source_file_len > 0
} }
/// Does the put request have a destination file?
pub fn has_dest_file(&self) -> bool { pub fn has_dest_file(&self) -> bool {
self.static_fields.dest_file_len > 0 self.static_fields.dest_file_len > 0
} }
/// Source file path.
pub fn source_file(&self) -> Result<&str, Utf8Error> { pub fn source_file(&self) -> Result<&str, Utf8Error> {
core::str::from_utf8( core::str::from_utf8(
&self.static_fields.source_file_buf[0..self.static_fields.source_file_len], &self.static_fields.source_file_buf[0..self.static_fields.source_file_len],
) )
} }
/// Destination file path.
pub fn dest_file(&self) -> Result<&str, Utf8Error> { 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]) core::str::from_utf8(&self.static_fields.dest_file_buf[0..self.static_fields.dest_file_len])
} }
/// Length of stored options TLVs.
pub fn opts_len(&self) -> usize { pub fn opts_len(&self) -> usize {
self.opts_len self.opts_len
} }
/// Raw options slice.
pub fn opts_slice(&self) -> &[u8] { pub fn opts_slice(&self) -> &[u8] {
&self.opts_buf[0..self.opts_len] &self.opts_buf[0..self.opts_len]
} }
@@ -388,6 +436,7 @@ impl<const BUF_SIZE: usize> StaticPutRequestCacher<BUF_SIZE> {
} }
} }
/// [alloc] support module.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod alloc_mod { pub mod alloc_mod {
@@ -400,19 +449,28 @@ pub mod alloc_mod {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PutRequestOwned { pub struct PutRequestOwned {
/// Destination entity ID.
pub destination_id: UnsignedByteField, pub destination_id: UnsignedByteField,
source_file: Option<alloc::string::String>, source_file: Option<alloc::string::String>,
dest_file: Option<alloc::string::String>, dest_file: Option<alloc::string::String>,
/// Transmission mode.
pub trans_mode: Option<TransmissionMode>, pub trans_mode: Option<TransmissionMode>,
/// Closure requested flag for unacknowledged file transfer.
pub closure_requested: Option<bool>, pub closure_requested: Option<bool>,
/// Segmentation control.
pub seg_ctrl: Option<SegmentationControl>, pub seg_ctrl: Option<SegmentationControl>,
/// Messages to user TLVs.
pub msgs_to_user: Option<alloc::vec::Vec<TlvOwned>>, pub msgs_to_user: Option<alloc::vec::Vec<TlvOwned>>,
/// Fault handler override TLVs.
pub fault_handler_overrides: Option<alloc::vec::Vec<TlvOwned>>, pub fault_handler_overrides: Option<alloc::vec::Vec<TlvOwned>>,
/// Flow label TLV.
pub flow_label: Option<TlvOwned>, pub flow_label: Option<TlvOwned>,
/// Filestore request TLVs.
pub fs_requests: Option<alloc::vec::Vec<TlvOwned>>, pub fs_requests: Option<alloc::vec::Vec<TlvOwned>>,
} }
impl PutRequestOwned { impl PutRequestOwned {
/// New regular put request with no additional TLVs.
pub fn new_regular_request( pub fn new_regular_request(
dest_id: UnsignedByteField, dest_id: UnsignedByteField,
source_file: &str, source_file: &str,
@@ -440,6 +498,7 @@ pub mod alloc_mod {
}) })
} }
/// New put request which only contains messages to the user TLVs.
pub fn new_msgs_to_user_only( pub fn new_msgs_to_user_only(
dest_id: UnsignedByteField, dest_id: UnsignedByteField,
msgs_to_user: &[MsgToUserTlv<'_>], msgs_to_user: &[MsgToUserTlv<'_>],

View File

@@ -36,6 +36,7 @@
//! 6. A finished PDU ACK packet will be generated to be sent to the remote CFDP entity. //! 6. A finished PDU ACK packet will be generated to be sent to the remote CFDP entity.
//! The [spacepackets::cfdp::pdu::finished::FinishedPduReader] can be used to inspect the //! The [spacepackets::cfdp::pdu::finished::FinishedPduReader] can be used to inspect the
//! generated PDU. //! generated PDU.
#![deny(missing_docs)]
use core::{ use core::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
ops::ControlFlow, ops::ControlFlow,
@@ -84,29 +85,42 @@ use super::{
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TransactionStep { pub enum TransactionStep {
/// Idle state, nothing to do.
Idle = 0, Idle = 0,
/// Transaction has started.
TransactionStart = 1, TransactionStart = 1,
/// Sending Metadata PDU.
SendingMetadata = 3, SendingMetadata = 3,
/// Sending file data PDUs.
SendingFileData = 4, SendingFileData = 4,
/// Re-transmitting missing packets in acknowledged mode /// Re-transmitting missing packets in acknowledged mode
Retransmitting = 5, Retransmitting = 5,
/// Sending an EOF PDU.
SendingEof = 6, SendingEof = 6,
/// Waiting for the acknowledgement of the EOF PDU.
WaitingForEofAck = 7, WaitingForEofAck = 7,
/// Waiting for the Finished PDU from the receiver.
WaitingForFinished = 8, WaitingForFinished = 8,
/// Performing the notice of completion.
NoticeOfCompletion = 10, NoticeOfCompletion = 10,
} }
/// Parameter related to the file transfer.
#[derive(Default, Debug, Copy, Clone)] #[derive(Default, Debug, Copy, Clone)]
pub struct FileParams { struct FileParams {
pub progress: u64, /// Progress of the file transfer.
pub segment_len: u64, progress: u64,
pub crc32: u32, /// Segment length for a single file segment which is limited by various factors.
pub metadata_only: bool, segment_len: u64,
pub file_size: u64, /// Metadata only flag.
pub empty_file: bool, metadata_only: bool,
/// File size.
file_size: u64,
/// Empty file flag.
empty_file: bool,
/// The checksum is cached to avoid expensive re-calculation when the EOF PDU needs to be /// The checksum is cached to avoid expensive re-calculation when the EOF PDU needs to be
/// re-sent. /// re-sent.
pub checksum_completed_file: Option<u32>, checksum_completed_file: Option<u32>,
} }
// Explicit choice to put all simple internal fields into Cells. // Explicit choice to put all simple internal fields into Cells.
@@ -137,6 +151,7 @@ impl StateHelper {
} }
} }
/// Parameters related to the Finished PDU.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct FinishedParams { pub struct FinishedParams {
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -144,68 +159,98 @@ pub struct FinishedParams {
file_status: FileStatus, file_status: FileStatus,
} }
/// Source handler errors.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum SourceError { pub enum SourceError {
/// Can not process the passed packet type.
#[error("can not process packet type {pdu_type:?} with directive type {directive_type:?}")] #[error("can not process packet type {pdu_type:?} with directive type {directive_type:?}")]
CantProcessPacketType { CantProcessPacketType {
/// PDU type.
pdu_type: PduType, pdu_type: PduType,
/// Directive type, if applicable.
directive_type: Option<FileDirectiveType>, directive_type: Option<FileDirectiveType>,
}, },
/// Unexpected PDU for current state.
#[error("unexpected PDU")] #[error("unexpected PDU")]
UnexpectedPdu { UnexpectedPdu {
/// PDU type.
pdu_type: PduType, pdu_type: PduType,
/// Directive type, if applicable.
directive_type: Option<FileDirectiveType>, directive_type: Option<FileDirectiveType>,
}, },
/// Put request is already active.
#[error("source handler is already busy with put request")] #[error("source handler is already busy with put request")]
PutRequestAlreadyActive, PutRequestAlreadyActive,
/// Error during the caching process of a put request.
#[error("error caching put request")] #[error("error caching put request")]
PutRequestCaching(ByteConversionError), PutRequestCaching(ByteConversionError),
/// Generic filestore error.
#[error("filestore error: {0}")] #[error("filestore error: {0}")]
FilestoreError(#[from] FilestoreError), FilestoreError(#[from] FilestoreError),
/// Source file name is not valid UTF-8.
#[error("source file does not have valid UTF8 format: {0}")] #[error("source file does not have valid UTF8 format: {0}")]
SourceFileNotValidUtf8(Utf8Error), SourceFileNotValidUtf8(Utf8Error),
/// Destination file name is not valid UTF-8.
#[error("destination file does not have valid UTF8 format: {0}")] #[error("destination file does not have valid UTF8 format: {0}")]
DestFileNotValidUtf8(Utf8Error), DestFileNotValidUtf8(Utf8Error),
/// Invalid NAK PDU error.
#[error("invalid NAK PDU received")] #[error("invalid NAK PDU received")]
InvalidNakPdu, InvalidNakPdu,
/// PDU creation error.
#[error("error related to PDU creation: {0}")] #[error("error related to PDU creation: {0}")]
Pdu(#[from] PduError), Pdu(#[from] PduError),
/// Feature not implemented error.
#[error("cfdp feature not implemented")] #[error("cfdp feature not implemented")]
NotImplemented, NotImplemented,
/// Generic send error.
#[error("issue sending PDU: {0}")] #[error("issue sending PDU: {0}")]
SendError(#[from] GenericSendError), SendError(#[from] GenericSendError),
} }
/// Put request errors.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum PutRequestError { pub enum PutRequestError {
/// Storage error.
#[error("error caching put request: {0}")] #[error("error caching put request: {0}")]
Storage(#[from] ByteConversionError), Storage(#[from] ByteConversionError),
/// Already busy with a put request.
#[error("already busy with put request")] #[error("already busy with put request")]
AlreadyBusy, AlreadyBusy,
/// No remote entity configuration was found for destination ID.
#[error("no remote entity configuration found for {0:?}")] #[error("no remote entity configuration found for {0:?}")]
NoRemoteCfgFound(UnsignedByteField), NoRemoteCfgFound(UnsignedByteField),
/// Source file name is not valid UTF-8.
#[error("source file does not have valid UTF8 format: {0}")] #[error("source file does not have valid UTF8 format: {0}")]
SourceFileNotValidUtf8(#[from] Utf8Error), SourceFileNotValidUtf8(#[from] Utf8Error),
/// File does not exist.
#[error("source file does not exist")] #[error("source file does not exist")]
FileDoesNotExist, FileDoesNotExist,
/// Generic filestore error.
#[error("filestore error: {0}")] #[error("filestore error: {0}")]
FilestoreError(#[from] FilestoreError), FilestoreError(#[from] FilestoreError),
} }
/// Anomaly tracker for the source handler.
///
/// Anomalies are unexpected events which are not severe errors.
#[derive(Debug, Default, Clone, Copy)] #[derive(Debug, Default, Clone, Copy)]
pub struct AnomalyTracker { pub struct AnomalyTracker {
invalid_ack_directive_code: u8, invalid_ack_directive_code: u8,
} }
/// Finite state-machine context.
#[derive(Debug, Default, PartialEq, Eq)] #[derive(Debug, Default, PartialEq, Eq)]
pub enum FsmContext { enum FsmContext {
/// None
#[default] #[default]
None, None,
/// The FSM should be reset when possible.
ResetWhenPossible, ResetWhenPossible,
} }
/// Transaction parameters.
#[derive(Debug)] #[derive(Debug)]
pub struct TransactionParams<CountdownInstance: Countdown> { struct TransactionParams<CountdownInstance: Countdown> {
transaction_id: Option<TransactionId>, transaction_id: Option<TransactionId>,
remote_cfg: Option<RemoteEntityConfig>, remote_cfg: Option<RemoteEntityConfig>,
transmission_mode: Option<super::TransmissionMode>, transmission_mode: Option<super::TransmissionMode>,
@@ -399,6 +444,9 @@ impl<
} }
} }
/// Transcation ID for the currently active transaction.
///
/// Returns [None] if no transaction is active.
#[inline] #[inline]
pub fn transaction_id(&self) -> Option<TransactionId> { pub fn transaction_id(&self) -> Option<TransactionId> {
self.transaction_params.transaction_id self.transaction_params.transaction_id
@@ -417,11 +465,13 @@ impl<
self.state_helper.step.get() self.state_helper.step.get()
} }
/// Current state of the source handler.
#[inline] #[inline]
pub fn state(&self) -> State { pub fn state(&self) -> State {
self.state_helper.state.get() self.state_helper.state.get()
} }
/// Local configuration of the source handler.
#[inline] #[inline]
pub fn local_cfg(&self) -> &LocalEntityConfig<UserFaultHookInstance> { pub fn local_cfg(&self) -> &LocalEntityConfig<UserFaultHookInstance> {
&self.local_cfg &self.local_cfg
@@ -442,12 +492,9 @@ impl<
return Err(PutRequestError::AlreadyBusy); return Err(PutRequestError::AlreadyBusy);
} }
self.put_request_cacher.set(put_request)?; self.put_request_cacher.set(put_request)?;
let remote_cfg = self.remote_cfg_table.get( let remote_cfg = self
self.put_request_cacher .remote_cfg_table
.static_fields .get(self.put_request_cacher.static_fields.destination_id.value());
.destination_id
.value_const(),
);
if remote_cfg.is_none() { if remote_cfg.is_none() {
return Err(PutRequestError::NoRemoteCfgFound( return Err(PutRequestError::NoRemoteCfgFound(
self.put_request_cacher.static_fields.destination_id, self.put_request_cacher.static_fields.destination_id,
@@ -495,7 +542,7 @@ impl<
); );
let create_id = |cached_id: &UnsignedByteField| { let create_id = |cached_id: &UnsignedByteField| {
if larger_entity_width != cached_id.size() { if larger_entity_width != cached_id.size() {
UnsignedByteField::new(larger_entity_width, cached_id.value_const()) UnsignedByteField::new(larger_entity_width, cached_id.value())
} else { } else {
*cached_id *cached_id
} }
@@ -558,22 +605,20 @@ impl<
.file_directive_type() .file_directive_type()
.expect("PDU directive type unexpectedly not set") .expect("PDU directive type unexpectedly not set")
{ {
FileDirectiveType::FinishedPdu => { FileDirectiveType::Finished => {
let finished_pdu = FinishedPduReader::new(packet_to_insert.raw_pdu())?; let finished_pdu = FinishedPduReader::new(packet_to_insert.raw_pdu())?;
self.handle_finished_pdu(&finished_pdu)? self.handle_finished_pdu(&finished_pdu)?
} }
FileDirectiveType::NakPdu => { FileDirectiveType::Nak => {
let nak_pdu = NakPduReader::new(packet_to_insert.raw_pdu())?; let nak_pdu = NakPduReader::new(packet_to_insert.raw_pdu())?;
sent_packets += self.handle_nak_pdu(&nak_pdu)?; sent_packets += self.handle_nak_pdu(&nak_pdu)?;
} }
FileDirectiveType::KeepAlivePdu => self.handle_keep_alive_pdu(), FileDirectiveType::KeepAlive => self.handle_keep_alive_pdu(),
FileDirectiveType::AckPdu => { FileDirectiveType::Ack => {
let ack_pdu = AckPdu::from_bytes(packet_to_insert.raw_pdu())?; let ack_pdu = AckPdu::from_bytes(packet_to_insert.raw_pdu())?;
self.handle_ack_pdu(&ack_pdu)? self.handle_ack_pdu(&ack_pdu)?
} }
FileDirectiveType::EofPdu FileDirectiveType::Eof | FileDirectiveType::Prompt | FileDirectiveType::Metadata => {
| FileDirectiveType::PromptPdu
| FileDirectiveType::MetadataPdu => {
return Err(SourceError::CantProcessPacketType { return Err(SourceError::CantProcessPacketType {
pdu_type: packet_to_insert.pdu_type(), pdu_type: packet_to_insert.pdu_type(),
directive_type: packet_to_insert.file_directive_type(), directive_type: packet_to_insert.file_directive_type(),
@@ -898,7 +943,7 @@ impl<
) -> Result<(), SourceError> { ) -> Result<(), SourceError> {
let ack_pdu = AckPdu::new( let ack_pdu = AckPdu::new(
PduHeader::new_for_file_directive(self.transaction_params.pdu_conf, 0), PduHeader::new_for_file_directive(self.transaction_params.pdu_conf, 0),
FileDirectiveType::FinishedPdu, FileDirectiveType::Finished,
condition_code, condition_code,
transaction_status, transaction_status,
) )
@@ -1093,7 +1138,7 @@ impl<
if self.step() != TransactionStep::WaitingForFinished { if self.step() != TransactionStep::WaitingForFinished {
return Err(SourceError::UnexpectedPdu { return Err(SourceError::UnexpectedPdu {
pdu_type: PduType::FileDirective, pdu_type: PduType::FileDirective,
directive_type: Some(FileDirectiveType::FinishedPdu), directive_type: Some(FileDirectiveType::Finished),
}); });
} }
// Unwrapping should be fine here, the transfer state is valid when we are not in IDLE // Unwrapping should be fine here, the transfer state is valid when we are not in IDLE
@@ -1122,10 +1167,10 @@ impl<
// Drop the packet, wrong state to handle it.. // Drop the packet, wrong state to handle it..
return Err(SourceError::UnexpectedPdu { return Err(SourceError::UnexpectedPdu {
pdu_type: PduType::FileDirective, pdu_type: PduType::FileDirective,
directive_type: Some(FileDirectiveType::AckPdu), directive_type: Some(FileDirectiveType::Ack),
}); });
} }
if ack_pdu.directive_code_of_acked_pdu() == FileDirectiveType::EofPdu { if ack_pdu.directive_code_of_acked_pdu() == FileDirectiveType::Eof {
self.set_step(TransactionStep::WaitingForFinished); self.set_step(TransactionStep::WaitingForFinished);
} else { } else {
self.anomalies.invalid_ack_directive_code = self.anomalies.invalid_ack_directive_code =
@@ -1134,6 +1179,9 @@ impl<
Ok(()) Ok(())
} }
/// Manually trigger a notice of cancellation.
///
/// This cancels any currently active transaction.
pub fn notice_of_cancellation( pub fn notice_of_cancellation(
&mut self, &mut self,
user: &mut impl CfdpUser, user: &mut impl CfdpUser,
@@ -1179,12 +1227,18 @@ impl<
} }
} }
/// Manually trigger a notice of suspension.
///
/// Please note that proper susopension handling is not implemented yet.
pub fn notice_of_suspension(&mut self) { pub fn notice_of_suspension(&mut self) {
self.notice_of_suspension_internal(); self.notice_of_suspension_internal();
} }
fn notice_of_suspension_internal(&self) {} fn notice_of_suspension_internal(&self) {
// TODO: Implement.
}
/// Manually abandon the currently active transaction.
pub fn abandon_transaction(&mut self) { pub fn abandon_transaction(&mut self) {
// I guess an abandoned transaction just stops whatever the handler is doing and resets // I guess an abandoned transaction just stops whatever the handler is doing and resets
// it to a clean state.. The implementation for this is quite easy. // it to a clean state.. The implementation for this is quite easy.
@@ -1550,7 +1604,7 @@ mod tests {
assert_eq!(next_pdu.pdu_type, PduType::FileDirective); assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(
next_pdu.file_directive_type, next_pdu.file_directive_type,
Some(FileDirectiveType::MetadataPdu) Some(FileDirectiveType::Metadata)
); );
let metadata_pdu = let metadata_pdu =
MetadataPduReader::new(&next_pdu.raw_pdu).expect("invalid metadata PDU format"); MetadataPduReader::new(&next_pdu.raw_pdu).expect("invalid metadata PDU format");
@@ -1599,7 +1653,7 @@ mod tests {
) { ) {
let ack_pdu = AckPdu::new( let ack_pdu = AckPdu::new(
transaction_info.pdu_header, transaction_info.pdu_header,
FileDirectiveType::EofPdu, FileDirectiveType::Eof,
ConditionCode::NoError, ConditionCode::NoError,
TransactionStatus::Active, TransactionStatus::Active,
) )
@@ -1616,16 +1670,13 @@ mod tests {
let next_pdu = self.get_next_sent_pdu().unwrap(); let next_pdu = self.get_next_sent_pdu().unwrap();
assert!(self.pdu_queue_empty()); assert!(self.pdu_queue_empty());
assert_eq!(next_pdu.pdu_type, PduType::FileDirective); assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(next_pdu.file_directive_type, Some(FileDirectiveType::Ack));
next_pdu.file_directive_type,
Some(FileDirectiveType::AckPdu)
);
let ack_pdu = AckPdu::from_bytes(&next_pdu.raw_pdu).unwrap(); let ack_pdu = AckPdu::from_bytes(&next_pdu.raw_pdu).unwrap();
self.common_pdu_check_for_file_transfer(ack_pdu.pdu_header(), CrcFlag::NoCrc); self.common_pdu_check_for_file_transfer(ack_pdu.pdu_header(), CrcFlag::NoCrc);
assert_eq!(ack_pdu.condition_code(), ConditionCode::NoError); assert_eq!(ack_pdu.condition_code(), ConditionCode::NoError);
assert_eq!( assert_eq!(
ack_pdu.directive_code_of_acked_pdu(), ack_pdu.directive_code_of_acked_pdu(),
FileDirectiveType::FinishedPdu FileDirectiveType::Finished
); );
assert_eq!(ack_pdu.transaction_status(), TransactionStatus::Active); assert_eq!(ack_pdu.transaction_status(), TransactionStatus::Active);
} }
@@ -1639,10 +1690,7 @@ mod tests {
) { ) {
let next_pdu = self.get_next_sent_pdu().unwrap(); let next_pdu = self.get_next_sent_pdu().unwrap();
assert_eq!(next_pdu.pdu_type, PduType::FileDirective); assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(next_pdu.file_directive_type, Some(FileDirectiveType::Eof));
next_pdu.file_directive_type,
Some(FileDirectiveType::EofPdu)
);
let eof_pdu = EofPdu::from_bytes(&next_pdu.raw_pdu).expect("invalid EOF PDU format"); let eof_pdu = EofPdu::from_bytes(&next_pdu.raw_pdu).expect("invalid EOF PDU format");
self.common_pdu_check_for_file_transfer(eof_pdu.pdu_header(), CrcFlag::NoCrc); self.common_pdu_check_for_file_transfer(eof_pdu.pdu_header(), CrcFlag::NoCrc);
assert_eq!(eof_pdu.condition_code(), eof_params.condition_code); assert_eq!(eof_pdu.condition_code(), eof_params.condition_code);
@@ -1653,7 +1701,7 @@ mod tests {
.pdu_header() .pdu_header()
.common_pdu_conf() .common_pdu_conf()
.transaction_seq_num .transaction_seq_num
.value_const(), .value(),
0 0
); );
if self.transmission_mode == TransmissionMode::Unacknowledged { if self.transmission_mode == TransmissionMode::Unacknowledged {
@@ -2031,10 +2079,7 @@ mod tests {
let eof_pdu = tb let eof_pdu = tb
.get_next_sent_pdu() .get_next_sent_pdu()
.expect("no EOF PDU generated like expected"); .expect("no EOF PDU generated like expected");
assert_eq!( assert_eq!(eof_pdu.file_directive_type.unwrap(), FileDirectiveType::Eof);
eof_pdu.file_directive_type.unwrap(),
FileDirectiveType::EofPdu
);
let eof_pdu = EofPdu::from_bytes(&eof_pdu.raw_pdu).unwrap(); let eof_pdu = EofPdu::from_bytes(&eof_pdu.raw_pdu).unwrap();
assert_eq!( assert_eq!(
eof_pdu.condition_code(), eof_pdu.condition_code(),
@@ -2096,7 +2141,7 @@ mod tests {
assert_eq!(next_packet.pdu_type, PduType::FileDirective); assert_eq!(next_packet.pdu_type, PduType::FileDirective);
assert_eq!( assert_eq!(
next_packet.file_directive_type.unwrap(), next_packet.file_directive_type.unwrap(),
FileDirectiveType::EofPdu FileDirectiveType::Eof
); );
// As specified in 4.11.2.2 of the standard, the file size will be the progress of the // As specified in 4.11.2.2 of the standard, the file size will be the progress of the
// file copy operation so far, and the checksum is calculated for that progress. // file copy operation so far, and the checksum is calculated for that progress.

View File

@@ -1,7 +1,11 @@
//! # Time support module.
#![deny(missing_docs)]
use core::fmt::Debug; use core::fmt::Debug;
/// Generic abstraction for a check/countdown timer. Should also be cheap to copy and clone. /// Generic abstraction for a check/countdown timer. Should also be cheap to copy and clone.
pub trait Countdown: Debug { pub trait Countdown: Debug {
/// The countdown has expired.
fn has_expired(&self) -> bool; fn has_expired(&self) -> bool;
/// Reset the countdown to its initial state.
fn reset(&mut self); fn reset(&mut self);
} }

View File

@@ -1,3 +1,5 @@
//! # User support and hooks module
#![deny(missing_docs)]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use spacepackets::cfdp::tlv::WritableTlv; use spacepackets::cfdp::tlv::WritableTlv;
use spacepackets::{ use spacepackets::{
@@ -14,34 +16,53 @@ use spacepackets::{
use super::TransactionId; use super::TransactionId;
/// Parameters related to a finished transfer.
#[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(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TransactionFinishedParams { pub struct TransactionFinishedParams {
/// ID of the transfer.
pub id: TransactionId, pub id: TransactionId,
/// Condition code.
pub condition_code: ConditionCode, pub condition_code: ConditionCode,
/// Delivery code.
pub delivery_code: DeliveryCode, pub delivery_code: DeliveryCode,
/// File status.
pub file_status: FileStatus, pub file_status: FileStatus,
} }
/// Parameters related to the reception of a metadata PDU, which might start file reception.
#[derive(Debug)] #[derive(Debug)]
pub struct MetadataReceivedParams<'src_file, 'dest_file, 'msgs_to_user> { pub struct MetadataReceivedParams<'src_file, 'dest_file, 'msgs_to_user> {
/// ID of the transfer.
pub id: TransactionId, pub id: TransactionId,
/// Source entity ID.
pub source_id: UnsignedByteField, pub source_id: UnsignedByteField,
/// File size.
pub file_size: u64, pub file_size: u64,
/// Source file name.
pub src_file_name: &'src_file str, pub src_file_name: &'src_file str,
/// Destination file name.
pub dest_file_name: &'dest_file str, pub dest_file_name: &'dest_file str,
/// Messages to user TLVs.
pub msgs_to_user: &'msgs_to_user [MsgToUserTlv<'msgs_to_user>], pub msgs_to_user: &'msgs_to_user [MsgToUserTlv<'msgs_to_user>],
} }
/// Owned variant of [MetadataReceivedParams].
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct OwnedMetadataRecvdParams { pub struct OwnedMetadataRecvdParams {
/// ID of the transfer.
pub id: TransactionId, pub id: TransactionId,
/// Source entity ID.
pub source_id: UnsignedByteField, pub source_id: UnsignedByteField,
/// File size.
pub file_size: u64, pub file_size: u64,
/// Source file name.
pub src_file_name: alloc::string::String, pub src_file_name: alloc::string::String,
/// Destination file name.
pub dest_file_name: alloc::string::String, pub dest_file_name: alloc::string::String,
/// Messages to user TLVs.
pub msgs_to_user: alloc::vec::Vec<alloc::vec::Vec<u8>>, pub msgs_to_user: alloc::vec::Vec<alloc::vec::Vec<u8>>,
} }
@@ -66,35 +87,63 @@ impl From<&MetadataReceivedParams<'_, '_, '_>> for OwnedMetadataRecvdParams {
} }
} }
/// Parameters related to the reception of a file segment PDU.
#[derive(Debug)] #[derive(Debug)]
pub struct FileSegmentRecvdParams<'seg_meta> { pub struct FileSegmentRecvdParams<'seg_meta> {
/// ID of the transfer.
pub id: TransactionId, pub id: TransactionId,
/// Offset of the segment.
pub offset: u64, pub offset: u64,
/// Length of the segment.
pub length: usize, pub length: usize,
/// Segment metadata, if present.
pub segment_metadata: Option<&'seg_meta SegmentMetadata<'seg_meta>>, pub segment_metadata: Option<&'seg_meta SegmentMetadata<'seg_meta>>,
} }
/// Generic CFDP user as specified in the CFDP standard.
///
/// This trait declares all indications which are possible.
pub trait CfdpUser { pub trait CfdpUser {
/// Indication that a new transaction has started.
fn transaction_indication(&mut self, id: &TransactionId); fn transaction_indication(&mut self, id: &TransactionId);
/// Indication that an EOF PDU has been sent.
fn eof_sent_indication(&mut self, id: &TransactionId); fn eof_sent_indication(&mut self, id: &TransactionId);
/// Indication that a transaction has finished.
fn transaction_finished_indication(&mut self, finished_params: &TransactionFinishedParams); fn transaction_finished_indication(&mut self, finished_params: &TransactionFinishedParams);
/// Indication that metadata has been received.
fn metadata_recvd_indication(&mut self, md_recvd_params: &MetadataReceivedParams); fn metadata_recvd_indication(&mut self, md_recvd_params: &MetadataReceivedParams);
/// Indication that a file segment has been received.
fn file_segment_recvd_indication(&mut self, segment_recvd_params: &FileSegmentRecvdParams); fn file_segment_recvd_indication(&mut self, segment_recvd_params: &FileSegmentRecvdParams);
// TODO: The standard does not strictly specify how the report information looks.. // TODO: The standard does not strictly specify how the report information looks..
/// Report information indication.
fn report_indication(&mut self, id: &TransactionId); fn report_indication(&mut self, id: &TransactionId);
/// Indication that a transfer has been suspended.
fn suspended_indication(&mut self, id: &TransactionId, condition_code: ConditionCode); fn suspended_indication(&mut self, id: &TransactionId, condition_code: ConditionCode);
/// Indication that a transfer has been resumed.
fn resumed_indication(&mut self, id: &TransactionId, progress: u64); fn resumed_indication(&mut self, id: &TransactionId, progress: u64);
/// Indication that a fault has occured.
fn fault_indication( fn fault_indication(
&mut self, &mut self,
id: &TransactionId, id: &TransactionId,
condition_code: ConditionCode, condition_code: ConditionCode,
progress: u64, progress: u64,
); );
/// Indication that a transfer has been abandoned.
fn abandoned_indication( fn abandoned_indication(
&mut self, &mut self,
id: &TransactionId, id: &TransactionId,
condition_code: ConditionCode, condition_code: ConditionCode,
progress: u64, progress: u64,
); );
/// Indication that an EOF PDU has been received.
fn eof_recvd_indication(&mut self, id: &TransactionId); fn eof_recvd_indication(&mut self, id: &TransactionId);
} }