Added CFDP packet stack

This PR adds the packet stack for the CCSDS File Delivery Protocol.
It also refactors the existing TMTC infastructure to allow sending
of CFDP packets to the CCSDS handlers.

This includes the whole PDU (Protocol Data Unit) stack:

- File Data PDUs

and all file directive PDUs

- ACK PDU
- NAK PDU
- Metadata PDU
- Finished PDU
- Prompt PDU
- Keep Alive PDU
- EOF PDU

The PR includes a full set of unittests for the packet stack
with a coverage of 90+ %.

The refactoring of the existing TMTC infastructure includes non-ideal
solutions like diamond inheritance.
Avoiding this solution would require refactoring the packet stack.
This would be a good idea anyway because the existing stack is tightly
coupled to the FSFW, making reuse more difficult if only the stack is
planned to be used without the store functionalities etc.

The PDU implementation provided here is only weakly coupled to the FSFW,
only using components like returnvalues or the Serialization modules.
There are dedicated serializers and deserializers, which also helps in
creating small focused modules which are easy to test.

Some of the modules here were provied by Matthias Tompert.
This commit is contained in:
2021-12-03 15:37:49 +01:00
parent bf31248a50
commit 5907f8ee9d
167 changed files with 7801 additions and 1121 deletions

View File

@ -21,3 +21,4 @@ add_subdirectory(storagemanager)
add_subdirectory(globalfunctions)
add_subdirectory(timemanager)
add_subdirectory(tmtcpacket)
add_subdirectory(cfdp)

View File

@ -0,0 +1,12 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
testCfdp.cpp
testTlvsLvs.cpp
testAckPdu.cpp
testEofPdu.cpp
testNakPdu.cpp
testFinishedPdu.cpp
testPromptPdu.cpp
testKeepAlivePdu.cpp
testMetadataPdu.cpp
testFileData.cpp
)

View File

