| 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 | 80 | void PduHeader::initialize(PduTypeEnum::T type, | |
| 17 | PduDirection direction, | ||
| 18 | Cfdp::Class::T txmMode, | ||
| 19 | EntityId sourceEid, | ||
| 20 | TransactionSeq transactionSeq, | ||
| 21 | EntityId destEid) { | ||
| 22 | 80 | this->m_type = type; | |
| 23 | 80 | this->m_version = 1; // CFDP version is always 1 | |
| 24 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 76 times.
|
80 | this->m_pduType = (type == PduTypeEnum::FILE_DATA) ? PduType::PDU_TYPE_FILE_DATA : PduType::PDU_TYPE_DIRECTIVE; |
| 25 | 80 | this->m_direction = direction; | |
| 26 | 80 | this->m_class = txmMode; | |
| 27 | 80 | this->m_crcFlag = CrcFlag::CRC_NOT_PRESENT; // CRC not currently supported | |
| 28 | 80 | this->m_largeFileFlag = LargeFileFlag::LARGE_FILE_32_BIT; // 32-bit file sizes | |
| 29 | 80 | this->m_segmentationControl = 0; | |
| 30 | 80 | this->m_segmentMetadataFlag = 0; | |
| 31 | 80 | this->m_pduDataLength = 0; // To be set later | |
| 32 | 80 | this->m_sourceEid = sourceEid; | |
| 33 | 80 | this->m_transactionSeq = transactionSeq; | |
| 34 | 80 | this->m_destEid = destEid; | |
| 35 | 80 | } | |
| 36 | |||
| 37 | // Helper function to calculate minimum bytes needed to encode a value | ||
| 38 | 436 | U8 PduHeader::getValueEncodedSize(U64 value) { | |
| 39 | U8 minSize; | ||
| 40 | 436 | U64 limit = 0x100; | |
| 41 | |||
| 42 |
3/4✓ Branch 0 taken 444 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 436 times.
|
444 | for (minSize = 1; minSize < 8 && value >= limit; ++minSize) { |
| 43 | 8 | limit <<= 8; | |
| 44 | } | ||
| 45 | |||
| 46 | 436 | return minSize; | |
| 47 | } | ||
| 48 | |||
| 49 | // Helper function to encode an integer in variable-length format | ||
| 50 | 201 | 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 205 times.
✓ Branch 1 taken 201 times.
|
406 | for (U8 i = 0; i < encodeSize; ++i) { |
| 53 | 205 | U8 shift = static_cast<U8>((encodeSize - 1 - i) * 8); | |
| 54 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 205 times.
|
205 | U8 byte = static_cast<U8>((value >> shift) & 0xFF); |
| 55 | 205 | Fw::SerializeStatus status = serialBuffer.serializeFrom(byte); | |
| 56 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 205 times.
|
205 | if (status != Fw::FW_SERIALIZE_OK) { |
| 57 | ✗ | return status; | |
| 58 | } | ||
| 59 | } | ||
| 60 | 201 | return Fw::FW_SERIALIZE_OK; | |
| 61 | } | ||
| 62 | |||
| 63 | // Helper function to decode an integer from variable-length format | ||
| 64 | 192 | static U64 decodeIntegerInSize(Fw::SerialBufferBase& serialBuffer, U8 decodeSize, Fw::SerializeStatus& status) { | |
| 65 | 192 | U64 value = 0; | |
| 66 | |||
| 67 | // Decode from MSB to LSB (big-endian) | ||
| 68 |
2/2✓ Branch 0 taken 196 times.
✓ Branch 1 taken 192 times.
|
388 | for (U8 i = 0; i < decodeSize; ++i) { |
| 69 | 196 | U8 byte; | |
| 70 |
1/1✓ Branch 7 taken 196 times.
|
196 | status = serialBuffer.deserializeTo(byte); |
| 71 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 196 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 196 times.
|
196 | if (status != Fw::FW_SERIALIZE_OK) { |
| 72 | ✗ | return 0; | |
| 73 | } | ||
| 74 | 196 | value = (value << 8) | byte; | |
| 75 | } | ||
| 76 | |||
| 77 | 192 | return value; | |
| 78 | } | ||
| 79 | |||
| 80 | 151 | U32 PduHeader::getBufferSize() const { | |
| 81 | // Fixed portion: flags(1) + length(2) + eidTsnLengths(1) = 4 bytes | ||
| 82 | 151 | U32 size = 4; | |
| 83 | |||
| 84 | // Variable-size entity IDs and transaction sequence number based on actual values | ||
| 85 | 151 | U8 eidSize = getValueEncodedSize(this->m_sourceEid > this->m_destEid ? this->m_sourceEid : this->m_destEid); | |
| 86 | 151 | U8 tsnSize = getValueEncodedSize(this->m_transactionSeq); | |
| 87 | |||
| 88 | 151 | size += eidSize; // source EID | |
| 89 | 151 | size += tsnSize; // transaction sequence number | |
| 90 | 151 | size += eidSize; // destination EID | |
| 91 | |||
| 92 | 151 | return size; | |
| 93 | } | ||
| 94 | |||
| 95 | 67 | 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 | 67 | U8 eidSize = getValueEncodedSize(this->m_sourceEid > this->m_destEid ? this->m_sourceEid : this->m_destEid); | |
| 100 | 67 | 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 | 67 | U8 flags = 0; | |
| 110 | 67 | flags = static_cast<U8>(flags | static_cast<U8>((this->m_version & 0x07U) << 5)); | |
| 111 | 67 | flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_pduType) & 0x01U) << 4)); | |
| 112 | 67 | 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 67 times.
|
67 | flags = static_cast<U8>(flags | static_cast<U8>((this->m_class & 0x01U) << 2)); |
| 114 | 67 | flags = static_cast<U8>(flags | static_cast<U8>((static_cast<U8>(this->m_crcFlag) & 0x01U) << 1)); | |
| 115 | 67 | flags = static_cast<U8>(flags | (static_cast<U8>(this->m_largeFileFlag) & 0x01U)); | |
| 116 | |||
| 117 | 67 | status = serialBuffer.serializeFrom(flags); | |
| 118 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
|
67 | if (status != Fw::FW_SERIALIZE_OK) { |
| 119 | ✗ | return status; | |
| 120 | } | ||
| 121 | |||
| 122 | // Bytes 1-2: PDU data length (big-endian) | ||
| 123 | 67 | status = serialBuffer.serializeFrom(static_cast<U16>(this->m_pduDataLength)); | |
| 124 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
|
67 | 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 | 67 | U8 eidTsnLengths = 0; | |
| 134 | 67 | eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>((this->m_segmentationControl & 0x01U) << 7)); | |
| 135 | 67 | eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>(((eidSize - 1U) & 0x07U) << 4)); | |
| 136 | 67 | eidTsnLengths = static_cast<U8>(eidTsnLengths | static_cast<U8>((this->m_segmentMetadataFlag & 0x01U) << 3)); | |
| 137 | 67 | eidTsnLengths = static_cast<U8>(eidTsnLengths | ((tsnSize - 1U) & 0x07U)); | |
| 138 | |||
| 139 | 67 | status = serialBuffer.serializeFrom(eidTsnLengths); | |
| 140 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
|
67 | if (status != Fw::FW_SERIALIZE_OK) { |
| 141 | ✗ | return status; | |
| 142 | } | ||
| 143 | |||
| 144 | // Variable-width fields (size based on actual values) | ||
| 145 | 67 | status = encodeIntegerInSize(serialBuffer, this->m_sourceEid, eidSize); | |
| 146 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
|
67 | if (status != Fw::FW_SERIALIZE_OK) { |
| 147 | ✗ | return status; | |
| 148 | } | ||
| 149 | |||
| 150 | 67 | status = encodeIntegerInSize(serialBuffer, this->m_transactionSeq, tsnSize); | |
| 151 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
|
67 | if (status != Fw::FW_SERIALIZE_OK) { |
| 152 | ✗ | return status; | |
| 153 | } | ||
| 154 | |||
| 155 | 67 | status = encodeIntegerInSize(serialBuffer, this->m_destEid, eidSize); | |
| 156 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
|
67 | if (status != Fw::FW_SERIALIZE_OK) { |
| 157 | ✗ | return status; | |
| 158 | } | ||
| 159 | |||
| 160 | 67 | return Fw::FW_SERIALIZE_OK; | |
| 161 | } | ||
| 162 | |||
| 163 | 64 | Fw::SerializeStatus PduHeader::fromSerialBuffer(Fw::SerialBufferBase& serialBuffer) { | |
| 164 | 64 | Fw::SerializeStatus status; | |
| 165 | |||
| 166 | // Byte 0: flags | ||
| 167 | 64 | U8 flags; | |
| 168 |
1/1✓ Branch 7 taken 64 times.
|
64 | status = serialBuffer.deserializeTo(flags); |
| 169 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
|
64 | if (status != Fw::FW_SERIALIZE_OK) { |
| 170 | ✗ | return status; | |
| 171 | } | ||
| 172 | |||
| 173 | 64 | this->m_version = (flags >> 5) & 0x07; | |
| 174 | 64 | this->m_pduType = static_cast<PduType>((flags >> 4) & 0x01); | |
| 175 | 64 | this->m_direction = static_cast<PduDirection>((flags >> 3) & 0x01); | |
| 176 | 64 | this->m_class = static_cast<Cfdp::Class::T>((flags >> 2) & 0x01); | |
| 177 | 64 | this->m_crcFlag = static_cast<CrcFlag>((flags >> 1) & 0x01); | |
| 178 | 64 | this->m_largeFileFlag = static_cast<LargeFileFlag>(flags & 0x01); | |
| 179 | |||
| 180 | // Bytes 1-2: PDU data length | ||
| 181 |
1/1✓ Branch 10 taken 64 times.
|
64 | status = serialBuffer.deserializeTo(this->m_pduDataLength); |
| 182 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
|
64 | if (status != Fw::FW_SERIALIZE_OK) { |
| 183 | ✗ | return status; | |
| 184 | } | ||
| 185 | |||
| 186 | // Byte 3: eidTsnLengths | ||
| 187 | 64 | U8 eidTsnLengths; | |
| 188 |
1/1✓ Branch 7 taken 64 times.
|
64 | status = serialBuffer.deserializeTo(eidTsnLengths); |
| 189 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
|
64 | if (status != Fw::FW_SERIALIZE_OK) { |
| 190 | ✗ | return status; | |
| 191 | } | ||
| 192 | |||
| 193 | 64 | this->m_segmentationControl = (eidTsnLengths >> 7) & 0x01; | |
| 194 | 64 | U8 eidSize = static_cast<U8>(((eidTsnLengths >> 4) & 0x07) + 1); | |
| 195 | 64 | this->m_segmentMetadataFlag = (eidTsnLengths >> 3) & 0x01; | |
| 196 | 64 | U8 tsnSize = static_cast<U8>((eidTsnLengths & 0x07) + 1); | |
| 197 | |||
| 198 | // Validate that the sizes are within bounds (1-8 bytes) | ||
| 199 | 64 | FW_ASSERT(eidSize >= 1 && eidSize <= 8, static_cast<FwAssertArgType>(eidSize)); | |
| 200 | 64 | 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 64 times.
|
64 | this->m_sourceEid = static_cast<EntityId>(decodeIntegerInSize(serialBuffer, eidSize, status)); |
| 204 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
|
64 | if (status != Fw::FW_SERIALIZE_OK) { |
| 205 | ✗ | return status; | |
| 206 | } | ||
| 207 | |||
| 208 |
1/1✓ Branch 1 taken 64 times.
|
64 | this->m_transactionSeq = static_cast<TransactionSeq>(decodeIntegerInSize(serialBuffer, tsnSize, status)); |
| 209 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
|
64 | if (status != Fw::FW_SERIALIZE_OK) { |
| 210 | ✗ | return status; | |
| 211 | } | ||
| 212 | |||
| 213 |
1/1✓ Branch 1 taken 64 times.
|
64 | this->m_destEid = static_cast<EntityId>(decodeIntegerInSize(serialBuffer, eidSize, status)); |
| 214 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
|
64 | 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 2 times.
✓ Branch 3 taken 62 times.
|
64 | if (this->m_pduType == PduType::PDU_TYPE_FILE_DATA) { |
| 221 | 2 | this->m_type = PduTypeEnum::FILE_DATA; | |
| 222 | } else { | ||
| 223 | // For directive PDUs, type will be set when directive code is read | ||
| 224 | 62 | this->m_type = PduTypeEnum::NONE; | |
| 225 | } | ||
| 226 | |||
| 227 | 64 | 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 |