From 34c50ce3e2d40ff2208d6a6b9a9820445c0b1a2e Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 28 Jun 2022 17:44:53 +0200 Subject: [PATCH 1/3] ignore .vscode --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index eb461072..1683d44d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,8 @@ .settings .metadata +# VSCode +.vscode + /build* /cmake-build* From 0c057c66b199813a2f5f2931a56d884833b075b0 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 28 Jun 2022 18:56:52 +0200 Subject: [PATCH 2/3] added introspection --- src/fsfw/CMakeLists.txt | 1 + src/fsfw/introspection/CMakeLists.txt | 1 + src/fsfw/introspection/ClasslessEnum.h | 48 ++++++++++++ src/fsfw/introspection/Enum.h | 64 ++++++++++++++++ src/fsfw/introspection/EnumCommon.h | 62 ++++++++++++++++ src/fsfw/introspection/EnumIF.h | 15 ++++ .../introspection/ParameterTypeSelector.cpp | 56 ++++++++++++++ .../introspection/ParameterTypeSelector.h | 13 ++++ src/fsfw/introspection/Types.h | 8 ++ src/fsfw/introspection/TypesHelper.h | 74 +++++++++++++++++++ 10 files changed, 342 insertions(+) create mode 100644 src/fsfw/introspection/CMakeLists.txt create mode 100644 src/fsfw/introspection/ClasslessEnum.h create mode 100644 src/fsfw/introspection/Enum.h create mode 100644 src/fsfw/introspection/EnumCommon.h create mode 100644 src/fsfw/introspection/EnumIF.h create mode 100644 src/fsfw/introspection/ParameterTypeSelector.cpp create mode 100644 src/fsfw/introspection/ParameterTypeSelector.h create mode 100644 src/fsfw/introspection/Types.h create mode 100644 src/fsfw/introspection/TypesHelper.h diff --git a/src/fsfw/CMakeLists.txt b/src/fsfw/CMakeLists.txt index 1daad714..100c2a5b 100644 --- a/src/fsfw/CMakeLists.txt +++ b/src/fsfw/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(globalfunctions) add_subdirectory(health) add_subdirectory(housekeeping) add_subdirectory(internalerror) +add_subdirectory(introspection) add_subdirectory(ipc) add_subdirectory(memory) add_subdirectory(modes) diff --git a/src/fsfw/introspection/CMakeLists.txt b/src/fsfw/introspection/CMakeLists.txt new file mode 100644 index 00000000..de99d508 --- /dev/null +++ b/src/fsfw/introspection/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE ParameterTypeSelector.cpp) diff --git a/src/fsfw/introspection/ClasslessEnum.h b/src/fsfw/introspection/ClasslessEnum.h new file mode 100644 index 00000000..5c3f3635 --- /dev/null +++ b/src/fsfw/introspection/ClasslessEnum.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include + +// TODO ifdef EnumIF, consistent naming of functions arrays and macros (probably enum values and +// descriptions) + +#include "EnumIF.h" +#include "EnumCommon.h" + +#ifdef FSFW_INTROSPECTION + +#define FSFW_CLASSLESS_ENUM(name, type, elements) \ + enum : type { BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(CLEAN_ENUM_ITEM, "", elements)) }; \ + \ + class name : public EnumIF { \ + public: \ + enum : type { BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(CLEAN_ENUM_ITEM, "", elements)) }; \ + name(type value) : value(value) {} \ + name() : value(-1) {} \ + name(const name &other) : value(other.value) {} \ + int64_t getValue() const override { return value; } \ + operator type() { return value; } \ + name &operator=(name other) { \ + value = other.value; \ + return *this; \ + } \ + name &operator=(type value) { \ + this->value = value; \ + return *this; \ + } \ + CREATE_KEY_ARRAY(elements, type) \ + VALUE_CHECK(type) \ + GET_INDEX() \ + CREATE_DESCRIPTION_ARRAY(elements) \ + GET_DESCRIPTION_FUNC() \ + private: \ + type value; \ + }; + +#else + +#define FSFW_CLASSLESS_ENUM(name, type, elements) \ + enum name : type { BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(CLEAN_ENUM_ITEM, "", elements)) }; + +#endif \ No newline at end of file diff --git a/src/fsfw/introspection/Enum.h b/src/fsfw/introspection/Enum.h new file mode 100644 index 00000000..93b01bdd --- /dev/null +++ b/src/fsfw/introspection/Enum.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +#include + +// TODO ifdef EnumIF, consistent naming of functions arrays and macros (probably enum values and +// descriptions) + +#include "EnumIF.h" +#include "EnumCommon.h" + + +#ifdef FSFW_INTROSPECTION + +#define FSFW_ENUM(name, type, elements) \ + class name : public EnumIF, public SerializeIF { \ + public: \ + enum : type { BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(CLEAN_ENUM_ITEM, "", elements)) }; \ + name(type value) : value(value) {} \ + name() : value(-1) {} \ + name(const name &other) : value(other.value) {} \ + int64_t getValue() const override { return value; } \ + operator type() { return value; } \ + name &operator=(name other) { \ + value = other.value; \ + return *this; \ + } \ + name &operator=(type value) { \ + this->value = value; \ + return *this; \ + } \ + CREATE_KEY_ARRAY(elements, type) \ + VALUE_CHECK(type) \ + GET_INDEX() \ + CREATE_DESCRIPTION_ARRAY(elements) \ + GET_DESCRIPTION_FUNC() \ + virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize, \ + Endianness streamEndianness) const override { \ + return SerializeAdapter::serialize<>(&value, buffer, size, maxSize, streamEndianness); \ + } \ + virtual size_t getSerializedSize() const override { \ + return SerializeAdapter::getSerializedSize<>(&value); \ + } \ + virtual ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, \ + Endianness streamEndianness) override { \ + return SerializeAdapter::deSerialize<>(&value, buffer, size, streamEndianness); \ + } \ + \ + private: \ + type value; \ + }; + + +#else + +#define FSFW_ENUM(name, type, elements) \ + enum class name : type { \ + BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(CLEAN_ENUM_ITEM, "", elements)) \ + }; + + +#endif \ No newline at end of file diff --git a/src/fsfw/introspection/EnumCommon.h b/src/fsfw/introspection/EnumCommon.h new file mode 100644 index 00000000..7d5523b4 --- /dev/null +++ b/src/fsfw/introspection/EnumCommon.h @@ -0,0 +1,62 @@ +#define CLEAN_ENUM_ITEM(r, data, element) \ + BOOST_PP_IF(BOOST_PP_SUB(BOOST_PP_TUPLE_SIZE(element), 2), \ + (BOOST_PP_TUPLE_ELEM(0, element) = BOOST_PP_TUPLE_ELEM(1, element)), \ + (BOOST_PP_TUPLE_ELEM(0, element))) + +#if defined FSFW_ENUM_VALUE_CHECKS || defined FSFW_INTROSPECTION + + +#define GET_KEY(r, data, element) (BOOST_PP_TUPLE_ELEM(0, element)) +#define GET_DESCRIPTION(r, data, element) \ + BOOST_PP_IF(BOOST_PP_SUB(BOOST_PP_TUPLE_SIZE(element), 2), (BOOST_PP_TUPLE_ELEM(2, element)), \ + (BOOST_PP_TUPLE_ELEM(1, element))) + +#define CREATE_KEY_ARRAY(enum_elements, type) \ + /*was static constexpr, but clang won't compile that*/ \ + int64_t elements[BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_FOR_EACH(GET_KEY, "", enum_elements))] = { \ + BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(GET_KEY, "", enum_elements))}; \ + const int64_t *getElements() const override { return elements; } \ + size_t getSize() const override { \ + return BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_FOR_EACH(GET_KEY, "", enum_elements)); \ + } +#define VALUE_CHECK(type) \ + bool isValid() const override { \ + for (size_t i = 0; i < sizeof(elements) / sizeof(elements[0]); i++) { \ + if (value == elements[i]) { \ + return true; \ + } \ + } \ + return false; \ + } + +#ifdef FSFW_INTROSPECTION +#define CREATE_DESCRIPTION_ARRAY(elements) \ + /*was static constexpr, but clang won't compile that*/ \ + const char \ + *descriptions[BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_FOR_EACH(GET_DESCRIPTION, "", elements))] = { \ + BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(GET_DESCRIPTION, "", elements))}; \ + const char *const *getDescriptions() const override { return descriptions; } +#define GET_INDEX() \ + size_t getIndex(int64_t value) const override { \ + for (size_t i = 0; i < sizeof(elements) / sizeof(elements[0]); i++) { \ + if (value == elements[i]) { \ + return i; \ + } \ + } \ + return -1; \ + } +#define GET_DESCRIPTION_FUNC() \ + const char *getDescription() const override { \ + if (getIndex(value) == static_cast(-1)) { \ + return nullptr; \ + } else { \ + return descriptions[getIndex(value)]; \ + } \ + } +#else +#define GET_INDEX() +#define CREATE_DESCRIPTION_ARRAY(elements) +#define GET_DESCRIPTION_FUNC() +#endif + +#endif \ No newline at end of file diff --git a/src/fsfw/introspection/EnumIF.h b/src/fsfw/introspection/EnumIF.h new file mode 100644 index 00000000..64040415 --- /dev/null +++ b/src/fsfw/introspection/EnumIF.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +class EnumIF { + public: + virtual int64_t getValue() const = 0; + virtual bool isValid() const = 0; + virtual size_t getSize() const = 0; + virtual size_t getIndex(int64_t value) const = 0; + virtual const int64_t *getElements() const = 0; + virtual const char *const *getDescriptions() const = 0; + virtual const char *getDescription() const = 0; +}; \ No newline at end of file diff --git a/src/fsfw/introspection/ParameterTypeSelector.cpp b/src/fsfw/introspection/ParameterTypeSelector.cpp new file mode 100644 index 00000000..f678271e --- /dev/null +++ b/src/fsfw/introspection/ParameterTypeSelector.cpp @@ -0,0 +1,56 @@ +#include "ParameterTypeSelector.h" + +#include + +#include "Types.h" + +#ifdef FSFW_INTROSPECTION + +template +Types::ParameterType ParameterTypeSelector::getType() { + return Types::UNSUPPORTED; + } + +template <> +Types::ParameterType ParameterTypeSelector::getType() { + return Types::SIGNED; +} +template <> +Types::ParameterType ParameterTypeSelector::getType() { + return Types::SIGNED; +} + +template <> +Types::ParameterType ParameterTypeSelector::getType() { + return Types::SIGNED; +} +template <> +Types::ParameterType ParameterTypeSelector::getType() { + return Types::SIGNED; +} +template <> +Types::ParameterType ParameterTypeSelector::getType() { + return Types::SIGNED; +} +template <> +Types::ParameterType ParameterTypeSelector::getType() { + return Types::SIGNED; +} +// template <> +// Types::ParameterType ParameterTypeSelector::getType() { +// return Types::UNSIGNED; +// } +template <> +Types::ParameterType ParameterTypeSelector::getType() { + return Types::SIGNED; +} +template <> +Types::ParameterType ParameterTypeSelector::getType() { + return Types::FLOATING; +} +template <> +Types::ParameterType ParameterTypeSelector::getType() { + return Types::FLOATING; +} + +#endif \ No newline at end of file diff --git a/src/fsfw/introspection/ParameterTypeSelector.h b/src/fsfw/introspection/ParameterTypeSelector.h new file mode 100644 index 00000000..934cd2c3 --- /dev/null +++ b/src/fsfw/introspection/ParameterTypeSelector.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Types.h" + +#ifdef FSFW_INTROSPECTION + +class ParameterTypeSelector { + public: + template + static Types::ParameterType getType(); +}; + +#endif \ No newline at end of file diff --git a/src/fsfw/introspection/Types.h b/src/fsfw/introspection/Types.h new file mode 100644 index 00000000..0fa9640d --- /dev/null +++ b/src/fsfw/introspection/Types.h @@ -0,0 +1,8 @@ +#pragma once + +//maybe call them MIB types as these are the ones exposed to the MIB? +// Note: some DBs (Postgress, Mongo) only support signed 64bit integers. To have a common denominator, all integers are int64_t. +// As such, ther is no unsigned Type, as there can not be a uint64_t and uint32_t completely fits into int64_t +namespace Types { +enum ParameterType { SIGNED, FLOATING, ENUM, UNSUPPORTED }; +} // namespace Types \ No newline at end of file diff --git a/src/fsfw/introspection/TypesHelper.h b/src/fsfw/introspection/TypesHelper.h new file mode 100644 index 00000000..30ef3635 --- /dev/null +++ b/src/fsfw/introspection/TypesHelper.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +#include "Enum.h" +#include "Types.h" +#include "ParameterTypeSelector.h" + +template +class enumHelper; + +template <> +class enumHelper { + public: + static bool isValid(EnumIF *anEnum) { return anEnum->isValid(); } + +#ifdef FSFW_INTROSPECTION + template + static Types::ParameterType getType() { + return Types::ENUM; + } + + template + static T getMin() { + return 0; + } + + template + static T getMax() { + return 0; + } + + static std::vector getEnumValues() { return std::vector(); } + + static std::vector getEnumValues(EnumIF *anEnum) { + std::vector vector; + for (size_t i = 0; i < anEnum->getSize(); i++) { + vector.push_back(anEnum->getElements()[i]); + } + return vector; + } + + static const char *const *getEnumDescriptions(EnumIF *anEnum) { + return anEnum->getDescriptions(); + } +#endif +}; + +template <> +class enumHelper { + public: + static bool isValid(void *) { return true; } + +#ifdef FSFW_INTROSPECTION + template + static Types::ParameterType getType() { + return ParameterTypeSelector::getType(); + } + + template + static T getMin() { + return std::numeric_limits::lowest(); + } + template + static T getMax() { + return std::numeric_limits::max(); + } + + static std::vector getEnumValues(void *) { return std::vector(); } + + static const char *const *getEnumDescriptions(void *) { return nullptr; } +#endif +}; \ No newline at end of file From ef18377cef6715e284b87b1636016ac9a05e5be1 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Wed, 29 Jun 2022 23:36:45 +0200 Subject: [PATCH 3/3] new Actions --- src/fsfw/action/Action.cpp | 62 ++++++++++ src/fsfw/action/Action.h | 47 +++++++ src/fsfw/action/ActionHelper.cpp | 32 ++++- src/fsfw/action/ActionHelper.h | 8 ++ src/fsfw/action/CMakeLists.txt | 2 +- src/fsfw/action/HasActionsIF.h | 6 +- src/fsfw/action/MinMaxParameter.h | 40 ++++++ src/fsfw/action/Parameter.h | 117 ++++++++++++++++++ src/fsfw/action/ParameterIF.h | 45 +++++++ src/fsfw/action/SimpleActionHelper.cpp | 22 +++- src/fsfw/action/TemplateAction.h | 23 ++++ .../controller/ExtendedControllerBase.cpp | 4 +- src/fsfw/controller/ExtendedControllerBase.h | 3 +- src/fsfw/devicehandlers/DeviceHandlerBase.cpp | 7 +- src/fsfw/devicehandlers/DeviceHandlerBase.h | 5 +- .../unit/action/TestActionHelper.cpp | 28 ++--- .../fsfw_tests/unit/action/TestActionHelper.h | 40 ++++-- 17 files changed, 450 insertions(+), 41 deletions(-) create mode 100644 src/fsfw/action/Action.cpp create mode 100644 src/fsfw/action/Action.h create mode 100644 src/fsfw/action/MinMaxParameter.h create mode 100644 src/fsfw/action/Parameter.h create mode 100644 src/fsfw/action/ParameterIF.h create mode 100644 src/fsfw/action/TemplateAction.h diff --git a/src/fsfw/action/Action.cpp b/src/fsfw/action/Action.cpp new file mode 100644 index 00000000..ca0f7c52 --- /dev/null +++ b/src/fsfw/action/Action.cpp @@ -0,0 +1,62 @@ +#include "Action.h" + +#include + +#undef Action + +#ifdef FSFW_INTROSPECTION + +Action::Action() {} + +void Action::setEnum(EnumIF *theEnum) { + id = theEnum->getValue(); + name = theEnum->getDescription(); +} + +const char *Action::getName() { return name; } +#else +Action::Action(ActionId_t id) : id(id) {} +#endif +ActionId_t Action::getId() { return id; } + +void Action::registerParameter(ParameterIF *parameter) { parameterList.push_back(parameter); } + +std::vector const *Action::getParameters() const { return ¶meterList; } + +size_t Action::getSerializedSize() const { + size_t size = SerializeAdapter::getSerializedSize(&id); + for (auto parameter : *getParameters()) { + size += parameter->getSerializedSize(); + } + return size; +} + +ReturnValue_t Action::serialize(uint8_t **buffer, size_t *size, size_t maxSize, + Endianness streamEndianness) const { + ReturnValue_t result = SerializeAdapter::serialize(&id, buffer, size, maxSize, streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + for (auto parameter : *getParameters()) { + result = parameter->serialize(buffer, size, maxSize, streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return result; +} + +ReturnValue_t Action::deSerialize(const uint8_t **buffer, size_t *size, + Endianness streamEndianness) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;/* TODO not needed as must have been read before to find this action = SerializeAdapter::deSerialize(&id, buffer, size, streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + }*/ + for (auto parameter : *getParameters()) { + result = parameter->deSerialize(buffer, size, streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return result; +} \ No newline at end of file diff --git a/src/fsfw/action/Action.h b/src/fsfw/action/Action.h new file mode 100644 index 00000000..b55593c6 --- /dev/null +++ b/src/fsfw/action/Action.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include + +#include +#include "ActionMessage.h" +#include "ParameterIF.h" + + +#ifdef FSFW_INTROSPECTION +#include "../introspection/Enum.h" +#endif + +class Action: public SerializeIF { + public: +#ifdef FSFW_INTROSPECTION + Action(); + void setEnum(EnumIF* id); + const char *getName(); +#else + Action(ActionId_t id); +#endif + ActionId_t getId(); + + virtual ReturnValue_t handle() = 0; + + void registerParameter(ParameterIF *parameter); + + std::vector const *getParameters() const; + + ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize, + Endianness streamEndianness) const override; + + size_t getSerializedSize() const override; + + ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, + Endianness streamEndianness) override; + + private: + ActionId_t id; +#ifdef FSFW_INTROSPECTION + const char *name; +#endif + std::vector parameterList; +}; \ No newline at end of file diff --git a/src/fsfw/action/ActionHelper.cpp b/src/fsfw/action/ActionHelper.cpp index d41c78b4..503e8080 100644 --- a/src/fsfw/action/ActionHelper.cpp +++ b/src/fsfw/action/ActionHelper.cpp @@ -57,6 +57,8 @@ void ActionHelper::finish(bool success, MessageQueueId_t reportTo, ActionId_t co void ActionHelper::setQueueToUse(MessageQueueIF* queue) { queueToUse = queue; } +#include + void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, store_address_t dataAddress) { const uint8_t* dataPtr = NULL; @@ -66,9 +68,29 @@ void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t act CommandMessage reply; ActionMessage::setStepReply(&reply, actionId, 0, result); queueToUse->sendMessage(commandedBy, &reply); + ipcStore->deleteData(dataAddress); return; } - result = owner->executeAction(actionId, commandedBy, dataPtr, size); + auto actionIter = actionMap.find(actionId); + if (actionIter == actionMap.end()){ + puts("end"); + CommandMessage reply; + ActionMessage::setStepReply(&reply, actionId, 0, HasActionsIF::INVALID_ACTION_ID); + queueToUse->sendMessage(commandedBy, &reply); + ipcStore->deleteData(dataAddress); + return; + } + Action* action = actionIter->second; + result = action->deSerialize(&dataPtr, &size, SerializeIF::Endianness::NETWORK); + if ((result != HasReturnvaluesIF::RETURN_OK) || (size != 0)){ //TODO write unittest for second condition + printf("serialze %i, %x\n", size, result); + CommandMessage reply; + ActionMessage::setStepReply(&reply, actionId, 0, HasActionsIF::INVALID_PARAMETERS); + queueToUse->sendMessage(commandedBy, &reply); + ipcStore->deleteData(dataAddress); + return; + } + result = action->handle(); ipcStore->deleteData(dataAddress); if (result == HasActionsIF::EXECUTION_FINISHED) { CommandMessage reply; @@ -163,3 +185,11 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, ActionId_t rep } return result; } + +void ActionHelper::registerAction(Action* action) { + //TODO error handling + ActionId_t id = action->getId(); + actionMap.emplace(id, action); +} + +std::map const* ActionHelper::getActionMap() { return &actionMap; } \ No newline at end of file diff --git a/src/fsfw/action/ActionHelper.h b/src/fsfw/action/ActionHelper.h index d86e87d2..cdda7040 100644 --- a/src/fsfw/action/ActionHelper.h +++ b/src/fsfw/action/ActionHelper.h @@ -1,9 +1,11 @@ #ifndef FSFW_ACTION_ACTIONHELPER_H_ #define FSFW_ACTION_ACTIONHELPER_H_ +#include #include "../ipc/MessageQueueIF.h" #include "../serialize/SerializeIF.h" #include "ActionMessage.h" +#include "Action.h" /** * @brief Action Helper is a helper class which handles action messages * @@ -98,6 +100,10 @@ class ActionHelper { */ void setQueueToUse(MessageQueueIF* queue); + void registerAction(Action* action); + + std::map const* getActionMap(); + protected: //! Increase of value of this per step static const uint8_t STEP_OFFSET = 1; @@ -108,6 +114,8 @@ class ActionHelper { MessageQueueIF* queueToUse; //! Pointer to an IPC Store, initialized during construction or StorageManagerIF* ipcStore = nullptr; + //! Map of all implemented Actions + std::map actionMap; /** * Internal function called by handleActionMessage diff --git a/src/fsfw/action/CMakeLists.txt b/src/fsfw/action/CMakeLists.txt index 7fb397af..5c11bc6e 100644 --- a/src/fsfw/action/CMakeLists.txt +++ b/src/fsfw/action/CMakeLists.txt @@ -1,3 +1,3 @@ target_sources( - ${LIB_FSFW_NAME} PRIVATE ActionHelper.cpp ActionMessage.cpp + ${LIB_FSFW_NAME} PRIVATE Action.cpp ActionHelper.cpp ActionMessage.cpp CommandActionHelper.cpp SimpleActionHelper.cpp) diff --git a/src/fsfw/action/HasActionsIF.h b/src/fsfw/action/HasActionsIF.h index acc502d7..bede4955 100644 --- a/src/fsfw/action/HasActionsIF.h +++ b/src/fsfw/action/HasActionsIF.h @@ -44,8 +44,11 @@ class HasActionsIF { /** * Function to get the MessageQueueId_t of the implementing object * @return MessageQueueId_t of the object + * */ virtual MessageQueueId_t getCommandQueue() const = 0; + + virtual ActionHelper* getActionHelper() = 0; /** * Execute or initialize the execution of a certain function. * The ActionHelpers will execute this function and behave differently @@ -55,8 +58,7 @@ class HasActionsIF { * -@c EXECUTION_FINISHED Finish reply will be generated * -@c Not RETURN_OK Step failure reply will be generated */ - virtual ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, - const uint8_t* data, size_t size) = 0; + virtual ReturnValue_t executeAction(Action* action, MessageQueueId_t commandedBy) = 0; }; #endif /* FSFW_ACTION_HASACTIONSIF_H_ */ diff --git a/src/fsfw/action/MinMaxParameter.h b/src/fsfw/action/MinMaxParameter.h new file mode 100644 index 00000000..10a751a9 --- /dev/null +++ b/src/fsfw/action/MinMaxParameter.h @@ -0,0 +1,40 @@ +#pragma once + +#include "Parameter.h" + +template +class MinMaxParameter : public Parameter { +#ifdef FSFW_INTROSPECTION + private: + MinMaxParameter(Action *owner, const char *name, T min, T max) + : Parameter(owner, name), min(min), max(max) {} + + public: + static MinMaxParameter createMinMaxParameter(Action *owner, const char *name, T min, T max) { + return MinMaxParameter(owner, name, min, max); + } + virtual double getMinFloating() override { return static_cast(min); } + virtual int64_t getMinSigned() override { return static_cast(min); } + + virtual double getMaxFloating() override { return static_cast(max); } + virtual int64_t getMaxSigned() override { return static_cast(max); } + +#else + private: + MinMaxParameter(Action *owner, T min, T max) : Parameter(owner), min(min), max(max) {} + + public: + static MinMaxParameter createMinMaxParameter(Action *owner, T min, T max) { + return MinMaxParameter(owner, min, max); + } +#endif + private: + T min; + T max; +}; + +#ifdef FSFW_INTROSPECTION +#define createMinMaxParameter(p1, p2, p3, p4) createMinMaxParameter(p1, p2, p3, p4) +#else +#define createMinMaxParameter(p1, p2, p3, p4) createMinMaxParameter(p1, p3, p4) +#endif \ No newline at end of file diff --git a/src/fsfw/action/Parameter.h b/src/fsfw/action/Parameter.h new file mode 100644 index 00000000..59b62b1e --- /dev/null +++ b/src/fsfw/action/Parameter.h @@ -0,0 +1,117 @@ +#pragma once + +#include + +#include "Action.h" +#include +#include +#include "ParameterIF.h" +// TODO: ifdef introspection stuff + + + +template +class Parameter : public ParameterIF { + protected: +#ifdef FSFW_INTROSPECTION + Parameter(Action *owner, const char *name) + : name(name) +#else + Parameter(Action *owner) +#endif + { + owner->registerParameter(this); + } + + public: +#ifdef FSFW_INTROSPECTION + static Parameter createParameter(Action *owner, const char *name) { + return Parameter(owner, name); + } +#else + static Parameter createParameter(Action *owner) { return Parameter(owner); } +#endif + + bool isValid() override { + return enumHelper::value>::isValid(&value); + } + + operator T(){ + return value; + } + +#ifdef FSFW_INTROSPECTION + Types::ParameterType getType() override { + return enumHelper::value>::template getType(); + } +#endif + + T value; + + ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize, + Endianness streamEndianness) const override { + return SerializeAdapter::serialize(&value, buffer, size, maxSize, streamEndianness); + } + + size_t getSerializedSize() const override { return SerializeAdapter::getSerializedSize(&value); } + + ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, + Endianness streamEndianness) override { + return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness); + } + +#ifdef FSFW_INTROSPECTION + double getFloating() override { return (double)value; } + int64_t getSigned() override { return (int64_t)value; } + + bool setFloating(double value) override { + if (getType() != Types::FLOATING) { + puts("fups"); + return false; + } + this->value = T(value); + return true; + } + + bool setSigned(int64_t value) override { + if ((getType() != Types::SIGNED) && (getType() != Types::ENUM)) { + puts("sups"); + return false; + } + this->value = T(value); + return true; + } + + double getMinFloating() override { + return enumHelper::value>::template getMin(); + } + int64_t getMinSigned() override { + return enumHelper::value>::template getMin(); + } + + double getMaxFloating() override { + return enumHelper::value>::template getMax(); + } + int64_t getMaxSigned() override { + return enumHelper::value>::template getMax(); + } + + std::vector getEnumValues() override { + return enumHelper::value>::getEnumValues(&value); + } + const char *const *getEnumDescriptions() override { + return enumHelper::value>::getEnumDescriptions(&value); + } + + const char *getName() override { return name; } + + private: + const char *name; +#endif +}; + +#ifdef FSFW_INTROSPECTION +#define createParameter(p1, p2) createParameter(p1, p2) +#else +#define createParameter(p1, p2) createParameter(p1) +#endif \ No newline at end of file diff --git a/src/fsfw/action/ParameterIF.h b/src/fsfw/action/ParameterIF.h new file mode 100644 index 00000000..05cc5e69 --- /dev/null +++ b/src/fsfw/action/ParameterIF.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#ifdef FSFW_INTROSPECTION +#include +#endif + +class ParameterIF : public SerializeIF { + public: + virtual bool isValid() = 0; +#ifdef FSFW_INTROSPECTION + + + + virtual const char *getName() = 0; + + virtual Types::ParameterType getType() = 0; + + virtual double getFloating() = 0; + virtual int64_t getSigned() = 0; + + virtual bool setFloating(double value) = 0; + virtual bool setSigned(int64_t value) = 0; + + virtual double getMinFloating() = 0; + virtual int64_t getMinSigned() = 0; + + virtual double getMaxFloating() = 0; + virtual int64_t getMaxSigned() = 0; + + virtual std::vector getEnumValues() = 0; + virtual const char *const * getEnumDescriptions() = 0; + + // ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize, + // Endianness streamEndianness) const override = 0; + + // size_t getSerializedSize() const override = 0; + + // ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, + // Endianness streamEndianness) override = 0; + +#endif +}; \ No newline at end of file diff --git a/src/fsfw/action/SimpleActionHelper.cpp b/src/fsfw/action/SimpleActionHelper.cpp index 894f0df6..e9573a34 100644 --- a/src/fsfw/action/SimpleActionHelper.cpp +++ b/src/fsfw/action/SimpleActionHelper.cpp @@ -44,11 +44,27 @@ void SimpleActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId if (result != HasReturnvaluesIF::RETURN_OK) { ActionMessage::setStepReply(&reply, actionId, 0, result); queueToUse->sendMessage(commandedBy, &reply); + ipcStore->deleteData(dataAddress); return; } - lastCommander = commandedBy; - lastAction = actionId; - result = owner->executeAction(actionId, commandedBy, dataPtr, size); + auto actionIter = actionMap.find(actionId); + if (actionIter == actionMap.end()){ + CommandMessage reply; + ActionMessage::setStepReply(&reply, actionId, 0, HasActionsIF::INVALID_ACTION_ID); + queueToUse->sendMessage(commandedBy, &reply); + ipcStore->deleteData(dataAddress); + return; + } + Action* action = actionIter->second; + result = action->deSerialize(&dataPtr, &size, SerializeIF::Endianness::NETWORK); + if (result != HasReturnvaluesIF::RETURN_OK){ + CommandMessage reply; + ActionMessage::setStepReply(&reply, actionId, 0, HasActionsIF::INVALID_PARAMETERS); + queueToUse->sendMessage(commandedBy, &reply); + ipcStore->deleteData(dataAddress); + return; + } + result = action->handle(); ipcStore->deleteData(dataAddress); switch (result) { case HasReturnvaluesIF::RETURN_OK: diff --git a/src/fsfw/action/TemplateAction.h b/src/fsfw/action/TemplateAction.h new file mode 100644 index 00000000..8b83379e --- /dev/null +++ b/src/fsfw/action/TemplateAction.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Action.h" + +template +class TemplateAction : public Action { + public: +#ifdef FSFW_INTROSPECTION + TemplateAction(owner *myOwner, ActionEnum id) : Action(), myOwner(myOwner) { + Action::setEnum(&id); + myOwner->getActionHelper()->registerAction(this); + } +#else + TemplateAction(owner *myOwner, ActionEnum id) : Action((uint32_t) id), myOwner(myOwner) { + myOwner->getActionHelper()->registerAction(this); + } +#endif + + ReturnValue_t handle() override { return myOwner->handleAction(dynamic_cast(this)); } + + private: + owner *myOwner; +}; \ No newline at end of file diff --git a/src/fsfw/controller/ExtendedControllerBase.cpp b/src/fsfw/controller/ExtendedControllerBase.cpp index 0dfd9dc7..78b9b03e 100644 --- a/src/fsfw/controller/ExtendedControllerBase.cpp +++ b/src/fsfw/controller/ExtendedControllerBase.cpp @@ -8,9 +8,7 @@ ExtendedControllerBase::ExtendedControllerBase(object_id_t objectId, object_id_t ExtendedControllerBase::~ExtendedControllerBase() {} -ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId, - MessageQueueId_t commandedBy, - const uint8_t *data, size_t size) { +ReturnValue_t ExtendedControllerBase::executeAction(Action *action, MessageQueueId_t commandedBy) { /* Needs to be overriden and implemented by child class. */ return HasReturnvaluesIF::RETURN_OK; } diff --git a/src/fsfw/controller/ExtendedControllerBase.h b/src/fsfw/controller/ExtendedControllerBase.h index 0c64f5b9..99d4eb04 100644 --- a/src/fsfw/controller/ExtendedControllerBase.h +++ b/src/fsfw/controller/ExtendedControllerBase.h @@ -50,8 +50,7 @@ class ExtendedControllerBase : public ControllerBase, void handleQueue() override; /* HasActionsIF overrides */ - virtual ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, - const uint8_t* data, size_t size) override; + virtual ReturnValue_t executeAction(Action* actionId, MessageQueueId_t commandedBy) override; /* HasLocalDatapoolIF overrides */ virtual LocalDataPoolManager* getHkManagerHandle() override; diff --git a/src/fsfw/devicehandlers/DeviceHandlerBase.cpp b/src/fsfw/devicehandlers/DeviceHandlerBase.cpp index dd9bd5d7..0aadc360 100644 --- a/src/fsfw/devicehandlers/DeviceHandlerBase.cpp +++ b/src/fsfw/devicehandlers/DeviceHandlerBase.cpp @@ -1263,19 +1263,18 @@ void DeviceHandlerBase::handleDeviceTM(SerializeIF* dataSet, DeviceCommandId_t r } } -ReturnValue_t DeviceHandlerBase::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, - const uint8_t* data, size_t size) { +ReturnValue_t DeviceHandlerBase::executeAction(Action *action, MessageQueueId_t commandedBy) { ReturnValue_t result = acceptExternalDeviceCommands(); if (result != HasReturnvaluesIF::RETURN_OK) { return result; } - DeviceCommandMap::iterator iter = deviceCommandMap.find(actionId); + DeviceCommandMap::iterator iter = deviceCommandMap.find(action->getId()); if (iter == deviceCommandMap.end()) { result = COMMAND_NOT_SUPPORTED; } else if (iter->second.isExecuting) { result = COMMAND_ALREADY_SENT; } else { - result = buildCommandFromCommand(actionId, data, size); + result = action->handle(); } if (result == RETURN_OK) { iter->second.sendReplyTo = commandedBy; diff --git a/src/fsfw/devicehandlers/DeviceHandlerBase.h b/src/fsfw/devicehandlers/DeviceHandlerBase.h index 84dcb8dc..d4c83cb1 100644 --- a/src/fsfw/devicehandlers/DeviceHandlerBase.h +++ b/src/fsfw/devicehandlers/DeviceHandlerBase.h @@ -201,9 +201,8 @@ class DeviceHandlerBase : public DeviceHandlerIF, */ virtual void setParentQueue(MessageQueueId_t parentQueueId); - /** @brief Implementation required for HasActionIF */ - ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, - const uint8_t *data, size_t size) override; + /** @brief Implementation required for HasActionIF */ + ReturnValue_t executeAction(Action *action, MessageQueueId_t commandedBy) override; Mode_t getTransitionSourceMode() const; Submode_t getTransitionSourceSubMode() const; diff --git a/tests/src/fsfw_tests/unit/action/TestActionHelper.cpp b/tests/src/fsfw_tests/unit/action/TestActionHelper.cpp index 923b7436..3f997810 100644 --- a/tests/src/fsfw_tests/unit/action/TestActionHelper.cpp +++ b/tests/src/fsfw_tests/unit/action/TestActionHelper.cpp @@ -9,37 +9,35 @@ #include "fsfw_tests/unit/mocks/MessageQueueMockBase.h" TEST_CASE("Action Helper", "[ActionHelper]") { - ActionHelperOwnerMockBase testDhMock; + MessageQueueMockBase testMqMock; - ActionHelper actionHelper = ActionHelper(&testDhMock, dynamic_cast(&testMqMock)); + ActionHelperOwnerMockBase testDhMock(&testMqMock); CommandMessage actionMessage; - ActionId_t testActionId = 777; + ActionId_t testActionId = (ActionId_t) TestActions::TEST_ACTION; std::array testParams{1, 2, 3}; store_address_t paramAddress; StorageManagerIF* ipcStore = tglob::getIpcStoreHandle(); REQUIRE(ipcStore != nullptr); ipcStore->addData(¶mAddress, testParams.data(), 3); - REQUIRE(actionHelper.initialize() == retval::CATCH_OK); + REQUIRE(testDhMock.getActionHelper()->initialize() == retval::CATCH_OK); SECTION("Simple tests") { ActionMessage::setCommand(&actionMessage, testActionId, paramAddress); CHECK(not testDhMock.executeActionCalled); - REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK); + REQUIRE(testDhMock.getActionHelper()->handleActionMessage(&actionMessage) == retval::CATCH_OK); CHECK(testDhMock.executeActionCalled); // No message is sent if everything is alright. CHECK(not testMqMock.wasMessageSent()); store_address_t invalidAddress; ActionMessage::setCommand(&actionMessage, testActionId, invalidAddress); - actionHelper.handleActionMessage(&actionMessage); + testDhMock.getActionHelper()->handleActionMessage(&actionMessage); CHECK(testMqMock.wasMessageSent()); const uint8_t* ptr = nullptr; size_t size = 0; REQUIRE(ipcStore->getData(paramAddress, &ptr, &size) == static_cast(StorageManagerIF::DATA_DOES_NOT_EXIST)); REQUIRE(ptr == nullptr); - REQUIRE(size == 0); - testDhMock.getBuffer(&ptr, &size); - REQUIRE(size == 3); + testDhMock.getBuffer(&ptr); for (uint8_t i = 0; i < 3; i++) { REQUIRE(ptr[i] == (i + 1)); } @@ -48,12 +46,12 @@ TEST_CASE("Action Helper", "[ActionHelper]") { SECTION("Handle failures") { actionMessage.setCommand(1234); - REQUIRE(actionHelper.handleActionMessage(&actionMessage) == + REQUIRE(testDhMock.getActionHelper()->handleActionMessage(&actionMessage) == static_cast(CommandMessage::UNKNOWN_COMMAND)); CHECK(not testMqMock.wasMessageSent()); uint16_t step = 5; ReturnValue_t status = 0x1234; - actionHelper.step(step, testMqMock.getId(), testActionId, status); + testDhMock.getActionHelper()->step(step, testMqMock.getId(), testActionId, status); step += 1; CHECK(testMqMock.wasMessageSent()); CommandMessage testMessage; @@ -69,7 +67,7 @@ TEST_CASE("Action Helper", "[ActionHelper]") { SECTION("Handle finish") { CHECK(not testMqMock.wasMessageSent()); ReturnValue_t status = 0x9876; - actionHelper.finish(false, testMqMock.getId(), testActionId, status); + testDhMock.getActionHelper()->finish(false, testMqMock.getId(), testActionId, status); CHECK(testMqMock.wasMessageSent()); CommandMessage testMessage; REQUIRE(testMqMock.receiveMessage(&testMessage) == @@ -85,14 +83,14 @@ TEST_CASE("Action Helper", "[ActionHelper]") { REQUIRE(ipcStore->addData(&toLongParamAddress, toLongData.data(), 5) == retval::CATCH_OK); ActionMessage::setCommand(&actionMessage, testActionId, toLongParamAddress); CHECK(not testDhMock.executeActionCalled); - REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK); + REQUIRE(testDhMock.getActionHelper()->handleActionMessage(&actionMessage) == retval::CATCH_OK); REQUIRE(ipcStore->getData(toLongParamAddress).first == static_cast(StorageManagerIF::DATA_DOES_NOT_EXIST)); CommandMessage testMessage; REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); - REQUIRE(ActionMessage::getReturnCode(&testMessage) == 0xAFFE); + REQUIRE(ActionMessage::getReturnCode(&testMessage) == HasActionsIF::INVALID_PARAMETERS); REQUIRE(ActionMessage::getStep(&testMessage) == 0); REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId); } @@ -100,7 +98,7 @@ TEST_CASE("Action Helper", "[ActionHelper]") { SECTION("Missing IPC Data") { ActionMessage::setCommand(&actionMessage, testActionId, StorageManagerIF::INVALID_ADDRESS); CHECK(not testDhMock.executeActionCalled); - REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK); + REQUIRE(testDhMock.getActionHelper()->handleActionMessage(&actionMessage) == retval::CATCH_OK); CommandMessage testMessage; REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); diff --git a/tests/src/fsfw_tests/unit/action/TestActionHelper.h b/tests/src/fsfw_tests/unit/action/TestActionHelper.h index 243f030a..55786a77 100644 --- a/tests/src/fsfw_tests/unit/action/TestActionHelper.h +++ b/tests/src/fsfw_tests/unit/action/TestActionHelper.h @@ -2,12 +2,27 @@ #define UNITTEST_HOSTED_TESTACTIONHELPER_H_ #include +#include +#include +#include #include #include #include "fsfw_tests/unit/CatchDefinitions.h" +class ActionHelperOwnerMockBase; + +FSFW_ENUM(TestActions, ActionId_t, ((TEST_ACTION, "Test Action"))) + +class TestAction : public TemplateAction < ActionHelperOwnerMockBase, TestAction, TestActions> { + public: + TestAction(ActionHelperOwnerMockBase* owner) : TemplateAction(owner, TestActions::TEST_ACTION) {} + Parameter p1 = Parameter::createParameter(this, "An uint8_t"); + Parameter p2 = Parameter::createParameter(this, "An uint8_t"); + Parameter p3 = Parameter::createParameter(this, "An uint8_t"); +}; + class ActionHelperOwnerMockBase : public HasActionsIF { public: bool getCommandQueueCalled = false; @@ -16,16 +31,26 @@ class ActionHelperOwnerMockBase : public HasActionsIF { uint8_t buffer[MAX_SIZE] = {0, 0, 0}; size_t size = 0; + ActionHelperOwnerMockBase(MessageQueueIF* useThisQueue) : actionHelper(this, useThisQueue) {} + MessageQueueId_t getCommandQueue() const override { return tconst::testQueueId; } - ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, - const uint8_t* data, size_t size) override { + ActionHelper* getActionHelper() override { return &actionHelper; } + + ReturnValue_t executeAction(Action* action, MessageQueueId_t commandedBy) override { executeActionCalled = true; if (size > MAX_SIZE) { return 0xAFFE; } this->size = size; - memcpy(buffer, data, size); + return action->handle(); + } + + ReturnValue_t handleAction(TestAction *action){ + executeActionCalled = true; + buffer[0] = action->p1; + buffer[1] = action->p2; + buffer[2] = action->p3; return HasReturnvaluesIF::RETURN_OK; } @@ -36,14 +61,15 @@ class ActionHelperOwnerMockBase : public HasActionsIF { } } - void getBuffer(const uint8_t** ptr, size_t* size) { - if (size != nullptr) { - *size = this->size; - } + void getBuffer(const uint8_t** ptr) { if (ptr != nullptr) { *ptr = buffer; } } + + private: + ActionHelper actionHelper; + TestAction testAction = TestAction(this); }; #endif /* UNITTEST_TESTFW_NEWTESTS_TESTACTIONHELPER_H_ */