diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ba3de72d540afb9266a558792676bd60953e005..0b046fbdf84bdb2de8dcf8f30a35096a515b4f73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ add_library(common OBJECT src/Services/TimeManagementService.cpp src/Services/TimeBasedSchedulingService.cpp src/Services/EventActionService.cpp + src/Services/FunctionManagementService.cpp ) # Specify the .cpp files for the executables diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp index a378b93badfc49086ce15de24f415e575a68e3ac..4f50837588ce77417bc2818c2093b675a9bfd267 100644 --- a/inc/ErrorHandler.hpp +++ b/inc/ErrorHandler.hpp @@ -69,7 +69,11 @@ public: /** * A function received a Message that was not of the correct type */ - OtherMessageType = 9, + OtherMessageType = 9, + /** + * Attempt to insert new function in a full function map (ST[08]) + */ + FunctionMapFull = 10, }; /** diff --git a/inc/Helpers/CRCHelper.hpp b/inc/Helpers/CRCHelper.hpp index 12da516b2d6718d6743b17779c2fcf01dec178cd..aa7149846b762498ac4e13979afbbe215119b3bd 100644 --- a/inc/Helpers/CRCHelper.hpp +++ b/inc/Helpers/CRCHelper.hpp @@ -6,7 +6,7 @@ class CRCHelper { /** - * CRC32 calculation helper class + * CRC16 calculation helper class * This class declares a function which calculates the CRC16 checksum of the given data. * * For now the actual implementation is the CRC16/CCITT variant (ECSS-E-ST-70-41C, pg.615) @@ -25,7 +25,7 @@ public: * Actual CRC calculation function. * @param message (pointer to the data to be checksummed) * @param length (size in bytes) - * @return the CRC32 checksum of the input data + * @return the CRC16 checksum of the input data */ static uint16_t calculateCRC(const uint8_t* message, uint32_t length); diff --git a/inc/ServicePool.hpp b/inc/ServicePool.hpp index 8a1e6035855eea8b35003ff37047ac4bb1b01f06..d928606dea82a53552f14b999559f9b6b9bb61f6 100644 --- a/inc/ServicePool.hpp +++ b/inc/ServicePool.hpp @@ -8,6 +8,7 @@ #include "Services/ParameterService.hpp" #include "Services/TestService.hpp" #include "Services/MemoryManagementService.hpp" +#include "Services/FunctionManagementService.hpp" /** * Defines a class that contains instances of all Services. @@ -25,6 +26,7 @@ public: EventActionService eventAction; TestService testService; ParameterService parameterManagement; + FunctionManagementService functionManagement; /** * The default ServicePool constructor diff --git a/inc/Services/FunctionManagementService.hpp b/inc/Services/FunctionManagementService.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2267f7ea112afabefbb97adce73d4844ad40a883 --- /dev/null +++ b/inc/Services/FunctionManagementService.hpp @@ -0,0 +1,95 @@ +#ifndef ECSS_SERVICES_FUNCTIONMANAGEMENTSERVICE_HPP +#define ECSS_SERVICES_FUNCTIONMANAGEMENTSERVICE_HPP + +#include "etl/map.h" +#include "etl/String.hpp" +#include "Message.hpp" +#include "Service.hpp" +#include "ErrorHandler.hpp" + +#define FUNC_MAP_SIZE 5 // size of the function map (number of elements) +#define FUNC_NAME_LENGTH 32 // max length of the function name +#define MAX_ARG_LENGTH 32 // maximum argument byte string length + +/** + * Implementation of the ST[08] function management service + * + * This class implements a skeleton framework for the ST[08] service as described in + * ECSS-E-ST-70-41C, pages 157-159. Final implementation is dependent on subsystem requirements + * which are, as of this writing, undefined yet. + * + * Caveats: + * 1) Function names shall be exactly MAXFUNCNAMELENGTH-lengthed in order to be properly read + * and stored! (not sure if this is a caveat though, as ECSS-E-ST-70-41C stipulates for ST[08] + * that all function names must be fixed-length character strings) + * + * You have been warned. + * + * @author Grigoris Pavlakis <grigpavl@ece.auth.gr> + */ + + /** + * Usage of the include() function: + * + * @code + * void foo(String<MAX_ARG_LENGTH> b) { + * std::cout << "SPAAAACE!" << std::endl; + * } + * + * void bar(String<MAX_ARG_LENGTH> b) { + * std::cout << "I HAZ A CUBESAT THAT SNAPS PIX!" << std::endl; + * } + * + * void baz(String<MAX_ARG_LENGTH> b) { + * std::cout << "QWERTYUIOP" << std::endl; + * } + * + * FunctionManagementService::FunctionManagementService() { + * include(String<FUNC_NAME_LENGTH>("foo"), &foo); + * include(String<FUNC_NAME_LENGTH>("bar"), &bar); + * include(String<FUNC_NAME_LENGTH>("baz"), &baz); + * } + */ + +typedef String<FUNC_NAME_LENGTH> functionName; +typedef etl::map<functionName, void(*)(String<MAX_ARG_LENGTH>), FUNC_MAP_SIZE> +FunctionMap; + +class FunctionManagementService : public Service { + /** + * Map of the function names to their respective pointers. Size controlled by FUNC_MAP_SIZE + */ + FunctionMap funcPtrIndex; + + +public: + /** + * Constructs the function pointer index with all the necessary functions at initialization time + * These functions need to be in scope. Un-default when needed. + * + * @param None + */ + FunctionManagementService() = default; + + /** + * Calls the function described in the TC[8,1] message *msg*, passing the arguments contained + * and, if non-existent, generates a failed start of execution notification. Returns an unneeded + * int, for testing purposes. + * @param msg A TC[8,1] message + */ + void call(Message& msg); + + /** + * Includes a new function in the pointer map. This enables it to be called by way of a valid + * TC [8,1] message. + * + * @param funcName the function's name. Max. length is FUNC_NAME_LENGTH bytes. + * @param ptr pointer to a function of void return type and a MAX_ARG_LENGTH-lengthed byte + * string as argument (which contains the actual arguments of the function) + */ + void include(String<FUNC_NAME_LENGTH> funcName, void(*ptr)(String<MAX_ARG_LENGTH>)); + + int getMapSize() { return funcPtrIndex.size(); } +}; + +#endif //ECSS_SERVICES_FUNCTIONMANAGEMENTSERVICE_HPP diff --git a/inc/etl/String.hpp b/inc/etl/String.hpp index 8fbd401cc4eb033cce095feb13d4fa577aa7d013..f9198df199888453fa690067020528ce9c2d1a36 100644 --- a/inc/etl/String.hpp +++ b/inc/etl/String.hpp @@ -1,7 +1,6 @@ #ifndef ECSS_SERVICES_ETL_STRING_HPP #define ECSS_SERVICES_ETL_STRING_HPP - #include <cstddef> #include <etl/cstring.h> diff --git a/src/Services/FunctionManagementService.cpp b/src/Services/FunctionManagementService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8dcc602792b45199d17bf95e53330905ebcfaab0 --- /dev/null +++ b/src/Services/FunctionManagementService.cpp @@ -0,0 +1,52 @@ +#include "Services/FunctionManagementService.hpp" + +void FunctionManagementService::call(Message& msg) { + msg.resetRead(); + ErrorHandler::assertRequest(msg.packetType == Message::TC, msg, + ErrorHandler::AcceptanceErrorType::UnacceptableMessage); + ErrorHandler::assertRequest(msg.messageType == 1, msg, + ErrorHandler::AcceptanceErrorType::UnacceptableMessage); + ErrorHandler::assertRequest(msg.serviceType == 8, msg, + ErrorHandler::AcceptanceErrorType::UnacceptableMessage); + + uint8_t funcName[FUNC_NAME_LENGTH]; // the function's name + uint8_t funcArgs[MAX_ARG_LENGTH]; // arguments for the function + + msg.readString(funcName, FUNC_NAME_LENGTH); + msg.readString(funcArgs, MAX_ARG_LENGTH); + + if (msg.dataSize > FUNC_NAME_LENGTH + MAX_ARG_LENGTH) { + ErrorHandler::reportError(msg, + ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError); // report failed + // start of execution as requested by the standard + return; + } + + // locate the appropriate function pointer + String<FUNC_NAME_LENGTH> name(funcName); + FunctionMap::iterator iter = funcPtrIndex.find(name); + void (*selected)(String<MAX_ARG_LENGTH>); + + if (iter != funcPtrIndex.end()) { + selected = *iter->second; + } else { + ErrorHandler::reportError(msg, + ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError); + return; + } + + // execute the function if there are no obvious flaws (defined in the standard, pg.158) + selected(funcArgs); +} + +void FunctionManagementService::include(String<FUNC_NAME_LENGTH> funcName, void(*ptr) + (String<MAX_ARG_LENGTH>)) { + + if (not funcPtrIndex.full()) { // CAUTION: etl::map won't check by itself if it's full + // before attempting to insert a key-value pair, causing segmentation faults. Check first! + funcName.append(FUNC_NAME_LENGTH - funcName.length(), '\0'); + funcPtrIndex.insert(std::make_pair(funcName, ptr)); + } else { + ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::FunctionMapFull); + } +} diff --git a/src/main.cpp b/src/main.cpp index 407e7a534a2df58096cfcac170a62a97da95ad64..6067a82a5459d2c2137f0b8702484c7ece768791 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include "Services/RequestVerificationService.hpp" #include "Services/MemoryManagementService.hpp" #include "Services/EventReportService.hpp" +#include "Services/FunctionManagementService.hpp" #include "Services/TimeManagementService.hpp" #include "Services/EventActionService.hpp" #include "Services/TimeBasedSchedulingService.hpp" diff --git a/test/Services/FunctionManagementService.cpp b/test/Services/FunctionManagementService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1cd99bf509a995fd08ce849965d0cd6d3f28a83c --- /dev/null +++ b/test/Services/FunctionManagementService.cpp @@ -0,0 +1,52 @@ +#include "catch2/catch.hpp" +#include "Services/FunctionManagementService.hpp" +#include "ServicePool.hpp" +#include "ServiceTests.hpp" +#include <iostream> + +FunctionManagementService & fms = Services.functionManagement; + +void test(String<MAX_ARG_LENGTH> a) { + std::cout << a.c_str() << std::endl; +} + +TEST_CASE("ST[08] - Call Tests") { + + SECTION("Malformed name") { + ServiceTests::reset(); + fms.include(String<FUNC_NAME_LENGTH>("test"), &test); + Message msg(8, 1, Message::TC, 1); + msg.appendString(String<FUNC_NAME_LENGTH>("t3st")); + fms.call(msg); + CHECK(ServiceTests::get(0).messageType == 4); + CHECK(ServiceTests::get(0).serviceType == 1); + } + + SECTION("Too long message") { + ServiceTests::reset(); + fms.include(String<FUNC_NAME_LENGTH>("test"), &test); + Message msg(8, 1, Message::TC, 1); + msg.appendString(String<FUNC_NAME_LENGTH>("test")); + msg.appendString(String<65> + ("eqrhjweghjhwqgthjkrghthjkdsfhgsdfhjsdjsfdhgkjdfsghfjdgkdfsgdfgsgd")); + fms.call(msg); + CHECK(ServiceTests::get(0).messageType == 4); + CHECK(ServiceTests::get(0).serviceType == 1); + } +} + + +TEST_CASE("ST[08] - Insert Tests") { + + SECTION("Insertion to full pointer map") { + // make sure the pointer map is full to the brim + ServiceTests::reset(); + std::string name = "test"; // FOR TESTING ONLY! + + for (int i = 0; i < FUNC_MAP_SIZE + 1; i++) { + name += std::to_string(i); // different names to fill up the map + fms.include(String<FUNC_NAME_LENGTH>(name.c_str()), &test); + } + CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::FunctionMapFull)); + } +}