diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e69de29b diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index b43c676d..0d3baa88 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -1,9 +1,11 @@ #include "ActionHelper.h" #include "HasActionsIF.h" +#include "../ipc/MessageQueueSenderIF.h" #include "../objectmanager/ObjectManagerIF.h" -ActionHelper::ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue) : - owner(setOwner), queueToUse(useThisQueue), ipcStore(nullptr) { +ActionHelper::ActionHelper(HasActionsIF* setOwner, + MessageQueueIF* useThisQueue) : + owner(setOwner), queueToUse(useThisQueue) { } ActionHelper::~ActionHelper() { @@ -32,13 +34,15 @@ ReturnValue_t ActionHelper::initialize(MessageQueueIF* queueToUse_) { return HasReturnvaluesIF::RETURN_OK; } -void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) { +void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo, + ActionId_t commandId, ReturnValue_t result) { CommandMessage reply; ActionMessage::setStepReply(&reply, commandId, step + STEP_OFFSET, result); queueToUse->sendMessage(reportTo, &reply); } -void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) { +void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId, + ReturnValue_t result) { CommandMessage reply; ActionMessage::setCompletionReply(&reply, commandId, result); queueToUse->sendMessage(reportTo, &reply); @@ -48,8 +52,8 @@ void ActionHelper::setQueueToUse(MessageQueueIF* queue) { queueToUse = queue; } -void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, - store_address_t dataAddress) { +void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, + ActionId_t actionId, store_address_t dataAddress) { const uint8_t* dataPtr = NULL; size_t size = 0; ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size); @@ -61,6 +65,11 @@ void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t act } result = owner->executeAction(actionId, commandedBy, dataPtr, size); ipcStore->deleteData(dataAddress); + if(result == HasActionsIF::EXECUTION_FINISHED) { + CommandMessage reply; + ActionMessage::setCompletionReply(&reply, actionId, result); + queueToUse->sendMessage(commandedBy, &reply); + } if (result != HasReturnvaluesIF::RETURN_OK) { CommandMessage reply; ActionMessage::setStepReply(&reply, actionId, 0, result); @@ -85,22 +94,28 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, if (result != HasReturnvaluesIF::RETURN_OK) { return result; } - result = data->serialize(&dataPtr, &size, maxSize, SerializeIF::Endianness::BIG); + result = data->serialize(&dataPtr, &size, maxSize, + SerializeIF::Endianness::BIG); if (result != HasReturnvaluesIF::RETURN_OK) { ipcStore->deleteData(storeAddress); return result; } - //We don't need to report the objectId, as we receive REQUESTED data before the completion success message. - //True aperiodic replies need to be reported with another dedicated message. + // We don't need to report the objectId, as we receive REQUESTED data + // before the completion success message. + // True aperiodic replies need to be reported with + // another dedicated message. ActionMessage::setDataReply(&reply, replyId, storeAddress); - //TODO Service Implementation sucks at the moment - if (hideSender){ + // If the sender needs to be hidden, for example to handle packet + // as unrequested reply, this will be done here. + if (hideSender) { result = MessageQueueSenderIF::sendMessage(reportTo, &reply); - } else { + } + else { result = queueToUse->sendMessage(reportTo, &reply); } - if ( result != HasReturnvaluesIF::RETURN_OK){ + + if (result != HasReturnvaluesIF::RETURN_OK){ ipcStore->deleteData(storeAddress); } return result; @@ -108,3 +123,39 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, void ActionHelper::resetHelper() { } + +ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, + ActionId_t replyId, const uint8_t *data, size_t dataSize, + bool hideSender) { + CommandMessage reply; + store_address_t storeAddress; + ReturnValue_t result = ipcStore->addData(&storeAddress, data, dataSize); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + if (result != HasReturnvaluesIF::RETURN_OK) { + ipcStore->deleteData(storeAddress); + return result; + } + + // We don't need to report the objectId, as we receive REQUESTED data + // before the completion success message. + // True aperiodic replies need to be reported with + // another dedicated message. + ActionMessage::setDataReply(&reply, replyId, storeAddress); + + // If the sender needs to be hidden, for example to handle packet + // as unrequested reply, this will be done here. + if (hideSender) { + result = MessageQueueSenderIF::sendMessage(reportTo, &reply); + } + else { + result = queueToUse->sendMessage(reportTo, &reply); + } + + if (result != HasReturnvaluesIF::RETURN_OK){ + ipcStore->deleteData(storeAddress); + } + return result; +} diff --git a/action/ActionHelper.h b/action/ActionHelper.h index bbc6d114..a91722f3 100644 --- a/action/ActionHelper.h +++ b/action/ActionHelper.h @@ -1,15 +1,18 @@ -#ifndef ACTIONHELPER_H_ -#define ACTIONHELPER_H_ +#ifndef FSFW_ACTION_ACTIONHELPER_H_ +#define FSFW_ACTION_ACTIONHELPER_H_ #include "ActionMessage.h" #include "../serialize/SerializeIF.h" #include "../ipc/MessageQueueIF.h" /** - * \brief Action Helper is a helper class which handles action messages + * @brief Action Helper is a helper class which handles action messages * - * Components which use the HasActionIF this helper can be used to handle the action messages. - * It does handle step messages as well as other answers to action calls. It uses the executeAction function - * of its owner as callback. The call of the initialize function is mandatory and it needs a valid messageQueueIF pointer! + * Components which use the HasActionIF this helper can be used to handle + * the action messages. + * It does handle step messages as well as other answers to action calls. + * It uses the executeAction function of its owner as callback. + * The call of the initialize function is mandatory and needs a + * valid MessageQueueIF pointer! */ class HasActionsIF; @@ -18,7 +21,8 @@ public: /** * Constructor of the action helper * @param setOwner Pointer to the owner of the interface - * @param useThisQueue messageQueue to be used, can be set during initialize function as well. + * @param useThisQueue messageQueue to be used, can be set during + * initialize function as well. */ ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue); @@ -26,28 +30,36 @@ public: /** * Function to be called from the owner with a new command message * - * If the message is a valid action message the helper will use the executeAction function from HasActionsIF. - * If the message is invalid or the callback fails a message reply will be send to the sender of the message automatically. + * If the message is a valid action message the helper will use the + * executeAction function from HasActionsIF. + * If the message is invalid or the callback fails a message reply will be + * send to the sender of the message automatically. * * @param command Pointer to a command message received by the owner - * @return HasReturnvaluesIF::RETURN_OK if the message is a action message, CommandMessage::UNKNOW_COMMAND if this message ID is unkown + * @return HasReturnvaluesIF::RETURN_OK if the message is a action message, + * CommandMessage::UNKNOW_COMMAND if this message ID is unkown */ ReturnValue_t handleActionMessage(CommandMessage* command); /** - * Helper initialize function. Must be called before use of any other helper function - * @param queueToUse_ Pointer to the messageQueue to be used, optional if queue was set in constructor + * Helper initialize function. Must be called before use of any other + * helper function + * @param queueToUse_ Pointer to the messageQueue to be used, optional + * if queue was set in constructor * @return Returns RETURN_OK if successful */ ReturnValue_t initialize(MessageQueueIF* queueToUse_ = nullptr); /** - * Function to be called from the owner to send a step message. Success or failure will be determined by the result value. + * Function to be called from the owner to send a step message. + * Success or failure will be determined by the result value. * * @param step Number of steps already done * @param reportTo The messageQueueId to report the step message to * @param commandId ID of the executed command * @param result Result of the execution */ - void step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + void step(uint8_t step, MessageQueueId_t reportTo, + ActionId_t commandId, + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); /** * Function to be called by the owner to send a action completion message * @@ -55,39 +67,59 @@ public: * @param commandId ID of the executed command * @param result Result of the execution */ - void finish(MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + void finish(MessageQueueId_t reportTo, ActionId_t commandId, + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); /** - * Function to be called by the owner if an action does report data - * - * @param reportTo MessageQueueId_t to report the action completion message to + * Function to be called by the owner if an action does report data. + * Takes a SerializeIF* pointer and serializes it into the IPC store. + * @param reportTo MessageQueueId_t to report the action completion + * message to * @param replyId ID of the executed command * @param data Pointer to the data * @return Returns RETURN_OK if successful, otherwise failure code */ - ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, SerializeIF* data, bool hideSender = false); + ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, + SerializeIF* data, bool hideSender = false); + /** + * Function to be called by the owner if an action does report data. + * Takes the raw data and writes it into the IPC store. + * @param reportTo MessageQueueId_t to report the action completion + * message to + * @param replyId ID of the executed command + * @param data Pointer to the data + * @return Returns RETURN_OK if successful, otherwise failure code + */ + ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, + const uint8_t* data, size_t dataSize, bool hideSender = false); /** - * Function to setup the MessageQueueIF* of the helper. Can be used to set the messageQueueIF* if - * message queue is unavailable at construction and initialize but must be setup before first call of other functions. + * Function to setup the MessageQueueIF* of the helper. Can be used to + * set the MessageQueueIF* if message queue is unavailable at construction + * and initialize but must be setup before first call of other functions. * @param queue Queue to be used by the helper */ void setQueueToUse(MessageQueueIF *queue); protected: - static const uint8_t STEP_OFFSET = 1;//!< Increase of value of this per step + //!< Increase of value of this per step + static const uint8_t STEP_OFFSET = 1; HasActionsIF* owner;//!< Pointer to the owner - MessageQueueIF* queueToUse;//!< Queue to be used as response sender, has to be set with - StorageManagerIF* ipcStore;//!< Pointer to an IPC Store, initialized during construction or initialize(MessageQueueIF* queueToUse_) or with setQueueToUse(MessageQueueIF *queue) + //! Queue to be used as response sender, has to be set in ctor or with + //! setQueueToUse + MessageQueueIF* queueToUse; + //! Pointer to an IPC Store, initialized during construction or + StorageManagerIF* ipcStore = nullptr; + /** - *Internal function called by handleActionMessage(CommandMessage* command) - * + * Internal function called by handleActionMessage * @param commandedBy MessageQueueID of Commander * @param actionId ID of action to be done * @param dataAddress Address of additional data in IPC Store */ - virtual void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, store_address_t dataAddress); + virtual void prepareExecution(MessageQueueId_t commandedBy, + ActionId_t actionId, store_address_t dataAddress); /** - * + * @brief Default implementation is empty. */ virtual void resetHelper(); }; -#endif /* ACTIONHELPER_H_ */ +#endif /* FSFW_ACTION_ACTIONHELPER_H_ */ diff --git a/action/HasActionsIF.h b/action/HasActionsIF.h index 886d0837..690f0369 100644 --- a/action/HasActionsIF.h +++ b/action/HasActionsIF.h @@ -1,11 +1,12 @@ -#ifndef FRAMEWORK_ACTION_HASACTIONSIF_H_ -#define FRAMEWORK_ACTION_HASACTIONSIF_H_ +#ifndef FSFW_ACTION_HASACTIONSIF_H_ +#define FSFW_ACTION_HASACTIONSIF_H_ #include "ActionHelper.h" #include "ActionMessage.h" #include "SimpleActionHelper.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../ipc/MessageQueueIF.h" + /** * @brief * Interface for component which uses actions @@ -47,14 +48,16 @@ public: virtual MessageQueueId_t getCommandQueue() const = 0; /** * Execute or initialize the execution of a certain function. - * Returning #EXECUTION_FINISHED or a failure code, nothing else needs to - * be done. When needing more steps, return RETURN_OK and issue steps and - * completion manually. - * One "step failed" or completion report must be issued! + * The ActionHelpers will execute this function and behave differently + * depending on the returnvalue. + * + * @return + * -@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; }; -#endif /* FRAMEWORK_ACTION_HASACTIONSIF_H_ */ +#endif /* FSFW_ACTION_HASACTIONSIF_H_ */ diff --git a/action/SimpleActionHelper.cpp b/action/SimpleActionHelper.cpp index d79a3c97..af57736c 100644 --- a/action/SimpleActionHelper.cpp +++ b/action/SimpleActionHelper.cpp @@ -1,16 +1,17 @@ #include "HasActionsIF.h" #include "SimpleActionHelper.h" + SimpleActionHelper::SimpleActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue) : - ActionHelper(setOwner, useThisQueue), isExecuting(false), lastCommander( - 0), lastAction(0), stepCount(0) { + ActionHelper(setOwner, useThisQueue), isExecuting(false) { } SimpleActionHelper::~SimpleActionHelper() { } void SimpleActionHelper::step(ReturnValue_t result) { - //STEP_OFFESET is subtracted to compensate for adding offset in base method, which is not necessary here. + // STEP_OFFESET is subtracted to compensate for adding offset in base + // method, which is not necessary here. ActionHelper::step(stepCount - STEP_OFFSET, lastCommander, lastAction, result); if (result != HasReturnvaluesIF::RETURN_OK) { diff --git a/action/SimpleActionHelper.h b/action/SimpleActionHelper.h index 1329b5fb..1f35d9fd 100644 --- a/action/SimpleActionHelper.h +++ b/action/SimpleActionHelper.h @@ -1,8 +1,13 @@ -#ifndef SIMPLEACTIONHELPER_H_ -#define SIMPLEACTIONHELPER_H_ +#ifndef FSFW_ACTION_SIMPLEACTIONHELPER_H_ +#define FSFW_ACTION_SIMPLEACTIONHELPER_H_ #include "ActionHelper.h" +/** + * @brief This is an action helper which is only able to service one action + * at a time but remembers last commander and last action which + * simplifies usage + */ class SimpleActionHelper: public ActionHelper { public: SimpleActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue); @@ -12,13 +17,14 @@ public: ReturnValue_t reportData(SerializeIF* data); protected: - void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, store_address_t dataAddress); - virtual void resetHelper(); + void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, + store_address_t dataAddress); + virtual void resetHelper(); private: bool isExecuting; - MessageQueueId_t lastCommander; - ActionId_t lastAction; - uint8_t stepCount; + MessageQueueId_t lastCommander = MessageQueueIF::NO_QUEUE; + ActionId_t lastAction = 0; + uint8_t stepCount = 0; }; #endif /* SIMPLEACTIONHELPER_H_ */ diff --git a/container/ArrayList.h b/container/ArrayList.h index be89761e..6bd5c1d5 100644 --- a/container/ArrayList.h +++ b/container/ArrayList.h @@ -1,15 +1,15 @@ -#ifndef ARRAYLIST_H_ -#define ARRAYLIST_H_ +#ifndef FSFW_CONTAINER_ARRAYLIST_H_ +#define FSFW_CONTAINER_ARRAYLIST_H_ #include "../returnvalues/HasReturnvaluesIF.h" #include "../serialize/SerializeAdapter.h" #include "../serialize/SerializeIF.h" /** - * A List that stores its values in an array. - * - * The backend is an array that can be allocated by the class itself or supplied via ctor. - * + * @brief A List that stores its values in an array. + * @details + * The underlying storage is an array that can be allocated by the class + * itself or supplied via ctor. * * @ingroup container */ @@ -20,6 +20,53 @@ public: static const uint8_t INTERFACE_ID = CLASS_ID::ARRAY_LIST; static const ReturnValue_t FULL = MAKE_RETURN_CODE(0x01); + /** + * This is the allocating constructor. + * It allocates an array of the specified size. + * @param maxSize + */ + ArrayList(count_t maxSize) : + size(0), maxSize_(maxSize), allocated(true) { + entries = new T[maxSize]; + } + + /** + * This is the non-allocating constructor + * + * It expects a pointer to an array of a certain size and initializes + * itself to it. + * + * @param storage the array to use as backend + * @param maxSize size of storage + * @param size size of data already present in storage + */ + ArrayList(T *storage, count_t maxSize, count_t size = 0) : + size(size), entries(storage), maxSize_(maxSize), allocated(false) { + } + + /** + * Copying is forbiden by declaring copy ctor and copy assignment deleted + * It is too ambigous in this case. + * (Allocate a new backend? Use the same? What to do in an modifying call?) + */ + ArrayList(const ArrayList& other) = delete; + const ArrayList& operator=(const ArrayList& other) = delete; + + /** + * Number of Elements stored in this List + */ + count_t size; + + + /** + * Destructor, if the allocating constructor was used, it deletes the array. + */ + virtual ~ArrayList() { + if (allocated) { + delete[] entries; + } + } + /** * An Iterator to go trough an ArrayList * @@ -31,10 +78,7 @@ public: /** * Empty ctor, points to NULL */ - Iterator() : - value(0) { - - } + Iterator(): value(0) {} /** * Initializes the Iterator to point to an element @@ -72,71 +116,31 @@ public: return tmp; } - T& operator*(){ + T& operator*() { return *value; } - const T& operator*() const{ + const T& operator*() const { return *value; } - T *operator->(){ + T *operator->() { return value; } - const T *operator->() const{ + const T *operator->() const { return value; } + }; - //SHOULDDO this should be implemented as non-member - bool operator==(const typename ArrayList::Iterator& other) const{ - return (value == other.value); - } - - //SHOULDDO this should be implemented as non-member - bool operator!=(const typename ArrayList::Iterator& other) const { - return !(*this == other); - } - } - ; - - /** - * Number of Elements stored in this List - */ - count_t size; - - /** - * This is the allocating constructor; - * - * It allocates an array of the specified size. - * - * @param maxSize - */ - ArrayList(count_t maxSize) : - size(0), maxSize_(maxSize), allocated(true) { - entries = new T[maxSize]; + friend bool operator==(const ArrayList::Iterator& lhs, + const ArrayList::Iterator& rhs) { + return (lhs.value == rhs.value); } - /** - * This is the non-allocating constructor - * - * It expects a pointer to an array of a certain size and initializes itself to it. - * - * @param storage the array to use as backend - * @param maxSize size of storage - * @param size size of data already present in storage - */ - ArrayList(T *storage, count_t maxSize, count_t size = 0) : - size(size), entries(storage), maxSize_(maxSize), allocated(false) { - } - - /** - * Destructor, if the allocating constructor was used, it deletes the array. - */ - virtual ~ArrayList() { - if (allocated) { - delete[] entries; - } + friend bool operator!=(const ArrayList::Iterator& lhs, + const ArrayList::Iterator& rhs) { + return not (lhs.value == rhs.value); } /** @@ -192,7 +196,7 @@ public: * * @return maximum number of elements */ - uint32_t maxSize() const { + size_t maxSize() const { return this->maxSize_; } @@ -227,19 +231,7 @@ public: count_t remaining() { return (maxSize_ - size); } -private: - /** - * This is the copy constructor - * - * It is private, as copying is too ambigous in this case. (Allocate a new backend? Use the same? - * What to do in an modifying call?) - * - * @param other - */ - ArrayList(const ArrayList& other) : - size(other.size), entries(other.entries), maxSize_(other.maxSize_), allocated( - false) { - } + protected: /** * pointer to the array in which the entries are stored @@ -248,12 +240,14 @@ protected: /** * remembering the maximum size */ - uint32_t maxSize_; + size_t maxSize_; /** * true if the array was allocated and needs to be deleted in the destructor. */ bool allocated; - }; -#endif /* ARRAYLIST_H_ */ + + + +#endif /* FSFW_CONTAINER_ARRAYLIST_H_ */ diff --git a/container/FIFOBase.h b/container/FIFOBase.h index b744706d..edd66d37 100644 --- a/container/FIFOBase.h +++ b/container/FIFOBase.h @@ -19,32 +19,46 @@ public: /** * Insert value into FIFO * @param value - * @return + * @return RETURN_OK on success, FULL if full */ ReturnValue_t insert(T value); /** * Retrieve item from FIFO. This removes the item from the FIFO. - * @param value - * @return + * @param value Must point to a valid T + * @return RETURN_OK on success, EMPTY if empty and FAILED if nullptr check failed */ ReturnValue_t retrieve(T *value); /** * Retrieve item from FIFO without removing it from FIFO. - * @param value - * @return + * @param value Must point to a valid T + * @return RETURN_OK on success, EMPTY if empty and FAILED if nullptr check failed */ ReturnValue_t peek(T * value); /** * Remove item from FIFO. - * @return + * @return RETURN_OK on success, EMPTY if empty */ ReturnValue_t pop(); + /*** + * Check if FIFO is empty + * @return True if empty, False if not + */ bool empty(); + /*** + * Check if FIFO is Full + * @return True if full, False if not + */ bool full(); + /*** + * Current used size (elements) used + * @return size_t in elements + */ size_t size(); - - + /*** + * Get maximal capacity of fifo + * @return size_t with max capacity of this fifo + */ size_t getMaxCapacity() const; protected: diff --git a/container/FIFOBase.tpp b/container/FIFOBase.tpp index d54b3f8f..763004b6 100644 --- a/container/FIFOBase.tpp +++ b/container/FIFOBase.tpp @@ -26,6 +26,9 @@ inline ReturnValue_t FIFOBase::retrieve(T* value) { if (empty()) { return EMPTY; } else { + if (value == nullptr){ + return HasReturnvaluesIF::RETURN_FAILED; + } *value = values[readIndex]; readIndex = next(readIndex); --currentSize; @@ -38,6 +41,9 @@ inline ReturnValue_t FIFOBase::peek(T* value) { if(empty()) { return EMPTY; } else { + if (value == nullptr){ + return HasReturnvaluesIF::RETURN_FAILED; + } *value = values[readIndex]; return HasReturnvaluesIF::RETURN_OK; } diff --git a/container/FixedArrayList.h b/container/FixedArrayList.h index 42b59177..e9e127cf 100644 --- a/container/FixedArrayList.h +++ b/container/FixedArrayList.h @@ -2,11 +2,13 @@ #define FIXEDARRAYLIST_H_ #include "ArrayList.h" +#include /** * \ingroup container */ -template +template class FixedArrayList: public ArrayList { + static_assert(MAX_SIZE <= (pow(2,sizeof(count_t)*8)-1), "count_t is not large enough to hold MAX_SIZE"); private: T data[MAX_SIZE]; public: @@ -18,11 +20,13 @@ public: ArrayList(data, MAX_SIZE) { memcpy(this->data, other.data, sizeof(this->data)); this->entries = data; + this->size = other.size; } FixedArrayList& operator=(FixedArrayList other) { memcpy(this->data, other.data, sizeof(this->data)); this->entries = data; + this->size = other.size; return *this; } diff --git a/container/FixedMap.h b/container/FixedMap.h index d1cc31ff..7a5220fa 100644 --- a/container/FixedMap.h +++ b/container/FixedMap.h @@ -7,14 +7,21 @@ #include /** + * @brief Map implementation for maps with a pre-defined size. + * @details + * Can be initialized with desired maximum size. + * Iterator is used to access pair and iterate through map entries. + * Complexity O(n). * @warning Iterators return a non-const key_t in the pair. * @warning A User is not allowed to change the key, otherwise the map is corrupted. * @ingroup container */ template class FixedMap: public SerializeIF { - static_assert (std::is_trivially_copyable::value or std::is_base_of::value, - "Types used in FixedMap must either be trivial copy-able or a derived Class from SerializeIF to be serialize-able"); + static_assert (std::is_trivially_copyable::value or + std::is_base_of::value, + "Types used in FixedMap must either be trivial copy-able or a " + "derived class from SerializeIF to be serialize-able"); public: static const uint8_t INTERFACE_ID = CLASS_ID::FIXED_MAP; static const ReturnValue_t KEY_ALREADY_EXISTS = MAKE_RETURN_CODE(0x01); @@ -54,6 +61,16 @@ public: } }; + friend bool operator==(const typename FixedMap::Iterator& lhs, + const typename FixedMap::Iterator& rhs) { + return (lhs.value == rhs.value); + } + + friend bool operator!=(const typename FixedMap::Iterator& lhs, + const typename FixedMap::Iterator& rhs) { + return not (lhs.value == rhs.value); + } + Iterator begin() const { return Iterator(&theMap[0]); } @@ -136,6 +153,24 @@ public: return HasReturnvaluesIF::RETURN_OK; } + bool empty() { + if(_size == 0) { + return true; + } + else { + return false; + } + } + + bool full() { + if(_size >= theMap.maxSize()) { + return true; + } + else { + return false; + } + } + void clear() { _size = 0; } diff --git a/container/FixedOrderedMultimap.h b/container/FixedOrderedMultimap.h index 31282f80..96bc0073 100644 --- a/container/FixedOrderedMultimap.h +++ b/container/FixedOrderedMultimap.h @@ -1,166 +1,206 @@ -#ifndef FRAMEWORK_CONTAINER_FIXEDORDEREDMULTIMAP_H_ -#define FRAMEWORK_CONTAINER_FIXEDORDEREDMULTIMAP_H_ +#ifndef FSFW_CONTAINER_FIXEDORDEREDMULTIMAP_H_ +#define FSFW_CONTAINER_FIXEDORDEREDMULTIMAP_H_ #include "ArrayList.h" #include -#include + /** + * @brief An associative container which allows multiple entries of the same key. + * @details + * Same keys are ordered by KEY_COMPARE function which is std::less > by default. + * + * It uses the ArrayList, so technically this is not a real map, it is an array of pairs + * of type key_t, T. It is ordered by key_t as FixedMap but allows same keys. Thus it has a linear + * complexity O(n). As long as the number of entries remains low, this + * should not be an issue. + * The number of insertion and deletion operation should be minimized + * as those incur extensive memory move operations (the underlying container + * is not node based). + * + * Its of fixed size so no allocations are performed after the construction. + * + * The maximum size is given as first parameter of the constructor. + * + * It provides an iterator to do list iterations. + * + * The type T must have a copy constructor if it is not trivial copy-able. + * + * @warning Iterators return a non-const key_t in the pair. + * @warning A User is not allowed to change the key, otherwise the map is corrupted. + * * \ingroup container */ template> class FixedOrderedMultimap { public: - static const uint8_t INTERFACE_ID = CLASS_ID::FIXED_MAP; - static const ReturnValue_t KEY_ALREADY_EXISTS = MAKE_RETURN_CODE(0x01); - static const ReturnValue_t MAP_FULL = MAKE_RETURN_CODE(0x02); - static const ReturnValue_t KEY_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x03); + static const uint8_t INTERFACE_ID = CLASS_ID::FIXED_MULTIMAP; + static const ReturnValue_t MAP_FULL = MAKE_RETURN_CODE(0x01); + static const ReturnValue_t KEY_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x02); -private: - typedef KEY_COMPARE compare; - compare myComp; - ArrayList, uint32_t> theMap; - uint32_t _size; + /*** + * Constructor which needs a size_t for the maximum allowed size + * + * Can not be resized during runtime + * + * Allocates memory at construction + * @param maxSize size_t of Maximum allowed size + */ + FixedOrderedMultimap(size_t maxSize):theMap(maxSize), _size(0){ + } - uint32_t findFirstIndex(key_t key, uint32_t startAt = 0) const { - if (startAt >= _size) { - return startAt + 1; - } - uint32_t i = startAt; - for (i = startAt; i < _size; ++i) { - if (theMap[i].first == key) { - return i; - } - } - return i; - } - - uint32_t findNicePlace(key_t key) const { - uint32_t i = 0; - for (i = 0; i < _size; ++i) { - if (myComp(key, theMap[i].first)) { - return i; - } - } - return i; - } - - void removeFromPosition(uint32_t position) { - if (_size <= position) { - return; - } - memmove(static_cast(&theMap[position]), static_cast(&theMap[position + 1]), - (_size - position - 1) * sizeof(std::pair)); - --_size; - } -public: - FixedOrderedMultimap(uint32_t maxSize) : - theMap(maxSize), _size(0) { - } + /*** + * Virtual destructor frees Memory by deleting its member + */ virtual ~FixedOrderedMultimap() { } - class Iterator: public ArrayList, uint32_t>::Iterator { + /*** + * Special iterator for FixedOrderedMultimap + */ + class Iterator: public ArrayList, size_t>::Iterator { public: Iterator() : - ArrayList, uint32_t>::Iterator() { + ArrayList, size_t>::Iterator() { } Iterator(std::pair *pair) : - ArrayList, uint32_t>::Iterator(pair) { + ArrayList, size_t>::Iterator(pair) { } }; + /*** + * Returns an iterator pointing to the first element + * @return Iterator pointing to first element + */ Iterator begin() const { return Iterator(&theMap[0]); } + /** + * Returns an iterator pointing to one element past the end + * @return Iterator pointing to one element past the end + */ Iterator end() const { return Iterator(&theMap[_size]); } - uint32_t size() const { + /*** + * Returns the current size of the map (not maximum size!) + * @return Current size + */ + size_t size() const{ return _size; } - ReturnValue_t insert(key_t key, T value, Iterator *storedValue = nullptr) { - if (_size == theMap.maxSize()) { - return MAP_FULL; - } - uint32_t position = findNicePlace(key); - memmove(static_cast(&theMap[position + 1]),static_cast(&theMap[position]), - (_size - position) * sizeof(std::pair)); - theMap[position].first = key; - theMap[position].second = value; - ++_size; - if (storedValue != nullptr) { - *storedValue = Iterator(&theMap[position]); - } - return HasReturnvaluesIF::RETURN_OK; + /** + * Clears the map, does not deallocate any memory + */ + void clear(){ + _size = 0; } - ReturnValue_t insert(std::pair pair) { - return insert(pair.fist, pair.second); + /** + * Returns the maximum size of the map + * @return Maximum size of the map + */ + size_t maxSize() const{ + return theMap.maxSize(); } - ReturnValue_t exists(key_t key) const { - ReturnValue_t result = KEY_DOES_NOT_EXIST; - if (findFirstIndex(key) < _size) { - result = HasReturnvaluesIF::RETURN_OK; - } - return result; - } + /*** + * Used to insert a key and value separately. + * + * @param[in] key Key of the new element + * @param[in] value Value of the new element + * @param[in/out] (optional) storedValue On success this points to the new value, otherwise a nullptr + * @return RETURN_OK if insert was successful, MAP_FULL if no space is available + */ + ReturnValue_t insert(key_t key, T value, Iterator *storedValue = nullptr); - ReturnValue_t erase(Iterator *iter) { - uint32_t i; - if ((i = findFirstIndex((*iter).value->first)) >= _size) { - return KEY_DOES_NOT_EXIST; - } - removeFromPosition(i); - if (*iter != begin()) { - (*iter)--; - } else { - *iter = begin(); - } - return HasReturnvaluesIF::RETURN_OK; - } + /*** + * Used to insert new pair instead of single values + * + * @param pair Pair to be inserted + * @return RETURN_OK if insert was successful, MAP_FULL if no space is available + */ + ReturnValue_t insert(std::pair pair); - ReturnValue_t erase(key_t key) { - uint32_t i; - if ((i = findFirstIndex(key)) >= _size) { - return KEY_DOES_NOT_EXIST; - } - do { - removeFromPosition(i); - i = findFirstIndex(key, i); - } while (i < _size); - return HasReturnvaluesIF::RETURN_OK; - } + /*** + * Can be used to check if a certain key is in the map + * @param key Key to be checked + * @return RETURN_OK if the key exists KEY_DOES_NOT_EXIST otherwise + */ + ReturnValue_t exists(key_t key) const; - Iterator find(key_t key) const { + /*** + * Used to delete the element in the iterator + * + * The iterator will point to the element before or begin(), + * but never to one element in front of the map. + * + * @warning The iterator needs to be valid and dereferenceable + * @param[in/out] iter Pointer to iterator to the element that needs to be ereased + * @return RETURN_OK if erased, KEY_DOES_NOT_EXIST if the there is no element like this + */ + ReturnValue_t erase(Iterator *iter); + + /*** + * Used to erase by key + * @param key Key to be erased + * @return RETURN_OK if erased, KEY_DOES_NOT_EXIST if the there is no element like this + */ + ReturnValue_t erase(key_t key); + + /*** + * Find returns the first appearance of the key + * + * If the key does not exist, it points to end() + * + * @param key Key to search for + * @return Iterator pointing to the first entry of key + */ + Iterator find(key_t key) const{ ReturnValue_t result = exists(key); if (result != HasReturnvaluesIF::RETURN_OK) { return end(); } return Iterator(&theMap[findFirstIndex(key)]); + }; + + /*** + * Finds first entry of the given key and returns a + * pointer to the value + * + * @param key Key to search for + * @param value Found value + * @return RETURN_OK if it points to the value, + * KEY_DOES_NOT_EXIST if the key is not in the map + */ + ReturnValue_t find(key_t key, T **value) const; + + friend bool operator==(const typename FixedOrderedMultimap::Iterator& lhs, + const typename FixedOrderedMultimap::Iterator& rhs) { + return (lhs.value == rhs.value); } - ReturnValue_t find(key_t key, T **value) const { - ReturnValue_t result = exists(key); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - *value = &theMap[findFirstIndex(key)].second; - return HasReturnvaluesIF::RETURN_OK; + friend bool operator!=(const typename FixedOrderedMultimap::Iterator& lhs, + const typename FixedOrderedMultimap::Iterator& rhs) { + return not (lhs.value == rhs.value); } - void clear() { - _size = 0; - } +private: + typedef KEY_COMPARE compare; + compare myComp; + ArrayList, size_t> theMap; + size_t _size; - uint32_t maxSize() const { - return theMap.maxSize(); - } + size_t findFirstIndex(key_t key, size_t startAt = 0) const; + size_t findNicePlace(key_t key) const; + + void removeFromPosition(size_t position); }; -#endif /* FRAMEWORK_CONTAINER_FIXEDORDEREDMULTIMAP_H_ */ +#include "FixedOrderedMultimap.tpp" + +#endif /* FSFW_CONTAINER_FIXEDORDEREDMULTIMAP_H_ */ diff --git a/container/FixedOrderedMultimap.tpp b/container/FixedOrderedMultimap.tpp new file mode 100644 index 00000000..4aa85e97 --- /dev/null +++ b/container/FixedOrderedMultimap.tpp @@ -0,0 +1,109 @@ +#ifndef FRAMEWORK_CONTAINER_FIXEDORDEREDMULTIMAP_TPP_ +#define FRAMEWORK_CONTAINER_FIXEDORDEREDMULTIMAP_TPP_ + + +template +inline ReturnValue_t FixedOrderedMultimap::insert(key_t key, T value, Iterator *storedValue) { + if (_size == theMap.maxSize()) { + return MAP_FULL; + } + size_t position = findNicePlace(key); + memmove(static_cast(&theMap[position + 1]),static_cast(&theMap[position]), + (_size - position) * sizeof(std::pair)); + theMap[position].first = key; + theMap[position].second = value; + ++_size; + if (storedValue != nullptr) { + *storedValue = Iterator(&theMap[position]); + } + return HasReturnvaluesIF::RETURN_OK; +} +template +inline ReturnValue_t FixedOrderedMultimap::insert(std::pair pair) { + return insert(pair.first, pair.second); +} + +template +inline ReturnValue_t FixedOrderedMultimap::exists(key_t key) const { + ReturnValue_t result = KEY_DOES_NOT_EXIST; + if (findFirstIndex(key) < _size) { + result = HasReturnvaluesIF::RETURN_OK; + } + return result; +} + +template +inline ReturnValue_t FixedOrderedMultimap::erase(Iterator *iter) { + size_t i; + if ((i = findFirstIndex((*iter).value->first)) >= _size) { + return KEY_DOES_NOT_EXIST; + } + removeFromPosition(i); + if (*iter != begin()) { + (*iter)--; + } else { + *iter = begin(); + } + return HasReturnvaluesIF::RETURN_OK; +} + +template +inline ReturnValue_t FixedOrderedMultimap::erase(key_t key) { + size_t i; + if ((i = findFirstIndex(key)) >= _size) { + return KEY_DOES_NOT_EXIST; + } + do { + removeFromPosition(i); + i = findFirstIndex(key, i); + } while (i < _size); + return HasReturnvaluesIF::RETURN_OK; +} + +template +inline ReturnValue_t FixedOrderedMultimap::find(key_t key, T **value) const { + ReturnValue_t result = exists(key); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + *value = &theMap[findFirstIndex(key)].second; + return HasReturnvaluesIF::RETURN_OK; +} + +template +inline size_t FixedOrderedMultimap::findFirstIndex(key_t key, size_t startAt) const { + if (startAt >= _size) { + return startAt + 1; + } + size_t i = startAt; + for (i = startAt; i < _size; ++i) { + if (theMap[i].first == key) { + return i; + } + } + return i; +} + +template +inline size_t FixedOrderedMultimap::findNicePlace(key_t key) const { + size_t i = 0; + for (i = 0; i < _size; ++i) { + if (myComp(key, theMap[i].first)) { + return i; + } + } + return i; +} + +template +inline void FixedOrderedMultimap::removeFromPosition(size_t position) { + if (_size <= position) { + return; + } + memmove(static_cast(&theMap[position]), static_cast(&theMap[position + 1]), + (_size - position - 1) * sizeof(std::pair)); + --_size; +} + + +#endif /* FRAMEWORK_CONTAINER_FIXEDORDEREDMULTIMAP_TPP_ */ diff --git a/container/PlacementFactory.h b/container/PlacementFactory.h index 05538478..a0aebb7d 100644 --- a/container/PlacementFactory.h +++ b/container/PlacementFactory.h @@ -3,26 +3,62 @@ #include "../storagemanager/StorageManagerIF.h" #include - +/** + * The Placement Factory is used to create objects at runtime in a specific pool. + * In general, this should be avoided and it should only be used if you know what you are doing. + * You are not allowed to use this container with a type that allocates memory internally like ArrayList. + * + * Also, you have to check the returned pointer in generate against nullptr! + * + * A backend of Type StorageManagerIF must be given as a place to store the new objects. + * Therefore ThreadSafety is only provided by your StorageManager Implementation. + * + * Objects must be destroyed by the user with "destroy"! Otherwise the pool will not be cleared. + * + * The concept is based on the placement new operator. + * + * @warning Do not use with any Type that allocates memory internally! + * @ingroup container + */ class PlacementFactory { public: PlacementFactory(StorageManagerIF* backend) : dataBackend(backend) { } + + /*** + * Generates an object of type T in the backend storage. + * + * @warning Do not use with any Type that allocates memory internally! + * + * @tparam T Type of Object + * @param args Constructor Arguments to be passed + * @return A pointer to the new object or a nullptr in case of failure + */ template T* generate(Args&&... args) { store_address_t tempId; - uint8_t* pData = NULL; + uint8_t* pData = nullptr; ReturnValue_t result = dataBackend->getFreeElement(&tempId, sizeof(T), &pData); if (result != HasReturnvaluesIF::RETURN_OK) { - return NULL; + return nullptr; } T* temp = new (pData) T(std::forward(args)...); return temp; } + /*** + * Function to destroy the object allocated with generate and free space in backend. + * This must be called by the user. + * + * @param thisElement Element to be destroyed + * @return RETURN_OK if the element was destroyed, different errors on failure + */ template ReturnValue_t destroy(T* thisElement) { + if (thisElement == nullptr){ + return HasReturnvaluesIF::RETURN_FAILED; + } //Need to call destructor first, in case something was allocated by the object (shouldn't do that, however). thisElement->~T(); uint8_t* pointer = (uint8_t*) (thisElement); diff --git a/container/RingBufferTest.cpp.ignore b/container/RingBufferTest.cpp.ignore deleted file mode 100644 index d660e29c..00000000 --- a/container/RingBufferTest.cpp.ignore +++ /dev/null @@ -1,79 +0,0 @@ - -#include -#include "SimpleRingBuffer.h" - - -int main() { - using namespace std; - SimpleRingBuffer buffer(64, false); - uint8_t data[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; - ReturnValue_t result = buffer.writeData(data, 8); - if (result != HasReturnvaluesIF::RETURN_OK) { - cout << "writeData failed." << endl; - } - result = buffer.writeData(data, 8); - if (result != HasReturnvaluesIF::RETURN_OK) { - cout << "writeData failed." << endl; - } - uint8_t buffer2[47] = {0}; - for (uint8_t count = 0; countcreateMutex(); } + SharedRingBuffer::SharedRingBuffer(object_id_t objectId, uint8_t *buffer, const size_t size, bool overwriteOld, size_t maxExcessBytes): SystemObject(objectId), SimpleRingBuffer(buffer, size, overwriteOld, @@ -16,6 +17,11 @@ SharedRingBuffer::SharedRingBuffer(object_id_t objectId, uint8_t *buffer, mutex = MutexFactory::instance()->createMutex(); } + +void SharedRingBuffer::setToUseReceiveSizeFIFO(size_t fifoDepth) { + this->fifoDepth = fifoDepth; +} + ReturnValue_t SharedRingBuffer::lockRingBufferMutex( MutexIF::TimeoutType timeoutType, dur_millis_t timeout) { return mutex->lockMutex(timeoutType, timeout); @@ -25,6 +31,25 @@ ReturnValue_t SharedRingBuffer::unlockRingBufferMutex() { return mutex->unlockMutex(); } + + MutexIF* SharedRingBuffer::getMutexHandle() const { return mutex; } + +ReturnValue_t SharedRingBuffer::initialize() { + if(fifoDepth > 0) { + receiveSizesFIFO = new DynamicFIFO(fifoDepth); + } + return SystemObject::initialize(); +} + +DynamicFIFO* SharedRingBuffer::getReceiveSizesFIFO() { + if(receiveSizesFIFO == nullptr) { + // Configuration error. + sif::warning << "SharedRingBuffer::getReceiveSizesFIFO: Ring buffer" + << " was not configured to have sizes FIFO, returning nullptr!" + << std::endl; + } + return receiveSizesFIFO; +} diff --git a/container/SharedRingBuffer.h b/container/SharedRingBuffer.h index 80c068b3..64d7ee29 100644 --- a/container/SharedRingBuffer.h +++ b/container/SharedRingBuffer.h @@ -2,6 +2,7 @@ #define FSFW_CONTAINER_SHAREDRINGBUFFER_H_ #include "SimpleRingBuffer.h" +#include "DynamicFIFO.h" #include "../ipc/MutexIF.h" #include "../objectmanager/SystemObject.h" #include "../timemanager/Clock.h" @@ -26,6 +27,16 @@ public: SharedRingBuffer(object_id_t objectId, const size_t size, bool overwriteOld, size_t maxExcessBytes); + /** + * @brief This function can be used to add an optional FIFO to the class + * @details + * This FIFO will be allocated in the initialize function (and will + * have a fixed maximum size after that). It can be used to store + * values like packet sizes, for example for a shared ring buffer + * used by producer/consumer tasks. + */ + void setToUseReceiveSizeFIFO(size_t fifoDepth); + /** * This constructor takes an external buffer with the specified size. * @param buffer @@ -59,8 +70,21 @@ public: * @return */ MutexIF* getMutexHandle() const; + + ReturnValue_t initialize() override; + + /** + * If the shared ring buffer was configured to have a sizes FIFO, a handle + * to that FIFO can be retrieved with this function. + * Do not forget to protect access with a lock if required! + * @return + */ + DynamicFIFO* getReceiveSizesFIFO(); private: MutexIF* mutex = nullptr; + + size_t fifoDepth = 0; + DynamicFIFO* receiveSizesFIFO = nullptr; }; diff --git a/container/listTest.cpp.ignore b/container/listTest.cpp.ignore deleted file mode 100644 index 257b733f..00000000 --- a/container/listTest.cpp.ignore +++ /dev/null @@ -1,365 +0,0 @@ -#include "FixedArrayList.h" -#include "SinglyLinkedList.h" -#include "HybridIterator.h" - -#include "FixedMap.h" - -#include - -/* - -class Packet: public SinglyLinkedList { -public: - SinglyLinkedList::Element element1; - SinglyLinkedList::Element element2; - - Packet() { - this->start = &element1; - element1.next = &element2; - } -}; - -class Packet2: public SinglyLinkedList { -public: - SinglyLinkedList::Element element1; - SinglyLinkedList::Element, 2>> element2; - SinglyLinkedList::Element element3; - - Packet2() { - this->start = &element1; - element1.next = &element2; - element2.next = &element3; - } -}; - -class Packet3: public SinglyLinkedList { -public: - SinglyLinkedList::TypedElement element1; - SinglyLinkedList::TypedElement element2; - - Packet3() { - this->start = &element1; - element1.next = &element2; - } -}; - -void arrayList() { - puts("** Array List **"); - FixedArrayList list; - FixedArrayList list2; - - list.size = 2; - - list[0] = 0xcafecafe; - - list[1] = 0x12345678; - - uint8_t buffer[100]; - uint8_t *pointer = buffer; - uint32_t size = 0; - uint32_t maxSize = 100; - uint32_t i; - int32_t size2; - - printf("printsize: %i\n", list.getPrintSize()); - - list.print(&pointer, &size, 100, true); - - printf("buffer(%i):", size); - for (i = 0; i < size; ++i) { - printf("%02x", buffer[i]); - } - printf("\n"); - - pointer = buffer; - - size2 = size; - - printf("list2 read: %x\n", list2.read(&pointer, &size2, true)); - - printf("list2(%i):", list2.size); - for (ArrayList::Iterator iter = list2.begin(); - iter != list2.end(); iter++) { - printf("0x%04x ", *iter); - } - printf("\n"); - - HybridIterator hiter(list.begin(),list.end()); - - printf("hybrid1: 0x%04x\n", *(hiter++)); - printf("hybrid2: 0x%04x\n", *hiter); - -} - -void allocatingList() { - puts("** Allocating List **"); - ArrayList myList(3), myList2(2); - myList[0] = 0xab; - myList[1] = 0xcd; - myList.size = 2; - - uint8_t buffer[100]; - uint8_t *pointer = buffer; - uint32_t size = 0; - uint32_t maxSize = 100; - uint32_t i; - int32_t size2; - - myList.print(&pointer, &size, 100, true); - - pointer = buffer; - size2 = size; - - printf("Read %x\n", myList2.read(&pointer, &size2, true)); - - printf("%x,%x\n", myList2[0], myList2[1]); - -} - -void linkedList() { - puts("** Linked List **"); - uint8_t buffer[100]; - uint8_t *pointer = buffer; - uint32_t size = 0; - uint32_t maxSize = 100; - uint32_t i; - int32_t size2; - - Packet myPacket; - myPacket.element1.entry = 0x12345678; - myPacket.element2.entry = 0x9abcdef0; - - pointer = buffer; - size = 0; - ReturnValue_t result = myPacket.print(&pointer, &size, 100, true); - - printf("result %02x\n", result); - - printf("printsize: %i\n", myPacket.getPrintSize()); - - printf("buffer(%i):", size); - for (i = 0; i < size; ++i) { - printf("%02x", buffer[i]); - } - printf("\n"); - - Packet3 myPacket3; - - myPacket3.element1.entry = 0x12345678; - myPacket3.element2.entry = 0xabcdeff; - - SinglyLinkedList::TypedIterator titer(&myPacket3.element1); - - printf("0x%04x\n", *titer); - - HybridIterator hiter(&myPacket3.element1); - - printf("hybrid1: 0x%04x\n", *hiter); - hiter++; - printf("hybrid2: 0x%04x\n", *hiter); -} - -void complex() { - puts("** complex **"); - uint8_t buffer[100]; - uint8_t *pointer = buffer; - uint32_t size = 0; - uint32_t maxSize = 100; - uint32_t i; - int32_t size2 = size; - - Packet myPacket2; - - size2 = size; - pointer = buffer; - - myPacket2.read(&pointer, &size2, true); - - printf("packet: 0x%04x, 0x%04x\n", myPacket2.element1.entry, - myPacket2.element2.entry); - - buffer[0] = 0x12; - buffer[1] = 0x34; - buffer[2] = 0x56; - buffer[3] = 0x78; - buffer[4] = 0x2; - buffer[5] = 0x3; - buffer[6] = 0xab; - buffer[7] = 0xcd; - buffer[8] = 0xef; - buffer[9] = 0x2; - buffer[10] = 0x11; - buffer[11] = 0x22; - buffer[12] = 0xca; - buffer[13] = 0xfe; - buffer[14] = 0x5a; - buffer[15] = 0xfe; - - pointer = buffer; - size2 = 23; - - Packet2 p2; - - ReturnValue_t result = p2.read(&pointer, &size2, true); - printf("result is %02x\n", result); - - printf("%04x; %i: %i: %x %x %x; %i: %x %x;; %04x\n", p2.element1.entry, - p2.element2.entry.size, p2.element2.entry[0].size, - p2.element2.entry[0][0], p2.element2.entry[0][1], - p2.element2.entry[0][2], p2.element2.entry[1].size, - p2.element2.entry[1][0], p2.element2.entry[1][1], - p2.element3.entry); - -} -*/ -struct Test { - uint32_t a; - uint32_t b; -}; - -template -void printMap(FixedMap *map) { - typename FixedMap::Iterator iter; - printf("Map (%i): ", map->getSize()); - for (iter = map->begin(); iter != map->end(); ++iter) { - printf("%x:%08x,%08x ", iter.value->first, (*iter).a, (*iter).b); - } - printf("\n"); -} - -template -void map() { - puts("** Map **"); - typename FixedMap::Iterator iter; - ReturnValue_t result; - FixedMap myMap(5); - - printMap(&myMap); - - Test a; - a.a = 0x01234567; - a.b = 0xabcdef89; - - myMap.insert(1, a); - printMap(&myMap); - - a.a = 0; - - myMap.insert(2, a); - printMap(&myMap); - - printf("2 exists: %x\n", myMap.exists(0x02)); - - printf("ff exists: %x\n", myMap.exists(0xff)); - - a.a = 1; - printf("insert 0x2: %x\n", myMap.insert(2, a)); - - result = myMap.insert(0xff, a); - a.a = 0x44; - result = myMap.insert(0xab, a); - result = myMap.insert(0xa, a); - - printMap(&myMap); - - printf("insert 0x5: %x\n", myMap.insert(5, a)); - - printf("erase 0xfe: %x\n", myMap.erase(0xfe)); - - printf("erase 0x2: %x\n", myMap.erase(0x2)); - - printMap(&myMap); - - printf("erase 0xab: %x\n", myMap.erase(0xab)); - printMap(&myMap); - - printf("insert 0x5: %x\n", myMap.insert(5, a)); - printMap(&myMap); - - iter = myMap.begin(); - ++iter; - ++iter; - ++iter; - - printf("iter: %i: %x,%x\n",iter.value->first, iter->a, iter->b); - - myMap.erase(&iter); - - printf("iter: %i: %x,%x\n",iter.value->first, iter->a, iter->b); - printMap(&myMap); - -} - -/* -void mapPrint() { - puts("** Map Print **"); - FixedMap myMap(5); - Packet2 myPacket; - myPacket.element1.entry = 0x12345678; - - myPacket.element2.entry[0][0] = 0xab; - myPacket.element2.entry[0][1] = 0xcd; - myPacket.element2.entry[0].size = 2; - myPacket.element2.entry.size = 1; - - myPacket.element3.entry = 0xabcdef90; - - myMap.insert(0x1234, myPacket); - - uint8_t buffer[100]; - uint32_t size = 0, i; - uint8_t *pointer = buffer; - - printf("printsize: %i\n", myMap.getPrintSize()); - - SerializeAdapter>::print(&myMap, &pointer, - &size, 100, false); - - printf("buffer(%i):", size); - for (i = 0; i < size; ++i) { - printf("%02x", buffer[i]); - } - printf("\n"); - - int32_t size2 = size; - pointer = buffer; - - FixedMap myMap2(5); - - ReturnValue_t result = SerializeAdapter>::read( - &myMap2, &pointer, &size2, false); - - Packet2 *myPacket2 = myMap2.find(0x1234); - - printf("Map (%i): Packet2: %x, Array (%i): Array (%i): %x, %x; %x\n", - myMap2.getSize(), myPacket2->element1.entry, - myPacket2->element2.entry.size, myPacket2->element2.entry[0].size, - myPacket2->element2.entry[0][0], myPacket2->element2.entry[0][1], - myPacket2->element3.entry); - -} - -void empty() { - puts("** Empty **"); - ArrayList list(0); - printf("%p %p\n", list.front(), list.back()); -} -*/ - -int main(void) { - -// arrayList(); -// linkedList(); -// allocatingList(); -// complex(); - - map(); -// -// mapPrint(); - -// empty(); - - - - return 0; -} diff --git a/datalinklayer/MapPacketExtraction.cpp b/datalinklayer/MapPacketExtraction.cpp index cb12b321..d64348e1 100644 --- a/datalinklayer/MapPacketExtraction.cpp +++ b/datalinklayer/MapPacketExtraction.cpp @@ -18,7 +18,7 @@ MapPacketExtraction::MapPacketExtraction(uint8_t setMapId, object_id_t setPacketDestination) : lastSegmentationFlag(NO_SEGMENTATION), mapId(setMapId), packetLength(0), bufferPosition( packetBuffer), packetDestination(setPacketDestination), packetStore( - NULL), tcQueueId(MessageQueueSenderIF::NO_QUEUE) { + NULL), tcQueueId(MessageQueueIF::NO_QUEUE) { memset(packetBuffer, 0, sizeof(packetBuffer)); } diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 017ea8d2..710e8a83 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -1224,7 +1224,7 @@ void DeviceHandlerBase::buildInternalCommand(void) { if (result == BUSY) { //so we can track misconfigurations sif::debug << std::hex << getObjectId() - << ": DHB::buildInternalCommand: Busy" << std::endl; + << ": DHB::buildInternalCommand: Busy" << std::dec << std::endl; result = NOTHING_TO_SEND; //no need to report this } } diff --git a/devicehandlers/FixedSequenceSlot.cpp b/devicehandlers/FixedSequenceSlot.cpp deleted file mode 100644 index 667aba1d..00000000 --- a/devicehandlers/FixedSequenceSlot.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @file PollingSlot.cpp - * @brief This file defines the PollingSlot class. - * @date 19.12.2012 - * @author baetz - */ - -#include "FixedSequenceSlot.h" -#include "../objectmanager/SystemObjectIF.h" -#include - -FixedSequenceSlot::FixedSequenceSlot(object_id_t handlerId, uint32_t setTime, - int8_t setSequenceId, PeriodicTaskIF* executingTask) : - handler(NULL), pollingTimeMs(setTime), opcode(setSequenceId) { - handler = objectManager->get(handlerId); - handler->setTaskIF(executingTask); -} - -FixedSequenceSlot::~FixedSequenceSlot() {} - diff --git a/devicehandlers/HealthDevice.cpp b/devicehandlers/HealthDevice.cpp index 411731dc..b15e5d2b 100644 --- a/devicehandlers/HealthDevice.cpp +++ b/devicehandlers/HealthDevice.cpp @@ -5,7 +5,7 @@ HealthDevice::HealthDevice(object_id_t setObjectId, MessageQueueId_t parentQueue) : SystemObject(setObjectId), lastHealth(HEALTHY), parentQueue( parentQueue), commandQueue(), healthHelper(this, setObjectId) { - commandQueue = QueueFactory::instance()->createMessageQueue(3, CommandMessage::COMMAND_MESSAGE_SIZE); + commandQueue = QueueFactory::instance()->createMessageQueue(3); } HealthDevice::~HealthDevice() { diff --git a/events/Event.h b/events/Event.h index e22c9db1..f8410f32 100644 --- a/events/Event.h +++ b/events/Event.h @@ -4,7 +4,7 @@ #include #include "fwSubsystemIdRanges.h" //could be move to more suitable location -#include +#include typedef uint16_t EventId_t; typedef uint8_t EventSeverity_t; diff --git a/ipc/CommandMessage.cpp b/ipc/CommandMessage.cpp index dbea8533..513debd3 100644 --- a/ipc/CommandMessage.cpp +++ b/ipc/CommandMessage.cpp @@ -1,124 +1,96 @@ -/** - * @file CommandMessage.cpp - * @brief This file defines the CommandMessage class. - * @date 20.06.2013 - * @author baetz - */ - -#include "../devicehandlers/DeviceHandlerMessage.h" -#include "../health/HealthMessage.h" #include "CommandMessage.h" -#include "../memory/MemoryMessage.h" -#include "../modes/ModeMessage.h" -#include "../monitoring/MonitoringMessage.h" -#include "../subsystem/modes/ModeSequenceMessage.h" -#include "../tmstorage/TmStoreMessage.h" -#include "../parameters/ParameterMessage.h" - -namespace messagetypes { -void clearMissionMessage(CommandMessage* message); -} - +#include "CommandMessageCleaner.h" +#include CommandMessage::CommandMessage() { - this->messageSize = COMMAND_MESSAGE_SIZE; - setCommand(CMD_NONE); + MessageQueueMessage::setMessageSize(DEFAULT_COMMAND_MESSAGE_SIZE); + setCommand(CMD_NONE); } CommandMessage::CommandMessage(Command_t command, uint32_t parameter1, - uint32_t parameter2) { - this->messageSize = COMMAND_MESSAGE_SIZE; - setCommand(command); - setParameter(parameter1); - setParameter2(parameter2); + uint32_t parameter2) { + MessageQueueMessage::setMessageSize(DEFAULT_COMMAND_MESSAGE_SIZE); + setCommand(command); + setParameter(parameter1); + setParameter2(parameter2); } Command_t CommandMessage::getCommand() const { - Command_t command; - memcpy(&command, getData(), sizeof(Command_t)); - return command; + Command_t command; + std::memcpy(&command, MessageQueueMessage::getData(), sizeof(Command_t)); + return command; } void CommandMessage::setCommand(Command_t command) { - memcpy(getData(), &command, sizeof(command)); + std::memcpy(MessageQueueMessage::getData(), &command, sizeof(Command_t)); +} + +uint8_t CommandMessage::getMessageType() const { + // first byte of command ID. + return getCommand() >> 8 & 0xff; } uint32_t CommandMessage::getParameter() const { - uint32_t parameter1; - memcpy(¶meter1, getData() + sizeof(Command_t), sizeof(parameter1)); - return parameter1; + uint32_t parameter1; + std::memcpy(¶meter1, this->getData(), sizeof(parameter1)); + return parameter1; } void CommandMessage::setParameter(uint32_t parameter1) { - memcpy(getData() + sizeof(Command_t), ¶meter1, sizeof(parameter1)); + std::memcpy(this->getData(), ¶meter1, sizeof(parameter1)); } uint32_t CommandMessage::getParameter2() const { - uint32_t parameter2; - memcpy(¶meter2, getData() + sizeof(Command_t) + sizeof(uint32_t), - sizeof(parameter2)); - return parameter2; + uint32_t parameter2; + std::memcpy(¶meter2, this->getData() + sizeof(uint32_t), + sizeof(parameter2)); + return parameter2; } void CommandMessage::setParameter2(uint32_t parameter2) { - memcpy(getData() + sizeof(Command_t) + sizeof(uint32_t), ¶meter2, - sizeof(parameter2)); + std::memcpy(this->getData() + sizeof(uint32_t), ¶meter2, + sizeof(parameter2)); } -void CommandMessage::clearCommandMessage() { - switch((getCommand()>>8) & 0xff){ - case messagetypes::MODE_COMMAND: - ModeMessage::clear(this); - break; - case messagetypes::HEALTH_COMMAND: - HealthMessage::clear(this); - break; - case messagetypes::MODE_SEQUENCE: - ModeSequenceMessage::clear(this); - break; - case messagetypes::ACTION: - ActionMessage::clear(this); - break; - case messagetypes::DEVICE_HANDLER_COMMAND: - DeviceHandlerMessage::clear(this); - break; - case messagetypes::MEMORY: - MemoryMessage::clear(this); - break; - case messagetypes::MONITORING: - MonitoringMessage::clear(this); - break; - case messagetypes::TM_STORE: - TmStoreMessage::clear(this); - break; - case messagetypes::PARAMETER: - ParameterMessage::clear(this); - break; - default: - messagetypes::clearMissionMessage(this); - break; - } +uint32_t CommandMessage::getParameter3() const { + uint32_t parameter3; + std::memcpy(¶meter3, this->getData() + 2 * sizeof(uint32_t), + sizeof(parameter3)); + return parameter3; } -bool CommandMessage::isClearedCommandMessage() { - return getCommand() == CMD_NONE; +void CommandMessage::setParameter3(uint32_t parameter3) { + std::memcpy(this->getData() + 2 * sizeof(uint32_t), ¶meter3, + sizeof(parameter3)); } size_t CommandMessage::getMinimumMessageSize() const { - return COMMAND_MESSAGE_SIZE; + return MINIMUM_COMMAND_MESSAGE_SIZE; +} + +void CommandMessage::clearCommandMessage() { + clear(); +} + +void CommandMessage::clear() { + CommandMessageCleaner::clearCommandMessage(this); +} + +bool CommandMessage::isClearedCommandMessage() { + return getCommand() == CMD_NONE; } void CommandMessage::setToUnknownCommand() { - Command_t initialCommand = getCommand(); - clearCommandMessage(); - setReplyRejected(UNKNOWN_COMMAND, initialCommand); + Command_t initialCommand = getCommand(); + this->clear(); + setReplyRejected(UNKNOWN_COMMAND, initialCommand); } void CommandMessage::setReplyRejected(ReturnValue_t reason, - Command_t initialCommand) { + Command_t initialCommand) { setCommand(REPLY_REJECTED); - setParameter(reason); - setParameter2(initialCommand); + setParameter(reason); + setParameter2(initialCommand); } ReturnValue_t CommandMessage::getReplyRejectedReason( @@ -129,3 +101,11 @@ ReturnValue_t CommandMessage::getReplyRejectedReason( } return reason; } + +uint8_t* CommandMessage::getData() { + return MessageQueueMessage::getData() + sizeof(Command_t); +} + +const uint8_t* CommandMessage::getData() const { + return MessageQueueMessage::getData() + sizeof(Command_t); +} diff --git a/ipc/CommandMessage.h b/ipc/CommandMessage.h index e984ad3d..28822dd0 100644 --- a/ipc/CommandMessage.h +++ b/ipc/CommandMessage.h @@ -1,114 +1,88 @@ -/** - * @file CommandMessage.h - * @brief This file defines the CommandMessage class. - * @date 20.06.2013 - * @author baetz - */ +#ifndef FSFW_IPC_COMMANDMESSAGE_H_ +#define FSFW_IPC_COMMANDMESSAGE_H_ -#ifndef COMMANDMESSAGE_H_ -#define COMMANDMESSAGE_H_ - - -#include "FwMessageTypes.h" -#include +#include "CommandMessageIF.h" #include "MessageQueueMessage.h" +#include "FwMessageTypes.h" -#define MAKE_COMMAND_ID( number ) ((MESSAGE_ID << 8) + (number)) -typedef ReturnValue_t Command_t; - -class CommandMessage : public MessageQueueMessage { +/** + * @brief Default command message used to pass command messages between tasks. + * Primary message type for IPC. Contains sender, 2-byte command ID + * field, and 3 4-byte parameter + * @details + * It operates on an external memory which is contained inside a + * class implementing MessageQueueMessageIF by taking its address. + * This allows for a more flexible designs of message implementations. + * The pointer can be passed to different message implementations without + * the need of unnecessary copying. + * + * The command message is based of the generic MessageQueueMessage which + * currently has an internal message size of 28 bytes. + * @author Bastian Baetz + */ +class CommandMessage: public MessageQueueMessage, public CommandMessageIF { public: - static const uint8_t INTERFACE_ID = CLASS_ID::COMMAND_MESSAGE; - static const ReturnValue_t UNKNOWN_COMMAND = MAKE_RETURN_CODE(0x01); - - - static const uint8_t MESSAGE_ID = messagetypes::COMMAND; - static const Command_t CMD_NONE = MAKE_COMMAND_ID( 0 );//!< Used internally, will be ignored - static const Command_t REPLY_COMMAND_OK = MAKE_COMMAND_ID( 3 ); - static const Command_t REPLY_REJECTED = MAKE_COMMAND_ID( 0xD1 );//!< Reply indicating that the current command was rejected, par1 should contain the error code + /** + * Default size can accomodate 3 4-byte parameters. + */ + static constexpr size_t DEFAULT_COMMAND_MESSAGE_SIZE = + CommandMessageIF::MINIMUM_COMMAND_MESSAGE_SIZE + + 3 * sizeof(uint32_t); /** - * This is the size of a message as it is seen by the MessageQueue - */ - static const size_t COMMAND_MESSAGE_SIZE = HEADER_SIZE - + sizeof(Command_t) + 2 * sizeof(uint32_t); - - /** - * Default Constructor, does not initialize anything. - * - * This constructor should be used when receiving a Message, as the content is filled by the MessageQueue. + * @brief Default Constructor, does not initialize anything. + * @details + * This constructor should be used when receiving a Message, as the + * content is filled by the MessageQueue. */ CommandMessage(); /** - * This constructor creates a new message with all message content initialized + * This constructor creates a new message with all message content + * initialized * * @param command The DeviceHandlerCommand_t that will be sent * @param parameter1 The first parameter * @param parameter2 The second parameter */ - CommandMessage(Command_t command, - uint32_t parameter1, uint32_t parameter2); + CommandMessage(Command_t command, uint32_t parameter1, uint32_t parameter2); /** - * Default Destructor + * @brief Default Destructor */ - virtual ~CommandMessage() { - } + virtual ~CommandMessage() {} - /** - * Read the DeviceHandlerCommand_t that is stored in the message, usually used after receiving - * - * @return the Command stored in the Message - */ - Command_t getCommand() const; + /** + * Read the DeviceHandlerCommand_t that is stored in the message, + * usually used after receiving. + * + * @return the Command stored in the Message + */ + virtual Command_t getCommand() const override; + /** + * Set the command type of the message. Default implementation also + * sets the message type, which will be the first byte of the command ID. + * @param the Command to be sent + */ + virtual void setCommand(Command_t command); - /** - * Set the DeviceHandlerCOmmand_t of the message - * - * @param the Command to be sent - */ - void setCommand(Command_t command); + virtual uint8_t* getData() override; + virtual const uint8_t* getData() const override; /** * Get the first parameter of the message - * * @return the first Parameter of the message */ uint32_t getParameter() const; - /** * Set the first parameter of the message - * * @param the first parameter of the message */ void setParameter(uint32_t parameter1); - - /** - * Get the second parameter of the message - * - * @return the second Parameter of the message - */ uint32_t getParameter2() const; - - /** - * Set the second parameter of the message - * - * @param the second parameter of the message - */ void setParameter2(uint32_t parameter2); - - /** - * Set the command to CMD_NONE and try to find - * the correct class to handle a more detailed - * clear. - * Also, calls a mission-specific clearMissionMessage - * function to separate between framework and mission - * messages. Not optimal, may be replaced by totally - * different auto-delete solution (e.g. smart pointers). - * - */ - void clearCommandMessage(); + uint32_t getParameter3() const; + void setParameter3(uint32_t parameter3); /** * check if a message was cleared @@ -117,18 +91,41 @@ public: */ bool isClearedCommandMessage(); - /** * Sets the command to REPLY_REJECTED with parameter UNKNOWN_COMMAND. * Is needed quite often, so we better code it once only. */ - void setToUnknownCommand(); - void setReplyRejected(ReturnValue_t reason, Command_t initialCommand = CMD_NONE); - ReturnValue_t getReplyRejectedReason( - Command_t *initialCommand = nullptr) const; + void setToUnknownCommand() override; - size_t getMinimumMessageSize() const; + /** + * A command message can be rejected and needs to offer a function + * to set a rejected reply + * @param reason + * @param initialCommand + */ + void setReplyRejected(ReturnValue_t reason, + Command_t initialCommand) override; + /** + * Corrensonding getter function. + * @param initialCommand + * @return + */ + ReturnValue_t getReplyRejectedReason( + Command_t* initialCommand = nullptr) const override; + + + virtual void clear() override; + void clearCommandMessage(); + + /** + * Extract message ID, which is the first byte of the command ID for the + * default implementation. + * @return + */ + virtual uint8_t getMessageType() const override; + + /** MessageQueueMessageIF functions used for minimum size check. */ + size_t getMinimumMessageSize() const override; }; - -#endif /* COMMANDMESSAGE_H_ */ +#endif /* FSFW_IPC_COMMANDMESSAGE_H_ */ diff --git a/ipc/CommandMessageCleaner.cpp b/ipc/CommandMessageCleaner.cpp new file mode 100644 index 00000000..6a99b4d2 --- /dev/null +++ b/ipc/CommandMessageCleaner.cpp @@ -0,0 +1,45 @@ +#include "../ipc/CommandMessageCleaner.h" + +#include "../devicehandlers/DeviceHandlerMessage.h" +#include "../health/HealthMessage.h" +#include "../memory/MemoryMessage.h" +#include "../modes/ModeMessage.h" +#include "../monitoring/MonitoringMessage.h" +#include "../subsystem/modes/ModeSequenceMessage.h" +#include "../tmstorage/TmStoreMessage.h" +#include "../parameters/ParameterMessage.h" + +void CommandMessageCleaner::clearCommandMessage(CommandMessage* message) { + switch(message->getMessageType()){ + case messagetypes::MODE_COMMAND: + ModeMessage::clear(message); + break; + case messagetypes::HEALTH_COMMAND: + HealthMessage::clear(message); + break; + case messagetypes::MODE_SEQUENCE: + ModeSequenceMessage::clear(message); + break; + case messagetypes::ACTION: + ActionMessage::clear(message); + break; + case messagetypes::DEVICE_HANDLER_COMMAND: + DeviceHandlerMessage::clear(message); + break; + case messagetypes::MEMORY: + MemoryMessage::clear(message); + break; + case messagetypes::MONITORING: + MonitoringMessage::clear(message); + break; + case messagetypes::TM_STORE: + TmStoreMessage::clear(message); + break; + case messagetypes::PARAMETER: + ParameterMessage::clear(message); + break; + default: + messagetypes::clearMissionMessage(message); + break; + } +} diff --git a/ipc/CommandMessageCleaner.h b/ipc/CommandMessageCleaner.h new file mode 100644 index 00000000..2bf4a193 --- /dev/null +++ b/ipc/CommandMessageCleaner.h @@ -0,0 +1,16 @@ +#ifndef FRAMEWORK_IPC_COMMANDMESSAGECLEANER_H_ +#define FRAMEWORK_IPC_COMMANDMESSAGECLEANER_H_ +#include "../ipc/CommandMessage.h" + +namespace messagetypes { +// Implemented in config. +void clearMissionMessage(CommandMessage* message); +} + +class CommandMessageCleaner { +public: + static void clearCommandMessage(CommandMessage* message); +}; + + +#endif /* FRAMEWORK_IPC_COMMANDMESSAGECLEANER_H_ */ diff --git a/ipc/CommandMessageIF.h b/ipc/CommandMessageIF.h new file mode 100644 index 00000000..aafa40ef --- /dev/null +++ b/ipc/CommandMessageIF.h @@ -0,0 +1,73 @@ +#ifndef FSFW_IPC_COMMANDMESSAGEIF_H_ +#define FSFW_IPC_COMMANDMESSAGEIF_H_ + +#include "MessageQueueMessageIF.h" +#include "FwMessageTypes.h" +#include "../returnvalues/HasReturnvaluesIF.h" + +#define MAKE_COMMAND_ID( number ) ((MESSAGE_ID << 8) + (number)) +typedef uint16_t Command_t; + +class CommandMessageIF { +public: + /** + * Header consists of sender ID and command ID. + */ + static constexpr size_t HEADER_SIZE = MessageQueueMessageIF::HEADER_SIZE + + sizeof(Command_t); + /** + * This minimum size is derived from the interface requirement to be able + * to set a rejected reply, which contains a returnvalue and the initial + * command. + */ + static constexpr size_t MINIMUM_COMMAND_MESSAGE_SIZE = + CommandMessageIF::HEADER_SIZE + sizeof(ReturnValue_t) + + sizeof(Command_t); + + static const uint8_t INTERFACE_ID = CLASS_ID::COMMAND_MESSAGE; + static const ReturnValue_t UNKNOWN_COMMAND = MAKE_RETURN_CODE(0x01); + + static const uint8_t MESSAGE_ID = messagetypes::COMMAND; + //! Used internally, shall be ignored + static const Command_t CMD_NONE = MAKE_COMMAND_ID( 0 ); + static const Command_t REPLY_COMMAND_OK = MAKE_COMMAND_ID( 1 ); + //! Reply indicating that the current command was rejected, + //! par1 should contain the error code + static const Command_t REPLY_REJECTED = MAKE_COMMAND_ID( 2 ); + + virtual ~CommandMessageIF() {}; + + /** + * A command message shall have a uint16_t command ID field. + * @return + */ + virtual Command_t getCommand() const = 0; + /** + * A command message shall have a uint8_t message type ID field. + * @return + */ + virtual uint8_t getMessageType() const = 0; + + /** + * A command message can be rejected and needs to offer a function + * to set a rejected reply + * @param reason + * @param initialCommand + */ + virtual void setReplyRejected(ReturnValue_t reason, + Command_t initialCommand) = 0; + /** + * Corrensonding getter function. + * @param initialCommand + * @return + */ + virtual ReturnValue_t getReplyRejectedReason( + Command_t* initialCommand = nullptr) const = 0; + + virtual void setToUnknownCommand() = 0; + + virtual void clear() = 0; + +}; + +#endif /* FSFW_IPC_COMMANDMESSAGEIF_H_ */ diff --git a/ipc/MessageQueueIF.h b/ipc/MessageQueueIF.h index 9fff5287..1c06521c 100644 --- a/ipc/MessageQueueIF.h +++ b/ipc/MessageQueueIF.h @@ -1,61 +1,77 @@ -#ifndef FRAMEWORK_IPC_MESSAGEQUEUEIF_H_ -#define FRAMEWORK_IPC_MESSAGEQUEUEIF_H_ +#ifndef FSFW_IPC_MESSAGEQUEUEIF_H_ +#define FSFW_IPC_MESSAGEQUEUEIF_H_ + +#include "messageQueueDefinitions.h" +#include "MessageQueueMessageIF.h" +#include "../returnvalues/HasReturnvaluesIF.h" + +#include + // COULDDO: We could support blocking calls +// semaphores are being implemented, which makes this idea even more iteresting. -#include "MessageQueueMessage.h" -#include "MessageQueueSenderIF.h" -#include "../returnvalues/HasReturnvaluesIF.h" +/** + * @defgroup message_queue Message Queue + * @brief Message Queue related software components + */ class MessageQueueIF { public: - - static const MessageQueueId_t NO_QUEUE = MessageQueueSenderIF::NO_QUEUE; //!< Ugly hack. + static const MessageQueueId_t NO_QUEUE = 0; static const uint8_t INTERFACE_ID = CLASS_ID::MESSAGE_QUEUE_IF; - /** - * No new messages on the queue - */ + //! No new messages on the queue static const ReturnValue_t EMPTY = MAKE_RETURN_CODE(1); - /** - * No space left for more messages - */ + //! No space left for more messages static const ReturnValue_t FULL = MAKE_RETURN_CODE(2); - /** - * Returned if a reply method was called without partner - */ + //! Returned if a reply method was called without partner static const ReturnValue_t NO_REPLY_PARTNER = MAKE_RETURN_CODE(3); + //! Returned if the target destination is invalid. + static constexpr ReturnValue_t DESTINVATION_INVALID = MAKE_RETURN_CODE(4); virtual ~MessageQueueIF() {} /** * @brief This operation sends a message to the last communication partner. - * @details This operation simplifies answering an incoming message by using the stored - * lastParnter information as destination. If there was no message received yet - * (i.e. lastPartner is zero), an error code is returned. - * @param message A pointer to a previously created message, which is sent. - * \return RETURN_OK if ok - * \return NO_REPLY_PARTNER Should return NO_REPLY_PARTNER if partner was found + * @details + * This operation simplifies answering an incoming message by using the + * stored lastParnter information as destination. If there was no message + * received yet (i.e. lastPartner is zero), an error code is returned. + * @param message + * A pointer to a previously created message, which is sent. + * @return + * -@c RETURN_OK if ok + * -@c NO_REPLY_PARTNER Should return NO_REPLY_PARTNER if partner was found. */ - virtual ReturnValue_t reply( MessageQueueMessage* message ) = 0; + virtual ReturnValue_t reply(MessageQueueMessageIF* message) = 0; /** - * @brief This function reads available messages from the message queue and returns the sender. - * @details It works identically to the other receiveMessage call, but in addition returns the - * sender's queue id. - * @param message A pointer to a message in which the received data is stored. - * @param receivedFrom A pointer to a queue id in which the sender's id is stored. + * @brief This function reads available messages from the message queue + * and returns the sender. + * @details + * It works identically to the other receiveMessage call, but in addition + * returns the sender's queue id. + * @param message + * A pointer to a message in which the received data is stored. + * @param receivedFrom + * A pointer to a queue id in which the sender's id is stored. */ - virtual ReturnValue_t receiveMessage(MessageQueueMessage* message, + virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message, MessageQueueId_t *receivedFrom) = 0; /** * @brief This function reads available messages from the message queue. - * @details If data is available it is stored in the passed message pointer. The message's - * original content is overwritten and the sendFrom information is stored in the - * lastPartner attribute. Else, the lastPartner information remains untouched, the - * message's content is cleared and the function returns immediately. - * @param message A pointer to a message in which the received data is stored. + * @details + * If data is available it is stored in the passed message pointer. + * The message's original content is overwritten and the sendFrom + * information is stored in theblastPartner attribute. Else, the lastPartner + * information remains untouched, the message's content is cleared and the + * function returns immediately. + * @param message + * A pointer to a message in which the received data is stored. + * @return -@c RETURN_OK on success + * -@c MessageQueueIF::EMPTY if queue is empty */ - virtual ReturnValue_t receiveMessage(MessageQueueMessage* message) = 0; + virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) = 0; /** * Deletes all pending messages in the queue. * @param count The number of flushed messages. @@ -63,57 +79,89 @@ public: */ virtual ReturnValue_t flush(uint32_t* count) = 0; /** - * @brief This method returns the message queue id of the last communication partner. + * @brief This method returns the message queue + * id of the last communication partner. */ virtual MessageQueueId_t getLastPartner() const = 0; /** - * @brief This method returns the message queue id of this class's message queue. + * @brief This method returns the message queue + * id of this class's message queue. */ virtual MessageQueueId_t getId() const = 0; /** - * \brief With the sendMessage call, a queue message is sent to a receiving queue. - * \details This method takes the message provided, adds the sentFrom information and passes - * it on to the destination provided with an operating system call. The OS's return - * value is returned. - * \param sendTo This parameter specifies the message queue id to send the message to. - * \param message This is a pointer to a previously created message, which is sent. - * \param sentFrom The sentFrom information can be set to inject the sender's queue id into the message. - * This variable is set to zero by default. - * \param ignoreFault If set to true, the internal software fault counter is not incremented if queue is full (if implemented). + * @brief With the sendMessage call, a queue message + * is sent to a receiving queue. + * @details + * This method takes the message provided, adds the sentFrom information + * and passes it on to the destination provided with an operating system + * call. The OS's returnvalue is returned. + * @param sendTo + * This parameter specifies the message queue id to send the message to. + * @param message + * This is a pointer to a previously created message, which is sent. + * @param sentFrom + * The sentFrom information can be set to inject the sender's queue id + * into the message. This variable is set to zero by default. + * @param ignoreFault + * If set to true, the internal software fault counter is not incremented + * if queue is full (if implemented). + * @return -@c RETURN_OK on success + * -@c MessageQueueIF::FULL if queue is full */ - virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, MessageQueueMessage* message, MessageQueueId_t sentFrom, bool ignoreFault = false ) = 0; - /** - * @brief This operation sends a message to the given destination. - * @details It directly uses the sendMessage call of the MessageQueueSender parent, but passes its - * queue id as "sentFrom" parameter. - * @param sendTo This parameter specifies the message queue id of the destination message queue. - * @param message A pointer to a previously created message, which is sent. - * @param ignoreFault If set to true, the internal software fault counter is not incremented if queue is full. - */ - virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo, MessageQueueMessage* message, bool ignoreFault = false ) = 0; + virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault = false ) = 0; /** - * \brief The sendToDefaultFrom method sends a queue message to the default destination. - * \details In all other aspects, it works identical to the sendMessage method. - * \param message This is a pointer to a previously created message, which is sent. - * \param sentFrom The sentFrom information can be set to inject the sender's queue id into the message. - * This variable is set to zero by default. + * @brief This operation sends a message to the given destination. + * @details + * It directly uses the sendMessage call of the MessageQueueSender parent, + * but passes its queue id as "sentFrom" parameter. + * @param sendTo + * This parameter specifies the message queue id of the destination + * message queue. + * @param message + * A pointer to a previously created message, which is sent. + * @param ignoreFault + * If set to true, the internal software fault counter is not incremented + * if queue is full. */ - virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessage* message, MessageQueueId_t sentFrom, bool ignoreFault = false ) = 0; + virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo, + MessageQueueMessageIF* message, bool ignoreFault = false ) = 0; + + /** + * @brief The sendToDefaultFrom method sends a queue message + * to the default destination. + * @details + * In all other aspects, it works identical to the sendMessage method. + * @param message + * This is a pointer to a previously created message, which is sent. + * @param sentFrom + * The sentFrom information can be set to inject the sender's queue id + * into the message. This variable is set to zero by default. + * @return -@c RETURN_OK on success + * -@c MessageQueueIF::FULL if queue is full + */ + virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, + MessageQueueId_t sentFrom, bool ignoreFault = false ) = 0; /** * @brief This operation sends a message to the default destination. - * @details As in the sendMessage method, this function uses the sendToDefault call of the - * Implementation class and adds its queue id as "sentFrom" information. + * @details + * As in the sendMessage method, this function uses the sendToDefault + * call of the Implementation class and adds its queue id as + * "sentFrom" information. * @param message A pointer to a previously created message, which is sent. + * @return -@c RETURN_OK on success + * -@c MessageQueueIF::FULL if queue is full */ - virtual ReturnValue_t sendToDefault( MessageQueueMessage* message ) = 0; + virtual ReturnValue_t sendToDefault( MessageQueueMessageIF* message ) = 0; /** - * \brief This method is a simple setter for the default destination. + * @brief This method is a simple setter for the default destination. */ virtual void setDefaultDestination(MessageQueueId_t defaultDestination) = 0; /** - * \brief This method is a simple getter for the default destination. + * @brief This method is a simple getter for the default destination. */ virtual MessageQueueId_t getDefaultDestination() const = 0; @@ -122,4 +170,4 @@ public: -#endif /* FRAMEWORK_IPC_MESSAGEQUEUEIF_H_ */ +#endif /* FSFW_IPC_MESSAGEQUEUEIF_H_ */ diff --git a/ipc/MessageQueueMessage.cpp b/ipc/MessageQueueMessage.cpp index 05dde1f5..dcd4def3 100644 --- a/ipc/MessageQueueMessage.cpp +++ b/ipc/MessageQueueMessage.cpp @@ -1,13 +1,27 @@ #include "MessageQueueMessage.h" #include "../serviceinterface/ServiceInterfaceStream.h" - -#include +#include "../globalfunctions/arrayprinter.h" +#include MessageQueueMessage::MessageQueueMessage() : - messageSize(this->HEADER_SIZE) { + messageSize(getMinimumMessageSize()) { memset(this->internalBuffer, 0, sizeof(this->internalBuffer)); } +MessageQueueMessage::MessageQueueMessage(uint8_t* data, size_t size) : + messageSize(this->HEADER_SIZE + size) { + if (size <= this->MAX_DATA_SIZE) { + memcpy(this->getData(), data, size); + this->messageSize = this->HEADER_SIZE + size; + } + else { + sif::warning << "MessageQueueMessage: Passed size larger than maximum" + "allowed size! Setting content to 0" << std::endl; + memset(this->internalBuffer, 0, sizeof(this->internalBuffer)); + this->messageSize = this->HEADER_SIZE; + } +} + MessageQueueMessage::~MessageQueueMessage() { } @@ -37,29 +51,34 @@ void MessageQueueMessage::setSender(MessageQueueId_t setId) { memcpy(this->internalBuffer, &setId, sizeof(MessageQueueId_t)); } -MessageQueueMessage::MessageQueueMessage(uint8_t* data, uint32_t size) : - messageSize(this->HEADER_SIZE + size) { - if (size <= this->MAX_DATA_SIZE) { - memcpy(this->getData(), data, size); - } else { - memset(this->internalBuffer, 0, sizeof(this->internalBuffer)); - this->messageSize = this->HEADER_SIZE; +void MessageQueueMessage::print(bool printWholeMessage) { + sif::debug << "MessageQueueMessage content: " << std::endl; + if(printWholeMessage) { + arrayprinter::print(getData(), getMaximumMessageSize()); } -} - -size_t MessageQueueMessage::getMinimumMessageSize() { - return this->HEADER_SIZE; -} - -void MessageQueueMessage::print() { - sif::debug << "MessageQueueMessage has size: " << this->messageSize << std::hex - << std::endl; - for (uint8_t count = 0; count < this->messageSize; count++) { - sif::debug << (uint32_t) this->internalBuffer[count] << ":"; + else { + arrayprinter::print(getData(), getMessageSize()); } - sif::debug << std::dec << std::endl; + } void MessageQueueMessage::clear() { memset(this->getBuffer(), 0, this->MAX_MESSAGE_SIZE); } + +size_t MessageQueueMessage::getMessageSize() const { + return this->messageSize; +} + +void MessageQueueMessage::setMessageSize(size_t messageSize) { + this->messageSize = messageSize; +} + +size_t MessageQueueMessage::getMinimumMessageSize() const { + return this->MIN_MESSAGE_SIZE; +} + +size_t MessageQueueMessage::getMaximumMessageSize() const { + return this->MAX_MESSAGE_SIZE; +} + diff --git a/ipc/MessageQueueMessage.h b/ipc/MessageQueueMessage.h index aa3559d6..5234f64f 100644 --- a/ipc/MessageQueueMessage.h +++ b/ipc/MessageQueueMessage.h @@ -1,118 +1,149 @@ -#ifndef MESSAGEQUEUEMESSAGE_H_ -#define MESSAGEQUEUEMESSAGE_H_ +#ifndef FSFW_IPC_MESSAGEQUEUEMESSAGE_H_ +#define FSFW_IPC_MESSAGEQUEUEMESSAGE_H_ -#include "MessageQueueSenderIF.h" -#include +#include "../ipc/MessageQueueMessageIF.h" +#include /** - * \brief This class is the representation and data organizer for interprocess messages. + * @brief This class is the representation and data organizer + * for interprocess messages. + * @details + * To facilitate and standardize interprocess communication, this class was + * created to handle a lightweight "interprocess message protocol". * - * \details To facilitate and standardize interprocess communication, this class was created - * to handle a lightweight "interprocess message protocol". It adds a header with the - * sender's queue id to every sent message and defines the maximum total message size. - * Specialized messages, such as device commanding messages, can be created by inheriting - * from this class and filling the buffer provided by getData with additional content. - * If larger amounts of data must be sent between processes, the data shall be stored in - * the IPC Store object and only the storage id is passed in a queue message. - * The class is used both to generate and send messages and to receive messages from - * other tasks. - * \ingroup message_queue + * It adds a header with the sender's queue id to every sent message and + * defines the maximum total message size. Specialized messages, such as + * device commanding messages, can be created by inheriting from this class + * and filling the buffer provided by getData with additional content. + * + * If larger amounts of data must be sent between processes, the data shall + * be stored in the IPC Store object and only the storage id is passed in a + * queue message.The class is used both to generate and send messages and to + * receive messages from other tasks. + * @ingroup message_queue */ -class MessageQueueMessage { +class MessageQueueMessage: public MessageQueueMessageIF { public: /** - * \brief This constant defines the maximum size of the data content, excluding the header. - * \details It may be changed if necessary, but in general should be kept as small as possible. + * @brief The class is initialized empty with this constructor. + * @details + * The messageSize attribute is set to the header's size and the whole + * content is set to zero. + */ + MessageQueueMessage(); + /** + * @brief With this constructor the class is initialized with + * the given content. + * @details + * If the passed message size fits into the buffer, the passed data is + * copied to the internal buffer and the messageSize information is set. + * Otherwise, messageSize is set to the header's size and the whole + * content is set to zero. + * @param data The data to be put in the message. + * @param size Size of the data to be copied. Must be smaller than + * MAX_MESSAGE_SIZE and larger than MIN_MESSAGE_SIZE. + */ + MessageQueueMessage(uint8_t* data, size_t size); + + /** + * @brief As no memory is allocated in this class, + * the destructor is empty. + */ + virtual ~MessageQueueMessage(); + + /** + * @brief The size information of each message is stored in + * this attribute. + * @details + * It is public to simplify usage and to allow for passing the size + * address as a pointer. Care must be taken when inheriting from this class, + * as every child class is responsible for managing the size information by + * itself. When using the class to receive a message, the size information + * is updated automatically. + * + * Please note that the minimum size is limited by the size of the header + * while the maximum size is limited by the maximum allowed message size. + */ + size_t messageSize; + /** + * @brief This constant defines the maximum size of the data content, + * excluding the header. + * @details + * It may be changed if necessary, but in general should be kept + * as small as possible. */ static const size_t MAX_DATA_SIZE = 24; + /** - * \brief This constants defines the size of the header, which is added to every message. + * @brief This constant defines the maximum total size in bytes + * of a sent message. + * @details + * It is the sum of the maximum data and the header size. Be aware that + * this constant is used to define the buffer sizes for every message + * queue in the system. So, a change here may have significant impact on + * the required resources. */ - static const size_t HEADER_SIZE = sizeof(MessageQueueId_t); + static constexpr size_t MAX_MESSAGE_SIZE = MAX_DATA_SIZE + HEADER_SIZE; /** - * \brief This constant defines the maximum total size in bytes of a sent message. - * \details It is the sum of the maximum data and the header size. Be aware that this constant - * is used to define the buffer sizes for every message queue in the system. So, a change - * here may have significant impact on the required resources. + * @brief Defines the minimum size of a message where only the + * header is included */ - static const size_t MAX_MESSAGE_SIZE = MAX_DATA_SIZE + HEADER_SIZE; + static constexpr size_t MIN_MESSAGE_SIZE = HEADER_SIZE; private: /** - * \brief This is the internal buffer that contains the actual message data. + * @brief This is the internal buffer that contains the + * actual message data. */ uint8_t internalBuffer[MAX_MESSAGE_SIZE]; public: /** - * \brief The size information of each message is stored in this attribute. - * \details It is public to simplify usage and to allow for passing the variable's address as a - * pointer. Care must be taken when inheriting from this class, as every child class is - * responsible for managing the size information by itself. When using the class to - * receive a message, the size information is updated automatically. + * @brief This method is used to get the complete data of the message. */ - size_t messageSize; + const uint8_t* getBuffer() const override; /** - * \brief The class is initialized empty with this constructor. - * \details The messageSize attribute is set to the header's size and the whole content is set to - * zero. + * @brief This method is used to get the complete data of the message. */ - MessageQueueMessage(); + uint8_t* getBuffer() override; /** - * \brief With this constructor the class is initialized with the given content. - * \details If the passed message size fits into the buffer, the passed data is copied to the - * internal buffer and the messageSize information is set. Otherwise, messageSize - * is set to the header's size and the whole content is set to zero. - * \param data The data to be put in the message. - * \param size Size of the data to be copied. Must be smaller than MAX_MESSAGE_SIZE. + * @brief This method is used to fetch the data content of the message. + * @details + * It shall be used by child classes to add data at the right position. */ - MessageQueueMessage(uint8_t* data, uint32_t size); + const uint8_t* getData() const override; /** - * \brief As no memory is allocated in this class, the destructor is empty. + * @brief This method is used to fetch the data content of the message. + * @details + * It shall be used by child classes to add data at the right position. */ - virtual ~MessageQueueMessage(); + uint8_t* getData() override; /** - * \brief This method is used to get the complete data of the message. + * @brief This method is used to extract the sender's message + * queue id information from a received message. */ - const uint8_t* getBuffer() const; + MessageQueueId_t getSender() const override; /** - * \brief This method is used to get the complete data of the message. + * @brief With this method, the whole content + * and the message size is set to zero. */ - uint8_t* getBuffer(); + void clear() override; + /** - * \brief This method is used to fetch the data content of the message. - * \details It shall be used by child classes to add data at the right position. + * @brief This method is used to set the sender's message queue id + * information prior to ing the message. + * @param setId + * The message queue id that identifies the sending message queue. */ - const uint8_t* getData() const; + void setSender(MessageQueueId_t setId) override; + + virtual size_t getMessageSize() const override; + virtual void setMessageSize(size_t messageSize) override; + virtual size_t getMinimumMessageSize() const override; + virtual size_t getMaximumMessageSize() const override; + /** - * \brief This method is used to fetch the data content of the message. - * \details It shall be used by child classes to add data at the right position. + * @brief This is a debug method that prints the content. */ - uint8_t* getData(); - /** - * \brief This method is used to extract the sender's message queue id information from a - * received message. - */ - MessageQueueId_t getSender() const; - /** - * \brief With this method, the whole content and the message size is set to zero. - */ - void clear(); - /** - * \brief This is a debug method that prints the content (till messageSize) to the debug output. - */ - void print(); - /** - * \brief This method is used to set the sender's message queue id information prior to - * sending the message. - * \param setId The message queue id that identifies the sending message queue. - */ - void setSender(MessageQueueId_t setId); - /** - * \brief This helper function is used by the MessageQueue class to check the size of an - * incoming message. - * \details The method must be overwritten by child classes if size checks shall be more strict. - * @return The default implementation returns HEADER_SIZE. - */ - virtual size_t getMinimumMessageSize(); + void print(bool printWholeMessage); }; -#endif /* MESSAGEQUEUEMESSAGE_H_ */ +#endif /* FSFW_IPC_MESSAGEQUEUEMESSAGE_H_ */ diff --git a/ipc/MessageQueueMessageIF.h b/ipc/MessageQueueMessageIF.h new file mode 100644 index 00000000..b5a30c08 --- /dev/null +++ b/ipc/MessageQueueMessageIF.h @@ -0,0 +1,80 @@ +#ifndef FRAMEWORK_IPC_MESSAGEQUEUEMESSAGEIF_H_ +#define FRAMEWORK_IPC_MESSAGEQUEUEMESSAGEIF_H_ + +#include +#include +#include + +class MessageQueueMessageIF { +public: + + /** + * @brief This constants defines the size of the header, + * which is added to every message. + */ + static const size_t HEADER_SIZE = sizeof(MessageQueueId_t); + + virtual ~MessageQueueMessageIF() {}; + + /** + * @brief With this method, the whole content and the message + * size is set to zero. + * @details + * Implementations should also take care to clear data which is stored + * indirectly (e.g. storage data). + */ + virtual void clear() = 0; + + /** + * @brief Get read-only pointer to the complete data of the message. + * @return + */ + virtual const uint8_t* getBuffer() const = 0; + + /** + * @brief This method is used to get the complete data of the message. + */ + virtual uint8_t* getBuffer() = 0; + + /** + * @brief This method is used to set the sender's message queue id + * information prior to sending the message. + * @param setId + * The message queue id that identifies the sending message queue. + */ + virtual void setSender(MessageQueueId_t setId) = 0; + + /** + * @brief This method is used to extract the sender's message queue id + * information from a received message. + */ + virtual MessageQueueId_t getSender() const = 0; + + /** + * @brief This method is used to fetch the data content of the message. + * @details + * It shall be used by child classes to add data at the right position. + */ + virtual const uint8_t* getData() const = 0; + /** + * @brief This method is used to fetch the data content of the message. + * @details + * It shall be used by child classes to add data at the right position. + */ + virtual uint8_t* getData() = 0; + + /** + * Get constant message size of current message implementation. + * @return + */ + virtual size_t getMessageSize() const = 0; + + virtual void setMessageSize(size_t messageSize) = 0; + virtual size_t getMinimumMessageSize() const = 0; + virtual size_t getMaximumMessageSize() const = 0; + +}; + + + +#endif /* FRAMEWORK_IPC_MESSAGEQUEUEMESSAGEIF_H_ */ diff --git a/ipc/MessageQueueSenderIF.h b/ipc/MessageQueueSenderIF.h index d9692f54..683c2908 100644 --- a/ipc/MessageQueueSenderIF.h +++ b/ipc/MessageQueueSenderIF.h @@ -1,37 +1,26 @@ -#ifndef FRAMEWORK_IPC_MESSAGEQUEUESENDERIF_H_ -#define FRAMEWORK_IPC_MESSAGEQUEUESENDERIF_H_ +#ifndef FSFW_IPC_MESSAGEQUEUESENDERIF_H_ +#define FSFW_IPC_MESSAGEQUEUESENDERIF_H_ +#include "MessageQueueIF.h" +#include "MessageQueueMessageIF.h" #include "../objectmanager/ObjectManagerIF.h" -class MessageQueueMessage; - - -//TODO: Actually, the definition of this ID to be a uint32_t is not ideal and breaks layering. -//However, it is difficult to keep layering, as the ID is stored in many places and sent around in -//MessageQueueMessage. -//Ideally, one would use the (current) object_id_t only, however, doing a lookup of queueIDs for every -//call does not sound ideal. -//In a first step, I'll circumvent the issue by not touching it, maybe in a second step. -//This also influences Interface design (getCommandQueue) and some other issues.. -typedef uint32_t MessageQueueId_t; class MessageQueueSenderIF { public: - static const MessageQueueId_t NO_QUEUE = 0; virtual ~MessageQueueSenderIF() {} /** - * Allows sending messages without actually "owing" a message queue. + * Allows sending messages without actually "owning" a message queue. * Not sure whether this is actually a good idea. - * Must be implemented by a subclass. */ static ReturnValue_t sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, MessageQueueId_t sentFrom = - MessageQueueSenderIF::NO_QUEUE, bool ignoreFault=false); + MessageQueueMessageIF* message, + MessageQueueId_t sentFrom = MessageQueueIF::NO_QUEUE, + bool ignoreFault = false); private: MessageQueueSenderIF() {} }; - -#endif /* FRAMEWORK_IPC_MESSAGEQUEUESENDERIF_H_ */ +#endif /* FSFW_IPC_MESSAGEQUEUESENDERIF_H_ */ diff --git a/ipc/QueueFactory.h b/ipc/QueueFactory.h index 4f4d72c7..9853d256 100644 --- a/ipc/QueueFactory.h +++ b/ipc/QueueFactory.h @@ -1,8 +1,10 @@ -#ifndef FRAMEWORK_IPC_QUEUEFACTORY_H_ -#define FRAMEWORK_IPC_QUEUEFACTORY_H_ +#ifndef FSFW_IPC_QUEUEFACTORY_H_ +#define FSFW_IPC_QUEUEFACTORY_H_ #include "MessageQueueIF.h" -#include +#include "MessageQueueMessage.h" +#include + /** * Creates message queues. * This class is a "singleton" interface, i.e. it provides an @@ -30,4 +32,4 @@ private: static QueueFactory* factoryInstance; }; -#endif /* FRAMEWORK_IPC_QUEUEFACTORY_H_ */ +#endif /* FSFW_IPC_QUEUEFACTORY_H_ */ diff --git a/ipc/messageQueueDefinitions.h b/ipc/messageQueueDefinitions.h new file mode 100644 index 00000000..d250da8a --- /dev/null +++ b/ipc/messageQueueDefinitions.h @@ -0,0 +1,18 @@ +#ifndef FSFW_IPC_MESSAGEQUEUEDEFINITIONS_H_ +#define FSFW_IPC_MESSAGEQUEUEDEFINITIONS_H_ + +#include + +/* + * TODO: Actually, the definition of this ID to be a uint32_t is not ideal and + * breaks layering. However, it is difficult to keep layering, as the ID is + * stored in many places and sent around in MessageQueueMessage. + * Ideally, one would use the (current) object_id_t only, however, doing a + * lookup of queueIDs for every call does not sound ideal. + * In a first step, I'll circumvent the issue by not touching it, + * maybe in a second step. This also influences Interface design + * (getCommandQueue) and some other issues.. + */ +using MessageQueueId_t = uint32_t; + +#endif /* FSFW_IPC_MESSAGEQUEUEDEFINITIONS_H_ */ diff --git a/memory/AcceptsMemoryMessagesIF.h b/memory/AcceptsMemoryMessagesIF.h index a1cbddf8..7491f47d 100644 --- a/memory/AcceptsMemoryMessagesIF.h +++ b/memory/AcceptsMemoryMessagesIF.h @@ -1,12 +1,5 @@ -/** - * @file AcceptsMemoryMessagesIF.h - * @brief This file defines the AcceptsMemoryMessagesIF class. - * @date 11.07.2013 - * @author baetz - */ - -#ifndef ACCEPTSMEMORYMESSAGESIF_H_ -#define ACCEPTSMEMORYMESSAGESIF_H_ +#ifndef FSFW_MEMORY_ACCEPTSMEMORYMESSAGESIF_H_ +#define FSFW_MEMORY_ACCEPTSMEMORYMESSAGESIF_H_ #include "HasMemoryIF.h" #include "MemoryMessage.h" @@ -18,4 +11,4 @@ public: }; -#endif /* ACCEPTSMEMORYMESSAGESIF_H_ */ +#endif /* FSFW_MEMORY_ACCEPTSMEMORYMESSAGESIF_H_ */ diff --git a/memory/MemoryHelper.cpp b/memory/MemoryHelper.cpp index 48a605be..6b11882f 100644 --- a/memory/MemoryHelper.cpp +++ b/memory/MemoryHelper.cpp @@ -1,14 +1,15 @@ -#include "../globalfunctions/CRC.h" #include "MemoryHelper.h" #include "MemoryMessage.h" + +#include "../globalfunctions/CRC.h" #include "../objectmanager/ObjectManagerIF.h" #include "../serialize/EndianConverter.h" #include "../serviceinterface/ServiceInterfaceStream.h" -MemoryHelper::MemoryHelper(HasMemoryIF* workOnThis, MessageQueueIF* useThisQueue) : - workOnThis(workOnThis), queueToUse(useThisQueue), ipcStore(NULL), ipcAddress(), lastCommand( - CommandMessage::CMD_NONE), lastSender(0), reservedSpaceInIPC( - NULL), busy(false) { +MemoryHelper::MemoryHelper(HasMemoryIF* workOnThis, + MessageQueueIF* useThisQueue): + workOnThis(workOnThis), queueToUse(useThisQueue), ipcAddress(), + lastCommand(CommandMessage::CMD_NONE), busy(false) { } ReturnValue_t MemoryHelper::handleMemoryCommand(CommandMessage* message) { @@ -33,17 +34,8 @@ ReturnValue_t MemoryHelper::handleMemoryCommand(CommandMessage* message) { } } -ReturnValue_t MemoryHelper::initialize() { - ipcStore = objectManager->get(objects::IPC_STORE); - if (ipcStore != NULL) { - return RETURN_OK; - } else { - return RETURN_FAILED; - } -} - void MemoryHelper::completeLoad(ReturnValue_t errorCode, - const uint8_t* dataToCopy, const uint32_t size, uint8_t* copyHere) { + const uint8_t* dataToCopy, const size_t size, uint8_t* copyHere) { busy = false; switch (errorCode) { case HasMemoryIF::DO_IT_MYSELF: @@ -67,13 +59,13 @@ void MemoryHelper::completeLoad(ReturnValue_t errorCode, return; } //Only reached on success - CommandMessage reply(CommandMessage::REPLY_COMMAND_OK, 0, 0); + CommandMessage reply( CommandMessage::REPLY_COMMAND_OK, 0, 0); queueToUse->sendMessage(lastSender, &reply); ipcStore->deleteData(ipcAddress); } void MemoryHelper::completeDump(ReturnValue_t errorCode, - const uint8_t* dataToCopy, const uint32_t size) { + const uint8_t* dataToCopy, const size_t size) { busy = false; CommandMessage reply; MemoryMessage::setMemoryReplyFailed(&reply, errorCode, lastCommand); @@ -125,12 +117,12 @@ void MemoryHelper::completeDump(ReturnValue_t errorCode, break; } if (queueToUse->sendMessage(lastSender, &reply) != RETURN_OK) { - reply.clearCommandMessage(); + reply.clear(); } } void MemoryHelper::swapMatrixCopy(uint8_t* out, const uint8_t *in, - uint32_t totalSize, uint8_t datatypeSize) { + size_t totalSize, uint8_t datatypeSize) { if (totalSize % datatypeSize != 0){ return; } @@ -185,11 +177,18 @@ void MemoryHelper::handleMemoryCheckOrDump(CommandMessage* message) { } ReturnValue_t MemoryHelper::initialize(MessageQueueIF* queueToUse_) { - if(queueToUse_!=NULL){ - this->queueToUse = queueToUse_; - }else{ - return MessageQueueIF::NO_QUEUE; + if(queueToUse_ == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; } - + this->queueToUse = queueToUse_; return initialize(); } + +ReturnValue_t MemoryHelper::initialize() { + ipcStore = objectManager->get(objects::IPC_STORE); + if (ipcStore != nullptr) { + return RETURN_OK; + } else { + return RETURN_FAILED; + } +} diff --git a/memory/MemoryHelper.h b/memory/MemoryHelper.h index 5e1a99b6..a651861c 100644 --- a/memory/MemoryHelper.h +++ b/memory/MemoryHelper.h @@ -1,11 +1,16 @@ -#ifndef MEMORYHELPER_H_ -#define MEMORYHELPER_H_ -#include "../ipc/CommandMessage.h" +#ifndef FSFW_MEMORY_MEMORYHELPER_H_ +#define FSFW_MEMORY_MEMORYHELPER_H_ + #include "AcceptsMemoryMessagesIF.h" + +#include "../ipc/CommandMessage.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../storagemanager/StorageManagerIF.h" #include "../ipc/MessageQueueIF.h" +/** + * @brief TODO: documentation. + */ class MemoryHelper : public HasReturnvaluesIF { public: static const uint8_t INTERFACE_ID = CLASS_ID::MEMORY_HELPER; @@ -13,25 +18,32 @@ public: static const ReturnValue_t INVALID_ADDRESS = MAKE_RETURN_CODE(0xE1); static const ReturnValue_t INVALID_SIZE = MAKE_RETURN_CODE(0xE2); static const ReturnValue_t STATE_MISMATCH = MAKE_RETURN_CODE(0xE3); + + MemoryHelper(HasMemoryIF* workOnThis, MessageQueueIF* useThisQueue); + ~MemoryHelper(); + + ReturnValue_t handleMemoryCommand(CommandMessage* message); + void completeLoad(ReturnValue_t errorCode, + const uint8_t* dataToCopy = nullptr, const size_t size = 0, + uint8_t* copyHere = nullptr); + void completeDump(ReturnValue_t errorCode, + const uint8_t* dataToCopy = nullptr, const size_t size = 0); + void swapMatrixCopy(uint8_t *out, const uint8_t *in, size_t totalSize, + uint8_t datatypeSize); + ReturnValue_t initialize(MessageQueueIF* queueToUse_); + private: HasMemoryIF* workOnThis; MessageQueueIF* queueToUse; - StorageManagerIF* ipcStore; + StorageManagerIF* ipcStore = nullptr; store_address_t ipcAddress; Command_t lastCommand; - MessageQueueId_t lastSender; - uint8_t* reservedSpaceInIPC; + MessageQueueId_t lastSender = MessageQueueIF::NO_QUEUE; + uint8_t* reservedSpaceInIPC = nullptr; bool busy; void handleMemoryLoad(CommandMessage* message); void handleMemoryCheckOrDump(CommandMessage* message); ReturnValue_t initialize(); -public: - ReturnValue_t handleMemoryCommand(CommandMessage* message); - void completeLoad( ReturnValue_t errorCode, const uint8_t* dataToCopy = NULL, const uint32_t size = 0, uint8_t* copyHere = NULL ); - void completeDump( ReturnValue_t errorCode, const uint8_t* dataToCopy = NULL, const uint32_t size = 0); - void swapMatrixCopy( uint8_t *out, const uint8_t *in, uint32_t totalSize, uint8_t datatypeSize); - ReturnValue_t initialize(MessageQueueIF* queueToUse_); - MemoryHelper( HasMemoryIF* workOnThis, MessageQueueIF* useThisQueue ); - ~MemoryHelper(); + }; -#endif /* MEMORYHELPER_H_ */ +#endif /* FSFW_MEMORY_MEMORYHELPER_H_ */ diff --git a/memory/MemoryMessage.cpp b/memory/MemoryMessage.cpp index 741565fc..94fa4691 100644 --- a/memory/MemoryMessage.cpp +++ b/memory/MemoryMessage.cpp @@ -1,7 +1,6 @@ #include "MemoryMessage.h" + #include "../objectmanager/ObjectManagerIF.h" -MemoryMessage::MemoryMessage() { -} uint32_t MemoryMessage::getAddress(const CommandMessage* message) { return message->getParameter(); @@ -17,26 +16,24 @@ uint32_t MemoryMessage::getLength(const CommandMessage* message) { return message->getParameter2(); } -ReturnValue_t MemoryMessage::setMemoryDumpCommand(CommandMessage* message, +void MemoryMessage::setMemoryDumpCommand(CommandMessage* message, uint32_t address, uint32_t length) { message->setCommand(CMD_MEMORY_DUMP); message->setParameter( address ); message->setParameter2( length ); - return HasReturnvaluesIF::RETURN_OK; } -ReturnValue_t MemoryMessage::setMemoryDumpReply(CommandMessage* message, store_address_t storageID) { +void MemoryMessage::setMemoryDumpReply(CommandMessage* message, + store_address_t storageID) { message->setCommand(REPLY_MEMORY_DUMP); message->setParameter2( storageID.raw ); - return HasReturnvaluesIF::RETURN_OK; } -ReturnValue_t MemoryMessage::setMemoryLoadCommand(CommandMessage* message, +void MemoryMessage::setMemoryLoadCommand(CommandMessage* message, uint32_t address, store_address_t storageID) { message->setCommand(CMD_MEMORY_LOAD); message->setParameter( address ); message->setParameter2( storageID.raw ); - return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t MemoryMessage::getErrorCode(const CommandMessage* message) { @@ -65,22 +62,21 @@ void MemoryMessage::clear(CommandMessage* message) { } } -ReturnValue_t MemoryMessage::setMemoryCheckCommand(CommandMessage* message, +void MemoryMessage::setMemoryCheckCommand(CommandMessage* message, uint32_t address, uint32_t length) { message->setCommand(CMD_MEMORY_CHECK); message->setParameter( address ); message->setParameter2( length ); - return HasReturnvaluesIF::RETURN_OK; } -ReturnValue_t MemoryMessage::setMemoryCheckReply(CommandMessage* message, +void MemoryMessage::setMemoryCheckReply(CommandMessage* message, uint16_t crc) { message->setCommand(REPLY_MEMORY_CHECK); message->setParameter( crc ); - return HasReturnvaluesIF::RETURN_OK; } -void MemoryMessage::setCrcReturnValue(CommandMessage* message, ReturnValue_t returnValue){ +void MemoryMessage::setCrcReturnValue(CommandMessage* message, + ReturnValue_t returnValue){ message->setParameter(returnValue<<16); }; @@ -96,18 +92,16 @@ Command_t MemoryMessage::getInitialCommand(const CommandMessage* message) { return message->getParameter2(); } -ReturnValue_t MemoryMessage::setMemoryReplyFailed(CommandMessage* message, +void MemoryMessage::setMemoryReplyFailed(CommandMessage* message, ReturnValue_t errorCode, Command_t initialCommand) { message->setCommand(REPLY_MEMORY_FAILED); message->setParameter(errorCode); message->setParameter2(initialCommand); - return HasReturnvaluesIF::RETURN_OK; } -ReturnValue_t MemoryMessage::setMemoryCopyEnd(CommandMessage* message) { +void MemoryMessage::setMemoryCopyEnd(CommandMessage* message) { message->setCommand(END_OF_MEMORY_COPY); message->setParameter(0); message->setParameter2(0); - return HasReturnvaluesIF::RETURN_OK; } diff --git a/memory/MemoryMessage.h b/memory/MemoryMessage.h index a4c2de63..05b9926d 100644 --- a/memory/MemoryMessage.h +++ b/memory/MemoryMessage.h @@ -1,14 +1,15 @@ -#ifndef MEMORYMESSAGE_H_ -#define MEMORYMESSAGE_H_ +#ifndef FSFW_MEMORY_MEMORYMESSAGE_H_ +#define FSFW_MEMORY_MEMORYMESSAGE_H_ #include "../ipc/CommandMessage.h" #include "../storagemanager/StorageManagerIF.h" class MemoryMessage { -private: - MemoryMessage(); //A private ctor inhibits instantiation public: + // Instantiation forbidden. + MemoryMessage() = delete; + static const uint8_t MESSAGE_ID = messagetypes::MEMORY; static const Command_t CMD_MEMORY_LOAD = MAKE_COMMAND_ID( 0x01 ); static const Command_t CMD_MEMORY_DUMP = MAKE_COMMAND_ID( 0x02 ); @@ -19,21 +20,29 @@ public: static const Command_t END_OF_MEMORY_COPY = MAKE_COMMAND_ID(0xF0); static uint32_t getAddress( const CommandMessage* message ); - static store_address_t getStoreID( const CommandMessage* message ); + static store_address_t getStoreID(const CommandMessage* message); static uint32_t getLength( const CommandMessage* message ); - static ReturnValue_t getErrorCode( const CommandMessage* message ); - static ReturnValue_t setMemoryDumpCommand( CommandMessage* message, uint32_t address, uint32_t length ); - static ReturnValue_t setMemoryDumpReply( CommandMessage* message, store_address_t storageID ); - static ReturnValue_t setMemoryLoadCommand( CommandMessage* message, uint32_t address, store_address_t storageID ); - static ReturnValue_t setMemoryCheckCommand( CommandMessage* message, uint32_t address, uint32_t length ); - static ReturnValue_t setMemoryCheckReply( CommandMessage* message, uint16_t crc ); - static ReturnValue_t setMemoryReplyFailed( CommandMessage* message, ReturnValue_t errorCode, Command_t initialCommand ); - static ReturnValue_t setMemoryCopyEnd( CommandMessage* message); + static ReturnValue_t getErrorCode(const CommandMessage* message); + static uint16_t getCrc(const CommandMessage* message ); + static ReturnValue_t getCrcReturnValue(const CommandMessage* message); + static Command_t getInitialCommand(const CommandMessage* message); + + static void setMemoryDumpCommand(CommandMessage* message, + uint32_t address, uint32_t length ); + static void setMemoryDumpReply(CommandMessage* message, + store_address_t storageID); + static void setMemoryLoadCommand(CommandMessage* message, + uint32_t address, store_address_t storageID ); + static void setMemoryCheckCommand(CommandMessage* message, + uint32_t address, uint32_t length); + static void setMemoryCheckReply(CommandMessage* message, + uint16_t crc); + static void setMemoryReplyFailed(CommandMessage* message, + ReturnValue_t errorCode, Command_t initialCommand); + static void setMemoryCopyEnd(CommandMessage* message); static void setCrcReturnValue(CommandMessage*, ReturnValue_t returnValue); - static uint16_t getCrc( const CommandMessage* message ); - static ReturnValue_t getCrcReturnValue(const CommandMessage* message); - static Command_t getInitialCommand( const CommandMessage* message ); + static void clear(CommandMessage* message); }; -#endif /* MEMORYMESSAGE_H_ */ +#endif /* FSFW_MEMORY_MEMORYMESSAGE_H_ */ diff --git a/memory/MemoryProxyIF.h b/memory/MemoryProxyIF.h deleted file mode 100644 index 1494d523..00000000 --- a/memory/MemoryProxyIF.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef FRAMEWORK_MEMORY_MEMORYPROXYIF_H_ -#define FRAMEWORK_MEMORY_MEMORYPROXYIF_H_ - -#include "AcceptsMemoryMessagesIF.h" - -/** - * This was a nice idea to transparently forward incoming messages to another object. - * But it doesn't work like that. - */ -class MemoryProxyIF : public AcceptsMemoryMessagesIF { -public: - virtual MessageQueueId_t getProxyQueue() const = 0; - MessageQueueId_t getCommandQueue() const { - return getProxyQueue(); - } - virtual ~MemoryProxyIF() {} - -}; - - - -#endif /* FRAMEWORK_MEMORY_MEMORYPROXYIF_H_ */ diff --git a/modes/HasModesIF.h b/modes/HasModesIF.h index 4381046c..34a15937 100644 --- a/modes/HasModesIF.h +++ b/modes/HasModesIF.h @@ -1,18 +1,11 @@ -/** - * @file HasModesIF.h - * @brief This file defines the HasModesIF class. - * @date 20.06.2013 - * @author baetz - */ +#ifndef FSFW_MODES_HASMODESIF_H_ +#define FSFW_MODES_HASMODESIF_H_ -#ifndef HASMODESIF_H_ -#define HASMODESIF_H_ - -#include "../events/Event.h" #include "ModeHelper.h" #include "ModeMessage.h" +#include "../events/Event.h" #include "../returnvalues/HasReturnvaluesIF.h" -#include +#include class HasModesIF { @@ -37,21 +30,22 @@ public: static const Mode_t MODE_ON = 1; //!< The device is powered and ready to perform operations. In this mode, no commands are sent by the device handler itself, but direct commands van be commanded and will be interpreted static const Mode_t MODE_OFF = 0; //!< The device is powered off. The only command accepted in this mode is a mode change to on. static const Submode_t SUBMODE_NONE = 0; //!< To avoid checks against magic number "0". - virtual ~HasModesIF() { - } + + virtual ~HasModesIF() {} virtual MessageQueueId_t getCommandQueue() const = 0; virtual void getMode(Mode_t *mode, Submode_t *submode) = 0; + protected: virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode) { return HasReturnvaluesIF::RETURN_FAILED; } - virtual void startTransition(Mode_t mode, Submode_t submode) { - } - virtual void setToExternalControl() { - } - virtual void announceMode(bool recursive) { - } + + virtual void startTransition(Mode_t mode, Submode_t submode) {} + + virtual void setToExternalControl() {} + + virtual void announceMode(bool recursive) {} }; -#endif /* HASMODESIF_H_ */ +#endif /*FSFW_MODES_HASMODESIF_H_ */ diff --git a/modes/ModeHelper.cpp b/modes/ModeHelper.cpp index cb62c468..6be4f776 100644 --- a/modes/ModeHelper.cpp +++ b/modes/ModeHelper.cpp @@ -1,48 +1,48 @@ #include "HasModesIF.h" #include "ModeHelper.h" + #include "../ipc/MessageQueueSenderIF.h" +#include "../serviceinterface/ServiceInterfaceStream.h" ModeHelper::ModeHelper(HasModesIF *owner) : - theOneWhoCommandedAMode(0), commandedMode(HasModesIF::MODE_OFF), commandedSubmode( - HasModesIF::SUBMODE_NONE), owner(owner), parentQueueId(0), forced( - false) { -} + commandedMode(HasModesIF::MODE_OFF), + commandedSubmode(HasModesIF::SUBMODE_NONE), + owner(owner), forced(false) {} ModeHelper::~ModeHelper() { } -ReturnValue_t ModeHelper::handleModeCommand(CommandMessage* message) { +ReturnValue_t ModeHelper::handleModeCommand(CommandMessage* command) { CommandMessage reply; Mode_t mode; Submode_t submode; - switch (message->getCommand()) { + switch (command->getCommand()) { case ModeMessage::CMD_MODE_COMMAND_FORCED: forced = true; /* NO BREAK falls through*/ case ModeMessage::CMD_MODE_COMMAND: { - mode = ModeMessage::getMode(message); - submode = ModeMessage::getSubmode(message); + mode = ModeMessage::getMode(command); + submode = ModeMessage::getSubmode(command); uint32_t timeout; ReturnValue_t result = owner->checkModeCommand(mode, submode, &timeout); if (result != HasReturnvaluesIF::RETURN_OK) { - ModeMessage::cantReachMode(&reply, result); - MessageQueueSenderIF::sendMessage(message->getSender(), &reply, - owner->getCommandQueue()); + ModeMessage::setCantReachMode(&reply, result); + MessageQueueSenderIF::sendMessage(command->getSender(), &reply, + owner->getCommandQueue()); break; } //Free to start transition - theOneWhoCommandedAMode = message->getSender(); + theOneWhoCommandedAMode = command->getSender(); commandedMode = mode; commandedSubmode = submode; - if ((parentQueueId != MessageQueueSenderIF::NO_QUEUE) + if ((parentQueueId != MessageQueueIF::NO_QUEUE) && (theOneWhoCommandedAMode != parentQueueId)) { owner->setToExternalControl(); } countdown.setTimeout(timeout); - owner->startTransition(mode, submode); } break; @@ -50,8 +50,8 @@ ReturnValue_t ModeHelper::handleModeCommand(CommandMessage* message) { owner->getMode(&mode, &submode); ModeMessage::setModeMessage(&reply, ModeMessage::REPLY_MODE_REPLY, mode, submode); - MessageQueueSenderIF::sendMessage(message->getSender(), &reply, - owner->getCommandQueue()); + MessageQueueSenderIF::sendMessage(command->getSender(), &reply, + owner->getCommandQueue()); } break; case ModeMessage::CMD_MODE_ANNOUNCE: @@ -71,27 +71,45 @@ ReturnValue_t ModeHelper::initialize(MessageQueueId_t parentQueueId) { return initialize(); } -void ModeHelper::modeChanged(Mode_t mode, Submode_t submode) { +void ModeHelper::modeChanged(Mode_t ownerMode, Submode_t ownerSubmode) { forced = false; + sendModeReplyMessage(ownerMode, ownerSubmode); + sendModeInfoMessage(ownerMode, ownerSubmode); + theOneWhoCommandedAMode = MessageQueueIF::NO_QUEUE; +} + +void ModeHelper::sendModeReplyMessage(Mode_t ownerMode, + Submode_t ownerSubmode) { CommandMessage reply; - if (theOneWhoCommandedAMode != MessageQueueSenderIF::NO_QUEUE) { - if ((mode != commandedMode) || (submode != commandedSubmode)) { + if (theOneWhoCommandedAMode != MessageQueueIF::NO_QUEUE) + { + if (ownerMode != commandedMode or ownerSubmode != commandedSubmode) + { ModeMessage::setModeMessage(&reply, - ModeMessage::REPLY_WRONG_MODE_REPLY, mode, submode); - } else { + ModeMessage::REPLY_WRONG_MODE_REPLY, ownerMode, + ownerSubmode); + } + else + { ModeMessage::setModeMessage(&reply, ModeMessage::REPLY_MODE_REPLY, - mode, submode); + ownerMode, ownerSubmode); } MessageQueueSenderIF::sendMessage(theOneWhoCommandedAMode, &reply, - owner->getCommandQueue()); + owner->getCommandQueue()); } +} + +void ModeHelper::sendModeInfoMessage(Mode_t ownerMode, + Submode_t ownerSubmode) { + CommandMessage reply; if (theOneWhoCommandedAMode != parentQueueId - && parentQueueId != MessageQueueSenderIF::NO_QUEUE) { - ModeMessage::setModeMessage(&reply, ModeMessage::REPLY_MODE_INFO, mode, - submode); - MessageQueueSenderIF::sendMessage(parentQueueId, &reply, owner->getCommandQueue()); + and parentQueueId != MessageQueueIF::NO_QUEUE) + { + ModeMessage::setModeMessage(&reply, ModeMessage::REPLY_MODE_INFO, + ownerMode, ownerSubmode); + MessageQueueSenderIF::sendMessage(parentQueueId, &reply, + owner->getCommandQueue()); } - theOneWhoCommandedAMode = MessageQueueSenderIF::NO_QUEUE; } void ModeHelper::startTimer(uint32_t timeoutMs) { diff --git a/modes/ModeHelper.h b/modes/ModeHelper.h index 542fe780..c2f089ec 100644 --- a/modes/ModeHelper.h +++ b/modes/ModeHelper.h @@ -1,7 +1,8 @@ -#ifndef MODEHELPER_H_ -#define MODEHELPER_H_ +#ifndef FSFW_MODES_MODEHELPER_H_ +#define FSFW_MODES_MODEHELPER_H_ #include "ModeMessage.h" +#include "../ipc/MessageQueueIF.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../timemanager/Countdown.h" @@ -9,7 +10,7 @@ class HasModesIF; class ModeHelper { public: - MessageQueueId_t theOneWhoCommandedAMode; + MessageQueueId_t theOneWhoCommandedAMode = MessageQueueIF::NO_QUEUE; Mode_t commandedMode; Submode_t commandedSubmode; @@ -19,14 +20,14 @@ public: ReturnValue_t handleModeCommand(CommandMessage *message); /** - * - * @param parentQueue the Queue id of the parent object. Set to 0 if no parent present + * @param parentQueue the Queue id of the parent object. + * Set to 0 if no parent present */ void setParentQueue(MessageQueueId_t parentQueueId); ReturnValue_t initialize(MessageQueueId_t parentQueueId); - ReturnValue_t initialize(void); //void is there to stop eclipse CODAN from falsely reporting an error + ReturnValue_t initialize(void); void modeChanged(Mode_t mode, Submode_t submode); @@ -39,11 +40,14 @@ public: void setForced(bool forced); protected: HasModesIF *owner; - MessageQueueId_t parentQueueId; + MessageQueueId_t parentQueueId = MessageQueueIF::NO_QUEUE; Countdown countdown; bool forced; +private: + void sendModeReplyMessage(Mode_t ownerMode, Submode_t ownerSubmode); + void sendModeInfoMessage(Mode_t ownerMode, Submode_t ownerSubmode); }; -#endif /* MODEHELPER_H_ */ +#endif /* FSFW_MODES_MODEHELPER_H_ */ diff --git a/modes/ModeMessage.cpp b/modes/ModeMessage.cpp index 14dbbb31..b33bba60 100644 --- a/modes/ModeMessage.cpp +++ b/modes/ModeMessage.cpp @@ -8,12 +8,11 @@ Submode_t ModeMessage::getSubmode(const CommandMessage* message) { return message->getParameter2(); } -ReturnValue_t ModeMessage::setModeMessage(CommandMessage* message, Command_t command, - Mode_t mode, Submode_t submode) { +void ModeMessage::setModeMessage(CommandMessage* message, + Command_t command, Mode_t mode, Submode_t submode) { message->setCommand( command ); message->setParameter( mode ); message->setParameter2( submode ); - return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t ModeMessage::getCantReachModeReason(const CommandMessage* message) { @@ -24,7 +23,8 @@ void ModeMessage::clear(CommandMessage* message) { message->setCommand(CommandMessage::CMD_NONE); } -void ModeMessage::cantReachMode(CommandMessage* message, ReturnValue_t reason) { +void ModeMessage::setCantReachMode(CommandMessage* message, + ReturnValue_t reason) { message->setCommand(REPLY_CANT_REACH_MODE); message->setParameter(reason); message->setParameter2(0); diff --git a/modes/ModeMessage.h b/modes/ModeMessage.h index 925f3fc1..856996cf 100644 --- a/modes/ModeMessage.h +++ b/modes/ModeMessage.h @@ -1,12 +1,5 @@ -/** - * @file ModeMessage.h - * @brief This file defines the ModeMessage class. - * @date 17.07.2013 - * @author baetz - */ - -#ifndef MODEMESSAGE_H_ -#define MODEMESSAGE_H_ +#ifndef FSFW_MODES_MODEMESSAGE_H_ +#define FSFW_MODES_MODEMESSAGE_H_ #include "../ipc/CommandMessage.h" @@ -30,11 +23,12 @@ public: static Mode_t getMode(const CommandMessage* message); static Submode_t getSubmode(const CommandMessage* message); - static ReturnValue_t setModeMessage(CommandMessage* message, + static ReturnValue_t getCantReachModeReason(const CommandMessage* message); + + static void setModeMessage(CommandMessage* message, Command_t command, Mode_t mode, Submode_t submode); - static void cantReachMode(CommandMessage* message, ReturnValue_t reason); - static ReturnValue_t getCantReachModeReason(const CommandMessage* message); + static void setCantReachMode(CommandMessage* message, ReturnValue_t reason); static void clear(CommandMessage* message); }; -#endif /* MODEMESSAGE_H_ */ +#endif /* FSFW_MODES_MODEMESSAGE_H_ */ diff --git a/objectmanager/frameworkObjects.h b/objectmanager/frameworkObjects.h index 87439c88..4d08f084 100644 --- a/objectmanager/frameworkObjects.h +++ b/objectmanager/frameworkObjects.h @@ -4,13 +4,13 @@ namespace objects { enum framework_objects { // Default verification reporter. - PUS_SERVICE_1 = 0x53000001, - PUS_SERVICE_2 = 0x53000002, - PUS_SERVICE_5 = 0x53000005, - PUS_SERVICE_8 = 0x53000008, - PUS_SERVICE_9 = 0x53000009, - PUS_SERVICE_17 = 0x53000017, - PUS_SERVICE_200 = 0x53000200, + PUS_SERVICE_1_VERIFICATION = 0x53000001, + PUS_SERVICE_2_DEVICE_ACCESS = 0x53000002, + PUS_SERVICE_5_EVENT_REPORTING = 0x53000005, + PUS_SERVICE_8_FUNCTION_MGMT = 0x53000008, + PUS_SERVICE_9_TIME_MGMT = 0x53000009, + PUS_SERVICE_17_TEST = 0x53000017, + PUS_SERVICE_200_MODE_MGMT = 0x53000200, //Generic IDs for IPC, modes, health, events HEALTH_TABLE = 0x53010000, diff --git a/osal/FreeRTOS/FixedTimeslotTask.cpp b/osal/FreeRTOS/FixedTimeslotTask.cpp index 4873dde4..062686e2 100644 --- a/osal/FreeRTOS/FixedTimeslotTask.cpp +++ b/osal/FreeRTOS/FixedTimeslotTask.cpp @@ -8,7 +8,7 @@ const size_t PeriodicTaskIF::MINIMUM_STACK_SIZE = configMINIMAL_STACK_SIZE; FixedTimeslotTask::FixedTimeslotTask(TaskName name, TaskPriority setPriority, TaskStackSize setStack, TaskPeriod overallPeriod, void (*setDeadlineMissedFunc)()) : - started(false), handle(NULL), pst(overallPeriod * 1000) { + started(false), handle(nullptr), pst(overallPeriod * 1000) { configSTACK_DEPTH_TYPE stackSize = setStack / sizeof(configSTACK_DEPTH_TYPE); xTaskCreate(taskEntryPoint, name, stackSize, this, setPriority, &handle); // All additional attributes are applied to the object. @@ -62,8 +62,10 @@ ReturnValue_t FixedTimeslotTask::startTask() { ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId, uint32_t slotTimeMs, int8_t executionStep) { - if (objectManager->get(componentId) != nullptr) { - pst.addSlot(componentId, slotTimeMs, executionStep, this); + ExecutableObjectIF* handler = + objectManager->get(componentId); + if (handler != nullptr) { + pst.addSlot(componentId, slotTimeMs, executionStep, handler, this); return HasReturnvaluesIF::RETURN_OK; } @@ -85,6 +87,8 @@ void FixedTimeslotTask::taskFunctionality() { // start time for the first entry. auto slotListIter = pst.current; + pst.intializeSequenceAfterTaskCreation(); + //The start time for the first entry is read. uint32_t intervalMs = slotListIter->pollingTimeMs; TickType_t interval = pdMS_TO_TICKS(intervalMs); @@ -143,10 +147,6 @@ void FixedTimeslotTask::checkMissedDeadline(const TickType_t xLastWakeTime, } void FixedTimeslotTask::handleMissedDeadline() { -#ifdef DEBUG - sif::warning << "FixedTimeslotTask: " << pcTaskGetName(NULL) << - " missed deadline!\n" << std::flush; -#endif if(deadlineMissedFunc != nullptr) { this->deadlineMissedFunc(); } diff --git a/osal/FreeRTOS/FixedTimeslotTask.h b/osal/FreeRTOS/FixedTimeslotTask.h index 84264c4c..7d2cdb70 100644 --- a/osal/FreeRTOS/FixedTimeslotTask.h +++ b/osal/FreeRTOS/FixedTimeslotTask.h @@ -1,12 +1,11 @@ -#ifndef FRAMEWORK_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_ -#define FRAMEWORK_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_ +#ifndef FSFW_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_ +#define FSFW_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_ #include "FreeRTOSTaskIF.h" -#include "../../devicehandlers/FixedSlotSequence.h" +#include "../../tasks/FixedSlotSequence.h" #include "../../tasks/FixedTimeslotTaskIF.h" #include "../../tasks/Typedef.h" - #include #include @@ -99,4 +98,4 @@ protected: void handleMissedDeadline(); }; -#endif /* FRAMEWORK_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_ */ +#endif /* FSFW_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_ */ diff --git a/osal/FreeRTOS/MessageQueue.cpp b/osal/FreeRTOS/MessageQueue.cpp index 3bbd4d9d..2dfe5ab6 100644 --- a/osal/FreeRTOS/MessageQueue.cpp +++ b/osal/FreeRTOS/MessageQueue.cpp @@ -1,41 +1,76 @@ #include "MessageQueue.h" - +#include "../../objectmanager/ObjectManagerIF.h" #include "../../serviceinterface/ServiceInterfaceStream.h" -// TODO I guess we should have a way of checking if we are in an ISR and then use the "fromISR" versions of all calls - -MessageQueue::MessageQueue(size_t message_depth, size_t max_message_size) : -defaultDestination(0),lastPartner(0) { - handle = xQueueCreate(message_depth, max_message_size); - if (handle == NULL) { - sif::error << "MessageQueue creation failed" << std::endl; +// TODO I guess we should have a way of checking if we are in an ISR and then +// use the "fromISR" versions of all calls +// As a first step towards this, introduces system context variable which needs +// to be switched manually +// Haven't found function to find system context. +MessageQueue::MessageQueue(size_t messageDepth, size_t maxMessageSize): + maxMessageSize(maxMessageSize) { + handle = xQueueCreate(messageDepth, maxMessageSize); + if (handle == nullptr) { + sif::error << "MessageQueue::MessageQueue Creation failed" << std::endl; } } MessageQueue::~MessageQueue() { - if (handle != NULL) { + if (handle != nullptr) { vQueueDelete(handle); } } +void MessageQueue::switchSystemContext(CallContext callContext) { + this->callContext = callContext; +} + ReturnValue_t MessageQueue::sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, bool ignoreFault) { + MessageQueueMessageIF* message, bool ignoreFault) { return sendMessageFrom(sendTo, message, this->getId(), ignoreFault); } -ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessage* message) { +ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessageIF* message) { return sendToDefaultFrom(message, this->getId()); } -ReturnValue_t MessageQueue::reply(MessageQueueMessage* message) { - if (this->lastPartner != 0) { +ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessageIF* message, + MessageQueueId_t sentFrom, bool ignoreFault) { + return sendMessageFrom(defaultDestination,message,sentFrom,ignoreFault); +} + +ReturnValue_t MessageQueue::reply(MessageQueueMessageIF* message) { + if (this->lastPartner != MessageQueueIF::NO_QUEUE) { return sendMessageFrom(this->lastPartner, message, this->getId()); } else { return NO_REPLY_PARTNER; } } -ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessage* message, +ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault) { + return sendMessageFromMessageQueue(sendTo, message, sentFrom, ignoreFault, + callContext); +} + + +ReturnValue_t MessageQueue::handleSendResult(BaseType_t result, bool ignoreFault) { + if (result != pdPASS) { + if (not ignoreFault) { + InternalErrorReporterIF* internalErrorReporter = objectManager-> + get( + objects::INTERNAL_ERROR_REPORTER); + if (internalErrorReporter != nullptr) { + internalErrorReporter->queueMessageNotSent(); + } + } + return MessageQueueIF::FULL; + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message, MessageQueueId_t* receivedFrom) { ReturnValue_t status = this->receiveMessage(message); if(status == HasReturnvaluesIF::RETURN_OK) { @@ -44,8 +79,9 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessage* message, return status; } -ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessage* message) { - BaseType_t result = xQueueReceive(handle,reinterpret_cast(message->getBuffer()), 0); +ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) { + BaseType_t result = xQueueReceive(handle,reinterpret_cast( + message->getBuffer()), 0); if (result == pdPASS){ this->lastPartner = message->getSender(); return HasReturnvaluesIF::RETURN_OK; @@ -66,50 +102,55 @@ ReturnValue_t MessageQueue::flush(uint32_t* count) { } MessageQueueId_t MessageQueue::getId() const { - return (MessageQueueId_t) handle; + return reinterpret_cast(handle); } void MessageQueue::setDefaultDestination(MessageQueueId_t defaultDestination) { + defaultDestinationSet = true; this->defaultDestination = defaultDestination; } -ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, - MessageQueueMessage* message, MessageQueueId_t sentFrom, - bool ignoreFault) { - return sendMessageFromMessageQueue(sendTo,message,sentFrom,ignoreFault); -} - -ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessage* message, - MessageQueueId_t sentFrom, bool ignoreFault) { - return sendMessageFrom(defaultDestination,message,sentFrom,ignoreFault); -} - MessageQueueId_t MessageQueue::getDefaultDestination() const { return defaultDestination; } bool MessageQueue::isDefaultDestinationSet() const { - return 0; + return defaultDestinationSet; } + +// static core function to send messages. ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, - MessageQueueMessage *message, MessageQueueId_t sentFrom, - bool ignoreFault) { - message->setSender(sentFrom); + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault, CallContext callContext) { + BaseType_t result = pdFALSE; + QueueHandle_t destination = nullptr; - BaseType_t result = xQueueSendToBack(reinterpret_cast(sendTo), - reinterpret_cast(message->getBuffer()), 0); - if (result != pdPASS) { - if (!ignoreFault) { - InternalErrorReporterIF* internalErrorReporter = objectManager->get( - objects::INTERNAL_ERROR_REPORTER); - if (internalErrorReporter != NULL) { - internalErrorReporter->queueMessageNotSent(); - } - } - return MessageQueueIF::FULL; + if(sendTo == MessageQueueIF::NO_QUEUE or sendTo == 0x00) { + return MessageQueueIF::DESTINVATION_INVALID; + } + else { + destination = reinterpret_cast(sendTo); } - return HasReturnvaluesIF::RETURN_OK; + message->setSender(sentFrom); + + + if(callContext == CallContext::TASK) { + result = xQueueSendToBack(destination, + static_cast(message->getBuffer()), 0); + } + else { + /* If the call context is from an interrupt, + * request a context switch if a higher priority task + * was blocked by the interrupt. */ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + result = xQueueSendFromISR(reinterpret_cast(sendTo), + static_cast(message->getBuffer()), + &xHigherPriorityTaskWoken); + if(xHigherPriorityTaskWoken == pdTRUE) { + TaskManagement::requestContextSwitch(callContext); + } + } + return handleSendResult(result, ignoreFault); } - diff --git a/osal/FreeRTOS/MessageQueue.h b/osal/FreeRTOS/MessageQueue.h index aa3d54c1..b99bf7c8 100644 --- a/osal/FreeRTOS/MessageQueue.h +++ b/osal/FreeRTOS/MessageQueue.h @@ -1,159 +1,150 @@ -#ifndef MESSAGEQUEUE_H_ -#define MESSAGEQUEUE_H_ +#ifndef FSFW_OSAL_FREERTOS_MESSAGEQUEUE_H_ +#define FSFW_OSAL_FREERTOS_MESSAGEQUEUE_H_ #include "../../internalError/InternalErrorReporterIF.h" #include "../../ipc/MessageQueueIF.h" -#include "../../ipc/MessageQueueMessage.h" +#include "../../ipc/MessageQueueMessageIF.h" +#include "../../osal/FreeRTOS/TaskManagement.h" -#include -#include +#include +#include +#include -//TODO this class assumes that MessageQueueId_t is the same size as void* (the FreeRTOS handle type), compiler will catch this but it might be nice to have something checking or even an always working solution +// TODO: this class assumes that MessageQueueId_t is the same size as void* +// (the FreeRTOS handle type), compiler will catch this but it might be nice +// to have something checking or even an always working solution // https://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/ /** - * @brief This class manages sending and receiving of message queue messages. + * @brief This class manages sending and receiving of + * message queue messages. + * @details + * Message queues are used to pass asynchronous messages between processes. + * They work like post boxes, where all incoming messages are stored in FIFO + * order. This class creates a new receiving queue and provides methods to fetch + * received messages. Being a child of MessageQueueSender, this class also + * provides methods to send a message to a user-defined or a default destination. + * In addition it also provides a reply method to answer to the queue it + * received its last message from. * - * @details Message queues are used to pass asynchronous messages between processes. - * They work like post boxes, where all incoming messages are stored in FIFO - * order. This class creates a new receiving queue and provides methods to fetch - * received messages. Being a child of MessageQueueSender, this class also provides - * methods to send a message to a user-defined or a default destination. In addition - * it also provides a reply method to answer to the queue it received its last message - * from. - * The MessageQueue should be used as "post box" for a single owning object. So all - * message queue communication is "n-to-one". - * For creating the queue, as well as sending and receiving messages, the class makes - * use of the operating system calls provided. - * \ingroup message_queue + * The MessageQueue should be used as "post box" for a single owning object. + * So all message queue communication is "n-to-one". + * For creating the queue, as well as sending and receiving messages, the class + * makes use of the operating system calls provided. + * + * Please keep in mind that FreeRTOS offers different calls for message queue + * operations if called from an ISR. + * For now, the system context needs to be switched manually. + * @ingroup osal + * @ingroup message_queue */ class MessageQueue : public MessageQueueIF { friend class MessageQueueSenderIF; public: /** * @brief The constructor initializes and configures the message queue. - * @details By making use of the according operating system call, a message queue is created - * and initialized. The message depth - the maximum number of messages to be - * buffered - may be set with the help of a parameter, whereas the message size is - * automatically set to the maximum message queue message size. The operating system - * sets the message queue id, or i case of failure, it is set to zero. - * @param message_depth The number of messages to be buffered before passing an error to the - * sender. Default is three. - * @param max_message_size With this parameter, the maximum message size can be adjusted. - * This should be left default. + * @details + * By making use of the according operating system call, a message queue + * is created and initialized. The message depth - the maximum number of + * messages to be buffered - may be set with the help of a parameter, + * whereas the message size is automatically set to the maximum message + * queue message size. The operating system sets the message queue id, or + * in case of failure, it is set to zero. + * @param message_depth + * The number of messages to be buffered before passing an error to the + * sender. Default is three. + * @param max_message_size + * With this parameter, the maximum message size can be adjusted. + * This should be left default. */ - MessageQueue( size_t message_depth = 3, size_t max_message_size = MessageQueueMessage::MAX_MESSAGE_SIZE ); + MessageQueue( size_t messageDepth = 3, + size_t maxMessageSize = MessageQueueMessage::MAX_MESSAGE_SIZE ); + + /** Copying message queues forbidden */ + MessageQueue(const MessageQueue&) = delete; + MessageQueue& operator=(const MessageQueue&) = delete; + /** * @brief The destructor deletes the formerly created message queue. - * @details This is accomplished by using the delete call provided by the operating system. + * @details This is accomplished by using the delete call provided + * by the operating system. */ virtual ~MessageQueue(); + /** - * @brief This operation sends a message to the given destination. - * @details It directly uses the sendMessage call of the MessageQueueSender parent, but passes its - * queue id as "sentFrom" parameter. - * @param sendTo This parameter specifies the message queue id of the destination message queue. - * @param message A pointer to a previously created message, which is sent. - * @param ignoreFault If set to true, the internal software fault counter is not incremented if queue is full. + * This function is used to switch the call context. This has to be called + * if a message is sent or received from an ISR! + * @param callContext */ + void switchSystemContext(CallContext callContext); + + /** MessageQueueIF implementation */ ReturnValue_t sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, bool ignoreFault = false ); - /** - * @brief This operation sends a message to the default destination. - * @details As in the sendMessage method, this function uses the sendToDefault call of the - * MessageQueueSender parent class and adds its queue id as "sentFrom" information. - * @param message A pointer to a previously created message, which is sent. - */ - ReturnValue_t sendToDefault( MessageQueueMessage* message ); - /** - * @brief This operation sends a message to the last communication partner. - * @details This operation simplifies answering an incoming message by using the stored - * lastParnter information as destination. If there was no message received yet - * (i.e. lastPartner is zero), an error code is returned. - * @param message A pointer to a previously created message, which is sent. - */ - ReturnValue_t reply( MessageQueueMessage* message ); + MessageQueueMessageIF* message, bool ignoreFault = false) override; - /** - * @brief This function reads available messages from the message queue and returns the sender. - * @details It works identically to the other receiveMessage call, but in addition returns the - * sender's queue id. - * @param message A pointer to a message in which the received data is stored. - * @param receivedFrom A pointer to a queue id in which the sender's id is stored. - */ - ReturnValue_t receiveMessage(MessageQueueMessage* message, - MessageQueueId_t *receivedFrom); + ReturnValue_t sendToDefault(MessageQueueMessageIF* message) override; - /** - * @brief This function reads available messages from the message queue. - * @details If data is available it is stored in the passed message pointer. The message's - * original content is overwritten and the sendFrom information is stored in the - * lastPartner attribute. Else, the lastPartner information remains untouched, the - * message's content is cleared and the function returns immediately. - * @param message A pointer to a message in which the received data is stored. - */ - ReturnValue_t receiveMessage(MessageQueueMessage* message); - /** - * Deletes all pending messages in the queue. - * @param count The number of flushed messages. - * @return RETURN_OK on success. - */ - ReturnValue_t flush(uint32_t* count); - /** - * @brief This method returns the message queue id of the last communication partner. - */ - MessageQueueId_t getLastPartner() const; - /** - * @brief This method returns the message queue id of this class's message queue. - */ - MessageQueueId_t getId() const; - /** - * \brief With the sendMessage call, a queue message is sent to a receiving queue. - * \details This method takes the message provided, adds the sentFrom information and passes - * it on to the destination provided with an operating system call. The OS's return - * value is returned. - * \param sendTo This parameter specifies the message queue id to send the message to. - * \param message This is a pointer to a previously created message, which is sent. - * \param sentFrom The sentFrom information can be set to inject the sender's queue id into the message. - * This variable is set to zero by default. - * \param ignoreFault If set to true, the internal software fault counter is not incremented if queue is full. - */ - virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, MessageQueueMessage* message, MessageQueueId_t sentFrom = NO_QUEUE, bool ignoreFault = false ); - /** - * \brief The sendToDefault method sends a queue message to the default destination. - * \details In all other aspects, it works identical to the sendMessage method. - * \param message This is a pointer to a previously created message, which is sent. - * \param sentFrom The sentFrom information can be set to inject the sender's queue id into the message. - * This variable is set to zero by default. - */ - virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessage* message, MessageQueueId_t sentFrom = NO_QUEUE, bool ignoreFault = false ); - /** - * \brief This method is a simple setter for the default destination. - */ - void setDefaultDestination(MessageQueueId_t defaultDestination); - /** - * \brief This method is a simple getter for the default destination. - */ - MessageQueueId_t getDefaultDestination() const; + ReturnValue_t reply(MessageQueueMessageIF* message) override; + virtual ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, + MessageQueueId_t sentFrom = NO_QUEUE, + bool ignoreFault = false) override; + + virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, + MessageQueueId_t sentFrom = NO_QUEUE, + bool ignoreFault = false) override; + + ReturnValue_t receiveMessage(MessageQueueMessageIF* message, + MessageQueueId_t *receivedFrom) override; + + ReturnValue_t receiveMessage(MessageQueueMessageIF* message) override; + + ReturnValue_t flush(uint32_t* count) override; + + MessageQueueId_t getLastPartner() const override; + + MessageQueueId_t getId() const override; + + void setDefaultDestination(MessageQueueId_t defaultDestination) override; + + MessageQueueId_t getDefaultDestination() const override; + + bool isDefaultDestinationSet() const override; - bool isDefaultDestinationSet() const; protected: /** - * Implementation to be called from any send Call within MessageQueue and MessageQueueSenderIF - * \details This method takes the message provided, adds the sentFrom information and passes - * it on to the destination provided with an operating system call. The OS's return - * value is returned. - * \param sendTo This parameter specifies the message queue id to send the message to. - * \param message This is a pointer to a previously created message, which is sent. - * \param sentFrom The sentFrom information can be set to inject the sender's queue id into the message. - * This variable is set to zero by default. - * \param ignoreFault If set to true, the internal software fault counter is not incremented if queue is full. + * @brief Implementation to be called from any send Call within + * MessageQueue and MessageQueueSenderIF. + * @details + * This method takes the message provided, adds the sentFrom information and + * passes it on to the destination provided with an operating system call. + * The OS's return value is returned. + * @param sendTo + * This parameter specifies the message queue id to send the message to. + * @param message + * This is a pointer to a previously created message, which is sent. + * @param sentFrom + * The sentFrom information can be set to inject the sender's queue id into + * the message. This variable is set to zero by default. + * @param ignoreFault + * If set to true, the internal software fault counter is not incremented + * if queue is full. + * @param context Specify whether call is made from task or from an ISR. */ - static ReturnValue_t sendMessageFromMessageQueue(MessageQueueId_t sendTo,MessageQueueMessage* message, MessageQueueId_t sentFrom = NO_QUEUE,bool ignoreFault=false); + static ReturnValue_t sendMessageFromMessageQueue(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom = NO_QUEUE, + bool ignoreFault=false, CallContext callContext = CallContext::TASK); + + static ReturnValue_t handleSendResult(BaseType_t result, bool ignoreFault); + private: + bool defaultDestinationSet = false; QueueHandle_t handle; - MessageQueueId_t defaultDestination; - MessageQueueId_t lastPartner; + MessageQueueId_t defaultDestination = MessageQueueIF::NO_QUEUE; + MessageQueueId_t lastPartner = MessageQueueIF::NO_QUEUE; + const size_t maxMessageSize; + //! Stores the current system context + CallContext callContext = CallContext::TASK; }; -#endif /* MESSAGEQUEUE_H_ */ +#endif /* FSFW_OSAL_FREERTOS_MESSAGEQUEUE_H_ */ diff --git a/osal/FreeRTOS/PeriodicTask.cpp b/osal/FreeRTOS/PeriodicTask.cpp index 990d38d6..5c0a840d 100644 --- a/osal/FreeRTOS/PeriodicTask.cpp +++ b/osal/FreeRTOS/PeriodicTask.cpp @@ -133,10 +133,6 @@ TaskHandle_t PeriodicTask::getTaskHandle() { } void PeriodicTask::handleMissedDeadline() { -#ifdef DEBUG - sif::warning << "PeriodicTask: " << pcTaskGetName(NULL) << - " missed deadline!\n" << std::flush; -#endif if(deadlineMissedFunc != nullptr) { this->deadlineMissedFunc(); } diff --git a/osal/FreeRTOS/QueueFactory.cpp b/osal/FreeRTOS/QueueFactory.cpp index e639179a..ed29e10c 100644 --- a/osal/FreeRTOS/QueueFactory.cpp +++ b/osal/FreeRTOS/QueueFactory.cpp @@ -1,18 +1,21 @@ -#include "../../ipc/QueueFactory.h" - #include "MessageQueue.h" +#include "../../ipc/MessageQueueSenderIF.h" +#include "../../ipc/QueueFactory.h" -QueueFactory* QueueFactory::factoryInstance = NULL; + +QueueFactory* QueueFactory::factoryInstance = nullptr; ReturnValue_t MessageQueueSenderIF::sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, MessageQueueId_t sentFrom,bool ignoreFault) { - return MessageQueue::sendMessageFromMessageQueue(sendTo,message,sentFrom,ignoreFault); + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault) { + return MessageQueue::sendMessageFromMessageQueue(sendTo,message, + sentFrom,ignoreFault); } QueueFactory* QueueFactory::instance() { - if (factoryInstance == NULL) { + if (factoryInstance == nullptr) { factoryInstance = new QueueFactory; } return factoryInstance; @@ -24,9 +27,9 @@ QueueFactory::QueueFactory() { QueueFactory::~QueueFactory() { } -MessageQueueIF* QueueFactory::createMessageQueue(uint32_t message_depth, +MessageQueueIF* QueueFactory::createMessageQueue(uint32_t messageDepth, size_t maxMessageSize) { - return new MessageQueue(message_depth, maxMessageSize); + return new MessageQueue(messageDepth, maxMessageSize); } void QueueFactory::deleteMessageQueue(MessageQueueIF* queue) { diff --git a/osal/linux/FixedTimeslotTask.cpp b/osal/linux/FixedTimeslotTask.cpp index 858a39a7..247a34ed 100644 --- a/osal/linux/FixedTimeslotTask.cpp +++ b/osal/linux/FixedTimeslotTask.cpp @@ -1,5 +1,5 @@ -#include "../../serviceinterface/ServiceInterfaceStream.h" #include "FixedTimeslotTask.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" #include @@ -39,13 +39,16 @@ uint32_t FixedTimeslotTask::getPeriodMs() const { ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId, uint32_t slotTimeMs, int8_t executionStep) { - if (objectManager->get(componentId) != nullptr) { - pst.addSlot(componentId, slotTimeMs, executionStep, this); + ExecutableObjectIF* executableObject = + objectManager->get(componentId); + if (executableObject != nullptr) { + pst.addSlot(componentId, slotTimeMs, executionStep, + executableObject,this); return HasReturnvaluesIF::RETURN_OK; } sif::error << "Component " << std::hex << componentId << - " not found, not adding it to pst" << std::endl; + " not found, not adding it to pst" << std::dec << std::endl; return HasReturnvaluesIF::RETURN_FAILED; } @@ -58,6 +61,9 @@ void FixedTimeslotTask::taskFunctionality() { if (!started) { suspend(); } + + pst.intializeSequenceAfterTaskCreation(); + //The start time for the first entry is read. uint64_t lastWakeTime = getCurrentMonotonicTimeMs(); uint64_t interval = pst.getIntervalToNextSlotMs(); diff --git a/osal/linux/FixedTimeslotTask.h b/osal/linux/FixedTimeslotTask.h index 42802b1d..5c5c1814 100644 --- a/osal/linux/FixedTimeslotTask.h +++ b/osal/linux/FixedTimeslotTask.h @@ -1,9 +1,9 @@ -#ifndef FRAMEWORK_OSAL_LINUX_FIXEDTIMESLOTTASK_H_ -#define FRAMEWORK_OSAL_LINUX_FIXEDTIMESLOTTASK_H_ +#ifndef FSFW_OSAL_LINUX_FIXEDTIMESLOTTASK_H_ +#define FSFW_OSAL_LINUX_FIXEDTIMESLOTTASK_H_ -#include "../../tasks/FixedTimeslotTaskIF.h" -#include "../../devicehandlers/FixedSlotSequence.h" #include "PosixThread.h" +#include "../../tasks/FixedTimeslotTaskIF.h" +#include "../../tasks/FixedSlotSequence.h" #include class FixedTimeslotTask: public FixedTimeslotTaskIF, public PosixThread { @@ -74,4 +74,4 @@ private: bool started; }; -#endif /* FRAMEWORK_OSAL_LINUX_FIXEDTIMESLOTTASK_H_ */ +#endif /* FSFW_OSAL_LINUX_FIXEDTIMESLOTTASK_H_ */ diff --git a/osal/linux/MessageQueue.cpp b/osal/linux/MessageQueue.cpp index e5c61cae..bc14374a 100644 --- a/osal/linux/MessageQueue.cpp +++ b/osal/linux/MessageQueue.cpp @@ -1,17 +1,19 @@ -#include "../../serviceinterface/ServiceInterfaceStream.h" #include "MessageQueue.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../objectmanager/ObjectManagerIF.h" #include - #include /* For O_* constants */ #include /* For mode constants */ #include #include + MessageQueue::MessageQueue(uint32_t messageDepth, size_t maxMessageSize): id(MessageQueueIF::NO_QUEUE),lastPartner(MessageQueueIF::NO_QUEUE), - defaultDestination(MessageQueueIF::NO_QUEUE) { + defaultDestination(MessageQueueIF::NO_QUEUE), + maxMessageSize(maxMessageSize) { //debug << "MessageQueue::MessageQueue: Creating a queue" << std::endl; mq_attr attributes; this->id = 0; @@ -46,7 +48,7 @@ MessageQueue::~MessageQueue() { status = mq_unlink(name); if(status != 0){ sif::error << "MessageQueue::Destructor: mq_unlink Failed with status: " - << strerror(errno) <> defaultMqMaxMsg and defaultMqMaxMsg < messageDepth) { - // See: https://www.man7.org/linux/man-pages/man3/mq_open.3.html - // This happens if the msg_max value is not large enough - // It is ignored if the executable is run in privileged mode. - // Run the unlockRealtime script or grant the mode manually by using: - // sudo setcap 'CAP_SYS_RESOURCE=+ep' + /* + See: https://www.man7.org/linux/man-pages/man3/mq_open.3.html + This happens if the msg_max value is not large enough + It is ignored if the executable is run in privileged mode. + Run the unlockRealtime script or grant the mode manually by using: + sudo setcap 'CAP_SYS_RESOURCE=+ep' - // Persistent solution for session: - // echo | sudo tee /proc/sys/fs/mqueue/msg_max + Persistent solution for session: + echo | sudo tee /proc/sys/fs/mqueue/msg_max - // Permanent solution: - // sudo nano /etc/sysctl.conf - // Append at end: fs/mqueue/msg_max = - // Apply changes with: sudo sysctl -p + Permanent solution: + sudo nano /etc/sysctl.conf + Append at end: fs/mqueue/msg_max = + Apply changes with: sudo sysctl -p + */ sif::error << "MessageQueue::MessageQueue: Default MQ size " << defaultMqMaxMsg << " is too small for requested size " << messageDepth << std::endl; + sif::error << "This error can be fixed by setting the maximum " + "allowed message size higher!" << std::endl; + } break; } @@ -118,15 +125,15 @@ ReturnValue_t MessageQueue::handleError(mq_attr* attributes, } ReturnValue_t MessageQueue::sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, bool ignoreFault) { + MessageQueueMessageIF* message, bool ignoreFault) { return sendMessageFrom(sendTo, message, this->getId(), false); } -ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessage* message) { +ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessageIF* message) { return sendToDefaultFrom(message, this->getId()); } -ReturnValue_t MessageQueue::reply(MessageQueueMessage* message) { +ReturnValue_t MessageQueue::reply(MessageQueueMessageIF* message) { if (this->lastPartner != 0) { return sendMessageFrom(this->lastPartner, message, this->getId()); } else { @@ -134,21 +141,34 @@ ReturnValue_t MessageQueue::reply(MessageQueueMessage* message) { } } -ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessage* message, +ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message, MessageQueueId_t* receivedFrom) { ReturnValue_t status = this->receiveMessage(message); *receivedFrom = this->lastPartner; return status; } -ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessage* message) { +ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) { + if(message == nullptr) { + sif::error << "MessageQueue::receiveMessage: Message is " + "nullptr!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + + if(message->getMaximumMessageSize() < maxMessageSize) { + sif::error << "MessageQueue::receiveMessage: Message size " + << message->getMaximumMessageSize() + << " too small to receive data!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + unsigned int messagePriority = 0; int status = mq_receive(id,reinterpret_cast(message->getBuffer()), - message->MAX_MESSAGE_SIZE,&messagePriority); + message->getMaximumMessageSize(),&messagePriority); if (status > 0) { this->lastPartner = message->getSender(); //Check size of incoming message. - if (message->messageSize < message->getMinimumMessageSize()) { + if (message->getMessageSize() < message->getMinimumMessageSize()) { return HasReturnvaluesIF::RETURN_FAILED; } return HasReturnvaluesIF::RETURN_OK; @@ -158,7 +178,7 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessage* message) { } else { //No message was received. Keep lastPartner anyway, I might send //something later. But still, delete packet content. - memset(message->getData(), 0, message->MAX_DATA_SIZE); + memset(message->getData(), 0, message->getMaximumMessageSize()); switch(errno){ case EAGAIN: //O_NONBLOCK or MQ_NONBLOCK was set and there are no messages @@ -258,18 +278,19 @@ void MessageQueue::setDefaultDestination(MessageQueueId_t defaultDestination) { this->defaultDestination = defaultDestination; } -ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, - MessageQueueMessage* message, MessageQueueId_t sentFrom, - bool ignoreFault) { - return sendMessageFromMessageQueue(sendTo,message,sentFrom,ignoreFault); - -} - -ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessage* message, +ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessageIF* message, MessageQueueId_t sentFrom, bool ignoreFault) { return sendMessageFrom(defaultDestination, message, sentFrom, ignoreFault); } + +ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault) { + return sendMessageFromMessageQueue(sendTo,message, sentFrom,ignoreFault); + +} + MessageQueueId_t MessageQueue::getDefaultDestination() const { return this->defaultDestination; } @@ -281,11 +302,18 @@ bool MessageQueue::isDefaultDestinationSet() const { uint16_t MessageQueue::queueCounter = 0; ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, - MessageQueueMessage *message, MessageQueueId_t sentFrom, + MessageQueueMessageIF *message, MessageQueueId_t sentFrom, bool ignoreFault) { + if(message == nullptr) { + sif::error << "MessageQueue::sendMessageFromMessageQueue: Message is " + "nullptr!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + message->setSender(sentFrom); int result = mq_send(sendTo, - reinterpret_cast(message->getBuffer()), message->messageSize,0); + reinterpret_cast(message->getBuffer()), + message->getMessageSize(),0); //TODO: Check if we're in ISR. if (result != 0) { @@ -303,13 +331,16 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, //MQ_NONBLOCK flag was set in its attributes, and the //specified queue is full. return MessageQueueIF::FULL; - case EBADF: + case EBADF: { //mq_des doesn't represent a valid message queue descriptor, //or mq_des wasn't opened for writing. - sif::error << "MessageQueue::sendMessage: Configuration error " - << strerror(errno) << " in mq_send mqSendTo: " << sendTo - << " sent from " << sentFrom << std::endl; - /*NO BREAK*/ + sif::error << "MessageQueue::sendMessage: Configuration error, MQ" + << " destination invalid." << std::endl; + sif::error << strerror(errno) << " in " + <<"mq_send to: " << sendTo << " sent from " + << sentFrom << std::endl; + return DESTINVATION_INVALID; + } case EINTR: //The call was interrupted by a signal. case EINVAL: diff --git a/osal/linux/MessageQueue.h b/osal/linux/MessageQueue.h index 31cdca11..239bbbdb 100644 --- a/osal/linux/MessageQueue.h +++ b/osal/linux/MessageQueue.h @@ -1,5 +1,5 @@ -#ifndef MESSAGEQUEUE_H_ -#define MESSAGEQUEUE_H_ +#ifndef FSFW_OSAL_LINUX_MESSAGEQUEUE_H_ +#define FSFW_OSAL_LINUX_MESSAGEQUEUE_H_ #include "../../internalError/InternalErrorReporterIF.h" #include "../../ipc/MessageQueueIF.h" @@ -56,14 +56,14 @@ public: * @param ignoreFault If set to true, the internal software fault counter is not incremented if queue is full. */ virtual ReturnValue_t sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, bool ignoreFault = false ); + MessageQueueMessageIF* message, bool ignoreFault = false ); /** * @brief This operation sends a message to the default destination. * @details As in the sendMessage method, this function uses the sendToDefault call of the * MessageQueueSender parent class and adds its queue id as "sentFrom" information. * @param message A pointer to a previously created message, which is sent. */ - virtual ReturnValue_t sendToDefault( MessageQueueMessage* message ); + virtual ReturnValue_t sendToDefault( MessageQueueMessageIF* message ); /** * @brief This operation sends a message to the last communication partner. * @details This operation simplifies answering an incoming message by using the stored @@ -71,7 +71,7 @@ public: * (i.e. lastPartner is zero), an error code is returned. * @param message A pointer to a previously created message, which is sent. */ - ReturnValue_t reply( MessageQueueMessage* message ); + ReturnValue_t reply( MessageQueueMessageIF* message ); /** * @brief This function reads available messages from the message queue and returns the sender. @@ -80,7 +80,7 @@ public: * @param message A pointer to a message in which the received data is stored. * @param receivedFrom A pointer to a queue id in which the sender's id is stored. */ - ReturnValue_t receiveMessage(MessageQueueMessage* message, + ReturnValue_t receiveMessage(MessageQueueMessageIF* message, MessageQueueId_t *receivedFrom); /** @@ -91,7 +91,7 @@ public: * message's content is cleared and the function returns immediately. * @param message A pointer to a message in which the received data is stored. */ - ReturnValue_t receiveMessage(MessageQueueMessage* message); + ReturnValue_t receiveMessage(MessageQueueMessageIF* message); /** * Deletes all pending messages in the queue. * @param count The number of flushed messages. @@ -114,7 +114,9 @@ public: * This variable is set to zero by default. * \param ignoreFault If set to true, the internal software fault counter is not incremented if queue is full. */ - virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, MessageQueueMessage* message, MessageQueueId_t sentFrom, bool ignoreFault = false ); + virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault = false ); /** * \brief The sendToDefault method sends a queue message to the default destination. * \details In all other aspects, it works identical to the sendMessage method. @@ -122,7 +124,8 @@ public: * \param sentFrom The sentFrom information can be set to inject the sender's queue id into the message. * This variable is set to zero by default. */ - virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessage* message, MessageQueueId_t sentFrom = NO_QUEUE, bool ignoreFault = false ); + virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, + MessageQueueId_t sentFrom = NO_QUEUE, bool ignoreFault = false ); /** * \brief This method is a simple setter for the default destination. */ @@ -145,7 +148,9 @@ protected: * This variable is set to zero by default. * \param ignoreFault If set to true, the internal software fault counter is not incremented if queue is full. */ - static ReturnValue_t sendMessageFromMessageQueue(MessageQueueId_t sendTo,MessageQueueMessage* message, MessageQueueId_t sentFrom = NO_QUEUE,bool ignoreFault=false); + static ReturnValue_t sendMessageFromMessageQueue(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom = NO_QUEUE, + bool ignoreFault=false); private: /** * @brief The class stores the queue id it got assigned from the operating system in this attribute. @@ -171,11 +176,12 @@ private: /** * The name of the message queue, stored for unlinking */ - char name[5]; + char name[16]; static uint16_t queueCounter; + const size_t maxMessageSize; ReturnValue_t handleError(mq_attr* attributes, uint32_t messageDepth); }; -#endif /* MESSAGEQUEUE_H_ */ +#endif /* FSFW_OSAL_LINUX_MESSAGEQUEUE_H_ */ diff --git a/osal/linux/QueueFactory.cpp b/osal/linux/QueueFactory.cpp index afeca547..44def48a 100644 --- a/osal/linux/QueueFactory.cpp +++ b/osal/linux/QueueFactory.cpp @@ -1,15 +1,21 @@ #include "../../ipc/QueueFactory.h" +#include "MessageQueue.h" + +#include "../../ipc/messageQueueDefinitions.h" +#include "../../ipc/MessageQueueSenderIF.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" + #include #include -#include "MessageQueue.h" -#include "../../serviceinterface/ServiceInterfaceStream.h" + + #include QueueFactory* QueueFactory::factoryInstance = nullptr; ReturnValue_t MessageQueueSenderIF::sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, MessageQueueId_t sentFrom, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, bool ignoreFault) { return MessageQueue::sendMessageFromMessageQueue(sendTo,message, sentFrom,ignoreFault); diff --git a/pus/CService200ModeCommanding.cpp b/pus/CService200ModeCommanding.cpp index 32f7e5a7..c63b47a6 100644 --- a/pus/CService200ModeCommanding.cpp +++ b/pus/CService200ModeCommanding.cpp @@ -107,13 +107,23 @@ ReturnValue_t CService200ModeCommanding::prepareWrongModeReply( const CommandMessage *reply, object_id_t objectId) { ModePacket wrongModeReply(objectId, ModeMessage::getMode(reply), ModeMessage::getSubmode(reply)); - return sendTmPacket(Subservice::REPLY_WRONG_MODE_REPLY, &wrongModeReply); + ReturnValue_t result = sendTmPacket(Subservice::REPLY_WRONG_MODE_REPLY, &wrongModeReply); + if(result == RETURN_OK){ + // We want to produce an error here in any case because the mode was not correct + return RETURN_FAILED; + } + return result; } ReturnValue_t CService200ModeCommanding::prepareCantReachModeReply( const CommandMessage *reply, object_id_t objectId) { CantReachModePacket cantReachModePacket(objectId, ModeMessage::getCantReachModeReason(reply)); - return sendTmPacket(Subservice::REPLY_CANT_REACH_MODE, + ReturnValue_t result = sendTmPacket(Subservice::REPLY_CANT_REACH_MODE, &cantReachModePacket); + if(result == RETURN_OK){ + // We want to produce an error here in any case because the mode was not reached + return RETURN_FAILED; + } + return result; } diff --git a/returnvalues/HasReturnvaluesIF.h b/returnvalues/HasReturnvaluesIF.h index 5fef91dd..3d3e3208 100644 --- a/returnvalues/HasReturnvaluesIF.h +++ b/returnvalues/HasReturnvaluesIF.h @@ -2,7 +2,7 @@ #define FRAMEWORK_RETURNVALUES_HASRETURNVALUESIF_H_ #include "FwClassIds.h" -#include +#include #include #define MAKE_RETURN_CODE( number ) ((INTERFACE_ID << 8) + (number)) diff --git a/storagemanager/ConstStorageAccessor.cpp b/storagemanager/ConstStorageAccessor.cpp new file mode 100644 index 00000000..842f1ce8 --- /dev/null +++ b/storagemanager/ConstStorageAccessor.cpp @@ -0,0 +1,88 @@ +#include "ConstStorageAccessor.h" +#include "StorageManagerIF.h" + +#include "../serviceinterface/ServiceInterfaceStream.h" +#include "../globalfunctions/arrayprinter.h" + +ConstStorageAccessor::ConstStorageAccessor(store_address_t storeId): + storeId(storeId) {} + +ConstStorageAccessor::ConstStorageAccessor(store_address_t storeId, + StorageManagerIF* store): + storeId(storeId), store(store) { + internalState = AccessState::ASSIGNED; +} + +ConstStorageAccessor::~ConstStorageAccessor() { + if(deleteData and store != nullptr) { + store->deleteData(storeId); + } +} + +ConstStorageAccessor::ConstStorageAccessor(ConstStorageAccessor&& other): + constDataPointer(other.constDataPointer), storeId(other.storeId), + size_(other.size_), store(other.store), deleteData(other.deleteData), + internalState(other.internalState) { + // This prevent premature deletion + other.store = nullptr; +} + +ConstStorageAccessor& ConstStorageAccessor::operator=( + ConstStorageAccessor&& other) { + constDataPointer = other.constDataPointer; + storeId = other.storeId; + store = other.store; + size_ = other.size_; + deleteData = other.deleteData; + this->store = other.store; + // This prevents premature deletion + other.store = nullptr; + return *this; +} + +const uint8_t* ConstStorageAccessor::data() const { + return constDataPointer; +} + +size_t ConstStorageAccessor::size() const { + if(internalState == AccessState::UNINIT) { + sif::warning << "StorageAccessor: Not initialized!" << std::endl; + } + return size_; +} + +ReturnValue_t ConstStorageAccessor::getDataCopy(uint8_t *pointer, + size_t maxSize) { + if(internalState == AccessState::UNINIT) { + sif::warning << "StorageAccessor: Not initialized!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + if(size_ > maxSize) { + sif::error << "StorageAccessor: Supplied buffer not large enough" + << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + std::copy(constDataPointer, constDataPointer + size_, pointer); + return HasReturnvaluesIF::RETURN_OK; +} + +void ConstStorageAccessor::release() { + deleteData = false; +} + +store_address_t ConstStorageAccessor::getId() const { + return storeId; +} + +void ConstStorageAccessor::print() const { + if(internalState == AccessState::UNINIT or constDataPointer == nullptr) { + sif::warning << "StorageAccessor: Not initialized!" << std::endl; + return; + } + arrayprinter::print(constDataPointer, size_); +} + +void ConstStorageAccessor::assignStore(StorageManagerIF* store) { + internalState = AccessState::ASSIGNED; + this->store = store; +} diff --git a/storagemanager/ConstStorageAccessor.h b/storagemanager/ConstStorageAccessor.h new file mode 100644 index 00000000..96d2dca2 --- /dev/null +++ b/storagemanager/ConstStorageAccessor.h @@ -0,0 +1,116 @@ +#ifndef FSFW_STORAGEMANAGER_CONSTSTORAGEACCESSOR_H_ +#define FSFW_STORAGEMANAGER_CONSTSTORAGEACCESSOR_H_ + +#include "storeAddress.h" +#include "../returnvalues/HasReturnvaluesIF.h" +#include + +class StorageManagerIF; + +/** + * @brief Helper classes to facilitate safe access to storages which is also + * conforming to RAII principles + * @details + * Accessor class which can be returned by pool manager or passed and set by + * pool managers to have safe access to the pool resources. + * + * These helper can be used together with the StorageManager classes to manage + * access to a storage. It can take care of thread-safety while also providing + * mechanisms to automatically clear storage data. + */ +class ConstStorageAccessor { + //! StorageManager classes have exclusive access to private variables. + template + friend class PoolManager; + template + friend class LocalPool; +public: + /** + * @brief Simple constructor which takes the store ID of the storage + * entry to access. + * @param storeId + */ + ConstStorageAccessor(store_address_t storeId); + ConstStorageAccessor(store_address_t storeId, StorageManagerIF* store); + + /** + * @brief The destructor in default configuration takes care of + * deleting the accessed pool entry and unlocking the mutex + */ + virtual ~ConstStorageAccessor(); + + /** + * @brief Returns a pointer to the read-only data + * @return + */ + const uint8_t* data() const; + + /** + * @brief Copies the read-only data to the supplied pointer + * @param pointer + */ + virtual ReturnValue_t getDataCopy(uint8_t *pointer, size_t maxSize); + + /** + * @brief Calling this will prevent the Accessor from deleting the data + * when the destructor is called. + */ + void release(); + + /** + * Get the size of the data + * @return + */ + size_t size() const; + + /** + * Get the storage ID. + * @return + */ + store_address_t getId() const; + + void print() const; + + /** + * @brief Move ctor and move assignment allow returning accessors as + * a returnvalue. They prevent resource being free prematurely. + * Refer to: https://github.com/MicrosoftDocs/cpp-docs/blob/master/docs/cpp/ + * move-constructors-and-move-assignment-operators-cpp.md + * @param + * @return + */ + ConstStorageAccessor& operator= (ConstStorageAccessor&&); + ConstStorageAccessor(ConstStorageAccessor&&); + + //! The copy ctor and copy assignemnt should be deleted implicitely + //! according to https://foonathan.net/2019/02/special-member-functions/ + //! but I still deleted them to make it more explicit. (remember rule of 5). + ConstStorageAccessor& operator=(const ConstStorageAccessor&) = delete; + ConstStorageAccessor(const ConstStorageAccessor&) = delete; +protected: + const uint8_t* constDataPointer = nullptr; + store_address_t storeId; + size_t size_ = 0; + //! Managing pool, has to assign itself. + StorageManagerIF* store = nullptr; + bool deleteData = true; + + enum class AccessState { + UNINIT, + ASSIGNED + }; + //! Internal state for safety reasons. + AccessState internalState = AccessState::UNINIT; + /** + * Used by the pool manager instances to assign themselves to the + * accessor. This is necessary to delete the data when the acessor + * exits the scope ! The internal state will be considered read + * when this function is called, so take care all data is set properly as + * well. + * @param + */ + void assignStore(StorageManagerIF*); +}; + + +#endif /* FSFW_STORAGEMANAGER_CONSTSTORAGEACCESSOR_H_ */ diff --git a/storagemanager/LocalPool.h b/storagemanager/LocalPool.h index ad3deee1..3a94c03d 100644 --- a/storagemanager/LocalPool.h +++ b/storagemanager/LocalPool.h @@ -1,18 +1,14 @@ -/** - * @file LocalPool - * @date 02.02.2012 - * @author Bastian Baetz - * @brief This file contains the definition of the LocalPool class. - */ -#ifndef FRAMEWORK_STORAGEMANAGER_LOCALPOOL_H_ -#define FRAMEWORK_STORAGEMANAGER_LOCALPOOL_H_ +#ifndef FSFW_STORAGEMANAGER_LOCALPOOL_H_ +#define FSFW_STORAGEMANAGER_LOCALPOOL_H_ -#include "../objectmanager/SystemObject.h" -#include "../serviceinterface/ServiceInterfaceStream.h" #include "StorageManagerIF.h" +#include "../objectmanager/SystemObject.h" #include "../objectmanager/ObjectManagerIF.h" +#include "../serviceinterface/ServiceInterfaceStream.h" #include "../internalError/InternalErrorReporterIF.h" -#include +#include "../storagemanager/StorageAccessor.h" +#include + /** * @brief The LocalPool class provides an intermediate data storage with @@ -27,6 +23,7 @@ * 0xFFFF-1 bytes. * It is possible to store empty packets in the pool. * The local pool is NOT thread-safe. + * @author Bastian Baetz */ template class LocalPool: public SystemObject, public StorageManagerIF { @@ -39,7 +36,7 @@ public: /** * @brief This is the default constructor for a pool manager instance. * @details By passing two arrays of size NUMBER_OF_POOLS, the constructor - * allocates memory (with \c new) for store and size_list. These + * allocates memory (with @c new) for store and size_list. These * regions are all set to zero on start up. * @param setObjectId The object identifier to be set. This allows for * multiple instances of LocalPool in the system. @@ -73,10 +70,17 @@ public: size_t size, bool ignoreFault = false) override; ReturnValue_t getFreeElement(store_address_t* storageId,const size_t size, uint8_t** p_data, bool ignoreFault = false) override; + + ConstAccessorPair getData(store_address_t packet_id) override; + ReturnValue_t getData(store_address_t packet_id, ConstStorageAccessor&) override; ReturnValue_t getData(store_address_t packet_id, const uint8_t** packet_ptr, size_t * size) override; + + AccessorPair modifyData(store_address_t packet_id) override; + ReturnValue_t modifyData(store_address_t packet_id, StorageAccessor&) override; ReturnValue_t modifyData(store_address_t packet_id, uint8_t** packet_ptr, size_t * size) override; + virtual ReturnValue_t deleteData(store_address_t) override; virtual ReturnValue_t deleteData(uint8_t* ptr, size_t size, store_address_t* storeId = NULL) override; @@ -84,7 +88,7 @@ public: ReturnValue_t initialize() override; protected: /** - * With this helper method, a free element of \c size is reserved. + * With this helper method, a free element of @c size is reserved. * @param size The minimum packet size that shall be reserved. * @param[out] address Storage ID of the reserved data. * @return - #RETURN_OK on success, @@ -97,7 +101,8 @@ protected: private: /** * Indicates that this element is free. - * This value limits the maximum size of a pool. Change to larger data type if increase is required. + * This value limits the maximum size of a pool. Change to larger data type + * if increase is required. */ static const uint32_t STORAGE_FREE = 0xFFFFFFFF; /** @@ -123,7 +128,9 @@ private: * is also dynamically allocated there. */ uint32_t* size_list[NUMBER_OF_POOLS]; - bool spillsToHigherPools; //!< A variable to determine whether higher n pools are used if the store is full. + //! A variable to determine whether higher n pools are used if + //! the store is full. + bool spillsToHigherPools; /** * @brief This method safely stores the given data in the given packet_id. * @details It also sets the size in size_list. The method does not perform @@ -180,4 +187,4 @@ private: #include "LocalPool.tpp" -#endif /* FRAMEWORK_STORAGEMANAGER_LOCALPOOL_H_ */ +#endif /* FSFW_STORAGEMANAGER_LOCALPOOL_H_ */ diff --git a/storagemanager/LocalPool.tpp b/storagemanager/LocalPool.tpp index 08685de2..5e61efe4 100644 --- a/storagemanager/LocalPool.tpp +++ b/storagemanager/LocalPool.tpp @@ -1,5 +1,9 @@ -#ifndef FRAMEWORK_STORAGEMANAGER_LOCALPOOL_TPP_ -#define FRAMEWORK_STORAGEMANAGER_LOCALPOOL_TPP_ +#ifndef FSFW_STORAGEMANAGER_LOCALPOOL_TPP_ +#define FSFW_STORAGEMANAGER_LOCALPOOL_TPP_ + +#ifndef FSFW_STORAGEMANAGER_LOCALPOOL_H_ +#error Include LocalPool.h before LocalPool.tpp! +#endif template inline LocalPool::LocalPool(object_id_t setObjectId, @@ -122,8 +126,9 @@ inline LocalPool::~LocalPool(void) { } template -inline ReturnValue_t LocalPool::addData(store_address_t* storageId, - const uint8_t* data, size_t size, bool ignoreFault) { +inline ReturnValue_t LocalPool::addData( + store_address_t* storageId, const uint8_t* data, size_t size, + bool ignoreFault) { ReturnValue_t status = reserveSpace(size, storageId, ignoreFault); if (status == RETURN_OK) { write(*storageId, data, size); @@ -144,15 +149,55 @@ inline ReturnValue_t LocalPool::getFreeElement( return status; } +template +inline ConstAccessorPair LocalPool::getData( + store_address_t storeId) { + uint8_t* tempData = nullptr; + ConstStorageAccessor constAccessor(storeId, this); + ReturnValue_t status = modifyData(storeId, &tempData, &constAccessor.size_); + constAccessor.constDataPointer = tempData; + return ConstAccessorPair(status, std::move(constAccessor)); +} + +template +inline ReturnValue_t LocalPool::getData(store_address_t storeId, + ConstStorageAccessor& storeAccessor) { + uint8_t* tempData = nullptr; + ReturnValue_t status = modifyData(storeId, &tempData, &storeAccessor.size_); + storeAccessor.assignStore(this); + storeAccessor.constDataPointer = tempData; + return status; +} + template inline ReturnValue_t LocalPool::getData( store_address_t packet_id, const uint8_t** packet_ptr, size_t* size) { - uint8_t* tempData = NULL; + uint8_t* tempData = nullptr; ReturnValue_t status = modifyData(packet_id, &tempData, size); *packet_ptr = tempData; return status; } +template +inline AccessorPair LocalPool::modifyData( + store_address_t storeId) { + StorageAccessor accessor(storeId, this); + ReturnValue_t status = modifyData(storeId, &accessor.dataPointer, + &accessor.size_); + accessor.assignConstPointer(); + return AccessorPair(status, std::move(accessor)); +} + +template +inline ReturnValue_t LocalPool::modifyData( + store_address_t storeId, StorageAccessor& storeAccessor) { + storeAccessor.assignStore(this); + ReturnValue_t status = modifyData(storeId, &storeAccessor.dataPointer, + &storeAccessor.size_); + storeAccessor.assignConstPointer(); + return status; +} + template inline ReturnValue_t LocalPool::modifyData( store_address_t packet_id, uint8_t** packet_ptr, size_t* size) { @@ -242,8 +287,8 @@ inline ReturnValue_t LocalPool::initialize() { } internalErrorReporter = objectManager->get( objects::INTERNAL_ERROR_REPORTER); - if (internalErrorReporter == NULL){ - return RETURN_FAILED; + if (internalErrorReporter == nullptr){ + return ObjectManagerIF::INTERNAL_ERR_REPORTER_UNINIT; } //Check if any pool size is large than the maximum allowed. @@ -251,10 +296,10 @@ inline ReturnValue_t LocalPool::initialize() { if (element_sizes[count] >= STORAGE_FREE) { sif::error << "LocalPool::initialize: Pool is too large! " "Max. allowed size is: " << (STORAGE_FREE - 1) << std::endl; - return RETURN_FAILED; + return StorageManagerIF::POOL_TOO_LARGE; } } return RETURN_OK; } -#endif +#endif /* FSFW_STORAGEMANAGER_LOCALPOOL_TPP_ */ diff --git a/storagemanager/PoolManager.h b/storagemanager/PoolManager.h index 0b101d66..8cc6c065 100644 --- a/storagemanager/PoolManager.h +++ b/storagemanager/PoolManager.h @@ -1,17 +1,18 @@ -#ifndef POOLMANAGER_H_ -#define POOLMANAGER_H_ - +#ifndef FSFW_STORAGEMANAGER_POOLMANAGER_H_ +#define FSFW_STORAGEMANAGER_POOLMANAGER_H_ #include "LocalPool.h" +#include "StorageAccessor.h" #include "../ipc/MutexHelper.h" + /** * @brief The PoolManager class provides an intermediate data storage with * a fixed pool size policy for inter-process communication. * @details Uses local pool calls but is thread safe by protecting the call * with a lock. + * @author Bastian Baetz */ - template class PoolManager : public LocalPool { public: @@ -19,16 +20,25 @@ public: const uint16_t element_sizes[NUMBER_OF_POOLS], const uint16_t n_elements[NUMBER_OF_POOLS]); - //! @brief In the PoolManager's destructor all allocated memory is freed. + /** + * @brief In the PoolManager's destructor all allocated memory + * is freed. + */ virtual ~PoolManager(); - //! @brief LocalPool overrides for thread-safety. + /** + * @brief LocalPool overrides for thread-safety. Decorator function + * which wraps LocalPool calls with a mutex protection. + */ ReturnValue_t deleteData(store_address_t) override; ReturnValue_t deleteData(uint8_t* buffer, size_t size, - store_address_t* storeId = NULL) override; - ReturnValue_t modifyData(store_address_t packet_id, uint8_t** packet_ptr, - size_t* size) override; + store_address_t* storeId = nullptr) override; + + void setMutexTimeout(uint32_t mutexTimeoutMs); protected: + //! Default mutex timeout value to prevent permanent blocking. + uint32_t mutexTimeoutMs = 20; + ReturnValue_t reserveSpace(const uint32_t size, store_address_t* address, bool ignoreFault) override; @@ -43,4 +53,4 @@ protected: #include "PoolManager.tpp" -#endif /* POOLMANAGER_H_ */ +#endif /* FSFW_STORAGEMANAGER_POOLMANAGER_H_ */ diff --git a/storagemanager/PoolManager.tpp b/storagemanager/PoolManager.tpp index 0e946b66..2be44ece 100644 --- a/storagemanager/PoolManager.tpp +++ b/storagemanager/PoolManager.tpp @@ -1,6 +1,10 @@ #ifndef FRAMEWORK_STORAGEMANAGER_POOLMANAGER_TPP_ #define FRAMEWORK_STORAGEMANAGER_POOLMANAGER_TPP_ +#ifndef FSFW_STORAGEMANAGER_POOLMANAGER_H_ +#error Include PoolManager.h before PoolManager.tpp! +#endif + template inline PoolManager::PoolManager(object_id_t setObjectId, const uint16_t element_sizes[NUMBER_OF_POOLS], @@ -17,7 +21,7 @@ inline PoolManager::~PoolManager(void) { template inline ReturnValue_t PoolManager::reserveSpace( const uint32_t size, store_address_t* address, bool ignoreFault) { - MutexHelper mutexHelper(mutex,MutexIF::BLOCKING); + MutexHelper mutexHelper(mutex,MutexIF::WAITING, mutexTimeoutMs); ReturnValue_t status = LocalPool::reserveSpace(size, address,ignoreFault); return status; @@ -29,7 +33,7 @@ inline ReturnValue_t PoolManager::deleteData( // debug << "PoolManager( " << translateObject(getObjectId()) << // " )::deleteData from store " << packet_id.pool_index << // ". id is "<< packet_id.packet_index << std::endl; - MutexHelper mutexHelper(mutex,MutexIF::BLOCKING); + MutexHelper mutexHelper(mutex,MutexIF::WAITING, mutexTimeoutMs); ReturnValue_t status = LocalPool::deleteData(packet_id); return status; } @@ -37,19 +41,16 @@ inline ReturnValue_t PoolManager::deleteData( template inline ReturnValue_t PoolManager::deleteData(uint8_t* buffer, size_t size, store_address_t* storeId) { - MutexHelper mutexHelper(mutex,MutexIF::BLOCKING); + MutexHelper mutexHelper(mutex,MutexIF::WAITING, mutexTimeoutMs); ReturnValue_t status = LocalPool::deleteData(buffer, size, storeId); return status; } template -inline ReturnValue_t PoolManager::modifyData( - store_address_t packet_id, uint8_t** packet_ptr, size_t* size) { - MutexHelper mutexHelper(mutex,MutexIF::BLOCKING); - ReturnValue_t status = LocalPool::modifyData(packet_id, - packet_ptr, size); - return status; +inline void PoolManager::setMutexTimeout( + uint32_t mutexTimeoutMs) { + this->mutexTimeout = mutexTimeoutMs; } -#endif +#endif /* FRAMEWORK_STORAGEMANAGER_POOLMANAGER_TPP_ */ diff --git a/storagemanager/StorageAccessor.cpp b/storagemanager/StorageAccessor.cpp new file mode 100644 index 00000000..9c2f936a --- /dev/null +++ b/storagemanager/StorageAccessor.cpp @@ -0,0 +1,67 @@ +#include "StorageAccessor.h" +#include "StorageManagerIF.h" +#include "../serviceinterface/ServiceInterfaceStream.h" + +StorageAccessor::StorageAccessor(store_address_t storeId): + ConstStorageAccessor(storeId) { +} + +StorageAccessor::StorageAccessor(store_address_t storeId, + StorageManagerIF* store): + ConstStorageAccessor(storeId, store) { +} + +StorageAccessor& StorageAccessor::operator =( + StorageAccessor&& other) { + // Call the parent move assignment and also assign own member. + dataPointer = other.dataPointer; + StorageAccessor::operator=(std::move(other)); + return * this; +} + +// Call the parent move ctor and also transfer own member. +StorageAccessor::StorageAccessor(StorageAccessor&& other): + ConstStorageAccessor(std::move(other)), dataPointer(other.dataPointer) { +} + +ReturnValue_t StorageAccessor::getDataCopy(uint8_t *pointer, size_t maxSize) { + if(internalState == AccessState::UNINIT) { + sif::warning << "StorageAccessor: Not initialized!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + if(size_ > maxSize) { + sif::error << "StorageAccessor: Supplied buffer not large " + "enough" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + std::copy(dataPointer, dataPointer + size_, pointer); + return HasReturnvaluesIF::RETURN_OK; +} + +uint8_t* StorageAccessor::data() { + if(internalState == AccessState::UNINIT) { + sif::warning << "StorageAccessor: Not initialized!" << std::endl; + } + return dataPointer; +} + +ReturnValue_t StorageAccessor::write(uint8_t *data, size_t size, + uint16_t offset) { + if(internalState == AccessState::UNINIT) { + sif::warning << "StorageAccessor: Not initialized!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + if(offset + size > size_) { + sif::error << "StorageAccessor: Data too large for pool " + "entry!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + std::copy(data, data + size, dataPointer + offset); + return HasReturnvaluesIF::RETURN_OK; +} + +void StorageAccessor::assignConstPointer() { + constDataPointer = dataPointer; +} + + diff --git a/storagemanager/StorageAccessor.h b/storagemanager/StorageAccessor.h new file mode 100644 index 00000000..5cf15d50 --- /dev/null +++ b/storagemanager/StorageAccessor.h @@ -0,0 +1,45 @@ +#ifndef FSFW_STORAGEMANAGER_STORAGEACCESSOR_H_ +#define FSFW_STORAGEMANAGER_STORAGEACCESSOR_H_ + +#include "ConstStorageAccessor.h" + +class StorageManagerIF; + +/** + * @brief Child class for modifyable data. Also has a normal pointer member. + */ +class StorageAccessor: public ConstStorageAccessor { + //! StorageManager classes have exclusive access to private variables. + template + friend class PoolManager; + template + friend class LocalPool; +public: + StorageAccessor(store_address_t storeId); + StorageAccessor(store_address_t storeId, StorageManagerIF* store); + + /** + * @brief Move ctor and move assignment allow returning accessors as + * a returnvalue. They prevent resource being freed prematurely. + * See: https://github.com/MicrosoftDocs/cpp-docs/blob/master/docs/cpp/ + * move-constructors-and-move-assignment-operators-cpp.md + * @param + * @return + */ + StorageAccessor& operator=(StorageAccessor&&); + StorageAccessor(StorageAccessor&&); + + ReturnValue_t write(uint8_t *data, size_t size, + uint16_t offset = 0); + uint8_t* data(); + ReturnValue_t getDataCopy(uint8_t *pointer, size_t maxSize) override; + +private: + //! Non-const pointer for modifyable data. + uint8_t* dataPointer = nullptr; + //! For modifyable data, the const pointer is assigned to the normal + //! pointer by the pool manager so both access functions can be used safely + void assignConstPointer(); +}; + +#endif /* FSFW_STORAGEMANAGER_STORAGEACCESSOR_H_ */ diff --git a/storagemanager/StorageManagerIF.h b/storagemanager/StorageManagerIF.h index 7c194d72..834e7563 100644 --- a/storagemanager/StorageManagerIF.h +++ b/storagemanager/StorageManagerIF.h @@ -1,60 +1,17 @@ -#ifndef STORAGEMANAGERIF_H_H -#define STORAGEMANAGERIF_H_H +#ifndef FSFW_STORAGEMANAGER_STORAGEMANAGERIF_H_ +#define FSFW_STORAGEMANAGER_STORAGEMANAGERIF_H_ + +#include "StorageAccessor.h" +#include "storeAddress.h" #include "../events/Event.h" #include "../returnvalues/HasReturnvaluesIF.h" -#include -/** - * @brief This union defines the type that identifies where a data packet is - * stored in the store. - * It consists of a raw part to read it as raw value and - * a structured part to use it in pool-like stores. - */ -union store_address_t { - /** - * Default Constructor, initializing to INVALID_ADDRESS - */ - store_address_t():raw(0xFFFFFFFF){} +#include +#include - /** - * Constructor to create an address object using the raw address - * @param rawAddress - */ - store_address_t(uint32_t rawAddress):raw(rawAddress){} - - /** - * Constructor to create an address object using pool - * and packet indices - * - * @param poolIndex - * @param packetIndex - */ - store_address_t(uint16_t poolIndex, uint16_t packetIndex): - pool_index(poolIndex),packet_index(packetIndex) {} - - /** - * A structure with two elements to access the store address pool-like. - */ - struct { - /** - * The index in which pool the packet lies. - */ - uint16_t pool_index; - /** - * The position in the chosen pool. - */ - uint16_t packet_index; - }; - /** - * Alternative access to the raw value. - */ - uint32_t raw; - - bool operator==(const store_address_t& other) const { - return raw == other.raw; - } -}; +using AccessorPair = std::pair; +using ConstAccessorPair = std::pair; /** * @brief This class provides an interface for intermediate data storage. @@ -77,6 +34,7 @@ public: static const ReturnValue_t ILLEGAL_STORAGE_ID = MAKE_RETURN_CODE(3); //!< This return code indicates that data was requested with an illegal storage ID. static const ReturnValue_t DATA_DOES_NOT_EXIST = MAKE_RETURN_CODE(4); //!< This return code indicates that the requested ID was valid, but no data is stored there. static const ReturnValue_t ILLEGAL_ADDRESS = MAKE_RETURN_CODE(5); + static const ReturnValue_t POOL_TOO_LARGE = MAKE_RETURN_CODE(6); //!< Pool size too large on initialization. static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::OBSW; static const Event GET_DATA_FAILED = MAKE_EVENT(0, SEVERITY::LOW); @@ -122,6 +80,29 @@ public: */ virtual ReturnValue_t deleteData(uint8_t* buffer, size_t size, store_address_t* storeId = nullptr) = 0; + + + /** + * @brief Access the data by supplying a store ID. + * @details + * A pair consisting of the retrieval result and an instance of a + * ConstStorageAccessor class is returned + * @param storeId + * @return Pair of return value and a ConstStorageAccessor instance + */ + virtual ConstAccessorPair getData(store_address_t storeId) = 0; + + /** + * @brief Access the data by supplying a store ID and a helper + * instance + * @param storeId + * @param constAccessor Wrapper function to access store data. + * @return + */ + virtual ReturnValue_t getData(store_address_t storeId, + ConstStorageAccessor& constAccessor) = 0; + + /** * @brief getData returns an address to data and the size of the data * for a given packet_id. @@ -135,8 +116,30 @@ public: */ virtual ReturnValue_t getData(store_address_t packet_id, const uint8_t** packet_ptr, size_t* size) = 0; + + /** - * Same as above, but not const and therefore modifiable. + * Modify data by supplying a store ID + * @param storeId + * @return Pair of return value and StorageAccessor helper + */ + virtual AccessorPair modifyData(store_address_t storeId) = 0; + + /** + * Modify data by supplying a store ID and a StorageAccessor helper instance. + * @param storeId + * @param accessor Helper class to access the modifiable data. + * @return + */ + virtual ReturnValue_t modifyData(store_address_t storeId, + StorageAccessor& accessor) = 0; + + /** + * Get pointer and size of modifiable data by supplying the storeId + * @param packet_id + * @param packet_ptr [out] Pointer to pointer of data to set + * @param size [out] Pointer to size to set + * @return */ virtual ReturnValue_t modifyData(store_address_t packet_id, uint8_t** packet_ptr, size_t* size) = 0; @@ -155,6 +158,7 @@ public: */ virtual ReturnValue_t getFreeElement(store_address_t* storageId, const size_t size, uint8_t** p_data, bool ignoreFault = false ) = 0; + /** * Clears the whole store. * Use with care! @@ -162,4 +166,4 @@ public: virtual void clearStore() = 0; }; -#endif /* STORAGEMANAGERIF_H_ */ +#endif /* FSFW_STORAGEMANAGER_STORAGEMANAGERIF_H_ */ diff --git a/storagemanager/storeAddress.h b/storagemanager/storeAddress.h new file mode 100644 index 00000000..044c0790 --- /dev/null +++ b/storagemanager/storeAddress.h @@ -0,0 +1,55 @@ +#ifndef FSFW_STORAGEMANAGER_STOREADDRESS_H_ +#define FSFW_STORAGEMANAGER_STOREADDRESS_H_ + +#include + +/** + * This union defines the type that identifies where a data packet is + * stored in the store. It comprises of a raw part to read it as raw value and + * a structured part to use it in pool-like stores. + */ +union store_address_t { + /** + * Default Constructor, initializing to INVALID_ADDRESS + */ + store_address_t():raw(0xFFFFFFFF){} + /** + * Constructor to create an address object using the raw address + * + * @param rawAddress + */ + store_address_t(uint32_t rawAddress):raw(rawAddress){} + + /** + * Constructor to create an address object using pool + * and packet indices + * + * @param poolIndex + * @param packetIndex + */ + store_address_t(uint16_t poolIndex, uint16_t packetIndex): + pool_index(poolIndex),packet_index(packetIndex){} + /** + * A structure with two elements to access the store address pool-like. + */ + struct { + /** + * The index in which pool the packet lies. + */ + uint16_t pool_index; + /** + * The position in the chosen pool. + */ + uint16_t packet_index; + }; + /** + * Alternative access to the raw value. + */ + uint32_t raw; + + bool operator==(const store_address_t& other) const { + return raw == other.raw; + } +}; + +#endif /* FSFW_STORAGEMANAGER_STOREADDRESS_H_ */ diff --git a/tasks/FixedSequenceSlot.cpp b/tasks/FixedSequenceSlot.cpp new file mode 100644 index 00000000..f5d82178 --- /dev/null +++ b/tasks/FixedSequenceSlot.cpp @@ -0,0 +1,17 @@ +#include "FixedSequenceSlot.h" +#include "PeriodicTaskIF.h" +#include + +FixedSequenceSlot::FixedSequenceSlot(object_id_t handlerId, uint32_t setTime, + int8_t setSequenceId, ExecutableObjectIF* executableObject, + PeriodicTaskIF* executingTask) : handlerId(handlerId), + pollingTimeMs(setTime), opcode(setSequenceId) { + if(executableObject == nullptr) { + return; + } + this->executableObject = executableObject; + this->executableObject->setTaskIF(executingTask); +} + +FixedSequenceSlot::~FixedSequenceSlot() {} + diff --git a/devicehandlers/FixedSequenceSlot.h b/tasks/FixedSequenceSlot.h similarity index 53% rename from devicehandlers/FixedSequenceSlot.h rename to tasks/FixedSequenceSlot.h index 73504955..1744ec19 100644 --- a/devicehandlers/FixedSequenceSlot.h +++ b/tasks/FixedSequenceSlot.h @@ -1,41 +1,41 @@ -/** - * @file FixedSequenceSlot.h - * @brief This file defines the PollingSlot class. - * @date 19.12.2012 - * @author baetz - */ - -#ifndef FIXEDSEQUENCESLOT_H_ -#define FIXEDSEQUENCESLOT_H_ +#ifndef FSFW_TASKS_FIXEDSEQUENCESLOT_H_ +#define FSFW_TASKS_FIXEDSEQUENCESLOT_H_ +#include "ExecutableObjectIF.h" #include "../objectmanager/ObjectManagerIF.h" -#include "../tasks/ExecutableObjectIF.h" + class PeriodicTaskIF; /** - * @brief This class is the representation of a single polling sequence table entry. - * - * @details The PollingSlot class is the representation of a single polling - * sequence table entry. + * @brief This class is the representation of a single polling sequence + * table entry. + * @details + * The PollingSlot class is the representation of a single polling + * sequence table entry. + * @author baetz */ class FixedSequenceSlot { public: FixedSequenceSlot( object_id_t handlerId, uint32_t setTimeMs, - int8_t setSequenceId, PeriodicTaskIF* executingTask ); + int8_t setSequenceId, ExecutableObjectIF* executableObject, + PeriodicTaskIF* executingTask); virtual ~FixedSequenceSlot(); + object_id_t handlerId; + /** - * @brief Handler identifies which device handler object is executed in this slot. + * @brief Handler identifies which object is executed in this slot. */ - ExecutableObjectIF* handler; + ExecutableObjectIF* executableObject = nullptr; /** * @brief This attribute defines when a device handler object is executed. - * - * @details The pollingTime attribute identifies the time the handler is executed in ms. - * It must be smaller than the period length of the polling sequence. + * @details + * The pollingTime attribute identifies the time the handler is + * executed in ms. It must be smaller than the period length of the + * polling sequence. */ - uint32_t pollingTimeMs; + uint32_t pollingTimeMs; /** * @brief This value defines the type of device communication. @@ -43,7 +43,7 @@ public: * @details The state of this value decides what communication routine is * called in the PST executable or the device handler object. */ - uint8_t opcode; + uint8_t opcode; /** * @brief Operator overload for the comparison operator to @@ -57,4 +57,4 @@ public: }; -#endif /* FIXEDSEQUENCESLOT_H_ */ +#endif /* FSFW_TASKS_FIXEDSEQUENCESLOT_H_ */ diff --git a/devicehandlers/FixedSlotSequence.cpp b/tasks/FixedSlotSequence.cpp similarity index 53% rename from devicehandlers/FixedSlotSequence.cpp rename to tasks/FixedSlotSequence.cpp index 38d4dcea..e5db4301 100644 --- a/devicehandlers/FixedSlotSequence.cpp +++ b/tasks/FixedSlotSequence.cpp @@ -1,5 +1,6 @@ #include "FixedSlotSequence.h" #include "../serviceinterface/ServiceInterfaceStream.h" +#include FixedSlotSequence::FixedSlotSequence(uint32_t setLengthMs) : lengthMs(setLengthMs) { @@ -12,7 +13,7 @@ FixedSlotSequence::~FixedSlotSequence() { } void FixedSlotSequence::executeAndAdvance() { - current->handler->performOperation(current->opcode); + current->executableObject->performOperation(current->opcode); // if (returnValue != RETURN_OK) { // this->sendErrorMessage( returnValue ); // } @@ -80,44 +81,82 @@ uint32_t FixedSlotSequence::getLengthMs() const { return this->lengthMs; } +void FixedSlotSequence::addSlot(object_id_t componentId, uint32_t slotTimeMs, + int8_t executionStep, ExecutableObjectIF* executableObject, + PeriodicTaskIF* executingTask) { + this->slotList.insert(FixedSequenceSlot(componentId, slotTimeMs, + executionStep, executableObject, executingTask)); + this->current = slotList.begin(); +} + ReturnValue_t FixedSlotSequence::checkSequence() const { if(slotList.empty()) { - sif::error << "Fixed Slot Sequence: Slot list is empty!" << std::endl; + sif::error << "FixedSlotSequence::checkSequence:" + << " Slot list is empty!" << std::endl; return HasReturnvaluesIF::RETURN_FAILED; } - auto slotIt = slotList.begin(); - uint32_t count = 0; - uint32_t time = 0; - while (slotIt != slotList.end()) { - if (slotIt->handler == nullptr) { - sif::error << "FixedSlotSequene::initialize: ObjectId does not exist!" - << std::endl; - count++; - } else if (slotIt->pollingTimeMs < time) { - sif::error << "FixedSlotSequence::initialize: Time: " - << slotIt->pollingTimeMs - << " is smaller than previous with " << time << std::endl; - count++; - } else { - // All ok, print slot. - //info << "Current slot polling time: " << std::endl; - //info << std::dec << slotIt->pollingTimeMs << std::endl; + if(customCheckFunction != nullptr) { + ReturnValue_t result = customCheckFunction(slotList); + if(result != HasReturnvaluesIF::RETURN_OK) { + // Continue for now but print error output. + sif::error << "FixedSlotSequence::checkSequence:" + << " Custom check failed!" << std::endl; } - time = slotIt->pollingTimeMs; - slotIt++; } - //info << "Number of elements in slot list: " + + uint32_t errorCount = 0; + uint32_t time = 0; + for(const auto& slot: slotList) { + if (slot.executableObject == nullptr) { + errorCount++; + } + else if (slot.pollingTimeMs < time) { + sif::error << "FixedSlotSequence::checkSequence: Time: " + << slot.pollingTimeMs << " is smaller than previous with " + << time << std::endl; + errorCount++; + } + else { + // All ok, print slot. + //sif::info << "Current slot polling time: " << std::endl; + //sif::info << std::dec << slotIt->pollingTimeMs << std::endl; + } + time = slot.pollingTimeMs; + + } + //sif::info << "Number of elements in slot list: " // << slotList.size() << std::endl; - if (count > 0) { + if (errorCount > 0) { return HasReturnvaluesIF::RETURN_FAILED; } return HasReturnvaluesIF::RETURN_OK; } -void FixedSlotSequence::addSlot(object_id_t componentId, uint32_t slotTimeMs, - int8_t executionStep, PeriodicTaskIF* executingTask) { - this->slotList.insert(FixedSequenceSlot(componentId, slotTimeMs, executionStep, - executingTask)); - this->current = slotList.begin(); + +ReturnValue_t FixedSlotSequence::intializeSequenceAfterTaskCreation() const { + std::set uniqueObjects; + uint32_t count = 0; + for(const auto& slot: slotList) { + // Ensure that each unique object is initialized once. + if(uniqueObjects.find(slot.executableObject) == uniqueObjects.end()) { + ReturnValue_t result = + slot.executableObject->initializeAfterTaskCreation(); + if(result != HasReturnvaluesIF::RETURN_OK) { + count++; + } + uniqueObjects.emplace(slot.executableObject); + } + } + if (count > 0) { + sif::error << "FixedSlotSequence::intializeSequenceAfterTaskCreation:" + "Counted " << count << " failed initializations!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + return HasReturnvaluesIF::RETURN_OK; +} + +void FixedSlotSequence::addCustomCheck(ReturnValue_t + (*customCheckFunction)(const SlotList&)) { + this->customCheckFunction = customCheckFunction; } diff --git a/devicehandlers/FixedSlotSequence.h b/tasks/FixedSlotSequence.h similarity index 58% rename from devicehandlers/FixedSlotSequence.h rename to tasks/FixedSlotSequence.h index be6dc2d1..077dd10b 100644 --- a/devicehandlers/FixedSlotSequence.h +++ b/tasks/FixedSlotSequence.h @@ -1,26 +1,30 @@ -#ifndef FRAMEWORK_DEVICEHANDLERS_FIXEDSLOTSEQUENCE_H_ -#define FRAMEWORK_DEVICEHANDLERS_FIXEDSLOTSEQUENCE_H_ +#ifndef FSFW_TASKS_FIXEDSLOTSEQUENCE_H_ +#define FSFW_TASKS_FIXEDSLOTSEQUENCE_H_ #include "FixedSequenceSlot.h" #include "../objectmanager/SystemObject.h" + #include /** - * @brief This class is the representation of a Polling Sequence Table in software. - * + * @brief This class is the representation of a + * Polling Sequence Table in software. * @details * The FixedSlotSequence object maintains the dynamic execution of - * device handler objects. + * objects with stricter timing requirements for the FixedTimeslotTask. * - * The main idea is to create a list of device handlers, to announce all - * handlers to thepolling sequence and to maintain a list of - * polling slot objects. This slot list represents the Polling Sequence Table - * in software. + * The main idea is to create a list of executable objects (for example + * device handlers), to announce all handlers to the polling sequence and to + * maintain a list of polling slot objects. + * This slot list represents the Polling Sequence Table in software. * * Each polling slot contains information to indicate when and - * which device handler shall be executed within a given polling period. - * The sequence is then executed by iterating through this slot list. - * Handlers are invoking by calling a certain function stored in the handler list. + * which executable object shall be executed within a given polling period. + * When adding a slot, a pointer to the executing task, a pointer to the + * executable object and a step number can be passed. The step number will be + * passed to the periodic handler. + * The sequence is executed by iterating through the slot sequence and + * executing the executable object in the correct timeslot. */ class FixedSlotSequence { public: @@ -29,41 +33,44 @@ public: /** * @brief The constructor of the FixedSlotSequence object. - * - * @details The constructor takes two arguments, the period length and the init function. - * * @param setLength The period length, expressed in ms. */ FixedSlotSequence(uint32_t setLengthMs); /** * @brief The destructor of the FixedSlotSequence object. - * - * @details The destructor frees all allocated memory by iterating through the slotList - * and deleting all allocated resources. + * @details + * The destructor frees all allocated memory by iterating through the + * slotList and deleting all allocated resources. */ virtual ~FixedSlotSequence(); /** * @brief This is a method to add an PollingSlot object to slotList. * - * @details Here, a polling slot object is added to the slot list. It is appended - * to the end of the list. The list is currently NOT reordered. - * Afterwards, the iterator current is set to the beginning of the list. - * @param Object ID of the object to add - * @param setTime Value between (0 to 1) * slotLengthMs, when a FixedTimeslotTask - * will be called inside the slot period. - * @param setSequenceId ID which can be used to distinguish - * different task operations + * @details + * Here, a polling slot object is added to the slot list. It is appended + * to the end of the list. The list is currently NOT reordered. + * Afterwards, the iterator current is set to the beginning of the list. + * @param handlerId ID of the object to add + * @param setTime + * Value between (0 to 1) * slotLengthMs, when a FixedTimeslotTask + * will be called inside the slot period. + * @param setSequenceId + * ID which can be used to distinguish different task operations. This + * value will be passed to the executable function. * @param * @param */ void addSlot(object_id_t handlerId, uint32_t setTime, int8_t setSequenceId, + ExecutableObjectIF* executableObject, PeriodicTaskIF* executingTask); /** - * Checks if the current slot shall be executed immediately after the one before. - * This allows to distinguish between grouped and not grouped handlers. + * @brief Checks if the current slot shall be executed immediately + * after the one before. + * @details + * This allows to distinguish between grouped and separated handlers. * @return - @c true if the slot has the same polling time as the previous * - @c false else */ @@ -125,12 +132,32 @@ public: SlotListIter current; /** - * Iterate through slotList and check successful creation. + * @brief Check and initialize slot list. + * @details * Checks if timing is ok (must be ascending) and if all handlers were found. * @return */ ReturnValue_t checkSequence() const; + /** + * @brief A custom check can be injected for the respective slot list. + * @details + * This can be used by the developer to check the validity of a certain + * sequence. The function will be run in the #checkSequence function. + * The general check will be continued for now if the custom check function + * fails but a diagnostic debug output will be given. + * @param customCheckFunction + */ + void addCustomCheck(ReturnValue_t (*customCheckFunction)(const SlotList &)); + + /** + * @brief Perform any initialization steps required after the executing + * task has been created. This function should be called from the + * executing task! + * @return + */ + ReturnValue_t intializeSequenceAfterTaskCreation() const; + protected: /** @@ -146,7 +173,9 @@ protected: */ SlotList slotList; + ReturnValue_t (*customCheckFunction)(const SlotList&) = nullptr; + uint32_t lengthMs; }; -#endif /* FIXEDSLOTSEQUENCE_H_ */ +#endif /* FSFW_TASKS_FIXEDSLOTSEQUENCE_H_ */ diff --git a/tcdistribution/CCSDSDistributor.cpp b/tcdistribution/CCSDSDistributor.cpp index 1ebb3185..ddd63308 100644 --- a/tcdistribution/CCSDSDistributor.cpp +++ b/tcdistribution/CCSDSDistributor.cpp @@ -1,31 +1,39 @@ -#include "../serviceinterface/ServiceInterfaceStream.h" #include "CCSDSDistributor.h" + +#include "../serviceinterface/ServiceInterfaceStream.h" #include "../tmtcpacket/SpacePacketBase.h" -CCSDSDistributor::CCSDSDistributor( uint16_t setDefaultApid, object_id_t setObjectId ) : - TcDistributor( setObjectId ), default_apid( setDefaultApid ), tcStore(NULL) { +CCSDSDistributor::CCSDSDistributor(uint16_t setDefaultApid, + object_id_t setObjectId): + TcDistributor(setObjectId), defaultApid( setDefaultApid ) { } -CCSDSDistributor::~CCSDSDistributor() { +CCSDSDistributor::~CCSDSDistributor() {} -} - -iterator_t CCSDSDistributor::selectDestination() { -// debug << "CCSDSDistributor::selectDestination received: " << this->currentMessage.getStorageId().pool_index << ", " << this->currentMessage.getStorageId().packet_index << std::endl; - const uint8_t* p_packet = NULL; +TcDistributor::TcMqMapIter CCSDSDistributor::selectDestination() { +// sif::debug << "CCSDSDistributor::selectDestination received: " << +// this->currentMessage.getStorageId().pool_index << ", " << +// this->currentMessage.getStorageId().packet_index << std::endl; + const uint8_t* packet = nullptr; size_t size = 0; - //TODO check returncode? - this->tcStore->getData( this->currentMessage.getStorageId(), &p_packet, &size ); - SpacePacketBase current_packet( p_packet ); -// info << "CCSDSDistributor::selectDestination has packet with APID " << std::hex << current_packet.getAPID() << std::dec << std::endl; - iterator_t position = this->queueMap.find( current_packet.getAPID() ); + ReturnValue_t result = this->tcStore->getData(currentMessage.getStorageId(), + &packet, &size ); + if(result != HasReturnvaluesIF::RETURN_OK) { + sif::error << "CCSDSDistributor::selectDestination: Getting data from" + " store failed!" << std::endl; + } + SpacePacketBase currentPacket(packet); + +// sif:: info << "CCSDSDistributor::selectDestination has packet with APID " +// << std::hex << currentPacket.getAPID() << std::dec << std::endl; + TcMqMapIter position = this->queueMap.find(currentPacket.getAPID()); if ( position != this->queueMap.end() ) { return position; } else { - //The APID was not found. Forward packet to main SW-APID anyway to create acceptance failure report. - return this->queueMap.find( this->default_apid ); + //The APID was not found. Forward packet to main SW-APID anyway to + // create acceptance failure report. + return this->queueMap.find( this->defaultApid ); } - } MessageQueueId_t CCSDSDistributor::getRequestQueue() { @@ -35,9 +43,9 @@ MessageQueueId_t CCSDSDistributor::getRequestQueue() { ReturnValue_t CCSDSDistributor::registerApplication( AcceptsTelecommandsIF* application) { ReturnValue_t returnValue = RETURN_OK; - bool errorCode = true; - errorCode = this->queueMap.insert( std::pair( application->getIdentifier(), application->getRequestQueue() ) ).second; - if( errorCode == false ) { + auto insertPair = this->queueMap.emplace(application->getIdentifier(), + application->getRequestQueue()); + if(not insertPair.second) { returnValue = RETURN_FAILED; } return returnValue; @@ -46,9 +54,8 @@ ReturnValue_t CCSDSDistributor::registerApplication( ReturnValue_t CCSDSDistributor::registerApplication(uint16_t apid, MessageQueueId_t id) { ReturnValue_t returnValue = RETURN_OK; - bool errorCode = true; - errorCode = this->queueMap.insert( std::pair( apid, id ) ).second; - if( errorCode == false ) { + auto insertPair = this->queueMap.emplace(apid, id); + if(not insertPair.second) { returnValue = RETURN_FAILED; } return returnValue; @@ -62,7 +69,11 @@ uint16_t CCSDSDistributor::getIdentifier() { ReturnValue_t CCSDSDistributor::initialize() { ReturnValue_t status = this->TcDistributor::initialize(); this->tcStore = objectManager->get( objects::TC_STORE ); - if (this->tcStore == NULL) status = RETURN_FAILED; + if (this->tcStore == nullptr) { + sif::error << "CCSDSDistributor::initialize: Could not initialize" + " TC store!" << std::endl; + status = RETURN_FAILED; + } return status; } diff --git a/tcdistribution/CCSDSDistributor.h b/tcdistribution/CCSDSDistributor.h index bff72092..e8d54c9c 100644 --- a/tcdistribution/CCSDSDistributor.h +++ b/tcdistribution/CCSDSDistributor.h @@ -1,58 +1,71 @@ -#ifndef CCSDSDISTRIBUTOR_H_ -#define CCSDSDISTRIBUTOR_H_ +#ifndef FRAMEWORK_TCDISTRIBUTION_CCSDSDISTRIBUTOR_H_ +#define FRAMEWORK_TCDISTRIBUTION_CCSDSDISTRIBUTOR_H_ #include "../objectmanager/ObjectManagerIF.h" #include "../storagemanager/StorageManagerIF.h" -#include "CCSDSDistributorIF.h" -#include "TcDistributor.h" +#include "../tcdistribution/CCSDSDistributorIF.h" +#include "../tcdistribution/TcDistributor.h" #include "../tmtcservices/AcceptsTelecommandsIF.h" + /** - * An instantiation of the CCSDSDistributorIF. - * It receives Space Packets, and selects a destination depending on the APID of the telecommands. + * @brief An instantiation of the CCSDSDistributorIF. + * @details + * It receives Space Packets, and selects a destination depending on the + * APID of the telecommands. * The Secondary Header (with Service/Subservice) is ignored. - * \ingroup tc_distribution + * @ingroup tc_distribution */ -class CCSDSDistributor : public TcDistributor, public CCSDSDistributorIF, public AcceptsTelecommandsIF { -protected: - /** - * This implementation checks if an Application with fitting APID has registered and forwards the - * packet to the according message queue. - * If the packet is not found, it returns the queue to \c default_apid, where a Acceptance Failure - * message should be generated. - * @return Iterator to map entry of found APID or iterator to default APID. - */ - iterator_t selectDestination(); - /** - * The default APID, where packets with unknown APID are sent to. - */ - uint16_t default_apid; - /** - * A reference to the TC storage must be maintained, as this class handles pure Space Packets and there - * exists no SpacePacketStored class. - */ - StorageManagerIF* tcStore; - /** - * The callback here handles the generation of acceptance success/failure messages. - */ - ReturnValue_t callbackAfterSending( ReturnValue_t queueStatus ); +class CCSDSDistributor : public TcDistributor, + public CCSDSDistributorIF, + public AcceptsTelecommandsIF { public: /** - * The constructor sets the default APID and calls the TcDistributor ctor with a certain object id. - * \c tcStore is set in the \c initialize method. - * @param set_default_apid The default APID, where packets with unknown destination are sent to. + * @brief The constructor sets the default APID and calls the + * TcDistributor ctor with a certain object id. + * @details + * @c tcStore is set in the @c initialize method. + * @param setDefaultApid The default APID, where packets with unknown + * destination are sent to. */ - CCSDSDistributor( uint16_t setDefaultApid, object_id_t setObjectId ); + CCSDSDistributor(uint16_t setDefaultApid, object_id_t setObjectId); /** * The destructor is empty. */ - ~CCSDSDistributor(); - MessageQueueId_t getRequestQueue(); - ReturnValue_t registerApplication( uint16_t apid, MessageQueueId_t id ); - ReturnValue_t registerApplication( AcceptsTelecommandsIF* application ); - uint16_t getIdentifier(); - ReturnValue_t initialize(); + virtual ~CCSDSDistributor(); + + MessageQueueId_t getRequestQueue() override; + ReturnValue_t registerApplication( uint16_t apid, + MessageQueueId_t id) override; + ReturnValue_t registerApplication( + AcceptsTelecommandsIF* application) override; + uint16_t getIdentifier() override; + ReturnValue_t initialize() override; + +protected: + /** + * This implementation checks if an application with fitting APID has + * registered and forwards the packet to the according message queue. + * If the packet is not found, it returns the queue to @c defaultApid, + * where a Acceptance Failure message should be generated. + * @return Iterator to map entry of found APID or iterator to default APID. + */ + TcMqMapIter selectDestination() override; + /** + * The callback here handles the generation of acceptance + * success/failure messages. + */ + ReturnValue_t callbackAfterSending( ReturnValue_t queueStatus ) override; + + /** + * The default APID, where packets with unknown APID are sent to. + */ + uint16_t defaultApid; + /** + * A reference to the TC storage must be maintained, as this class handles + * pure Space Packets and there exists no SpacePacketStored class. + */ + StorageManagerIF* tcStore = nullptr; + }; - - -#endif /* CCSDSDISTRIBUTOR_H_ */ +#endif /* FRAMEWORK_TCDISTRIBUTION_CCSDSDISTRIBUTOR_H_ */ diff --git a/tcdistribution/CCSDSDistributorIF.h b/tcdistribution/CCSDSDistributorIF.h index be3c2811..6334a35a 100644 --- a/tcdistribution/CCSDSDistributorIF.h +++ b/tcdistribution/CCSDSDistributorIF.h @@ -1,34 +1,38 @@ -#ifndef CCSDSDISTRIBUTORIF_H_ -#define CCSDSDISTRIBUTORIF_H_ +#ifndef FSFW_TCDISTRIBUTION_CCSDSDISTRIBUTORIF_H_ +#define FSFW_TCDISTRIBUTION_CCSDSDISTRIBUTORIF_H_ #include "../tmtcservices/AcceptsTelecommandsIF.h" #include "../ipc/MessageQueueSenderIF.h" /** * This is the Interface to a CCSDS Distributor. - * On a CCSDS Distributor, Applications (in terms of CCSDS) may register themselves, - * either by passing a pointer to themselves (and implementing the CCSDSApplicationIF, - * or by explicitly passing an APID and a MessageQueueId to route the TC's to. - * \ingroup tc_distribution + * On a CCSDS Distributor, Applications (in terms of CCSDS) may register + * themselves, either by passing a pointer to themselves (and implementing the + * CCSDSApplicationIF), or by explicitly passing an APID and a MessageQueueId + * to route the TC's to. + * @ingroup tc_distribution */ class CCSDSDistributorIF { public: /** - * With this call, a class implementing the CCSDSApplicationIF can register at the - * distributor. + * With this call, a class implementing the CCSDSApplicationIF can register + * at the distributor. * @param application A pointer to the Application to register. - * @return - \c RETURN_OK on success, - * - \c RETURN_FAILED on failure. + * @return - @c RETURN_OK on success, + * - @c RETURN_FAILED on failure. */ - virtual ReturnValue_t registerApplication( AcceptsTelecommandsIF* application ) = 0; + virtual ReturnValue_t registerApplication( + AcceptsTelecommandsIF* application) = 0; /** * With this call, other Applications can register to the CCSDS distributor. * This is done by passing an APID and a MessageQueueId to the method. * @param apid The APID to register. - * @param id The MessageQueueId of the message queue to send the TC Packets to. - * @return - \c RETURN_OK on success, - * - \c RETURN_FAILED on failure. + * @param id The MessageQueueId of the message queue to send the + * TC Packets to. + * @return - @c RETURN_OK on success, + * - @c RETURN_FAILED on failure. */ - virtual ReturnValue_t registerApplication( uint16_t apid, MessageQueueId_t id ) = 0; + virtual ReturnValue_t registerApplication( uint16_t apid, + MessageQueueId_t id) = 0; /** * The empty virtual destructor. */ @@ -37,4 +41,4 @@ public: }; -#endif /* CCSDSDISTRIBUTORIF_H_ */ +#endif /* FSFW_TCDISTRIBUTION_CCSDSDISTRIBUTORIF_H_ */ diff --git a/tcdistribution/PUSDistributor.cpp b/tcdistribution/PUSDistributor.cpp index f77eb99d..d964202f 100644 --- a/tcdistribution/PUSDistributor.cpp +++ b/tcdistribution/PUSDistributor.cpp @@ -1,61 +1,71 @@ -#include "../serviceinterface/ServiceInterfaceStream.h" #include "CCSDSDistributorIF.h" #include "PUSDistributor.h" + +#include "../serviceinterface/ServiceInterfaceStream.h" #include "../tmtcpacket/pus/TcPacketStored.h" #include "../tmtcservices/PusVerificationReport.h" -PUSDistributor::PUSDistributor(uint16_t setApid, object_id_t setObjectId, object_id_t setPacketSource) : - TcDistributor(setObjectId), checker(setApid), verifyChannel(), currentPacket(), tcStatus( - RETURN_FAILED), packetSource(setPacketSource) { +PUSDistributor::PUSDistributor(uint16_t setApid, object_id_t setObjectId, + object_id_t setPacketSource) : + TcDistributor(setObjectId), checker(setApid), verifyChannel(), + tcStatus(RETURN_FAILED), packetSource(setPacketSource) {} + +PUSDistributor::~PUSDistributor() {} + +PUSDistributor::TcMqMapIter PUSDistributor::selectDestination() { + // sif:: debug << "PUSDistributor::handlePacket received: " + // << this->current_packet_id.store_index << ", " + // << this->current_packet_id.packet_index << std::endl; + TcMqMapIter queueMapIt = this->queueMap.end(); + if(this->currentPacket == nullptr) { + return queueMapIt; + } + this->currentPacket->setStoreAddress(this->currentMessage.getStorageId()); + if (currentPacket->getWholeData() != nullptr) { + tcStatus = checker.checkPacket(currentPacket); +#ifdef DEBUG + if(tcStatus != HasReturnvaluesIF::RETURN_OK) { + sif::debug << "PUSDistributor::handlePacket: Packet format " + << "invalid, code "<< static_cast(tcStatus) + << std::endl; + } +#endif + uint32_t queue_id = currentPacket->getService(); + queueMapIt = this->queueMap.find(queue_id); + } + else { + tcStatus = PACKET_LOST; + } + + if (queueMapIt == this->queueMap.end()) { + tcStatus = DESTINATION_NOT_FOUND; +#ifdef DEBUG + sif::debug << "PUSDistributor::handlePacket: Destination not found, " + << "code "<< static_cast(tcStatus) << std::endl; +#endif + } + + if (tcStatus != RETURN_OK) { + return this->queueMap.end(); + } + else { + return queueMapIt; + } } -PUSDistributor::~PUSDistributor() { - -} - -iterator_t PUSDistributor::selectDestination() { -// debug << "PUSDistributor::handlePacket received: " << this->current_packet_id.store_index << ", " << this->current_packet_id.packet_index << std::endl; - iterator_t queueMapIt = this->queueMap.end(); - this->currentPacket.setStoreAddress(this->currentMessage.getStorageId()); - if (currentPacket.getWholeData() != NULL) { - tcStatus = checker.checkPacket(¤tPacket); -// info << "PUSDistributor::handlePacket: packetCheck returned with " << (int)tc_status << std::endl; - uint32_t queue_id = currentPacket.getService(); - queueMapIt = this->queueMap.find(queue_id); - } else { - tcStatus = PACKET_LOST; - } - if (queueMapIt == this->queueMap.end()) { - tcStatus = DESTINATION_NOT_FOUND; - } - - if (tcStatus != RETURN_OK) { - sif::debug << "PUSDistributor::handlePacket: error with " << (int) tcStatus - << std::endl; - return this->queueMap.end(); - } else { - return queueMapIt; - } - -} - -//uint16_t PUSDistributor::createDestination( uint8_t service_id, uint8_t subservice_id ) { -// return ( service_id << 8 ) + subservice_id; -//} ReturnValue_t PUSDistributor::registerService(AcceptsTelecommandsIF* service) { - ReturnValue_t returnValue = RETURN_OK; - bool errorCode = true; uint16_t serviceId = service->getIdentifier(); + // sif::info << "Service ID: " << (int)serviceId << std::endl; MessageQueueId_t queue = service->getRequestQueue(); - errorCode = this->queueMap.insert( - std::pair(serviceId, queue)).second; - if (errorCode == false) { - //TODO Return Code - returnValue = MessageQueueIF::NO_QUEUE; + auto returnPair = queueMap.emplace(serviceId, queue); + if (not returnPair.second) { + sif::error << "PUSDistributor::registerService: Service ID already" + " exists in map." << std::endl; + return SERVICE_ID_ALREADY_EXISTS; } - return returnValue; + return HasReturnvaluesIF::RETURN_OK; } MessageQueueId_t PUSDistributor::getRequestQueue() { @@ -68,13 +78,14 @@ ReturnValue_t PUSDistributor::callbackAfterSending(ReturnValue_t queueStatus) { } if (tcStatus != RETURN_OK) { this->verifyChannel.sendFailureReport(TC_VERIFY::ACCEPTANCE_FAILURE, - ¤tPacket, tcStatus); - //A failed packet is deleted immediately after reporting, otherwise it will block memory. - currentPacket.deletePacket(); + currentPacket, tcStatus); + // A failed packet is deleted immediately after reporting, + // otherwise it will block memory. + currentPacket->deletePacket(); return RETURN_FAILED; } else { this->verifyChannel.sendSuccessReport(TC_VERIFY::ACCEPTANCE_SUCCESS, - ¤tPacket); + currentPacket); return RETURN_OK; } } @@ -84,11 +95,19 @@ uint16_t PUSDistributor::getIdentifier() { } ReturnValue_t PUSDistributor::initialize() { + currentPacket = new TcPacketStored(); + if(currentPacket == nullptr) { + // Should not happen, memory allocation failed! + return ObjectManagerIF::CHILD_INIT_FAILED; + } + CCSDSDistributorIF* ccsdsDistributor = objectManager->get(packetSource); - if (ccsdsDistributor == NULL) { - return RETURN_FAILED; - } else { - return ccsdsDistributor->registerApplication(this); + if (ccsdsDistributor == nullptr) { + sif::error << "PUSDistributor::initialize: Packet source invalid." + << " Make sure it exists and implements CCSDSDistributorIF!" + << std::endl; + return RETURN_FAILED; } + return ccsdsDistributor->registerApplication(this); } diff --git a/tcdistribution/PUSDistributor.h b/tcdistribution/PUSDistributor.h index e9d4be8c..be3804ef 100644 --- a/tcdistribution/PUSDistributor.h +++ b/tcdistribution/PUSDistributor.h @@ -1,67 +1,79 @@ -#ifndef PUSDISTRIBUTOR_H_ -#define PUSDISTRIBUTOR_H_ +#ifndef FSFW_TCDISTRIBUTION_PUSDISTRIBUTOR_H_ +#define FSFW_TCDISTRIBUTION_PUSDISTRIBUTOR_H_ -#include "../returnvalues/HasReturnvaluesIF.h" #include "PUSDistributorIF.h" #include "TcDistributor.h" #include "TcPacketCheck.h" + +#include "../returnvalues/HasReturnvaluesIF.h" #include "../tmtcservices/AcceptsTelecommandsIF.h" #include "../tmtcservices/VerificationReporter.h" /** - * This class accepts PUS Telecommands and forwards them to Application services. - * In addition, the class performs a formal packet check and sends acceptance success - * or failure messages. - * \ingroup tc_distribution + * This class accepts PUS Telecommands and forwards them to Application + * services. In addition, the class performs a formal packet check and + * sends acceptance success or failure messages. + * @ingroup tc_distribution */ class PUSDistributor: public TcDistributor, public PUSDistributorIF, public AcceptsTelecommandsIF { public: /** - * The ctor passes \c set_apid to the checker class and calls the TcDistribution ctor with a certain object id. + * The ctor passes @c set_apid to the checker class and calls the + * TcDistribution ctor with a certain object id. * @param setApid The APID of this receiving Application. * @param setObjectId Object ID of the distributor itself - * @param setPacketSource Object ID of the source of TC packets. Must implement CCSDSDistributorIF. + * @param setPacketSource Object ID of the source of TC packets. + * Must implement CCSDSDistributorIF. */ - PUSDistributor(uint16_t setApid, object_id_t setObjectId, object_id_t setPacketSource); + PUSDistributor(uint16_t setApid, object_id_t setObjectId, + object_id_t setPacketSource); /** * The destructor is empty. */ virtual ~PUSDistributor(); - ReturnValue_t registerService(AcceptsTelecommandsIF* service); - MessageQueueId_t getRequestQueue(); - uint16_t getIdentifier(); - ReturnValue_t initialize(); + ReturnValue_t registerService(AcceptsTelecommandsIF* service) override; + MessageQueueId_t getRequestQueue() override; + ReturnValue_t initialize() override; + uint16_t getIdentifier() override; + protected: /** * This attribute contains the class, that performs a formal packet check. */ TcPacketCheck checker; /** - * With this class, verification messages are sent to the TC Verification service. + * With this class, verification messages are sent to the + * TC Verification service. */ VerificationReporter verifyChannel; /** * The currently handled packet is stored here. */ - TcPacketStored currentPacket; + TcPacketStored* currentPacket = nullptr; /** - * With this variable, the current check status is stored to generate acceptance messages later. + * With this variable, the current check status is stored to generate + * acceptance messages later. */ ReturnValue_t tcStatus; const object_id_t packetSource; + /** - * This method reads the packet service, checks if such a service is registered and forwards the packet to the destination. - * It also initiates the formal packet check and sending of verification messages. - * @return Iterator to map entry of found service id or iterator to \c map.end(). + * This method reads the packet service, checks if such a service is + * registered and forwards the packet to the destination. + * It also initiates the formal packet check and sending of verification + * messages. + * @return Iterator to map entry of found service id + * or iterator to @c map.end(). */ - iterator_t selectDestination(); + TcMqMapIter selectDestination() override; /** - * The callback here handles the generation of acceptance success/failure messages. + * The callback here handles the generation of acceptance + * success/failure messages. */ - ReturnValue_t callbackAfterSending(ReturnValue_t queueStatus); + ReturnValue_t callbackAfterSending(ReturnValue_t queueStatus) override; }; -#endif /* PUSDISTRIBUTOR_H_ */ +#endif /* FSFW_TCDISTRIBUTION_PUSDISTRIBUTOR_H_ */ diff --git a/tcdistribution/PUSDistributorIF.h b/tcdistribution/PUSDistributorIF.h index 5e27b35c..0125c08f 100644 --- a/tcdistribution/PUSDistributorIF.h +++ b/tcdistribution/PUSDistributorIF.h @@ -1,11 +1,12 @@ -#ifndef PUSDISTRIBUTORIF_H_ -#define PUSDISTRIBUTORIF_H_ +#ifndef FSFW_TCDISTRIBUTION_PUSDISTRIBUTORIF_H_ +#define FSFW_TCDISTRIBUTION_PUSDISTRIBUTORIF_H_ #include "../tmtcservices/AcceptsTelecommandsIF.h" #include "../ipc/MessageQueueSenderIF.h" + /** * This interface allows PUS Services to register themselves at a PUS Distributor. - * \ingroup tc_distribution + * @ingroup tc_distribution */ class PUSDistributorIF { public: @@ -17,10 +18,10 @@ public: /** * With this method, Services can register themselves at the PUS Distributor. * @param service A pointer to the registering Service. - * @return - \c RETURN_OK on success, - * - \c RETURN_FAILED on failure. + * @return - @c RETURN_OK on success, + * - @c RETURN_FAILED on failure. */ virtual ReturnValue_t registerService( AcceptsTelecommandsIF* service ) = 0; }; -#endif /* PUSDISTRIBUTORIF_H_ */ +#endif /* FSFW_TCDISTRIBUTION_PUSDISTRIBUTORIF_H_ */ diff --git a/tcdistribution/TcDistributor.cpp b/tcdistribution/TcDistributor.cpp index 49a996d9..06e1817f 100644 --- a/tcdistribution/TcDistributor.cpp +++ b/tcdistribution/TcDistributor.cpp @@ -1,12 +1,13 @@ -#include "../serviceinterface/ServiceInterfaceStream.h" -#include "../serviceinterface/ServiceInterfaceStream.h" #include "TcDistributor.h" + +#include "../serviceinterface/ServiceInterfaceStream.h" #include "../tmtcservices/TmTcMessage.h" #include "../ipc/QueueFactory.h" -TcDistributor::TcDistributor(object_id_t set_object_id) : - SystemObject(set_object_id), tcQueue(NULL) { - tcQueue = QueueFactory::instance()->createMessageQueue(DISTRIBUTER_MAX_PACKETS); +TcDistributor::TcDistributor(object_id_t objectId) : + SystemObject(objectId) { + tcQueue = QueueFactory::instance()-> + createMessageQueue(DISTRIBUTER_MAX_PACKETS); } TcDistributor::~TcDistributor() { @@ -15,7 +16,6 @@ TcDistributor::~TcDistributor() { ReturnValue_t TcDistributor::performOperation(uint8_t opCode) { ReturnValue_t status = RETURN_OK; -// debug << "TcDistributor: performing Operation." << std::endl; for (status = tcQueue->receiveMessage(¤tMessage); status == RETURN_OK; status = tcQueue->receiveMessage(¤tMessage)) { status = handlePacket(); @@ -29,7 +29,7 @@ ReturnValue_t TcDistributor::performOperation(uint8_t opCode) { ReturnValue_t TcDistributor::handlePacket() { - iterator_t queueMapIt = this->selectDestination(); + TcMqMapIter queueMapIt = this->selectDestination(); ReturnValue_t returnValue = RETURN_FAILED; if (queueMapIt != this->queueMap.end()) { returnValue = this->tcQueue->sendMessage(queueMapIt->second, @@ -39,14 +39,14 @@ ReturnValue_t TcDistributor::handlePacket() { } void TcDistributor::print() { - sif::debug << "Distributor content is: " << std::endl << "ID\t| message queue id" - << std::endl; - for (iterator_t it = this->queueMap.begin(); it != this->queueMap.end(); - it++) { - sif::debug << it->first << "\t| 0x" << std::hex << it->second << std::dec - << std::endl; + sif::debug << "Distributor content is: " << std::endl + << "ID\t| Message Queue ID" << std::endl; + sif::debug << std::setfill('0') << std::setw(8) << std::hex; + for (const auto& queueMapIter: queueMap) { + sif::debug << queueMapIter.first << "\t| 0x" << queueMapIter.second + << std::endl; } - sif::debug << std::dec; + sif::debug << std::setfill(' ') << std::dec; } diff --git a/tcdistribution/TcDistributor.h b/tcdistribution/TcDistributor.h index fed1cb3e..5d0ca45d 100644 --- a/tcdistribution/TcDistributor.h +++ b/tcdistribution/TcDistributor.h @@ -1,5 +1,6 @@ -#ifndef TCDISTRIBUTOR_H_ -#define TCDISTRIBUTOR_H_ +#ifndef FSFW_TMTCSERVICES_TCDISTRIBUTOR_H_ +#define FSFW_TMTCSERVICES_TCDISTRIBUTOR_H_ + #include "../objectmanager/ObjectManagerIF.h" #include "../objectmanager/SystemObject.h" #include "../returnvalues/HasReturnvaluesIF.h" @@ -9,16 +10,12 @@ #include "../ipc/MessageQueueIF.h" #include - /** - * \defgroup tc_distribution Telecommand Distribution - * All classes associated with Routing and Distribution of Telecommands belong to this group. + * @defgroup tc_distribution Telecommand Distribution + * All classes associated with Routing and Distribution of Telecommands + * belong to this group. */ -/** - * This typedef simplifies writing down the \c map iterator. - */ -typedef std::map::iterator iterator_t; /** * This is the base class to implement distributors for Space Packets. @@ -28,62 +25,19 @@ typedef std::map::iterator iterator_t; * message queue ids to some identifier. The process of unpacking the * destination information from the packet is handled by the child class * implementations. - * \ingroup tc_distribution + * @ingroup tc_distribution */ -class TcDistributor : public SystemObject, public ExecutableObjectIF, public HasReturnvaluesIF { -private: - /** - * This constant sets the maximum number of packets distributed per call. - */ - static const uint8_t DISTRIBUTER_MAX_PACKETS = 128; -protected: - /** - * This is the receiving queue for incoming Telecommands. - * The child classes must make its queue id public. - */ - MessageQueueIF* tcQueue; - /** - * The last received incoming packet information is stored in this - * member. - * As different child classes unpack the incoming packet differently - * (i.e. as a CCSDS Space Packet or as a PUS Telecommand Packet), it - * is not tried to unpack the packet information within this class. - */ - TmTcMessage currentMessage; - /** - * The map that links certain packet information to a destination. - * The packet information may be the APID of the packet or the service - * identifier. Filling of the map is under control of the different child - * classes. - */ - std::map queueMap; - /** - * This method shall unpack the routing information from the incoming - * packet and select the map entry which represents the packet's target. - * @return An iterator to the map element to forward to or queuMap.end(). - */ - virtual iterator_t selectDestination() = 0; - /** - * The handlePacket method calls the child class's selectDestination method - * and forwards the packet to its destination, if found. - * @return The message queue return value or \c RETURN_FAILED, in case no - * destination was found. - */ - ReturnValue_t handlePacket(); - /** - * This method gives the child class a chance to perform some kind of operation - * after the parent tried to forward the message. - * A typically application would be sending success/failure messages. - * The default implementation just returns \c RETURN_OK. - * @param queueStatus The status of the message queue after an attempt to send the TC. - * @return - \c RETURN_OK on success - * - \c RETURN_FAILED on failure - */ - virtual ReturnValue_t callbackAfterSending( ReturnValue_t queueStatus ); +class TcDistributor : public SystemObject, + public ExecutableObjectIF, + public HasReturnvaluesIF { public: - static const uint8_t INTERFACE_ID = CLASS_ID::PACKET_DISTRIBUTION; - static const ReturnValue_t PACKET_LOST = MAKE_RETURN_CODE( 1 ); - static const ReturnValue_t DESTINATION_NOT_FOUND = MAKE_RETURN_CODE( 2 ); + using TcMessageQueueMap = std::map; + using TcMqMapIter = std::map::iterator; + + static constexpr uint8_t INTERFACE_ID = CLASS_ID::PACKET_DISTRIBUTION; + static constexpr ReturnValue_t PACKET_LOST = MAKE_RETURN_CODE( 1 ); + static constexpr ReturnValue_t DESTINATION_NOT_FOUND = MAKE_RETURN_CODE( 2 ); + static constexpr ReturnValue_t SERVICE_ID_ALREADY_EXISTS = MAKE_RETURN_CODE(3); /** * Within the default constructor, the SystemObject id is set and the * message queue is initialized. @@ -91,7 +45,7 @@ public: * @param set_object_id This id is assigned to the distributor * implementation. */ - TcDistributor( object_id_t set_object_id ); + TcDistributor(object_id_t objectId); /** * The destructor is empty, the message queues are not in the vicinity of * this class. @@ -110,7 +64,59 @@ public: * queueMap. */ void print(); + +protected: + /** + * This is the receiving queue for incoming Telecommands. + * The child classes must make its queue id public. + */ + MessageQueueIF* tcQueue = nullptr; + /** + * The last received incoming packet information is stored in this + * member. + * As different child classes unpack the incoming packet differently + * (i.e. as a CCSDS Space Packet or as a PUS Telecommand Packet), it + * is not tried to unpack the packet information within this class. + */ + TmTcMessage currentMessage; + /** + * The map that links certain packet information to a destination. + * The packet information may be the APID of the packet or the service + * identifier. Filling of the map is under control of the different child + * classes. + */ + TcMessageQueueMap queueMap; + /** + * This method shall unpack the routing information from the incoming + * packet and select the map entry which represents the packet's target. + * @return An iterator to the map element to forward to or queuMap.end(). + */ + virtual TcMqMapIter selectDestination() = 0; + /** + * The handlePacket method calls the child class's selectDestination method + * and forwards the packet to its destination, if found. + * @return The message queue return value or @c RETURN_FAILED, in case no + * destination was found. + */ + ReturnValue_t handlePacket(); + /** + * This method gives the child class a chance to perform some kind of + * operation after the parent tried to forward the message. + * A typically application would be sending success/failure messages. + * The default implementation just returns @c RETURN_OK. + * @param queueStatus The status of the message queue after an attempt + * to send the TC. + * @return - @c RETURN_OK on success + * - @c RETURN_FAILED on failure + */ + virtual ReturnValue_t callbackAfterSending( ReturnValue_t queueStatus ); + +private: + /** + * This constant sets the maximum number of packets distributed per call. + */ + static constexpr uint8_t DISTRIBUTER_MAX_PACKETS = 128; }; -#endif /* TCDISTRIBUTOR_H_ */ +#endif /* FSFW_TMTCSERVICES_TCDISTRIBUTOR_H_ */ diff --git a/tcdistribution/TcPacketCheck.cpp b/tcdistribution/TcPacketCheck.cpp index b5881083..38ed04aa 100644 --- a/tcdistribution/TcPacketCheck.cpp +++ b/tcdistribution/TcPacketCheck.cpp @@ -1,31 +1,33 @@ +#include "TcPacketCheck.h" + #include "../globalfunctions/CRC.h" #include "../serviceinterface/ServiceInterfaceStream.h" #include "../storagemanager/StorageManagerIF.h" -#include "TcPacketCheck.h" #include "../tmtcservices/VerificationCodes.h" -TcPacketCheck::TcPacketCheck( uint16_t set_apid ) : apid(set_apid) { +TcPacketCheck::TcPacketCheck( uint16_t setApid ) : apid(setApid) { } -ReturnValue_t TcPacketCheck::checkPacket( TcPacketStored* current_packet ) { - uint16_t calculated_crc = CRC::crc16ccitt( current_packet->getWholeData(), current_packet->getFullSize() ); +ReturnValue_t TcPacketCheck::checkPacket( TcPacketStored* currentPacket ) { + uint16_t calculated_crc = CRC::crc16ccitt( currentPacket->getWholeData(), + currentPacket->getFullSize() ); if ( calculated_crc != 0 ) { return INCORRECT_CHECKSUM; } - bool condition = !(current_packet->hasSecondaryHeader()) || - current_packet->getPacketVersionNumber() != CCSDS_VERSION_NUMBER || - !(current_packet->isTelecommand()); + bool condition = (not currentPacket->hasSecondaryHeader()) or + (currentPacket->getPacketVersionNumber() != CCSDS_VERSION_NUMBER) or + (not currentPacket->isTelecommand()); if ( condition ) { return INCORRECT_PRIMARY_HEADER; } - if ( current_packet->getAPID() != this->apid ) + if ( currentPacket->getAPID() != this->apid ) return ILLEGAL_APID; - if ( !current_packet->isSizeCorrect() ) { + if ( not currentPacket->isSizeCorrect() ) { return INCOMPLETE_PACKET; } - condition = (current_packet->getSecondaryHeaderFlag() != CCSDS_SECONDARY_HEADER_FLAG) || - (current_packet->getPusVersionNumber() != PUS_VERSION_NUMBER); + condition = (currentPacket->getSecondaryHeaderFlag() != CCSDS_SECONDARY_HEADER_FLAG) || + (currentPacket->getPusVersionNumber() != PUS_VERSION_NUMBER); if ( condition ) { return INCORRECT_SECONDARY_HEADER; } diff --git a/tcdistribution/TcPacketCheck.h b/tcdistribution/TcPacketCheck.h index 4ba269f5..703bb1bb 100644 --- a/tcdistribution/TcPacketCheck.h +++ b/tcdistribution/TcPacketCheck.h @@ -1,28 +1,29 @@ -#ifndef TCPACKETCHECK_H_ -#define TCPACKETCHECK_H_ +#ifndef FSFW_TCDISTRIBUTION_TCPACKETCHECK_H_ +#define FSFW_TCDISTRIBUTION_TCPACKETCHECK_H_ #include "../returnvalues/HasReturnvaluesIF.h" #include "../tmtcpacket/pus/TcPacketStored.h" #include "../tmtcservices/PusVerificationReport.h" + /** * This class performs a formal packet check for incoming PUS Telecommand Packets. * Currently, it only checks if the APID and CRC are correct. - * \ingroup tc_distribution + * @ingroup tc_distribution */ class TcPacketCheck : public HasReturnvaluesIF { protected: /** * Describes the version number a packet must have to pass. */ - static const uint8_t CCSDS_VERSION_NUMBER = 0; + static constexpr uint8_t CCSDS_VERSION_NUMBER = 0; /** * Describes the secondary header a packet must have to pass. */ - static const uint8_t CCSDS_SECONDARY_HEADER_FLAG = 0; + static constexpr uint8_t CCSDS_SECONDARY_HEADER_FLAG = 0; /** * Describes the TC Packet PUS Version Number a packet must have to pass. */ - static const uint8_t PUS_VERSION_NUMBER = 1; + static constexpr uint8_t PUS_VERSION_NUMBER = 1; /** * The packet id each correct packet should have. * It is composed of the APID and some static fields. @@ -41,19 +42,19 @@ public: * The constructor only sets the APID attribute. * @param set_apid The APID to set. */ - TcPacketCheck( uint16_t set_apid ); + TcPacketCheck( uint16_t setApid ); /** * This is the actual method to formally check a certain Telecommand Packet. * The packet's Application Data can not be checked here. * @param current_packet The packt to check - * @return - \c RETURN_OK on success. - * - \c INCORRECT_CHECKSUM if checksum is invalid. - * - \c ILLEGAL_APID if APID does not match. + * @return - @c RETURN_OK on success. + * - @c INCORRECT_CHECKSUM if checksum is invalid. + * - @c ILLEGAL_APID if APID does not match. */ - ReturnValue_t checkPacket( TcPacketStored* current_packet ); + ReturnValue_t checkPacket( TcPacketStored* currentPacket ); uint16_t getApid() const; }; -#endif /* TCPACKETCHECK_H_ */ +#endif /* FSFW_TCDISTRIBUTION_TCPACKETCHECK_H_ */ diff --git a/timemanager/TimeStamperIF.h b/timemanager/TimeStamperIF.h index bdc5e7e3..96011088 100644 --- a/timemanager/TimeStamperIF.h +++ b/timemanager/TimeStamperIF.h @@ -14,8 +14,12 @@ public: static const uint8_t INTERFACE_ID = CLASS_ID::TIME_STAMPER_IF; static const ReturnValue_t BAD_TIMESTAMP = MAKE_RETURN_CODE(1); - static const uint8_t MISSION_TIMESTAMP_SIZE = 8; //!< This is a mission-specific constant and determines the total size reserved for timestamps. - virtual ReturnValue_t addTimeStamp(uint8_t* buffer, const uint8_t maxSize) = 0; + //! This is a mission-specific constant and determines the total + //! size reserved for timestamps. + //! TODO: Default define in FSFWConfig ? + static const uint8_t MISSION_TIMESTAMP_SIZE = 8; + virtual ReturnValue_t addTimeStamp(uint8_t* buffer, + const uint8_t maxSize) = 0; virtual ~TimeStamperIF() {} }; diff --git a/tmtcpacket/SpacePacketBase.cpp b/tmtcpacket/SpacePacketBase.cpp index a37e024b..e13af8d0 100644 --- a/tmtcpacket/SpacePacketBase.cpp +++ b/tmtcpacket/SpacePacketBase.cpp @@ -1,6 +1,6 @@ -#include "../serviceinterface/ServiceInterfaceStream.h" #include "SpacePacketBase.h" -#include +#include "../serviceinterface/ServiceInterfaceStream.h" +#include SpacePacketBase::SpacePacketBase( const uint8_t* set_address ) { this->data = (SpacePacketPointer*) set_address; @@ -14,7 +14,8 @@ uint8_t SpacePacketBase::getPacketVersionNumber( void ) { return (this->data->header.packet_id_h & 0b11100000) >> 5; } -void SpacePacketBase::initSpacePacketHeader(bool isTelecommand, bool hasSecondaryHeader, uint16_t apid, uint16_t sequenceCount) { +void SpacePacketBase::initSpacePacketHeader(bool isTelecommand, + bool hasSecondaryHeader, uint16_t apid, uint16_t sequenceCount) { //reset header to zero: memset(data,0, sizeof(this->data->header) ); //Set TC/TM bit. @@ -81,7 +82,7 @@ void SpacePacketBase::setPacketDataLength( uint16_t new_length) { this->data->header.packet_length_l = ( new_length & 0x00FF ); } -uint32_t SpacePacketBase::getFullSize() { +size_t SpacePacketBase::getFullSize() { //+1 is done because size in packet data length field is: size of data field -1 return this->getPacketDataLength() + sizeof(this->data->header) + 1; } diff --git a/tmtcpacket/SpacePacketBase.h b/tmtcpacket/SpacePacketBase.h index ef3ad79d..19cbd074 100644 --- a/tmtcpacket/SpacePacketBase.h +++ b/tmtcpacket/SpacePacketBase.h @@ -1,10 +1,11 @@ -#ifndef SPACEPACKETBASE_H_ -#define SPACEPACKETBASE_H_ +#ifndef FSFW_TMTCPACKET_SPACEPACKETBASE_H_ +#define FSFW_TMTCPACKET_SPACEPACKETBASE_H_ #include "ccsds_header.h" +#include /** - * \defgroup tmtcpackets Space Packets + * @defgroup tmtcpackets Space Packets * This is the group, where all classes associated with Telecommand and * Telemetry packets belong to. * The class hierarchy resembles the dependency between the different standards @@ -81,7 +82,8 @@ public: */ bool isTelecommand( void ); - void initSpacePacketHeader(bool isTelecommand, bool hasSecondaryHeader, uint16_t apid, uint16_t sequenceCount = 0); + void initSpacePacketHeader(bool isTelecommand, bool hasSecondaryHeader, + uint16_t apid, uint16_t sequenceCount = 0); /** * The CCSDS header provides a secondary header flag (the fifth-highest bit), * which is checked with this method. @@ -167,10 +169,10 @@ public: * This method returns the full raw packet size. * @return The full size of the packet in bytes. */ - uint32_t getFullSize(); + size_t getFullSize(); uint32_t getApidAndSequenceCount() const; }; -#endif /* SPACEPACKETBASE_H_ */ +#endif /* FSFW_TMTCPACKET_SPACEPACKETBASE_H_ */ diff --git a/tmtcpacket/pus/PacketTimestampInterpreterIF.h b/tmtcpacket/pus/PacketTimestampInterpreterIF.h index dd0c0328..40e7a2ea 100644 --- a/tmtcpacket/pus/PacketTimestampInterpreterIF.h +++ b/tmtcpacket/pus/PacketTimestampInterpreterIF.h @@ -7,7 +7,8 @@ class TmPacketMinimal; class PacketTimestampInterpreterIF { public: virtual ~PacketTimestampInterpreterIF() {} - virtual ReturnValue_t getPacketTime(TmPacketMinimal* packet, timeval* timestamp) const = 0; + virtual ReturnValue_t getPacketTime(TmPacketMinimal* packet, + timeval* timestamp) const = 0; virtual ReturnValue_t getPacketTimeRaw(TmPacketMinimal* packet, const uint8_t** timePtr, uint32_t* size) const = 0; }; diff --git a/tmtcpacket/pus/TcPacketBase.cpp b/tmtcpacket/pus/TcPacketBase.cpp index 3815eb20..eaa8416a 100644 --- a/tmtcpacket/pus/TcPacketBase.cpp +++ b/tmtcpacket/pus/TcPacketBase.cpp @@ -1,11 +1,14 @@ -#include "../../globalfunctions/CRC.h" -#include "../../serviceinterface/ServiceInterfaceStream.h" #include "TcPacketBase.h" -#include -TcPacketBase::TcPacketBase(const uint8_t* set_data) : - SpacePacketBase(set_data) { - tcData = (TcPacketPointer*) set_data; +#include "../../globalfunctions/CRC.h" +#include "../../globalfunctions/arrayprinter.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" + +#include + +TcPacketBase::TcPacketBase(const uint8_t* setData) : + SpacePacketBase(setData) { + tcData = reinterpret_cast(const_cast(setData)); } TcPacketBase::~TcPacketBase() { @@ -13,28 +16,28 @@ TcPacketBase::~TcPacketBase() { } uint8_t TcPacketBase::getService() { - return tcData->data_field.service_type; + return tcData->dataField.service_type; } uint8_t TcPacketBase::getSubService() { - return tcData->data_field.service_subtype; + return tcData->dataField.service_subtype; } uint8_t TcPacketBase::getAcknowledgeFlags() { - return tcData->data_field.version_type_ack & 0b00001111; + return tcData->dataField.version_type_ack & 0b00001111; } const uint8_t* TcPacketBase::getApplicationData() const { - return &tcData->data; + return &tcData->appData; } uint16_t TcPacketBase::getApplicationDataSize() { - return getPacketDataLength() - sizeof(tcData->data_field) - CRC_SIZE + 1; + return getPacketDataLength() - sizeof(tcData->dataField) - CRC_SIZE + 1; } uint16_t TcPacketBase::getErrorControl() { uint16_t size = getApplicationDataSize() + CRC_SIZE; - uint8_t* p_to_buffer = &tcData->data; + uint8_t* p_to_buffer = &tcData->appData; return (p_to_buffer[size - 2] << 8) + p_to_buffer[size - 1]; } @@ -42,41 +45,42 @@ void TcPacketBase::setErrorControl() { uint32_t full_size = getFullSize(); uint16_t crc = CRC::crc16ccitt(getWholeData(), full_size - CRC_SIZE); uint32_t size = getApplicationDataSize(); - (&tcData->data)[size] = (crc & 0XFF00) >> 8; // CRCH - (&tcData->data)[size + 1] = (crc) & 0X00FF; // CRCL + (&tcData->appData)[size] = (crc & 0XFF00) >> 8; // CRCH + (&tcData->appData)[size + 1] = (crc) & 0X00FF; // CRCL } -void TcPacketBase::setData(const uint8_t* p_Data) { - SpacePacketBase::setData(p_Data); - tcData = (TcPacketPointer*) p_Data; +void TcPacketBase::setData(const uint8_t* pData) { + SpacePacketBase::setData(pData); + tcData = (TcPacketPointer*) pData; } uint8_t TcPacketBase::getSecondaryHeaderFlag() { - return (tcData->data_field.version_type_ack & 0b10000000) >> 7; + return (tcData->dataField.version_type_ack & 0b10000000) >> 7; } uint8_t TcPacketBase::getPusVersionNumber() { - return (tcData->data_field.version_type_ack & 0b01110000) >> 4; + return (tcData->dataField.version_type_ack & 0b01110000) >> 4; } void TcPacketBase::print() { - uint8_t * wholeData = getWholeData(); - sif::debug << "TcPacket contains: " << std::endl; - for (uint8_t count = 0; count < getFullSize(); ++count) { - sif::debug << std::hex << (uint16_t) wholeData[count] << " "; - } - sif::debug << std::dec << std::endl; + sif::debug << "TcPacketBase::print: " << std::endl; + arrayprinter::print(getWholeData(), getFullSize()); } void TcPacketBase::initializeTcPacket(uint16_t apid, uint16_t sequenceCount, - uint8_t ack, uint8_t service, uint8_t subservice) { + uint8_t ack, uint8_t service, uint8_t subservice) { initSpacePacketHeader(true, true, apid, sequenceCount); - memset(&tcData->data_field, 0, sizeof(tcData->data_field)); - setPacketDataLength(sizeof(tcData->data_field) + CRC_SIZE); + std::memset(&tcData->dataField, 0, sizeof(tcData->dataField)); + setPacketDataLength(sizeof(PUSTcDataFieldHeader) + CRC_SIZE - 1); //Data Field Header: - //Set CCSDS_secondary_header_flag to 0, version number to 001 and ack to 0000 - tcData->data_field.version_type_ack = 0b00010000; - tcData->data_field.version_type_ack |= (ack & 0x0F); - tcData->data_field.service_type = service; - tcData->data_field.service_subtype = subservice; + //Set CCSDS_secondary_header_flag to 0 and version number to 001 + tcData->dataField.version_type_ack = 0b00010000; + tcData->dataField.version_type_ack |= (ack & 0x0F); + tcData->dataField.service_type = service; + tcData->dataField.service_subtype = subservice; +} + +size_t TcPacketBase::calculateFullPacketLength(size_t appDataLen) { + return sizeof(CCSDSPrimaryHeader) + sizeof(PUSTcDataFieldHeader) + + appDataLen + TcPacketBase::CRC_SIZE; } diff --git a/tmtcpacket/pus/TcPacketBase.h b/tmtcpacket/pus/TcPacketBase.h index dd63d2be..582a2214 100644 --- a/tmtcpacket/pus/TcPacketBase.h +++ b/tmtcpacket/pus/TcPacketBase.h @@ -1,7 +1,9 @@ -#ifndef TCPACKETBASE_H_ -#define TCPACKETBASE_H_ +#ifndef TMTCPACKET_PUS_TCPACKETBASE_H_ +#define TMTCPACKET_PUS_TCPACKETBASE_H_ #include "../../tmtcpacket/SpacePacketBase.h" +#include + /** * This struct defines a byte-wise structured PUS TC Data Field Header. @@ -23,14 +25,14 @@ struct PUSTcDataFieldHeader { */ struct TcPacketPointer { CCSDSPrimaryHeader primary; - PUSTcDataFieldHeader data_field; - uint8_t data; + PUSTcDataFieldHeader dataField; + uint8_t appData; }; /** * This class is the basic data handler for any ECSS PUS Telecommand packet. * - * In addition to \SpacePacketBase, the class provides methods to handle + * In addition to #SpacePacketBase, the class provides methods to handle * the standardized entries of the PUS TC Packet Data Field Header. * It does not contain the packet data itself but a pointer to the * data must be set on instantiation. An invalid pointer may cause @@ -39,67 +41,38 @@ struct TcPacketPointer { * @ingroup tmtcpackets */ class TcPacketBase : public SpacePacketBase { -protected: - /** - * A pointer to a structure which defines the data structure of - * the packet's data. - * - * To be hardware-safe, all elements are of byte size. - */ - TcPacketPointer* tcData; public: - static const uint16_t TC_PACKET_MIN_SIZE = (sizeof(CCSDSPrimaryHeader) + sizeof(PUSTcDataFieldHeader) + 2); - /** - * With this constant for the acknowledge field responses on all levels are expected. - */ - static const uint8_t ACK_ALL = 0b1111; - /** - * With this constant for the acknowledge field a response on acceptance is expected. - */ - static const uint8_t ACK_ACCEPTANCE = 0b0001; - /** - * With this constant for the acknowledge field a response on start of execution is expected. - */ - static const uint8_t ACK_START = 0b0010; - /** - * With this constant for the acknowledge field responses on execution steps are expected. - */ - static const uint8_t ACK_STEP = 0b0100; - /** - * With this constant for the acknowledge field a response on completion is expected. - */ - static const uint8_t ACK_COMPLETION = 0b1000; - /** - * With this constant for the acknowledge field no responses are expected. - */ - static const uint8_t ACK_NONE = 0b000; + static const uint16_t TC_PACKET_MIN_SIZE = (sizeof(CCSDSPrimaryHeader) + + sizeof(PUSTcDataFieldHeader) + 2); + + enum AckField { + //! No acknowledgements are expected. + ACK_NONE = 0b0000, + //! Acknowledgements on acceptance are expected. + ACK_ACCEPTANCE = 0b0001, + //! Acknowledgements on start are expected. + ACK_START = 0b0010, + //! Acknowledgements on step are expected. + ACK_STEP = 0b0100, + //! Acknowledfgement on completion are expected. + ACK_COMPLETION = 0b1000 + }; + + static constexpr uint8_t ACK_ALL = ACK_ACCEPTANCE | ACK_START | ACK_STEP | + ACK_COMPLETION; + /** * This is the default constructor. * It sets its internal data pointer to the address passed and also * forwards the data pointer to the parent SpacePacketBase class. - * @param set_address The position where the packet data lies. + * @param setData The position where the packet data lies. */ - TcPacketBase( const uint8_t* set_data ); + TcPacketBase( const uint8_t* setData ); /** * This is the empty default destructor. */ virtual ~TcPacketBase(); - /** - * Initializes the Tc Packet header. - * @param apid APID used. - * @param service PUS Service - * @param subservice PUS Subservice - * @param packetSubcounter Additional subcounter used. - */ - /** - * Initializes the Tc Packet header. - * @param apid APID used. - * @param sequenceCount Sequence Count in the primary header. - * @param ack Which acknowledeges are expected from the receiver. - * @param service PUS Service - * @param subservice PUS Subservice - */ - void initializeTcPacket(uint16_t apid, uint16_t sequenceCount, uint8_t ack, uint8_t service, uint8_t subservice); + /** * This command returns the CCSDS Secondary Header Flag. * It shall always be zero for PUS Packets. This is the @@ -166,22 +139,49 @@ public: * current content of the packet. */ void setErrorControl(); - /** - * With this method, the packet data pointer can be redirected to another - * location. - * - * This call overwrites the parent's setData method to set both its - * \c tc_data pointer and the parent's \c data pointer. - * - * @param p_data A pointer to another PUS Telecommand Packet. - */ - void setData( const uint8_t* p_data ); + /** * This is a debugging helper method that prints the whole packet content * to the screen. */ void print(); + /** + * Calculate full packet length from application data length. + * @param appDataLen + * @return + */ + static size_t calculateFullPacketLength(size_t appDataLen); + +protected: + /** + * A pointer to a structure which defines the data structure of + * the packet's data. + * + * To be hardware-safe, all elements are of byte size. + */ + TcPacketPointer* tcData; + + /** + * Initializes the Tc Packet header. + * @param apid APID used. + * @param sequenceCount Sequence Count in the primary header. + * @param ack Which acknowledeges are expected from the receiver. + * @param service PUS Service + * @param subservice PUS Subservice + */ + void initializeTcPacket(uint16_t apid, uint16_t sequenceCount, uint8_t ack, + uint8_t service, uint8_t subservice); + + /** + * With this method, the packet data pointer can be redirected to another + * location. + * This call overwrites the parent's setData method to set both its + * @c tc_data pointer and the parent's @c data pointer. + * + * @param p_data A pointer to another PUS Telecommand Packet. + */ + void setData( const uint8_t* pData ); }; -#endif /* TCPACKETBASE_H_ */ +#endif /* TMTCPACKET_PUS_TCPACKETBASE_H_ */ diff --git a/tmtcpacket/pus/TcPacketStored.cpp b/tmtcpacket/pus/TcPacketStored.cpp index 216f89c9..fffc86ae 100644 --- a/tmtcpacket/pus/TcPacketStored.cpp +++ b/tmtcpacket/pus/TcPacketStored.cpp @@ -1,37 +1,50 @@ +#include "TcPacketStored.h" #include "../../objectmanager/ObjectManagerIF.h" #include "../../serviceinterface/ServiceInterfaceStream.h" -#include "TcPacketStored.h" -#include + +#include + +StorageManagerIF* TcPacketStored::store = nullptr; TcPacketStored::TcPacketStored(store_address_t setAddress) : - TcPacketBase(NULL), storeAddress(setAddress) { - this->setStoreAddress(this->storeAddress); + TcPacketBase(nullptr), storeAddress(setAddress) { + setStoreAddress(storeAddress); } -TcPacketStored::TcPacketStored(uint16_t apid, uint8_t ack, uint8_t service, - uint8_t subservice, uint8_t sequence_count, const uint8_t* data, - uint32_t size) : - TcPacketBase(NULL) { +TcPacketStored::TcPacketStored(uint16_t apid, uint8_t service, + uint8_t subservice, uint8_t sequenceCount, const uint8_t* data, + size_t size, uint8_t ack) : + TcPacketBase(nullptr) { this->storeAddress.raw = StorageManagerIF::INVALID_ADDRESS; - if (!this->checkAndSetStore()) { + if (not this->checkAndSetStore()) { return; } - uint8_t* p_data = NULL; + uint8_t* pData = nullptr; ReturnValue_t returnValue = this->store->getFreeElement(&this->storeAddress, - (TC_PACKET_MIN_SIZE + size), &p_data); + (TC_PACKET_MIN_SIZE + size), &pData); if (returnValue != this->store->RETURN_OK) { + sif::warning << "TcPacketStored: Could not get free element from store!" + << std::endl; return; } - this->setData(p_data); - initializeTcPacket(apid, sequence_count, ack, service, subservice); - memcpy(&tcData->data, data, size); + this->setData(pData); + initializeTcPacket(apid, sequenceCount, ack, service, subservice); + memcpy(&tcData->appData, data, size); this->setPacketDataLength( size + sizeof(PUSTcDataFieldHeader) + CRC_SIZE - 1); this->setErrorControl(); } -TcPacketStored::TcPacketStored() : - TcPacketBase(NULL) { +ReturnValue_t TcPacketStored::getData(const uint8_t ** dataPtr, + size_t* dataSize) { + auto result = this->store->getData(storeAddress, dataPtr, dataSize); + if(result != HasReturnvaluesIF::RETURN_OK) { + sif::warning << "TcPacketStored: Could not get data!" << std::endl; + } + return result; +} + +TcPacketStored::TcPacketStored(): TcPacketBase(nullptr) { this->storeAddress.raw = StorageManagerIF::INVALID_ADDRESS; this->checkAndSetStore(); @@ -40,14 +53,14 @@ TcPacketStored::TcPacketStored() : ReturnValue_t TcPacketStored::deletePacket() { ReturnValue_t result = this->store->deleteData(this->storeAddress); this->storeAddress.raw = StorageManagerIF::INVALID_ADDRESS; - this->setData( NULL); + this->setData(nullptr); return result; } bool TcPacketStored::checkAndSetStore() { - if (this->store == NULL) { + if (this->store == nullptr) { this->store = objectManager->get(objects::TC_STORE); - if (this->store == NULL) { + if (this->store == nullptr) { sif::error << "TcPacketStored::TcPacketStored: TC Store not found!" << std::endl; return false; @@ -58,17 +71,17 @@ bool TcPacketStored::checkAndSetStore() { void TcPacketStored::setStoreAddress(store_address_t setAddress) { this->storeAddress = setAddress; - const uint8_t* temp_data = NULL; + const uint8_t* tempData = nullptr; size_t temp_size; ReturnValue_t status = StorageManagerIF::RETURN_FAILED; if (this->checkAndSetStore()) { - status = this->store->getData(this->storeAddress, &temp_data, + status = this->store->getData(this->storeAddress, &tempData, &temp_size); } if (status == StorageManagerIF::RETURN_OK) { - this->setData(temp_data); + this->setData(tempData); } else { - this->setData(NULL); + this->setData(nullptr); this->storeAddress.raw = StorageManagerIF::INVALID_ADDRESS; } } @@ -78,7 +91,7 @@ store_address_t TcPacketStored::getStoreAddress() { } bool TcPacketStored::isSizeCorrect() { - const uint8_t* temp_data = NULL; + const uint8_t* temp_data = nullptr; size_t temp_size; ReturnValue_t status = this->store->getData(this->storeAddress, &temp_data, &temp_size); @@ -90,8 +103,6 @@ bool TcPacketStored::isSizeCorrect() { return false; } -StorageManagerIF* TcPacketStored::store = NULL; - TcPacketStored::TcPacketStored(const uint8_t* data, uint32_t size) : TcPacketBase(data) { if (getFullSize() != size) { @@ -100,7 +111,7 @@ TcPacketStored::TcPacketStored(const uint8_t* data, uint32_t size) : if (this->checkAndSetStore()) { ReturnValue_t status = store->addData(&storeAddress, data, size); if (status != HasReturnvaluesIF::RETURN_OK) { - this->setData(NULL); + this->setData(nullptr); } } } diff --git a/tmtcpacket/pus/TcPacketStored.h b/tmtcpacket/pus/TcPacketStored.h index 2d86f89b..1666107b 100644 --- a/tmtcpacket/pus/TcPacketStored.h +++ b/tmtcpacket/pus/TcPacketStored.h @@ -1,8 +1,8 @@ -#ifndef TCPACKETSTORED_H_ -#define TCPACKETSTORED_H_ +#ifndef TMTCPACKET_PUS_TCPACKETSTORED_H_ +#define TMTCPACKET_PUS_TCPACKETSTORED_H_ -#include "../../storagemanager/StorageManagerIF.h" #include "TcPacketBase.h" +#include "../../storagemanager/StorageManagerIF.h" /** * This class generates a ECSS PUS Telecommand packet within a given @@ -15,26 +15,6 @@ * @ingroup tmtcpackets */ class TcPacketStored : public TcPacketBase { -private: - /** - * This is a pointer to the store all instances of the class use. - * If the store is not yet set (i.e. \c store is NULL), every constructor - * call tries to set it and throws an error message in case of failures. - * The default store is objects::TC_STORE. - */ - static StorageManagerIF* store; - /** - * The address where the packet data of the object instance is stored. - */ - store_address_t storeAddress; - /** - * A helper method to check if a store is assigned to the class. - * If not, the method tries to retrieve the store from the global - * ObjectManager. - * @return @li \c true if the store is linked or could be created. - * @li \c false otherwise. - */ - bool checkAndSetStore(); public: /** * This is a default constructor which does not set the data pointer. @@ -53,18 +33,20 @@ public: * a new PUS Telecommand Packet is created there. * Packet Application Data passed in data is copied into the packet. * @param apid Sets the packet's APID field. - * @param ack Set's the packet's Ack field, - * which specifies number and size of verification packets returned - * for this command. * @param service Sets the packet's Service ID field. - * This specifies the destination service. + * This specifies the destination service. * @param subservice Sets the packet's Service Subtype field. - * This specifies the destination sub-service. - * @param sequence_count Sets the packet's Source Sequence Count field. + * This specifies the destination sub-service. + * @param sequence_count Sets the packet's Source Sequence Count field. * @param data The data to be copied to the Application Data Field. * @param size The amount of data to be copied. + * @param ack Set's the packet's Ack field, which specifies + * number of verification packets returned + * for this command. */ - TcPacketStored( uint16_t apid, uint8_t ack, uint8_t service, uint8_t subservice, uint8_t sequence_count = 0, const uint8_t* data = NULL, uint32_t size = 0 ); + TcPacketStored(uint16_t apid, uint8_t service, uint8_t subservice, + uint8_t sequence_count = 0, const uint8_t* data = nullptr, + size_t size = 0, uint8_t ack = TcPacketBase::ACK_ALL); /** * Another constructor to create a TcPacket from a raw packet stream. * Takes the data and adds it unchecked to the TcStore. @@ -72,10 +54,19 @@ public: * @param Size size of the packet. */ TcPacketStored( const uint8_t* data, uint32_t size); + + /** + * Getter function for the raw data. + * @param dataPtr [out] Pointer to the data pointer to set + * @param dataSize [out] Address of size to set. + * @return -@c RETURN_OK if data was retrieved successfully. + */ + ReturnValue_t getData(const uint8_t ** dataPtr, + size_t* dataSize); /** * This is a getter for the current store address of the packet. - * @return The current store address. The (raw) value is \c StorageManagerIF::INVALID_ADDRESS if - * the packet is not linked. + * @return The current store address. The (raw) value is + * @c StorageManagerIF::INVALID_ADDRESS if the packet is not linked. */ store_address_t getStoreAddress(); /** @@ -99,7 +90,28 @@ public: * store or size is incorrect. */ bool isSizeCorrect(); + +private: + /** + * This is a pointer to the store all instances of the class use. + * If the store is not yet set (i.e. @c store is NULL), every constructor + * call tries to set it and throws an error message in case of failures. + * The default store is objects::TC_STORE. + */ + static StorageManagerIF* store; + /** + * The address where the packet data of the object instance is stored. + */ + store_address_t storeAddress; + /** + * A helper method to check if a store is assigned to the class. + * If not, the method tries to retrieve the store from the global + * ObjectManager. + * @return @li @c true if the store is linked or could be created. + * @li @c false otherwise. + */ + bool checkAndSetStore(); }; -#endif /* TCPACKETSTORED_H_ */ +#endif /* TMTCPACKET_PUS_TCPACKETSTORED_H_ */ diff --git a/tmtcpacket/pus/TmPacketBase.cpp b/tmtcpacket/pus/TmPacketBase.cpp index 3c1a8ca4..f3bc3f00 100644 --- a/tmtcpacket/pus/TmPacketBase.cpp +++ b/tmtcpacket/pus/TmPacketBase.cpp @@ -1,13 +1,19 @@ +#include "TmPacketBase.h" + #include "../../globalfunctions/CRC.h" +#include "../../globalfunctions/arrayprinter.h" #include "../../objectmanager/ObjectManagerIF.h" #include "../../serviceinterface/ServiceInterfaceStream.h" -#include "TmPacketBase.h" #include "../../timemanager/CCSDSTime.h" -#include -TmPacketBase::TmPacketBase(uint8_t* set_data) : - SpacePacketBase(set_data) { - tm_data = (TmPacketPointer*) set_data; +#include + +TimeStamperIF* TmPacketBase::timeStamper = nullptr; +object_id_t TmPacketBase::timeStamperId = 0; + +TmPacketBase::TmPacketBase(uint8_t* setData) : + SpacePacketBase(setData) { + tmData = reinterpret_cast(setData); } TmPacketBase::~TmPacketBase() { @@ -15,25 +21,25 @@ TmPacketBase::~TmPacketBase() { } uint8_t TmPacketBase::getService() { - return tm_data->data_field.service_type; + return tmData->data_field.service_type; } uint8_t TmPacketBase::getSubService() { - return tm_data->data_field.service_subtype; + return tmData->data_field.service_subtype; } uint8_t* TmPacketBase::getSourceData() { - return &tm_data->data; + return &tmData->data; } uint16_t TmPacketBase::getSourceDataSize() { - return getPacketDataLength() - sizeof(tm_data->data_field) + return getPacketDataLength() - sizeof(tmData->data_field) - CRC_SIZE + 1; } uint16_t TmPacketBase::getErrorControl() { uint32_t size = getSourceDataSize() + CRC_SIZE; - uint8_t* p_to_buffer = &tm_data->data; + uint8_t* p_to_buffer = &tmData->data; return (p_to_buffer[size - 2] << 8) + p_to_buffer[size - 1]; } @@ -47,16 +53,12 @@ void TmPacketBase::setErrorControl() { void TmPacketBase::setData(const uint8_t* p_Data) { SpacePacketBase::setData(p_Data); - tm_data = (TmPacketPointer*) p_Data; + tmData = (TmPacketPointer*) p_Data; } void TmPacketBase::print() { - /*uint8_t * wholeData = getWholeData(); - debug << "TmPacket contains: " << std::endl; - for (uint8_t count = 0; count < getFullSize(); ++count ) { - debug << std::hex << (uint16_t)wholeData[count] << " "; - } - debug << std::dec << std::endl;*/ + sif::debug << "TmPacketBase::print: " << std::endl; + arrayprinter::print(getWholeData(), getFullSize()); } bool TmPacketBase::checkAndSetStamper() { @@ -73,32 +75,36 @@ bool TmPacketBase::checkAndSetStamper() { ReturnValue_t TmPacketBase::getPacketTime(timeval* timestamp) const { uint32_t tempSize = 0; - return CCSDSTime::convertFromCcsds(timestamp, tm_data->data_field.time, - &tempSize, sizeof(tm_data->data_field.time)); + return CCSDSTime::convertFromCcsds(timestamp, tmData->data_field.time, + &tempSize, sizeof(tmData->data_field.time)); } uint8_t* TmPacketBase::getPacketTimeRaw() const{ - return tm_data->data_field.time; + return tmData->data_field.time; } -void TmPacketBase::initializeTmPacket(uint16_t apid, uint8_t service, uint8_t subservice, uint8_t packetSubcounter) { +void TmPacketBase::initializeTmPacket(uint16_t apid, uint8_t service, + uint8_t subservice, uint8_t packetSubcounter) { //Set primary header: initSpacePacketHeader(false, true, apid); //Set data Field Header: //First, set to zero. - memset(&tm_data->data_field, 0, sizeof(tm_data->data_field)); - //Set CCSDS_secondary header flag to 0, version number to 001 and ack to 0000 + memset(&tmData->data_field, 0, sizeof(tmData->data_field)); + // NOTE: In PUS-C, the PUS Version is 2 and specified for the first 4 bits. - // The other 4 bits of the first byte are the spacecraft time reference status - // To change to PUS-C, set 0b00100000 - tm_data->data_field.version_type_ack = 0b00010000; - tm_data->data_field.service_type = service; - tm_data->data_field.service_subtype = subservice; - tm_data->data_field.subcounter = packetSubcounter; + // The other 4 bits of the first byte are the spacecraft time reference + // status. To change to PUS-C, set 0b00100000. + // Set CCSDS_secondary header flag to 0, version number to 001 and ack + // to 0000 + tmData->data_field.version_type_ack = 0b00010000; + tmData->data_field.service_type = service; + tmData->data_field.service_subtype = subservice; + tmData->data_field.subcounter = packetSubcounter; //Timestamp packet if (checkAndSetStamper()) { - timeStamper->addTimeStamp(tm_data->data_field.time, sizeof(tm_data->data_field.time)); + timeStamper->addTimeStamp(tmData->data_field.time, + sizeof(tmData->data_field.time)); } } @@ -106,9 +112,6 @@ void TmPacketBase::setSourceDataSize(uint16_t size) { setPacketDataLength(size + sizeof(PUSTmDataFieldHeader) + CRC_SIZE - 1); } -uint32_t TmPacketBase::getTimestampSize() const { - return sizeof(tm_data->data_field.time); +size_t TmPacketBase::getTimestampSize() const { + return sizeof(tmData->data_field.time); } - -TimeStamperIF* TmPacketBase::timeStamper = NULL; -object_id_t TmPacketBase::timeStamperId = 0; diff --git a/tmtcpacket/pus/TmPacketBase.h b/tmtcpacket/pus/TmPacketBase.h index 85ae4dac..6efc0165 100644 --- a/tmtcpacket/pus/TmPacketBase.h +++ b/tmtcpacket/pus/TmPacketBase.h @@ -1,8 +1,8 @@ -#ifndef TMPACKETBASE_H_ -#define TMPACKETBASE_H_ +#ifndef TMTCPACKET_PUS_TMPACKETBASE_H_ +#define TMTCPACKET_PUS_TMPACKETBASE_H_ +#include "../SpacePacketBase.h" #include "../../timemanager/TimeStamperIF.h" -#include "../../tmtcpacket/SpacePacketBase.h" #include "../../timemanager/Clock.h" #include "../../objectmanager/SystemObjectIF.h" @@ -14,7 +14,7 @@ void setStaticFrameworkObjectIds(); * This struct defines a byte-wise structured PUS TM Data Field Header. * Any optional fields in the header must be added or removed here. * Currently, no Destination field is present, but an eigth-byte representation - * for a time tag [TBD]. + * for a time tag. * @ingroup tmtcpackets */ struct PUSTmDataFieldHeader { @@ -40,7 +40,7 @@ struct TmPacketPointer { /** * This class is the basic data handler for any ECSS PUS Telemetry packet. * - * In addition to \SpacePacketBase, the class provides methods to handle + * In addition to #SpacePacketBase, the class provides methods to handle * the standardized entries of the PUS TM Packet Data Field Header. * It does not contain the packet data itself but a pointer to the * data must be set on instantiation. An invalid pointer may cause @@ -54,29 +54,27 @@ public: /** * This constant defines the minimum size of a valid PUS Telemetry Packet. */ - static const uint32_t TM_PACKET_MIN_SIZE = (sizeof(CCSDSPrimaryHeader) + sizeof(PUSTmDataFieldHeader) + 2); //!< Minimum size of a valid PUS Telemetry Packet. - static const uint32_t MISSION_TM_PACKET_MAX_SIZE = 2048; //!< Maximum size of a TM Packet in this mission. - static const uint8_t VERSION_NUMBER_BYTE_PUS_A = 0b00010000; //!< First byte of secondary header for PUS-A packets. + static const uint32_t TM_PACKET_MIN_SIZE = (sizeof(CCSDSPrimaryHeader) + + sizeof(PUSTmDataFieldHeader) + 2); + //! Maximum size of a TM Packet in this mission. + //! TODO: Make this dependant on a config variable. + static const uint32_t MISSION_TM_PACKET_MAX_SIZE = 2048; + //! First byte of secondary header for PUS-A packets. + //! TODO: Maybe also support PUS-C via config? + static const uint8_t VERSION_NUMBER_BYTE_PUS_A = 0b00010000; + /** * This is the default constructor. * It sets its internal data pointer to the address passed and also * forwards the data pointer to the parent SpacePacketBase class. * @param set_address The position where the packet data lies. */ - TmPacketBase( uint8_t* set_data ); + TmPacketBase( uint8_t* setData ); /** * This is the empty default destructor. */ virtual ~TmPacketBase(); - /** - * Initializes the Tm Packet header. - * Does set the timestamp (to now), but not the error control field. - * @param apid APID used. - * @param service PUS Service - * @param subservice PUS Subservice - * @param packetSubcounter Additional subcounter used. - */ - void initializeTmPacket(uint16_t apid, uint8_t service, uint8_t subservice, uint8_t packetSubcounter); + /** * This is a getter for the packet's PUS Service ID, which is the second * byte of the Data Field Header. @@ -106,11 +104,14 @@ public: */ uint16_t getSourceDataSize(); - /** - * In case data was filled manually (almost never the case). - * @param size Size of source data (without CRC and data filed header!). - */ - void setSourceDataSize(uint16_t size); + /** + * With this method, the Error Control Field is updated to match the + * current content of the packet. This method is not protected because + * a recalculation by the user might be necessary when manipulating fields + * like the sequence count. + */ + void setErrorControl(); + /** * This getter returns the Error Control Field of the packet. * @@ -120,28 +121,15 @@ public: * @return The PUS Error Control */ uint16_t getErrorControl(); - /** - * With this method, the Error Control Field is updated to match the - * current content of the packet. - */ - void setErrorControl(); - /** - * With this method, the packet data pointer can be redirected to another - * location. - * - * This call overwrites the parent's setData method to set both its - * \c tc_data pointer and the parent's \c data pointer. - * - * @param p_data A pointer to another PUS Telemetry Packet. - */ - void setData( const uint8_t* p_Data ); + /** * This is a debugging helper method that prints the whole packet content * to the screen. */ void print(); /** - * Interprets the "time"-field in the secondary header and returns it in timeval format. + * Interprets the "time"-field in the secondary header and returns it in + * timeval format. * @return Converted timestamp of packet. */ ReturnValue_t getPacketTime(timeval* timestamp) const; @@ -151,7 +139,7 @@ public: */ uint8_t* getPacketTimeRaw() const; - uint32_t getTimestampSize() const; + size_t getTimestampSize() const; protected: /** @@ -160,20 +148,49 @@ protected: * * To be hardware-safe, all elements are of byte size. */ - TmPacketPointer* tm_data; + TmPacketPointer* tmData; /** * The timeStamper is responsible for adding a timestamp to the packet. * It is initialized lazy. */ static TimeStamperIF* timeStamper; + //! The ID to use when looking for a time stamper. + static object_id_t timeStamperId; - static object_id_t timeStamperId; //!< The ID to use when looking for a time stamper. - /** - * Checks if a time stamper is available and tries to set it if not. - * @return Returns false if setting failed. - */ - bool checkAndSetStamper(); + /** + * Initializes the Tm Packet header. + * Does set the timestamp (to now), but not the error control field. + * @param apid APID used. + * @param service PUS Service + * @param subservice PUS Subservice + * @param packetSubcounter Additional subcounter used. + */ + void initializeTmPacket(uint16_t apid, uint8_t service, uint8_t subservice, + uint8_t packetSubcounter); + + /** + * With this method, the packet data pointer can be redirected to another + * location. + * + * This call overwrites the parent's setData method to set both its + * @c tc_data pointer and the parent's @c data pointer. + * + * @param p_data A pointer to another PUS Telemetry Packet. + */ + void setData( const uint8_t* pData ); + + /** + * In case data was filled manually (almost never the case). + * @param size Size of source data (without CRC and data filed header!). + */ + void setSourceDataSize(uint16_t size); + + /** + * Checks if a time stamper is available and tries to set it if not. + * @return Returns false if setting failed. + */ + bool checkAndSetStamper(); }; -#endif /* TMPACKETBASE_H_ */ +#endif /* TMTCPACKET_PUS_TMPACKETBASE_H_ */ diff --git a/tmtcpacket/pus/TmPacketStored.cpp b/tmtcpacket/pus/TmPacketStored.cpp index 4e4c9434..0fb789aa 100644 --- a/tmtcpacket/pus/TmPacketStored.cpp +++ b/tmtcpacket/pus/TmPacketStored.cpp @@ -1,11 +1,16 @@ +#include "TmPacketStored.h" + #include "../../objectmanager/ObjectManagerIF.h" #include "../../serviceinterface/ServiceInterfaceStream.h" -#include "TmPacketStored.h" #include "../../tmtcservices/TmTcMessage.h" -#include + +#include + +StorageManagerIF *TmPacketStored::store = nullptr; +InternalErrorReporterIF *TmPacketStored::internalErrorReporter = nullptr; TmPacketStored::TmPacketStored(store_address_t setAddress) : - TmPacketBase(NULL), storeAddress(setAddress) { + TmPacketBase(nullptr), storeAddress(setAddress) { setStoreAddress(storeAddress); } @@ -14,10 +19,10 @@ TmPacketStored::TmPacketStored(uint16_t apid, uint8_t service, uint32_t size, const uint8_t *headerData, uint32_t headerSize) : TmPacketBase(NULL) { storeAddress.raw = StorageManagerIF::INVALID_ADDRESS; - if (!checkAndSetStore()) { + if (not checkAndSetStore()) { return; } - uint8_t *pData = NULL; + uint8_t *pData = nullptr; ReturnValue_t returnValue = store->getFreeElement(&storeAddress, (TmPacketBase::TM_PACKET_MIN_SIZE + size + headerSize), &pData); @@ -38,7 +43,7 @@ TmPacketStored::TmPacketStored(uint16_t apid, uint8_t service, SerializeIF *header) : TmPacketBase(NULL) { storeAddress.raw = StorageManagerIF::INVALID_ADDRESS; - if (!checkAndSetStore()) { + if (not checkAndSetStore()) { return; } size_t sourceDataSize = 0; @@ -77,29 +82,29 @@ store_address_t TmPacketStored::getStoreAddress() { void TmPacketStored::deletePacket() { store->deleteData(storeAddress); storeAddress.raw = StorageManagerIF::INVALID_ADDRESS; - setData(NULL); + setData(nullptr); } void TmPacketStored::setStoreAddress(store_address_t setAddress) { storeAddress = setAddress; - const uint8_t* temp_data = NULL; - size_t temp_size; - if (!checkAndSetStore()) { + const uint8_t* tempData = nullptr; + size_t tempSize; + if (not checkAndSetStore()) { return; } - ReturnValue_t status = store->getData(storeAddress, &temp_data, &temp_size); + ReturnValue_t status = store->getData(storeAddress, &tempData, &tempSize); if (status == StorageManagerIF::RETURN_OK) { - setData(temp_data); + setData(tempData); } else { - setData(NULL); + setData(nullptr); storeAddress.raw = StorageManagerIF::INVALID_ADDRESS; } } bool TmPacketStored::checkAndSetStore() { - if (store == NULL) { + if (store == nullptr) { store = objectManager->get(objects::TM_STORE); - if (store == NULL) { + if (store == nullptr) { sif::error << "TmPacketStored::TmPacketStored: TM Store not found!" << std::endl; return false; @@ -108,12 +113,9 @@ bool TmPacketStored::checkAndSetStore() { return true; } -StorageManagerIF *TmPacketStored::store = NULL; -InternalErrorReporterIF *TmPacketStored::internalErrorReporter = NULL; - ReturnValue_t TmPacketStored::sendPacket(MessageQueueId_t destination, MessageQueueId_t sentFrom, bool doErrorReporting) { - if (getWholeData() == NULL) { + if (getWholeData() == nullptr) { //SHOULDDO: More decent code. return HasReturnvaluesIF::RETURN_FAILED; } @@ -133,11 +135,11 @@ ReturnValue_t TmPacketStored::sendPacket(MessageQueueId_t destination, } void TmPacketStored::checkAndReportLostTm() { - if (internalErrorReporter == NULL) { + if (internalErrorReporter == nullptr) { internalErrorReporter = objectManager->get( objects::INTERNAL_ERROR_REPORTER); } - if (internalErrorReporter != NULL) { + if (internalErrorReporter != nullptr) { internalErrorReporter->lostTm(); } } diff --git a/tmtcpacket/pus/TmPacketStored.h b/tmtcpacket/pus/TmPacketStored.h index 8962d343..6ddbf131 100644 --- a/tmtcpacket/pus/TmPacketStored.h +++ b/tmtcpacket/pus/TmPacketStored.h @@ -1,9 +1,10 @@ -#ifndef TMPACKETSTORED_H_ -#define TMPACKETSTORED_H_ +#ifndef TMTCPACKET_PUS_TMPACKETSTORED_H_ +#define TMTCPACKET_PUS_TMPACKETSTORED_H_ + +#include "TmPacketBase.h" #include "../../serialize/SerializeIF.h" #include "../../storagemanager/StorageManagerIF.h" -#include "TmPacketBase.h" #include "../../internalError/InternalErrorReporterIF.h" #include "../../ipc/MessageQueueSenderIF.h" @@ -18,31 +19,6 @@ * @ingroup tmtcpackets */ class TmPacketStored : public TmPacketBase { -private: - /** - * This is a pointer to the store all instances of the class use. - * If the store is not yet set (i.e. \c store is NULL), every constructor - * call tries to set it and throws an error message in case of failures. - * The default store is objects::TM_STORE. - */ - static StorageManagerIF* store; - - static InternalErrorReporterIF *internalErrorReporter; - - /** - * The address where the packet data of the object instance is stored. - */ - store_address_t storeAddress; - /** - * A helper method to check if a store is assigned to the class. - * If not, the method tries to retrieve the store from the global - * ObjectManager. - * @return @li \c true if the store is linked or could be created. - * @li \c false otherwise. - */ - bool checkAndSetStore(); - - void checkAndReportLostTm(); public: /** * This is a default constructor which does not set the data pointer. @@ -52,28 +28,38 @@ public: /** * With this constructor, new space is allocated in the packet store and * a new PUS Telemetry Packet is created there. - * Packet Application Data passed in data is copied into the packet. The Application data is - * passed in two parts, first a header, then a data field. This allows building a Telemetry - * Packet from two separate data sources. + * Packet Application Data passed in data is copied into the packet. + * The Application data is passed in two parts, first a header, then a + * data field. This allows building a Telemetry Packet from two separate + * data sources. * @param apid Sets the packet's APID field. * @param service Sets the packet's Service ID field. * This specifies the source service. * @param subservice Sets the packet's Service Subtype field. * This specifies the source sub-service. * @param packet_counter Sets the Packet counter field of this packet - * @param data The payload data to be copied to the Application Data Field + * @param data The payload data to be copied to the + * Application Data Field * @param size The amount of data to be copied. - * @param headerData The header Data of the Application field; will be copied in front of data + * @param headerData The header Data of the Application field, + * will be copied in front of data * @param headerSize The size of the headerDataF */ - TmPacketStored( uint16_t apid, uint8_t service, uint8_t subservice, uint8_t packet_counter = 0, const uint8_t* data = NULL, uint32_t size = 0, const uint8_t* headerData = NULL, uint32_t headerSize = 0); + TmPacketStored( uint16_t apid, uint8_t service, uint8_t subservice, + uint8_t packet_counter = 0, const uint8_t* data = nullptr, + uint32_t size = 0, const uint8_t* headerData = nullptr, + uint32_t headerSize = 0); /** - * Another ctor to directly pass structured content and header data to the packet to avoid additional buffers. + * Another ctor to directly pass structured content and header data to the + * packet to avoid additional buffers. */ - TmPacketStored( uint16_t apid, uint8_t service, uint8_t subservice, uint8_t packet_counter, SerializeIF* content, SerializeIF* header = NULL); + TmPacketStored( uint16_t apid, uint8_t service, uint8_t subservice, + uint8_t packet_counter, SerializeIF* content, + SerializeIF* header = nullptr); /** * This is a getter for the current store address of the packet. - * @return The current store address. The (raw) value is \c StorageManagerIF::INVALID_ADDRESS if + * @return The current store address. The (raw) value is + * @c StorageManagerIF::INVALID_ADDRESS if * the packet is not linked. */ store_address_t getStoreAddress(); @@ -89,8 +75,34 @@ public: */ void setStoreAddress( store_address_t setAddress ); - ReturnValue_t sendPacket( MessageQueueId_t destination, MessageQueueId_t sentFrom, bool doErrorReporting = true ); + ReturnValue_t sendPacket( MessageQueueId_t destination, + MessageQueueId_t sentFrom, bool doErrorReporting = true ); +private: + /** + * This is a pointer to the store all instances of the class use. + * If the store is not yet set (i.e. @c store is NULL), every constructor + * call tries to set it and throws an error message in case of failures. + * The default store is objects::TM_STORE. + */ + static StorageManagerIF* store; + + static InternalErrorReporterIF *internalErrorReporter; + + /** + * The address where the packet data of the object instance is stored. + */ + store_address_t storeAddress; + /** + * A helper method to check if a store is assigned to the class. + * If not, the method tries to retrieve the store from the global + * ObjectManager. + * @return @li @c true if the store is linked or could be created. + * @li @c false otherwise. + */ + bool checkAndSetStore(); + + void checkAndReportLostTm(); }; -#endif /* TMPACKETSTORED_H_ */ +#endif /* TMTCPACKET_PUS_TMPACKETSTORED_H_ */ diff --git a/tmtcservices/PusVerificationReport.cpp b/tmtcservices/PusVerificationReport.cpp index b047576b..bfad9ec5 100644 --- a/tmtcservices/PusVerificationReport.cpp +++ b/tmtcservices/PusVerificationReport.cpp @@ -4,31 +4,6 @@ PusVerificationMessage::PusVerificationMessage() { } -//PusVerificationMessage::PusVerificationMessage(uint8_t set_report_id, -// TcPacketBase* current_packet, ReturnValue_t set_error_code, -// uint8_t set_step, uint32_t parameter1, uint32_t parameter2) { -// uint8_t ackFlags = current_packet->getAcknowledgeFlags(); -// uint16_t tcPacketId = current_packet->getPacketId(); -// uint16_t tcSequenceControl = current_packet->getPacketSequenceControl(); -// uint8_t* data = this->getBuffer(); -// data[messageSize] = set_report_id; -// messageSize += sizeof(set_report_id); -// data[messageSize] = ackFlags; -// messageSize += sizeof(ackFlags); -// memcpy(&data[messageSize], &tcPacketId, sizeof(tcPacketId)); -// messageSize += sizeof(tcPacketId); -// memcpy(&data[messageSize], &tcSequenceControl, sizeof(tcSequenceControl)); -// messageSize += sizeof(tcSequenceControl); -// data[messageSize] = set_step; -// messageSize += sizeof(set_step); -// memcpy(&data[messageSize], &set_error_code, sizeof(set_error_code)); -// messageSize += sizeof(set_error_code); -// memcpy(&data[messageSize], ¶meter1, sizeof(parameter1)); -// messageSize += sizeof(parameter1); -// memcpy(&data[messageSize], ¶meter2, sizeof(parameter2)); -// messageSize += sizeof(parameter2); -//} - PusVerificationMessage::PusVerificationMessage(uint8_t set_report_id, uint8_t ackFlags, uint16_t tcPacketId, uint16_t tcSequenceControl, ReturnValue_t set_error_code, uint8_t set_step, uint32_t parameter1, diff --git a/tmtcservices/PusVerificationReport.h b/tmtcservices/PusVerificationReport.h index b4739577..88954cfd 100644 --- a/tmtcservices/PusVerificationReport.h +++ b/tmtcservices/PusVerificationReport.h @@ -1,9 +1,13 @@ -#ifndef PUSVERIFICATIONREPORT_H_ -#define PUSVERIFICATIONREPORT_H_ +#ifndef FSFW_TMTCSERVICES_PUSVERIFICATIONREPORT_H_ +#define FSFW_TMTCSERVICES_PUSVERIFICATIONREPORT_H_ + +#include "VerificationCodes.h" #include "../ipc/MessageQueueMessage.h" #include "../tmtcpacket/pus/TcPacketBase.h" -#include "VerificationCodes.h" +#include "../returnvalues/HasReturnvaluesIF.h" + +#include class PusVerificationMessage: public MessageQueueMessage { private: @@ -30,7 +34,7 @@ private: public: static const uint8_t VERIFICATION_MIN_SIZE = 6; PusVerificationMessage(); -// PusVerificationMessage( uint8_t set_report_id, TcPacketBase* current_packet, ReturnValue_t set_error_code = 0, uint8_t set_step = 0, uint32_t parameter1 = 0, uint32_t parameter2 = 0 ); + PusVerificationMessage(uint8_t set_report_id, uint8_t ackFlags, uint16_t tcPacketId, uint16_t tcSequenceControl, ReturnValue_t set_error_code = 0, uint8_t set_step = 0, @@ -74,4 +78,4 @@ public: uint8_t* getReport(); }; -#endif /* PUSVERIFICATIONREPORT_H_ */ +#endif /* FSFW_TMTCSERVICES_PUSVERIFICATIONREPORT_H_ */ diff --git a/tmtcservices/VerificationReporter.cpp b/tmtcservices/VerificationReporter.cpp index 43e712a2..7e40bd27 100644 --- a/tmtcservices/VerificationReporter.cpp +++ b/tmtcservices/VerificationReporter.cpp @@ -1,55 +1,59 @@ -#include "../serviceinterface/ServiceInterfaceStream.h" +#include "VerificationReporter.h" #include "AcceptsVerifyMessageIF.h" #include "PusVerificationReport.h" -#include "VerificationReporter.h" -object_id_t VerificationReporter::messageReceiver = 0; +#include "../ipc/MessageQueueIF.h" +#include "../serviceinterface/ServiceInterfaceStream.h" +#include "../objectmanager/frameworkObjects.h" + +object_id_t VerificationReporter::messageReceiver = + objects::PUS_SERVICE_1_VERIFICATION; VerificationReporter::VerificationReporter() : - acknowledgeQueue() { + acknowledgeQueue(MessageQueueIF::NO_QUEUE) { } -VerificationReporter::~VerificationReporter() { - //Default, empty -} +VerificationReporter::~VerificationReporter() {} void VerificationReporter::sendSuccessReport(uint8_t set_report_id, TcPacketBase* current_packet, uint8_t set_step) { - if (this->acknowledgeQueue == 0) { + if (acknowledgeQueue == MessageQueueIF::NO_QUEUE) { this->initialize(); } PusVerificationMessage message(set_report_id, current_packet->getAcknowledgeFlags(), current_packet->getPacketId(), current_packet->getPacketSequenceControl(), 0, set_step); - ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, &message); + ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, + &message); if (status != HasReturnvaluesIF::RETURN_OK) { - sif::error - << "VerificationReporter::sendSuccessReport: Error writing to queue. Code: " - << (uint16_t) status << std::endl; + sif::error << "VerificationReporter::sendSuccessReport: Error writing " + << "to queue. Code: " << std::hex << status << std::dec + << std::endl; } } void VerificationReporter::sendSuccessReport(uint8_t set_report_id, uint8_t ackFlags, uint16_t tcPacketId, uint16_t tcSequenceControl, uint8_t set_step) { - if (this->acknowledgeQueue == 0) { + if (acknowledgeQueue == MessageQueueIF::NO_QUEUE) { this->initialize(); } PusVerificationMessage message(set_report_id, ackFlags, tcPacketId, tcSequenceControl, 0, set_step); - ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, &message); + ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, + &message); if (status != HasReturnvaluesIF::RETURN_OK) { - sif::error - << "VerificationReporter::sendSuccessReport: Error writing to queue. Code: " - << (uint16_t) status << std::endl; + sif::error << "VerificationReporter::sendSuccessReport: Error writing " + << "to queue. Code: " << std::hex << status << std::dec + << std::endl; } } void VerificationReporter::sendFailureReport(uint8_t report_id, TcPacketBase* current_packet, ReturnValue_t error_code, uint8_t step, uint32_t parameter1, uint32_t parameter2) { - if (this->acknowledgeQueue == 0) { + if (acknowledgeQueue == MessageQueueIF::NO_QUEUE) { this->initialize(); } PusVerificationMessage message(report_id, @@ -57,11 +61,12 @@ void VerificationReporter::sendFailureReport(uint8_t report_id, current_packet->getPacketId(), current_packet->getPacketSequenceControl(), error_code, step, parameter1, parameter2); - ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, &message); + ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, + &message); if (status != HasReturnvaluesIF::RETURN_OK) { - sif::error - << "VerificationReporter::sendFailureReport Error writing to queue. Code: " - << (uint16_t) status << std::endl; + sif::error << "VerificationReporter::sendFailureReport: Error writing " + << "to queue. Code: " << std::hex << "0x" << status << std::dec + << std::endl; } } @@ -69,27 +74,33 @@ void VerificationReporter::sendFailureReport(uint8_t report_id, uint8_t ackFlags, uint16_t tcPacketId, uint16_t tcSequenceControl, ReturnValue_t error_code, uint8_t step, uint32_t parameter1, uint32_t parameter2) { - if (this->acknowledgeQueue == 0) { + if (acknowledgeQueue == MessageQueueIF::NO_QUEUE) { this->initialize(); } PusVerificationMessage message(report_id, ackFlags, tcPacketId, tcSequenceControl, error_code, step, parameter1, parameter2); - ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, &message); + ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, + &message); if (status != HasReturnvaluesIF::RETURN_OK) { - sif::error - << "VerificationReporter::sendFailureReport Error writing to queue. Code: " - << (uint16_t) status << std::endl; + sif::error << "VerificationReporter::sendFailureReport: Error writing " + << "to queue. Code: " << std::hex << "0x" << status << std::dec + << std::endl; } } void VerificationReporter::initialize() { + if(messageReceiver == objects::NO_OBJECT) { + sif::warning << "VerificationReporter::initialize: Verification message" + " receiver object ID not set yet in Factory!" << std::endl; + return; + } AcceptsVerifyMessageIF* temp = objectManager->get( messageReceiver); - if (temp != NULL) { - this->acknowledgeQueue = temp->getVerificationQueue(); - } else { - sif::error - << "VerificationReporter::VerificationReporter: Configuration error." - << std::endl; + if (temp == nullptr) { + sif::error << "VerificationReporter::initialize: Message " + << "receiver invalid. Make sure it is set up properly and " + << "implementsAcceptsVerifyMessageIF" << std::endl; + return; } + this->acknowledgeQueue = temp->getVerificationQueue(); } diff --git a/tmtcservices/VerificationReporter.h b/tmtcservices/VerificationReporter.h index 29a273c5..f26fa54f 100644 --- a/tmtcservices/VerificationReporter.h +++ b/tmtcservices/VerificationReporter.h @@ -1,31 +1,50 @@ -#ifndef VERIFICATIONREPORTER_H_ -#define VERIFICATIONREPORTER_H_ +#ifndef FSFW_TMTCSERVICES_VERIFICATIONREPORTER_H_ +#define FSFW_TMTCSERVICES_VERIFICATIONREPORTER_H_ -#include "../objectmanager/ObjectManagerIF.h" #include "PusVerificationReport.h" +#include "../objectmanager/ObjectManagerIF.h" namespace Factory{ void setStaticFrameworkObjectIds(); } +/** + * @brief This helper object is used to forward verification messages + * which are generated by the Flight Software Framework. + * @details + * The messages can be relayed to an arbitrary object, for example a dedicated + * Verification Reporter. The destination is set by setting the static framework + * Id VerificationReporter::messageReceiver. The default verification reporter + * will be the PUS service 1, which sends verification messages according + * to the PUS standard. + * + */ class VerificationReporter { friend void (Factory::setStaticFrameworkObjectIds)(); public: VerificationReporter(); virtual ~VerificationReporter(); - void sendSuccessReport( uint8_t set_report_id, TcPacketBase* current_packet, uint8_t set_step = 0 ); - void sendSuccessReport(uint8_t set_report_id, uint8_t ackFlags, uint16_t tcPacketId, uint16_t tcSequenceControl, uint8_t set_step = 0); - void sendFailureReport( uint8_t report_id, TcPacketBase* current_packet, ReturnValue_t error_code = 0, - uint8_t step = 0, uint32_t parameter1 = 0, uint32_t parameter2 = 0 ); + + void sendSuccessReport( uint8_t set_report_id, TcPacketBase* current_packet, + uint8_t set_step = 0 ); + void sendSuccessReport(uint8_t set_report_id, uint8_t ackFlags, + uint16_t tcPacketId, uint16_t tcSequenceControl, + uint8_t set_step = 0); + + void sendFailureReport( uint8_t report_id, TcPacketBase* current_packet, + ReturnValue_t error_code = 0, + uint8_t step = 0, uint32_t parameter1 = 0, + uint32_t parameter2 = 0 ); void sendFailureReport(uint8_t report_id, - uint8_t ackFlags, uint16_t tcPacketId, uint16_t tcSequenceControl, ReturnValue_t error_code = 0, uint8_t step = 0, + uint8_t ackFlags, uint16_t tcPacketId, uint16_t tcSequenceControl, + ReturnValue_t error_code = 0, uint8_t step = 0, uint32_t parameter1 = 0, uint32_t parameter2 = 0); + void initialize(); + private: static object_id_t messageReceiver; MessageQueueId_t acknowledgeQueue; - - }; -#endif /* VERIFICATIONREPORTER_H_ */ +#endif /* FSFW_TMTCSERVICES_VERIFICATIONREPORTER_H_ */ diff --git a/unittest/README.md b/unittest/README.md new file mode 100644 index 00000000..e628d43e --- /dev/null +++ b/unittest/README.md @@ -0,0 +1,49 @@ +## FSFW Testing +This repository contains testing and unit testing components. + +[Catch2](https://github.com/catchorg/Catch2) has been used as a framework, +and these unit tests can only be run on a linux host machine. +The makefile with default settings creates the unit test binary which can be +run in the terminal or in eclipse. + +### Instructions + +### Eclipse CDT settings + +The default eclipse terminal has issues displaying the colors used +when running the unit test binary by catch2. To fix this issue, +install the ANSI Escape In Console package from the eclipse marketplace. + +### GCOV integration + +GCOV has been integrated as a code coverage tool. +It can be enabled by adding `GCOV=1` to the build process as an additional argument. +Coverage data will be provided in form of .gcno and .gcda files. +These can be displayed in eclipse by looking +for a .gcno or .gcda file in the \_obj folder, double-clicking it +and picking the right source-binary. This will generate +information about which lines of a file have run, provided it is open in +eclipse. + +### LCOV integration + +The files generated by GCOV can also be processed by the tool LCOV. +On ubuntu, the tool can be installed with the following command: + +```sh +sudo apt-get install lcov +```` + +After that, the tool can be run by building the unit tests with `GCOV=1`, +running them at least one time and then executing the `lcov.sh` script. + +### Adding unit tests + +The catch unit tests are located in unittest/testfw. To add new unit tests, +add them to the UnitTestCatch.cpp file or add a new source file which +includes catch.hpp. + +For writing basics tests, the [assertion documentation](https://github.com/catchorg/Catch2/blob/master/docs/assertions.md#top) +or the existing examples are a good guideliens. +For more advanced tests, refer to the [catch2 documentation](https://github.com/catchorg/Catch2/blob/master/docs/Readme.md#top). + diff --git a/unittest/core/CatchDefinitions.cpp b/unittest/core/CatchDefinitions.cpp new file mode 100644 index 00000000..0b66558a --- /dev/null +++ b/unittest/core/CatchDefinitions.cpp @@ -0,0 +1,11 @@ +#include "CatchDefinitions.h" +#include + +StorageManagerIF* tglob::getIpcStoreHandle() { + if(objectManager != nullptr) { + return objectManager->get(objects::IPC_STORE); + } else { + sif::error << "Global object manager uninitialized" << std::endl; + return nullptr; + } +} diff --git a/unittest/core/CatchDefinitions.h b/unittest/core/CatchDefinitions.h new file mode 100644 index 00000000..366cda7e --- /dev/null +++ b/unittest/core/CatchDefinitions.h @@ -0,0 +1,21 @@ +#ifndef FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_ +#define FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_ + +#include +#include +#include + +namespace retval { +static constexpr int CATCH_OK = static_cast(HasReturnvaluesIF::RETURN_OK); +static constexpr int CATCH_FAILED = static_cast(HasReturnvaluesIF::RETURN_FAILED); +} + +namespace tconst { + static constexpr MessageQueueId_t testQueueId = 42; +} + +namespace tglob { + StorageManagerIF* getIpcStoreHandle(); +} + +#endif /* FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_ */ diff --git a/unittest/core/CatchRunner.cpp b/unittest/core/CatchRunner.cpp new file mode 100644 index 00000000..35c53cd4 --- /dev/null +++ b/unittest/core/CatchRunner.cpp @@ -0,0 +1,31 @@ +/** + * @file CatchSource.cpp + * @brief Source file to compile catch framework. + * @details All tests should be written in other files. + * For eclipse console output, install ANSI Escape in Console + * from the eclipse market place to get colored characters. + */ + +#ifndef NO_UNIT_TEST_FRAMEWORK + +#define CATCH_CONFIG_RUNNER +#include + +#if CUSTOM_UNITTEST_RUNNER == 0 + +extern int customSetup(); + +int main( int argc, char* argv[] ) { + customSetup(); + + // Catch internal function call + int result = Catch::Session().run( argc, argv ); + + // global clean-up + return result; +} + +#endif + + +#endif diff --git a/unittest/core/CatchSetup.cpp b/unittest/core/CatchSetup.cpp new file mode 100644 index 00000000..f8543fd2 --- /dev/null +++ b/unittest/core/CatchSetup.cpp @@ -0,0 +1,42 @@ +#include "CatchDefinitions.h" + +#include +#include + + +#ifdef GCOV +#include +#endif + +#include "../../objectmanager/ObjectManager.h" +#include "../../objectmanager/ObjectManagerIF.h" +#include "../../storagemanager/StorageManagerIF.h" +#include "../../datapool/DataPool.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" + + +/* Global instantiations normally done in main.cpp */ +/* Initialize Data Pool */ +//namespace glob { +DataPool dataPool(datapool::dataPoolInit); +//} + + +namespace sif { +/* Set up output streams */ +ServiceInterfaceStream debug("DEBUG"); +ServiceInterfaceStream info("INFO"); +ServiceInterfaceStream error("ERROR"); +ServiceInterfaceStream warning("WARNING"); +} + +/* Global object manager */ +ObjectManagerIF *objectManager; + +int customSetup() { + // global setup + objectManager = new ObjectManager(Factory::produce); + objectManager -> initialize(); + return 0; +} + diff --git a/unittest/core/core.mk b/unittest/core/core.mk new file mode 100644 index 00000000..3e5626d3 --- /dev/null +++ b/unittest/core/core.mk @@ -0,0 +1,3 @@ +CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp) + +INCLUDES += $(CURRENTPATH) \ No newline at end of file diff --git a/unittest/core/printChar.cpp b/unittest/core/printChar.cpp new file mode 100644 index 00000000..755a13b2 --- /dev/null +++ b/unittest/core/printChar.cpp @@ -0,0 +1,10 @@ +#include +#include + +void printChar(const char* character, bool errStream) { + if(errStream) { + std::putc(*character, stderr); + return; + } + std::putc(*character, stdout); +} diff --git a/unittest/core/printChar.h b/unittest/core/printChar.h new file mode 100644 index 00000000..80e867f6 --- /dev/null +++ b/unittest/core/printChar.h @@ -0,0 +1,8 @@ +#ifndef FSFW_UNITTEST_CORE_PRINTCHAR_H_ +#define FSFW_UNITTEST_CORE_PRINTCHAR_H_ + + +extern "C" void printChar(const char*, bool errStream); + + +#endif /* FSFW_UNITTEST_CORE_PRINTCHAR_H_ */ diff --git a/unittest/internal/InternalUnitTester.cpp b/unittest/internal/InternalUnitTester.cpp new file mode 100644 index 00000000..503a71ec --- /dev/null +++ b/unittest/internal/InternalUnitTester.cpp @@ -0,0 +1,27 @@ +#include "InternalUnitTester.h" +#include "UnittDefinitions.h" + +#include "osal/IntTestMq.h" +#include "osal/IntTestSemaphore.h" +#include "osal/IntTestMutex.h" +#include "serialize/IntTestSerialization.h" + +#include + +InternalUnitTester::InternalUnitTester() {} + +InternalUnitTester::~InternalUnitTester() {} + +ReturnValue_t InternalUnitTester::performTests() { + sif::info << "Running internal unit tests.." << std::endl; + testserialize::test_serialization(); + testmq::testMq(); + testsemaph::testBinSemaph(); + testsemaph::testCountingSemaph(); + testmutex::testMutex(); + sif::info << "Internal unit tests finished." << std::endl; + return RETURN_OK; +} + + + diff --git a/unittest/internal/InternalUnitTester.h b/unittest/internal/InternalUnitTester.h new file mode 100644 index 00000000..b301b923 --- /dev/null +++ b/unittest/internal/InternalUnitTester.h @@ -0,0 +1,29 @@ +#ifndef FRAMEWORK_TEST_UNITTESTCLASS_H_ +#define FRAMEWORK_TEST_UNITTESTCLASS_H_ + +#include "UnittDefinitions.h" +#include + +/** + * @brief Can be used for internal testing, for example for hardware specific + * tests which can not be run on a host-machine. + * + * TODO: A lot of ways to improve this class. A way for tests to subscribe + * in this central class would be nice. Right now, this is the class + * which simply calls all other tests from other files manually. + * Maybe there is a better way.. + */ +class InternalUnitTester: public HasReturnvaluesIF { +public: + InternalUnitTester(); + virtual~ InternalUnitTester(); + + /** + * Some function which calls all other tests + * @return + */ + virtual ReturnValue_t performTests(); +}; + + +#endif /* FRAMEWORK_TEST_UNITTESTCLASS_H_ */ diff --git a/unittest/internal/UnittDefinitions.cpp b/unittest/internal/UnittDefinitions.cpp new file mode 100644 index 00000000..0bdbfcc7 --- /dev/null +++ b/unittest/internal/UnittDefinitions.cpp @@ -0,0 +1,7 @@ +#include + + ReturnValue_t unitt::put_error(std::string errorId) { + sif::error << "Unit Tester error: Failed at test ID " + << errorId << "\n" << std::flush; + return HasReturnvaluesIF::RETURN_FAILED; +} diff --git a/unittest/internal/UnittDefinitions.h b/unittest/internal/UnittDefinitions.h new file mode 100644 index 00000000..ea36fea4 --- /dev/null +++ b/unittest/internal/UnittDefinitions.h @@ -0,0 +1,33 @@ +#ifndef UNITTEST_INTERNAL_UNITTDEFINITIONS_H_ +#define UNITTEST_INTERNAL_UNITTDEFINITIONS_H_ + +#include "../../returnvalues/HasReturnvaluesIF.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" +#include +#include + +namespace tv { +// POD test values +static const bool tv_bool = true; +static const uint8_t tv_uint8 {5}; +static const uint16_t tv_uint16 {283}; +static const uint32_t tv_uint32 {929221}; +static const uint64_t tv_uint64 {2929329429}; + +static const int8_t tv_int8 {-16}; +static const int16_t tv_int16 {-829}; +static const int32_t tv_int32 {-2312}; + +static const float tv_float {8.2149214}; +static const float tv_sfloat = {-922.2321321}; +static const double tv_double {9.2132142141e8}; +static const double tv_sdouble {-2.2421e19}; +} + +namespace unitt { +ReturnValue_t put_error(std::string errorId); +} + + + +#endif /* UNITTEST_INTERNAL_UNITTDEFINITIONS_H_ */ diff --git a/unittest/internal/internal.mk b/unittest/internal/internal.mk new file mode 100644 index 00000000..799fd796 --- /dev/null +++ b/unittest/internal/internal.mk @@ -0,0 +1,3 @@ +CXXSRC += $(wildcard $(CURRENTPATH)/osal/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/serialize/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp) \ No newline at end of file diff --git a/unittest/internal/osal/IntTestMq.cpp b/unittest/internal/osal/IntTestMq.cpp new file mode 100644 index 00000000..63016374 --- /dev/null +++ b/unittest/internal/osal/IntTestMq.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include + +using retval = HasReturnvaluesIF; + +void testmq::testMq() { + std::string id = "[testMq]"; + MessageQueueIF* testSenderMq = + QueueFactory::instance()->createMessageQueue(1); + MessageQueueId_t testSenderMqId = testSenderMq->getId(); + + MessageQueueIF* testReceiverMq = + QueueFactory::instance()->createMessageQueue(1); + MessageQueueId_t testReceiverMqId = testReceiverMq->getId(); + std::array testData { 0 }; + testData[0] = 42; + MessageQueueMessage testMessage(testData.data(), 1); + testSenderMq->setDefaultDestination(testReceiverMqId); + + + auto result = testSenderMq->sendMessage(testReceiverMqId, &testMessage); + if(result != retval::RETURN_OK) { + unitt::put_error(id); + } + MessageQueueMessage recvMessage; + result = testReceiverMq->receiveMessage(&recvMessage); + if(result != retval::RETURN_OK or recvMessage.getData()[0] != 42) { + unitt::put_error(id); + } + + result = testSenderMq->sendMessage(testReceiverMqId, &testMessage); + if(result != retval::RETURN_OK) { + unitt::put_error(id); + } + MessageQueueId_t senderId = 0; + result = testReceiverMq->receiveMessage(&recvMessage,&senderId); + if(result != retval::RETURN_OK or recvMessage.getData()[0] != 42) { + unitt::put_error(id); + } + if(senderId != testSenderMqId) { + unitt::put_error(id); + } + senderId = testReceiverMq->getLastPartner(); + if(senderId != testSenderMqId) { + unitt::put_error(id); + } + +} diff --git a/unittest/internal/osal/IntTestMq.h b/unittest/internal/osal/IntTestMq.h new file mode 100644 index 00000000..bc12a9b0 --- /dev/null +++ b/unittest/internal/osal/IntTestMq.h @@ -0,0 +1,9 @@ +#ifndef UNITTEST_INTERNAL_INTESTMQ_H_ +#define UNITTEST_INTERNAL_INTESTMQ_H_ + +namespace testmq { +void testMq(); +} + + +#endif /* UNITTEST_INTERNAL_INTESTMQ_H_ */ diff --git a/unittest/internal/osal/IntTestMutex.cpp b/unittest/internal/osal/IntTestMutex.cpp new file mode 100644 index 00000000..3fd668df --- /dev/null +++ b/unittest/internal/osal/IntTestMutex.cpp @@ -0,0 +1,42 @@ +#include "IntTestMutex.h" + +#include +#include + +#if defined(hosted) +#include +#include +#include +#endif + + +void testmutex::testMutex() { + std::string id = "[testMutex]"; + MutexIF* mutex = MutexFactory::instance()->createMutex(); + auto result = mutex->lockMutex(MutexIF::POLLING); + if(result != HasReturnvaluesIF::RETURN_OK) { + unitt::put_error(id); + } + // timed_mutex from the C++ library specifies undefined behaviour if + // the timed mutex is locked twice from the same thread. +#if defined(hosted) + // hold on, this actually worked ? :-D This calls the function from + // another thread and stores the returnvalue in a future. + auto future = std::async(&MutexIF::lockMutex, mutex, 1); + result = future.get(); +#else + result = mutex->lockMutex(MutexIF::TimeoutType::WAITING, 1); +#endif + if(result != MutexIF::MUTEX_TIMEOUT) { + unitt::put_error(id); + } + + result = mutex->unlockMutex(); + if(result != HasReturnvaluesIF::RETURN_OK) { + unitt::put_error(id); + } + result = mutex->unlockMutex(); + if(result != MutexIF::CURR_THREAD_DOES_NOT_OWN_MUTEX) { + unitt::put_error(id); + } +} diff --git a/unittest/internal/osal/IntTestMutex.h b/unittest/internal/osal/IntTestMutex.h new file mode 100644 index 00000000..b467ea63 --- /dev/null +++ b/unittest/internal/osal/IntTestMutex.h @@ -0,0 +1,10 @@ +#ifndef UNITTEST_INTERNAL_INTTESTMUTEX_H_ +#define UNITTEST_INTERNAL_INTTESTMUTEX_H_ + +namespace testmutex { +void testMutex(); +} + + + +#endif /* UNITTEST_INTERNAL_INTTESTMUTEX_H_ */ diff --git a/unittest/internal/osal/IntTestSemaphore.cpp b/unittest/internal/osal/IntTestSemaphore.cpp new file mode 100644 index 00000000..534a6a6d --- /dev/null +++ b/unittest/internal/osal/IntTestSemaphore.cpp @@ -0,0 +1,160 @@ +#include "IntTestSemaphore.h" +#include +#include +#include +#include + + +void testsemaph::testBinSemaph() { + std::string id = "[BinSemaphore]"; + SemaphoreIF* binSemaph = + SemaphoreFactory::instance()->createBinarySemaphore(); + if(binSemaph == nullptr) { + return; + } + testBinSemaphoreImplementation(binSemaph, id); + SemaphoreFactory::instance()->deleteSemaphore(binSemaph); +#if defined(freeRTOS) + SemaphoreIF* binSemaphUsingTask = + SemaphoreFactory::instance()->createBinarySemaphore(1); + testBinSemaphoreImplementation(binSemaphUsingTask, id); + SemaphoreFactory::instance()->deleteSemaphore(binSemaphUsingTask); +#endif +} + + +void testsemaph::testCountingSemaph() { + std::string id = "[CountingSemaph]"; + { + // First test: create a binary semaphore by using a counting semaphore. + SemaphoreIF* countingSemaph = SemaphoreFactory::instance()-> + createCountingSemaphore(1,1); + if(countingSemaph == nullptr) { + return; + } + testBinSemaphoreImplementation(countingSemaph, id); + SemaphoreFactory::instance()->deleteSemaphore(countingSemaph); +#if defined(freeRTOS) + countingSemaph = SemaphoreFactory::instance()-> + createCountingSemaphore(1, 1, 1); + testBinSemaphoreImplementation(countingSemaph, id); + SemaphoreFactory::instance()->deleteSemaphore(countingSemaph); +#endif + } + + { + // Second test: counting semaphore with count 3 and init count of 3. + SemaphoreIF* countingSemaph = SemaphoreFactory::instance()-> + createCountingSemaphore(3,3); + testCountingSemaphImplementation(countingSemaph, id); + SemaphoreFactory::instance()->deleteSemaphore(countingSemaph); +#if defined(freeRTOS) + countingSemaph = SemaphoreFactory::instance()-> + createCountingSemaphore(3, 0, 1); + uint8_t semaphCount = countingSemaph->getSemaphoreCounter(); + if(semaphCount != 0) { + unitt::put_error(id); + } + // release 3 times in a row + for(int i = 0; i < 3; i++) { + auto result = countingSemaph->release(); + if(result != HasReturnvaluesIF::RETURN_OK) { + unitt::put_error(id); + } + } + testCountingSemaphImplementation(countingSemaph, id); + SemaphoreFactory::instance()->deleteSemaphore(countingSemaph); +#endif + } +} + + +void testsemaph::testBinSemaphoreImplementation(SemaphoreIF* binSemaph, + std::string id) { + uint8_t semaphCount = binSemaph->getSemaphoreCounter(); + if(semaphCount != 1) { + unitt::put_error(id); + } + + ReturnValue_t result = binSemaph->release(); + if(result != SemaphoreIF::SEMAPHORE_NOT_OWNED) { + unitt::put_error(id); + } + result = binSemaph->acquire(SemaphoreIF::BLOCKING); + if(result != HasReturnvaluesIF::RETURN_OK) { + unitt::put_error(id); + } + + // There is not really a point in testing time related, the task + // might get interrupted.. + { + //Stopwatch stopwatch(false); + result = binSemaph->acquire(SemaphoreIF::TimeoutType::WAITING, 10); + //dur_millis_t time = stopwatch.stop(); +// if(abs(time - 10) > 2) { +// sif::error << "UnitTester: Semaphore timeout measured incorrect." +// << std::endl; +// unitt::put_error(id); +// } + } + + if(result != SemaphoreIF::SEMAPHORE_TIMEOUT) { + unitt::put_error(id); + } + + semaphCount = binSemaph->getSemaphoreCounter(); + if(semaphCount != 0) { + unitt::put_error(id); + } + + result = binSemaph->release(); + if(result != HasReturnvaluesIF::RETURN_OK) { + unitt::put_error(id); + } +} + +void testsemaph::testCountingSemaphImplementation(SemaphoreIF* countingSemaph, + std::string id) { + // check count getter function + uint8_t semaphCount = countingSemaph->getSemaphoreCounter(); + if(semaphCount != 3) { + unitt::put_error(id); + } + ReturnValue_t result = countingSemaph->release(); + if(result != SemaphoreIF::SEMAPHORE_NOT_OWNED) { + unitt::put_error(id); + } + // acquire 3 times in a row + for(int i = 0; i < 3; i++) { + result = countingSemaph->acquire(SemaphoreIF::BLOCKING); + if(result != HasReturnvaluesIF::RETURN_OK) { + unitt::put_error(id); + } + } + + { + Stopwatch stopwatch(false); + // attempt to take when count is 0, measure time + result = countingSemaph->acquire(SemaphoreIF::TimeoutType::WAITING, 10); + dur_millis_t time = stopwatch.stop(); + if(abs(time - 10) > 1) { + unitt::put_error(id); + } + } + + if(result != SemaphoreIF::SEMAPHORE_TIMEOUT) { + unitt::put_error(id); + } + + // release 3 times in a row + for(int i = 0; i < 3; i++) { + result = countingSemaph->release(); + if(result != HasReturnvaluesIF::RETURN_OK) { + unitt::put_error(id); + } + } + // assert correct full count + if(countingSemaph->getSemaphoreCounter() != 3) { + unitt::put_error(id); + } +} diff --git a/unittest/internal/osal/IntTestSemaphore.h b/unittest/internal/osal/IntTestSemaphore.h new file mode 100644 index 00000000..af26c131 --- /dev/null +++ b/unittest/internal/osal/IntTestSemaphore.h @@ -0,0 +1,15 @@ +#ifndef UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_ +#define UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_ +class SemaphoreIF; +#include + +namespace testsemaph { +void testBinSemaph(); +void testBinSemaphoreImplementation(SemaphoreIF* binSemaph, std::string id); +void testCountingSemaph(); +void testCountingSemaphImplementation(SemaphoreIF* countingSemaph, + std::string id); +} + + +#endif /* UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_ */ diff --git a/unittest/internal/serialize/IntTestSerialization.cpp b/unittest/internal/serialize/IntTestSerialization.cpp new file mode 100644 index 00000000..3f231a41 --- /dev/null +++ b/unittest/internal/serialize/IntTestSerialization.cpp @@ -0,0 +1,230 @@ +#include "IntTestSerialization.h" +#include +#include +#include +#include +#include + +using retval = HasReturnvaluesIF; +std::array testserialize::test_array = { 0 }; + +ReturnValue_t testserialize::test_serialization() { + // Here, we test all serialization tools. First test basic cases. + ReturnValue_t result = test_endianness_tools(); + if(result != retval::RETURN_OK) { + return result; + } + result = test_autoserialization(); + if(result != retval::RETURN_OK) { + return result; + } + result = test_serial_buffer_adapter(); + if(result != retval::RETURN_OK) { + return result; + } + return retval::RETURN_OK; +} + +ReturnValue_t testserialize::test_endianness_tools() { + std::string id = "[test_endianness_tools]"; + test_array[0] = 0; + test_array[1] = 0; + uint16_t two_byte_value = 1; + size_t size = 0; + uint8_t* p_array = test_array.data(); + SerializeAdapter::serialize(&two_byte_value, &p_array, &size, 2, + SerializeIF::Endianness::MACHINE); + // Little endian: Value one on first byte + if(test_array[0] != 1 and test_array[1] != 0) { + return unitt::put_error(id); + + } + + p_array = test_array.data(); + size = 0; + SerializeAdapter::serialize(&two_byte_value, &p_array, &size, 2, + SerializeIF::Endianness::BIG); + // Big endian: Value one on second byte + if(test_array[0] != 0 and test_array[1] != 1) { + return unitt::put_error(id); + } + return retval::RETURN_OK; +} + +ReturnValue_t testserialize::test_autoserialization() { + std::string id = "[test_autoserialization]"; + // Unit Test getSerializedSize + if(SerializeAdapter:: + getSerializedSize(&tv::tv_bool) != sizeof(tv::tv_bool) or + SerializeAdapter:: + getSerializedSize(&tv::tv_uint8) != sizeof(tv::tv_uint8) or + SerializeAdapter:: + getSerializedSize(&tv::tv_uint16) != sizeof(tv::tv_uint16) or + SerializeAdapter:: + getSerializedSize(&tv::tv_uint32) != sizeof(tv::tv_uint32) or + SerializeAdapter:: + getSerializedSize(&tv::tv_uint64) != sizeof(tv::tv_uint64) or + SerializeAdapter:: + getSerializedSize(&tv::tv_int8) != sizeof(tv::tv_int8) or + SerializeAdapter:: + getSerializedSize(&tv::tv_double) != sizeof(tv::tv_double) or + SerializeAdapter:: + getSerializedSize(&tv::tv_int16) != sizeof(tv::tv_int16) or + SerializeAdapter:: + getSerializedSize(&tv::tv_int32) != sizeof(tv::tv_int32) or + SerializeAdapter:: + getSerializedSize(&tv::tv_float) != sizeof(tv::tv_float)) + { + return unitt::put_error(id); + } + + size_t serialized_size = 0; + uint8_t * p_array = test_array.data(); + + SerializeAdapter::serialize(&tv::tv_bool, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_uint8, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_uint16, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_uint32, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_int8, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_int16, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_int32, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_uint64, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_float, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_double, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_sfloat, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv::tv_sdouble, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + // expected size is 1 + 1 + 2 + 4 + 1 + 2 + 4 + 8 + 4 + 8 + 4 + 8 + if(serialized_size != 47) { + return unitt::put_error(id); + } + + p_array = test_array.data(); + size_t remaining_size = serialized_size; + bool tv_bool; + uint8_t tv_uint8; + uint16_t tv_uint16; + uint32_t tv_uint32; + int8_t tv_int8; + int16_t tv_int16; + int32_t tv_int32; + uint64_t tv_uint64; + float tv_float; + double tv_double; + float tv_sfloat; + double tv_sdouble; + + SerializeAdapter::deSerialize(&tv_bool, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint8, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint16, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint32, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int8, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int16, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int32, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint64, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_float, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_double, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_sfloat, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_sdouble, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + + if(tv_bool != tv::tv_bool or tv_uint8 != tv::tv_uint8 or + tv_uint16 != tv::tv_uint16 or tv_uint32 != tv::tv_uint32 or + tv_uint64 != tv::tv_uint64 or tv_int8 != tv::tv_int8 or + tv_int16 != tv::tv_int16 or tv_int32 != tv::tv_int32) + { + return unitt::put_error(id); + } + + // These epsilon values were just guessed.. It appears to work though. + if(abs(tv_float - tv::tv_float) > 0.0001 or + abs(tv_double - tv::tv_double) > 0.01 or + abs(tv_sfloat - tv::tv_sfloat) > 0.0001 or + abs(tv_sdouble - tv::tv_sdouble) > 0.01) { + return unitt::put_error(id); + } + + // Check overflow + return retval::RETURN_OK; +} + +// TODO: Also test for constant buffers. +ReturnValue_t testserialize::test_serial_buffer_adapter() { + std::string id = "[test_serial_buffer_adapter]"; + + // I will skip endian swapper testing, its going to be changed anyway.. + // uint8_t tv::tv_uint8_swapped = EndianSwapper::swap(tv::tv_uint8); + + size_t serialized_size = 0; + uint8_t * p_array = test_array.data(); + std::array test_serial_buffer {5, 4, 3, 2, 1}; + SerialBufferAdapter tv_serial_buffer_adapter = + SerialBufferAdapter(test_serial_buffer.data(), + test_serial_buffer.size(), false); + uint16_t testUint16 = 16; + + SerializeAdapter::serialize(&tv::tv_bool, &p_array,&serialized_size, + test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_serial_buffer_adapter, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&testUint16, &p_array, &serialized_size, + test_array.size(), SerializeIF::Endianness::MACHINE); + + if(serialized_size != 8 or test_array[0] != true or test_array[1] != 5 + or test_array[2] != 4 or test_array[3] != 3 or test_array[4] != 2 + or test_array[5] != 1) + { + return unitt::put_error(id); + } + memcpy(&testUint16, test_array.data() + 6, sizeof(testUint16)); + if(testUint16 != 16) { + return unitt::put_error(id); + } + + // Serialize with size field + SerialBufferAdapter tv_serial_buffer_adapter2 = + SerialBufferAdapter(test_serial_buffer.data(), + test_serial_buffer.size(), true); + serialized_size = 0; + p_array = test_array.data(); + SerializeAdapter::serialize(&tv::tv_bool, &p_array,&serialized_size, + test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_serial_buffer_adapter2, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&testUint16, &p_array, &serialized_size, + test_array.size(), SerializeIF::Endianness::MACHINE); + + if(serialized_size != 9 or test_array[0] != true or test_array[1] != 5 + or test_array[2] != 5 or test_array[3] != 4 or test_array[4] != 3 + or test_array[5] != 2 or test_array[6] != 1) + { + return unitt::put_error(id); + } + memcpy(&testUint16, test_array.data() + 7, sizeof(testUint16)); + if(testUint16 != 16) { + return unitt::put_error(id); + } + return retval::RETURN_OK; +} diff --git a/unittest/internal/serialize/IntTestSerialization.h b/unittest/internal/serialize/IntTestSerialization.h new file mode 100644 index 00000000..f8841b82 --- /dev/null +++ b/unittest/internal/serialize/IntTestSerialization.h @@ -0,0 +1,15 @@ +#ifndef UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ +#define UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ +#include +#include + +namespace testserialize { +ReturnValue_t test_serialization(); +ReturnValue_t test_endianness_tools(); +ReturnValue_t test_autoserialization(); +ReturnValue_t test_serial_buffer_adapter(); + +extern std::array test_array; +} + +#endif /* UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ */ diff --git a/unittest/lcov.sh b/unittest/lcov.sh new file mode 100644 index 00000000..4db16e5f --- /dev/null +++ b/unittest/lcov.sh @@ -0,0 +1,3 @@ +#!/bin/bash +lcov --capture --directory . --output-file coverage.info +genhtml coverage.info --output-directory _coverage diff --git a/unittest/testcfg/FSFWConfig.h b/unittest/testcfg/FSFWConfig.h new file mode 100644 index 00000000..4fb224c1 --- /dev/null +++ b/unittest/testcfg/FSFWConfig.h @@ -0,0 +1,46 @@ +#ifndef CONFIG_FSFWCONFIG_H_ +#define CONFIG_FSFWCONFIG_H_ + +#include + +//! Used to determine whether C++ ostreams are used +//! Those can lead to code bloat. +#define FSFW_CPP_OSTREAM_ENABLED 1 + +//! Reduced printout to further decrese code size +//! Be careful, this also turns off most diagnostic prinouts! +#define FSFW_REDUCED_PRINTOUT 0 + +//! Can be used to enable debugging printouts for developing the FSFW +#define FSFW_DEBUGGING 0 + +//! Defines the FIFO depth of each commanding service base which +//! also determines how many commands a CSB service can handle in one cycle +//! simulataneously. This will increase the required RAM for +//! each CSB service ! +#define FSFW_CSB_FIFO_DEPTH 6 + +//! If FSFW_OBJ_EVENT_TRANSLATION is set to one, +//! additional output which requires the translation files translateObjects +//! and translateEvents (and their compiled source files) +#define FSFW_OBJ_EVENT_TRANSLATION 0 + +//! If -DDEBUG is supplied in the build defines, there will be +//! additional output which requires the translation files translateObjects +//! and translateEvents (and their compiles source files) +#if FSFW_OBJ_EVENT_TRANSLATION == 1 +#define FSFW_DEBUG_OUTPUT 1 +//! Specify whether info events are printed too. +#define FSFW_DEBUG_INFO 1 +#include +#include +#else +#define FSFW_DEBUG_OUTPUT 0 +#endif + +//! When using the newlib nano library, C99 support for stdio facilities +//! will not be provided. This define should be set to 1 if this is the case. +#define FSFW_NO_C99_IO 1 + + +#endif /* CONFIG_FSFWCONFIG_H_ */ diff --git a/unittest/testcfg/Makefile-FSFW-Tests b/unittest/testcfg/Makefile-FSFW-Tests new file mode 100644 index 00000000..d43a6edc --- /dev/null +++ b/unittest/testcfg/Makefile-FSFW-Tests @@ -0,0 +1,415 @@ +#------------------------------------------------------------------------------- +# Makefile for FSFW Test +#------------------------------------------------------------------------------- +# User-modifiable options +#------------------------------------------------------------------------------- +# Fundamentals on the build process of C/C++ Software: +# https://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html + +# Make documentation: https://www.gnu.org/software/make/manual/make.pdf +# Online: https://www.gnu.org/software/make/manual/make.html +# General rules: http://make.mad-scientist.net/papers/rules-of-makefiles/#rule3 +SHELL = /bin/sh + +# Chip & board used for compilation +# (can be overriden by adding CHIP=chip and BOARD=board to the command-line) +# Unit Test can only be run on host machine for now (Linux) +FRAMEWORK_PATH = fsfw +FILE_ROOT = $(FRAMEWORK_PATH)/unittest +BOARD = unittest +LINUX = 1 +OS_FSFW = linux +CUSTOM_DEFINES += -D$(OS_FSFW) + +# Copied from stackoverflow, can be used to differentiate between Windows +# and Linux +ifeq ($(OS),Windows_NT) + CUSTOM_DEFINES += -DWIN32 + ifeq ($(PROCESSOR_ARCHITEW6432),AMD64) + CUSTOM_DEFINES += -DAMD64 + else + ifeq ($(PROCESSOR_ARCHITECTURE),AMD64) + CUSTOM_DEFINES += -DAMD64 + endif + ifeq ($(PROCESSOR_ARCHITECTURE),x86) + CUSTOM_DEFINES += -DIA32 + endif + endif +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + DETECTED_OS = LINUX + CUSTOM_DEFINES += -DLINUX + endif + ifeq ($(UNAME_S),Darwin) + CUSTOM_DEFINES += -DOSX + endif + UNAME_P := $(shell uname -p) + ifeq ($(UNAME_P),x86_64) + CUSTOM_DEFINES += -DAMD64 + endif + ifneq ($(filter %86,$(UNAME_P)),) + CUSTOM_DEFINES += -DIA32 + endif + ifneq ($(filter arm%,$(UNAME_P)),) + CUSTOM_DEFINES += -DARM + endif +endif + +UNIT_TEST = 1 +# General folder paths +CONFIG_PATH = $(FILE_ROOT)/config +UNIT_TEST_PATH = $(FILE_ROOT)/tests +CORE_PATH = $(FILE_ROOT)/core + +# Output file basename +BASENAME = fsfw +BINARY_NAME := $(BASENAME)-$(BOARD) +# Output files will be put in this directory inside +OUTPUT_FOLDER = $(OS) + +# Optimization level. Optimized for debugging. +OPTIMIZATION = -O0 + +# Default debug output. Optimized for debugging. +DEBUG_LEVEL = -g3 + +ifdef GCOV +CUSTOM_DEFINES += -DGCOV +endif + + +# Output directories +BUILDPATH = _bin +DEPENDPATH = _dep +OBJECTPATH = _obj + +ifeq ($(MAKECMDGOALS),mission) +BUILD_FOLDER = mission +else +BUILD_FOLDER = devel +endif + +DEPENDDIR = $(DEPENDPATH)/$(OUTPUT_FOLDER)/$(BUILD_FOLDER) +OBJDIR = $(OBJECTPATH)/$(OUTPUT_FOLDER)/$(BUILD_FOLDER) +BINDIR = $(BUILDPATH) + +CLEANDEP = $(DEPENDPATH)/$(OUTPUT_FOLDER) +CLEANOBJ = $(OBJECTPATH)/$(OUTPUT_FOLDER) +CLEANBIN = $(BUILDPATH) +#------------------------------------------------------------------------------- +# Tools and Includes +#------------------------------------------------------------------------------- + +# Tool suffix when cross-compiling +CROSS_COMPILE = + +# C Compiler +CC = $(CROSS_COMPILE)gcc + +# C++ compiler +CXX = $(CROSS_COMPILE)g++ + +# Additional Tools +SIZE = $(CROSS_COMPILE)size +STRIP = $(CROSS_COMPILE)strip +CP = $(CROSS_COMPILE)objcopy + +HEXCOPY = $(CP) -O ihex +BINCOPY = $(CP) -O binary +# files to be compiled, will be filled in by include makefiles +# := assignment is neccessary so we get all paths right +# https://www.gnu.org/software/make/manual/html_node/Flavors.html +CSRC := +CXXSRC := +ASRC := +INCLUDES := + +# Directories where $(directoryname).mk files should be included from +SUBDIRS := $(FRAMEWORK_PATH) $(TEST_PATH) $(UNIT_TEST_PATH) $(CONFIG_PATH) \ + $(CORE_PATH) + + +I_INCLUDES += $(addprefix -I, $(INCLUDES)) + +# This is a hack from http://make.mad-scientist.net/the-eval-function/ +# +# The problem is, that included makefiles should be aware of their relative path +# but not need to guess or hardcode it. So we set $(CURRENTPATH) for them. If +# we do this globally and the included makefiles want to include other makefiles as +# well, they would overwrite $(CURRENTPATH), screwing the include after them. +# +# By using a for-loop with an eval'd macro, we can generate the code to include all +# sub-makefiles (with the correct $(CURRENTPATH) set) before actually evaluating +# (and by this possibly changing $(CURRENTPATH)) them. +# +# This works recursively, if an included makefile wants to include, it can safely set +# $(SUBDIRS) (which has already been evaluated here) and do +# "$(foreach S,$(SUBDIRS),$(eval $(INCLUDE_FILE)))" +# $(SUBDIRS) must be relative to the project root, so to include subdir foo, set +# $(SUBDIRS) = $(CURRENTPATH)/foo. +define INCLUDE_FILE +CURRENTPATH := $S +include $(S)/$(notdir $S).mk +endef +$(foreach S,$(SUBDIRS),$(eval $(INCLUDE_FILE))) + +INCLUDES += $(FILE_ROOT) +INCLUDES += $(FILE_ROOT)/catch2/ + +#------------------------------------------------------------------------------- +# Source Files +#------------------------------------------------------------------------------- + +# All source files which are not includes by the .mk files are added here +# Please ensure that no files are included by both .mk file and here ! + +# if a target is not listed in the current directory, +# make searches in the directories specified with VPATH + +# All C Sources included by .mk files are assigned here +# Add the objects to sources so dependency handling works +C_OBJECTS += $(CSRC:.c=.o) + +# Objects built from Assembly source files +ASM_OBJECTS = $(ASRC:.S=.o) + +# Objects built from C++ source files +CXX_OBJECTS += $(CXXSRC:.cpp=.o) + +#------------------------------------------------------------------------------- +# Build Configuration + Output +#------------------------------------------------------------------------------- + +TARGET = Debug build. +DEBUG_MESSAGE = Off +OPTIMIZATION_MESSAGE = Off + +# Define Messages +MSG_INFO = Software: Hosted unittest \(Catch2\) for the FSFW. +MSG_OPTIMIZATION = Optimization: $(OPTIMIZATION), $(OPTIMIZATION_MESSAGE) +MSG_TARGET = Target Build: $(TARGET) +MSG_DEBUG = Debug level: $(DEBUG_LEVEL), FSFW Debugging: $(DEBUG_MESSAGE) + +MSG_LINKING = Linking: +MSG_COMPILING = Compiling: +MSG_ASSEMBLING = Assembling: +MSG_DEPENDENCY = Collecting dependencies for: +MSG_BINARY = Generate binary: + +# See https://stackoverflow.com/questions/6687630/how-to-remove-unused-c-c-symbols-with-gcc-and-ld +# Used to throw away unused code. Reduces code size significantly ! +# -Wl,--gc-sections: needs to be passed to the linker to throw aways unused code +ifdef KEEP_UNUSED_CODE +PROTOTYPE_OPTIMIZATION = +UNUSED_CODE_REMOVAL = +else +PROTOTYPE_OPTIMIZATION = -ffunction-sections -fdata-sections +UNUSED_CODE_REMOVAL = -Wl,--gc-sections +# Link time optimization +# See https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html for reference +# Link time is larger and size of object files can not be retrieved +# but resulting binary is smaller. Could be used in mission/deployment build +# Requires -ffunction-section in linker call +LINK_TIME_OPTIMIZATION = -flto +OPTIMIZATION += $(PROTOTYPE_OPTIMIZATION) +endif + +# Dependency Flags +# These flags tell the compiler to build dependencies +# See: https://www.gnu.org/software/make/manual/html_node/Automatic-Prerequisites.html +# Using following guide: http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/#combine +DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPENDDIR)/$*.d + +# Flags for the compiler call +# - std: Which C++ version to use. Common versions: c++11, c++14 and c++17 +# - Wall: enable all warnings +# - Wextra: enable extra warnings +# - g: defines debug level +# - fmessage-length: to control the formatting algorithm for diagnostic messages; +# =0 means no line-wrapping is done; each error message appears on a single line +# - fno-exceptions: stops generating extra code needed to propagate exceptions, +# which can produce significant data size overhead +CUSTOM_DEFINES += -DUNIT_TEST +WARNING_FLAGS = -Wall -Wshadow=local -Wextra -Wimplicit-fallthrough=1 \ + -Wno-unused-parameter + +CXXDEFINES := $(CUSTOM_DEFINES) +CFLAGS += +CXXFLAGS += -I. $(DEBUG_LEVEL) $(WARNING_FLAGS) $(DEPFLAGS) -fmessage-length=0 $(OPTIMIZATION)\ + $(I_INCLUDES) $(CXXDEFINES) +CPPFLAGS += -std=c++11 + +# Flags for the linker call +# LINK_INCLUDES specify the path to used libraries and the linker script +# LINK_LIBRARIES: Link real time support +LDFLAGS := $(DEBUG_LEVEL) $(UNUSED_CODE_REMOVAL) $(OPTIMIZATION) -pthread +LINK_INCLUDES := +LINK_LIBRARIES := + +ifdef LINUX +LINK_LIBRARIES += -lrt +endif + +ifeq ($(OS),Windows_NT) +LINK_LIBRARIES += -lwsock32 -lws2_32 +LDFLASGS += -fuse-ld=lld +endif + +# Gnu Coverage Tools Flags +ifdef GCOV +GCOV_CXXFLAGS = -fprofile-arcs -ftest-coverage --coverage -fno-inline \ + -fno-inline-small-functions -fno-default-inline +CXXFLAGS += $(GCOV_CXXFLAGS) +GCOV_LINKER_LIBS = -lgcov -fprofile-arcs -ftest-coverage +LINK_LIBRARIES += $(GCOV_LINKER_LIBS) +endif + +# $(info $${CXXFLAGS} is [${CXXFLAGS}]) + +#------------------------------------------------------------------------------- +# Rules +#------------------------------------------------------------------------------- +# the call function assigns parameters to temporary variables +# https://www.gnu.org/software/make/manual/make.html#Call-Function +# $(1) = Memory names +# Rules are called for each memory type +# Two Expansion Symbols $$ are to escape the dollar sign for eval. +# See: http://make.mad-scientist.net/the-eval-function/ + +default: all + +# Cleans all files +hardclean: + -rm -rf $(BUILDPATH) + -rm -rf $(OBJECTPATH) + -rm -rf $(DEPENDPATH) + +# Only clean files for current build +clean: + -rm -rf $(CLEANOBJ) + -rm -rf $(CLEANBIN) + -rm -rf $(CLEANDEP) + +# Only clean binaries. Useful for changing the binary type when object files +# are already compiled so complete rebuild is not necessary +cleanbin: + -rm -rf $(CLEANBIN) + +# In this section, the binaries are built for all selected memories +# notestfw: all +all: executable + +# Build target configuration +release: OPTIMIZATION = -Os $(PROTOTYPE_OPTIMIZATION) $(LINK_TIME_OPTIMIZATION) +release: LINK_TIME_OPTIMIZATION = -flto +release: TARGET = Mission build. +release: OPTIMIZATION_MESSAGE = On with Link Time Optimization + +debug: CXXDEFINES += -DDEBUG +debug: TARGET = Debug +debug: DEBUG_MESSAGE = On + +ifndef KEEP_UNUSED_CODE +debug release: OPTIMIZATION_MESSAGE += , no unused code removal +endif + +debug release notestfw: executable + +executable: $(BINDIR)/$(BINARY_NAME).elf + @echo + @echo $(MSG_INFO) + @echo $(MSG_TARGET) + @echo $(MSG_OPTIMIZATION) + @echo $(MSG_DEBUG) + +C_OBJECTS_PREFIXED = $(addprefix $(OBJDIR)/, $(C_OBJECTS)) +CXX_OBJECTS_PREFIXED = $(addprefix $(OBJDIR)/, $(CXX_OBJECTS)) +ASM_OBJECTS_PREFIXED = $(addprefix $(OBJDIR)/, $(ASM_OBJECTS)) +ALL_OBJECTS = $(ASM_OBJECTS_PREFIXED) $(C_OBJECTS_PREFIXED) \ + $(CXX_OBJECTS_PREFIXED) + +# Useful for debugging the Makefile +# Also see: https://www.oreilly.com/openbook/make3/book/ch12.pdf +# $(info $${ALL_OBJECTS} is [${ALL_OBJECTS}]) +# $(info $${CXXSRC} is [${CXXSRC}]) + +# Automatic variables are used here extensively. Some of them +# are escaped($$) to suppress immediate evaluation. The most important ones are: +# $@: Name of Target (left side of rule) +# $<: Name of the first prerequisite (right side of rule) +# @^: List of all prerequisite, omitting duplicates +# @D: Directory and file-within-directory part of $@ + +# Generates binary and displays all build properties +# -p with mkdir ignores error and creates directory when needed. + +# SHOW_DETAILS = 1 + + +# Link with required libraries: HAL (Hardware Abstraction Layer) and +# HCC (File System Library) +$(BINDIR)/$(BINARY_NAME).elf: $(ALL_OBJECTS) + @echo + @echo $(MSG_LINKING) Target $@ + @mkdir -p $(@D) +ifdef SHOW_DETAILS + $(CXX) $(LDFLAGS) $(LINK_INCLUDES) -o $@ $^ $(LINK_LIBRARIES) +else + @$(CXX) $(LDFLAGS) $(LINK_INCLUDES) -o $@ $^ $(LINK_LIBRARIES) +endif +ifeq ($(BUILD_FOLDER), mission) +# With Link Time Optimization, section size is not available + $(SIZE) $@ +else + $(SIZE) $^ $@ +endif + +$(BINDIR)/$(BINARY_NAME).hex: $(BINDIR)/$(BINARY_NAME).elf + @echo + @echo $(MSG_BINARY) + @mkdir -p $(@D) + $(HEXCOPY) $< $@ + +# Build new objects for changed dependencies. +$(OBJDIR)/%.o: %.cpp +$(OBJDIR)/%.o: %.cpp $(DEPENDDIR)/%.d | $(DEPENDDIR) + @echo + @echo $(MSG_COMPILING) $< + @mkdir -p $(@D) +ifdef SHOW_DETAILS + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< +else + @$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< +endif + +$(OBJDIR)/%.o: %.c +$(OBJDIR)/%.o: %.c $(DEPENDDIR)/%.d | $(DEPENDDIR) + @echo + @echo $(MSG_COMPILING) $< + @mkdir -p $(@D) +ifdef SHOW_DETAILS + $(CC) $(CXXFLAGS) $(CFLAGS) -c -o $@ $< +else + @$(CC) $(CXXFLAGS) $(CFLAGS) -c -o $@ $< +endif + +#------------------------------------------------------------------------------- +# Dependency Handling +#------------------------------------------------------------------------------- + +# Dependency Handling according to following guide: +# http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/ +$(DEPENDDIR): + @mkdir -p $(@D) +DEPENDENCY_RELATIVE = $(CSRC:.c=.d) $(CXXSRC:.cpp=.d) +# This is the list of all dependencies +DEPFILES = $(addprefix $(DEPENDDIR)/, $(DEPENDENCY_RELATIVE)) +# Create subdirectories for dependencies +$(DEPFILES): + @mkdir -p $(@D) +# Include all dependencies +include $(wildcard $(DEPFILES)) + +# .PHONY tells make that these targets aren't files +.PHONY: clean release debug all hardclean cleanbin diff --git a/unittest/testcfg/TestsConfig.h b/unittest/testcfg/TestsConfig.h new file mode 100644 index 00000000..cd967fa7 --- /dev/null +++ b/unittest/testcfg/TestsConfig.h @@ -0,0 +1,8 @@ +#ifndef FSFW_UNITTEST_CONFIG_TESTSCONFIG_H_ +#define FSFW_UNITTEST_CONFIG_TESTSCONFIG_H_ + + +#define CUSTOM_UNITTEST_RUNNER 0 + + +#endif /* FSFW_UNITTEST_CONFIG_TESTSCONFIG_H_ */ diff --git a/unittest/testcfg/cdatapool/dataPoolInit.cpp b/unittest/testcfg/cdatapool/dataPoolInit.cpp new file mode 100644 index 00000000..ad2dc4ef --- /dev/null +++ b/unittest/testcfg/cdatapool/dataPoolInit.cpp @@ -0,0 +1,5 @@ +#include "dataPoolInit.h" + +void datapool::dataPoolInit(std::map * poolMap) { + +} diff --git a/unittest/testcfg/cdatapool/dataPoolInit.h b/unittest/testcfg/cdatapool/dataPoolInit.h new file mode 100644 index 00000000..23a3d01f --- /dev/null +++ b/unittest/testcfg/cdatapool/dataPoolInit.h @@ -0,0 +1,17 @@ +#ifndef HOSTED_CONFIG_CDATAPOOL_DATAPOOLINIT_H_ +#define HOSTED_CONFIG_CDATAPOOL_DATAPOOLINIT_H_ + +#include +#include +#include +#include + + +namespace datapool { + void dataPoolInit(std::map * poolMap); + + enum datapoolvariables { + NO_PARAMETER = 0, + }; +} +#endif /* CONFIG_CDATAPOOL_DATAPOOLINIT_H_ */ diff --git a/unittest/testcfg/devices/logicalAddresses.cpp b/unittest/testcfg/devices/logicalAddresses.cpp new file mode 100644 index 00000000..c7ce314d --- /dev/null +++ b/unittest/testcfg/devices/logicalAddresses.cpp @@ -0,0 +1,5 @@ +#include "logicalAddresses.h" + + + + diff --git a/unittest/testcfg/devices/logicalAddresses.h b/unittest/testcfg/devices/logicalAddresses.h new file mode 100644 index 00000000..cdf87025 --- /dev/null +++ b/unittest/testcfg/devices/logicalAddresses.h @@ -0,0 +1,15 @@ +#ifndef CONFIG_DEVICES_LOGICALADDRESSES_H_ +#define CONFIG_DEVICES_LOGICALADDRESSES_H_ + +#include +#include +#include + +namespace addresses { + /* Logical addresses have uint32_t datatype */ + enum logicalAddresses: address_t { + }; +} + + +#endif /* CONFIG_DEVICES_LOGICALADDRESSES_H_ */ diff --git a/unittest/testcfg/devices/powerSwitcherList.cpp b/unittest/testcfg/devices/powerSwitcherList.cpp new file mode 100644 index 00000000..343f78d0 --- /dev/null +++ b/unittest/testcfg/devices/powerSwitcherList.cpp @@ -0,0 +1,4 @@ +#include "powerSwitcherList.h" + + + diff --git a/unittest/testcfg/devices/powerSwitcherList.h b/unittest/testcfg/devices/powerSwitcherList.h new file mode 100644 index 00000000..86ddea57 --- /dev/null +++ b/unittest/testcfg/devices/powerSwitcherList.h @@ -0,0 +1,12 @@ +#ifndef CONFIG_DEVICES_POWERSWITCHERLIST_H_ +#define CONFIG_DEVICES_POWERSWITCHERLIST_H_ + +namespace switches { + /* Switches are uint8_t datatype and go from 0 to 255 */ + enum switcherList { + }; + +} + + +#endif /* CONFIG_DEVICES_POWERSWITCHERLIST_H_ */ diff --git a/unittest/testcfg/events/subsystemIdRanges.h b/unittest/testcfg/events/subsystemIdRanges.h new file mode 100644 index 00000000..24eee819 --- /dev/null +++ b/unittest/testcfg/events/subsystemIdRanges.h @@ -0,0 +1,18 @@ +#ifndef CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ +#define CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ + +#include +#include + +/** + * @brief Custom subsystem IDs can be added here + * @details + * Subsystem IDs are used to create unique events. + */ +namespace SUBSYSTEM_ID { +enum: uint8_t { + SUBSYSTEM_ID_START = FW_SUBSYSTEM_ID_RANGE, +}; +} + +#endif /* CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ */ diff --git a/unittest/testcfg/ipc/MissionMessageTypes.cpp b/unittest/testcfg/ipc/MissionMessageTypes.cpp new file mode 100644 index 00000000..d68cb58c --- /dev/null +++ b/unittest/testcfg/ipc/MissionMessageTypes.cpp @@ -0,0 +1,12 @@ +#include "MissionMessageTypes.h" + +#include + +void messagetypes::clearMissionMessage(CommandMessage* message) { + switch(message->getMessageType()) { + default: + break; + } +} + + diff --git a/unittest/testcfg/ipc/MissionMessageTypes.h b/unittest/testcfg/ipc/MissionMessageTypes.h new file mode 100644 index 00000000..832d8e58 --- /dev/null +++ b/unittest/testcfg/ipc/MissionMessageTypes.h @@ -0,0 +1,22 @@ +#ifndef CONFIG_IPC_MISSIONMESSAGETYPES_H_ +#define CONFIG_IPC_MISSIONMESSAGETYPES_H_ + +#include + +class CommandMessage; + +/** + * Custom command messages are specified here. + * Most messages needed to use FSFW are already located in + * + * @param message Generic Command Message + */ +namespace messagetypes{ +enum MESSAGE_TYPE { + MISSION_MESSAGE_TYPE_START = FW_MESSAGES_COUNT, +}; + +void clearMissionMessage(CommandMessage* message); +} + +#endif /* CONFIG_IPC_MISSIONMESSAGETYPES_H_ */ diff --git a/unittest/testcfg/objects/Factory.cpp b/unittest/testcfg/objects/Factory.cpp new file mode 100644 index 00000000..e05b7942 --- /dev/null +++ b/unittest/testcfg/objects/Factory.cpp @@ -0,0 +1,34 @@ +#include "Factory.h" + +#include +#include + +#include +#include + +/** + * @brief Produces system objects. + * @details + * Build tasks by using SystemObject Interface (Interface). + * Header files of all tasks must be included + * Please note that an object has to implement the system object interface + * if the interface validity is checked or retrieved later by using the + * get(object_id) function from the ObjectManagerIF. + * + * Framework objects are created first. + * + * @ingroup init + */ +void Factory::produce(void) { + setStaticFrameworkObjectIds(); + new EventManager(objects::EVENT_MANAGER); + new HealthTable(objects::HEALTH_TABLE); + new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); + +} + +void Factory::setStaticFrameworkObjectIds() { + +} + + diff --git a/unittest/testcfg/objects/Factory.h b/unittest/testcfg/objects/Factory.h new file mode 100644 index 00000000..84f9207e --- /dev/null +++ b/unittest/testcfg/objects/Factory.h @@ -0,0 +1,16 @@ +#ifndef FACTORY_H_ +#define FACTORY_H_ + +#include + +namespace Factory { + /** + * @brief Creates all SystemObject elements which are persistent + * during execution. + */ + void produce(); + void setStaticFrameworkObjectIds(); + +} + +#endif /* FACTORY_H_ */ diff --git a/unittest/testcfg/objects/systemObjectList.h b/unittest/testcfg/objects/systemObjectList.h new file mode 100644 index 00000000..0e034aff --- /dev/null +++ b/unittest/testcfg/objects/systemObjectList.h @@ -0,0 +1,16 @@ +#ifndef HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ +#define HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ + +#include +#include + +// The objects will be instantiated in the ID order +namespace objects { + enum sourceObjects: uint32_t { + /* All addresses between start and end are reserved for the FSFW */ + FSFW_CONFIG_RESERVED_START = PUS_SERVICE_1_VERIFICATION, + FSFW_CONFIG_RESERVED_END = TM_STORE + }; +} + +#endif /* BSP_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ */ diff --git a/unittest/testcfg/pollingsequence/PollingSequenceFactory.cpp b/unittest/testcfg/pollingsequence/PollingSequenceFactory.cpp new file mode 100644 index 00000000..f836a746 --- /dev/null +++ b/unittest/testcfg/pollingsequence/PollingSequenceFactory.cpp @@ -0,0 +1,23 @@ +#include "PollingSequenceFactory.h" + +#include +#include +#include + +ReturnValue_t pst::pollingSequenceInitDefault( + FixedTimeslotTaskIF *thisSequence) { + /* Length of a communication cycle */ + uint32_t length = thisSequence->getPeriodMs(); + + /* Add polling sequence table here */ + + if (thisSequence->checkSequence() == HasReturnvaluesIF::RETURN_OK) { + return HasReturnvaluesIF::RETURN_OK; + } + else { + sif::error << "pst::pollingSequenceInitDefault: Sequence invalid!" + << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } +} + diff --git a/unittest/testcfg/pollingsequence/PollingSequenceFactory.h b/unittest/testcfg/pollingsequence/PollingSequenceFactory.h new file mode 100644 index 00000000..c5d41b7d --- /dev/null +++ b/unittest/testcfg/pollingsequence/PollingSequenceFactory.h @@ -0,0 +1,32 @@ +#ifndef POLLINGSEQUENCEFACTORY_H_ +#define POLLINGSEQUENCEFACTORY_H_ + +#include + +class FixedTimeslotTaskIF; + +/** + * All device handlers are scheduled by adding them into Polling Sequence Tables (PST) + * to satisfy stricter timing requirements of device communication, + * A device handler has four different communication steps: + * 1. DeviceHandlerIF::SEND_WRITE -> Send write via interface + * 2. DeviceHandlerIF::GET_WRITE -> Get confirmation for write + * 3. DeviceHandlerIF::SEND_READ -> Send read request + * 4. DeviceHandlerIF::GET_READ -> Read from interface + * The PST specifies precisely when the respective ComIF functions are called + * during the communication cycle time. + * The task is created using the FixedTimeslotTaskIF, + * which utilises the underlying Operating System Abstraction Layer (OSAL) + * + * @param thisSequence FixedTimeslotTaskIF * object is passed inside the Factory class when creating the PST + * @return + */ +namespace pst { + +/* Default PST */ +ReturnValue_t pollingSequenceInitDefault(FixedTimeslotTaskIF *thisSequence); + + +} + +#endif /* POLLINGSEQUENCEINIT_H_ */ diff --git a/unittest/testcfg/returnvalues/classIds.h b/unittest/testcfg/returnvalues/classIds.h new file mode 100644 index 00000000..606cc60b --- /dev/null +++ b/unittest/testcfg/returnvalues/classIds.h @@ -0,0 +1,16 @@ +#ifndef CONFIG_RETURNVALUES_CLASSIDS_H_ +#define CONFIG_RETURNVALUES_CLASSIDS_H_ + +#include + +/** + * @brief CLASS_ID defintions which are required for custom returnvalues. + */ +namespace CLASS_ID { +enum { + MISSION_CLASS_ID_START = FW_CLASS_ID_COUNT, +}; +} + + +#endif /* CONFIG_RETURNVALUES_CLASSIDS_H_ */ diff --git a/unittest/testcfg/testcfg.mk b/unittest/testcfg/testcfg.mk new file mode 100644 index 00000000..64fa87f6 --- /dev/null +++ b/unittest/testcfg/testcfg.mk @@ -0,0 +1,15 @@ +CXXSRC += $(wildcard $(CURRENTPATH)/cdatapool/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/ipc/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/objects/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/pollingsequence/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/events/*.cpp) + +INCLUDES += $(CURRENTPATH) +INCLUDES += $(CURRENTPATH)/objects +INCLUDES += $(CURRENTPATH)/ipc +INCLUDES += $(CURRENTPATH)/pollingsequence +INCLUDES += $(CURRENTPATH)/returnvalues +INCLUDES += $(CURRENTPATH)/tmtc +INCLUDES += $(CURRENTPATH)/events +INCLUDES += $(CURRENTPATH)/devices +INCLUDES += $(CURRENTPATH)/cdatapool diff --git a/unittest/testcfg/tmtc/apid.h b/unittest/testcfg/tmtc/apid.h new file mode 100644 index 00000000..c0231bca --- /dev/null +++ b/unittest/testcfg/tmtc/apid.h @@ -0,0 +1,18 @@ +#ifndef CONFIG_TMTC_APID_H_ +#define CONFIG_TMTC_APID_H_ + +#include + +/** + * Application Process Definition: entity, uniquely identified by an + * application process ID (APID), capable of generating telemetry source + * packets and receiving telecommand packets. + * + * Chose APID(s) for mission and define it here. + */ +namespace apid { + static const uint16_t DEFAULT_APID = 0x00; +} + + +#endif /* CONFIG_TMTC_APID_H_ */ diff --git a/unittest/testcfg/tmtc/pusIds.h b/unittest/testcfg/tmtc/pusIds.h new file mode 100644 index 00000000..821a9982 --- /dev/null +++ b/unittest/testcfg/tmtc/pusIds.h @@ -0,0 +1,25 @@ +#ifndef CONFIG_TMTC_PUSIDS_HPP_ +#define CONFIG_TMTC_PUSIDS_HPP_ + +#include + +namespace pus { +enum Ids: uint8_t { + PUS_SERVICE_1 = 1, + PUS_SERVICE_2 = 2, + PUS_SERVICE_3 = 3, + PUS_SERVICE_5 = 5, + PUS_SERVICE_6 = 6, + PUS_SERVICE_8 = 8, + PUS_SERVICE_9 = 9, + PUS_SERVICE_11 = 11, + PUS_SERVICE_17 = 17, + PUS_SERVICE_19 = 19, + PUS_SERVICE_20 = 20, + PUS_SERVICE_23 = 23, + PUS_SERVICE_200 = 200, + PUS_SERVICE_201 = 201, +}; +}; + +#endif /* CONFIG_TMTC_PUSIDS_HPP_ */ diff --git a/unittest/tests/action/TestActionHelper.cpp b/unittest/tests/action/TestActionHelper.cpp new file mode 100644 index 00000000..d5b2e467 --- /dev/null +++ b/unittest/tests/action/TestActionHelper.cpp @@ -0,0 +1,106 @@ +//#include "TestActionHelper.h" +//#include +//#include +//#include +//#include "../../core/CatchDefinitions.h" +// +// +//TEST_CASE( "Action Helper" , "[ActionHelper]") { +// ActionHelperOwnerMockBase testDhMock; +// MessageQueueMockBase testMqMock; +// ActionHelper actionHelper = ActionHelper( +// &testDhMock, dynamic_cast(&testMqMock)); +// CommandMessage actionMessage; +// ActionId_t testActionId = 777; +// std::array testParams {1, 2, 3}; +// store_address_t paramAddress; +// StorageManagerIF *ipcStore = tglob::getIpcStoreHandle(); +// ipcStore->addData(¶mAddress, testParams.data(), 3); +// REQUIRE(actionHelper.initialize() == retval::CATCH_OK); +// +// SECTION ("Simple tests") { +// ActionMessage::setCommand(&actionMessage, testActionId, paramAddress); +// CHECK(not testDhMock.executeActionCalled); +// REQUIRE(actionHelper.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); +// 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); +// for(uint8_t i = 0; i<3;i++){ +// REQUIRE(ptr[i] == (i+1)); +// } +// testDhMock.clearBuffer(); +// } +// +// SECTION("Handle failures"){ +// actionMessage.setCommand(1234); +// REQUIRE(actionHelper.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); +// step += 1; +// CHECK(testMqMock.wasMessageSent()); +// CommandMessage testMessage; +// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); +// REQUIRE(testMessage.getParameter() == static_cast(testActionId)); +// uint32_t parameter2 = ((uint32_t)step << 16) | (uint32_t)status; +// REQUIRE(testMessage.getParameter2() == parameter2); +// REQUIRE(ActionMessage::getStep(&testMessage) == step); +// } +// +// SECTION("Handle finish"){ +// CHECK(not testMqMock.wasMessageSent()); +// ReturnValue_t status = 0x9876; +// actionHelper.finish(testMqMock.getId(), testActionId, status); +// CHECK(testMqMock.wasMessageSent()); +// CommandMessage testMessage; +// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::COMPLETION_FAILED)); +// REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId); +// REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast(status)); +// } +// +// SECTION("Handle failed"){ +// store_address_t toLongParamAddress = StorageManagerIF::INVALID_ADDRESS; +// std::array toLongData = {5, 4, 3, 2, 1}; +// 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(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::getStep(&testMessage) == 0); +// REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId); +// } +// +// SECTION("Missing IPC Data"){ +// ActionMessage::setCommand(&actionMessage, testActionId, StorageManagerIF::INVALID_ADDRESS); +// CHECK(not testDhMock.executeActionCalled); +// REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK); +// CommandMessage testMessage; +// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); +// REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast(StorageManagerIF::ILLEGAL_STORAGE_ID)); +// REQUIRE(ActionMessage::getStep(&testMessage) == 0); +// } +// +// +// SECTION("Data Reply"){ +// +// } +//} diff --git a/unittest/tests/action/TestActionHelper.h b/unittest/tests/action/TestActionHelper.h new file mode 100644 index 00000000..9bc93d3e --- /dev/null +++ b/unittest/tests/action/TestActionHelper.h @@ -0,0 +1,131 @@ +//#ifndef UNITTEST_HOSTED_TESTACTIONHELPER_H_ +//#define UNITTEST_HOSTED_TESTACTIONHELPER_H_ +// +//#include +//#include +//#include +//#include +// +// +//class ActionHelperOwnerMockBase: public HasActionsIF { +//public: +// bool getCommandQueueCalled = false; +// bool executeActionCalled = false; +// static const size_t MAX_SIZE = 3; +// uint8_t buffer[MAX_SIZE] = {0, 0, 0}; +// size_t size = 0; +// +// 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 { +// executeActionCalled = true; +// if(size > MAX_SIZE){ +// return 0xAFFE; +// } +// this->size = size; +// memcpy(buffer, data, size); +// return HasReturnvaluesIF::RETURN_OK; +// } +// +// void clearBuffer(){ +// this->size = 0; +// for(size_t i = 0; isize; +// } +// if(ptr != nullptr){ +// *ptr = buffer; +// } +// } +//}; +// +// +//class MessageQueueMockBase: public MessageQueueIF { +//public: +// MessageQueueId_t myQueueId = 0; +// bool defaultDestSet = false; +// bool messageSent = false; +// +// +// +// bool wasMessageSent() { +// bool tempMessageSent = messageSent; +// messageSent = false; +// return tempMessageSent; +// } +// +// virtual ReturnValue_t reply( MessageQueueMessage* message ) { +// messageSent = true; +// lastMessage = (*message); +// return HasReturnvaluesIF::RETURN_OK; +// }; +// virtual ReturnValue_t receiveMessage(MessageQueueMessage* message, +// MessageQueueId_t *receivedFrom) { +// (*message) = lastMessage; +// lastMessage.clear(); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual ReturnValue_t receiveMessage(MessageQueueMessage* message) { +// (*message) = lastMessage; +// lastMessage.clear(); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual ReturnValue_t flush(uint32_t* count) { +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual MessageQueueId_t getLastPartner() const { +// return tconst::testQueueId; +// } +// virtual MessageQueueId_t getId() const { +// return tconst::testQueueId; +// } +// virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, +// MessageQueueMessage* message, MessageQueueId_t sentFrom, +// bool ignoreFault = false ) { +// messageSent = true; +// lastMessage = (*message); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo, +// MessageQueueMessage* message, bool ignoreFault = false ) override { +// messageSent = true; +// lastMessage = (*message); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessage* message, +// MessageQueueId_t sentFrom, bool ignoreFault = false ) { +// messageSent = true; +// lastMessage = (*message); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual ReturnValue_t sendToDefault( MessageQueueMessage* message ) { +// messageSent = true; +// lastMessage = (*message); +// return HasReturnvaluesIF::RETURN_OK; +// } +// virtual void setDefaultDestination(MessageQueueId_t defaultDestination) { +// myQueueId = defaultDestination; +// defaultDestSet = true; +// } +// +// virtual MessageQueueId_t getDefaultDestination() const { +// return myQueueId; +// } +// virtual bool isDefaultDestinationSet() const { +// return defaultDestSet; +// } +//private: +// MessageQueueMessage lastMessage; +// +//}; +// +// +//#endif /* UNITTEST_TESTFW_NEWTESTS_TESTACTIONHELPER_H_ */ diff --git a/unittest/tests/container/RingBufferTest.cpp b/unittest/tests/container/RingBufferTest.cpp new file mode 100644 index 00000000..8b82d407 --- /dev/null +++ b/unittest/tests/container/RingBufferTest.cpp @@ -0,0 +1,327 @@ +#include +#include +#include "../../core/CatchDefinitions.h" + +#include + +TEST_CASE("Ring Buffer Test" , "[RingBufferTest]") { + uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; + SimpleRingBuffer ringBuffer(10, false, 5); + + SECTION("Simple Test") { + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK); + REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_FAILED); + REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 5; i++) { + CHECK(readBuffer[i] == i); + } + REQUIRE(ringBuffer.availableWriteSpace() == 5); + ringBuffer.clear(); + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK); + REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 4; i++) { + CHECK(readBuffer[i] == i); + } + REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK); + REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 9; i++) { + CHECK(readBuffer[i] == i); + } + + } + + SECTION("Get Free Element Test") { + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK); + REQUIRE(ringBuffer.availableWriteSpace() == 1); + REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK); + REQUIRE(ringBuffer.availableWriteSpace() == 9); + uint8_t *testPtr = nullptr; + REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_FAILED); + + + REQUIRE(ringBuffer.writeTillWrap() == 2); + // too many excess bytes. + REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_FAILED); + REQUIRE(ringBuffer.getFreeElement(&testPtr, 5) == retval::CATCH_OK); + REQUIRE(ringBuffer.getExcessBytes() == 3); + std::memcpy(testPtr, testData, 5); + ringBuffer.confirmBytesWritten(5); + REQUIRE(ringBuffer.getAvailableReadData() == 5); + ringBuffer.readData(readBuffer, 5, true); + for(uint8_t i = 0; i< 5; i++) { + CHECK(readBuffer[i] == i); + } + } + + SECTION("Read Remaining Test") { + REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK); + REQUIRE(ringBuffer.getAvailableReadData() == 3); + REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED); + size_t trueSize = 0; + REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK); + REQUIRE(trueSize == 3); + for(uint8_t i = 0; i< 3; i++) { + CHECK(readBuffer[i] == i); + } + trueSize = 0; + REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED); + REQUIRE(trueSize == 0); + REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK); + REQUIRE(trueSize == 3); + } +} + +TEST_CASE("Ring Buffer Test2" , "[RingBufferTest2]") { + uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; + uint8_t* newBuffer = new uint8_t[10]; + SimpleRingBuffer ringBuffer(newBuffer, 10, true, 5); + + SECTION("Simple Test") { + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK); + REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 5; i++) { + CHECK(readBuffer[i] == i); + } + REQUIRE(ringBuffer.availableWriteSpace() == 5); + ringBuffer.clear(); + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK); + REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 4; i++) { + CHECK(readBuffer[i] == i); + } + REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK); + REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 9; i++) { + CHECK(readBuffer[i] == i); + } + + } + + SECTION("Get Free Element Test") { + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK); + REQUIRE(ringBuffer.availableWriteSpace() == 1); + REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK); + REQUIRE(ringBuffer.availableWriteSpace() == 9); + uint8_t *testPtr = nullptr; + REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_FAILED); + + + REQUIRE(ringBuffer.writeTillWrap() == 2); + // too many excess bytes. + REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_FAILED); + REQUIRE(ringBuffer.getFreeElement(&testPtr, 5) == retval::CATCH_OK); + REQUIRE(ringBuffer.getExcessBytes() == 3); + std::memcpy(testPtr, testData, 5); + ringBuffer.confirmBytesWritten(5); + REQUIRE(ringBuffer.getAvailableReadData() == 5); + ringBuffer.readData(readBuffer, 5, true); + for(uint8_t i = 0; i< 5; i++) { + CHECK(readBuffer[i] == i); + } + } + + SECTION("Read Remaining Test") { + REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK); + REQUIRE(ringBuffer.getAvailableReadData() == 3); + REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED); + size_t trueSize = 0; + REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK); + REQUIRE(trueSize == 3); + for(uint8_t i = 0; i< 3; i++) { + CHECK(readBuffer[i] == i); + } + trueSize = 0; + REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED); + REQUIRE(trueSize == 0); + REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK); + REQUIRE(trueSize == 3); + } + + SECTION("Overflow"){ + REQUIRE(ringBuffer.availableWriteSpace()==9); + //Writing more than the buffer is large, technically thats allowed + //But it is senseless and has undesired impact on read call + REQUIRE(ringBuffer.writeData(testData, 13) == retval::CATCH_OK); + REQUIRE(ringBuffer.getAvailableReadData()==3); + ringBuffer.clear(); + uint8_t * ptr = nullptr; + REQUIRE(ringBuffer.getFreeElement(&ptr, 13) == retval::CATCH_OK); + REQUIRE(ptr != nullptr); + memcpy(ptr, testData, 13); + ringBuffer.confirmBytesWritten(13); + REQUIRE(ringBuffer.getAvailableReadData()==3); + REQUIRE(ringBuffer.readData(readBuffer, 3, true)== retval::CATCH_OK); + for(auto i =0;i<3;i++){ + REQUIRE(readBuffer[i] == testData[i+10]); + } + } +} + +TEST_CASE("Ring Buffer Test3" , "[RingBufferTest3]") { + uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; + uint8_t* newBuffer = new uint8_t[10]; + SimpleRingBuffer ringBuffer(newBuffer, 10, true, 15); + + SECTION("Simple Test") { + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK); + REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 5; i++) { + CHECK(readBuffer[i] == i); + } + REQUIRE(ringBuffer.availableWriteSpace() == 5); + ringBuffer.clear(); + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK); + REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 4; i++) { + CHECK(readBuffer[i] == i); + } + REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK); + REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 9; i++) { + CHECK(readBuffer[i] == i); + } + + } + + SECTION("Get Free Element Test") { + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK); + REQUIRE(ringBuffer.availableWriteSpace() == 1); + REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK); + REQUIRE(ringBuffer.availableWriteSpace() == 9); + uint8_t *testPtr = nullptr; + REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_OK); + REQUIRE(ringBuffer.getExcessBytes() == 8); + + REQUIRE(ringBuffer.writeTillWrap() == 2); + // too many excess bytes. + REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_FAILED); + // Less Execss bytes overwrites before + REQUIRE(ringBuffer.getFreeElement(&testPtr, 3) == retval::CATCH_OK); + REQUIRE(ringBuffer.getExcessBytes() == 1); + std::memcpy(testPtr, testData, 3); + ringBuffer.confirmBytesWritten(3); + REQUIRE(ringBuffer.getAvailableReadData() == 3); + ringBuffer.readData(readBuffer, 3, true); + for(uint8_t i = 0; i< 3; i++) { + CHECK(readBuffer[i] == i); + } + } + + SECTION("Read Remaining Test") { + REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK); + REQUIRE(ringBuffer.getAvailableReadData() == 3); + REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED); + size_t trueSize = 0; + REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK); + REQUIRE(trueSize == 3); + for(uint8_t i = 0; i< 3; i++) { + CHECK(readBuffer[i] == i); + } + trueSize = 0; + REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED); + REQUIRE(trueSize == 0); + REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK); + REQUIRE(trueSize == 3); + } + + SECTION("Overflow"){ + REQUIRE(ringBuffer.availableWriteSpace()==9); + //Writing more than the buffer is large, technically thats allowed + //But it is senseless and has undesired impact on read call + REQUIRE(ringBuffer.writeData(testData, 13) == retval::CATCH_OK); + REQUIRE(ringBuffer.getAvailableReadData()==3); + ringBuffer.clear(); + uint8_t * ptr = nullptr; + REQUIRE(ringBuffer.getFreeElement(&ptr, 13) == retval::CATCH_OK); + REQUIRE(ptr != nullptr); + memcpy(ptr, testData, 13); + ringBuffer.confirmBytesWritten(13); + REQUIRE(ringBuffer.getAvailableReadData()==3); + REQUIRE(ringBuffer.readData(readBuffer, 3, true)== retval::CATCH_OK); + for(auto i =0;i<3;i++){ + REQUIRE(readBuffer[i] == testData[i+10]); + } + } +} + +TEST_CASE("Ring Buffer Test4" , "[RingBufferTest4]") { + uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; + SimpleRingBuffer ringBuffer(10, false, 15); + + SECTION("Simple Test") { + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK); + REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_FAILED); + REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 5; i++) { + CHECK(readBuffer[i] == i); + } + REQUIRE(ringBuffer.availableWriteSpace() == 5); + ringBuffer.clear(); + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK); + REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 4; i++) { + CHECK(readBuffer[i] == i); + } + REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK); + REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK); + for(uint8_t i = 0; i< 9; i++) { + CHECK(readBuffer[i] == i); + } + + } + + SECTION("Get Free Element Test") { + REQUIRE(ringBuffer.availableWriteSpace() == 9); + REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK); + REQUIRE(ringBuffer.availableWriteSpace() == 1); + REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK); + REQUIRE(ringBuffer.availableWriteSpace() == 9); + uint8_t *testPtr = nullptr; + REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_FAILED); + + + REQUIRE(ringBuffer.writeTillWrap() == 2); + REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_OK); + REQUIRE(ringBuffer.getFreeElement(&testPtr, 5) == retval::CATCH_OK); + REQUIRE(ringBuffer.getExcessBytes() == 3); + std::memcpy(testPtr, testData, 5); + ringBuffer.confirmBytesWritten(5); + REQUIRE(ringBuffer.getAvailableReadData() == 5); + ringBuffer.readData(readBuffer, 5, true); + for(uint8_t i = 0; i< 5; i++) { + CHECK(readBuffer[i] == i); + } + } + + SECTION("Read Remaining Test") { + REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK); + REQUIRE(ringBuffer.getAvailableReadData() == 3); + REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED); + size_t trueSize = 0; + REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK); + REQUIRE(trueSize == 3); + for(uint8_t i = 0; i< 3; i++) { + CHECK(readBuffer[i] == i); + } + trueSize = 0; + REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED); + REQUIRE(trueSize == 0); + REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK); + REQUIRE(trueSize == 3); + } +} diff --git a/unittest/tests/container/TestArrayList.cpp b/unittest/tests/container/TestArrayList.cpp new file mode 100644 index 00000000..914188cb --- /dev/null +++ b/unittest/tests/container/TestArrayList.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include "../../core/CatchDefinitions.h" + +/** + * @brief Array List test + */ +TEST_CASE("Array List" , "[ArrayListTest]") { + //perform set-up here + ArrayList list(20); + struct TestClass{ + public: + TestClass(){}; + TestClass(uint32_t number1, uint64_t number2): + number1(number1), number2(number2){}; + uint32_t number1 = -1; + uint64_t number2 = -1; + bool operator==(const TestClass& other){ + return ((this->number1 == other.number1) and (this->number2 == other.number2)); + }; + }; + ArrayList complexList(20); + SECTION("SimpleTest") { + REQUIRE(list.maxSize()==20); + REQUIRE(list.size == 0); + REQUIRE(list.insert(10) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(list[0] == 10); + REQUIRE(list.front() != nullptr); + REQUIRE((*list.front()) == 10); + REQUIRE(list.back() != nullptr); + REQUIRE((*list.back()) == 10); + // Need to test the const version of back as well + const uint16_t* number = const_cast*>(&list)->back(); + REQUIRE(*number == 10); + list.clear(); + REQUIRE(list.size == 0); + } + SECTION("Fill and check"){ + //This is an invalid element but its not a nullptr + REQUIRE(list.back() != nullptr); + for (auto i =0; i < 20; i++){ + REQUIRE(list.insert(i) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + REQUIRE(list.insert(20) == static_cast(ArrayList::FULL)); + ArrayList::Iterator it = list.begin(); + REQUIRE((*it) == 0); + it++; + REQUIRE((*it) == 1); + it--; + REQUIRE((*it) == 0); + it++; + for(auto it2 = list.begin(); it2!=list.end(); it2++){ + if (it == it2){ + REQUIRE((*it) == (*it2)); + break; + }else{ + REQUIRE((*it2) == 0); + REQUIRE(it2 != it); + } + } + } + SECTION("Const Iterator"){ + ArrayList::Iterator it = list.begin(); + for (auto i =0; i < 10; i++){ + REQUIRE(list.insert(i) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + it++; + const uint16_t* number = it.value; + REQUIRE(*number == 1); + } + + SECTION("Const Iterator"){ + ArrayList::Iterator it = complexList.begin(); + for (auto i =0; i < 10; i++){ + REQUIRE(complexList.insert(TestClass(i, i+1)) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + it++; + const TestClass* secondTest = it.value; + bool compare = TestClass(1, 2) == *secondTest; + REQUIRE(compare); + it++; + REQUIRE(it->number1 == 2); + REQUIRE(it->number2 == 3); + const ArrayList::Iterator it4(&(complexList[2])); + REQUIRE(it4->number1 == 2); + REQUIRE((*it4).number2 == 3); + REQUIRE(complexList.remaining()==10); + } +} diff --git a/unittest/tests/container/TestDynamicFifo.cpp b/unittest/tests/container/TestDynamicFifo.cpp new file mode 100644 index 00000000..6c9b7415 --- /dev/null +++ b/unittest/tests/container/TestDynamicFifo.cpp @@ -0,0 +1,149 @@ + +#include +#include +#include + +#include +#include + +TEST_CASE( "Dynamic Fifo Tests", "[TestDynamicFifo]") { + INFO("Dynamic Fifo Tests"); + struct Test{ + uint64_t number1; + uint32_t number2; + uint8_t number3; + bool operator==(struct Test& other){ + if ((other.number1 == this->number1) and + (other.number1 == this->number1) and + (other.number1 == this->number1)){ + return true; + } + return false; + } + }; + DynamicFIFO fifo(3); + std::vector list; + + struct Test structOne({UINT64_MAX, UINT32_MAX, UINT8_MAX}); + struct Test structTwo({0, 1, 2}); + struct Test structThree({42, 43, 44}); + list.push_back(structThree); + list.push_back(structTwo); + list.push_back(structOne); + SECTION("Insert, retrieval test"){ + REQUIRE(fifo.getMaxCapacity()==3); + REQUIRE(fifo.size()==0); + REQUIRE(fifo.empty()); + REQUIRE(not fifo.full()); + + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(FIFOBase::FULL)); + + struct Test testptr; + REQUIRE(fifo.peek(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + bool equal = testptr == structOne; + REQUIRE(equal); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + for(size_t i=2;i<3;i--){ + testptr.number1 = 0; + testptr.number2 = 0; + testptr.number3 = 0; + REQUIRE(fifo.retrieve(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + equal = testptr == list[i]; + REQUIRE(equal); + REQUIRE(fifo.size()==i); + } + testptr.number1 = 0; + testptr.number2 = 0; + testptr.number3 = 0; + REQUIRE(fifo.retrieve(&testptr)==static_cast(FIFOBase::EMPTY)); + REQUIRE(fifo.peek(&testptr)==static_cast(FIFOBase::EMPTY)); + REQUIRE(not fifo.full()); + REQUIRE(fifo.empty()); + REQUIRE(fifo.pop()==static_cast(FIFOBase::EMPTY)); + + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==1); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==2); + REQUIRE(fifo.pop()==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==1); + testptr.number1 = 0; + testptr.number2 = 0; + testptr.number3 = 0; + REQUIRE(fifo.peek(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + equal = testptr == structTwo; + REQUIRE(equal); + REQUIRE(fifo.pop()==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==0); + REQUIRE(fifo.empty()); + //struct Test* ptr = nullptr; + //REQUIRE(fifo.retrieve(ptr) == static_cast(HasReturnvaluesIF::RETURN_FAILED)); + //REQUIRE(fifo.peek(ptr) == static_cast(HasReturnvaluesIF::RETURN_FAILED)); + }; + SECTION("Copy Test"){ + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + DynamicFIFO fifo2(fifo); + REQUIRE(fifo2.size()==3); + REQUIRE(fifo2.full()); + REQUIRE(not fifo2.empty()); + + }; + + SECTION("Assignment Test"){ + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + DynamicFIFO fifo2(6); + fifo2 = fifo; + REQUIRE(fifo2.size()==3); + REQUIRE(fifo2.full()); + REQUIRE(not fifo2.empty()); + for(size_t i=2;i<3;i--){ + struct Test testptr = {0, 0, 0}; + REQUIRE(fifo2.retrieve(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + bool equal = testptr == list[i]; + REQUIRE(equal); + REQUIRE(fifo2.size()==i); + } + + }; + + SECTION("Assignment Test Smaller"){ + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + DynamicFIFO fifo2(2); + fifo2 = fifo; + REQUIRE(fifo2.size()==3); + REQUIRE(fifo2.full()); + REQUIRE(not fifo2.empty()); + for(size_t i=2;i<3;i--){ + struct Test testptr = {0, 0, 0}; + REQUIRE(fifo2.retrieve(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + bool equal = testptr == list[i]; + REQUIRE(equal); + REQUIRE(fifo2.size()==i); + } + }; + +}; diff --git a/unittest/tests/container/TestFifo.cpp b/unittest/tests/container/TestFifo.cpp new file mode 100644 index 00000000..3775f424 --- /dev/null +++ b/unittest/tests/container/TestFifo.cpp @@ -0,0 +1,138 @@ + +#include +#include +#include + +#include +#include "../../core/CatchDefinitions.h" + +TEST_CASE( "Static Fifo Tests", "[TestFifo]") { + INFO("Fifo Tests"); + struct Test{ + uint64_t number1; + uint32_t number2; + uint8_t number3; + bool operator==(struct Test& other){ + if ((other.number1 == this->number1) and + (other.number1 == this->number1) and + (other.number1 == this->number1)){ + return true; + } + return false; + } + }; + FIFO fifo; + std::vector list; + + struct Test structOne({UINT64_MAX, UINT32_MAX, UINT8_MAX}); + struct Test structTwo({0, 1, 2}); + struct Test structThree({42, 43, 44}); + list.push_back(structThree); + list.push_back(structTwo); + list.push_back(structOne); + SECTION("Insert, retrieval test"){ + REQUIRE(fifo.getMaxCapacity()==3); + REQUIRE(fifo.size()==0); + REQUIRE(fifo.empty()); + REQUIRE(not fifo.full()); + + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(FIFOBase::FULL)); + + struct Test testptr; + REQUIRE(fifo.peek(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + bool equal = testptr == structOne; + REQUIRE(equal); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + for(size_t i=2;i<3;i--){ + testptr.number1 = 0; + testptr.number2 = 0; + testptr.number3 = 0; + REQUIRE(fifo.retrieve(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + equal = testptr == list[i]; + REQUIRE(equal); + REQUIRE(fifo.size()==i); + } + testptr.number1 = 0; + testptr.number2 = 0; + testptr.number3 = 0; + REQUIRE(fifo.retrieve(&testptr)==static_cast(FIFOBase::EMPTY)); + REQUIRE(fifo.peek(&testptr)==static_cast(FIFOBase::EMPTY)); + REQUIRE(not fifo.full()); + REQUIRE(fifo.empty()); + REQUIRE(fifo.pop()==static_cast(FIFOBase::EMPTY)); + + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==1); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==2); + REQUIRE(fifo.pop()==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==1); + testptr.number1 = 0; + testptr.number2 = 0; + testptr.number3 = 0; + + // Test that retrieve and peek will not cause a nullptr dereference + struct Test* ptr = nullptr; + REQUIRE(fifo.retrieve(ptr) == static_cast(HasReturnvaluesIF::RETURN_FAILED)); + REQUIRE(fifo.peek(ptr) == static_cast(HasReturnvaluesIF::RETURN_FAILED)); + + + REQUIRE(fifo.peek(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + equal = testptr == structTwo; + REQUIRE(equal); + REQUIRE(fifo.pop()==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==0); + REQUIRE(fifo.empty()); + + + }; + SECTION("Copy Test"){ + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + FIFO fifo2(fifo); + REQUIRE(fifo2.size()==3); + REQUIRE(fifo2.full()); + REQUIRE(not fifo2.empty()); + for(size_t i=2;i<3;i--){ + struct Test testptr = {0, 0, 0}; + REQUIRE(fifo2.retrieve(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + bool equal = testptr == list[i]; + REQUIRE(equal); + REQUIRE(fifo2.size()==i); + } + + }; + + SECTION("Assignment Test"){ + REQUIRE(fifo.insert(structOne)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structTwo)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.insert(structThree)==static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(fifo.size()==3); + REQUIRE(fifo.full()); + REQUIRE(not fifo.empty()); + + FIFO fifo2; + fifo2 = fifo; + REQUIRE(fifo2.size()==3); + REQUIRE(fifo2.full()); + REQUIRE(not fifo2.empty()); + for(size_t i=2;i<3;i--){ + struct Test testptr = {0, 0, 0}; + REQUIRE(fifo2.retrieve(&testptr)==static_cast(HasReturnvaluesIF::RETURN_OK)); + bool equal = testptr == list[i]; + REQUIRE(equal); + REQUIRE(fifo2.size()==i); + } + }; +}; diff --git a/unittest/tests/container/TestFixedArrayList.cpp b/unittest/tests/container/TestFixedArrayList.cpp new file mode 100644 index 00000000..737932e3 --- /dev/null +++ b/unittest/tests/container/TestFixedArrayList.cpp @@ -0,0 +1,42 @@ +#include "../../core/CatchDefinitions.h" + +#include +#include + +#include + + +TEST_CASE( "FixedArrayList Tests", "[TestFixedArrayList]") { + INFO("FixedArrayList Tests"); + using testList = FixedArrayList; + testList list; + REQUIRE(list.size==0); + REQUIRE(list.insert(10) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(list.size==1); + REQUIRE(list.maxSize()==260); + SECTION("Copy Constructor"){ + testList list2(list); + REQUIRE(list2.size==1); + REQUIRE(list2[0] == 10); + REQUIRE(list.maxSize()==260); + }; + SECTION("Assignment copy"){ + testList list2; + REQUIRE(list2.size==0); + list2 = list; + REQUIRE(list2.size==1); + REQUIRE(list2[0] == 10); + REQUIRE(list.maxSize()==260); + }; + SECTION("Fill"){ + for(auto i=1;i<260;i++){ + REQUIRE(list.insert(i) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + REQUIRE(list.insert(260) == static_cast(ArrayList::FULL)); + list.clear(); + REQUIRE(list.size == 0); + } +} + + + diff --git a/unittest/tests/container/TestFixedMap.cpp b/unittest/tests/container/TestFixedMap.cpp new file mode 100644 index 00000000..079062f0 --- /dev/null +++ b/unittest/tests/container/TestFixedMap.cpp @@ -0,0 +1,172 @@ +#include +#include + +#include +#include "../../core/CatchDefinitions.h" + +template class FixedMap; + +TEST_CASE( "FixedMap Tests", "[TestFixedMap]") { + INFO("FixedMap Tests"); + + FixedMap map(30); + REQUIRE(map.size() == 0); + REQUIRE(map.maxSize() == 30); + REQUIRE(map.getSerializedSize() == sizeof(uint32_t)); + REQUIRE(map.empty()); + REQUIRE(not map.full()); + + SECTION("Fill and erase"){ + for (uint16_t i=0;i<30;i++){ + REQUIRE(map.insert(std::make_pair(i, i+1))== static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.exists(i) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(i)->second==i+1); + REQUIRE(not map.empty()); + } + REQUIRE(map.insert(0, 0) == static_cast(FixedMap::KEY_ALREADY_EXISTS)); + REQUIRE(map.insert(31, 0) == static_cast(FixedMap::MAP_FULL)); + REQUIRE(map.exists(31) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 30); + REQUIRE(map.full()); + { + uint16_t* ptr; + REQUIRE(map.find(5,&ptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(*ptr == 6); + REQUIRE(*(map.findValue(6)) == 7); + REQUIRE(map.find(31,&ptr) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + } + + REQUIRE(map.getSerializedSize() == (sizeof(uint32_t)+ 30*(sizeof(uint32_t) + sizeof(uint16_t)))); + REQUIRE(map.erase(2) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.erase(31) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.exists(2) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 29); + + for (auto element: map){ + if (element.first == 5){ + REQUIRE(element.second == 6); + } + } + + for (FixedMap::Iterator it = map.begin(); it != map.end(); it++){ + REQUIRE(it->second == it->first + 1); + REQUIRE((*it).second == (*it).first + 1); + it->second = it->second + 1; + REQUIRE(it->second == it->first + 2); + } + + for (FixedMap::Iterator it = map.begin(); it != map.end(); it++){ + REQUIRE(map.erase(&it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + + REQUIRE(map.size() == 0); + + for (FixedMap::Iterator it = map.begin(); it != map.end(); it++){ + // This line should never executed if begin and end is correct + FAIL("Should never be reached, Iterators invalid"); + } + }; + + SECTION("Insert variants"){ + FixedMap::Iterator it = map.end(); + REQUIRE(map.insert(36, 37, &it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(it->first == 36); + REQUIRE(it->second == 37); + REQUIRE(map.size() == 1); + REQUIRE(map.insert(37, 38, nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(37)->second == 38); + REQUIRE(map.size() == 2); + REQUIRE(map.insert(37, 24, nullptr) == static_cast(FixedMap::KEY_ALREADY_EXISTS)); + REQUIRE(map.find(37)->second != 24); + REQUIRE(map.size() == 2); + }; + SECTION("Serialize and DeSerialize") { + REQUIRE(map.insert(36, 37, nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.insert(37, 38, nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + uint8_t buffer[sizeof(uint32_t) + + 2 * (sizeof(uint32_t) + sizeof(uint16_t))]; + REQUIRE( + map.getSerializedSize() + == (sizeof(uint32_t) + + 2 * (sizeof(uint32_t) + sizeof(uint16_t)))); + uint8_t *loc_ptr = buffer; + size_t size = 0; + REQUIRE( + map.serialize(&loc_ptr, &size, 10, SerializeIF::Endianness::BIG) + == static_cast(SerializeIF::BUFFER_TOO_SHORT)); + loc_ptr = buffer; + size = 0; + REQUIRE( + map.serialize(&loc_ptr, &size, + sizeof(uint32_t) + + 2 * (sizeof(uint32_t) + sizeof(uint16_t)), + SerializeIF::Endianness::BIG) + == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(size == 16); + + uint32_t internal_size = 0; + const uint8_t *ptr2 = buffer; + REQUIRE( + SerializeAdapter::deSerialize(&internal_size, &ptr2, &size, + SerializeIF::Endianness::BIG) + == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(internal_size == 2); + for (uint8_t i = 36; i < 38; i++) { + uint32_t first_element = 0; + REQUIRE( + SerializeAdapter::deSerialize(&first_element, &ptr2, &size, + SerializeIF::Endianness::BIG) + == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(first_element == i); + uint16_t second_element = 0; + REQUIRE( + SerializeAdapter::deSerialize(&second_element, &ptr2, &size, + SerializeIF::Endianness::BIG) + == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(second_element == i + 1); + } + REQUIRE(size == 0); + map.clear(); + const uint8_t* constPtr = buffer; + size = 16; + REQUIRE(map.size() == 0); + REQUIRE(map.deSerialize(&constPtr, &size, + SerializeIF::Endianness::BIG) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.size() == 2); + REQUIRE(map.find(36)->second == 37); + for(auto& element: map){ + REQUIRE((element.first+1) == element.second); + } + }; + + + SECTION("Failed erase and deSerialize"){ + FixedMap::Iterator it; + std::pair pair = std::make_pair(44, 43); + it = FixedMap::Iterator(&pair); + REQUIRE(map.erase(&it) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.find(45) == map.end()); + size_t toLargeMap = 100; + const uint8_t* ptr = reinterpret_cast(&toLargeMap); + size_t size = sizeof(size_t); + REQUIRE(map.deSerialize(&ptr, &size, SerializeIF::Endianness::BIG) == + static_cast(SerializeIF::TOO_MANY_ELEMENTS)); + }; + SECTION("Little Endianess"){ + map.clear(); + map.insert(10,20, nullptr); + uint8_t newBuffer[sizeof(uint32_t)+ 1*(sizeof(uint32_t) + sizeof(uint16_t))]; + uint8_t* ptr = newBuffer; + size_t size = 0; + size_t max_size = sizeof(uint32_t)+ 1*(sizeof(uint32_t) + sizeof(uint16_t)); + REQUIRE(map.serialize(&ptr, &size, max_size, + SerializeIF::Endianness::LITTLE) == static_cast(HasReturnvaluesIF::RETURN_OK)); + map.clear(); + REQUIRE(map.size()==0); + const uint8_t* ptr2 = newBuffer; + REQUIRE(map.deSerialize(&ptr2, &size, + SerializeIF::Endianness::LITTLE) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.size()==1); + REQUIRE(map.find(10)->second == 20); + }; +} diff --git a/unittest/tests/container/TestFixedOrderedMultimap.cpp b/unittest/tests/container/TestFixedOrderedMultimap.cpp new file mode 100644 index 00000000..95194cb5 --- /dev/null +++ b/unittest/tests/container/TestFixedOrderedMultimap.cpp @@ -0,0 +1,203 @@ +#include +#include + +#include +#include "../../core/CatchDefinitions.h" + +TEST_CASE( "FixedOrderedMultimap Tests", "[TestFixedOrderedMultimap]") { + INFO("FixedOrderedMultimap Tests"); + + FixedOrderedMultimap map(30); + REQUIRE(map.size() == 0); + REQUIRE(map.maxSize() == 30); + + SECTION("Test insert, find, exists"){ + for (uint16_t i=0;i<30;i++){ + REQUIRE(map.insert(std::make_pair(i, i+1))== static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.exists(i) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(i)->second==i+1); + } + REQUIRE(map.insert(0, 0) == static_cast(FixedOrderedMultimap::MAP_FULL)); + REQUIRE(map.exists(31) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 30); + { + uint16_t* ptr; + REQUIRE(map.find(5,&ptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(*ptr == 6); + REQUIRE(map.find(31,&ptr) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + } + REQUIRE(map.erase(2) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.erase(31) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.exists(2) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 29); + + for (auto element: map){ + if (element.first == 5){ + REQUIRE(element.second == 6); + } + } + + for (FixedOrderedMultimap::Iterator it = map.begin(); it != map.end(); it++){ + REQUIRE(it->second == it->first + 1); + REQUIRE((*it).second == (*it).first + 1); + it->second = it->second + 1; + REQUIRE(it->second == it->first + 2); + } + + { + FixedOrderedMultimap::Iterator it = map.begin(); + while(it != map.end()){ + REQUIRE(map.erase(&it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + REQUIRE(map.size() == 0); + } + + for (FixedOrderedMultimap::Iterator it = map.begin(); it != map.end(); it++){ + // This line should never executed if begin and end is correct + FAIL("Should never be reached, Iterators invalid"); + } + }; + + SECTION("Test different insert variants") + { + FixedOrderedMultimap::Iterator it = map.end(); + REQUIRE(map.insert(36, 37, &it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(it->first == 36); + REQUIRE(it->second == 37); + REQUIRE(map.size() == 1); + REQUIRE(map.insert(37, 38, nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(37)->second == 38); + REQUIRE(map.size() == 2); + REQUIRE(map.insert(37, 24, nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(37)->second == 38); + REQUIRE(map.insert(0, 1, nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.find(0)->second == 1); + REQUIRE(map.size() == 4); + map.clear(); + REQUIRE(map.size() == 0); + } + SECTION("Test different erase and find with no entries"){ + FixedOrderedMultimap::Iterator it; + it = map.end(); + REQUIRE(map.erase(&it) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.find(1)== map.end()); + } +} + +TEST_CASE( "FixedOrderedMultimap Non Trivial Type", "[TestFixedOrderedMultimapNonTrivial]") { + INFO("FixedOrderedMultimap Non Trivial Type"); + + class TestClass{ + public: + TestClass(){}; + TestClass(uint32_t number1, uint64_t number2): + number1(number1),number2(number2){}; + ~TestClass(){}; + + bool operator==(const TestClass& lhs){ + return ((this->number1 == lhs.number1) and (this->number2 == lhs.number2)); + } + bool operator!=(const TestClass& lhs){ + return not(this->operator ==(lhs)); + } + + TestClass(const TestClass& other){ + this->number1 = other.number1; + this->number2 = other.number2; + }; + TestClass& operator=(const TestClass& other){ + this->number1 = other.number1; + this->number2 = other.number2; + return *this; + }; + + private: + uint32_t number1 = 0; + uint64_t number2 = 5; + }; + FixedOrderedMultimap map(30); + REQUIRE(map.size() == 0); + REQUIRE(map.maxSize() == 30); + + SECTION("Test insert, find, exists"){ + for (uint16_t i=0;i<30;i++){ + REQUIRE(map.insert(std::make_pair(i, TestClass(i+1,i)))== static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.exists(i) == static_cast(HasReturnvaluesIF::RETURN_OK)); + bool compare = map.find(i)->second == TestClass(i+1,i); + REQUIRE(compare); + } + REQUIRE(map.insert(0, TestClass()) == static_cast(FixedOrderedMultimap::MAP_FULL)); + REQUIRE(map.exists(31) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 30); + { + TestClass* ptr = nullptr; + REQUIRE(map.find(5,&ptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + bool compare = *ptr == TestClass(6, 5); + REQUIRE(compare); + REQUIRE(map.find(31,&ptr) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + } + REQUIRE(map.erase(2) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(map.erase(31) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.exists(2) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.size() == 29); + + for (auto element: map){ + if (element.first == 5){ + bool compare = element.second == TestClass(6, 5); + REQUIRE(compare); + } + } + + for (FixedOrderedMultimap::Iterator it = map.begin(); it != map.end(); it++){ + bool compare = it->second == TestClass(it->first + 1, it->first); + REQUIRE(compare); + compare = (*it).second == TestClass((*it).first + 1, (*it).first); + REQUIRE(compare); + it->second = TestClass(it->first + 2, it->first); + compare = it->second == TestClass(it->first + 2, it->first); + REQUIRE(compare); + } + + { + FixedOrderedMultimap::Iterator it = map.begin(); + while(it != map.end()){ + REQUIRE(map.erase(&it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + } + REQUIRE(map.size() == 0); + } + + for (FixedOrderedMultimap::Iterator it = map.begin(); it != map.end(); it++){ + // This line should never executed if begin and end is correct + FAIL("Should never be reached, Iterators invalid"); + } + }; + + SECTION("Test different insert variants") + { + FixedOrderedMultimap::Iterator it = map.end(); + REQUIRE(map.insert(36, TestClass(37, 36), &it) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(it->first == 36); + bool compare = it->second == TestClass(37, 36); + REQUIRE(compare); + REQUIRE(map.size() == 1); + REQUIRE(map.insert(37, TestClass(38, 37), nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + compare = map.find(37)->second == TestClass(38, 37); + REQUIRE(compare); + REQUIRE(map.size() == 2); + REQUIRE(map.insert(37, TestClass(24, 37), nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + compare = map.find(37)->second == TestClass(38, 37); + REQUIRE(compare); + REQUIRE(map.insert(0, TestClass(1, 0), nullptr) == static_cast(HasReturnvaluesIF::RETURN_OK)); + compare = map.find(0)->second == TestClass(1, 0); + REQUIRE(compare); + REQUIRE(map.size() == 4); + map.clear(); + REQUIRE(map.size() == 0); + } + SECTION("Test different erase and find with no entries"){ + FixedOrderedMultimap::Iterator it; + it = map.end(); + REQUIRE(map.erase(&it) == static_cast(FixedOrderedMultimap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.find(1)== map.end()); + } +} diff --git a/unittest/tests/container/TestPlacementFactory.cpp b/unittest/tests/container/TestPlacementFactory.cpp new file mode 100644 index 00000000..5edbb9d2 --- /dev/null +++ b/unittest/tests/container/TestPlacementFactory.cpp @@ -0,0 +1,45 @@ +//#include +//#include +//#include +//#include +// +//#include +//#include "../../core/CatchDefinitions.h" +// +//TEST_CASE( "PlacementFactory Tests", "[TestPlacementFactory]") { +// INFO("PlacementFactory Tests"); +// +// const uint16_t element_sizes[3] = {sizeof(uint16_t), sizeof(uint32_t), sizeof(uint64_t)}; +// const uint16_t n_elements[3] = {1, 1, 1}; +// LocalPool<3> storagePool(0x1, element_sizes, n_elements, false, true); +// PlacementFactory factory(&storagePool); +// +// SECTION("Pool overload"){ +// store_address_t address; +// uint8_t* ptr = nullptr; +// REQUIRE(storagePool.getFreeElement(&address, sizeof(ArrayList), &ptr) +// == static_cast(StorageManagerIF::DATA_TOO_LARGE)); +// ArrayList* list2 = factory.generate >(80); +// REQUIRE(list2 == nullptr); +// } +// +// SECTION("Test generate and destroy"){ +// uint64_t* number = factory.generate(32000); +// REQUIRE(number != nullptr); +// REQUIRE(*number == 32000); +// store_address_t address; +// uint8_t* ptr = nullptr; +// REQUIRE(storagePool.getFreeElement(&address, sizeof(uint64_t), &ptr) +// == static_cast(StorageManagerIF::DATA_TOO_LARGE)); +// uint64_t* number2 = factory.generate(12345); +// REQUIRE(number2 == nullptr); +// REQUIRE(factory.destroy(number) == static_cast(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(storagePool.getFreeElement(&address, sizeof(uint64_t), &ptr) +// == static_cast(HasReturnvaluesIF::RETURN_OK)); +// REQUIRE(storagePool.deleteData(address) == static_cast(HasReturnvaluesIF::RETURN_OK)); +// +// //Check that PlacementFactory checks for nullptr +// ptr = nullptr; +// REQUIRE(factory.destroy(ptr) == static_cast(HasReturnvaluesIF::RETURN_FAILED)); +// } +//} diff --git a/unittest/tests/osal/TestMessageQueue.cpp b/unittest/tests/osal/TestMessageQueue.cpp new file mode 100644 index 00000000..8e59fa08 --- /dev/null +++ b/unittest/tests/osal/TestMessageQueue.cpp @@ -0,0 +1,40 @@ +#include +#include +#include "catch.hpp" +#include +#include "core/CatchDefinitions.h" + +TEST_CASE("MessageQueue Basic Test","[TestMq]") { + MessageQueueIF* testSenderMq = + QueueFactory::instance()->createMessageQueue(1); + MessageQueueId_t testSenderMqId = testSenderMq->getId(); + + MessageQueueIF* testReceiverMq = + QueueFactory::instance()->createMessageQueue(1); + MessageQueueId_t testReceiverMqId = testReceiverMq->getId(); + std::array testData { 0 }; + testData[0] = 42; + MessageQueueMessage testMessage(testData.data(), 1); + testSenderMq->setDefaultDestination(testReceiverMqId); + + SECTION("Simple Tests") { + auto result = testSenderMq->sendMessage(testReceiverMqId, &testMessage); + REQUIRE(result == retval::CATCH_OK); + MessageQueueMessage recvMessage; + result = testReceiverMq->receiveMessage(&recvMessage); + REQUIRE(result == retval::CATCH_OK); + CHECK(recvMessage.getData()[0] == 42); + + result = testSenderMq->sendMessage(testReceiverMqId, &testMessage); + REQUIRE(result == retval::CATCH_OK); + MessageQueueId_t senderId = 0; + result = testReceiverMq->receiveMessage(&recvMessage,&senderId); + REQUIRE(result == retval::CATCH_OK); + CHECK(recvMessage.getData()[0] == 42); + CHECK(senderId == testSenderMqId); + senderId = testReceiverMq->getLastPartner(); + CHECK(senderId == testSenderMqId); + } + + +} diff --git a/unittest/tests/osal/TestSemaphore.cpp b/unittest/tests/osal/TestSemaphore.cpp new file mode 100644 index 00000000..f7b25534 --- /dev/null +++ b/unittest/tests/osal/TestSemaphore.cpp @@ -0,0 +1,46 @@ + +#ifdef LINUX + +/* +#include "core/CatchDefinitions.h" +#include "catch.hpp" + +#include +#include + +TEST_CASE("Binary Semaphore Test" , "[BinSemaphore]") { + //perform set-up here + SemaphoreIF* binSemaph = SemaphoreFactory::instance()-> + createBinarySemaphore(); + REQUIRE(binSemaph != nullptr); + SECTION("Simple Test") { + // set-up is run for each section + REQUIRE(binSemaph->getSemaphoreCounter() == 1); + REQUIRE(binSemaph->release() == + static_cast(SemaphoreIF::SEMAPHORE_NOT_OWNED)); + REQUIRE(binSemaph->acquire(SemaphoreIF::POLLING) == + retval::CATCH_OK); + { + // not precise enough on linux.. should use clock instead.. + //Stopwatch stopwatch(false); + //REQUIRE(binSemaph->acquire(SemaphoreIF::TimeoutType::WAITING, 5) == + // SemaphoreIF::SEMAPHORE_TIMEOUT); + //dur_millis_t time = stopwatch.stop(); + //CHECK(time == 5); + } + REQUIRE(binSemaph->getSemaphoreCounter() == 0); + REQUIRE(binSemaph->release() == retval::CATCH_OK); + } + SemaphoreFactory::instance()->deleteSemaphore(binSemaph); + // perform tear-down here +} + + +TEST_CASE("Counting Semaphore Test" , "[CountingSemaph]") { + SECTION("Simple Test") { + + } +} +*/ + +#endif diff --git a/unittest/tests/serialize/TestSerialBufferAdapter.cpp b/unittest/tests/serialize/TestSerialBufferAdapter.cpp new file mode 100644 index 00000000..9919ed84 --- /dev/null +++ b/unittest/tests/serialize/TestSerialBufferAdapter.cpp @@ -0,0 +1,143 @@ +#include + +#include +#include "../../core/CatchDefinitions.h" + + +static bool test_value_bool = true; +static uint16_t tv_uint16 {283}; +static std::array testArray; + +TEST_CASE("Serial Buffer Adapter", "[single-file]") { + size_t serialized_size = 0; + test_value_bool = true; + uint8_t * arrayPtr = testArray.data(); + std::array test_serial_buffer {5, 4, 3, 2, 1}; + SerialBufferAdapter tv_serial_buffer_adapter = + SerialBufferAdapter(test_serial_buffer.data(), + test_serial_buffer.size(), false); + tv_uint16 = 16; + + SECTION("Serialize without size field") { + SerializeAdapter::serialize(&test_value_bool, &arrayPtr, + &serialized_size, testArray.size(), + SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_serial_buffer_adapter, &arrayPtr, + &serialized_size, testArray.size(), + SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint16, &arrayPtr, &serialized_size, + testArray.size(), SerializeIF::Endianness::MACHINE); + + REQUIRE(serialized_size == 8); + REQUIRE(testArray[0] == true); + REQUIRE(testArray[1] == 5); + REQUIRE(testArray[2] == 4); + REQUIRE(testArray[3] == 3); + REQUIRE(testArray[4] == 2); + REQUIRE(testArray[5] == 1); + memcpy(&tv_uint16, testArray.data() + 6, sizeof(tv_uint16)); + REQUIRE(tv_uint16 == 16); + } + + SECTION("Serialize with size field") { + SerialBufferAdapter tv_serial_buffer_adapter_loc = + SerialBufferAdapter(test_serial_buffer.data(), + test_serial_buffer.size(), true); + serialized_size = 0; + arrayPtr = testArray.data(); + SerializeAdapter::serialize(&test_value_bool, &arrayPtr,&serialized_size, + testArray.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_serial_buffer_adapter_loc, &arrayPtr, + &serialized_size, testArray.size(), + SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint16, &arrayPtr, &serialized_size, + testArray.size(), SerializeIF::Endianness::MACHINE); + + REQUIRE(serialized_size == 9); + REQUIRE(testArray[0] == true); + REQUIRE(testArray[1] == 5); + REQUIRE(testArray[2] == 5); + REQUIRE(testArray[3] == 4); + REQUIRE(testArray[4] == 3); + REQUIRE(testArray[5] == 2); + REQUIRE(testArray[6] == 1); + memcpy(&tv_uint16, testArray.data() + 7, sizeof(tv_uint16)); + REQUIRE(tv_uint16 == 16); + } + + SECTION("Test set buffer function") { + SerialBufferAdapter tv_serial_buffer_adapter_loc = + SerialBufferAdapter((uint8_t*)nullptr, + 0, true); + tv_serial_buffer_adapter_loc.setBuffer(test_serial_buffer.data(), + test_serial_buffer.size()); + serialized_size = 0; + arrayPtr = testArray.data(); + SerializeAdapter::serialize(&test_value_bool, &arrayPtr,&serialized_size, + testArray.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_serial_buffer_adapter_loc, &arrayPtr, + &serialized_size, testArray.size(), + SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint16, &arrayPtr, &serialized_size, + testArray.size(), SerializeIF::Endianness::MACHINE); + REQUIRE(serialized_size == 9); + REQUIRE(testArray[0] == true); + REQUIRE(testArray[1] == 5); + REQUIRE(testArray[2] == 5); + REQUIRE(testArray[3] == 4); + REQUIRE(testArray[4] == 3); + REQUIRE(testArray[5] == 2); + REQUIRE(testArray[6] == 1); + memcpy(&tv_uint16, testArray.data() + 7, sizeof(tv_uint16)); + REQUIRE(tv_uint16 == 16); + } + + SECTION("Deserialization with size field") { + size_t buffer_size = 4; + memcpy(testArray.data(), &buffer_size, sizeof(uint16_t)); + testArray[2] = 1; + testArray[3] = 1; + testArray[4] = 1; + testArray[5] = 0; + std::array test_recv_array; + arrayPtr = testArray.data(); + // copy testArray[1] to testArray[4] into receive buffer, skip + // size field (testArray[0]) for deSerialization. + SerialBufferAdapter tv_serial_buffer_adapter3 = + SerialBufferAdapter(test_recv_array.data(), 4, true); + // Deserialization + size_t size = 6; + auto result = tv_serial_buffer_adapter3.deSerialize( + const_cast(&arrayPtr), &size, + SerializeIF::Endianness::MACHINE); + REQUIRE(result == retval::CATCH_OK); + CHECK(test_recv_array[0] == 1); + CHECK(test_recv_array[1] == 1); + CHECK(test_recv_array[2] == 1); + CHECK(test_recv_array[3] == 0); + } + + SECTION("Deserialization without size field") { + size_t buffer_size = 4; + memcpy(testArray.data(), &buffer_size, sizeof(uint16_t)); + testArray[2] = 1; + testArray[3] = 1; + testArray[4] = 1; + testArray[5] = 0; + std::array test_recv_array; + arrayPtr = testArray.data() + 2; + // copy testArray[1] to testArray[4] into receive buffer, skip + // size field (testArray[0]) + SerialBufferAdapter tv_serial_buffer_adapter3 = + SerialBufferAdapter(test_recv_array.data(), 4, false); + // Deserialization + size_t size = 4; + tv_serial_buffer_adapter3.deSerialize( + const_cast(&arrayPtr), &size, + SerializeIF::Endianness::MACHINE); + CHECK(test_recv_array[0] == 1); + CHECK(test_recv_array[1] == 1); + CHECK(test_recv_array[2] == 1); + CHECK(test_recv_array[3] == 0); + } +} diff --git a/unittest/tests/serialize/TestSerialLinkedPacket.cpp b/unittest/tests/serialize/TestSerialLinkedPacket.cpp new file mode 100644 index 00000000..0a09e430 --- /dev/null +++ b/unittest/tests/serialize/TestSerialLinkedPacket.cpp @@ -0,0 +1,73 @@ +#include + +#include +#include "../../core/CatchDefinitions.h" +#include "TestSerialLinkedPacket.h" + + +TEST_CASE("Serial Linked Packet" , "[SerLinkPacket]") { + // perform set-up here + uint32_t header = 42; + std::array testArray {1,2,3}; + uint32_t tail = 96; + size_t packetMaxSize = 256; + uint8_t packet [packetMaxSize] = {}; + size_t packetLen = 0; + + SECTION("Test Deserialization with Serial Buffer Adapter.") { + // This is a serialization of a packet, made "manually". + // We generate a packet which store data big-endian by swapping some + // values. (like coming from ground). + header = EndianConverter::convertBigEndian(header); + std::memcpy(packet, &header, sizeof(header)); + packetLen += sizeof(header); + + std::copy(testArray.data(), testArray.data() + testArray.size(), + packet + packetLen); + packetLen += testArray.size(); + + tail = EndianConverter::convertBigEndian(tail); + std::memcpy(packet + packetLen, &tail, sizeof(tail)); + packetLen += sizeof(tail); + + //arrayprinter::print(packet, packetLen, OutputType::DEC); + + // This is the buffer which will be filled when testClass.deSerialize + // is called. + std::array bufferAdaptee = {}; + TestPacket testClass(packet, packetLen, bufferAdaptee.data(), + bufferAdaptee.size()); + const uint8_t* readOnlyPointer = packet; + // Deserialize big endian packet by setting bigEndian to true. + ReturnValue_t result = testClass.deSerialize(&readOnlyPointer, + &packetLen, SerializeIF::Endianness::BIG); + REQUIRE(result == retval::CATCH_OK); + CHECK(testClass.getHeader() == 42); + // Equivalent check. + // CHECK(testClass.getBuffer()[0] == 1); + CHECK(bufferAdaptee[0] == 1); + CHECK(bufferAdaptee[1] == 2); + CHECK(bufferAdaptee[2] == 3); + CHECK(testClass.getTail() == 96); + } + + SECTION("Test Serialization") { + // Same process as performed in setup, this time using the class + // instead of doing it manually. + TestPacket testClass(header, tail, testArray.data(), testArray.size()); + size_t serializedSize = 0; + uint8_t* packetPointer = packet; + // serialize for ground: bigEndian = true. + ReturnValue_t result = testClass.serialize(&packetPointer, + &serializedSize, packetMaxSize, SerializeIF::Endianness::BIG); + REQUIRE(result == retval::CATCH_OK); + // Result should be big endian now. + CHECK(packet[3] == 42); + CHECK(packet[4] == 1); + CHECK(packet[5] == 2); + CHECK(packet[6] == 3); + CHECK(packet[10] == 96); + } + + // perform tear-down here +} diff --git a/unittest/tests/serialize/TestSerialLinkedPacket.h b/unittest/tests/serialize/TestSerialLinkedPacket.h new file mode 100644 index 00000000..6c720577 --- /dev/null +++ b/unittest/tests/serialize/TestSerialLinkedPacket.h @@ -0,0 +1,61 @@ +#ifndef UNITTEST_HOSTED_TESTSERIALLINKEDPACKET_H_ +#define UNITTEST_HOSTED_TESTSERIALLINKEDPACKET_H_ + +#include +#include +#include +#include +#include + +class TestPacket: public SerialLinkedListAdapter { +public: + /** + * For Deserialization + */ + TestPacket(const uint8_t *somePacket, size_t size, uint8_t * storePointer, + size_t storeSize): + buffer(storePointer, storeSize) + { + setLinks(); + } + + /** + * For Serialization + */ + TestPacket(uint32_t header, uint32_t tail, + const uint8_t* parameters, size_t paramSize): + header(header), buffer(parameters, paramSize), + tail(tail) { + setLinks(); + } + + uint32_t getHeader() const { + return header.entry; + } + + const uint8_t * getBuffer() { + return buffer.entry.getConstBuffer(); + } + + const size_t getBufferLength() { + return buffer.getSerializedSize(); + } + + + uint16_t getTail() const { + return tail.entry; + } +private: + void setLinks() { + setStart(&header); + header.setNext(&buffer); + buffer.setNext(&tail); + tail.setEnd(); + } + + SerializeElement header = 0; + SerializeElement> buffer; + SerializeElement tail = 0; +}; + +#endif /* UNITTEST_TESTFW_NEWTESTS_TESTTEMPLATE_H_ */ diff --git a/unittest/tests/serialize/TestSerialization.cpp b/unittest/tests/serialize/TestSerialization.cpp new file mode 100644 index 00000000..4c9ba181 --- /dev/null +++ b/unittest/tests/serialize/TestSerialization.cpp @@ -0,0 +1,129 @@ +#include + +#include "catch.hpp" +#include +#include "../../core/CatchDefinitions.h" + +static bool test_value_bool = true; +static uint8_t tv_uint8 {5}; +static uint16_t tv_uint16 {283}; +static uint32_t tv_uint32 {929221}; +static uint64_t tv_uint64 {2929329429}; + +static int8_t tv_int8 {-16}; +static int16_t tv_int16 {-829}; +static int32_t tv_int32 {-2312}; + +static float tv_float {8.2149214}; +static float tv_sfloat = {-922.2321321}; +static double tv_double {9.2132142141e8}; +static double tv_sdouble {-2.2421e19}; + +static std::array test_array; + +TEST_CASE( "Serialization size tests", "[TestSerialization]") { + //REQUIRE(unitTestClass.test_autoserialization() == 0); + REQUIRE(SerializeAdapter::getSerializedSize(&test_value_bool) == + sizeof(test_value_bool)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint8) == + sizeof(tv_uint8)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint16) == + sizeof(tv_uint16)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint32 ) == + sizeof(tv_uint32)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint64) == + sizeof(tv_uint64)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_int8) == + sizeof(tv_int8)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_int16) == + sizeof(tv_int16)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_int32) == + sizeof(tv_int32)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_float) == + sizeof(tv_float)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_sfloat) == + sizeof(tv_sfloat )); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_double) == + sizeof(tv_double)); + REQUIRE(SerializeAdapter::getSerializedSize(&tv_sdouble) == + sizeof(tv_sdouble)); +} + + +TEST_CASE("Auto Serialize Adapter testing", "[single-file]") { + size_t serialized_size = 0; + uint8_t * p_array = test_array.data(); + + SECTION("Serializing...") { + SerializeAdapter::serialize(&test_value_bool, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint8, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint16, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint32, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_int8, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_int16, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_int32, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_uint64, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_float, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_double, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_sfloat, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + SerializeAdapter::serialize(&tv_sdouble, &p_array, + &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE); + REQUIRE (serialized_size == 47); + } + + SECTION("Deserializing") { + p_array = test_array.data(); + size_t remaining_size = serialized_size; + SerializeAdapter::deSerialize(&test_value_bool, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint8, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint16, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint32, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int8, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int16, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_int32, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_uint64, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_float, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_double, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_sfloat, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + SerializeAdapter::deSerialize(&tv_sdouble, + const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE); + + REQUIRE(test_value_bool == true); + REQUIRE(tv_uint8 == 5); + REQUIRE(tv_uint16 == 283); + REQUIRE(tv_uint32 == 929221); + REQUIRE(tv_uint64 == 2929329429); + REQUIRE(tv_int8 == -16); + REQUIRE(tv_int16 == -829); + REQUIRE(tv_int32 == -2312); + + REQUIRE(tv_float == Approx(8.214921)); + REQUIRE(tv_double == Approx(9.2132142141e8)); + REQUIRE(tv_sfloat == Approx(-922.2321321)); + REQUIRE(tv_sdouble == Approx(-2.2421e19)); + } +} + + diff --git a/unittest/tests/storagemanager/TestNewAccessor.cpp b/unittest/tests/storagemanager/TestNewAccessor.cpp new file mode 100644 index 00000000..7bd0dee2 --- /dev/null +++ b/unittest/tests/storagemanager/TestNewAccessor.cpp @@ -0,0 +1,161 @@ +//#include +//#include +//#include "../../core/CatchDefinitions.h" +//#include +// +//TEST_CASE( "New Accessor" , "[NewAccessor]") { +// uint16_t numberOfElements[1] = {1}; +// uint16_t sizeofElements[1] = {10}; +// LocalPool<1> SimplePool = LocalPool<1>(0, sizeofElements, numberOfElements); +// std::array testDataArray; +// std::array receptionArray; +// store_address_t testStoreId; +// ReturnValue_t result = retval::CATCH_FAILED; +// +// for(size_t i = 0; i < testDataArray.size(); i++) { +// testDataArray[i] = i; +// } +// size_t size = 10; +// +// SECTION ("Simple tests getter functions") { +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// auto resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// resultPair.second.getDataCopy(receptionArray.data(), 20); +// CHECK(resultPair.second.getId() == testStoreId); +// CHECK(resultPair.second.size() == 10); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// +// std::copy(resultPair.second.data(), resultPair.second.data() + +// resultPair.second.size(), receptionArray.data()); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// +// { +// auto resultPairLoc = SimplePool.getData(testStoreId); +// REQUIRE(resultPairLoc.first == retval::CATCH_OK); +// // data should be deleted when accessor goes out of scope. +// } +// resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == (int) StorageManagerIF::DATA_DOES_NOT_EXIST); +// +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// { +// ConstStorageAccessor constAccessor(testStoreId); +// result = SimplePool.getData(testStoreId, constAccessor); +// REQUIRE(result == retval::CATCH_OK); +// constAccessor.getDataCopy(receptionArray.data(), 20); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// // likewise, data should be deleted when accessor gets out of scope. +// } +// resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == (int) StorageManagerIF::DATA_DOES_NOT_EXIST); +// +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// { +// resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// resultPair.second.release(); +// // now data should not be deleted anymore +// } +// resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// resultPair.second.getDataCopy(receptionArray.data(), 20); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// } +// +// +// SECTION("Simple tests modify functions") { +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// { +// StorageAccessor accessor(testStoreId); +// result = SimplePool.modifyData(testStoreId, accessor); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(accessor.getId() == testStoreId); +// CHECK(accessor.size() == 10); +// accessor.getDataCopy(receptionArray.data(), 20); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// std::copy(accessor.data(), accessor.data() + +// accessor.size(), receptionArray.data()); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// // data should be deleted when accessor goes out of scope +// } +// auto resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == (int) StorageManagerIF::DATA_DOES_NOT_EXIST); +// +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// { +// auto resultPairLoc = SimplePool.modifyData(testStoreId); +// REQUIRE(resultPairLoc.first == retval::CATCH_OK); +// CHECK(resultPairLoc.second.getId() == testStoreId); +// CHECK(resultPairLoc.second.size() == 10); +// resultPairLoc.second.getDataCopy(receptionArray.data(), 20); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// std::copy(resultPairLoc.second.data(), resultPairLoc.second.data() + +// resultPairLoc.second.size(), receptionArray.data()); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// resultPairLoc.second.release(); +// // data should not be deleted when accessor goes out of scope +// } +// resultPair = SimplePool.getData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// } +// +// +// SECTION("Write tests") { +// result = SimplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// { +// auto resultPair = SimplePool.modifyData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// testDataArray[9] = 42; +// resultPair.second.write(testDataArray.data(), 10, 0); +// // now data should not be deleted +// resultPair.second.release(); +// } +// auto resultConstPair = SimplePool.getData(testStoreId); +// REQUIRE(resultConstPair.first == retval::CATCH_OK); +// +// resultConstPair.second.getDataCopy(receptionArray.data(), 10); +// for(size_t i = 0; i < size-1; i++) { +// CHECK(receptionArray[i] == i ); +// } +// CHECK(receptionArray[9] == 42 ); +// +// auto resultPair = SimplePool.modifyData(testStoreId); +// REQUIRE(resultPair.first == retval::CATCH_OK); +// result = resultPair.second.write(testDataArray.data(), 20, 0); +// REQUIRE(result == retval::CATCH_FAILED); +// result = resultPair.second.write(testDataArray.data(), 10, 5); +// REQUIRE(result == retval::CATCH_FAILED); +// +// memset(testDataArray.data(), 42, 5); +// result = resultPair.second.write(testDataArray.data(), 5, 5); +// REQUIRE(result == retval::CATCH_OK); +// resultConstPair = SimplePool.getData(testStoreId); +// resultPair.second.getDataCopy(receptionArray.data(), 20); +// for(size_t i = 5; i < 10; i++) { +// CHECK(receptionArray[i] == 42 ); +// } +// +// } +//} diff --git a/unittest/tests/storagemanager/TestPool.cpp b/unittest/tests/storagemanager/TestPool.cpp new file mode 100644 index 00000000..f278c40c --- /dev/null +++ b/unittest/tests/storagemanager/TestPool.cpp @@ -0,0 +1,296 @@ +//#include "CatchDefinitions.h" +// +//#include +//#include +//#include +// +//#include +//#include +// +//#include +// +// +//TEST_CASE( "Local Pool Simple Tests [1 Pool]" , "[TestPool]") { +//// uint16_t numberOfElements[1] = {1}; +//// uint16_t sizeofElements[1] = {10}; +// LocalPool::LocalPoolConfig config = {{1, 10}}; +// LocalPool simplePool(0, config); +// std::array testDataArray; +// std::array receptionArray; +// store_address_t testStoreId; +// ReturnValue_t result = retval::CATCH_FAILED; +// uint8_t *pointer = nullptr; +// const uint8_t * constPointer = nullptr; +// +// for(size_t i = 0; i < testDataArray.size(); i++) { +// testDataArray[i] = i; +// } +// size_t size = 10; +// +// SECTION ( "Basic tests") { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.getData(testStoreId, &constPointer, &size); +// REQUIRE(result == retval::CATCH_OK); +// memcpy(receptionArray.data(), constPointer, size); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// memset(receptionArray.data(), 0, size); +// result = simplePool.modifyData(testStoreId, &pointer, &size); +// memcpy(receptionArray.data(), pointer, size); +// REQUIRE(result == retval::CATCH_OK); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// result = simplePool.deleteData(testStoreId); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.addData(&testStoreId, testDataArray.data(), 15); +// CHECK (result == (int) StorageManagerIF::DATA_TOO_LARGE); +// } +// +// SECTION ( "Reservation Tests ") { +// pointer = nullptr; +// result = simplePool.getFreeElement(&testStoreId, size, &pointer); +// REQUIRE (result == retval::CATCH_OK); +// memcpy(pointer, testDataArray.data(), size); +// constPointer = nullptr; +// result = simplePool.getData(testStoreId, &constPointer, &size); +// +// REQUIRE (result == retval::CATCH_OK); +// memcpy(receptionArray.data(), constPointer, size); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// } +// +// SECTION ( "Add, delete, add, add when full") { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.getData(testStoreId, &constPointer, &size); +// REQUIRE( result == retval::CATCH_OK); +// memcpy(receptionArray.data(), constPointer, size); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// +// result = simplePool.deleteData(testStoreId); +// REQUIRE(result == retval::CATCH_OK); +// +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.getData(testStoreId, &constPointer, &size); +// REQUIRE( result == retval::CATCH_OK); +// memcpy(receptionArray.data(), constPointer, size); +// for(size_t i = 0; i < size; i++) { +// CHECK(receptionArray[i] == i ); +// } +// +// store_address_t newAddress; +// result = simplePool.addData(&newAddress, testDataArray.data(), size); +// REQUIRE(result == (int) StorageManagerIF::DATA_STORAGE_FULL); +// +// // Packet Index to high intentionally +// newAddress.packetIndex = 2; +// pointer = testDataArray.data(); +// result = simplePool.modifyData(newAddress, &pointer, &size); +// REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); +// +// result = simplePool.deleteData(newAddress); +// REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); +// +// newAddress.packetIndex = 0; +// newAddress.poolIndex = 2; +// result = simplePool.deleteData(newAddress); +// REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); +// } +// +// SECTION ( "Initialize and clear store, delete with pointer") { +// result = simplePool.initialize(); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// simplePool.clearStore(); +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// result = simplePool.modifyData(testStoreId, &pointer, &size); +// REQUIRE(result == retval::CATCH_OK); +// store_address_t newId; +// result = simplePool.deleteData(pointer, size, &testStoreId); +// REQUIRE(result == retval::CATCH_OK); +// REQUIRE(testStoreId.raw != (uint32_t) StorageManagerIF::INVALID_ADDRESS); +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// } +//} +// +//int runIdx = 0; +// +//TEST_CASE( "Local Pool Extended Tests [3 Pools]" , "[TestPool2]") { +// LocalPool::LocalPoolConfig* config; +// if(runIdx == 0) { +// config = new LocalPool::LocalPoolConfig{{10, 5}, {5, 10}, {2, 20}}; +// } +// else { +// // shufle the order, they should be sort implictely so that the +// // order is ascending for the page sizes. +// config = new LocalPool::LocalPoolConfig{{5, 10}, {2, 20}, {10, 5}}; +// size_t lastSize = 0; +// for(const auto& pair: *config) { +// CHECK(pair.second > lastSize); +// lastSize = pair.second; +// } +// } +// runIdx++; +// +// LocalPool simplePool(0, *config); +// std::array testDataArray; +// std::array receptionArray; +// store_address_t testStoreId; +// ReturnValue_t result = retval::CATCH_FAILED; +// for(size_t i = 0; i < testDataArray.size(); i++) { +// testDataArray[i] = i; +// } +// size_t size = 0; +// +// SECTION ("Basic tests") { +// size = 8; +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// // Should be on second page of the pool now for 8 bytes +// CHECK(testStoreId.poolIndex == 1); +// CHECK(testStoreId.packetIndex == 0); +// +// size = 15; +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// // Should be on third page of the pool now for 15 bytes +// CHECK(testStoreId.poolIndex == 2); +// CHECK(testStoreId.packetIndex == 0); +// +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// // Should be on third page of the pool now for 15 bytes +// CHECK(testStoreId.poolIndex == 2); +// CHECK(testStoreId.packetIndex == 1); +// +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// // Should be on third page of the pool now for 15 bytes +// REQUIRE(result == (int) LocalPool::DATA_STORAGE_FULL); +// +// size = 8; +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// // Should still work +// CHECK(testStoreId.poolIndex == 1); +// CHECK(testStoreId.packetIndex == 1); +// +// // fill the rest of the pool +// for(uint8_t idx = 2; idx < 5; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 1); +// CHECK(testStoreId.packetIndex == idx); +// } +// } +// +// SECTION ("Fill Count and Clearing") { +// //SECTION("Basic tests"); +// uint8_t bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// // fill count should be all zeros now. +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 0); +// CHECK(receptionArray[1] == 0); +// CHECK(receptionArray[2] == 0); +// CHECK(receptionArray[3] == 0); +// +// // now fill the store completely. +// size = 5; +// for(uint8_t idx = 0; idx < 10; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 0); +// CHECK(testStoreId.packetIndex == idx); +// } +// size = 10; +// for(uint8_t idx = 0; idx < 5; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 1); +// CHECK(testStoreId.packetIndex == idx); +// } +// size = 20; +// for(uint8_t idx = 0; idx < 2; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 2); +// CHECK(testStoreId.packetIndex == idx); +// } +// bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// // fill count should be all 100 now. +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 100); +// CHECK(receptionArray[1] == 100); +// CHECK(receptionArray[2] == 100); +// CHECK(receptionArray[3] == 100); +// +// // now clear the store +// simplePool.clearStore(); +// bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 0); +// CHECK(receptionArray[1] == 0); +// CHECK(receptionArray[2] == 0); +// CHECK(receptionArray[3] == 0); +// +// // now fill one page +// size = 5; +// for(uint8_t idx = 0; idx < 10; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 0); +// CHECK(testStoreId.packetIndex == idx); +// } +// bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// // First page full, median fill count is 33 % +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 100); +// CHECK(receptionArray[1] == 0); +// CHECK(receptionArray[2] == 0); +// CHECK(receptionArray[3] == 33); +// +// // now fill second page +// size = 10; +// for(uint8_t idx = 0; idx < 5; idx++) { +// result = simplePool.addData(&testStoreId, testDataArray.data(), size); +// REQUIRE(result == retval::CATCH_OK); +// CHECK(testStoreId.poolIndex == 1); +// CHECK(testStoreId.packetIndex == idx); +// } +// bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// // First and second page full, median fill count is 66 % +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 100); +// CHECK(receptionArray[1] == 100); +// CHECK(receptionArray[2] == 0); +// CHECK(receptionArray[3] == 66); +// +// // now clear first page +// simplePool.clearPage(0); +// bytesWritten = 0; +// simplePool.getFillCount(receptionArray.data(), &bytesWritten); +// // Second page full, median fill count is 33 % +// CHECK(bytesWritten == 4); +// CHECK(receptionArray[0] == 0); +// CHECK(receptionArray[1] == 100); +// CHECK(receptionArray[2] == 0); +// CHECK(receptionArray[3] == 33); +// } +// +// delete(config); +//} diff --git a/unittest/tests/tests.mk b/unittest/tests/tests.mk new file mode 100644 index 00000000..47e634a2 --- /dev/null +++ b/unittest/tests/tests.mk @@ -0,0 +1,8 @@ +CXXSRC += $(wildcard $(CURRENTPATH)/container/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/action/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/serialize/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/storagemanager/*.cpp) + +# OSAL not included for now. + +INCLUDES += $(CURRENTPATH) \ No newline at end of file diff --git a/unittest/testtemplate/TestTemplate.cpp b/unittest/testtemplate/TestTemplate.cpp new file mode 100644 index 00000000..6b5fc3d2 --- /dev/null +++ b/unittest/testtemplate/TestTemplate.cpp @@ -0,0 +1,31 @@ +#include +#include + + +/** + * @brief Template test file + * @details + * In each test case, the code outside the sections is executed + * for EACH section. + * The most common macros to perform tests are: + * - CHECK(...): assert expression and continues even if it fails + * - REQUIRE(...): test case fails if assertion fails + * + * Tests are generally sturctured in test cases and sections, see example + * below. + * + * More Documentation: + * - https://github.com/catchorg/Catch2 + * - https://github.com/catchorg/Catch2/blob/master/docs/assertions.md + * - https://github.com/catchorg/Catch2/blob/master/docs/test-cases-and-sections.md + */ +TEST_CASE("Dummy Test" , "[DummyTest]") { + uint8_t testVariable = 1; + //perform set-up here + CHECK(testVariable == 1); + SECTION("TestSection") { + // set-up is run for each section + REQUIRE(testVariable == 1); + } + // perform tear-down here +} diff --git a/unittest/unlockRealtime.sh b/unittest/unlockRealtime.sh new file mode 100644 index 00000000..b28d5490 --- /dev/null +++ b/unittest/unlockRealtime.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Run this script to unlock all permissions to run the linux binaries +# and create threads + +binaries=$(find $directory -type f -name "*.elf") + +echo Unlocking real time permissions for binaries and bash console... + +# Set up the soft realtime limit to maximum (99) +# Please note that the hard limit needs to be set to 99 too +# for this to work (check with ulimit -Hr). +# If that has not been done yet, add +# hard rtprio 99 +# to /etc/security/limits.conf +# It is also necessary and recommended to add +# soft rtprio 99 +# as well. This can also be done in the command line +# but would need to be done for each session. +ulimit -Sr 99 + +for binary in ${binaries}; do + sudo setcap 'cap_sys_nice=eip' ${binary} + result=$? + if [ ${result} = 0 ];then + echo ${binary} was unlocked + fi +done + +# sudo setcap 'cap_sys_nice=eip' /bin/bash +# result=$? +# if [ ${result} = 0 ];then +# echo /bin/bash was unlocked +# fi +