GCC Code Coverage Report


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