This commit is contained in:
parent
143b0869a4
commit
afd9395cee
@ -71,7 +71,7 @@ version = "0.7.0-beta.0"
|
|||||||
# path = "../../spacepackets"
|
# path = "../../spacepackets"
|
||||||
git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git"
|
git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git"
|
||||||
# rev = ""
|
# rev = ""
|
||||||
branch = "update-lv-tlv"
|
branch = "pdu-additions"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -7,7 +7,7 @@ use std::{
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
user::{CfdpUser, MetadataReceivedParams},
|
user::{CfdpUser, MetadataReceivedParams},
|
||||||
State, TransactionId, TransactionStep, CRC_32,
|
PacketInfo, PacketTarget, State, TransactionId, TransactionStep, CRC_32,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
@ -20,7 +20,7 @@ use spacepackets::{
|
|||||||
CommonPduConfig, FileDirectiveType, PduError, PduHeader,
|
CommonPduConfig, FileDirectiveType, PduError, PduHeader,
|
||||||
},
|
},
|
||||||
tlv::{msg_to_user::MsgToUserTlv, EntityIdTlv, TlvType},
|
tlv::{msg_to_user::MsgToUserTlv, EntityIdTlv, TlvType},
|
||||||
ConditionCode, PduType,
|
ConditionCode, PduType, TransmissionMode,
|
||||||
},
|
},
|
||||||
util::UnsignedByteField,
|
util::UnsignedByteField,
|
||||||
};
|
};
|
||||||
@ -142,28 +142,29 @@ impl DestinationHandler {
|
|||||||
match self.state {
|
match self.state {
|
||||||
State::Idle => todo!(),
|
State::Idle => todo!(),
|
||||||
State::BusyClass1Nacked => self.fsm_nacked(cfdp_user),
|
State::BusyClass1Nacked => self.fsm_nacked(cfdp_user),
|
||||||
State::BusyClass2Acked => todo!(),
|
State::BusyClass2Acked => todo!("acknowledged mode not implemented yet"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_packet(_raw_packet: &[u8]) -> Result<(), DestError> {
|
pub fn insert_packet(&mut self, packet_info: &PacketInfo) -> Result<(), DestError> {
|
||||||
todo!();
|
if packet_info.target() != PacketTarget::DestEntity {
|
||||||
//Ok(())
|
// Unwrap is okay here, a PacketInfo for a file data PDU should always have the
|
||||||
|
// destination as the target.
|
||||||
|
return Err(DestError::CantProcessPacketType(
|
||||||
|
packet_info.pdu_directive().unwrap(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
pub fn insert_packet_with_known_type(
|
match packet_info.pdu_type {
|
||||||
&mut self,
|
|
||||||
pdu_type: PduType,
|
|
||||||
pdu_directive: Option<FileDirectiveType>,
|
|
||||||
raw_packet: &[u8],
|
|
||||||
) -> Result<(), DestError> {
|
|
||||||
match pdu_type {
|
|
||||||
PduType::FileDirective => {
|
PduType::FileDirective => {
|
||||||
if pdu_directive.is_none() {
|
if packet_info.pdu_directive.is_none() {
|
||||||
return Err(DestError::DirectiveExpected);
|
return Err(DestError::DirectiveExpected);
|
||||||
}
|
}
|
||||||
self.handle_file_directive(pdu_directive.unwrap(), raw_packet)
|
self.handle_file_directive(
|
||||||
|
packet_info.pdu_directive.unwrap(),
|
||||||
|
packet_info.raw_packet,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
PduType::FileData => self.handle_file_data(raw_packet),
|
PduType::FileData => self.handle_file_data(packet_info.raw_packet),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,6 +263,13 @@ impl DestinationHandler {
|
|||||||
self.transaction_params.file_properties.dest_file_name_len = dest_name.len_value();
|
self.transaction_params.file_properties.dest_file_name_len = dest_name.len_value();
|
||||||
self.transaction_params.pdu_conf = *metadata_pdu.pdu_header().common_pdu_conf();
|
self.transaction_params.pdu_conf = *metadata_pdu.pdu_header().common_pdu_conf();
|
||||||
self.transaction_params.msgs_to_user_size = 0;
|
self.transaction_params.msgs_to_user_size = 0;
|
||||||
|
if metadata_pdu.pdu_header().common_pdu_conf().trans_mode
|
||||||
|
== TransmissionMode::Unacknowledged
|
||||||
|
{
|
||||||
|
self.state = State::BusyClass1Nacked;
|
||||||
|
} else {
|
||||||
|
self.state = State::BusyClass2Acked;
|
||||||
|
}
|
||||||
if metadata_pdu.options().is_some() {
|
if metadata_pdu.options().is_some() {
|
||||||
for option_tlv in metadata_pdu.options_iter().unwrap() {
|
for option_tlv in metadata_pdu.options_iter().unwrap() {
|
||||||
if option_tlv.is_standard_tlv()
|
if option_tlv.is_standard_tlv()
|
||||||
@ -459,6 +467,9 @@ impl DestinationHandler {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use std::println;
|
||||||
|
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
cfdp::{lv::Lv, ChecksumType},
|
cfdp::{lv::Lv, ChecksumType},
|
||||||
util::{UbfU16, UnsignedByteFieldU16},
|
util::{UbfU16, UnsignedByteFieldU16},
|
||||||
@ -556,12 +567,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_empty_file_transfer() {
|
fn test_empty_file_transfer() {
|
||||||
let mut buf: [u8; 512] = [0; 512];
|
let mut buf: [u8; 512] = [0; 512];
|
||||||
let test_user = TestCfdpUser::default();
|
let mut test_user = TestCfdpUser::default();
|
||||||
let mut dest_handler = DestinationHandler::new(LOCAL_ID);
|
let mut dest_handler = DestinationHandler::new(LOCAL_ID);
|
||||||
init_check(&dest_handler);
|
init_check(&dest_handler);
|
||||||
|
|
||||||
let seq_num = UbfU16::new(0);
|
let seq_num = UbfU16::new(0);
|
||||||
let pdu_conf = CommonPduConfig::new_with_byte_fields(REMOTE_ID, LOCAL_ID, seq_num).unwrap();
|
let mut pdu_conf =
|
||||||
|
CommonPduConfig::new_with_byte_fields(REMOTE_ID, LOCAL_ID, seq_num).unwrap();
|
||||||
|
pdu_conf.trans_mode = TransmissionMode::Unacknowledged;
|
||||||
let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
|
let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
|
||||||
let metadata_params = MetadataGenericParams::new(false, ChecksumType::Crc32, 0);
|
let metadata_params = MetadataGenericParams::new(false, ChecksumType::Crc32, 0);
|
||||||
let metadata_pdu = MetadataPdu::new(
|
let metadata_pdu = MetadataPdu::new(
|
||||||
@ -574,11 +587,13 @@ mod tests {
|
|||||||
let written_len = metadata_pdu
|
let written_len = metadata_pdu
|
||||||
.write_to_bytes(&mut buf)
|
.write_to_bytes(&mut buf)
|
||||||
.expect("writing metadata PDU failed");
|
.expect("writing metadata PDU failed");
|
||||||
// TODO: Create Metadata PDU and EOF PDU for empty file transfer.
|
let packet_info =
|
||||||
dest_handler.insert_packet(
|
PacketInfo::new(&buf[..written_len]).expect("generating packet info failed");
|
||||||
PduType::FileDirective,
|
let insert_result = dest_handler.insert_packet(&packet_info);
|
||||||
Some(FileDirectiveType::MetadataPdu),
|
if let Err(e) = insert_result {
|
||||||
&buf[..written_len],
|
panic!("insert result error: {e}");
|
||||||
);
|
}
|
||||||
|
let result = dest_handler.state_machine(&mut test_user);
|
||||||
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
use crc::{Crc, CRC_32_CKSUM};
|
use crc::{Crc, CRC_32_CKSUM};
|
||||||
use spacepackets::util::UnsignedByteField;
|
use spacepackets::{
|
||||||
|
cfdp::{
|
||||||
|
pdu::{FileDirectiveType, PduError, PduHeader},
|
||||||
|
PduType,
|
||||||
|
},
|
||||||
|
util::UnsignedByteField,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub mod dest;
|
pub mod dest;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct TransactionId {
|
pub struct TransactionId {
|
||||||
source_id: UnsignedByteField,
|
source_id: UnsignedByteField,
|
||||||
seq_num: UnsignedByteField,
|
seq_num: UnsignedByteField,
|
||||||
@ -26,6 +36,7 @@ impl TransactionId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum TransactionStep {
|
pub enum TransactionStep {
|
||||||
Idle = 0,
|
Idle = 0,
|
||||||
TransactionStart = 1,
|
TransactionStart = 1,
|
||||||
@ -36,6 +47,7 @@ pub enum TransactionStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum State {
|
pub enum State {
|
||||||
Idle = 0,
|
Idle = 0,
|
||||||
BusyClass1Nacked = 2,
|
BusyClass1Nacked = 2,
|
||||||
@ -44,6 +56,105 @@ pub enum State {
|
|||||||
|
|
||||||
pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_CKSUM);
|
pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_CKSUM);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub enum PacketTarget {
|
||||||
|
SourceEntity,
|
||||||
|
DestEntity,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is a helper struct which contains base information about a particular PDU packet.
|
||||||
|
/// This is also necessary information for CFDP packet routing. For example, some packet types
|
||||||
|
/// like file data PDUs can only be used by CFDP source entities.
|
||||||
|
pub struct PacketInfo<'raw_packet> {
|
||||||
|
pdu_type: PduType,
|
||||||
|
pdu_directive: Option<FileDirectiveType>,
|
||||||
|
target: PacketTarget,
|
||||||
|
raw_packet: &'raw_packet [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'raw> PacketInfo<'raw> {
|
||||||
|
pub fn new(raw_packet: &'raw [u8]) -> Result<Self, PduError> {
|
||||||
|
let (pdu_header, header_len) = PduHeader::from_bytes(raw_packet)?;
|
||||||
|
if pdu_header.pdu_type() == PduType::FileData {
|
||||||
|
return Ok(Self {
|
||||||
|
pdu_type: pdu_header.pdu_type(),
|
||||||
|
pdu_directive: None,
|
||||||
|
target: PacketTarget::DestEntity,
|
||||||
|
raw_packet,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if pdu_header.pdu_datafield_len() < 1 {
|
||||||
|
return Err(PduError::FormatError);
|
||||||
|
}
|
||||||
|
// Route depending on PDU type and directive type if applicable. Retrieve directive type
|
||||||
|
// from the raw stream for better performance (with sanity and directive code check).
|
||||||
|
// The routing is based on section 4.5 of the CFDP standard which specifies the PDU forwarding
|
||||||
|
// procedure.
|
||||||
|
let directive = FileDirectiveType::try_from(raw_packet[header_len]).map_err(|_| {
|
||||||
|
PduError::InvalidDirectiveType {
|
||||||
|
found: raw_packet[header_len],
|
||||||
|
expected: None,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
let packet_target = match directive {
|
||||||
|
// 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,
|
||||||
|
// 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,
|
||||||
|
// 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 => {
|
||||||
|
let acked_directive = FileDirectiveType::try_from(raw_packet[header_len + 1])
|
||||||
|
.map_err(|_| PduError::InvalidDirectiveType {
|
||||||
|
found: raw_packet[header_len],
|
||||||
|
expected: None,
|
||||||
|
})?;
|
||||||
|
if acked_directive == FileDirectiveType::EofPdu {
|
||||||
|
PacketTarget::SourceEntity
|
||||||
|
} else if acked_directive == FileDirectiveType::FinishedPdu {
|
||||||
|
PacketTarget::DestEntity
|
||||||
|
} else {
|
||||||
|
// TODO: Maybe a better error? This might be confusing..
|
||||||
|
return Err(PduError::InvalidDirectiveType {
|
||||||
|
found: raw_packet[header_len + 1],
|
||||||
|
expected: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
pdu_type: pdu_header.pdu_type(),
|
||||||
|
pdu_directive: Some(directive),
|
||||||
|
target: packet_target,
|
||||||
|
raw_packet,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pdu_type(&self) -> PduType {
|
||||||
|
self.pdu_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pdu_directive(&self) -> Option<FileDirectiveType> {
|
||||||
|
self.pdu_directive
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target(&self) -> PacketTarget {
|
||||||
|
self.target
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw_packet(&self) -> &[u8] {
|
||||||
|
self.raw_packet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user