diff --git a/Fw/CMakeLists.txt b/Fw/CMakeLists.txt index 86291b90835..0aa5dcc1936 100644 --- a/Fw/CMakeLists.txt +++ b/Fw/CMakeLists.txt @@ -22,6 +22,9 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/SerializableFile/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Test/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Types/") +# CCSDS subdirectories +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ccsds/Cfdp/") + # Setup an interface target for Fw for efficiency add_library(Fw INTERFACE) add_dependencies(Fw ${FPRIME_FRAMEWORK_MODULES}) diff --git a/Fw/Ccsds/Cfdp/CMakeLists.txt b/Fw/Ccsds/Cfdp/CMakeLists.txt new file mode 100644 index 00000000000..947bd0bac8b --- /dev/null +++ b/Fw/Ccsds/Cfdp/CMakeLists.txt @@ -0,0 +1,31 @@ +#### +# CMakeLists.txt: +# +# Sets up the fprime module build within CMake. +#### +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/EndOfFile.cpp" + "${CMAKE_CURRENT_LIST_DIR}/FileData.cpp" + "${CMAKE_CURRENT_LIST_DIR}/FilePacket.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Finished.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Header.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Metadata.cpp" +) + +set(MOD_DEPS + Fw/Types + Fw/Buffer +) + +register_fprime_module() + +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/ut/TestEndOfFile.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/TestFileData.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/TestFilePacketMain.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/TestFilePacket.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/TestFinished.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/TestHeader.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/TestMetadata.cpp" +) +register_fprime_ut() diff --git a/Fw/Ccsds/Cfdp/EndOfFile.cpp b/Fw/Ccsds/Cfdp/EndOfFile.cpp new file mode 100644 index 00000000000..0c5dacd4101 --- /dev/null +++ b/Fw/Ccsds/Cfdp/EndOfFile.cpp @@ -0,0 +1,111 @@ +//! ============================================================================ +//! @file EndOfFile.cpp +//! @brief cpp file for a CFDP file packet End-of-file data field. +//! @author chownw +//! ============================================================================ + +#include + +#include +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +FilePacket::DirectiveType FilePacket::EndOfFile::directiveCode = + FilePacket::DirectiveType::END_OF_FILE; + +FilePacket::EndOfFile:: + EndOfFile() +{ +} + +FilePacket::EndOfFile:: + EndOfFile(ConditionCode conditionCode, U32 fileChecksum, U64 fileSize) +{ + this->conditionCode = conditionCode; + this->spare = 0; + this->fileChecksum = fileChecksum; + this->fileSize = FileSizeSensitive(fileSize); +} + +FilePacket::ConditionCode FilePacket::EndOfFile:: + getConditionCode() const +{ + return this->conditionCode; +} + +U32 FilePacket::EndOfFile:: + getFileChecksum() const +{ + return this->fileChecksum; +} + +U64 FilePacket::EndOfFile:: + getFileSize() const +{ + return this->fileSize.getValue(); +} + +void FilePacket::EndOfFile:: + serialize(const Fw::Buffer& buf, U32 offset, const Header& header) const +{ + U8* data = buf.getData() + offset; + + // Serialize octet 0 + data[0] = static_cast(this->directiveCode); + + // Serialize octet 1 + data[1] = (static_cast(this->conditionCode) & 15) << 4; + + // Push file checksum in big-endian format + FilePacket::serializeValue( + &data[2], + this->fileChecksum, + FieldLength::FILE_CHECKSUM / 8 + ); + + // Serialize the FSS field file size + U32 fileSizeOffset = offset + FixedSize::BYTES; + this->fileSize.serialize(buf, fileSizeOffset, header); +} + +void FilePacket::EndOfFile:: + deserialize(const Fw::Buffer& buf, U32 offset, const Header& header) +{ + U8* data = buf.getData() + offset; + + // Deserialize octet 1 + this->conditionCode = + static_cast((data[1] >> 4) & 15); + this->spare = data[1] & 15; + + // Deserialize file checksum bytes + this->fileChecksum = FilePacket::deserializeValue( + &data[2], + FieldLength::FILE_CHECKSUM / 8 + ); + + // Deserialize the FSS field file size + U32 fileSizeOffset = offset + FixedSize::BYTES; + this->fileSize.deserialize(buf, fileSizeOffset, header); +} + +U32 FilePacket::EndOfFile:: + getSerializedLength(const Header& header) const +{ + return ( + FixedSize::BYTES + + this->fileSize.getSerializedLength(header) + ); +} + +} // Cfdp + +} // Fw diff --git a/Fw/Ccsds/Cfdp/EndOfFile.hpp b/Fw/Ccsds/Cfdp/EndOfFile.hpp new file mode 100644 index 00000000000..d96e96373a6 --- /dev/null +++ b/Fw/Ccsds/Cfdp/EndOfFile.hpp @@ -0,0 +1,141 @@ +//! ============================================================================ +//! @file EndOfFile.hpp +//! @brief hpp file for a CFDP file packet End-of-file data field. +//! @author chownw +//! ============================================================================ + +#ifndef FW_CFDP_ENDOFFILE_HPP +#define FW_CFDP_ENDOFFILE_HPP + +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +//! @brief A CFDP file packet End-of-file data field. +//! +class FilePacket::EndOfFile : public DataField +{ + friend FilePacket; + + public: + //! @brief Construct an empty CFDP End-of-file PDU. + //! + //! This can be used to construct an End-of-file PDU to hold deserialized + //! data. + //! + EndOfFile(); + + //! @brief Construct a filled CFDP End-of-file PDU. + //! + //! This can be used to construct an End-of-file PDU for serialization. + //! + //! @param conditionCode The condition code. + //! @param fileChecksum The file checksum. + //! @param fileSize The number of file data octets transmitted by sender. + //! + EndOfFile( + ConditionCode conditionCode, + U32 fileChecksum, + U64 fileSize + ); + + //! @brief Get the condition code. + //! + ConditionCode getConditionCode() const; + + //! @brief Get the file checksum. + //! + U32 getFileChecksum() const; + + //! @brief Get the file size. + //! + U64 getFileSize() const; + + PRIVATE: + //! @brief Serialize this End-of-file PDU into a buffer. + //! + //! @param buf The buffer to hold the serialized data. + //! @param offset The byte offset to start serialization from. + //! @param header The header attached to this PDU. + //! + void serialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ) const; + + //! @brief Deserialize a buffer containing serialized End-of-file PDU data. + //! + //! @param buf The buffer containing serialized data. + //! @param offset The byte offset to start deserialization from. + //! @param header The header attached to this PDU. + //! + void deserialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ); + + //! @brief Get the length in octets of this End-of-file PDU when serialized. + //! + //! @param header The header attached to this PDU. + //! + U32 getSerializedLength(const Header& header) const; + + PRIVATE: + //! @brief Length in bits of fixed-size End-of-file PDU fields. + //! + enum FieldLength : U32 + { + DIRECTIVE_CODE = 8, + CONDITION_CODE = 4, + SPARE = 4, + FILE_CHECKSUM = 32, + }; + + //! @brief Total length of fixed-size End-of-file PDU fields. + //! + enum FixedSize : U32 + { + BITS = + FieldLength::DIRECTIVE_CODE + + FieldLength::CONDITION_CODE + + FieldLength::SPARE + + FieldLength::FILE_CHECKSUM, + BYTES = BITS / 8, + }; + + //! @brief Indicates that the data field contains an End-of-file PDU. + //! + static DirectiveType directiveCode; + + //! @brief Condition code. + //! + ConditionCode conditionCode; + + //! @brief Spare bits, set to all zeroes. + //! + U8 spare; + + //! @brief File checksum. + //! + U32 fileChecksum; + + //! @brief Number of file data octets transmitted by sender. + //! + FileSizeSensitive fileSize; + + // NOTE: Fault location field not supported. +}; + +} // Cfdp + +} // Fw + +#endif // FW_CFDP_ENDOFFILE_HPP diff --git a/Fw/Ccsds/Cfdp/FileData.cpp b/Fw/Ccsds/Cfdp/FileData.cpp new file mode 100644 index 00000000000..ef7178ff4c7 --- /dev/null +++ b/Fw/Ccsds/Cfdp/FileData.cpp @@ -0,0 +1,196 @@ +//! ============================================================================ +//! @file FileData.cpp +//! @brief cpp file for a CFDP file packet File Data data field. +//! @author chownw +//! ============================================================================ + +#include + +#include +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +FilePacket::FileData:: + FileData() +{ +} + +FilePacket::FileData:: + FileData( + U64 offset, + U16 fileDataLength, + const U8* fileData, + ContinuationState continuationState, + U8 metadataLength, + const U8* metadata + ) +{ + this->offset = FileSizeSensitive(offset); + this->fileDataLength = fileDataLength; + this->fileData = fileData; + this->continuationState = continuationState; + this->metadataLength = metadataLength; + this->metadata = metadata; +} + +U64 FilePacket::FileData:: + getOffset() const +{ + return this->offset.getValue(); +} + +U16 FilePacket::FileData:: + getFileDataLength() const +{ + return this->fileDataLength; +} + +const U8* FilePacket::FileData:: + getFileData() const +{ + return this->fileData; +} + +FilePacket::ContinuationState FilePacket::FileData:: + getContinuationState() const +{ + return this->continuationState; +} + +U8 FilePacket::FileData:: + getSegmentMetadataLength() const +{ + return this->metadataLength; +} + +const U8* FilePacket::FileData:: + getSegmentMetadata() const +{ + return this->metadata; +} + +void FilePacket::FileData:: + serialize(const Fw::Buffer& buf, U32 offset, const Header& header) const +{ + U8* data = buf.getData() + offset; + + // Truncate metadata length to only 6 bits + U8 metadataLength = this->metadataLength & 63; + + // Initialize offset of the offset field + U32 offsetOffset = offset; + + if (header.segmentMetadataFlag == SegmentMetadataFlag::PRESENT) + { + // Serialize record continuation state and segment metadata length + data[0] = 0; + data[0] |= (static_cast(this->continuationState) & 3) << 6; + data[0] |= metadataLength; + + // Serialize segment metadata + for (int i = 0; i < metadataLength; ++i) + { + data[i + 1] = this->metadata[i]; + } + + // Update offset of the offset field + offsetOffset += FixedSize::BYTES + metadataLength; + } + + // Serialize offset + this->offset.serialize(buf, offsetOffset, header); + + U32 fileDataOffset = offsetOffset + this->offset.getSerializedLength(header); + + // Serialize file data + for (int i = 0; i < this->fileDataLength; ++i) + { + data[i + fileDataOffset] = this->fileData[i]; + } +} + +void FilePacket::FileData:: + deserialize(const Fw::Buffer& buf, U32 offset, const Header& header) +{ + U8* data = buf.getData() + offset; + + // Initialize offset of the offset field + U32 offsetOffset = offset; + + if (header.segmentMetadataFlag == SegmentMetadataFlag::PRESENT) + { + // Deserialize record continuation state and segment metadata length + this->continuationState = + static_cast((data[0] >> 6) & 3); + this->metadataLength = data[0] & 63; + + // Deserialize segment metadata + if (this->metadataLength == 0) + { + this->metadata = NULL; // Segment metadata length is 0 + } + else + { + this->metadata = &data[1]; + } + + // Update offset of the offset field + offsetOffset += FixedSize::BYTES + this->metadataLength; + } + + // Serialize offset + this->offset.deserialize(buf, offsetOffset, header); + + // Calculate the length of file data + this->fileDataLength = + header.dataFieldLength - this->getFileDataMetadataLength(header); + + U32 fileDataOffset = offsetOffset + this->offset.getSerializedLength(header); + + // Serialize file data + if (this->fileDataLength == 0) + { + this->fileData = NULL; // File data length is 0 + } + else + { + this->fileData = &data[fileDataOffset]; + } +} + +U32 FilePacket::FileData:: + getSerializedLength(const Header& header) const +{ + return this->getFileDataMetadataLength(header) + this->fileDataLength; +} + +U16 FilePacket::FileData:: + getFileDataMetadataLength(const Header& header) const +{ + U32 fixedLength = 0; // Record continuation state and segment metadata length + U32 metadataLength = 0; // Segment metadata + + if (header.segmentMetadataFlag == SegmentMetadataFlag::PRESENT) + { + fixedLength = FixedSize::BYTES; + metadataLength = this->metadataLength; + } + + return ( + fixedLength + + metadataLength + + this->offset.getSerializedLength(header) + ); +} + +} // Cfdp + +} // Fw diff --git a/Fw/Ccsds/Cfdp/FileData.hpp b/Fw/Ccsds/Cfdp/FileData.hpp new file mode 100644 index 00000000000..851d6bc0c10 --- /dev/null +++ b/Fw/Ccsds/Cfdp/FileData.hpp @@ -0,0 +1,185 @@ +//! ============================================================================ +//! @file FileData.hpp +//! @brief hpp file for a CFDP file packet File Data data field. +//! @author chownw +//! ============================================================================ + +#ifndef FW_CFDP_FILEDATA_HPP +#define FW_CFDP_FILEDATA_HPP + +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +//! @brief Record continuation state options. +//! +enum class FilePacket::ContinuationState +{ + NONE = 0, //!< File data contains neither the start nor the end of any record. + FIRST = 1, //!< File data starts with the first octet of a record. + LAST = 2, //!< File data ends with the last octet of a record. + BOTH = 3, //!< File data starts and ends with first and last octet of record. +}; + +//! @brief A CFDP file packet File Data data field. +//! +class FilePacket::FileData : public DataField +{ + friend FilePacket; + + public: + //! @brief Construct an empty CFDP file packet File Data data field type. + //! + //! This can be used to construct an initially empty data field for + //! deserialized data. + //! + FileData(); + + //! @brief Construct a filled CFDP file packet File Data data field type. + //! + //! This can be used to construct a data field with values for + //! serialization. + //! + //! @param offset The file data offset in octets. + //! @param fileDataLength The length of file data in octets. + //! @param fileData A pointer to variable length file data. + //! @param continuationState The record continuation state. + //! @param metadataLength The length of metadata in octets, from 0 to 63. + //! @param metadata A pointer to segment metadata. + //! + FileData( + U64 offset, + U16 fileDataLength, + const U8* fileData, + ContinuationState continuationState, + U8 metadataLength, + const U8* metadata + ); + + //! @brief Get the offset. + //! + U64 getOffset() const; + + //! @brief Get the length of file data. + //! + U16 getFileDataLength() const; + + //! @brief Get the pointer to file data. + //! + const U8* getFileData() const; + + //! @brief Get the record continuation state. + //! + ContinuationState getContinuationState() const; + + //! @brief Get the length of segment metadata. + //! + U8 getSegmentMetadataLength() const; + + //! @brief Get the pointer to segment metadata. + //! + const U8* getSegmentMetadata() const; + + PRIVATE: + //! @brief Serialize this data field into a buffer. + //! + //! @param buf The buffer to hold the serialized data. + //! @param offset The byte offset to start serialization from. + //! @param header The header attached to this file packet. + //! + void serialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ) const; + + //! @brief Deserialize a buffer containing a serialized data field. + //! + //! @param buf The buffer containing serialized data. + //! @param offset The byte offset to start deserialization from. + //! @param header The header attached to this file packet. + //! + void deserialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ); + + //! @brief Get the length in octets of this data field when serialized. + //! + //! @param header The header attached to this file packet. + //! + U32 getSerializedLength(const Header& header) const; + + PRIVATE: + //! @brief Get the length in octets of non file data fields. + //! + //! Length includes the record continuation state, segment metadata length, + //! segment metadata, and offset fields. + //! + U16 getFileDataMetadataLength(const Header& header) const; + + PRIVATE: + //! @brief Length in bits of fixed-size header fields. + //! + enum FieldLength : U32 + { + RECORD_CONTINUATION_STATE = 2, + SEGMENT_METADATA_LENGTH = 6, + }; + + //! @brief Total length of fixed-size fields. + //! + enum FixedSize : U32 + { + BITS = + FieldLength::RECORD_CONTINUATION_STATE + + FieldLength::SEGMENT_METADATA_LENGTH, + BYTES = BITS / 8, + }; + + //! @brief The file data offset in octets. + //! + FileSizeSensitive offset; + + //! @brief The length of file data in octets. + //! + U16 fileDataLength; + + //! @brief The pointer to file data. + //! + const U8* fileData; + + //! @brief The record continuation state. + //! + //! Present if and only if the value of the segment metadata flag in the + //! header is 1. + //! + ContinuationState continuationState; + + //! @brief The length of segment metadata. + //! + //! Present if and only if the value of the segment metadata flag in the + //! header is 1. + //! + U8 metadataLength; + + //! @brief The pointer to segment metadata. + //! + //! Present if and only if the value of the segment metadata flag in the + //! header is 1. + //! + const U8* metadata; +}; + +} // Cfdp + +} // Fw + +#endif // FW_CFDP_FILEDATA_HPP diff --git a/Fw/Ccsds/Cfdp/FilePacket.cpp b/Fw/Ccsds/Cfdp/FilePacket.cpp new file mode 100644 index 00000000000..d0ab2fd7c36 --- /dev/null +++ b/Fw/Ccsds/Cfdp/FilePacket.cpp @@ -0,0 +1,220 @@ +//! ============================================================================ +//! @file FilePacket.cpp +//! @brief cpp file for a CFDP file packet. +//! @author chownw +//! ============================================================================ + +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +void FilePacket:: + serializeValue(U8* data, U64 value, U8 size) +{ + U64 input = value; + + for (U8 i = 0; i < size; ++i) + { + data[size - i - 1] = input & 0xFF; + input >>= 8; + } +} + +U64 FilePacket:: + deserializeValue(const U8* data, U8 size) +{ + U64 output = 0; + + for (U8 i = 0; i < size; ++i) + { + output <<= 8; + output |= data[i]; + } + + return output; +} + +FilePacket::FileSizeSensitive:: + FileSizeSensitive() +{ + this->value = 0; +} + +FilePacket::FileSizeSensitive:: + FileSizeSensitive(U64 value) +{ + this->value = value; +} + +U64 FilePacket::FileSizeSensitive:: + getValue() const +{ + return this->value; +} + +void FilePacket::FileSizeSensitive:: + serialize(const Fw::Buffer& buf, U32 offset, const Header& header) const +{ + U8* data = buf.getData() + offset; + + if (header.getLargeFileFlag() == FilePacket::LargeFileFlag::SMALL_FILE) + { + FilePacket::serializeValue(data, this->value, 4); + } + else + { + FilePacket::serializeValue(data, this->value, 8); + } +} + +void FilePacket::FileSizeSensitive:: + deserialize(const Fw::Buffer& buf, U32 offset, const Header& header) +{ + U8* data = buf.getData() + offset; + + if (header.getLargeFileFlag() == FilePacket::LargeFileFlag::SMALL_FILE) + { + this->value = FilePacket::deserializeValue(data, 4); + } + else + { + this->value = FilePacket::deserializeValue(data, 8); + } +} + +U32 FilePacket::FileSizeSensitive:: + getSerializedLength(const Header& header) const +{ + return ( + (header.getLargeFileFlag() == FilePacket::LargeFileFlag::SMALL_FILE) + ? 4 // 32-bits for small files + : 8 // 64-bits for large files + ); +} + +FilePacket::LengthValue:: + LengthValue() +{ + this->length = 0; + this->value = NULL; +} + +FilePacket::LengthValue:: + LengthValue(U8 length, const U8* value) +{ + this->length = length; + this->value = value; +} + +U8 FilePacket::LengthValue:: + getLength() const +{ + return this->length; +} + +const U8* FilePacket::LengthValue:: + getValue() const +{ + return this->value; +} + +void FilePacket::LengthValue:: + serialize(const Fw::Buffer& buf, U32 offset) const +{ + U8* data = buf.getData() + offset; + + // Octet 0 + data[0] = this->length; + + // Copy value into the buffer starting at octet 1, skip if length is 0 + for (int i = 0; i < this->length; ++i) + { + data[i + 1] = this->value[i]; + } +} + +void FilePacket::LengthValue:: + deserialize(const Fw::Buffer& buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + // Octet 0 + this->length = data[0]; + + if (this->length == 0) + { + // Length is 0, there is no value field to deserialize + this->value = NULL; + } + else + { + // Get pointer to octet 1 which is the start of value field in the buffer + this->value = &data[1]; + } +} + +U32 FilePacket::LengthValue:: + getSerializedLength() const +{ + return this->length + 1; +} + +FilePacket:: + FilePacket + ( + Header& header, + DataField& dataField + ) : header(header), dataField(dataField) +{ + +} + +void FilePacket:: + serialize(const Fw::Buffer& buf, U32 offset) const +{ + this->header.serialize(buf, offset); + + this->dataField.serialize( + buf, + this->header.getSerializedLength(), + this->header + ); +} + +void FilePacket:: + deserialize(const Fw::Buffer& buf, U32 offset) +{ + this->header.deserialize(buf, offset); + + this->dataField.deserialize( + buf, + this->header.getSerializedLength(), + this->header + ); +} + +FilePacket::Type FilePacket:: + getTypeFromBuffer(const Fw::Buffer& buf, U32 offset) +{ + // Deserialize the header + Header header; + header.deserialize(buf, offset); + + // Get pointer to the start of the data field + U8* data = buf.getData() + offset + header.getSerializedLength(); + + return static_cast( + (static_cast(header.getType()) << 8) | data[0] + ); +} + +} // namespace Cfdp + +} // namespace Fw diff --git a/Fw/Ccsds/Cfdp/FilePacket.hpp b/Fw/Ccsds/Cfdp/FilePacket.hpp new file mode 100644 index 00000000000..7e5a5fa4ba0 --- /dev/null +++ b/Fw/Ccsds/Cfdp/FilePacket.hpp @@ -0,0 +1,385 @@ +//! ============================================================================ +//! @file FilePacket.hpp +//! @brief hpp file for a CFDP file packet. +//! @author chownw +//! ============================================================================ + +#ifndef FW_CFDP_FILEPACKET_HPP +#define FW_CFDP_FILEPACKET_HPP + +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +//! @brief A CFDP file packet. +//! +//! Each CFDP file packet consists of a header and a data field. Currently +//! supported data field formats include: End-of-file, Finished, Metadata, and +//! File Data. To construct a file packet, first construct a header, the desired +//! data field type, and then pass those into the file packet constructor. +//! +//! This class wraps file packet enumerations, the header format, and data field +//! formats. It provides the public interfaces for serializing and deserializing +//! file packets. +//! +//! NOTE: This implementation does not currently support Type Length Value (TLV) +//! fields. Fields with TLV format are ignored, so filestore responses, messages +//! to user, fault handler overrides, flow labels, and other features that +//! require TLV fields are not yet supported by this implementation. +//! +class FilePacket +{ + /* + * Enumerations. + */ + public: + //! @brief File packet data field type options. + //! + enum class DataType + { + FILE_DIRECTIVE = 0, //!< Indicates data field is a file directive. + FILE_DATA = 1, //!< Indicates data field is file data. + }; + + //! @brief File directive type options. + //! + enum class DirectiveType + { + END_OF_FILE = 0x04, //!< Indicates an End-of-file file directive. + FINISHED = 0x05, //!< Indicates a Finished file directive. + // ACK = 0x06, + METADATA = 0x07, //!< Indicates a Metadata file directive. + // NAK = 0x08, + // PROMPT = 0x09, + // KEEP_ALIVE = 0x0C, + }; + + //! @brief File packet type options. + //! + //! Combines the data field type and the directive type into a single + //! enumeration. + //! + enum class Type + { + //!< Indicates a File Data file packet. + FILE_DATA_PACKET = + static_cast(DataType::FILE_DATA) << 8, + //!< Indicates an End-of-file file packet. + EOF_PACKET = + (static_cast(DataType::FILE_DIRECTIVE) << 8) + | static_cast(DirectiveType::END_OF_FILE), + //!< Indicates a Finished file packet. + FINISHED_PACKET = + (static_cast(DataType::FILE_DIRECTIVE) << 8) + | static_cast(DirectiveType::FINISHED), + // ACK_PACKET = + // (static_cast(DataType::FILE_DIRECTIVE) << 8) + // | static_cast(DirectiveType::ACK), + //!< Indicates a Metadata file packet. + METADATA_PACKET = + (static_cast(DataType::FILE_DIRECTIVE) << 8) + | static_cast(DirectiveType::METADATA), + // NAK_PACKET = + // (static_cast(DataType::FILE_DIRECTIVE) << 8) + // | static_cast(DirectiveType::NAK), + // PROMPT_PACKET = + // (static_cast(DataType::FILE_DIRECTIVE) << 8) + // | static_cast(DirectiveType::PROMPT), + // KEEP_ALIVE_PACKET = + // (static_cast(DataType::FILE_DIRECTIVE) << 8) + // | static_cast(DirectiveType::KEEP_ALIVE), + }; + + //! @brief Condition code options. + //! + enum class ConditionCode + { + NO_ERROR = 0, //!< 'No error' condition. + ACK_LIMIT = 1, //!< 'Positive ACK limit reached' condition. + KEEP_ALIVE_LIMIT = 2, //!< 'Keep alive limit reached' condition. + INVALID_TRANSMISSION_MODE = 3, //!< 'Invalid transmission mode' condition. + FILESTORE_REJECTION = 4, //!< 'Filestore rejection' condition. + FILE_CHECKSUM_FAIL = 5, //!< 'File checksum failure' condition. + FILE_SIZE_ERROR = 6, //!< 'File size error' condition. + NAK_LIMIT = 7, //!< 'NAK limit reached' condition. + INACTIVITY_DETECTED = 8, //!< 'Inactivity detected' condition. + INVALID_FILE = 9, //!< 'Invalid file structure' condition. + CHECK_LIMIT = 10, //!< 'Check limit reached' condition. + UNSUPPORTED_CHECKSUM = 11, //!< 'Unsupported checksum type' condition. + SUSPEND_RECEIVED = 14, //!< 'Suspend.request received' condition. + CANCEL_RECEIVED = 15, //!< 'Cancel.request received' condition. + }; + + /* + * Enumeration forward declarations. + */ + public: + // Enums used in the Header + enum class Direction; + enum class TransmissionMode; + enum class CrcFlag; + enum class LargeFileFlag; + enum class SegmentationControl; + enum class SegmentMetadataFlag; + + // Enums used in the File Data data field + enum class ContinuationState; + + // Enums used in the Finished data field + enum class DeliveryCode; + enum class FileStatus; + + // Enums used in the Metadata data field + enum class ClosureRequested; + enum class ChecksumType; + + /* + * Nested class forward declarations. + */ + public: + class Header; + class FileData; + class EndOfFile; + class Finished; + class Metadata; + + /* + * Data field common type. + */ + PRIVATE: + //! @brief A file packet data field. + //! + class DataField + { + friend FilePacket; + + /* + * Funtions that all data field types should have. + */ + PRIVATE: + virtual void serialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ) const = 0; + + virtual void deserialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ) = 0; + + virtual U32 getSerializedLength(const Header& header) const = 0; + }; + + /* + * Public functions. + */ + public: + //! @brief Construct a CFDP file packet. + //! + FilePacket(Header& header, DataField& dataField); + + //! @brief Serialize this file packet into a buffer. + //! + //! Buffer data should have enough memory allocated to hold the serialized + //! header. + //! + //! @param buf The buffer to hold the serialized data. + //! @param offset The byte offset to start serialization from. + //! + void serialize(const Fw::Buffer& buf, U32 offset) const; + + //! @brief Deserialize a buffer containing serialized file packet data. + //! + //! @param buf The buffer containing serialized data. + //! @param offset The byte offset to start deserialization from. + //! + void deserialize(const Fw::Buffer& buf, U32 offset); + + //! @brief Gets the file packet type from a serialized packet in a buffer. + //! + //! @param buf The buffer holding a serialized packet. + //! @param offset The byte offset to the beginning of the serialized packet. + //! + static Type getTypeFromBuffer(const Fw::Buffer& buf, U32 offset); + + /* + * Public member variables. + */ + public: + //! @brief The file packet header. + //! + Header& header; + + //! @brief The file packet data field. + //! + DataField& dataField; + + /* + * Private static functions. + */ + PRIVATE: + //! @brief Serialize an integer value in big-endian format. + //! + //! @param data A pointer to the start of the data. + //! @param value The integer value to write. + //! @param size The number of bytes required to store the value. + //! + static void serializeValue(U8* data, U64 value, U8 size); + + //! @brief Read a serialized integer value in big-endian format. + //! + //! @param data A pointer to the start of the data. + //! @param size The number of bytes required to store the value. + //! + static U64 deserializeValue(const U8* data, U8 size); + + /* + * Variable-length formats used within some data field types. + */ + PRIVATE: + //! @brief The File Size Sensitive (FSS) object format. + //! + //! The serialized size of an FSS object is dependent on the large file flag + //! in the header. If the large file flag indicates a small file, the + //! value of an FSS object can be represented by a 32-bit unsigned integer + //! and its serialized size is 4 octets. If the large file flag indicates + //! a large file, the value of an FSS object can be represented by a 64-bit + //! unsigned integer and its serialized size is 8 octets. + //! + class FileSizeSensitive + { + friend FileData; + friend EndOfFile; + friend Metadata; + + PRIVATE: + //! @brief Default constructor for an FSS object. + //! + FileSizeSensitive(); + + //! @brief Construct an FSS object. + //! + //! @param value The value of the FSS object. + //! + FileSizeSensitive(U64 value); + + //! @brief Get the value. + //! + U64 getValue() const; + + PRIVATE: + //! @brief The value. At most a 64-bit unsigned integer. + //! + U64 value; + + PRIVATE: + //! @brief Serialize this FSS object. + //! + //! @param buf The buffer to hold the serialized data. + //! @param offset The byte offset to start serialization from. + //! + void serialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ) const; + + //! @brief Deserialize a buffer containing a serialized FSS object. + //! + //! @param buf The buffer containing serialized data. + //! @param offset The byte offset to start deserialization from. + //! + void deserialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ); + + //! @brief Get the length in octets when serialized. + //! + U32 getSerializedLength(const Header& header) const; + }; + + //! @brief The Length Value (LV) object format. + //! + //! An LV object is a variable length object with an 8-bit 'length' field + //! and a 'value' field containing 'length' number of octets. + //! + //! Pre-serialization, the 'value' field will hold a pointer to the value + //! supplied during construction. Post-deserialization, 'value' will hold a + //! pointer to the value in the serialized buffer. + //! + class LengthValue + { + friend Metadata; + + PRIVATE: + //! @brief Maximum length for the value field in an LV object. + //! + enum + { + MAX_LENGTH = 0xFF, //!< Maximum length in octets. + }; + + PRIVATE: + //! @brief Construct an empty LV object. + //! + LengthValue(); + + //! @brief Construct an LV object. + //! + //! @param length The length of value in octets. + //! @param value A pointer to the value. + //! + LengthValue(U8 length, const U8* value); + + //! @brief Get the length of value. + //! + U8 getLength() const; + + //! @brief Get a pointer to the value. + //! + const U8* getValue() const; + + PRIVATE: + //! @brief The length of value in octets. + //! + U8 length; + + //! @brief A pointer to the value. + //! + const U8* value; + + PRIVATE: + //! @brief Serialize this LV object. + //! + //! @param buf The buffer to hold the serialized data. + //! @param offset The byte offset to start serialization from. + //! + void serialize(const Fw::Buffer& buf, U32 offset) const; + + //! @brief Deserialize a buffer containing a serialized LV object. + //! + //! @param buf The buffer containing serialized data. + //! @param offset The byte offset to start deserialization from. + //! + void deserialize(const Fw::Buffer& buf, U32 offset); + + //! @brief Get the length in octets of LV obejct when serialized. + //! + U32 getSerializedLength() const; + }; +}; + +} // namespace Cfdp + +} // namespace Fw + +#endif // FW_CFDP_FILEPACKET_HPP diff --git a/Fw/Ccsds/Cfdp/Finished.cpp b/Fw/Ccsds/Cfdp/Finished.cpp new file mode 100644 index 00000000000..b9c60301154 --- /dev/null +++ b/Fw/Ccsds/Cfdp/Finished.cpp @@ -0,0 +1,98 @@ +//! ============================================================================ +//! @file Finished.cpp +//! @brief cpp file for a CFDP file packet Finished data field. +//! @author chownw +//! ============================================================================ + +#include + +#include +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +FilePacket::DirectiveType FilePacket::Finished::directiveCode = + FilePacket::DirectiveType::FINISHED; + +FilePacket::Finished:: + Finished() +{ +} + +FilePacket::Finished:: + Finished( + ConditionCode conditionCode, + DeliveryCode deliveryCode, + FileStatus fileStatus + ) +{ + this->conditionCode = conditionCode; + this->spare = 0; + this->deliveryCode = deliveryCode; + this->fileStatus = fileStatus; +} + +FilePacket::ConditionCode FilePacket::Finished:: + getConditionCode() const +{ + return this->conditionCode; +} + +FilePacket::DeliveryCode FilePacket::Finished:: + getDeliveryCode() const +{ + return this->deliveryCode; +} + +FilePacket::FileStatus FilePacket::Finished:: + getFileStatus() const +{ + return this->fileStatus; +} + +void FilePacket::Finished:: + serialize(const Fw::Buffer& buf, U32 offset, const Header& header) const +{ + U8* data = buf.getData() + offset; + + // Serialize octet 0 + data[0] = static_cast(this->directiveCode); + + // Serialize octet 1 + data[1] = 0; + data[1] |= (static_cast(this->conditionCode) & 15) << 4; + data[1] |= (static_cast(this->deliveryCode) & 1) << 2; + data[1] |= static_cast(this->fileStatus) & 3; +} + +void FilePacket::Finished:: + deserialize(const Fw::Buffer& buf, U32 offset, const Header& header) +{ + U8* data = buf.getData() + offset; + + // Deserialize octet 1 + this->conditionCode = + static_cast((data[1] >> 4) & 15); + this->spare = ((data[1] >> 3) & 1); + this->deliveryCode = + static_cast((data[1] >> 2) & 1); + this->fileStatus = + static_cast(data[1] & 3); +} + +U32 FilePacket::Finished:: + getSerializedLength(const Header& header) const +{ + return FixedSize::BYTES; +} + +} // Cfdp + +} // Fw diff --git a/Fw/Ccsds/Cfdp/Finished.hpp b/Fw/Ccsds/Cfdp/Finished.hpp new file mode 100644 index 00000000000..57917cc4c7f --- /dev/null +++ b/Fw/Ccsds/Cfdp/Finished.hpp @@ -0,0 +1,166 @@ +//! ============================================================================ +//! @file Finished.hpp +//! @brief hpp file for a CFDP file packet Finished data field. +//! @author chownw +//! ============================================================================ + +#ifndef FW_CFDP_FINISHED_HPP +#define FW_CFDP_FINISHED_HPP + +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +//! @brief Delivery code options. +//! +//! DATA_COMPELTE indicates that metadata, all file data, and EOF have been +//! received, and the checksum has been verified. +//! +enum class FilePacket::DeliveryCode +{ + DATA_COMPLETE = 0, //!< Indicates data complete. + DATA_INCOMPLETE = 1, //!< Indicates data incomplete. +}; + +//! @brief File status options. +//! +enum class FilePacket::FileStatus +{ + DISCARDED_DELIBERATE = 0, //!< Delivered file discarded deliberately. + DISCARDED_REJECTED = 1, //!< Delivered file discarded filestore rejection. + SUCCESS = 2, //!< Delivered file retained in filestore successfully. + UNREPORTED = 3, //!< Delivered file status unreported. +}; + +//! @brief A CFDP file packet Finished data field. +//! +class FilePacket::Finished : public DataField +{ + friend FilePacket; + + public: + //! @brief Construct an empty CFDP file packet Finished data field type. + //! + //! This can be used to construct an initially empty data field for + //! deserialized data. + //! + Finished(); + + //! @brief Construct a filled CFDP file packet Finished data field type. + //! + //! This can be used to construct a data field with values for + //! serialization. + //! + //! @param conditionCode The condition code. + //! @param deliveryCode The delivery code. + //! @param fileStatus The file status. + //! + Finished( + ConditionCode conditionCode, + DeliveryCode deliveryCode, + FileStatus fileStatus + ); + + //! @brief Get the condition code. + //! + ConditionCode getConditionCode() const; + + //! @brief Get the delivery code. + //! + DeliveryCode getDeliveryCode() const; + + //! @brief Get the file status. + //! + FileStatus getFileStatus() const; + + PRIVATE: + //! @brief Serialize this data field into a buffer. + //! + //! @param buf The buffer to hold the serialized data. + //! @param offset The byte offset to start serialization from. + //! @param header The header attached to this file packet. + //! + void serialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ) const; + + //! @brief Deserialize a buffer containing a serialized data field. + //! + //! @param buf The buffer containing serialized data. + //! @param offset The byte offset to start deserialization from. + //! @param header The header attached to this file packet. + //! + void deserialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ); + + //! @brief Get the length in octets of this data field when serialized. + //! + //! @param header The header attached to this file packet. + //! + U32 getSerializedLength(const Header& header) const; + + PRIVATE: + //! @brief Length in bits of fixed-size fields. + //! + enum FieldLength : U32 + { + DIRECTIVE_CODE = 8, + CONDITION_CODE = 4, + SPARE = 1, + DELIVERY_CODE = 1, + FILE_STATUS = 2, + }; + + //! @brief Total length of fixed-size fields. + //! + enum FixedSize : U32 + { + BITS = + FieldLength::DIRECTIVE_CODE + + FieldLength::CONDITION_CODE + + FieldLength::SPARE + + FieldLength::DELIVERY_CODE + + FieldLength::FILE_STATUS, + BYTES = BITS / 8, + }; + + //! @brief Identifies the data field type. + //! + static DirectiveType directiveCode; + + //! @brief Condition code. + //! + ConditionCode conditionCode; + + //! @brief Spare bits, set to all zeroes. + //! + U8 spare; + + //! @brief Delivery code. + //! + DeliveryCode deliveryCode; + + //! @brief Delivered file status. + //! + FileStatus fileStatus; + + // NOTE: Filestore responses field not supported. + // NOTE: Fault location field not supported. +}; + +} // Cfdp + +} // Fw + +#endif // FW_CFDP_FINISHED_HPP diff --git a/Fw/Ccsds/Cfdp/Header.cpp b/Fw/Ccsds/Cfdp/Header.cpp new file mode 100644 index 00000000000..33120a02232 --- /dev/null +++ b/Fw/Ccsds/Cfdp/Header.cpp @@ -0,0 +1,265 @@ +//! ============================================================================ +//! @file Header.cpp +//! @brief cpp file for a CFDP file packet header. +//! @author chownw +//! ============================================================================ + +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +FilePacket::Header:: + Header() +{ +} + +FilePacket::Header:: + Header( + FilePacket::DataType type, + FilePacket::Direction direction, + FilePacket::TransmissionMode transmissionMode, + FilePacket::CrcFlag crcFlag, + FilePacket::LargeFileFlag largeFileFlag, + FilePacket::SegmentationControl segmentationControl, + FilePacket::SegmentMetadataFlag segmentMetadataFlag, + U8 transSeqNumLength, + U64 transSeqNumber, + U8 entityIdLength, + U64 sourceEntityId, + U64 destEntityId, + U16 dataFieldLength + ) +{ + // TODO: Check invariants + + // Indicates second version of the protocol + this->version = 0x001; + + this->type = type; + this->direction = direction; + this->transmissionMode = transmissionMode; + this->crcFlag = crcFlag; + this->largeFileFlag = largeFileFlag; + this->transSeqNumLength = transSeqNumLength; + this->entityIdLength = entityIdLength; + this->transSeqNumber = transSeqNumber; + this->sourceEntityId = sourceEntityId; + this->destEntityId = destEntityId; + this->dataFieldLength = dataFieldLength; + + // These fields are always '0' and should be ignored for file directive PDUs + if (this->type == FilePacket::DataType::FILE_DIRECTIVE) + { + this->segmentationControl = FilePacket::SegmentationControl::NOT_PRESERVED; + this->segmentMetadataFlag = FilePacket::SegmentMetadataFlag::NOT_PRESENT; + } + else + { + this->segmentationControl = segmentationControl; + this->segmentMetadataFlag = segmentMetadataFlag; + } +} + +U8 FilePacket::Header:: + getVersion() const +{ + return this->version; +} + +FilePacket::DataType FilePacket::Header:: + getType() const +{ + return this->type; +} + +FilePacket::Direction FilePacket::Header:: + getDirection() const +{ + return this->direction; +} + +FilePacket::TransmissionMode FilePacket::Header:: + getTransmissionMode() const +{ + return this->transmissionMode; +} + +FilePacket::CrcFlag FilePacket::Header:: + getCrcFlag() const +{ + return this->crcFlag; +} + +FilePacket::LargeFileFlag FilePacket::Header:: + getLargeFileFlag() const +{ + return this->largeFileFlag; +} + +U16 FilePacket::Header:: + getDataFieldLength() const +{ + return this->dataFieldLength; +} + +FilePacket::SegmentationControl FilePacket::Header:: + getSegmentationControl() const +{ + return this->segmentationControl; +} + +U8 FilePacket::Header:: + getEntityIdLength() const +{ + return this->entityIdLength; +} + +FilePacket::SegmentMetadataFlag FilePacket::Header:: + getSegmentMetadataFlag() const +{ + return this->segmentMetadataFlag; +} + +U8 FilePacket::Header:: + getTransSeqNumLength() const +{ + return this->transSeqNumLength; +} + +U64 FilePacket::Header:: + getSourceEntityId() const +{ + return this->sourceEntityId; +} + +U64 FilePacket::Header:: + getTransSeqNumber() const +{ + return this->transSeqNumber; +} + +U64 FilePacket::Header:: + getDestEntityId() const +{ + return this->destEntityId; +} + +void FilePacket::Header:: + serialize(const Fw::Buffer& buf, U32 offset) const +{ + // TODO: Check buffer size is >= header size + offset + + U8* data = buf.getData() + offset; + + // Serialize octet 0 + data[0] = 0; + data[0] |= this->version << 5; + data[0] |= (static_cast(this->type) & 1) << 4; + data[0] |= (static_cast(this->direction) & 1) << 3; + data[0] |= (static_cast(this->transmissionMode) & 1) << 2; + data[0] |= (static_cast(this->crcFlag) & 1) << 1; + data[0] |= (static_cast(this->largeFileFlag) & 1); + + // Serialize octet 1 + data[1] = this->dataFieldLength >> 8; + + // Serialize octet 2 + data[2] = this->dataFieldLength; + + // Serialize octet 3 + data[3] = 0; + data[3] |= (static_cast(this->segmentationControl) & 1) << 7; + data[3] |= ((this->entityIdLength & 7) - 1) << 4; + data[3] |= (static_cast(this->segmentMetadataFlag) & 1) << 3; + data[3] |= ((this->transSeqNumLength & 7) - 1); + + // Push source entity ID onto buffer in big-endian format + FilePacket::serializeValue( + &data[4], + this->sourceEntityId, + this->entityIdLength + ); + + // Push transaction sequence number onto buffer in big-endian format + FilePacket::serializeValue( + &data[4 + this->entityIdLength], + this->transSeqNumber, + this->transSeqNumLength + ); + + // Push destination entity ID onto buffer in big-endian format + FilePacket::serializeValue( + &data[4 + this->entityIdLength + this->transSeqNumLength], + this->destEntityId, + this->entityIdLength + ); +} + +void FilePacket::Header:: + deserialize(const Fw::Buffer& buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + // Deserialize octet 0 + this->version = data[0] >> 5; + this->type = static_cast((data[0] >> 4) & 1); + this->direction = static_cast((data[0] >> 3) & 1); + this->transmissionMode = + static_cast((data[0] >> 2) & 1); + this->crcFlag = static_cast((data[0] >> 1) & 1); + this->largeFileFlag = static_cast(data[0] & 1); + + // Deserialize octet 1 + this->dataFieldLength = data[1] * 0x100; + + // Deserialize octet 2 + this->dataFieldLength += data[2]; + + // Deserialize octet 3 + this->segmentationControl = + static_cast((data[3] >> 7) & 1); + this->entityIdLength = ((data[3] >> 4) & 7) + 1; + this->segmentMetadataFlag = + static_cast((data[3] >> 3) & 1); + this->transSeqNumLength = (data[3] & 7) + 1; + + // Deserialize source entity ID bytes + this->sourceEntityId = FilePacket::deserializeValue( + &data[4], + this->entityIdLength + ); + + // Deserialize transaction sequence number bytes + this->transSeqNumber = FilePacket::deserializeValue( + &data[4 + this->entityIdLength], + this->transSeqNumLength + ); + + // Deserialize destination entity ID bytes + this->destEntityId = FilePacket::deserializeValue( + &data[4 + this->entityIdLength + this->transSeqNumLength], + this->entityIdLength + ); +} + +U32 FilePacket::Header:: + getSerializedLength() const +{ + return ( + FixedSize::BYTES + + this->entityIdLength + + this->transSeqNumLength + + this->entityIdLength + ); +} + +} // namespace Cfdp + +} // namespace Fw diff --git a/Fw/Ccsds/Cfdp/Header.hpp b/Fw/Ccsds/Cfdp/Header.hpp new file mode 100644 index 00000000000..521008d60b4 --- /dev/null +++ b/Fw/Ccsds/Cfdp/Header.hpp @@ -0,0 +1,305 @@ +//! ============================================================================ +//! @file Header.hpp +//! @brief hpp file for a CFDP file packet header. +//! @author chownw +//! ============================================================================ + +#ifndef FW_CFDP_HEADER_HPP +#define FW_CFDP_HEADER_HPP + +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +//! @brief PDU direction options. +//! +//! Used to perform PDU forwarding. +//! +enum class FilePacket::Direction +{ + TOWARD_RECEIVER = 0, //!< Indicates a PDU towards the file receiver. + TOWARD_SENDER = 1, //!< Indicates a PDU towards the file sender. +}; + +//! @brief Transmission mode options. +//! +//! If the transmission mode is ACKNOWLEDGED, protocol entity will use +//! reliable file transfer procedures. Acknowledged transmission mode +//! requires duplex transmission paths. +//! +enum class FilePacket::TransmissionMode +{ + ACKNOWLEDGED = 0, //!< Indicates acknowledged transmission mode. + UNACKNOWLEDGED = 1, //!< Indicates unacknowledged transmission mode. +}; + +//! @brief CRC flag options. +//! +//! If the flag is PRESENT, the sending protocol entity will calculate the +//! CRC for each outgoing PDU and append it to the data field. The receiving +//! protocol entity will calculate the CRC for each incoming PDU. +//! +enum class FilePacket::CrcFlag +{ + NOT_PRESENT = 0, //!< Indicates a CRC is not included in the data field. + PRESENT = 1, //!< Indicates a CRC is included in the data field. +}; + +//! @brief Large file flag options. +//! +//! If the file is a LARGE_FILE, then its size cannot be represented in an +//! unsigned 32-bit integer. All files of unbounded size shall be flagged as +//! LARGE_FILE, all other files shall be flagged as SMALL_FILE. +//! +enum class FilePacket::LargeFileFlag +{ + SMALL_FILE = 0, //!< Indicates a 'small' file. + LARGE_FILE = 1, //!< Indicates a 'large' file. +}; + +//! @brief Segmentation control options. +//! +//! If flag is PRESERVED, then record boundaries are preserved in file data +//! segmentation. +//! +enum class FilePacket::SegmentationControl +{ + NOT_PRESERVED = 0, //!< Indicates record boundaries are not preserved. + PRESERVED = 1, //!< Indicates record boundaries are preserved. +}; + +//! @brief Segment metadata flag options. +//! +enum class FilePacket::SegmentMetadataFlag +{ + NOT_PRESENT = 0, //!< Indicates segment metadata is not in the PDU. + PRESENT = 1, //!< Indicates segment metadata is in the PDU. +}; + +//! @brief A CFDP file packet header. +//! +class FilePacket::Header +{ + friend FilePacket; + + public: + //! @brief Construct an empty CFDP PDU header. + //! + //! This can be used to construct a header to hold deserialized data. + //! + Header(); + + //! @brief Construct a filled CFDP PDU header. + //! + //! This can be used to construct a header for serialization. + //! + //! @param type The type of the PDU. + //! @param direction The direction of the PDU. + //! @param transmissionMode The transmission mode for the PDU. + //! @param crcFlag Whether to calculate a CRC for the PDU. + //! @param largeFileFlag Whether the file is large or small. + //! @param segmentationControl Whether to preserve record boundaries. + //! @param segmentMetadataFlag Whether to include segment metadata. + //! @param transSeqNumLength The sequence number length in octets. + //! @param transSeqNumber The transaction sequence number. + //! @param entityIdLength Entity ID lengths in octets. + //! @param sourceEntityId The source entity ID. + //! @param destEntityId The destination entity ID. + //! @param dataFieldLength The data field length in octets. + //! + Header( + DataType type, + Direction direction, + TransmissionMode transmissionMode, + CrcFlag crcFlag, + LargeFileFlag largeFileFlag, + SegmentationControl segmentationControl, + SegmentMetadataFlag segmentMetadataFlag, + U8 transSeqNumLength, + U64 transSeqNumber, + U8 entityIdLength, + U64 sourceEntityId, + U64 destEntityId, + U16 dataFieldLength + ); + + //! @brief Get the protocol version. + //! + U8 getVersion() const; + + //! @brief Get the PDU type. + //! + DataType getType() const; + + //! @brief Get the direction used to perform PDU forwarding. + //! + Direction getDirection() const; + + //! @brief Get the transmission mode. + //! + TransmissionMode getTransmissionMode() const; + + //! @brief Get the CRC flag. + //! + CrcFlag getCrcFlag() const; + + //! @brief Get the large file flag. + //! + LargeFileFlag getLargeFileFlag() const; + + //! @brief Get the PDU data field length in octets. + //! + U16 getDataFieldLength() const; + + //! @brief Get whether record boundaries are preserved in data segmentation. + //! + SegmentationControl getSegmentationControl() const; + + //! @brief Get the number of octets in the entity ID less one. + //! + U8 getEntityIdLength() const; + + //! @brief Get whether segment metadata is present in the PDU. + //! + SegmentMetadataFlag getSegmentMetadataFlag() const; + + //! @brief Get the number of octets in the sequence number less one. + //! + U8 getTransSeqNumLength() const; + + //! @brief Get the source protocol entity ID. + //! + U64 getSourceEntityId() const; + + //! @brief Get the transaction sequence number. + //! + U64 getTransSeqNumber() const; + + //! @brief Get the destination protocol entity ID. + //! + U64 getDestEntityId() const; + + PRIVATE: + //! @brief Serialize this header into a buffer. + //! + //! @param buf The buffer to hold the serialized data. + //! @param offset The byte offset to start serialization from. + //! + void serialize(const Fw::Buffer& buf, U32 offset) const; + + //! @brief Deserialize a buffer containing serialized header data. + //! + //! @param buf The buffer containing serialized data. + //! @param offset The byte offset to start deserialization from. + //! + void deserialize(const Fw::Buffer& buf, U32 offset); + + //! @brief Get the length in octets of this header when serialized. + //! + U32 getSerializedLength() const; + + PRIVATE: + //! @brief Length in bits of fixed-size header fields. + //! + enum FieldLength : U32 + { + VERSION = 3, + TYPE = 1, + DIRECTION = 1, + TRANSMISSION_MODE = 1, + CRC_FLAG = 1, + LARGE_FILE_FLAG = 1, + DATA_FIELD_LENGTH = 16, + SEGMENTATION_CONTROL = 1, + ENTITY_ID_LENGTH = 3, + SEGMENT_METADATA_FLAG = 1, + TRANS_SEQ_NUM_LENGTH = 3, + }; + + //! @brief Total length of fixed-size header fields. + //! + enum FixedSize : U32 + { + BITS = + FieldLength::VERSION + + FieldLength::TYPE + + FieldLength::DIRECTION + + FieldLength::TRANSMISSION_MODE + + FieldLength::CRC_FLAG + + FieldLength::LARGE_FILE_FLAG + + FieldLength::DATA_FIELD_LENGTH + + FieldLength::SEGMENTATION_CONTROL + + FieldLength::ENTITY_ID_LENGTH + + FieldLength::SEGMENT_METADATA_FLAG + + FieldLength::TRANS_SEQ_NUM_LENGTH, + BYTES = BITS / 8, + }; + + //! @brief The protocol version. + //! + U8 version; + + //! @brief The PDU type. + //! + DataType type; + + //! @brief The direction. + //! + Direction direction; + + //! @brief The transmission mode. + //! + TransmissionMode transmissionMode; + + //! @brief The CRC flag. + //! + CrcFlag crcFlag; + + //! @brief The large file flag. + //! + LargeFileFlag largeFileFlag; + + //! @brief The PDU data field length in octets. + //! + U16 dataFieldLength; + + //! @brief Segmentation control. + //! + SegmentationControl segmentationControl; + + //! @brief Entity ID lengths in octets. + //! + U8 entityIdLength; + + //! @brief The segment metadata flag. + //! + SegmentMetadataFlag segmentMetadataFlag; + + //! @brief The transaction sequence number length in octets. + //! + U8 transSeqNumLength; + + //! @brief The source protocol entity ID. + //! + U64 sourceEntityId; + + //! @brief The transaction sequence number. + //! + U64 transSeqNumber; + + //! @brief The destination protocol entity ID. + //! + U64 destEntityId; +}; + +} // namespace Cfdp + +} // namespace Fw + +#endif // FW_CFDP_HEADER_HPP diff --git a/Fw/Ccsds/Cfdp/Metadata.cpp b/Fw/Ccsds/Cfdp/Metadata.cpp new file mode 100644 index 00000000000..1dcee9ddc29 --- /dev/null +++ b/Fw/Ccsds/Cfdp/Metadata.cpp @@ -0,0 +1,165 @@ +//! ============================================================================ +//! @file Metadata.cpp +//! @brief cpp file for a CFDP file packet Metadata data field. +//! @author chownw +//! ============================================================================ + +#include + +#include +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +FilePacket::DirectiveType FilePacket::Metadata::directiveCode = + FilePacket::DirectiveType::METADATA; + +FilePacket::Metadata:: + Metadata() +{ +} + +FilePacket::Metadata:: + Metadata( + FilePacket::ClosureRequested closureRequested, + FilePacket::ChecksumType checksumType, + U64 fileSize, + const char* sourceFilename, + const char* destFilename + ) +{ + this->reserved0 = 0; + this->closureRequested = closureRequested; + this->reserved1 = 0; + this->checksumType = checksumType; + this->fileSize = FileSizeSensitive(fileSize); + + this->sourceFilename = FilePacket::LengthValue( + strnlen(sourceFilename, FilePacket::LengthValue::MAX_LENGTH), + reinterpret_cast(sourceFilename) + ); + + this->destFilename = FilePacket::LengthValue( + strnlen(destFilename, FilePacket::LengthValue::MAX_LENGTH), + reinterpret_cast(destFilename) + ); +} + +FilePacket::ClosureRequested FilePacket::Metadata:: + getClosureRequested() const +{ + return this->closureRequested; +} + +FilePacket::ChecksumType FilePacket::Metadata:: + getChecksumType() const +{ + return this->checksumType; +} + +U64 FilePacket::Metadata:: + getFileSize() const +{ + return this->fileSize.getValue(); +} + +const char* FilePacket::Metadata:: + getSourceFilename() const +{ + return reinterpret_cast(this->sourceFilename.getValue()); +} + +U8 FilePacket::Metadata:: + getSourceFilenameLength() const +{ + return this->sourceFilename.getLength(); +} + +const char* FilePacket::Metadata:: + getDestFilename() const +{ + return reinterpret_cast(this->destFilename.getValue()); +} + +U8 FilePacket::Metadata:: + getDestFilenameLength() const +{ + return this->destFilename.getLength(); +} + +void FilePacket::Metadata:: + serialize(const Fw::Buffer& buf, U32 offset, const Header& header) const +{ + U8* data = buf.getData() + offset; + + // Serialize octet 0 + data[0] = static_cast(this->directiveCode); + + // Serialize octet 1 + data[1] = 0; + data[1] |= (static_cast(this->closureRequested) & 1) << 6; + data[1] |= (static_cast(this->checksumType) & 15); + + // Serialize the FSS field file size + U32 fileSizeOffset = offset + FixedSize::BYTES; + this->fileSize.serialize(buf, fileSizeOffset, header); + + // Serialize the LV field source file name + U32 sourceFilenameOffset = + fileSizeOffset + this->fileSize.getSerializedLength(header); + this->sourceFilename.serialize(buf, sourceFilenameOffset); + + // Serialize the LV field destination file name + U32 destFilenameOffset = + sourceFilenameOffset + this->sourceFilename.getSerializedLength(); + this->destFilename.serialize(buf, destFilenameOffset); +} + +void FilePacket::Metadata:: + deserialize(const Fw::Buffer& buf, U32 offset, const Header& header) +{ + U8* data = buf.getData() + offset; + + // Deserialize octet 1 + this->reserved0 = (data[1] >> 7) & 1; + this->closureRequested = + static_cast((data[1] >> 6) & 1); + this->reserved1 = (data[1] >> 4) & 3; + this->checksumType = static_cast(data[1] & 15); + + // Deserialize the FSS field file size + U32 fileSizeOffset = offset + FixedSize::BYTES; + this->fileSize.deserialize(buf, fileSizeOffset, header); + + // Deserialize the LV field source file name + U32 sourceFilenameOffset = + fileSizeOffset + this->fileSize.getSerializedLength(header); + this->sourceFilename.deserialize(buf, sourceFilenameOffset); + + // Deserialize the LV field destination file name + U32 destFilenameOffset = + sourceFilenameOffset + this->sourceFilename.getSerializedLength(); + this->destFilename.deserialize(buf, destFilenameOffset); +} + +U32 FilePacket::Metadata:: + getSerializedLength(const Header& header) const +{ + return ( + FixedSize::BYTES + + this->fileSize.getSerializedLength(header) + + this->sourceFilename.getSerializedLength() + + this->destFilename.getSerializedLength() + ); +} + +} // Cfdp + +} // Fw diff --git a/Fw/Ccsds/Cfdp/Metadata.hpp b/Fw/Ccsds/Cfdp/Metadata.hpp new file mode 100644 index 00000000000..7151d5449a2 --- /dev/null +++ b/Fw/Ccsds/Cfdp/Metadata.hpp @@ -0,0 +1,192 @@ +//! ============================================================================ +//! @file Metadata.hpp +//! @brief hpp file for a CFDP file packet Metadata data field. +//! @author chownw +//! ============================================================================ + +#ifndef FW_CFDP_METADATA_HPP +#define FW_CFDP_METADATA_HPP + +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +//! @brief Closure requested options. +//! +enum class FilePacket::ClosureRequested +{ + NOT_REQUESTED = 0, //!< Indicates transaction closure is not requested. + REQUESTED = 1, //!< Indicates transaction closure is requested. +}; + +//! @brief CFDP checksum identifiers defined in the SANA registry. +//! +enum class FilePacket::ChecksumType +{ + MODULAR_CHECKSUM = 0, //!< Identifies the CFDP Modular Checksum algorithm. + PROXIMITY1_CRC32 = 1, //!< Identifies the CCSDS Proximity-1 CRC algorithm. + CRC32C = 2, //!< Identifies the RFC 4960 CRC32c Checksum algorithm. + IEEE_802_3_FCS = 3, //!< Identifies the IEEE Std 802.3 2018 FCS algorithm. + NULL_CHECKSUM = 15, //!< Identifies the CFDP Null Checksum algorithm. +}; + +//! @brief A CFDP file packet Metadata data field. +//! +class FilePacket::Metadata : public DataField +{ + friend FilePacket; + + public: + //! @brief Construct an empty CFDP Metadata PDU. + //! + //! This can be used to construct a metadata PDU to hold deserialized data. + //! + Metadata(); + + //! @brief Construct a filled CFDP Metadata PDU. + //! + //! This can be used to construct a metadata PDU for serialization. + //! + //! @param closureRequested Whether transaction closure is requsted. + //! @param checksumType The file checksum algorithm to use. + //! @param fileSize The length of the file in octets, or 0 for unbounded. + //! @param sourceFilename The source file name, or NULL. + //! @param destFilename The destination file name, or NULL. + //! + Metadata( + ClosureRequested closureRequested, + ChecksumType checksumType, + U64 fileSize, + const char* sourceFilename, + const char* destFilename + ); + + //! @brief Get whether closure is requested. + //! + ClosureRequested getClosureRequested() const; + + //! @brief Get the checksum type. + //! + ChecksumType getChecksumType() const; + + //! @brief Get the file size. + //! + U64 getFileSize() const; + + //! @brief Get the source file name. + //! + const char* getSourceFilename() const; + + //! @brief Get the source file name length in bytes. + //! + U8 getSourceFilenameLength() const; + + //! @brief Get the destination file name. + //! + const char* getDestFilename() const; + + //! @brief Get the destination file name length in bytes. + //! + U8 getDestFilenameLength() const; + + PRIVATE: + //! @brief Serialize this Metadata PDU into a buffer. + //! + //! @param buf The buffer to hold the serialized data. + //! @param offset The byte offset to start serialization from. + //! @param header The header attached to this PDU. + //! + void serialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ) const; + + //! @brief Deserialize a buffer containing serialized Metadata PDU data. + //! + //! @param buf The buffer containing serialized data. + //! @param offset The byte offset to start deserialization from. + //! @param header The header attached to this PDU. + //! + void deserialize( + const Fw::Buffer& buf, + U32 offset, + const Header& header + ); + + //! @brief Get the length in octets of this Metadata PDU when serialized. + //! + //! @param header The header attached to this PDU. + //! + U32 getSerializedLength(const Header& header) const; + + PRIVATE: + //! @brief Length in bits of fixed-size Metadata PDU fields. + //! + enum FieldLength : U32 + { + DIRECTIVE_CODE = 8, + RESERVED0 = 1, + CLOSURE_REQUESTED = 1, + RESERVED1 = 2, + CHECKSUM_TYPE = 4, + }; + + //! @brief Total length of fixed-size Metadata PDU fields. + //! + enum FixedSize : U32 + { + BITS = + FieldLength::DIRECTIVE_CODE + + FieldLength::RESERVED0 + + FieldLength::CLOSURE_REQUESTED + + FieldLength::RESERVED1 + + FieldLength::CHECKSUM_TYPE, + BYTES = BITS / 8, + }; + + //! @brief Indicates that the data field contains a metadata PDU. + //! + static DirectiveType directiveCode; + + //! @brief Reserved for future use, set to all zeroes. + //! + U8 reserved0; + + //! @brief Closure requested. + ClosureRequested closureRequested; + + //! @brief Reserved for future use, set to all zeroes. + //! + U8 reserved1; + + //! @brief The checksum algorithm identifier. + //! + ChecksumType checksumType; + + //! @brief The file size in octets. + //! + FileSizeSensitive fileSize; + + //! @brief The source file name. + //! + LengthValue sourceFilename; + + //! @brief The destination file name. + //! + LengthValue destFilename; + + // NOTE: Options field not supported. +}; + +} // Cfdp + +} // Fw + +#endif // FW_CFDP_METADATA_HPP diff --git a/Fw/Ccsds/Cfdp/test/ut/TestEndOfFile.cpp b/Fw/Ccsds/Cfdp/test/ut/TestEndOfFile.cpp new file mode 100644 index 00000000000..143e75d1218 --- /dev/null +++ b/Fw/Ccsds/Cfdp/test/ut/TestEndOfFile.cpp @@ -0,0 +1,191 @@ +//! ============================================================================ +//! @file TestEndOfFile.cpp +//! @brief CFDP file packet End-of-file test file. +//! @author chownw +//! ============================================================================ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +FilePacket::EndOfFile TestEndOfFile1:: + create() +{ + Fw::Cfdp::FilePacket::EndOfFile endOfFile( + TestEndOfFile1::Values::conditionCode, + TestEndOfFile1::Values::fileChecksum, + TestEndOfFile1::Values::fileSize + ); + + return endOfFile; +} + +void TestEndOfFile1:: + fillBuffer(Buffer &buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + data[0] = TestEndOfFile1::Serialized::OCTET_00; + data[1] = TestEndOfFile1::Serialized::OCTET_01; + data[2] = TestEndOfFile1::Serialized::OCTET_02; + data[3] = TestEndOfFile1::Serialized::OCTET_03; + data[4] = TestEndOfFile1::Serialized::OCTET_04; + data[5] = TestEndOfFile1::Serialized::OCTET_05; + data[6] = TestEndOfFile1::Serialized::OCTET_06; + data[7] = TestEndOfFile1::Serialized::OCTET_07; + data[8] = TestEndOfFile1::Serialized::OCTET_08; + data[9] = TestEndOfFile1::Serialized::OCTET_09; +} + +void TestEndOfFile1:: + verifyBuffer(Buffer& buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + EXPECT_EQ( + data[0], + TestEndOfFile1::Serialized::OCTET_00 + ); + EXPECT_EQ( + data[1], + TestEndOfFile1::Serialized::OCTET_01 + ); + EXPECT_EQ( + data[2], + TestEndOfFile1::Serialized::OCTET_02 + ); + EXPECT_EQ( + data[3], + TestEndOfFile1::Serialized::OCTET_03 + ); + EXPECT_EQ( + data[4], + TestEndOfFile1::Serialized::OCTET_04 + ); + EXPECT_EQ( + data[5], + TestEndOfFile1::Serialized::OCTET_05 + ); + EXPECT_EQ( + data[6], + TestEndOfFile1::Serialized::OCTET_06 + ); + EXPECT_EQ( + data[7], + TestEndOfFile1::Serialized::OCTET_07 + ); + EXPECT_EQ( + data[8], + TestEndOfFile1::Serialized::OCTET_08 + ); + EXPECT_EQ( + data[9], + TestEndOfFile1::Serialized::OCTET_09 + ); +} + +void TestEndOfFile1:: + verifyObject(FilePacket::EndOfFile& endOfFile) +{ + EXPECT_EQ( + endOfFile.directiveCode, + FilePacket::DirectiveType::END_OF_FILE + ); + EXPECT_EQ( + endOfFile.getConditionCode(), + TestEndOfFile1::Values::conditionCode + ); + EXPECT_EQ( + endOfFile.spare, + TestEndOfFile1::Values::spare + ); + EXPECT_EQ( + endOfFile.getFileChecksum(), + TestEndOfFile1::Values::fileChecksum + ); + EXPECT_EQ( + endOfFile.getFileSize(), + TestEndOfFile1::Values::fileSize + ); +} + +TEST(FilePacketEndOfFile, Serialize) +{ + // Allocate buffer for serialization + U8 data[TestEndOfFile1::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestEndOfFile1::Serialized::LENGTH); + + FilePacket::Header header = TestHeader1::create(); + FilePacket::EndOfFile endOfFile = TestEndOfFile1::create(); + endOfFile.serialize(buffer, 0, header); + + // Verify buffer + TestEndOfFile1::verifyBuffer(buffer, 0); +} + +TEST(FilePacketEndOfFile, Deserialize) +{ + // Allocate buffer for serialization + U8 data[TestEndOfFile1::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestEndOfFile1::Serialized::LENGTH); + + // Fill buffer with serialization + TestEndOfFile1::fillBuffer(buffer, 0); + + // Call deserialize function + FilePacket::Header header = TestHeader1::create(); + FilePacket::EndOfFile endOfFile; + endOfFile.deserialize(buffer, 0, header); + + // Verify End-of-file + TestEndOfFile1::verifyObject(endOfFile); +} + +TEST(FilePacketEndOfFile, Offset) +{ + // Test (de)serialization with arbitrary offset in the buffer + U32 offset = 15; + + // Allocate buffer for serialization + U8 data[TestEndOfFile1::Serialized::LENGTH + offset]; + Fw::Buffer buffer(data, TestEndOfFile1::Serialized::LENGTH + offset); + + // Test serialize function + FilePacket::Header header = TestHeader1::create(); + FilePacket::EndOfFile srcEndOfFile = TestEndOfFile1::create(); + srcEndOfFile.serialize(buffer, offset, header); + TestEndOfFile1::verifyBuffer(buffer, offset); + + // Test deserialize function + FilePacket::EndOfFile destEndOfFile; + destEndOfFile.deserialize(buffer, offset, header); + TestEndOfFile1::verifyObject(destEndOfFile); +} + +TEST(FilePacketEndOfFile, SerializedLength) +{ + FilePacket::Header header = TestHeader1::create(); + FilePacket::EndOfFile endOfFile = TestEndOfFile1::create(); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + endOfFile.getSerializedLength(header), + TestEndOfFile1::Serialized::LENGTH + ); +} + +} // namespace Cfdp + +} // namespace Fw diff --git a/Fw/Ccsds/Cfdp/test/ut/TestFileData.cpp b/Fw/Ccsds/Cfdp/test/ut/TestFileData.cpp new file mode 100644 index 00000000000..4918135f283 --- /dev/null +++ b/Fw/Ccsds/Cfdp/test/ut/TestFileData.cpp @@ -0,0 +1,495 @@ +//! ============================================================================ +//! @file TestFileData.cpp +//! @brief CFDP file packet File Data test file. +//! @author chownw +//! ============================================================================ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +// Compare File Data data. +// +bool compareData(const U8* data1, const U8* data2, U16 length) +{ + for (int i = 0; i < length; ++i) + { + if (data1[i] != data2[i]) + { + return false; + } + } + return true; +} + +} // + +namespace Fw +{ + +namespace Cfdp +{ + +FilePacket::FileData TestFileData1:: + create() +{ + Fw::Cfdp::FilePacket::FileData fileData( + TestFileData1::Values::offset, + TestFileData1::Values::fileDataLength, + TestFileData1::Values::fileData, + TestFileData1::Values::continuationState, + TestFileData1::Values::metadataLength, + TestFileData1::Values::metadata + ); + + return fileData; +} + +void TestFileData1:: + fillBuffer(Buffer &buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + data[0] = TestFileData1::Serialized::OCTET_00; + data[1] = TestFileData1::Serialized::OCTET_01; + data[2] = TestFileData1::Serialized::OCTET_02; + data[3] = TestFileData1::Serialized::OCTET_03; + data[4] = TestFileData1::Serialized::OCTET_04; + data[5] = TestFileData1::Serialized::OCTET_05; + data[6] = TestFileData1::Serialized::OCTET_06; + data[7] = TestFileData1::Serialized::OCTET_07; + data[8] = TestFileData1::Serialized::OCTET_08; + data[9] = TestFileData1::Serialized::OCTET_09; + data[10] = TestFileData1::Serialized::OCTET_10; + data[11] = TestFileData1::Serialized::OCTET_11; + data[12] = TestFileData1::Serialized::OCTET_12; + data[13] = TestFileData1::Serialized::OCTET_13; + data[14] = TestFileData1::Serialized::OCTET_14; + data[15] = TestFileData1::Serialized::OCTET_15; +} + +void TestFileData1:: + verifyBuffer(Buffer& buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + EXPECT_EQ( + data[0], + TestFileData1::Serialized::OCTET_00 + ); + EXPECT_EQ( + data[1], + TestFileData1::Serialized::OCTET_01 + ); + EXPECT_EQ( + data[2], + TestFileData1::Serialized::OCTET_02 + ); + EXPECT_EQ( + data[3], + TestFileData1::Serialized::OCTET_03 + ); + EXPECT_EQ( + data[4], + TestFileData1::Serialized::OCTET_04 + ); + EXPECT_EQ( + data[5], + TestFileData1::Serialized::OCTET_05 + ); + EXPECT_EQ( + data[6], + TestFileData1::Serialized::OCTET_06 + ); + EXPECT_EQ( + data[7], + TestFileData1::Serialized::OCTET_07 + ); + EXPECT_EQ( + data[8], + TestFileData1::Serialized::OCTET_08 + ); + EXPECT_EQ( + data[9], + TestFileData1::Serialized::OCTET_09 + ); + EXPECT_EQ( + data[10], + TestFileData1::Serialized::OCTET_10 + ); + EXPECT_EQ( + data[11], + TestFileData1::Serialized::OCTET_11 + ); + EXPECT_EQ( + data[12], + TestFileData1::Serialized::OCTET_12 + ); + EXPECT_EQ( + data[13], + TestFileData1::Serialized::OCTET_13 + ); + EXPECT_EQ( + data[14], + TestFileData1::Serialized::OCTET_14 + ); + EXPECT_EQ( + data[15], + TestFileData1::Serialized::OCTET_15 + ); +} + +void TestFileData1:: + verifyObject(FilePacket::FileData& fileData) +{ + EXPECT_EQ( + fileData.getOffset(), + TestFileData1::Values::offset + ); + EXPECT_EQ( + fileData.getFileDataLength(), + TestFileData1::Values::fileDataLength + ); + EXPECT_TRUE( + compareData( + fileData.getFileData(), + TestFileData1::Values::fileData, + TestFileData1::Values::fileDataLength + ) + ); + EXPECT_TRUE( + fileData.getFileData() != NULL + ); + // NOTE: Don't check other values since they are meaningless for file data + // with no segment metadata +} + +FilePacket::FileData TestFileData2:: + create() +{ + Fw::Cfdp::FilePacket::FileData fileData( + TestFileData2::Values::offset, + TestFileData2::Values::fileDataLength, + TestFileData2::Values::fileData, + TestFileData2::Values::continuationState, + TestFileData2::Values::metadataLength, + TestFileData2::Values::metadata + ); + + return fileData; +} + +void TestFileData2:: + fillBuffer(Buffer &buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + data[0] = TestFileData2::Serialized::OCTET_00; + data[1] = TestFileData2::Serialized::OCTET_01; + data[2] = TestFileData2::Serialized::OCTET_02; + data[3] = TestFileData2::Serialized::OCTET_03; + data[4] = TestFileData2::Serialized::OCTET_04; + data[5] = TestFileData2::Serialized::OCTET_05; + data[6] = TestFileData2::Serialized::OCTET_06; + data[7] = TestFileData2::Serialized::OCTET_07; + data[8] = TestFileData2::Serialized::OCTET_08; + data[9] = TestFileData2::Serialized::OCTET_09; + data[10] = TestFileData2::Serialized::OCTET_10; + data[11] = TestFileData2::Serialized::OCTET_11; + data[12] = TestFileData2::Serialized::OCTET_12; + data[13] = TestFileData2::Serialized::OCTET_13; + data[14] = TestFileData2::Serialized::OCTET_14; + data[15] = TestFileData2::Serialized::OCTET_15; + data[16] = TestFileData2::Serialized::OCTET_16; + data[17] = TestFileData2::Serialized::OCTET_17; + data[18] = TestFileData2::Serialized::OCTET_18; + data[19] = TestFileData2::Serialized::OCTET_19; + data[20] = TestFileData2::Serialized::OCTET_20; + data[21] = TestFileData2::Serialized::OCTET_21; + data[22] = TestFileData2::Serialized::OCTET_22; + data[23] = TestFileData2::Serialized::OCTET_23; + data[24] = TestFileData2::Serialized::OCTET_24; +} + +void TestFileData2:: + verifyBuffer(Buffer& buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + EXPECT_EQ( + data[0], + TestFileData2::Serialized::OCTET_00 + ); + EXPECT_EQ( + data[1], + TestFileData2::Serialized::OCTET_01 + ); + EXPECT_EQ( + data[2], + TestFileData2::Serialized::OCTET_02 + ); + EXPECT_EQ( + data[3], + TestFileData2::Serialized::OCTET_03 + ); + EXPECT_EQ( + data[4], + TestFileData2::Serialized::OCTET_04 + ); + EXPECT_EQ( + data[5], + TestFileData2::Serialized::OCTET_05 + ); + EXPECT_EQ( + data[6], + TestFileData2::Serialized::OCTET_06 + ); + EXPECT_EQ( + data[7], + TestFileData2::Serialized::OCTET_07 + ); + EXPECT_EQ( + data[8], + TestFileData2::Serialized::OCTET_08 + ); + EXPECT_EQ( + data[9], + TestFileData2::Serialized::OCTET_09 + ); + EXPECT_EQ( + data[10], + TestFileData2::Serialized::OCTET_10 + ); + EXPECT_EQ( + data[11], + TestFileData2::Serialized::OCTET_11 + ); + EXPECT_EQ( + data[12], + TestFileData2::Serialized::OCTET_12 + ); + EXPECT_EQ( + data[13], + TestFileData2::Serialized::OCTET_13 + ); + EXPECT_EQ( + data[14], + TestFileData2::Serialized::OCTET_14 + ); + EXPECT_EQ( + data[15], + TestFileData2::Serialized::OCTET_15 + ); + EXPECT_EQ( + data[16], + TestFileData2::Serialized::OCTET_16 + ); + EXPECT_EQ( + data[17], + TestFileData2::Serialized::OCTET_17 + ); + EXPECT_EQ( + data[18], + TestFileData2::Serialized::OCTET_18 + ); + EXPECT_EQ( + data[19], + TestFileData2::Serialized::OCTET_19 + ); + EXPECT_EQ( + data[20], + TestFileData2::Serialized::OCTET_20 + ); + EXPECT_EQ( + data[21], + TestFileData2::Serialized::OCTET_21 + ); + EXPECT_EQ( + data[22], + TestFileData2::Serialized::OCTET_22 + ); + EXPECT_EQ( + data[23], + TestFileData2::Serialized::OCTET_23 + ); + EXPECT_EQ( + data[24], + TestFileData2::Serialized::OCTET_24 + ); +} + +void TestFileData2:: + verifyObject(FilePacket::FileData& fileData) +{ + EXPECT_EQ( + fileData.getOffset(), + TestFileData2::Values::offset + ); + EXPECT_EQ( + fileData.getFileDataLength(), + TestFileData2::Values::fileDataLength + ); + EXPECT_TRUE( + compareData( + fileData.getFileData(), + TestFileData2::Values::fileData, + TestFileData2::Values::fileDataLength + ) + ); + EXPECT_TRUE( + fileData.getFileData() != NULL + ); + EXPECT_EQ( + fileData.getContinuationState(), + TestFileData2::Values::continuationState + ); + EXPECT_EQ( + fileData.getSegmentMetadataLength(), + TestFileData2::Values::metadataLength + ); + EXPECT_TRUE( + compareData( + fileData.getSegmentMetadata(), + TestFileData2::Values::metadata, + TestFileData2::Values::metadataLength + ) + ); + EXPECT_TRUE( + fileData.getSegmentMetadata() != NULL + ); +} + +TEST(FilePacketFileData, Serialize1) +{ + // Allocate buffer for serialization + U8 data[TestFileData1::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestFileData1::Serialized::LENGTH); + + FilePacket::Header header = TestHeader1::create(); + FilePacket::FileData fileData = TestFileData1::create(); + fileData.serialize(buffer, 0, header); + + // Verify buffer + TestFileData1::verifyBuffer(buffer, 0); +} + +TEST(FilePacketFileData, Serialize2) +{ + // Allocate buffer for serialization + U8 data[TestFileData2::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestFileData2::Serialized::LENGTH); + + FilePacket::Header header = TestHeader2::create(); + FilePacket::FileData fileData = TestFileData2::create(); + fileData.serialize(buffer, 0, header); + + // Verify buffer + TestFileData2::verifyBuffer(buffer, 0); +} + +TEST(FilePacketFileData, Deserialize1) +{ + // Allocate buffer for serialization + U8 data[TestFileData1::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestFileData1::Serialized::LENGTH); + + // Fill buffer with serialization + TestFileData1::fillBuffer(buffer, 0); + + // Call deserialize function + FilePacket::Header header = TestHeader1::create(); + FilePacket::FileData fileData; + fileData.deserialize(buffer, 0, header); + + // Verify fileData + TestFileData1::verifyObject(fileData); +} + +TEST(FilePacketFileData, Deserialize2) +{ + // Allocate buffer for serialization + U8 data[TestFileData2::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestFileData2::Serialized::LENGTH); + + // Fill buffer with serialization + TestFileData2::fillBuffer(buffer, 0); + + // Call deserialize function + FilePacket::Header header = TestHeader2::create(); + FilePacket::FileData fileData; + fileData.deserialize(buffer, 0, header); + + // Verify fileData + TestFileData2::verifyObject(fileData); +} + +TEST(FilePacketFileData, FileDataMetadataLength0) +{ + FilePacket::Header header = TestHeader1::create(); + FilePacket::FileData fileData = TestFileData1::create(); + + // Sanity check header and file data make sense + EXPECT_EQ( + TestHeader1::Values::dataFieldLength, + TestFileData1::Serialized::LENGTH + ); + // Verify getFileDataMetadataLength returns the expected length + EXPECT_EQ( + fileData.getFileDataMetadataLength(header), + 4 // Length not including file data + ); +} + +TEST(FilePacketFileData, FileDataMetadataLength1) +{ + FilePacket::Header header = TestHeader2::create(); + FilePacket::FileData fileData = TestFileData2::create(); + + // Sanity check header and file data make sense + EXPECT_EQ( + TestHeader2::Values::dataFieldLength, + TestFileData2::Serialized::LENGTH + ); + // Verify getFileDataMetadataLength returns the expected length + EXPECT_EQ( + fileData.getFileDataMetadataLength(header), + 13 // Length not including file data + ); +} + +TEST(FilePacketFileData, SerializedLength1) +{ + FilePacket::Header header = TestHeader1::create(); + FilePacket::FileData fileData = TestFileData1::create(); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + fileData.getSerializedLength(header), + TestFileData1::Serialized::LENGTH + ); +} + +TEST(FilePacketFileData, SerializedLength2) +{ + FilePacket::Header header = TestHeader2::create(); + FilePacket::FileData fileData = TestFileData2::create(); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + fileData.getSerializedLength(header), + TestFileData2::Serialized::LENGTH + ); +} + +} // Cfdp + +} // Fw diff --git a/Fw/Ccsds/Cfdp/test/ut/TestFilePacket.cpp b/Fw/Ccsds/Cfdp/test/ut/TestFilePacket.cpp new file mode 100644 index 00000000000..8b1822e324a --- /dev/null +++ b/Fw/Ccsds/Cfdp/test/ut/TestFilePacket.cpp @@ -0,0 +1,328 @@ +//! ============================================================================ +//! @file TestFilePacket.cpp +//! @brief CFDP file packet FilePacket test file. +//! @author chownw +//! ============================================================================ + +#include + +#include +#include +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +TEST(FilePacket, FileSizeSensitiveSmallFile) +{ + // Allocate buffer for serialization + U8 data[4]; + Fw::Buffer buffer(data, 4); + + FilePacket::Header header = TestHeader1::create(); + FilePacket::FileSizeSensitive fss(UINT32_MAX); + + // Verify values before serialization + EXPECT_EQ( + fss.getValue(), + UINT32_MAX + ); + + // Call serialize and deserialize functions + fss.serialize(buffer, 0, header); + fss.deserialize(buffer, 0, header); + + // Verify fss contains expected values after deserialization + EXPECT_EQ( + fss.getValue(), + UINT32_MAX + ); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + fss.getSerializedLength(header), + 4 + ); +} + +TEST(FilePacket, FileSizeSensitiveLargeFile) +{ + // Allocate buffer for serialization + U8 data[8]; + Fw::Buffer buffer(data, 8); + + FilePacket::Header header = TestHeader2::create(); + FilePacket::FileSizeSensitive fss(UINT64_MAX); + + // Verify values before serialization + EXPECT_EQ( + fss.getValue(), + UINT64_MAX + ); + + // Call serialize and deserialize functions + fss.serialize(buffer, 0, header); + fss.deserialize(buffer, 0, header); + + // Verify fss contains expected values after deserialization + EXPECT_EQ( + fss.getValue(), + UINT64_MAX + ); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + fss.getSerializedLength(header), + 8 + ); +} + +TEST(FilePacket, LengthValue) +{ + // Allocate buffer for serialization + U8 data[11]; + Fw::Buffer buffer(data, 11); + + const char* value = "test/value"; + FilePacket::LengthValue lv(strlen(value), reinterpret_cast(value)); + + // Verify values before serialization + EXPECT_EQ( + lv.getLength(), + strlen(value) + ); + EXPECT_EQ( + lv.getValue(), + reinterpret_cast(value) + ); + + // Call serialize and deserialize functions + lv.serialize(buffer, 0); + lv.deserialize(buffer, 0); + + // Verify lv contains expected values after deserialization + EXPECT_EQ( + lv.getLength(), + strlen(value) + ); + EXPECT_EQ( + strncmp( + reinterpret_cast(lv.getValue()), + value, + lv.getLength() + ), + 0 + ); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + lv.getSerializedLength(), + strlen(value) + 1 + ); +} + +TEST(FilePacket, LengthValueEmpty) +{ + // Allocate buffer for serialization + U8 data[11]; + Fw::Buffer buffer(data, 11); + + // Call default constructor which should initialize an empty LV object + FilePacket::LengthValue lv; + + // Verify values before serialization + EXPECT_EQ( + lv.getLength(), + 0 + ); + + // Call serialize and deserialize functions + lv.serialize(buffer, 0); + lv.deserialize(buffer, 0); + + // Verify lv contains expected values after deserialization + EXPECT_EQ( + lv.getLength(), + 0 + ); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + lv.getSerializedLength(), + 1 // Serialization should just contain the 'length' byte + ); +} + +TEST(FilePacket, GetTypeFromBuffer) +{ + U32 filePacketLength = + TestHeader1::Serialized::LENGTH + TestMetadata1::Serialized::LENGTH; + + // Allocate buffer for serialization + U8 data[filePacketLength]; + Fw::Buffer buffer(data, filePacketLength); + + // Fill buffer with a serialized test packet + TestHeader1::fillBuffer(buffer, 0); + TestMetadata1::fillBuffer(buffer, TestHeader1::Serialized::LENGTH); + + // Verify function returns the expected type + EXPECT_EQ( + FilePacket::getTypeFromBuffer(buffer, 0), + FilePacket::Type::METADATA_PACKET + ); +} + +TEST(FilePacket, SerializeEndOfFile) +{ + U32 filePacketLength = + TestHeader1::Serialized::LENGTH + TestEndOfFile1::Serialized::LENGTH; + + // Allocate buffer for serialization + U8 data[filePacketLength]; + Fw::Buffer buffer(data, filePacketLength); + + // Create source file packet + FilePacket::Header header = TestHeader1::create(); + FilePacket::EndOfFile endOfFile = TestEndOfFile1::create(); + FilePacket filePacket(header, endOfFile); + + // Serialize file packet + filePacket.serialize(buffer, 0); + + // Verify buffer + TestHeader1::verifyBuffer(buffer, 0); + TestEndOfFile1::verifyBuffer(buffer, TestHeader1::Serialized::LENGTH); +} + +TEST(FilePacket, SerializeFinished) +{ + U32 filePacketLength = + TestHeader1::Serialized::LENGTH + TestFinished1::Serialized::LENGTH; + + // Allocate buffer for serialization + U8 data[filePacketLength]; + Fw::Buffer buffer(data, filePacketLength); + + // Create source file packet + FilePacket::Header header = TestHeader1::create(); + FilePacket::Finished finished = TestFinished1::create(); + FilePacket filePacket(header, finished); + + // Serialize file packet + filePacket.serialize(buffer, 0); + + // Verify buffer + TestHeader1::verifyBuffer(buffer, 0); + TestFinished1::verifyBuffer(buffer, TestHeader1::Serialized::LENGTH); +} + +TEST(FilePacket, SerializeMetadata) +{ + U32 filePacketLength = + TestHeader1::Serialized::LENGTH + TestMetadata1::Serialized::LENGTH; + + // Allocate buffer for serialization + U8 data[filePacketLength]; + Fw::Buffer buffer(data, filePacketLength); + + // Create source file packet + FilePacket::Header header = TestHeader1::create(); + FilePacket::Metadata metadata = TestMetadata1::create(); + FilePacket filePacket(header, metadata); + + // Serialize file packet + filePacket.serialize(buffer, 0); + + // Verify buffer + TestHeader1::verifyBuffer(buffer, 0); + TestMetadata1::verifyBuffer(buffer, TestHeader1::Serialized::LENGTH); +} + +TEST(FilePacket, DeserializeEndOfFile) +{ + U32 filePacketLength = + TestHeader1::Serialized::LENGTH + TestEndOfFile1::Serialized::LENGTH; + + // Allocate buffer for serialization + U8 data[filePacketLength]; + Fw::Buffer buffer(data, filePacketLength); + + // Fill buffer with a serialized test packet + TestHeader1::fillBuffer(buffer, 0); + TestEndOfFile1::fillBuffer(buffer, TestHeader1::Serialized::LENGTH); + + // Create destination file packet to fill + FilePacket::Header header; + FilePacket::EndOfFile endOfFile; + FilePacket filePacket(header, endOfFile); + + // Deserialize file packet + filePacket.deserialize(buffer, 0); + + // Verify file packets + TestHeader1::verifyObject(header); + TestEndOfFile1::verifyObject(endOfFile); +} + +TEST(FilePacket, DeserializeFinished) +{ + U32 filePacketLength = + TestHeader1::Serialized::LENGTH + TestFinished1::Serialized::LENGTH; + + // Allocate buffer for serialization + U8 data[filePacketLength]; + Fw::Buffer buffer(data, filePacketLength); + + // Fill buffer with a serialized test packet + TestHeader1::fillBuffer(buffer, 0); + TestFinished1::fillBuffer(buffer, TestHeader1::Serialized::LENGTH); + + // Create destination file packet to fill + FilePacket::Header header; + FilePacket::Finished finished; + FilePacket filePacket(header, finished); + + // Deserialize file packet + filePacket.deserialize(buffer, 0); + + // Verify file packets + TestHeader1::verifyObject(header); + TestFinished1::verifyObject(finished); +} + +TEST(FilePacket, DeserializeMetadata) +{ + U32 filePacketLength = + TestHeader1::Serialized::LENGTH + TestMetadata1::Serialized::LENGTH; + + // Allocate buffer for serialization + U8 data[filePacketLength]; + Fw::Buffer buffer(data, filePacketLength); + + // Fill buffer with a serialized test packet + TestHeader1::fillBuffer(buffer, 0); + TestMetadata1::fillBuffer(buffer, TestHeader1::Serialized::LENGTH); + + // Create destination file packet to fill + FilePacket::Header header; + FilePacket::Metadata metadata; + FilePacket filePacket(header, metadata); + + // Deserialize file packet + filePacket.deserialize(buffer, 0); + + // Verify file packets + TestHeader1::verifyObject(header); + TestMetadata1::verifyObject(metadata); +} + +} // namespace Cfdp + +} // namespace Fw diff --git a/Fw/Ccsds/Cfdp/test/ut/TestFilePacket.hpp b/Fw/Ccsds/Cfdp/test/ut/TestFilePacket.hpp new file mode 100644 index 00000000000..2b143b01819 --- /dev/null +++ b/Fw/Ccsds/Cfdp/test/ut/TestFilePacket.hpp @@ -0,0 +1,471 @@ +//! ============================================================================ +//! @file TestFilePacket.hpp +//! @brief hpp file for CFDP file packet tests. +//! @author chownw +//! ============================================================================ + +#ifndef FW_CFDP_TESTFILEPACKET_HPP +#define FW_CFDP_TESTFILEPACKET_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +/* + * Test Header helpers. + */ +namespace TestHeader1 +{ + namespace Values + { + const U8 version = + 0x001; + + const FilePacket::DataType type = + FilePacket::DataType::FILE_DIRECTIVE; + + const FilePacket::Direction direction = + FilePacket::Direction::TOWARD_RECEIVER; + + const FilePacket::TransmissionMode transmissionMode = + FilePacket::TransmissionMode::UNACKNOWLEDGED; + + const FilePacket::CrcFlag crcFlag = + FilePacket::CrcFlag::NOT_PRESENT; + + const FilePacket::LargeFileFlag largeFileFlag = + FilePacket::LargeFileFlag::SMALL_FILE; + + const FilePacket::SegmentationControl segmentationControl = + FilePacket::SegmentationControl::NOT_PRESERVED; + + const FilePacket::SegmentMetadataFlag segmentMetadataFlag = + FilePacket::SegmentMetadataFlag::NOT_PRESENT; + + const U8 transSeqNumLength = 3; + + const U64 transSeqNumber = 5; + + const U8 entityIdLength = 3; + + const U64 sourceEntityId = 6; + + const U64 destEntityId = 7; + + const U16 dataFieldLength = 16; + } + + enum Serialized : U8 + { + OCTET_00 = 0x24, // |0 0 1|0|0|1|0|0| - Version through large file flag + OCTET_01 = 0x00, // |0 0 0 0 0 0 0 0| - Data field length bits 0-7 + OCTET_02 = 0x10, // |0 0 0 1 0 0 0 0| - Data field length bits 8-15 + OCTET_03 = 0x22, // |0|0 1 0|0|0 1 0| - Seg control through seq num length + OCTET_04 = 0x00, // |0 0 0 0 0 0 0 0| - Source entity ID octet 0 + OCTET_05 = 0x00, // |0 0 0 0 0 0 0 0| - Source entity ID octet 1 + OCTET_06 = 0x06, // |0 0 0 0 0 1 1 0| - Source entity ID octet 2 + OCTET_07 = 0x00, // |0 0 0 0 0 0 0 0| - Seq num octet 0 + OCTET_08 = 0x00, // |0 0 0 0 0 0 0 0| - Seq num octet 1 + OCTET_09 = 0x05, // |0 0 0 0 0 1 0 1| - Seq num octet 2 + OCTET_10 = 0x00, // |0 0 0 0 0 0 0 0| - Destination entity ID octet 0 + OCTET_11 = 0x00, // |0 0 0 0 0 0 0 0| - Destination entity ID octet 1 + OCTET_12 = 0x07, // |0 0 0 0 0 1 1 1| - Destination entity ID octet 2 + LENGTH = 13, + }; + + // Create a test object with the above values. + FilePacket::Header create(); + + // Fill buffer with the expected serialization. + void fillBuffer(Buffer& buf, U32 offset); + + // Verify data in buffer matches the expected serialization. + void verifyBuffer(Buffer& buf, U32 offset); + + // Verify object contains the expected values. + void verifyObject(FilePacket::Header& header); +}; + +/* + * Test Header helpers. + */ +namespace TestHeader2 +{ + namespace Values + { + const U8 version = + 0x001; + + const FilePacket::DataType type = + FilePacket::DataType::FILE_DATA; + + const FilePacket::Direction direction = + FilePacket::Direction::TOWARD_RECEIVER; + + const FilePacket::TransmissionMode transmissionMode = + FilePacket::TransmissionMode::UNACKNOWLEDGED; + + const FilePacket::CrcFlag crcFlag = + FilePacket::CrcFlag::NOT_PRESENT; + + const FilePacket::LargeFileFlag largeFileFlag = + FilePacket::LargeFileFlag::LARGE_FILE; + + const FilePacket::SegmentationControl segmentationControl = + FilePacket::SegmentationControl::NOT_PRESERVED; + + const FilePacket::SegmentMetadataFlag segmentMetadataFlag = + FilePacket::SegmentMetadataFlag::PRESENT; + + const U8 transSeqNumLength = 3; + + const U64 transSeqNumber = 5; + + const U8 entityIdLength = 3; + + const U64 sourceEntityId = 6; + + const U64 destEntityId = 7; + + const U16 dataFieldLength = 25; + } + + enum Serialized : U8 + { + OCTET_00 = 0x35, // |0 0 1|1|0|1|0|1| - Version through large file flag + OCTET_01 = 0x00, // |0 0 0 0 0 0 0 0| - Data field length bits 0-7 + OCTET_02 = 0x19, // |0 0 0 1 1 0 0 1| - Data field length bits 8-15 + OCTET_03 = 0x2A, // |0|0 1 0|1|0 1 0| - Seg control through seq num length + OCTET_04 = 0x00, // |0 0 0 0 0 0 0 0| - Source entity ID octet 0 + OCTET_05 = 0x00, // |0 0 0 0 0 0 0 0| - Source entity ID octet 1 + OCTET_06 = 0x06, // |0 0 0 0 0 1 1 0| - Source entity ID octet 2 + OCTET_07 = 0x00, // |0 0 0 0 0 0 0 0| - Seq num octet 0 + OCTET_08 = 0x00, // |0 0 0 0 0 0 0 0| - Seq num octet 1 + OCTET_09 = 0x05, // |0 0 0 0 0 1 0 1| - Seq num octet 2 + OCTET_10 = 0x00, // |0 0 0 0 0 0 0 0| - Destination entity ID octet 0 + OCTET_11 = 0x00, // |0 0 0 0 0 0 0 0| - Destination entity ID octet 1 + OCTET_12 = 0x07, // |0 0 0 0 0 1 1 1| - Destination entity ID octet 2 + LENGTH = 13, + }; + + // Create a test object with the above values. + FilePacket::Header create(); + + // Fill buffer with the expected serialization. + void fillBuffer(Buffer& buf, U32 offset); + + // Verify data in buffer matches the expected serialization. + void verifyBuffer(Buffer& buf, U32 offset); + + // Verify object contains the expected values. + void verifyObject(FilePacket::Header& header); +}; + +/* + * Test File Data helpers. + */ +namespace TestFileData1 +{ + namespace Values + { + const U64 offset = + 154; + + const U8 fileData[] = {'T', 'e', 's', 't', 'F', 'i', + 'l', 'e', 'D', 'a', 't', 'a'}; + + const U16 fileDataLength = 12; + + /* + * These values should be ignored when (de)serializing. + */ + + const FilePacket::ContinuationState continuationState = + FilePacket::ContinuationState::NONE; + + const U8* const metadata = NULL; + + const U8 metadataLength = 0; + } + + enum Serialized : U8 + { + OCTET_00 = 0x00, // |0 0 0 0 0 0 0 0| - Offset value octet 0. + OCTET_01 = 0x00, // |0 0 0 0 0 0 0 0| - Offset value octet 1. + OCTET_02 = 0x00, // |0 0 0 0 0 0 0 0| - Offset value octet 2. + OCTET_03 = 0x9A, // |1 0 0 1 1 0 1 0| - Offset value octet 3. + OCTET_04 = 0x54, // |0 1 0 1 0 1 0 0| - File data octet 0. + OCTET_05 = 0x65, // |0 1 1 0 0 1 0 1| - File data octet 1. + OCTET_06 = 0x73, // |0 1 1 1 0 0 1 1| - File data octet 2. + OCTET_07 = 0x74, // |0 1 1 1 0 1 0 0| - File data octet 3. + OCTET_08 = 0x46, // |0 1 0 0 0 1 1 0| - File data octet 4. + OCTET_09 = 0x69, // |0 1 1 0 1 0 0 1| - File data octet 5. + OCTET_10 = 0x6C, // |0 1 1 0 1 1 0 0| - File data octet 6. + OCTET_11 = 0x65, // |0 1 1 0 0 1 0 1| - File data octet 7. + OCTET_12 = 0x44, // |0 1 0 0 0 1 0 0| - File data octet 8. + OCTET_13 = 0x61, // |0 1 1 0 0 0 0 1| - File data octet 9. + OCTET_14 = 0x74, // |0 1 1 1 0 1 0 0| - File data octet 10. + OCTET_15 = 0x61, // |0 1 1 0 0 0 0 1| - File data octet 11. + LENGTH = 16, + }; + + // Create a test object with the above values. + FilePacket::FileData create(); + + // Fill buffer with the expected serialization. + void fillBuffer(Buffer& buf, U32 offset); + + // Verify data in buffer matches the expected serialization. + void verifyBuffer(Buffer& buf, U32 offset); + + // Verify object contains the expected values. + void verifyObject(FilePacket::FileData& fileData); +}; + +/* + * Test File Data helpers. + */ +namespace TestFileData2 +{ + namespace Values + { + const U64 offset = + 154; + + const U8 fileData[] = {'T', 'e', 's', 't', 'F', 'i', + 'l', 'e', 'D', 'a', 't', 'a'}; + + const U16 fileDataLength = 12; + + const FilePacket::ContinuationState continuationState = + FilePacket::ContinuationState::BOTH; + + const U8 metadata[] = {'M', 'E', 'T', 'A'}; + + const U8 metadataLength = 4; + } + + enum Serialized : U8 + { + OCTET_00 = 0xC4, // |1 1|0 0 0 1 0 0| - State through metadata length. + OCTET_01 = 0x4D, // |0 1 0 0 1 1 0 1| - Metadata octet 0. + OCTET_02 = 0x45, // |0 1 0 0 0 1 0 1| - Metadata octet 1. + OCTET_03 = 0x54, // |0 1 0 1 0 1 0 0| - Metadata octet 2. + OCTET_04 = 0x41, // |0 1 0 0 0 0 0 1| - Metadata octet 3. + OCTET_05 = 0x00, // |0 0 0 0 0 0 0 0| - Offset value octet 0. + OCTET_06 = 0x00, // |0 0 0 0 0 0 0 0| - Offset value octet 1. + OCTET_07 = 0x00, // |0 0 0 0 0 0 0 0| - Offset value octet 2. + OCTET_08 = 0x00, // |0 0 0 0 0 0 0 0| - Offset value octet 3. + OCTET_09 = 0x00, // |0 0 0 0 0 0 0 0| - Offset value octet 4. + OCTET_10 = 0x00, // |0 0 0 0 0 0 0 0| - Offset value octet 5. + OCTET_11 = 0x00, // |0 0 0 0 0 0 0 0| - Offset value octet 6. + OCTET_12 = 0x9A, // |1 0 0 1 1 0 1 0| - Offset value octet 7. + OCTET_13 = 0x54, // |0 1 0 1 0 1 0 0| - File data octet 0. + OCTET_14 = 0x65, // |0 1 1 0 0 1 0 1| - File data octet 1. + OCTET_15 = 0x73, // |0 1 1 1 0 0 1 1| - File data octet 2. + OCTET_16 = 0x74, // |0 1 1 1 0 1 0 0| - File data octet 3. + OCTET_17 = 0x46, // |0 1 0 0 0 1 1 0| - File data octet 4. + OCTET_18 = 0x69, // |0 1 1 0 1 0 0 1| - File data octet 5. + OCTET_19 = 0x6C, // |0 1 1 0 1 1 0 0| - File data octet 6. + OCTET_20 = 0x65, // |0 1 1 0 0 1 0 1| - File data octet 7. + OCTET_21 = 0x44, // |0 1 0 0 0 1 0 0| - File data octet 8. + OCTET_22 = 0x61, // |0 1 1 0 0 0 0 1| - File data octet 9. + OCTET_23 = 0x74, // |0 1 1 1 0 1 0 0| - File data octet 10. + OCTET_24 = 0x61, // |0 1 1 0 0 0 0 1| - File data octet 11. + LENGTH = 25, + }; + + // Create a test object with the above values. + FilePacket::FileData create(); + + // Fill buffer with the expected serialization. + void fillBuffer(Buffer& buf, U32 offset); + + // Verify data in buffer matches the expected serialization. + void verifyBuffer(Buffer& buf, U32 offset); + + // Verify object contains the expected values. + void verifyObject(FilePacket::FileData& fileData); +}; + +/* + * Test EndOfFile helpers. + */ +namespace TestEndOfFile1 +{ + namespace Values + { + const FilePacket::ConditionCode conditionCode = + FilePacket::ConditionCode::NO_ERROR; + + const U8 spare = 0; + + const U32 fileChecksum = 0xA4B8; + + const U64 fileSize = 128; + } + + enum Serialized : U8 + { + OCTET_00 = 0x04, // |0 0 0 0 0 1 0 0| - End-of-file directive code. + OCTET_01 = 0x00, // |0 0 0 0|0 0 0 0| - Condition code through spare. + OCTET_02 = 0x00, // |0 0 0 0 0 0 0 0| - File checksum octet 0. + OCTET_03 = 0x00, // |0 0 0 0 0 0 0 0| - File checksum octet 1. + OCTET_04 = 0xA4, // |1 0 1 0 0 1 0 0| - File checksum octet 2. + OCTET_05 = 0xB8, // |1 0 1 1 1 0 0 0| - File checksum octet 3. + OCTET_06 = 0x00, // |0 0 0 0 0 0 0 0| - File size value octet 0. + OCTET_07 = 0x00, // |0 0 0 0 0 0 0 0| - File size value octet 1. + OCTET_08 = 0x00, // |0 0 0 0 0 0 0 0| - File size value octet 2. + OCTET_09 = 0x80, // |1 0 0 0 0 0 0 0| - File size value octet 3. + LENGTH = 10, + }; + + // Create a test object with the above values. + FilePacket::EndOfFile create(); + + // Fill buffer with the expected serialization. + void fillBuffer(Buffer& buf, U32 offset); + + // Verify data in buffer matches the expected serialization. + void verifyBuffer(Buffer& buf, U32 offset); + + // Verify object contains the expected values. + void verifyObject(FilePacket::EndOfFile& endOfFile); +}; + +/* + * Test Finished helpers. + */ +namespace TestFinished1 +{ + namespace Values + { + const FilePacket::ConditionCode conditionCode = + FilePacket::ConditionCode::NO_ERROR; + + const U8 spare = 0; + + const FilePacket::DeliveryCode deliveryCode = + FilePacket::DeliveryCode::DATA_COMPLETE; + + const FilePacket::FileStatus fileStatus = + FilePacket::FileStatus::SUCCESS; + } + + enum Serialized : U8 + { + OCTET_00 = 0x05, // |0 0 0 0 0 1 0 1| - Finished directive code. + OCTET_01 = 0x02, // |0 0 0 0|0|0|1|0| - Condition code through file status. + LENGTH = 2, + }; + + // Create a test object with the above values. + FilePacket::Finished create(); + + // Fill buffer with the expected serialization. + void fillBuffer(Buffer& buf, U32 offset); + + // Verify data in buffer matches the expected serialization. + void verifyBuffer(Buffer& buf, U32 offset); + + // Verify object contains the expected values. + void verifyObject(FilePacket::Finished& finished); +}; + +/* + * Test Metadata helpers. + */namespace TestMetadata1 +{ + namespace Values + { + const U8 reserved0 = 0; + + const FilePacket::ClosureRequested closureRequested = + FilePacket::ClosureRequested::REQUESTED; + + const U8 reserved1 = 0; + + const FilePacket::ChecksumType checksumType = + FilePacket::ChecksumType::NULL_CHECKSUM; + + const U64 fileSize = 16; + + const char* const sourceFilename = "SourceFilename"; + + const U8 sourceFilenameLength = strlen(sourceFilename); + + const char* const destFilename = "DestFilename"; + + const U8 destFilenameLength = strlen(destFilename); + } + + enum Serialized : U8 + { + OCTET_00 = 0x07, // |0 0 0 0 0 1 1 1| - Metadata directive code. + OCTET_01 = 0x4F, // |0|1|0|0|1|1|1|1| - Reserved through checksum type. + OCTET_02 = 0x00, // |0 0 0 0 0 0 0 0| - Small file size octet 0. + OCTET_03 = 0x00, // |0 0 0 0 0 0 0 0| - Small file size octet 1. + OCTET_04 = 0x00, // |0 0 0 0 0 0 0 0| - Small file size octet 2. + OCTET_05 = 0x10, // |0 0 0 1 0 0 0 0| - Small file size octet 3. + OCTET_06 = 0x0E, // |0 0 0 0 1 1 1 0| - Source file name LV length. + OCTET_07 = 0x53, // |0 1 0 1 0 0 1 1| - Source file name LV value octet 0. + OCTET_08 = 0x6F, // |0 1 1 0 1 1 1 1| - Source file name LV value octet 1. + OCTET_09 = 0x75, // |0 1 1 1 0 1 0 1| - Source file name LV value octet 2. + OCTET_10 = 0x72, // |0 1 1 1 0 0 1 0| - Source file name LV value octet 3. + OCTET_11 = 0x63, // |0 1 1 0 0 0 1 1| - Source file name LV value octet 4. + OCTET_12 = 0x65, // |0 1 1 0 0 1 0 1| - Source file name LV value octet 5. + OCTET_13 = 0x46, // |0 1 0 0 0 1 1 0| - Source file name LV value octet 6. + OCTET_14 = 0x69, // |0 1 1 0 1 0 0 1| - Source file name LV value octet 7. + OCTET_15 = 0x6C, // |0 1 1 0 1 1 0 0| - Source file name LV value octet 8. + OCTET_16 = 0x65, // |0 1 1 0 0 1 0 1| - Source file name LV value octet 9. + OCTET_17 = 0x6E, // |0 1 1 0 1 1 1 0| - Source file name LV value octet 10. + OCTET_18 = 0x61, // |0 1 1 0 0 0 0 1| - Source file name LV value octet 11. + OCTET_19 = 0x6D, // |0 1 1 0 1 1 0 1| - Source file name LV value octet 12. + OCTET_20 = 0x65, // |0 1 1 0 0 1 0 1| - Source file name LV value octet 13. + OCTET_21 = 0x0C, // |0 0 0 0 1 1 0 0| - Dest file name LV length. + OCTET_22 = 0x44, // |0 1 0 0 0 1 0 0| - Dest file name LV value octet 0. + OCTET_23 = 0x65, // |0 1 1 0 0 1 0 1| - Dest file name LV value octet 1. + OCTET_24 = 0x73, // |0 1 1 1 0 0 1 1| - Dest file name LV value octet 2. + OCTET_25 = 0x74, // |0 1 1 1 0 1 0 0| - Dest file name LV value octet 3. + OCTET_26 = 0x46, // |0 1 0 0 0 1 1 0| - Dest file name LV value octet 4. + OCTET_27 = 0x69, // |0 1 1 0 1 0 0 1| - Dest file name LV value octet 5. + OCTET_28 = 0x6C, // |0 1 1 0 1 1 0 0| - Dest file name LV value octet 6. + OCTET_29 = 0x65, // |0 1 1 0 0 1 0 1| - Dest file name LV value octet 7. + OCTET_30 = 0x6E, // |0 1 1 0 1 1 1 0| - Dest file name LV value octet 8. + OCTET_31 = 0x61, // |0 1 1 0 0 0 0 1| - Dest file name LV value octet 9. + OCTET_32 = 0x6D, // |0 1 1 0 1 1 0 1| - Dest file name LV value octet 10. + OCTET_33 = 0x65, // |0 1 1 0 0 1 0 1| - Dest file name LV value octet 11. + LENGTH = 34, + }; + + // Create a test object with the above values. + FilePacket::Metadata create(); + + // Fill buffer with the expected serialization. + void fillBuffer(Buffer& buf, U32 offset); + + // Verify data in buffer matches the expected serialization. + void verifyBuffer(Buffer& buf, U32 offset); + + // Verify object contains the expected values. + void verifyObject(FilePacket::Metadata& metadata); +}; + +} // namespace Cfdp + +} // namespace Fw + +#endif // FW_CFDP_TESTFILEPACKET_HPP diff --git a/Fw/Ccsds/Cfdp/test/ut/TestFilePacketMain.cpp b/Fw/Ccsds/Cfdp/test/ut/TestFilePacketMain.cpp new file mode 100644 index 00000000000..60241c1ec88 --- /dev/null +++ b/Fw/Ccsds/Cfdp/test/ut/TestFilePacketMain.cpp @@ -0,0 +1,23 @@ +//! ============================================================================ +//! @file TestFilePacketMain.cpp +//! @brief CFDP file packet main test file. +//! @author chownw +//! ============================================================================ + +#include + +namespace Fw +{ + +namespace Cfdp +{ + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +} // namespace Cfdp + +} // namespace Fw diff --git a/Fw/Ccsds/Cfdp/test/ut/TestFinished.cpp b/Fw/Ccsds/Cfdp/test/ut/TestFinished.cpp new file mode 100644 index 00000000000..b77c4ca8423 --- /dev/null +++ b/Fw/Ccsds/Cfdp/test/ut/TestFinished.cpp @@ -0,0 +1,151 @@ +//! ============================================================================ +//! @file TestFinished.cpp +//! @brief CFDP file packet Finished test file. +//! @author chownw +//! ============================================================================ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +FilePacket::Finished TestFinished1:: + create() +{ + Fw::Cfdp::FilePacket::Finished finished( + TestFinished1::Values::conditionCode, + TestFinished1::Values::deliveryCode, + TestFinished1::Values::fileStatus + ); + + return finished; +} + +void TestFinished1:: + fillBuffer(Buffer &buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + data[0] = TestFinished1::Serialized::OCTET_00; + data[1] = TestFinished1::Serialized::OCTET_01; +} + +void TestFinished1:: + verifyBuffer(Buffer& buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + EXPECT_EQ( + data[0], + TestFinished1::Serialized::OCTET_00 + ); + EXPECT_EQ( + data[1], + TestFinished1::Serialized::OCTET_01 + ); +} + +void TestFinished1:: + verifyObject(FilePacket::Finished& finished) +{ + EXPECT_EQ( + finished.directiveCode, + FilePacket::DirectiveType::FINISHED + ); + EXPECT_EQ( + finished.getConditionCode(), + TestFinished1::Values::conditionCode + ); + EXPECT_EQ( + finished.spare, + TestFinished1::Values::spare + ); + EXPECT_EQ( + finished.getDeliveryCode(), + TestFinished1::Values::deliveryCode + ); + EXPECT_EQ( + finished.getFileStatus(), + TestFinished1::Values::fileStatus + ); +} + +TEST(FilePacketFinished, Serialize) +{ + // Allocate buffer for serialization + U8 data[TestFinished1::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestFinished1::Serialized::LENGTH); + + FilePacket::Header header = TestHeader1::create(); + FilePacket::Finished finished = TestFinished1::create(); + finished.serialize(buffer, 0, header); + + // Verify buffer + TestFinished1::verifyBuffer(buffer, 0); +} + +TEST(FilePacketFinished, Deserialize) +{ + // Allocate buffer for serialization + U8 data[TestFinished1::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestFinished1::Serialized::LENGTH); + + // Fill buffer with serialization + TestFinished1::fillBuffer(buffer, 0); + + // Call deserialize function + FilePacket::Header header = TestHeader1::create(); + FilePacket::Finished finished; + finished.deserialize(buffer, 0, header); + + // Verify End-of-file + TestFinished1::verifyObject(finished); +} + +TEST(FilePacketFinished, Offset) +{ + // Test (de)serialization with arbitrary offset in the buffer + U32 offset = 15; + + // Allocate buffer for serialization + U8 data[TestFinished1::Serialized::LENGTH + offset]; + Fw::Buffer buffer(data, TestFinished1::Serialized::LENGTH + offset); + + // Test serialize function + FilePacket::Header header = TestHeader1::create(); + FilePacket::Finished srcFinished = TestFinished1::create(); + srcFinished.serialize(buffer, offset, header); + TestFinished1::verifyBuffer(buffer, offset); + + // Test deserialize function + FilePacket::Finished destFinished; + destFinished.deserialize(buffer, offset, header); + TestFinished1::verifyObject(destFinished); +} + +TEST(FilePacketFinished, SerializedLength) +{ + FilePacket::Header header = TestHeader1::create(); + FilePacket::Finished finished = TestFinished1::create(); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + finished.getSerializedLength(header), + TestFinished1::Serialized::LENGTH + ); +} + +} // namespace Cfdp + +} // namespace Fw diff --git a/Fw/Ccsds/Cfdp/test/ut/TestHeader.cpp b/Fw/Ccsds/Cfdp/test/ut/TestHeader.cpp new file mode 100644 index 00000000000..b781ec1c727 --- /dev/null +++ b/Fw/Ccsds/Cfdp/test/ut/TestHeader.cpp @@ -0,0 +1,470 @@ +//! ============================================================================ +//! @file TestHeader.cpp +//! @brief CFDP file packet Header test file. +//! @author chownw +//! ============================================================================ + +#include + +#include +#include +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +FilePacket::Header TestHeader1:: + create() +{ + Fw::Cfdp::FilePacket::Header header( + TestHeader1::Values::type, + TestHeader1::Values::direction, + TestHeader1::Values::transmissionMode, + TestHeader1::Values::crcFlag, + TestHeader1::Values::largeFileFlag, + TestHeader1::Values::segmentationControl, + TestHeader1::Values::segmentMetadataFlag, + TestHeader1::Values::transSeqNumLength, + TestHeader1::Values::transSeqNumber, + TestHeader1::Values::entityIdLength, + TestHeader1::Values::sourceEntityId, + TestHeader1::Values::destEntityId, + TestHeader1::Values::dataFieldLength + ); + + return header; +} + +void TestHeader1:: + fillBuffer(Buffer &buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + data[0] = TestHeader1::Serialized::OCTET_00; + data[1] = TestHeader1::Serialized::OCTET_01; + data[2] = TestHeader1::Serialized::OCTET_02; + data[3] = TestHeader1::Serialized::OCTET_03; + data[4] = TestHeader1::Serialized::OCTET_04; + data[5] = TestHeader1::Serialized::OCTET_05; + data[6] = TestHeader1::Serialized::OCTET_06; + data[7] = TestHeader1::Serialized::OCTET_07; + data[8] = TestHeader1::Serialized::OCTET_08; + data[9] = TestHeader1::Serialized::OCTET_09; + data[10] = TestHeader1::Serialized::OCTET_10; + data[11] = TestHeader1::Serialized::OCTET_11; + data[12] = TestHeader1::Serialized::OCTET_12; +} + +void TestHeader1:: + verifyBuffer(Buffer& buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + EXPECT_EQ( + data[0], + TestHeader1::Serialized::OCTET_00 + ); + EXPECT_EQ( + data[1], + TestHeader1::Serialized::OCTET_01 + ); + EXPECT_EQ( + data[2], + TestHeader1::Serialized::OCTET_02 + ); + EXPECT_EQ( + data[3], + TestHeader1::Serialized::OCTET_03 + ); + EXPECT_EQ( + data[4], + TestHeader1::Serialized::OCTET_04 + ); + EXPECT_EQ( + data[5], + TestHeader1::Serialized::OCTET_05 + ); + EXPECT_EQ( + data[6], + TestHeader1::Serialized::OCTET_06 + ); + EXPECT_EQ( + data[7], + TestHeader1::Serialized::OCTET_07 + ); + EXPECT_EQ( + data[8], + TestHeader1::Serialized::OCTET_08 + ); + EXPECT_EQ( + data[9], + TestHeader1::Serialized::OCTET_09 + ); + EXPECT_EQ( + data[10], + TestHeader1::Serialized::OCTET_10 + ); + EXPECT_EQ( + data[11], + TestHeader1::Serialized::OCTET_11 + ); + EXPECT_EQ( + data[12], + TestHeader1::Serialized::OCTET_12 + ); +} + +void TestHeader1:: + verifyObject(FilePacket::Header& header) +{ + EXPECT_EQ( + header.getVersion(), + TestHeader1::Values::version + ); + EXPECT_EQ( + header.getType(), + TestHeader1::Values::type + ); + EXPECT_EQ( + header.getDirection(), + TestHeader1::Values::direction + ); + EXPECT_EQ( + header.getTransmissionMode(), + TestHeader1::Values::transmissionMode + ); + EXPECT_EQ( + header.getCrcFlag(), + TestHeader1::Values::crcFlag + ); + EXPECT_EQ( + header.getLargeFileFlag(), + TestHeader1::Values::largeFileFlag + ); + EXPECT_EQ( + header.getDataFieldLength(), + TestHeader1::Values::dataFieldLength + ); + EXPECT_EQ( + header.getSegmentationControl(), + TestHeader1::Values::segmentationControl + ); + EXPECT_EQ( + header.getEntityIdLength(), + TestHeader1::Values::entityIdLength + ); + EXPECT_EQ( + header.getSegmentMetadataFlag(), + TestHeader1::Values::segmentMetadataFlag + ); + EXPECT_EQ( + header.getTransSeqNumLength(), + TestHeader1::Values::transSeqNumLength + ); + EXPECT_EQ( + header.getSourceEntityId(), + TestHeader1::Values::sourceEntityId + ); + EXPECT_EQ( + header.getTransSeqNumber(), + TestHeader1::Values::transSeqNumber + ); + EXPECT_EQ( + header.getDestEntityId(), + TestHeader1::Values::destEntityId + ); +} + +FilePacket::Header TestHeader2:: + create() +{ + Fw::Cfdp::FilePacket::Header header( + TestHeader2::Values::type, + TestHeader2::Values::direction, + TestHeader2::Values::transmissionMode, + TestHeader2::Values::crcFlag, + TestHeader2::Values::largeFileFlag, + TestHeader2::Values::segmentationControl, + TestHeader2::Values::segmentMetadataFlag, + TestHeader2::Values::transSeqNumLength, + TestHeader2::Values::transSeqNumber, + TestHeader2::Values::entityIdLength, + TestHeader2::Values::sourceEntityId, + TestHeader2::Values::destEntityId, + TestHeader2::Values::dataFieldLength + ); + + return header; +} + +void TestHeader2:: + fillBuffer(Buffer &buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + data[0] = TestHeader2::Serialized::OCTET_00; + data[1] = TestHeader2::Serialized::OCTET_01; + data[2] = TestHeader2::Serialized::OCTET_02; + data[3] = TestHeader2::Serialized::OCTET_03; + data[4] = TestHeader2::Serialized::OCTET_04; + data[5] = TestHeader2::Serialized::OCTET_05; + data[6] = TestHeader2::Serialized::OCTET_06; + data[7] = TestHeader2::Serialized::OCTET_07; + data[8] = TestHeader2::Serialized::OCTET_08; + data[9] = TestHeader2::Serialized::OCTET_09; + data[10] = TestHeader2::Serialized::OCTET_10; + data[11] = TestHeader2::Serialized::OCTET_11; + data[12] = TestHeader2::Serialized::OCTET_12; +} + +void TestHeader2:: + verifyBuffer(Buffer& buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + EXPECT_EQ( + data[0], + TestHeader2::Serialized::OCTET_00 + ); + EXPECT_EQ( + data[1], + TestHeader2::Serialized::OCTET_01 + ); + EXPECT_EQ( + data[2], + TestHeader2::Serialized::OCTET_02 + ); + EXPECT_EQ( + data[3], + TestHeader2::Serialized::OCTET_03 + ); + EXPECT_EQ( + data[4], + TestHeader2::Serialized::OCTET_04 + ); + EXPECT_EQ( + data[5], + TestHeader2::Serialized::OCTET_05 + ); + EXPECT_EQ( + data[6], + TestHeader2::Serialized::OCTET_06 + ); + EXPECT_EQ( + data[7], + TestHeader2::Serialized::OCTET_07 + ); + EXPECT_EQ( + data[8], + TestHeader2::Serialized::OCTET_08 + ); + EXPECT_EQ( + data[9], + TestHeader2::Serialized::OCTET_09 + ); + EXPECT_EQ( + data[10], + TestHeader2::Serialized::OCTET_10 + ); + EXPECT_EQ( + data[11], + TestHeader2::Serialized::OCTET_11 + ); + EXPECT_EQ( + data[12], + TestHeader2::Serialized::OCTET_12 + ); +} + +void TestHeader2:: + verifyObject(FilePacket::Header& header) +{ + EXPECT_EQ( + header.getVersion(), + TestHeader2::Values::version + ); + EXPECT_EQ( + header.getType(), + TestHeader2::Values::type + ); + EXPECT_EQ( + header.getDirection(), + TestHeader2::Values::direction + ); + EXPECT_EQ( + header.getTransmissionMode(), + TestHeader2::Values::transmissionMode + ); + EXPECT_EQ( + header.getCrcFlag(), + TestHeader2::Values::crcFlag + ); + EXPECT_EQ( + header.getLargeFileFlag(), + TestHeader2::Values::largeFileFlag + ); + EXPECT_EQ( + header.getDataFieldLength(), + TestHeader2::Values::dataFieldLength + ); + EXPECT_EQ( + header.getSegmentationControl(), + TestHeader2::Values::segmentationControl + ); + EXPECT_EQ( + header.getEntityIdLength(), + TestHeader2::Values::entityIdLength + ); + EXPECT_EQ( + header.getSegmentMetadataFlag(), + TestHeader2::Values::segmentMetadataFlag + ); + EXPECT_EQ( + header.getTransSeqNumLength(), + TestHeader2::Values::transSeqNumLength + ); + EXPECT_EQ( + header.getSourceEntityId(), + TestHeader2::Values::sourceEntityId + ); + EXPECT_EQ( + header.getTransSeqNumber(), + TestHeader2::Values::transSeqNumber + ); + EXPECT_EQ( + header.getDestEntityId(), + TestHeader2::Values::destEntityId + ); +} + +TEST(FilePacketHeader, Serialize1) +{ + // Allocate buffer for serialization + U8 data[TestHeader1::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestHeader1::Serialized::LENGTH); + + FilePacket::Header header = TestHeader1::create(); + header.serialize(buffer, 0); + + // Verify buffer + TestHeader1::verifyBuffer(buffer, 0); +} + +TEST(FilePacketHeader, Serialize2) +{ + // Allocate buffer for serialization + U8 data[TestHeader2::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestHeader2::Serialized::LENGTH); + + FilePacket::Header header = TestHeader2::create(); + header.serialize(buffer, 0); + + // Verify buffer + TestHeader2::verifyBuffer(buffer, 0); +} + +TEST(FilePacketHeader, Deserialize1) +{ + // Allocate buffer for serialization + U8 data[TestHeader1::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestHeader1::Serialized::LENGTH); + + // Fill buffer with serialization + TestHeader1::fillBuffer(buffer, 0); + + // Call deserialize function + FilePacket::Header header; + header.deserialize(buffer, 0); + + // Verify header + TestHeader1::verifyObject(header); +} + +TEST(FilePacketHeader, Deserialize2) +{ + // Allocate buffer for serialization + U8 data[TestHeader2::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestHeader2::Serialized::LENGTH); + + // Fill buffer with serialization + TestHeader2::fillBuffer(buffer, 0); + + // Call deserialize function + FilePacket::Header header; + header.deserialize(buffer, 0); + + // Verify header + TestHeader2::verifyObject(header); +} + +TEST(FilePacketHeader, Offset1) +{ + // Test (de)serialization of header with arbitrary offset in the buffer + U32 offset = 11; + + // Allocate buffer for serialization + U8 data[TestHeader1::Serialized::LENGTH + offset]; + Fw::Buffer buffer(data, TestHeader1::Serialized::LENGTH + offset); + + // Test serialize function + FilePacket::Header sourceHeader = TestHeader1::create(); + sourceHeader.serialize(buffer, offset); + TestHeader1::verifyBuffer(buffer, offset); + + // Test deserialize function + FilePacket::Header destHeader; + destHeader.deserialize(buffer, offset); + TestHeader1::verifyObject(destHeader); +} + +TEST(FilePacketHeader, Offset2) +{ + // Test (de)serialization of header with arbitrary offset in the buffer + U32 offset = 11; + + // Allocate buffer for serialization + U8 data[TestHeader2::Serialized::LENGTH + offset]; + Fw::Buffer buffer(data, TestHeader2::Serialized::LENGTH + offset); + + // Test serialize function + FilePacket::Header sourceHeader = TestHeader2::create(); + sourceHeader.serialize(buffer, offset); + TestHeader2::verifyBuffer(buffer, offset); + + // Test deserialize function + FilePacket::Header destHeader; + destHeader.deserialize(buffer, offset); + TestHeader2::verifyObject(destHeader); +} + +TEST(FilePacketHeader, SerializedLength1) +{ + FilePacket::Header header = TestHeader1::create(); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + header.getSerializedLength(), + TestHeader1::Serialized::LENGTH + ); +} + +TEST(FilePacketHeader, SerializedLength2) +{ + FilePacket::Header header = TestHeader2::create(); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + header.getSerializedLength(), + TestHeader2::Serialized::LENGTH + ); +} + +} // namespace Cfdp + +} // namespace Fw diff --git a/Fw/Ccsds/Cfdp/test/ut/TestMetadata.cpp b/Fw/Ccsds/Cfdp/test/ut/TestMetadata.cpp new file mode 100644 index 00000000000..fd8803316cb --- /dev/null +++ b/Fw/Ccsds/Cfdp/test/ut/TestMetadata.cpp @@ -0,0 +1,341 @@ +//! ============================================================================ +//! @file TestMetadata.cpp +//! @brief CFDP file packet Metadata test file. +//! @author chownw +//! ============================================================================ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Fw +{ + +namespace Cfdp +{ + +FilePacket::Metadata TestMetadata1:: + create() +{ + Fw::Cfdp::FilePacket::Metadata metadata( + TestMetadata1::Values::closureRequested, + TestMetadata1::Values::checksumType, + TestMetadata1::Values::fileSize, + TestMetadata1::Values::sourceFilename, + TestMetadata1::Values::destFilename + ); + + return metadata; +} + +void TestMetadata1:: + fillBuffer(Buffer &buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + data[0] = TestMetadata1::Serialized::OCTET_00; + data[1] = TestMetadata1::Serialized::OCTET_01; + data[2] = TestMetadata1::Serialized::OCTET_02; + data[3] = TestMetadata1::Serialized::OCTET_03; + data[4] = TestMetadata1::Serialized::OCTET_04; + data[5] = TestMetadata1::Serialized::OCTET_05; + data[6] = TestMetadata1::Serialized::OCTET_06; + data[7] = TestMetadata1::Serialized::OCTET_07; + data[8] = TestMetadata1::Serialized::OCTET_08; + data[9] = TestMetadata1::Serialized::OCTET_09; + data[10] = TestMetadata1::Serialized::OCTET_10; + data[11] = TestMetadata1::Serialized::OCTET_11; + data[12] = TestMetadata1::Serialized::OCTET_12; + data[13] = TestMetadata1::Serialized::OCTET_13; + data[14] = TestMetadata1::Serialized::OCTET_14; + data[15] = TestMetadata1::Serialized::OCTET_15; + data[16] = TestMetadata1::Serialized::OCTET_16; + data[17] = TestMetadata1::Serialized::OCTET_17; + data[18] = TestMetadata1::Serialized::OCTET_18; + data[19] = TestMetadata1::Serialized::OCTET_19; + data[20] = TestMetadata1::Serialized::OCTET_20; + data[21] = TestMetadata1::Serialized::OCTET_21; + data[22] = TestMetadata1::Serialized::OCTET_22; + data[23] = TestMetadata1::Serialized::OCTET_23; + data[24] = TestMetadata1::Serialized::OCTET_24; + data[25] = TestMetadata1::Serialized::OCTET_25; + data[26] = TestMetadata1::Serialized::OCTET_26; + data[27] = TestMetadata1::Serialized::OCTET_27; + data[28] = TestMetadata1::Serialized::OCTET_28; + data[29] = TestMetadata1::Serialized::OCTET_29; + data[30] = TestMetadata1::Serialized::OCTET_30; + data[31] = TestMetadata1::Serialized::OCTET_31; + data[32] = TestMetadata1::Serialized::OCTET_32; + data[33] = TestMetadata1::Serialized::OCTET_33; +} + +void TestMetadata1:: + verifyBuffer(Buffer& buf, U32 offset) +{ + U8* data = buf.getData() + offset; + + EXPECT_EQ( + data[0], + TestMetadata1::Serialized::OCTET_00 + ); + EXPECT_EQ( + data[1], + TestMetadata1::Serialized::OCTET_01 + ); + EXPECT_EQ( + data[2], + TestMetadata1::Serialized::OCTET_02 + ); + EXPECT_EQ( + data[3], + TestMetadata1::Serialized::OCTET_03 + ); + EXPECT_EQ( + data[4], + TestMetadata1::Serialized::OCTET_04 + ); + EXPECT_EQ( + data[5], + TestMetadata1::Serialized::OCTET_05 + ); + EXPECT_EQ( + data[6], + TestMetadata1::Serialized::OCTET_06 + ); + EXPECT_EQ( + data[7], + TestMetadata1::Serialized::OCTET_07 + ); + EXPECT_EQ( + data[8], + TestMetadata1::Serialized::OCTET_08 + ); + EXPECT_EQ( + data[9], + TestMetadata1::Serialized::OCTET_09 + ); + EXPECT_EQ( + data[10], + TestMetadata1::Serialized::OCTET_10 + ); + EXPECT_EQ( + data[11], + TestMetadata1::Serialized::OCTET_11 + ); + EXPECT_EQ( + data[12], + TestMetadata1::Serialized::OCTET_12 + ); + EXPECT_EQ( + data[13], + TestMetadata1::Serialized::OCTET_13 + ); + EXPECT_EQ( + data[14], + TestMetadata1::Serialized::OCTET_14 + ); + EXPECT_EQ( + data[15], + TestMetadata1::Serialized::OCTET_15 + ); + EXPECT_EQ( + data[16], + TestMetadata1::Serialized::OCTET_16 + ); + EXPECT_EQ( + data[17], + TestMetadata1::Serialized::OCTET_17 + ); + EXPECT_EQ( + data[18], + TestMetadata1::Serialized::OCTET_18 + ); + EXPECT_EQ( + data[19], + TestMetadata1::Serialized::OCTET_19 + ); + EXPECT_EQ( + data[20], + TestMetadata1::Serialized::OCTET_20 + ); + EXPECT_EQ( + data[21], + TestMetadata1::Serialized::OCTET_21 + ); + EXPECT_EQ( + data[22], + TestMetadata1::Serialized::OCTET_22 + ); + EXPECT_EQ( + data[23], + TestMetadata1::Serialized::OCTET_23 + ); + EXPECT_EQ( + data[24], + TestMetadata1::Serialized::OCTET_24 + ); + EXPECT_EQ( + data[25], + TestMetadata1::Serialized::OCTET_25 + ); + EXPECT_EQ( + data[26], + TestMetadata1::Serialized::OCTET_26 + ); + EXPECT_EQ( + data[27], + TestMetadata1::Serialized::OCTET_27 + ); + EXPECT_EQ( + data[28], + TestMetadata1::Serialized::OCTET_28 + ); + EXPECT_EQ( + data[29], + TestMetadata1::Serialized::OCTET_29 + ); + EXPECT_EQ( + data[30], + TestMetadata1::Serialized::OCTET_30 + ); + EXPECT_EQ( + data[31], + TestMetadata1::Serialized::OCTET_31 + ); + EXPECT_EQ( + data[32], + TestMetadata1::Serialized::OCTET_32 + ); + EXPECT_EQ( + data[33], + TestMetadata1::Serialized::OCTET_33 + ); +} + +void TestMetadata1:: + verifyObject(FilePacket::Metadata& metadata) +{ + EXPECT_EQ( + metadata.directiveCode, + FilePacket::DirectiveType::METADATA + ); + EXPECT_EQ( + metadata.reserved0, + TestMetadata1::Values::reserved0 + ); + EXPECT_EQ( + metadata.getClosureRequested(), + TestMetadata1::Values::closureRequested + ); + EXPECT_EQ( + metadata.reserved1, + TestMetadata1::Values::reserved1 + ); + EXPECT_EQ( + metadata.getChecksumType(), + TestMetadata1::Values::checksumType + ); + EXPECT_EQ( + metadata.getFileSize(), + TestMetadata1::Values::fileSize + ); + EXPECT_EQ( + metadata.getSourceFilenameLength(), + TestMetadata1::Values::sourceFilenameLength + ); + EXPECT_EQ( + strncmp( + metadata.getSourceFilename(), + TestMetadata1::Values::sourceFilename, + metadata.getSourceFilenameLength() + ), + 0 + ); + EXPECT_EQ( + metadata.getDestFilenameLength(), + TestMetadata1::Values::destFilenameLength + ); + EXPECT_EQ( + strncmp( + metadata.getDestFilename(), + TestMetadata1::Values::destFilename, + metadata.getDestFilenameLength() + ), + 0 + ); +} + +TEST(FilePacketMetadata, Serialize) +{ + // Allocate buffer for serialization + U8 data[TestMetadata1::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestMetadata1::Serialized::LENGTH); + + FilePacket::Header header = TestHeader1::create(); + FilePacket::Metadata metadata = TestMetadata1::create(); + metadata.serialize(buffer, 0, header); + + // Verify buffer + TestMetadata1::verifyBuffer(buffer, 0); +} + +TEST(FilePacketMetadata, Deserialize) +{ + // Allocate buffer for serialization + U8 data[TestMetadata1::Serialized::LENGTH]; + Fw::Buffer buffer(data, TestMetadata1::Serialized::LENGTH); + + // Fill buffer with serialization + TestMetadata1::fillBuffer(buffer, 0); + + // Call deserialize function + FilePacket::Header header = TestHeader1::create(); + FilePacket::Metadata metadata; + metadata.deserialize(buffer, 0, header); + + // Verify metadata + TestMetadata1::verifyObject(metadata); +} + +TEST(FilePacketMetadata, Offset) +{ + // Test (de)serialization of metadata with arbitrary offset in the buffer + U32 offset = 7; + + // Allocate buffer for serialization + U8 data[TestMetadata1::Serialized::LENGTH + offset]; + Fw::Buffer buffer(data, TestMetadata1::Serialized::LENGTH + offset); + + // Test serialize function + FilePacket::Header header = TestHeader1::create(); + FilePacket::Metadata sourceMetadata = TestMetadata1::create(); + sourceMetadata.serialize(buffer, offset, header); + TestMetadata1::verifyBuffer(buffer, offset); + + // Test deserialize function + FilePacket::Metadata destMetadata; + destMetadata.deserialize(buffer, offset, header); + TestMetadata1::verifyObject(destMetadata); +} + +TEST(FilePacketMetadata, SerializedLength) +{ + FilePacket::Header header = TestHeader1::create(); + FilePacket::Metadata metadata = TestMetadata1::create(); + + // Verify getSerializedLength returns the expected length + EXPECT_EQ( + metadata.getSerializedLength(header), + TestMetadata1::Serialized::LENGTH + ); +} + +} // namespace Cfdp + +} // namespace Fw