From afd9395ceeb7886af06167060caf914ac6afc8dd Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 17 Aug 2023 22:11:27 +0200 Subject: [PATCH] phew --- satrs-core/Cargo.toml | 2 +- satrs-core/src/cfdp/dest.rs | 65 +++++++++++++-------- satrs-core/src/cfdp/mod.rs | 113 +++++++++++++++++++++++++++++++++++- 3 files changed, 153 insertions(+), 27 deletions(-) diff --git a/satrs-core/Cargo.toml b/satrs-core/Cargo.toml index d0e258b..c53fd08 100644 --- a/satrs-core/Cargo.toml +++ b/satrs-core/Cargo.toml @@ -71,7 +71,7 @@ version = "0.7.0-beta.0" # path = "../../spacepackets" git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git" # rev = "" -branch = "update-lv-tlv" +branch = "pdu-additions" default-features = false [dev-dependencies] diff --git a/satrs-core/src/cfdp/dest.rs b/satrs-core/src/cfdp/dest.rs index ae88b01..04273eb 100644 --- a/satrs-core/src/cfdp/dest.rs +++ b/satrs-core/src/cfdp/dest.rs @@ -7,7 +7,7 @@ use std::{ use super::{ user::{CfdpUser, MetadataReceivedParams}, - State, TransactionId, TransactionStep, CRC_32, + PacketInfo, PacketTarget, State, TransactionId, TransactionStep, CRC_32, }; use smallvec::SmallVec; use spacepackets::{ @@ -20,7 +20,7 @@ use spacepackets::{ CommonPduConfig, FileDirectiveType, PduError, PduHeader, }, tlv::{msg_to_user::MsgToUserTlv, EntityIdTlv, TlvType}, - ConditionCode, PduType, + ConditionCode, PduType, TransmissionMode, }, util::UnsignedByteField, }; @@ -142,28 +142,29 @@ impl DestinationHandler { match self.state { State::Idle => todo!(), 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> { - todo!(); - //Ok(()) - } - pub fn insert_packet_with_known_type( - &mut self, - pdu_type: PduType, - pdu_directive: Option, - raw_packet: &[u8], - ) -> Result<(), DestError> { - match pdu_type { + pub fn insert_packet(&mut self, packet_info: &PacketInfo) -> Result<(), DestError> { + if packet_info.target() != PacketTarget::DestEntity { + // 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(), + )); + } + match packet_info.pdu_type { PduType::FileDirective => { - if pdu_directive.is_none() { + if packet_info.pdu_directive.is_none() { 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.pdu_conf = *metadata_pdu.pdu_header().common_pdu_conf(); 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() { for option_tlv in metadata_pdu.options_iter().unwrap() { if option_tlv.is_standard_tlv() @@ -459,6 +467,9 @@ impl DestinationHandler { #[cfg(test)] mod tests { + #[allow(unused_imports)] + use std::println; + use spacepackets::{ cfdp::{lv::Lv, ChecksumType}, util::{UbfU16, UnsignedByteFieldU16}, @@ -556,12 +567,14 @@ mod tests { #[test] fn test_empty_file_transfer() { 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); init_check(&dest_handler); 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 metadata_params = MetadataGenericParams::new(false, ChecksumType::Crc32, 0); let metadata_pdu = MetadataPdu::new( @@ -574,11 +587,13 @@ mod tests { let written_len = metadata_pdu .write_to_bytes(&mut buf) .expect("writing metadata PDU failed"); - // TODO: Create Metadata PDU and EOF PDU for empty file transfer. - dest_handler.insert_packet( - PduType::FileDirective, - Some(FileDirectiveType::MetadataPdu), - &buf[..written_len], - ); + let packet_info = + PacketInfo::new(&buf[..written_len]).expect("generating packet info failed"); + let insert_result = dest_handler.insert_packet(&packet_info); + if let Err(e) = insert_result { + panic!("insert result error: {e}"); + } + let result = dest_handler.state_machine(&mut test_user); + assert!(result.is_ok()); } } diff --git a/satrs-core/src/cfdp/mod.rs b/satrs-core/src/cfdp/mod.rs index f50faff..4e74d24 100644 --- a/satrs-core/src/cfdp/mod.rs +++ b/satrs-core/src/cfdp/mod.rs @@ -1,11 +1,21 @@ 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")] pub mod dest; pub mod user; #[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TransactionId { source_id: UnsignedByteField, seq_num: UnsignedByteField, @@ -26,6 +36,7 @@ impl TransactionId { } #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum TransactionStep { Idle = 0, TransactionStart = 1, @@ -36,6 +47,7 @@ pub enum TransactionStep { } #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum State { Idle = 0, BusyClass1Nacked = 2, @@ -44,6 +56,105 @@ pub enum State { pub const CRC_32: Crc = Crc::::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, + target: PacketTarget, + raw_packet: &'raw_packet [u8], +} + +impl<'raw> PacketInfo<'raw> { + pub fn new(raw_packet: &'raw [u8]) -> Result { + 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 { + self.pdu_directive + } + + pub fn target(&self) -> PacketTarget { + self.target + } + + pub fn raw_packet(&self) -> &[u8] { + self.raw_packet + } +} + #[cfg(test)] mod tests { #[test]