diff --git a/CMakeLists.txt b/CMakeLists.txt index 6844fc4ab6b2489b09ab3fb4bbdcb013820089ab..27b0dd6db906e42c31e1bd25944d96f81d10b4a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,15 +15,34 @@ add_custom_target(check # Specify the .cpp files for the executables add_executable(ecss_services src/main.cpp src/Message.cpp src/Service.cpp - src/Services/TestService.cpp src/Services/RequestVerificationService.cpp +<<<<<<< CMakeLists.txt + src/Services/TestService.cpp + src/Services/RequestVerificationService.cpp + src/Services/MemoryManagementService.cpp src/Services/ParameterService.cpp) +======= + src/Services/TestService.cpp + src/Services/RequestVerificationService.cpp) +>>>>>>> CMakeLists.txt -IF (EXISTS "${PROJECT_SOURCE_DIR}/lib/Catch2/CMakeLists.txt") +IF(EXISTS "${PROJECT_SOURCE_DIR}/lib/Catch2/CMakeLists.txt") add_subdirectory(lib/Catch2) add_executable(tests src/Message.cpp src/Services/TestService.cpp - src/Services/RequestVerificationService.cpp src/Services/ParameterService.cpp +<<<<<<< CMakeLists.txt + src/Services/RequestVerificationService.cpp + src/Services/ParameterService.cpp + src/Services/MemoryManagementService.cpp test/tests.cpp - test/Message.cpp test/TestPlatform.cpp test/Services/TestService.cpp - test/Services/RequestVerificationService.cpp test/Services/ParameterService.cpp) + test/Message.cpp + test/TestPlatform.cpp + test/Services/TestService.cpp + test/Services/RequestVerificationService.cpp + test/Services/ParameterService.cpp + test/Services/MemoryManagementService.cpp) +======= + src/Services/RequestVerificationService.cpp + test/tests.cpp test/Message.cpp test/TestPlatform.cpp test/Services/TestService.cpp + test/Services/RequestVerificationService.cpp) +>>>>>>> CMakeLists.txt target_link_libraries(tests Catch2::Catch2) -ENDIF () +ENDIF() diff --git a/ci/cppcheck.sh b/ci/cppcheck.sh index 3b8d4cf3f1ee124eaa6787290a505837e7fefbaf..f23bf34fdc66defa8c3f49d1622ec4d328c5d1a8 100755 --- a/ci/cppcheck.sh +++ b/ci/cppcheck.sh @@ -10,4 +10,4 @@ echo -e "\033[0;34mRunning cppcheck...\033[0m" cd "$(dirname "$0")/.." -cppcheck --enable=all --suppress=unusedFunction --suppress=missingIncludeSystem --error-exitcode=1 -I inc src tests +cppcheck --enable=all --suppress=unusedFunction --suppress=missingIncludeSystem --error-exitcode=1 -I inc src test diff --git a/ci/vera.sh b/ci/vera.sh index b758842ff8b395ee548e6389a8f6b08426fbaf87..470c348bf7e4d999de64b3e58df311a444c87bcc 100755 --- a/ci/vera.sh +++ b/ci/vera.sh @@ -10,4 +10,4 @@ echo -e "\033[0;34mRunning vera++...\033[0m" cd "$(dirname "$0")/.." -vera++ --error --profile custom `find src inc tests -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` +vera++ --error --profile custom `find src inc test -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` diff --git a/inc/Message.hpp b/inc/Message.hpp index 28ba738b09ab3babeb2173514da8704d892a5301..0e311b4e1b796b00cb0bd6294a3b5afd9e5cb651 100644 --- a/inc/Message.hpp +++ b/inc/Message.hpp @@ -4,6 +4,7 @@ #include "ECSS_Definitions.hpp" #include <cstdint> #include <cassert> +#include <iostream> /** * A telemetry (TM) or telecommand (TC) message (request/report), as specified in ECSS-E-ST-70-41C @@ -78,6 +79,16 @@ public: */ void appendString(uint8_t size, const char *value); + /** + * Appends \p size bytes to the message + * + * @param size The amount of byte to append + * @param value An array containing at least \p size bytes + * @todo See if more than uint8_t strings will be supported + * @todo Is uint16_t size too much or not enough? It has to be defined + */ + void appendString(uint16_t size, uint8_t *value); + /** * Reads the next \p numBits bits from the the message in a big-endian format * @param numBits @@ -108,6 +119,15 @@ public: */ void readString(char *string, uint8_t size); + /** + * Reads the next \p size bytes from the message, and stores them into the allocated \p string + * + * NOTE: We assume that \p string is already allocated, and its size is at least + * ECSS_MAX_STRING_SIZE. This function does placs a \0 at the end of the created string + * @todo Is uint16_t size too much or not enough? It has to be defined + */ + void readString(uint8_t *string, uint16_t size); + public: Message(uint8_t serviceType, uint8_t messageType, PacketType packetType, uint16_t applicationId); @@ -187,6 +207,16 @@ public: return appendWord(value); } + /** + * Adds an 8 byte unsigned integer to the end of the message + * + * PTC = 3, PFC = 16 + */ + void appendUint64(uint64_t value) { + appendWord(static_cast<uint32_t >(value >> 32)); + appendWord(static_cast<uint32_t >(value)); + } + /** * Adds a 1 byte signed integer to the end of the message * @@ -226,6 +256,17 @@ public: return appendWord(reinterpret_cast<uint32_t &>(value)); } + /** + * Adds a N-byte string to the end of the message + * + * + * PTC = 7, PFC = 0 + */ + void appendOctetString(uint16_t size, uint8_t *byteString) { + appendUint16(size); + appendString(size, byteString); + } + /** * Fetches a single-byte boolean value from the current position in the message * @@ -301,6 +342,15 @@ public: return readWord(); } + /** + * Fetches an 8-byte unsigned integer from the current position in the message + * + * PTC = 3, PFC = 16 + */ + uint64_t readUint64() { + return (static_cast<uint64_t >(readWord()) << 32) | static_cast<uint64_t >(readWord()); + } + /** * Fetches an 1-byte signed integer from the current position in the message * @@ -347,6 +397,22 @@ public: return reinterpret_cast<float &>(value); } + /** + * Fetches a N-byte string from the current position in the message + * + * @details In the current implementation we assume that a preallocated array of + * sufficient size is provided as the argument + * @todo Specify if the provided array size is too small or too large + * + * PTC = 7, PFC = 0 + */ + uint16_t readOctetString(uint8_t *byteString) { + uint16_t size = readUint16(); // Get the data length from the message + readString(byteString, size); // Read the string data + + return size; // Return the string size + } + /** * Reset the message reading status, and start reading data from it again */ diff --git a/inc/Services/MemoryManagementService.hpp b/inc/Services/MemoryManagementService.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1d2de9be32b3589b84e84b07007f05380e81a062 --- /dev/null +++ b/inc/Services/MemoryManagementService.hpp @@ -0,0 +1,55 @@ +#ifndef ECSS_SERVICES_MEMMANGSERVICE_HPP +#define ECSS_SERVICES_MEMMANGSERVICE_HPP + +#include "Service.hpp" +#include <memory> +#include <iostream> + +class MemoryManagementService : public Service { +public: + // Memory type ID's + enum MemoryID { + RAM = 0, + FLASH = 1, + EXTERNAL = 2 + }; + + MemoryManagementService(); + + /** + * Raw data memory management subservice class + * + * @details A class defining the raw data memory management subservice functions. + * As per the ECSS manual, each memory service has to have at most one raw memory + * data management subservice + */ + class RawDataMemoryManagement { + private: + MemoryManagementService &mainService; // Used to access main class's members + + public: + explicit RawDataMemoryManagement(MemoryManagementService &parent); + + /** + * TC[6,2] load raw values to memory + * + * @details This function loads new values to memory data areas + * specified in the request + * @param request: Provide the received message as a parameter + */ + void loadRawData(Message &request); + + /** + * TC[6,5] read raw memory values + * + * @details This function reads the raw data from the RAM memory and + * triggers a TM[6,6] report + * @param request: Provide the received message as a parameter + * @todo In later embedded version, implement error checking for address validity for + * different memory types + */ + void dumpRawData(Message &request); + } rawDataMemorySubservice; +}; + +#endif //ECSS_SERVICES_MEMMANGSERVICE_HPP diff --git a/lib/Catch2 b/lib/Catch2 index 62460fafe6b54c3173bc5cbc46d05a5f071017ff..77f29c2f1cde8bd2e17f06cc04092b990d2acc2c 160000 --- a/lib/Catch2 +++ b/lib/Catch2 @@ -1 +1 @@ -Subproject commit 62460fafe6b54c3173bc5cbc46d05a5f071017ff +Subproject commit 77f29c2f1cde8bd2e17f06cc04092b990d2acc2c diff --git a/src/Message.cpp b/src/Message.cpp index 15baf5f217098562171fc6ff63a0705b0e212f7c..60259a597339b14b686352bd67f4eb1ff8f1eb0d 100644 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -73,6 +73,15 @@ void Message::appendString(uint8_t size, const char *value) { dataSize += size; } +void Message::appendString(uint16_t size, uint8_t *value) { + assert(dataSize + size <= ECSS_MAX_MESSAGE_SIZE); + assert(size < ECSS_MAX_STRING_SIZE); + + memcpy(data + dataSize, value, size); + + dataSize += size; +} + uint16_t Message::readBits(uint8_t numBits) { assert(numBits <= 16); // TODO: Add assert @@ -134,7 +143,16 @@ void Message::readString(char *string, uint8_t size) { assert(size < ECSS_MAX_STRING_SIZE); memcpy(string, data + readPosition, size); - string[size] = '\0'; + string[size] = '\0'; // todo: Use that for now to avoid problems. Later to be removed + + readPosition += size; +} + +void Message::readString(uint8_t *string, uint16_t size) { + assert(readPosition + size <= ECSS_MAX_MESSAGE_SIZE); + assert(size < ECSS_MAX_STRING_SIZE); + + memcpy(string, data + readPosition, size); readPosition += size; } diff --git a/src/Services/MemoryManagementService.cpp b/src/Services/MemoryManagementService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd84b3dd01df44dac75852009ec90c587c500889 --- /dev/null +++ b/src/Services/MemoryManagementService.cpp @@ -0,0 +1,88 @@ +#include "Services/MemoryManagementService.hpp" +#include <iostream> +#include <cerrno> + +// Define the constructors for the classes +MemoryManagementService::MemoryManagementService() : rawDataMemorySubservice(*this) { + serviceType = 6; +} + +MemoryManagementService::RawDataMemoryManagement::RawDataMemoryManagement( + MemoryManagementService &parent) : mainService(parent) {} + + +// Function declarations for the raw data memory management subservice +void MemoryManagementService::RawDataMemoryManagement::loadRawData(Message &request) { + /** + * Bare in mind that there is currently no error checking for invalid parameters. + * A future version will include error checking and the corresponding error report/notification, + * as the manual implies. + * + * @todo Add error checking and reporting for the parameters + * @todo Add failure reporting + */ + // Check if we have the correct packet + assert(request.serviceType == 6); + assert(request.messageType == 2); + + // Variable declaration + uint8_t readData[ECSS_MAX_STRING_SIZE]; // Preallocate the array + + uint8_t memoryID = request.readEnum8(); // Read the memory ID from the request + uint16_t iterationCount = request.readUint16(); // Get the iteration count + + if (memoryID == MemoryManagementService::MemoryID::RAM) { + for (std::size_t j = 0; j < iterationCount; j++) { + uint64_t startAddress = request.readUint64(); // Start address of the memory + uint16_t dataLength = request.readOctetString(readData); // Data length to load + // todo: Error logging has to be included, if memory allocation above fails + // todo: Continue only if the checksum passes (when the checksum will be implemented) + + for (std::size_t i = 0; i < dataLength; i++) { + *(reinterpret_cast<uint8_t *>(startAddress) + i) = readData[i]; + } + } + } else if (memoryID == MemoryManagementService::MemoryID::FLASH) { + // todo: Define FLASH specific access code when we transfer to embedded + } +} + +void MemoryManagementService::RawDataMemoryManagement::dumpRawData(Message &request) { + // Check if we have the correct packet + assert(request.serviceType == 6); + assert(request.messageType == 5); + + // Create the report message object of telemetry message subtype 6 + Message report = mainService.createTM(6); + + // Variable declaration + uint8_t readData[ECSS_MAX_STRING_SIZE]; // Preallocate the array + + uint8_t memoryID = request.readEnum8(); // Read the memory ID from the request + // todo: Add checks depending on the memory type + + uint16_t iterationCount = request.readUint16(); // Get the iteration count + + // Append the data to report message + report.appendEnum8(memoryID); // Memory ID + report.appendUint16(iterationCount); // Iteration count + + // Iterate N times, as specified in the command message + for (std::size_t j = 0; j < iterationCount; j++) { + uint64_t startAddress = request.readUint64(); // Data length to read + uint16_t readLength = request.readUint16(); // Start address for the memory read + + // Read memory data, an octet at a time + for (std::size_t i = 0; i < readLength; i++) { + readData[i] = *(reinterpret_cast<uint8_t *>(startAddress) + i); + } + + // This part is repeated N-times (N = iteration count) + report.appendUint64(startAddress); // Start address + report.appendOctetString(readLength, readData); // Save the read data + } + // todo: implement and append the checksum part of the reporting packet + + mainService.storeMessage(report); // Save the report message + request.resetRead(); // Reset the reading count +} diff --git a/src/main.cpp b/src/main.cpp index f22b6bf5de37b2dad2172aa6bf662c57b05544cc..206ee15c1529693d0efc5b0cabbf2504a4075129 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include "Services/ParameterService.hpp" #include "Services/RequestVerificationService.hpp" #include "Message.hpp" +#include "Services/MemoryManagementService.hpp" int main() { Message packet = Message(0, 0, Message::TC, 1); @@ -30,6 +31,7 @@ int main() { receivedPacket.appendUint16(7); testService.onBoardConnection(receivedPacket); +<<<<<<< src/main.cpp //ST[20] test ParameterService paramService; @@ -52,7 +54,43 @@ int main() { paramService.setParameterIds(sentPacket2); paramService.reportParameterIds(sentPacket); -// ST[01] test +// ST[06] testing + char anotherStr[8] = "Fgthred"; + char yetAnotherStr[2] = "F"; + char *pStr = static_cast<char *>(malloc(4)); + *pStr = 'T'; + *(pStr + 1) = 'G'; + *(pStr + 2) = '\0'; + + MemoryManagementService memMangService; + Message rcvPack = Message(6, 5, Message::TC, 1); + rcvPack.appendEnum8(MemoryManagementService::MemoryID::RAM); // Memory ID + rcvPack.appendUint16(3); // Iteration count + rcvPack.appendUint64(reinterpret_cast<uint64_t >(string)); // Start address + rcvPack.appendUint16(sizeof(string)/ sizeof(string[0])); // Data read length + + rcvPack.appendUint64(reinterpret_cast<uint64_t >(anotherStr)); + rcvPack.appendUint16(sizeof(anotherStr)/ sizeof(anotherStr[0])); + + rcvPack.appendUint64(reinterpret_cast<uint64_t >(yetAnotherStr)); + rcvPack.appendUint16(sizeof(yetAnotherStr)/ sizeof(yetAnotherStr[0])); + memMangService.rawDataMemorySubservice.dumpRawData(rcvPack); + + rcvPack = Message(6, 2, Message::TC, 1); + + uint8_t data[2] = {'h', 'R'}; + rcvPack.appendEnum8(MemoryManagementService::MemoryID::RAM); // Memory ID + rcvPack.appendUint16(2); // Iteration count + rcvPack.appendUint64(reinterpret_cast<uint64_t >(pStr)); // Start address + rcvPack.appendOctetString(2, data); + rcvPack.appendUint64(reinterpret_cast<uint64_t >(pStr + 1)); // Start address + rcvPack.appendOctetString(1, data); + memMangService.rawDataMemorySubservice.loadRawData(rcvPack); +======= + + + // ST[01] test +>>>>>>> src/main.cpp // parameters take random values and works as expected RequestVerificationService reqVerifService; reqVerifService.successAcceptanceVerification(Message::TC, true, 2, 2, 10); diff --git a/test/Message.cpp b/test/Message.cpp index 4a745e04e5c1bdf8cf449996f5bff135fb1312bf..dce148d340fff2b12dc09fabebc86608d3200493 100644 --- a/test/Message.cpp +++ b/test/Message.cpp @@ -96,12 +96,14 @@ TEST_CASE("Requirement 7.3.4 (Unsigned integer)", "[message][ecss]") { message.appendUint8(230); message.appendUint16(15933); message.appendUint32(2000001); + message.appendUint64(12446744073709551615ULL); - REQUIRE(message.dataSize == 1 + 2 + 4); + REQUIRE(message.dataSize == 1 + 2 + 4 + 8); CHECK(message.readUint8() == 230); CHECK(message.readUint16() == 15933); CHECK(message.readUint32() == 2000001); + CHECK(message.readUint64() == 12446744073709551615ULL); SECTION("7.4.3") { /** diff --git a/test/Services/MemoryManagementService.cpp b/test/Services/MemoryManagementService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d59c8de2c6490d99b9113904a08ce313dc25ef26 --- /dev/null +++ b/test/Services/MemoryManagementService.cpp @@ -0,0 +1,87 @@ +#include <catch2/catch.hpp> +#include <Services/MemoryManagementService.hpp> +#include <Message.hpp> +#include "ServiceTests.hpp" + +TEST_CASE("TM[6,2]", "[service][st06]") { + // Required test variables + char *pStr = static_cast<char *>(malloc(4)); + *pStr = 'T'; + *(pStr + 1) = 'G'; + *(pStr + 2) = '\0'; + uint8_t data[2] = {'h', 'R'}; + + MemoryManagementService memMangService; + + Message receivedPacket = Message(6, 2, Message::TC, 1); + receivedPacket.appendEnum8(MemoryManagementService::MemoryID::RAM); // Memory ID + receivedPacket.appendUint16(2); // Iteration count + receivedPacket.appendUint64(reinterpret_cast<uint64_t >(pStr)); // Start address + receivedPacket.appendOctetString(2, data); + receivedPacket.appendUint64(reinterpret_cast<uint64_t >(pStr + 2)); // Start address + receivedPacket.appendOctetString(1, data); + memMangService.rawDataMemorySubservice.loadRawData(receivedPacket); + + CHECK(pStr[0] == 'h'); + CHECK(pStr[1] == 'R'); + CHECK(pStr[2] == 'h'); +} + +TEST_CASE("TM[6,5]", "[service][st06]") { + uint8_t testString_1[6] = "FStrT"; + uint8_t testString_2[8] = "SecStrT"; + uint8_t testString_3[2] = {5, 8}; + + uint8_t checkString[ECSS_MAX_STRING_SIZE]; + uint16_t readSize = 0; + + MemoryManagementService memMangService; + Message receivedPacket = Message(6, 5, Message::TC, 1); + receivedPacket.appendEnum8(MemoryManagementService::MemoryID::RAM); // Memory ID + receivedPacket.appendUint16(3); // Iteration count (Equal to 3 test strings) + receivedPacket.appendUint64(reinterpret_cast<uint64_t >(testString_1)); // Start address + receivedPacket.appendUint16(sizeof(testString_1)/ sizeof(testString_1[0])); // Data read length + + receivedPacket.appendUint64(reinterpret_cast<uint64_t >(testString_2)); + receivedPacket.appendUint16(sizeof(testString_2)/ sizeof(testString_2[0])); + + receivedPacket.appendUint64(reinterpret_cast<uint64_t >(testString_3)); + receivedPacket.appendUint16(sizeof(testString_3)/ sizeof(testString_3[0])); + memMangService.rawDataMemorySubservice.dumpRawData(receivedPacket); + REQUIRE(ServiceTests::hasOneMessage()); + + Message response = ServiceTests::get(0); + CHECK(response.serviceType == 6); + CHECK(response.messageType == 6); + REQUIRE(response.dataSize == 49); + + CHECK(response.readEnum8() == MemoryManagementService::MemoryID::RAM); + CHECK(response.readUint16() == 3); + CHECK(response.readUint64() == reinterpret_cast<uint64_t >(testString_1)); + readSize = response.readOctetString(checkString); + CHECK(readSize == sizeof(testString_1)/ sizeof(testString_1[0])); + CHECK(checkString[0] == 'F'); + CHECK(checkString[1] == 'S'); + CHECK(checkString[2] == 't'); + CHECK(checkString[3] == 'r'); + CHECK(checkString[4] == 'T'); + CHECK(checkString[5] == '\0'); + + CHECK(response.readUint64() == reinterpret_cast<uint64_t >(testString_2)); + readSize = response.readOctetString(checkString); + CHECK(readSize == sizeof(testString_2)/ sizeof(testString_2[0])); + CHECK(checkString[0] == 'S'); + CHECK(checkString[1] == 'e'); + CHECK(checkString[2] == 'c'); + CHECK(checkString[3] == 'S'); + CHECK(checkString[4] == 't'); + CHECK(checkString[5] == 'r'); + CHECK(checkString[6] == 'T'); + CHECK(checkString[7] == '\0'); + + CHECK(response.readUint64() == reinterpret_cast<uint64_t >(testString_3)); + readSize = response.readOctetString(checkString); + CHECK(readSize == sizeof(testString_3)/ sizeof(testString_3[0])); + CHECK(checkString[0] == 5); + CHECK(checkString[1] == 8); +} diff --git a/test/Services/RequestVerificationService.cpp b/test/Services/RequestVerificationService.cpp index 6c9e3c8ccda90deb72c48e62f585f6dd9394a7ba..ebb97762346f5c03b07a82052e4e1baeca928d46 100644 --- a/test/Services/RequestVerificationService.cpp +++ b/test/Services/RequestVerificationService.cpp @@ -110,4 +110,3 @@ TEST_CASE("TM[1,10]", "[service][st01]") { CHECK(response.readBits(14) == 10); // packet sequence count CHECK(response.readEnum16() == 7); // error code } -