GCC Code Coverage Report


Directory: ./
File: PduHeader.cpp
Date: 2026-09-03 22:13:23
Exec Total Coverage
Lines: 0 147 0.0%
Functions: 0 8 0.0%
Branches: 0 59 0.0%

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 void PduHeader::initialize(PduTypeEnum::T type,
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 = 1; // CFDP version is always 1
24 this->m_pduType = (type == PduTypeEnum::FILE_DATA) ? PduType::PDU_TYPE_FILE_DATA : PduType::PDU_TYPE_DIRECTIVE;
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
38 U8 PduHeader::getValueEncodedSize(U64 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 U32 PduHeader::getBufferSize() const {
81 // Fixed portion: flags(1) + length(2) + eidTsnLengths(1) = 4 bytes
82 U32 size = 4;
83
84 // Variable-size entity IDs and transaction sequence number based on actual values
85 U8 eidSize = getValueEncodedSize(this->m_sourceEid > this->m_destEid ? this->m_sourceEid : this->m_destEid);
86 U8 tsnSize = getValueEncodedSize(this->m_transactionSeq);
87
88 size += eidSize; // source EID
89 size += tsnSize; // transaction sequence number
90 size += eidSize; // destination EID
91
92 return size;
93 }
94
95 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 U8 eidSize = getValueEncodedSize(this->m_sourceEid > this->m_destEid ? this->m_sourceEid : this->m_destEid);
100 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 U8 flags = 0;
110 flags = static_cast<U8>(flags | static_cast<U8>((this->m_version & 0x07U) << 5));
111 flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_pduType) & 0x01U) << 4));
112 flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_direction) & 0x01U) << 3));
113 flags = static_cast<U8>(flags | static_cast<U8>((this->m_class & 0x01U) << 2));
114 flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_crcFlag) & 0x01U) << 1));
115 flags = static_cast<U8>(flags | (static_cast<U8>(this->m_largeFileFlag) & 0x01U));
116
117 status = serialBuffer.serializeFrom(flags);
118 if (status != Fw::FW_SERIALIZE_OK) {
119 return status;
120 }
121
122 // Bytes 1-2: PDU data length (big-endian)
123 status = serialBuffer.serializeFrom(static_cast<U16>(this->m_pduDataLength));
124 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 U8 eidTsnLengths = 0;
134 eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>((this->m_segmentationControl & 0x01U) << 7));
135 eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>(((eidSize - 1U) & 0x07U) << 4));
136 eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>((this->m_segmentMetadataFlag & 0x01U) << 3));
137 eidTsnLengths = static_cast<U8>(eidTsnLengths | ((tsnSize - 1U) & 0x07U));
138
139 status = serialBuffer.serializeFrom(eidTsnLengths);
140 if (status != Fw::FW_SERIALIZE_OK) {
141 return status;
142 }
143
144 // Variable-width fields (size based on actual values)
145 status = encodeIntegerInSize(serialBuffer, this->m_sourceEid, eidSize);
146 if (status != Fw::FW_SERIALIZE_OK) {
147 return status;
148 }
149
150 status = encodeIntegerInSize(serialBuffer, this->m_transactionSeq, tsnSize);
151 if (status != Fw::FW_SERIALIZE_OK) {
152 return status;
153 }
154
155 status = encodeIntegerInSize(serialBuffer, this->m_destEid, eidSize);
156 if (status != Fw::FW_SERIALIZE_OK) {
157 return status;
158 }
159
160 return Fw::FW_SERIALIZE_OK;
161 }
162
163 Fw::SerializeStatus PduHeader::fromSerialBuffer(Fw::SerialBufferBase& serialBuffer) {
164 Fw::SerializeStatus status;
165
166 // Byte 0: flags
167 U8 flags;
168 status = serialBuffer.deserializeTo(flags);
169 if (status != Fw::FW_SERIALIZE_OK) {
170 return status;
171 }
172
173 this->m_version = (flags >> 5) & 0x07;
174 this->m_pduType = static_cast<PduType>((flags >> 4) & 0x01);
175 this->m_direction = static_cast<PduDirection>((flags >> 3) & 0x01);
176 this->m_class = static_cast<Cfdp::Class::T>((flags >> 2) & 0x01);
177 this->m_crcFlag = static_cast<CrcFlag>((flags >> 1) & 0x01);
178 this->m_largeFileFlag = static_cast<LargeFileFlag>(flags & 0x01);
179
180 // Bytes 1-2: PDU data length
181 status = serialBuffer.deserializeTo(this->m_pduDataLength);
182 if (status != Fw::FW_SERIALIZE_OK) {
183 return status;
184 }
185
186 // Byte 3: eidTsnLengths
187 U8 eidTsnLengths;
188 status = serialBuffer.deserializeTo(eidTsnLengths);
189 if (status != Fw::FW_SERIALIZE_OK) {
190 return status;
191 }
192
193 this->m_segmentationControl = (eidTsnLengths >> 7) & 0x01;
194 U8 eidSize = static_cast<U8>(((eidTsnLengths >> 4) & 0x07) + 1);
195 this->m_segmentMetadataFlag = (eidTsnLengths >> 3) & 0x01;
196 U8 tsnSize = static_cast<U8>((eidTsnLengths & 0x07) + 1);
197
198 // Validate that the sizes are within bounds (1-8 bytes)
199 FW_ASSERT(eidSize >= 1 && eidSize <= 8, static_cast<FwAssertArgType>(eidSize));
200 FW_ASSERT(tsnSize >= 1 && tsnSize <= 8, static_cast<FwAssertArgType>(tsnSize));
201
202 // Variable-width fields (size determined by encoded length)
203 this->m_sourceEid = static_cast<EntityId>(decodeIntegerInSize(serialBuffer, eidSize, status));
204 if (status != Fw::FW_SERIALIZE_OK) {
205 return status;
206 }
207
208 this->m_transactionSeq = static_cast<TransactionSeq>(decodeIntegerInSize(serialBuffer, tsnSize, status));
209 if (status != Fw::FW_SERIALIZE_OK) {
210 return status;
211 }
212
213 this->m_destEid = static_cast<EntityId>(decodeIntegerInSize(serialBuffer, eidSize, status));
214 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 if (this->m_pduType == PduType::PDU_TYPE_FILE_DATA) {
221 this->m_type = PduTypeEnum::FILE_DATA;
222 } else {
223 // For directive PDUs, type will be set when directive code is read
224 this->m_type = PduTypeEnum::NONE;
225 }
226
227 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