diff --git a/CMakeLists.txt b/CMakeLists.txt index fb561a7167901794677f8bd21dcc4ecec0c20a7d..2d60013b3ee2fb8f46558d46b891e11be189d1c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,6 @@ include_directories("${PROJECT_SOURCE_DIR}/inc" "${PROJECT_SOURCE_DIR}/lib/etl/i add_custom_target(check COMMAND ./cppcheck.sh - COMMAND ./vera.sh COMMAND ./clang-tidy.sh COMMAND ./cppcheck-misra.sh WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/ci") diff --git a/inc/Message.hpp b/inc/Message.hpp index b37d4ac2d866fed774af516443d5863b2106c2aa..2ee8d3c85f0d302c6d2e71ff73d7cb3d110f2d43 100644 --- a/inc/Message.hpp +++ b/inc/Message.hpp @@ -358,6 +358,20 @@ public: */ void appendOctetString(const etl::istring& string); + /** + * Generic function to append any type of data to the message. The amount of bytes appended is equal to the size of + * the @ref T value. + * + * The data is appended on the current write position (deduced by @ref dataSize) + * + * Calling this or any of the other `append...` functions for equivalent types is exactly the same. + * + * @tparam T The type of the value to be appended + * @return The value to append + */ + template <typename T> + void append(const T& value); + /** * Adds a nested TC or TM Message within the current Message * @@ -514,6 +528,42 @@ public: return size; // Return the string size } + /** + * Fetches an N-byte string from the current position in the message. The string can be at most MAX_SIZE long. + * + * @note This function was not implemented as Message::read() due to an inherent C++ limitation, see + * https://www.fluentcpp.com/2017/08/15/function-templates-partial-specialization-cpp/ + * @tparam MAX_SIZE The memory size of the string in bytes, which corresponds to the max string size + */ + template<const size_t MAX_SIZE> + String<MAX_SIZE> readOctetString() { + String<MAX_SIZE> string(""); + + uint16_t length = readUint16(); + ASSERT_REQUEST(length <= string.max_size(), ErrorHandler::StringTooShort); + ASSERT_REQUEST((readPosition + length) <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooShort); + + string.append(data + readPosition, length); + readPosition += length; + + return std::move(string); + } + + /** + * Generic function to read any type of data from the message. The amount of bytes read is equal to the size of + * the @ref T value. + * + * After the data is read, the message pointer @ref readPosition moves forward so that the next amount of data + * can be read. + * + * Calling this or any of the other `read...` functions for equivalent types is exactly the same. + * + * @tparam T The type to be read + * @return The value that has been read from the string + */ + template <typename T> + T read(); + /** * @brief Skip read bytes in the read string * @details Skips the provided number of bytes, by incrementing the readPosition and this is @@ -564,4 +614,37 @@ public: } }; +template<> inline void Message::append(const uint8_t& value) { appendUint8(value); } +template<> inline void Message::append(const uint16_t& value) { appendUint16(value); } +template<> inline void Message::append(const uint32_t& value) { appendUint32(value); } +template<> inline void Message::append(const uint64_t& value) { appendUint64(value); } + +template<> inline void Message::append(const int8_t& value) { appendSint8(value); } +template<> inline void Message::append(const int16_t& value) { appendSint16(value); } +template<> inline void Message::append(const int32_t& value) { appendSint32(value); } + +template<> inline void Message::append(const bool& value) { appendBoolean(value); } +template<> inline void Message::append(const char& value) { appendByte(value); } +template<> inline void Message::append(const float& value) { appendFloat(value); } + +/** + * Appends an ETL string to the message. ETL strings are handled as ECSS octet strings, meaning that the string size + * is appended as a byte before the string itself. To append other string sequences, see the Message::appendString() + * functions + */ +template<> inline void Message::append(const etl::istring& value) { appendOctetString(value); } + +template<> inline uint8_t Message::read() { return readUint8(); } +template<> inline uint16_t Message::read() { return readUint16(); } +template<> inline uint32_t Message::read() { return readUint32(); } +template<> inline uint64_t Message::read() { return readUint64(); } + +template<> inline int8_t Message::read() { return readSint8(); } +template<> inline int16_t Message::read() { return readSint16(); } +template<> inline int32_t Message::read() { return readSint32(); } + +template<> inline bool Message::read<bool>() { return readBoolean(); } +template<> inline char Message::read() { return readByte(); } +template<> inline float Message::read() { return readFloat(); } + #endif // ECSS_SERVICES_PACKET_H diff --git a/inc/Services/Parameter.hpp b/inc/Services/Parameter.hpp index 932d450f5389fb6cb67c9979f52106aef00d9b4c..d2fbe07053aa21e514b3782cd14c109151b02116 100644 --- a/inc/Services/Parameter.hpp +++ b/inc/Services/Parameter.hpp @@ -47,9 +47,7 @@ private: DataType currentValue; public: - Parameter(DataType initialValue) { - currentValue = initialValue; - } + explicit Parameter(DataType initialValue) : currentValue(initialValue) {} inline void setValue(DataType value) { currentValue = value; @@ -59,31 +57,19 @@ public: return currentValue; } - inline void setValueFromMessage(Message& message) override; + /** + * Given an ECSS message that contains this parameter as its first input, this loads the value from that paremeter + */ + inline void setValueFromMessage(Message& message) override { + currentValue = message.read<DataType>(); + }; - inline void appendValueToMessage(Message& message) override; + /** + * Appends the parameter as an ECSS value to an ECSS Message + */ + inline void appendValueToMessage(Message& message) override { + message.append<DataType>(currentValue); + }; }; -template<> inline void Parameter<uint8_t>::setValueFromMessage(Message& message) { - currentValue = message.readUint8(); -} -template<> inline void Parameter<uint16_t>::setValueFromMessage(Message& message) { - currentValue = message.readUint16(); -} - -template<> inline void Parameter<uint32_t>::setValueFromMessage(Message& message) { - currentValue = message.readUint32(); -} - -template<> inline void Parameter<uint8_t>::appendValueToMessage(Message& message) { - message.appendUint8(this->currentValue); -} - -template<> inline void Parameter<uint16_t>::appendValueToMessage(Message& message) { - message.appendUint16(this->currentValue); -} - -template<> inline void Parameter<uint32_t>::appendValueToMessage(Message& message) { - message.appendUint32(this->currentValue); -} #endif //ECSS_SERVICES_PARAMETER_HPP diff --git a/test/Message.cpp b/test/Message.cpp index 8d356b6eafc9ea886067d3c84b5c73f6dcf041e1..4e1d12f56280bd21d2e122bd3544d42e716d03ea 100644 --- a/test/Message.cpp +++ b/test/Message.cpp @@ -1,6 +1,7 @@ #include <catch2/catch.hpp> #include <Message.hpp> #include <ServicePool.hpp> +#include "etl/String.hpp" #include "Services/EventReportService.hpp" TEST_CASE("Message is usable", "[message]") { @@ -63,13 +64,13 @@ TEST_CASE("Requirement 5.3.1", "[message][ecss]") { TEST_CASE("Requirement 7.3.2 (Boolean)", "[message][ecss]") { Message message(0, 0, Message::TC, 0); - message.appendBoolean(false); - message.appendBoolean(true); + message.append<bool>(false); + message.append<bool>(true); REQUIRE(message.dataSize == 2); - CHECK_FALSE(message.readBoolean()); - CHECK(message.readBoolean()); + CHECK_FALSE(message.read<bool>()); + CHECK(message.read<bool>()); } TEST_CASE("Requirement 7.3.3 (Enumerated)", "[message][ecss]") { @@ -93,17 +94,19 @@ TEST_CASE("Requirement 7.3.3 (Enumerated)", "[message][ecss]") { TEST_CASE("Requirement 7.3.4 (Unsigned integer)", "[message][ecss]") { Message message(0, 0, Message::TC, 0); - message.appendUint8(230); - message.appendUint16(15933); - message.appendUint32(2000001); - message.appendUint64(12446744073709551615ULL); + message.append<char>(110); + message.append<uint8_t>(230); + message.append<uint16_t>(15933); + message.append<uint32_t>(2000001); + message.append<uint64_t>(12446744073709551615ULL); - REQUIRE(message.dataSize == 1 + 2 + 4 + 8); + REQUIRE(message.dataSize == 1 + 1 + 2 + 4 + 8); - CHECK(message.readUint8() == 230); - CHECK(message.readUint16() == 15933); - CHECK(message.readUint32() == 2000001); - CHECK(message.readUint64() == 12446744073709551615ULL); + CHECK(message.read<char>() == 110); + CHECK(message.read<uint8_t>() == 230); + CHECK(message.read<uint16_t>() == 15933); + CHECK(message.read<uint32_t>() == 2000001); + CHECK(message.read<uint64_t>() == 12446744073709551615ULL); SECTION("7.4.3") { /** @@ -112,25 +115,25 @@ TEST_CASE("Requirement 7.3.4 (Unsigned integer)", "[message][ecss]") { * processors store data in little endian format. As a result, special care needs to be * taken for compliance. */ - CHECK(message.data[1] == 0x3e); - CHECK(message.data[2] == 0x3d); + CHECK(message.data[2] == 0x3e); + CHECK(message.data[3] == 0x3d); } } TEST_CASE("Requirement 7.3.5 (Signed integer)", "[message][ecss]") { Message message(0, 0, Message::TC, 0); - message.appendSint8(-16); - message.appendSint16(-7009); - message.appendSint32(-2000001); - message.appendSint32(15839011); + message.append<int8_t>(-16); + message.append<int16_t>(-7009); + message.append<int32_t>(-2000001); + message.append<int32_t>(15839011); REQUIRE(message.dataSize == 1 + 2 + 4 + 4); - CHECK(message.readSint8() == -16); - CHECK(message.readSint16() == -7009); - CHECK(message.readSint32() == -2000001); - CHECK(message.readSint32() == 15839011); + CHECK(message.read<int8_t>() == -16); + CHECK(message.read<int16_t>() == -7009); + CHECK(message.read<int32_t>() == -2000001); + CHECK(message.read<int32_t>() == 15839011); SECTION("7.4.3") { // Make sure the endianness of the message data is correct @@ -145,25 +148,29 @@ TEST_CASE("Requirement 7.3.5 (Signed integer)", "[message][ecss]") { TEST_CASE("Requirement 7.3.6 (Real)", "[message][ecss]") { Message message(0, 0, Message::TC, 0); - message.appendFloat(7.209f); - message.appendFloat(-9003.53135f); + message.append<float>(7.209f); + message.append<float>(-9003.53135f); REQUIRE(message.dataSize == 8); - CHECK(message.readFloat() == 7.209f); - CHECK(message.readFloat() == -9003.53135f); + CHECK(message.read<float>() == 7.209f); + CHECK(message.read<float>() == -9003.53135f); } TEST_CASE("Requirement 7.3.8 (Octet-string)", "[message][ecss]") { Message message(0, 0, Message::TC, 0); message.appendString(String<4>("test")); + message.append<etl::istring>(String<4>("gaus")); - REQUIRE(message.dataSize == 4); + REQUIRE(message.dataSize == 4 + 6); char string[5]; message.readCString(string, 4); CHECK_THAT(string, Catch::Matchers::Equals("test")); + + auto output = message.readOctetString<10>(); + CHECK_THAT(output.c_str(), Catch::Matchers::Equals("gaus")); } TEST_CASE("Requirement 7.3.13 (Packet)", "[message][ecss]") { diff --git a/test/Services/Parameter.cpp b/test/Services/Parameter.cpp index 3a9b8efec4994e60d7b21a473df4c50c2a7e3787..837be7643c39cb5021a261723bde9e5bb9d1488f 100644 --- a/test/Services/Parameter.cpp +++ b/test/Services/Parameter.cpp @@ -6,9 +6,9 @@ TEST_CASE("Parameter Append") { SECTION("Check correct appending") { Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, Message::TC, 1); - Parameter<uint8_t> parameter1 = Parameter<uint8_t>(1); - Parameter<uint16_t> parameter2 = Parameter<uint16_t>(500); - Parameter<uint32_t> parameter3 = Parameter<uint32_t>(70000); + auto parameter1 = Parameter<uint8_t>(1); + auto parameter2 = Parameter<uint16_t>(500); + auto parameter3 = Parameter<uint32_t>(70000); parameter1.appendValueToMessage(request); parameter2.appendValueToMessage(request); @@ -23,9 +23,9 @@ TEST_CASE("Parameter Append") { TEST_CASE("Parameter Set") { SECTION("Check correct setting") { Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, Message::TC, 1); - Parameter<uint8_t> parameter1 = Parameter<uint8_t>(1); - Parameter<uint16_t> parameter2 = Parameter<uint16_t>(500); - Parameter<uint32_t> parameter3 = Parameter<uint32_t>(70000); + auto parameter1 = Parameter<uint8_t>(1); + auto parameter2 = Parameter<uint16_t>(500); + auto parameter3 = Parameter<uint32_t>(70000); request.appendUint8(10); request.appendUint16(1000);