GCC Code Coverage Report


Directory: Svc/Ccsds/CfdpManager/
File: Types/PduHeader.cpp
Date: 2026-11-03 21:17:54
Exec Total Coverage
Lines: 140 151 92.7%
Functions: 8 8 100.0%
Branches: 58 89 65.2%

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