| 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 |