GCC Code Coverage Report


Directory: ./
File: Svc/Ccsds/CfdpManager/Types/PduHeader.cpp
Date: 2026-09-23 22:11:34
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