@ -0,0 +1,101 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/AckPduDeserializer.h"
#include "fsfw/cfdp/pdu/AckPduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include <array>
TEST_CASE( "ACK PDU" , "[AckPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> buf = {};
uint8_t* bufptr = buf.data();
size_t maxsz = buf.size();
size_t sz = 0;
auto seqNum = TransactionSeqNum(WidthInBytes::TWO_BYTES, 15);
auto sourceId = EntityId(WidthInBytes::TWO_BYTES, 1);
auto destId = EntityId(WidthInBytes::TWO_BYTES, 2);
auto pduConf = PduConfig(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
AckInfo ackInfo(FileDirectives::EOF_DIRECTIVE, ConditionCode::NO_ERROR, AckTransactionStatus::ACTIVE);
auto ackSerializer = AckPduSerializer(ackInfo, pduConf);
result = ackSerializer.serialize(&bufptr, &sz, maxsz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
SECTION("Serialize") {
REQUIRE(buf.data()[sz - 3] == cfdp::FileDirectives::ACK);
REQUIRE((buf.data()[sz - 2] >> 4) == FileDirectives::EOF_DIRECTIVE);
REQUIRE((buf.data()[sz - 2] & 0x0f) == 0);
REQUIRE(buf.data()[sz - 1] == AckTransactionStatus::ACTIVE);
ackInfo.setAckedDirective(FileDirectives::FINISH);
ackInfo.setAckedConditionCode(ConditionCode::FILESTORE_REJECTION);
ackInfo.setTransactionStatus(AckTransactionStatus::TERMINATED);
auto ackSerializer2 = AckPduSerializer(ackInfo, pduConf);
bufptr = buf.data();
sz = 0;
result = ackSerializer2.serialize(&bufptr, &sz, maxsz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(buf.data()[sz - 3] == cfdp::FileDirectives::ACK);
REQUIRE((buf.data()[sz - 2] >> 4) == FileDirectives::FINISH);
REQUIRE((buf.data()[sz - 2] & 0x0f) == 0b0001);
REQUIRE((buf.data()[sz - 1] >> 4) == ConditionCode::FILESTORE_REJECTION);
REQUIRE((buf.data()[sz - 1] & 0b11) == AckTransactionStatus::TERMINATED);
bufptr = buf.data();
sz = 0;
ackInfo.setAckedDirective(FileDirectives::KEEP_ALIVE);
auto ackSerializer3 = AckPduSerializer(ackInfo, pduConf);
result = ackSerializer3.serialize(&bufptr, &sz, maxsz, SerializeIF::Endianness::NETWORK);
// Invalid file directive
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
ackInfo.setAckedDirective(FileDirectives::FINISH);
// buffer too small
result = ackSerializer.serialize(&bufptr, &sz, 8, SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
}
SECTION("Deserialize") {
AckInfo ackInfo;
auto reader = AckPduDeserializer(buf.data(), sz, ackInfo);
result = reader.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(ackInfo.getAckedDirective() == FileDirectives::EOF_DIRECTIVE);
REQUIRE(ackInfo.getAckedConditionCode() == ConditionCode::NO_ERROR);
REQUIRE(ackInfo.getDirectiveSubtypeCode() == 0);
REQUIRE(ackInfo.getTransactionStatus() == AckTransactionStatus::ACTIVE);
AckInfo newInfo = AckInfo(FileDirectives::FINISH,
ConditionCode::FILESTORE_REJECTION, AckTransactionStatus::TERMINATED);
auto ackSerializer2 = AckPduSerializer(newInfo, pduConf);
bufptr = buf.data();
sz = 0;
result = ackSerializer2.serialize(&bufptr, &sz, maxsz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
auto reader2 = AckPduDeserializer(buf.data(), sz, ackInfo);
result = reader2.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(ackInfo.getAckedDirective() == FileDirectives::FINISH);
REQUIRE(ackInfo.getAckedConditionCode() == ConditionCode::FILESTORE_REJECTION);
REQUIRE(ackInfo.getDirectiveSubtypeCode() == 0b0001);
REQUIRE(ackInfo.getTransactionStatus() == AckTransactionStatus::TERMINATED);
uint8_t prevVal = buf[sz - 2];
buf[sz - 2] = FileDirectives::INVALID_DIRECTIVE << 4;
result = reader2.parseData();
REQUIRE(result == cfdp::INVALID_ACK_DIRECTIVE_FIELDS);
buf[sz - 2] = FileDirectives::FINISH << 4 | 0b1111;
result = reader2.parseData();
REQUIRE(result == cfdp::INVALID_ACK_DIRECTIVE_FIELDS);
buf[sz - 2] = prevVal;
buf[sz - 3] = cfdp::FileDirectives::INVALID_DIRECTIVE;
result = reader2.parseData();
REQUIRE(result == cfdp::INVALID_DIRECTIVE_FIELDS);
buf[sz - 3] = cfdp::FileDirectives::ACK;
auto maxSizeTooSmall = AckPduDeserializer(buf.data(), sz - 2, ackInfo);
result = maxSizeTooSmall.parseData();
REQUIRE(result == SerializeIF::STREAM_TOO_SHORT);
}
}

View File

@ -0,0 +1,372 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/FileDirectiveDeserializer.h"
#include "fsfw/cfdp/pdu/FileDirectiveSerializer.h"
#include "fsfw/cfdp/pdu/HeaderDeserializer.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
#include "fsfw/cfdp/FileSize.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include "fsfw/cfdp/pdu/HeaderSerializer.h"
#include "fsfw/serialize/SerializeAdapter.h"
#include <cstring>
#include <array>
TEST_CASE( "CFDP Base" , "[CfdpBase]") {
using namespace cfdp;
std::array<uint8_t, 32> serBuf;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
cfdp::TransactionSeqNum seqNum = TransactionSeqNum(cfdp::WidthInBytes::ONE_BYTE, 2);
cfdp::EntityId sourceId = EntityId(cfdp::WidthInBytes::ONE_BYTE, 0);
cfdp::EntityId destId = EntityId(cfdp::WidthInBytes::ONE_BYTE, 1);
PduConfig pduConf = PduConfig(cfdp::TransmissionModes::ACKNOWLEDGED, seqNum, sourceId,
destId, false);
uint8_t* serTarget = serBuf.data();
const uint8_t* deserTarget = serTarget;
size_t serSize = 0;
SECTION("Header Serialization") {
auto headerSerializer = HeaderSerializer(pduConf, cfdp::PduType::FILE_DIRECTIVE, 0);
const uint8_t** dummyPtr = nullptr;
ReturnValue_t deserResult = headerSerializer.deSerialize(dummyPtr, &serSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(deserResult == retval::CATCH_FAILED);
deserResult = headerSerializer.serialize(nullptr, &serSize, serBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(deserResult == retval::CATCH_FAILED);
REQUIRE(seqNum.getSerializedSize() == 1);
REQUIRE(headerSerializer.getPduDataFieldLen() == 0);
REQUIRE(headerSerializer.getSerializedSize() == 7);
REQUIRE(headerSerializer.getWholePduSize() == 7);
REQUIRE(headerSerializer.getCrcFlag() == false);
REQUIRE(headerSerializer.getDirection() == cfdp::Direction::TOWARDS_RECEIVER);
REQUIRE(headerSerializer.getLargeFileFlag() == false);
REQUIRE(headerSerializer.getLenEntityIds() == 1);
REQUIRE(headerSerializer.getLenSeqNum() == 1);
REQUIRE(headerSerializer.getPduType() == cfdp::PduType::FILE_DIRECTIVE);
REQUIRE(headerSerializer.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::NOT_PRESENT);
REQUIRE(headerSerializer.getSegmentationControl() == false);
REQUIRE(headerSerializer.getTransmissionMode() == cfdp::TransmissionModes::ACKNOWLEDGED);
cfdp::TransactionSeqNum seqNumLocal;
headerSerializer.getTransactionSeqNum(seqNumLocal);
REQUIRE(seqNumLocal.getWidth() == cfdp::WidthInBytes::ONE_BYTE);
REQUIRE(seqNumLocal.getValue() == 2);
cfdp::EntityId sourceDestId;
headerSerializer.getSourceId(sourceDestId);
REQUIRE(sourceDestId.getWidth() == cfdp::WidthInBytes::ONE_BYTE);
REQUIRE(sourceDestId.getValue() == 0);
headerSerializer.getDestId(sourceDestId);
REQUIRE(sourceDestId.getWidth() == cfdp::WidthInBytes::ONE_BYTE);
REQUIRE(sourceDestId.getValue() == 1);
result = headerSerializer.serialize(&serTarget, &serSize,
serBuf.size(), SerializeIF::Endianness::BIG);
REQUIRE(result == retval::CATCH_OK);
REQUIRE(serSize == 7);
// Only version bits are set
REQUIRE(serBuf[0] == 0b00100000);
// PDU data field length is 0
REQUIRE(serBuf[1] == 0);
REQUIRE(serBuf[2] == 0);
// Entity and Transaction Sequence number are 1 byte large
REQUIRE(serBuf[3] == 0b00010001);
// Source ID
REQUIRE(serBuf[4] == 0);
// Transaction Seq Number
REQUIRE(serBuf[5] == 2);
// Dest ID
REQUIRE(serBuf[6] == 1);
for(uint8_t idx = 0; idx < 7; idx++) {
ReturnValue_t result = headerSerializer.serialize(&serTarget, &serSize,
idx, SerializeIF::Endianness::BIG);
REQUIRE(result == static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));
}
// Set PDU data field len
headerSerializer.setPduDataFieldLen(0x0ff0);
REQUIRE(headerSerializer.getPduDataFieldLen() == 0x0ff0);
REQUIRE(headerSerializer.getSerializedSize() == 7);
REQUIRE(headerSerializer.getWholePduSize() == 7 + 0x0ff0);
serTarget = serBuf.data();
serSize = 0;
result = headerSerializer.serialize(&serTarget, &serSize,
serBuf.size(), SerializeIF::Endianness::BIG);
REQUIRE(serBuf[1] == 0x0f);
REQUIRE(serBuf[2] == 0xf0);
pduConf.crcFlag = true;
pduConf.largeFile = true;
pduConf.direction = cfdp::Direction::TOWARDS_SENDER;
pduConf.mode = cfdp::TransmissionModes::UNACKNOWLEDGED;
headerSerializer.setSegmentationControl(
cfdp::SegmentationControl::RECORD_BOUNDARIES_PRESERVATION);
headerSerializer.setPduType(cfdp::PduType::FILE_DATA);
headerSerializer.setSegmentMetadataFlag(cfdp::SegmentMetadataFlag::PRESENT);
serTarget = serBuf.data();
serSize = 0;
result = headerSerializer.serialize(&serTarget, &serSize,
serBuf.size(), SerializeIF::Endianness::BIG);
// Everything except version bit flipped to one now
REQUIRE(serBuf[0] == 0x3f);
REQUIRE(serBuf[3] == 0x99);
pduConf.seqNum.setValue(cfdp::WidthInBytes::TWO_BYTES, 0x0fff);
pduConf.sourceId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 0xff00ff00);
pduConf.destId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 0x00ff00ff);
REQUIRE(pduConf.sourceId.getSerializedSize() == 4);
REQUIRE(headerSerializer.getSerializedSize() == 14);
serTarget = serBuf.data();
serSize = 0;
result = headerSerializer.serialize(&serTarget, &serSize,
serBuf.size(), SerializeIF::Endianness::BIG);
for(uint8_t idx = 0; idx < 14; idx++) {
ReturnValue_t result = headerSerializer.serialize(&serTarget, &serSize,
idx, SerializeIF::Endianness::BIG);
REQUIRE(result == static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));
}
REQUIRE(headerSerializer.getCrcFlag() == true);
REQUIRE(headerSerializer.getDirection() == cfdp::Direction::TOWARDS_SENDER);
REQUIRE(headerSerializer.getLargeFileFlag() == true);
REQUIRE(headerSerializer.getLenEntityIds() == 4);
REQUIRE(headerSerializer.getLenSeqNum() == 2);
REQUIRE(headerSerializer.getPduType() == cfdp::PduType::FILE_DATA);
REQUIRE(headerSerializer.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::PRESENT);
REQUIRE(headerSerializer.getTransmissionMode() == cfdp::TransmissionModes::UNACKNOWLEDGED);
REQUIRE(headerSerializer.getSegmentationControl() == true);
// Last three bits are 2 now (length of seq number) and bit 1 to bit 3 is 4 (len entity IDs)
REQUIRE(serBuf[3] == 0b11001010);
uint32_t entityId = 0;
size_t deSerSize = 0;
SerializeAdapter::deSerialize(&entityId, serBuf.data() + 4, &deSerSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(deSerSize == 4);
REQUIRE(entityId == 0xff00ff00);
uint16_t seqNum = 0;
SerializeAdapter::deSerialize(&seqNum, serBuf.data() + 8, &deSerSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(deSerSize == 2);
REQUIRE(seqNum == 0x0fff);
SerializeAdapter::deSerialize(&entityId, serBuf.data() + 10, &deSerSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(deSerSize == 4);
REQUIRE(entityId == 0x00ff00ff);
result = pduConf.sourceId.setValue(cfdp::WidthInBytes::ONE_BYTE, 0xfff);
REQUIRE(result == retval::CATCH_FAILED);
result = pduConf.sourceId.setValue(cfdp::WidthInBytes::TWO_BYTES, 0xfffff);
REQUIRE(result == retval::CATCH_FAILED);
result = pduConf.sourceId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 0xfffffffff);
REQUIRE(result == retval::CATCH_FAILED);
uint8_t oneByteSourceId = 32;
serTarget = &oneByteSourceId;
size_t deserLen = 1;
pduConf.sourceId.deSerialize(cfdp::WidthInBytes::ONE_BYTE,
const_cast<const uint8_t**>(&serTarget), &deserLen,
SerializeIF::Endianness::MACHINE);
REQUIRE(pduConf.sourceId.getValue() == 32);
uint16_t twoByteSourceId = 0xf0f0;
serTarget = reinterpret_cast<uint8_t*>(&twoByteSourceId);
deserLen = 2;
pduConf.sourceId.deSerialize(cfdp::WidthInBytes::TWO_BYTES,
const_cast<const uint8_t**>(&serTarget), &deserLen,
SerializeIF::Endianness::MACHINE);
REQUIRE(pduConf.sourceId.getValue() == 0xf0f0);
uint32_t fourByteSourceId = 0xf0f0f0f0;
serTarget = reinterpret_cast<uint8_t*>(&fourByteSourceId);
deserLen = 4;
pduConf.sourceId.deSerialize(cfdp::WidthInBytes::FOUR_BYTES,
const_cast<const uint8_t**>(&serTarget), &deserLen,
SerializeIF::Endianness::MACHINE);
REQUIRE(pduConf.sourceId.getValue() == 0xf0f0f0f0);
pduConf.sourceId.setValue(cfdp::WidthInBytes::ONE_BYTE, 1);
serTarget = serBuf.data();
serSize = 1;
result = pduConf.sourceId.serialize(&serTarget, &serSize, 1,
SerializeIF::Endianness::MACHINE);
REQUIRE(result == static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));
}
SECTION( "Header Deserialization" ) {
// We unittested the serializer before, so we can use it now to generate valid raw CFDP
// data
auto headerSerializer = HeaderSerializer(pduConf, cfdp::PduType::FILE_DIRECTIVE, 0);
ReturnValue_t result = headerSerializer.serialize(&serTarget, &serSize,
serBuf.size(), SerializeIF::Endianness::BIG);
REQUIRE(result == retval::CATCH_OK);
REQUIRE(serBuf[1] == 0);
REQUIRE(serBuf[2] == 0);
// Entity and Transaction Sequence number are 1 byte large
REQUIRE(serBuf[3] == 0b00010001);
REQUIRE(serSize == 7);
// Deser call not strictly necessary
auto headerDeser = HeaderDeserializer(serBuf.data(), serBuf.size());
ReturnValue_t serResult = headerDeser.parseData();
REQUIRE(serResult == retval::CATCH_OK);
REQUIRE(headerDeser.getPduDataFieldLen() == 0);
REQUIRE(headerDeser.getHeaderSize() == 7);
REQUIRE(headerDeser.getWholePduSize() == 7);
REQUIRE(headerDeser.getCrcFlag() == false);
REQUIRE(headerDeser.getDirection() == cfdp::Direction::TOWARDS_RECEIVER);
REQUIRE(headerDeser.getLargeFileFlag() == false);
REQUIRE(headerDeser.getLenEntityIds() == 1);
REQUIRE(headerDeser.getLenSeqNum() == 1);
REQUIRE(headerDeser.getPduType() == cfdp::PduType::FILE_DIRECTIVE);
REQUIRE(headerDeser.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::NOT_PRESENT);
REQUIRE(headerDeser.getSegmentationControl() == false);
REQUIRE(headerDeser.getTransmissionMode() == cfdp::TransmissionModes::ACKNOWLEDGED);
pduConf.crcFlag = true;
pduConf.largeFile = true;
pduConf.direction = cfdp::Direction::TOWARDS_SENDER;
pduConf.mode = cfdp::TransmissionModes::UNACKNOWLEDGED;
headerSerializer.setSegmentationControl(
cfdp::SegmentationControl::RECORD_BOUNDARIES_PRESERVATION);
headerSerializer.setPduType(cfdp::PduType::FILE_DATA);
headerSerializer.setSegmentMetadataFlag(cfdp::SegmentMetadataFlag::PRESENT);
result = pduConf.seqNum.setValue(cfdp::WidthInBytes::TWO_BYTES, 0x0fff);
REQUIRE(result == retval::CATCH_OK);
result = pduConf.sourceId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 0xff00ff00);
REQUIRE(result == retval::CATCH_OK);
result = pduConf.destId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 0x00ff00ff);
REQUIRE(result == retval::CATCH_OK);
serTarget = serBuf.data();
serSize = 0;
result = headerSerializer.serialize(&serTarget, &serSize,
serBuf.size(), SerializeIF::Endianness::BIG);
headerDeser = HeaderDeserializer(serBuf.data(), serBuf.size());
result = headerDeser.parseData();
REQUIRE(result == retval::CATCH_OK);
// Everything except version bit flipped to one now
REQUIRE(serBuf[0] == 0x3f);
REQUIRE(serBuf[3] == 0b11001010);
REQUIRE(headerDeser.getWholePduSize() == 14);
REQUIRE(headerDeser.getCrcFlag() == true);
REQUIRE(headerDeser.getDirection() == cfdp::Direction::TOWARDS_SENDER);
REQUIRE(headerDeser.getLargeFileFlag() == true);
REQUIRE(headerDeser.getLenEntityIds() == 4);
REQUIRE(headerDeser.getLenSeqNum() == 2);
REQUIRE(headerDeser.getPduType() == cfdp::PduType::FILE_DATA);
REQUIRE(headerDeser.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::PRESENT);
REQUIRE(headerDeser.getSegmentationControl() == true);
REQUIRE(headerDeser.getTransmissionMode() == cfdp::TransmissionModes::UNACKNOWLEDGED);
cfdp::TransactionSeqNum seqNumLocal;
headerDeser.getTransactionSeqNum(seqNumLocal);
REQUIRE(seqNumLocal.getWidth() == cfdp::WidthInBytes::TWO_BYTES);
REQUIRE(seqNumLocal.getValue() == 0x0fff);
cfdp::EntityId sourceDestId;
headerDeser.getSourceId(sourceDestId);
REQUIRE(sourceDestId.getWidth() == cfdp::WidthInBytes::FOUR_BYTES);
REQUIRE(sourceDestId.getValue() == 0xff00ff00);
headerDeser.getDestId(sourceDestId);
REQUIRE(sourceDestId.getWidth() == cfdp::WidthInBytes::FOUR_BYTES);
REQUIRE(sourceDestId.getValue() == 0x00ff00ff);
size_t deSerSize = headerDeser.getWholePduSize();
serTarget = serBuf.data();
const uint8_t** serTargetConst = const_cast<const uint8_t**>(&serTarget);
result = headerDeser.parseData();
REQUIRE(result == retval::CATCH_OK);
headerDeser.setData(nullptr, -1);
REQUIRE(headerDeser.getHeaderSize() == 0);
headerDeser.setData(serBuf.data(), serBuf.size());
serTarget = serBuf.data();
serSize = 0;
pduConf.sourceId.setValue(cfdp::WidthInBytes::ONE_BYTE, 22);
pduConf.destId.setValue(cfdp::WidthInBytes::ONE_BYTE, 48);
result = headerSerializer.serialize(&serTarget, &serSize,
serBuf.size(), SerializeIF::Endianness::BIG);
REQUIRE(result == retval::CATCH_OK);
REQUIRE(headerDeser.getWholePduSize() == 8);
headerDeser.setData(serBuf.data(), serBuf.size());
headerDeser.getSourceId(sourceDestId);
REQUIRE(sourceDestId.getWidth() == cfdp::WidthInBytes::ONE_BYTE);
REQUIRE(sourceDestId.getValue() == 22);
}
SECTION("File Directive") {
auto fdSer = FileDirectiveSerializer(pduConf, FileDirectives::ACK, 4);
REQUIRE(fdSer.getSerializedSize() == 8);
serTarget = serBuf.data();
serSize = 0;
result = fdSer.serialize(&serTarget, &serSize, serBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
// Only version bits are set
REQUIRE(serBuf[0] == 0b00100000);
// PDU data field length is 5 (4 + Directive code octet)
REQUIRE(serBuf[1] == 0);
REQUIRE(serBuf[2] == 5);
// Entity and Transaction Sequence number are 1 byte large
REQUIRE(serBuf[3] == 0b00010001);
// Source ID
REQUIRE(serBuf[4] == 0);
// Transaction Seq Number
REQUIRE(serBuf[5] == 2);
// Dest ID
REQUIRE(serBuf[6] == 1);
REQUIRE(serBuf[7] == FileDirectives::ACK);
serTarget = serBuf.data();
size_t deserSize = 20;
serSize = 0;
REQUIRE(fdSer.deSerialize(&deserTarget, &deserSize, SerializeIF::Endianness::NETWORK) ==
HasReturnvaluesIF::RETURN_FAILED);
REQUIRE(fdSer.serialize(nullptr, nullptr, 85, SerializeIF::Endianness::NETWORK) ==
HasReturnvaluesIF::RETURN_FAILED);
for(uint8_t idx = 0; idx < 8; idx++) {
serTarget = serBuf.data();
serSize = 0;
REQUIRE(fdSer.serialize(&serTarget, &serSize, idx, SerializeIF::Endianness::NETWORK) ==
SerializeIF::BUFFER_TOO_SHORT);
}
deserTarget = serBuf.data();
deserSize = 0;
auto fdDeser = FileDirectiveDeserializer(deserTarget, serBuf.size());
REQUIRE(fdDeser.getEndianness() == SerializeIF::Endianness::NETWORK);
fdDeser.setEndianness(SerializeIF::Endianness::MACHINE);
REQUIRE(fdDeser.getEndianness() == SerializeIF::Endianness::MACHINE);
fdDeser.setEndianness(SerializeIF::Endianness::NETWORK);
REQUIRE(fdDeser.parseData() == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fdDeser.getFileDirective() == FileDirectives::ACK);
REQUIRE(fdDeser.getPduDataFieldLen() == 5);
REQUIRE(fdDeser.getHeaderSize() == 8);
REQUIRE(fdDeser.getPduType() == cfdp::PduType::FILE_DIRECTIVE);
serBuf[7] = 0xff;
// Invalid file directive
REQUIRE(fdDeser.parseData() == cfdp::INVALID_DIRECTIVE_FIELDS);
}
SECTION("FileSize") {
std::array<uint8_t, 8> fssBuf = {};
uint8_t* buffer = fssBuf.data();
size_t size = 0;
cfdp::FileSize fss;
REQUIRE(fss.getSize() == 0);
fss.setFileSize(0x20, false);
ReturnValue_t result = fss.serialize(&buffer, &size, fssBuf.size(),
SerializeIF::Endianness::MACHINE);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
uint32_t fileSize = 0;
result = SerializeAdapter::deSerialize(&fileSize, fssBuf.data(), nullptr,
SerializeIF::Endianness::MACHINE);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fileSize == 0x20);
}
}

View File

@ -0,0 +1,124 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/EofPduDeserializer.h"
#include "fsfw/cfdp/pdu/EofPduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include <array>
TEST_CASE( "EOF PDU" , "[EofPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 128> buf = {};
uint8_t* bufPtr = buf.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
EntityIdTlv faultLoc(destId);
FileSize fileSize(12);
// We can already set the fault location, it will be ignored
EofInfo eofInfo(cfdp::ConditionCode::NO_ERROR, 5, fileSize, &faultLoc);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
auto eofSerializer = EofPduSerializer(pduConf, eofInfo);
SECTION("Serialize") {
result = eofSerializer.serialize(&bufPtr, &sz, buf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(((buf[1] << 8) | buf[2]) == 10);
uint32_t checksum = 0;
result = SerializeAdapter::deSerialize(&checksum, buf.data() + sz - 8, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(checksum == 5);
uint32_t fileSizeVal = 0;
result = SerializeAdapter::deSerialize(&fileSizeVal, buf.data() + sz - 4, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fileSizeVal == 12);
REQUIRE(buf[sz - 10] == cfdp::FileDirectives::EOF_DIRECTIVE);
REQUIRE(buf[sz - 9] == 0x00);
REQUIRE(sz == 20);
eofInfo.setConditionCode(cfdp::ConditionCode::FILESTORE_REJECTION);
eofInfo.setFileSize(0x10ffffff10, true);
pduConf.largeFile = true;
// Should serialize with fault location now
auto serializeWithFaultLocation = EofPduSerializer(pduConf, eofInfo);
bufPtr = buf.data();
sz = 0;
result = serializeWithFaultLocation.serialize(&bufPtr, &sz, buf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(sz == 28);
REQUIRE(buf[10] == cfdp::FileDirectives::EOF_DIRECTIVE);
REQUIRE(buf[11] >> 4 == cfdp::ConditionCode::FILESTORE_REJECTION);
uint64_t fileSizeLarge = 0;
result = SerializeAdapter::deSerialize(&fileSizeLarge, buf.data() + 16, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(fileSizeLarge == 0x10ffffff10);
REQUIRE(buf[sz - 4] == cfdp::TlvTypes::ENTITY_ID);
// width of entity ID is 2
REQUIRE(buf[sz - 3] == 2);
uint16_t entityIdRaw = 0;
result = SerializeAdapter::deSerialize(&entityIdRaw, buf.data() + sz - 2, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(entityIdRaw == 2);
bufPtr = buf.data();
sz = 0;
for(size_t idx = 0; idx < 27; idx++) {
result = serializeWithFaultLocation.serialize(&bufPtr, &sz, idx,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
bufPtr = buf.data();
sz = 0;
}
eofInfo.setChecksum(16);
eofInfo.setFaultLoc(nullptr);
}
SECTION("Deserialize") {
result = eofSerializer.serialize(&bufPtr, &sz, buf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
EntityIdTlv tlv(destId);
EofInfo emptyInfo(&tlv);
auto deserializer = EofPduDeserializer(buf.data(), buf.size(), emptyInfo);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyInfo.getConditionCode() == cfdp::ConditionCode::NO_ERROR);
REQUIRE(emptyInfo.getChecksum() == 5);
REQUIRE(emptyInfo.getFileSize().getSize() == 12);
eofInfo.setConditionCode(cfdp::ConditionCode::FILESTORE_REJECTION);
eofInfo.setFileSize(0x10ffffff10, true);
pduConf.largeFile = true;
// Should serialize with fault location now
auto serializeWithFaultLocation = EofPduSerializer(pduConf, eofInfo);
bufPtr = buf.data();
sz = 0;
result = serializeWithFaultLocation.serialize(&bufPtr, &sz, buf.size(),
SerializeIF::Endianness::NETWORK);
auto deserializer2 = EofPduDeserializer(buf.data(), buf.size(), emptyInfo);
result = deserializer2.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyInfo.getConditionCode() == cfdp::ConditionCode::FILESTORE_REJECTION);
REQUIRE(emptyInfo.getChecksum() == 5);
REQUIRE(emptyInfo.getFileSize().getSize() == 0x10ffffff10);
REQUIRE(emptyInfo.getFaultLoc()->getType() == cfdp::TlvTypes::ENTITY_ID);
REQUIRE(emptyInfo.getFaultLoc()->getSerializedSize() == 4);
uint16_t destId = emptyInfo.getFaultLoc()->getEntityId().getValue();
REQUIRE(destId == 2);
for(size_t maxSz = 0; maxSz < deserializer2.getWholePduSize() - 1; maxSz++) {
auto invalidDeser = EofPduDeserializer(buf.data(), maxSz, emptyInfo);
result = invalidDeser.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
}

View File

@ -0,0 +1,175 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/FileDataDeserializer.h"
#include "fsfw/cfdp/pdu/FileDataSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include "fsfw/serviceinterface.h"
#include <array>
TEST_CASE( "File Data PDU" , "[FileDataPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 128> fileBuffer = {};
std::array<uint8_t, 256> fileDataBuffer = {};
uint8_t* buffer = fileDataBuffer.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
for(uint8_t idx = 0; idx < 10; idx++) {
fileBuffer[idx] = idx;
}
FileSize offset(50);
FileDataInfo info(offset, fileBuffer.data(), 10);
SECTION("Serialization") {
FileDataSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, fileDataBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(sz == 24);
// 10 file bytes plus 4 byte offset
REQUIRE(((fileDataBuffer[1] << 8) | fileDataBuffer[2]) == 14);
// File Data -> Fourth bit is one
REQUIRE(fileDataBuffer[0] == 0b00110000);
uint32_t offsetRaw = 0;
buffer = fileDataBuffer.data();
result = SerializeAdapter::deSerialize(&offsetRaw, buffer + 10, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(offsetRaw == 50);
buffer = fileDataBuffer.data() + 14;
for(size_t idx = 0; idx < 10; idx++) {
REQUIRE(buffer[idx] == idx);
}
REQUIRE(info.hasSegmentMetadata() == false);
info.addSegmentMetadataInfo(cfdp::RecordContinuationState::CONTAINS_START_AND_END,
fileBuffer.data(), 10);
REQUIRE(info.hasSegmentMetadata() == true);
REQUIRE(info.getSegmentationControl() ==
cfdp::SegmentationControl::NO_RECORD_BOUNDARIES_PRESERVATION);
info.setSegmentationControl(cfdp::SegmentationControl::RECORD_BOUNDARIES_PRESERVATION);
serializer.update();
REQUIRE(serializer.getSegmentationControl() ==
cfdp::SegmentationControl::RECORD_BOUNDARIES_PRESERVATION);
buffer = fileDataBuffer.data();
sz = 0;
serializer.setSegmentationControl(cfdp::SegmentationControl::RECORD_BOUNDARIES_PRESERVATION);
result = serializer.serialize(&buffer, &sz, fileDataBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(((fileDataBuffer[1] << 8) | fileDataBuffer[2]) == 25);
// First bit: Seg Ctrl is set
// Bits 1 to 3 length of enitity IDs is 2
// Bit 4: Segment metadata flag is set
// Bit 5 to seven: length of transaction seq num is 2
REQUIRE(fileDataBuffer[3] == 0b10101010);
REQUIRE((fileDataBuffer[10] >> 6) & 0b11 ==
cfdp::RecordContinuationState::CONTAINS_START_AND_END);
// Segment metadata length
REQUIRE((fileDataBuffer[10] & 0x3f) == 10);
buffer = fileDataBuffer.data() + 11;
// Check segment metadata
for(size_t idx = 0; idx < 10; idx++) {
REQUIRE(buffer[idx] == idx);
}
// Check filedata
buffer = fileDataBuffer.data() + 25;
for(size_t idx = 0; idx < 10; idx++) {
REQUIRE(buffer[idx] == idx);
}
for(size_t invalidStartSz = 1; invalidStartSz < sz; invalidStartSz ++) {
buffer = fileDataBuffer.data();
sz = 0;
result = serializer.serialize(&buffer, &invalidStartSz, sz,
SerializeIF::Endianness::NETWORK);
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
info.setSegmentMetadataFlag(true);
REQUIRE(info.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::PRESENT);
info.setSegmentMetadataFlag(false);
REQUIRE(info.getSegmentMetadataFlag() == cfdp::SegmentMetadataFlag::NOT_PRESENT);
info.setRecordContinuationState(cfdp::RecordContinuationState::CONTAINS_END_NO_START);
info.setSegmentMetadataLen(10);
info.setSegmentMetadata(nullptr);
info.setFileData(nullptr, 0);
}
SECTION("Deserialization") {
FileDataSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, fileDataBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
FileSize emptyOffset;
FileDataInfo emptyInfo(emptyOffset);
FileDataDeserializer deserializer(fileDataBuffer.data(), fileDataBuffer.size(), emptyInfo);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserializer.getWholePduSize() == 24);
REQUIRE(deserializer.getPduDataFieldLen() == 14);
REQUIRE(deserializer.getSegmentationControl() ==
cfdp::SegmentationControl::NO_RECORD_BOUNDARIES_PRESERVATION);
REQUIRE(deserializer.getSegmentMetadataFlag() ==
cfdp::SegmentMetadataFlag::NOT_PRESENT);
REQUIRE(emptyInfo.getOffset().getSize() == 50);
REQUIRE(emptyInfo.hasSegmentMetadata() == false);
size_t emptyFileSize = 0;
const uint8_t* fileData = emptyInfo.getFileData(&emptyFileSize);
REQUIRE(emptyFileSize == 10);
for(size_t idx = 0; idx < 10; idx++) {
REQUIRE(fileData[idx] == idx);
}
deserializer.setEndianness(SerializeIF::Endianness::NETWORK);
info.addSegmentMetadataInfo(cfdp::RecordContinuationState::CONTAINS_START_AND_END,
fileBuffer.data(), 10);
serializer.update();
buffer = fileDataBuffer.data();
sz = 0;
result = serializer.serialize(&buffer, &sz, fileDataBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyInfo.getOffset().getSize() == 50);
REQUIRE(emptyInfo.hasSegmentMetadata() == true);
REQUIRE(emptyInfo.getRecordContinuationState() ==
cfdp::RecordContinuationState::CONTAINS_START_AND_END);
emptyFileSize = 0;
fileData = emptyInfo.getFileData(&emptyFileSize);
REQUIRE(emptyFileSize == 10);
for(size_t idx = 0; idx < 10; idx++) {
REQUIRE(fileData[idx] == idx);
}
size_t segmentMetadataLen = 0;
fileData = emptyInfo.getSegmentMetadata(&segmentMetadataLen);
REQUIRE(segmentMetadataLen == 10);
for(size_t idx = 0; idx < 10; idx++) {
REQUIRE(fileData[idx] == idx);
}
for(size_t invalidPduField = 0; invalidPduField < 24; invalidPduField++) {
fileDataBuffer[1] = (invalidPduField >> 8) & 0xff;
fileDataBuffer[2] = invalidPduField & 0xff;
result = deserializer.parseData();
// Starting at 15, the file data is parsed. There is not leading file data length
// field to the parser can't check whether the remaining length is valid
if(invalidPduField < 15) {
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
}
}

View File

@ -0,0 +1,196 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/globalfunctions/arrayprinter.h"
#include "fsfw/cfdp/pdu/FinishedPduDeserializer.h"
#include "fsfw/cfdp/pdu/FinishedPduSerializer.h"
#include <array>
TEST_CASE( "Finished PDU" , "[FinishedPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> fnBuffer = {};
uint8_t* buffer = fnBuffer.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
cfdp::Lv emptyFsMsg;
FinishedInfo info(cfdp::ConditionCode::INACTIVITY_DETECTED,
cfdp::FinishedDeliveryCode::DATA_INCOMPLETE,
cfdp::FinishedFileStatus::DISCARDED_DELIBERATELY);
SECTION("Serialize") {
FinishPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, fnBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getSerializedSize() == 12);
REQUIRE(((fnBuffer[1] << 8) | fnBuffer[2]) == 2);
REQUIRE(fnBuffer[10] == cfdp::FileDirectives::FINISH);
REQUIRE(((fnBuffer[sz - 1] >> 4) & 0x0f) == cfdp::ConditionCode::INACTIVITY_DETECTED);
REQUIRE(((fnBuffer[sz - 1] >> 2) & 0x01) == cfdp::FinishedDeliveryCode::DATA_INCOMPLETE);
REQUIRE((fnBuffer[sz - 1] & 0b11) == cfdp::FinishedFileStatus::DISCARDED_DELIBERATELY);
REQUIRE(sz == 12);
// Add a filestore response
std::string firstName = "hello.txt";
cfdp::Lv firstNameLv(reinterpret_cast<uint8_t*>(firstName.data()), firstName.size());
FilestoreResponseTlv response(cfdp::FilestoreActionCode::DELETE_FILE,
cfdp::FSR_APPEND_FILE_1_NOT_EXISTS, firstNameLv, nullptr);
FilestoreResponseTlv* responsePtr = &response;
REQUIRE(response.getSerializedSize() == 14);
size_t len = 1;
info.setFilestoreResponsesArray(&responsePtr, &len, &len);
serializer.updateDirectiveFieldLen();
REQUIRE(serializer.getSerializedSize() == 12 + 14);
sz = 0;
buffer = fnBuffer.data();
result = serializer.serialize(&buffer, &sz, fnBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getSerializedSize() == 12 + 14);
REQUIRE(serializer.getPduDataFieldLen() == 16);
// Add two filestore responses and a fault location parameter
std::string secondName = "hello2.txt";
cfdp::Lv secondNameLv(reinterpret_cast<uint8_t*>(secondName.data()), secondName.size());
FilestoreResponseTlv response2(cfdp::FilestoreActionCode::DENY_FILE ,
cfdp::FSR_SUCCESS, secondNameLv, nullptr);
REQUIRE(response2.getSerializedSize() == 15);
len = 2;
std::array<FilestoreResponseTlv*, 2> responses {&response, &response2};
info.setFilestoreResponsesArray(responses.data(), &len, &len);
serializer.updateDirectiveFieldLen();
EntityIdTlv faultLoc(destId);
REQUIRE(faultLoc.getSerializedSize() == 4);
info.setFaultLocation(&faultLoc);
serializer.updateDirectiveFieldLen();
sz = 0;
buffer = fnBuffer.data();
result = serializer.serialize(&buffer, &sz, fnBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
info.setConditionCode(cfdp::ConditionCode::FILESTORE_REJECTION);
REQUIRE(serializer.getSerializedSize() == 12 + 14 + 15 + 4);
REQUIRE(sz == 12 + 14 + 15 + 4);
info.setFileStatus(cfdp::FinishedFileStatus::DISCARDED_FILESTORE_REJECTION);
REQUIRE(info.getFileStatus() == cfdp::FinishedFileStatus::DISCARDED_FILESTORE_REJECTION);
info.setDeliveryCode(cfdp::FinishedDeliveryCode::DATA_INCOMPLETE);
REQUIRE(info.getDeliveryCode() == cfdp::FinishedDeliveryCode::DATA_INCOMPLETE);
for(size_t maxSz = 0; maxSz < 45; maxSz++) {
sz = 0;
buffer = fnBuffer.data();
result = serializer.serialize(&buffer, &sz, maxSz, SerializeIF::Endianness::NETWORK);
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
SECTION("Deserialize") {
FinishedInfo emptyInfo;
FinishPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, fnBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
FinishPduDeserializer deserializer(fnBuffer.data(), fnBuffer.size(), emptyInfo);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyInfo.getFileStatus() == cfdp::FinishedFileStatus::DISCARDED_DELIBERATELY);
REQUIRE(emptyInfo.getConditionCode() == cfdp::ConditionCode::INACTIVITY_DETECTED);
REQUIRE(emptyInfo.getDeliveryCode() == cfdp::FinishedDeliveryCode::DATA_INCOMPLETE);
// Add a filestore response
sz = 0;
buffer = fnBuffer.data();
std::string firstName = "hello.txt";
cfdp::Lv firstNameLv(reinterpret_cast<uint8_t*>(firstName.data()), firstName.size());
FilestoreResponseTlv response(cfdp::FilestoreActionCode::DELETE_FILE,
cfdp::FSR_NOT_PERFORMED, firstNameLv, nullptr);
FilestoreResponseTlv* responsePtr = &response;
size_t len = 1;
info.setFilestoreResponsesArray(&responsePtr, &len, &len);
serializer.updateDirectiveFieldLen();
REQUIRE(serializer.getPduDataFieldLen() == 16);
result = serializer.serialize(&buffer, &sz, fnBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
FilestoreResponseTlv emptyResponse(firstNameLv, nullptr);
responsePtr = &emptyResponse;
emptyInfo.setFilestoreResponsesArray(&responsePtr, nullptr, &len);
FinishPduDeserializer deserializer2(fnBuffer.data(), fnBuffer.size(), emptyInfo);
result = deserializer2.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyInfo.getFsResponsesLen() == 1);
FilestoreResponseTlv** responseArray = nullptr;
emptyInfo.getFilestoreResonses(&responseArray, nullptr, nullptr);
REQUIRE(responseArray[0]->getActionCode() == cfdp::FilestoreActionCode::DELETE_FILE);
REQUIRE(responseArray[0]->getStatusCode() == cfdp::FSR_NOT_PERFORMED);
auto& fileNameRef = responseArray[0]->getFirstFileName();
size_t stringSize = 0;
const char* string = reinterpret_cast<const char*>(fileNameRef.getValue(&stringSize));
std::string firstFileName(string, stringSize);
REQUIRE(firstFileName == "hello.txt");
// Add two filestore responses and a fault location parameter
std::string secondName = "hello2.txt";
cfdp::Lv secondNameLv(reinterpret_cast<uint8_t*>(secondName.data()), secondName.size());
FilestoreResponseTlv response2(cfdp::FilestoreActionCode::DENY_FILE ,
cfdp::FSR_SUCCESS, secondNameLv, nullptr);
REQUIRE(response2.getSerializedSize() == 15);
len = 2;
std::array<FilestoreResponseTlv*, 2> responses {&response, &response2};
info.setFilestoreResponsesArray(responses.data(), &len, &len);
serializer.updateDirectiveFieldLen();
EntityIdTlv faultLoc(destId);
info.setFaultLocation(&faultLoc);
serializer.updateDirectiveFieldLen();
sz = 0;
buffer = fnBuffer.data();
result = serializer.serialize(&buffer, &sz, fnBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
EntityId emptyId;
EntityIdTlv emptyFaultLoc(emptyId);
emptyInfo.setFaultLocation(&emptyFaultLoc);
response.setFilestoreMessage(&emptyFsMsg);
emptyInfo.setFilestoreResponsesArray(responses.data(), &len, &len);
response2.setFilestoreMessage(&emptyFsMsg);
FinishPduDeserializer deserializer3(fnBuffer.data(), fnBuffer.size(), emptyInfo);
result = deserializer3.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
auto& infoRef = deserializer3.getInfo();
REQUIRE(deserializer3.getWholePduSize() == 45);
size_t invalidMaxLen = 1;
emptyInfo.setFilestoreResponsesArray(responses.data(), &len, &invalidMaxLen);
result = deserializer3.parseData();
REQUIRE(result == cfdp::FINISHED_CANT_PARSE_FS_RESPONSES);
emptyInfo.setFilestoreResponsesArray(nullptr, nullptr, nullptr);
result = deserializer3.parseData();
REQUIRE(result == cfdp::FINISHED_CANT_PARSE_FS_RESPONSES);
// Clear condition code
auto tmp = fnBuffer[11];
fnBuffer[11] = fnBuffer[11] & ~0xf0;
fnBuffer[11] = fnBuffer[11] | (cfdp::ConditionCode::NO_ERROR << 4);
emptyInfo.setFilestoreResponsesArray(responses.data(), &len, &len);
result = deserializer3.parseData();
REQUIRE(result == cfdp::INVALID_TLV_TYPE);
fnBuffer[11] = tmp;
// Invalid TLV type, should be entity ID
fnBuffer[sz - 4] = cfdp::TlvTypes::FILESTORE_REQUEST;
result = deserializer3.parseData();
REQUIRE(result == cfdp::INVALID_TLV_TYPE);
for(size_t maxSz = 0; maxSz < 45; maxSz++) {
FinishPduDeserializer faultyDeser(fnBuffer.data(), maxSz, emptyInfo);
result = faultyDeser.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
}

View File

@ -0,0 +1,84 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/KeepAlivePduDeserializer.h"
#include "fsfw/cfdp/pdu/KeepAlivePduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include <array>
TEST_CASE( "Keep Alive PDU" , "[KeepAlivePdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> kaBuffer = {};
uint8_t* buffer = kaBuffer.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
FileSize progress(0x50);
SECTION("Serialize") {
KeepAlivePduSerializer serializer(pduConf, progress);
result = serializer.serialize(&buffer, &sz, kaBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(kaBuffer[10] == cfdp::FileDirectives::KEEP_ALIVE);
uint32_t fsRaw = 0;
result = SerializeAdapter::deSerialize(&fsRaw, kaBuffer.data() + 11, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fsRaw == 0x50);
REQUIRE(sz == 15);
REQUIRE(serializer.getWholePduSize() == 15);
REQUIRE(serializer.getPduDataFieldLen() == 5);
pduConf.largeFile = true;
serializer.updateDirectiveFieldLen();
buffer = kaBuffer.data();
sz = 0;
result = serializer.serialize(&buffer, &sz, kaBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getWholePduSize() == 19);
REQUIRE(serializer.getPduDataFieldLen() == 9);
uint64_t fsRawLarge = 0;
result = SerializeAdapter::deSerialize(&fsRawLarge, kaBuffer.data() + 11, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fsRawLarge == 0x50);
for(size_t invalidMaxSz = 0; invalidMaxSz < sz; invalidMaxSz++) {
buffer = kaBuffer.data();
sz = 0;
result = serializer.serialize(&buffer, &sz, invalidMaxSz,
SerializeIF::Endianness::NETWORK);
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
SECTION("Deserialize") {
KeepAlivePduSerializer serializer(pduConf, progress);
result = serializer.serialize(&buffer, &sz, kaBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
// Set another file size
progress.setFileSize(200, false);
KeepAlivePduDeserializer deserializer(kaBuffer.data(), kaBuffer.size(), progress);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
auto& progRef = deserializer.getProgress();
// Should have been overwritten
REQUIRE(progRef.getSize() == 0x50);
sz = deserializer.getWholePduSize();
// invalid max size
for(size_t invalidMaxSz = 0; invalidMaxSz < sz; invalidMaxSz++) {
deserializer.setData(kaBuffer.data(), invalidMaxSz);
result = deserializer.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
}

View File

@ -0,0 +1,185 @@
#include <catch2/catch_test_macros.hpp>
#include <fsfw/cfdp/tlv/MessageToUserTlv.h>
#include "fsfw/cfdp/tlv/FilestoreResponseTlv.h"
#include "fsfw/cfdp/pdu/MetadataPduDeserializer.h"
#include "fsfw/cfdp/pdu/MetadataPduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include <array>
TEST_CASE( "Metadata PDU" , "[MetadataPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> mdBuffer = {};
uint8_t* buffer = mdBuffer.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
std::string firstFileName = "hello.txt";
cfdp::Lv sourceFileName(reinterpret_cast<const uint8_t*>(firstFileName.data()),
firstFileName.size());
cfdp::Lv destFileName(nullptr, 0);
FileSize fileSize(35);
MetadataInfo info(false, ChecksumType::MODULAR, fileSize, sourceFileName, destFileName);
FilestoreResponseTlv response(FilestoreActionCode::CREATE_DIRECTORY, FSR_CREATE_NOT_ALLOWED,
sourceFileName, nullptr);
std::array<uint8_t, 3> msg = {0x41, 0x42, 0x43};
cfdp::Tlv responseTlv;
std::array<uint8_t, 64> responseBuf = {};
uint8_t* responseBufPtr = responseBuf.data();
response.convertToTlv(responseTlv, buffer, responseBuf.size(),
SerializeIF::Endianness::MACHINE);
MessageToUserTlv msgToUser(msg.data(), msg.size());
std::array<Tlv*, 2> options {&responseTlv, &msgToUser};
REQUIRE(options[0]->getSerializedSize() == 2 + 1 + 10 + 1);
REQUIRE(options[1]->getSerializedSize() == 5);
SECTION("Serialize") {
MetadataPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, mdBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getWholePduSize() == 27);
REQUIRE(info.getSourceFileName().getSerializedSize() == 10);
REQUIRE(info.getDestFileName().getSerializedSize() == 1);
REQUIRE(info.getSerializedSize() == 16);
REQUIRE((mdBuffer[1] << 8 | mdBuffer[2]) == 17);
REQUIRE(mdBuffer[10] == FileDirectives::METADATA);
// no closure requested and checksum type is modular => 0x00
REQUIRE(mdBuffer[11] == 0x00);
uint32_t fileSizeRaw = 0;
result = SerializeAdapter::deSerialize(&fileSizeRaw, mdBuffer.data() + 12, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(fileSizeRaw == 35);
REQUIRE(mdBuffer[16] == 9);
REQUIRE(mdBuffer[17] == 'h');
REQUIRE(mdBuffer[18] == 'e');
REQUIRE(mdBuffer[19] == 'l');
REQUIRE(mdBuffer[20] == 'l');
REQUIRE(mdBuffer[21] == 'o');
REQUIRE(mdBuffer[22] == '.');
REQUIRE(mdBuffer[23] == 't');
REQUIRE(mdBuffer[24] == 'x');
REQUIRE(mdBuffer[25] == 't');
REQUIRE(mdBuffer[26] == 0);
std::string otherFileName = "hello2.txt";
cfdp::Lv otherFileNameLv(reinterpret_cast<const uint8_t*>(otherFileName.data()),
otherFileName.size());
info.setSourceFileName(otherFileNameLv);
size_t sizeOfOptions = options.size();
info.setOptionsArray(options.data(), &sizeOfOptions, &sizeOfOptions);
REQUIRE(info.getMaxOptionsLen() == 2);
info.setMaxOptionsLen(3);
REQUIRE(info.getMaxOptionsLen() == 3);
info.setChecksumType(cfdp::ChecksumType::CRC_32C);
info.setClosureRequested(true);
uint8_t* buffer = mdBuffer.data();
size_t sz = 0;
serializer.updateDirectiveFieldLen();
result = serializer.serialize(&buffer, &sz, mdBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE((mdBuffer[1] << 8 | mdBuffer[2]) == 37);
auto checksumType = static_cast<cfdp::ChecksumType>(mdBuffer[11] & 0x0f);
REQUIRE(checksumType == cfdp::ChecksumType::CRC_32C);
bool closureRequested = mdBuffer[11] >> 6 & 0x01;
REQUIRE(closureRequested == true);
// The size of the two options is 19. Summing up:
// - 11 bytes of source file name
// - 1 byte for dest file name
// - 4 for FSS
// - 1 leading byte.
// - 1 byte for PDU type
// PDU header has 10 bytes.
// I am not going to check the options raw content, those are part of the dedicated
// TLV unittests
REQUIRE(sz == 10 + 37);
for(size_t maxSz = 0; maxSz < sz; maxSz++) {
uint8_t* buffer = mdBuffer.data();
size_t sz = 0;
result = serializer.serialize(&buffer, &sz, maxSz,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
}
for(size_t initSz = 1; initSz < 47; initSz++) {
uint8_t* buffer = mdBuffer.data();
size_t sz = initSz;
result = serializer.serialize(&buffer, &sz, 46, SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
}
info.setDestFileName(destFileName);
}
SECTION("Deserialize") {
MetadataPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, mdBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
MetadataPduDeserializer deserializer(mdBuffer.data(), mdBuffer.size(), info);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
size_t fullSize = deserializer.getWholePduSize();
for(size_t maxSz = 0; maxSz < fullSize; maxSz++) {
MetadataPduDeserializer invalidSzDeser(mdBuffer.data(), maxSz, info);
result = invalidSzDeser.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
size_t sizeOfOptions = options.size();
size_t maxSize = 4;
info.setOptionsArray(options.data(), &sizeOfOptions, &maxSize);
REQUIRE(info.getOptionsLen() == 2);
info.setChecksumType(cfdp::ChecksumType::CRC_32C);
info.setClosureRequested(true);
uint8_t* buffer = mdBuffer.data();
size_t sz = 0;
serializer.updateDirectiveFieldLen();
info.setSourceFileName(sourceFileName);
result = serializer.serialize(&buffer, &sz, mdBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
MetadataPduDeserializer deserializer2(mdBuffer.data(), mdBuffer.size(), info);
result = deserializer2.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(options[0]->getType() == cfdp::TlvTypes::FILESTORE_RESPONSE);
REQUIRE(options[0]->getSerializedSize() == 14);
REQUIRE(options[1]->getType() == cfdp::TlvTypes::MSG_TO_USER);
REQUIRE(options[1]->getSerializedSize() == 5);
for(size_t invalidFieldLen = 0; invalidFieldLen < 36; invalidFieldLen++) {
mdBuffer[1] = (invalidFieldLen >> 8) & 0xff;
mdBuffer[2] = invalidFieldLen & 0xff;
result = deserializer2.parseData();
if(invalidFieldLen == 17) {
REQUIRE(info.getOptionsLen() == 0);
}
if(invalidFieldLen == 31) {
REQUIRE(info.getOptionsLen() == 1);
}
// This is the precise length where there are no options or one option
if(invalidFieldLen != 17 and invalidFieldLen != 31) {
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
mdBuffer[1] = (36 >> 8) & 0xff;
mdBuffer[2] = 36 & 0xff;
info.setOptionsArray(nullptr, nullptr, nullptr);
REQUIRE(deserializer2.parseData() == cfdp::METADATA_CANT_PARSE_OPTIONS);
info.setOptionsArray(options.data(), &sizeOfOptions, nullptr);
for(size_t maxSz = 0; maxSz < 46; maxSz++) {
MetadataPduDeserializer invalidSzDeser(mdBuffer.data(), maxSz, info);
result = invalidSzDeser.parseData();
REQUIRE(result == SerializeIF::STREAM_TOO_SHORT);
}
}
}

View File

@ -0,0 +1,160 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/PduConfig.h"
#include "fsfw/cfdp/pdu/NakPduDeserializer.h"
#include "fsfw/cfdp/pdu/NakPduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include <array>
TEST_CASE( "NAK PDU" , "[NakPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> nakBuffer = {};
uint8_t* buffer = nakBuffer.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
FileSize startOfScope(50);
FileSize endOfScope(1050);
NakInfo info(startOfScope, endOfScope);
SECTION("Serializer") {
NakPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, nakBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getSerializedSize() == 19);
REQUIRE(serializer.FileDirectiveSerializer::getSerializedSize() == 11);
REQUIRE(sz == 19);
REQUIRE(serializer.getPduDataFieldLen() == 9);
REQUIRE(((nakBuffer[1] << 8) | nakBuffer[2]) == 0x09);
REQUIRE(nakBuffer[10] == cfdp::FileDirectives::NAK);
uint32_t scope = 0;
result = SerializeAdapter::deSerialize(&scope, nakBuffer.data() + 11, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(scope == 50);
result = SerializeAdapter::deSerialize(&scope, nakBuffer.data() + 15, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(scope == 1050);
NakInfo::SegmentRequest segReq0(cfdp::FileSize(2020), cfdp::FileSize(2520));
NakInfo::SegmentRequest segReq1(cfdp::FileSize(2932), cfdp::FileSize(3021));
// Now add 2 segment requests to NAK info and serialize them as well
std::array<NakInfo::SegmentRequest, 2> segReqs = { segReq0, segReq1 };
size_t segReqsLen = segReqs.size();
info.setSegmentRequests(segReqs.data(), &segReqsLen , &segReqsLen);
uint8_t* buffer = nakBuffer.data();
size_t sz = 0;
serializer.updateDirectiveFieldLen();
result = serializer.serialize(&buffer, &sz, nakBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getSerializedSize() == 35);
REQUIRE(serializer.getPduDataFieldLen() == 25);
REQUIRE(((nakBuffer[1] << 8) | nakBuffer[2]) == 25);
uint32_t segReqScopes = 0;
result = SerializeAdapter::deSerialize(&segReqScopes, nakBuffer.data() + 19, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(segReqScopes == 2020);
result = SerializeAdapter::deSerialize(&segReqScopes, nakBuffer.data() + 23, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(segReqScopes == 2520);
result = SerializeAdapter::deSerialize(&segReqScopes, nakBuffer.data() + 27, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(segReqScopes == 2932);
result = SerializeAdapter::deSerialize(&segReqScopes, nakBuffer.data() + 31, nullptr,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(segReqScopes == 3021);
for(size_t maxSz = 0; maxSz < 35; maxSz++) {
uint8_t* buffer = nakBuffer.data();
size_t sz = 0;
result = serializer.serialize(&buffer, &sz, maxSz,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
}
for(size_t sz = 35; sz > 0; sz--) {
uint8_t* buffer = nakBuffer.data();
size_t locSize = sz;
result = serializer.serialize(&buffer, &locSize, 35,
SerializeIF::Endianness::NETWORK);
REQUIRE(result == SerializeIF::BUFFER_TOO_SHORT);
}
}
SECTION("Deserializer") {
NakPduSerializer serializer(pduConf, info);
result = serializer.serialize(&buffer, &sz, nakBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
info.getStartOfScope().setFileSize(0, false);
info.getEndOfScope().setFileSize(0, false);
NakPduDeserializer deserializer(nakBuffer.data(), nakBuffer.size(), info);
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserializer.getWholePduSize() == 19);
REQUIRE(info.getStartOfScope().getSize() == 50);
REQUIRE(info.getEndOfScope().getSize() == 1050);
NakInfo::SegmentRequest segReq0(cfdp::FileSize(2020), cfdp::FileSize(2520));
NakInfo::SegmentRequest segReq1(cfdp::FileSize(2932), cfdp::FileSize(3021));
// Now add 2 segment requests to NAK info and serialize them as well
std::array<NakInfo::SegmentRequest, 2> segReqs = { segReq0, segReq1 };
size_t segReqsLen = segReqs.size();
info.setSegmentRequests(segReqs.data(), &segReqsLen, &segReqsLen);
uint8_t* buffer = nakBuffer.data();
size_t sz = 0;
serializer.updateDirectiveFieldLen();
result = serializer.serialize(&buffer, &sz, nakBuffer.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
NakPduDeserializer deserializeWithSegReqs(nakBuffer.data(), nakBuffer.size(), info);
result = deserializeWithSegReqs.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
NakInfo::SegmentRequest* segReqsPtr = nullptr;
size_t readSegReqs = 0;
info.getSegmentRequests(&segReqsPtr, &readSegReqs, nullptr);
REQUIRE(readSegReqs == 2);
REQUIRE(segReqsPtr[0].first.getSize() == 2020);
REQUIRE(segReqsPtr[0].second.getSize() == 2520);
REQUIRE(segReqsPtr[1].first.getSize() == 2932);
REQUIRE(segReqsPtr[1].second.getSize() == 3021);
REQUIRE(deserializeWithSegReqs.getPduDataFieldLen() == 25);
REQUIRE(info.getSegmentRequestsLen() == 2);
for(size_t idx = 0; idx < 34; idx ++) {
NakPduDeserializer faultyDeserializer(nakBuffer.data(), idx, info);
result = faultyDeserializer.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
for(size_t pduFieldLen = 0; pduFieldLen < 25; pduFieldLen++) {
nakBuffer[1] = (pduFieldLen >> 8) & 0xff;
nakBuffer[2] = pduFieldLen & 0xff;
NakPduDeserializer faultyDeserializer(nakBuffer.data(), nakBuffer.size(), info);
result = faultyDeserializer.parseData();
if(pduFieldLen == 9) {
REQUIRE(info.getSegmentRequestsLen() == 0);
} else if(pduFieldLen == 17) {
REQUIRE(info.getSegmentRequestsLen() == 1);
} else if(pduFieldLen == 25) {
REQUIRE(info.getSegmentRequestsLen() == 2);
}
if(pduFieldLen != 9 and pduFieldLen != 17 and pduFieldLen != 25) {
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
info.setMaxSegmentRequestLen(5);
REQUIRE(info.getSegmentRequestsMaxLen() == 5);
}
}

View File

@ -0,0 +1,71 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/cfdp/pdu/PromptPduDeserializer.h"
#include "fsfw/cfdp/pdu/PromptPduSerializer.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include <array>
TEST_CASE( "Prompt PDU" , "[PromptPdu]") {
using namespace cfdp;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 256> rawBuf = {};
uint8_t* buffer = rawBuf.data();
size_t sz = 0;
EntityId destId(WidthInBytes::TWO_BYTES, 2);
TransactionSeqNum seqNum(WidthInBytes::TWO_BYTES, 15);
EntityId sourceId(WidthInBytes::TWO_BYTES, 1);
PduConfig pduConf(TransmissionModes::ACKNOWLEDGED, seqNum, sourceId, destId);
SECTION("Serialize") {
PromptPduSerializer serializer(pduConf, cfdp::PromptResponseRequired::PROMPT_KEEP_ALIVE);
result = serializer.serialize(&buffer, &sz, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(serializer.getWholePduSize() == 12);
REQUIRE(sz == 12);
REQUIRE(serializer.getPduDataFieldLen() == 2);
REQUIRE(rawBuf[10] == FileDirectives::PROMPT);
REQUIRE((rawBuf[sz - 1] >> 7) & 0x01 == cfdp::PromptResponseRequired::PROMPT_KEEP_ALIVE);
for(size_t invalidMaxSz = 0; invalidMaxSz < sz; invalidMaxSz++) {
uint8_t* buffer = rawBuf.data();
size_t sz = 0;
result = serializer.serialize(&buffer, &sz, invalidMaxSz,
SerializeIF::Endianness::NETWORK);
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
for(size_t invalidSz = 1; invalidSz < sz; invalidSz++) {
size_t locSz = invalidSz;
uint8_t* buffer = rawBuf.data();
result = serializer.serialize(&buffer, &locSz, sz,
SerializeIF::Endianness::NETWORK);
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
SECTION("Deserialize") {
PromptPduSerializer serializer(pduConf, cfdp::PromptResponseRequired::PROMPT_KEEP_ALIVE);
result = serializer.serialize(&buffer, &sz, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
PromptPduDeserializer deserializer(rawBuf.data(), rawBuf.size());
result = deserializer.parseData();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserializer.getPromptResponseRequired() ==
cfdp::PromptResponseRequired::PROMPT_KEEP_ALIVE);
sz = deserializer.getWholePduSize();
rawBuf[2] = 1;
result = deserializer.parseData();
REQUIRE(result == SerializeIF::STREAM_TOO_SHORT);
rawBuf[2] = 2;
for(size_t invalidMaxSz = 0; invalidMaxSz < sz; invalidMaxSz++) {
deserializer.setData(rawBuf.data(), invalidMaxSz);
result = deserializer.parseData();
REQUIRE(result != HasReturnvaluesIF::RETURN_OK);
}
}
}

View File

@ -0,0 +1,334 @@
#include "fsfw/cfdp/pdu/PduConfig.h"
#include "fsfw/cfdp/tlv/Tlv.h"
#include "fsfw/cfdp/tlv/Lv.h"
#include <catch2/catch_test_macros.hpp>
#include <fsfw/cfdp/tlv/EntityIdTlv.h>
#include <fsfw/cfdp/tlv/FaultHandlerOverrideTlv.h>
#include <fsfw/cfdp/tlv/FilestoreRequestTlv.h>
#include "fsfw/globalfunctions/arrayprinter.h"
#include <fsfw/cfdp/tlv/FilestoreResponseTlv.h>
#include <fsfw/cfdp/tlv/FlowLabelTlv.h>
#include <fsfw/cfdp/tlv/MessageToUserTlv.h>
#include <array>
#include <string>
TEST_CASE( "CFDP TLV LV" , "[CfdpTlvLv]") {
using namespace cfdp;
int result = HasReturnvaluesIF::RETURN_OK;
std::array<uint8_t, 255> rawBuf;
uint8_t* serPtr = rawBuf.data();
const uint8_t* deserPtr = rawBuf.data();
size_t deserSize = 0;
cfdp::EntityId sourceId = EntityId(cfdp::WidthInBytes::TWO_BYTES, 0x0ff0);
SECTION("TLV Serialization") {
std::array<uint8_t, 8> tlvRawBuf;
serPtr = tlvRawBuf.data();
result = sourceId.serialize(&serPtr, &deserSize, tlvRawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 2);
auto tlv = Tlv(TlvTypes::ENTITY_ID, tlvRawBuf.data(), deserSize);
REQUIRE(tlv.getSerializedSize() == 4);
REQUIRE(tlv.getLengthField() == 2);
serPtr = rawBuf.data();
deserSize = 0;
result = tlv.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 4);
REQUIRE(rawBuf[0] == TlvTypes::ENTITY_ID);
REQUIRE(rawBuf[1] == 2);
uint16_t entityId = 0;
SerializeAdapter::deSerialize(&entityId, rawBuf.data() + 2, &deserSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(entityId == 0x0ff0);
// Set new value
sourceId.setValue(cfdp::WidthInBytes::FOUR_BYTES, 12);
serPtr = tlvRawBuf.data();
deserSize = 0;
result = sourceId.serialize(&serPtr, &deserSize, tlvRawBuf.size(),
SerializeIF::Endianness::NETWORK);
tlv.setValue(tlvRawBuf.data(), cfdp::WidthInBytes::FOUR_BYTES);
serPtr = rawBuf.data();
deserSize = 0;
result = tlv.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(rawBuf[0] == TlvTypes::ENTITY_ID);
REQUIRE(rawBuf[1] == 4);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
serPtr = rawBuf.data();
deserSize = 0;
auto tlvInvalid = Tlv(cfdp::TlvTypes::INVALID_TLV, tlvRawBuf.data(), 0);
REQUIRE(tlvInvalid.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK) != HasReturnvaluesIF::RETURN_OK);
tlvInvalid = Tlv(cfdp::TlvTypes::ENTITY_ID, nullptr, 3);
REQUIRE(tlvInvalid.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK) != HasReturnvaluesIF::RETURN_OK);
REQUIRE(tlvInvalid.serialize(&serPtr, &deserSize, 0,
SerializeIF::Endianness::NETWORK) != HasReturnvaluesIF::RETURN_OK);
REQUIRE(tlvInvalid.getSerializedSize() == 0);
REQUIRE(tlvInvalid.serialize(nullptr, nullptr, 0,
SerializeIF::Endianness::NETWORK) != HasReturnvaluesIF::RETURN_OK);
Tlv zeroLenField(TlvTypes::FAULT_HANDLER, nullptr, 0);
REQUIRE(zeroLenField.getSerializedSize() == 2);
serPtr = rawBuf.data();
deserSize = 0;
REQUIRE(zeroLenField.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK) == HasReturnvaluesIF::RETURN_OK);
REQUIRE(rawBuf[0] == TlvTypes::FAULT_HANDLER);
REQUIRE(rawBuf[1] == 0);
}
SECTION("TLV Deserialization") {
// Serialization was tested before, generate raw data now
std::array<uint8_t, 8> tlvRawBuf;
serPtr = tlvRawBuf.data();
result = sourceId.serialize(&serPtr, &deserSize, tlvRawBuf.size(),
SerializeIF::Endianness::NETWORK);
auto tlvSerialization = Tlv(TlvTypes::ENTITY_ID, tlvRawBuf.data(), deserSize);
serPtr = rawBuf.data();
deserSize = 0;
result = tlvSerialization.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
Tlv tlv;
deserPtr = rawBuf.data();
result = tlv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(tlv.getSerializedSize() == 4);
REQUIRE(tlv.getType() == TlvTypes::ENTITY_ID);
deserPtr = tlv.getValue();
uint16_t entityId = 0;
deserSize = 0;
SerializeAdapter::deSerialize(&entityId, deserPtr, &deserSize,
SerializeIF::Endianness::NETWORK);
REQUIRE(entityId == 0x0ff0);
REQUIRE(tlv.deSerialize(nullptr, nullptr, SerializeIF::Endianness::NETWORK) !=
HasReturnvaluesIF::RETURN_OK);
deserPtr = rawBuf.data();
deserSize = 0;
REQUIRE(tlv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::NETWORK) ==
SerializeIF::STREAM_TOO_SHORT);
// Set invalid TLV
rawBuf[0] = TlvTypes::INVALID_TLV;
deserSize = 4;
REQUIRE(tlv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::NETWORK) !=
HasReturnvaluesIF::RETURN_OK);
Tlv zeroLenField(TlvTypes::FAULT_HANDLER, nullptr, 0);
serPtr = rawBuf.data();
deserSize = 0;
REQUIRE(zeroLenField.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK) == HasReturnvaluesIF::RETURN_OK);
deserPtr = rawBuf.data();
result = zeroLenField.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(zeroLenField.getSerializedSize() == 2);
REQUIRE(deserSize == 0);
}
SECTION("LV Serialization") {
std::array<uint8_t, 8> lvRawBuf;
serPtr = lvRawBuf.data();
result = sourceId.serialize(&serPtr, &deserSize, lvRawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 2);
auto lv = cfdp::Lv(lvRawBuf.data(), 2);
auto lvCopy = cfdp::Lv(lv);
REQUIRE(lv.getSerializedSize() == 3);
REQUIRE(lvCopy.getSerializedSize() == 3);
REQUIRE(lv.getValue(nullptr) == lvCopy.getValue(nullptr));
serPtr = rawBuf.data();
deserSize = 0;
result = lv.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 3);
REQUIRE(rawBuf[0] == 2);
uint16_t sourceId = 0;
result = SerializeAdapter::deSerialize(&sourceId, rawBuf.data() + 1, &deserSize,
SerializeIF::Endianness::BIG);
REQUIRE(sourceId == 0x0ff0);
auto lvEmpty = Lv(nullptr, 0);
REQUIRE(lvEmpty.getSerializedSize() == 1);
serPtr = rawBuf.data();
deserSize = 0;
result = lvEmpty.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 1);
}
SECTION("LV Deserialization") {
std::array<uint8_t, 8> lvRawBuf;
serPtr = lvRawBuf.data();
result = sourceId.serialize(&serPtr, &deserSize, lvRawBuf.size(),
SerializeIF::Endianness::NETWORK);
auto lv = cfdp::Lv(lvRawBuf.data(), 2);
serPtr = rawBuf.data();
deserSize = 0;
result = lv.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
Lv uninitLv;
deserPtr = rawBuf.data();
deserSize = 3;
result = uninitLv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::BIG);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(uninitLv.getSerializedSize() == 3);
const uint8_t* storedValue = uninitLv.getValue(nullptr);
uint16_t sourceId = 0;
result = SerializeAdapter::deSerialize(&sourceId, storedValue, &deserSize,
SerializeIF::Endianness::BIG);
REQUIRE(sourceId == 0x0ff0);
auto lvEmpty = Lv(nullptr, 0);
REQUIRE(lvEmpty.getSerializedSize() == 1);
serPtr = rawBuf.data();
deserSize = 0;
result = lvEmpty.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(deserSize == 1);
deserPtr = rawBuf.data();
result = uninitLv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::BIG);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(uninitLv.getSerializedSize() == 1);
REQUIRE(uninitLv.deSerialize(nullptr, nullptr, SerializeIF::Endianness::BIG) ==
HasReturnvaluesIF::RETURN_FAILED);
serPtr = rawBuf.data();
deserSize = 0;
REQUIRE(uninitLv.serialize(&serPtr, &deserSize, 0, SerializeIF::Endianness::BIG) ==
SerializeIF::BUFFER_TOO_SHORT);
REQUIRE(uninitLv.serialize(nullptr, nullptr, 12, SerializeIF::Endianness::BIG));
deserSize = 0;
REQUIRE(uninitLv.deSerialize(&deserPtr, &deserSize, SerializeIF::Endianness::BIG) ==
SerializeIF::STREAM_TOO_SHORT);
}
SECTION("Filestore Response TLV") {
std::string name = "hello.txt";
cfdp::Lv firstName(reinterpret_cast<const uint8_t*>(name.data()), name.size());
std::string name2 = "hello2.txt";
cfdp::Lv secondName(reinterpret_cast<const uint8_t*>(name2.data()), name2.size());
std::string msg = "12345";
cfdp::Lv fsMsg(reinterpret_cast<const uint8_t*>(msg.data()), msg.size());
FilestoreResponseTlv response(cfdp::FilestoreActionCode::APPEND_FILE, cfdp::FSR_SUCCESS,
firstName, &fsMsg);
response.setSecondFileName(&secondName);
REQUIRE(response.getLengthField() == 10 + 11 + 6 + 1);
REQUIRE(response.getSerializedSize() == response.getLengthField() + 2);
cfdp::Tlv rawResponse;
std::array<uint8_t, 128> serBuf = {};
result = response.convertToTlv(rawResponse, serBuf.data(), serBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(rawResponse.getType() == cfdp::TlvTypes::FILESTORE_RESPONSE);
cfdp::Lv emptyMsg;
cfdp::Lv emptySecondName;
FilestoreResponseTlv emptyTlv(firstName, &emptyMsg);
emptyTlv.setSecondFileName(&emptySecondName);
result = emptyTlv.deSerialize(rawResponse, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyTlv.getActionCode() == cfdp::FilestoreActionCode::APPEND_FILE);
REQUIRE(emptyTlv.getStatusCode() == cfdp::FSR_SUCCESS);
size_t firstNameLen = 0;
const char* firstNamePtr = reinterpret_cast<const char*>(
emptyTlv.getFirstFileName().getValue(&firstNameLen));
auto helloString = std::string(firstNamePtr, firstNameLen);
REQUIRE(helloString == "hello.txt");
}
SECTION("Filestore Request TLV") {
std::string name = "hello.txt";
cfdp::Lv firstName(reinterpret_cast<const uint8_t*>(name.data()), name.size());
std::string name2 = "hello2.txt";
cfdp::Lv secondName(reinterpret_cast<const uint8_t*>(name2.data()), name2.size());
FilestoreRequestTlv request(cfdp::FilestoreActionCode::APPEND_FILE, firstName);
// second name not set yet
REQUIRE(request.getLengthField() == 10 + 1);
REQUIRE(request.getSerializedSize() == request.getLengthField() + 2);
std::array<uint8_t, 128> serBuf = {};
uint8_t* ptr = serBuf.data();
size_t sz = 0;
result = request.serialize(&ptr, &sz, serBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == cfdp::FILESTORE_REQUIRES_SECOND_FILE);
ptr = serBuf.data();
sz = 0;
request.setSecondFileName(&secondName);
size_t expectedSz = request.getLengthField();
REQUIRE(expectedSz == 10 + 11 + 1);
REQUIRE(request.getSerializedSize() == expectedSz + 2);
result = request.serialize(&ptr, &sz, serBuf.size(), SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(sz == expectedSz + 2);
FilestoreRequestTlv emptyRequest(firstName);
emptyRequest.setSecondFileName(&secondName);
const uint8_t* constptr = serBuf.data();
result = emptyRequest.deSerialize(&constptr, &sz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
cfdp::Tlv rawRequest;
ptr = serBuf.data();
sz = 0;
result = request.convertToTlv(rawRequest, serBuf.data(), serBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(rawRequest.getType() == cfdp::TlvTypes::FILESTORE_REQUEST);
emptyRequest.setActionCode(cfdp::FilestoreActionCode::DELETE_FILE);
result = emptyRequest.deSerialize(rawRequest, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(emptyRequest.getType() == cfdp::TlvTypes::FILESTORE_REQUEST);
REQUIRE(emptyRequest.getActionCode() == cfdp::FilestoreActionCode::APPEND_FILE);
}
SECTION("Other") {
MessageToUserTlv emptyTlv;
uint8_t flowLabel = 1;
FlowLabelTlv flowLabelTlv(&flowLabel, 1);
FaultHandlerOverrideTlv faultOverrideTlv(cfdp::ConditionCode::FILESTORE_REJECTION,
cfdp::FaultHandlerCode::NOTICE_OF_CANCELLATION);
size_t sz = 0;
ReturnValue_t result = faultOverrideTlv.serialize(&serPtr, &sz, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
REQUIRE(faultOverrideTlv.getSerializedSize() == 3);
REQUIRE(sz == 3);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
FaultHandlerOverrideTlv emptyOverrideTlv;
result = emptyOverrideTlv.deSerialize(&deserPtr, &sz, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
EntityId entId(cfdp::WidthInBytes::TWO_BYTES, 0x42);
EntityId emptyId;
EntityIdTlv idTlv(emptyId);
serPtr = rawBuf.data();
result = idTlv.serialize(&serPtr, &deserSize, rawBuf.size(),
SerializeIF::Endianness::NETWORK);
cfdp::Tlv rawTlv(cfdp::TlvTypes::ENTITY_ID, rawBuf.data() + 2, 2);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
deserPtr = rawBuf.data();
result = idTlv.deSerialize(rawTlv, SerializeIF::Endianness::NETWORK);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
}
}