CFDP extracted to library #201
@ -31,7 +31,7 @@ default-features = false
|
||||
version = "0.12"
|
||||
default-features = false
|
||||
git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets"
|
||||
branch = "main"
|
||||
branch = "file-data-pdu-update"
|
||||
|
||||
[dependencies.cobs]
|
||||
git = "https://github.com/robamu/cobs.rs.git"
|
||||
|
@ -1,13 +1,14 @@
|
||||
use core::{cell::RefCell, str::Utf8Error};
|
||||
use core::{cell::RefCell, ops::ControlFlow, str::Utf8Error};
|
||||
|
||||
use spacepackets::{
|
||||
cfdp::{
|
||||
lv::Lv,
|
||||
pdu::{
|
||||
file_data::FileDataPduCreatorWithReservedDatafield,
|
||||
metadata::{MetadataGenericParams, MetadataPduCreator},
|
||||
CfdpPdu, CommonPduConfig, FileDirectiveType, PduError, PduHeader, WritablePduPacket,
|
||||
},
|
||||
Direction, LargeFileFlag, PduType,
|
||||
Direction, LargeFileFlag, PduType, SegmentMetadataFlag, SegmentationControl,
|
||||
},
|
||||
util::{UnsignedByteField, UnsignedEnum},
|
||||
ByteConversionError,
|
||||
@ -41,12 +42,12 @@ pub enum TransactionStep {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FileParams {
|
||||
pub progress: usize,
|
||||
pub segment_len: usize,
|
||||
pub progress: u64,
|
||||
pub segment_len: u64,
|
||||
//pub crc32: Option<[u8; 4]>,
|
||||
pub metadata_only: bool,
|
||||
pub file_size: u64,
|
||||
pub no_eof: bool,
|
||||
pub empty_file: bool,
|
||||
}
|
||||
|
||||
pub struct StateHelper {
|
||||
@ -275,6 +276,7 @@ impl<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn transmission_mode(&self) -> Option<super::TransmissionMode> {
|
||||
self.tstate.map(|v| v.transmission_mode)
|
||||
}
|
||||
@ -289,6 +291,13 @@ impl<
|
||||
}
|
||||
if self.state_helper.step == TransactionStep::SendingMetadata {
|
||||
self.prepare_and_send_metadata_pdu()?;
|
||||
self.state_helper.step = TransactionStep::SendingFileData;
|
||||
return Ok(1);
|
||||
}
|
||||
if self.state_helper.step == TransactionStep::SendingFileData {
|
||||
if let ControlFlow::Break(result) = self.file_data_fsm()? {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
@ -300,7 +309,7 @@ impl<
|
||||
let tstate = &self.tstate.expect("transfer state unexpectedly empty");
|
||||
if !self.put_request_cacher.has_source_file() {
|
||||
self.fparams.metadata_only = true;
|
||||
self.fparams.no_eof = true;
|
||||
self.fparams.empty_file = true;
|
||||
} else {
|
||||
let source_file = self
|
||||
.put_request_cacher
|
||||
@ -351,7 +360,7 @@ impl<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_and_send_metadata_pdu(&self) -> Result<(), PduError> {
|
||||
fn prepare_and_send_metadata_pdu(&mut self) -> Result<(), PduError> {
|
||||
let tstate = &self.tstate.expect("transfer state unexpectedly empty");
|
||||
let metadata_params = MetadataGenericParams::new(
|
||||
tstate.closure_requested,
|
||||
@ -378,6 +387,111 @@ impl<
|
||||
self.pdu_send_helper(metadata_pdu)
|
||||
}
|
||||
|
||||
fn file_data_fsm(&mut self) -> Result<ControlFlow<u32>, SourceError> {
|
||||
if self.transmission_mode().unwrap() == super::TransmissionMode::Acknowledged {
|
||||
// TODO: Handle re-transmission
|
||||
}
|
||||
if !self.fparams.metadata_only
|
||||
&& self.fparams.progress < self.fparams.file_size
|
||||
&& self.send_progressing_file_data_pdu()?
|
||||
{
|
||||
return Ok(ControlFlow::Break(1));
|
||||
}
|
||||
if self.fparams.empty_file {
|
||||
// EOF is still expected.
|
||||
self.state_helper.step = TransactionStep::SendingEof;
|
||||
} else if self.fparams.metadata_only {
|
||||
// Special case: Metadata Only, no EOF required.
|
||||
if self.tstate.as_ref().unwrap().closure_requested {
|
||||
self.state_helper.step = TransactionStep::WaitingForFinished;
|
||||
} else {
|
||||
self.state_helper.step = TransactionStep::NoticeOfCompletion;
|
||||
}
|
||||
}
|
||||
Ok(ControlFlow::Continue(()))
|
||||
}
|
||||
|
||||
fn send_progressing_file_data_pdu(&mut self) -> Result<bool, SourceError> {
|
||||
// Should never be called, but use defensive programming here.
|
||||
if self.fparams.progress >= self.fparams.file_size {
|
||||
return Ok(false);
|
||||
}
|
||||
let read_len = if self.fparams.file_size < self.fparams.segment_len {
|
||||
self.fparams.file_size
|
||||
} else if self.fparams.progress + self.fparams.segment_len > self.fparams.file_size {
|
||||
self.fparams.file_size - self.fparams.progress
|
||||
} else {
|
||||
self.fparams.segment_len
|
||||
};
|
||||
// TODO: Send File Data PDU.
|
||||
let pdu_creator = FileDataPduCreatorWithReservedDatafield::new_no_seg_metadata(
|
||||
PduHeader::new_for_file_data(
|
||||
self.pdu_conf,
|
||||
0,
|
||||
SegmentMetadataFlag::NotPresent,
|
||||
SegmentationControl::NoRecordBoundaryPreservation,
|
||||
),
|
||||
self.fparams.progress,
|
||||
read_len,
|
||||
);
|
||||
let mut unwritten_pdu = pdu_creator.write_to_bytes_partially(self.pdu_buffer.get_mut())?;
|
||||
self.vfs.read_data(
|
||||
self.put_request_cacher.source_file().unwrap(),
|
||||
self.fparams.progress,
|
||||
read_len,
|
||||
unwritten_pdu.file_data_field_mut(),
|
||||
)?;
|
||||
let written_len = unwritten_pdu.finish();
|
||||
self.pdu_sender.send_pdu(
|
||||
PduType::FileData,
|
||||
None,
|
||||
&self.pdu_buffer.borrow()[0..written_len],
|
||||
)?;
|
||||
self.fparams.progress += read_len;
|
||||
/*
|
||||
"""Generic function to prepare a file data PDU. This function can also be used to
|
||||
re-transmit file data PDUs of segments which were already sent."""
|
||||
assert self._put_req is not None
|
||||
assert self._put_req.source_file is not None
|
||||
with open(self._put_req.source_file, "rb") as of:
|
||||
file_data = self.user.vfs.read_from_opened_file(of, offset, read_len)
|
||||
# TODO: Support for record continuation state not implemented yet. Segment metadata
|
||||
# flag is therefore always set to False. Segment metadata support also omitted
|
||||
# for now. Implementing those generically could be done in form of a callback,
|
||||
# e.g. abstractmethod of this handler as a first way, another one being
|
||||
# to expect the user to supply some helper class to split up a file
|
||||
fd_params = FileDataParams(
|
||||
file_data=file_data, offset=offset, segment_metadata=None
|
||||
)
|
||||
file_data_pdu = FileDataPdu(
|
||||
pdu_conf=self._params.pdu_conf, params=fd_params
|
||||
)
|
||||
self._add_packet_to_be_sent(file_data_pdu)
|
||||
*/
|
||||
/*
|
||||
"""Prepare the next file data PDU, which also progresses the file copy operation.
|
||||
|
||||
:return: True if a packet was prepared, False if PDU handling is done and the next steps
|
||||
in the Copy File procedure can be performed
|
||||
"""
|
||||
# This function should only be called if file segments still need to be sent.
|
||||
assert self._params.fp.progress < self._params.fp.file_size
|
||||
if self._params.fp.file_size < self._params.fp.segment_len:
|
||||
read_len = self._params.fp.file_size
|
||||
else:
|
||||
if (
|
||||
self._params.fp.progress + self._params.fp.segment_len
|
||||
> self._params.fp.file_size
|
||||
):
|
||||
read_len = self._params.fp.file_size - self._params.fp.progress
|
||||
else:
|
||||
read_len = self._params.fp.segment_len
|
||||
self._prepare_file_data_pdu(self._params.fp.progress, read_len)
|
||||
self._params.fp.progress += read_len
|
||||
*/
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn pdu_send_helper(&self, pdu: impl WritablePduPacket + CfdpPdu) -> Result<(), PduError> {
|
||||
let mut pdu_buffer_mut = self.pdu_buffer.borrow_mut();
|
||||
let written_len = pdu.write_to_bytes(&mut pdu_buffer_mut)?;
|
||||
|
Loading…
Reference in New Issue
Block a user