2023-06-12 03:57:38 +02:00
|
|
|
use crate::cfdp::pdu::{
|
2023-06-12 13:00:58 +02:00
|
|
|
add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
|
|
|
|
FileDirectiveType, PduError, PduHeader,
|
2023-06-12 03:57:38 +02:00
|
|
|
};
|
2023-06-07 01:12:07 +02:00
|
|
|
use crate::cfdp::tlv::EntityIdTlv;
|
2023-06-12 13:00:58 +02:00
|
|
|
use crate::cfdp::{ConditionCode, CrcFlag, LargeFileFlag};
|
2023-06-08 20:50:18 +02:00
|
|
|
use crate::{ByteConversionError, SizeMissmatch};
|
2023-06-07 01:12:07 +02:00
|
|
|
#[cfg(feature = "serde")]
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
2023-07-02 17:20:57 +02:00
|
|
|
/// Finished PDU abstraction.
|
|
|
|
///
|
|
|
|
/// For more information, refer to CFDP chapter 5.2.2.
|
2023-06-07 01:12:07 +02:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
|
|
pub struct EofPdu {
|
2023-06-08 19:24:55 +02:00
|
|
|
pdu_header: PduHeader,
|
2023-06-07 01:12:07 +02:00
|
|
|
condition_code: ConditionCode,
|
|
|
|
file_checksum: u32,
|
|
|
|
file_size: u64,
|
|
|
|
fault_location: Option<EntityIdTlv>,
|
|
|
|
}
|
2023-06-08 19:24:55 +02:00
|
|
|
|
|
|
|
impl EofPdu {
|
|
|
|
pub fn new_no_error(pdu_header: PduHeader, file_checksum: u32, file_size: u64) -> Self {
|
2023-06-08 20:50:18 +02:00
|
|
|
let mut eof_pdu = Self {
|
|
|
|
pdu_header,
|
|
|
|
condition_code: ConditionCode::NoError,
|
|
|
|
file_checksum,
|
|
|
|
file_size,
|
|
|
|
fault_location: None,
|
|
|
|
};
|
|
|
|
eof_pdu.pdu_header.pdu_datafield_len = eof_pdu.calc_pdu_datafield_len() as u16;
|
|
|
|
eof_pdu
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn pdu_header(&self) -> &PduHeader {
|
|
|
|
&self.pdu_header
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn written_len(&self) -> usize {
|
|
|
|
self.pdu_header.header_len() + self.calc_pdu_datafield_len()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn condition_code(&self) -> ConditionCode {
|
|
|
|
self.condition_code
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn file_checksum(&self) -> u32 {
|
|
|
|
self.file_checksum
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn file_size(&self) -> u64 {
|
|
|
|
self.file_size
|
|
|
|
}
|
|
|
|
|
|
|
|
fn calc_pdu_datafield_len(&self) -> usize {
|
|
|
|
// One directive type octet, 4 bits condition code, 4 spare bits.
|
|
|
|
let mut len = 2 + core::mem::size_of::<u32>() + 4;
|
|
|
|
if self.pdu_header.pdu_conf.file_flag == LargeFileFlag::Large {
|
|
|
|
len += 4;
|
|
|
|
}
|
|
|
|
if let Some(fault_location) = self.fault_location {
|
|
|
|
len += fault_location.len_full();
|
|
|
|
}
|
|
|
|
len
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
|
|
|
|
let expected_len = self.written_len();
|
|
|
|
if buf.len() < expected_len {
|
|
|
|
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
|
|
|
found: buf.len(),
|
|
|
|
expected: expected_len,
|
|
|
|
})
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
|
|
|
|
buf[current_idx] = FileDirectiveType::EofPdu as u8;
|
|
|
|
current_idx += 1;
|
|
|
|
buf[current_idx] = (self.condition_code as u8) << 4;
|
|
|
|
current_idx += 1;
|
|
|
|
buf[current_idx..current_idx + 4].copy_from_slice(&self.file_checksum.to_be_bytes());
|
|
|
|
current_idx += 4;
|
|
|
|
current_idx += write_fss_field(
|
|
|
|
self.pdu_header.pdu_conf.file_flag,
|
|
|
|
self.file_size,
|
|
|
|
&mut buf[current_idx..],
|
|
|
|
)?;
|
|
|
|
if let Some(fault_location) = self.fault_location {
|
|
|
|
current_idx += fault_location.write_to_be_bytes(buf)?;
|
|
|
|
}
|
2023-06-12 13:00:58 +02:00
|
|
|
if self.pdu_header.pdu_conf.crc_flag == CrcFlag::WithCrc {
|
|
|
|
current_idx = add_pdu_crc(buf, current_idx);
|
|
|
|
}
|
2023-06-08 20:50:18 +02:00
|
|
|
Ok(current_idx)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_bytes(buf: &[u8]) -> Result<EofPdu, PduError> {
|
|
|
|
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
|
|
|
|
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
|
|
|
|
let is_large_file = pdu_header.pdu_conf.file_flag == LargeFileFlag::Large;
|
|
|
|
let mut min_expected_len = 2 + 4 + 4;
|
|
|
|
if is_large_file {
|
|
|
|
min_expected_len += 4;
|
|
|
|
}
|
2023-06-12 03:57:38 +02:00
|
|
|
generic_length_checks_pdu_deserialization(buf, min_expected_len, full_len_without_crc)?;
|
2023-06-08 20:50:18 +02:00
|
|
|
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
|
|
|
|
PduError::InvalidDirectiveType((buf[current_idx], FileDirectiveType::EofPdu))
|
|
|
|
})?;
|
|
|
|
if directive_type != FileDirectiveType::EofPdu {
|
|
|
|
return Err(PduError::WrongDirectiveType((
|
|
|
|
directive_type,
|
|
|
|
FileDirectiveType::EofPdu,
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
current_idx += 1;
|
|
|
|
let condition_code = ConditionCode::try_from((buf[current_idx] >> 4) & 0b1111)
|
|
|
|
.map_err(|_| PduError::InvalidConditionCode((buf[current_idx] >> 4) & 0b1111))?;
|
|
|
|
current_idx += 1;
|
|
|
|
let file_checksum =
|
|
|
|
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
|
|
|
|
current_idx += 4;
|
|
|
|
let (fss_field_len, file_size) =
|
|
|
|
read_fss_field(pdu_header.pdu_conf.file_flag, &buf[current_idx..]);
|
|
|
|
current_idx += fss_field_len;
|
|
|
|
let mut fault_location = None;
|
|
|
|
if condition_code != ConditionCode::NoError && current_idx < full_len_without_crc {
|
|
|
|
fault_location = Some(EntityIdTlv::from_bytes(&buf[current_idx..])?);
|
|
|
|
}
|
|
|
|
Ok(Self {
|
|
|
|
pdu_header,
|
|
|
|
condition_code,
|
|
|
|
file_checksum,
|
|
|
|
file_size,
|
|
|
|
fault_location,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::cfdp::pdu::eof::EofPdu;
|
|
|
|
use crate::cfdp::pdu::tests::{common_pdu_conf, verify_raw_header};
|
|
|
|
use crate::cfdp::pdu::{FileDirectiveType, PduHeader};
|
|
|
|
use crate::cfdp::{ConditionCode, CrcFlag, LargeFileFlag};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_basic() {
|
|
|
|
let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
|
|
|
|
let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
|
|
|
|
let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
|
|
|
|
assert_eq!(eof_pdu.written_len(), pdu_header.header_len() + 2 + 4 + 4);
|
|
|
|
assert_eq!(eof_pdu.file_checksum(), 0x01020304);
|
|
|
|
assert_eq!(eof_pdu.file_size(), 12);
|
|
|
|
assert_eq!(eof_pdu.condition_code(), ConditionCode::NoError);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_serialization() {
|
|
|
|
let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
|
|
|
|
let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
|
|
|
|
let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
|
|
|
|
let mut buf: [u8; 64] = [0; 64];
|
|
|
|
let res = eof_pdu.write_to_bytes(&mut buf);
|
|
|
|
assert!(res.is_ok());
|
|
|
|
let written = res.unwrap();
|
|
|
|
assert_eq!(written, eof_pdu.written_len());
|
|
|
|
verify_raw_header(eof_pdu.pdu_header(), &buf);
|
|
|
|
let mut current_idx = eof_pdu.pdu_header().header_len();
|
|
|
|
buf[current_idx] = FileDirectiveType::EofPdu as u8;
|
|
|
|
current_idx += 1;
|
|
|
|
assert_eq!(
|
|
|
|
(buf[current_idx] >> 4) & 0b1111,
|
|
|
|
ConditionCode::NoError as u8
|
|
|
|
);
|
|
|
|
current_idx += 1;
|
|
|
|
assert_eq!(
|
|
|
|
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()),
|
|
|
|
0x01020304
|
|
|
|
);
|
|
|
|
current_idx += 4;
|
|
|
|
assert_eq!(
|
|
|
|
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()),
|
|
|
|
12
|
|
|
|
);
|
|
|
|
current_idx += 4;
|
|
|
|
assert_eq!(current_idx, written);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_deserialization() {
|
|
|
|
let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
|
|
|
|
let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
|
|
|
|
let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
|
|
|
|
let mut buf: [u8; 64] = [0; 64];
|
|
|
|
eof_pdu.write_to_bytes(&mut buf).unwrap();
|
|
|
|
let eof_read_back = EofPdu::from_bytes(&buf);
|
|
|
|
if !eof_read_back.is_ok() {
|
|
|
|
let e = eof_read_back.unwrap_err();
|
|
|
|
panic!("deserialization failed with: {e}")
|
|
|
|
}
|
|
|
|
let eof_read_back = eof_read_back.unwrap();
|
|
|
|
assert_eq!(eof_read_back, eof_pdu);
|
|
|
|
}
|
|
|
|
}
|