F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
PduHeader.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title PduHeader.cpp
3 // \author Brian Campuzano
4 // \brief cpp file for CFDP PDU Header
5 // ======================================================================
6 
7 #include <Fw/Types/Assert.hpp>
11 
12 namespace Svc {
13 namespace Ccsds {
14 namespace Cfdp {
15 
17  PduDirection direction,
18  Cfdp::Class::T txmMode,
19  EntityId sourceEid,
20  TransactionSeq transactionSeq,
21  EntityId destEid) {
22  this->m_type = type;
23  this->m_version = CFDP_VERSION;
25  this->m_direction = direction;
26  this->m_class = txmMode;
27  this->m_crcFlag = CrcFlag::CRC_NOT_PRESENT; // CRC not currently supported
28  this->m_largeFileFlag = LargeFileFlag::LARGE_FILE_32_BIT; // 32-bit file sizes
29  this->m_segmentationControl = 0;
30  this->m_segmentMetadataFlag = 0;
31  this->m_pduDataLength = 0; // To be set later
32  this->m_sourceEid = sourceEid;
33  this->m_transactionSeq = transactionSeq;
34  this->m_destEid = destEid;
35 }
36 
37 // Helper function to calculate minimum bytes needed to encode a value
39  U8 minSize;
40  U64 limit = 0x100;
41 
42  for (minSize = 1; minSize < 8 && value >= limit; ++minSize) {
43  limit <<= 8;
44  }
45 
46  return minSize;
47 }
48 
49 // Helper function to encode an integer in variable-length format
50 static Fw::SerializeStatus encodeIntegerInSize(Fw::SerialBufferBase& serialBuffer, U64 value, U8 encodeSize) {
51  // Encode from MSB to LSB (big-endian)
52  for (U8 i = 0; i < encodeSize; ++i) {
53  U8 shift = static_cast<U8>((encodeSize - 1 - i) * 8);
54  U8 byte = static_cast<U8>((value >> shift) & 0xFF);
55  Fw::SerializeStatus status = serialBuffer.serializeFrom(byte);
56  if (status != Fw::FW_SERIALIZE_OK) {
57  return status;
58  }
59  }
60  return Fw::FW_SERIALIZE_OK;
61 }
62 
63 // Helper function to decode an integer from variable-length format
64 static U64 decodeIntegerInSize(Fw::SerialBufferBase& serialBuffer, U8 decodeSize, Fw::SerializeStatus& status) {
65  U64 value = 0;
66 
67  // Decode from MSB to LSB (big-endian)
68  for (U8 i = 0; i < decodeSize; ++i) {
69  U8 byte;
70  status = serialBuffer.deserializeTo(byte);
71  if (status != Fw::FW_SERIALIZE_OK) {
72  return 0;
73  }
74  value = (value << 8) | byte;
75  }
76 
77  return value;
78 }
79 
80 // Helper function to decode a variable-width field into a configured field type, rejecting values that do not fit
81 template <typename T>
82 static Fw::SerializeStatus decodeFieldInSize(Fw::SerialBufferBase& serialBuffer, U8 decodeSize, T& field) {
84  const U64 value = decodeIntegerInSize(serialBuffer, decodeSize, status);
85  if (status != Fw::FW_SERIALIZE_OK) {
86  return status;
87  }
88  if (static_cast<U64>(static_cast<T>(value)) != value) {
90  }
91  field = static_cast<T>(value);
92  return Fw::FW_SERIALIZE_OK;
93 }
94 
96  // Fixed portion: flags(1) + length(2) + eidTsnLengths(1) = 4 bytes
97  U32 size = 4;
98 
99  // Variable-size entity IDs and transaction sequence number based on actual values
100  U8 eidSize = getValueEncodedSize(this->m_sourceEid > this->m_destEid ? this->m_sourceEid : this->m_destEid);
101  U8 tsnSize = getValueEncodedSize(this->m_transactionSeq);
102 
103  size += eidSize; // source EID
104  size += tsnSize; // transaction sequence number
105  size += eidSize; // destination EID
106 
107  return size;
108 }
109 
111  Fw::SerializeStatus status;
112 
113  // Variable-size entity IDs and transaction sequence number based on actual values
114  U8 eidSize = getValueEncodedSize(this->m_sourceEid > this->m_destEid ? this->m_sourceEid : this->m_destEid);
115  U8 tsnSize = getValueEncodedSize(this->m_transactionSeq);
116 
117  // Byte 0: flags
118  // bits 7-5: version (001b = 1)
119  // bit 4: pdu_type (0=directive, 1=file data)
120  // bit 3: direction (0=toward receiver, 1=toward sender)
121  // bit 2: txm_mode (0=ack, 1=unack)
122  // bit 1: crc_flag (0=not present, 1=present)
123  // bit 0: large_file_flag (0=32-bit, 1=64-bit)
124  U8 flags = 0;
125  flags = static_cast<U8>(flags | static_cast<U8>((this->m_version & 0x07U) << 5));
126  flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_pduType) & 0x01U) << 4));
127  flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_direction) & 0x01U) << 3));
128  flags = static_cast<U8>(flags | static_cast<U8>((this->m_class & 0x01U) << 2));
129  flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_crcFlag) & 0x01U) << 1));
130  flags = static_cast<U8>(flags | (static_cast<U8>(this->m_largeFileFlag) & 0x01U));
131 
132  status = serialBuffer.serializeFrom(flags);
133  if (status != Fw::FW_SERIALIZE_OK) {
134  return status;
135  }
136 
137  // Bytes 1-2: PDU data length (big-endian)
138  status = serialBuffer.serializeFrom(static_cast<U16>(this->m_pduDataLength));
139  if (status != Fw::FW_SERIALIZE_OK) {
140  return status;
141  }
142 
143  // Byte 3: eidTsnLengths
144  // bit 7: segmentation_control
145  // bits 6-4: eid_length - 1 (3 bits)
146  // bit 3: segment_metadata_flag
147  // bits 2-0: tsn_length - 1 (3 bits)
148  U8 eidTsnLengths = 0;
149  eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>((this->m_segmentationControl & 0x01U) << 7));
150  eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>(((eidSize - 1U) & 0x07U) << 4));
151  eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>((this->m_segmentMetadataFlag & 0x01U) << 3));
152  eidTsnLengths = static_cast<U8>(eidTsnLengths | ((tsnSize - 1U) & 0x07U));
153 
154  status = serialBuffer.serializeFrom(eidTsnLengths);
155  if (status != Fw::FW_SERIALIZE_OK) {
156  return status;
157  }
158 
159  // Variable-width fields (size based on actual values)
160  status = encodeIntegerInSize(serialBuffer, this->m_sourceEid, eidSize);
161  if (status != Fw::FW_SERIALIZE_OK) {
162  return status;
163  }
164 
165  status = encodeIntegerInSize(serialBuffer, this->m_transactionSeq, tsnSize);
166  if (status != Fw::FW_SERIALIZE_OK) {
167  return status;
168  }
169 
170  status = encodeIntegerInSize(serialBuffer, this->m_destEid, eidSize);
171  if (status != Fw::FW_SERIALIZE_OK) {
172  return status;
173  }
174 
175  return Fw::FW_SERIALIZE_OK;
176 }
177 
179  Fw::SerializeStatus status;
180 
181  // Byte 0: flags
182  U8 flags;
183  status = serialBuffer.deserializeTo(flags);
184  if (status != Fw::FW_SERIALIZE_OK) {
185  return status;
186  }
187 
188  this->m_version = (flags >> 5) & 0x07;
189  this->m_pduType = static_cast<PduType>((flags >> 4) & 0x01);
190  this->m_direction = static_cast<PduDirection>((flags >> 3) & 0x01);
191  this->m_class = static_cast<Cfdp::Class::T>((flags >> 2) & 0x01);
192  this->m_crcFlag = static_cast<CrcFlag>((flags >> 1) & 0x01);
193  this->m_largeFileFlag = static_cast<LargeFileFlag>(flags & 0x01);
194 
195  // Reject headers announcing protocol features the body decoders do not implement:
196  // other protocol versions, a trailing CRC, and 64-bit file sizes when FileSize is narrower
197  if ((this->m_version != CFDP_VERSION) || (this->m_crcFlag != CrcFlag::CRC_NOT_PRESENT) ||
198  ((this->m_largeFileFlag == LargeFileFlag::LARGE_FILE_64_BIT) && (sizeof(FileSize) < sizeof(U64)))) {
200  }
201 
202  // Bytes 1-2: PDU data length
203  status = serialBuffer.deserializeTo(this->m_pduDataLength);
204  if (status != Fw::FW_SERIALIZE_OK) {
205  return status;
206  }
207 
208  // Byte 3: eidTsnLengths
209  U8 eidTsnLengths;
210  status = serialBuffer.deserializeTo(eidTsnLengths);
211  if (status != Fw::FW_SERIALIZE_OK) {
212  return status;
213  }
214 
215  this->m_segmentationControl = (eidTsnLengths >> 7) & 0x01;
216  U8 eidSize = static_cast<U8>(((eidTsnLengths >> 4) & 0x07) + 1);
217  this->m_segmentMetadataFlag = (eidTsnLengths >> 3) & 0x01;
218  U8 tsnSize = static_cast<U8>((eidTsnLengths & 0x07) + 1);
219 
220  // Validate that the sizes are within bounds (1-8 bytes)
221  FW_ASSERT(eidSize >= 1 && eidSize <= 8, static_cast<FwAssertArgType>(eidSize));
222  FW_ASSERT(tsnSize >= 1 && tsnSize <= 8, static_cast<FwAssertArgType>(tsnSize));
223 
224  // Variable-width fields (size determined by encoded length). Any wire width up to 8 bytes is
225  // accepted, but the decoded value must fit the configured EntityId/TransactionSeq type.
226  status = decodeFieldInSize(serialBuffer, eidSize, this->m_sourceEid);
227  if (status != Fw::FW_SERIALIZE_OK) {
228  return status;
229  }
230 
231  status = decodeFieldInSize(serialBuffer, tsnSize, this->m_transactionSeq);
232  if (status != Fw::FW_SERIALIZE_OK) {
233  return status;
234  }
235 
236  status = decodeFieldInSize(serialBuffer, eidSize, this->m_destEid);
237  if (status != Fw::FW_SERIALIZE_OK) {
238  return status;
239  }
240 
241  // Don't set m_type yet - it will be determined by the directive code for directive PDUs
242  // or set to FILE_DATA for file data PDUs
243  if (this->m_pduType == PduType::PDU_TYPE_FILE_DATA) {
244  this->m_type = PduTypeEnum::FILE_DATA;
245  } else {
246  // For directive PDUs, type will be set when directive code is read
247  this->m_type = PduTypeEnum::NONE;
248  }
249 
250  return Fw::FW_SERIALIZE_OK;
251 }
252 
254  PduTypeEnum::T pduTypeEnum = PduTypeEnum::NONE;
255 
256  // Check minimum size for a PDU header
257  if (buffer.getSize() >= PduHeader::MIN_HEADERSIZE) {
258  const U8* data = buffer.getData();
259  FW_ASSERT(data != nullptr);
260 
261  // Byte 0: flags
262  // Bit 4 is PDU type: 0 = FILE_DATA, 1 = FILE_DIRECTIVE
263  U8 flags = data[0];
264  PduType pduType = static_cast<PduType>((flags >> 4) & 0x01);
265 
266  if (pduType == PduType::PDU_TYPE_FILE_DATA) {
267  pduTypeEnum = PduTypeEnum::FILE_DATA;
268  } else {
269  // For directive PDUs, we need to read the directive code
270  // Parse byte 3 to get EID and TSN lengths
271  U8 eidTsnLengths = data[3];
272  U8 eidSize = static_cast<U8>(((eidTsnLengths >> 4) & 0x07) + 1); // Bits 6-4: EID length - 1
273  U8 tsnSize = static_cast<U8>((eidTsnLengths & 0x07) + 1); // Bits 2-0: TSN length - 1
274 
275  // Calculate offset to directive code: 4 (fixed header) + eidSize + tsnSize + eidSize
276  U32 directiveCodeOffset = 4 + (2 * eidSize) + tsnSize;
277 
278  // Validate offset is within buffer bounds before reading
279  if (directiveCodeOffset < buffer.getSize()) {
280  // Read directive code
281  U8 directiveCode = data[directiveCodeOffset];
282 
283  // Map directive code to PduTypeEnum
284  switch (directiveCode) {
285  case static_cast<U8>(FileDirective::FILE_DIRECTIVE_METADATA):
286  pduTypeEnum = PduTypeEnum::METADATA;
287  break;
289  pduTypeEnum = PduTypeEnum::END_OF_FILE;
290  break;
291  case static_cast<U8>(FileDirective::FILE_DIRECTIVE_FIN):
292  pduTypeEnum = PduTypeEnum::FINISHED;
293  break;
294  case static_cast<U8>(FileDirective::FILE_DIRECTIVE_ACK):
295  pduTypeEnum = PduTypeEnum::ACKNOWLEDGMENT;
296  break;
297  case static_cast<U8>(FileDirective::FILE_DIRECTIVE_NAK):
298  pduTypeEnum = PduTypeEnum::NEGATIVE_ACK;
299  break;
300  default:
301  pduTypeEnum = PduTypeEnum::NONE; // Unknown directive code
302  }
303  }
304  }
305  }
306  return pduTypeEnum;
307 }
308 
309 } // namespace Cfdp
310 } // namespace Ccsds
311 } // namespace Svc
Serialization/Deserialization operation was successful.
U32 EntityId
Entity id size.
static U64 decodeIntegerInSize(Fw::SerialBufferBase &serialBuffer, U8 decodeSize, Fw::SerializeStatus &status)
Definition: PduHeader.cpp:64
Invalid or unknown PDU type.
static Fw::SerializeStatus decodeFieldInSize(Fw::SerialBufferBase &serialBuffer, U8 decodeSize, T &field)
Definition: PduHeader.cpp:82
static Fw::SerializeStatus encodeIntegerInSize(Fw::SerialBufferBase &serialBuffer, U64 value, U8 encodeSize)
Definition: PduHeader.cpp:50
U8 * getData() const
Definition: Buffer.cpp:82
Deserialization data had incorrect values (unexpected data types)
void initialize(PduTypeEnum::T type, PduDirection direction, Cfdp::Class::T txmMode, EntityId sourceEid, TransactionSeq transactionSeq, EntityId destEid)
Initialize a PDU header.
Definition: PduHeader.cpp:16
virtual SerializeStatus serializeFrom(U8 val, Endianness mode=Endianness::BIG)=0
Serialize an 8-bit unsigned integer value.
Fw::SerializeStatus fromSerialBuffer(Fw::SerialBufferBase &serialBuffer)
Initialize this Header from a SerialBufferBase.
Definition: PduHeader.cpp:178
SerializeStatus
forward declaration for string
virtual SerializeStatus deserializeTo(U8 &val, Endianness mode=Endianness::BIG)=0
Deserialize an 8-bit unsigned integer value.
U32 TransactionSeq
transaction sequence number size
U32 getBufferSize() const
Compute the buffer size needed to hold this Header.
Definition: PduHeader.cpp:95
U32 FileSize
File size and offset type.
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:54
FwSizeType getSize() const
Definition: Buffer.cpp:90
PduTypeEnum::T peekPduType(const Fw::Buffer &buffer)
Definition: PduHeader.cpp:253
Fw::SerializeStatus toSerialBuffer(Fw::SerialBufferBase &serialBuffer) const
Write this Header to a SerialBufferBase.
Definition: PduHeader.cpp:110
RateGroupDivider component implementation.
T
The raw enum type.
Definition: ClassEnumAc.hpp:42
#define FW_ASSERT(...)
Definition: Assert.hpp:14
static U8 getValueEncodedSize(U64 value)
Definition: PduHeader.cpp:38