diff --git a/CMakeLists.txt b/CMakeLists.txt index 40f819b605d47ef1805b71809de9e16eb927f141..91e767a62033766d2b6104eb2e0907f86e9981b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,14 +15,19 @@ 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/MemoryManagementService.cpp - src/Services/RequestVerificationService.cpp) + src/Services/TestService.cpp src/Services/RequestVerificationService.cpp + src/Services/MemoryManagementService.cpp src/Services/ParameterService.cpp + src/Services/TestService.cpp + src/Services/RequestVerificationService.cpp) 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/MemoryManagementService.cpp - test/tests.cpp test/Message.cpp test/TestPlatform.cpp test/Services/TestService.cpp - test/Services/MemoryManagementService.cpp test/Services/RequestVerificationService.cpp) + add_executable(tests src/Message.cpp src/Services/TestService.cpp + 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/Services/MemoryManagementService.cpp) + target_link_libraries(tests Catch2::Catch2) ENDIF() diff --git a/inc/Service.hpp b/inc/Service.hpp index 55aa5f83a4964eafef4e37ba0bc15efbd30cb9fa..3afd67fa73d6ce148555bd03ead9a8743666364c 100644 --- a/inc/Service.hpp +++ b/inc/Service.hpp @@ -16,7 +16,7 @@ class Service { private: uint16_t messageTypeCounter = 0; protected: - uint8_t serviceType; + uint8_t serviceType{}; /** * Creates a new empty telemetry package originating from this service diff --git a/inc/Services/ParameterService.hpp b/inc/Services/ParameterService.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c331ae2743d7ffbd4e137d043889515f491d0372 --- /dev/null +++ b/inc/Services/ParameterService.hpp @@ -0,0 +1,89 @@ +#ifndef ECSS_SERVICES_PARAMETERSERVICE_HPP +#define ECSS_SERVICES_PARAMETERSERVICE_HPP + +#include "Service.hpp" +// #include "Services/RequestVerificationService.hpp" + +#define CONFIGLENGTH 5 + +/** + * Implementation of the ST[20] parameter management service, + * as defined in ECSS-E-ST-70-41C + * + * @author Grigoris Pavlakis <grigpavl@ece.auth.gr> + */ + +/** + * Generic parameter structure + * PTC and PFC for each parameter shall be specified as in + * ECSS-E-ST-70-41C, chapter 7.3 + */ +struct Parameter { + uint8_t ptc; // Packet field type code (PTC) + uint8_t pfc; // Packet field format code (PFC) + uint16_t paramId; // Unique ID of the parameter + + uint32_t settingData; + // Actual data defining the operation of a peripheral or subsystem. + // Peripheral-dependent normally (void* maybe?) (it's a memory address according to spec). + // Dummy int for now. +}; + +/** + * Parameter manager - ST[20] + * Holds the list with the parameters and provides functions + * for parameter reporting and modification. + * + * @todo Ensure that the parameter list is sorted by ID + */ + +class ParameterService : public Service { +private: + Parameter paramsList[CONFIGLENGTH]; + // CONFIGLENGTH is just a dummy number for now, this should be statically set + static uint16_t numOfValidIds(Message idMsg); //count the valid ids in a given TC[20, 1] + +public: + /** + * Initializes the parameter list with some dummy values for now. + */ + ParameterService(); + + /** + * This function receives a TC[20, 1] packet and returns a TM[20, 2] packet + * containing the current configuration + * **for the parameters specified in the carried valid IDs**. + * + * No sophisticated error checking for now, just whether the package is of the correct type + * and whether the requested IDs are valid, ignoring the invalid ones. If no IDs are correct, + * the returned message shall be empty. + * + * @param paramId: a valid TC[20, 1] packet carrying the requested parameter IDs + * @return None (messages are stored using storeMessage()) + * + * @todo Generate failure notifs where needed when ST[01] is ready + * + * NOTES: + * Method for valid ID counting is a hack (clones the message and figures out the number + * separately, due to message access being non-random). Should be enough for now. + * + * Everything apart from the setting data is uint16 (setting data are uint32 for now) + */ + void reportParameterIds(Message paramIds); + + /** + * This function receives a TC[20, 3] message and after checking whether its type is correct, + * iterates over all contained parameter IDs and replaces the settings for each valid parameter, + * while ignoring all invalid IDs. + * + * @param newParamValues: a valid TC[20, 3] message carrying parameter ID and replacement value + * @return None + * + * @todo Generate failure notifications where needed (eg. when an invalid ID is encountered) + * @todo Use pointers for changing and storing addresses to comply with the standard + */ + void setParameterIds(Message newParamValues); + +}; + +#endif //ECSS_SERVICES_PARAMETERSERVICE_HPP diff --git a/src/Services/ParameterService.cpp b/src/Services/ParameterService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf19881992e6265b8254ae0c09bc118a665a780f --- /dev/null +++ b/src/Services/ParameterService.cpp @@ -0,0 +1,94 @@ +#include "Services/ParameterService.hpp" + +#define DEMOMODE + +#ifdef DEMOMODE + +#include <ctime> +#include <cstdlib> + +#endif + +ParameterService::ParameterService() { +#ifdef DEMOMODE + // Test code, setting up some of the parameter fields + + time_t currTime = time(nullptr); + struct tm *today = localtime(&currTime); + + paramsList[0].paramId = 0; // random parameter ID + paramsList[0].settingData = today->tm_hour; // the current hour + paramsList[0].ptc = 3; // unsigned int + paramsList[0].pfc = 14; // 32 bits + + paramsList[1].paramId = 1; // random parameter ID + paramsList[1].settingData = today->tm_min; // the current minute + paramsList[1].ptc = 3; // unsigned int + paramsList[1].pfc = 14; // 32 bits +#endif +} + +void ParameterService::reportParameterIds(Message paramIds) { + Message reqParam(20, 2, Message::TM, 1); // empty TM[20, 2] parameter report message + + if (paramIds.packetType == Message::TC && paramIds.serviceType == 20 && + paramIds.messageType == 1) { + uint16_t ids = paramIds.readUint16(); + reqParam.appendUint16(numOfValidIds(paramIds)); // include the number of valid IDs + + for (int i = 0; i < ids; i++) { + uint16_t currId = paramIds.readUint16(); // current ID to be appended + + if (currId < CONFIGLENGTH) { // check to prevent out-of-bounds access due to invalid id + reqParam.appendUint16(currId); + reqParam.appendUint32(paramsList[currId].settingData); + } else { + // generate failure of execution notification for ST[06] + continue; //ignore the invalid ID + } + } + } + + storeMessage(reqParam); +} + +void ParameterService::setParameterIds(Message newParamValues) { + if (newParamValues.packetType == Message::TC && newParamValues.serviceType == 20 && + newParamValues.messageType == 3) { + uint16_t ids = newParamValues.readUint16(); //get number of ID's + + for (int i = 0; i < ids; i++) { + uint16_t currId = newParamValues.readUint16(); + + if (currId < CONFIGLENGTH) { + paramsList[currId].settingData = newParamValues.readUint32(); + } else { + // generate failure of execution notification for ST[06] + continue; // ignore the invalid ID + } + } + } +} + +uint16_t ParameterService::numOfValidIds(Message idMsg) { + idMsg.resetRead(); + // start reading from the beginning of the idMsg object + // (original obj. will not be influenced if this is called by value) + + uint16_t ids = idMsg.readUint16(); // first 16bits of the packet are # of IDs + uint16_t validIds = 0; + + for (int i = 0; i < ids; i++) { + uint16_t currId = idMsg.readUint16(); + + if (idMsg.messageType == 3) { + idMsg.readUint32(); //skip the 32bit settings blocks, we need only the IDs + } + + if (currId < CONFIGLENGTH) { + validIds++; + } + } + + return validIds; +} diff --git a/src/main.cpp b/src/main.cpp index a65e88ca40567509f00b257cadd5445286980ef2..d0f0ff29bc763ded60421813adf2cd78dc7c8959 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include <iostream> -#include <Services/TestService.hpp> -#include <Services/RequestVerificationService.hpp> +#include "Services/TestService.hpp" +#include "Services/ParameterService.hpp" +#include "Services/RequestVerificationService.hpp" #include "Message.hpp" #include "Services/MemoryManagementService.hpp" @@ -30,7 +31,29 @@ int main() { receivedPacket.appendUint16(7); testService.onBoardConnection(receivedPacket); - // ST[06] testing + + // ST[20] test + ParameterService paramService; + + // Test code for reportParameter + Message sentPacket = Message(20, 1, Message::TC, 1); //application id is a dummy number (1) + sentPacket.appendUint16(2); //number of contained IDs + sentPacket.appendUint16(0); //first ID + sentPacket.appendUint16(1); //second ID + paramService.reportParameterIds(sentPacket); + + // Test code for setParameter + Message sentPacket2 = Message(20, 3, Message::TC, 1); //application id is a dummy number (1) + sentPacket2.appendUint16(2); //number of contained IDs + sentPacket2.appendUint16(0); //first parameter ID + sentPacket2.appendUint32(63238); //settings for first parameter + sentPacket2.appendUint16(1); //2nd parameter ID + sentPacket2.appendUint32(45823); //settings for 2nd parameter + + paramService.setParameterIds(sentPacket2); + paramService.reportParameterIds(sentPacket); + +// ST[06] testing char anotherStr[8] = "Fgthred"; char yetAnotherStr[2] = "F"; char *pStr = static_cast<char *>(malloc(4)); @@ -62,6 +85,7 @@ int main() { rcvPack.appendUint64(reinterpret_cast<uint64_t >(pStr + 1)); // Start address rcvPack.appendOctetString(1, data); memMangService.rawDataMemorySubservice.loadRawData(rcvPack); + // ST[01] test // parameters take random values and works as expected @@ -71,6 +95,5 @@ int main() { reqVerifService.successExecutionVerification(Message::TC, true, 2, 2, 10); reqVerifService.failExecutionVerification(Message::TC, true, 2, 2, 10, 6); reqVerifService.failRoutingVerification(Message::TC, true, 2, 2, 10, 7); - return 0; } diff --git a/test/Services/ParameterService.cpp b/test/Services/ParameterService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dbf1820b51e3d1d13f13f70e5f3eb7e139289894 --- /dev/null +++ b/test/Services/ParameterService.cpp @@ -0,0 +1,77 @@ +#include "catch2/catch.hpp" +#include "Services/ParameterService.hpp" +#include "Message.hpp" +#include "ServiceTests.hpp" + +#define CATCH_CONFIG_MAIN + +TEST_CASE("Parameter Report Subservice") { + ParameterService pserv; + + SECTION("Faulty Instruction Handling Test") { + Message request(20, 1, Message::TC, 1); + Message report(20, 2, Message::TM, 1); + + request.appendUint16(2); // number of requested IDs + request.appendUint16(34672); // faulty ID in this context + request.appendUint16(3); // valid + + pserv.reportParameterIds(request); + report = ServiceTests::get(0); + request.resetRead(); + + report.readUint16(); + request.readUint16(); + + while (report.readPosition <= report.dataSize) { + CHECK_FALSE(report.readUint16() == 34672); //fail if faulty ID is present in report + report.readUint32(); //ignore the carried settings + } + } + + SECTION("Wrong Message Type Handling Test") { + Message falseRequest(15, 3, Message::TM, 1); // a totally wrong message + + pserv.reportParameterIds(falseRequest); + Message response = ServiceTests::get(0); + CHECK(response.messageType == 2); + CHECK(response.serviceType == 20); + CHECK(response.packetType == Message::TM); + CHECK(response.readPosition == 0); // if empty, this should't change from 0 + } +} + +TEST_CASE("Parameter Setting Subservice") { + ParameterService pserv; + + SECTION("Faulty Instruction Handling Test") { + Message setRequest(20, 3, Message::TC, 1); + Message reportRequest(20, 1, Message::TC, 1); + + setRequest.appendUint16(2); // correct number of IDs + setRequest.appendUint16(3); // correct ID + setRequest.appendUint32(3735928559); // 0xDEADBEEF in hex (new setting) + setRequest.appendUint16(16742); // faulty ID in this context + setRequest.appendUint32(3131746989); // 0xBAAAAAAD (this shouldn't be found in the report) + + reportRequest.appendUint16(2); + reportRequest.appendUint16(16742); + reportRequest.appendUint16(3); + + pserv.reportParameterIds(reportRequest); + Message before = ServiceTests::get(0); + + pserv.setParameterIds(setRequest); + + pserv.reportParameterIds(reportRequest); + Message after = ServiceTests::get(1); + + before.readUint16(); + after.readUint16(); // skip the number of IDs + + while (after.readPosition <= after.dataSize) { + CHECK(before.readUint16() == after.readUint16()); // check if all IDs are present + CHECK_FALSE(after.readUint32() == 0xBAAAAAAD); // fail if any settings are BAAAAAAD :P + } + } +}