Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 748142b0a4 | |||
|
|
52b21db579 | ||
| f4d66bb482 | |||
|
|
98096fdea8 | ||
| 9494e29781 | |||
|
|
0d876c5525
|
||
| 44c31c935a |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -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.87
|
||||
- run: cargo check --release
|
||||
|
||||
cross-check:
|
||||
@@ -63,7 +63,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 --features "serde, defmt" --no-deps
|
||||
|
||||
clippy:
|
||||
name: Clippy
|
||||
|
||||
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [unreleased]
|
||||
|
||||
- Bumped `spacepackets` to v0.17
|
||||
|
||||
# [v0.3.0] 2025-09-25
|
||||
|
||||
- Bumped `spacepackets` to v0.16
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "cfdp-rs"
|
||||
version = "0.3.0"
|
||||
edition = "2024"
|
||||
rust-version = "1.86.0"
|
||||
rust-version = "1.87"
|
||||
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,7 +20,7 @@ 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.17", default-features = false }
|
||||
thiserror = { version = "2", default-features = false }
|
||||
heapless = "0.9"
|
||||
serde = { version = "1", optional = true }
|
||||
|
||||
@@ -28,7 +28,7 @@ use spacepackets::{
|
||||
ChecksumType, ConditionCode, TransmissionMode,
|
||||
pdu::{PduError, file_data::FileDataPdu, metadata::MetadataPduReader},
|
||||
},
|
||||
util::{UnsignedByteFieldU16, UnsignedEnum},
|
||||
util::UnsignedByteFieldU16,
|
||||
};
|
||||
|
||||
static KILL_APP: AtomicBool = AtomicBool::new(false);
|
||||
@@ -258,17 +258,17 @@ impl UdpServer {
|
||||
fn pdu_printout(pdu: &PduOwnedWithInfo) {
|
||||
match pdu.pdu_type() {
|
||||
spacepackets::cfdp::PduType::FileDirective => match pdu.file_directive_type().unwrap() {
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::EofPdu => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::FinishedPdu => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::AckPdu => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::MetadataPdu => {
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::Eof => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::Finished => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::Ack => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::Metadata => {
|
||||
let meta_pdu =
|
||||
MetadataPduReader::new(pdu.raw_pdu()).expect("creating metadata pdu failed");
|
||||
debug!("Metadata PDU: {:?}", meta_pdu)
|
||||
}
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::NakPdu => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::PromptPdu => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::KeepAlivePdu => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::Nak => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::Prompt => (),
|
||||
spacepackets::cfdp::pdu::FileDirectiveType::KeepAlive => (),
|
||||
},
|
||||
spacepackets::cfdp::PduType::FileData => {
|
||||
let fd_pdu =
|
||||
|
||||
@@ -1 +1 @@
|
||||
cfdp-py @ git+https://github.com/us-irs/cfdp-py.git@main
|
||||
cfdp-py == 0.6.0
|
||||
|
||||
11
justfile
11
justfile
@@ -1,9 +1,12 @@
|
||||
all: check build embedded clippy fmt docs test coverage
|
||||
all: check build embedded clippy check-fmt docs test coverage
|
||||
|
||||
clippy:
|
||||
cargo clippy -- -D warnings
|
||||
|
||||
fmt:
|
||||
cargo fmt --all
|
||||
|
||||
check-fmt:
|
||||
cargo fmt --all -- --check
|
||||
|
||||
check:
|
||||
@@ -20,12 +23,10 @@ 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"
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --features "serde, defmt" --no-deps
|
||||
|
||||
docs-html:
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --features "serde, defmt" --open
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --features "serde, defmt" --open
|
||||
|
||||
coverage:
|
||||
cargo llvm-cov nextest
|
||||
|
||||
82
src/dest.rs
82
src/dest.rs
@@ -62,7 +62,7 @@ use spacepackets::{
|
||||
},
|
||||
tlv::{EntityIdTlv, GenericTlv, ReadableTlv, TlvType, msg_to_user::MsgToUserTlv},
|
||||
},
|
||||
util::{UnsignedByteField, UnsignedEnum},
|
||||
util::UnsignedByteField,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -608,33 +608,31 @@ impl<
|
||||
) -> Result<u32, DestError> {
|
||||
let mut sent_packets = 0;
|
||||
match pdu_directive {
|
||||
FileDirectiveType::EofPdu => {
|
||||
FileDirectiveType::Eof => {
|
||||
let eof_pdu = EofPdu::from_bytes(raw_packet)?;
|
||||
sent_packets += self.handle_eof_pdu(cfdp_user, eof_pdu)?
|
||||
}
|
||||
FileDirectiveType::FinishedPdu
|
||||
| FileDirectiveType::NakPdu
|
||||
| FileDirectiveType::KeepAlivePdu => {
|
||||
FileDirectiveType::Finished | FileDirectiveType::Nak | FileDirectiveType::KeepAlive => {
|
||||
return Err(DestError::CantProcessPacketType {
|
||||
pdu_type: PduType::FileDirective,
|
||||
directive_type: Some(pdu_directive),
|
||||
});
|
||||
}
|
||||
FileDirectiveType::AckPdu => {
|
||||
FileDirectiveType::Ack => {
|
||||
let ack_pdu = AckPdu::from_bytes(raw_packet)?;
|
||||
self.handle_ack_pdu(ack_pdu)?;
|
||||
}
|
||||
FileDirectiveType::MetadataPdu => {
|
||||
FileDirectiveType::Metadata => {
|
||||
let metadata_pdu = MetadataPduReader::from_bytes(raw_packet)?;
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
.anomaly_tracker
|
||||
.increment_invalid_ack_directive_code();
|
||||
@@ -827,7 +825,7 @@ impl<
|
||||
.finish(0, self.transaction_params.progress)
|
||||
.map_err(PduError::from)?;
|
||||
self.pdu_sender.send_file_directive_pdu(
|
||||
FileDirectiveType::NakPdu,
|
||||
FileDirectiveType::Nak,
|
||||
&self.pdu_and_cksum_buffer.borrow()[0..written_size],
|
||||
)?;
|
||||
packets_sent += 1;
|
||||
@@ -954,7 +952,7 @@ impl<
|
||||
};
|
||||
let written_len = nak_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?;
|
||||
self.pdu_sender.send_file_directive_pdu(
|
||||
FileDirectiveType::NakPdu,
|
||||
FileDirectiveType::Nak,
|
||||
&self.pdu_and_cksum_buffer.borrow()[0..written_len],
|
||||
)?;
|
||||
sent_packets += 1;
|
||||
@@ -998,7 +996,7 @@ impl<
|
||||
if self.transmission_mode().unwrap() == TransmissionMode::Unacknowledged {
|
||||
return Err(DestError::WrongStepForPdu {
|
||||
pdu_type: PduType::FileDirective,
|
||||
file_directive_type: Some(FileDirectiveType::EofPdu),
|
||||
file_directive_type: Some(FileDirectiveType::Eof),
|
||||
step: self.step(),
|
||||
});
|
||||
}
|
||||
@@ -1116,7 +1114,7 @@ impl<
|
||||
);
|
||||
let written_len = ack_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?;
|
||||
self.pdu_sender.send_file_directive_pdu(
|
||||
FileDirectiveType::AckPdu,
|
||||
FileDirectiveType::Ack,
|
||||
&self.pdu_and_cksum_buffer.borrow()[0..written_len],
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -1280,7 +1278,7 @@ impl<
|
||||
.finish(0, self.transaction_params.file_size)
|
||||
.map_err(PduError::from)?;
|
||||
self.pdu_sender.send_file_directive_pdu(
|
||||
FileDirectiveType::NakPdu,
|
||||
FileDirectiveType::Nak,
|
||||
&self.pdu_and_cksum_buffer.borrow()[0..written_len],
|
||||
)?;
|
||||
sent_packets += 1;
|
||||
@@ -1336,7 +1334,7 @@ impl<
|
||||
.finish(current_start_of_scope, current_end_of_scope)
|
||||
.map_err(PduError::from)?;
|
||||
self.pdu_sender.send_file_directive_pdu(
|
||||
FileDirectiveType::NakPdu,
|
||||
FileDirectiveType::Nak,
|
||||
&self.pdu_and_cksum_buffer.borrow()[..written_len],
|
||||
)?;
|
||||
sent_packets += 1;
|
||||
@@ -1382,7 +1380,7 @@ impl<
|
||||
.finish(current_start_of_scope, current_end_of_scope)
|
||||
.map_err(PduError::from)?;
|
||||
self.pdu_sender.send_file_directive_pdu(
|
||||
FileDirectiveType::NakPdu,
|
||||
FileDirectiveType::Nak,
|
||||
&self.pdu_and_cksum_buffer.borrow()[..written_len],
|
||||
)?;
|
||||
sent_packets += 1;
|
||||
@@ -1898,7 +1896,7 @@ impl<
|
||||
};
|
||||
finished_pdu.write_to_bytes(self.pdu_and_cksum_buffer.get_mut())?;
|
||||
self.pdu_sender.send_file_directive_pdu(
|
||||
FileDirectiveType::FinishedPdu,
|
||||
FileDirectiveType::Finished,
|
||||
&self.pdu_and_cksum_buffer.borrow()[0..finished_pdu.len_written()],
|
||||
)?;
|
||||
Ok(1)
|
||||
@@ -1926,7 +1924,7 @@ mod tests {
|
||||
nak::NakPduReader,
|
||||
},
|
||||
},
|
||||
util::UnsignedByteFieldU8,
|
||||
util::{UnsignedByteFieldU8, UnsignedEnum},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -2053,7 +2051,7 @@ mod tests {
|
||||
fn remote_cfg_mut(&mut self) -> &mut RemoteEntityConfig {
|
||||
self.handler
|
||||
.remote_cfg_table
|
||||
.get_mut(LOCAL_ID.value())
|
||||
.get_mut(LOCAL_ID.value_raw())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -2214,13 +2212,13 @@ mod tests {
|
||||
assert!(!self.pdu_queue_empty());
|
||||
let pdu = self.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(ack_pdu.condition_code(), cond_code);
|
||||
assert_eq!(ack_pdu.transaction_status(), TransactionStatus::Active);
|
||||
assert_eq!(
|
||||
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.file_directive_type.unwrap(),
|
||||
FileDirectiveType::FinishedPdu
|
||||
FileDirectiveType::Finished
|
||||
);
|
||||
let finished_pdu = FinishedPduReader::from_bytes(&pdu.raw_pdu).unwrap();
|
||||
assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Complete);
|
||||
@@ -2248,7 +2246,7 @@ mod tests {
|
||||
assert_eq!(pdu.pdu_type, PduType::FileDirective);
|
||||
assert_eq!(
|
||||
pdu.file_directive_type.unwrap(),
|
||||
FileDirectiveType::FinishedPdu
|
||||
FileDirectiveType::Finished
|
||||
);
|
||||
let finished_pdu = FinishedPduReader::from_bytes(&pdu.raw_pdu).unwrap();
|
||||
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.file_directive_type,
|
||||
Some(FileDirectiveType::FinishedPdu)
|
||||
Some(FileDirectiveType::Finished)
|
||||
);
|
||||
let finished_pdu = FinishedPduReader::from_bytes(&sent_pdu.raw_pdu).unwrap();
|
||||
assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
|
||||
@@ -2826,7 +2824,7 @@ mod tests {
|
||||
} = error
|
||||
{
|
||||
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.file_directive_type,
|
||||
Some(FileDirectiveType::FinishedPdu)
|
||||
Some(FileDirectiveType::Finished)
|
||||
);
|
||||
let finished_pdu = FinishedPduReader::from_bytes(&sent_pdu.raw_pdu).unwrap();
|
||||
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.file_directive_type.unwrap(),
|
||||
FileDirectiveType::FinishedPdu
|
||||
FileDirectiveType::Finished
|
||||
);
|
||||
let finished_pdu = FinishedPduReader::from_bytes(&finished_pdu.raw_pdu).unwrap();
|
||||
assert_eq!(
|
||||
@@ -3197,7 +3195,7 @@ mod tests {
|
||||
assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
|
||||
assert_eq!(
|
||||
next_pdu.file_directive_type.unwrap(),
|
||||
FileDirectiveType::FinishedPdu
|
||||
FileDirectiveType::Finished
|
||||
);
|
||||
let finished_pdu =
|
||||
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.file_directive_type.unwrap(),
|
||||
FileDirectiveType::FinishedPdu
|
||||
FileDirectiveType::Finished
|
||||
);
|
||||
let finished_pdu =
|
||||
FinishedPduReader::new(&next_pdu.raw_pdu).expect("finished pdu read failed");
|
||||
@@ -3284,7 +3282,7 @@ mod tests {
|
||||
let remote_cfg_mut = tb
|
||||
.handler
|
||||
.remote_cfg_table
|
||||
.get_mut(LOCAL_ID.value())
|
||||
.get_mut(LOCAL_ID.value_raw())
|
||||
.unwrap();
|
||||
remote_cfg_mut.disposition_on_cancellation = true;
|
||||
let mut user = tb.test_user_from_cached_paths(0);
|
||||
@@ -3322,7 +3320,7 @@ mod tests {
|
||||
let remote_cfg_mut = tb
|
||||
.handler
|
||||
.remote_cfg_table
|
||||
.get_mut(LOCAL_ID.value())
|
||||
.get_mut(LOCAL_ID.value_raw())
|
||||
.unwrap();
|
||||
remote_cfg_mut.disposition_on_cancellation = true;
|
||||
let mut user = tb.test_user_from_cached_paths(file_size);
|
||||
@@ -3378,7 +3376,7 @@ mod tests {
|
||||
assert_eq!(tb.pdu_queue_len(), 1);
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.pdu_header().common_pdu_conf().file_flag, file_flag);
|
||||
assert_eq!(nak_pdu.start_of_scope(), 0);
|
||||
@@ -3444,7 +3442,7 @@ mod tests {
|
||||
assert_eq!(tb.pdu_queue_len(), 1);
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.start_of_scope(), 0);
|
||||
assert_eq!(nak_pdu.end_of_scope(), file_size);
|
||||
@@ -3494,7 +3492,7 @@ mod tests {
|
||||
assert_eq!(tb.pdu_queue_len(), 1);
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.start_of_scope(), 0);
|
||||
assert_eq!(nak_pdu.end_of_scope(), file_size);
|
||||
@@ -3552,7 +3550,7 @@ mod tests {
|
||||
tb.check_eof_ack_pdu(ConditionCode::NoError);
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.start_of_scope(), 0);
|
||||
assert_eq!(nak_pdu.end_of_scope(), file_size);
|
||||
@@ -3614,7 +3612,7 @@ mod tests {
|
||||
assert_eq!(tb.pdu_queue_len(), 1);
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.start_of_scope(), 0);
|
||||
assert_eq!(nak_pdu.end_of_scope(), file_size);
|
||||
@@ -3628,7 +3626,7 @@ mod tests {
|
||||
assert_eq!(tb.pdu_queue_len(), 1);
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.start_of_scope(), 0);
|
||||
assert_eq!(nak_pdu.end_of_scope(), file_size);
|
||||
@@ -3842,7 +3840,7 @@ mod tests {
|
||||
assert_eq!(tb.pdu_queue_len(), 1);
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.start_of_scope(), 0);
|
||||
assert_eq!(nak_pdu.end_of_scope(), file_size);
|
||||
@@ -3926,7 +3924,7 @@ mod tests {
|
||||
tb.check_eof_ack_pdu(ConditionCode::NoError);
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.start_of_scope(), 0);
|
||||
assert_eq!(nak_pdu.end_of_scope(), 10);
|
||||
@@ -3935,7 +3933,7 @@ mod tests {
|
||||
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.start_of_scope(), 10);
|
||||
assert_eq!(nak_pdu.end_of_scope(), file_size);
|
||||
@@ -4036,7 +4034,7 @@ mod tests {
|
||||
tb.check_eof_ack_pdu(ConditionCode::NoError);
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.start_of_scope(), 0);
|
||||
assert_eq!(nak_pdu.end_of_scope(), 14);
|
||||
@@ -4045,7 +4043,7 @@ mod tests {
|
||||
|
||||
let pdu = tb.get_next_pdu().unwrap();
|
||||
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();
|
||||
assert_eq!(nak_pdu.start_of_scope(), 14);
|
||||
assert_eq!(nak_pdu.end_of_scope(), file_size);
|
||||
|
||||
60
src/lib.rs
60
src/lib.rs
@@ -109,7 +109,7 @@
|
||||
//! [threadpool](https://docs.rs/threadpool/latest/threadpool/).
|
||||
#![no_std]
|
||||
// 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")]
|
||||
extern crate alloc;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
@@ -138,7 +138,7 @@ use spacepackets::{
|
||||
ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
|
||||
pdu::{FileDirectiveType, PduError, PduHeader},
|
||||
},
|
||||
util::{UnsignedByteField, UnsignedEnum},
|
||||
util::UnsignedByteField,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
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 {
|
||||
// Section c) of 4.5.3: These PDUs should always be targeted towards the file sender a.k.a.
|
||||
// the source handler
|
||||
FileDirectiveType::NakPdu
|
||||
| FileDirectiveType::FinishedPdu
|
||||
| FileDirectiveType::KeepAlivePdu => PacketTarget::SourceEntity,
|
||||
FileDirectiveType::Nak | FileDirectiveType::Finished | FileDirectiveType::KeepAlive => {
|
||||
PacketTarget::SourceEntity
|
||||
}
|
||||
// Section b) of 4.5.3: These PDUs should always be targeted towards the file receiver a.k.a.
|
||||
// the destination handler
|
||||
FileDirectiveType::MetadataPdu
|
||||
| FileDirectiveType::EofPdu
|
||||
| FileDirectiveType::PromptPdu => PacketTarget::DestEntity,
|
||||
FileDirectiveType::Metadata | FileDirectiveType::Eof | FileDirectiveType::Prompt => {
|
||||
PacketTarget::DestEntity
|
||||
}
|
||||
// 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
|
||||
// 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)
|
||||
.map_err(|_| PduError::InvalidDirectiveType {
|
||||
found: (raw_pdu[header_len + 1] >> 4),
|
||||
expected: None,
|
||||
})?;
|
||||
if acked_directive == FileDirectiveType::EofPdu {
|
||||
if acked_directive == FileDirectiveType::Eof {
|
||||
PacketTarget::SourceEntity
|
||||
} else if acked_directive == FileDirectiveType::FinishedPdu {
|
||||
} else if acked_directive == FileDirectiveType::Finished {
|
||||
PacketTarget::DestEntity
|
||||
} else {
|
||||
// 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_eq!(
|
||||
packet_info.file_directive_type().unwrap(),
|
||||
FileDirectiveType::MetadataPdu
|
||||
FileDirectiveType::Metadata
|
||||
);
|
||||
assert_eq!(
|
||||
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.file_directive_type().unwrap(),
|
||||
FileDirectiveType::EofPdu
|
||||
FileDirectiveType::Eof
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1659,7 +1659,7 @@ pub(crate) mod tests {
|
||||
TransmissionMode::Unacknowledged,
|
||||
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.max_packet_len, 1024);
|
||||
assert!(remote_entity_retrieved.closure_requested_by_default);
|
||||
@@ -1668,7 +1668,7 @@ pub(crate) mod tests {
|
||||
remote_entity_retrieved.default_crc_type,
|
||||
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());
|
||||
let dummy = RemoteEntityConfig::new_with_default_values(
|
||||
LOCAL_ID.into(),
|
||||
@@ -1682,11 +1682,11 @@ pub(crate) mod tests {
|
||||
remote_entity_cfg.add_config(&dummy).unwrap_err(),
|
||||
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());
|
||||
// Does not exist.
|
||||
assert!(remote_entity_cfg.get(LOCAL_ID.value()).is_none());
|
||||
assert!(remote_entity_cfg.get_mut(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_raw()).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1711,20 +1711,20 @@ pub(crate) mod tests {
|
||||
TransmissionMode::Unacknowledged,
|
||||
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());
|
||||
remote_cfg_provider
|
||||
.add_config(&remote_entity_cfg_2)
|
||||
.unwrap();
|
||||
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!(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);
|
||||
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;
|
||||
assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value()));
|
||||
assert!(remote_cfg_provider.get_mut(REMOTE_ID.value()).is_none());
|
||||
assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value_raw()));
|
||||
assert!(remote_cfg_provider.get_mut(REMOTE_ID.value_raw()).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1749,7 +1749,7 @@ pub(crate) mod tests {
|
||||
TransmissionMode::Unacknowledged,
|
||||
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!(
|
||||
remote_cfg_provider
|
||||
@@ -1757,14 +1757,14 @@ pub(crate) mod tests {
|
||||
.unwrap()
|
||||
);
|
||||
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!(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);
|
||||
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;
|
||||
assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value()));
|
||||
assert!(remote_cfg_provider.get_mut(REMOTE_ID.value()).is_none());
|
||||
assert!(!remote_cfg_provider.remove_config(REMOTE_ID.value_raw()));
|
||||
assert!(remote_cfg_provider.get_mut(REMOTE_ID.value_raw()).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! # Request module
|
||||
#![deny(missing_docs)]
|
||||
use core::str::Utf8Error;
|
||||
|
||||
use spacepackets::{
|
||||
@@ -12,6 +14,9 @@ use spacepackets::{
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use alloc_mod::*;
|
||||
|
||||
/// File path is too large.
|
||||
///
|
||||
/// The file path length is limited to 255 bytes.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[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
|
||||
/// by Put Request consumers.
|
||||
pub trait ReadablePutRequest {
|
||||
/// Destination entity ID.
|
||||
fn destination_id(&self) -> UnsignedByteField;
|
||||
/// Source file path.
|
||||
fn source_file(&self) -> Option<&str>;
|
||||
/// Destination file path.
|
||||
fn dest_file(&self) -> Option<&str>;
|
||||
/// Transmission mode if explicitely specified.
|
||||
fn trans_mode(&self) -> Option<TransmissionMode>;
|
||||
/// Closure is requested for unacknowledged file transfer.
|
||||
fn closure_requested(&self) -> Option<bool>;
|
||||
/// Segmentation control.
|
||||
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<'_>>>;
|
||||
/// Iterator over fault handler override TLVs, if any are supplied.
|
||||
fn fault_handler_overrides(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
|
||||
/// Flow label TLV, if it is supplied.
|
||||
fn flow_label(&self) -> Option<Tlv<'_>>;
|
||||
/// Iterator over filestore request TLVs, if any are supplied.
|
||||
fn fs_requests(&self) -> Option<impl Iterator<Item = Tlv<'_>>>;
|
||||
}
|
||||
|
||||
/// Put request structure.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct PutRequest<'src_file, 'dest_file, 'msgs_to_user, 'fh_ovrds, 'flow_label, 'fs_requests> {
|
||||
/// Destination entity ID.
|
||||
pub destination_id: UnsignedByteField,
|
||||
source_file: Option<&'src_file str>,
|
||||
dest_file: Option<&'dest_file str>,
|
||||
/// Transmission mode.
|
||||
pub trans_mode: Option<TransmissionMode>,
|
||||
/// Closure requested flag for unacknowledged file transfer.
|
||||
pub closure_requested: Option<bool>,
|
||||
/// Segmentation control.
|
||||
pub seg_ctrl: Option<SegmentationControl>,
|
||||
/// Messages to user TLVs.
|
||||
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>]>,
|
||||
/// Flow label TLV.
|
||||
pub flow_label: Option<Tlv<'flow_label>>,
|
||||
/// Filestore request TLVs.
|
||||
pub fs_requests: Option<&'fs_requests [Tlv<'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>
|
||||
{
|
||||
/// Create a new put request with all possible fields.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
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(
|
||||
source_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> {
|
||||
/// New regular put request with no additional TLVs.
|
||||
pub fn new_regular_request(
|
||||
dest_id: UnsignedByteField,
|
||||
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)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct TlvWithInvalidType(pub(crate) ());
|
||||
|
||||
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(
|
||||
dest_id: UnsignedByteField,
|
||||
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>(
|
||||
opt_tlvs: Option<&[TlvProvider]>,
|
||||
tlv_type: TlvType,
|
||||
@@ -229,7 +261,9 @@ pub fn generic_tlv_list_type_check<TlvProvider: GenericTlv>(
|
||||
true
|
||||
}
|
||||
|
||||
/// Structure for all static put request fields.
|
||||
pub struct StaticPutRequestFields {
|
||||
/// Destination entity ID.
|
||||
pub destination_id: UnsignedByteField,
|
||||
/// Static buffer to store source file path.
|
||||
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],
|
||||
/// Current destination path length.
|
||||
pub dest_file_len: usize,
|
||||
/// Transmission mode.
|
||||
pub trans_mode: Option<TransmissionMode>,
|
||||
/// Closure requested flag for unacknowledged file transfer.
|
||||
pub closure_requested: Option<bool>,
|
||||
/// Segmentation control.
|
||||
pub seg_ctrl: Option<SegmentationControl>,
|
||||
}
|
||||
|
||||
@@ -260,6 +297,7 @@ impl Default for StaticPutRequestFields {
|
||||
}
|
||||
|
||||
impl StaticPutRequestFields {
|
||||
/// Clears and resets the fields.
|
||||
pub fn clear(&mut self) {
|
||||
self.destination_id = UnsignedByteField::new(0, 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
|
||||
/// without requiring run-time allocation. The user must specify the static buffer sizes used
|
||||
/// to store TLVs or list of TLVs.
|
||||
/// 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> {
|
||||
/// Static fields.
|
||||
pub static_fields: StaticPutRequestFields,
|
||||
opts_buf: [u8; BUF_SIZE],
|
||||
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> {
|
||||
/// Constructor.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
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(
|
||||
&mut self,
|
||||
put_request: &impl ReadablePutRequest,
|
||||
@@ -352,28 +394,34 @@ impl<const BUF_SIZE: usize> StaticPutRequestCacher<BUF_SIZE> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Does the put request have a source file?
|
||||
pub fn has_source_file(&self) -> bool {
|
||||
self.static_fields.source_file_len > 0
|
||||
}
|
||||
|
||||
/// Does the put request have a destination file?
|
||||
pub fn has_dest_file(&self) -> bool {
|
||||
self.static_fields.dest_file_len > 0
|
||||
}
|
||||
|
||||
/// Source file path.
|
||||
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],
|
||||
)
|
||||
}
|
||||
|
||||
/// Destination file path.
|
||||
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])
|
||||
}
|
||||
|
||||
/// Length of stored options TLVs.
|
||||
pub fn opts_len(&self) -> usize {
|
||||
self.opts_len
|
||||
}
|
||||
|
||||
/// Raw options slice.
|
||||
pub fn opts_slice(&self) -> &[u8] {
|
||||
&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")]
|
||||
pub mod alloc_mod {
|
||||
|
||||
@@ -400,19 +449,28 @@ pub mod alloc_mod {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PutRequestOwned {
|
||||
/// Destination entity ID.
|
||||
pub destination_id: UnsignedByteField,
|
||||
source_file: Option<alloc::string::String>,
|
||||
dest_file: Option<alloc::string::String>,
|
||||
/// Transmission mode.
|
||||
pub trans_mode: Option<TransmissionMode>,
|
||||
/// Closure requested flag for unacknowledged file transfer.
|
||||
pub closure_requested: Option<bool>,
|
||||
/// Segmentation control.
|
||||
pub seg_ctrl: Option<SegmentationControl>,
|
||||
/// Messages to user TLVs.
|
||||
pub msgs_to_user: Option<alloc::vec::Vec<TlvOwned>>,
|
||||
/// Fault handler override TLVs.
|
||||
pub fault_handler_overrides: Option<alloc::vec::Vec<TlvOwned>>,
|
||||
/// Flow label TLV.
|
||||
pub flow_label: Option<TlvOwned>,
|
||||
/// Filestore request TLVs.
|
||||
pub fs_requests: Option<alloc::vec::Vec<TlvOwned>>,
|
||||
}
|
||||
|
||||
impl PutRequestOwned {
|
||||
/// New regular put request with no additional TLVs.
|
||||
pub fn new_regular_request(
|
||||
dest_id: UnsignedByteField,
|
||||
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(
|
||||
dest_id: UnsignedByteField,
|
||||
msgs_to_user: &[MsgToUserTlv<'_>],
|
||||
|
||||
137
src/source.rs
137
src/source.rs
@@ -36,6 +36,7 @@
|
||||
//! 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
|
||||
//! generated PDU.
|
||||
#![deny(missing_docs)]
|
||||
use core::{
|
||||
cell::{Cell, RefCell},
|
||||
ops::ControlFlow,
|
||||
@@ -84,29 +85,42 @@ use super::{
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TransactionStep {
|
||||
/// Idle state, nothing to do.
|
||||
Idle = 0,
|
||||
/// Transaction has started.
|
||||
TransactionStart = 1,
|
||||
/// Sending Metadata PDU.
|
||||
SendingMetadata = 3,
|
||||
/// Sending file data PDUs.
|
||||
SendingFileData = 4,
|
||||
/// Re-transmitting missing packets in acknowledged mode
|
||||
Retransmitting = 5,
|
||||
/// Sending an EOF PDU.
|
||||
SendingEof = 6,
|
||||
/// Waiting for the acknowledgement of the EOF PDU.
|
||||
WaitingForEofAck = 7,
|
||||
/// Waiting for the Finished PDU from the receiver.
|
||||
WaitingForFinished = 8,
|
||||
/// Performing the notice of completion.
|
||||
NoticeOfCompletion = 10,
|
||||
}
|
||||
|
||||
/// Parameter related to the file transfer.
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub struct FileParams {
|
||||
pub progress: u64,
|
||||
pub segment_len: u64,
|
||||
pub crc32: u32,
|
||||
pub metadata_only: bool,
|
||||
pub file_size: u64,
|
||||
pub empty_file: bool,
|
||||
struct FileParams {
|
||||
/// Progress of the file transfer.
|
||||
progress: u64,
|
||||
/// Segment length for a single file segment which is limited by various factors.
|
||||
segment_len: u64,
|
||||
/// Metadata only flag.
|
||||
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
|
||||
/// re-sent.
|
||||
pub checksum_completed_file: Option<u32>,
|
||||
checksum_completed_file: Option<u32>,
|
||||
}
|
||||
|
||||
// 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)]
|
||||
pub struct FinishedParams {
|
||||
condition_code: ConditionCode,
|
||||
@@ -144,68 +159,98 @@ pub struct FinishedParams {
|
||||
file_status: FileStatus,
|
||||
}
|
||||
|
||||
/// Source handler errors.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SourceError {
|
||||
/// Can not process the passed packet type.
|
||||
#[error("can not process packet type {pdu_type:?} with directive type {directive_type:?}")]
|
||||
CantProcessPacketType {
|
||||
/// PDU type.
|
||||
pdu_type: PduType,
|
||||
/// Directive type, if applicable.
|
||||
directive_type: Option<FileDirectiveType>,
|
||||
},
|
||||
/// Unexpected PDU for current state.
|
||||
#[error("unexpected PDU")]
|
||||
UnexpectedPdu {
|
||||
/// PDU type.
|
||||
pdu_type: PduType,
|
||||
/// Directive type, if applicable.
|
||||
directive_type: Option<FileDirectiveType>,
|
||||
},
|
||||
/// Put request is already active.
|
||||
#[error("source handler is already busy with put request")]
|
||||
PutRequestAlreadyActive,
|
||||
/// Error during the caching process of a put request.
|
||||
#[error("error caching put request")]
|
||||
PutRequestCaching(ByteConversionError),
|
||||
/// Generic filestore error.
|
||||
#[error("filestore error: {0}")]
|
||||
FilestoreError(#[from] FilestoreError),
|
||||
/// Source file name is not valid UTF-8.
|
||||
#[error("source file does not have valid UTF8 format: {0}")]
|
||||
SourceFileNotValidUtf8(Utf8Error),
|
||||
/// Destination file name is not valid UTF-8.
|
||||
#[error("destination file does not have valid UTF8 format: {0}")]
|
||||
DestFileNotValidUtf8(Utf8Error),
|
||||
/// Invalid NAK PDU error.
|
||||
#[error("invalid NAK PDU received")]
|
||||
InvalidNakPdu,
|
||||
/// PDU creation error.
|
||||
#[error("error related to PDU creation: {0}")]
|
||||
Pdu(#[from] PduError),
|
||||
/// Feature not implemented error.
|
||||
#[error("cfdp feature not implemented")]
|
||||
NotImplemented,
|
||||
/// Generic send error.
|
||||
#[error("issue sending PDU: {0}")]
|
||||
SendError(#[from] GenericSendError),
|
||||
}
|
||||
|
||||
/// Put request errors.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum PutRequestError {
|
||||
/// Storage error.
|
||||
#[error("error caching put request: {0}")]
|
||||
Storage(#[from] ByteConversionError),
|
||||
/// Already busy with a put request.
|
||||
#[error("already busy with put request")]
|
||||
AlreadyBusy,
|
||||
/// No remote entity configuration was found for destination ID.
|
||||
#[error("no remote entity configuration found for {0:?}")]
|
||||
NoRemoteCfgFound(UnsignedByteField),
|
||||
/// Source file name is not valid UTF-8.
|
||||
#[error("source file does not have valid UTF8 format: {0}")]
|
||||
SourceFileNotValidUtf8(#[from] Utf8Error),
|
||||
/// File does not exist.
|
||||
#[error("source file does not exist")]
|
||||
FileDoesNotExist,
|
||||
/// Generic filestore error.
|
||||
#[error("filestore error: {0}")]
|
||||
FilestoreError(#[from] FilestoreError),
|
||||
}
|
||||
|
||||
/// Anomaly tracker for the source handler.
|
||||
///
|
||||
/// Anomalies are unexpected events which are not severe errors.
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct AnomalyTracker {
|
||||
invalid_ack_directive_code: u8,
|
||||
}
|
||||
|
||||
/// Finite state-machine context.
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub enum FsmContext {
|
||||
enum FsmContext {
|
||||
/// None
|
||||
#[default]
|
||||
None,
|
||||
/// The FSM should be reset when possible.
|
||||
ResetWhenPossible,
|
||||
}
|
||||
|
||||
/// Transaction parameters.
|
||||
#[derive(Debug)]
|
||||
pub struct TransactionParams<CountdownInstance: Countdown> {
|
||||
struct TransactionParams<CountdownInstance: Countdown> {
|
||||
transaction_id: Option<TransactionId>,
|
||||
remote_cfg: Option<RemoteEntityConfig>,
|
||||
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]
|
||||
pub fn transaction_id(&self) -> Option<TransactionId> {
|
||||
self.transaction_params.transaction_id
|
||||
@@ -417,11 +465,13 @@ impl<
|
||||
self.state_helper.step.get()
|
||||
}
|
||||
|
||||
/// Current state of the source handler.
|
||||
#[inline]
|
||||
pub fn state(&self) -> State {
|
||||
self.state_helper.state.get()
|
||||
}
|
||||
|
||||
/// Local configuration of the source handler.
|
||||
#[inline]
|
||||
pub fn local_cfg(&self) -> &LocalEntityConfig<UserFaultHookInstance> {
|
||||
&self.local_cfg
|
||||
@@ -442,12 +492,9 @@ impl<
|
||||
return Err(PutRequestError::AlreadyBusy);
|
||||
}
|
||||
self.put_request_cacher.set(put_request)?;
|
||||
let remote_cfg = self.remote_cfg_table.get(
|
||||
self.put_request_cacher
|
||||
.static_fields
|
||||
.destination_id
|
||||
.value_const(),
|
||||
);
|
||||
let remote_cfg = self
|
||||
.remote_cfg_table
|
||||
.get(self.put_request_cacher.static_fields.destination_id.value());
|
||||
if remote_cfg.is_none() {
|
||||
return Err(PutRequestError::NoRemoteCfgFound(
|
||||
self.put_request_cacher.static_fields.destination_id,
|
||||
@@ -495,7 +542,7 @@ impl<
|
||||
);
|
||||
let create_id = |cached_id: &UnsignedByteField| {
|
||||
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 {
|
||||
*cached_id
|
||||
}
|
||||
@@ -558,22 +605,20 @@ impl<
|
||||
.file_directive_type()
|
||||
.expect("PDU directive type unexpectedly not set")
|
||||
{
|
||||
FileDirectiveType::FinishedPdu => {
|
||||
FileDirectiveType::Finished => {
|
||||
let finished_pdu = FinishedPduReader::new(packet_to_insert.raw_pdu())?;
|
||||
self.handle_finished_pdu(&finished_pdu)?
|
||||
}
|
||||
FileDirectiveType::NakPdu => {
|
||||
FileDirectiveType::Nak => {
|
||||
let nak_pdu = NakPduReader::new(packet_to_insert.raw_pdu())?;
|
||||
sent_packets += self.handle_nak_pdu(&nak_pdu)?;
|
||||
}
|
||||
FileDirectiveType::KeepAlivePdu => self.handle_keep_alive_pdu(),
|
||||
FileDirectiveType::AckPdu => {
|
||||
FileDirectiveType::KeepAlive => self.handle_keep_alive_pdu(),
|
||||
FileDirectiveType::Ack => {
|
||||
let ack_pdu = AckPdu::from_bytes(packet_to_insert.raw_pdu())?;
|
||||
self.handle_ack_pdu(&ack_pdu)?
|
||||
}
|
||||
FileDirectiveType::EofPdu
|
||||
| FileDirectiveType::PromptPdu
|
||||
| FileDirectiveType::MetadataPdu => {
|
||||
FileDirectiveType::Eof | FileDirectiveType::Prompt | FileDirectiveType::Metadata => {
|
||||
return Err(SourceError::CantProcessPacketType {
|
||||
pdu_type: packet_to_insert.pdu_type(),
|
||||
directive_type: packet_to_insert.file_directive_type(),
|
||||
@@ -898,7 +943,7 @@ impl<
|
||||
) -> Result<(), SourceError> {
|
||||
let ack_pdu = AckPdu::new(
|
||||
PduHeader::new_for_file_directive(self.transaction_params.pdu_conf, 0),
|
||||
FileDirectiveType::FinishedPdu,
|
||||
FileDirectiveType::Finished,
|
||||
condition_code,
|
||||
transaction_status,
|
||||
)
|
||||
@@ -1093,7 +1138,7 @@ impl<
|
||||
if self.step() != TransactionStep::WaitingForFinished {
|
||||
return Err(SourceError::UnexpectedPdu {
|
||||
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
|
||||
@@ -1122,10 +1167,10 @@ impl<
|
||||
// Drop the packet, wrong state to handle it..
|
||||
return Err(SourceError::UnexpectedPdu {
|
||||
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);
|
||||
} else {
|
||||
self.anomalies.invalid_ack_directive_code =
|
||||
@@ -1134,6 +1179,9 @@ impl<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Manually trigger a notice of cancellation.
|
||||
///
|
||||
/// This cancels any currently active transaction.
|
||||
pub fn notice_of_cancellation(
|
||||
&mut self,
|
||||
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) {
|
||||
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) {
|
||||
// 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.
|
||||
@@ -1550,7 +1604,7 @@ mod tests {
|
||||
assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
|
||||
assert_eq!(
|
||||
next_pdu.file_directive_type,
|
||||
Some(FileDirectiveType::MetadataPdu)
|
||||
Some(FileDirectiveType::Metadata)
|
||||
);
|
||||
let metadata_pdu =
|
||||
MetadataPduReader::new(&next_pdu.raw_pdu).expect("invalid metadata PDU format");
|
||||
@@ -1599,7 +1653,7 @@ mod tests {
|
||||
) {
|
||||
let ack_pdu = AckPdu::new(
|
||||
transaction_info.pdu_header,
|
||||
FileDirectiveType::EofPdu,
|
||||
FileDirectiveType::Eof,
|
||||
ConditionCode::NoError,
|
||||
TransactionStatus::Active,
|
||||
)
|
||||
@@ -1616,16 +1670,13 @@ mod tests {
|
||||
let next_pdu = self.get_next_sent_pdu().unwrap();
|
||||
assert!(self.pdu_queue_empty());
|
||||
assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
|
||||
assert_eq!(
|
||||
next_pdu.file_directive_type,
|
||||
Some(FileDirectiveType::AckPdu)
|
||||
);
|
||||
assert_eq!(next_pdu.file_directive_type, Some(FileDirectiveType::Ack));
|
||||
let ack_pdu = AckPdu::from_bytes(&next_pdu.raw_pdu).unwrap();
|
||||
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.directive_code_of_acked_pdu(),
|
||||
FileDirectiveType::FinishedPdu
|
||||
FileDirectiveType::Finished
|
||||
);
|
||||
assert_eq!(ack_pdu.transaction_status(), TransactionStatus::Active);
|
||||
}
|
||||
@@ -1639,10 +1690,7 @@ mod tests {
|
||||
) {
|
||||
let next_pdu = self.get_next_sent_pdu().unwrap();
|
||||
assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
|
||||
assert_eq!(
|
||||
next_pdu.file_directive_type,
|
||||
Some(FileDirectiveType::EofPdu)
|
||||
);
|
||||
assert_eq!(next_pdu.file_directive_type, Some(FileDirectiveType::Eof));
|
||||
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);
|
||||
assert_eq!(eof_pdu.condition_code(), eof_params.condition_code);
|
||||
@@ -1653,7 +1701,7 @@ mod tests {
|
||||
.pdu_header()
|
||||
.common_pdu_conf()
|
||||
.transaction_seq_num
|
||||
.value_const(),
|
||||
.value(),
|
||||
0
|
||||
);
|
||||
if self.transmission_mode == TransmissionMode::Unacknowledged {
|
||||
@@ -2031,10 +2079,7 @@ mod tests {
|
||||
let eof_pdu = tb
|
||||
.get_next_sent_pdu()
|
||||
.expect("no EOF PDU generated like expected");
|
||||
assert_eq!(
|
||||
eof_pdu.file_directive_type.unwrap(),
|
||||
FileDirectiveType::EofPdu
|
||||
);
|
||||
assert_eq!(eof_pdu.file_directive_type.unwrap(), FileDirectiveType::Eof);
|
||||
let eof_pdu = EofPdu::from_bytes(&eof_pdu.raw_pdu).unwrap();
|
||||
assert_eq!(
|
||||
eof_pdu.condition_code(),
|
||||
@@ -2096,7 +2141,7 @@ mod tests {
|
||||
assert_eq!(next_packet.pdu_type, PduType::FileDirective);
|
||||
assert_eq!(
|
||||
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
|
||||
// file copy operation so far, and the checksum is calculated for that progress.
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
//! # Time support module.
|
||||
#![deny(missing_docs)]
|
||||
use core::fmt::Debug;
|
||||
|
||||
/// Generic abstraction for a check/countdown timer. Should also be cheap to copy and clone.
|
||||
pub trait Countdown: Debug {
|
||||
/// The countdown has expired.
|
||||
fn has_expired(&self) -> bool;
|
||||
/// Reset the countdown to its initial state.
|
||||
fn reset(&mut self);
|
||||
}
|
||||
|
||||
51
src/user.rs
51
src/user.rs
@@ -1,3 +1,5 @@
|
||||
//! # User support and hooks module
|
||||
#![deny(missing_docs)]
|
||||
#[cfg(feature = "alloc")]
|
||||
use spacepackets::cfdp::tlv::WritableTlv;
|
||||
use spacepackets::{
|
||||
@@ -14,34 +16,53 @@ use spacepackets::{
|
||||
|
||||
use super::TransactionId;
|
||||
|
||||
/// Parameters related to a finished transfer.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct TransactionFinishedParams {
|
||||
/// ID of the transfer.
|
||||
pub id: TransactionId,
|
||||
/// Condition code.
|
||||
pub condition_code: ConditionCode,
|
||||
/// Delivery code.
|
||||
pub delivery_code: DeliveryCode,
|
||||
/// File status.
|
||||
pub file_status: FileStatus,
|
||||
}
|
||||
|
||||
/// Parameters related to the reception of a metadata PDU, which might start file reception.
|
||||
#[derive(Debug)]
|
||||
pub struct MetadataReceivedParams<'src_file, 'dest_file, 'msgs_to_user> {
|
||||
/// ID of the transfer.
|
||||
pub id: TransactionId,
|
||||
/// Source entity ID.
|
||||
pub source_id: UnsignedByteField,
|
||||
/// File size.
|
||||
pub file_size: u64,
|
||||
/// Source file name.
|
||||
pub src_file_name: &'src_file str,
|
||||
/// Destination file name.
|
||||
pub dest_file_name: &'dest_file str,
|
||||
/// Messages to user TLVs.
|
||||
pub msgs_to_user: &'msgs_to_user [MsgToUserTlv<'msgs_to_user>],
|
||||
}
|
||||
|
||||
/// Owned variant of [MetadataReceivedParams].
|
||||
#[cfg(feature = "alloc")]
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OwnedMetadataRecvdParams {
|
||||
/// ID of the transfer.
|
||||
pub id: TransactionId,
|
||||
/// Source entity ID.
|
||||
pub source_id: UnsignedByteField,
|
||||
/// File size.
|
||||
pub file_size: u64,
|
||||
/// Source file name.
|
||||
pub src_file_name: alloc::string::String,
|
||||
/// Destination file name.
|
||||
pub dest_file_name: alloc::string::String,
|
||||
/// Messages to user TLVs.
|
||||
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)]
|
||||
pub struct FileSegmentRecvdParams<'seg_meta> {
|
||||
/// ID of the transfer.
|
||||
pub id: TransactionId,
|
||||
/// Offset of the segment.
|
||||
pub offset: u64,
|
||||
/// Length of the segment.
|
||||
pub length: usize,
|
||||
/// Segment metadata, if present.
|
||||
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 {
|
||||
/// Indication that a new transaction has started.
|
||||
fn transaction_indication(&mut self, id: &TransactionId);
|
||||
|
||||
/// Indication that an EOF PDU has been sent.
|
||||
fn eof_sent_indication(&mut self, id: &TransactionId);
|
||||
|
||||
/// Indication that a transaction has finished.
|
||||
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);
|
||||
|
||||
/// Indication that a file segment has been received.
|
||||
fn file_segment_recvd_indication(&mut self, segment_recvd_params: &FileSegmentRecvdParams);
|
||||
|
||||
// TODO: The standard does not strictly specify how the report information looks..
|
||||
/// Report information indication.
|
||||
fn report_indication(&mut self, id: &TransactionId);
|
||||
|
||||
/// Indication that a transfer has been suspended.
|
||||
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);
|
||||
|
||||
/// Indication that a fault has occured.
|
||||
fn fault_indication(
|
||||
&mut self,
|
||||
id: &TransactionId,
|
||||
condition_code: ConditionCode,
|
||||
progress: u64,
|
||||
);
|
||||
|
||||
/// Indication that a transfer has been abandoned.
|
||||
fn abandoned_indication(
|
||||
&mut self,
|
||||
id: &TransactionId,
|
||||
condition_code: ConditionCode,
|
||||
progress: u64,
|
||||
);
|
||||
|
||||
/// Indication that an EOF PDU has been received.
|
||||
fn eof_recvd_indication(&mut self, id: &TransactionId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user