diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d90274bf9e0327fe706ffcfa337b49322e0bb3e..9a23f059268899791574cc8ae43f573e7d129fbe 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -65,14 +65,6 @@ cppcheck-misra: script: - ci/cppcheck-misra.sh -vera: - stage: build - before_script: - - vera++ --version - - cp ci/vera.profile /usr/lib/vera++/profiles/custom - script: - - ci/vera.sh - clang-tidy: stage: build variables: diff --git a/CMakeLists.txt b/CMakeLists.txt index d56b1c4e859d1de9b54dd2558ff170eb8584363b..50c416bbd0ee88fef12adb1af819d0675a4e5d51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ add_library(common OBJECT src/Helpers/CRCHelper.cpp src/Helpers/TimeAndDate.cpp src/Helpers/TimeHelper.cpp + src/Platform/x86/Parameters/SystemParameters.cpp src/Services/EventReportService.cpp src/Services/MemoryManagementService.cpp src/Services/ParameterService.cpp diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp index eb2f737d61cdc9908902200278d251f14b740910..a8523462496cad6652af9cb1c8eeb05d02fee2f4 100644 --- a/inc/ECSS_Definitions.hpp +++ b/inc/ECSS_Definitions.hpp @@ -141,4 +141,8 @@ */ #define LOGGER_MAX_MESSAGE_SIZE 512 +/** + * @brief Size of the array holding the Parameter objects for the ST[20] parameter service + */ +#define ECSS_PARAMETER_COUNT 3 #endif // ECSS_SERVICES_ECSS_DEFINITIONS_H diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp index caeae9c80520866bd895f9e1e3db2bd4e4080204..0c07fe079c4f98002cbcc7d17c56a17e93e521d2 100644 --- a/inc/ErrorHandler.hpp +++ b/inc/ErrorHandler.hpp @@ -70,13 +70,13 @@ public: */ OtherMessageType = 9, /** - * Attempt to insert new function in a full function map (ST[08]) + * Attempt to insert new element in a full map ST[08] */ - FunctionMapFull = 10, + MapFull = 10, /** * A Message that is included within another message is too large */ - NestedMessageTooLarge = 11, + NestedMessageTooLarge = 11 }; /** @@ -102,7 +102,7 @@ public: /** * Cannot parse a Message, because there is an error in its secondary header */ - UnacceptableMessage = 5, + UnacceptableMessage = 5 }; /** @@ -135,7 +135,14 @@ public: EventActionUnknownEventActionDefinitionIDError = 4, SubServiceExecutionStartError = 5, InstructionExecutionStartError = 6, - + /** + * Attempt to change the value of a non existing parameter (ST[20]) + */ + SetNonExistingParameter = 7, + /** + * Attempt to access a non existing parameter (ST[20]) + */ + GetNonExistingParameter = 8 }; /** diff --git a/inc/Helpers/TimeHelper.hpp b/inc/Helpers/TimeHelper.hpp index 027e078e817bf6748ac693821d7527a9e5c7c811..ecbad93d35d5893842e080e585c210f549bbce1d 100644 --- a/inc/Helpers/TimeHelper.hpp +++ b/inc/Helpers/TimeHelper.hpp @@ -66,7 +66,7 @@ public: * @todo check if we need to change the epoch to the recommended one from the standard, 1 * January 1958 */ - static uint32_t utcToSeconds(TimeAndDate& TimeInfo); + static uint32_t utcToSeconds(const TimeAndDate& TimeInfo); /** * Convert elapsed seconds since Unix epoch to UTC date. @@ -94,7 +94,7 @@ public: * @todo time security for critical time operations * @todo declare the implicit P-field */ - static uint64_t generateCDSTimeFormat(struct TimeAndDate& TimeInfo); + static uint64_t generateCDSTimeFormat(const struct TimeAndDate& TimeInfo); /** * Parse the CDS time format (3.3 in CCSDS 301.0-B-4 standard) @@ -124,7 +124,7 @@ public: * @todo time security for critical time operations * @todo declare the implicit P-field */ - static uint32_t generateCUCTimeFormat(struct TimeAndDate& TimeInfo); + static uint32_t generateCUCTimeFormat(const struct TimeAndDate& TimeInfo); /** * Parse the CUC time format (3.3 in CCSDS 301.0-B-4 standard) diff --git a/inc/Platform/x86/Parameters/SystemParameters.hpp b/inc/Platform/x86/Parameters/SystemParameters.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1e58687e46c5d39b4d374fcc68cb4d2aa8b8f564 --- /dev/null +++ b/inc/Platform/x86/Parameters/SystemParameters.hpp @@ -0,0 +1,32 @@ +#include "Services/Parameter.hpp" +#include "etl/vector.h" +/** + * @author Athanasios Theocharis <athatheoc@gmail.com> + */ + +/** + * This class was created for the purpose of initializing and storing explicitly + * parameters (that are instances of the \ref Parameter class). It stores all the parameters + * of the specific application. Different subsystems should have their own implementations of this class. + * The position of the parameter in the vector is also called the parameter ID. + * + * It is initialised statically. + * + * The parameters here are under the responsibility of \ref ParameterService. + */ +class SystemParameters { +public: + Parameter<uint8_t> parameter1 = Parameter<uint8_t>(3); + Parameter<uint16_t> parameter2 = Parameter<uint16_t>(7); + Parameter<uint32_t> parameter3 = Parameter<uint32_t>(10); + /** + * The key of the array is the ID of the parameter as specified in PUS + */ + etl::array<std::reference_wrapper<ParameterBase>, ECSS_PARAMETER_COUNT> parametersArray = { + parameter1, parameter2, parameter3 + }; + + SystemParameters() = default; +}; + +extern SystemParameters systemParameters; diff --git a/inc/Services/Parameter.hpp b/inc/Services/Parameter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..932d450f5389fb6cb67c9979f52106aef00d9b4c --- /dev/null +++ b/inc/Services/Parameter.hpp @@ -0,0 +1,89 @@ +#ifndef ECSS_SERVICES_PARAMETER_HPP +#define ECSS_SERVICES_PARAMETER_HPP + +#include "etl/String.hpp" +#include "Message.hpp" +#include "ECSS_Definitions.hpp" + +/** + * Implementation of a Parameter field, as specified in ECSS-E-ST-70-41C. + * + * @author Grigoris Pavlakis <grigpavl@ece.auth.gr> + * @author Athanasios Theocharis <athatheoc@gmail.com> + * + * @section Introduction + * The Parameter class implements a way of storing and updating system parameters + * of arbitrary size and type, while avoiding std::any and dynamic memory allocation. + * It is split in two distinct parts: + * 1) an abstract \ref ParameterBase class which provides a + * common data type used to create any pointers to \ref Parameter objects, as well as + * virtual functions for accessing the parameter's data part, and + * 2) a templated \ref Parameter used to store any type-specific parameter information, + * such as the actual data field where the parameter's value will be stored. + * + * @section Architecture Rationale + * The ST[20] Parameter service is implemented with the need of arbitrary type storage + * in mind, while avoiding any use of dynamic memory allocation, a requirement for use + * in embedded systems. Since lack of Dynamic Memory Access precludes usage of stl::any + * and the need for truly arbitrary (even for template-based objects like etl::string) type storage + * would exclude from consideration constructs like etl::variant due to limitations on + * the number of supported distinct types, a custom solution was needed. + * Furthermore, the \ref ParameterService should provide ID-based access to parameters. + */ +class ParameterBase { +public: + virtual void appendValueToMessage(Message& message) = 0; + virtual void setValueFromMessage(Message& message) = 0; +}; + +/** + * Implementation of a parameter containing its value. See \ref ParameterBase for more information. + * @tparam DataType The type of the Parameter value. This is the type used for transmission and reception + * as per the PUS. + */ +template <typename DataType> +class Parameter : public ParameterBase { +private: + DataType currentValue; + +public: + Parameter(DataType initialValue) { + currentValue = initialValue; + } + + inline void setValue(DataType value) { + currentValue = value; + } + + DataType getValue() { + return currentValue; + } + + inline void setValueFromMessage(Message& message) override; + + inline void appendValueToMessage(Message& message) override; +}; + +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/inc/Services/ParameterService.hpp b/inc/Services/ParameterService.hpp index 6b2a7af751c4904540f037929bcbd78f56588b8e..f347f651b1dda1e1a8e7073f5d19528ffeba7f2e 100644 --- a/inc/Services/ParameterService.hpp +++ b/inc/Services/ParameterService.hpp @@ -1,77 +1,40 @@ #ifndef ECSS_SERVICES_PARAMETERSERVICE_HPP #define ECSS_SERVICES_PARAMETERSERVICE_HPP +#include "ECSS_Definitions.hpp" #include "Service.hpp" #include "ErrorHandler.hpp" -#include "etl/map.h" +#include "Parameter.hpp" +#include "Parameters/SystemParameters.hpp" /** * Implementation of the ST[20] parameter management service, * as defined in ECSS-E-ST-70-41C * * @author Grigoris Pavlakis <grigpavl@ece.auth.gr> + * @author Athanasios Theocharis <athatheoc@gmail.com> */ -/** - * Generic parameter structure - * PTC and PFC for each parameter shall be specified as in - * ECSS-E-ST-70-41C, chapter 7.3 - */ - -typedef uint16_t ParamId; // parameter IDs are given sequentially -struct Parameter { - uint8_t ptc; // Packet field type code (PTC) - uint8_t pfc; // Packet field format code (PFC) - - 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. * - * The parameter list is stored in a map with the parameter IDs as keys and values - * corresponding Parameter structs containing the PTC, PFC and the parameter's value. - * - * @ingroup Services + * The purpose of this class is to handle functions regarding the access and modification + * of the various parameters of the CubeSat. + * The parameters to be managed are initialized and kept in \ref SystemParameters. */ - class ParameterService : public Service { -private: - etl::map<ParamId, Parameter, ECSS_MAX_PARAMETERS> paramsList; - 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(); + ParameterService() = default; /** * 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 packet is of the correct type - * and whether the requested IDs are valid, ignoring the invalid ones. - * If the packet has an incorrect header, an InternalError::UnacceptablePacket is raised. - * If no IDs are correct, the returned message shall be empty. - * - * @param paramId: a valid TC[20, 1] packet carrying the requested parameter IDs + * @param paramId: a TC[20, 1] packet carrying the requested parameter IDs * @return None (messages are stored using storeMessage()) - * - * - * 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); + void reportParameters(Message& paramIds); /** * This function receives a TC[20, 3] message and after checking whether its type is correct, @@ -79,11 +42,8 @@ public: * while ignoring all invalid IDs. * * @param newParamValues: a valid TC[20, 3] message carrying parameter ID and replacement value - * @return None - * - * @todo Use pointers for changing and storing addresses to comply with the standard */ - void setParameterIds(Message& newParamValues); + void setParameters(Message& newParamValues); /** * It is responsible to call the suitable function that executes a telecommand packet. The source of that packet diff --git a/src/Helpers/TimeHelper.cpp b/src/Helpers/TimeHelper.cpp index 04792af7c90b0be0153d8212aa3af4262673870b..b0e02d9b4debc90164b0b041ca16ee11bc3b0e28 100644 --- a/src/Helpers/TimeHelper.cpp +++ b/src/Helpers/TimeHelper.cpp @@ -10,7 +10,7 @@ bool TimeHelper::IsLeapYear(uint16_t year) { return (year % 400) == 0; } -uint32_t TimeHelper::utcToSeconds(TimeAndDate& TimeInfo) { +uint32_t TimeHelper::utcToSeconds(const TimeAndDate& TimeInfo) { // the date, that \p TimeInfo represents, should be greater than or equal to 1/1/2019 and the // date should be valid according to Gregorian calendar ASSERT_INTERNAL(TimeInfo.year >= 2019, ErrorHandler::InternalErrorType::InvalidDate); @@ -91,7 +91,7 @@ struct TimeAndDate TimeHelper::secondsToUTC(uint32_t seconds) { return TimeInfo; } -uint64_t TimeHelper::generateCDSTimeFormat(TimeAndDate& TimeInfo) { +uint64_t TimeHelper::generateCDSTimeFormat(const TimeAndDate& TimeInfo) { /** * Define the T-field. The total number of octets for the implementation of T-field is 6(2 for * the `DAY` and 4 for the `ms of day` @@ -126,7 +126,7 @@ TimeAndDate TimeHelper::parseCDStimeFormat(const uint8_t* data) { return secondsToUTC(seconds); } -uint32_t TimeHelper::generateCUCTimeFormat(struct TimeAndDate& TimeInfo) { +uint32_t TimeHelper::generateCUCTimeFormat(const struct TimeAndDate& TimeInfo) { return (utcToSeconds(TimeInfo) + LEAP_SECONDS); } diff --git a/src/Platform/x86/Parameters/SystemParameters.cpp b/src/Platform/x86/Parameters/SystemParameters.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b5f68de3991e7aea68d3a6de2578aa48a4783b85 --- /dev/null +++ b/src/Platform/x86/Parameters/SystemParameters.cpp @@ -0,0 +1,3 @@ +#include "Parameters/SystemParameters.hpp" + +SystemParameters systemParameters; diff --git a/src/Platform/x86/main.cpp b/src/Platform/x86/main.cpp index 391780a790d452b7d5e0fd46a3aec0c280882818..07e1f968d556647caa0db2e5b0e7b0c7ea41457c 100644 --- a/src/Platform/x86/main.cpp +++ b/src/Platform/x86/main.cpp @@ -55,7 +55,7 @@ int main() { sentPacket.appendUint16(2); // number of contained IDs sentPacket.appendUint16(0); // first ID sentPacket.appendUint16(1); // second ID - paramService.reportParameterIds(sentPacket); + paramService.reportParameters(sentPacket); // Test code for setParameter Message sentPacket2 = Message(20, 3, Message::TC, 1); // application id is a dummy number (1) @@ -65,8 +65,8 @@ int main() { sentPacket2.appendUint16(1); // 2nd parameter ID sentPacket2.appendUint32(45823); // settings for 2nd parameter - paramService.setParameterIds(sentPacket2); - paramService.reportParameterIds(sentPacket); + paramService.setParameters(sentPacket2); + paramService.reportParameters(sentPacket); // ST[06] testing char anotherStr[8] = "Fgthred"; diff --git a/src/Services/EventActionService.cpp b/src/Services/EventActionService.cpp index f42079794019ef1d4fca34b4ed176e7d40e5c18c..b0f2f7e22f4157d7d9b239309b8095f108663d53 100644 --- a/src/Services/EventActionService.cpp +++ b/src/Services/EventActionService.cpp @@ -221,4 +221,4 @@ void EventActionService::execute(Message& message) { } } -#endif \ No newline at end of file +#endif diff --git a/src/Services/FunctionManagementService.cpp b/src/Services/FunctionManagementService.cpp index 4cee2c0c7f77749b1ff34f1bd4aa87c0809d8fb5..5400f038bb6efb3193712b8a800881478c89a338 100644 --- a/src/Services/FunctionManagementService.cpp +++ b/src/Services/FunctionManagementService.cpp @@ -46,7 +46,7 @@ void FunctionManagementService::include(String<ECSS_FUNCTION_NAME_LENGTH> funcNa funcName.append(ECSS_FUNCTION_NAME_LENGTH - funcName.length(), 0); funcPtrIndex.insert(std::make_pair(funcName, ptr)); } else { - ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::FunctionMapFull); + ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::MapFull); } } diff --git a/src/Services/ParameterService.cpp b/src/Services/ParameterService.cpp index aee2de5da27401a6a523f041cda172e112504281..3557f0ed7c66133dd50d075d6760649d97a4c7ef 100644 --- a/src/Services/ParameterService.cpp +++ b/src/Services/ParameterService.cpp @@ -2,50 +2,12 @@ #ifdef SERVICE_PARAMETER #include "Services/ParameterService.hpp" +#include "Services/Parameter.hpp" -#define DEMOMODE +void ParameterService::reportParameters(Message& paramIds) { + // TM[20,2] + Message parameterReport(20, 2, Message::TM, 1); -#include <ctime> -#include <cstdlib> - -ParameterService::ParameterService() { -#ifdef DEMOMODE - // Test code, setting up some of the parameter fields - - time_t currTime = time(nullptr); - struct tm* today = localtime(&currTime); - - Parameter test1; - test1.settingData = today->tm_hour; // the current hour - test1.ptc = 3; // unsigned int - test1.pfc = 14; // 32 bits - - Parameter test2; - test2.settingData = today->tm_min; // the current minute - test2.ptc = 3; // unsigned int - test2.pfc = 14; // 32 bits - - // MAKE SURE THE IDS ARE UNIQUE WHEN INSERTING! - /** - * @todo: Make a separate insert() function for parameter insertion to protect from blunders - * if needed to - */ - - paramsList.insert(std::make_pair(0, test1)); - paramsList.insert(std::make_pair(1, test2)); - -#endif -} - -void ParameterService::reportParameterIds(Message& paramIds) { - paramIds.assertTC(20, 1); - Message reqParam(20, 2, Message::TM, 1); // empty TM[20, 2] parameter report message - - paramIds.resetRead(); // since we're passing a reference, the reading position shall be reset - // to its default before any read operations (to ensure the correct data is being read) - - // assertion: correct message, packet and service type (at failure throws an - // InternalError::UnacceptablePacket) ErrorHandler::assertRequest(paramIds.packetType == Message::TC, paramIds, ErrorHandler::AcceptanceErrorType::UnacceptableMessage); ErrorHandler::assertRequest(paramIds.messageType == 1, paramIds, @@ -53,29 +15,31 @@ void ParameterService::reportParameterIds(Message& paramIds) { ErrorHandler::assertRequest(paramIds.serviceType == 20, paramIds, ErrorHandler::AcceptanceErrorType::UnacceptableMessage); - uint16_t ids = paramIds.readUint16(); - reqParam.appendUint16(numOfValidIds(paramIds)); // include the number of valid IDs - - for (uint16_t i = 0; i < ids; i++) { - uint16_t currId = paramIds.readUint16(); // current ID to be appended - - if (paramsList.find(currId) != paramsList.end()) { - reqParam.appendUint16(currId); - reqParam.appendUint32(paramsList[currId].settingData); + uint16_t numOfIds = paramIds.readUint16(); + uint16_t numberOfValidIds = 0; + for (uint16_t i = 0; i < numOfIds; i++) { + if (paramIds.readUint16() < systemParameters.parametersArray.size()) { + numberOfValidIds++; + } + } + parameterReport.appendUint16(numberOfValidIds); + paramIds.resetRead(); + + numOfIds = paramIds.readUint16(); + for (uint16_t i = 0; i < numOfIds; i++) { + uint16_t currId = paramIds.readUint16(); + if (currId < systemParameters.parametersArray.size()) { + parameterReport.appendUint16(currId); + systemParameters.parametersArray[currId].get().appendValueToMessage(parameterReport); } else { - ErrorHandler::reportError(paramIds, ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError); - continue; // generate failed start of execution notification & ignore + ErrorHandler::reportError(paramIds, ErrorHandler::GetNonExistingParameter); } } - storeMessage(reqParam); + storeMessage(parameterReport); } -void ParameterService::setParameterIds(Message& newParamValues) { - newParamValues.assertTC(20, 3); - - // assertion: correct message, packet and service type (at failure throws an - // InternalError::UnacceptablePacket which gets logged) +void ParameterService::setParameters(Message& newParamValues) { ErrorHandler::assertRequest(newParamValues.packetType == Message::TC, newParamValues, ErrorHandler::AcceptanceErrorType::UnacceptableMessage); @@ -84,51 +48,26 @@ void ParameterService::setParameterIds(Message& newParamValues) { ErrorHandler::assertRequest(newParamValues.serviceType == 20, newParamValues, ErrorHandler::AcceptanceErrorType::UnacceptableMessage); - uint16_t ids = newParamValues.readUint16(); // get number of ID's + uint16_t numOfIds = newParamValues.readUint16(); - for (uint16_t i = 0; i < ids; i++) { + for (uint16_t i = 0; i < numOfIds; i++) { uint16_t currId = newParamValues.readUint16(); - - if (paramsList.find(currId) != paramsList.end()) { - paramsList[currId].settingData = newParamValues.readUint32(); + if (currId < systemParameters.parametersArray.size()) { + systemParameters.parametersArray[currId].get().setValueFromMessage(newParamValues); } else { - ErrorHandler::reportError(newParamValues, - ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError); - continue; // generate failed start of execution notification & ignore - } - } -} - -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 (uint16_t 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 (paramsList.find(currId) != paramsList.end()) { - validIds++; + ErrorHandler::reportError(newParamValues, ErrorHandler::SetNonExistingParameter); + break; // Setting next parameters is impossible, since the size of value to be read is unknown } } - - return validIds; } void ParameterService::execute(Message& message) { switch (message.messageType) { case 1: - reportParameterIds(message); // TC[20,1] + reportParameters(message); // TC[20,1] break; case 3: - setParameterIds(message); // TC[20,3] + setParameters(message); // TC[20,3] break; default: ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); diff --git a/test/Services/EventActionService.cpp b/test/Services/EventActionService.cpp index d87262dce4bf2fad920224dd25c306f40ae6eb60..1931fc56dcee2120c7b0db8f5c3ad23716490195 100644 --- a/test/Services/EventActionService.cpp +++ b/test/Services/EventActionService.cpp @@ -3,9 +3,7 @@ #include <Message.hpp> #include "ServiceTests.hpp" #include <etl/String.hpp> -#include <etl/map.h> #include <cstring> -#include <iostream> #include <ServicePool.hpp> EventActionService& eventActionService = Services.eventAction; diff --git a/test/Services/FunctionManagementService.cpp b/test/Services/FunctionManagementService.cpp index a4893a1a7561c2deb783ba8a12e46779c4a2d127..a793c3e9035ed37eb4aa9351d235fe1774f866a6 100644 --- a/test/Services/FunctionManagementService.cpp +++ b/test/Services/FunctionManagementService.cpp @@ -71,6 +71,6 @@ TEST_CASE("ST[08] - Insert Tests") { name += std::to_string(i); // different names to fill up the map fms.include(String<ECSS_FUNCTION_NAME_LENGTH>(name.c_str()), &test); } - CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::FunctionMapFull)); + CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::MapFull)); } } diff --git a/test/Services/Parameter.cpp b/test/Services/Parameter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cf629ee627eb1ced39be89518618663425e0240 --- /dev/null +++ b/test/Services/Parameter.cpp @@ -0,0 +1,41 @@ +#include "catch2/catch.hpp" +#include "Services/Parameter.hpp" +#include "Message.hpp" + +TEST_CASE("Parameter Append") { + SECTION("Check correct appending") { + Message request = Message(20, 1, 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); + + parameter1.appendValueToMessage(request); + parameter2.appendValueToMessage(request); + parameter3.appendValueToMessage(request); + + CHECK(request.readUint8() == 1); + CHECK(request.readUint16() == 500); + CHECK(request.readUint32() == 70000); + } +} + +TEST_CASE("Parameter Set") { + SECTION("Check correct setting") { + Message request = Message(20, 1, 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); + + request.appendUint8(10); + request.appendUint16(1000); + request.appendUint32(70001); + + parameter1.setValueFromMessage(request); + parameter2.setValueFromMessage(request); + parameter3.setValueFromMessage(request); + + CHECK(parameter1.getValue() == 10); + CHECK(parameter2.getValue() == 1000); + CHECK(parameter3.getValue() == 70001); + } +} diff --git a/test/Services/ParameterService.cpp b/test/Services/ParameterService.cpp index 6153f6c5d57f69b32bbfdc1bb1410c040d0f8a7d..f29143633e2b692fb55226330627831f094044bc 100644 --- a/test/Services/ParameterService.cpp +++ b/test/Services/ParameterService.cpp @@ -1,103 +1,206 @@ #include "catch2/catch.hpp" -#include "Services/ParameterService.hpp" #include "Message.hpp" #include "ServiceTests.hpp" -ParameterService& pserv = Services.parameterManagement; +static void resetParameterValues() { + systemParameters.parameter1.setValue(3); + systemParameters.parameter2.setValue(7); + systemParameters.parameter3.setValue(10); +}; TEST_CASE("Parameter Report Subservice") { - 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(1); // valid + SECTION("All requested parameters invalid") { + Message request = Message(20, 1, Message::TC, 1); + request.appendUint16(3); + request.appendUint16(54432); + request.appendUint16(60000); + request.appendUint16(65535); MessageParser::execute(request); - report = ServiceTests::get(0); - request.resetRead(); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::GetNonExistingParameter) == 3); + CHECK(ServiceTests::count() == 4); + + Message report = ServiceTests::get(3); + CHECK(report.serviceType == 20); + CHECK(report.messageType == 2); + CHECK(report.readUint16() == 0); // the message shall be empty - report.readUint16(); - request.readUint16(); + ServiceTests::reset(); + Services.reset(); + } - 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("Some requested parameters invalid") { + Message request = Message(20, 1, Message::TC, 1); + request.appendUint16(3); + request.appendUint16(1); + request.appendUint16(10000); + request.appendUint16(2); - CHECK(ServiceTests::countErrors() == 1); - CHECK(ServiceTests::thrownError(ErrorHandler::UnknownExecutionStartError)); + MessageParser::execute(request); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::GetNonExistingParameter) == 1); + CHECK(ServiceTests::count() == 2); + + Message report = ServiceTests::get(1); + CHECK(report.serviceType == 20); + CHECK(report.messageType == 2); + CHECK(report.readUint16() == 2); + CHECK(report.readUint16() == 1); + CHECK(report.readUint16() == 7); + CHECK(report.readUint16() == 2); + CHECK(report.readUint32() == 10); + + ServiceTests::reset(); + Services.reset(); } - // **WARNING** - // TODO: Update this test (and all tests in general) to use the error handler's output when - // checking for assertions. - SECTION("Wrong Message Type Handling Test") { - Message falseRequest(15, 3, Message::TM, 1); // a totally wrong message + SECTION("Parameters are of different types") { + Message request = Message(20, 1, Message::TC, 1); + request.appendUint16(3); + request.appendUint16(0); + request.appendUint16(1); + request.appendUint16(2); - MessageParser::execute(falseRequest); - Message errorNotif = ServiceTests::get(0); - CHECK(errorNotif.messageType == 4); // check for proper failed start of - // execution notification - CHECK(errorNotif.serviceType == 1); - - Message response = ServiceTests::get(1); - 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 + MessageParser::execute(request); + + Message report = ServiceTests::get(0); + CHECK(report.serviceType == 20); + CHECK(report.messageType == 2); + CHECK(report.readUint16() == 3); + CHECK(report.readUint16() == 0); + CHECK(report.readUint8() == 3); + CHECK(report.readUint16() == 1); + CHECK(report.readUint16() == 7); + CHECK(report.readUint16() == 2); + CHECK(report.readUint32() == 10); + + ServiceTests::reset(); + Services.reset(); } } TEST_CASE("Parameter Setting Subservice") { - SECTION("Faulty Instruction Handling Test") { - Message setRequest(20, 3, Message::TC, 1); - Message reportRequest(20, 1, Message::TC, 1); - - setRequest.appendUint16(2); // total number of IDs - setRequest.appendUint16(1); // correct ID in this context - 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(1); // used to be 3, which pointed the bug with - // numOfValidIds out, now is 1 in order to be a valid ID (a separate test for - // numOfValidIds shall be introduced) - - // Since every reporting and setting is called with the same (sometimes faulty) parameters, - // and there are errors generated (as should be) it is important to catch and check for - // them in order to preserve the integrity of the test. - MessageParser::execute(reportRequest); - Message errorNotif1 = ServiceTests::get(0); - CHECK(errorNotif1.messageType == 4); - CHECK(errorNotif1.serviceType == 1); - - Message before = ServiceTests::get(1); - - MessageParser::execute(setRequest); - Message errorNotif2 = ServiceTests::get(2); - CHECK(errorNotif2.messageType == 4); - CHECK(errorNotif2.serviceType == 1); - - MessageParser::execute(reportRequest); - Message errorNotif3 = ServiceTests::get(3); - CHECK(errorNotif3.messageType == 4); - CHECK(errorNotif3.serviceType == 1); - - Message after = ServiceTests::get(4); - - 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 - } - - CHECK(ServiceTests::countErrors() == 3); - CHECK(ServiceTests::thrownError(ErrorHandler::UnknownExecutionStartError)); + SECTION("All parameter IDs are invalid") { + Message request = Message(20, 3, Message::TC, 1); + request.appendUint16(3); + request.appendUint16(54432); + request.appendUint16(1); + request.appendUint16(60000); + request.appendUint16(1); + request.appendUint16(65534); + request.appendUint16(1); + + MessageParser::execute(request); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::SetNonExistingParameter) == 1); + CHECK(ServiceTests::count() == 1); + + CHECK(systemParameters.parameter1.getValue() == 3); + CHECK(systemParameters.parameter2.getValue() == 7); + CHECK(systemParameters.parameter3.getValue() == 10); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("The last parameter ID is invalid") { + Message request = Message(20, 3, Message::TC, 1); + request.appendUint16(3); + request.appendUint16(0); + request.appendUint8(1); + request.appendUint16(1); + request.appendUint16(2); + request.appendUint16(65534); + request.appendUint16(1); + + MessageParser::execute(request); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::SetNonExistingParameter) == 1); + CHECK(ServiceTests::count() == 1); + + CHECK(systemParameters.parameter1.getValue() == 1); + CHECK(systemParameters.parameter2.getValue() == 2); + CHECK(systemParameters.parameter3.getValue() == 10); + + resetParameterValues(); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("The middle parameter ID is invalid") { + Message request = Message(20, 3, Message::TC, 1); + request.appendUint16(3); + request.appendUint16(0); + request.appendUint8(1); + request.appendUint16(65534); + request.appendUint16(1); + request.appendUint16(2); + request.appendUint16(3); + + MessageParser::execute(request); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::SetNonExistingParameter) == 1); + CHECK(ServiceTests::count() == 1); + + CHECK(systemParameters.parameter1.getValue() == 1); + CHECK(systemParameters.parameter2.getValue() == 7); + CHECK(systemParameters.parameter3.getValue() == 10); + + resetParameterValues(); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("All IDs are valid") { + Message request = Message(20, 3, Message::TC, 1); + request.appendUint16(3); + request.appendUint16(0); + request.appendUint8(1); + request.appendUint16(1); + request.appendUint16(2); + request.appendUint16(2); + request.appendUint32(3); + + MessageParser::execute(request); + + CHECK(systemParameters.parameter1.getValue() == 1); + CHECK(systemParameters.parameter2.getValue() == 2); + CHECK(systemParameters.parameter3.getValue() == 3); + + resetParameterValues(); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Wrong Messages") { + + SECTION("Wrong Service Type Handling Test for Report") { + Message falseRequest(62, 1, Message::TM, 1); + + MessageParser::execute(falseRequest); + CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::OtherMessageType)); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Wrong Service Type Handling Test for Set") { + Message falseRequest(62, 3, Message::TM, 1); + + MessageParser::execute(falseRequest); + CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::OtherMessageType)); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Wrong Message Type") { + Message falseRequest(20, 127, Message::TM, 1); + + MessageParser::execute(falseRequest); + CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::OtherMessageType)); + + ServiceTests::reset(); + Services.reset(); } } diff --git a/test/Services/ServiceTests.hpp b/test/Services/ServiceTests.hpp index df769167d28843854d9d6919a7730c5015d5b2f7..d5b76b882b1f788b7da4267f5ab4d3f4841f5168 100644 --- a/test/Services/ServiceTests.hpp +++ b/test/Services/ServiceTests.hpp @@ -123,7 +123,7 @@ public: } /** - * Find if an error + * Find if an error exists * @tparam ErrorType An enumeration of ErrorHandler * @param errorType The error code of the Error, corresponding to the correct type as * specified in ErrorHandler @@ -136,6 +136,20 @@ public: return thrownErrors.find(std::make_pair(errorSource, errorType)) != thrownErrors.end(); } + /** + * Find the number of times that an error exists + * @tparam ErrorType An enumeration of ErrorHandler + * @param errorType The error code of the Error, corresponding to the correct type as + * specified in ErrorHandler + */ + template <typename ErrorType> + static int countThrownErrors(ErrorType errorType) { + ErrorHandler::ErrorSource errorSource = ErrorHandler::findErrorSource(errorType); + + expectingErrors = true; + + return thrownErrors.count(std::make_pair(errorSource, errorType)); + } }; #endif // ECSS_SERVICES_TESTS_SERVICES_SERVICETESTS_HPP