diff --git a/action/HasActionsIF.h b/action/HasActionsIF.h index f30fcb45e..4d66ad1f6 100644 --- a/action/HasActionsIF.h +++ b/action/HasActionsIF.h @@ -7,8 +7,10 @@ #include #include /** - * \brief Interface for component which uses actions + * @brief + * Interface for component which uses actions * + * @details * This interface is used to execute actions in the component. Actions, in the sense of this interface, are activities with a well-defined beginning and * end in time. They may adjust sub-states of components, but are not supposed to change * the main mode of operation, which is handled with the HasModesIF described below. @@ -23,6 +25,8 @@ * parameters and immediately start execution of the action. It is, however, not required to * immediately finish execution. Instead, this may be deferred to a later point in time, at * which the component needs to inform the caller about finished or failed execution. + * + * \ingroup interfaces */ class HasActionsIF { public: diff --git a/container/ArrayList.h b/container/ArrayList.h index 9c4c4cebc..57b2f94df 100644 --- a/container/ArrayList.h +++ b/container/ArrayList.h @@ -6,10 +6,9 @@ #include /** - * 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 backend is an array that can be allocated + * by the class itself or supplied via ctor. * * @ingroup container */ @@ -223,6 +222,8 @@ public: count_t remaining() { return (maxSize_ - size); } + + private: /** * This is the copy constructor @@ -233,9 +234,8 @@ private: * @param other */ ArrayList(const ArrayList& other) : - size(other.size), entries(other.entries), maxSize_(other.maxSize_), allocated( - false) { - } + size(other.size), entries(other.entries), maxSize_(other.maxSize_), + allocated(false) {} protected: /** * pointer to the array in which the entries are stored @@ -251,5 +251,24 @@ protected: */ bool allocated; + /** + * Swap the endianness of the Array list (not the length field !) + * Useful if the case the buffer type is larger than uint8_t + * @param list + */ + void swapArrayListEndianness() { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + count_t i = 0; + // uint8_t buffer does not require swapping of entries. + if(sizeof(T) == 1) { + return; + } + while ((result == HasReturnvaluesIF::RETURN_OK) && (i < size)) { + T newEntry = EndianSwapper::swap(entries[i]); + entries[i] = newEntry; + ++i; + } + } + }; #endif /* ARRAYLIST_H_ */ diff --git a/container/FIFO.h b/container/FIFO.h index 670a50d74..00f56993a 100644 --- a/container/FIFO.h +++ b/container/FIFO.h @@ -3,6 +3,11 @@ #include +/** + * @brief Simple First-In-First-Out data structure + * @tparam T Entry Type + * @tparam capacity Maximum capacity + */ template class FIFO { private: @@ -21,7 +26,7 @@ public: readIndex(0), writeIndex(0), currentSize(0) { } - bool emtpy() { + bool empty() { return (currentSize == 0); } @@ -45,7 +50,7 @@ public: } ReturnValue_t retrieve(T *value) { - if (emtpy()) { + if (empty()) { return EMPTY; } else { *value = data[readIndex]; diff --git a/container/FixedArrayList.h b/container/FixedArrayList.h index eeffcc870..12e292b4a 100644 --- a/container/FixedArrayList.h +++ b/container/FixedArrayList.h @@ -2,8 +2,10 @@ #define FIXEDARRAYLIST_H_ #include + /** - * \ingroup container + * @brief Array List with a fixed maximum size + * @ingroup container */ template class FixedArrayList: public ArrayList { @@ -14,6 +16,18 @@ public: ArrayList(data, MAX_SIZE) { } + //We could create a constructor to initialize the fixed array list with data and the known size field + //so it can be used for serialization too (with SerialFixedArrrayListAdapter) + //is this feasible? + FixedArrayList(T * data_, count_t count, bool swapArrayListEndianess = false): + ArrayList(data, MAX_SIZE) { + memcpy(this->data, data_, count * sizeof(T)); + this->size = count; + if(swapArrayListEndianess) { + ArrayList::swapArrayListEndianness(); + } + } + FixedArrayList(const FixedArrayList& other) : ArrayList(data, MAX_SIZE) { memcpy(this->data, other.data, sizeof(this->data)); diff --git a/container/FixedMap.h b/container/FixedMap.h index 0b84bf4ea..f5ee42445 100644 --- a/container/FixedMap.h +++ b/container/FixedMap.h @@ -6,7 +6,11 @@ #include /** - * \ingroup container + * @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). + * @ingroup container */ template class FixedMap: public SerializeIF { @@ -52,12 +56,24 @@ public: return ArrayList, uint32_t>::Iterator::value->second; } + // -> operator overloaded, can be used to access value T *operator->() { return &ArrayList, uint32_t>::Iterator::value->second; } + // Can be used to access the key of the iterator + key_t first() { + return ArrayList, uint32_t>::Iterator::value->first; + } + + // Alternative to access value, similar to std::map implementation + T second() { + return ArrayList, uint32_t>::Iterator::value->second; + } }; + + Iterator begin() const { return Iterator(&theMap[0]); } @@ -72,10 +88,10 @@ public: ReturnValue_t insert(key_t key, T value, Iterator *storedValue = NULL) { if (exists(key) == HasReturnvaluesIF::RETURN_OK) { - return KEY_ALREADY_EXISTS; + return FixedMap::KEY_ALREADY_EXISTS; } if (_size == theMap.maxSize()) { - return MAP_FULL; + return FixedMap::MAP_FULL; } theMap[_size].first = key; theMap[_size].second = value; @@ -87,7 +103,7 @@ public: } ReturnValue_t insert(std::pair pair) { - return insert(pair.fist, pair.second); + return insert(pair.first, pair.second); } ReturnValue_t exists(key_t key) const { @@ -148,6 +164,15 @@ public: return theMap.maxSize(); } + bool full() { + if(_size == theMap.maxSize()) { + return true; + } + else { + return false; + } + } + virtual ReturnValue_t serialize(uint8_t** buffer, uint32_t* size, const uint32_t max_size, bool bigEndian) const { ReturnValue_t result = SerializeAdapter::serialize(&this->_size, diff --git a/container/FixedOrderedMultimap.h b/container/FixedOrderedMultimap.h index 21629664b..3dd20a749 100644 --- a/container/FixedOrderedMultimap.h +++ b/container/FixedOrderedMultimap.h @@ -10,7 +10,7 @@ template> class FixedOrderedMultimap { public: - static const uint8_t INTERFACE_ID = CLASS_ID::FIXED_MAP; + static const uint8_t INTERFACE_ID = CLASS_ID::FIXED_MULTIMAP; 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); diff --git a/container/HybridIterator.h b/container/HybridIterator.h index b34fdfd01..1c56aa13b 100644 --- a/container/HybridIterator.h +++ b/container/HybridIterator.h @@ -67,7 +67,7 @@ public: } bool operator==(HybridIterator other) { - return value == other->value; + return value == other.value; } bool operator!=(HybridIterator other) { diff --git a/container/IndexedRingMemoryArray.h b/container/IndexedRingMemoryArray.h index eacfe3e5d..4ab174f4b 100644 --- a/container/IndexedRingMemoryArray.h +++ b/container/IndexedRingMemoryArray.h @@ -8,20 +8,27 @@ #include #include +/** + * Index is the Type used for the list of indices. + * + * @tparam T Type which destribes the index. Needs to be a child of SerializeIF + * to be able to make it persistent + */ template class Index: public SerializeIF{ /** - * Index is the Type used for the list of indices. The template parameter is the type which describes the index, it needs to be a child of SerializeIF to be able to make it persistent + * */ - static_assert(std::is_base_of::value,"Wrong Type for Index, Type must implement SerializeIF"); + static_assert(std::is_base_of::value, + "Wrong Type for Index, Type must implement SerializeIF"); public: Index():blockStartAddress(0),size(0),storedPackets(0){} - Index(uint32_t startAddress):blockStartAddress(startAddress),size(0),storedPackets(0){ - + Index(uint32_t startAddress):blockStartAddress(startAddress), + size(0),storedPackets(0) { } - void setBlockStartAddress(uint32_t newAddress){ + void setBlockStartAddress(uint32_t newAddress) { this->blockStartAddress = newAddress; } @@ -33,7 +40,7 @@ public: return &indexType; } - T* modifyIndexType(){ + T* modifyIndexType() { return &indexType; } /** @@ -128,26 +135,35 @@ private: }; - +/** + * @brief Indexed Ring Memory Array is a class for a ring memory with indices. + * @details + * It assumes that the newest data comes in last + * It uses the currentWriteBlock as pointer to the current writing position + * The currentReadBlock must be set manually + * @tparam T + */ template class IndexedRingMemoryArray: public SerializeIF, public ArrayList, uint32_t>{ /** - * Indexed Ring Memory Array is a class for a ring memory with indices. It assumes that the newest data comes in last - * It uses the currentWriteBlock as pointer to the current writing position - * The currentReadBlock must be set manually + * */ public: - IndexedRingMemoryArray(uint32_t startAddress, uint32_t size, uint32_t bytesPerBlock, SerializeIF* additionalInfo, - bool overwriteOld) :ArrayList,uint32_t>(NULL,(uint32_t)10,(uint32_t)0),totalSize(size),indexAddress(startAddress),currentReadSize(0),currentReadBlockSizeCached(0),lastBlockToReadSize(0), additionalInfo(additionalInfo),overwriteOld(overwriteOld){ - + IndexedRingMemoryArray(uint32_t startAddress, uint32_t size, uint32_t bytesPerBlock, + SerializeIF* additionalInfo, bool overwriteOld): + ArrayList,uint32_t>(NULL,(uint32_t)10,(uint32_t)0),totalSize(size), + indexAddress(startAddress),currentReadSize(0),currentReadBlockSizeCached(0), + lastBlockToReadSize(0), additionalInfo(additionalInfo),overwriteOld(overwriteOld) + { //Calculate the maximum number of indices needed for this blocksize uint32_t maxNrOfIndices = floor(static_cast(size)/static_cast(bytesPerBlock)); //Calculate the Size needeed for the index itself uint32_t serializedSize = 0; - if(additionalInfo!=NULL){ + if(additionalInfo!=NULL) { serializedSize += additionalInfo->getSerializedSize(); } + //Size of current iterator type Index tempIndex; serializedSize += tempIndex.getSerializedSize(); @@ -162,6 +178,7 @@ public: error << "IndexedRingMemory: Store is too small for index" << std::endl; } uint32_t useableSize = totalSize - serializedSize; + //Update the totalSize for calculations totalSize = useableSize; @@ -178,12 +195,10 @@ public: this->allocated = true; //Check trueNumberOfBlocks - if(trueNumberOfBlocks<1){ + if(trueNumberOfBlocks<1) { error << "IndexedRingMemory: Invalid Number of Blocks: " << trueNumberOfBlocks; } - - //Fill address into index uint32_t address = trueStartAddress; for (typename IndexedRingMemoryArray::Iterator it = this->begin();it!=this->end();++it) { @@ -193,7 +208,6 @@ public: address += bytesPerBlock; } - //Initialize iterators currentWriteBlock = this->begin(); currentReadBlock = this->begin(); @@ -232,10 +246,10 @@ public: (*typeResetFnc)(it->modifyIndexType()); } - /* + /** * Reading + * @param it */ - void setCurrentReadBlock(typename IndexedRingMemoryArray::Iterator it){ currentReadBlock = it; currentReadBlockSizeCached = it->getSize(); @@ -248,6 +262,7 @@ public: lastBlockToRead = currentWriteBlock; lastBlockToReadSize = currentWriteBlock->getSize(); } + /** * Sets the last block to read to this iterator. * Can be used to dump until block x @@ -292,33 +307,39 @@ public: uint32_t getCurrentReadAddress() const { return getAddressOfCurrentReadBlock() + currentReadSize; } + /** - * Adds readSize to the current size and checks if the read has no more data left and advances the read block + * Adds readSize to the current size and checks if the read has no more data + * left and advances the read block. * @param readSize The size that was read * @return Returns true if the read can go on */ bool addReadSize(uint32_t readSize) { - if(currentReadBlock == lastBlockToRead){ + if(currentReadBlock == lastBlockToRead) { //The current read block is the last to read - if((currentReadSize+readSize) return true currentReadSize += readSize; return true; - }else{ + } + else { //Reached end of read -> return false currentReadSize = lastBlockToReadSize; return false; } - }else{ + } + else { //We are not in the last Block - if((currentReadSize + readSize)::Iterator it(currentReadBlock); //Search if any block between this and the last block is not empty @@ -421,13 +442,13 @@ public: T* modifyCurrentWriteBlockIndexType(){ return currentWriteBlock->modifyIndexType(); } + void updatePreviousWriteSize(uint32_t size, uint32_t storedPackets){ typename IndexedRingMemoryArray::Iterator it = getPreviousBlock(currentWriteBlock); it->addSize(size); it->addStoredPackets(storedPackets); } - /** * Checks if the block has enough space for sizeToWrite * @param sizeToWrite The data to be written in the Block @@ -436,7 +457,10 @@ public: bool hasCurrentWriteBlockEnoughSpace(uint32_t sizeToWrite){ typename IndexedRingMemoryArray::Iterator next = getNextWrite(); uint32_t addressOfNextBlock = next->getBlockStartAddress(); - uint32_t availableSize = ((addressOfNextBlock+totalSize) - (getAddressOfCurrentWriteBlock()+getSizeOfCurrentWriteBlock()))%totalSize; + uint32_t availableSize = + ( ( addressOfNextBlock + totalSize ) - + (getAddressOfCurrentWriteBlock() + getSizeOfCurrentWriteBlock())) + % totalSize; return (sizeToWrite < availableSize); } @@ -694,7 +718,4 @@ private: }; - - - #endif /* FRAMEWORK_CONTAINER_INDEXEDRINGMEMORY_H_ */ diff --git a/container/SimpleRingBuffer.h b/container/SimpleRingBuffer.h index 6d7d67e1d..4552a1c0a 100644 --- a/container/SimpleRingBuffer.h +++ b/container/SimpleRingBuffer.h @@ -4,12 +4,41 @@ #include #include +/** + * @brief Circular buffer implementation, useful for buffering into data streams. + * @details Note that the deleteData() has to be called to increment the read pointer + * @ingroup containers + */ class SimpleRingBuffer: public RingBufferBase<> { public: SimpleRingBuffer(uint32_t size, bool overwriteOld); virtual ~SimpleRingBuffer(); + + /** + * Write to circular buffer and increment write pointer by amount + * @param data + * @param amount + * @return + */ ReturnValue_t writeData(const uint8_t* data, uint32_t amount); + + /** + * Read from circular buffer at read pointer + * @param data + * @param amount + * @param readRemaining + * @param trueAmount + * @return + */ ReturnValue_t readData(uint8_t* data, uint32_t amount, bool readRemaining = false, uint32_t* trueAmount = NULL); + + /** + * Delete data starting by incrementing read pointer + * @param amount + * @param deleteRemaining + * @param trueAmount + * @return + */ ReturnValue_t deleteData(uint32_t amount, bool deleteRemaining = false, uint32_t* trueAmount = NULL); private: // static const uint8_t TEMP_READ_PTR = 1; diff --git a/container/SinglyLinkedList.h b/container/SinglyLinkedList.h index 0a2e0531d..b926bd007 100644 --- a/container/SinglyLinkedList.h +++ b/container/SinglyLinkedList.h @@ -4,7 +4,9 @@ #include #include /** - * \ingroup container + * @brief Linked list data structure, + * each entry has a pointer to the next entry (singly) + * @ingroup container */ template class LinkedElement { @@ -58,6 +60,11 @@ public: virtual void setNext(LinkedElement* next) { this->next = next; } + + void setEnd() { + this->next = NULL; + } + LinkedElement* begin() { return this; } diff --git a/container/group.h b/container/group.h index 5a39d34ec..9c70d5236 100644 --- a/container/group.h +++ b/container/group.h @@ -4,11 +4,9 @@ /** * @defgroup container Container * - * General Purpose Container to store various elements. - * - * Also contains Adapter classes to print elements to a - * bytestream and to read them from a bytestream, as well - * as an Adapter to swap the endianness. + * General Purpose Containers to store various elements. + * As opposed to the STL library implementation, these implementations + * don't allocate memory dynamically. */ diff --git a/datapool/DataPool.cpp b/datapool/DataPool.cpp index 89543d776..1d3af7423 100644 --- a/datapool/DataPool.cpp +++ b/datapool/DataPool.cpp @@ -79,6 +79,7 @@ void DataPool::print() { } } +template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); diff --git a/datapool/DataPool.h b/datapool/DataPool.h index 6e38dc1b9..a9af8af0b 100644 --- a/datapool/DataPool.h +++ b/datapool/DataPool.h @@ -29,7 +29,7 @@ * \brief This class represents the OBSW global data-pool. * * \details All variables are registered and space is allocated in an initialization - * function, which is passed do the constructor. + * function, which is passed to the constructor. * Space for the variables is allocated on the heap (with a new call). * The data is found by a data pool id, which uniquely represents a variable. * Data pool variables should be used with a blackboard logic in mind, diff --git a/datapool/DataSet.h b/datapool/DataSet.h index 982be6928..8e0132239 100644 --- a/datapool/DataSet.h +++ b/datapool/DataSet.h @@ -37,44 +37,11 @@ * \ingroup data_pool */ class DataSet: public DataSetIF, public HasReturnvaluesIF, public SerializeIF { -private: + +public: //SHOULDDO we could use a linked list of datapool variables static const uint8_t DATA_SET_MAX_SIZE = 63; //!< This definition sets the maximum number of variables to register in one DataSet. - /** - * \brief This array represents all pool variables registered in this set. - * \details It has a maximum size of DATA_SET_MAX_SIZE. - */ - PoolVariableIF* registeredVariables[DATA_SET_MAX_SIZE]; - /** - * \brief The fill_count attribute ensures that the variables register in the correct array - * position and that the maximum number of variables is not exceeded. - */ - uint16_t fill_count; - /** - * States of the seet. - */ - enum States { - DATA_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED - DATA_SET_WAS_READ //!< DATA_SET_WAS_READ - }; - /** - * \brief state manages the internal state of the data set, which is important e.g. for the - * behavior on destruction. - */ - States state; - /** - * \brief This is a small helper function to facilitate locking the global data pool. - * \details It makes use of the lockDataPool method offered by the DataPool class. - */ - uint8_t lockDataPool(); - /** - * \brief This is a small helper function to facilitate unlocking the global data pool. - * \details It makes use of the freeDataPoolLock method offered by the DataPool class. - */ - uint8_t freeDataPoolLock(); - -public: static const uint8_t INTERFACE_ID = CLASS_ID::DATA_SET_CLASS; static const ReturnValue_t INVALID_PARAMETER_DEFINITION = MAKE_RETURN_CODE( 0x01 ); @@ -146,6 +113,14 @@ public: */ void setValid(uint8_t valid); + /** + * Serialize all registered pool variables into the provided buffer + * @param buffer + * @param size Is incremented by serialized size + * @param max_size + * @param bigEndian + * @return + */ ReturnValue_t serialize(uint8_t** buffer, uint32_t* size, const uint32_t max_size, bool bigEndian) const; @@ -154,6 +129,40 @@ public: ReturnValue_t deSerialize(const uint8_t** buffer, int32_t* size, bool bigEndian); +private: + /** + * \brief This array represents all pool variables registered in this set. + * \details It has a maximum size of DATA_SET_MAX_SIZE. + */ + PoolVariableIF* registeredVariables[DATA_SET_MAX_SIZE]; + /** + * \brief The fill_count attribute ensures that the variables register in the correct array + * position and that the maximum number of variables is not exceeded. + */ + uint16_t fill_count; + /** + * States of the seet. + */ + enum States { + DATA_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED + DATA_SET_WAS_READ //!< DATA_SET_WAS_READ + }; + /** + * \brief state manages the internal state of the data set, which is important e.g. for the + * behavior on destruction. + */ + States state; + /** + * \brief This is a small helper function to facilitate locking the global data pool. + * \details It makes use of the lockDataPool method offered by the DataPool class. + */ + uint8_t lockDataPool(); + /** + * \brief This is a small helper function to facilitate unlocking the global data pool. + * \details It makes use of the freeDataPoolLock method offered by the DataPool class. + */ + uint8_t freeDataPoolLock(); + }; #endif /* DATASET_H_ */ diff --git a/datapool/PoolEntry.cpp b/datapool/PoolEntry.cpp index c303e56b8..898d226e2 100644 --- a/datapool/PoolEntry.cpp +++ b/datapool/PoolEntry.cpp @@ -1,6 +1,17 @@ #include #include +template +PoolEntry::PoolEntry(std::initializer_list initValue, uint8_t set_length, uint8_t set_valid ) : length(set_length), valid(set_valid) { + this->address = new T[this->length]; + if(initValue.size() == 0) { + memset(this->address, 0, this->getByteSize()); + } + else { + memcpy(this->address, initValue.begin(), this->getByteSize()); + } +} + template PoolEntry::PoolEntry( T* initValue, uint8_t set_length, uint8_t set_valid ) : length(set_length), valid(set_valid) { this->address = new T[this->length]; @@ -11,6 +22,7 @@ PoolEntry::PoolEntry( T* initValue, uint8_t set_length, uint8_t set_valid ) : } } + //As the data pool is global, this dtor is only be called on program exit. //Warning! Never copy pool entries! template @@ -56,6 +68,7 @@ Type PoolEntry::getType() { return PodTypeConversion::type; } +template class PoolEntry; template class PoolEntry; template class PoolEntry; template class PoolEntry; diff --git a/datapool/PoolEntry.h b/datapool/PoolEntry.h index ce41a9917..a02c6c60d 100644 --- a/datapool/PoolEntry.h +++ b/datapool/PoolEntry.h @@ -5,6 +5,7 @@ #include #include #include +#include /** * \brief This is a small helper class that defines a single data pool entry. * @@ -31,6 +32,7 @@ public: * \param set_length Defines the array length of this entry. * \param set_valid Sets the initialization flag. It is invalid (0) by default. */ + PoolEntry( std::initializer_list initValue = {}, uint8_t set_length = 1, uint8_t set_valid = 0 ); PoolEntry( T* initValue = NULL, uint8_t set_length = 1, uint8_t set_valid = 0 ); /** * \brief The allocated memory for the variable is freed in the destructor. diff --git a/datapool/PoolRawAccess.cpp b/datapool/PoolRawAccess.cpp index 555896bb2..ff8fb9561 100644 --- a/datapool/PoolRawAccess.cpp +++ b/datapool/PoolRawAccess.cpp @@ -14,41 +14,62 @@ PoolRawAccess::PoolRawAccess(uint32_t set_id, uint8_t setArrayEntry, } } -PoolRawAccess::~PoolRawAccess() { - -} +PoolRawAccess::~PoolRawAccess() {} ReturnValue_t PoolRawAccess::read() { + ReturnValue_t result = RETURN_FAILED; PoolEntryIF* read_out = ::dataPool.getRawData(dataPoolId); if (read_out != NULL) { - valid = read_out->getValid(); - if (read_out->getSize() > arrayEntry) { - arraySize = read_out->getSize(); - typeSize = read_out->getByteSize() / read_out->getSize(); - type = read_out->getType(); - if (typeSize <= sizeof(value)) { - uint16_t arrayPosition = arrayEntry * typeSize; - sizeTillEnd = read_out->getByteSize() - arrayPosition; - uint8_t* ptr = - &((uint8_t*) read_out->getRawData())[arrayPosition]; - memcpy(value, ptr, typeSize); - return HasReturnvaluesIF::RETURN_OK; - } else { - //Error value type too large. - } - } else { - //Error index requested too large + result = handleReadOut(read_out); + if(result == RETURN_OK) { + return result; } } else { - //Error entry does not exist. + result = READ_ENTRY_NON_EXISTENT; } + handleReadError(result); + return result; +} + +ReturnValue_t PoolRawAccess::handleReadOut(PoolEntryIF* read_out) { + ReturnValue_t result = RETURN_FAILED; + valid = read_out->getValid(); + if (read_out->getSize() > arrayEntry) { + arraySize = read_out->getSize(); + typeSize = read_out->getByteSize() / read_out->getSize(); + type = read_out->getType(); + if (typeSize <= sizeof(value)) { + uint16_t arrayPosition = arrayEntry * typeSize; + sizeTillEnd = read_out->getByteSize() - arrayPosition; + uint8_t* ptr = &((uint8_t*) read_out->getRawData())[arrayPosition]; + memcpy(value, ptr, typeSize); + return RETURN_OK; + } else { + result = READ_TYPE_TOO_LARGE; + } + } else { + //debug << "PoolRawAccess: Size: " << (int)read_out->getSize() << std::endl; + result = READ_INDEX_TOO_LARGE; + } + return result; +} + +void PoolRawAccess::handleReadError(ReturnValue_t result) { error << "PoolRawAccess: read of DP Variable 0x" << std::hex << dataPoolId - << std::dec << " failed." << std::endl; + << std::dec << " failed, "; + if(result == READ_TYPE_TOO_LARGE) { + error << "type too large." << std::endl; + } + else if(result == READ_INDEX_TOO_LARGE) { + error << "index too large." << std::endl; + } + else if(result == READ_ENTRY_NON_EXISTENT) { + error << "entry does not exist." << std::endl; + } valid = INVALID; typeSize = 0; sizeTillEnd = 0; memset(value, 0, sizeof(value)); - return HasReturnvaluesIF::RETURN_FAILED; } ReturnValue_t PoolRawAccess::commit() { @@ -89,6 +110,32 @@ ReturnValue_t PoolRawAccess::getEntryEndianSafe(uint8_t* buffer, return HasReturnvaluesIF::RETURN_OK; } + +ReturnValue_t PoolRawAccess::serialize(uint8_t** buffer, uint32_t* size, + const uint32_t max_size, bool bigEndian) const { + if (typeSize + *size <= max_size) { + if (bigEndian) { +#ifndef BYTE_ORDER_SYSTEM +#error BYTE_ORDER_SYSTEM not defined +#elif BYTE_ORDER_SYSTEM == LITTLE_ENDIAN + for (uint8_t count = 0; count < typeSize; count++) { + (*buffer)[count] = value[typeSize - count - 1]; + } +#elif BYTE_ORDER_SYSTEM == BIG_ENDIAN + memcpy(*buffer, value, typeSize); +#endif + } else { + memcpy(*buffer, value, typeSize); + } + *size += typeSize; + (*buffer) += typeSize; + return HasReturnvaluesIF::RETURN_OK; + } else { + return SerializeIF::BUFFER_TOO_SHORT; + } +} + + Type PoolRawAccess::getType() { return type; } @@ -145,29 +192,6 @@ uint16_t PoolRawAccess::getSizeTillEnd() const { return sizeTillEnd; } -ReturnValue_t PoolRawAccess::serialize(uint8_t** buffer, uint32_t* size, - const uint32_t max_size, bool bigEndian) const { - if (typeSize + *size <= max_size) { - if (bigEndian) { -#ifndef BYTE_ORDER_SYSTEM -#error BYTE_ORDER_SYSTEM not defined -#elif BYTE_ORDER_SYSTEM == LITTLE_ENDIAN - for (uint8_t count = 0; count < typeSize; count++) { - (*buffer)[count] = value[typeSize - count - 1]; - } -#elif BYTE_ORDER_SYSTEM == BIG_ENDIAN - memcpy(*buffer, value, typeSize); -#endif - } else { - memcpy(*buffer, value, typeSize); - } - *size += typeSize; - (*buffer) += typeSize; - return HasReturnvaluesIF::RETURN_OK; - } else { - return SerializeIF::BUFFER_TOO_SHORT; - } -} uint32_t PoolRawAccess::getSerializedSize() const { return typeSize; diff --git a/datapool/PoolRawAccess.h b/datapool/PoolRawAccess.h index 3e2bd7ae3..52ba8b210 100644 --- a/datapool/PoolRawAccess.h +++ b/datapool/PoolRawAccess.h @@ -3,85 +3,57 @@ #include #include +#include /** - * This class allows accessing Data Pool variables as raw bytes. + * @brief This class allows accessing Data Pool variables as raw bytes. + * @details * This is necessary to have an access method for HK data, as the PID's alone do not - * provide a type information. - * \ingroup data_pool + * provide a type information. Please note that the the raw pool access read() and commit() + * calls are not thread-safe. + * Please supply a data set and use the data set read(), commit() calls for thread-safe + * data pool access. + * @ingroup data_pool */ -class PoolRawAccess: public PoolVariableIF { -private: - /** - * \brief To access the correct data pool entry on read and commit calls, the data pool id - * is stored. - */ - uint32_t dataPoolId; - /** - * \brief The array entry that is fetched from the data pool. - */ - uint8_t arrayEntry; - /** - * \brief The valid information as it was stored in the data pool is copied to this attribute. - */ - uint8_t valid; - /** - * \brief This value contains the type of the data pool entry. - */ - Type type; - /** - * \brief This value contains the size of the data pool entry in bytes. - */ - uint8_t typeSize; - /** - * The size of the DP array (single values return 1) - */ - uint8_t arraySize; - /** - * The size (in bytes) from the selected entry till the end of this DataPool variable. - */ - uint16_t sizeTillEnd; - /** - * \brief The information whether the class is read-write or read-only is stored here. - */ - ReadWriteMode_t readWriteMode; - static const uint8_t RAW_MAX_SIZE = sizeof(double); -protected: - /** - * \brief This is a call to read the value from the global data pool. - * \details When executed, this operation tries to fetch the pool entry with matching - * data pool id from the global data pool and copies the value and the valid - * information to its local attributes. In case of a failure (wrong type or - * pool id not found), the variable is set to zero and invalid. - * The operation does NOT provide any mutual exclusive protection by itself. - */ - ReturnValue_t read(); - /** - * \brief The commit call writes back the variable's value to the data pool. - * \details It checks type and size, as well as if the variable is writable. If so, - * the value is copied and the valid flag is automatically set to "valid". - * The operation does NOT provide any mutual exclusive protection by itself. - * - */ - ReturnValue_t commit(); +class PoolRawAccess: public PoolVariableIF, HasReturnvaluesIF { public: static const uint8_t INTERFACE_ID = CLASS_ID::POOL_RAW_ACCESS_CLASS; static const ReturnValue_t INCORRECT_SIZE = MAKE_RETURN_CODE(0x01); static const ReturnValue_t DATA_POOL_ACCESS_FAILED = MAKE_RETURN_CODE(0x02); + static const ReturnValue_t READ_TYPE_TOO_LARGE = MAKE_RETURN_CODE(0x03); + static const ReturnValue_t READ_INDEX_TOO_LARGE = MAKE_RETURN_CODE(0x04); + static const ReturnValue_t READ_ENTRY_NON_EXISTENT = MAKE_RETURN_CODE(0x05); + static const uint8_t RAW_MAX_SIZE = sizeof(double); uint8_t value[RAW_MAX_SIZE]; + + /** + * This constructor is used to access a data pool entry with a + * given ID if the target type is not known. A DataSet object is supplied + * and the data pool entry with the given ID is registered to that data set. + * Please note that a pool raw access buffer only has a buffer + * with a size of double. As such, for vector entries which have + * @param data_pool_id Target data pool entry ID + * @param arrayEntry + * @param data_set Dataset to register data pool entry to + * @param setReadWriteMode + * @param registerVectors If set to true, the constructor checks if + * there are multiple vector entries to registers + * and registers all of them recursively into the data_set + * + */ PoolRawAccess(uint32_t data_pool_id, uint8_t arrayEntry, DataSetIF* data_set, ReadWriteMode_t setReadWriteMode = - PoolVariableIF::VAR_READ); + PoolVariableIF::VAR_READ); /** * \brief The classes destructor is empty. If commit() was not called, the local value is * discarded and not written back to the data pool. */ ~PoolRawAccess(); + /** * \brief This operation returns a pointer to the entry fetched. - * \details This means, it does not return a pointer to byte "index", but to the start byte of - * array entry "index". Example: If the original data pool array consists of an double - * array of size four, getEntry(1) returns &(this->value[8]). + * \details Return pointer to the buffer containing the raw data + * Size and number of data can be retrieved by other means. */ uint8_t* getEntry(); /** @@ -99,6 +71,19 @@ public: */ ReturnValue_t getEntryEndianSafe(uint8_t* buffer, uint32_t* size, uint32_t max_size); + + /** + * @brief Serialize raw pool entry into provided buffer directly + * @param buffer Provided buffer. Raw pool data will be copied here + * @param size [out] Increment provided size value by serialized size + * @param max_size Maximum allowed serialization size + * @param bigEndian Specify endianess + * @return - @c RETURN_OK if serialization was successfull + * - @c SerializeIF::BUFFER_TOO_SHORT if range check failed + */ + ReturnValue_t serialize(uint8_t** buffer, uint32_t* size, + const uint32_t max_size, bool bigEndian) const; + /** * With this method, the content can be set from a big endian buffer safely. * @param buffer Pointer to the data to set @@ -140,13 +125,73 @@ public: */ uint16_t getSizeTillEnd() const; - ReturnValue_t serialize(uint8_t** buffer, uint32_t* size, - const uint32_t max_size, bool bigEndian) const; - uint32_t getSerializedSize() const; ReturnValue_t deSerialize(const uint8_t** buffer, int32_t* size, bool bigEndian); + +protected: + /** + * \brief This is a call to read the value from the global data pool. + * \details When executed, this operation tries to fetch the pool entry with matching + * data pool id from the global data pool and copies the value and the valid + * information to its local attributes. In case of a failure (wrong type or + * pool id not found), the variable is set to zero and invalid. + * The operation does NOT provide any mutual exclusive protection by itself ! + * If reading from the data pool without information about the type is desired, + * initialize the raw pool access by supplying a data set and using the data set + * read function, which calls this read function. + * @return -@c RETURN_OK Read successfull + * -@c READ_TYPE_TOO_LARGE + * -@c READ_INDEX_TOO_LARGE + * -@c READ_ENTRY_NON_EXISTENT + */ + ReturnValue_t read(); + /** + * \brief The commit call writes back the variable's value to the data pool. + * \details It checks type and size, as well as if the variable is writable. If so, + * the value is copied and the valid flag is automatically set to "valid". + * The operation does NOT provide any mutual exclusive protection by itself. + * + */ + ReturnValue_t commit(); + + ReturnValue_t handleReadOut(PoolEntryIF* read_out); + void handleReadError(ReturnValue_t result); +private: + /** + * \brief To access the correct data pool entry on read and commit calls, the data pool id + * is stored. + */ + uint32_t dataPoolId; + /** + * \brief The array entry that is fetched from the data pool. + */ + uint8_t arrayEntry; + /** + * \brief The valid information as it was stored in the data pool is copied to this attribute. + */ + uint8_t valid; + /** + * \brief This value contains the type of the data pool entry. + */ + Type type; + /** + * \brief This value contains the size of the data pool entry type in bytes. + */ + uint8_t typeSize; + /** + * The size of the DP array (single values return 1) + */ + uint8_t arraySize; + /** + * The size (in bytes) from the selected entry till the end of this DataPool variable. + */ + uint16_t sizeTillEnd; + /** + * \brief The information whether the class is read-write or read-only is stored here. + */ + ReadWriteMode_t readWriteMode; }; #endif /* POOLRAWACCESS_H_ */ diff --git a/datapool/PoolRawAccessHelper.cpp b/datapool/PoolRawAccessHelper.cpp new file mode 100644 index 000000000..07ea44651 --- /dev/null +++ b/datapool/PoolRawAccessHelper.cpp @@ -0,0 +1,168 @@ +/** + * @file PoolRawAccessHelper.cpp + * + * @date 22.12.2019 + * @author R. Mueller + */ + +#include + +#include + +#include + +PoolRawAccessHelper::PoolRawAccessHelper(uint32_t * poolIdBuffer_, + uint8_t numberOfParameters_): + poolIdBuffer(reinterpret_cast(poolIdBuffer_)), + numberOfParameters(numberOfParameters_), validBufferIndex(0), validBufferIndexBit(1){ +} + +PoolRawAccessHelper::~PoolRawAccessHelper() { +} + +ReturnValue_t PoolRawAccessHelper::serialize(uint8_t **buffer, uint32_t *size, + const uint32_t max_size, bool bigEndian) { + SerializationArgs serializationArgs = {buffer, size, max_size, bigEndian}; + ReturnValue_t result; + int32_t remainingParametersSize = numberOfParameters * 4; + for(uint8_t count=0; count < numberOfParameters; count++) { + result = serializeCurrentPoolEntryIntoBuffer(serializationArgs, + &remainingParametersSize, false); + if(result != RETURN_OK) { + return result; + } + } + if(remainingParametersSize != 0) { + debug << "Pool Raw Access: Remaining parameters size not 0 !" << std::endl; + result = RETURN_FAILED; + } + return result; +} + +ReturnValue_t PoolRawAccessHelper::serializeWithValidityMask(uint8_t ** buffer, uint32_t * size, + const uint32_t max_size, bool bigEndian) { + ReturnValue_t result; + SerializationArgs argStruct = {buffer, size, max_size, bigEndian}; + int32_t remainingParametersSize = numberOfParameters * 4; + uint8_t validityMaskSize = ceil((float)numberOfParameters/8.0); + uint8_t validityMask[validityMaskSize]; + memset(validityMask,0, validityMaskSize); + for(uint8_t count = 0; count < numberOfParameters; count++) { + result = serializeCurrentPoolEntryIntoBuffer(argStruct, + &remainingParametersSize,true,validityMask); + if (result != RETURN_OK) { + return result; + } + } + if(remainingParametersSize != 0) { + debug << "Pool Raw Access: Remaining parameters size not 0 !" << std::endl; + result = RETURN_FAILED; + } + + memcpy(*argStruct.buffer, validityMask, validityMaskSize); + *size += validityMaskSize; + validBufferIndex = 1; + validBufferIndexBit = 0; + return result; +} + +ReturnValue_t PoolRawAccessHelper::serializeCurrentPoolEntryIntoBuffer(SerializationArgs argStruct, + int32_t * remainingParameters, bool withValidMask, uint8_t * validityMask) { + uint32_t currentPoolId; + // Deserialize current pool ID from pool ID buffer + ReturnValue_t result = AutoSerializeAdapter::deSerialize(¤tPoolId, + &poolIdBuffer,remainingParameters,true); + if(result != RETURN_OK) { + debug << std::hex << "Pool Raw Access Helper: Error deSeralizing pool IDs" << std::dec << std::endl; + return result; + } + result = handlePoolEntrySerialization(currentPoolId, argStruct, withValidMask, validityMask); + return result; +} + +ReturnValue_t PoolRawAccessHelper::handlePoolEntrySerialization(uint32_t currentPoolId,SerializationArgs argStruct, + bool withValidMask, uint8_t * validityMask) { + ReturnValue_t result = RETURN_FAILED; + uint8_t arrayPosition = 0; + uint8_t counter = 0; + bool poolEntrySerialized = false; + //debug << "Pool Raw Access Helper: Handling Pool ID: " << std::hex << currentPoolId << std::endl; + while(not poolEntrySerialized) { + + if(counter > DataSet::DATA_SET_MAX_SIZE) { + error << "Pool Raw Access Helper: Config error, max. number of possible data set variables exceeded" << std::endl; + return result; + } + counter ++; + + DataSet currentDataSet = DataSet(); + //debug << "Current array position: " << (int)arrayPosition << std::endl; + PoolRawAccess currentPoolRawAccess(currentPoolId,arrayPosition,¤tDataSet,PoolVariableIF::VAR_READ); + + result = currentDataSet.read(); + if (result != RETURN_OK) { + debug << std::hex << "Pool Raw Access Helper: Error reading raw dataset with returncode 0x" + << result << std::dec << std::endl; + return result; + } + + result = checkRemainingSize(¤tPoolRawAccess, &poolEntrySerialized, &arrayPosition); + if(result != RETURN_OK) { + error << "Pool Raw Access Helper: Configuration Error at pool ID " << std::hex << currentPoolId + << ". Size till end smaller than 0" << std::dec << std::endl; + return result; + } + + // set valid mask bit if necessary + if(withValidMask) { + if(currentPoolRawAccess.isValid()) { + handleMaskModification(validityMask); + } + validBufferIndexBit ++; + } + + result = currentDataSet.serialize(argStruct.buffer, argStruct.size, + argStruct.max_size, argStruct.bigEndian); + if (result != RETURN_OK) { + debug << "Pool Raw Access Helper: Error serializing pool data with ID 0x" << std::hex << + currentPoolId << " into send buffer with return code " << result << std::dec << std::endl; + return result; + } + + } + return result; +} + +ReturnValue_t PoolRawAccessHelper::checkRemainingSize(PoolRawAccess * currentPoolRawAccess, + bool * isSerialized, uint8_t * arrayPosition) { + int8_t remainingSize = currentPoolRawAccess->getSizeTillEnd() - currentPoolRawAccess->getSizeOfType(); + if(remainingSize == 0) { + *isSerialized = true; + } + else if(remainingSize > 0) { + *arrayPosition += 1; + } + else { + return RETURN_FAILED; + } + return RETURN_OK; +} + +void PoolRawAccessHelper::handleMaskModification(uint8_t * validityMask) { + validityMask[validBufferIndex] = + bitSetter(validityMask[validBufferIndex], validBufferIndexBit, true); + if(validBufferIndexBit == 8) { + validBufferIndex ++; + validBufferIndexBit = 1; + } +} + +uint8_t PoolRawAccessHelper::bitSetter(uint8_t byte, uint8_t position, bool value) { + if(position < 1 or position > 8) { + debug << "Pool Raw Access: Bit setting invalid position" << std::endl; + return byte; + } + uint8_t shiftNumber = position + (6 - 2 * (position - 1)); + byte |= 1UL << shiftNumber; + return byte; +} diff --git a/datapool/PoolRawAccessHelper.h b/datapool/PoolRawAccessHelper.h new file mode 100644 index 000000000..72f2324c6 --- /dev/null +++ b/datapool/PoolRawAccessHelper.h @@ -0,0 +1,106 @@ +/** + * @file PoolRawAccessHelper.h + * + * @date 22.12.2019 + */ + +#ifndef FRAMEWORK_DATAPOOL_POOLRAWACCESSHELPER_H_ +#define FRAMEWORK_DATAPOOL_POOLRAWACCESSHELPER_H_ + +#include +#include + +/** + * @brief This helper function simplifies accessing data pool entries + * via PoolRawAccess + * @details Can be used for a Housekeeping Service + * like ECSS PUS Service 3 if the type of the datapool entries is unknown. + * The provided dataset can be serialized into a provided buffer automatically by + * providing a buffer of pool IDs + * @ingroup data_pool + */ +class PoolRawAccessHelper: public HasReturnvaluesIF { +public: + /** + * Call this constructor if a dataset needs to be serialized via + * Pool Raw Access + * @param dataSet_ This dataset will be used to perform thread-safe reading + * @param poolIdBuffer_ A buffer of uint32_t pool IDs + * @param numberOfParameters_ The number of parameters / pool IDs + */ + PoolRawAccessHelper(uint32_t * poolIdBuffer_, uint8_t numberOfParameters_); + virtual ~PoolRawAccessHelper(); + + /** + * Serialize the datapool entries derived from the pool ID buffer + * directly into a provided buffer + * @param [out] buffer + * @param [out] size Size of the serialized buffer + * @param max_size + * @param bigEndian + * @return @c RETURN_OK On success + * @c RETURN_FAILED on failure + */ + ReturnValue_t serialize(uint8_t ** buffer, uint32_t * size, + const uint32_t max_size, bool bigEndian); + + /** + * Serializes data pool entries into provided buffer with the validity mask buffer + * at the end of the buffer. Every bit of the validity mask denotes + * the validity of a corresponding data pool entry from left to right. + * @param [out] buffer + * @param [out] size Size of the serialized buffer plus size + * of the validity mask + * @return @c RETURN_OK On success + * @c RETURN_FAILED on failure + */ + ReturnValue_t serializeWithValidityMask(uint8_t ** buffer, uint32_t * size, + const uint32_t max_size, bool bigEndian); + + +private: + // DataSet * dataSet; + const uint8_t * poolIdBuffer; + uint8_t numberOfParameters; + + uint8_t validBufferIndex; + uint8_t validBufferIndexBit; + + struct SerializationArgs { + uint8_t ** buffer; + uint32_t * size; + const uint32_t max_size; + bool bigEndian; + }; + /** + * Helper function to serialize single pool entries + * @param pPoolIdBuffer + * @param buffer + * @param remainingParameters + * @param hkDataSize + * @param max_size + * @param bigEndian + * @param withValidMask Can be set optionally to set a provided validity mask + * @param validityMask Can be supplied and will be set if @c withValidMask is set to true + * @return + */ + ReturnValue_t serializeCurrentPoolEntryIntoBuffer(SerializationArgs argStruct, + int32_t * remainingParameters, bool withValidMask = false, uint8_t * validityMask = NULL); + + ReturnValue_t handlePoolEntrySerialization(uint32_t currentPoolId,SerializationArgs argStruct, + bool withValidMask = false, uint8_t * validityMask = NULL); + + ReturnValue_t checkRemainingSize(PoolRawAccess * currentPoolRawAccess, + bool * isSerialized, uint8_t * arrayPosition); + void handleMaskModification(uint8_t * validityMask); + /** + * Sets specific bit of a byte + * @param byte + * @param position Position of byte to set from 1 to 8 + * @param value Binary value to set + * @return + */ + uint8_t bitSetter(uint8_t byte, uint8_t position, bool value); +}; + +#endif /* FRAMEWORK_DATAPOOL_POOLRAWACCESSHELPER_H_ */ diff --git a/datapool/PoolVariable.h b/datapool/PoolVariable.h index 25345c0ac..2c48ca3e1 100644 --- a/datapool/PoolVariable.h +++ b/datapool/PoolVariable.h @@ -112,8 +112,7 @@ public: * corresponds to. * \param dataSet The data set in which the variable shall register itself. If NULL, * the variable is not registered. - * \param setWritable If this flag is set to true, changes in the value attribute can be - * written back to the data pool, otherwise not. + * \param setReadWriteMode */ PoolVariable(uint32_t set_id, DataSetIF* dataSet, ReadWriteMode_t setReadWriteMode) : diff --git a/devicehandlers/AssemblyBase.cpp b/devicehandlers/AssemblyBase.cpp index c250cf735..bd85d1de5 100644 --- a/devicehandlers/AssemblyBase.cpp +++ b/devicehandlers/AssemblyBase.cpp @@ -2,10 +2,10 @@ AssemblyBase::AssemblyBase(object_id_t objectId, object_id_t parentId, uint16_t commandQueueDepth) : - SubsystemBase(objectId, parentId, MODE_OFF, commandQueueDepth), internalState( - STATE_NONE), recoveryState(RECOVERY_IDLE), recoveringDevice( - childrenMap.end()), targetMode(MODE_OFF), targetSubmode( - SUBMODE_NONE) { + SubsystemBase(objectId, parentId, MODE_OFF, commandQueueDepth), + internalState(STATE_NONE), recoveryState(RECOVERY_IDLE), + recoveringDevice(childrenMap.end()), targetMode(MODE_OFF), + targetSubmode(SUBMODE_NONE) { recoveryOffTimer.setTimeout(POWER_OFF_TIME_MS); } diff --git a/devicehandlers/CommunicationMessage.cpp b/devicehandlers/CommunicationMessage.cpp new file mode 100644 index 000000000..028e8a2e1 --- /dev/null +++ b/devicehandlers/CommunicationMessage.cpp @@ -0,0 +1,201 @@ +/** + * @file CommunicationMessage.cpp + * + * @date 28.02.2020 + */ + +#include +#include +#include + +CommunicationMessage::CommunicationMessage(): uninitialized(true) { +} + +CommunicationMessage::~CommunicationMessage() {} + +void CommunicationMessage::setSendRequestFromPointer(uint32_t address, + uint32_t dataLen, const uint8_t * data) { + setMessageType(SEND_DATA_FROM_POINTER); + setAddress(address); + setDataLen(dataLen); + setDataPointer(data); +} + +void CommunicationMessage::setSendRequestFromIpcStore(uint32_t address, store_address_t storeId) { + setMessageType(SEND_DATA_FROM_IPC_STORE); + setAddress(address); + setStoreId(storeId.raw); +} + +void CommunicationMessage::setSendRequestRaw(uint32_t address, uint32_t length, + uint16_t sendBufferPosition) { + setMessageType(SEND_DATA_RAW); + setAddress(address); + setDataLen(length); + if(sendBufferPosition != 0) { + setBufferPosition(sendBufferPosition); + } +} + +void CommunicationMessage::setDataReplyFromIpcStore(uint32_t address, store_address_t storeId) { + setMessageType(REPLY_DATA_IPC_STORE); + setAddress(address); + setStoreId(storeId.raw); +} +void CommunicationMessage::setDataReplyFromPointer(uint32_t address, + uint32_t dataLen, uint8_t *data) { + setMessageType(REPLY_DATA_FROM_POINTER); + setAddress(address); + setDataLen(dataLen); + setDataPointer(data); +} + +void CommunicationMessage::setDataReplyRaw(uint32_t address, + uint32_t length, uint16_t receiveBufferPosition) { + setMessageType(REPLY_DATA_RAW); + setAddress(address); + setDataLen(length); + if(receiveBufferPosition != 0) { + setBufferPosition(receiveBufferPosition); + } +} + +void CommunicationMessage::setMessageType(messageType status) { + uint8_t status_uint8 = status; + memcpy(getData() + sizeof(uint32_t), &status_uint8, sizeof(status_uint8)); +} + +void CommunicationMessage::setAddress(address_t address) { + memcpy(getData(),&address,sizeof(address)); +} + +address_t CommunicationMessage::getAddress() const { + address_t address; + memcpy(&address,getData(),sizeof(address)); + return address; +} + +void CommunicationMessage::setBufferPosition(uint16_t bufferPosition) { + memcpy(getData() + sizeof(uint32_t) + sizeof(uint16_t), + &bufferPosition, sizeof(bufferPosition)); +} + +uint16_t CommunicationMessage::getBufferPosition() const { + uint16_t bufferPosition; + memcpy(&bufferPosition, + getData() + sizeof(uint32_t) + sizeof(uint16_t), sizeof(bufferPosition)); + return bufferPosition; +} + +void CommunicationMessage::setDataPointer(const void * data) { + memcpy(getData() + 3 * sizeof(uint32_t), &data, sizeof(uint32_t)); +} + +void CommunicationMessage::setStoreId(store_address_t storeId) { + memcpy(getData() + 2 * sizeof(uint32_t), &storeId.raw, sizeof(uint32_t)); +} + +store_address_t CommunicationMessage::getStoreId() const{ + store_address_t temp; + memcpy(&temp.raw,getData() + 2 * sizeof(uint32_t), sizeof(uint32_t)); + return temp; +} + +void CommunicationMessage::setDataLen(uint32_t length) { + memcpy(getData() + 2 * sizeof(uint32_t), &length, sizeof(length)); +} + +uint32_t CommunicationMessage::getDataLen() const { + uint32_t len; + memcpy(&len, getData() + 2 * sizeof(uint32_t), sizeof(len)); + return len; +} + +void CommunicationMessage::setUint32Data(uint32_t data) { + memcpy(getData() + 3 * sizeof(uint32_t), &data, sizeof(data)); +} + +uint32_t CommunicationMessage::getUint32Data() const{ + uint32_t data; + memcpy(&data,getData() + 3 * sizeof(uint32_t), sizeof(data)); + return data; +} + +void CommunicationMessage::setDataByte(uint8_t byte, uint8_t position) { + if(0 <= position && position <= 3) { + memcpy(getData() + 3 * sizeof(uint32_t) + position * sizeof(uint8_t), &byte, sizeof(byte)); + } + else { + error << "Comm Message: Invalid byte position" << std::endl; + } +} + +uint8_t CommunicationMessage::getDataByte(uint8_t position) const { + if(0 <= position && position <= 3) { + uint8_t byte; + memcpy(&byte, getData() + 3 * sizeof(uint32_t) + position * sizeof(uint8_t), sizeof(byte)); + return byte; + } + else { + return 0; + error << "Comm Message: Invalid byte position" << std::endl; + } +} + +void CommunicationMessage::setDataUint16(uint16_t data, uint8_t position) { + if(position == 0 || position == 1) { + memcpy(getData() + 3 * sizeof(uint32_t) + position * sizeof(uint16_t), &data, sizeof(data)); + } + else { + error << "Comm Message: Invalid byte position" << std::endl; + } + +} + +uint16_t CommunicationMessage::getDataUint16(uint8_t position) const{ + if(position == 0 || position == 1) { + uint16_t data; + memcpy(&data, getData() + 3 * sizeof(uint32_t) + position * sizeof(uint16_t), sizeof(data)); + return data; + } + else { + return 0; + error << "Comm Message: Invalid byte position" << std::endl; + } +} + +CommunicationMessage::messageType CommunicationMessage::getMessageType() const{ + messageType messageType; + memcpy(&messageType, getData() + sizeof(uint32_t),sizeof(uint8_t)); + return messageType; +} + +void CommunicationMessage::setMessageId(uint8_t messageId) { + memcpy(getData() + sizeof(uint32_t) + sizeof(uint8_t), &messageId, sizeof(messageId)); +} + +uint8_t CommunicationMessage::getMessageId() const { + uint8_t messageId; + memcpy(&messageId, getData() + sizeof(uint32_t) + sizeof(uint8_t), sizeof(messageId)); + return messageId; +} + +void CommunicationMessage::clearCommunicationMessage() { + messageType messageType = getMessageType(); + switch(messageType) { + case(messageType::REPLY_DATA_IPC_STORE): + case(messageType::SEND_DATA_FROM_IPC_STORE): { + store_address_t storeId = getStoreId(); + StorageManagerIF *ipcStore = objectManager-> + get(objects::IPC_STORE); + if (ipcStore != NULL) { + ipcStore->deleteData(storeId); + } + } + /* NO BREAK falls through*/ + default: + memset(getData(),0,4*sizeof(uint32_t)); + break; + } +} + diff --git a/devicehandlers/CommunicationMessage.h b/devicehandlers/CommunicationMessage.h new file mode 100644 index 000000000..a92c4b5cf --- /dev/null +++ b/devicehandlers/CommunicationMessage.h @@ -0,0 +1,177 @@ +/** + * @file CommunicationMessage.h + * + * @date 28.02.2020 + */ + +#ifndef FRAMEWORK_DEVICEHANDLERS_COMMUNICATIONMESSAGE_H_ +#define FRAMEWORK_DEVICEHANDLERS_COMMUNICATIONMESSAGE_H_ +#include + +#include +#include +#include + +/** + * @brief Used to pass communication information between tasks + * + * @details + * Can be used to pass information like data pointers and + * data sizes between communication tasks + * like the Device Handler Comm Interfaces and Polling Tasks. + * + * Can also be used to exchange actual data if its not too large + * (e.g. Sensor values). + * + */ +class CommunicationMessage: public MessageQueueMessage { +public: + enum messageType { + NONE, + SEND_DATA_FROM_POINTER, + SEND_DATA_FROM_IPC_STORE, + SEND_DATA_RAW, + REPLY_DATA_FROM_POINTER, + REPLY_DATA_IPC_STORE, + REPLY_DATA_RAW, + FAULTY, + }; + + //Add other messageIDs here if necessary. + static const uint8_t COMMUNICATION_MESSAGE_SIZE = HEADER_SIZE + 4 * sizeof(uint32_t); + + CommunicationMessage(); + virtual ~CommunicationMessage(); + + /** + * Message Type is stored as the fifth byte of the message data + * @param status + */ + void setMessageType(messageType status); + messageType getMessageType() const; + + /** + * This is a unique ID which can be used to handle different kinds of messages. + * For example, the same interface (e.g. SPI) could be used to exchange raw data + * (e.g. sensor values) and data stored in the IPC store. + * The ID can be used to distinguish the messages in child implementations. + * The message ID is stored as the sixth byte of the message data. + * @param messageId + */ + void setMessageId(uint8_t messageId); + uint8_t getMessageId() const; + + /** + * Send requests with pointer to the data to be sent and send data length + * @param address Target Address, first four bytes + * @param dataLen Length of data to send, next four bytes + * @param data Pointer to data to send + * + */ + void setSendRequestFromPointer(uint32_t address, uint32_t dataLen, const uint8_t * data); + + /** + * Send requests with a store ID, using the IPC store + * @param address Target Address, first four bytes + * @param storeId Store ID in the IPC store + * + */ + void setSendRequestFromIpcStore(uint32_t address, store_address_t storeId); + + /** + * Send requests with data length and data in message (max. 4 bytes) + * @param address Target Address, first four bytes + * @param dataLen Length of data to send, next four bytes + * @param data Pointer to data to send + * + */ + void setSendRequestRaw(uint32_t address, uint32_t length, + uint16_t sendBufferPosition = 0); + + /** + * Data message with data stored in IPC store + * @param address Target Address, first four bytes + * @param length + * @param storeId + */ + void setDataReplyFromIpcStore(uint32_t address, store_address_t storeId); + + /** + * Data reply with data stored in buffer, passing the pointer to + * the buffer and the data size + * @param address Target Address, first four bytes + * @param dataLen Length of data to send, next four bytes + * @param data Pointer to the data + */ + void setDataReplyFromPointer(uint32_t address, uint32_t dataLen, uint8_t * data); + + /** + * Data message with data stored in actual message. + * 4 byte datafield is intialized with 0. + * Set data with specific setter functions below. + * Can also be used to supply information at which position the raw data should be stored + * in a receive buffer. + */ + void setDataReplyRaw(uint32_t address, uint32_t length, uint16_t receiveBufferPosition = 0); + + /** + * First four bytes of message data + * @param address + */ + void setAddress(address_t address); + + address_t getAddress() const; + /** + * Set byte as position of 4 byte data field + * @param byte + * @param position Position, 0 to 3 possible + */ + void setDataByte(uint8_t byte, uint8_t position); + uint8_t getDataByte(uint8_t position) const; + + /** + * Set 2 byte value at position 1 or 2 of data field + * @param data + * @param position 0 or 1 possible + */ + void setDataUint16(uint16_t data, uint8_t position); + uint16_t getDataUint16(uint8_t position) const; + + void setUint32Data(uint32_t data); + uint32_t getUint32Data() const; + + /** + * Stored in Bytes 13-16 of message data + * @param length + */ + void setDataLen(uint32_t length); + + uint32_t getDataLen() const; + + /** + * Stored in last four bytes (Bytes 17-20) of message data + * @param sendData + */ + void setDataPointer(const void * data); + + /** + * In case the send request data or reply data is to be stored in a buffer, + * a buffer Position can be stored here as the seventh and eigth byte of + * the message, so the receive buffer can't be larger than sizeof(uint16_t) for now. + * @param bufferPosition In case the data is stored in a buffer, the position can be supplied here + */ + void setBufferPosition(uint16_t bufferPosition); + uint16_t getBufferPosition() const; + void setStoreId(store_address_t storeId); + store_address_t getStoreId() const; + + /** + * Clear the message. Deletes IPC Store data + * and sets all data to 0. Also sets message type to NONE + */ + void clearCommunicationMessage(); +private: + bool uninitialized; //!< Could be used to warn if data has not been set. +}; + +#endif /* FRAMEWORK_DEVICEHANDLERS_COMMUNICATIONMESSAGE_H_ */ diff --git a/devicehandlers/Cookie.h b/devicehandlers/Cookie.h index 495c8c403..ec22ec3a5 100644 --- a/devicehandlers/Cookie.h +++ b/devicehandlers/Cookie.h @@ -1,6 +1,15 @@ #ifndef COOKIE_H_ #define COOKIE_H_ +/** + * @brief This datatype is used to identify different connection over a single interface + * (like RMAP or I2C) + * @details + * To use this class, implement a communication specific child cookie. This cookie + * can be used in the device communication interface by performing + * a C++ dynamic cast. The cookie can be used to store all kinds of information + * about the communication between read and send calls. + */ class Cookie{ public: virtual ~Cookie(){} diff --git a/devicehandlers/DeviceCommunicationIF.h b/devicehandlers/DeviceCommunicationIF.h index e0aca5731..9b0e0a3a9 100644 --- a/devicehandlers/DeviceCommunicationIF.h +++ b/devicehandlers/DeviceCommunicationIF.h @@ -3,7 +3,33 @@ #include #include +/** + * @defgroup interfaces Interfaces + * @brief Communication interfaces for flight software objects + */ +/** + * Physical address type + */ +typedef uint32_t address_t; + +/** + * @brief This is an interface to decouple device communication from + * the device handler to allow reuse of these components. + * @details + * Documentation: Dissertation Baetz p.138 + * It works with the assumption that received data + * is polled by a component. There are four generic steps of device communication: + * + * 1. Send data to a device + * 2. Get acknowledgement for sending + * 3. Request reading data from a device + * 4. Read received data + * + * To identify different connection over a single interface can return so-called cookies to components. + * The CommunicationMessage message type can be used to extend the functionality of the + * ComIF if a separate polling task is required. + */ class DeviceCommunicationIF: public HasReturnvaluesIF { public: static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_COMMUNICATION_IF; @@ -16,48 +42,96 @@ public: static const ReturnValue_t PROTOCOL_ERROR = MAKE_RETURN_CODE(0x06); static const ReturnValue_t CANT_CHANGE_REPLY_LEN = MAKE_RETURN_CODE(0x07); - virtual ~DeviceCommunicationIF() { + virtual ~DeviceCommunicationIF() {} - } - - virtual ReturnValue_t open(Cookie **cookie, uint32_t address, + /** + * Open a connection. Define a communication specific cookie which can + * be used to store information about the communication. + * + * @param cookie [in/out] This data class stores information about the communication. + * @param address Logical device address + * @param maxReplyLen Maximum length of expected reply + * @return + */ + virtual ReturnValue_t open(Cookie **cookie, address_t address, uint32_t maxReplyLen) = 0; /** * Use an existing cookie to open a connection to a new DeviceCommunication. * The previous connection must not be closed. - * If the returnvalue is not RETURN_OK, the cookie is unchanged and - * can be used with the previous connection. * * @param cookie * @param address * @param maxReplyLen - * @return + * @return -@c RETURN_OK New communication set up successfully + * - Everything else: Cookie is unchanged and can be used with + * previous connection */ - virtual ReturnValue_t reOpen(Cookie *cookie, uint32_t address, + virtual ReturnValue_t reOpen(Cookie *cookie, address_t address, uint32_t maxReplyLen) = 0; + /** + * Closing call of connection. Don't forget to free memory of cookie. + * @param cookie + */ virtual void close(Cookie *cookie) = 0; - //SHOULDDO can data be const? - virtual ReturnValue_t sendMessage(Cookie *cookie, uint8_t *data, + /** + * Called by DHB in the SEND_WRITE doSendWrite(). + * This function is used to send data to the physical device + * by implementing and calling related drivers or wrapper functions. + * @param cookie + * @param data + * @param len + * @return -@c RETURN_OK for successfull send + * -Everything else triggers sending failed event with + * returnvalue as parameter 1 + */ + virtual ReturnValue_t sendMessage(Cookie *cookie, const uint8_t *data, uint32_t len) = 0; virtual ReturnValue_t getSendSuccess(Cookie *cookie) = 0; + /** + * Called by DHB in the SEND_WRITE doSendRead(). + * Instructs the Communication Interface to prepare + * @param cookie + * @return + */ virtual ReturnValue_t requestReceiveMessage(Cookie *cookie) = 0; + /** + * Called by DHB in the GET_WRITE doGetRead(). + * This function is used to receive data from the physical device + * by implementing and calling related drivers or wrapper functions. + * @param cookie + * @param data + * @param len + * @return @c RETURN_OK for successfull receive + * Everything else triggers receiving failed with returnvalue as parameter 1 + */ virtual ReturnValue_t readReceivedMessage(Cookie *cookie, uint8_t **buffer, uint32_t *size) = 0; - virtual ReturnValue_t setAddress(Cookie *cookie, uint32_t address) = 0; + virtual ReturnValue_t setAddress(Cookie *cookie, address_t address) = 0; - virtual uint32_t getAddress(Cookie *cookie) = 0; + virtual address_t getAddress(Cookie *cookie) = 0; + /** + * Can be used by DeviceHandlerBase getParameter() call to set DeviceComIF parameters + * @param cookie + * @param parameter + * @return + */ virtual ReturnValue_t setParameter(Cookie *cookie, uint32_t parameter) = 0; + /** + * Can be used by DeviceHandlerBase getParameter() call to set DeviceComIF parameters + * @param cookie + * @param parameter + * @return + */ virtual uint32_t getParameter(Cookie *cookie) = 0; - }; #endif /* DEVICECOMMUNICATIONIF_H_ */ diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index ae9199f2c..e7b4c6844 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -16,32 +16,32 @@ object_id_t DeviceHandlerBase::powerSwitcherId = 0; object_id_t DeviceHandlerBase::rawDataReceiverId = 0; object_id_t DeviceHandlerBase::defaultFDIRParentId = 0; -DeviceHandlerBase::DeviceHandlerBase(uint32_t ioBoardAddress, +DeviceHandlerBase::DeviceHandlerBase(uint32_t logicalAddress_, object_id_t setObjectId, uint32_t maxDeviceReplyLen, uint8_t setDeviceSwitch, object_id_t deviceCommunication, uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId, FailureIsolationBase* fdirInstance, uint32_t cmdQueueSize) : - SystemObject(setObjectId), rawPacket(0), rawPacketLen(0), mode( - MODE_OFF), submode(SUBMODE_NONE), pstStep(0), maxDeviceReplyLen( - maxDeviceReplyLen), wiretappingMode(OFF), defaultRawReceiver(0), storedRawData( - StorageManagerIF::INVALID_ADDRESS), requestedRawTraffic(0), powerSwitcher( - NULL), IPCStore(NULL), deviceCommunicationId(deviceCommunication), communicationInterface( - NULL), cookie( - NULL), commandQueue(NULL), deviceThermalStatePoolId(thermalStatePoolId), deviceThermalRequestPoolId( - thermalRequestPoolId), healthHelper(this, setObjectId), modeHelper( - this), parameterHelper(this), childTransitionFailure(RETURN_OK), ignoreMissedRepliesCount( - 0), fdirInstance(fdirInstance), hkSwitcher(this), defaultFDIRUsed( - fdirInstance == NULL), switchOffWasReported(false),executingTask(NULL), actionHelper(this, NULL), cookieInfo(), ioBoardAddress( - ioBoardAddress), timeoutStart(0), childTransitionDelay(5000), transitionSourceMode( - _MODE_POWER_DOWN), transitionSourceSubMode(SUBMODE_NONE), deviceSwitch( - setDeviceSwitch) { - commandQueue = QueueFactory::instance()->createMessageQueue(cmdQueueSize, - CommandMessage::MAX_MESSAGE_SIZE); + SystemObject(setObjectId), rawPacket(0), rawPacketLen(0), mode(MODE_OFF), + submode(SUBMODE_NONE), pstStep(0), maxDeviceReplyLen(maxDeviceReplyLen), + wiretappingMode(OFF), defaultRawReceiver(0), storedRawData(StorageManagerIF::INVALID_ADDRESS), + requestedRawTraffic(0), powerSwitcher(NULL), IPCStore(NULL), + deviceCommunicationId(deviceCommunication), communicationInterface(NULL), + cookie(NULL), commandQueue(NULL), deviceThermalStatePoolId(thermalStatePoolId), + deviceThermalRequestPoolId(thermalRequestPoolId), healthHelper(this, setObjectId), + modeHelper(this), parameterHelper(this), childTransitionFailure(RETURN_OK), + ignoreMissedRepliesCount(0), fdirInstance(fdirInstance), hkSwitcher(this), + defaultFDIRUsed(fdirInstance == NULL), switchOffWasReported(false), + executingTask(NULL), actionHelper(this, NULL), cookieInfo(), logicalAddress(logicalAddress_), + timeoutStart(0), childTransitionDelay(5000), transitionSourceMode(_MODE_POWER_DOWN), + transitionSourceSubMode(SUBMODE_NONE), deviceSwitch(setDeviceSwitch) +{ + commandQueue = QueueFactory::instance()-> + createMessageQueue(cmdQueueSize, CommandMessage::MAX_MESSAGE_SIZE); cookieInfo.state = COOKIE_UNUSED; insertInCommandMap(RAW_COMMAND_ID); if (this->fdirInstance == NULL) { - this->fdirInstance = new DeviceHandlerFailureIsolation(setObjectId, - defaultFDIRParentId); + this->fdirInstance = + new DeviceHandlerFailureIsolation(setObjectId, defaultFDIRParentId); } } @@ -55,7 +55,6 @@ DeviceHandlerBase::~DeviceHandlerBase() { ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { this->pstStep = counter; - if (counter == 0) { cookieInfo.state = COOKIE_UNUSED; readCommandQueue(); @@ -64,10 +63,12 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { decrementDeviceReplyMap(); fdirInstance->checkForFailures(); hkSwitcher.performOperation(); + performOperationHook(); } if (mode == MODE_OFF) { return RETURN_OK; } + switch (getRmapAction()) { case SEND_WRITE: if ((cookieInfo.state == COOKIE_UNUSED)) { @@ -88,6 +89,85 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { default: break; } + + return RETURN_OK; +} + +ReturnValue_t DeviceHandlerBase::initialize() { + ReturnValue_t result = SystemObject::initialize(); + if (result != RETURN_OK) { + return result; + } + + communicationInterface = objectManager->get( + deviceCommunicationId); + if (communicationInterface == NULL) { + return RETURN_FAILED; + } + + result = communicationInterface->open(&cookie, logicalAddress, + maxDeviceReplyLen); + if (result != RETURN_OK) { + return result; + } + + IPCStore = objectManager->get(objects::IPC_STORE); + if (IPCStore == NULL) { + return RETURN_FAILED; + } + + AcceptsDeviceResponsesIF *rawReceiver = objectManager->get< + AcceptsDeviceResponsesIF>(rawDataReceiverId); + + if (rawReceiver == NULL) { + return RETURN_FAILED; + } + + defaultRawReceiver = rawReceiver->getDeviceQueue(); + + powerSwitcher = objectManager->get(powerSwitcherId); + if (powerSwitcher == NULL) { + return RETURN_FAILED; + } + + result = healthHelper.initialize(); + if (result != RETURN_OK) { + return result; + } + + result = modeHelper.initialize(); + if (result != RETURN_OK) { + return result; + } + result = actionHelper.initialize(commandQueue); + if (result != RETURN_OK) { + return result; + } + result = fdirInstance->initialize(); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + result = parameterHelper.initialize(); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + result = hkSwitcher.initialize(); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + fillCommandAndReplyMap(); + + //Set temperature target state to NON_OP. + DataSet mySet; + PoolVariable thermalRequest(deviceThermalRequestPoolId, &mySet, + PoolVariableIF::VAR_WRITE); + mySet.read(); + thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; + mySet.commit(PoolVariableIF::VALID); + return RETURN_OK; } @@ -455,7 +535,8 @@ void DeviceHandlerBase::doGetWrite() { if (wiretappingMode == RAW) { replyRawData(rawPacket, rawPacketLen, requestedRawTraffic, true); } - //We need to distinguish here, because a raw command never expects a reply. (Could be done in eRIRM, but then child implementations need to be careful. + //We need to distinguish here, because a raw command never expects a reply. + //(Could be done in eRIRM, but then child implementations need to be careful. result = enableReplyInReplyMap(cookieInfo.pendingCommand); } else { //always generate a failure event, so that FDIR knows what's up @@ -470,7 +551,6 @@ void DeviceHandlerBase::doGetWrite() { void DeviceHandlerBase::doSendRead() { ReturnValue_t result; - result = communicationInterface->requestReceiveMessage(cookie); if (result == RETURN_OK) { cookieInfo.state = COOKIE_READ_SENT; @@ -539,6 +619,8 @@ void DeviceHandlerBase::doGetRead() { break; case IGNORE_REPLY_DATA: break; + case IGNORE_FULL_PACKET: + return; default: //We need to wait for timeout.. don't know what command failed and who sent it. replyRawReplyIfnotWiretapped(receivedData, foundLen); @@ -579,84 +661,6 @@ ReturnValue_t DeviceHandlerBase::getStorageData(store_address_t storageAddress, } -ReturnValue_t DeviceHandlerBase::initialize() { - ReturnValue_t result = SystemObject::initialize(); - if (result != RETURN_OK) { - return result; - } - - communicationInterface = objectManager->get( - deviceCommunicationId); - if (communicationInterface == NULL) { - return RETURN_FAILED; - } - - result = communicationInterface->open(&cookie, ioBoardAddress, - maxDeviceReplyLen); - if (result != RETURN_OK) { - return result; - } - - IPCStore = objectManager->get(objects::IPC_STORE); - if (IPCStore == NULL) { - return RETURN_FAILED; - } - - AcceptsDeviceResponsesIF *rawReceiver = objectManager->get< - AcceptsDeviceResponsesIF>(rawDataReceiverId); - - if (rawReceiver == NULL) { - return RETURN_FAILED; - } - - defaultRawReceiver = rawReceiver->getDeviceQueue(); - - powerSwitcher = objectManager->get(powerSwitcherId); - if (powerSwitcher == NULL) { - return RETURN_FAILED; - } - - result = healthHelper.initialize(); - if (result != RETURN_OK) { - return result; - } - - result = modeHelper.initialize(); - if (result != RETURN_OK) { - return result; - } - result = actionHelper.initialize(commandQueue); - if (result != RETURN_OK) { - return result; - } - result = fdirInstance->initialize(); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - - result = parameterHelper.initialize(); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - - result = hkSwitcher.initialize(); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - - fillCommandAndReplyMap(); - - //Set temperature target state to NON_OP. - DataSet mySet; - PoolVariable thermalRequest(deviceThermalRequestPoolId, &mySet, - PoolVariableIF::VAR_WRITE); - mySet.read(); - thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; - mySet.commit(PoolVariableIF::VALID); - - return RETURN_OK; - -} void DeviceHandlerBase::replyRawData(const uint8_t *data, size_t len, MessageQueueId_t sendTo, bool isCommand) { @@ -672,7 +676,6 @@ void DeviceHandlerBase::replyRawData(const uint8_t *data, size_t len, } CommandMessage message; - DeviceHandlerMessage::setDeviceHandlerRawReplyMessage(&message, getObjectId(), address, isCommand); @@ -753,7 +756,7 @@ ReturnValue_t DeviceHandlerBase::switchCookieChannel(object_id_t newChannelId) { DeviceCommunicationIF>(newChannelId); if (newCommunication != NULL) { - ReturnValue_t result = newCommunication->reOpen(cookie, ioBoardAddress, + ReturnValue_t result = newCommunication->reOpen(cookie, logicalAddress, maxDeviceReplyLen); if (result != RETURN_OK) { return result; @@ -837,8 +840,9 @@ ReturnValue_t DeviceHandlerBase::getStateOfSwitches(void) { ReturnValue_t result = getSwitches(&switches, &numberOfSwitches); if ((result == RETURN_OK) && (numberOfSwitches != 0)) { while (numberOfSwitches > 0) { - if (powerSwitcher->getSwitchState(switches[numberOfSwitches - 1]) - == PowerSwitchIF::SWITCH_OFF) { + if (powerSwitcher-> getSwitchState(switches[numberOfSwitches - 1]) + == PowerSwitchIF::SWITCH_OFF) + { return PowerSwitchIF::SWITCH_OFF; } numberOfSwitches--; @@ -1112,8 +1116,7 @@ void DeviceHandlerBase::handleDeviceTM(SerializeIF* data, // hiding of sender needed so the service will handle it as unexpected Data, no matter what state //(progress or completed) it is in - actionHelper.reportData(defaultRawReceiver, replyId, &wrapper, - true); + actionHelper.reportData(defaultRawReceiver, replyId, &wrapper, true); } } else { //unrequested/aperiodic replies @@ -1270,3 +1273,13 @@ void DeviceHandlerBase::changeHK(Mode_t mode, Submode_t submode, bool enable) { void DeviceHandlerBase::setTaskIF(PeriodicTaskIF* task_){ executingTask = task_; } + +void DeviceHandlerBase::debugInterface(uint8_t positionTracker, object_id_t objectId, uint32_t parameter) { +} + +uint32_t DeviceHandlerBase::getLogicalAddress() { + return logicalAddress; +} + +void DeviceHandlerBase::performOperationHook() { +} diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index 756ad530d..60e8888e2 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -18,9 +18,9 @@ #include #include #include -#include #include #include +#include namespace Factory{ void setStaticFrameworkObjectIds(); @@ -34,24 +34,42 @@ class StorageManagerIF; */ /** - * \brief This is the abstract base class for device handlers. - * + * @brief This is the abstract base class for device handlers. + * @details * Documentation: Dissertation Baetz p.138,139, p.141-149 - * SpaceWire Remote Memory Access Protocol (RMAP) * - * It features handling of @link DeviceHandlerIF::Mode_t Modes @endlink, the RMAP communication and the - * communication with commanding objects. + * It features handling of @link DeviceHandlerIF::Mode_t Modes @endlink, + * communication with physical devices, using the @link DeviceCommunicationIF @endlink, + * and communication with commanding objects. * It inherits SystemObject and thus can be created by the ObjectManagerIF. * * This class uses the opcode of ExecutableObjectIF to perform a step-wise execution. - * For each step an RMAP action is selected and executed. If data has been received (eg in case of an RMAP Read), the data will be interpreted. - * The action for each step can be defined by the child class but as most device handlers share a 4-call (Read-getRead-write-getWrite) structure, - * a default implementation is provided. + * For each step an RMAP action is selected and executed. + * If data has been received (GET_READ), the data will be interpreted. + * The action for each step can be defined by the child class but as most + * device handlers share a 4-call (sendRead-getRead-sendWrite-getWrite) structure, + * a default implementation is provided. NOTE: RMAP is a standard which is used for FLP. + * RMAP communication is not mandatory for projects implementing the FSFW. + * However, the communication principles are similar to RMAP as there are + * two write and two send calls involved. * * Device handler instances should extend this class and implement the abstract functions. * Components and drivers can send so called cookies which are used for communication + * and contain information about the communcation (e.g. slave address for I2C or RMAP structs). + * The following abstract methods must be implemented by a device handler: + * 1. doStartUp() + * 2. doShutDown() + * 3. buildTransitionDeviceCommand() + * 4. buildNormalDeviceCommand() + * 5. buildCommandFromCommand() + * 6. fillCommandAndReplyMap() + * 7. scanForReply() + * 8. interpretDeviceReply() * - * \ingroup devices + * Other important virtual methods with a default implementation + * are the getTransitionDelayMs() function and the getSwitches() function. + * + * @ingroup devices */ class DeviceHandlerBase: public DeviceHandlerIF, public HasReturnvaluesIF, @@ -67,57 +85,286 @@ public: * The constructor passes the objectId to the SystemObject(). * * @param setObjectId the ObjectId to pass to the SystemObject() Constructor - * @param maxDeviceReplyLen the length the RMAP getRead call will be sent with + * @param maxDeviceReplyLen the largest allowed reply size * @param setDeviceSwitch the switch the device is connected to, for devices using two switches, overwrite getSwitches() + * @param deviceCommuncation Communcation Interface object which is used to implement communication functions + * @param thermalStatePoolId + * @param thermalRequestPoolId + * @param fdirInstance + * @param cmdQueueSize */ - DeviceHandlerBase(uint32_t ioBoardAddress, object_id_t setObjectId, + DeviceHandlerBase(address_t logicalAddress, object_id_t setObjectId, uint32_t maxDeviceReplyLen, uint8_t setDeviceSwitch, - object_id_t deviceCommunication, uint32_t thermalStatePoolId = - PoolVariableIF::NO_PARAMETER, + object_id_t deviceCommunication, + uint32_t thermalStatePoolId = PoolVariableIF::NO_PARAMETER, uint32_t thermalRequestPoolId = PoolVariableIF::NO_PARAMETER, FailureIsolationBase* fdirInstance = NULL, uint32_t cmdQueueSize = 20); - virtual MessageQueueId_t getCommandQueue(void) const; - - /** - * This function is a core component and is called periodically. - * General sequence: - * If the State is SEND_WRITE: - * 1. Set the cookie state to COOKIE_UNUSED and read the command queue - * 2. Handles Device State Modes by calling doStateMachine(). - * This function calls callChildStatemachine() which calls the abstract functions - * doStartUp() and doShutDown() - * 3. Check switch states by calling checkSwitchStates() - * 4. Decrements counter for timeout of replies by calling decrementDeviceReplyMap() - * 5. Performs FDIR check for failures - * 6. Calls hkSwitcher.performOperation() - * 7. If the device mode is MODE_OFF, return RETURN_OK. Otherwise, perform the Action property - * and performs depending on value specified - * by input value counter. The child class tells base class what to do by setting this value. - * - SEND_WRITE: Send data or commands to device by calling doSendWrite() - * Calls abstract funtions buildNomalDeviceCommand() - * or buildTransitionDeviceCommand() - * - GET_WRITE: Get ackknowledgement for sending by calling doGetWrite(). - * Calls abstract functions scanForReply() and interpretDeviceReply(). - * - SEND_READ: Request reading data from device by calling doSendRead() - * - GET_READ: Access requested reading data by calling doGetRead() - * @param counter Specifies which Action to perform - * @return RETURN_OK for successful execution - */ + * @brief This function is the device handler base core component and is called periodically. + * @details + * General sequence, showing where abstract virtual functions are called: + * If the State is SEND_WRITE: + * 1. Set the cookie state to COOKIE_UNUSED and read the command queue + * 2. Handles Device State Modes by calling doStateMachine(). + * This function calls callChildStatemachine() which calls the abstract functions + * doStartUp() and doShutDown() + * 3. Check switch states by calling checkSwitchStates() + * 4. Decrements counter for timeout of replies by calling decrementDeviceReplyMap() + * 5. Performs FDIR check for failures + * 6. Calls hkSwitcher.performOperation() + * 7. If the device mode is MODE_OFF, return RETURN_OK. Otherwise, perform the Action property + * and performs depending on value specified + * by input value counter. The child class tells base class what to do by setting this value. + * - SEND_WRITE: Send data or commands to device by calling doSendWrite() which calls + * sendMessage function of #communicationInterface + * and calls buildInternalCommand if the cookie state is COOKIE_UNUSED + * - GET_WRITE: Get ackknowledgement for sending by calling doGetWrite() which calls + * getSendSuccess of #communicationInterface. + * Calls abstract functions scanForReply() and interpretDeviceReply(). + * - SEND_READ: Request reading data from device by calling doSendRead() which calls + * requestReceiveMessage of #communcationInterface + * - GET_READ: Access requested reading data by calling doGetRead() which calls + * readReceivedMessage of #communicationInterface + * @param counter Specifies which Action to perform + * @return RETURN_OK for successful execution + */ virtual ReturnValue_t performOperation(uint8_t counter); + /** + * @brief Initializes the device handler + * @details + * Initialize Device Handler as system object and + * initializes all important helper classes. + * Calls fillCommandAndReplyMap(). + * @return + */ virtual ReturnValue_t initialize(); - /** - * - * @param parentQueueId - */ - virtual void setParentQueue(MessageQueueId_t parentQueueId); /** * Destructor. */ virtual ~DeviceHandlerBase(); +protected: + /** + * This is used to let the child class handle the transition from mode @c _MODE_START_UP to @c MODE_ON + * + * It is only called when the device handler is in mode @c _MODE_START_UP. That means, the device switch(es) are already set to on. + * Device handler commands are read and can be handled by the child class. If the child class handles a command, it should also send + * an reply accordingly. + * If an Command is not handled (ie #DeviceHandlerCommand is not @c CMD_NONE, the base class handles rejecting the command and sends a reply. + * The replies for mode transitions are handled by the base class. + * + * If the device is started and ready for operation, the mode should be set to MODE_ON. It is possible to set the mode to _MODE_TO_ON to + * use the to on transition if available. + * If the power-up fails, the mode should be set to _MODE_POWER_DOWN which will lead to the device being powered off. + * If the device does not change the mode, the mode will be changed to _MODE_POWER_DOWN, after the timeout (from getTransitionDelay()) has passed. + * + * #transitionFailure can be set to a failure code indicating the reason for a failed transition + */ + virtual void doStartUp() = 0; + + /** + * This is used to let the child class handle the transition from mode @c _MODE_SHUT_DOWN to @c _MODE_POWER_DOWN + * + * It is only called when the device handler is in mode @c _MODE_SHUT_DOWN. + * Device handler commands are read and can be handled by the child class. If the child class handles a command, it should also send + * an reply accordingly. + * If an Command is not handled (ie #DeviceHandlerCommand is not @c CMD_NONE, the base class handles rejecting the command and sends a reply. + * The replies for mode transitions are handled by the base class. + * + * If the device ready to be switched off, the mode should be set to _MODE_POWER_DOWN. + * If the device should not be switched off, the mode can be changed to _MODE_TO_ON (or MODE_ON if no transition is needed). + * If the device does not change the mode, the mode will be changed to _MODE_POWER_DOWN, when the timeout (from getTransitionDelay()) has passed. + * + * #transitionFailure can be set to a failure code indicating the reason for a failed transition + */ + virtual void doShutDown() = 0; + + /** + * Build the device command to send for normal mode. + * + * This is only called in @c MODE_NORMAL. If multiple submodes for @c MODE_NORMAL are supported, + * different commands can built returned depending on the submode. + * + * #rawPacket and #rawPacketLen must be set by this method to the packet to be sent. + * + * @param[out] id the device command id that has been built + * @return + * - @c RETURN_OK when a command is to be sent + * - not @c RETURN_OK when no command is to be sent + */ + virtual ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t * id) = 0; + + /** + * Build the device command to send for a transitional mode. + * + * This is only called in @c _MODE_TO_NORMAL, @c _MODE_TO_ON, @c _MODE_TO_RAW, + * @c _MODE_START_UP and @c _MODE_TO_POWER_DOWN. So it is used by doStartUp() and doShutDown() as well as doTransition() + * + * A good idea is to implement a flag indicating a command has to be built and a variable containing the command number to be built + * and filling them in doStartUp(), doShutDown() and doTransition() so no modes have to be checked here. + * + * #rawPacket and #rawPacketLen must be set by this method to the packet to be sent. + * + * @param[out] id the device command id built + * @return + * - @c RETURN_OK when a command is to be sent + * - not @c RETURN_OK when no command is to be sent + */ + virtual ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t * id) = 0; + + /** + * Build a device command packet from data supplied by a direct command. + * + * #rawPacket and #rawPacketLen should be set by this method to the packet to be sent. + * + * @param deviceCommand the command to build, already checked against deviceCommandMap + * @param commandData pointer to the data from the direct command + * @param commandDataLen length of commandData + * @return + * - @c RETURN_OK when #rawPacket is valid + * - @c RETURN_FAILED when #rawPacket is invalid and no data should be sent + */ + virtual ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, + const uint8_t * commandData, size_t commandDataLen) = 0; + + /** + * @brief fill the #deviceCommandMap + * called by the initialize() of the base class + * @details + * This is used to let the base class know which replies are expected. + * There are different scenarios regarding this: + * - "Normal" commands. These are commands, that trigger a direct reply from the device. + * In this case, the id of the command should be added to the command map + * with a commandData_t where maxDelayCycles is set to the maximum expected + * number of PST cycles the reply will take. Then, scanForReply returns + * the id of the command and the base class can handle time-out and missing replies. + * - Periodic, unrequested replies. These are replies that, once enabled, are sent by the device + * on its own in a defined interval. In this case, the id of the reply + * or a placeholder id should be added to the deviceCommandMap with a commandData_t + * where maxDelayCycles is set to the maximum expected number of PST cycles between + * two replies (also a tolerance should be added, as an FDIR message will be generated if it is missed). + * As soon as the replies are enabled, DeviceCommandInfo.periodic must be set to 1, + * DeviceCommandInfo.delayCycles to DeviceCommandInfo.MaxDelayCycles. + * From then on, the base class handles the reception. + * Then, scanForReply returns the id of the reply or the placeholder id and the base class will + * take care of checking that all replies are received and the interval is correct. + * When the replies are disabled, DeviceCommandInfo.periodic must be set to 0, + * DeviceCommandInfo.delayCycles to 0; + * - Aperiodic, unrequested replies. These are replies that are sent + * by the device without any preceding command and not in a defined interval. + * These are not entered in the deviceCommandMap but handled by returning @c APERIODIC_REPLY in scanForReply(). + * + */ + virtual void fillCommandAndReplyMap() = 0; + + /** + * @brief Scans a buffer for a valid reply. + * @details + * This is used by the base class to check the data received for valid packets. + * It only checks if a valid packet starts at @c start. + * It also only checks the structural validy of the packet, eg checksums lengths and protocol data. + * No information check is done, e.g. range checks etc. + * + * Errors should be reported directly, the base class does NOT report any errors based on the return + * value of this function. + * + * @param start start of remaining buffer to be scanned + * @param len length of remaining buffer to be scanned + * @param[out] foundId the id of the data found in the buffer. + * @param[out] foundLen length of the data found. Is to be set in function, buffer is scanned at previous position + foundLen. + * @return + * - @c RETURN_OK a valid packet was found at @c start, @c foundLen is valid + * - @c RETURN_FAILED no reply could be found starting at @c start, implies @c foundLen is not valid, base class will call scanForReply() again with ++start + * - @c DeviceHandlerIF::INVALID_DATA a packet was found but it is invalid, eg checksum error, implies @c foundLen is valid, can be used to skip some bytes + * - @c DeviceHandlerIF::LENGTH_MISSMATCH @c len is invalid + * - @c DeviceHandlerIF::IGNORE_REPLY_DATA Ignore this specific part of the packet + * - @c DeviceHandlerIF::IGNORE_FULL_PACKET Ignore the packet + * - @c APERIODIC_REPLY if a valid reply is received that has not been requested by a command, but should be handled anyway (@see also fillCommandAndCookieMap() ) + */ + virtual ReturnValue_t scanForReply(const uint8_t *start, uint32_t len, + DeviceCommandId_t *foundId, uint32_t *foundLen) = 0; + + /** + * @brief Interpret a reply from the device. + * @details + * This is called after scanForReply() found a valid packet, it can be assumed that the length and structure is valid. + * This routine extracts the data from the packet into a DataSet and then calls handleDeviceTM(), which either sends + * a TM packet or stores the data in the DataPool depending on whether the it was an external command. + * No packet length is given, as it should be defined implicitly by the id. + * + * @param id the id found by scanForReply() + * @param packet + * @return + * - @c RETURN_OK when the reply was interpreted. + * - @c RETURN_FAILED when the reply could not be interpreted, eg. logical errors or range violations occurred + */ + virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, + const uint8_t *packet) = 0; + + /** + * set all datapool variables that are update periodically in normal mode invalid + * + * Child classes should provide an implementation which sets all those variables invalid + * which are set periodically during any normal mode. + */ + virtual void setNormalDatapoolEntriesInvalid() = 0; + + /** + * @brief Can be implemented by child handler to + * perform debugging + * @details Example: Calling this in performOperation + * to track values like mode. + * @param positionTracker Provide the child handler a way to know where the debugInterface was called + * @param objectId Provide the child handler object Id to specify actions for spefic devices + * @param parameter Supply a parameter of interest + * Please delete all debugInterface calls in DHB after debugging is finished ! + */ + virtual void debugInterface(uint8_t positionTracker = 0, object_id_t objectId = 0, uint32_t parameter = 0); + + /** + * Get the time needed to transit from modeFrom to modeTo. + * + * Used for the following transitions: + * modeFrom -> modeTo: + * - MODE_ON -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] + * - MODE_NORMAL -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] + * - MODE_RAW -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] + * - _MODE_START_UP -> MODE_ON (do not include time to set the switches, the base class got you covered) + * + * The default implementation returns 0 ! + * @param modeFrom + * @param modeTo + * @return time in ms + */ + virtual uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo); + + /** + * Return the switches connected to the device. + * + * The default implementation returns one switch set in the ctor. + * + * @param[out] switches pointer to an array of switches + * @param[out] numberOfSwitches length of returned array + * @return + * - @c RETURN_OK if the parameters were set + * - @c RETURN_FAILED if no switches exist + */ + virtual ReturnValue_t getSwitches(const uint8_t **switches, + uint8_t *numberOfSwitches); + + /** + * Can be used to perform device specific periodic operations. + * This is called on the SEND_READ step of the performOperation() call + */ + virtual void performOperationHook(); + +public: + /** + * @param parentQueueId + */ + virtual void setParentQueue(MessageQueueId_t parentQueueId); ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, uint32_t size); @@ -136,6 +383,8 @@ public: * @param task_ Pointer to the taskIF of this task */ virtual void setTaskIF(PeriodicTaskIF* task_); + virtual MessageQueueId_t getCommandQueue(void) const; + protected: /** * The Returnvalues id of this class, required by HasReturnvaluesIF @@ -143,8 +392,9 @@ protected: static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_BASE; static const ReturnValue_t INVALID_CHANNEL = MAKE_RETURN_CODE(4); - static const ReturnValue_t APERIODIC_REPLY = MAKE_RETURN_CODE(5); - static const ReturnValue_t IGNORE_REPLY_DATA = MAKE_RETURN_CODE(6); + static const ReturnValue_t APERIODIC_REPLY = MAKE_RETURN_CODE(5); //!< This is used to specify for replies from a device which are not replies to requests + static const ReturnValue_t IGNORE_REPLY_DATA = MAKE_RETURN_CODE(6); //!< Ignore parts of the received packet + static const ReturnValue_t IGNORE_FULL_PACKET = MAKE_RETURN_CODE(7); //!< Ignore full received packet // static const ReturnValue_t ONE_SWITCH = MAKE_RETURN_CODE(8); // static const ReturnValue_t TWO_SWITCHES = MAKE_RETURN_CODE(9); static const ReturnValue_t NO_SWITCH = MAKE_RETURN_CODE(10); @@ -242,7 +492,8 @@ protected: DeviceCommunicationIF *communicationInterface; /** - * Cookie used for communication + * Cookie used for communication. This is passed to the communication + * interface. */ Cookie *cookie; @@ -318,9 +569,10 @@ protected: uint32_t parameter = 0); /** - * - - * @param parameter2 additional parameter + * Send reply to a command, differentiate between raw command + * and normal command. + * @param status + * @param parameter */ void replyToCommand(ReturnValue_t status, uint32_t parameter = 0); @@ -345,41 +597,6 @@ protected: */ void setMode(Mode_t newMode, Submode_t submode); - /** - * This is used to let the child class handle the transition from mode @c _MODE_START_UP to @c MODE_ON - * - * It is only called when the device handler is in mode @c _MODE_START_UP. That means, the device switch(es) are already set to on. - * Device handler commands are read and can be handled by the child class. If the child class handles a command, it should also send - * an reply accordingly. - * If an Command is not handled (ie #DeviceHandlerCommand is not @c CMD_NONE, the base class handles rejecting the command and sends a reply. - * The replies for mode transitions are handled by the base class. - * - * If the device is started and ready for operation, the mode should be set to MODE_ON. It is possible to set the mode to _MODE_TO_ON to - * use the to on transition if available. - * If the power-up fails, the mode should be set to _MODE_POWER_DOWN which will lead to the device being powered off. - * If the device does not change the mode, the mode will be changed to _MODE_POWER_DOWN, after the timeout (from getTransitionDelay()) has passed. - * - * #transitionFailure can be set to a failure code indicating the reason for a failed transition - */ - virtual void doStartUp() = 0; - - /** - * This is used to let the child class handle the transition from mode @c _MODE_SHUT_DOWN to @c _MODE_POWER_DOWN - * - * It is only called when the device handler is in mode @c _MODE_SHUT_DOWN. - * Device handler commands are read and can be handled by the child class. If the child class handles a command, it should also send - * an reply accordingly. - * If an Command is not handled (ie #DeviceHandlerCommand is not @c CMD_NONE, the base class handles rejecting the command and sends a reply. - * The replies for mode transitions are handled by the base class. - * - * If the device ready to be switched off, the mode should be set to _MODE_POWER_DOWN. - * If the device should not be switched off, the mode can be changed to _MODE_TO_ON (or MODE_ON if no transition is needed). - * If the device does not change the mode, the mode will be changed to _MODE_POWER_DOWN, when the timeout (from getTransitionDelay()) has passed. - * - * #transitionFailure can be set to a failure code indicating the reason for a failed transition - */ - virtual void doShutDown() = 0; - /** * Do the transition to the main modes (MODE_ON, MODE_NORMAL and MODE_RAW). * @@ -409,24 +626,6 @@ protected: */ virtual void doTransition(Mode_t modeFrom, Submode_t subModeFrom); - /** - * Get the time needed to transit from modeFrom to modeTo. - * - * Used for the following transitions: - * modeFrom -> modeTo: - * MODE_ON -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] - * MODE_NORMAL -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] - * MODE_RAW -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] - * _MODE_START_UP -> MODE_ON (do not include time to set the switches, the base class got you covered) - * - * The default implementation returns 0; - * - * @param modeFrom - * @param modeTo - * @return time in ms - */ - virtual uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo); - /** * Is the combination of mode and submode valid? * @@ -448,40 +647,6 @@ protected: */ virtual RmapAction_t getRmapAction(); - /** - * Build the device command to send for normal mode. - * - * This is only called in @c MODE_NORMAL. If multiple submodes for @c MODE_NORMAL are supported, - * different commands can built returned depending on the submode. - * - * #rawPacket and #rawPacketLen must be set by this method to the packet to be sent. - * - * @param[out] id the device command id that has been built - * @return - * - @c RETURN_OK when a command is to be sent - * - not @c RETURN_OK when no command is to be sent - */ - virtual ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t * id) = 0; - - /** - * Build the device command to send for a transitional mode. - * - * This is only called in @c _MODE_TO_NORMAL, @c _MODE_TO_ON, @c _MODE_TO_RAW, - * @c _MODE_START_UP and @c _MODE_TO_POWER_DOWN. So it is used by doStartUp() and doShutDown() as well as doTransition() - * - * A good idea is to implement a flag indicating a command has to be built and a variable containing the command number to be built - * and filling them in doStartUp(), doShutDown() and doTransition() so no modes have to be checked here. - * - * #rawPacket and #rawPacketLen must be set by this method to the packet to be sent. - * - * @param[out] id the device command id built - * @return - * - @c RETURN_OK when a command is to be sent - * - not @c RETURN_OK when no command is to be sent - */ - virtual ReturnValue_t buildTransitionDeviceCommand( - DeviceCommandId_t * id) = 0; - /** * Build the device command to send for raw mode. * @@ -502,47 +667,15 @@ protected: */ virtual ReturnValue_t buildChildRawCommand(); - /** - * Build a device command packet from data supplied by a direct command. - * - * #rawPacket and #rawPacketLen should be set by this method to the packet to be sent. - * - * @param deviceCommand the command to build, already checked against deviceCommandMap - * @param commandData pointer to the data from the direct command - * @param commandDataLen length of commandData - * @return - * - @c RETURN_OK when #rawPacket is valid - * - @c RETURN_FAILED when #rawPacket is invalid and no data should be sent - */ - virtual ReturnValue_t buildCommandFromCommand( - DeviceCommandId_t deviceCommand, const uint8_t * commandData, - size_t commandDataLen) = 0; - - /** - * fill the #deviceCommandMap - * - * called by the initialize() of the base class - * - * This is used to let the base class know which replies are expected. - * There are different scenarios regarding this: - * - "Normal" commands. These are commands, that trigger a direct reply from the device. In this case, the id of the command should be added to the command map - * with a commandData_t where maxDelayCycles is set to the maximum expected number of PST cycles the reply will take. Then, scanForReply returns the id of the command and the base class can handle time-out and missing replies. - * - Periodic, unrequested replies. These are replies that, once enabled, are sent by the device on its own in a defined interval. In this case, the id of the reply or a placeholder id should be added to the deviceCommandMap - * with a commandData_t where maxDelayCycles is set to the maximum expected number of PST cycles between two replies (also a tolerance should be added, as an FDIR message will be generated if it is missed). - * As soon as the replies are enabled, DeviceCommandInfo.periodic must be set to 1, DeviceCommandInfo.delayCycles to DeviceCommandInfo.MaxDelayCycles. From then on, the base class handles the reception. - * Then, scanForReply returns the id of the reply or the placeholder id and the base class will take care of checking that all replies are received and the interval is correct. - * When the replies are disabled, DeviceCommandInfo.periodic must be set to 0, DeviceCommandInfo.delayCycles to 0; - * - Aperiodic, unrequested replies. These are replies that are sent by the device without any preceding command and not in a defined interval. These are not entered in the deviceCommandMap but handled by returning @c APERIODIC_REPLY in scanForReply(). - * - */ - virtual void fillCommandAndReplyMap() = 0; /** * This is a helper method to facilitate inserting entries in the command map. * @param deviceCommand Identifier of the command to add. * @param maxDelayCycles The maximum number of delay cycles the command waits until it times out. - * @param periodic Indicates if the command is periodic (i.e. it is sent by the device repeatedly without request) or not. + * @param periodic Indicates if the reply is periodic (i.e. it is sent by the device repeatedly without request) or not. * Default is aperiodic (0) + * @param hasDifferentReplyId + * @param replyId * @return RETURN_OK when the command was successfully inserted, COMMAND_MAP_ERROR else. */ ReturnValue_t insertInCommandAndReplyMap(DeviceCommandId_t deviceCommand, @@ -583,48 +716,6 @@ protected: * @return The current delay count. If the command does not exist (should never happen) it returns 0. */ uint8_t getReplyDelayCycles(DeviceCommandId_t deviceCommand); - /** - * Scans a buffer for a valid reply. - * - * This is used by the base class to check the data received from the RMAP stack for valid packets. - * It only checks if a valid packet starts at @c start. - * It also only checks the structural validy of the packet, eg checksums lengths and protocol data. No - * information check is done, eg range checks etc. - * - * Errors should be reported directly, the base class does NOT report any errors based on the return - * value of this function. - * - * @param start start of data - * @param len length of data - * @param[out] foundId the id of the packet starting at @c start - * @param[out] foundLen length of the packet found - * @return - * - @c RETURN_OK a valid packet was found at @c start, @c foundLen is valid - * - @c NO_VALID_REPLY no reply could be found starting at @c start, implies @c foundLen is not valid, base class will call scanForReply() again with ++start - * - @c INVALID_REPLY a packet was found but it is invalid, eg checksum error, implies @c foundLen is valid, can be used to skip some bytes - * - @c TOO_SHORT @c len is too short for any valid packet - * - @c APERIODIC_REPLY if a valid reply is received that has not been requested by a command, but should be handled anyway (@see also fillCommandAndCookieMap() ) - */ - virtual ReturnValue_t scanForReply(const uint8_t *start, uint32_t len, - DeviceCommandId_t *foundId, uint32_t *foundLen) = 0; - - /** - * Interpret a reply from the device. - * - * This is called after scanForReply() found a valid packet, it can be assumed that the length and structure is valid. - * This routine extracts the data from the packet into a DataSet and then calls handleDeviceTM(), which either sends - * a TM packet or stores the data in the DataPool depending on whether the it was an external command. - * No packet length is given, as it should be defined implicitly by the id. - * - * @param id the id found by scanForReply() - * @param packet - * @param commander the one who initiated the command, is 0 if not external commanded - * @return - * - @c RETURN_OK when the reply was interpreted. - * - @c RETURN_FAILED when the reply could not be interpreted, eg. logical errors or range violations occurred - */ - virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, - const uint8_t *packet) = 0; /** * Construct a command reply containing a raw reply. @@ -649,21 +740,6 @@ protected: */ void replyRawReplyIfnotWiretapped(const uint8_t *data, size_t len); - /** - * Return the switches connected to the device. - * - * The default implementation returns one switch set in the ctor. - * - * - * @param[out] switches pointer to an array of switches - * @param[out] numberOfSwitches length of returned array - * @return - * - @c RETURN_OK if the parameters were set - * - @c RETURN_FAILED if no switches exist - */ - virtual ReturnValue_t getSwitches(const uint8_t **switches, - uint8_t *numberOfSwitches); - /** * notify child about mode change */ @@ -671,7 +747,8 @@ protected: struct DeviceCommandInfo { bool isExecuting; //!< Indicates if the command is already executing. - uint8_t expectedReplies; //!< Dynamic value to indicate how many replies are expected. + uint8_t expectedReplies; //!< Dynamic value to indicate how many replies are expected. Inititated with 0. + uint8_t expectedRepliesWhenEnablingReplyMap; //!< Constant value which specifies expected replies when enabling reply map. Inititated in insertInCommandAndReplyMap() MessageQueueId_t sendReplyTo; //!< if this is != NO_COMMANDER, DHB was commanded externally and shall report everything to commander. }; @@ -711,14 +788,6 @@ protected: */ ReturnValue_t getStateOfSwitches(void); - /** - * set all datapool variables that are update periodically in normal mode invalid - * - * Child classes should provide an implementation which sets all those variables invalid - * which are set periodically during any normal mode. - */ - virtual void setNormalDatapoolEntriesInvalid() = 0; - /** * build a list of sids and pass it to the #hkSwitcher */ @@ -735,6 +804,11 @@ protected: */ virtual bool dontCheckQueue(); + /** + * Used to retrieve logical address + * @return logicalAddress + */ + virtual uint32_t getLogicalAddress(); Mode_t getBaseMode(Mode_t transitionMode); bool isAwaitingReply(); @@ -747,7 +821,6 @@ protected: virtual void startTransition(Mode_t mode, Submode_t submode); virtual void setToExternalControl(); virtual void announceMode(bool recursive); - virtual ReturnValue_t letChildHandleMessage(CommandMessage *message); /** @@ -829,12 +902,13 @@ protected: DeviceCommandMap deviceCommandMap; ActionHelper actionHelper; + private: /** * State a cookie is in. * - * Used to keep track of the state of the RMAP communication. + * Used to keep track of the state of the communication. */ enum CookieState_t { COOKIE_UNUSED, //!< The Cookie is unused @@ -862,9 +936,9 @@ private: CookieInfo cookieInfo; /** - * cached from ctor for initialize() - */ - const uint32_t ioBoardAddress; + * cached from ctor for initialize() + */ + const uint32_t logicalAddress; /** * Used for timing out mode transitions. @@ -913,22 +987,20 @@ private: * - checks whether commanded mode transitions are required and calls handleCommandedModeTransition() * - does the necessary action for the current mode or calls doChildStateMachine in modes @c MODE_TO_ON and @c MODE_TO_OFF * - actions that happen in transitions (eg setting a timeout) are handled in setMode() + * - Maybe export this into own class to increase modularity of software + * and reduce the massive class size ? */ void doStateMachine(void); void buildRawDeviceCommand(CommandMessage* message); void buildInternalCommand(void); -// /** -// * Send a reply with the current mode and submode. -// */ -// void announceMode(void); - /** * Decrement the counter for the timout of replies. * * This is called at the beginning of each cycle. It checks whether a reply has timed out (that means a reply was expected * but not received). + * In case the reply is periodic, the counter is simply set back to a specified value. */ void decrementDeviceReplyMap(void); @@ -1027,7 +1099,14 @@ private: */ ReturnValue_t switchCookieChannel(object_id_t newChannelId); + /** + * Handle device handler messages (e.g. commands sent by PUS Service 2) + * @param message + * @return + */ ReturnValue_t handleDeviceHandlerMessage(CommandMessage *message); + + }; #endif /* DEVICEHANDLERBASE_H_ */ diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index fe0ee8e8b..cce403493 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -22,80 +22,82 @@ public: * * @details The mode of the device handler must not be confused with the mode the device is in. * The mode of the device itself is transparent to the user but related to the mode of the handler. + * MODE_ON and MODE_OFF are included in hasModesIF.h */ -// MODE_ON = 0, //!< 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 -// MODE_OFF = 1, //!< The device is powered off. The only command accepted in this mode is a mode change to on. - static const Mode_t MODE_NORMAL = 2; //!< The device is powered on and the device handler periodically sends commands. The commands to be sent are selected by the handler according to the submode. - static const Mode_t MODE_RAW = 3; //!< The device is powered on and ready to perform operations. In this mode, raw commands can be sent. The device handler will send all replies received from the command back to the commanding object. - static const Mode_t MODE_ERROR_ON = 4; //!4< The device is shut down but the switch could not be turned off, so the device still is powered. In this mode, only a mode change to @c MODE_OFF can be commanded, which tries to switch off the device again. - static const Mode_t _MODE_START_UP = TRANSITION_MODE_CHILD_ACTION_MASK | 5; //!< This is a transitional state which can not be commanded. The device handler performs all commands to get the device in a state ready to perform commands. When this is completed, the mode changes to @c MODE_ON. - static const Mode_t _MODE_SHUT_DOWN = TRANSITION_MODE_CHILD_ACTION_MASK | 6; //!< This is a transitional state which can not be commanded. The device handler performs all actions and commands to get the device shut down. When the device is off, the mode changes to @c MODE_OFF. - static const Mode_t _MODE_TO_ON = TRANSITION_MODE_CHILD_ACTION_MASK | HasModesIF::MODE_ON; - static const Mode_t _MODE_TO_RAW = TRANSITION_MODE_CHILD_ACTION_MASK | MODE_RAW; - static const Mode_t _MODE_TO_NORMAL = TRANSITION_MODE_CHILD_ACTION_MASK | MODE_NORMAL; - static const Mode_t _MODE_POWER_DOWN = TRANSITION_MODE_BASE_ACTION_MASK | 1; //!< This is a transitional state which can not be commanded. The device is shut down and ready to be switched off. After the command to set the switch off has been sent, the mode changes to @c MODE_WAIT_OFF - static const Mode_t _MODE_POWER_ON = TRANSITION_MODE_BASE_ACTION_MASK | 2; //!< This is a transitional state which can not be commanded. The device will be switched on in this state. After the command to set the switch on has been sent, the mode changes to @c MODE_WAIT_ON - static const Mode_t _MODE_WAIT_OFF = TRANSITION_MODE_BASE_ACTION_MASK | 3; //!< This is a transitional state which can not be commanded. The switch has been commanded off and the handler waits for it to be off. When the switch is off, the mode changes to @c MODE_OFF. - static const Mode_t _MODE_WAIT_ON = TRANSITION_MODE_BASE_ACTION_MASK | 4; //!< This is a transitional state which can not be commanded. The switch has been commanded on and the handler waits for it to be on. When the switch is on, the mode changes to @c MODE_TO_ON. - static const Mode_t _MODE_SWITCH_IS_OFF = TRANSITION_MODE_BASE_ACTION_MASK | 5; //!< This is a transitional state which can not be commanded. The switch has been commanded off and is off now. This state is only to do an RMAP cycle once more where the doSendRead() function will set the mode to MODE_OFF. The reason to do this is to get rid of stuck packets in the IO Board - static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::CDH; - static const Event DEVICE_BUILDING_COMMAND_FAILED = MAKE_EVENT(0, SEVERITY::LOW); - static const Event DEVICE_SENDING_COMMAND_FAILED = MAKE_EVENT(1, SEVERITY::LOW); - static const Event DEVICE_REQUESTING_REPLY_FAILED = MAKE_EVENT(2, SEVERITY::LOW); - static const Event DEVICE_READING_REPLY_FAILED = MAKE_EVENT(3, SEVERITY::LOW); - static const Event DEVICE_INTERPRETING_REPLY_FAILED = MAKE_EVENT(4, SEVERITY::LOW); - static const Event DEVICE_MISSED_REPLY = MAKE_EVENT(5, SEVERITY::LOW); - static const Event DEVICE_UNKNOWN_REPLY = MAKE_EVENT(6, SEVERITY::LOW); - static const Event DEVICE_UNREQUESTED_REPLY = MAKE_EVENT(7, SEVERITY::LOW); - static const Event INVALID_DEVICE_COMMAND = MAKE_EVENT(8, SEVERITY::LOW); //!< Indicates a SW bug in child class. - static const Event MONITORING_LIMIT_EXCEEDED = MAKE_EVENT(9, SEVERITY::LOW); - static const Event MONITORING_AMBIGUOUS = MAKE_EVENT(10, SEVERITY::HIGH); + // MODE_ON = 0, //!< 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 + // MODE_OFF = 1, //!< The device is powered off. The only command accepted in this mode is a mode change to on. + static const Mode_t MODE_NORMAL = 2; //!< The device is powered on and the device handler periodically sends commands. The commands to be sent are selected by the handler according to the submode. + static const Mode_t MODE_RAW = 3; //!< The device is powered on and ready to perform operations. In this mode, raw commands can be sent. The device handler will send all replies received from the command back to the commanding object. + static const Mode_t MODE_ERROR_ON = 4; //!4< The device is shut down but the switch could not be turned off, so the device still is powered. In this mode, only a mode change to @c MODE_OFF can be commanded, which tries to switch off the device again. + static const Mode_t _MODE_START_UP = TRANSITION_MODE_CHILD_ACTION_MASK | 5; //!< This is a transitional state which can not be commanded. The device handler performs all commands to get the device in a state ready to perform commands. When this is completed, the mode changes to @c MODE_ON. + static const Mode_t _MODE_SHUT_DOWN = TRANSITION_MODE_CHILD_ACTION_MASK | 6; //!< This is a transitional state which can not be commanded. The device handler performs all actions and commands to get the device shut down. When the device is off, the mode changes to @c MODE_OFF. + static const Mode_t _MODE_TO_ON = TRANSITION_MODE_CHILD_ACTION_MASK | HasModesIF::MODE_ON; //!< It is possible to set the mode to _MODE_TO_ON to use the to on transition if available. + static const Mode_t _MODE_TO_RAW = TRANSITION_MODE_CHILD_ACTION_MASK | MODE_RAW; + static const Mode_t _MODE_TO_NORMAL = TRANSITION_MODE_CHILD_ACTION_MASK | MODE_NORMAL; + static const Mode_t _MODE_POWER_DOWN = TRANSITION_MODE_BASE_ACTION_MASK | 1; //!< This is a transitional state which can not be commanded. The device is shut down and ready to be switched off. After the command to set the switch off has been sent, the mode changes to @c MODE_WAIT_OFF + static const Mode_t _MODE_POWER_ON = TRANSITION_MODE_BASE_ACTION_MASK | 2; //!< This is a transitional state which can not be commanded. The device will be switched on in this state. After the command to set the switch on has been sent, the mode changes to @c MODE_WAIT_ON + static const Mode_t _MODE_WAIT_OFF = TRANSITION_MODE_BASE_ACTION_MASK | 3; //!< This is a transitional state which can not be commanded. The switch has been commanded off and the handler waits for it to be off. When the switch is off, the mode changes to @c MODE_OFF. + static const Mode_t _MODE_WAIT_ON = TRANSITION_MODE_BASE_ACTION_MASK | 4; //!< This is a transitional state which can not be commanded. The switch has been commanded on and the handler waits for it to be on. When the switch is on, the mode changes to @c MODE_TO_ON. + static const Mode_t _MODE_SWITCH_IS_OFF = TRANSITION_MODE_BASE_ACTION_MASK | 5; //!< This is a transitional state which can not be commanded. The switch has been commanded off and is off now. This state is only to do an RMAP cycle once more where the doSendRead() function will set the mode to MODE_OFF. The reason to do this is to get rid of stuck packets in the IO Board - static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_IF; - static const ReturnValue_t NO_COMMAND_DATA = MAKE_RETURN_CODE(0xA0); - static const ReturnValue_t COMMAND_NOT_SUPPORTED = MAKE_RETURN_CODE(0xA1); - static const ReturnValue_t COMMAND_ALREADY_SENT = MAKE_RETURN_CODE(0xA2); - static const ReturnValue_t COMMAND_WAS_NOT_SENT = MAKE_RETURN_CODE(0xA3); - static const ReturnValue_t CANT_SWITCH_IOBOARD = MAKE_RETURN_CODE(0xA4); - static const ReturnValue_t WRONG_MODE_FOR_COMMAND = MAKE_RETURN_CODE(0xA5); - static const ReturnValue_t TIMEOUT = MAKE_RETURN_CODE(0xA6); - static const ReturnValue_t BUSY = MAKE_RETURN_CODE(0xA7); - static const ReturnValue_t NO_REPLY_EXPECTED = MAKE_RETURN_CODE(0xA8); //!< Used to indicate that this is a command-only command. - static const ReturnValue_t NON_OP_TEMPERATURE = MAKE_RETURN_CODE(0xA9); - static const ReturnValue_t COMMAND_NOT_IMPLEMENTED = MAKE_RETURN_CODE(0xAA); + static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::CDH; + static const Event DEVICE_BUILDING_COMMAND_FAILED = MAKE_EVENT(0, SEVERITY::LOW); + static const Event DEVICE_SENDING_COMMAND_FAILED = MAKE_EVENT(1, SEVERITY::LOW); + static const Event DEVICE_REQUESTING_REPLY_FAILED = MAKE_EVENT(2, SEVERITY::LOW); + static const Event DEVICE_READING_REPLY_FAILED = MAKE_EVENT(3, SEVERITY::LOW); + static const Event DEVICE_INTERPRETING_REPLY_FAILED = MAKE_EVENT(4, SEVERITY::LOW); + static const Event DEVICE_MISSED_REPLY = MAKE_EVENT(5, SEVERITY::LOW); + static const Event DEVICE_UNKNOWN_REPLY = MAKE_EVENT(6, SEVERITY::LOW); + static const Event DEVICE_UNREQUESTED_REPLY = MAKE_EVENT(7, SEVERITY::LOW); + static const Event INVALID_DEVICE_COMMAND = MAKE_EVENT(8, SEVERITY::LOW); //!< Indicates a SW bug in child class. + static const Event MONITORING_LIMIT_EXCEEDED = MAKE_EVENT(9, SEVERITY::LOW); + static const Event MONITORING_AMBIGUOUS = MAKE_EVENT(10, SEVERITY::HIGH); - //standard codes used in scan for reply - // static const ReturnValue_t TOO_SHORT = MAKE_RETURN_CODE(0xB1); - static const ReturnValue_t CHECKSUM_ERROR = MAKE_RETURN_CODE(0xB2); - static const ReturnValue_t LENGTH_MISSMATCH = MAKE_RETURN_CODE(0xB3); - static const ReturnValue_t INVALID_DATA = MAKE_RETURN_CODE(0xB4); - static const ReturnValue_t PROTOCOL_ERROR = MAKE_RETURN_CODE(0xB5); + static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_IF; + static const ReturnValue_t NO_COMMAND_DATA = MAKE_RETURN_CODE(0xA0); + static const ReturnValue_t COMMAND_NOT_SUPPORTED = MAKE_RETURN_CODE(0xA1); + static const ReturnValue_t COMMAND_ALREADY_SENT = MAKE_RETURN_CODE(0xA2); + static const ReturnValue_t COMMAND_WAS_NOT_SENT = MAKE_RETURN_CODE(0xA3); + static const ReturnValue_t CANT_SWITCH_IOBOARD = MAKE_RETURN_CODE(0xA4); + static const ReturnValue_t WRONG_MODE_FOR_COMMAND = MAKE_RETURN_CODE(0xA5); + static const ReturnValue_t TIMEOUT = MAKE_RETURN_CODE(0xA6); + static const ReturnValue_t BUSY = MAKE_RETURN_CODE(0xA7); + static const ReturnValue_t NO_REPLY_EXPECTED = MAKE_RETURN_CODE(0xA8); //!< Used to indicate that this is a command-only command. + static const ReturnValue_t NON_OP_TEMPERATURE = MAKE_RETURN_CODE(0xA9); + static const ReturnValue_t COMMAND_NOT_IMPLEMENTED = MAKE_RETURN_CODE(0xAA); - //standard codes used in interpret device reply - static const ReturnValue_t DEVICE_DID_NOT_EXECUTE = MAKE_RETURN_CODE(0xC1); //the device reported, that it did not execute the command - static const ReturnValue_t DEVICE_REPORTED_ERROR = MAKE_RETURN_CODE(0xC2); - static const ReturnValue_t UNKNOW_DEVICE_REPLY = MAKE_RETURN_CODE(0xC3); //the deviceCommandId reported by scanforReply is unknown - static const ReturnValue_t DEVICE_REPLY_INVALID = MAKE_RETURN_CODE(0xC4); //syntax etc is correct but still not ok, eg parameters where none are expected + // Standard codes used in scan for reply + //static const ReturnValue_t TOO_SHORT = MAKE_RETURN_CODE(0xB1); + static const ReturnValue_t CHECKSUM_ERROR = MAKE_RETURN_CODE(0xB2); + static const ReturnValue_t LENGTH_MISSMATCH = MAKE_RETURN_CODE(0xB3); + static const ReturnValue_t INVALID_DATA = MAKE_RETURN_CODE(0xB4); + static const ReturnValue_t PROTOCOL_ERROR = MAKE_RETURN_CODE(0xB5); - //Standard codes used in buildCommandFromCommand - static const ReturnValue_t INVALID_COMMAND_PARAMETER = MAKE_RETURN_CODE( - 0xD0); - static const ReturnValue_t INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS = - MAKE_RETURN_CODE(0xD1); + // Standard codes used in interpret device reply + static const ReturnValue_t DEVICE_DID_NOT_EXECUTE = MAKE_RETURN_CODE(0xC1); //the device reported, that it did not execute the command + static const ReturnValue_t DEVICE_REPORTED_ERROR = MAKE_RETURN_CODE(0xC2); + static const ReturnValue_t UNKNOW_DEVICE_REPLY = MAKE_RETURN_CODE(0xC3); //the deviceCommandId reported by scanforReply is unknown + static const ReturnValue_t DEVICE_REPLY_INVALID = MAKE_RETURN_CODE(0xC4); //syntax etc is correct but still not ok, eg parameters where none are expected - /** - * RMAP Action that will be executed. - * - * This is used by the child class to tell the base class what to do. - */ - enum RmapAction_t { - SEND_WRITE,//!< RMAP send write - GET_WRITE, //!< RMAP get write - SEND_READ, //!< RMAP send read - GET_READ, //!< RMAP get read - NOTHING //!< Do nothing. - }; + // Standard codes used in buildCommandFromCommand + static const ReturnValue_t INVALID_COMMAND_PARAMETER = MAKE_RETURN_CODE( + 0xD0); + static const ReturnValue_t INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS = + MAKE_RETURN_CODE(0xD1); + + /** + * RMAP Action that will be executed. + * + * This is used by the child class to tell the base class what to do. + */ + enum RmapAction_t { + SEND_WRITE,//!< RMAP send write + GET_WRITE, //!< RMAP get write + SEND_READ, //!< RMAP send read + GET_READ, //!< RMAP get read + NOTHING //!< Do nothing. + }; /** * Default Destructor */ diff --git a/devicehandlers/FixedSequenceSlot.h b/devicehandlers/FixedSequenceSlot.h index 5aff0f4f3..0ed285b38 100644 --- a/devicehandlers/FixedSequenceSlot.h +++ b/devicehandlers/FixedSequenceSlot.h @@ -31,7 +31,7 @@ public: * \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, what is ensured by automated calculation + * smaller than the period length of the polling sequence, which is ensured by automated calculation * from a database. */ uint32_t pollingTimeMs; diff --git a/devicehandlers/FixedSlotSequence.h b/devicehandlers/FixedSlotSequence.h index f8fd9a36f..d72de9131 100644 --- a/devicehandlers/FixedSlotSequence.h +++ b/devicehandlers/FixedSlotSequence.h @@ -104,7 +104,7 @@ protected: * \brief This list contains all OPUSPollingSlot objects, defining order and execution time of the * device handler objects. * - * \details The slot list is a std:list object that contains all created OPUSPollingSlot instances. + * @details The slot list is a std:list object that contains all created PollingSlot instances. * They are NOT ordered automatically, so by adding entries, the correct order needs to be ensured. * By iterating through this list the polling sequence is executed. Two entries with identical * polling times are executed immediately one after another. diff --git a/events/EventManager.cpp b/events/EventManager.cpp index 01334bac9..46c537520 100644 --- a/events/EventManager.cpp +++ b/events/EventManager.cpp @@ -137,9 +137,10 @@ void EventManager::printEvent(EventMessage* message) { error << "0x" << std::hex << message->getReporter() << std::dec; } error << " reported " << translateEvents(message->getEvent()) << " (" - << std::dec << message->getEventId() << std::hex << ") P1: 0x" - << message->getParameter1() << " P2: 0x" - << message->getParameter2() << std::dec << std::endl; + << std::dec << message->getEventId() << ") " << std::endl; + + error << std::hex << "P1 Hex: 0x" << message->getParameter1() << ", P1 Dec: " << std::dec << message->getParameter1() << std::endl; + error << std::hex << "P2 Hex: 0x" << message->getParameter2() << ", P2 Dec: " << std::dec << message->getParameter2() << std::endl; break; } diff --git a/fdir/FailureIsolationBase.cpp b/fdir/FailureIsolationBase.cpp index 4b7caac53..c65640c27 100644 --- a/fdir/FailureIsolationBase.cpp +++ b/fdir/FailureIsolationBase.cpp @@ -5,9 +5,10 @@ #include #include -FailureIsolationBase::FailureIsolationBase(object_id_t owner, object_id_t parent, uint8_t messageDepth, uint8_t parameterDomainBase) : - eventQueue(NULL), ownerId( - owner), owner(NULL), faultTreeParent(parent), parameterDomainBase(parameterDomainBase) { +FailureIsolationBase::FailureIsolationBase(object_id_t owner, + object_id_t parent, uint8_t messageDepth, uint8_t parameterDomainBase) : + eventQueue(NULL), ownerId(owner), owner(NULL), + faultTreeParent(parent), parameterDomainBase(parameterDomainBase) { eventQueue = QueueFactory::instance()->createMessageQueue(messageDepth, EventMessage::EVENT_MESSAGE_SIZE); } diff --git a/fdir/FailureIsolationBase.h b/fdir/FailureIsolationBase.h index cd582e399..894f63562 100644 --- a/fdir/FailureIsolationBase.h +++ b/fdir/FailureIsolationBase.h @@ -21,6 +21,10 @@ public: uint8_t messageDepth = 10, uint8_t parameterDomainBase = 0xF0); virtual ~FailureIsolationBase(); virtual ReturnValue_t initialize(); + + /** + * This is called by the DHB in performOperation() + */ void checkForFailures(); MessageQueueId_t getEventReceptionQueue(); virtual void triggerEvent(Event event, uint32_t parameter1 = 0, diff --git a/health/HealthHelper.h b/health/HealthHelper.h index a1ca2c525..24fd0d14a 100644 --- a/health/HealthHelper.h +++ b/health/HealthHelper.h @@ -27,8 +27,8 @@ public: /** * ctor * + * @param owner * @param objectId the object Id to use when communication with the HealthTable - * @param useAsFrom id to use as from id when sending replies, can be set to 0 */ HealthHelper(HasHealthIF* owner, object_id_t objectId); diff --git a/internalError/InternalErrorReporter.cpp b/internalError/InternalErrorReporter.cpp index 9a8ede002..81c4a0e58 100644 --- a/internalError/InternalErrorReporter.cpp +++ b/internalError/InternalErrorReporter.cpp @@ -8,10 +8,9 @@ InternalErrorReporter::InternalErrorReporter(object_id_t setObjectId, uint32_t queuePoolId, uint32_t tmPoolId, uint32_t storePoolId) : - SystemObject(setObjectId), mutex(NULL), queuePoolId(queuePoolId), tmPoolId( - tmPoolId), storePoolId( - storePoolId), queueHits(0), tmHits(0), storeHits( - 0) { + SystemObject(setObjectId), mutex(NULL), queuePoolId(queuePoolId), + tmPoolId(tmPoolId),storePoolId(storePoolId), queueHits(0), tmHits(0), + storeHits(0) { mutex = MutexFactory::instance()->createMutex(); } diff --git a/ipc/CommandMessage.h b/ipc/CommandMessage.h index 2d966063a..21c393e59 100644 --- a/ipc/CommandMessage.h +++ b/ipc/CommandMessage.h @@ -17,6 +17,9 @@ #define MAKE_COMMAND_ID( number ) ((MESSAGE_ID << 8) + (number)) typedef ReturnValue_t Command_t; +/** + * @brief Used to pass command messages between tasks + */ class CommandMessage : public MessageQueueMessage { public: static const uint8_t INTERFACE_ID = CLASS_ID::COMMAND_MESSAGE; diff --git a/ipc/MessageQueueIF.h b/ipc/MessageQueueIF.h index 18e2d99ab..47da694cd 100644 --- a/ipc/MessageQueueIF.h +++ b/ipc/MessageQueueIF.h @@ -3,12 +3,16 @@ // COULDDO: We could support blocking calls +/** + * @defgroup message_queue Message Queue + * @brief Message Queue related software components + */ + #include #include #include class MessageQueueIF { public: - static const MessageQueueId_t NO_QUEUE = MessageQueueSenderIF::NO_QUEUE; //!< Ugly hack. static const uint8_t INTERFACE_ID = CLASS_ID::MESSAGE_QUEUE_IF; @@ -54,6 +58,8 @@ public: * 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. + * @return -@c RETURN_OK on success + * -@c MessageQueueIF::EMPTY if queue is empty */ virtual ReturnValue_t receiveMessage(MessageQueueMessage* message) = 0; /** @@ -72,33 +78,38 @@ public: 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 + * @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. + * @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). + * @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. - */ + * @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; /** - * \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. + * @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( MessageQueueMessage* message, MessageQueueId_t sentFrom, bool ignoreFault = false ) = 0; /** @@ -106,6 +117,8 @@ public: * @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; /** diff --git a/ipc/MessageQueueSenderIF.h b/ipc/MessageQueueSenderIF.h index 9e7e11c03..6eb7733d1 100644 --- a/ipc/MessageQueueSenderIF.h +++ b/ipc/MessageQueueSenderIF.h @@ -26,8 +26,8 @@ public: * Must be implemented by a subclass. */ static ReturnValue_t sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, MessageQueueId_t sentFrom = - MessageQueueSenderIF::NO_QUEUE, bool ignoreFault=false); + MessageQueueMessage* message, MessageQueueId_t sentFrom = MessageQueueSenderIF::NO_QUEUE, + bool ignoreFault=false); private: MessageQueueSenderIF() {} }; diff --git a/ipc/MutexIF.h b/ipc/MutexIF.h index 35786d6a3..a4aff1cd8 100644 --- a/ipc/MutexIF.h +++ b/ipc/MutexIF.h @@ -3,6 +3,13 @@ #include +/** + * @brief Common interface for OS Mutex objects which provide MUTual EXclusion. + * + * @details https://en.wikipedia.org/wiki/Lock_(computer_science) + * @ingroup osal + * @ingroup interface + */ class MutexIF { public: static const uint32_t NO_TIMEOUT; //!< Needs to be defined in implementation. diff --git a/modes/ModeHelper.cpp b/modes/ModeHelper.cpp index 7b98bdc64..d9266c354 100644 --- a/modes/ModeHelper.cpp +++ b/modes/ModeHelper.cpp @@ -1,6 +1,7 @@ #include #include #include +#include ModeHelper::ModeHelper(HasModesIF *owner) : theOneWhoCommandedAMode(0), commandedMode(HasModesIF::MODE_OFF), commandedSubmode( @@ -42,7 +43,6 @@ ReturnValue_t ModeHelper::handleModeCommand(CommandMessage* message) { } countdown.setTimeout(timeout); - owner->startTransition(mode, submode); } break; diff --git a/modes/ModeMessage.cpp b/modes/ModeMessage.cpp index 62fea7e43..1d3baad54 100644 --- a/modes/ModeMessage.cpp +++ b/modes/ModeMessage.cpp @@ -16,6 +16,10 @@ ReturnValue_t ModeMessage::setModeMessage(CommandMessage* message, Command_t com return HasReturnvaluesIF::RETURN_OK; } +ReturnValue_t ModeMessage::getCantReachModeReason(const CommandMessage* message) { + return message->getParameter(); +} + void ModeMessage::clear(CommandMessage* message) { message->setCommand(CommandMessage::CMD_NONE); } diff --git a/modes/ModeMessage.h b/modes/ModeMessage.h index fad347264..0a72aee04 100644 --- a/modes/ModeMessage.h +++ b/modes/ModeMessage.h @@ -24,6 +24,7 @@ public: static const Command_t REPLY_MODE_INFO = MAKE_COMMAND_ID(0x03); //!> Unrequested info about the current mode (used for composites to inform their container of a changed mode) static const Command_t REPLY_CANT_REACH_MODE = MAKE_COMMAND_ID(0x04); //!> Reply in case a mode command can't be executed. Par1: returnCode, Par2: 0 //SHOULDDO is there a way we can transmit a returnvalue when responding that the mode is wrong, so we can give a nice failure code when commanded by PUS? + // shouldn't that possible with parameter 2 when submode only takes 1 byte? static const Command_t REPLY_WRONG_MODE_REPLY = MAKE_COMMAND_ID(0x05);//!> Reply to a CMD_MODE_COMMAND, indicating that a mode was commanded and a transition started but was aborted; the parameters contain the mode that was reached static const Command_t CMD_MODE_READ = MAKE_COMMAND_ID(0x06);//!> Command to read the current mode and reply with a REPLY_MODE_REPLY static const Command_t CMD_MODE_ANNOUNCE = MAKE_COMMAND_ID(0x07);//!> Command to trigger an ModeInfo Event. This command does NOT have a reply. @@ -34,6 +35,7 @@ public: static ReturnValue_t 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 clear(CommandMessage* message); }; diff --git a/monitoring/LimitMonitor.h b/monitoring/LimitMonitor.h index 45abe48bf..c24629a35 100644 --- a/monitoring/LimitMonitor.h +++ b/monitoring/LimitMonitor.h @@ -16,12 +16,13 @@ public: uint16_t confirmationLimit, T lowerLimit, T upperLimit, Event belowLowEvent = MonitoringIF::VALUE_BELOW_LOW_LIMIT, Event aboveHighEvent = MonitoringIF::VALUE_ABOVE_HIGH_LIMIT) : - MonitorBase(reporterId, monitorId, parameterId, confirmationLimit), lowerLimit( - lowerLimit), upperLimit(upperLimit), belowLowEvent( - belowLowEvent), aboveHighEvent(aboveHighEvent) { - } - virtual ~LimitMonitor() { + MonitorBase(reporterId, monitorId, parameterId, confirmationLimit), + lowerLimit(lowerLimit), upperLimit(upperLimit), belowLowEvent(belowLowEvent), + aboveHighEvent(aboveHighEvent) { } + + virtual ~LimitMonitor() {} + virtual ReturnValue_t checkSample(T sample, T* crossedLimit) { *crossedLimit = 0.0; if (sample > upperLimit) { diff --git a/objectmanager/ObjectManagerIF.h b/objectmanager/ObjectManagerIF.h index aca24a24a..1d7674c64 100644 --- a/objectmanager/ObjectManagerIF.h +++ b/objectmanager/ObjectManagerIF.h @@ -78,6 +78,8 @@ public: virtual void printList() = 0; }; + +/*Documentation can be found in the class method declaration above.*/ template T* ObjectManagerIF::get( object_id_t id ) { SystemObjectIF* temp = this->getSystemObject(id); diff --git a/osal/Endiness.h b/osal/Endiness.h index 65cc0a10f..55d0aeb33 100644 --- a/osal/Endiness.h +++ b/osal/Endiness.h @@ -1,6 +1,10 @@ #ifndef FRAMEWORK_OSAL_ENDINESS_H_ #define FRAMEWORK_OSAL_ENDINESS_H_ +/** + * @defgroup osal Operating System Abstraction Layer + * @brief Provides clean interfaces to use OS functionalities + */ /* * BSD-style endian declaration diff --git a/osal/FreeRTOS/BinarySemaphore.cpp b/osal/FreeRTOS/BinarySemaphore.cpp new file mode 100644 index 000000000..d429f1a07 --- /dev/null +++ b/osal/FreeRTOS/BinarySemaphore.cpp @@ -0,0 +1,102 @@ +/** + * @file BinarySemaphore.cpp + * + * @date 25.02.2020 + */ +#include +#include + +#include +#include "portmacro.h" +#include "task.h" + +BinarySemaphore::BinarySemaphore() { + vSemaphoreCreateBinary(handle); + if(handle == NULL) { + error << "Binary semaphore creation failure" << std::endl; + } +} + +BinarySemaphore::~BinarySemaphore() { + vSemaphoreDelete(handle); +} + +ReturnValue_t BinarySemaphore::takeBinarySemaphore(uint32_t timeoutMs) { + if(handle == NULL) { + return HasReturnvaluesIF::RETURN_FAILED; + } + TickType_t timeout = portMAX_DELAY; + if(timeoutMs != 0) { + timeout = pdMS_TO_TICKS(timeoutMs); + } + + BaseType_t returncode = xSemaphoreTake(handle, timeout); + if (returncode == pdPASS) { + return HasReturnvaluesIF::RETURN_OK; + } else { + return SEMAPHORE_NOT_FOUND; + } +} + +ReturnValue_t BinarySemaphore::takeBinarySemaphoreTickTimeout(TickType_t timeoutTicks) { + if(handle == NULL) { + return SEMAPHORE_NOT_FOUND; + } + + BaseType_t returncode = xSemaphoreTake(handle, timeoutTicks); + if (returncode == pdPASS) { + return HasReturnvaluesIF::RETURN_OK; + } else { + return SEMAPHORE_TIMEOUT; + } +} + +ReturnValue_t BinarySemaphore::giveBinarySemaphore() { + if (handle == NULL) { + return HasReturnvaluesIF::RETURN_FAILED; + } + BaseType_t returncode = xSemaphoreGive(handle); + if (returncode == pdPASS) { + return HasReturnvaluesIF::RETURN_OK; + } else { + return SEMAPHORE_NOT_OWNED; + } +} + +SemaphoreHandle_t BinarySemaphore::getSemaphore() { + return handle; +} + +ReturnValue_t BinarySemaphore::giveBinarySemaphore(SemaphoreHandle_t semaphore) { + if (semaphore == NULL) { + return HasReturnvaluesIF::RETURN_FAILED; + } + BaseType_t returncode = xSemaphoreGive(semaphore); + if (returncode == pdPASS) { + return HasReturnvaluesIF::RETURN_OK; + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } +} + +void BinarySemaphore::resetSemaphore() { + vSemaphoreDelete(handle); + vSemaphoreCreateBinary(handle); +} + +ReturnValue_t BinarySemaphore::giveBinarySemaphoreFromISR(SemaphoreHandle_t semaphore, + BaseType_t * higherPriorityTaskWoken) { + if (semaphore == NULL) { + return HasReturnvaluesIF::RETURN_FAILED; + } + BaseType_t returncode = xSemaphoreGiveFromISR(semaphore, higherPriorityTaskWoken); + if (returncode == pdPASS) { + if(*higherPriorityTaskWoken == pdPASS) { + // Request context switch + TaskManagement::requestContextSwitch(CallContext::isr); + } + return HasReturnvaluesIF::RETURN_OK; + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } +} diff --git a/osal/FreeRTOS/BinarySemaphore.h b/osal/FreeRTOS/BinarySemaphore.h new file mode 100644 index 000000000..a809f0bb1 --- /dev/null +++ b/osal/FreeRTOS/BinarySemaphore.h @@ -0,0 +1,103 @@ +/** + * @file BinarySempahore.h + * + * @date 25.02.2020 + */ +#ifndef FRAMEWORK_OSAL_FREERTOS_BINARYSEMPAHORE_H_ +#define FRAMEWORK_OSAL_FREERTOS_BINARYSEMPAHORE_H_ + +#include +#include +#include "semphr.h" + +/** + * @brief OS Tool to achieve synchronization of between tasks or between task and ISR + * @details + * Documentation: https://www.freertos.org/Embedded-RTOS-Binary-Semaphores.html + * @ingroup osal + */ +class BinarySemaphore: public HasReturnvaluesIF { +public: + static const uint8_t INTERFACE_ID = CLASS_ID::SEMAPHORE_IF; + + /** Semaphore object not found */ + static const ReturnValue_t SEMAPHORE_NOT_FOUND = MAKE_RETURN_CODE(1); + /** Semaphore timeout */ + static const ReturnValue_t SEMAPHORE_TIMEOUT = MAKE_RETURN_CODE(2); + /** The current semaphore can not be given, because it is not owned */ + static const ReturnValue_t SEMAPHORE_NOT_OWNED = MAKE_RETURN_CODE(3); + + /** + * Create a binary semaphore + */ + BinarySemaphore(); + + /** + * Delete the binary semaphore to prevent a memory leak + */ + ~BinarySemaphore(); + + /** + * Take the binary semaphore. + * If the semaphore has already been taken, the task will be blocked for a maximum + * of #timeoutMs or until the semaphore is given back, + * for example by an ISR or another task. + * @param timeoutMs + * @return -@c RETURN_OK on success + * -@c RETURN_FAILED on failure + */ + ReturnValue_t takeBinarySemaphore(uint32_t timeoutMs); + + /** + * Same as lockBinarySemaphore() with timeout in FreeRTOS ticks. + * @param timeoutTicks + * @return -@c RETURN_OK on success + * -@c RETURN_FAILED on failure + */ + ReturnValue_t takeBinarySemaphoreTickTimeout(TickType_t timeoutTicks); + + /** + * Give back the binary semaphore + * @return -@c RETURN_OK on success + * -@c RETURN_FAILED on failure + */ + ReturnValue_t giveBinarySemaphore(); + + /** + * Get Handle to the semaphore. + * @return + */ + SemaphoreHandle_t getSemaphore(); + + /** + * Reset the semaphore. + */ + void resetSemaphore(); + + /** + * Wrapper function to give back semaphore from handle + * @param semaphore + * @return -@c RETURN_OK on success + * -@c RETURN_FAILED on failure + */ + static ReturnValue_t giveBinarySemaphore(SemaphoreHandle_t semaphore); + + /** + * Wrapper function to give back semaphore from handle when called from an ISR + * @param semaphore + * @param higherPriorityTaskWoken This will be set to pdPASS if a task with a higher priority + * was unblocked + * @return -@c RETURN_OK on success + * -@c RETURN_FAILED on failure + */ + static ReturnValue_t giveBinarySemaphoreFromISR(SemaphoreHandle_t semaphore, + BaseType_t * higherPriorityTaskWoken); +private: + SemaphoreHandle_t handle; +}; + + + + + +#endif /* FRAMEWORK_OSAL_FREERTOS_BINARYSEMPAHORE_H_ */ diff --git a/osal/FreeRTOS/FixedTimeslotTask.cpp b/osal/FreeRTOS/FixedTimeslotTask.cpp index 413d7596b..604a10b86 100644 --- a/osal/FreeRTOS/FixedTimeslotTask.cpp +++ b/osal/FreeRTOS/FixedTimeslotTask.cpp @@ -58,6 +58,10 @@ ReturnValue_t FixedTimeslotTask::startTask() { ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId, uint32_t slotTimeMs, int8_t executionStep) { if (objectManager->get(componentId) != NULL) { + if(slotTimeMs == 0) { + // FreeRTOS throws errors for zero values + slotTimeMs = 1; + } pst.addSlot(componentId, slotTimeMs, executionStep, this); return HasReturnvaluesIF::RETURN_OK; } @@ -114,4 +118,3 @@ ReturnValue_t FixedTimeslotTask::sleepFor(uint32_t ms) { vTaskDelay(pdMS_TO_TICKS(ms)); return HasReturnvaluesIF::RETURN_OK; } - diff --git a/osal/FreeRTOS/FixedTimeslotTask.h b/osal/FreeRTOS/FixedTimeslotTask.h index bed130511..1ab8724f6 100644 --- a/osal/FreeRTOS/FixedTimeslotTask.h +++ b/osal/FreeRTOS/FixedTimeslotTask.h @@ -51,6 +51,7 @@ public: ReturnValue_t checkSequence() const; ReturnValue_t sleepFor(uint32_t ms); + protected: bool started; TaskHandle_t handle; diff --git a/osal/FreeRTOS/MessageQueue.cpp b/osal/FreeRTOS/MessageQueue.cpp index f3aebcd1c..cc38e77d3 100644 --- a/osal/FreeRTOS/MessageQueue.cpp +++ b/osal/FreeRTOS/MessageQueue.cpp @@ -3,9 +3,10 @@ #include // 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 message_depth, size_t max_message_size) : -defaultDestination(0),lastPartner(0) { +defaultDestination(0),lastPartner(0), callContext(CallContext::task) { handle = xQueueCreate(message_depth, max_message_size); if (handle == NULL) { error << "MessageQueue creation failed" << std::endl; @@ -18,6 +19,10 @@ MessageQueue::~MessageQueue() { } } +void MessageQueue::switchSystemContext(CallContext callContext) { + this->callContext = callContext; +} + ReturnValue_t MessageQueue::sendMessage(MessageQueueId_t sendTo, MessageQueueMessage* message, bool ignoreFault) { return sendMessageFrom(sendTo, message, this->getId(), ignoreFault); @@ -27,6 +32,11 @@ ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessage* message) { return sendToDefaultFrom(message, this->getId()); } +ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessage* message, + MessageQueueId_t sentFrom, bool ignoreFault) { + return sendMessageFrom(defaultDestination,message,sentFrom,ignoreFault); +} + ReturnValue_t MessageQueue::reply(MessageQueueMessage* message) { if (this->lastPartner != 0) { return sendMessageFrom(this->lastPartner, message, this->getId()); @@ -35,10 +45,54 @@ ReturnValue_t MessageQueue::reply(MessageQueueMessage* message) { } } +ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, + MessageQueueMessage* message, MessageQueueId_t sentFrom, + bool ignoreFault) { + return sendMessageFromMessageQueue(sendTo,message,sentFrom,ignoreFault, callContext); +} + +ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, + MessageQueueMessage *message, MessageQueueId_t sentFrom, + bool ignoreFault, CallContext callContext) { + message->setSender(sentFrom); + BaseType_t result; + if(callContext == CallContext::task) { + result = xQueueSendToBack(reinterpret_cast(sendTo), + reinterpret_cast(message->getBuffer()), 0); + } + else { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + result = xQueueSendFromISR(reinterpret_cast(sendTo), + reinterpret_cast(message->getBuffer()), &xHigherPriorityTaskWoken); + if(xHigherPriorityTaskWoken == pdTRUE) { + TaskManagement::requestContextSwitch(callContext); + } + } + return handleSendResult(result, ignoreFault); +} + + + +ReturnValue_t MessageQueue::handleSendResult(BaseType_t result, bool ignoreFault) { + if (result != pdPASS) { + if (!ignoreFault) { + InternalErrorReporterIF* internalErrorReporter = objectManager->get( + objects::INTERNAL_ERROR_REPORTER); + if (internalErrorReporter != NULL) { + internalErrorReporter->queueMessageNotSent(); + } + } + return MessageQueueIF::FULL; + } + return HasReturnvaluesIF::RETURN_OK; +} + ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessage* message, MessageQueueId_t* receivedFrom) { ReturnValue_t status = this->receiveMessage(message); - *receivedFrom = this->lastPartner; + if(status == HasReturnvaluesIF::RETURN_OK) { + *receivedFrom = this->lastPartner; + } return status; } @@ -71,17 +125,6 @@ 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, - MessageQueueId_t sentFrom, bool ignoreFault) { - return sendMessageFrom(defaultDestination,message,sentFrom,ignoreFault); -} - MessageQueueId_t MessageQueue::getDefaultDestination() const { return defaultDestination; } @@ -89,23 +132,6 @@ MessageQueueId_t MessageQueue::getDefaultDestination() const { bool MessageQueue::isDefaultDestinationSet() const { return 0; } -ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, - MessageQueueMessage *message, MessageQueueId_t sentFrom, - bool ignoreFault) { - message->setSender(sentFrom); - 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; - } - return HasReturnvaluesIF::RETURN_OK; -} diff --git a/osal/FreeRTOS/MessageQueue.h b/osal/FreeRTOS/MessageQueue.h index 32f41b44d..8edfed10c 100644 --- a/osal/FreeRTOS/MessageQueue.h +++ b/osal/FreeRTOS/MessageQueue.h @@ -4,9 +4,11 @@ #include #include #include +#include #include -#include +#include "queue.h" + //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/ @@ -21,11 +23,17 @@ * 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 + * + * 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; @@ -43,11 +51,15 @@ public: * This should be left default. */ MessageQueue( size_t message_depth = 3, size_t max_message_size = MessageQueueMessage::MAX_MESSAGE_SIZE ); + /** * @brief The destructor deletes the formerly created message queue. * @details This is accomplished by using the delete call provided by the operating system. */ virtual ~MessageQueue(); + + void switchSystemContext(CallContext callContext); + /** * @brief This operation sends a message to the given destination. * @details It directly uses the sendMessage call of the MessageQueueSender parent, but passes its @@ -74,6 +86,29 @@ public: */ ReturnValue_t reply( MessageQueueMessage* message ); + /** + * \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 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 @@ -107,26 +142,7 @@ public: * @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. */ @@ -148,12 +164,20 @@ protected: * \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 */ - static ReturnValue_t sendMessageFromMessageQueue(MessageQueueId_t sendTo,MessageQueueMessage* message, MessageQueueId_t sentFrom = NO_QUEUE,bool ignoreFault=false); + static ReturnValue_t sendMessageFromMessageQueue(MessageQueueId_t sendTo, + MessageQueueMessage* message, MessageQueueId_t sentFrom = NO_QUEUE, + bool ignoreFault=false, CallContext callContex = CallContext::task); + + static ReturnValue_t handleSendResult(BaseType_t result, bool ignoreFault); + + private: QueueHandle_t handle; MessageQueueId_t defaultDestination; MessageQueueId_t lastPartner; + CallContext callContext; //!< Stores the current system context }; #endif /* MESSAGEQUEUE_H_ */ diff --git a/osal/FreeRTOS/Mutex.cpp b/osal/FreeRTOS/Mutex.cpp index 7c5110915..616ef7b85 100644 --- a/osal/FreeRTOS/Mutex.cpp +++ b/osal/FreeRTOS/Mutex.cpp @@ -6,7 +6,9 @@ const uint32_t MutexIF::NO_TIMEOUT = 0; Mutex::Mutex() { handle = xSemaphoreCreateMutex(); - //TODO print error + if(handle == NULL) { + error << "Mutex creation failure" << std::endl; + } } Mutex::~Mutex() { @@ -18,8 +20,7 @@ Mutex::~Mutex() { ReturnValue_t Mutex::lockMutex(uint32_t timeoutMs) { if (handle == 0) { - //TODO Does not exist - return HasReturnvaluesIF::RETURN_FAILED; + return MutexIF::MUTEX_NOT_FOUND; } TickType_t timeout = portMAX_DELAY; if (timeoutMs != NO_TIMEOUT) { @@ -30,21 +31,18 @@ ReturnValue_t Mutex::lockMutex(uint32_t timeoutMs) { if (returncode == pdPASS) { return HasReturnvaluesIF::RETURN_OK; } else { - //TODO could not be acquired/timeout - return HasReturnvaluesIF::RETURN_FAILED; + return MutexIF::MUTEX_TIMEOUT; } } ReturnValue_t Mutex::unlockMutex() { if (handle == 0) { - //TODO Does not exist - return HasReturnvaluesIF::RETURN_FAILED; + return MutexIF::MUTEX_NOT_FOUND; } BaseType_t returncode = xSemaphoreGive(handle); if (returncode == pdPASS) { return HasReturnvaluesIF::RETURN_OK; } else { - //TODO is not owner - return HasReturnvaluesIF::RETURN_FAILED; + return MutexIF::CURR_THREAD_DOES_NOT_OWN_MUTEX; } } diff --git a/osal/FreeRTOS/Mutex.h b/osal/FreeRTOS/Mutex.h index 91f295853..2703df4eb 100644 --- a/osal/FreeRTOS/Mutex.h +++ b/osal/FreeRTOS/Mutex.h @@ -1,5 +1,5 @@ -#ifndef OS_RTEMS_MUTEX_H_ -#define OS_RTEMS_MUTEX_H_ +#ifndef FRAMEWORK_FREERTOS_MUTEX_H_ +#define FRAMEWORK_FREERTOS_MUTEX_H_ #include @@ -7,8 +7,14 @@ #include #include "semphr.h" - - +/** + * @brief OS component to implement MUTual EXclusion + * + * @details + * Mutexes are binary semaphores which include a priority inheritance mechanism. + * Documentation: https://www.freertos.org/Real-time-embedded-RTOS-mutexes.html + * @ingroup osal + */ class Mutex : public MutexIF { public: Mutex(); @@ -19,4 +25,4 @@ private: SemaphoreHandle_t handle; }; -#endif /* OS_RTEMS_MUTEX_H_ */ +#endif /* FRAMEWORK_FREERTOS_MUTEX_H_ */ diff --git a/osal/FreeRTOS/MutexFactory.cpp b/osal/FreeRTOS/MutexFactory.cpp index cadb54fbd..b3e3f02c9 100644 --- a/osal/FreeRTOS/MutexFactory.cpp +++ b/osal/FreeRTOS/MutexFactory.cpp @@ -1,6 +1,5 @@ #include - -#include "../FreeRTOS/Mutex.h" +#include //TODO: Different variant than the lazy loading in QueueFactory. What's better and why? -> one is on heap the other on bss/data //MutexFactory* MutexFactory::factoryInstance = new MutexFactory(); diff --git a/osal/FreeRTOS/PeriodicTask.h b/osal/FreeRTOS/PeriodicTask.h index e7ba8dd26..4cbffd5a2 100644 --- a/osal/FreeRTOS/PeriodicTask.h +++ b/osal/FreeRTOS/PeriodicTask.h @@ -1,5 +1,5 @@ -#ifndef MULTIOBJECTTASK_H_ -#define MULTIOBJECTTASK_H_ +#ifndef PERIODICTASK_H_ +#define PERIODICTASK_H_ #include #include @@ -107,4 +107,4 @@ protected: void taskFunctionality(void); }; -#endif /* MULTIOBJECTTASK_H_ */ +#endif /* PERIODICTASK_H_ */ diff --git a/osal/FreeRTOS/QueueFactory.cpp b/osal/FreeRTOS/QueueFactory.cpp index 5c7087dee..eaf245d37 100644 --- a/osal/FreeRTOS/QueueFactory.cpp +++ b/osal/FreeRTOS/QueueFactory.cpp @@ -1,6 +1,6 @@ #include -#include "../FreeRTOS/MessageQueue.h" +#include QueueFactory* QueueFactory::factoryInstance = NULL; diff --git a/osal/FreeRTOS/TaskFactory.cpp b/osal/FreeRTOS/TaskFactory.cpp index 7b2f70a3a..753da60f7 100644 --- a/osal/FreeRTOS/TaskFactory.cpp +++ b/osal/FreeRTOS/TaskFactory.cpp @@ -15,6 +15,7 @@ TaskFactory* TaskFactory::instance() { } /*** * Keep in Mind that you need to call before this vTaskStartScheduler()! + * High taskPriority_ number means high priority. */ PeriodicTaskIF* TaskFactory::createPeriodicTask(TaskName name_, TaskPriority taskPriority_, TaskStackSize stackSize_, diff --git a/osal/FreeRTOS/TaskManagement.cpp b/osal/FreeRTOS/TaskManagement.cpp new file mode 100644 index 000000000..9946833d2 --- /dev/null +++ b/osal/FreeRTOS/TaskManagement.cpp @@ -0,0 +1,32 @@ +/** + * @file TaskManagement.cpp + * + * @date 26.02.2020 + * + */ +#include +#include +#include "portmacro.h" +#include "task.h" + +/** + * TODO: This stuff is hardware and architecture and mission dependant... + * Some FreeRTOS implementations might be able to determine their own task context for example. + * If not ISRs are used, or task preemption is enabled, some of this stuff might + * not be necessary anyway. Maybe there is a better solution? + */ +void TaskManagement::requestContextSwitchFromTask() { + vTaskDelay(0); +} + +void TaskManagement::requestContextSwitch(CallContext callContext = CallContext::task) { + if(callContext == CallContext::isr) { + // This function depends on the partmacro.h definition for the specific device + portYIELD_FROM_ISR(); + } else { + requestContextSwitchFromTask(); + } +} + + + diff --git a/osal/FreeRTOS/TaskManagement.h b/osal/FreeRTOS/TaskManagement.h new file mode 100644 index 000000000..fd2bfc0b1 --- /dev/null +++ b/osal/FreeRTOS/TaskManagement.h @@ -0,0 +1,38 @@ +/** + * @file TaskManagement.h + * + * @date 26.02.2020 + */ + +#ifndef FRAMEWORK_OSAL_FREERTOS_TASKMANAGEMENT_H_ +#define FRAMEWORK_OSAL_FREERTOS_TASKMANAGEMENT_H_ + +/*! + * Used by functions to tell if they are being called from + * within an ISR or from a regular task. This is required because FreeRTOS + * has different functions for handling semaphores and messages from within an ISR and task. + */ + +enum CallContext { + task = 0x00,//!< task_context + isr = 0xFF //!< isr_context +}; + +class TaskManagement { +public: + /** + * In this function, a function dependant on the portmacro.h header function calls + * to request a context switch can be specified. + * This can be used if sending to the queue from an ISR caused a task to unblock + * and a context switch is required. + */ + static void requestContextSwitch(CallContext callContext); + + /** + * If task preemption in FreeRTOS is disabled, a context switch + * can be requested manually by calling this function. + */ + static void requestContextSwitchFromTask(void); +}; + +#endif /* FRAMEWORK_OSAL_FREERTOS_TASKMANAGEMENT_H_ */ diff --git a/osal/linux/FixedTimeslotTask.cpp b/osal/linux/FixedTimeslotTask.cpp index 99cbf8185..21040e6ef 100644 --- a/osal/linux/FixedTimeslotTask.cpp +++ b/osal/linux/FixedTimeslotTask.cpp @@ -40,6 +40,11 @@ uint32_t FixedTimeslotTask::getPeriodMs() const { ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId, uint32_t slotTimeMs, int8_t executionStep) { + if (!objectManager->get(componentId)) { + error << "Component " << std::hex << componentId << " not found, not adding it to pst" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + pst.addSlot(componentId, slotTimeMs, executionStep, this); return HasReturnvaluesIF::RETURN_OK; } diff --git a/parameters/ParameterHelper.cpp b/parameters/ParameterHelper.cpp index 75b71a7e9..6ebc9e629 100644 --- a/parameters/ParameterHelper.cpp +++ b/parameters/ParameterHelper.cpp @@ -3,7 +3,7 @@ #include ParameterHelper::ParameterHelper(ReceivesParameterMessagesIF* owner) : - owner(owner), storage(NULL) { + owner(owner), ownerQueueId(0), storage(NULL){ } diff --git a/power/PowerSwitchIF.h b/power/PowerSwitchIF.h index 1c4c06f7f..f25589c4d 100644 --- a/power/PowerSwitchIF.h +++ b/power/PowerSwitchIF.h @@ -11,8 +11,13 @@ #include #include /** - * This interface defines a connection to a device that is capable of turning on and off - * switches of devices identified by a switch ID. + * + * @brief This interface defines a connection to a device that is capable of turning on and off + * switches of devices identified by a switch ID. + * @details The virtual functions of this interface do not allow to make any assignments + * because they can be called asynchronosuly (const ending). + * + * @ingroup interfaces */ class PowerSwitchIF : public HasReturnvaluesIF { public: diff --git a/returnvalues/FwClassIds.h b/returnvalues/FwClassIds.h index d21861c08..120d8b8c9 100644 --- a/returnvalues/FwClassIds.h +++ b/returnvalues/FwClassIds.h @@ -24,6 +24,7 @@ enum { MEMORY_HELPER, //MH SERIALIZE_IF, //SE FIXED_MAP, //FM + FIXED_MULTIMAP, //FMM HAS_HEALTH_IF, //HHI FIFO_CLASS, //FF MESSAGE_PROXY, //MQP @@ -59,6 +60,7 @@ enum { SGP4PROPAGATOR_CLASS, //SGP4 53 MUTEX_IF, //MUX 54 MESSAGE_QUEUE_IF,//MQI 55 + SEMAPHORE_IF, //SPH 56 FW_CLASS_ID_COUNT //is actually count + 1 ! }; diff --git a/rmap/RmapDeviceCommunicationIF.h b/rmap/RmapDeviceCommunicationIF.h index 12ae67e4a..45963d860 100644 --- a/rmap/RmapDeviceCommunicationIF.h +++ b/rmap/RmapDeviceCommunicationIF.h @@ -4,7 +4,8 @@ #include /** - * @brief This class is a implementation of a DeviceCommunicationIF for RMAP calls. It expects RMAPCookies or a derived class of RMAPCookies + * @brief This class is a implementation of a DeviceCommunicationIF for RMAP calls. + * It expects RMAPCookies or a derived class of RMAPCookies * * @details The open, close and reOpen calls are mission specific * The open call might return any child of RMAPCookies @@ -44,7 +45,8 @@ public: /** - * Closing call of connection and memory free of cookie. Mission dependent call + * Closing call of connection and free memory of cookie. + * Mission dependent call * @param cookie */ virtual void close(Cookie *cookie) = 0; diff --git a/serialize/EndianSwapper.h b/serialize/EndianSwapper.h index 6ff544447..25fb681e9 100644 --- a/serialize/EndianSwapper.h +++ b/serialize/EndianSwapper.h @@ -5,6 +5,10 @@ #include #include +/** + * @brief Can be used to swap endianness of data + * into big endian + */ class EndianSwapper { private: EndianSwapper() { @@ -40,6 +44,32 @@ public: #elif BYTE_ORDER_SYSTEM == BIG_ENDIAN memcpy(out, in, size); return; +#endif + } + + /** + * Swap endianness of buffer entries + * Template argument specifies buffer type + * @param out + * @param in + * @param size + */ + template + static void swap(T * out, const T * in, uint32_t size) { +#ifndef BYTE_ORDER_SYSTEM +#error BYTE_ORDER_SYSTEM not defined +#elif BYTE_ORDER_SYSTEM == LITTLE_ENDIAN + const uint8_t * in_buffer = reinterpret_cast(in); + uint8_t * out_buffer = reinterpret_cast(out); + for (uint8_t count = 0; count < size; count++) { + for(uint8_t i = 0; i < sizeof(T);i++) { + out_buffer[sizeof(T)* (count + 1) - i - 1] = in_buffer[count * sizeof(T) + i]; + } + } + return; +#elif BYTE_ORDER_SYSTEM == BIG_ENDIAN + memcpy(out, in, size*sizeof(T)); + return; #endif } }; diff --git a/serialize/SerialArrayListAdapter.h b/serialize/SerialArrayListAdapter.h index 5ffbc3757..3f80e97b6 100644 --- a/serialize/SerialArrayListAdapter.h +++ b/serialize/SerialArrayListAdapter.h @@ -12,6 +12,7 @@ #include /** + * Also serializes length field ! * \ingroup serialize */ template @@ -27,6 +28,7 @@ public: static ReturnValue_t serialize(const ArrayList* list, uint8_t** buffer, uint32_t* size, const uint32_t max_size, bool bigEndian) { + // Serialize length field first ReturnValue_t result = SerializeAdapter::serialize(&list->size, buffer, size, max_size, bigEndian); count_t i = 0; @@ -76,6 +78,11 @@ public: } return result; } + + + static void swapArrayListEndianness(ArrayList* list) { + list->swapArrayListEndianness(); + } private: ArrayList *adaptee; }; diff --git a/serialize/SerialBufferAdapter.cpp b/serialize/SerialBufferAdapter.cpp index 3ed083bf5..6ac2894fe 100644 --- a/serialize/SerialBufferAdapter.cpp +++ b/serialize/SerialBufferAdapter.cpp @@ -1,20 +1,28 @@ #include +#include #include - - template SerialBufferAdapter::SerialBufferAdapter(const uint8_t* buffer, - T bufferLength, bool serializeLenght) : - serializeLength(serializeLenght), constBuffer(buffer), buffer(NULL), bufferLength( - bufferLength) { + T bufferLength, bool serializeLength) : + currentBufferType(bufferType::CONST), serializeLength(serializeLength), + constBuffer(buffer), buffer(NULL), bufferLength(bufferLength) { + } template SerialBufferAdapter::SerialBufferAdapter(uint8_t* buffer, T bufferLength, - bool serializeLenght) : - serializeLength(serializeLenght), constBuffer(NULL), buffer(buffer), bufferLength( - bufferLength) { + bool serializeLength) : + currentBufferType(bufferType::NORMAL),serializeLength(serializeLength), constBuffer(NULL), buffer(buffer), + bufferLength(bufferLength) { +} + +template +SerialBufferAdapter::SerialBufferAdapter(uint32_t* buffer, + T bufferLength, bool serializeLength) : + currentBufferType(bufferType::NORMAL),serializeLength(serializeLength), + constBuffer(NULL), buffer(reinterpret_cast(buffer)), + bufferLength(bufferLength*4) { } template @@ -63,6 +71,11 @@ ReturnValue_t SerialBufferAdapter::deSerialize(const uint8_t** buffer, //TODO Ignores Endian flag! if (buffer != NULL) { if(serializeLength){ + // Suggestion (would require removing rest of the block inside this if clause !): + //ReturnValue_t result = AutoSerializeAdapter::deSerialize(&bufferLength,buffer,size,bigEndian); + //if (result != HasReturnvaluesIF::RETURN_OK) { + // return result; + //} T serializedSize = AutoSerializeAdapter::getSerializedSize( &bufferLength); if((*size - bufferLength - serializedSize) >= 0){ @@ -86,6 +99,35 @@ ReturnValue_t SerialBufferAdapter::deSerialize(const uint8_t** buffer, } } +template +uint8_t * SerialBufferAdapter::getBuffer() { + if(currentBufferType != NORMAL) { + warning << "Wrong access function for stored type ! Use getConstBuffer()" << std::endl; + return 0; + } + return buffer; +} + +template +const uint8_t * SerialBufferAdapter::getConstBuffer() { + if(currentBufferType != CONST) { + warning << "Wrong access function for stored type ! Use getBuffer()" << std::endl; + return 0; + } + return constBuffer; +} + +template +void SerialBufferAdapter::setBuffer(uint8_t * buffer_, T bufferLength_) { + buffer = buffer_; + bufferLength = bufferLength_; +} + +template +void SerialBufferAdapter::setBuffer(uint32_t * buffer_, T bufferLength_) { + buffer = reinterpret_cast(buffer_); + bufferLength = 4 * bufferLength_; +} //forward Template declaration for linker template class SerialBufferAdapter; diff --git a/serialize/SerialBufferAdapter.h b/serialize/SerialBufferAdapter.h index 7cd75d556..600bd692a 100644 --- a/serialize/SerialBufferAdapter.h +++ b/serialize/SerialBufferAdapter.h @@ -5,14 +5,46 @@ #include /** + * This adapter provides an interface for SerializeIF to serialize or deserialize + * buffers with no length header but a known size. + * + * Additionally, the buffer length can be serialized too and will be put in front of the serialized buffer. + * + * Can be used with SerialLinkedListAdapter by declaring a SerializeElement with + * SerialElement> serialBufferElement. + * Right now, the SerialBufferAdapter must always be initialized with the buffer and size ! + * * \ingroup serialize */ template class SerialBufferAdapter: public SerializeIF { public: - SerialBufferAdapter(const uint8_t * buffer, T bufferLength, bool serializeLenght = false); - SerialBufferAdapter(uint8_t* buffer, T bufferLength, - bool serializeLenght = false); + /** + * Constructor for constant uint8_t buffer. Length field can be serialized optionally. + * Type of length can be supplied as template type. + * @param buffer + * @param bufferLength + * @param serializeLength + */ + SerialBufferAdapter(const uint8_t * buffer, T bufferLength, bool serializeLength = false); + + /** + * Constructor for non-constant uint8_t buffer. Length field can be serialized optionally. + * Type of length can be supplied as template type. + * @param buffer + * @param bufferLength + * @param serializeLength + */ + SerialBufferAdapter(uint8_t* buffer, T bufferLength, bool serializeLength = false); + + /** + * Constructoor for non-constant uint32_t buffer. Length field can be serialized optionally. + * Type of length can be supplied as template type. + * @param buffer + * @param bufferLength + * @param serializeLength + */ + SerialBufferAdapter(uint32_t* buffer,T bufferLength, bool serializeLength = false); virtual ~SerialBufferAdapter(); @@ -23,7 +55,19 @@ public: virtual ReturnValue_t deSerialize(const uint8_t** buffer, int32_t* size, bool bigEndian); + + uint8_t * getBuffer(); + const uint8_t * getConstBuffer(); + void setBuffer(uint8_t * buffer_, T bufferLength_); + void setBuffer(uint32_t * buffer_, T bufferLength_); private: + + enum bufferType { + NORMAL, + CONST + }; + bufferType currentBufferType; + bool serializeLength; const uint8_t *constBuffer; uint8_t *buffer; diff --git a/serialize/SerialBufferAdapter2.h b/serialize/SerialBufferAdapter2.h new file mode 100644 index 000000000..98e2d187d --- /dev/null +++ b/serialize/SerialBufferAdapter2.h @@ -0,0 +1,148 @@ +#ifndef SERIALBUFFERADAPTER2_H_ +#define SERIALBUFFERADAPTER2_H_ + +#include +#include +#include + +#include + +/** + * This adapter provides an interface for SerializeIF to serialize or deserialize + * buffers with no length header but a known size. + * + * Additionally, the buffer length can be serialized too and will be put in front of the serialized buffer. + * + * Can be used with SerialLinkedListAdapter by declaring a SerializeElement with + * SerialElement> serialBufferElement. + * Right now, the SerialBufferAdapter must always be initialized with the buffer and size ! + * + * \ingroup serialize + */ +template +class SerialBufferAdapter2: public SerializeIF { +public: + /** + * Constructor for constant uint8_t buffer. Length field can be serialized optionally. + * Type of length can be supplied as template type. + * @param buffer + * @param bufferLength + * @param serializeLength + */ + SerialBufferAdapter2(void * buffer_, count_t bufferLength_, bool serializeLength_ = false): + bufferLength(bufferLength_), serializeLength(serializeLength_) { + determineLengthInBytes(sizeof(BUFFER_TYPE)); + buffer = reinterpret_cast(buffer_); + constBuffer = NULL; + } + + SerialBufferAdapter2(const void * buffer_, count_t bufferLength_, bool serializeLength_ = false): + bufferLength(bufferLength_), serializeLength(serializeLength_) { + determineLengthInBytes(sizeof(BUFFER_TYPE)); + constBuffer = reinterpret_cast(buffer_); + buffer = NULL; + } + + ReturnValue_t serialize(uint8_t ** buffer, uint32_t* size, + const uint32_t max_size, bool bigEndian) const { + uint32_t serializedLength = bufferLength; + if (serializeLength) { + serializedLength += AutoSerializeAdapter::getSerializedSize( + &bufferLength); + } + if (*size + serializedLength > max_size) { + return BUFFER_TOO_SHORT; + } else { + if (serializeLength) { + AutoSerializeAdapter::serialize(&bufferLength, buffer, size, + max_size, bigEndian); + } + memcpy(*buffer, this->buffer, bufferLength); + *size += bufferLength; + (*buffer) += bufferLength; + return HasReturnvaluesIF::RETURN_OK; + } + } + + uint32_t getSerializedSize() const { + if (serializeLength) { + return bufferLength + AutoSerializeAdapter::getSerializedSize(&bufferLength); + } else { + return bufferLength; + } + } + + ReturnValue_t deSerialize(const uint8_t** buffer, + int32_t* size, bool bigEndian) { + //TODO Ignores Endian flag! + if (buffer != NULL) { + if(serializeLength){ + // Suggestion (would require removing rest of the block inside this if clause !): + //ReturnValue_t result = AutoSerializeAdapter::deSerialize(&bufferLength,buffer,size,bigEndian); + //if (result != HasReturnvaluesIF::RETURN_OK) { + // return result; + //} + count_t serializedSize = AutoSerializeAdapter::getSerializedSize( + &bufferLength); + if((*size - bufferLength - serializedSize) >= 0){ + *buffer += serializedSize; + *size -= serializedSize; + }else{ + return STREAM_TOO_SHORT; + } + } + //No Else If, go on with buffer + if (*size - bufferLength >= 0) { + *size -= bufferLength; + memcpy(this->buffer, *buffer, bufferLength); + (*buffer) += bufferLength; + return HasReturnvaluesIF::RETURN_OK; + } else { + return STREAM_TOO_SHORT; + } + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } + } + + + BUFFER_TYPE * getBuffer() { + return reinterpret_cast(buffer); + } + + void setBuffer(void * buffer_, count_t bufferLength_, bool serializeLength_ = false) { + buffer = buffer_; + bufferLength = bufferLength_; + serializeLength = serializeLength_; + determineLengthInBytes(sizeof(BUFFER_TYPE)); + } + + void setConstBuffer(const void * buffer_, count_t bufferLength_, bool serializeLength_ = false) { + constBuffer = buffer_; + bufferLength = bufferLength_; + serializeLength = serializeLength_; + determineLengthInBytes(sizeof(BUFFER_TYPE)); + } +private: + uint8_t * buffer; + const uint8_t * constBuffer; + count_t bufferLength; + bool serializeLength; + + void determineLengthInBytes(uint8_t typeSize) { + switch(typeSize) { + case(1): break; + case(2): + bufferLength *= 2; break; + case(4): + bufferLength *= 4; break; + case(8): + bufferLength *= 8; break; + default: + error << "Serial Buffer Adapter 2: Invalid type size, assuming regular uint8_t." << std::endl; + error << "Detected type size: " << (int) typeSize << std::endl; + } + } +}; + +#endif /* SERIALBUFFERADAPTER2_H_ */ diff --git a/serialize/SerialFixedArrayListAdapter.h b/serialize/SerialFixedArrayListAdapter.h index 16919b623..67954d685 100644 --- a/serialize/SerialFixedArrayListAdapter.h +++ b/serialize/SerialFixedArrayListAdapter.h @@ -5,24 +5,48 @@ #include /** - * \ingroup serialize + * @brief This adapter provides an interface for SerializeIF to serialize and deserialize + * buffers with a header containing the buffer length. + * @details + * + * Can be used by SerialLinkedListAdapter by declaring + * as a linked element with SerializeElement>. + * The sequence of objects is defined in the constructor by using the setStart and setNext functions. + * + * 1. Buffers with a size header inside that class can be declared with + * SerialFixedArrayListAdapter. + * 2. MAX_BUFFER_LENGTH specifies the maximum allowed number of elements in FixedArrayList + * 3. LENGTH_FIELD_TYPE specifies the data type of the buffer header containing the buffer size + * (defaults to 1 byte length field) that follows + * + * @ingroup serialize */ -template -class SerialFixedArrayListAdapter : public FixedArrayList, public SerializeIF { +template +class SerialFixedArrayListAdapter : public FixedArrayList, public SerializeIF { public: + /** + * Constructor Arguments are forwarded to FixedArrayList constructor + * @param args + */ template - SerialFixedArrayListAdapter(Args... args) : FixedArrayList(std::forward(args)...) { + SerialFixedArrayListAdapter(Args... args) : FixedArrayList(std::forward(args)...) { } + ReturnValue_t serialize(uint8_t** buffer, uint32_t* size, const uint32_t max_size, bool bigEndian) const { - return SerialArrayListAdapter::serialize(this, buffer, size, max_size, bigEndian); + return SerialArrayListAdapter::serialize(this, buffer, size, max_size, bigEndian); } + uint32_t getSerializedSize() const { - return SerialArrayListAdapter::getSerializedSize(this); + return SerialArrayListAdapter::getSerializedSize(this); } ReturnValue_t deSerialize(const uint8_t** buffer, int32_t* size, bool bigEndian) { - return SerialArrayListAdapter::deSerialize(this, buffer, size, bigEndian); + return SerialArrayListAdapter::deSerialize(this, buffer, size, bigEndian); + } + + void swapArrayListEndianness() { + SerialArrayListAdapter::swapArrayListEndianness(this); } }; diff --git a/serialize/SerialLinkedListAdapter.h b/serialize/SerialLinkedListAdapter.h index 29952c4a3..ef1fa2007 100644 --- a/serialize/SerialLinkedListAdapter.h +++ b/serialize/SerialLinkedListAdapter.h @@ -13,8 +13,30 @@ #include //This is where we need the SerializeAdapter! -/** - * \ingroup serialize + /** + * @brief Implement the conversion of object data to data streams + * or vice-versa, using linked lists. + * @details + * An alternative to the AutoSerializeAdapter functions + * - All object members with a datatype are declared as SerializeElement + * members inside the class implementing this adapter. + * - The element type can also be a SerialBufferAdapter to de-/serialize buffers, + * with a known size, where the size can also be serialized + * - The element type can also be a SerialFixedArrayListAdapter to de-/serialize buffers + * with a size header, which is scanned automatically + * + * The sequence of objects is defined in the constructor by using + * the setStart and setNext functions. + * + * - The serialization process is done by instantiating the class and + * calling serializ after all SerializeElement entries have been set by + * using the constructor or setter functions. An additional size variable can be supplied + * which is calculated/incremented automatically + * - The deserialization process is done by instantiating the class and supplying + * a buffer with the data which is converted into an object. The size of + * data to serialize can be supplied and is decremented in the function + * + * @ingroup serialize */ template class SerialLinkedListAdapter: public SinglyLinkedList, public SerializeIF { @@ -31,6 +53,16 @@ public: SinglyLinkedList(), printCount(printCount) { } + /** + * Serialize object implementing this adapter into the supplied buffer + * and calculate the serialized size + * @param buffer [out] Object is serialized into this buffer. Note that the buffer pointer + * *buffer is incremented automatically inside the respective serialize functions + * @param size [out] Calculated serialized size. Don't forget to set to 0. + * @param max_size + * @param bigEndian Specify endianness + * @return + */ virtual ReturnValue_t serialize(uint8_t** buffer, uint32_t* size, const uint32_t max_size, bool bigEndian) const { if (printCount) { @@ -73,6 +105,13 @@ public: return size; } + /** + * Deserialize supplied buffer with supplied size into object implementing this adapter + * @param buffer + * @param size Decremented in respective deSerialize functions automatically + * @param bigEndian Specify endianness + * @return + */ virtual ReturnValue_t deSerialize(const uint8_t** buffer, int32_t* size, bool bigEndian) { return deSerialize(SinglyLinkedList::start, buffer, size, bigEndian); diff --git a/serialize/SerializeAdapter.h b/serialize/SerializeAdapter.h index fd9e1b6af..18240a95e 100644 --- a/serialize/SerializeAdapter.h +++ b/serialize/SerializeAdapter.h @@ -7,8 +7,53 @@ #include #include -/** - * \ingroup serialize + /** + * @brief This adapter provides an interface to use the SerializeIF functions + * with arbitrary template objects to facilitate and simplify the serialization of classes + * with different multiple different data types into buffers and vice-versa. + * @details + * Examples: + * A report class is converted into a TM buffer. The report class implements a serialize functions and calls + * the AutoSerializeAdapter::serialize function repeatedly on all object data fields. + * The getSerializedSize function is implemented by calling the + * AutoSerializeAdapter::getSerializedSize function repeatedly on all data fields. + * + * The AutoSerializeAdapter functions can also be used as an alternative to memcpy + * to retrieve data out of a buffer directly into a class variable with data type T while being able to specify endianness. + * The boolean bigEndian specifies whether an endian swap is performed on the data before + * serialization or deserialization. + * + * If the target architecture is little endian (ARM), any data types created might + * have the wrong endiness if they are to be used for the FSFW. + * There are three ways to retrieve data out of a buffer to be used in the FSFW + * to use regular aligned (big endian) data. + * This can also be applied to uint32_t and uint64_t: + * + * 1. Use the AutoSerializeAdapter::deSerialize function + * The pointer *buffer will be incremented automatically by the typeSize of the object, + * so this function can be called on &buffer repeatedly without adjusting pointer position. + * Set bigEndian parameter to true to perform endian swapping. + * + * uint16_t data; + * int32_t dataLen = sizeof(data); + * ReturnValue_t result = AutoSerializeAdapter::deSerialize(&data,&buffer,&dataLen,true); + * + * 2. Perform a bitshift operation. Perform endian swapping if necessary: + * + * uint16_t data; + * data = buffer[targetByte1] << 8 | buffer[targetByte2]; + * data = EndianSwapper::swap(data); + * + * 3. Memcpy can be used when data is little-endian. Perform endian-swapping if necessary. + * + * uint16_t data; + * memcpy(&data,buffer + positionOfTargetByte1,sizeof(data)); + * data = EndianSwapper::swap(data); + * + * When serializing for downlink, the packets are generally serialized assuming big endian data format + * like seen in TmPacketStored.cpp for example. + * + * @ingroup serialize */ template class SerializeAdapter_ { @@ -35,6 +80,14 @@ public: } } + /** + * Deserialize buffer into object + * @param object [out] Object to be deserialized with buffer data + * @param buffer buffer containing the data. Non-Const pointer to non-const pointer to const buffer. + * @param size int32_t type to allow value to be values smaller than 0, needed for range/size checking + * @param bigEndian Specify endianness + * @return + */ ReturnValue_t deSerialize(T* object, const uint8_t** buffer, int32_t* size, bool bigEndian) { T tmp; @@ -100,7 +153,7 @@ public: } }; - +// No type specification necessary here. class AutoSerializeAdapter { public: template diff --git a/serialize/SerializeElement.h b/serialize/SerializeElement.h index db7db20a9..cd040c0f7 100644 --- a/serialize/SerializeElement.h +++ b/serialize/SerializeElement.h @@ -6,11 +6,23 @@ #include /** - * \ingroup serialize + * @brief This class is used to mark datatypes for serialization with the + * SerialLinkedListAdapter + * @details + * Used by declaring any arbitrary datatype with SerializeElement myVariable, + * inside a SerialLinkedListAdapter implementation and setting the sequence + * of objects with setNext() and setStart(). + * Serilization and Deserialization is then performed automatically in + * specified sequence order. + * @ingroup serialize */ template class SerializeElement : public SerializeIF, public LinkedElement { public: + /** + * Arguments are forwarded to the element datatype constructor + * @param args + */ template SerializeElement(Args... args) : LinkedElement(this), entry(std::forward(args)...) { diff --git a/serialize/SerializeIF.h b/serialize/SerializeIF.h index 701fbf563..fb750a083 100644 --- a/serialize/SerializeIF.h +++ b/serialize/SerializeIF.h @@ -4,13 +4,27 @@ #include /** - * \defgroup serialize Serialization + * @defgroup serialize Serialization * Contains serialisation services. */ /** - * Translation of objects into data streams. - * \ingroup serialize + * @brief An interface for alle classes which require + * translation of objects data into data streams and vice-versa. + * @details + * If the target architecture is little endian (e.g. ARM), any data types created might + * have the wrong endiness if they are to be used for the FSFW. + * There are three ways to retrieve data out of a buffer to be used in the FSFW to use + * regular aligned (big endian) data. This can also be applied to uint32_t and uint64_t: + * + * 1. Use the @c AutoSerializeAdapter::deSerialize function with @c bigEndian = true + * 2. Perform a bitshift operation + * 3. @c memcpy can be used when data is in little-endian format. Otherwise, @c EndianSwapper has to be used in conjuction. + * + * When serializing for downlink, the packets are generally serialized assuming big endian data format + * like seen in TmPacketStored.cpp for example. + * + * @ingroup serialize */ class SerializeIF { public: diff --git a/serviceinterface/ServiceInterfaceBuffer.cpp b/serviceinterface/ServiceInterfaceBuffer.cpp index 58065994a..6798b776d 100644 --- a/serviceinterface/ServiceInterfaceBuffer.cpp +++ b/serviceinterface/ServiceInterfaceBuffer.cpp @@ -1,10 +1,13 @@ #include #include #include +#include // to be implemented by bsp extern "C" void printChar(const char*); + + int ServiceInterfaceBuffer::overflow(int c) { // Handle output putChars(pbase(), pptr()); @@ -24,11 +27,12 @@ int ServiceInterfaceBuffer::sync(void) { Clock::TimeOfDay_t loggerTime; Clock::getDateAndTime(&loggerTime); char preamble[96] = { 0 }; - sprintf(preamble, "%s: | %lu:%02lu:%02lu.%03lu | ", - this->log_message.c_str(), (unsigned long) loggerTime.hour, - (unsigned long) loggerTime.minute, - (unsigned long) loggerTime.second, - (unsigned long) loggerTime.usecond /1000); + sprintf(preamble, "%s: | %" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ".%03" PRIu32 " | ", + this->log_message.c_str(), + loggerTime.hour, + loggerTime.minute, + loggerTime.second, + loggerTime.usecond /1000); // Write log_message and time this->putChars(preamble, preamble + sizeof(preamble)); // Handle output diff --git a/storagemanager/LocalPool.h b/storagemanager/LocalPool.h index 08cb017fc..f6d5ba8ac 100644 --- a/storagemanager/LocalPool.h +++ b/storagemanager/LocalPool.h @@ -39,7 +39,95 @@ public: * @brief This definition generally sets the number of different sized pools. * @details This must be less than the maximum number of pools (currently 0xff). */ -// static const uint32_t NUMBER_OF_POOLS; + // static const uint32_t NUMBER_OF_POOLS; + /** + * @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 + * 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. + * @param element_sizes An array of size NUMBER_OF_POOLS in which the size + * of a single element in each pool is determined. + * The sizes must be provided in ascending order. + * + * @param n_elements An array of size NUMBER_OF_POOLS in which the + * number of elements for each pool is determined. + * The position of these values correspond to those in + * element_sizes. + * @param registered Register the pool in object manager or not. Default is false (local pool). + * @param spillsToHigherPools + * A variable to determine whether higher n pools are used if the store is full. + */ + LocalPool(object_id_t setObjectId, + const uint16_t element_sizes[NUMBER_OF_POOLS], + const uint16_t n_elements[NUMBER_OF_POOLS], + bool registered = false, + bool spillsToHigherPools = false); + /** + * @brief In the LocalPool's destructor all allocated memory is freed. + */ + virtual ~LocalPool(void); + + /** + * Add data to local data pool, performs range check + * @param storageId [out] Store ID in which the data will be stored + * @param data + * @param size + * @param ignoreFault + * @return @c RETURN_OK if write was successful + */ + ReturnValue_t addData(store_address_t* storageId, const uint8_t * data, + uint32_t size, bool ignoreFault = false); + + /** + * With this helper method, a free element of \c size is reserved. + * @param storageId [out] storeID of the free element + * @param size The minimum packet size that shall be reserved. + * @param p_data [out] pointer to the pointer of free element + * @param ignoreFault + * @return Returns the storage identifier within the storage or + * StorageManagerIF::INVALID_ADDRESS (in raw). + */ + ReturnValue_t getFreeElement(store_address_t* storageId, + const uint32_t size, uint8_t** p_data, bool ignoreFault = false); + + /** + * Retrieve data from local pool + * @param packet_id + * @param packet_ptr + * @param size [out] Size of retrieved data + * @return @c RETURN_OK if data retrieval was successfull + */ + ReturnValue_t getData(store_address_t packet_id, const uint8_t** packet_ptr, + uint32_t* size); + + /** + * Modify data by supplying a packet pointer and using that packet pointer + * to access and modify the pool entry (via *pointer call) + * @param packet_id Store ID of data to modify + * @param packet_ptr [out] pointer to the pool entry to modify + * @param size [out] size of pool entry + * @return + */ + ReturnValue_t modifyData(store_address_t packet_id, uint8_t** packet_ptr, + uint32_t* size); + virtual ReturnValue_t deleteData(store_address_t); + virtual ReturnValue_t deleteData(uint8_t* ptr, uint32_t size, + store_address_t* storeId = NULL); + void clearStore(); + ReturnValue_t initialize(); +protected: + /** + * 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, + * - the return codes of #getPoolIndex or #findEmpty otherwise. + */ + virtual ReturnValue_t reserveSpace(const uint32_t size, store_address_t* address, bool ignoreFault); + + InternalErrorReporterIF *internalErrorReporter; private: /** * Indicates that this element is free. @@ -121,65 +209,6 @@ private: * - #DATA_STORAGE_FULL if the store is full */ ReturnValue_t findEmpty(uint16_t pool_index, uint16_t* element); -protected: - /** - * 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, - * - the return codes of #getPoolIndex or #findEmpty otherwise. - */ - virtual ReturnValue_t reserveSpace(const uint32_t size, store_address_t* address, bool ignoreFault); - - InternalErrorReporterIF *internalErrorReporter; -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 - * 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. - * @param element_sizes An array of size NUMBER_OF_POOLS in which the size - * of a single element in each pool is determined. - * The sizes must be provided in ascending order. - * - * @param n_elements An array of size NUMBER_OF_POOLS in which the - * number of elements for each pool is determined. - * The position of these values correspond to those in - * element_sizes. - * @param registered Register the pool in object manager or not. Default is false (local pool). - */ - LocalPool(object_id_t setObjectId, - const uint16_t element_sizes[NUMBER_OF_POOLS], - const uint16_t n_elements[NUMBER_OF_POOLS], - bool registered = false, - bool spillsToHigherPools = false); - /** - * @brief In the LocalPool's destructor all allocated memory is freed. - */ - virtual ~LocalPool(void); - ReturnValue_t addData(store_address_t* storageId, const uint8_t * data, - uint32_t size, bool ignoreFault = false); - - /** - * With this helper method, a free element of \c size is reserved. - * - * @param size The minimum packet size that shall be reserved. - * @return Returns the storage identifier within the storage or - * StorageManagerIF::INVALID_ADDRESS (in raw). - */ - ReturnValue_t getFreeElement(store_address_t* storageId, - const uint32_t size, uint8_t** p_data, bool ignoreFault = false); - ReturnValue_t getData(store_address_t packet_id, const uint8_t** packet_ptr, - uint32_t* size); - ReturnValue_t modifyData(store_address_t packet_id, uint8_t** packet_ptr, - uint32_t* size); - virtual ReturnValue_t deleteData(store_address_t); - virtual ReturnValue_t deleteData(uint8_t* ptr, uint32_t size, - store_address_t* storeId = NULL); - void clearStore(); - ReturnValue_t initialize(); }; template @@ -266,8 +295,8 @@ inline ReturnValue_t LocalPool::reserveSpace( if (!ignoreFault) { internalErrorReporter->storeFull(); } -// error << "LocalPool( " << std::hex << getObjectId() << std::dec -// << " )::reserveSpace: Packet store is full." << std::endl; + error << "LocalPool( " << std::hex << getObjectId() << std::dec + << " )::reserveSpace: Packet store is full." << std::endl; } return status; } @@ -276,7 +305,7 @@ template inline LocalPool::LocalPool(object_id_t setObjectId, const uint16_t element_sizes[NUMBER_OF_POOLS], const uint16_t n_elements[NUMBER_OF_POOLS], bool registered, bool spillsToHigherPools) : - SystemObject(setObjectId, registered), spillsToHigherPools(spillsToHigherPools), internalErrorReporter(NULL) { + SystemObject(setObjectId, registered), internalErrorReporter(NULL), spillsToHigherPools(spillsToHigherPools){ for (uint16_t n = 0; n < NUMBER_OF_POOLS; n++) { this->element_sizes[n] = element_sizes[n]; this->n_elements[n] = n_elements[n]; diff --git a/subsystem/SubsystemBase.h b/subsystem/SubsystemBase.h index 3294c46d7..1a050a9aa 100644 --- a/subsystem/SubsystemBase.h +++ b/subsystem/SubsystemBase.h @@ -12,6 +12,10 @@ #include #include +/** + * @defgroup subsystems Subsystem Objects + * Contains all Subsystem and Assemblies + */ class SubsystemBase: public SystemObject, public HasModesIF, public HasHealthIF, diff --git a/tcdistribution/PUSDistributor.cpp b/tcdistribution/PUSDistributor.cpp index a209620f9..7b413eab7 100644 --- a/tcdistribution/PUSDistributor.cpp +++ b/tcdistribution/PUSDistributor.cpp @@ -31,7 +31,7 @@ iterator_t PUSDistributor::selectDestination() { } if (tcStatus != RETURN_OK) { - debug << "PUSDistributor::handlePacket: error with " << (int) tcStatus + debug << "PUSDistributor::handlePacket: error with 0x" << std::hex << (int) tcStatus << std::endl; return this->queueMap.end(); } else { @@ -48,6 +48,7 @@ ReturnValue_t PUSDistributor::registerService(AcceptsTelecommandsIF* service) { ReturnValue_t returnValue = RETURN_OK; bool errorCode = true; uint16_t serviceId = service->getIdentifier(); + //info << "Service ID: " << (int)serviceId << std::endl; MessageQueueId_t queue = service->getRequestQueue(); errorCode = this->queueMap.insert( std::pair(serviceId, queue)).second; diff --git a/thermal/AbstractTemperatureSensor.h b/thermal/AbstractTemperatureSensor.h index 75437dca8..4b8693655 100644 --- a/thermal/AbstractTemperatureSensor.h +++ b/thermal/AbstractTemperatureSensor.h @@ -10,6 +10,16 @@ #include "ThermalModuleIF.h" #include "tcsDefinitions.h" +/** + * @defgroup thermal Thermal Components + * @brief Contains all components related to thermal tasks (sensors, heaters) + */ + +/** + * @brief Base class for Temperature Sensor, implements all important interfaces. + * Please use the TemperatureSensor class to implement the actual sensors. + * @ingroup thermal + */ class AbstractTemperatureSensor: public HasHealthIF, public SystemObject, public ExecutableObjectIF, diff --git a/thermal/AcceptsThermalMessagesIF.h b/thermal/AcceptsThermalMessagesIF.h new file mode 100644 index 000000000..28c62af6a --- /dev/null +++ b/thermal/AcceptsThermalMessagesIF.h @@ -0,0 +1,22 @@ +/** + * \file AcceptsThermalMessagesIF.h + * + * \date 16.02.2020 + */ + +#ifndef FRAMEWORK_THERMAL_ACCEPTSTHERMALMESSAGESIF_H_ +#define FRAMEWORK_THERMAL_ACCEPTSTHERMALMESSAGESIF_H_ +#include + +class AcceptsThermalMessagesIF { +public: + + /** + * @brief This is the empty virtual destructor as required for C++ interfaces. + */ + virtual ~AcceptsThermalMessagesIF() { } + + virtual MessageQueueId_t getReceptionQueue() const = 0; +}; + +#endif /* FRAMEWORK_THERMAL_ACCEPTSTHERMALMESSAGESIF_H_ */ diff --git a/thermal/CoreComponent.h b/thermal/CoreComponent.h index f7e7aff8d..da77c65b3 100644 --- a/thermal/CoreComponent.h +++ b/thermal/CoreComponent.h @@ -8,6 +8,7 @@ #include #include +// TODO: Documentaiton, how to use this? only use Thermal Component, which inherits core component? class CoreComponent: public ThermalComponentIF { public: struct Parameters { diff --git a/thermal/RedundantHeater.h b/thermal/RedundantHeater.h index 29791a9a1..55d402d75 100644 --- a/thermal/RedundantHeater.h +++ b/thermal/RedundantHeater.h @@ -10,15 +10,14 @@ public: Parameters(uint32_t objectIdHeater0, uint32_t objectIdHeater1, uint8_t switch0Heater0, uint8_t switch1Heater0, uint8_t switch0Heater1, uint8_t switch1Heater1) : - objectIdHeater0(objectIdHeater0), objectIdHeater1( - objectIdHeater1), switch0Heater0(switch0Heater0), switch1Heater0( - switch1Heater0), switch0Heater1(switch0Heater1), switch1Heater1( - switch1Heater1) { + objectIdHeater0(objectIdHeater0), objectIdHeater1(objectIdHeater1), + switch0Heater0(switch0Heater0),switch1Heater0(switch1Heater0), + switch0Heater1(switch0Heater1), switch1Heater1(switch1Heater1) { } Parameters() : - objectIdHeater0(0), objectIdHeater1(0), switch0Heater0(0), switch1Heater0( - 0), switch0Heater1(0), switch1Heater1(0) { + objectIdHeater0(0), objectIdHeater1(0), switch0Heater0(0), + switch1Heater0(0), switch0Heater1(0), switch1Heater1(0) { } uint32_t objectIdHeater0; diff --git a/thermal/TemperatureSensor.h b/thermal/TemperatureSensor.h index de280d877..b4172670e 100644 --- a/thermal/TemperatureSensor.h +++ b/thermal/TemperatureSensor.h @@ -1,40 +1,100 @@ #ifndef TEMPERATURESENSOR_H_ #define TEMPERATURESENSOR_H_ +#include #include -#include "AbstractTemperatureSensor.h" #include -template +/** + * @brief This building block handles non-linear value conversion and + * range checks for analog temperature sensors. + * @details This class can be used to perform all necessary tasks for temperature sensors. + * A sensor can be instantiated by calling the constructor. + * The temperature is calculated from an input value with + * the calculateOutputTemperature() function. Range checking and + * limit monitoring is performed automatically. + * The inputType specifies the type of the raw input while the + * limitType specifies the type of the upper and lower limit to check against. + * @ingroup thermal + */ + +template class TemperatureSensor: public AbstractTemperatureSensor { public: + /** + * This structure contains parameters required for range checking + * and the conversion from the input value to the output temperature. + * a, b and c can be any parameters required to calculate the output + * temperature from the input value, depending on the formula used. + * + * The parameters a,b and c are used in the calculateOutputTemperature() call. + * + * The lower and upper limits can be specified in any type, for example float for C° values + * or any other type for raw values. + */ struct Parameters { float a; float b; float c; - T lowerLimit; - T upperLimit; - float gradient; + limitType lowerLimit; + limitType upperLimit; + float maxGradient; }; + + /** + * Forward declaration for explicit instantiation of used parameters. + */ struct UsedParameters { UsedParameters(Parameters parameters) : - a(parameters.a), b(parameters.b), c(parameters.c), gradient( - parameters.gradient) { - } + a(parameters.a), b(parameters.b), c(parameters.c), + gradient(parameters.maxGradient) {} float a; float b; float c; float gradient; }; - static const uint16_t ADDRESS_A = 0; - static const uint16_t ADDRESS_B = 1; - static const uint16_t ADDRESS_C = 2; - static const uint16_t ADDRESS_GRADIENT = 3; + /** + * Instantiate Temperature Sensor Object. + * @param setObjectid objectId of the sensor object + * @param inputValue Input value which is converted to a temperature + * @param poolVariable Pool Variable to store the temperature value + * @param vectorIndex Vector Index for the sensor monitor + * @param parameters Calculation parameters, temperature limits, gradient limit + * @param datapoolId Datapool ID of the output temperature + * @param outputSet Output dataset for the output temperature to fetch it with read() + * @param thermalModule respective thermal module, if it has one + */ + TemperatureSensor(object_id_t setObjectid, + inputType *inputValue, PoolVariableIF *poolVariable, + uint8_t vectorIndex, uint32_t datapoolId, Parameters parameters = {0, 0, 0, 0, 0, 0}, + DataSet *outputSet = NULL, ThermalModuleIF *thermalModule = NULL) : + AbstractTemperatureSensor(setObjectid, thermalModule), parameters(parameters), + inputValue(inputValue), poolVariable(poolVariable), + outputTemperature(datapoolId, outputSet, PoolVariableIF::VAR_WRITE), + sensorMonitor(setObjectid, DOMAIN_ID_SENSOR, + DataPool::poolIdAndPositionToPid(poolVariable->getDataPoolId(), vectorIndex), + DEFAULT_CONFIRMATION_COUNT, parameters.lowerLimit, parameters.upperLimit, + TEMP_SENSOR_LOW, TEMP_SENSOR_HIGH), + oldTemperature(20), uptimeOfOldTemperature( { INVALID_TEMPERATURE, 0 }) { + } + + +protected: + /** + * This formula is used to calculate the temperature from an input value + * with an arbitrary type. + * A default implementation is provided but can be replaced depending + * on the required calculation. + * @param inputTemperature + * @return + */ + virtual float calculateOutputTemperature(inputType inputValue) { + return parameters.a * inputValue * inputValue + + parameters.b * inputValue + parameters.c; + } - static const uint16_t DEFAULT_CONFIRMATION_COUNT = 1; //!< Changed due to issue with later temperature checking even tough the sensor monitor was confirming already (Was 10 before with comment = Correlates to a 10s confirmation time. Chosen rather large, should not be so bad for components and helps survive glitches.) - static const uint8_t DOMAIN_ID_SENSOR = 1; private: void setInvalid() { outputTemperature = INVALID_TEMPERATURE; @@ -47,22 +107,17 @@ protected: UsedParameters parameters; - T *inputTemperature; + inputType * inputValue; PoolVariableIF *poolVariable; PoolVariable outputTemperature; - LimitMonitor sensorMonitor; + LimitMonitor sensorMonitor; float oldTemperature; timeval uptimeOfOldTemperature; - virtual float calculateOutputTemperature(T inputTemperature) { - return parameters.a * inputTemperature * inputTemperature - + parameters.b * inputTemperature + parameters.c; - } - void doChildOperation() { if (!poolVariable->isValid() || !healthHelper.healthTable->isHealthy(getObjectId())) { @@ -70,7 +125,7 @@ protected: return; } - outputTemperature = calculateOutputTemperature(*inputTemperature); + outputTemperature = calculateOutputTemperature(*inputValue); outputTemperature.setValid(PoolVariableIF::VALID); timeval uptime; @@ -78,7 +133,7 @@ protected: if (uptimeOfOldTemperature.tv_sec != INVALID_UPTIME) { //In theory, we could use an AbsValueMonitor to monitor the gradient. - //But this would require storing the gradient in DP and quite some overhead. + //But this would require storing the maxGradient in DP and quite some overhead. //The concept of delta limits is a bit strange anyway. float deltaTime; float deltaTemp; @@ -96,8 +151,8 @@ protected: } } - //Check is done against raw limits. SHOULDDO: Why? Using °C would be more easy to handle. - sensorMonitor.doCheck(*inputTemperature); + //Check is done against raw limits. SHOULDDO: Why? Using °C would be more easy to handle. + sensorMonitor.doCheck(outputTemperature.value); if (sensorMonitor.isOutOfLimits()) { uptimeOfOldTemperature.tv_sec = INVALID_UPTIME; @@ -110,23 +165,6 @@ protected: } public: - TemperatureSensor(object_id_t setObjectid, - T *inputTemperature, PoolVariableIF *poolVariable, - uint8_t vectorIndex, Parameters parameters, uint32_t datapoolId, - DataSet *outputSet, ThermalModuleIF *thermalModule) : - AbstractTemperatureSensor(setObjectid, thermalModule), parameters( - parameters), inputTemperature(inputTemperature), poolVariable( - poolVariable), outputTemperature(datapoolId, outputSet, - PoolVariableIF::VAR_WRITE), sensorMonitor(setObjectid, - DOMAIN_ID_SENSOR, - DataPool::poolIdAndPositionToPid( - poolVariable->getDataPoolId(), vectorIndex), - DEFAULT_CONFIRMATION_COUNT, parameters.lowerLimit, - parameters.upperLimit, TEMP_SENSOR_LOW, TEMP_SENSOR_HIGH), oldTemperature( - 20), uptimeOfOldTemperature( { INVALID_TEMPERATURE, 0 }) { - - } - float getTemperature() { return outputTemperature; } @@ -135,6 +173,15 @@ public: return outputTemperature.isValid(); } + static const uint16_t ADDRESS_A = 0; + static const uint16_t ADDRESS_B = 1; + static const uint16_t ADDRESS_C = 2; + static const uint16_t ADDRESS_GRADIENT = 3; + + static const uint16_t DEFAULT_CONFIRMATION_COUNT = 1; //!< Changed due to issue with later temperature checking even tough the sensor monitor was confirming already (Was 10 before with comment = Correlates to a 10s confirmation time. Chosen rather large, should not be so bad for components and helps survive glitches.) + + static const uint8_t DOMAIN_ID_SENSOR = 1; + virtual ReturnValue_t getParameter(uint8_t domainId, uint16_t parameterId, ParameterWrapper *parameterWrapper, const ParameterWrapper *newValues, uint16_t startAtIndex) { diff --git a/thermal/ThermalComponent.cpp b/thermal/ThermalComponent.cpp index bf6f73986..15a2bbc9c 100644 --- a/thermal/ThermalComponent.cpp +++ b/thermal/ThermalComponent.cpp @@ -12,12 +12,10 @@ ThermalComponent::ThermalComponent(object_id_t reportingObjectId, CoreComponent(reportingObjectId, domainId, temperaturePoolId, targetStatePoolId, currentStatePoolId, requestPoolId, dataSet, sensor, firstRedundantSensor, secondRedundantSensor, - thermalModule, - { parameters.lowerOpLimit, parameters.upperOpLimit, - parameters.heaterOn, parameters.hysteresis, - parameters.heaterSwitchoff }, priority, - ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL), nopParameters( - { parameters.lowerNopLimit, parameters.upperNopLimit }) { + thermalModule,{ parameters.lowerOpLimit, parameters.upperOpLimit, + parameters.heaterOn, parameters.hysteresis, parameters.heaterSwitchoff }, + priority, ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL), + nopParameters({ parameters.lowerNopLimit, parameters.upperNopLimit }) { } ThermalComponent::~ThermalComponent() { diff --git a/thermal/ThermalComponent.h b/thermal/ThermalComponent.h index 932438684..4e32e97cd 100644 --- a/thermal/ThermalComponent.h +++ b/thermal/ThermalComponent.h @@ -3,6 +3,9 @@ #include "CoreComponent.h" +/** + * What is it. How to use + */ class ThermalComponent: public CoreComponent { public: struct Parameters { @@ -14,10 +17,31 @@ public: float hysteresis; float heaterSwitchoff; }; + + /** + * Non-Operational Temperatures + */ struct NopParameters { float lowerNopLimit; float upperNopLimit; }; + + /** + * How to use. + * @param reportingObjectId + * @param domainId + * @param temperaturePoolId + * @param targetStatePoolId + * @param currentStatePoolId + * @param requestPoolId + * @param dataSet + * @param sensor + * @param firstRedundantSensor + * @param secondRedundantSensor + * @param thermalModule + * @param parameters + * @param priority + */ ThermalComponent(object_id_t reportingObjectId, uint8_t domainId, uint32_t temperaturePoolId, uint32_t targetStatePoolId, uint32_t currentStatePoolId, uint32_t requestPoolId, DataSet *dataSet, AbstractTemperatureSensor *sensor, diff --git a/thermal/ThermalModule.h b/thermal/ThermalModule.h index d8ac7d732..e65560cd6 100644 --- a/thermal/ThermalModule.h +++ b/thermal/ThermalModule.h @@ -11,6 +11,9 @@ #include "RedundantHeater.h" class PowerSwitchIF; +/** + * @brief Allows creation of different thermal control domains within a system. + */ class ThermalModule: public ThermalModuleIF { friend class ThermalController; public: diff --git a/thermal/ThermalMonitor.h b/thermal/ThermalMonitor.h index 6aca55abc..a38889d01 100644 --- a/thermal/ThermalMonitor.h +++ b/thermal/ThermalMonitor.h @@ -4,6 +4,9 @@ #include #include +/** + * What does it do. How to use it. + */ class ThermalMonitor: public MonitorReporter { public: template diff --git a/timemanager/CCSDSTime.cpp b/timemanager/CCSDSTime.cpp index 320327161..29aafb68d 100644 --- a/timemanager/CCSDSTime.cpp +++ b/timemanager/CCSDSTime.cpp @@ -1,5 +1,6 @@ #include #include +#include #include CCSDSTime::CCSDSTime() { @@ -154,15 +155,58 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t* if (length < 19) { return RETURN_FAILED; } +// Newlib nano can't parse uint8, see SCNu8 documentation and https://sourceware.org/newlib/README +// Suggestion: use uint16 all the time. This should work on all systems. +#ifdef NEWLIB_NANO_NO_C99_IO + uint16_t year; + uint16_t month; + uint16_t day; + uint16_t hour; + uint16_t minute; + float second; + int count = sscanf((char *) from, "%4" SCNu16 "-%2" SCNu16 "-%2" SCNu16 "T%2" SCNu16 ":%2" SCNu16 ":%fZ", &year, + &month, &day, &hour, &minute, &second); + if (count == 6) { + to->year = year; + to->month = month; + to->day = day; + to->hour = hour; + to->minute = minute; + to->second = second; + to->usecond = (second - floor(second)) * 1000000; + return RETURN_OK; + } + + // try Code B (yyyy-ddd) + count = sscanf((char *) from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu16 ":%2" SCNu16 ":%fZ", &year, &day, + &hour, &minute, &second); + if (count == 5) { + uint8_t tempDay; + ReturnValue_t result = CCSDSTime::convertDaysOfYear(day, year, + reinterpret_cast(&month), reinterpret_cast(&tempDay)); + if (result != RETURN_OK) { + return RETURN_FAILED; + } + to->year = year; + to->month = month; + to->day = tempDay; + to->hour = hour; + to->minute = minute; + to->second = second; + to->usecond = (second - floor(second)) * 1000000; + return RETURN_OK; + } +// Warning: Compiler/Linker fails ambiguously if library does not implement C99 I/O +#else uint16_t year; uint8_t month; uint16_t day; uint8_t hour; uint8_t minute; float second; -//try Code A (yyyy-mm-dd) + //try Code A (yyyy-mm-dd) int count = sscanf((char *) from, "%4hi-%2hhi-%2hiT%2hhi:%2hhi:%fZ", &year, - &month, &day, &hour, &minute, &second); + &month, &day, &hour, &minute, &second); if (count == 6) { to->year = year; to->month = month; @@ -193,6 +237,7 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t* to->usecond = (second - floor(second)) * 1000000; return RETURN_OK; } +#endif return UNSUPPORTED_TIME_FORMAT; } diff --git a/timemanager/ReceivesTimeInfoIF.h b/timemanager/ReceivesTimeInfoIF.h index 14a750c56..f65f9dadd 100644 --- a/timemanager/ReceivesTimeInfoIF.h +++ b/timemanager/ReceivesTimeInfoIF.h @@ -7,6 +7,7 @@ #ifndef RECEIVESTIMEINFOIF_H_ #define RECEIVESTIMEINFOIF_H_ +#include /** * This is a Interface for classes that receive timing information diff --git a/tmstorage/TmStoreBackendIF.h b/tmstorage/TmStoreBackendIF.h index 162bd7665..9b913b35a 100644 --- a/tmstorage/TmStoreBackendIF.h +++ b/tmstorage/TmStoreBackendIF.h @@ -49,14 +49,33 @@ public: static const Event AUTO_CATALOGS_SENDING_FAILED = MAKE_EVENT(15, SEVERITY::INFO);//!< Info that the a auto catalog report failed virtual ~TmStoreBackendIF() {} + + /** + * What do I need to implement here ? + * @param opCode + * @return + */ virtual ReturnValue_t performOperation(uint8_t opCode) = 0; virtual ReturnValue_t initialize() = 0; + + /** + * Implement the storage of TM packets to mass memory + * @param tmPacket + * @return + */ virtual ReturnValue_t storePacket(TmPacketMinimal* tmPacket) = 0; virtual ReturnValue_t setFetchLimitTime(const timeval* loverLimit, const timeval* upperLimit) = 0; virtual ReturnValue_t setFetchLimitBlocks(uint32_t startAddress, uint32_t endAddress) = 0; virtual ReturnValue_t fetchPackets(bool fromBegin = false) = 0; virtual ReturnValue_t initializeStore(object_id_t dumpTarget) = 0; virtual ReturnValue_t dumpIndex(store_address_t* storeId) = 0; + + /** + * TODO: Adapt for file management system? + * @param startAddress + * @param endAddress + * @return + */ virtual ReturnValue_t deleteBlocks(uint32_t startAddress, uint32_t endAddress) = 0; virtual ReturnValue_t deleteTime(const timeval* timeUntil, uint32_t* deletedPackets) = 0; diff --git a/tmstorage/TmStoreFrontendIF.h b/tmstorage/TmStoreFrontendIF.h index 787c35972..7d38cfc6b 100644 --- a/tmstorage/TmStoreFrontendIF.h +++ b/tmstorage/TmStoreFrontendIF.h @@ -11,6 +11,14 @@ class TmStoreBackendIF; class TmStoreFrontendIF { public: virtual TmStoreBackendIF* getBackend() const = 0; + + /** + * What do I need to implement here? + * This is propably used by PUS Service 15 so we should propably check for messages.. + * Provide base implementation? + * @param opCode + * @return + */ virtual ReturnValue_t performOperation(uint8_t opCode) = 0; /** * Callback from the back-end to indicate a certain packet was received. diff --git a/tmtcpacket/pus/TmPacketStored.cpp b/tmtcpacket/pus/TmPacketStored.cpp index f2c1eb28a..b1c1ec624 100644 --- a/tmtcpacket/pus/TmPacketStored.cpp +++ b/tmtcpacket/pus/TmPacketStored.cpp @@ -22,6 +22,7 @@ TmPacketStored::TmPacketStored(uint16_t apid, uint8_t service, (TmPacketBase::TM_PACKET_MIN_SIZE + size + headerSize), &pData); if (returnValue != store->RETURN_OK) { + debug << "TM Packet Stored: Issue getting free storage" << std::endl; checkAndReportLostTm(); return; } diff --git a/tmtcservices/AcceptsTelecommandsIF.h b/tmtcservices/AcceptsTelecommandsIF.h index 03a57aaeb..0952d3a60 100644 --- a/tmtcservices/AcceptsTelecommandsIF.h +++ b/tmtcservices/AcceptsTelecommandsIF.h @@ -12,7 +12,7 @@ class AcceptsTelecommandsIF { public: static const uint8_t INTERFACE_ID = CLASS_ID::ACCEPTS_TELECOMMANDS_IF; - static const ReturnValue_t ACTIVITY_STARTED = MAKE_RETURN_CODE(1); + static const ReturnValue_t ACTIVITY_STARTED = MAKE_RETURN_CODE(1); // is this used anywhere or can it be removed? static const ReturnValue_t INVALID_SUBSERVICE = MAKE_RETURN_CODE(2); static const ReturnValue_t ILLEGAL_APPLICATION_DATA = MAKE_RETURN_CODE(3); static const ReturnValue_t SEND_TM_FAILED = MAKE_RETURN_CODE(4); diff --git a/tmtcservices/CommandingServiceBase.h b/tmtcservices/CommandingServiceBase.h index ee59ffe44..a62b7397d 100644 --- a/tmtcservices/CommandingServiceBase.h +++ b/tmtcservices/CommandingServiceBase.h @@ -64,7 +64,7 @@ public: virtual ~CommandingServiceBase(); /*** - * This is the periodic called function + * This is the periodically called function. * Handle request queue for external commands. * Handle command Queue for internal commands. * @param opCode is unused here at the moment @@ -104,6 +104,66 @@ public: }; protected: + /** + * Check the target subservice + * @param subservice + * @return + */ + virtual ReturnValue_t isValidSubservice(uint8_t subservice) = 0; + + /** + * Once a TC Request is valid, the existence of the destination and its target interface is checked and retrieved. + * The target message queue ID can then be acquired by using the target interface. + * @param subservice + * @param tcData Application Data of TC Packet + * @param tcDataLen + * @param id MessageQueue ID is stored here + * @param objectId Object ID is extracted and stored here + * @return - @c RETURN_OK on success + * - @c RETURN_FAILED + * - @c CSB or implementation specific return codes + */ + virtual ReturnValue_t getMessageQueueAndObject(uint8_t subservice, + const uint8_t *tcData, uint32_t tcDataLen, MessageQueueId_t *id, + object_id_t *objectId) = 0; + + /** + * After the Message Queue and Object ID are determined, + * the command is prepared by using an implementation specific CommandMessage type + * which is sent to the target object. + * It contains all necessary information for the device to execute telecommands. + * @param message + * @param subservice + * @param tcData + * @param tcDataLen + * @param state + * @param objectId Target object ID + * @return + */ + virtual ReturnValue_t prepareCommand(CommandMessage *message, + uint8_t subservice, const uint8_t *tcData, uint32_t tcDataLen, + uint32_t *state, object_id_t objectId) = 0; + + /** + * This function is responsible for the communication between the Command Service Base + * and the respective PUS Commanding Service once the execution has started. + * The PUS Commanding Service receives replies from the target device and forwards them by calling this function. + * There are different translations of these replies to specify how the Command Service proceeds. + * @param reply[out] Command Message which contains information about the command + * @param previousCommand [out] + * @param state + * @param optionalNextCommand + * @param objectId Source object ID + * @param isStep Flag value to mark steps of command execution + * @return - @c RETURN_OK, @c EXECUTION_COMPLETE or @c NO_STEP_MESSAGE to generate TC verification success + * - @c INVALID_REPLY can handle unrequested replies + * - Anything else triggers a TC verification failure + */ + virtual ReturnValue_t handleReply(const CommandMessage *reply, + Command_t previousCommand, uint32_t *state, + CommandMessage *optionalNextCommand, object_id_t objectId, + bool *isStep) = 0; + struct CommandInfo { struct tcInfo { uint8_t ackFlags; @@ -180,20 +240,6 @@ protected: */ void sendTmPacket(uint8_t subservice, SerializeIF* content, SerializeIF* header = NULL); - virtual ReturnValue_t isValidSubservice(uint8_t subservice) = 0; - - virtual ReturnValue_t prepareCommand(CommandMessage *message, - uint8_t subservice, const uint8_t *tcData, uint32_t tcDataLen, - uint32_t *state, object_id_t objectId) = 0; - - virtual ReturnValue_t handleReply(const CommandMessage *reply, - Command_t previousCommand, uint32_t *state, - CommandMessage *optionalNextCommand, object_id_t objectId, - bool *isStep) = 0; - - virtual ReturnValue_t getMessageQueueAndObject(uint8_t subservice, - const uint8_t *tcData, uint32_t tcDataLen, MessageQueueId_t *id, - object_id_t *objectId) = 0; virtual void handleUnrequestedReply(CommandMessage *reply); @@ -209,7 +255,8 @@ private: * It handles replies generated by the devices and relayed by the specific service implementation. * This means that it determines further course of action depending on the return values specified * in the service implementation. - * This includes the generation of TC verification messages: + * This includes the generation of TC verification messages. Note that + * the static framework object ID @c VerificationReporter::messageReceiver needs to be set. * - TM[1,5] Step Successs * - TM[1,6] Step Failure * - TM[1,7] Completion Success diff --git a/tmtcservices/PusServiceBase.cpp b/tmtcservices/PusServiceBase.cpp index c8f6accf8..6e1053252 100644 --- a/tmtcservices/PusServiceBase.cpp +++ b/tmtcservices/PusServiceBase.cpp @@ -23,14 +23,14 @@ ReturnValue_t PusServiceBase::performOperation(uint8_t opCode) { TmTcMessage message; for (uint8_t count = 0; count < PUS_SERVICE_MAX_RECEPTION; count++) { ReturnValue_t status = this->requestQueue->receiveMessage(&message); - // debug << "PusServiceBase::performOperation: Receiving from MQ ID: " << std::hex << this->requestQueue.getId() << std::dec << " returned: " << status << std::endl; + // debug << "PusServiceBase::performOperation: Receiving from MQ ID: " << std::hex << this->requestQueue.getId() + // << std::dec << " returned: " << status << std::endl; if (status == RETURN_OK) { this->currentPacket.setStoreAddress(message.getStorageId()); -// info << "Service " << (uint16_t) this->serviceId << ": new packet!" -// << std::endl; + // info << "Service " << (uint16_t) this->serviceId << ": new packet!" << std::endl; ReturnValue_t return_code = this->handleRequest(); - // debug << "Service " << (uint16_t)this->serviceId << ": handleRequest returned: " << (int)return_code << std::endl; + // debug << "Service " << (uint16_t)this->serviceId << ": handleRequest returned: " << (int)return_code << std::endl; if (return_code == RETURN_OK) { this->verifyReporter.sendSuccessReport( TC_VERIFY::COMPLETION_SUCCESS, &this->currentPacket); @@ -44,7 +44,7 @@ ReturnValue_t PusServiceBase::performOperation(uint8_t opCode) { errorParameter2 = 0; } else if (status == MessageQueueIF::EMPTY) { status = RETURN_OK; - // debug << "PusService " << (uint16_t)this->serviceId << ": no new packet." << std::endl; + // debug << "PusService " << (uint16_t)this->serviceId << ": no new packet." << std::endl; break; } else { diff --git a/tmtcservices/PusServiceBase.h b/tmtcservices/PusServiceBase.h index 782d375ff..6c8c5f617 100644 --- a/tmtcservices/PusServiceBase.h +++ b/tmtcservices/PusServiceBase.h @@ -46,13 +46,20 @@ public: */ virtual ~PusServiceBase(); /** - * The handleRequest method shall handle any kind of Telecommand Request immediately. + * @brief The handleRequest method shall handle any kind of Telecommand Request immediately. + * @details * Implemetations can take the Telecommand in currentPacket and perform any kind of operation. * They may send additional "Start Success (1,3)" messages with the verifyReporter, but Completion * Success or Failure Reports are generated automatically after execution of this method. - * If a Telecommand can not be executed within one call cycle, this Base class is not the right parent. - * The child class may add additional error information in #errorParameters which are attached to the generated - * verification message. + * + * If a Telecommand can not be executed within one call cycle, + * this Base class is not the right parent. + * + * The child class may add additional error information by setting #errorParameters which are + * attached to the generated verification message. + * + * Subservice checking should be implemented in this method. + * * @return The returned status_code is directly taken as main error code in the Verification Report. * On success, RETURN_OK shall be returned. */ @@ -68,8 +75,8 @@ public: * It checks for new requests, and, if found, calls handleRequest, sends completion verification messages and deletes * the TC requests afterwards. * performService is always executed afterwards. - * @return - \c RETURN_OK if the periodic performService was successful. - * - \c RETURN_FAILED else. + * @return \c RETURN_OK if the periodic performService was successful. + * \c RETURN_FAILED else. */ ReturnValue_t performOperation(uint8_t opCode); virtual uint16_t getIdentifier(); @@ -91,7 +98,8 @@ protected: /** * One of two error parameters for additional error information. */ - uint8_t errorParameter2; + // shouldn't this be uint32_t ? The PUS Verification Message structure param2 has the size 4 + uint32_t errorParameter2; /** * This is a complete instance of the Telecommand reception queue of the class. * It is initialized on construction of the class. diff --git a/tmtcservices/TmTcBridge.cpp b/tmtcservices/TmTcBridge.cpp new file mode 100644 index 000000000..e402d79eb --- /dev/null +++ b/tmtcservices/TmTcBridge.cpp @@ -0,0 +1,175 @@ +/** + * @file TmTcBridge.cpp + * + * @date 26.12.2019 + * @author R. Mueller + */ + +#include + +#include +#include +#include + +TmTcBridge::TmTcBridge(object_id_t objectId_, object_id_t ccsdsPacketDistributor_): + SystemObject(objectId_),tcStore(NULL), tmStore(NULL), + ccsdsPacketDistributor(ccsdsPacketDistributor_), communicationLinkUp(false), + tmStored(false),recvBuffer(NULL), size(0) { + TmTcReceptionQueue = QueueFactory::instance()-> + createMessageQueue(TMTC_RECEPTION_QUEUE_DEPTH); + +} + +TmTcBridge::~TmTcBridge() { +} + +ReturnValue_t TmTcBridge::initialize() { + tcStore = objectManager->get(objects::TC_STORE); + if (tcStore == NULL) { + return RETURN_FAILED; + } + tmStore = objectManager->get(objects::TM_STORE); + if (tmStore == NULL) { + return RETURN_FAILED; + } + AcceptsTelecommandsIF* tcDistributor = + objectManager->get(ccsdsPacketDistributor); + if (tcDistributor == NULL) { + return RETURN_FAILED; + } + TmTcReceptionQueue->setDefaultDestination(tcDistributor->getRequestQueue()); + return RETURN_OK; +} + +ReturnValue_t TmTcBridge::performOperation(uint8_t operationCode) { + ReturnValue_t result; + result = handleTc(); + if(result != RETURN_OK) { + error << "TMTC Bridge: Error handling TCs" << std::endl; + } + result = handleTm(); + if (result != RETURN_OK) { + error << "TMTC Bridge: Error handling TMs" << std::endl; + } + return result; +} + +ReturnValue_t TmTcBridge::handleTc() { + ReturnValue_t result = receiveTc(&recvBuffer, &size); + return result; +} + +ReturnValue_t TmTcBridge::handleTm() { + ReturnValue_t result = readTmQueue(); + if(result != RETURN_OK) { + error << "TMTC Bridge: Reading TM Queue failed" << std::endl; + return RETURN_FAILED; + } + + if(tmStored && communicationLinkUp) { + result = sendStoredTm(); + } + return result; + +} + +ReturnValue_t TmTcBridge::readTmQueue() { + TmTcMessage message; + const uint8_t* data = NULL; + uint32_t size = 0; + for (ReturnValue_t result = TmTcReceptionQueue->receiveMessage(&message); + result == RETURN_OK; result = TmTcReceptionQueue->receiveMessage(&message)) + { + if(communicationLinkUp == false) { + result = storeDownlinkData(&message); + return result; + } + + result = tmStore->getData(message.getStorageId(), &data, &size); + if (result != HasReturnvaluesIF::RETURN_OK) { + continue; + } + + result = sendTm(data, size); + if (result != RETURN_OK) { + error << "TMTC Bridge: Could not send TM packet"<< std::endl; + tmStore->deleteData(message.getStorageId()); + return result; + + } + tmStore->deleteData(message.getStorageId()); + } + return RETURN_OK; +} + +ReturnValue_t TmTcBridge::storeDownlinkData(TmTcMessage *message) { + info << "TMTC Bridge: Comm Link down. " + "Saving packet ID to be sent later " << std::endl; + store_address_t storeId; + + if(fifo.full()) { + info << "TMTC Bridge: TM downlink max. number of stored packet IDs reached." + " Overwriting old data" << std::endl; + fifo.retrieve(&storeId); + tmStore->deleteData(storeId); + } + storeId = message->getStorageId(); + fifo.insert(storeId); + tmStored = true; + return RETURN_OK; +} + +ReturnValue_t TmTcBridge::sendStoredTm() { + uint8_t counter = 0; + ReturnValue_t result = RETURN_OK; + while(!fifo.empty() && counter < MAX_STORED_DATA_SENT_PER_CYCLE) { + info << "UDP Server: Sending stored TM data. There are " + << (int) fifo.size() << " left to send" << std::endl; + store_address_t storeId; + const uint8_t* data = NULL; + uint32_t size = 0; + fifo.retrieve(&storeId); + result = tmStore->getData(storeId, &data, &size); + sendTm(data,size); + if(result != RETURN_OK) { + error << "UDP Server: Could not send stored downlink data" << std::endl; + result = RETURN_FAILED; + } + counter ++; + + if(fifo.empty()) { + tmStored = false; + } + tmStore->deleteData(storeId); + } + return result; +} + +void TmTcBridge::registerCommConnect() { + if(!communicationLinkUp) { + info << "TMTC Bridge: Registered Comm Link Connect" << std::endl; + communicationLinkUp = true; + } +} + +void TmTcBridge::registerCommDisconnect() { + info << "TMTC Bridge: Registered Comm Link Disconnect" << std::endl; + if(communicationLinkUp) { + communicationLinkUp = false; + } +} + +MessageQueueId_t TmTcBridge::getReportReceptionQueue(uint8_t virtualChannel) { + return TmTcReceptionQueue->getId(); +} + +void TmTcBridge::printData(uint8_t * data, uint32_t dataLen) { + info << "TMTC Bridge: Printing data: ["; + for(uint32_t i=0;i +#include +#include +#include +#include + +#include +#include + +class TmTcBridge : public AcceptsTelemetryIF, + public ExecutableObjectIF, + public HasReturnvaluesIF, + public SystemObject { +public: + TmTcBridge(object_id_t objectId_, object_id_t ccsdsPacketDistributor_); + virtual ~TmTcBridge(); + + /** + * Initializes basic FSFW components for the TMTC Bridge + * @return + */ + virtual ReturnValue_t initialize(); + + /** + * @brief The performOperation method is executed in a task. + * @details There are no restrictions for calls within this method, so any + * other member of the class can be used. + * @return Currently, the return value is ignored. + */ + virtual ReturnValue_t performOperation(uint8_t operationCode = 0); + + /** + * Return TMTC Reception Queue + * @param virtualChannel + * @return + */ + virtual MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel = 0); + + void registerCommConnect(); + void registerCommDisconnect(); +protected: + MessageQueueIF* TmTcReceptionQueue; //!< Used to send and receive TMTC messages. TmTcMessage is used to transport messages between tasks. + StorageManagerIF* tcStore; + StorageManagerIF* tmStore; + object_id_t ccsdsPacketDistributor; + bool communicationLinkUp; //!< Used to specify whether communication link is up + bool tmStored; + + /** + * Handle TC reception. Default implementation provided + * @return + */ + virtual ReturnValue_t handleTc(); + + /** + * Implemented by child class. Perform receiving of Telecommand, for example by implementing + * specific drivers or wrappers, e.g. UART Communication or lwIP stack + * @param recvBuffer [out] Received data + * @param size [out] Size of received data + * @return + */ + virtual ReturnValue_t receiveTc(uint8_t ** recvBuffer, uint32_t * size) = 0; + + /** + * Handle Telemetry. Default implementation provided. + * Calls sendTm() + * @return + */ + virtual ReturnValue_t handleTm(); + + /** + * Read the TM Queue and send TM if necessary. Default implementation provided + * @return + */ + virtual ReturnValue_t readTmQueue(); + + /** + * Implemented by child class. Perform sending of Telemetry by implementing + * communication drivers or wrappers, e.g. UART communication or lwIP stack. + * @param data + * @param dataLen + * @return + */ + virtual ReturnValue_t sendTm(const uint8_t * data, uint32_t dataLen) = 0; + + /** + * Store data to be sent later if communication link is not up. + * @param message + * @return + */ + ReturnValue_t storeDownlinkData(TmTcMessage * message); + + /** + * Send stored data if communication link is active + * @return + */ + ReturnValue_t sendStoredTm(); + + /** + * Print data as hexidecimal array + * @param data + * @param dataLen + */ + void printData(uint8_t * data, uint32_t dataLen); + +private: + static const uint8_t TMTC_RECEPTION_QUEUE_DEPTH = 20; + static const uint8_t MAX_STORED_DATA_SENT_PER_CYCLE = 10; + static const uint8_t MAX_DOWNLINK_PACKETS_STORED = 15; + + FIFO fifo; + uint8_t * recvBuffer; + uint32_t size; +}; + + +#endif /* FRAMEWORK_TMTCSERVICES_TMTCBRIDGE_H_ */