GCC Code Coverage Report


Directory: Svc/Ccsds/CfdpManager/Types/
File: PduHeader.cpp
Date: 2026-09-03 21:16:30
Exec Total Coverage
Lines: 103 151 68.2%
Functions: 7 8 87.5%
Branches: 41 89 46.1%

Line Branch Exec Source
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>
8 #include <Fw/Types/StringUtils.hpp>
9 #include <Svc/Ccsds/CfdpManager/Types/PduHeader.hpp>
10 #include <Svc/Ccsds/CfdpManager/Types/Types.hpp>
11
12 namespace Svc {
13 namespace Ccsds {
14 namespace Cfdp {
15
16 80 void PduHeader::initialize(PduTypeEnum::T type,
17 PduDirection direction,
18 Cfdp::Class::T txmMode,
19 EntityId sourceEid,
20 TransactionSeq transactionSeq,
21 EntityId destEid) {
22 80 this->m_type = type;
23 80 this->m_version = 1; // CFDP version is always 1
24
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 76 times.
80 this->m_pduType = (type == PduTypeEnum::FILE_DATA) ? PduType::PDU_TYPE_FILE_DATA : PduType::PDU_TYPE_DIRECTIVE;
25 80 this->m_direction = direction;
26 80 this->m_class = txmMode;
27 80 this->m_crcFlag = CrcFlag::CRC_NOT_PRESENT; // CRC not currently supported
28 80 this->m_largeFileFlag = LargeFileFlag::LARGE_FILE_32_BIT; // 32-bit file sizes
29 80 this->m_segmentationControl = 0;
30 80 this->m_segmentMetadataFlag = 0;
31 80 this->m_pduDataLength = 0; // To be set later
32 80 this->m_sourceEid = sourceEid;
33 80 this->m_transactionSeq = transactionSeq;
34 80 this->m_destEid = destEid;
35 80 }
36
37 // Helper function to calculate minimum bytes needed to encode a value
38 436 U8 PduHeader::getValueEncodedSize(U64 value) {
39 U8 minSize;
40 436 U64 limit = 0x100;
41
42
3/4
✓ Branch 0 taken 444 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 436 times.
444 for (minSize = 1; minSize < 8 && value >= limit; ++minSize) {
43 8 limit <<= 8;
44 }
45
46 436 return minSize;
47 }
48
49 // Helper function to encode an integer in variable-length format
50 201 static Fw::SerializeStatus encodeIntegerInSize(Fw::SerialBufferBase& serialBuffer, U64 value, U8 encodeSize) {
51 // Encode from MSB to LSB (big-endian)
52
2/2
✓ Branch 0 taken 205 times.
✓ Branch 1 taken 201 times.
406 for (U8 i = 0; i < encodeSize; ++i) {
53 205 U8 shift = static_cast<U8>((encodeSize - 1 - i) * 8);
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 205 times.
205 U8 byte = static_cast<U8>((value >> shift) & 0xFF);
55 205 Fw::SerializeStatus status = serialBuffer.serializeFrom(byte);
56
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 205 times.
205 if (status != Fw::FW_SERIALIZE_OK) {
57 return status;
58 }
59 }
60 201 return Fw::FW_SERIALIZE_OK;
61 }
62
63 // Helper function to decode an integer from variable-length format
64 192 static U64 decodeIntegerInSize(Fw::SerialBufferBase& serialBuffer, U8 decodeSize, Fw::SerializeStatus& status) {
65 192 U64 value = 0;
66
67 // Decode from MSB to LSB (big-endian)
68
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 192 times.
388 for (U8 i = 0; i < decodeSize; ++i) {
69 196 U8 byte;
70
1/1
✓ Branch 7 taken 196 times.
196 status = serialBuffer.deserializeTo(byte);
71
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 196 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 196 times.
196 if (status != Fw::FW_SERIALIZE_OK) {
72 return 0;
73 }
74 196 value = (value << 8) | byte;
75 }
76
77 192 return value;
78 }
79
80 151 U32 PduHeader::getBufferSize() const {
81 // Fixed portion: flags(1) + length(2) + eidTsnLengths(1) = 4 bytes
82 151 U32 size = 4;
83
84 // Variable-size entity IDs and transaction sequence number based on actual values
85 151 U8 eidSize = getValueEncodedSize(this->m_sourceEid > this->m_destEid ? this->m_sourceEid : this->m_destEid);
86 151 U8 tsnSize = getValueEncodedSize(this->m_transactionSeq);
87
88 151 size += eidSize; // source EID
89 151 size += tsnSize; // transaction sequence number
90 151 size += eidSize; // destination EID
91
92 151 return size;
93 }
94
95 67 Fw::SerializeStatus PduHeader::toSerialBuffer(Fw::SerialBufferBase& serialBuffer) const {
96 Fw::SerializeStatus status;
97
98 // Variable-size entity IDs and transaction sequence number based on actual values
99 67 U8 eidSize = getValueEncodedSize(this->m_sourceEid > this->m_destEid ? this->m_sourceEid : this->m_destEid);
100 67 U8 tsnSize = getValueEncodedSize(this->m_transactionSeq);
101
102 // Byte 0: flags
103 // bits 7-5: version (001b = 1)
104 // bit 4: pdu_type (0=directive, 1=file data)
105 // bit 3: direction (0=toward receiver, 1=toward sender)
106 // bit 2: txm_mode (0=ack, 1=unack)
107 // bit 1: crc_flag (0=not present, 1=present)
108 // bit 0: large_file_flag (0=32-bit, 1=64-bit)
109 67 U8 flags = 0;
110 67 flags = static_cast<U8>(flags | static_cast<U8>((this->m_version & 0x07U) << 5));
111 67 flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_pduType) & 0x01U) << 4));
112 67 flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_direction) & 0x01U) << 3));
113
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
67 flags = static_cast<U8>(flags | static_cast<U8>((this->m_class & 0x01U) << 2));
114 67 flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_crcFlag) & 0x01U) << 1));
115 67 flags = static_cast<U8>(flags | (static_cast<U8>(this->m_largeFileFlag) & 0x01U));
116
117 67 status = serialBuffer.serializeFrom(flags);
118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
67 if (status != Fw::FW_SERIALIZE_OK) {
119 return status;
120 }
121
122 // Bytes 1-2: PDU data length (big-endian)
123 67 status = serialBuffer.serializeFrom(static_cast<U16>(this->m_pduDataLength));
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
67 if (status != Fw::FW_SERIALIZE_OK) {
125 return status;
126 }
127
128 // Byte 3: eidTsnLengths
129 // bit 7: segmentation_control
130 // bits 6-4: eid_length - 1 (3 bits)
131 // bit 3: segment_metadata_flag
132 // bits 2-0: tsn_length - 1 (3 bits)
133 67 U8 eidTsnLengths = 0;
134 67 eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>((this->m_segmentationControl & 0x01U) << 7));
135 67 eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>(((eidSize - 1U) & 0x07U) << 4));
136 67 eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>((this->m_segmentMetadataFlag & 0x01U) << 3));
137 67 eidTsnLengths = static_cast<U8>(eidTsnLengths | ((tsnSize - 1U) & 0x07U));
138
139 67 status = serialBuffer.serializeFrom(eidTsnLengths);
140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
67 if (status != Fw::FW_SERIALIZE_OK) {
141 return status;
142 }
143
144 // Variable-width fields (size based on actual values)
145 67 status = encodeIntegerInSize(serialBuffer, this->m_sourceEid, eidSize);
146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
67 if (status != Fw::FW_SERIALIZE_OK) {
147 return status;
148 }
149
150 67 status = encodeIntegerInSize(serialBuffer, this->m_transactionSeq, tsnSize);
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
67 if (status != Fw::FW_SERIALIZE_OK) {
152 return status;
153 }
154
155 67 status = encodeIntegerInSize(serialBuffer, this->m_destEid, eidSize);
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
67 if (status != Fw::FW_SERIALIZE_OK) {
157 return status;
158 }
159
160 67 return Fw::FW_SERIALIZE_OK;
161 }
162
163 64 Fw::SerializeStatus PduHeader::fromSerialBuffer(Fw::SerialBufferBase& serialBuffer) {
164 64 Fw::SerializeStatus status;
165
166 // Byte 0: flags
167 64 U8 flags;
168
1/1
✓ Branch 7 taken 64 times.
64 status = serialBuffer.deserializeTo(flags);
169
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
64 if (status != Fw::FW_SERIALIZE_OK) {
170 return status;
171 }
172
173 64 this->m_version = (flags >> 5) & 0x07;
174 64 this->m_pduType = static_cast<PduType>((flags >> 4) & 0x01);
175 64 this->m_direction = static_cast<PduDirection>((flags >> 3) & 0x01);
176 64 this->m_class = static_cast<Cfdp::Class::T>((flags >> 2) & 0x01);
177 64 this->m_crcFlag = static_cast<CrcFlag>((flags >> 1) & 0x01);
178 64 this->m_largeFileFlag = static_cast<LargeFileFlag>(flags & 0x01);
179
180 // Bytes 1-2: PDU data length
181
1/1
✓ Branch 10 taken 64 times.
64 status = serialBuffer.deserializeTo(this->m_pduDataLength);
182
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
64 if (status != Fw::FW_SERIALIZE_OK) {
183 return status;
184 }
185
186 // Byte 3: eidTsnLengths
187 64 U8 eidTsnLengths;
188
1/1
✓ Branch 7 taken 64 times.
64 status = serialBuffer.deserializeTo(eidTsnLengths);
189
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
64 if (status != Fw::FW_SERIALIZE_OK) {
190 return status;
191 }
192
193 64 this->m_segmentationControl = (eidTsnLengths >> 7) & 0x01;
194 64 U8 eidSize = static_cast<U8>(((eidTsnLengths >> 4) & 0x07) + 1);
195 64 this->m_segmentMetadataFlag = (eidTsnLengths >> 3) & 0x01;
196 64 U8 tsnSize = static_cast<U8>((eidTsnLengths & 0x07) + 1);
197
198 // Validate that the sizes are within bounds (1-8 bytes)
199 64 FW_ASSERT(eidSize >= 1 && eidSize <= 8, static_cast<FwAssertArgType>(eidSize));
200 64 FW_ASSERT(tsnSize >= 1 && tsnSize <= 8, static_cast<FwAssertArgType>(tsnSize));
201
202 // Variable-width fields (size determined by encoded length)
203
1/1
✓ Branch 1 taken 64 times.
64 this->m_sourceEid = static_cast<EntityId>(decodeIntegerInSize(serialBuffer, eidSize, status));
204
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
64 if (status != Fw::FW_SERIALIZE_OK) {
205 return status;
206 }
207
208
1/1
✓ Branch 1 taken 64 times.
64 this->m_transactionSeq = static_cast<TransactionSeq>(decodeIntegerInSize(serialBuffer, tsnSize, status));
209
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
64 if (status != Fw::FW_SERIALIZE_OK) {
210 return status;
211 }
212
213
1/1
✓ Branch 1 taken 64 times.
64 this->m_destEid = static_cast<EntityId>(decodeIntegerInSize(serialBuffer, eidSize, status));
214
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
64 if (status != Fw::FW_SERIALIZE_OK) {
215 return status;
216 }
217
218 // Don't set m_type yet - it will be determined by the directive code for directive PDUs
219 // or set to FILE_DATA for file data PDUs
220
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 62 times.
64 if (this->m_pduType == PduType::PDU_TYPE_FILE_DATA) {
221 2 this->m_type = PduTypeEnum::FILE_DATA;
222 } else {
223 // For directive PDUs, type will be set when directive code is read
224 62 this->m_type = PduTypeEnum::NONE;
225 }
226
227 64 return Fw::FW_SERIALIZE_OK;
228 }
229
230 PduTypeEnum::T peekPduType(const Fw::Buffer& buffer) {
231 PduTypeEnum::T pduTypeEnum = PduTypeEnum::NONE;
232
233 // Check minimum size for a PDU header
234 if (buffer.getSize() >= PduHeader::MIN_HEADERSIZE) {
235 const U8* data = buffer.getData();
236 FW_ASSERT(data != nullptr);
237
238 // Byte 0: flags
239 // Bit 4 is PDU type: 0 = FILE_DATA, 1 = FILE_DIRECTIVE
240 U8 flags = data[0];
241 PduType pduType = static_cast<PduType>((flags >> 4) & 0x01);
242
243 if (pduType == PduType::PDU_TYPE_FILE_DATA) {
244 pduTypeEnum = PduTypeEnum::FILE_DATA;
245 } else {
246 // For directive PDUs, we need to read the directive code
247 // Parse byte 3 to get EID and TSN lengths
248 U8 eidTsnLengths = data[3];
249 U8 eidSize = static_cast<U8>(((eidTsnLengths >> 4) & 0x07) + 1); // Bits 6-4: EID length - 1
250 U8 tsnSize = static_cast<U8>((eidTsnLengths & 0x07) + 1); // Bits 2-0: TSN length - 1
251
252 // Calculate offset to directive code: 4 (fixed header) + eidSize + tsnSize + eidSize
253 U32 directiveCodeOffset = 4 + (2 * eidSize) + tsnSize;
254
255 // Validate offset is within buffer bounds before reading
256 if (directiveCodeOffset < buffer.getSize()) {
257 // Read directive code
258 U8 directiveCode = data[directiveCodeOffset];
259
260 // Map directive code to PduTypeEnum
261 switch (directiveCode) {
262 case static_cast<U8>(FileDirective::FILE_DIRECTIVE_METADATA):
263 pduTypeEnum = PduTypeEnum::METADATA;
264 break;
265 case static_cast<U8>(FileDirective::FILE_DIRECTIVE_END_OF_FILE):
266 pduTypeEnum = PduTypeEnum::END_OF_FILE;
267 break;
268 case static_cast<U8>(FileDirective::FILE_DIRECTIVE_FIN):
269 pduTypeEnum = PduTypeEnum::FINISHED;
270 break;
271 case static_cast<U8>(FileDirective::FILE_DIRECTIVE_ACK):
272 pduTypeEnum = PduTypeEnum::ACKNOWLEDGMENT;
273 break;
274 case static_cast<U8>(FileDirective::FILE_DIRECTIVE_NAK):
275 pduTypeEnum = PduTypeEnum::NEGATIVE_ACK;
276 break;
277 default:
278 pduTypeEnum = PduTypeEnum::NONE; // Unknown directive code
279 }
280 }
281 }
282 }
283 return pduTypeEnum;
284 }
285
286 } // namespace Cfdp
287 } // namespace Ccsds
288 } // namespace Svc
289