From e9b86e51df95c5880ace312ed978933f98abde68 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 6 Sep 2020 15:23:38 +0200 Subject: [PATCH 001/119] init commit --- datapool/ControllerSet.h | 15 - datapool/DataPool.cpp | 131 -------- datapool/DataPool.h | 135 -------- datapool/DataSet.cpp | 150 --------- datapool/DataSet.h | 159 ---------- datapool/DataSetIF.h | 56 ++-- datapool/HkSwitchHelper.cpp | 13 +- datapool/PoolDataSetBase.cpp | 169 ++++++++++ datapool/PoolDataSetBase.h | 152 +++++++++ datapool/PoolDataSetIF.h | 33 ++ datapool/PoolEntry.cpp | 1 + datapool/PoolEntry.h | 6 +- datapool/PoolEntryIF.h | 6 +- datapool/PoolRawAccess.cpp | 187 ----------- datapool/PoolRawAccess.h | 152 --------- datapool/PoolVarList.h | 17 +- datapool/PoolVariable.h | 295 ------------------ datapool/PoolVariableIF.h | 114 ++++--- datapool/PoolVector.h | 233 -------------- datapool/SharedDataSetIF.h | 14 + {datapool => datapoolglob}/ControllerSet.cpp | 2 +- datapoolglob/ControllerSet.h | 15 + {datapool => datapoolglob}/DataPoolAdmin.cpp | 25 +- {datapool => datapoolglob}/DataPoolAdmin.h | 18 +- .../DataPoolParameterWrapper.cpp | 14 +- .../DataPoolParameterWrapper.h | 0 datapoolglob/GlobalDataPool.cpp | 133 ++++++++ datapoolglob/GlobalDataPool.h | 149 +++++++++ datapoolglob/GlobalDataSet.cpp | 48 +++ datapoolglob/GlobalDataSet.h | 98 ++++++ datapoolglob/GlobalPoolVariable.h | 213 +++++++++++++ datapoolglob/GlobalPoolVariable.tpp | 117 +++++++ datapoolglob/GlobalPoolVector.h | 185 +++++++++++ datapoolglob/GlobalPoolVector.tpp | 117 +++++++ {datapool => datapoolglob}/PIDReader.h | 39 ++- {datapool => datapoolglob}/PIDReaderList.h | 10 +- datapoolglob/PoolRawAccess.cpp | 239 ++++++++++++++ datapoolglob/PoolRawAccess.h | 220 +++++++++++++ 38 files changed, 2090 insertions(+), 1590 deletions(-) delete mode 100644 datapool/ControllerSet.h delete mode 100644 datapool/DataPool.cpp delete mode 100644 datapool/DataPool.h delete mode 100644 datapool/DataSet.cpp delete mode 100644 datapool/DataSet.h create mode 100644 datapool/PoolDataSetBase.cpp create mode 100644 datapool/PoolDataSetBase.h create mode 100644 datapool/PoolDataSetIF.h delete mode 100644 datapool/PoolRawAccess.cpp delete mode 100644 datapool/PoolRawAccess.h delete mode 100644 datapool/PoolVariable.h delete mode 100644 datapool/PoolVector.h create mode 100644 datapool/SharedDataSetIF.h rename {datapool => datapoolglob}/ControllerSet.cpp (79%) create mode 100644 datapoolglob/ControllerSet.h rename {datapool => datapoolglob}/DataPoolAdmin.cpp (94%) rename {datapool => datapoolglob}/DataPoolAdmin.h (92%) rename {datapool => datapoolglob}/DataPoolParameterWrapper.cpp (95%) rename {datapool => datapoolglob}/DataPoolParameterWrapper.h (100%) create mode 100644 datapoolglob/GlobalDataPool.cpp create mode 100644 datapoolglob/GlobalDataPool.h create mode 100644 datapoolglob/GlobalDataSet.cpp create mode 100644 datapoolglob/GlobalDataSet.h create mode 100644 datapoolglob/GlobalPoolVariable.h create mode 100644 datapoolglob/GlobalPoolVariable.tpp create mode 100644 datapoolglob/GlobalPoolVector.h create mode 100644 datapoolglob/GlobalPoolVector.tpp rename {datapool => datapoolglob}/PIDReader.h (77%) rename {datapool => datapoolglob}/PIDReaderList.h (68%) create mode 100644 datapoolglob/PoolRawAccess.cpp create mode 100644 datapoolglob/PoolRawAccess.h diff --git a/datapool/ControllerSet.h b/datapool/ControllerSet.h deleted file mode 100644 index e27debff..00000000 --- a/datapool/ControllerSet.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef CONTROLLERSET_H_ -#define CONTROLLERSET_H_ - -#include "DataSet.h" - -class ControllerSet :public DataSet { -public: - ControllerSet(); - virtual ~ControllerSet(); - - virtual void setToDefault() = 0; - void setInvalid(); -}; - -#endif /* CONTROLLERSET_H_ */ diff --git a/datapool/DataPool.cpp b/datapool/DataPool.cpp deleted file mode 100644 index 1b6cb7a1..00000000 --- a/datapool/DataPool.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "DataPool.h" -#include "../serviceinterface/ServiceInterfaceStream.h" -#include "../ipc/MutexFactory.h" - -DataPool::DataPool( void ( *initFunction )( std::map* pool_map ) ) { - mutex = MutexFactory::instance()->createMutex(); - if (initFunction != NULL ) { - initFunction( &this->data_pool ); - } -} - -DataPool::~DataPool() { - MutexFactory::instance()->deleteMutex(mutex); - for ( std::map::iterator it = this->data_pool.begin(); it != this->data_pool.end(); ++it ) { - delete it->second; - } -} - -//The function checks PID, type and array length before returning a copy of the PoolEntry. In failure case, it returns a temp-Entry with size 0 and NULL-ptr. -template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t sizeOrPosition ) { - std::map::iterator it = this->data_pool.find( data_pool_id ); - if ( it != this->data_pool.end() ) { - PoolEntry* entry = dynamic_cast< PoolEntry* >( it->second ); - if (entry != NULL ) { - if ( sizeOrPosition <= entry->length ) { - return entry; - } - } - } - return NULL; -} - -PoolEntryIF* DataPool::getRawData( uint32_t data_pool_id ) { - std::map::iterator it = this->data_pool.find( data_pool_id ); - if ( it != this->data_pool.end() ) { - return it->second; - } else { - return NULL; - } -} - -//uint8_t DataPool::getRawData( uint32_t data_pool_id, uint8_t* address, uint16_t* size, uint32_t maxSize ) { -// std::map::iterator it = this->data_pool.find( data_pool_id ); -// if ( it != this->data_pool.end() ) { -// if ( it->second->getByteSize() <= maxSize ) { -// *size = it->second->getByteSize(); -// memcpy( address, it->second->getRawData(), *size ); -// return DP_SUCCESSFUL; -// } -// } -// *size = 0; -// return DP_FAILURE; -//} - -ReturnValue_t DataPool::freeDataPoolLock() { - ReturnValue_t status = mutex->unlockMutex(); - if ( status != RETURN_OK ) { - sif::error << "DataPool::DataPool: unlock of mutex failed with error code: " << status << std::endl; - } - return status; -} - -ReturnValue_t DataPool::lockDataPool() { - ReturnValue_t status = mutex->lockMutex(MutexIF::BLOCKING); - if ( status != RETURN_OK ) { - sif::error << "DataPool::DataPool: lock of mutex failed with error code: " << status << std::endl; - } - return status; -} - -void DataPool::print() { - sif::debug << "DataPool contains: " << std::endl; - std::map::iterator dataPoolIt; - dataPoolIt = this->data_pool.begin(); - while( dataPoolIt != this->data_pool.end() ) { - sif::debug << std::hex << dataPoolIt->first << std::dec << " |"; - dataPoolIt->second->print(); - dataPoolIt++; - } -} - -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); -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 ); -template PoolEntry* DataPool::getData(uint32_t data_pool_id, - uint8_t size); - - -uint32_t DataPool::PIDToDataPoolId(uint32_t parameter_id) { - return (parameter_id >> 8) & 0x00FFFFFF; -} - -uint8_t DataPool::PIDToArrayIndex(uint32_t parameter_id) { - return (parameter_id & 0x000000FF); -} - -uint32_t DataPool::poolIdAndPositionToPid(uint32_t poolId, uint8_t index) { - return (poolId << 8) + index; -} - - -//SHOULDDO: Do we need a mutex lock here... I don't think so, as we only check static const values of elements in a list that do not change. -//there is no guarantee in the standard, but it seems to me that the implementation is safe -UM -ReturnValue_t DataPool::getType(uint32_t parameter_id, Type* type) { - std::map::iterator it = this->data_pool.find( PIDToDataPoolId(parameter_id)); - if ( it != this->data_pool.end() ) { - *type = it->second->getType(); - return RETURN_OK; - } else { - *type = Type::UNKNOWN_TYPE; - return RETURN_FAILED; - } -} - -bool DataPool::exists(uint32_t parameterId) { - uint32_t poolId = PIDToDataPoolId(parameterId); - uint32_t index = PIDToArrayIndex(parameterId); - std::map::iterator it = this->data_pool.find( poolId ); - if (it != data_pool.end()) { - if (it->second->getSize() >= index) { - return true; - } - } - return false; -} diff --git a/datapool/DataPool.h b/datapool/DataPool.h deleted file mode 100644 index 7abf2898..00000000 --- a/datapool/DataPool.h +++ /dev/null @@ -1,135 +0,0 @@ -/** - * \file DataPool.h - * - * \date 10/17/2012 - * \author Bastian Baetz - * - * \brief This file contains the definition of the DataPool class and (temporarily) - * the "extern" definition of the global dataPool instance. - */ - -#ifndef DATAPOOL_H_ -#define DATAPOOL_H_ - -#include "PoolEntry.h" -#include "../globalfunctions/Type.h" -#include "../ipc/MutexIF.h" -#include - -/** - * \defgroup data_pool Data Pool - * This is the group, where all classes associated with Data Pool Handling belong to. - * This includes classes to access Data Pool variables. - */ - -#define DP_SUCCESSFUL 0 -#define DP_FAILURE 1 - -/** - * \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. - * 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, - * which means read data is valid (if flagged so), but not necessarily up-to-date. - * Variables are either single values or arrays. - * \ingroup data_pool - */ -class DataPool : public HasReturnvaluesIF { -private: - /** - * \brief This is the actual data pool itself. - * \details It is represented by a map - * with the data pool id as index and a pointer to a single PoolEntry as value. - */ - std::map data_pool; -public: - /** - * \brief The mutex is created in the constructor and makes access mutual exclusive. - * \details Locking and unlocking the pool is only done by the DataSet class. - */ - MutexIF* mutex; - /** - * \brief In the classes constructor, the passed initialization function is called. - * \details To enable filling the pool, - * a pointer to the map is passed, allowing direct access to the pool's content. - * On runtime, adding or removing variables is forbidden. - */ - DataPool( void ( *initFunction )( std::map* pool_map ) ); - /** - * \brief The destructor iterates through the data_pool map and calls all Entries destructors to clean up the heap. - */ - ~DataPool(); - /** - * \brief This is the default call to access the pool. - * \details A pointer to the PoolEntry object is returned. - * The call checks data pool id, type and array size. Returns NULL in case of failure. - * \param data_pool_id The data pool id to search. - * \param sizeOrPosition The array size (not byte size!) of the pool entry, or the position the user wants to read. - * If smaller than the entry size, everything's ok. - */ - template PoolEntry* getData( uint32_t data_pool_id, uint8_t sizeOrPosition ); - /** - * \brief An alternative call to get a data pool entry in case the type is not implicitly known - * (i.e. in Housekeeping Telemetry). - * \details It returns a basic interface and does NOT perform - * a size check. The caller has to assure he does not copy too much data. - * Returns NULL in case the entry is not found. - * \param data_pool_id The data pool id to search. - */ - PoolEntryIF* getRawData( uint32_t data_pool_id ); - /** - * \brief This is a small helper function to facilitate locking the global data pool. - * \details It fetches the pool's mutex id and tries to acquire the mutex. - */ - ReturnValue_t lockDataPool(); - /** - * \brief This is a small helper function to facilitate unlocking the global data pool. - * \details It fetches the pool's mutex id and tries to free the mutex. - */ - ReturnValue_t freeDataPoolLock(); - /** - * \brief The print call is a simple debug method. - * \details It prints the current content of the data pool. - * It iterates through the data_pool map and calls each entry's print() method. - */ - void print(); - /** - * Extracts the data pool id from a SCOS 2000 PID. - * @param parameter_id The passed Parameter ID. - * @return The data pool id as used within the OBSW. - */ - static uint32_t PIDToDataPoolId( uint32_t parameter_id ); - /** - * Extracts an array index out of a SCOS 2000 PID. - * @param parameter_id The passed Parameter ID. - * @return The index of the corresponding data pool entry. - */ - static uint8_t PIDToArrayIndex( uint32_t parameter_id ); - /** - * Retransforms a data pool id and an array index to a SCOS 2000 PID. - */ - static uint32_t poolIdAndPositionToPid( uint32_t poolId, uint8_t index ); - - /** - * Method to return the type of a pool variable. - * @param parameter_id A parameterID (not pool id) of a DP member. - * @param type Returns the type or TYPE::UNKNOWN_TYPE - * @return RETURN_OK if parameter exists, RETURN_FAILED else. - */ - ReturnValue_t getType( uint32_t parameter_id, Type* type ); - - /** - * Method to check if a PID exists. - * Does not lock, as there's no possibility to alter the list that is checked during run-time. - * @param parameterId The PID (not pool id!) of a parameter. - * @return true if exists, false else. - */ - bool exists(uint32_t parameterId); -}; - -//We assume someone globally instantiates a DataPool. -extern DataPool dataPool; -#endif /* DATAPOOL_H_ */ diff --git a/datapool/DataSet.cpp b/datapool/DataSet.cpp deleted file mode 100644 index e41489bd..00000000 --- a/datapool/DataSet.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "DataSet.h" -#include "../serviceinterface/ServiceInterfaceStream.h" - -DataSet::DataSet() : - fill_count(0), state(DATA_SET_UNINITIALISED) { - for (unsigned count = 0; count < DATA_SET_MAX_SIZE; count++) { - registeredVariables[count] = NULL; - } -} - -DataSet::~DataSet() { - //Don't do anything with your variables, they are dead already! (Destructor is already called) -} - -ReturnValue_t DataSet::read() { - ReturnValue_t result = RETURN_OK; - if (state == DATA_SET_UNINITIALISED) { - lockDataPool(); - for (uint16_t count = 0; count < fill_count; count++) { - if (registeredVariables[count]->getReadWriteMode() - != PoolVariableIF::VAR_WRITE - && registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - ReturnValue_t status = registeredVariables[count]->read(); - if (status != RETURN_OK) { - result = INVALID_PARAMETER_DEFINITION; - break; - } - } - } - state = DATA_SET_WAS_READ; - freeDataPoolLock(); - } else { - sif::error << "DataSet::read(): Call made in wrong position." << std::endl; - result = SET_WAS_ALREADY_READ; - } - return result; -} - -ReturnValue_t DataSet::commit(uint8_t valid) { - setValid(valid); - return commit(); -} - -ReturnValue_t DataSet::commit() { - if (state == DATA_SET_WAS_READ) { - lockDataPool(); - for (uint16_t count = 0; count < fill_count; count++) { - if (registeredVariables[count]->getReadWriteMode() - != PoolVariableIF::VAR_READ - && registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - registeredVariables[count]->commit(); - } - } - state = DATA_SET_UNINITIALISED; - freeDataPoolLock(); - return RETURN_OK; - } else { - ReturnValue_t result = RETURN_OK; - lockDataPool(); - for (uint16_t count = 0; count < fill_count; count++) { - if (registeredVariables[count]->getReadWriteMode() - == PoolVariableIF::VAR_WRITE - && registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - registeredVariables[count]->commit(); - } else if (registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - if (result != COMMITING_WITHOUT_READING) { - sif::error << - "DataSet::commit(): commit-without-read " - "call made with non write-only variable." << std::endl; - result = COMMITING_WITHOUT_READING; - } - } - } - state = DATA_SET_UNINITIALISED; - freeDataPoolLock(); - return result; - } - -} - -void DataSet::registerVariable(PoolVariableIF* variable) { - if (state == DATA_SET_UNINITIALISED) { - if (variable != NULL) { - if (fill_count < DATA_SET_MAX_SIZE) { - registeredVariables[fill_count] = variable; - fill_count++; - return; - } - } - } - sif::error - << "DataSet::registerVariable: failed. Either NULL, or set is full, or call made in wrong position." - << std::endl; - return; -} - -uint8_t DataSet::freeDataPoolLock() { - return ::dataPool.freeDataPoolLock(); -} - -uint8_t DataSet::lockDataPool() { - return ::dataPool.lockDataPool(); -} - -ReturnValue_t DataSet::serialize(uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) const { - ReturnValue_t result = RETURN_FAILED; - for (uint16_t count = 0; count < fill_count; count++) { - result = registeredVariables[count]->serialize(buffer, size, maxSize, - streamEndianness); - if (result != RETURN_OK) { - return result; - } - } - return result; -} - -size_t DataSet::getSerializedSize() const { - size_t size = 0; - for (uint16_t count = 0; count < fill_count; count++) { - size += registeredVariables[count]->getSerializedSize(); - } - return size; -} - -void DataSet::setValid(uint8_t valid) { - for (uint16_t count = 0; count < fill_count; count++) { - if (registeredVariables[count]->getReadWriteMode() - != PoolVariableIF::VAR_READ) { - registeredVariables[count]->setValid(valid); - } - } -} - -ReturnValue_t DataSet::deSerialize(const uint8_t** buffer, size_t* size, - Endianness streamEndianness) { - ReturnValue_t result = RETURN_FAILED; - for (uint16_t count = 0; count < fill_count; count++) { - result = registeredVariables[count]->deSerialize(buffer, size, - streamEndianness); - if (result != RETURN_OK) { - return result; - } - } - return result; -} diff --git a/datapool/DataSet.h b/datapool/DataSet.h deleted file mode 100644 index 04044bfe..00000000 --- a/datapool/DataSet.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * \file DataSet.h - * - * \brief This file contains the DataSet class and a small structure called DataSetContent. - * - * \date 10/17/2012 - * - * \author Bastian Baetz - * - */ - -#ifndef DATASET_H_ -#define DATASET_H_ - -#include "DataPool.h" -#include "DataSetIF.h" -#include "PoolRawAccess.h" -#include "PoolVariable.h" -#include "PoolVarList.h" -#include "PoolVector.h" -#include "../serialize/SerializeAdapter.h" -/** - * \brief The DataSet class manages a set of locally checked out variables. - * - * \details This class manages a list, where a set of local variables (or pool variables) are - * registered. They are checked-out (i.e. their values are looked up and copied) - * with the read call. After the user finishes working with the pool variables, - * he can write back all variable values to the pool with the commit call. - * The data set manages locking and freeing the data pool, to ensure that all values - * are read and written back at once. - * An internal state manages usage of this class. Variables may only be registered before - * the read call is made, and the commit call only after the read call. - * If pool variables are writable and not committed until destruction of the set, the - * DataSet class automatically sets the valid flag in the data pool to invalid (without) - * changing the variable's value. - * - * \ingroup data_pool - */ -class DataSet: public DataSetIF, public HasReturnvaluesIF, public SerializeIF { -private: - //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 ); - static const ReturnValue_t SET_WAS_ALREADY_READ = MAKE_RETURN_CODE( 0x02 ); - static const ReturnValue_t COMMITING_WITHOUT_READING = - MAKE_RETURN_CODE(0x03); - - /** - * \brief The constructor simply sets the fill_count to zero and sets the state to "uninitialized". - */ - DataSet(); - /** - * \brief The destructor automatically manages writing the valid information of variables. - * \details In case the data set was read out, but not committed (indicated by state), - * the destructor parses all variables that are still registered to the set. - * For each, the valid flag in the data pool is set to "invalid". - */ - ~DataSet(); - /** - * \brief The read call initializes reading out all registered variables. - * \details It iterates through the list of registered variables and calls all read() - * functions of the registered pool variables (which read out their values from the - * data pool) which are not write-only. In case of an error (e.g. a wrong data type, - * or an invalid data pool id), the operation is aborted and - * \c INVALID_PARAMETER_DEFINITION returned. - * The data pool is locked during the whole read operation and freed afterwards. - * The state changes to "was written" after this operation. - * \return - \c RETURN_OK if all variables were read successfully. - * - \c INVALID_PARAMETER_DEFINITION if PID, size or type of the - * requested variable is invalid. - * - \c SET_WAS_ALREADY_READ if read() is called twice without calling - * commit() in between - */ - ReturnValue_t read(); - /** - * \brief The commit call initializes writing back the registered variables. - * \details It iterates through the list of registered variables and calls - * the commit() method of the remaining registered variables (which write back - * their values to the pool). - * The data pool is locked during the whole commit operation and freed afterwards. - * The state changes to "was committed" after this operation. - * If the set does contain at least one variable which is not write-only commit() - * can only be called after read(). If the set only contains variables which are - * write only, commit() can be called without a preceding read() call. - * \return - \c RETURN_OK if all variables were read successfully. - * - \c COMMITING_WITHOUT_READING if set was not read yet and contains non write-only - * variables - */ - ReturnValue_t commit(void); - /** - * Variant of method above which sets validity of all elements of the set. - * @param valid Validity information from PoolVariableIF. - * \return - \c RETURN_OK if all variables were read successfully. - * - \c COMMITING_WITHOUT_READING if set was not read yet and contains non write-only - * variables - */ - ReturnValue_t commit(uint8_t valid); - /** - * \brief This operation is used to register the local variables in the set. - * \details It copies all required information to the currently - * free space in the registeredVariables list. - */ - void registerVariable(PoolVariableIF* variable); - - /** - * Set the valid information of all variables contained in the set which are not readonly - * - * @param valid Validity information from PoolVariableIF. - */ - void setValid(uint8_t valid); - - ReturnValue_t serialize(uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) const override; - - size_t getSerializedSize() const override; - - ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, - Endianness streamEndianness) override; - -}; - -#endif /* DATASET_H_ */ diff --git a/datapool/DataSetIF.h b/datapool/DataSetIF.h index 7741477d..a6634a5c 100644 --- a/datapool/DataSetIF.h +++ b/datapool/DataSetIF.h @@ -1,39 +1,47 @@ -/** - * \file DataSetIF.h - * - * \brief This file contains the small interface to access the DataSet class. - * - * \date 10/23/2012 - * - * \author Bastian Baetz - * - */ - -#ifndef DATASETIF_H_ -#define DATASETIF_H_ +#ifndef FSFW_DATAPOOL_DATASETIF_H_ +#define FSFW_DATAPOOL_DATASETIF_H_ +#include "../returnvalues/HasReturnvaluesIF.h" +#include "../timemanager/Clock.h" class PoolVariableIF; /** - * \brief This class defines a small interface to register on a DataSet. + * @brief This class defines a small interface to register on a DataSet. * - * \details Currently, the only purpose of this interface is to provide a method for locally - * checked-out variables to register on a data set. Still, it may become useful for - * other purposes as well. - * - * \ingroup data_pool + * @details + * Currently, the only purpose of this interface is to provide a + * method for locally checked-out variables to register on a data set. + * Still, it may become useful for other purposes as well. + * @author Bastian Baetz + * @ingroup data_pool */ class DataSetIF { public: + static constexpr uint8_t INTERFACE_ID = CLASS_ID::DATA_SET_CLASS; + static constexpr ReturnValue_t INVALID_PARAMETER_DEFINITION = + MAKE_RETURN_CODE( 0x01 ); + static constexpr ReturnValue_t SET_WAS_ALREADY_READ = MAKE_RETURN_CODE( 0x02 ); + static constexpr ReturnValue_t COMMITING_WITHOUT_READING = + MAKE_RETURN_CODE(0x03); + + static constexpr ReturnValue_t DATA_SET_UNINITIALISED = MAKE_RETURN_CODE( 0x04 ); + static constexpr ReturnValue_t DATA_SET_FULL = MAKE_RETURN_CODE( 0x05 ); + static constexpr ReturnValue_t POOL_VAR_NULL = MAKE_RETURN_CODE( 0x06 ); + /** - * \brief This is an empty virtual destructor, as it is proposed for C++ interfaces. + * @brief This is an empty virtual destructor, + * as it is proposed for C++ interfaces. */ virtual ~DataSetIF() {} + /** - * \brief This operation provides a method to register local data pool variables - * to register in a data set by passing itself to this DataSet operation. + * @brief This operation provides a method to register local data pool + * variables to register in a data set by passing itself + * to this DataSet operation. */ - virtual void registerVariable( PoolVariableIF* variable ) = 0; + virtual ReturnValue_t registerVariable(PoolVariableIF* variable) = 0; + + virtual uint16_t getFillCount() const = 0; }; -#endif /* DATASETIF_H_ */ +#endif /* FSFW_DATAPOOL_DATASETIF_H_ */ diff --git a/datapool/HkSwitchHelper.cpp b/datapool/HkSwitchHelper.cpp index 04096aca..1a2a25eb 100644 --- a/datapool/HkSwitchHelper.cpp +++ b/datapool/HkSwitchHelper.cpp @@ -1,5 +1,4 @@ -#include "HkSwitchHelper.h" -//#include +#include "../datapool/HkSwitchHelper.h" #include "../ipc/QueueFactory.h" HkSwitchHelper::HkSwitchHelper(EventReportingProxyIF* eventProxy) : @@ -22,14 +21,14 @@ ReturnValue_t HkSwitchHelper::initialize() { } ReturnValue_t HkSwitchHelper::performOperation(uint8_t operationCode) { - CommandMessage message; - while (actionQueue->receiveMessage(&message) == HasReturnvaluesIF::RETURN_OK) { - ReturnValue_t result = commandActionHelper.handleReply(&message); + CommandMessage command; + while (actionQueue->receiveMessage(&command) == HasReturnvaluesIF::RETURN_OK) { + ReturnValue_t result = commandActionHelper.handleReply(&command); if (result == HasReturnvaluesIF::RETURN_OK) { continue; } - message.setToUnknownCommand(); - actionQueue->reply(&message); + command.setToUnknownCommand(); + actionQueue->reply(&command); } return HasReturnvaluesIF::RETURN_OK; diff --git a/datapool/PoolDataSetBase.cpp b/datapool/PoolDataSetBase.cpp new file mode 100644 index 00000000..1acd3fd3 --- /dev/null +++ b/datapool/PoolDataSetBase.cpp @@ -0,0 +1,169 @@ +#include "PoolDataSetBase.h" +#include "../serviceinterface/ServiceInterfaceStream.h" + +PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray, + const size_t maxFillCount): + registeredVariables(registeredVariablesArray), + maxFillCount(maxFillCount) { +} + +PoolDataSetBase::~PoolDataSetBase() {} + +ReturnValue_t PoolDataSetBase::registerVariable( + PoolVariableIF *variable) { + if (state != States::DATA_SET_UNINITIALISED) { + sif::error << "DataSet::registerVariable: " + "Call made in wrong position." << std::endl; + return DataSetIF::DATA_SET_UNINITIALISED; + } + if (variable == nullptr) { + sif::error << "DataSet::registerVariable: " + "Pool variable is nullptr." << std::endl; + return DataSetIF::POOL_VAR_NULL; + } + if (fillCount >= maxFillCount) { + sif::error << "DataSet::registerVariable: " + "DataSet is full." << std::endl; + return DataSetIF::DATA_SET_FULL; + } + registeredVariables[fillCount] = variable; + fillCount++; + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t PoolDataSetBase::read(uint32_t lockTimeout) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + if (state == States::DATA_SET_UNINITIALISED) { + lockDataPool(lockTimeout); + for (uint16_t count = 0; count < fillCount; count++) { + result = readVariable(count); + if(result != RETURN_OK) { + break; + } + } + state = States::DATA_SET_WAS_READ; + unlockDataPool(); + } + else { + sif::error << "DataSet::read(): " + "Call made in wrong position. Don't forget to commit" + " member datasets!" << std::endl; + result = SET_WAS_ALREADY_READ; + } + return result; +} + +uint16_t PoolDataSetBase::getFillCount() const { + return fillCount; +} + +ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + // These checks are often performed by the respective + // variable implementation too, but I guess a double check does not hurt. + if (registeredVariables[count]->getReadWriteMode() != + PoolVariableIF::VAR_WRITE and + registeredVariables[count]->getDataPoolId() + != PoolVariableIF::NO_PARAMETER) + { + result = registeredVariables[count]->readWithoutLock(); + if(result != HasReturnvaluesIF::RETURN_OK) { + result = INVALID_PARAMETER_DEFINITION; + } + } + return result; +} + +ReturnValue_t PoolDataSetBase::commit(uint32_t lockTimeout) { + if (state == States::DATA_SET_WAS_READ) { + handleAlreadyReadDatasetCommit(lockTimeout); + return HasReturnvaluesIF::RETURN_OK; + } + else { + return handleUnreadDatasetCommit(lockTimeout); + } +} + +void PoolDataSetBase::handleAlreadyReadDatasetCommit(uint32_t lockTimeout) { + lockDataPool(lockTimeout); + for (uint16_t count = 0; count < fillCount; count++) { + if (registeredVariables[count]->getReadWriteMode() + != PoolVariableIF::VAR_READ + && registeredVariables[count]->getDataPoolId() + != PoolVariableIF::NO_PARAMETER) { + registeredVariables[count]->commitWithoutLock(); + } + } + state = States::DATA_SET_UNINITIALISED; + unlockDataPool(); +} + +ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit(uint32_t lockTimeout) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + lockDataPool(lockTimeout); + for (uint16_t count = 0; count < fillCount; count++) { + if (registeredVariables[count]->getReadWriteMode() + == PoolVariableIF::VAR_WRITE + && registeredVariables[count]->getDataPoolId() + != PoolVariableIF::NO_PARAMETER) { + registeredVariables[count]->commitWithoutLock(); + } else if (registeredVariables[count]->getDataPoolId() + != PoolVariableIF::NO_PARAMETER) { + if (result != COMMITING_WITHOUT_READING) { + sif::error << "DataSet::commit(): commit-without-read call made " + "with non write-only variable." << std::endl; + result = COMMITING_WITHOUT_READING; + } + } + } + state = States::DATA_SET_UNINITIALISED; + unlockDataPool(); + return result; +} + + +ReturnValue_t PoolDataSetBase::lockDataPool(uint32_t timeoutMs) { + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t PoolDataSetBase::unlockDataPool() { + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, + const size_t maxSize, SerializeIF::Endianness streamEndianness) const { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t count = 0; count < fillCount; count++) { + result = registeredVariables[count]->serialize(buffer, size, maxSize, + streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return result; +} + +ReturnValue_t PoolDataSetBase::deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t count = 0; count < fillCount; count++) { + result = registeredVariables[count]->deSerialize(buffer, size, + streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return result; +} + +size_t PoolDataSetBase::getSerializedSize() const { + uint32_t size = 0; + for (uint16_t count = 0; count < fillCount; count++) { + size += registeredVariables[count]->getSerializedSize(); + } + return size; +} + +void PoolDataSetBase::setContainer(PoolVariableIF **variablesContainer) { + this->registeredVariables = variablesContainer; +} diff --git a/datapool/PoolDataSetBase.h b/datapool/PoolDataSetBase.h new file mode 100644 index 00000000..a8931d62 --- /dev/null +++ b/datapool/PoolDataSetBase.h @@ -0,0 +1,152 @@ +#ifndef FSFW_DATAPOOL_POOLDATASETBASE_H_ +#define FSFW_DATAPOOL_POOLDATASETBASE_H_ + +#include "PoolDataSetIF.h" +#include "PoolVariableIF.h" +#include "../ipc/MutexIF.h" + +/** + * @brief The DataSetBase class manages a set of locally checked out variables. + * @details + * This class manages a list, where a set of local variables (or pool variables) + * are registered. They are checked-out (i.e. their values are looked + * up and copied) with the read call. After the user finishes working with the + * pool variables, he can write back all variable values to the pool with + * the commit call. The data set manages locking and freeing the data pool, + * to ensure that all values are read and written back at once. + * + * An internal state manages usage of this class. Variables may only be + * registered before the read call is made, and the commit call only + * after the read call. + * + * If pool variables are writable and not committed until destruction + * of the set, the DataSet class automatically sets the valid flag in the + * data pool to invalid (without) changing the variable's value. + * + * The base class lockDataPool und unlockDataPool implementation are empty + * and should be implemented to protect the underlying pool type. + * @author Bastian Baetz + * @ingroup data_pool + */ +class PoolDataSetBase: public PoolDataSetIF, + public SerializeIF, + public HasReturnvaluesIF { +public: + + /** + * @brief Creates an empty dataset. Use registerVariable or + * supply a pointer to this dataset to PoolVariable + * initializations to register pool variables. + */ + PoolDataSetBase(PoolVariableIF** registeredVariablesArray, + const size_t maxFillCount); + virtual~ PoolDataSetBase(); + + /** + * @brief The read call initializes reading out all registered variables. + * @details + * It iterates through the list of registered variables and calls all read() + * functions of the registered pool variables (which read out their values + * from the data pool) which are not write-only. + * In case of an error (e.g. a wrong data type, or an invalid data pool id), + * the operation is aborted and @c INVALID_PARAMETER_DEFINITION returned. + * + * The data pool is locked during the whole read operation and + * freed afterwards.The state changes to "was written" after this operation. + * @return + * - @c RETURN_OK if all variables were read successfully. + * - @c INVALID_PARAMETER_DEFINITION if PID, size or type of the + * requested variable is invalid. + * - @c SET_WAS_ALREADY_READ if read() is called twice without calling + * commit() in between + */ + virtual ReturnValue_t read(uint32_t lockTimeout = + MutexIF::BLOCKING) override; + /** + * @brief The commit call initializes writing back the registered variables. + * @details + * It iterates through the list of registered variables and calls the + * commit() method of the remaining registered variables (which write back + * their values to the pool). + * + * The data pool is locked during the whole commit operation and + * freed afterwards. The state changes to "was committed" after this operation. + * + * If the set does contain at least one variable which is not write-only + * commit() can only be called after read(). If the set only contains + * variables which are write only, commit() can be called without a + * preceding read() call. + * @return - @c RETURN_OK if all variables were read successfully. + * - @c COMMITING_WITHOUT_READING if set was not read yet and + * contains non write-only variables + */ + virtual ReturnValue_t commit(uint32_t lockTimeout = + MutexIF::BLOCKING) override; + + /** + * Register the passed pool variable instance into the data set. + * @param variable + * @return + */ + virtual ReturnValue_t registerVariable( PoolVariableIF* variable) override; + /** + * Provides the means to lock the underlying data structure to ensure + * thread-safety. Default implementation is empty + * @return Always returns -@c RETURN_OK + */ + virtual ReturnValue_t lockDataPool(uint32_t timeoutMs = + MutexIF::BLOCKING) override; + /** + * Provides the means to unlock the underlying data structure to ensure + * thread-safety. Default implementation is empty + * @return Always returns -@c RETURN_OK + */ + virtual ReturnValue_t unlockDataPool() override; + + virtual uint16_t getFillCount() const; + + /* SerializeIF implementations */ + virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, + const size_t maxSize, + SerializeIF::Endianness streamEndianness) const override; + virtual size_t getSerializedSize() const override; + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) override; + +protected: + /** + * @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 fillCount = 0; + /** + * States of the seet. + */ + enum class 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 = States::DATA_SET_UNINITIALISED; + + /** + * @brief This array represents all pool variables registered in this set. + * Child classes can use a static or dynamic container to create + * an array of registered variables and assign the first entry here. + */ + PoolVariableIF** registeredVariables = nullptr; + const size_t maxFillCount = 0; + + void setContainer(PoolVariableIF** variablesContainer); + +private: + ReturnValue_t readVariable(uint16_t count); + void handleAlreadyReadDatasetCommit(uint32_t lockTimeout); + ReturnValue_t handleUnreadDatasetCommit(uint32_t lockTimeout); +}; + +#endif /* FSFW_DATAPOOL_POOLDATASETBASE_H_ */ diff --git a/datapool/PoolDataSetIF.h b/datapool/PoolDataSetIF.h new file mode 100644 index 00000000..aa45fa54 --- /dev/null +++ b/datapool/PoolDataSetIF.h @@ -0,0 +1,33 @@ +#ifndef FSFW_DATAPOOL_POOLDATASETIF_H_ +#define FSFW_DATAPOOL_POOLDATASETIF_H_ + +#include "DataSetIF.h" + +/** + * @brief Extendes the DataSetIF by adding abstract functions to lock + * and unlock a data pool and read/commit semantics. + */ +class PoolDataSetIF: public DataSetIF { +public: + virtual~ PoolDataSetIF() {}; + + virtual ReturnValue_t read(dur_millis_t lockTimeout) = 0; + virtual ReturnValue_t commit(dur_millis_t lockTimeout) = 0; + + /** + * @brief Most underlying data structures will have a pool like structure + * and will require a lock and unlock mechanism to ensure + * thread-safety + * @return Lock operation result + */ + virtual ReturnValue_t lockDataPool(dur_millis_t timeoutMs) = 0; + /** + * @brief Unlock call corresponding to the lock call. + * @return Unlock operation result + */ + virtual ReturnValue_t unlockDataPool() = 0; + + virtual bool isValid() const = 0; +}; + +#endif /* FSFW_DATAPOOL_POOLDATASETIF_H_ */ diff --git a/datapool/PoolEntry.cpp b/datapool/PoolEntry.cpp index 56e489a5..fb73328c 100644 --- a/datapool/PoolEntry.cpp +++ b/datapool/PoolEntry.cpp @@ -1,4 +1,5 @@ #include "PoolEntry.h" + #include "../serviceinterface/ServiceInterfaceStream.h" #include "../globalfunctions/arrayprinter.h" #include diff --git a/datapool/PoolEntry.h b/datapool/PoolEntry.h index 7b47c673..033db40d 100644 --- a/datapool/PoolEntry.h +++ b/datapool/PoolEntry.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_DATAPOOL_POOLENTRY_H_ -#define FRAMEWORK_DATAPOOL_POOLENTRY_H_ +#ifndef FSFW_DATAPOOL_POOLENTRY_H_ +#define FSFW_DATAPOOL_POOLENTRY_H_ #include "PoolEntryIF.h" @@ -127,4 +127,4 @@ public: Type getType(); }; -#endif /* POOLENTRY_H_ */ +#endif /* FSFW_DATAPOOL_POOLENTRY_H_ */ diff --git a/datapool/PoolEntryIF.h b/datapool/PoolEntryIF.h index 462de18b..d9db5237 100644 --- a/datapool/PoolEntryIF.h +++ b/datapool/PoolEntryIF.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_DATAPOOL_POOLENTRYIF_H_ -#define FRAMEWORK_DATAPOOL_POOLENTRYIF_H_ +#ifndef FSFW_DATAPOOL_POOLENTRYIF_H_ +#define FSFW_DATAPOOL_POOLENTRYIF_H_ #include "../globalfunctions/Type.h" #include @@ -60,4 +60,4 @@ public: virtual Type getType() = 0; }; -#endif /* POOLENTRYIF_H_ */ +#endif /* FSFW_DATAPOOL_POOLENTRYIF_H_ */ diff --git a/datapool/PoolRawAccess.cpp b/datapool/PoolRawAccess.cpp deleted file mode 100644 index f9264081..00000000 --- a/datapool/PoolRawAccess.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include "DataPool.h" -#include "PoolEntryIF.h" -#include "PoolRawAccess.h" -#include "../serviceinterface/ServiceInterfaceStream.h" -#include "../serialize/EndianConverter.h" - -#include - -PoolRawAccess::PoolRawAccess(uint32_t set_id, uint8_t setArrayEntry, - DataSetIF *data_set, ReadWriteMode_t setReadWriteMode) : - dataPoolId(set_id), arrayEntry(setArrayEntry), valid(false), type( - Type::UNKNOWN_TYPE), typeSize(0), arraySize(0), sizeTillEnd(0), readWriteMode( - setReadWriteMode) { - memset(value, 0, sizeof(value)); - if (data_set != NULL) { - data_set->registerVariable(this); - } -} - -PoolRawAccess::~PoolRawAccess() { - -} - -ReturnValue_t PoolRawAccess::read() { - 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 - } - } else { - //Error entry does not exist. - } - sif::error << "PoolRawAccess: read of DP Variable 0x" << std::hex - << dataPoolId << std::dec << " failed." << std::endl; - valid = INVALID; - typeSize = 0; - sizeTillEnd = 0; - memset(value, 0, sizeof(value)); - return HasReturnvaluesIF::RETURN_FAILED; -} - -ReturnValue_t PoolRawAccess::commit() { - PoolEntryIF *write_back = ::dataPool.getRawData(dataPoolId); - if ((write_back != NULL) && (readWriteMode != VAR_READ)) { - write_back->setValid(valid); - uint8_t array_position = arrayEntry * typeSize; - uint8_t *ptr = &((uint8_t*) write_back->getRawData())[array_position]; - memcpy(ptr, value, typeSize); - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } -} - -uint8_t* PoolRawAccess::getEntry() { - return value; -} - -ReturnValue_t PoolRawAccess::getEntryEndianSafe(uint8_t *buffer, - size_t *writtenBytes, size_t maxSize) { - uint8_t *data_ptr = getEntry(); -// debug << "PoolRawAccess::getEntry: Array position: " << index * size_of_type << " Size of T: " << (int)size_of_type << " ByteSize: " << byte_size << " Position: " << *size << std::endl; - if (typeSize == 0) { - return DATA_POOL_ACCESS_FAILED; - } - if (typeSize > maxSize) { - return INCORRECT_SIZE; - } - EndianConverter::convertBigEndian(buffer, data_ptr, typeSize); - *writtenBytes = typeSize; - return HasReturnvaluesIF::RETURN_OK; -} - -Type PoolRawAccess::getType() { - return type; -} - -size_t PoolRawAccess::getSizeOfType() { - return typeSize; -} - -size_t PoolRawAccess::getArraySize() { - return arraySize; -} - -uint32_t PoolRawAccess::getDataPoolId() const { - return dataPoolId; -} - -PoolVariableIF::ReadWriteMode_t PoolRawAccess::getReadWriteMode() const { - return readWriteMode; -} - -ReturnValue_t PoolRawAccess::setEntryFromBigEndian(const uint8_t *buffer, - size_t setSize) { - if (typeSize == setSize) { - EndianConverter::convertBigEndian(value, buffer, typeSize); - return HasReturnvaluesIF::RETURN_OK; - } else { - sif::error - << "PoolRawAccess::setEntryFromBigEndian: Illegal sizes: Internal" - << (uint32_t) typeSize << ", Requested: " << setSize - << std::endl; - return INCORRECT_SIZE; - } -} - -bool PoolRawAccess::isValid() const { - if (valid != INVALID) - return true; - else - return false; -} - -void PoolRawAccess::setValid(uint8_t valid) { - this->valid = valid; -} - -size_t PoolRawAccess::getSizeTillEnd() const { - return sizeTillEnd; -} - -ReturnValue_t PoolRawAccess::serialize(uint8_t **buffer, size_t *size, - size_t maxSize, Endianness streamEndianness) const { - if (typeSize + *size <= maxSize) { - switch (streamEndianness) { - case (Endianness::BIG): - EndianConverter::convertBigEndian(*buffer, value, typeSize); - break; - case (Endianness::LITTLE): - EndianConverter::convertLittleEndian(*buffer, value, typeSize); - break; - default: - case (Endianness::MACHINE): - memcpy(*buffer, value, typeSize); - break; - } - *size += typeSize; - (*buffer) += typeSize; - return HasReturnvaluesIF::RETURN_OK; - } else { - return SerializeIF::BUFFER_TOO_SHORT; - } -} - -size_t PoolRawAccess::getSerializedSize() const { - return typeSize; -} - -ReturnValue_t PoolRawAccess::deSerialize(const uint8_t **buffer, size_t *size, - Endianness streamEndianness) { - - if (*size >= typeSize) { - switch (streamEndianness) { - case (Endianness::BIG): - EndianConverter::convertBigEndian(value, *buffer, typeSize); - break; - case (Endianness::LITTLE): - EndianConverter::convertLittleEndian(value, *buffer, typeSize); - break; - default: - case (Endianness::MACHINE): - memcpy(value, *buffer, typeSize); - break; - } - *size -= typeSize; - *buffer += typeSize; - return HasReturnvaluesIF::RETURN_OK; - } else { - return SerializeIF::STREAM_TOO_SHORT; - } -} diff --git a/datapool/PoolRawAccess.h b/datapool/PoolRawAccess.h deleted file mode 100644 index 0c7a06bf..00000000 --- a/datapool/PoolRawAccess.h +++ /dev/null @@ -1,152 +0,0 @@ -#ifndef POOLRAWACCESS_H_ -#define POOLRAWACCESS_H_ - -#include "DataSetIF.h" -#include "PoolVariableIF.h" - -/** - * This class allows accessing Data Pool variables as raw bytes. - * 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 - */ -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. - */ - size_t typeSize; - /** - * The size of the DP array (single values return 1) - */ - size_t arraySize; - /** - * The size (in bytes) from the selected entry till the end of this DataPool variable. - */ - size_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(); -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); - uint8_t value[RAW_MAX_SIZE]; - PoolRawAccess(uint32_t data_pool_id, uint8_t arrayEntry, - DataSetIF *data_set, ReadWriteMode_t setReadWriteMode = - 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]). - */ - uint8_t* getEntry(); - /** - * \brief This operation returns the fetched entry from the data pool and - * flips the bytes, if necessary. - * \details It makes use of the getEntry call of this function, but additionally flips the - * bytes to big endian, which is the default for external communication (as House- - * keeping telemetry). To achieve this, the data is copied directly to the passed - * buffer, if it fits in the given maxSize. - * \param buffer A pointer to a buffer to write to - * \param writtenBytes The number of bytes written is returned with this value. - * \param maxSize The maximum size that the function may write to buffer. - * \return - \c RETURN_OK if entry could be acquired - * - \c RETURN_FAILED else. - */ - ReturnValue_t getEntryEndianSafe(uint8_t *buffer, size_t *size, - size_t maxSize); - /** - * With this method, the content can be set from a big endian buffer safely. - * @param buffer Pointer to the data to set - * @param size Size of the data to write. Must fit this->size. - * @return - \c RETURN_OK on success - * - \c RETURN_FAILED on failure - */ - ReturnValue_t setEntryFromBigEndian(const uint8_t *buffer, - size_t setSize); - /** - * \brief This operation returns the type of the entry currently stored. - */ - Type getType(); - /** - * \brief This operation returns the size of the entry currently stored. - */ - size_t getSizeOfType(); - /** - * - * @return the size of the datapool array - */ - size_t getArraySize(); - /** - * \brief This operation returns the data pool id of the variable. - */ - uint32_t getDataPoolId() const; - /** - * This method returns if the variable is read-write or read-only. - */ - ReadWriteMode_t getReadWriteMode() const; - /** - * \brief With this call, the valid information of the variable is returned. - */ - bool isValid() const; - - void setValid(uint8_t valid); - /** - * Getter for the remaining size. - */ - size_t getSizeTillEnd() const; - - ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize, - Endianness streamEndianness) const override; - - size_t getSerializedSize() const override; - - ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, - Endianness streamEndianness) override; -}; - -#endif /* POOLRAWACCESS_H_ */ diff --git a/datapool/PoolVarList.h b/datapool/PoolVarList.h index 035ff2ac..0b2b0f06 100644 --- a/datapool/PoolVarList.h +++ b/datapool/PoolVarList.h @@ -1,14 +1,15 @@ -#ifndef POOLVARLIST_H_ -#define POOLVARLIST_H_ +#ifndef FSFW_DATAPOOL_POOLVARLIST_H_ +#define FSFW_DATAPOOL_POOLVARLIST_H_ -#include "PoolVariable.h" -#include "PoolVariableIF.h" +#include "../datapool/PoolVariableIF.h" +#include "../datapoolglob/GlobalPoolVariable.h" template class PoolVarList { private: - PoolVariable variables[n_var]; + GlobPoolVar variables[n_var]; public: - PoolVarList( const uint32_t set_id[n_var], DataSetIF* dataSet, PoolVariableIF::ReadWriteMode_t setReadWriteMode ) { + PoolVarList( const uint32_t set_id[n_var], DataSetIF* dataSet, + PoolVariableIF::ReadWriteMode_t setReadWriteMode ) { //I really should have a look at the new init list c++ syntax. if (dataSet == NULL) { return; @@ -20,9 +21,9 @@ public: } } - PoolVariable &operator [](int i) { return variables[i]; } + GlobPoolVar &operator [](int i) { return variables[i]; } }; -#endif /* POOLVARLIST_H_ */ +#endif /* FSFW_DATAPOOL_POOLVARLIST_H_ */ diff --git a/datapool/PoolVariable.h b/datapool/PoolVariable.h deleted file mode 100644 index a9d3407d..00000000 --- a/datapool/PoolVariable.h +++ /dev/null @@ -1,295 +0,0 @@ -/* - * \file PoolVariable.h - * - * \brief This file contains the PoolVariable class, which locally represents a non-array data pool variable. - * - * \date 10/17/2012 - * - * \author Bastian Baetz - */ - -#ifndef POOLVARIABLE_H_ -#define POOLVARIABLE_H_ - -#include "DataSetIF.h" -#include "PoolEntry.h" -#include "PoolVariableIF.h" -#include "../serialize/SerializeAdapter.h" -#include "../serviceinterface/ServiceInterfaceStream.h" - -template class PoolVarList; - -/** - * \brief This is the access class for non-array data pool entries. - * - * \details To ensure safe usage of the data pool, operation is not done directly on the data pool - * entries, but on local copies. This class provides simple type-safe access to single - * data pool entries (i.e. entries with length = 1). - * The class can be instantiated as read-write and read only. - * It provides a commit-and-roll-back semantic, which means that the variable's value in - * the data pool is not changed until the commit call is executed. - * \tparam T The template parameter sets the type of the variable. Currently, all plain data types - * are supported, but in principle any type is possible. - * \ingroup data_pool - */ -template -class PoolVariable: public PoolVariableIF { - template friend class PoolVarList; -protected: - /** - * \brief To access the correct data pool entry on read and commit calls, the data pool id - * is stored. - */ - uint32_t dataPoolId; - /** - * \brief The valid information as it was stored in the data pool is copied to this attribute. - */ - uint8_t valid; - /** - * \brief The information whether the class is read-write or read-only is stored here. - */ - ReadWriteMode_t readWriteMode; - /** - * \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() { - PoolEntry *read_out = ::dataPool.getData < T > (dataPoolId, 1); - if (read_out != NULL) { - valid = read_out->valid; - value = *(read_out->address); - return HasReturnvaluesIF::RETURN_OK; - } else { - value = 0; - valid = false; - sif::error << "PoolVariable: read of DP Variable 0x" << std::hex - << dataPoolId << std::dec << " failed." << std::endl; - return HasReturnvaluesIF::RETURN_FAILED; - } - } - /** - * \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() { - PoolEntry *write_back = ::dataPool.getData < T > (dataPoolId, 1); - if ((write_back != NULL) && (readWriteMode != VAR_READ)) { - write_back->valid = valid; - *(write_back->address) = value; - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } - } - /** - * Empty ctor for List initialization - */ - PoolVariable() : - dataPoolId(PoolVariableIF::NO_PARAMETER), valid( - PoolVariableIF::INVALID), readWriteMode(VAR_READ), value(0) { - - } -public: - /** - * \brief This is the local copy of the data pool entry. - * \details The user can work on this attribute - * just like he would on a simple local variable. - */ - T value; - /** - * \brief In the constructor, the variable can register itself in a DataSet (if not NULL is - * passed). - * \details It DOES NOT fetch the current value from the data pool, but sets the value - * attribute to default (0). The value is fetched within the read() operation. - * \param set_id This is the id in the global data pool this instance of the access class - * 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. - */ - PoolVariable(uint32_t set_id, DataSetIF *dataSet, - ReadWriteMode_t setReadWriteMode) : - dataPoolId(set_id), valid(PoolVariableIF::INVALID), readWriteMode( - setReadWriteMode), value(0) { - if (dataSet != NULL) { - dataSet->registerVariable(this); - } - } - /** - * Copy ctor to copy classes containing Pool Variables. - */ - PoolVariable(const PoolVariable &rhs) : - dataPoolId(rhs.dataPoolId), valid(rhs.valid), readWriteMode( - rhs.readWriteMode), value(rhs.value) { - } - - /** - * \brief The classes destructor is empty. - * \details If commit() was not called, the local value is - * discarded and not written back to the data pool. - */ - ~PoolVariable() { - - } - /** - * \brief This operation returns the data pool id of the variable. - */ - uint32_t getDataPoolId() const { - return dataPoolId; - } - /** - * This operation sets the data pool id of the variable. - * The method is necessary to set id's of data pool member variables with bad initialization. - */ - void setDataPoolId(uint32_t poolId) { - dataPoolId = poolId; - } - /** - * This method returns if the variable is write-only, read-write or read-only. - */ - ReadWriteMode_t getReadWriteMode() const { - return readWriteMode; - } - /** - * \brief With this call, the valid information of the variable is returned. - */ - bool isValid() const { - if (valid) - return true; - else - return false; - } - - uint8_t getValid() { - return valid; - } - - void setValid(uint8_t valid) { - this->valid = valid; - } - - operator T() { - return value; - } - - operator T() const { - return value; - } - - PoolVariable& operator=(T newValue) { - value = newValue; - return *this; - } - - PoolVariable& operator=(PoolVariable newPoolVariable) { - value = newPoolVariable.value; - return *this; - } - - virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, - size_t maxSize, Endianness streamEndianness) const override { - return SerializeAdapter::serialize(&value, buffer, size, maxSize, - streamEndianness); - } - - virtual size_t getSerializedSize() const override { - return SerializeAdapter::getSerializedSize(&value); - } - - virtual ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, - Endianness streamEndianness) override { - return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness); - } -}; - -typedef PoolVariable db_uint8_t; -typedef PoolVariable db_uint16_t; -typedef PoolVariable db_uint32_t; -typedef PoolVariable db_int8_t; -typedef PoolVariable db_int16_t; -typedef PoolVariable db_int32_t; -typedef PoolVariable db_bool_t; -typedef PoolVariable db_float_t; -typedef PoolVariable db_double_t; -//Alternative (but I thing this is not as useful: code duplication, differences too small): - -//template -//class PoolReader : public PoolVariableIF { -//private: -// uint32_t parameter_id; -// uint8_t valid; -//public: -// T value; -// PoolReader( uint32_t set_id, DataSetIF* set ) : parameter_id(set_id), valid(false), value(0) { -// set->registerVariable( this ); -// } -// -// ~PoolReader() {}; -// -// uint8_t commit() { -// return HasReturnvaluesIF::RETURN_OK; -// } -// -// uint8_t read() { -// PoolEntry* read_out = ::dataPool.getData( parameter_id, 1 ); -// if ( read_out != NULL ) { -// valid = read_out->valid; -// value = *(read_out->address); -// return HasReturnvaluesIF::RETURN_OK; -// } else { -// value = 0; -// valid = false; -// return CHECKOUT_FAILED; -// } -// } -// uint32_t getParameterId() { return parameter_id; } -// bool isWritable() { return false; }; -// bool isValid() { if (valid) return true; else return false; } -//}; -// -//template -//class PoolWriter : public PoolVariableIF { -//private: -// uint32_t parameter_id; -//public: -// T value; -// PoolWriter( uint32_t set_id, DataSetIF* set ) : parameter_id(set_id), value(0) { -// set->registerVariable( this ); -// } -// -// ~PoolWriter() {}; -// -// uint8_t commit() { -// PoolEntry* write_back = ::dataPool.getData( parameter_id, 1 ); -// if ( write_back != NULL ) { -// write_back->valid = true; -// *(write_back->address) = value; -// return HasReturnvaluesIF::RETURN_OK; -// } else { -// return CHECKOUT_FAILED; -// } -// } -// uint8_t read() { -// PoolEntry* read_out = ::dataPool.getData( parameter_id, 1 ); -// if ( read_out != NULL ) { -// value = *(read_out->address); -// return HasReturnvaluesIF::RETURN_OK; -// } else { -// value = 0; -// return CHECKOUT_FAILED; -// } -// } -// uint32_t getParameterId() { return parameter_id; } -// bool isWritable() { return true; }; -// bool isValid() { return false; } -//}; - -#endif /* POOLVARIABLE_H_ */ diff --git a/datapool/PoolVariableIF.h b/datapool/PoolVariableIF.h index f47173df..cd15f744 100644 --- a/datapool/PoolVariableIF.h +++ b/datapool/PoolVariableIF.h @@ -1,71 +1,99 @@ -/* - * \file PoolVariableIF.h - * - * \brief This file contains the interface definition for pool variables. - * - * \date 10/17/2012 - * - * \author Bastian Baetz - */ - -#ifndef POOLVARIABLEIF_H_ -#define POOLVARIABLEIF_H_ +#ifndef FSFW_DATAPOOL_POOLVARIABLEIF_H_ +#define FSFW_DATAPOOL_POOLVARIABLEIF_H_ #include "../returnvalues/HasReturnvaluesIF.h" #include "../serialize/SerializeIF.h" /** - * \brief This interface is used to control local data pool variable representations. - * - * \details To securely handle data pool variables, all pool entries are locally managed by - * data pool variable access classes, which are called pool variables. To ensure a - * common state of a set of variables needed in a function, these local pool variables - * again are managed by other classes, e.g. the DataSet. This interface provides unified - * access to local pool variables for such manager classes. - * \ingroup data_pool + * @brief This interface is used to control data pool + * variable representations. + * @details + * To securely handle data pool variables, all pool entries are locally + * managed by data pool variable access classes, which are called pool + * variables. To ensure a common state of a set of variables needed in a + * function, these local pool variables again are managed by other classes, + * like the DataSet classes. This interface provides unified access to + * local pool variables for such manager classes. + * @author Bastian Baetz + * @ingroup data_pool */ class PoolVariableIF : public SerializeIF { - friend class DataSet; -protected: - /** - * \brief The commit call shall write back a newly calculated local value to the data pool. - */ - virtual ReturnValue_t commit() = 0; - /** - * \brief The read call shall read the value of this parameter from the data pool and store - * the content locally. - */ - virtual ReturnValue_t read() = 0; + friend class PoolDataSetBase; + friend class GlobDataSet; + friend class LocalPoolDataSetBase; public: - static const uint8_t VALID = 1; - static const uint8_t INVALID = 0; - static const uint32_t NO_PARAMETER = 0; + static constexpr uint8_t INTERFACE_ID = CLASS_ID::POOL_VARIABLE_IF; + static constexpr ReturnValue_t INVALID_READ_WRITE_MODE = MAKE_RETURN_CODE(0xA0); + + static constexpr bool VALID = 1; + static constexpr bool INVALID = 0; + static constexpr uint32_t NO_PARAMETER = 0xffffffff; + enum ReadWriteMode_t { VAR_READ, VAR_WRITE, VAR_READ_WRITE }; /** - * \brief This is an empty virtual destructor, as it is proposed for C++ interfaces. + * @brief This is an empty virtual destructor, + * as it is proposed for C++ interfaces. */ - virtual ~PoolVariableIF() { - } + virtual ~PoolVariableIF() {} /** - * \brief This method returns if the variable is write-only, read-write or read-only. + * @brief This method returns if the variable is write-only, + * read-write or read-only. */ virtual ReadWriteMode_t getReadWriteMode() const = 0; /** - * \brief This operation shall return the data pool id of the variable. + * @brief This operation shall return the data pool id of the variable. */ virtual uint32_t getDataPoolId() const = 0; /** - * \brief With this call, the valid information of the variable is returned. + * @brief With this call, the valid information of the + * variable is returned. */ virtual bool isValid() const = 0; /** - * \brief With this call, the valid information of the variable is set. + * @brief With this call, the valid information of the variable is set. */ - virtual void setValid(uint8_t validity) = 0; + virtual void setValid(bool validity) = 0; + /** + * @brief The commit call shall write back a newly calculated local + * value to the data pool. + * @details + * It is assumed that these calls are implemented in a thread-safe manner! + */ + virtual ReturnValue_t commit(uint32_t lockTimeout) = 0; + /** + * @brief The read call shall read the value of this parameter from + * the data pool and store the content locally. + * @details + * It is assumbed that these calls are implemented in a thread-safe manner! + */ + virtual ReturnValue_t read(uint32_t lockTimeout) = 0; + +protected: + + /** + * @brief Same as commit with the difference that comitting will be + * performed without a lock + * @return + * This can be used if the lock protection is handled externally + * to avoid the overhead of locking and unlocking consecutively. + * Declared protected to avoid free public usage. + */ + virtual ReturnValue_t readWithoutLock() = 0; + /** + * @brief Same as commit with the difference that comitting will be + * performed without a lock + * @return + * This can be used if the lock protection is handled externally + * to avoid the overhead of locking and unlocking consecutively. + * Declared protected to avoid free public usage. + */ + virtual ReturnValue_t commitWithoutLock() = 0; }; -#endif /* POOLVARIABLEIF_H_ */ +using pool_rwm_t = PoolVariableIF::ReadWriteMode_t; + +#endif /* FSFW_DATAPOOL_POOLVARIABLEIF_H_ */ diff --git a/datapool/PoolVector.h b/datapool/PoolVector.h deleted file mode 100644 index d4ca0842..00000000 --- a/datapool/PoolVector.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * \file PoolVector.h - * - * \brief This file contains the PoolVector class, the header only class to handle data pool vectors. - * - * \date 10/23/2012 - * - * \author Bastian Baetz - */ - -#ifndef POOLVECTOR_H_ -#define POOLVECTOR_H_ - -#include "DataSetIF.h" -#include "PoolEntry.h" -#include "PoolVariableIF.h" -#include "../serialize/SerializeAdapter.h" -#include "../serviceinterface/ServiceInterfaceStream.h" - -/** - * \brief This is the access class for array-type data pool entries. - * - * \details To ensure safe usage of the data pool, operation is not done directly on the data pool - * entries, but on local copies. This class provides simple type- and length-safe access - * to vector-style data pool entries (i.e. entries with length > 1). - * The class can be instantiated as read-write and read only. - * It provides a commit-and-roll-back semantic, which means that no array entry in - * the data pool is changed until the commit call is executed. - * There are two template parameters: - * \tparam T This template parameter specifies the data type of an array entry. Currently, all - * plain data types are supported, but in principle any type is possible. - * \tparam vector_size This template parameter specifies the vector size of this entry. - * Using a template parameter for this is not perfect, but avoids dynamic memory allocation. - * \ingroup data_pool - */ -template -class PoolVector: 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 valid information as it was stored in the data pool is copied to this attribute. - */ - uint8_t valid; - /** - * \brief The information whether the class is read-write or read-only is stored here. - */ - ReadWriteMode_t readWriteMode; - -protected: - /** - * \brief This is a call to read the array's values 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 all array values and the valid - * information to its local attributes. In case of a failure (wrong type, size 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() { - PoolEntry* read_out = ::dataPool.getData(this->dataPoolId, - vector_size); - if (read_out != NULL) { - this->valid = read_out->valid; - memcpy(this->value, read_out->address, read_out->getByteSize()); - - return HasReturnvaluesIF::RETURN_OK; - - } else { - memset(this->value, 0, vector_size * sizeof(T)); - sif::error << "PoolVector: read of DP Variable 0x" << std::hex - << dataPoolId << std::dec << " failed." << std::endl; - this->valid = INVALID; - return HasReturnvaluesIF::RETURN_FAILED; - } - } - /** - * \brief The commit call copies the array values back 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() { - PoolEntry* write_back = ::dataPool.getData(this->dataPoolId, - vector_size); - if ((write_back != NULL) && (this->readWriteMode != VAR_READ)) { - write_back->valid = valid; - memcpy(write_back->address, this->value, write_back->getByteSize()); - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } - } -public: - /** - * \brief This is the local copy of the data pool entry. - * \detials The user can work on this attribute - * just like he would on a local array of this type. - */ - T value[vector_size]; - /** - * \brief In the constructor, the variable can register itself in a DataSet (if not NULL is - * passed). - * \details It DOES NOT fetch the current value from the data pool, but sets the value - * attribute to default (0). The value is fetched within the read() operation. - * \param set_id This is the id in the global data pool this instance of the access class - * 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. - */ - PoolVector(uint32_t set_id, DataSetIF* set, - ReadWriteMode_t setReadWriteMode) : - dataPoolId(set_id), valid(false), readWriteMode(setReadWriteMode) { - memset(this->value, 0, vector_size * sizeof(T)); - if (set != NULL) { - set->registerVariable(this); - } - } - /** - * Copy ctor to copy classes containing Pool Variables. - */ -// PoolVector(const PoolVector& rhs) { -// PoolVector temp(rhs.dataPoolId, rhs.) -// memcpy(value, rhs.value, sizeof(T)*vector_size); -// } - /** - * \brief The classes destructor is empty. - * \details If commit() was not called, the local value is - * discarded and not written back to the data pool. - */ - ~PoolVector() { - } - ; - /** - * \brief The operation returns the number of array entries in this variable. - */ - uint8_t getSize() { - return vector_size; - } - /** - * \brief This operation returns the data pool id of the variable. - */ - uint32_t getDataPoolId() const { - return dataPoolId; - } - /** - * This operation sets the data pool id of the variable. - * The method is necessary to set id's of data pool member variables with bad initialization. - */ - void setDataPoolId(uint32_t poolId) { - dataPoolId = poolId; - } - /** - * This method returns if the variable is write-only, read-write or read-only. - */ - ReadWriteMode_t getReadWriteMode() const { - return readWriteMode; - } - ; - /** - * \brief With this call, the valid information of the variable is returned. - */ - bool isValid() const { - if (valid != INVALID) - return true; - else - return false; - } - - void setValid(uint8_t valid) { - this->valid = valid; - } - - uint8_t getValid() { - return valid; - } - - T &operator [](int i) { - return value[i]; - } - - const T &operator [](int i) const { - return value[i]; - } - - PoolVector &operator=( - PoolVector newPoolVector) { - - for (uint16_t i = 0; i < vector_size; i++) { - this->value[i] = newPoolVector.value[i]; - } - return *this; - } - - virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) const { - uint16_t i; - ReturnValue_t result; - for (i = 0; i < vector_size; i++) { - result = SerializeAdapter::serialize(&(value[i]), buffer, size, - maxSize, streamEndianness); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - } - return result; - } - - virtual size_t getSerializedSize() const { - return vector_size * SerializeAdapter::getSerializedSize(value); - } - - virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, - Endianness streamEndianness) { - uint16_t i; - ReturnValue_t result; - for (i = 0; i < vector_size; i++) { - result = SerializeAdapter::deSerialize(&(value[i]), buffer, size, - streamEndianness); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - } - return result; - } -}; - -#endif /* POOLVECTOR_H_ */ diff --git a/datapool/SharedDataSetIF.h b/datapool/SharedDataSetIF.h new file mode 100644 index 00000000..b8d98794 --- /dev/null +++ b/datapool/SharedDataSetIF.h @@ -0,0 +1,14 @@ +#ifndef FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_ +#define FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_ +#include "PoolDataSetIF.h" + +class SharedDataSetIF: public PoolDataSetIF { +public: + virtual ~SharedDataSetIF() {}; + +private: + virtual ReturnValue_t lockDataset(dur_millis_t mutexTimeout) = 0; + virtual ReturnValue_t unlockDataset() = 0; +}; + +#endif /* FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_ */ diff --git a/datapool/ControllerSet.cpp b/datapoolglob/ControllerSet.cpp similarity index 79% rename from datapool/ControllerSet.cpp rename to datapoolglob/ControllerSet.cpp index 6339034c..54fc6e8c 100644 --- a/datapool/ControllerSet.cpp +++ b/datapoolglob/ControllerSet.cpp @@ -1,4 +1,4 @@ -#include "ControllerSet.h" +#include ControllerSet::ControllerSet() { diff --git a/datapoolglob/ControllerSet.h b/datapoolglob/ControllerSet.h new file mode 100644 index 00000000..5da11397 --- /dev/null +++ b/datapoolglob/ControllerSet.h @@ -0,0 +1,15 @@ +#ifndef FSFW_DATAPOOLGLOB_CONTROLLERSET_H_ +#define FSFW_DATAPOOLGLOB_CONTROLLERSET_H_ + +#include "../datapoolglob/GlobalDataSet.h" + +class ControllerSet :public GlobDataSet { +public: + ControllerSet(); + virtual ~ControllerSet(); + + virtual void setToDefault() = 0; + void setInvalid(); +}; + +#endif /* FSFW_DATAPOOLGLOB_CONTROLLERSET_H_ */ diff --git a/datapool/DataPoolAdmin.cpp b/datapoolglob/DataPoolAdmin.cpp similarity index 94% rename from datapool/DataPoolAdmin.cpp rename to datapoolglob/DataPoolAdmin.cpp index f298d530..6ede0841 100644 --- a/datapool/DataPoolAdmin.cpp +++ b/datapoolglob/DataPoolAdmin.cpp @@ -1,7 +1,8 @@ -#include "DataPool.h" #include "DataPoolAdmin.h" -#include "DataSet.h" +#include "GlobalDataSet.h" +#include "GlobalDataPool.h" #include "PoolRawAccess.h" + #include "../ipc/CommandMessage.h" #include "../ipc/QueueFactory.h" #include "../parameters/ParameterMessage.h" @@ -40,9 +41,9 @@ ReturnValue_t DataPoolAdmin::executeAction(ActionId_t actionId, uint8_t valid = data[4]; - uint32_t poolId = ::dataPool.PIDToDataPoolId(address); + uint32_t poolId = glob::dataPool.PIDToDataPoolId(address); - DataSet mySet; + GlobDataSet mySet; PoolRawAccess variable(poolId, 0, &mySet, PoolVariableIF::VAR_READ_WRITE); ReturnValue_t status = mySet.read(); if (status != RETURN_OK) { @@ -92,9 +93,9 @@ void DataPoolAdmin::handleCommand() { ReturnValue_t DataPoolAdmin::handleMemoryLoad(uint32_t address, const uint8_t* data, size_t size, uint8_t** dataPointer) { - uint32_t poolId = ::dataPool.PIDToDataPoolId(address); - uint8_t arrayIndex = ::dataPool.PIDToArrayIndex(address); - DataSet testSet; + uint32_t poolId = glob::dataPool.PIDToDataPoolId(address); + uint8_t arrayIndex = glob::dataPool.PIDToArrayIndex(address); + GlobDataSet testSet; PoolRawAccess varToGetSize(poolId, arrayIndex, &testSet, PoolVariableIF::VAR_READ); ReturnValue_t status = testSet.read(); @@ -113,7 +114,7 @@ ReturnValue_t DataPoolAdmin::handleMemoryLoad(uint32_t address, const uint8_t* readPosition = data; for (; size > 0; size -= typeSize) { - DataSet rawSet; + GlobDataSet rawSet; PoolRawAccess variable(poolId, arrayIndex, &rawSet, PoolVariableIF::VAR_READ_WRITE); status = rawSet.read(); @@ -131,9 +132,9 @@ ReturnValue_t DataPoolAdmin::handleMemoryLoad(uint32_t address, ReturnValue_t DataPoolAdmin::handleMemoryDump(uint32_t address, size_t size, uint8_t** dataPointer, uint8_t* copyHere) { - uint32_t poolId = ::dataPool.PIDToDataPoolId(address); - uint8_t arrayIndex = ::dataPool.PIDToArrayIndex(address); - DataSet testSet; + uint32_t poolId = glob::dataPool.PIDToDataPoolId(address); + uint8_t arrayIndex = glob::dataPool.PIDToArrayIndex(address); + GlobDataSet testSet; PoolRawAccess varToGetSize(poolId, arrayIndex, &testSet, PoolVariableIF::VAR_READ); ReturnValue_t status = testSet.read(); @@ -146,7 +147,7 @@ ReturnValue_t DataPoolAdmin::handleMemoryDump(uint32_t address, size_t size, } uint8_t* ptrToCopy = copyHere; for (; size > 0; size -= typeSize) { - DataSet rawSet; + GlobDataSet rawSet; PoolRawAccess variable(poolId, arrayIndex, &rawSet, PoolVariableIF::VAR_READ); status = rawSet.read(); diff --git a/datapool/DataPoolAdmin.h b/datapoolglob/DataPoolAdmin.h similarity index 92% rename from datapool/DataPoolAdmin.h rename to datapoolglob/DataPoolAdmin.h index e2d1e9e1..d8871b65 100644 --- a/datapool/DataPoolAdmin.h +++ b/datapoolglob/DataPoolAdmin.h @@ -1,15 +1,17 @@ -#ifndef DATAPOOLADMIN_H_ -#define DATAPOOLADMIN_H_ +#ifndef FSFW_DATAPOOLGLOB_DATAPOOLADMIN_H_ +#define FSFW_DATAPOOLGLOB_DATAPOOLADMIN_H_ + +#include "DataPoolParameterWrapper.h" -#include "../memory/MemoryHelper.h" -#include "../action/HasActionsIF.h" -#include "../action/SimpleActionHelper.h" #include "../objectmanager/SystemObject.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../tasks/ExecutableObjectIF.h" -#include "../parameters/ReceivesParameterMessagesIF.h" -#include "DataPoolParameterWrapper.h" +#include "../action/HasActionsIF.h" #include "../ipc/MessageQueueIF.h" +#include "../parameters/ReceivesParameterMessagesIF.h" +#include "../action/SimpleActionHelper.h" +#include "../memory/MemoryHelper.h" + class DataPoolAdmin: public HasActionsIF, public ExecutableObjectIF, @@ -55,4 +57,4 @@ private: Command_t initialCommand); }; -#endif /* DATAPOOLADMIN_H_ */ +#endif /* FSFW_DATAPOOLGLOB_DATAPOOLADMIN_H_ */ diff --git a/datapool/DataPoolParameterWrapper.cpp b/datapoolglob/DataPoolParameterWrapper.cpp similarity index 95% rename from datapool/DataPoolParameterWrapper.cpp rename to datapoolglob/DataPoolParameterWrapper.cpp index 79367405..3a57d90d 100644 --- a/datapool/DataPoolParameterWrapper.cpp +++ b/datapoolglob/DataPoolParameterWrapper.cpp @@ -1,10 +1,8 @@ -#include "DataPoolParameterWrapper.h" - -//for returncodes +#include "../datapoolglob/GlobalDataSet.h" +#include "../datapoolglob/DataPoolParameterWrapper.h" +#include "../datapoolglob/PoolRawAccess.h" #include "../parameters/HasParametersIF.h" -#include "DataSet.h" -#include "PoolRawAccess.h" DataPoolParameterWrapper::DataPoolParameterWrapper() : type(Type::UNKNOWN_TYPE), rows(0), columns(0), poolId( @@ -20,7 +18,7 @@ ReturnValue_t DataPoolParameterWrapper::set(uint8_t domainId, uint16_t parameterId) { poolId = (domainId << 16) + parameterId; - DataSet mySet; + GlobDataSet mySet; PoolRawAccess raw(poolId, 0, &mySet, PoolVariableIF::VAR_READ); ReturnValue_t status = mySet.read(); if (status != HasReturnvaluesIF::RETURN_OK) { @@ -57,7 +55,7 @@ ReturnValue_t DataPoolParameterWrapper::serialize(uint8_t** buffer, } for (uint8_t index = 0; index < rows; index++){ - DataSet mySet; + GlobDataSet mySet; PoolRawAccess raw(poolId, index, &mySet,PoolVariableIF::VAR_READ); mySet.read(); result = raw.serialize(buffer,size,maxSize,streamEndianness); @@ -94,7 +92,7 @@ ReturnValue_t DataPoolParameterWrapper::deSerializeData(uint8_t startingRow, for (uint8_t fromRow = 0; fromRow < fromRows; fromRow++) { - DataSet mySet; + GlobDataSet mySet; PoolRawAccess raw(poolId, startingRow + fromRow, &mySet, PoolVariableIF::VAR_READ_WRITE); mySet.read(); diff --git a/datapool/DataPoolParameterWrapper.h b/datapoolglob/DataPoolParameterWrapper.h similarity index 100% rename from datapool/DataPoolParameterWrapper.h rename to datapoolglob/DataPoolParameterWrapper.h diff --git a/datapoolglob/GlobalDataPool.cpp b/datapoolglob/GlobalDataPool.cpp new file mode 100644 index 00000000..afb27b77 --- /dev/null +++ b/datapoolglob/GlobalDataPool.cpp @@ -0,0 +1,133 @@ +#include "../datapoolglob/GlobalDataPool.h" +#include "../serviceinterface/ServiceInterfaceStream.h" +#include "../ipc/MutexFactory.h" + +GlobalDataPool::GlobalDataPool( + void(*initFunction)(GlobPoolMap* pool_map)) { + mutex = MutexFactory::instance()->createMutex(); + if (initFunction != NULL ) { + initFunction( &this->globDataPool ); + } +} + +GlobalDataPool::~GlobalDataPool() { + MutexFactory::instance()->deleteMutex(mutex); + for(GlobPoolMapIter it = this->globDataPool.begin(); + it != this->globDataPool.end(); ++it ) + { + delete it->second; + } +} + +// The function checks PID, type and array length before returning a copy of +// the PoolEntry. In failure case, it returns a temp-Entry with size 0 and NULL-ptr. +template PoolEntry* GlobalDataPool::getData( uint32_t data_pool_id, + uint8_t sizeOrPosition ) { + GlobPoolMapIter it = this->globDataPool.find( data_pool_id ); + if ( it != this->globDataPool.end() ) { + PoolEntry* entry = dynamic_cast< PoolEntry* >( it->second ); + if (entry != nullptr ) { + if ( sizeOrPosition <= entry->length ) { + return entry; + } + } + } + return nullptr; +} + +PoolEntryIF* GlobalDataPool::getRawData( uint32_t data_pool_id ) { + GlobPoolMapIter it = this->globDataPool.find( data_pool_id ); + if ( it != this->globDataPool.end() ) { + return it->second; + } else { + return nullptr; + } +} + +ReturnValue_t GlobalDataPool::unlockDataPool() { + ReturnValue_t status = mutex->unlockMutex(); + if(status != RETURN_OK) { + sif::error << "DataPool::DataPool: unlock of mutex failed with" + " error code: " << status << std::endl; + } + return status; +} + +ReturnValue_t GlobalDataPool::lockDataPool(uint32_t timeoutMs) { + ReturnValue_t status = mutex->lockMutex(MutexIF::TimeoutType::WAITING, + timeoutMs); + if(status != RETURN_OK) { + sif::error << "DataPool::DataPool: lock of mutex failed " + "with error code: " << status << std::endl; + } + return status; +} + +void GlobalDataPool::print() { + sif::debug << "DataPool contains: " << std::endl; + std::map::iterator dataPoolIt; + dataPoolIt = this->globDataPool.begin(); + while( dataPoolIt != this->globDataPool.end() ) { + sif::debug << std::hex << dataPoolIt->first << std::dec << " |"; + dataPoolIt->second->print(); + dataPoolIt++; + } +} + +uint32_t GlobalDataPool::PIDToDataPoolId(uint32_t parameter_id) { + return (parameter_id >> 8) & 0x00FFFFFF; +} + +uint8_t GlobalDataPool::PIDToArrayIndex(uint32_t parameter_id) { + return (parameter_id & 0x000000FF); +} + +uint32_t GlobalDataPool::poolIdAndPositionToPid(uint32_t poolId, uint8_t index) { + return (poolId << 8) + index; +} + + +//SHOULDDO: Do we need a mutex lock here... I don't think so, +//as we only check static const values of elements in a list that do not change. +//there is no guarantee in the standard, but it seems to me that the implementation is safe -UM +ReturnValue_t GlobalDataPool::getType(uint32_t parameter_id, Type* type) { + GlobPoolMapIter it = this->globDataPool.find( PIDToDataPoolId(parameter_id)); + if ( it != this->globDataPool.end() ) { + *type = it->second->getType(); + return RETURN_OK; + } else { + *type = Type::UNKNOWN_TYPE; + return RETURN_FAILED; + } +} + +bool GlobalDataPool::exists(uint32_t parameterId) { + uint32_t poolId = PIDToDataPoolId(parameterId); + uint32_t index = PIDToArrayIndex(parameterId); + GlobPoolMapIter it = this->globDataPool.find( poolId ); + if (it != globDataPool.end()) { + if (it->second->getSize() >= index) { + return true; + } + } + return false; +} + +template PoolEntry* GlobalDataPool::getData( + uint32_t data_pool_id, uint8_t size ); +template PoolEntry* GlobalDataPool::getData( + uint32_t data_pool_id, uint8_t size ); +template PoolEntry* GlobalDataPool::getData( + uint32_t data_pool_id, uint8_t size ); +template PoolEntry* GlobalDataPool::getData( + uint32_t data_pool_id, uint8_t size); +template PoolEntry* GlobalDataPool::getData( + uint32_t data_pool_id, uint8_t size ); +template PoolEntry* GlobalDataPool::getData( + uint32_t data_pool_id, uint8_t size ); +template PoolEntry* GlobalDataPool::getData( + uint32_t data_pool_id, uint8_t size ); +template PoolEntry* GlobalDataPool::getData( + uint32_t data_pool_id, uint8_t size ); +template PoolEntry* GlobalDataPool::getData( + uint32_t data_pool_id, uint8_t size); diff --git a/datapoolglob/GlobalDataPool.h b/datapoolglob/GlobalDataPool.h new file mode 100644 index 00000000..ce5b132c --- /dev/null +++ b/datapoolglob/GlobalDataPool.h @@ -0,0 +1,149 @@ +#ifndef GLOBALDATAPOOL_H_ +#define GLOBALDATAPOOL_H_ + +#include "../datapool/PoolEntry.h" +#include "../globalfunctions/Type.h" +#include "../ipc/MutexIF.h" +#include + +/** + * @defgroup data_pool Global data pool + * This is the group, where all classes associated with global + * data pool handling belong to. + * This includes classes to access Data Pool variables. + */ + +/** + * Typedefs for the global pool representations + */ +using GlobPoolMap = std::map; +using GlobPoolMapIter = GlobPoolMap::iterator; + +/** + * @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. 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, + * which means read data is valid (if flagged so), + * but not necessarily up-to-date. + * + * Variables are either single values or arrays. + * @author Bastian Baetz + * @ingroup data_pool + */ +class GlobalDataPool : public HasReturnvaluesIF { +private: + /** + * @brief This is the actual data pool itself. + * @details It is represented by a map with the data pool id as index + * and a pointer to a single PoolEntry as value. + */ + GlobPoolMap globDataPool; + + /** + * @brief The mutex is created in the constructor and makes + * access mutual exclusive. + * @details Locking and unlocking the pool is only done by the DataSet class. + */ + MutexIF* mutex; +public: + /** + * @brief In the classes constructor, + * the passed initialization function is called. + * @details + * To enable filling the pool, a pointer to the map is passed, + * allowing direct access to the pool's content. + * On runtime, adding or removing variables is forbidden. + */ + GlobalDataPool( void ( *initFunction )( GlobPoolMap* pool_map ) ); + + /** + * @brief The destructor iterates through the data_pool map and + * calls all entries destructors to clean up the heap. + */ + ~GlobalDataPool(); + + /** + * @brief This is the default call to access the pool. + * @details + * A pointer to the PoolEntry object is returned. + * The call checks data pool id, type and array size. + * Returns NULL in case of failure. + * @param data_pool_id The data pool id to search. + * @param sizeOrPosition The array size (not byte size!) of the pool entry, + * or the position the user wants to read. + * If smaller than the entry size, everything's ok. + */ + template PoolEntry* getData( uint32_t data_pool_id, + uint8_t sizeOrPosition ); + + /** + * @brief An alternative call to get a data pool entry in case the type is not implicitly known + * (i.e. in Housekeeping Telemetry). + * @details It returns a basic interface and does NOT perform + * a size check. The caller has to assure he does not copy too much data. + * Returns NULL in case the entry is not found. + * @param data_pool_id The data pool id to search. + */ + PoolEntryIF* getRawData( uint32_t data_pool_id ); + /** + * @brief This is a small helper function to facilitate locking the global data pool. + * @details It fetches the pool's mutex id and tries to acquire the mutex. + */ + ReturnValue_t lockDataPool(uint32_t timeoutMs = MutexIF::BLOCKING); + /** + * @brief This is a small helper function to facilitate unlocking the global data pool. + * @details It fetches the pool's mutex id and tries to free the mutex. + */ + ReturnValue_t unlockDataPool(); + /** + * @brief The print call is a simple debug method. + * @details It prints the current content of the data pool. + * It iterates through the data_pool map and calls each entry's print() method. + */ + void print(); + /** + * Extracts the data pool id from a SCOS 2000 PID. + * @param parameter_id The passed Parameter ID. + * @return The data pool id as used within the OBSW. + */ + static uint32_t PIDToDataPoolId( uint32_t parameter_id ); + /** + * Extracts an array index out of a SCOS 2000 PID. + * @param parameter_id The passed Parameter ID. + * @return The index of the corresponding data pool entry. + */ + static uint8_t PIDToArrayIndex( uint32_t parameter_id ); + /** + * Retransforms a data pool id and an array index to a SCOS 2000 PID. + */ + static uint32_t poolIdAndPositionToPid( uint32_t poolId, uint8_t index ); + + /** + * Method to return the type of a pool variable. + * @param parameter_id A parameterID (not pool id) of a DP member. + * @param type Returns the type or TYPE::UNKNOWN_TYPE + * @return RETURN_OK if parameter exists, RETURN_FAILED else. + */ + ReturnValue_t getType( uint32_t parameter_id, Type* type ); + + /** + * Method to check if a PID exists. Does not lock, as there's no + * possibility to alter the list that is checked during run-time. + * @param parameterId The PID (not pool id!) of a parameter. + * @return true if exists, false else. + */ + bool exists(uint32_t parameterId); +}; + +//We assume someone globally instantiates a DataPool. +namespace glob { +extern GlobalDataPool dataPool; +} + +#endif /* DATAPOOL_H_ */ diff --git a/datapoolglob/GlobalDataSet.cpp b/datapoolglob/GlobalDataSet.cpp new file mode 100644 index 00000000..1e99f805 --- /dev/null +++ b/datapoolglob/GlobalDataSet.cpp @@ -0,0 +1,48 @@ +#include "../datapoolglob/GlobalDataPool.h" +#include "../datapoolglob/GlobalDataSet.h" +#include "../serviceinterface/ServiceInterfaceStream.h" + +GlobDataSet::GlobDataSet(): PoolDataSetBase( + reinterpret_cast(®isteredVariables), + DATA_SET_MAX_SIZE) {} + +// Don't do anything with your variables, they are dead already! +// (Destructor is already called) +GlobDataSet::~GlobDataSet() {} + +ReturnValue_t GlobDataSet::commit(bool valid, uint32_t lockTimeout) { + setEntriesValid(valid); + setSetValid(valid); + return commit(lockTimeout); +} + +ReturnValue_t GlobDataSet::commit(uint32_t lockTimeout) { + return PoolDataSetBase::commit(lockTimeout); +} + +bool GlobDataSet::isValid() const { + return this->valid; +} + +ReturnValue_t GlobDataSet::unlockDataPool() { + return glob::dataPool.unlockDataPool(); +} + +ReturnValue_t GlobDataSet::lockDataPool(uint32_t timeoutMs) { + return glob::dataPool.lockDataPool(timeoutMs); +} + +void GlobDataSet::setEntriesValid(bool valid) { + for (uint16_t count = 0; count < fillCount; count++) { + if (registeredVariables[count]->getReadWriteMode() + != PoolVariableIF::VAR_READ) { + registeredVariables[count]->setValid(valid); + } + } +} + +void GlobDataSet::setSetValid(bool valid) { + this->valid = valid; +} + + diff --git a/datapoolglob/GlobalDataSet.h b/datapoolglob/GlobalDataSet.h new file mode 100644 index 00000000..2f0edbd5 --- /dev/null +++ b/datapoolglob/GlobalDataSet.h @@ -0,0 +1,98 @@ +#ifndef FRAMEWORK_DATAPOOLGLOB_DATASET_H_ +#define FRAMEWORK_DATAPOOLGLOB_DATASET_H_ + +#include "../datapool/PoolDataSetBase.h" + +/** + * @brief The DataSet class manages a set of locally checked out variables + * for the global data pool. + * @details + * This class uses the read-commit() semantic provided by the DataSetBase class. + * It extends the base class by using the global data pool, + * having a valid state and implementing lock und unlock calls for the global + * datapool. + * + * For more information on how this class works, see the DataSetBase + * documentation. + * @author Bastian Baetz + * @ingroup data_pool + */ +class GlobDataSet: public PoolDataSetBase { +public: + + /** + * @brief Creates an empty GlobDataSet. Use registerVariable or + * supply a pointer to this dataset to PoolVariable + * initializations to register pool variables. + */ + GlobDataSet(); + + /** + * @brief The destructor automatically manages writing the valid + * information of variables. + * @details + * In case the data set was read out, but not committed(indicated by state), + * the destructor parses all variables that are still registered to the set. + * For each, the valid flag in the data pool is set to "invalid". + */ + ~GlobDataSet(); + + /** + * Variant of method above which sets validity of all elements of the set. + * @param valid Validity information from PoolVariableIF. + * @return - @c RETURN_OK if all variables were read successfully. + * - @c COMMITING_WITHOUT_READING if set was not read yet and + * contains non write-only variables + */ + ReturnValue_t commit(bool valid, uint32_t lockTimeout = MutexIF::BLOCKING); + ReturnValue_t commit(uint32_t lockTimeout = MutexIF::BLOCKING) override; + + /** + * Set all entries + * @param valid + */ + void setSetValid(bool valid); + + bool isValid() const override; + + /** + * Set the valid information of all variables contained in the set which + * are not read-only + * + * @param valid Validity information from PoolVariableIF. + */ + void setEntriesValid(bool valid); + + //!< This definition sets the maximum number of variables to + //! register in one DataSet. + static const uint8_t DATA_SET_MAX_SIZE = 63; + +private: + /** + * If the valid state of a dataset is always relevant to the whole + * data set we can use this flag. + */ + bool valid = false; + + /** + * @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. + */ + ReturnValue_t lockDataPool(uint32_t timeoutMs) override; + /** + * @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. + */ + ReturnValue_t unlockDataPool() override; + + void handleAlreadyReadDatasetCommit(); + ReturnValue_t handleUnreadDatasetCommit(); + + PoolVariableIF* registeredVariables[DATA_SET_MAX_SIZE]; +}; + +#endif /* FRAMEWORK_DATAPOOLGLOB_DATASET_H_ */ diff --git a/datapoolglob/GlobalPoolVariable.h b/datapoolglob/GlobalPoolVariable.h new file mode 100644 index 00000000..a995bfaf --- /dev/null +++ b/datapoolglob/GlobalPoolVariable.h @@ -0,0 +1,213 @@ +#ifndef GLOBALPOOLVARIABLE_H_ +#define GLOBALPOOLVARIABLE_H_ + +#include "../datapool/DataSetIF.h" +#include "../datapoolglob/GlobalDataPool.h" +#include "../datapool/PoolVariableIF.h" +#include "../datapool/PoolEntry.h" +#include "../serialize/SerializeAdapter.h" +#include "../serviceinterface/ServiceInterfaceStream.h" + +template class PoolVarList; + + +/** + * @brief This is the access class for non-array data pool entries. + * + * @details + * To ensure safe usage of the data pool, operation is not done directly + * on the data pool entries, but on local copies. This class provides simple + * type-safe access to single data pool entries (i.e. entries with length = 1). + * The class can be instantiated as read-write and read only. + * It provides a commit-and-roll-back semantic, which means that the + * variable's value in the data pool is not changed until the + * commit call is executed. + * @tparam T The template parameter sets the type of the variable. + * Currently, all plain data types are supported, but in principle + * any type is possible. + * @ingroup data_pool + */ +template +class GlobPoolVar: public PoolVariableIF { + template friend class PoolVarList; + static_assert(not std::is_same::value, + "Do not use boolean for the PoolEntry type, use uint8_t instead!" + "There is no boolean type in CCSDS."); +public: + /** + * @brief In the constructor, the variable can register itself in a + * DataSet (if nullptr is not passed). + * @details + * It DOES NOT fetch the current value from the data pool, but + * sets the value attribute to default (0). + * The value is fetched within the read() operation. + * @param set_id This is the id in the global data pool + * this instance of the access class 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. + */ + GlobPoolVar(uint32_t set_id, DataSetIF* dataSet, + ReadWriteMode_t setReadWriteMode); + + /** + * @brief This is the local copy of the data pool entry. + * @details The user can work on this attribute + * just like he would on a simple local variable. + */ + T value = 0; + + /** + * @brief Copy ctor to copy classes containing Pool Variables. + * (Robin): This only copies member variables, which is done + * by the default copy ctor. maybe we can ommit this ctor? + */ + GlobPoolVar(const GlobPoolVar& rhs); + + /** + * @brief The classes destructor is empty. + * @details If commit() was not called, the local value is + * discarded and not written back to the data pool. + */ + ~GlobPoolVar() {} + + /** + * @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 read call is protected with a lock. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + */ + ReturnValue_t read(uint32_t lockTimeout) override; + /** + * @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. + * The commit call is protected with a lock. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + */ + ReturnValue_t commit(uint32_t lockTimeout) override; + +protected: + /** + * @brief Like #read, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t readWithoutLock() override; + /** + * @brief Like #commit, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t commitWithoutLock() override; + /** + * @brief To access the correct data pool entry on read and commit calls, + * the data pool is stored. + */ + uint32_t dataPoolId; + + /** + * @brief The valid information as it was stored in the data pool is + * copied to this attribute. + */ + uint8_t valid; + + /** + * @brief The information whether the class is read-write or read-only + * is stored here. + */ + pool_rwm_t readWriteMode; + + /** + * Empty ctor for List initialization + */ + GlobPoolVar(); +public: + /** + * \brief This operation returns the data pool id of the variable. + */ + uint32_t getDataPoolId() const override; + + /** + * This method returns if the variable is write-only, read-write or read-only. + */ + ReadWriteMode_t getReadWriteMode() const override; + /** + * This operation sets the data pool id of the variable. + * The method is necessary to set id's of data pool member variables with bad initialization. + */ + void setDataPoolId(uint32_t poolId); + + /** + * \brief With this call, the valid information of the variable is returned. + */ + bool isValid() const override; + + uint8_t getValid(); + + void setValid(bool valid) override; + + operator T() { + return value; + } + + operator T() const { + return value; + } + + GlobPoolVar &operator=(T newValue) { + value = newValue; + return *this; + } + + GlobPoolVar &operator=(GlobPoolVar newPoolVariable) { + value = newPoolVariable.value; + return *this; + } + + virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, + const size_t max_size, + SerializeIF::Endianness streamEndianness) const override { + return SerializeAdapter::serialize(&value, buffer, size, max_size, + streamEndianness); + } + + virtual size_t getSerializedSize() const { + return SerializeAdapter::getSerializedSize(&value); + } + + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) { + return SerializeAdapter::deSerialize(&value, buffer, size, + streamEndianness); + } +}; + +#include "../datapoolglob/GlobalPoolVariable.tpp" + +typedef GlobPoolVar gp_bool_t; +typedef GlobPoolVar gp_uint8_t; +typedef GlobPoolVar gp_uint16_t; +typedef GlobPoolVar gp_uint32_t; +typedef GlobPoolVar gp_int8_t; +typedef GlobPoolVar gp_int16_t; +typedef GlobPoolVar gp_int32_t; +typedef GlobPoolVar gp_float_t; +typedef GlobPoolVar gp_double_t; + +#endif /* POOLVARIABLE_H_ */ diff --git a/datapoolglob/GlobalPoolVariable.tpp b/datapoolglob/GlobalPoolVariable.tpp new file mode 100644 index 00000000..d61d605d --- /dev/null +++ b/datapoolglob/GlobalPoolVariable.tpp @@ -0,0 +1,117 @@ +#ifndef GLOBALPOOLVARIABLE_TPP_ +#define GLOBALPOOLVARIABLE_TPP_ + +template +inline GlobPoolVar::GlobPoolVar(uint32_t set_id, + DataSetIF* dataSet, ReadWriteMode_t setReadWriteMode): + dataPoolId(set_id), valid(PoolVariableIF::INVALID), + readWriteMode(setReadWriteMode) +{ + if (dataSet != nullptr) { + dataSet->registerVariable(this); + } +} + +template +inline ReturnValue_t GlobPoolVar::read(uint32_t lockTimeout) { + ReturnValue_t result = glob::dataPool.lockDataPool(lockTimeout); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = readWithoutLock(); + ReturnValue_t unlockResult = glob::dataPool.unlockDataPool(); + if(unlockResult != HasReturnvaluesIF::RETURN_OK) { + sif::error << "GlobPoolVar::read: Could not unlock global data pool" + << std::endl; + } + return result; +} + +template +inline ReturnValue_t GlobPoolVar::commit(uint32_t lockTimeout) { + ReturnValue_t result = glob::dataPool.lockDataPool(lockTimeout); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = commitWithoutLock(); + ReturnValue_t unlockResult = glob::dataPool.unlockDataPool(); + if(unlockResult != HasReturnvaluesIF::RETURN_OK) { + sif::error << "GlobPoolVar::read: Could not unlock global data pool" + << std::endl; + } + return result; +} + +template +inline ReturnValue_t GlobPoolVar::readWithoutLock() { + PoolEntry* read_out = glob::dataPool.getData(dataPoolId, 1); + if (read_out != NULL) { + valid = read_out->valid; + value = *(read_out->address); + return HasReturnvaluesIF::RETURN_OK; + } else { + value = 0; + valid = false; + sif::error << "PoolVariable: read of DP Variable 0x" << std::hex + << dataPoolId << std::dec << " failed." << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } +} + +template +inline ReturnValue_t GlobPoolVar::commitWithoutLock() { + PoolEntry* write_back = glob::dataPool.getData(dataPoolId, 1); + if ((write_back != NULL) && (readWriteMode != VAR_READ)) { + write_back->valid = valid; + *(write_back->address) = value; + return HasReturnvaluesIF::RETURN_OK; + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } +} + +template +inline GlobPoolVar::GlobPoolVar(): + dataPoolId(PoolVariableIF::NO_PARAMETER), + valid(PoolVariableIF::INVALID), + readWriteMode(VAR_READ), value(0) {} + +template +inline GlobPoolVar::GlobPoolVar(const GlobPoolVar& rhs) : + dataPoolId(rhs.dataPoolId), valid(rhs.valid), readWriteMode( + rhs.readWriteMode), value(rhs.value) {} + +template +inline pool_rwm_t GlobPoolVar::getReadWriteMode() const { + return readWriteMode; +} + +template +inline uint32_t GlobPoolVar::getDataPoolId() const { + return dataPoolId; +} + +template +inline void GlobPoolVar::setDataPoolId(uint32_t poolId) { + dataPoolId = poolId; +} + +template +inline bool GlobPoolVar::isValid() const { + if (valid) + return true; + else + return false; +} + +template +inline uint8_t GlobPoolVar::getValid() { + return valid; +} + +template +inline void GlobPoolVar::setValid(bool valid) { + this->valid = valid; +} + +#endif diff --git a/datapoolglob/GlobalPoolVector.h b/datapoolglob/GlobalPoolVector.h new file mode 100644 index 00000000..0f5daacd --- /dev/null +++ b/datapoolglob/GlobalPoolVector.h @@ -0,0 +1,185 @@ +#ifndef FSFW_DATAPOOLGLOB_GLOBALPOOLVECTOR_H_ +#define FSFW_DATAPOOLGLOB_GLOBALPOOLVECTOR_H_ + +#include "../datapool/DataSetIF.h" +#include "../datapool/PoolEntry.h" +#include "../datapool/PoolVariableIF.h" +#include "../serialize/SerializeAdapter.h" +#include "../serviceinterface/ServiceInterfaceStream.h" + +/** + * @brief This is the access class for array-type data pool entries. + * + * @details + * To ensure safe usage of the data pool, operation is not done directly on the + * data pool entries, but on local copies. This class provides simple type- + * and length-safe access to vector-style data pool entries (i.e. entries with + * length > 1). The class can be instantiated as read-write and read only. + * + * It provides a commit-and-roll-back semantic, which means that no array + * entry in the data pool is changed until the commit call is executed. + * There are two template parameters: + * @tparam T + * This template parameter specifies the data type of an array entry. Currently, + * all plain data types are supported, but in principle any type is possible. + * @tparam vector_size + * This template parameter specifies the vector size of this entry. Using a + * template parameter for this is not perfect, but avoids + * dynamic memory allocation. + * @ingroup data_pool + */ +template +class GlobPoolVector: public PoolVariableIF { +public: + /** + * @brief In the constructor, the variable can register itself in a + * DataSet (if no nullptr is passed). + * @details + * It DOES NOT fetch the current value from the data pool, but sets the + * value attribute to default (0). The value is fetched within the + * read() operation. + * @param set_id + * This is the id in the global data pool this instance of the access + * class corresponds to. + * @param dataSet + * The data set in which the variable shall register itself. If nullptr, + * 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. + */ + GlobPoolVector(uint32_t set_id, DataSetIF* set, + ReadWriteMode_t setReadWriteMode); + + /** + * @brief This is the local copy of the data pool entry. + * @details The user can work on this attribute + * just like he would on a local array of this type. + */ + T value[vectorSize]; + /** + * @brief The classes destructor is empty. + * @details If commit() was not called, the local value is + * discarded and not written back to the data pool. + */ + ~GlobPoolVector() {}; + /** + * @brief The operation returns the number of array entries + * in this variable. + */ + uint8_t getSize() { + return vectorSize; + } + /** + * @brief This operation returns the data pool id of the variable. + */ + uint32_t getDataPoolId() const { + return dataPoolId; + } + /** + * @brief This operation sets the data pool id of the variable. + * @details + * The method is necessary to set id's of data pool member variables + * with bad initialization. + */ + void setDataPoolId(uint32_t poolId) { + dataPoolId = poolId; + } + /** + * This method returns if the variable is write-only, read-write or read-only. + */ + ReadWriteMode_t getReadWriteMode() const { + return readWriteMode; + } + + + /** + * @brief With this call, the valid information of the variable is returned. + */ + bool isValid() const { + if (valid != INVALID) + return true; + else + return false; + } + void setValid(bool valid) {this->valid = valid;} + uint8_t getValid() {return valid;} + + T &operator [](int i) {return value[i];} + const T &operator [](int i) const {return value[i];} + + virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, + size_t max_size, Endianness streamEndianness) const override; + virtual size_t getSerializedSize() const override; + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + Endianness streamEndianness) override; + + /** + * @brief This is a call to read the array's values + * 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 all array values + * and the valid information to its local attributes. + * In case of a failure (wrong type, size or pool id not found), the + * variable is set to zero and invalid. + * The read call is protected by a lock of the global data pool. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + */ + ReturnValue_t read(uint32_t lockTimeout = MutexIF::BLOCKING) override; + /** + * @brief The commit call copies the array values back 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 commit call is protected by a lock of the global data pool. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + */ + ReturnValue_t commit(uint32_t lockTimeout = MutexIF::BLOCKING) override; + +protected: + /** + * @brief Like #read, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t readWithoutLock() override; + /** + * @brief Like #commit, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t commitWithoutLock() override; + +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 valid information as it was stored in the data pool + * is copied to this attribute. + */ + uint8_t valid; + /** + * @brief The information whether the class is read-write or + * read-only is stored here. + */ + ReadWriteMode_t readWriteMode; +}; + +#include "../datapoolglob/GlobalPoolVector.tpp" + +template +using gp_vec_t = GlobPoolVector; + +#endif /* FSFW_DATAPOOLGLOB_GLOBALPOOLVECTOR_H_ */ diff --git a/datapoolglob/GlobalPoolVector.tpp b/datapoolglob/GlobalPoolVector.tpp new file mode 100644 index 00000000..013a682a --- /dev/null +++ b/datapoolglob/GlobalPoolVector.tpp @@ -0,0 +1,117 @@ +#ifndef FSFW_DATAPOOLGLOB_GLOBALPOOLVECTOR_TPP_ +#define FSFW_DATAPOOLGLOB_GLOBALPOOLVECTOR_TPP_ + + +template +inline GlobPoolVector::GlobPoolVector(uint32_t set_id, + DataSetIF* set, ReadWriteMode_t setReadWriteMode) : + dataPoolId(set_id), valid(false), readWriteMode(setReadWriteMode) { + memset(this->value, 0, vectorSize * sizeof(T)); + if (set != nullptr) { + set->registerVariable(this); + } +} + + +template +inline ReturnValue_t GlobPoolVector::read(uint32_t lockTimeout) { + ReturnValue_t result = glob::dataPool.lockDataPool(lockTimeout); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = readWithoutLock(); + ReturnValue_t unlockResult = glob::dataPool.unlockDataPool(); + if(unlockResult != HasReturnvaluesIF::RETURN_OK) { + sif::error << "GlobPoolVar::read: Could not unlock global data pool" + << std::endl; + } + return result; +} + +template +inline ReturnValue_t GlobPoolVector::commit( + uint32_t lockTimeout) { + ReturnValue_t result = glob::dataPool.lockDataPool(lockTimeout); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = commitWithoutLock(); + ReturnValue_t unlockResult = glob::dataPool.unlockDataPool(); + if(unlockResult != HasReturnvaluesIF::RETURN_OK) { + sif::error << "GlobPoolVar::read: Could not unlock global data pool" + << std::endl; + } + return result; +} + +template +inline ReturnValue_t GlobPoolVector::readWithoutLock() { + PoolEntry* read_out = glob::dataPool.getData(this->dataPoolId, + vectorSize); + if (read_out != nullptr) { + this->valid = read_out->valid; + memcpy(this->value, read_out->address, read_out->getByteSize()); + + return HasReturnvaluesIF::RETURN_OK; + + } else { + memset(this->value, 0, vectorSize * sizeof(T)); + sif::error << "PoolVector: Read of DP Variable 0x" << std::hex + << std::setw(8) << std::setfill('0') << dataPoolId << + std::dec << " failed." << std::endl; + this->valid = INVALID; + return HasReturnvaluesIF::RETURN_FAILED; + } +} + +template +inline ReturnValue_t GlobPoolVector::commitWithoutLock() { + PoolEntry* writeBack = glob::dataPool.getData(this->dataPoolId, + vectorSize); + if ((writeBack != nullptr) && (this->readWriteMode != VAR_READ)) { + writeBack->valid = valid; + memcpy(writeBack->address, this->value, writeBack->getByteSize()); + return HasReturnvaluesIF::RETURN_OK; + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } +} + +template +inline ReturnValue_t GlobPoolVector::serialize(uint8_t** buffer, + size_t* size, size_t max_size, + SerializeIF::Endianness streamEndianness) const { + uint16_t i; + ReturnValue_t result; + for (i = 0; i < vectorSize; i++) { + result = SerializeAdapter::serialize(&(value[i]), buffer, size, + max_size, streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return result; +} + +template +inline size_t GlobPoolVector::getSerializedSize() const { + return vectorSize * SerializeAdapter::getSerializedSize(value); +} + +template +inline ReturnValue_t GlobPoolVector::deSerialize( + const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) { + uint16_t i; + ReturnValue_t result; + for (i = 0; i < vectorSize; i++) { + result = SerializeAdapter::deSerialize(&(value[i]), buffer, size, + streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return result; +} + +#endif diff --git a/datapool/PIDReader.h b/datapoolglob/PIDReader.h similarity index 77% rename from datapool/PIDReader.h rename to datapoolglob/PIDReader.h index 3b38b51d..9431e1d4 100644 --- a/datapool/PIDReader.h +++ b/datapoolglob/PIDReader.h @@ -1,9 +1,9 @@ #ifndef PIDREADER_H_ #define PIDREADER_H_ -#include "DataPool.h" -#include "DataSetIF.h" -#include "PoolEntry.h" -#include "PoolVariableIF.h" +#include "../datapool/DataSetIF.h" +#include "../datapoolglob/GlobalDataPool.h" +#include "../datapool/PoolEntry.h" +#include "../datapool/PoolVariableIF.h" #include "../serialize/SerializeAdapter.h" #include "../serviceinterface/ServiceInterfaceStream.h" @@ -15,10 +15,10 @@ class PIDReader: public PoolVariableIF { protected: uint32_t parameterId; uint8_t valid; - ReturnValue_t read() { - uint8_t arrayIndex = DataPool::PIDToArrayIndex(parameterId); - PoolEntry *read_out = ::dataPool.getData( - DataPool::PIDToDataPoolId(parameterId), arrayIndex); + ReturnValue_t readWithoutLock() { + uint8_t arrayIndex = GlobalDataPool::PIDToArrayIndex(parameterId); + PoolEntry *read_out = glob::dataPool.getData( + GlobalDataPool::PIDToDataPoolId(parameterId), arrayIndex); if (read_out != NULL) { valid = read_out->valid; value = read_out->address[arrayIndex]; @@ -36,9 +36,13 @@ protected: * Reason is the possibility to access a single DP vector element, but if we commit, * we set validity of the whole vector. */ - ReturnValue_t commit() { + ReturnValue_t commit(uint32_t lockTimeout) override { return HasReturnvaluesIF::RETURN_FAILED; } + ReturnValue_t commitWithoutLock() override { + return HasReturnvaluesIF::RETURN_FAILED; + } + /** * Empty ctor for List initialization */ @@ -72,6 +76,19 @@ public: } } + ReturnValue_t read(uint32_t lockTimeout) override { + ReturnValue_t result = glob::dataPool.lockDataPool(); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = readWithoutLock(); + ReturnValue_t unlockResult = glob::dataPool.unlockDataPool(); + if(unlockResult != HasReturnvaluesIF::RETURN_OK) { + sif::error << "PIDReader::read: Could not unlock data pool!" + << std::endl; + } + return result; + } /** * Copy ctor to copy classes containing Pool Variables. */ @@ -89,7 +106,7 @@ public: * \brief This operation returns the data pool id of the variable. */ uint32_t getDataPoolId() const { - return DataPool::PIDToDataPoolId(parameterId); + return GlobalDataPool::PIDToDataPoolId(parameterId); } uint32_t getParameterId() const { return parameterId; @@ -114,7 +131,7 @@ public: return valid; } - void setValid(uint8_t valid) { + void setValid(bool valid) { this->valid = valid; } diff --git a/datapool/PIDReaderList.h b/datapoolglob/PIDReaderList.h similarity index 68% rename from datapool/PIDReaderList.h rename to datapoolglob/PIDReaderList.h index 1f6aa99c..ae99f3aa 100644 --- a/datapool/PIDReaderList.h +++ b/datapoolglob/PIDReaderList.h @@ -1,8 +1,8 @@ -#ifndef FRAMEWORK_DATAPOOL_PIDREADERLIST_H_ -#define FRAMEWORK_DATAPOOL_PIDREADERLIST_H_ +#ifndef FRAMEWORK_DATAPOOLGLOB_PIDREADERLIST_H_ +#define FRAMEWORK_DATAPOOLGLOB_PIDREADERLIST_H_ -#include "PIDReader.h" -#include "PoolVariableIF.h" +#include "../datapool/PoolVariableIF.h" +#include "../datapoolglob/PIDReader.h" template class PIDReaderList { private: @@ -24,4 +24,4 @@ public: -#endif /* FRAMEWORK_DATAPOOL_PIDREADERLIST_H_ */ +#endif /* FRAMEWORK_DATAPOOLGLOB_PIDREADERLIST_H_ */ diff --git a/datapoolglob/PoolRawAccess.cpp b/datapoolglob/PoolRawAccess.cpp new file mode 100644 index 00000000..53706c6d --- /dev/null +++ b/datapoolglob/PoolRawAccess.cpp @@ -0,0 +1,239 @@ +#include "../datapoolglob/GlobalDataPool.h" +#include "../datapoolglob/PoolRawAccess.h" +#include "../serviceinterface/ServiceInterfaceStream.h" +#include "../serialize/EndianConverter.h" + +#include + +PoolRawAccess::PoolRawAccess(uint32_t set_id, uint8_t setArrayEntry, + DataSetIF* dataSet, ReadWriteMode_t setReadWriteMode) : + dataPoolId(set_id), arrayEntry(setArrayEntry), valid(false), + type(Type::UNKNOWN_TYPE), typeSize(0), arraySize(0), sizeTillEnd(0), + readWriteMode(setReadWriteMode) { + memset(value, 0, sizeof(value)); + if (dataSet != nullptr) { + dataSet->registerVariable(this); + } +} + +PoolRawAccess::~PoolRawAccess() {} + +ReturnValue_t PoolRawAccess::read(uint32_t lockTimeout) { + ReturnValue_t result = glob::dataPool.lockDataPool(lockTimeout); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = readWithoutLock(); + ReturnValue_t unlockResult = glob::dataPool.unlockDataPool(); + if(unlockResult != HasReturnvaluesIF::RETURN_OK) { + sif::error << "GlobPoolVar::read: Could not unlock global data pool" + << std::endl; + } + return result; +} + +ReturnValue_t PoolRawAccess::readWithoutLock() { + ReturnValue_t result = RETURN_FAILED; + PoolEntryIF* readOut = glob::dataPool.getRawData(dataPoolId); + if (readOut != nullptr) { + result = handleReadOut(readOut); + if(result == RETURN_OK) { + return result; + } + } else { + result = READ_ENTRY_NON_EXISTENT; + } + handleReadError(result); + return result; +} + +ReturnValue_t PoolRawAccess::handleReadOut(PoolEntryIF* readOut) { + ReturnValue_t result = RETURN_FAILED; + valid = readOut->getValid(); + if (readOut->getSize() > arrayEntry) { + arraySize = readOut->getSize(); + typeSize = readOut->getByteSize() / readOut->getSize(); + type = readOut->getType(); + if (typeSize <= sizeof(value)) { + uint16_t arrayPosition = arrayEntry * typeSize; + sizeTillEnd = readOut->getByteSize() - arrayPosition; + uint8_t* ptr = &((uint8_t*) readOut->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) { + sif::error << "PoolRawAccess: read of DP Variable 0x" << std::hex << dataPoolId + << std::dec << " failed, "; + if(result == READ_TYPE_TOO_LARGE) { + sif::error << "type too large." << std::endl; + } + else if(result == READ_INDEX_TOO_LARGE) { + sif::error << "index too large." << std::endl; + } + else if(result == READ_ENTRY_NON_EXISTENT) { + sif::error << "entry does not exist." << std::endl; + } + + valid = INVALID; + typeSize = 0; + sizeTillEnd = 0; + memset(value, 0, sizeof(value)); +} + +ReturnValue_t PoolRawAccess::commit(uint32_t lockTimeout) { + ReturnValue_t result = glob::dataPool.lockDataPool(lockTimeout); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = commitWithoutLock(); + ReturnValue_t unlockResult = glob::dataPool.unlockDataPool(); + if(unlockResult != HasReturnvaluesIF::RETURN_OK) { + sif::error << "GlobPoolVar::read: Could not unlock global data pool" + << std::endl; + } + return result; +} + +ReturnValue_t PoolRawAccess::commitWithoutLock() { + PoolEntryIF* write_back = glob::dataPool.getRawData(dataPoolId); + if ((write_back != NULL) && (readWriteMode != VAR_READ)) { + write_back->setValid(valid); + uint8_t array_position = arrayEntry * typeSize; + uint8_t* ptr = &((uint8_t*) write_back->getRawData())[array_position]; + memcpy(ptr, value, typeSize); + return HasReturnvaluesIF::RETURN_OK; + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } +} + +uint8_t* PoolRawAccess::getEntry() { + return value; +} + +ReturnValue_t PoolRawAccess::getEntryEndianSafe(uint8_t* buffer, + size_t* writtenBytes, size_t max_size) { + uint8_t* data_ptr = getEntry(); + // debug << "PoolRawAccess::getEntry: Array position: " << + // index * size_of_type << " Size of T: " << (int)size_of_type << + // " ByteSize: " << byte_size << " Position: " << *size << std::endl; + if (typeSize == 0) + return DATA_POOL_ACCESS_FAILED; + if (typeSize > max_size) + return INCORRECT_SIZE; + EndianConverter::convertBigEndian(buffer, data_ptr, typeSize); + *writtenBytes = typeSize; + return HasReturnvaluesIF::RETURN_OK; +} + + +ReturnValue_t PoolRawAccess::serialize(uint8_t** buffer, size_t* size, + size_t maxSize, Endianness streamEndianness) const { + if (typeSize + *size <= maxSize) { + switch(streamEndianness) { + case(Endianness::BIG): + EndianConverter::convertBigEndian(*buffer, value, typeSize); + break; + case(Endianness::LITTLE): + EndianConverter::convertLittleEndian(*buffer, value, typeSize); + break; + case(Endianness::MACHINE): + default: + memcpy(*buffer, value, typeSize); + break; + } + *size += typeSize; + (*buffer) += typeSize; + return HasReturnvaluesIF::RETURN_OK; + } else { + return SerializeIF::BUFFER_TOO_SHORT; + } +} + + +Type PoolRawAccess::getType() { + return type; +} + +size_t PoolRawAccess::getSizeOfType() { + return typeSize; +} + +size_t PoolRawAccess::getArraySize(){ + return arraySize; +} + +uint32_t PoolRawAccess::getDataPoolId() const { + return dataPoolId; +} + +PoolVariableIF::ReadWriteMode_t PoolRawAccess::getReadWriteMode() const { + return readWriteMode; +} + +ReturnValue_t PoolRawAccess::setEntryFromBigEndian(const uint8_t *buffer, + size_t setSize) { + if (typeSize == setSize) { + EndianConverter::convertBigEndian(value, buffer, typeSize); + return HasReturnvaluesIF::RETURN_OK; + } else { + sif::error << "PoolRawAccess::setEntryFromBigEndian: Illegal sizes: " + "Internal" << (uint32_t) typeSize << ", Requested: " << setSize + << std::endl; + return INCORRECT_SIZE; + } +} + +bool PoolRawAccess::isValid() const { + if (valid != INVALID) + return true; + else + return false; +} + +void PoolRawAccess::setValid(bool valid) { + this->valid = valid; +} + +size_t PoolRawAccess::getSizeTillEnd() const { + return sizeTillEnd; +} + + +size_t PoolRawAccess::getSerializedSize() const { + return typeSize; +} + +ReturnValue_t PoolRawAccess::deSerialize(const uint8_t **buffer, size_t *size, + Endianness streamEndianness) { + + if (*size >= typeSize) { + switch(streamEndianness) { + case(Endianness::BIG): + EndianConverter::convertBigEndian(value, *buffer, typeSize); + break; + case(Endianness::LITTLE): + EndianConverter::convertLittleEndian(value, *buffer, typeSize); + break; + case(Endianness::MACHINE): + default: + memcpy(value, *buffer, typeSize); + break; + } + *size -= typeSize; + *buffer += typeSize; + return HasReturnvaluesIF::RETURN_OK; + } + else { + return SerializeIF::STREAM_TOO_SHORT; + } +} diff --git a/datapoolglob/PoolRawAccess.h b/datapoolglob/PoolRawAccess.h new file mode 100644 index 00000000..15643a41 --- /dev/null +++ b/datapoolglob/PoolRawAccess.h @@ -0,0 +1,220 @@ +#ifndef POOLRAWACCESS_H_ +#define POOLRAWACCESS_H_ + +#include "../datapool/DataSetIF.h" +#include "../datapool/PoolEntryIF.h" +#include "../datapool/PoolVariableIF.h" +#include "../globalfunctions/Type.h" + +/** + * @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 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, HasReturnvaluesIF { +public: + /** + * 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); + + /** + * @brief This operation returns a pointer to the entry fetched. + * @details Return pointer to the buffer containing the raw data + * Size and number of data can be retrieved by other means. + */ + uint8_t* getEntry(); + /** + * @brief This operation returns the fetched entry from the data pool and + * flips the bytes, if necessary. + * @details It makes use of the getEntry call of this function, but additionally flips the + * bytes to big endian, which is the default for external communication (as House- + * keeping telemetry). To achieve this, the data is copied directly to the passed + * buffer, if it fits in the given max_size. + * @param buffer A pointer to a buffer to write to + * @param writtenBytes The number of bytes written is returned with this value. + * @param max_size The maximum size that the function may write to buffer. + * @return - @c RETURN_OK if entry could be acquired + * - @c RETURN_FAILED else. + */ + ReturnValue_t getEntryEndianSafe(uint8_t *buffer, size_t *size, + size_t maxSize); + + /** + * @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, size_t *size, + size_t maxSize, Endianness streamEndianness) const override; + + size_t getSerializedSize() const override; + + ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, + Endianness streamEndianness) override; + + /** + * With this method, the content can be set from a big endian buffer safely. + * @param buffer Pointer to the data to set + * @param size Size of the data to write. Must fit this->size. + * @return - @c RETURN_OK on success + * - @c RETURN_FAILED on failure + */ + ReturnValue_t setEntryFromBigEndian(const uint8_t* buffer, + size_t setSize); + /** + * @brief This operation returns the type of the entry currently stored. + */ + Type getType(); + /** + * @brief This operation returns the size of the entry currently stored. + */ + size_t getSizeOfType(); + /** + * + * @return the size of the datapool array + */ + size_t getArraySize(); + /** + * @brief This operation returns the data pool id of the variable. + */ + uint32_t getDataPoolId() const; + + 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]; + + + /** + * @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(); + + /** + * This method returns if the variable is read-write or read-only. + */ + ReadWriteMode_t getReadWriteMode() const; + /** + * @brief With this call, the valid information of the variable is returned. + */ + bool isValid() const; + + void setValid(bool valid); + /** + * Getter for the remaining size. + */ + size_t getSizeTillEnd() const; + + /** + * @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 call is protected by a lock of the global data pool. + * @return -@c RETURN_OK Read successfull + * -@c READ_TYPE_TOO_LARGE + * -@c READ_INDEX_TOO_LARGE + * -@c READ_ENTRY_NON_EXISTENT + */ + ReturnValue_t read(uint32_t lockTimeout = MutexIF::BLOCKING) override; + /** + * @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 call is protected by a lock of the global data pool. + * + */ + ReturnValue_t commit(uint32_t lockTimeout = MutexIF::BLOCKING) override; + +protected: + /** + * @brief Like #read, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t readWithoutLock() override; + /** + * @brief Like #commit, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t commitWithoutLock() override; + + 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. + */ + size_t typeSize; + /** + * The size of the DP array (single values return 1) + */ + size_t arraySize; + /** + * The size (in bytes) from the selected entry till the end of this DataPool variable. + */ + size_t sizeTillEnd; + /** + * @brief The information whether the class is read-write or read-only is stored here. + */ + ReadWriteMode_t readWriteMode; +}; + +#endif /* POOLRAWACCESS_H_ */ From 790d3f14654cf1b7f937c36f9b1d02289dc596ce Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 6 Sep 2020 15:25:56 +0200 Subject: [PATCH 002/119] taken over all changes --- fsfw.mk | 16 ++++++++++++++ internalError/InternalErrorReporter.cpp | 19 ++++++++--------- monitoring/MonitorBase.h | 14 ++++++------- power/Fuse.cpp | 6 +++--- power/Fuse.h | 15 ++++++------- power/PowerSensor.h | 10 ++++----- thermal/CoreComponent.cpp | 12 +++++------ thermal/CoreComponent.h | 23 ++++++++++---------- thermal/ThermalComponent.cpp | 18 +++++++--------- thermal/ThermalComponent.h | 28 +++++++++++++++++++++++-- thermal/ThermalModule.cpp | 8 +++---- thermal/ThermalModule.h | 17 ++++++++------- 12 files changed, 114 insertions(+), 72 deletions(-) diff --git a/fsfw.mk b/fsfw.mk index c2c6e747..c5847554 100644 --- a/fsfw.mk +++ b/fsfw.mk @@ -9,6 +9,9 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/controller/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/coordinates/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datalinklayer/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datapool/*.cpp) +CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datapoolglob/*.cpp) +CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datapoollocal/*.cpp) +CXXSRC += $(wildcard $(FRAMEWORK_PATH)/housekeeping/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/devicehandlers/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/events/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/events/eventmatching/*.cpp) @@ -28,12 +31,25 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/*.cpp) # select the OS ifeq ($(OS_FSFW),rtems) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/rtems/*.cpp) + else ifeq ($(OS_FSFW),linux) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/linux/*.cpp) + else ifeq ($(OS_FSFW),freeRTOS) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/FreeRTOS/*.cpp) + else ifeq ($(OS_FSFW),host) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/host/*.cpp) +ifeq ($(OS),Windows_NT) +CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/windows/*.cpp) +else +# For now, the linux UDP bridge sources needs to be included manually by upper makefile +# for host OS because we can't be sure the OS is linux. +# Following lines can be used to do this: +# CXXSRC += $(FRAMEWORK_PATH)/osal/linux/TcUnixUdpPollingTask.cpp +# CXXSRC += $(FRAMEWORK_PATH)/osal/linux/TmTcUnixUdpBridge.cpp +endif + else $(error invalid OS_FSFW specified, valid OS_FSFW are rtems, linux, freeRTOS, host) endif diff --git a/internalError/InternalErrorReporter.cpp b/internalError/InternalErrorReporter.cpp index 861e1595..3424266b 100644 --- a/internalError/InternalErrorReporter.cpp +++ b/internalError/InternalErrorReporter.cpp @@ -1,17 +1,16 @@ +#include "../datapoolglob/GlobalDataSet.h" #include "InternalErrorReporter.h" -#include "../datapool/DataSet.h" -#include "../datapool/PoolVariable.h" +#include "../datapoolglob/GlobalPoolVariable.h" #include "../ipc/MutexFactory.h" #include "../serviceinterface/ServiceInterfaceStream.h" 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(); } @@ -21,13 +20,13 @@ InternalErrorReporter::~InternalErrorReporter() { ReturnValue_t InternalErrorReporter::performOperation(uint8_t opCode) { - DataSet mySet; - PoolVariable queueHitsInPool(queuePoolId, &mySet, + GlobDataSet mySet; + gp_uint32_t queueHitsInPool(queuePoolId, &mySet, PoolVariableIF::VAR_READ_WRITE); - PoolVariable tmHitsInPool(tmPoolId, &mySet, + gp_uint32_t tmHitsInPool(tmPoolId, &mySet, PoolVariableIF::VAR_READ_WRITE); - PoolVariable storeHitsInPool(storePoolId, &mySet, + gp_uint32_t storeHitsInPool(storePoolId, &mySet, PoolVariableIF::VAR_READ_WRITE); mySet.read(); diff --git a/monitoring/MonitorBase.h b/monitoring/MonitorBase.h index b2d0e6cb..5173c479 100644 --- a/monitoring/MonitorBase.h +++ b/monitoring/MonitorBase.h @@ -1,12 +1,12 @@ #ifndef MONITORBASE_H_ #define MONITORBASE_H_ -#include "../datapool/DataSet.h" -#include "../datapool/PIDReader.h" -#include "LimitViolationReporter.h" -#include "MonitoringIF.h" -#include "MonitoringMessageContent.h" -#include "MonitorReporter.h" +#include "../datapoolglob/GlobalDataSet.h" +#include "../datapoolglob/PIDReader.h" +#include "../monitoring/LimitViolationReporter.h" +#include "../monitoring/MonitoringIF.h" +#include "../monitoring/MonitoringMessageContent.h" +#include "../monitoring/MonitorReporter.h" /** * Base class for monitoring of parameters. @@ -48,7 +48,7 @@ public: protected: virtual ReturnValue_t fetchSample(T* sample) { - DataSet mySet; + GlobDataSet mySet; PIDReader parameter(this->parameterId, &mySet); mySet.read(); if (!parameter.isValid()) { diff --git a/power/Fuse.cpp b/power/Fuse.cpp index 9986ab6b..3ea0b18c 100644 --- a/power/Fuse.cpp +++ b/power/Fuse.cpp @@ -1,7 +1,7 @@ #include "../monitoring/LimitViolationReporter.h" #include "../monitoring/MonitoringMessageContent.h" #include "../objectmanager/ObjectManagerIF.h" -#include "Fuse.h" +#include "../power/Fuse.h" #include "../serialize/SerialFixedArrayListAdapter.h" #include "../ipc/QueueFactory.h" @@ -12,7 +12,7 @@ Fuse::Fuse(object_id_t fuseObjectId, uint8_t fuseId, VariableIds ids, SystemObject(fuseObjectId), oldFuseState(0), fuseId(fuseId), powerIF( NULL), currentLimit(fuseObjectId, 1, ids.pidCurrent, confirmationCount, maxCurrent, FUSE_CURRENT_HIGH), powerMonitor(fuseObjectId, 2, - DataPool::poolIdAndPositionToPid(ids.poolIdPower, 0), + GlobalDataPool::poolIdAndPositionToPid(ids.poolIdPower, 0), confirmationCount), set(), voltage(ids.pidVoltage, &set), current( ids.pidCurrent, &set), state(ids.pidState, &set), power( ids.poolIdPower, &set, PoolVariableIF::VAR_READ_WRITE), commandQueue( @@ -109,7 +109,7 @@ size_t Fuse::getSerializedSize() const { } ReturnValue_t Fuse::deSerialize(const uint8_t** buffer, size_t* size, -Endianness streamEndianness) { + Endianness streamEndianness) { ReturnValue_t result = RETURN_FAILED; for (DeviceList::iterator iter = devices.begin(); iter != devices.end(); iter++) { diff --git a/power/Fuse.h b/power/Fuse.h index 279642be..dd8759f4 100644 --- a/power/Fuse.h +++ b/power/Fuse.h @@ -1,12 +1,13 @@ #ifndef FUSE_H_ #define FUSE_H_ -#include "../datapool/DataSet.h" -#include "../datapool/PIDReader.h" +#include "../datapoolglob/GlobalDataSet.h" +#include "../datapoolglob/GlobalPoolVariable.h" +#include "../datapoolglob/PIDReader.h" #include "../devicehandlers/HealthDevice.h" #include "../monitoring/AbsLimitMonitor.h" -#include "PowerComponentIF.h" -#include "PowerSwitchIF.h" +#include "../power/PowerComponentIF.h" +#include "../power/PowerSwitchIF.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../parameters/ParameterHelper.h" #include @@ -84,12 +85,12 @@ private: }; PowerMonitor powerMonitor; - DataSet set; + GlobDataSet set; PIDReader voltage; PIDReader current; PIDReader state; - db_float_t power; - MessageQueueIF *commandQueue; + gp_float_t power; + MessageQueueIF* commandQueue; ParameterHelper parameterHelper; HealthHelper healthHelper; static object_id_t powerSwitchId; diff --git a/power/PowerSensor.h b/power/PowerSensor.h index 0f973e45..da0bd8c7 100644 --- a/power/PowerSensor.h +++ b/power/PowerSensor.h @@ -1,9 +1,9 @@ #ifndef POWERSENSOR_H_ #define POWERSENSOR_H_ -#include "../datapool/DataSet.h" -#include "../datapool/PIDReader.h" -#include "../datapool/PoolVariable.h" +#include "../datapoolglob/GlobalDataSet.h" +#include "../datapoolglob/GlobalPoolVariable.h" +#include "../datapoolglob/PIDReader.h" #include "../devicehandlers/HealthDevice.h" #include "../monitoring/LimitMonitor.h" #include "../parameters/ParameterHelper.h" @@ -53,12 +53,12 @@ private: MessageQueueIF* commandQueue; ParameterHelper parameterHelper; HealthHelper healthHelper; - DataSet set; + GlobDataSet set; //Variables in PIDReader current; PIDReader voltage; //Variables out - db_float_t power; + gp_float_t power; static const uint8_t MODULE_ID_CURRENT = 1; static const uint8_t MODULE_ID_VOLTAGE = 2; diff --git a/thermal/CoreComponent.cpp b/thermal/CoreComponent.cpp index 304712ef..657e79cb 100644 --- a/thermal/CoreComponent.cpp +++ b/thermal/CoreComponent.cpp @@ -2,7 +2,7 @@ CoreComponent::CoreComponent(object_id_t reportingObjectId, uint8_t domainId, uint32_t temperaturePoolId, uint32_t targetStatePoolId, - uint32_t currentStatePoolId, uint32_t requestPoolId, DataSet* dataSet, + uint32_t currentStatePoolId, uint32_t requestPoolId, GlobDataSet* dataSet, AbstractTemperatureSensor* sensor, AbstractTemperatureSensor* firstRedundantSensor, AbstractTemperatureSensor* secondRedundantSensor, @@ -18,14 +18,14 @@ CoreComponent::CoreComponent(object_id_t reportingObjectId, uint8_t domainId, AbstractTemperatureSensor::ZERO_KELVIN_C), parameters( parameters), temperatureMonitor(reportingObjectId, domainId + 1, - DataPool::poolIdAndPositionToPid(temperaturePoolId, 0), + GlobalDataPool::poolIdAndPositionToPid(temperaturePoolId, 0), COMPONENT_TEMP_CONFIRMATION), domainId(domainId) { if (thermalModule != NULL) { thermalModule->registerComponent(this, priority); } //Set thermal state once, then leave to operator. - DataSet mySet; - PoolVariable writableTargetState(targetStatePoolId, &mySet, + GlobDataSet mySet; + gp_uint8_t writableTargetState(targetStatePoolId, &mySet, PoolVariableIF::VAR_WRITE); writableTargetState = initialTargetState; mySet.commit(PoolVariableIF::VALID); @@ -70,8 +70,8 @@ float CoreComponent::getLowerOpLimit() { } ReturnValue_t CoreComponent::setTargetState(int8_t newState) { - DataSet mySet; - PoolVariable writableTargetState(targetState.getDataPoolId(), + GlobDataSet mySet; + gp_uint8_t writableTargetState(targetState.getDataPoolId(), &mySet, PoolVariableIF::VAR_READ_WRITE); mySet.read(); if ((writableTargetState == STATE_REQUEST_OPERATIONAL) diff --git a/thermal/CoreComponent.h b/thermal/CoreComponent.h index 48a49f7d..8093711d 100644 --- a/thermal/CoreComponent.h +++ b/thermal/CoreComponent.h @@ -1,13 +1,14 @@ #ifndef MISSION_CONTROLLERS_TCS_CORECOMPONENT_H_ #define MISSION_CONTROLLERS_TCS_CORECOMPONENT_H_ -#include "../datapool/DataSet.h" -#include "../datapool/PoolVariable.h" -#include "ThermalComponentIF.h" -#include "AbstractTemperatureSensor.h" -#include "ThermalModule.h" -#include "ThermalMonitor.h" +#include "../datapoolglob/GlobalDataSet.h" +#include "../datapoolglob/GlobalPoolVariable.h" +#include "../thermal/ThermalComponentIF.h" +#include "../thermal/AbstractTemperatureSensor.h" +#include "../thermal/ThermalModule.h" +#include "../thermal/ThermalMonitor.h" +// TODO: Documentaiton, how to use this? only use Thermal Component, which inherits core component? class CoreComponent: public ThermalComponentIF { public: struct Parameters { @@ -22,7 +23,7 @@ public: CoreComponent(object_id_t reportingObjectId, uint8_t domainId, uint32_t temperaturePoolId, uint32_t targetStatePoolId, uint32_t currentStatePoolId, - uint32_t requestPoolId, DataSet *dataSet, + uint32_t requestPoolId, GlobDataSet *dataSet, AbstractTemperatureSensor *sensor, AbstractTemperatureSensor *firstRedundantSensor, AbstractTemperatureSensor *secondRedundantSensor, @@ -57,10 +58,10 @@ protected: AbstractTemperatureSensor *secondRedundantSensor; ThermalModuleIF *thermalModule; - db_float_t temperature; - db_int8_t targetState; - db_int8_t currentState; - db_uint8_t heaterRequest; + gp_float_t temperature; + gp_int8_t targetState; + gp_int8_t currentState; + gp_uint8_t heaterRequest; bool isHeating; diff --git a/thermal/ThermalComponent.cpp b/thermal/ThermalComponent.cpp index 5dcd0bc6..50786654 100644 --- a/thermal/ThermalComponent.cpp +++ b/thermal/ThermalComponent.cpp @@ -3,7 +3,7 @@ ThermalComponent::ThermalComponent(object_id_t reportingObjectId, uint8_t domainId, uint32_t temperaturePoolId, uint32_t targetStatePoolId, uint32_t currentStatePoolId, - uint32_t requestPoolId, DataSet* dataSet, + uint32_t requestPoolId, GlobDataSet* dataSet, AbstractTemperatureSensor* sensor, AbstractTemperatureSensor* firstRedundantSensor, AbstractTemperatureSensor* secondRedundantSensor, @@ -12,20 +12,18 @@ 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() { } ReturnValue_t ThermalComponent::setTargetState(int8_t newState) { - DataSet mySet; - PoolVariable writableTargetState(targetState.getDataPoolId(), + GlobDataSet mySet; + gp_int8_t writableTargetState(targetState.getDataPoolId(), &mySet, PoolVariableIF::VAR_READ_WRITE); mySet.read(); if ((writableTargetState == STATE_REQUEST_OPERATIONAL) @@ -42,7 +40,7 @@ ReturnValue_t ThermalComponent::setTargetState(int8_t newState) { } } -ReturnValue_t ThermalComponent::setLimits(const uint8_t* data, uint32_t size) { +ReturnValue_t ThermalComponent::setLimits(const uint8_t* data, size_t size) { if (size != 4 * sizeof(parameters.lowerOpLimit)) { return MonitoringIF::INVALID_SIZE; } diff --git a/thermal/ThermalComponent.h b/thermal/ThermalComponent.h index 93243868..195f03b1 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,13 +17,34 @@ 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, + GlobDataSet *dataSet, AbstractTemperatureSensor *sensor, AbstractTemperatureSensor *firstRedundantSensor, AbstractTemperatureSensor *secondRedundantSensor, ThermalModuleIF *thermalModule, Parameters parameters, @@ -29,7 +53,7 @@ public: ReturnValue_t setTargetState(int8_t newState); - virtual ReturnValue_t setLimits( const uint8_t* data, uint32_t size); + virtual ReturnValue_t setLimits( const uint8_t* data, size_t size); virtual ReturnValue_t getParameter(uint8_t domainId, uint16_t parameterId, ParameterWrapper *parameterWrapper, diff --git a/thermal/ThermalModule.cpp b/thermal/ThermalModule.cpp index c573008e..fbd6939e 100644 --- a/thermal/ThermalModule.cpp +++ b/thermal/ThermalModule.cpp @@ -6,7 +6,7 @@ ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, uint32_t currentStatePoolId, uint32_t targetStatePoolId, - DataSet *dataSet, Parameters parameters, + GlobDataSet *dataSet, Parameters parameters, RedundantHeater::Parameters heaterParameters) : oldStrategy(ACTIVE_SINGLE), survivalTargetTemp(0), targetTemp(0), heating( false), parameters(parameters), moduleTemperature( @@ -16,7 +16,7 @@ ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, heater = new RedundantHeater(heaterParameters); } -ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, DataSet* dataSet) : +ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, GlobDataSet* dataSet) : oldStrategy(ACTIVE_SINGLE), survivalTargetTemp(0), targetTemp(0), heating( false), parameters( { 0, 0 }), moduleTemperature( moduleTemperaturePoolId, dataSet, PoolVariableIF::VAR_WRITE), heater( @@ -250,8 +250,8 @@ bool ThermalModule::calculateModuleHeaterRequestAndSetModuleStatus( } void ThermalModule::setHeating(bool on) { - DataSet mySet; - PoolVariable writableTargetState(targetState.getDataPoolId(), + GlobDataSet mySet; + gp_int8_t writableTargetState(targetState.getDataPoolId(), &mySet, PoolVariableIF::VAR_WRITE); if (on) { writableTargetState = STATE_REQUEST_HEATING; diff --git a/thermal/ThermalModule.h b/thermal/ThermalModule.h index 19ab9a54..41be6baa 100644 --- a/thermal/ThermalModule.h +++ b/thermal/ThermalModule.h @@ -1,8 +1,8 @@ #ifndef THERMALMODULE_H_ #define THERMALMODULE_H_ -#include "../datapool/DataSet.h" -#include "../datapool/PoolVariable.h" +#include "../datapoolglob/GlobalDataSet.h" +#include "../datapoolglob/GlobalPoolVariable.h" #include "../devicehandlers/HealthDevice.h" #include "../events/EventReportingProxyIF.h" #include "ThermalModuleIF.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: @@ -20,10 +23,10 @@ public: }; ThermalModule(uint32_t moduleTemperaturePoolId, uint32_t currentStatePoolId, - uint32_t targetStatePoolId, DataSet *dataSet, Parameters parameters, + uint32_t targetStatePoolId, GlobDataSet *dataSet, Parameters parameters, RedundantHeater::Parameters heaterParameters); - ThermalModule(uint32_t moduleTemperaturePoolId, DataSet *dataSet); + ThermalModule(uint32_t moduleTemperaturePoolId, GlobDataSet *dataSet); virtual ~ThermalModule(); @@ -67,12 +70,12 @@ protected: Parameters parameters; - db_float_t moduleTemperature; + gp_float_t moduleTemperature; RedundantHeater *heater; - db_int8_t currentState; - db_int8_t targetState; + gp_int8_t currentState; + gp_int8_t targetState; std::list sensors; std::list components; From f61056eeb7213cf111c35dde65827e55b2cd8d0a Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 6 Sep 2020 15:30:56 +0200 Subject: [PATCH 003/119] adapted DHB to renaming --- devicehandlers/DeviceHandlerBase.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 017ea8d2..6fcba64b 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -5,8 +5,8 @@ #include "../objectmanager/ObjectManager.h" #include "../storagemanager/StorageManagerIF.h" #include "../thermal/ThermalComponentIF.h" -#include "../datapool/DataSet.h" -#include "../datapool/PoolVariable.h" +#include "../datapoolglob/GlobalDataSet.h" +#include "../datapoolglob/GlobalPoolVariable.h" #include "../globalfunctions/CRC.h" #include "../subsystem/SubsystemBase.h" #include "../ipc/QueueFactory.h" @@ -186,8 +186,8 @@ ReturnValue_t DeviceHandlerBase::initialize() { fillCommandAndReplyMap(); //Set temperature target state to NON_OP. - DataSet mySet; - db_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, + GlobDataSet mySet; + gp_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_WRITE); mySet.read(); thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; @@ -469,8 +469,8 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) { Clock::getUptime(&timeoutStart); if (mode == MODE_OFF) { - DataSet mySet; - db_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, + GlobDataSet mySet; + gp_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_READ_WRITE); mySet.read(); if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { @@ -926,10 +926,10 @@ ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, if ((commandedMode == MODE_ON) && (mode == MODE_OFF) && (deviceThermalStatePoolId != PoolVariableIF::NO_PARAMETER)) { - DataSet mySet; - db_int8_t thermalState(deviceThermalStatePoolId, &mySet, + GlobDataSet mySet; + gp_int8_t thermalState(deviceThermalStatePoolId, &mySet, PoolVariableIF::VAR_READ); - db_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, + gp_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_READ); mySet.read(); if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { @@ -956,8 +956,8 @@ void DeviceHandlerBase::startTransition(Mode_t commandedMode, childTransitionDelay = getTransitionDelayMs(_MODE_START_UP, MODE_ON); triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); - DataSet mySet; - db_int8_t thermalRequest(deviceThermalRequestPoolId, + GlobDataSet mySet; + gp_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_READ_WRITE); mySet.read(); if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { @@ -1185,7 +1185,7 @@ void DeviceHandlerBase::handleDeviceTM(SerializeIF* data, } //Try to cast to GlobDataSet and commit data. if (!neverInDataPool) { - DataSet* dataSet = dynamic_cast(data); + GlobDataSet* dataSet = dynamic_cast(data); if (dataSet != NULL) { dataSet->commit(PoolVariableIF::VALID); } From 4d1492f130e64a4e8ec803814ca6f4bf9bcaccbc Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sat, 26 Sep 2020 14:13:20 +0200 Subject: [PATCH 004/119] event cfg file loc not explicit anymore --- events/Event.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events/Event.h b/events/Event.h index e22c9db1..f8410f32 100644 --- a/events/Event.h +++ b/events/Event.h @@ -4,7 +4,7 @@ #include #include "fwSubsystemIdRanges.h" //could be move to more suitable location -#include +#include typedef uint16_t EventId_t; typedef uint8_t EventSeverity_t; From b313043e4312af0f54a24a2b8e560ba22ce287c5 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 1 Oct 2020 12:05:24 +0200 Subject: [PATCH 005/119] added new local data pool files --- datapoollocal/HasLocalDataPoolIF.h | 95 +++++ datapoollocal/LocalDataPoolManager.cpp | 427 ++++++++++++++++++++ datapoollocal/LocalDataPoolManager.h | 291 +++++++++++++ datapoollocal/LocalDataSet.cpp | 22 + datapoollocal/LocalDataSet.h | 21 + datapoollocal/LocalPoolDataSetBase.cpp | 259 ++++++++++++ datapoollocal/LocalPoolDataSetBase.h | 190 +++++++++ datapoollocal/LocalPoolVariable.h | 177 ++++++++ datapoollocal/LocalPoolVariable.tpp | 169 ++++++++ datapoollocal/LocalPoolVector.h | 202 +++++++++ datapoollocal/LocalPoolVector.tpp | 209 ++++++++++ datapoollocal/SharedLocalDataSet.cpp | 16 + datapoollocal/SharedLocalDataSet.h | 24 ++ datapoollocal/StaticLocalDataSet.h | 50 +++ housekeeping/AcceptsHkPacketsIF.h | 11 + housekeeping/HousekeepingMessage.cpp | 162 ++++++++ housekeeping/HousekeepingMessage.h | 157 +++++++ housekeeping/HousekeepingPacketDownlink.h | 34 ++ housekeeping/HousekeepingPacketUpdate.h | 73 ++++ housekeeping/HousekeepingSetPacket.h | 59 +++ housekeeping/PeriodicHousekeepingHelper.cpp | 48 +++ housekeeping/PeriodicHousekeepingHelper.h | 32 ++ 22 files changed, 2728 insertions(+) create mode 100644 datapoollocal/HasLocalDataPoolIF.h create mode 100644 datapoollocal/LocalDataPoolManager.cpp create mode 100644 datapoollocal/LocalDataPoolManager.h create mode 100644 datapoollocal/LocalDataSet.cpp create mode 100644 datapoollocal/LocalDataSet.h create mode 100644 datapoollocal/LocalPoolDataSetBase.cpp create mode 100644 datapoollocal/LocalPoolDataSetBase.h create mode 100644 datapoollocal/LocalPoolVariable.h create mode 100644 datapoollocal/LocalPoolVariable.tpp create mode 100644 datapoollocal/LocalPoolVector.h create mode 100644 datapoollocal/LocalPoolVector.tpp create mode 100644 datapoollocal/SharedLocalDataSet.cpp create mode 100644 datapoollocal/SharedLocalDataSet.h create mode 100644 datapoollocal/StaticLocalDataSet.h create mode 100644 housekeeping/AcceptsHkPacketsIF.h create mode 100644 housekeeping/HousekeepingMessage.cpp create mode 100644 housekeeping/HousekeepingMessage.h create mode 100644 housekeeping/HousekeepingPacketDownlink.h create mode 100644 housekeeping/HousekeepingPacketUpdate.h create mode 100644 housekeeping/HousekeepingSetPacket.h create mode 100644 housekeeping/PeriodicHousekeepingHelper.cpp create mode 100644 housekeeping/PeriodicHousekeepingHelper.h diff --git a/datapoollocal/HasLocalDataPoolIF.h b/datapoollocal/HasLocalDataPoolIF.h new file mode 100644 index 00000000..f8f4ef4c --- /dev/null +++ b/datapoollocal/HasLocalDataPoolIF.h @@ -0,0 +1,95 @@ +#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ +#define FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ + +#include "../datapool/PoolEntryIF.h" +#include "../ipc/MessageQueueSenderIF.h" +#include "../housekeeping/HousekeepingMessage.h" + +#include + +class LocalDataPoolManager; +class LocalPoolDataSetBase; + +/** + * @brief Type definition for local pool entries. + */ +using lp_id_t = uint32_t; +using LocalDataPool = std::map; +using LocalDataPoolMapIter = LocalDataPool::iterator; + +/** + * @brief This interface is implemented by classes which posses a local + * data pool (not the managing class). It defines the relationship + * between the local data pool owner and the LocalDataPoolManager. + * @details + * Any class implementing this interface shall also have a LocalDataPoolManager + * member class which contains the actual pool data structure + * and exposes the public interface for it. + * This is required because the pool entries are templates, which makes + * specifying an interface rather difficult. The local data pool can be + * accessed by using the LocalPoolVariable, LocalPoolVector or LocalDataSet + * classes. + * + * Architectural Note: + * This could be circumvented by using a wrapper/accessor function or + * implementing the templated function in this interface.. + * The first solution sounds better than the second but + * the LocalPoolVariable classes are templates as well, so this just shifts + * the problem somewhere else. Interfaces are nice, but the most + * pragmatic solution I found was to offer the client the full interface + * of the LocalDataPoolManager. + */ +class HasLocalDataPoolIF { +public: + virtual~ HasLocalDataPoolIF() {}; + + static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF; + static constexpr lp_id_t NO_POOL_ID = 0xffffffff; + + virtual object_id_t getObjectId() const = 0; + + /** Command queue for housekeeping messages. */ + virtual MessageQueueId_t getCommandQueue() const = 0; + + /** + * Is used by pool owner to initialize the pool map once + * The manager instance shall also be passed to this function. + * It can be used to subscribe for periodic packets for for updates. + */ + virtual ReturnValue_t initializeLocalDataPool( + LocalDataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) = 0; + + /** Can be used to get a handle to the local data pool manager. */ + virtual LocalDataPoolManager* getHkManagerHandle() = 0; + + /** + * Returns the minimum sampling frequency in milliseconds, which will + * usually be the period the pool owner performs its periodic operation. + * @return + */ + virtual uint32_t getPeriodicOperationFrequency() const = 0; + + /** + * This function is used by the pool manager to get a valid dataset + * from a SID + * @param sid Corresponding structure ID + * @return + */ + virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) = 0; + + /* These function can be implemented by pool owner, as they are required + * by the housekeeping message interface */ + virtual ReturnValue_t addDataSet(sid_t sid) { + return HasReturnvaluesIF::RETURN_FAILED; + }; + virtual ReturnValue_t removeDataSet(sid_t sid) { + return HasReturnvaluesIF::RETURN_FAILED; + }; + virtual ReturnValue_t changeCollectionInterval(sid_t sid, + float newIntervalSeconds) { + return HasReturnvaluesIF::RETURN_FAILED; + }; +}; + +#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ */ diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp new file mode 100644 index 00000000..c23f183b --- /dev/null +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -0,0 +1,427 @@ +#include "LocalDataPoolManager.h" +#include "LocalPoolDataSetBase.h" + +#include "../housekeeping/HousekeepingSetPacket.h" +#include "../housekeeping/AcceptsHkPacketsIF.h" +#include "../ipc/MutexFactory.h" +#include "../ipc/MutexHelper.h" +#include "../ipc/QueueFactory.h" +#include "../objectmanager/frameworkObjects.h" + +#include +#include + +object_id_t LocalDataPoolManager::defaultHkDestination = + objects::PUS_SERVICE_3_HOUSEKEEPING; + +LocalDataPoolManager::LocalDataPoolManager(HasLocalDataPoolIF* owner, + MessageQueueIF* queueToUse, bool appendValidityBuffer): + appendValidityBuffer(appendValidityBuffer) { + if(owner == nullptr) { + sif::error << "LocalDataPoolManager::LocalDataPoolManager: " + << "Invalid supplied owner!" << std::endl; + return; + } + this->owner = owner; + mutex = MutexFactory::instance()->createMutex(); + if(mutex == nullptr) { + sif::error << "LocalDataPoolManager::LocalDataPoolManager: " + << "Could not create mutex." << std::endl; + } + + hkQueue = queueToUse; +} + +LocalDataPoolManager::~LocalDataPoolManager() {} + +ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) { + if(queueToUse == nullptr) { + sif::error << "LocalDataPoolManager::initialize: " + << std::hex << "0x" << owner->getObjectId() << ". Supplied " + << "queue invalid!" << std::dec << std::endl; + } + hkQueue = queueToUse; + + ipcStore = objectManager->get(objects::IPC_STORE); + if(ipcStore == nullptr) { + sif::error << "LocalDataPoolManager::initialize: " + << std::hex << "0x" << owner->getObjectId() << ": Could not " + << "set IPC store." <get(defaultHkDestination); + if(hkPacketReceiver != nullptr) { + hkDestinationId = hkPacketReceiver->getHkQueue(); + } + else { + sif::error << "LocalDataPoolManager::LocalDataPoolManager: " + << "Default HK destination object is invalid!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + } + + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::initializeAfterTaskCreation( + uint8_t nonDiagInvlFactor) { + setNonDiagnosticIntervalFactor(nonDiagInvlFactor); + return initializeHousekeepingPoolEntriesOnce(); +} + +ReturnValue_t LocalDataPoolManager::initializeHousekeepingPoolEntriesOnce() { + if(not mapInitialized) { + ReturnValue_t result = owner->initializeLocalDataPool(localPoolMap, + *this); + if(result == HasReturnvaluesIF::RETURN_OK) { + mapInitialized = true; + } + return result; + } + sif::warning << "HousekeepingManager: The map should only be initialized " + << "once!" << std::endl; + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::performHkOperation() { + for(auto& receiver: hkReceiversMap) { + //HkReceiver* receiver = &hkReceiversIter.second; + + switch(receiver.reportingType) { + case(ReportingType::PERIODIC): { + if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { + // Periodic packets shall only be generated from datasets. + continue; + } + performPeriodicHkGeneration(receiver); + break; + } + case(ReportingType::UPDATE_SNAPSHOT): { + // check whether data has changed and send messages in case it has. + break; + } + default: + // This should never happen. + return HasReturnvaluesIF::RETURN_FAILED; + } + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(sid_t sid, + bool enableReporting, float collectionInterval, bool isDiagnostics, + object_id_t packetDestination) { + AcceptsHkPacketsIF* hkReceiverObject = + objectManager->get(packetDestination); + if(hkReceiverObject == nullptr) { + sif::error << "LocalDataPoolManager::subscribeForPeriodicPacket:" + << " Invalid receiver!"<< std::endl; + return HasReturnvaluesIF::RETURN_OK; + } + + struct HkReceiver hkReceiver; + hkReceiver.dataId.sid = sid; + hkReceiver.reportingType = ReportingType::PERIODIC; + hkReceiver.destinationQueue = hkReceiverObject->getHkQueue(); + + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + if(dataSet != nullptr) { + dataSet->setReportingEnabled(enableReporting); + dataSet->setDiagnostic(isDiagnostics); + dataSet->initializePeriodicHelper(collectionInterval, + owner->getPeriodicOperationFrequency(), isDiagnostics); + } + + hkReceiversMap.push_back(hkReceiver); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage( + CommandMessage* message) { + Command_t command = message->getCommand(); + sid_t sid = HousekeepingMessage::getSid(message); + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + switch(command) { + case(HousekeepingMessage::ENABLE_PERIODIC_DIAGNOSTICS_GENERATION): { + result = togglePeriodicGeneration(sid, true, true); + break; + } + + case(HousekeepingMessage::DISABLE_PERIODIC_DIAGNOSTICS_GENERATION): { + result = togglePeriodicGeneration(sid, false, true); + break; + } + + case(HousekeepingMessage::ENABLE_PERIODIC_HK_REPORT_GENERATION): { + result = togglePeriodicGeneration(sid, true, false); + break; + } + + case(HousekeepingMessage::DISABLE_PERIODIC_HK_REPORT_GENERATION): { + result = togglePeriodicGeneration(sid, false, false); + break; + } + + case(HousekeepingMessage::REPORT_DIAGNOSTICS_REPORT_STRUCTURES): + return generateSetStructurePacket(sid, true); + case(HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): + return generateSetStructurePacket(sid, false); + case(HousekeepingMessage::MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL): + case(HousekeepingMessage::MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL): { + float newCollIntvl = 0; + HousekeepingMessage::getCollectionIntervalModificationCommand(message, + &newCollIntvl); + if(command == HousekeepingMessage:: + MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL) { + result = changeCollectionInterval(sid, newCollIntvl, true); + } + else { + result = changeCollectionInterval(sid, newCollIntvl, false); + } + break; + } + + case(HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT): + case(HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT): { + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + if(command == HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT + and dataSet->isDiagnostics()) { + return WRONG_HK_PACKET_TYPE; + } + else if(command == HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT + and not dataSet->isDiagnostics()) { + return WRONG_HK_PACKET_TYPE; + } + return generateHousekeepingPacket(HousekeepingMessage::getSid(message), + dataSet, true); + } + + default: + return CommandMessageIF::UNKNOWN_COMMAND; + } + + CommandMessage reply; + if(result != HasReturnvaluesIF::RETURN_OK) { + HousekeepingMessage::setHkRequestFailureReply(&reply, sid, result); + } + else { + HousekeepingMessage::setHkRequestSuccessReply(&reply, sid); + } + hkQueue->sendMessage(hkDestinationId, &reply); + return result; +} + +ReturnValue_t LocalDataPoolManager::printPoolEntry( + lp_id_t localPoolId) { + auto poolIter = localPoolMap.find(localPoolId); + if (poolIter == localPoolMap.end()) { + sif::debug << "HousekeepingManager::fechPoolEntry:" + << " Pool entry not found." << std::endl; + return POOL_ENTRY_NOT_FOUND; + } + poolIter->second->print(); + return HasReturnvaluesIF::RETURN_OK; +} + +MutexIF* LocalDataPoolManager::getMutexHandle() { + return mutex; +} + +HasLocalDataPoolIF* LocalDataPoolManager::getOwner() { + return owner; +} + +ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid, + LocalPoolDataSetBase* dataSet, bool forDownlink, + MessageQueueId_t destination) { + if(dataSet == nullptr) { + // Configuration error. + sif::warning << "HousekeepingManager::generateHousekeepingPacket:" + << " Set ID not found or dataset not assigned!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + + store_address_t storeId; + HousekeepingPacketDownlink hkPacket(sid, dataSet); + size_t serializedSize = 0; + ReturnValue_t result = serializeHkPacketIntoStore(hkPacket, storeId, + forDownlink, &serializedSize); + if(result != HasReturnvaluesIF::RETURN_OK or serializedSize == 0) { + return result; + } + + // and now we set a HK message and send it the HK packet destination. + CommandMessage hkMessage; + if(dataSet->isDiagnostics()) { + HousekeepingMessage::setHkDiagnosticsReply(&hkMessage, sid, storeId); + } + else { + HousekeepingMessage::setHkReportReply(&hkMessage, sid, storeId); + } + + if(hkQueue == nullptr) { + return QUEUE_OR_DESTINATION_NOT_SET; + } + if(destination == MessageQueueIF::NO_QUEUE) { + if(hkDestinationId == MessageQueueIF::NO_QUEUE) { + // error, all destinations invalid + return HasReturnvaluesIF::RETURN_FAILED; + } + destination = hkDestinationId; + } + + return hkQueue->sendMessage(destination, &hkMessage); +} + +ReturnValue_t LocalDataPoolManager::serializeHkPacketIntoStore( + HousekeepingPacketDownlink& hkPacket, + store_address_t& storeId, bool forDownlink, + size_t* serializedSize) { + uint8_t* dataPtr = nullptr; + const size_t maxSize = hkPacket.getSerializedSize(); + ReturnValue_t result = ipcStore->getFreeElement(&storeId, + maxSize, &dataPtr); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + if(forDownlink) { + return hkPacket.serialize(&dataPtr, serializedSize, maxSize, + SerializeIF::Endianness::BIG); + } + return hkPacket.serialize(&dataPtr, serializedSize, maxSize, + SerializeIF::Endianness::MACHINE); +} + +void LocalDataPoolManager::setNonDiagnosticIntervalFactor( + uint8_t nonDiagInvlFactor) { + this->nonDiagnosticIntervalFactor = nonDiagInvlFactor; +} + +void LocalDataPoolManager::performPeriodicHkGeneration(HkReceiver& receiver) { + sid_t sid = receiver.dataId.sid; + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + if(not dataSet->getReportingEnabled()) { + return; + } + + if(dataSet->periodicHelper == nullptr) { + // Configuration error. + return; + } + + if(not dataSet->periodicHelper->checkOpNecessary()) { + return; + } + + ReturnValue_t result = generateHousekeepingPacket( + sid, dataSet, true); + if(result != HasReturnvaluesIF::RETURN_OK) { + // configuration error + sif::debug << "LocalDataPoolManager::performHkOperation:" + << "0x" << std::hex << std::setfill('0') << std::setw(8) + << owner->getObjectId() << " Error generating " + << "HK packet" << std::setfill(' ') << std::dec << std::endl; + } +} + + +ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid, + bool enable, bool isDiagnostics) { + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + if((dataSet->isDiagnostics() and not isDiagnostics) or + (not dataSet->isDiagnostics() and isDiagnostics)) { + return WRONG_HK_PACKET_TYPE; + } + + if((dataSet->getReportingEnabled() and enable) or + (not dataSet->getReportingEnabled() and not enable)) { + return REPORTING_STATUS_UNCHANGED; + } + + dataSet->setReportingEnabled(enable); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid, + float newCollectionInterval, bool isDiagnostics) { + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + bool targetIsDiagnostics = dataSet->isDiagnostics(); + if((targetIsDiagnostics and not isDiagnostics) or + (not targetIsDiagnostics and isDiagnostics)) { + return WRONG_HK_PACKET_TYPE; + } + + if(dataSet->periodicHelper == nullptr) { + // config error + return PERIODIC_HELPER_INVALID; + } + + dataSet->periodicHelper->changeCollectionInterval(newCollectionInterval); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid, + bool isDiagnostics) { + // Get and check dataset first. + LocalPoolDataSetBase* dataSet = dynamic_cast( + owner->getDataSetHandle(sid)); + if(dataSet == nullptr) { + sif::warning << "HousekeepingManager::generateHousekeepingPacket:" + << " Set ID not found" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + + + bool targetIsDiagnostics = dataSet->isDiagnostics(); + if((targetIsDiagnostics and not isDiagnostics) or + (not targetIsDiagnostics and isDiagnostics)) { + return WRONG_HK_PACKET_TYPE; + } + + bool valid = dataSet->isValid(); + bool reportingEnabled = dataSet->getReportingEnabled(); + float collectionInterval = + dataSet->periodicHelper->getCollectionIntervalInSeconds(); + + // Generate set packet which can be serialized. + HousekeepingSetPacket setPacket = HousekeepingSetPacket(sid, + reportingEnabled, valid, collectionInterval, dataSet); + size_t expectedSize = setPacket.getSerializedSize(); + uint8_t* storePtr = nullptr; + store_address_t storeId; + ReturnValue_t result = ipcStore->getFreeElement(&storeId, + expectedSize,&storePtr); + if(result != HasReturnvaluesIF::RETURN_OK) { + sif::error << "HousekeepingManager::generateHousekeepingPacket: " + << "Could not get free element from IPC store." << std::endl; + return result; + } + + // Serialize set packet into store. + size_t size = 0; + result = setPacket.serialize(&storePtr, &size, expectedSize, + SerializeIF::Endianness::BIG); + if(expectedSize != size) { + sif::error << "HousekeepingManager::generateSetStructurePacket: " + << "Expected size is not equal to serialized size" << std::endl; + } + + // Send structure reporting reply. + CommandMessage reply; + if(isDiagnostics) { + HousekeepingMessage::setDiagnosticsStuctureReportReply(&reply, + sid, storeId); + } + else { + HousekeepingMessage::setHkStuctureReportReply(&reply, + sid, storeId); + } + + hkQueue->reply(&reply); + return result; +} diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h new file mode 100644 index 00000000..779e3050 --- /dev/null +++ b/datapoollocal/LocalDataPoolManager.h @@ -0,0 +1,291 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ +#define FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ + +#include "HasLocalDataPoolIF.h" + +#include "../housekeeping/HousekeepingPacketDownlink.h" +#include "../housekeeping/HousekeepingMessage.h" +#include "../housekeeping/PeriodicHousekeepingHelper.h" +#include "../datapool/DataSetIF.h" +#include "../datapool/PoolEntry.h" +#include "../objectmanager/SystemObjectIF.h" +#include "../ipc/MutexIF.h" +#include "../ipc/CommandMessage.h" +#include "../ipc/MessageQueueIF.h" +#include "../ipc/MutexHelper.h" + +#include + +namespace Factory { +void setStaticFrameworkObjectIds(); +} + +class LocalDataSetBase; + + +/** + * @brief This class is the managing instance for the local data pool. + * @details + * The actual data pool structure is a member of this class. Any class which + * has a local data pool shall have this class as a member and implement + * the HasLocalDataPoolIF. + * + * Users of the data pool use the helper classes LocalDataSet, + * LocalPoolVariable and LocalPoolVector to access pool entries in + * a thread-safe and efficient way. + * + * The local data pools employ a blackboard logic: Only the most recent + * value is stored. The helper classes offer a read() and commit() interface + * through the PoolVariableIF which is used to read and update values. + * Each pool entry has a valid state too. + * @author R. Mueller + */ +class LocalDataPoolManager { + template + friend class LocalPoolVar; + template + friend class LocalPoolVector; + friend class LocalPoolDataSetBase; + friend void (Factory::setStaticFrameworkObjectIds)(); +public: + static constexpr uint8_t INTERFACE_ID = CLASS_ID::HOUSEKEEPING_MANAGER; + + static constexpr ReturnValue_t POOL_ENTRY_NOT_FOUND = MAKE_RETURN_CODE(0x00); + static constexpr ReturnValue_t POOL_ENTRY_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01); + + static constexpr ReturnValue_t QUEUE_OR_DESTINATION_NOT_SET = MAKE_RETURN_CODE(0x02); + + static constexpr ReturnValue_t WRONG_HK_PACKET_TYPE = MAKE_RETURN_CODE(0x03); + static constexpr ReturnValue_t REPORTING_STATUS_UNCHANGED = MAKE_RETURN_CODE(0x04); + static constexpr ReturnValue_t PERIODIC_HELPER_INVALID = MAKE_RETURN_CODE(0x05); + + /** + * This constructor is used by a class which wants to implement + * a personal local data pool. The queueToUse can be supplied if it + * is already known. + * + * initialize() has to be called in any case before using the object! + * @param owner + * @param queueToUse + * @param appendValidityBuffer + */ + LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQueueIF* queueToUse, + bool appendValidityBuffer = true); + virtual~ LocalDataPoolManager(); + + /** + * Assigns the queue to use. + * @param queueToUse + * @param nonDiagInvlFactor See #setNonDiagnosticIntervalFactor doc + * @return + */ + ReturnValue_t initialize(MessageQueueIF* queueToUse); + + /** + * Initializes the map by calling the map initialization function and + * setting the periodic factor for non-diagnostic packets. + * Don't forget to call this, otherwise the map will be invalid! + * @param nonDiagInvlFactor + * @return + */ + ReturnValue_t initializeAfterTaskCreation(uint8_t nonDiagInvlFactor = 5); + + /** + * This should be called in the periodic handler of the owner. + * It performs all the periodic functionalities of the data pool manager, + * for example generating periodic HK packets. + * @return + */ + ReturnValue_t performHkOperation(); + + /** + * @return + */ + ReturnValue_t subscribeForPeriodicPacket(sid_t sid, bool enableReporting, + float collectionInterval, bool isDiagnostics, + object_id_t packetDestination = defaultHkDestination); + + /** + * Non-Diagnostics packets usually have a lower minimum sampling frequency + * than diagnostic packets. + * A factor can be specified to determine the minimum sampling frequency + * for non-diagnostic packets. The minimum sampling frequency of the + * diagnostics packets,which is usually jusst the period of the + * performOperation calls, is multiplied with that factor. + * @param factor + */ + void setNonDiagnosticIntervalFactor(uint8_t nonDiagInvlFactor); + + + /** + * Generate a housekeeping packet with a given SID. + * @param sid + * @return + */ + ReturnValue_t generateHousekeepingPacket(sid_t sid, + LocalPoolDataSetBase* dataSet, bool forDownlink, + MessageQueueId_t destination = MessageQueueIF::NO_QUEUE); + + ReturnValue_t handleHousekeepingMessage(CommandMessage* message); + + /** + * This function is used to fill the local data pool map with pool + * entries. It should only be called once by the pool owner. + * @param localDataPoolMap + * @return + */ + ReturnValue_t initializeHousekeepingPoolEntriesOnce(); + + HasLocalDataPoolIF* getOwner(); + + ReturnValue_t printPoolEntry(lp_id_t localPoolId); + + /** + * Different types of housekeeping reporting are possible. + * 1. PERIODIC: + * HK packets are generated in fixed intervals and sent to + * destination. Fromat will be raw. + * 2. UPDATE_NOTIFICATION: + * Notification will be sent out if HK data has changed. + * 3. UPDATE_SNAPSHOT: + * HK packets are only generated if explicitely requested. + * Propably not necessary, just use multiple local data sets or + * shared datasets. + */ + enum class ReportingType: uint8_t { + //! Periodic generation of HK packets. + PERIODIC, + //! Housekeeping packet will be generated if values have changed. + UPDATE_HK, + //! Update notification will be sent out as message. + UPDATE_NOTIFICATION, + //! Notification will be sent out as message and a snapshot of the + //! current data will be generated. + UPDATE_SNAPSHOT, + }; + + /** + * Different data types are possible in the HK receiver map. + * For example, updates can be requested for full datasets or + * for single pool variables. Periodic reporting is only possible for + * data sets. + */ + enum class DataType: uint8_t { + LOCAL_POOL_VARIABLE, + DATA_SET + }; + + /* Copying forbidden */ + LocalDataPoolManager(const LocalDataPoolManager &) = delete; + LocalDataPoolManager operator=(const LocalDataPoolManager&) = delete; + +private: + LocalDataPool localPoolMap; + //! Every housekeeping data manager has a mutex to protect access + //! to it's data pool. + MutexIF* mutex = nullptr; + + /** The class which actually owns the manager (and its datapool). */ + HasLocalDataPoolIF* owner = nullptr; + + uint8_t nonDiagnosticIntervalFactor = 0; + + /** Default receiver for periodic HK packets */ + static object_id_t defaultHkDestination; + MessageQueueId_t hkDestinationId = MessageQueueIF::NO_QUEUE; + + /** The data pool manager will keep an internal map of HK receivers. */ + struct HkReceiver { + /** Object ID of receiver */ + object_id_t objectId = objects::NO_OBJECT; + + DataType dataType = DataType::DATA_SET; + union DataId { + DataId(): sid() {}; + sid_t sid; + lp_id_t localPoolId; + }; + DataId dataId; + + ReportingType reportingType = ReportingType::PERIODIC; + MessageQueueId_t destinationQueue = MessageQueueIF::NO_QUEUE; + }; + + /** This vector will contain the list of HK receivers. */ + using HkReceivers = std::vector; + + HkReceivers hkReceiversMap; + + /** This is the map holding the actual data. Should only be initialized + * once ! */ + bool mapInitialized = false; + /** This specifies whether a validity buffer is appended at the end + * of generated housekeeping packets. */ + bool appendValidityBuffer = true; + + /** + * @brief Queue used for communication, for example commands. + * Is also used to send messages. Can be set either in the constructor + * or in the initialize() function. + */ + MessageQueueIF* hkQueue = nullptr; + + /** Global IPC store is used to store all packets. */ + StorageManagerIF* ipcStore = nullptr; + /** + * Get the pointer to the mutex. Can be used to lock the data pool + * eternally. Use with care and don't forget to unlock locked mutexes! + * For now, only friend classes can accss this function. + * @return + */ + MutexIF* getMutexHandle(); + + /** + * Read a variable by supplying its local pool ID and assign the pool + * entry to the supplied PoolEntry pointer. The type of the pool entry + * is deduced automatically. This call is not thread-safe! + * For now, only friend classes like LocalPoolVar may access this + * function. + * @tparam T Type of the pool entry + * @param localPoolId Pool ID of the variable to read + * @param poolVar [out] Corresponding pool entry will be assigned to the + * supplied pointer. + * @return + */ + template ReturnValue_t fetchPoolEntry(lp_id_t localPoolId, + PoolEntry **poolEntry); + + ReturnValue_t serializeHkPacketIntoStore( + HousekeepingPacketDownlink& hkPacket, + store_address_t& storeId, bool forDownlink, size_t* serializedSize); + + void performPeriodicHkGeneration(HkReceiver& hkReceiver); + ReturnValue_t togglePeriodicGeneration(sid_t sid, bool enable, + bool isDiagnostics); + ReturnValue_t changeCollectionInterval(sid_t sid, + float newCollectionInterval, bool isDiagnostics); + ReturnValue_t generateSetStructurePacket(sid_t sid, bool isDiagnostics); +}; + + +template inline +ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, + PoolEntry **poolEntry) { + auto poolIter = localPoolMap.find(localPoolId); + if (poolIter == localPoolMap.end()) { + sif::warning << "HousekeepingManager::fechPoolEntry: Pool entry " + "not found." << std::endl; + return POOL_ENTRY_NOT_FOUND; + } + + *poolEntry = dynamic_cast< PoolEntry* >(poolIter->second); + if(*poolEntry == nullptr) { + sif::debug << "HousekeepingManager::fetchPoolEntry:" + " Pool entry not found." << std::endl; + return POOL_ENTRY_TYPE_CONFLICT; + } + return HasReturnvaluesIF::RETURN_OK; +} + + +#endif /* FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ */ diff --git a/datapoollocal/LocalDataSet.cpp b/datapoollocal/LocalDataSet.cpp new file mode 100644 index 00000000..394a9abe --- /dev/null +++ b/datapoollocal/LocalDataSet.cpp @@ -0,0 +1,22 @@ +#include "LocalDataSet.h" +#include "../datapoollocal/LocalDataPoolManager.h" +#include "../serialize/SerializeAdapter.h" + +#include +#include + +LocalDataSet::LocalDataSet(HasLocalDataPoolIF *hkOwner, uint32_t setId, + const size_t maxNumberOfVariables): + LocalPoolDataSetBase(hkOwner, setId, nullptr, maxNumberOfVariables), + poolVarList(maxNumberOfVariables) { + this->setContainer(poolVarList.data()); +} + +LocalDataSet::LocalDataSet(sid_t sid, const size_t maxNumberOfVariables): + LocalPoolDataSetBase(sid, nullptr, maxNumberOfVariables), + poolVarList(maxNumberOfVariables) { + this->setContainer(poolVarList.data()); +} + +LocalDataSet::~LocalDataSet() {} + diff --git a/datapoollocal/LocalDataSet.h b/datapoollocal/LocalDataSet.h new file mode 100644 index 00000000..0368f26d --- /dev/null +++ b/datapoollocal/LocalDataSet.h @@ -0,0 +1,21 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALDATASET_H_ +#define FSFW_DATAPOOLLOCAL_LOCALDATASET_H_ + +#include "LocalPoolDataSetBase.h" +#include + +class LocalDataSet: public LocalPoolDataSetBase { +public: + LocalDataSet(HasLocalDataPoolIF* hkOwner, uint32_t setId, + const size_t maxSize); + LocalDataSet(sid_t sid, const size_t maxSize); + virtual~ LocalDataSet(); + + //! Copying forbidden for now. + LocalDataSet(const LocalDataSet&) = delete; + LocalDataSet& operator=(const LocalDataSet&) = delete; +private: + std::vector poolVarList; +}; + +#endif /* FSFW_DATAPOOLLOCAL_LOCALDATASET_H_ */ diff --git a/datapoollocal/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp new file mode 100644 index 00000000..da0a86b2 --- /dev/null +++ b/datapoollocal/LocalPoolDataSetBase.cpp @@ -0,0 +1,259 @@ +#include "LocalPoolDataSetBase.h" +#include "../datapoollocal/LocalDataPoolManager.h" +#include "../housekeeping/PeriodicHousekeepingHelper.h" +#include "../serialize/SerializeAdapter.h" + +#include +#include + +LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner, + uint32_t setId, PoolVariableIF** registeredVariablesArray, + const size_t maxNumberOfVariables, bool noPeriodicHandling): + PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { + if(hkOwner == nullptr) { + // Configuration error. + sif::error << "LocalPoolDataSetBase::LocalPoolDataSetBase: Owner " + << "invalid!" << std::endl; + return; + } + hkManager = hkOwner->getHkManagerHandle(); + this->sid.objectId = hkOwner->getObjectId(); + this->sid.ownerSetId = setId; + + // Data creators get a periodic helper for periodic HK data generation. + if(not noPeriodicHandling) { + periodicHelper = new PeriodicHousekeepingHelper(this); + } +} + +LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, + PoolVariableIF** registeredVariablesArray, + const size_t maxNumberOfVariables): + PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { + HasLocalDataPoolIF* hkOwner = objectManager->get( + sid.objectId); + if(hkOwner == nullptr) { + // Configuration error. + sif::error << "LocalPoolDataSetBase::LocalPoolDataSetBase: Owner " + << "invalid!" << std::endl; + return; + } + hkManager = hkOwner->getHkManagerHandle(); + this->sid = sid; +} + +LocalPoolDataSetBase::~LocalPoolDataSetBase() { +} + +ReturnValue_t LocalPoolDataSetBase::lockDataPool(uint32_t timeoutMs) { + MutexIF* mutex = hkManager->getMutexHandle(); + return mutex->lockMutex(MutexIF::TimeoutType::WAITING, timeoutMs); +} + +ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer, + size_t *size, size_t maxSize, + SerializeIF::Endianness streamEndianness) const { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + uint8_t validityMaskSize = std::ceil(static_cast(fillCount)/8.0); + uint8_t validityMask[validityMaskSize]; + uint8_t validBufferIndex = 0; + uint8_t validBufferIndexBit = 0; + for (uint16_t count = 0; count < fillCount; count++) { + if(registeredVariables[count]->isValid()) { + // set validity buffer here. + this->bitSetter(validityMask + validBufferIndex, + validBufferIndexBit); + if(validBufferIndexBit == 7) { + validBufferIndex ++; + validBufferIndexBit = 0; + } + else { + validBufferIndexBit ++; + } + } + result = registeredVariables[count]->serialize(buffer, size, maxSize, + streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + // copy validity buffer to end + std::memcpy(*buffer, validityMask, validityMaskSize); + *size += validityMaskSize; + return result; +} + +ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer( + const uint8_t **buffer, size_t *size, + SerializeIF::Endianness streamEndianness) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t count = 0; count < fillCount; count++) { + result = registeredVariables[count]->deSerialize(buffer, size, + streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + uint8_t validBufferIndex = 0; + uint8_t validBufferIndexBit = 0; + // could be made more efficient but make it work first + for (uint16_t count = 0; count < fillCount; count++) { + // set validity buffer here. + bool nextVarValid = this->bitGetter(*buffer + + validBufferIndex, validBufferIndexBit); + registeredVariables[count]->setValid(nextVarValid); + + if(validBufferIndexBit == 7) { + validBufferIndex ++; + validBufferIndexBit = 0; + } + else { + validBufferIndexBit ++; + } + } + return result; +} +ReturnValue_t LocalPoolDataSetBase::unlockDataPool() { + MutexIF* mutex = hkManager->getMutexHandle(); + return mutex->unlockMutex(); +} + +ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t** buffer, + size_t* size, size_t maxSize,SerializeIF::Endianness streamEndianness, + bool serializeFillCount) const { + // Serialize as uint8_t + uint8_t fillCount = this->fillCount; + if(serializeFillCount) { + SerializeAdapter::serialize(&fillCount, buffer, size, maxSize, + streamEndianness); + } + for (uint16_t count = 0; count < fillCount; count++) { + lp_id_t currentPoolId = registeredVariables[count]->getDataPoolId(); + auto result = SerializeAdapter::serialize(¤tPoolId, buffer, + size, maxSize, streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK) { + sif::warning << "LocalDataSet::serializeLocalPoolIds: Serialization" + " error!" << std::endl; + return result; + } + } + return HasReturnvaluesIF::RETURN_OK; +} + + +uint8_t LocalPoolDataSetBase::getLocalPoolIdsSerializedSize( + bool serializeFillCount) const { + if(serializeFillCount) { + return fillCount * sizeof(lp_id_t) + sizeof(uint8_t); + } + else { + return fillCount * sizeof(lp_id_t); + } +} + +size_t LocalPoolDataSetBase::getSerializedSize() const { + if(withValidityBuffer) { + uint8_t validityMaskSize = std::ceil(static_cast(fillCount)/8.0); + return validityMaskSize + PoolDataSetBase::getSerializedSize(); + } + else { + return PoolDataSetBase::getSerializedSize(); + } +} + +void LocalPoolDataSetBase::setValidityBufferGeneration( + bool withValidityBuffer) { + this->withValidityBuffer = withValidityBuffer; +} + +ReturnValue_t LocalPoolDataSetBase::deSerialize(const uint8_t **buffer, + size_t *size, SerializeIF::Endianness streamEndianness) { + if(withValidityBuffer) { + return this->deSerializeWithValidityBuffer(buffer, size, + streamEndianness); + } + else { + return PoolDataSetBase::deSerialize(buffer, size, streamEndianness); + } +} + +ReturnValue_t LocalPoolDataSetBase::serialize(uint8_t **buffer, size_t *size, + size_t maxSize, SerializeIF::Endianness streamEndianness) const { + if(withValidityBuffer) { + return this->serializeWithValidityBuffer(buffer, size, + maxSize, streamEndianness); + } + else { + return PoolDataSetBase::serialize(buffer, size, maxSize, + streamEndianness); + } +} + +void LocalPoolDataSetBase::bitSetter(uint8_t* byte, uint8_t position) const { + if(position > 7) { + sif::debug << "Pool Raw Access: Bit setting invalid position" + << std::endl; + return; + } + uint8_t shiftNumber = position + (7 - 2 * position); + *byte |= 1 << shiftNumber; +} + +void LocalPoolDataSetBase::setDiagnostic(bool isDiagnostics) { + this->diagnostic = isDiagnostics; +} + +bool LocalPoolDataSetBase::isDiagnostics() const { + return diagnostic; +} + +void LocalPoolDataSetBase::setReportingEnabled(bool reportingEnabled) { + this->reportingEnabled = reportingEnabled; +} + +bool LocalPoolDataSetBase::getReportingEnabled() const { + return reportingEnabled; +} + +void LocalPoolDataSetBase::initializePeriodicHelper( + float collectionInterval, dur_millis_t minimumPeriodicInterval, + bool isDiagnostics, uint8_t nonDiagIntervalFactor) { + periodicHelper->initialize(collectionInterval, minimumPeriodicInterval, + isDiagnostics, nonDiagIntervalFactor); +} + +void LocalPoolDataSetBase::setChanged(bool changed) { + this->changed = changed; +} + +bool LocalPoolDataSetBase::isChanged() const { + return changed; +} + +sid_t LocalPoolDataSetBase::getSid() const { + return sid; +} + +bool LocalPoolDataSetBase::bitGetter(const uint8_t* byte, + uint8_t position) const { + if(position > 7) { + sif::debug << "Pool Raw Access: Bit setting invalid position" + << std::endl; + return false; + } + uint8_t shiftNumber = position + (7 - 2 * position); + return *byte & (1 << shiftNumber); +} + +bool LocalPoolDataSetBase::isValid() const { + return this->valid; +} + +void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) { + if(setEntriesRecursively) { + for(size_t idx = 0; idx < this->getFillCount(); idx++) { + registeredVariables[idx] -> setValid(valid); + } + } + this->valid = valid; +} diff --git a/datapoollocal/LocalPoolDataSetBase.h b/datapoollocal/LocalPoolDataSetBase.h new file mode 100644 index 00000000..d00af992 --- /dev/null +++ b/datapoollocal/LocalPoolDataSetBase.h @@ -0,0 +1,190 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_ + +#include "HasLocalDataPoolIF.h" +#include "../datapool/DataSetIF.h" +#include "../datapool/PoolDataSetBase.h" +#include "../serialize/SerializeIF.h" + +#include + +class LocalDataPoolManager; +class PeriodicHousekeepingHelper; + +/** + * @brief The LocalDataSet class manages a set of locally checked out + * variables for local data pools + * @details + * Extends the PoolDataSetBase class for local data pools by introducing + * a validity state, a flag to mark the set as changed, and various other + * functions to make it usable by the LocalDataPoolManager class. + * + * This class manages a list, where a set of local variables (or pool variables) + * are registered. They are checked-out (i.e. their values are looked + * up and copied) with the read call. After the user finishes working with the + * pool variables, he can write back all variable values to the pool with + * the commit call. The data set manages locking and freeing the local data + * pools, to ensure thread-safety. + * + * Pool variables can be added to the dataset by using the constructor + * argument of the pool variable or using the #registerVariable member function. + * + * An internal state manages usage of this class. Variables may only be + * registered before any read call is made, and the commit call can only happen + * after the read call. + * + * If pool variables are writable and not committed until destruction + * of the set, the DataSet class automatically sets the valid flag in the + * data pool to invalid (without) changing the variable's value. + * + * @ingroup data_pool + */ +class LocalPoolDataSetBase: public PoolDataSetBase { + friend class LocalDataPoolManager; + friend class PeriodicHousekeepingHelper; +public: + /** + * @brief Constructor for the creator of local pool data. + * @details + * This constructor also initializes the components required for + * periodic handling. + */ + LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner, + uint32_t setId, PoolVariableIF** registeredVariablesArray, + const size_t maxNumberOfVariables, bool noPeriodicHandling = false); + + /** + * @brief Constructor for users of local pool data. + * @details + * @param sid Unique identifier of dataset consisting of object ID and + * set ID. + * @param registeredVariablesArray + * @param maxNumberOfVariables + */ + LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray, + const size_t maxNumberOfVariables); + + /** + * @brief The destructor automatically manages writing the valid + * information of variables. + * @details + * In case the data set was read out, but not committed(indicated by state), + * the destructor parses all variables that are still registered to the set. + * For each, the valid flag in the data pool is set to "invalid". + */ + ~LocalPoolDataSetBase(); + + void setValidityBufferGeneration(bool withValidityBuffer); + + sid_t getSid() const; + + /** SerializeIF overrides */ + ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize, + SerializeIF::Endianness streamEndianness) const override; + ReturnValue_t deSerialize(const uint8_t** buffer, size_t *size, + SerializeIF::Endianness streamEndianness) override; + size_t getSerializedSize() const override; + + /** + * Special version of the serilization function which appends a + * validity buffer at the end. Each bit of this validity buffer + * denotes whether the container data set entries are valid from left + * to right, MSB first. (length = ceil(N/8), N = number of pool variables) + * @param buffer + * @param size + * @param maxSize + * @param bigEndian + * @param withValidityBuffer + * @return + */ + ReturnValue_t serializeWithValidityBuffer(uint8_t** buffer, + size_t* size, size_t maxSize, + SerializeIF::Endianness streamEndianness) const; + ReturnValue_t deSerializeWithValidityBuffer(const uint8_t** buffer, + size_t *size, SerializeIF::Endianness streamEndianness); + ReturnValue_t serializeLocalPoolIds(uint8_t** buffer, + size_t* size, size_t maxSize, + SerializeIF::Endianness streamEndianness, + bool serializeFillCount = true) const; + uint8_t getLocalPoolIdsSerializedSize(bool serializeFillCount = true) const; + + /** + * Set the dataset valid or invalid + * @param setEntriesRecursively + * If this is true, all contained datasets will also be set recursively. + */ + void setValidity(bool valid, bool setEntriesRecursively); + bool isValid() const override; + + void setChanged(bool changed); + bool isChanged() const; + +protected: + sid_t sid; + + bool diagnostic = false; + void setDiagnostic(bool diagnostics); + bool isDiagnostics() const; + + /** + * Used for periodic generation. + */ + bool reportingEnabled = false; + void setReportingEnabled(bool enabled); + bool getReportingEnabled() const; + + void initializePeriodicHelper(float collectionInterval, + dur_millis_t minimumPeriodicInterval, + bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5); + + /** + * If the valid state of a dataset is always relevant to the whole + * data set we can use this flag. + */ + bool valid = false; + + /** + * Can be used to mark the dataset as changed, which is used + * by the LocalDataPoolManager to send out update messages. + */ + bool changed = false; + + /** + * Specify whether the validity buffer is serialized too when serializing + * or deserializing the packet. Each bit of the validity buffer will + * contain the validity state of the pool variables from left to right. + * The size of validity buffer thus will be ceil(N / 8) with N = number of + * pool variables. + */ + bool withValidityBuffer = true; + + /** + * @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. + */ + ReturnValue_t lockDataPool(uint32_t timeoutMs) override; + /** + * @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. + */ + ReturnValue_t unlockDataPool() override; + + LocalDataPoolManager* hkManager; + + /** + * Set n-th bit of a byte, with n being the position from 0 + * (most significant bit) to 7 (least significant bit) + */ + void bitSetter(uint8_t* byte, uint8_t position) const; + bool bitGetter(const uint8_t* byte, uint8_t position) const; + + PeriodicHousekeepingHelper* periodicHelper = nullptr; + +}; + + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_ */ diff --git a/datapoollocal/LocalPoolVariable.h b/datapoollocal/LocalPoolVariable.h new file mode 100644 index 00000000..ec5c8cd1 --- /dev/null +++ b/datapoollocal/LocalPoolVariable.h @@ -0,0 +1,177 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ + +#include "HasLocalDataPoolIF.h" +#include "LocalDataPoolManager.h" + +#include "../datapool/PoolVariableIF.h" +#include "../datapool/DataSetIF.h" +#include "../objectmanager/ObjectManagerIF.h" +#include "../serialize/SerializeAdapter.h" + +/** + * @brief Local Pool Variable class which is used to access the local pools. + * @details + * This class is not stored in the map. Instead, it is used to access + * the pool entries by using a pointer to the map storing the pool + * entries. It can also be used to organize these pool entries into data sets. + * + * @tparam T The template parameter sets the type of the variable. Currently, + * all plain data types are supported, but in principle any type is possible. + * @ingroup data_pool + */ +template +class LocalPoolVar: public PoolVariableIF, HasReturnvaluesIF { +public: + //! Default ctor is forbidden. + LocalPoolVar() = delete; + + /** + * This constructor is used by the data creators to have pool variable + * instances which can also be stored in datasets. + * + * It does not fetch the current value from the data pool, which + * has to be done by calling the read() operation. + * Datasets can be used to access multiple local pool entries in an + * efficient way. A pointer to a dataset can be passed to register + * the pool variable in that dataset directly. + * @param poolId ID of the local pool entry. + * @param hkOwner Pointer of the owner. This will generally be the calling + * class itself which passes "this". + * @param dataSet The data set in which the variable shall register itself. + * If nullptr, the variable is not registered. + * @param setReadWriteMode Specify the read-write mode of the pool variable. + */ + LocalPoolVar(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, + DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + + /** + * This constructor is used by data users like controllers to have + * access to the local pool variables of data creators by supplying + * the respective creator object ID. + * + * It does not fetch the current value from the data pool, which + * has to be done by calling the read() operation. + * Datasets can be used to access multiple local pool entries in an + * efficient way. A pointer to a dataset can be passed to register + * the pool variable in that dataset directly. + * @param poolId ID of the local pool entry. + * @param hkOwner object ID of the pool owner. + * @param dataSet The data set in which the variable shall register itself. + * If nullptr, the variable is not registered. + * @param setReadWriteMode Specify the read-write mode of the pool variable. + * + */ + LocalPoolVar(lp_id_t poolId, object_id_t poolOwner, + DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + + virtual~ LocalPoolVar() {}; + + /** + * @brief This is the local copy of the data pool entry. + * @details The user can work on this attribute + * just like he would on a simple local variable. + */ + T value = 0; + + pool_rwm_t getReadWriteMode() const override; + + lp_id_t getDataPoolId() const override; + void setDataPoolId(lp_id_t poolId); + + bool isValid() const override; + void setValid(bool validity) override; + uint8_t getValid() const; + + ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize, + SerializeIF::Endianness streamEndianness) const override; + virtual size_t getSerializedSize() const override; + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) override; + + /** + * @brief This is a call to read the array's values + * from the global data pool. + * @details + * When executed, this operation tries to fetch the pool entry with matching + * data pool id from the data pool and copies all array values and the valid + * information to its local attributes. + * In case of a failure (wrong type, size or pool id not found), the + * variable is set to zero and invalid. + * The read call is protected with a lock. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + * + */ + ReturnValue_t read(dur_millis_t lockTimeout = MutexIF::BLOCKING) override; + /** + * @brief The commit call copies the array values back 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 local valid flag is written back as well. + * The read call is protected with a lock. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + */ + ReturnValue_t commit(dur_millis_t lockTimeout = MutexIF::BLOCKING) override; + + + LocalPoolVar &operator=(T newValue); +protected: + /** + * @brief Like #read, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t readWithoutLock() override; + /** + * @brief Like #commit, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t commitWithoutLock() override; + + // std::ostream is the type for object std::cout + template + friend std::ostream& operator<< (std::ostream &out, + const LocalPoolVar &var); + +private: + //! @brief Pool ID of pool entry inside the used local pool. + lp_id_t localPoolId = PoolVariableIF::NO_PARAMETER; + //! @brief Read-write mode of the pool variable + pool_rwm_t readWriteMode = pool_rwm_t::VAR_READ_WRITE; + //! @brief Specifies whether the entry is valid or invalid. + bool valid = false; + + //! Pointer to the class which manages the HK pool. + LocalDataPoolManager* hkManager; +}; + +#include "LocalPoolVariable.tpp" + +template +using lp_var_t = LocalPoolVar; + +using lp_bool_t = LocalPoolVar; +using lp_uint8_t = LocalPoolVar; +using lp_uint16_t = LocalPoolVar; +using lp_uint32_t = LocalPoolVar; +using lp_uint64_t = LocalPoolVar; +using lp_int8_t = LocalPoolVar; +using lp_int16_t = LocalPoolVar; +using lp_int32_t = LocalPoolVar; +using lp_int64_t = LocalPoolVar; +using lp_float_t = LocalPoolVar; +using lp_double_t = LocalPoolVar; + + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ */ diff --git a/datapoollocal/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp new file mode 100644 index 00000000..b0bdd7b9 --- /dev/null +++ b/datapoollocal/LocalPoolVariable.tpp @@ -0,0 +1,169 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_ + +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ +#error Include LocalPoolVariable.h before LocalPoolVariable.tpp! +#endif + +template +inline LocalPoolVar::LocalPoolVar(lp_id_t poolId, + HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, + pool_rwm_t setReadWriteMode): + localPoolId(poolId), readWriteMode(setReadWriteMode) { + if(poolId == PoolVariableIF::NO_PARAMETER) { + sif::warning << "LocalPoolVar::LocalPoolVar: 0 passed as pool ID, " + << "which is the NO_PARAMETER value!" << std::endl; + } + if(hkOwner == nullptr) { + sif::error << "LocalPoolVar::LocalPoolVar: The supplied pool " + << "owner is a invalid!" << std::endl; + return; + } + hkManager = hkOwner->getHkManagerHandle(); + if(dataSet != nullptr) { + dataSet->registerVariable(this); + } +} + +template +inline LocalPoolVar::LocalPoolVar(lp_id_t poolId, object_id_t poolOwner, + DataSetIF *dataSet, pool_rwm_t setReadWriteMode): + localPoolId(poolId), readWriteMode(setReadWriteMode) { + if(poolId == PoolVariableIF::NO_PARAMETER) { + sif::warning << "LocalPoolVar::LocalPoolVar: 0 passed as pool ID, " + << "which is the NO_PARAMETER value!" << std::endl; + } + HasLocalDataPoolIF* hkOwner = + objectManager->get(poolOwner); + if(hkOwner == nullptr) { + sif::error << "LocalPoolVariable: The supplied pool owner did not " + << "implement the correct interface " + << "HasLocalDataPoolIF!" << std::endl; + return; + } + hkManager = hkOwner->getHkManagerHandle(); + if(dataSet != nullptr) { + dataSet->registerVariable(this); + } +} + +template +inline ReturnValue_t LocalPoolVar::read(dur_millis_t lockTimeout) { + MutexHelper(hkManager->getMutexHandle(), MutexIF::TimeoutType::WAITING, + lockTimeout); + return readWithoutLock(); +} + +template +inline ReturnValue_t LocalPoolVar::readWithoutLock() { + if(readWriteMode == pool_rwm_t::VAR_WRITE) { + sif::debug << "LocalPoolVar: Invalid read write " + "mode for read() call." << std::endl; + return PoolVariableIF::INVALID_READ_WRITE_MODE; + } + + PoolEntry* poolEntry = nullptr; + ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry); + if(result != RETURN_OK and poolEntry != nullptr) { + sif::error << "PoolVector: Read of local pool variable of object " + "0x" << std::hex << std::setw(8) << std::setfill('0') << + hkManager->getOwner() << " and lp ID 0x" << localPoolId << + std::dec << " failed.\n" << std::flush; + return result; + } + this->value = *(poolEntry->address); + this->valid = poolEntry->valid; + return RETURN_OK; +} + +template +inline ReturnValue_t LocalPoolVar::commit(dur_millis_t lockTimeout) { + MutexHelper(hkManager->getMutexHandle(), MutexIF::TimeoutType::WAITING, + lockTimeout); + return commitWithoutLock(); +} + +template +inline ReturnValue_t LocalPoolVar::commitWithoutLock() { + if(readWriteMode == pool_rwm_t::VAR_READ) { + sif::debug << "LocalPoolVar: Invalid read write " + "mode for commit() call." << std::endl; + return PoolVariableIF::INVALID_READ_WRITE_MODE; + } + PoolEntry* poolEntry = nullptr; + ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry); + if(result != RETURN_OK) { + sif::error << "PoolVector: Read of local pool variable of object " + "0x" << std::hex << std::setw(8) << std::setfill('0') << + hkManager->getOwner() << " and lp ID 0x" << localPoolId << + std::dec << " failed.\n" << std::flush; + return result; + } + *(poolEntry->address) = this->value; + poolEntry->valid = this->valid; + return RETURN_OK; +} + +template +inline LocalPoolVar & LocalPoolVar::operator =(T newValue) { + value = newValue; + return *this; +} + + +template +inline pool_rwm_t LocalPoolVar::getReadWriteMode() const { + return readWriteMode; +} + +template +inline lp_id_t LocalPoolVar::getDataPoolId() const { + return localPoolId; +} + +template +inline void LocalPoolVar::setDataPoolId(lp_id_t poolId) { + this->localPoolId = poolId; +} + +template +inline bool LocalPoolVar::isValid() const { + return valid; +} + +template +inline void LocalPoolVar::setValid(bool validity) { + this->valid = validity; +} + +template +inline uint8_t LocalPoolVar::getValid() const { + return valid; +} + +template +inline ReturnValue_t LocalPoolVar::serialize(uint8_t** buffer, size_t* size, + const size_t max_size, SerializeIF::Endianness streamEndianness) const { + return SerializeAdapter::serialize(&value, + buffer, size ,max_size, streamEndianness); +} + +template +inline size_t LocalPoolVar::getSerializedSize() const { + return SerializeAdapter::getSerializedSize(&value); +} + +template +inline ReturnValue_t LocalPoolVar::deSerialize(const uint8_t** buffer, + size_t* size, SerializeIF::Endianness streamEndianness) { + return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness); +} + +template +inline std::ostream& operator<< (std::ostream &out, + const LocalPoolVar &var) { + out << var.value; + return out; +} + +#endif diff --git a/datapoollocal/LocalPoolVector.h b/datapoollocal/LocalPoolVector.h new file mode 100644 index 00000000..57c4b90b --- /dev/null +++ b/datapoollocal/LocalPoolVector.h @@ -0,0 +1,202 @@ +#ifndef FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#define FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ + +#include "../datapool/DataSetIF.h" +#include "../datapool/PoolEntry.h" +#include "../datapool/PoolVariableIF.h" +#include "../datapoollocal/LocalDataPoolManager.h" +#include "../serialize/SerializeAdapter.h" +#include "../serviceinterface/ServiceInterfaceStream.h" + + +/** + * @brief This is the access class for array-type data pool entries. + * @details + * To ensure safe usage of the data pool, operation is not done directly on the + * data pool entries, but on local copies. This class provides simple type- + * and length-safe access to vector-style data pool entries (i.e. entries with + * length > 1). The class can be instantiated as read-write and read only. + * + * It provides a commit-and-roll-back semantic, which means that no array + * entry in the data pool is changed until the commit call is executed. + * There are two template parameters: + * @tparam T + * This template parameter specifies the data type of an array entry. Currently, + * all plain data types are supported, but in principle any type is possible. + * @tparam vector_size + * This template parameter specifies the vector size of this entry. Using a + * template parameter for this is not perfect, but avoids + * dynamic memory allocation. + * @ingroup data_pool + */ +template +class LocalPoolVector: public PoolVariableIF, public HasReturnvaluesIF { +public: + LocalPoolVector() = delete; + /** + * This constructor is used by the data creators to have pool variable + * instances which can also be stored in datasets. + * It does not fetch the current value from the data pool. This is performed + * by the read() operation (which is not thread-safe). + * Datasets can be used to access local pool entires in a thread-safe way. + * @param poolId ID of the local pool entry. + * @param hkOwner Pointer of the owner. This will generally be the calling + * class itself which passes "this". + * @param setReadWriteMode Specify the read-write mode of the pool variable. + * @param dataSet The data set in which the variable shall register itself. + * If nullptr, the variable is not registered. + */ + LocalPoolVector(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, + DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE + ); + + /** + * This constructor is used by data users like controllers to have + * access to the local pool variables of data creators by supplying + * the respective creator object ID. + * It does not fetch the current value from the data pool. This is performed + * by the read() operation (which is not thread-safe). + * Datasets can be used to access local pool entires in a thread-safe way. + * @param poolId ID of the local pool entry. + * @param hkOwner Pointer of the owner. This will generally be the calling + * class itself which passes "this". + * @param setReadWriteMode Specify the read-write mode of the pool variable. + * @param dataSet The data set in which the variable shall register itself. + * If nullptr, the variable is not registered. + */ + LocalPoolVector(lp_id_t poolId, object_id_t poolOwner, + DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE + ); + + /** + * @brief This is the local copy of the data pool entry. + * @details + * The user can work on this attribute just like he would on a local + * array of this type. + */ + T value[vectorSize]; + /** + * @brief The classes destructor is empty. + * @details If commit() was not called, the local value is + * discarded and not written back to the data pool. + */ + ~LocalPoolVector() {}; + /** + * @brief The operation returns the number of array entries + * in this variable. + */ + uint8_t getSize() { + return vectorSize; + } + + uint32_t getDataPoolId() const override; + /** + * @brief This operation sets the data pool ID of the variable. + * @details + * The method is necessary to set id's of data pool member variables + * with bad initialization. + */ + void setDataPoolId(uint32_t poolId); + + /** + * This method returns if the variable is write-only, read-write or read-only. + */ + pool_rwm_t getReadWriteMode() const; + + /** + * @brief With this call, the valid information of the variable is returned. + */ + bool isValid() const override; + void setValid(bool valid) override; + uint8_t getValid() const; + + T& operator [](int i); + const T &operator [](int i) const; + + virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, + const size_t maxSize, + SerializeIF::Endianness streamEndiannes) const override; + virtual size_t getSerializedSize() const override; + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) override; + + /** + * @brief This is a call to read the array's values + * from the global data pool. + * @details + * When executed, this operation tries to fetch the pool entry with matching + * data pool id from the data pool and copies all array values and the valid + * information to its local attributes. + * In case of a failure (wrong type, size or pool id not found), the + * variable is set to zero and invalid. + * The read call is protected with a lock. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + */ + ReturnValue_t read(uint32_t lockTimeout = MutexIF::BLOCKING) override; + /** + * @brief The commit call copies the array values back 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 local valid flag is written back as well. + * The read call is protected with a lock. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + */ + ReturnValue_t commit(uint32_t lockTimeout = MutexIF::BLOCKING) override; + +protected: + /** + * @brief Like #read, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t readWithoutLock() override; + /** + * @brief Like #commit, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t commitWithoutLock() override; + +private: + /** + * @brief To access the correct data pool entry on read and commit calls, + * the data pool id is stored. + */ + uint32_t localPoolId; + /** + * @brief The valid information as it was stored in the data pool + * is copied to this attribute. + */ + bool valid; + /** + * @brief The information whether the class is read-write or + * read-only is stored here. + */ + ReadWriteMode_t readWriteMode; + //! @brief Pointer to the class which manages the HK pool. + LocalDataPoolManager* hkManager; + + // std::ostream is the type for object std::cout + template + friend std::ostream& operator<< (std::ostream &out, + const LocalPoolVector &var); + + +}; + +#include "LocalPoolVector.tpp" + +template +using lp_vec_t = LocalPoolVector; + +#endif /* FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ */ diff --git a/datapoollocal/LocalPoolVector.tpp b/datapoollocal/LocalPoolVector.tpp new file mode 100644 index 00000000..2aa6fbb5 --- /dev/null +++ b/datapoollocal/LocalPoolVector.tpp @@ -0,0 +1,209 @@ +#ifndef FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ +#define FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ + +#ifndef FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#error Include LocalPoolVector.h before LocalPoolVector.tpp! +#endif + +template +inline LocalPoolVector::LocalPoolVector(lp_id_t poolId, + HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, + pool_rwm_t setReadWriteMode): + localPoolId(poolId), valid(false), readWriteMode(setReadWriteMode) { + if(poolId == PoolVariableIF::NO_PARAMETER) { + sif::warning << "LocalPoolVector: PoolVariableIF::NO_PARAMETER passed " + << "as pool ID, which is the NO_PARAMETER value!" << std::endl; + } + std::memset(this->value, 0, vectorSize * sizeof(T)); + hkManager = hkOwner->getHkManagerHandle(); + if (dataSet != nullptr) { + dataSet->registerVariable(this); + } +} + +template +inline LocalPoolVector::LocalPoolVector(lp_id_t poolId, + object_id_t poolOwner, DataSetIF *dataSet, pool_rwm_t setReadWriteMode): + readWriteMode(setReadWriteMode) { + if(poolId == PoolVariableIF::NO_PARAMETER) { + sif::warning << "LocalPoolVector: PoolVariableIF::NO_PARAMETER passed " + << "as pool ID, which is the NO_PARAMETER value!" << std::endl; + } + HasLocalDataPoolIF* hkOwner = + objectManager->get(poolOwner); + if(hkOwner == nullptr) { + sif::error << "LocalPoolVariable: The supplied pool owner did not " + << "implement the correct interface HasHkPoolParametersIF!" + << std::endl; + return; + } + hkManager = hkOwner->getHkManagerHandle(); + if(dataSet != nullptr) { + dataSet->registerVariable(this); + } +} + +template +inline ReturnValue_t LocalPoolVector::read(uint32_t lockTimeout) { + MutexHelper(hkManager->getMutexHandle(), MutexIF::TimeoutType::WAITING, + lockTimeout); + return readWithoutLock(); +} +template +inline ReturnValue_t LocalPoolVector::readWithoutLock() { + if(readWriteMode == pool_rwm_t::VAR_WRITE) { + sif::debug << "LocalPoolVar: Invalid read write " + "mode for read() call." << std::endl; + return PoolVariableIF::INVALID_READ_WRITE_MODE; + } + + PoolEntry* poolEntry = nullptr; + ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry); + memset(this->value, 0, vectorSize * sizeof(T)); + + if(result != RETURN_OK) { + sif::error << "PoolVector: Read of local pool variable of object " + "0x" << std::hex << std::setw(8) << std::setfill('0') << + hkManager->getOwner() << "and lp ID 0x" << localPoolId << + std::dec << " failed." << std::endl; + return result; + } + std::memcpy(this->value, poolEntry->address, poolEntry->getByteSize()); + this->valid = poolEntry->valid; + return RETURN_OK; +} + +template +inline ReturnValue_t LocalPoolVector::commit( + uint32_t lockTimeout) { + MutexHelper(hkManager->getMutexHandle(), MutexIF::TimeoutType::WAITING, + lockTimeout); + return commitWithoutLock(); +} + +template +inline ReturnValue_t LocalPoolVector::commitWithoutLock() { + if(readWriteMode == pool_rwm_t::VAR_READ) { + sif::debug << "LocalPoolVar: Invalid read write " + "mode for commit() call." << std::endl; + return PoolVariableIF::INVALID_READ_WRITE_MODE; + } + PoolEntry* poolEntry = nullptr; + ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry); + if(result != RETURN_OK) { + sif::error << "PoolVector: Read of local pool variable of object " + "0x" << std::hex << std::setw(8) << std::setfill('0') << + hkManager->getOwner() << " and lp ID 0x" << localPoolId << + std::dec << " failed.\n" << std::flush; + return result; + } + std::memcpy(poolEntry->address, this->value, poolEntry->getByteSize()); + poolEntry->valid = this->valid; + return RETURN_OK; +} + +template +inline T& LocalPoolVector::operator [](int i) { + if(i <= vectorSize) { + return value[i]; + } + // If this happens, I have to set some value. I consider this + // a configuration error, but I wont exit here. + sif::error << "LocalPoolVector: Invalid index. Setting or returning" + " last value!" << std::endl; + return value[i]; +} + +template +inline const T& LocalPoolVector::operator [](int i) const { + if(i <= vectorSize) { + return value[i]; + } + // If this happens, I have to set some value. I consider this + // a configuration error, but I wont exit here. + sif::error << "LocalPoolVector: Invalid index. Setting or returning" + " last value!" << std::endl; + return value[i]; +} + +template +inline ReturnValue_t LocalPoolVector::serialize(uint8_t** buffer, + size_t* size, size_t maxSize, + SerializeIF::Endianness streamEndianness) const { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t i = 0; i < vectorSize; i++) { + result = SerializeAdapter::serialize(&(value[i]), buffer, size, + maxSize, streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + break; + } + } + return result; +} + +template +inline size_t LocalPoolVector::getSerializedSize() const { + return vectorSize * SerializeAdapter::getSerializedSize(value); +} + +template +inline ReturnValue_t LocalPoolVector::deSerialize( + const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t i = 0; i < vectorSize; i++) { + result = SerializeAdapter::deSerialize(&(value[i]), buffer, size, + streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + break; + } + } + return result; +} + +template +inline pool_rwm_t LocalPoolVector::getReadWriteMode() const { + return this->readWriteMode; +} + + +template +inline uint32_t LocalPoolVector::getDataPoolId() const { + return localPoolId; +} + +template +inline void LocalPoolVector::setDataPoolId(uint32_t poolId) { + this->localPoolId = poolId; +} + +template +inline void LocalPoolVector::setValid(bool valid) { + this->valid = valid; +} + +template +inline uint8_t LocalPoolVector::getValid() const { + return valid; +} + +template +inline bool LocalPoolVector::isValid() const { + return valid; +} + +template +inline std::ostream& operator<< (std::ostream &out, + const LocalPoolVector &var) { + out << "Vector: ["; + for(int i = 0;i < vectorSize; i++) { + out << var.value[i]; + if(i < vectorSize - 1) { + out << ", "; + } + } + out << "]"; + return out; +} + +#endif diff --git a/datapoollocal/SharedLocalDataSet.cpp b/datapoollocal/SharedLocalDataSet.cpp new file mode 100644 index 00000000..dd1bdcc4 --- /dev/null +++ b/datapoollocal/SharedLocalDataSet.cpp @@ -0,0 +1,16 @@ +#include "SharedLocalDataSet.h" + +SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, sid_t sid, + const size_t maxSize): SystemObject(objectId), + LocalPoolDataSetBase(sid, nullptr, maxSize) { + this->setContainer(poolVarVector.data()); + datasetLock = MutexFactory::instance()->createMutex(); +} + +ReturnValue_t SharedLocalDataSet::lockDataset(dur_millis_t mutexTimeout) { + return datasetLock->lockMutex(MutexIF::TimeoutType::WAITING, mutexTimeout); +} + +ReturnValue_t SharedLocalDataSet::unlockDataset() { + return datasetLock->unlockMutex(); +} diff --git a/datapoollocal/SharedLocalDataSet.h b/datapoollocal/SharedLocalDataSet.h new file mode 100644 index 00000000..2e5a76fa --- /dev/null +++ b/datapoollocal/SharedLocalDataSet.h @@ -0,0 +1,24 @@ +#ifndef FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_ +#define FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_ + +#include "LocalPoolDataSetBase.h" +#include "../datapool/SharedDataSetIF.h" +#include "../objectmanager/SystemObject.h" +#include + +class SharedLocalDataSet: public SystemObject, + public LocalPoolDataSetBase, + public SharedDataSetIF { +public: + SharedLocalDataSet(object_id_t objectId, sid_t sid, + const size_t maxSize); + ReturnValue_t lockDataset(dur_millis_t mutexTimeout) override; + ReturnValue_t unlockDataset() override; +private: + + MutexIF* datasetLock = nullptr; + std::vector poolVarVector; +}; + + +#endif /* FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_ */ diff --git a/datapoollocal/StaticLocalDataSet.h b/datapoollocal/StaticLocalDataSet.h new file mode 100644 index 00000000..a637e360 --- /dev/null +++ b/datapoollocal/StaticLocalDataSet.h @@ -0,0 +1,50 @@ +#ifndef FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_ +#define FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_ + +#include "LocalPoolDataSetBase.h" +#include "../objectmanager/SystemObjectIF.h" +#include + +/** + * @brief This local dataset type is created on the stack. + * @details + * This will is the primary data structure to organize pool variables into + * sets which can be accessed via the housekeeping service interface or + * which can be sent to other software objects. + * + * It is recommended to read the documentation of the LocalPoolDataSetBase + * class for more information on how this class works and how to use it. + * @tparam capacity Capacity of the static dataset, which is usually known + * beforehand. + */ +template +class StaticLocalDataSet: public LocalPoolDataSetBase { +public: + /** + * Constructor used by data owner and creator like device handlers. + * This constructor also initialized the components required for + * periodic handling. + * @param hkOwner + * @param setId + */ + StaticLocalDataSet(HasLocalDataPoolIF* hkOwner, + uint32_t setId): LocalPoolDataSetBase(hkOwner, setId, nullptr, + NUM_VARIABLES) { + this->setContainer(poolVarList.data()); + } + + /** + * Constructor used by data users like controllers. + * @param hkOwner + * @param setId + */ + StaticLocalDataSet(sid_t sid): LocalPoolDataSetBase(sid, nullptr, + NUM_VARIABLES) { + this->setContainer(poolVarList.data()); + } + +private: + std::array poolVarList; +}; + +#endif /* FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_ */ diff --git a/housekeeping/AcceptsHkPacketsIF.h b/housekeeping/AcceptsHkPacketsIF.h new file mode 100644 index 00000000..b3ba6e7c --- /dev/null +++ b/housekeeping/AcceptsHkPacketsIF.h @@ -0,0 +1,11 @@ +#ifndef FRAMEWORK_HOUSEKEEPING_ACCEPTSHKPACKETSIF_H_ +#define FRAMEWORK_HOUSEKEEPING_ACCEPTSHKPACKETSIF_H_ +#include "../ipc/MessageQueueMessageIF.h" + +class AcceptsHkPacketsIF { +public: + virtual~ AcceptsHkPacketsIF() {}; + virtual MessageQueueId_t getHkQueue() const = 0; +}; + +#endif /* FRAMEWORK_HOUSEKEEPING_ACCEPTSHKPACKETSIF_H_ */ diff --git a/housekeeping/HousekeepingMessage.cpp b/housekeeping/HousekeepingMessage.cpp new file mode 100644 index 00000000..d2ab546b --- /dev/null +++ b/housekeeping/HousekeepingMessage.cpp @@ -0,0 +1,162 @@ +#include +#include "HousekeepingMessage.h" +#include + +HousekeepingMessage::~HousekeepingMessage() {} + +void HousekeepingMessage::setHkReportReply(CommandMessage* message, sid_t sid, + store_address_t storeId) { + message->setCommand(HK_REPORT); + message->setMessageSize(HK_MESSAGE_SIZE); + setSid(message, sid); + message->setParameter3(storeId.raw); +} + +void HousekeepingMessage::setHkDiagnosticsReply(CommandMessage* message, + sid_t sid, store_address_t storeId) { + message->setCommand(DIAGNOSTICS_REPORT); + message->setMessageSize(HK_MESSAGE_SIZE); + setSid(message, sid); + message->setParameter3(storeId.raw); +} + +sid_t HousekeepingMessage::getHkDataReply(const CommandMessage *message, + store_address_t *storeIdToSet) { + if(storeIdToSet != nullptr) { + *storeIdToSet = message->getParameter3(); + } + return getSid(message); +} + +void HousekeepingMessage::setToggleReportingCommand(CommandMessage *message, + sid_t sid, bool enableReporting, bool isDiagnostics) { + if(isDiagnostics) { + if(enableReporting) { + message->setCommand(ENABLE_PERIODIC_DIAGNOSTICS_GENERATION); + } + else { + message->setCommand(DISABLE_PERIODIC_DIAGNOSTICS_GENERATION); + } + } + else { + if(enableReporting) { + message->setCommand(ENABLE_PERIODIC_HK_REPORT_GENERATION); + } + else { + message->setCommand(DISABLE_PERIODIC_HK_REPORT_GENERATION); + } + } + + setSid(message, sid); +} + +void HousekeepingMessage::setStructureReportingCommand(CommandMessage *command, + sid_t sid, bool isDiagnostics) { + if(isDiagnostics) { + command->setCommand(REPORT_DIAGNOSTICS_REPORT_STRUCTURES); + } + else { + command->setCommand(REPORT_HK_REPORT_STRUCTURES); + } + + setSid(command, sid); +} + +void HousekeepingMessage::setOneShotReportCommand(CommandMessage *command, + sid_t sid, bool isDiagnostics) { + if(isDiagnostics) { + command->setCommand(GENERATE_ONE_DIAGNOSTICS_REPORT); + } + else { + command->setCommand(GENERATE_ONE_PARAMETER_REPORT); + } + + setSid(command, sid); +} + +void HousekeepingMessage::setCollectionIntervalModificationCommand( + CommandMessage *command, sid_t sid, float collectionInterval, + bool isDiagnostics) { + if(isDiagnostics) { + command->setCommand(MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL); + } + else { + command->setCommand(MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL); + } + command->setParameter3(collectionInterval); + + setSid(command, sid); +} + +sid_t HousekeepingMessage::getCollectionIntervalModificationCommand( + const CommandMessage* command, float* newCollectionInterval) { + if(newCollectionInterval != nullptr) { + *newCollectionInterval = command->getParameter3(); + } + + return getSid(command); +} + +void HousekeepingMessage::setHkRequestSuccessReply(CommandMessage *reply, + sid_t sid) { + setSid(reply, sid); + reply->setCommand(HK_REQUEST_SUCCESS); +} + +void HousekeepingMessage::setHkRequestFailureReply(CommandMessage *reply, + sid_t sid, ReturnValue_t error) { + setSid(reply, sid); + reply->setCommand(HK_REQUEST_FAILURE); + reply->setParameter3(error); +} + +sid_t HousekeepingMessage::getHkRequestFailureReply(const CommandMessage *reply, + ReturnValue_t *error) { + if(error != nullptr) { + *error = reply->getParameter3(); + } + return getSid(reply); +} + +sid_t HousekeepingMessage::getSid(const CommandMessage* message) { + sid_t sid; + std::memcpy(&sid.raw, message->getData(), sizeof(sid.raw)); + return sid; +} + +void HousekeepingMessage::setSid(CommandMessage *message, sid_t sid) { + std::memcpy(message->getData(), &sid.raw, sizeof(sid.raw)); +} + +void HousekeepingMessage::setHkStuctureReportReply(CommandMessage *reply, + sid_t sid, store_address_t storeId) { + reply->setCommand(HK_DEFINITIONS_REPORT); + setSid(reply, sid); + reply->setParameter3(storeId.raw); +} + +void HousekeepingMessage::setDiagnosticsStuctureReportReply( + CommandMessage *reply, sid_t sid, store_address_t storeId) { + reply->setCommand(DIAGNOSTICS_DEFINITION_REPORT); + setSid(reply, sid); + reply->setParameter3(storeId.raw); +} + +void HousekeepingMessage::clear(CommandMessage* message) { + switch(message->getCommand()) { + case(HK_REPORT): + case(DIAGNOSTICS_REPORT): + case(HK_DEFINITIONS_REPORT): + case(DIAGNOSTICS_DEFINITION_REPORT): + case(UPDATE_SNAPSHOT): { + store_address_t storeId; + getHkDataReply(message, &storeId); + StorageManagerIF *ipcStore = objectManager->get( + objects::IPC_STORE); + if (ipcStore != nullptr) { + ipcStore->deleteData(storeId); + } + } + } + message->setCommand(CommandMessage::CMD_NONE); +} diff --git a/housekeeping/HousekeepingMessage.h b/housekeeping/HousekeepingMessage.h new file mode 100644 index 00000000..6dc95f54 --- /dev/null +++ b/housekeeping/HousekeepingMessage.h @@ -0,0 +1,157 @@ +#ifndef FSFW_HOUSEKEEPING_HOUSEKEEPINGMESSAGE_H_ +#define FSFW_HOUSEKEEPING_HOUSEKEEPINGMESSAGE_H_ + +#include "../ipc/CommandMessage.h" +#include "../ipc/FwMessageTypes.h" +#include "../objectmanager/frameworkObjects.h" +#include "../objectmanager/SystemObjectIF.h" +#include "../storagemanager/StorageManagerIF.h" + +union sid_t { + static constexpr uint64_t INVALID_SID = -1; + static constexpr uint32_t INVALID_SET_ID = -1; + static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT; + sid_t(): raw(INVALID_SID) {} + + sid_t(object_id_t objectId, uint32_t setId): + objectId(objectId), + ownerSetId(setId) {} + + struct { + object_id_t objectId ; + /** + * A generic 32 bit ID to identify unique HK packets for a single + * object. For example, the DeviceCommandId_t is used for + * DeviceHandlers + */ + uint32_t ownerSetId; + }; + /** + * Alternative access to the raw value. This is also the size of the type. + */ + uint64_t raw; + + bool notSet() const { + return raw == INVALID_SID; + } + + bool operator==(const sid_t& other) const { + return raw == other.raw; + } + + bool operator!=(const sid_t& other) const { + return not (raw == other.raw); + } +}; + + +/** + * @brief Special command message type for housekeeping messages + * @details + * This message is slightly larger than regular command messages to accomodate + * the uint64_t structure ID (SID). + */ +class HousekeepingMessage { +public: + + static constexpr size_t HK_MESSAGE_SIZE = CommandMessageIF::HEADER_SIZE + + sizeof(sid_t) + sizeof(uint32_t); + + /** + * Concrete instance is not used, instead this class operates on + * command message instances. + */ + HousekeepingMessage() = delete; + virtual ~HousekeepingMessage(); + + static constexpr uint8_t MESSAGE_ID = messagetypes::HOUSEKEEPING; + + static constexpr Command_t ENABLE_PERIODIC_HK_REPORT_GENERATION = + MAKE_COMMAND_ID(5); + static constexpr Command_t DISABLE_PERIODIC_HK_REPORT_GENERATION = + MAKE_COMMAND_ID(6); + + static constexpr Command_t ENABLE_PERIODIC_DIAGNOSTICS_GENERATION = + MAKE_COMMAND_ID(7); + static constexpr Command_t DISABLE_PERIODIC_DIAGNOSTICS_GENERATION = + MAKE_COMMAND_ID(8); + + static constexpr Command_t REPORT_HK_REPORT_STRUCTURES = MAKE_COMMAND_ID(9); + static constexpr Command_t REPORT_DIAGNOSTICS_REPORT_STRUCTURES = + MAKE_COMMAND_ID(11); + + static constexpr Command_t HK_DEFINITIONS_REPORT = MAKE_COMMAND_ID(10); + static constexpr Command_t DIAGNOSTICS_DEFINITION_REPORT = MAKE_COMMAND_ID(12); + + static constexpr Command_t HK_REPORT = MAKE_COMMAND_ID(25); + static constexpr Command_t DIAGNOSTICS_REPORT = MAKE_COMMAND_ID(26); + + static constexpr Command_t GENERATE_ONE_PARAMETER_REPORT = + MAKE_COMMAND_ID(27); + static constexpr Command_t GENERATE_ONE_DIAGNOSTICS_REPORT = + MAKE_COMMAND_ID(28); + + static constexpr Command_t MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL = + MAKE_COMMAND_ID(31); + static constexpr Command_t MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL = + MAKE_COMMAND_ID(32); + + static constexpr Command_t HK_REQUEST_SUCCESS = + MAKE_COMMAND_ID(128); + static constexpr Command_t HK_REQUEST_FAILURE = + MAKE_COMMAND_ID(129); + + static constexpr Command_t UPDATE_NOTIFICATION = MAKE_COMMAND_ID(130); + static constexpr Command_t UPDATE_SNAPSHOT = MAKE_COMMAND_ID(131); + + static constexpr Command_t UPDATE_HK_REPORT = MAKE_COMMAND_ID(132); + + static sid_t getSid(const CommandMessage* message); + + /** Setter functions */ + static void setToggleReportingCommand(CommandMessage* command, sid_t sid, + bool enableReporting, bool isDiagnostics); + static void setStructureReportingCommand(CommandMessage* command, sid_t sid, + bool isDiagnostics); + static void setOneShotReportCommand(CommandMessage* command, sid_t sid, + bool isDiagnostics); + static void setCollectionIntervalModificationCommand( + CommandMessage* command, sid_t sid, float collectionInterval, + bool isDiagnostics); + + static void setHkReportReply(CommandMessage* reply, sid_t sid, + store_address_t storeId); + static void setHkDiagnosticsReply(CommandMessage* reply, sid_t sid, + store_address_t storeId); + + static void setHkRequestSuccessReply(CommandMessage* reply, sid_t sid); + static void setHkRequestFailureReply(CommandMessage* reply, sid_t sid, + ReturnValue_t error); + + static void setHkStuctureReportReply(CommandMessage* reply, + sid_t sid, store_address_t storeId); + static void setDiagnosticsStuctureReportReply(CommandMessage* reply, + sid_t sid, store_address_t storeId); + + static sid_t getHkRequestFailureReply(const CommandMessage* reply, + ReturnValue_t* error); + + /** + * @brief Generic getter function for housekeeping data replies + * @details + * Command ID can be used beforehand to distinguish between diagnostics and + * regular HK packets. This getter function should be used for the + * command IDs 10, 12, 25 and 26. + */ + static sid_t getHkDataReply(const CommandMessage* message, + store_address_t * storeIdToSet); + static sid_t getCollectionIntervalModificationCommand( + const CommandMessage* command, float* newCollectionInterval); + + static void clear(CommandMessage* message); +private: + static void setSid(CommandMessage* message, sid_t sid); +}; + + +#endif /* FSFW_HOUSEKEEPING_HOUSEKEEPINGMESSAGE_H_ */ diff --git a/housekeeping/HousekeepingPacketDownlink.h b/housekeeping/HousekeepingPacketDownlink.h new file mode 100644 index 00000000..ae0cc988 --- /dev/null +++ b/housekeeping/HousekeepingPacketDownlink.h @@ -0,0 +1,34 @@ +#ifndef FSFW_HOUSEKEEPING_HOUSEKEEPINGPACKETDOWNLINK_H_ +#define FSFW_HOUSEKEEPING_HOUSEKEEPINGPACKETDOWNLINK_H_ + +#include "../datapoollocal/LocalPoolDataSetBase.h" +#include "../serialize/SerialLinkedListAdapter.h" +#include "../storagemanager/StorageManagerIF.h" + +/** + * @brief This class will be used to serialize general housekeeping packets + * which are destined to be downlinked into the store. + * @details + * The housekeeping packets are stored into the IPC store and forwarded + * to the designated housekeeping handler. + */ +class HousekeepingPacketDownlink: public SerialLinkedListAdapter { +public: + HousekeepingPacketDownlink(sid_t sid, LocalPoolDataSetBase* dataSetPtr): + sourceId(sid.objectId), setId(sid.ownerSetId), hkData(dataSetPtr) { + setLinks(); + } + +private: + void setLinks() { + setStart(&sourceId); + sourceId.setNext(&setId); + setId.setNext(&hkData); + } + + SerializeElement sourceId; + SerializeElement setId; + LinkedElement hkData; +}; + +#endif /* FRAMEWORK_HOUSEKEEPING_HOUSEKEEPINGPACKETDOWNLINK_H_ */ diff --git a/housekeeping/HousekeepingPacketUpdate.h b/housekeeping/HousekeepingPacketUpdate.h new file mode 100644 index 00000000..eebdc11c --- /dev/null +++ b/housekeeping/HousekeepingPacketUpdate.h @@ -0,0 +1,73 @@ +#ifndef FRAMEWORK_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_ +#define FRAMEWORK_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_ + +#include "../serialize/SerialBufferAdapter.h" +#include "../serialize/SerialLinkedListAdapter.h" +#include "../datapoollocal/LocalPoolDataSetBase.h" + +/** + * @brief This helper class will be used to serialize and deserialize + * update housekeeping packets into the store. + */ +class HousekeepingPacketUpdate: public SerializeIF { +public: + /** + * @param timeStamp + * @param timeStampSize + * @param hkData + * @param hkDataSize + */ + HousekeepingPacketUpdate(uint8_t* timeStamp, size_t timeStampSize, + LocalPoolDataSetBase* dataSetPtr): + timeStamp(timeStamp), timeStampSize(timeStampSize), + dataSetPtr(dataSetPtr) {}; + + virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, + size_t maxSize, Endianness streamEndianness) const { + if(timeStamp != nullptr) { + /* Endianness will always be MACHINE, so we can simply use memcpy + here. */ + std::memcpy(*buffer, timeStamp, timeStampSize); + *size += timeStampSize; + *buffer += timeStampSize; + } + if(dataSetPtr == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + return dataSetPtr->serialize(buffer, size, maxSize, streamEndianness); + } + + virtual size_t getSerializedSize() const { + if(dataSetPtr == nullptr) { + return 0; + } + return timeStampSize + dataSetPtr->getSerializedSize(); + } + + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) override { + if(timeStamp != nullptr) { + /* Endianness will always be MACHINE, so we can simply use memcpy + here. */ + std::memcpy(timeStamp, *buffer, timeStampSize); + *size += timeStampSize; + *buffer += timeStampSize; + } + + if(dataSetPtr == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return dataSetPtr->deSerialize(buffer, size, streamEndianness); + } + +private: + uint8_t* timeStamp; + size_t timeStampSize; + + LocalPoolDataSetBase* dataSetPtr = nullptr; +}; + + + +#endif /* FRAMEWORK_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_ */ diff --git a/housekeeping/HousekeepingSetPacket.h b/housekeeping/HousekeepingSetPacket.h new file mode 100644 index 00000000..f94720d4 --- /dev/null +++ b/housekeeping/HousekeepingSetPacket.h @@ -0,0 +1,59 @@ +#ifndef FSFW_HOUSEKEEPING_HOUSEKEEPINGSETPACKET_H_ +#define FSFW_HOUSEKEEPING_HOUSEKEEPINGSETPACKET_H_ + +#include "../housekeeping/HousekeepingMessage.h" +#include "../serialize/SerialLinkedListAdapter.h" +#include "../datapoollocal/LocalPoolDataSetBase.h" + +class HousekeepingSetPacket: public SerialLinkedListAdapter { +public: + HousekeepingSetPacket(sid_t sid, bool reportingEnabled, bool valid, + float collectionInterval, LocalPoolDataSetBase* dataSetPtr): + objectId(sid.objectId), setId(sid.ownerSetId), + reportingEnabled(reportingEnabled), valid(valid), + collectionIntervalSeconds(collectionInterval), dataSet(dataSetPtr) { + setLinks(); + } + + ReturnValue_t serialize(uint8_t** buffer, size_t* size, + size_t maxSize, Endianness streamEndianness) const override { + ReturnValue_t result = SerialLinkedListAdapter::serialize(buffer, size, + maxSize, streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + return dataSet->serializeLocalPoolIds(buffer, size ,maxSize, + streamEndianness); + } + + size_t getSerializedSize() const override { + size_t linkedSize = SerialLinkedListAdapter::getSerializedSize(); + linkedSize += dataSet->getLocalPoolIdsSerializedSize(); + return linkedSize; + } + + ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + Endianness streamEndianness) override { + return HasReturnvaluesIF::RETURN_OK; + } + +private: + + void setLinks() { + setStart(&objectId); + objectId.setNext(&setId); + setId.setNext(&reportingEnabled); + reportingEnabled.setNext(&valid); + valid.setNext(&collectionIntervalSeconds); + collectionIntervalSeconds.setEnd(); + } + + SerializeElement objectId; + SerializeElement setId; + SerializeElement reportingEnabled; + SerializeElement valid; + SerializeElement collectionIntervalSeconds; + LocalPoolDataSetBase* dataSet; +}; + +#endif /* FSFW_HOUSEKEEPING_HOUSEKEEPINGSETPACKET_H_ */ diff --git a/housekeeping/PeriodicHousekeepingHelper.cpp b/housekeeping/PeriodicHousekeepingHelper.cpp new file mode 100644 index 00000000..37349f81 --- /dev/null +++ b/housekeeping/PeriodicHousekeepingHelper.cpp @@ -0,0 +1,48 @@ +#include "../datapoollocal/LocalPoolDataSetBase.h" +#include "PeriodicHousekeepingHelper.h" +#include + +PeriodicHousekeepingHelper::PeriodicHousekeepingHelper( + LocalPoolDataSetBase* owner): owner(owner) {} + + +void PeriodicHousekeepingHelper::initialize(float collectionInterval, + dur_millis_t minimumPeriodicInterval, bool isDiagnostics, + uint8_t nonDiagIntervalFactor) { + this->minimumPeriodicInterval = minimumPeriodicInterval; + if(not isDiagnostics) { + this->minimumPeriodicInterval = this->minimumPeriodicInterval * + nonDiagIntervalFactor; + } + collectionIntervalTicks = intervalSecondsToInterval(collectionInterval); +} + +float PeriodicHousekeepingHelper::getCollectionIntervalInSeconds() { + return intervalToIntervalSeconds(collectionIntervalTicks); +} + +bool PeriodicHousekeepingHelper::checkOpNecessary() { + if(internalTickCounter >= collectionIntervalTicks) { + internalTickCounter = 1; + return true; + } + internalTickCounter++; + return false; +} + +uint32_t PeriodicHousekeepingHelper::intervalSecondsToInterval( + float collectionIntervalSeconds) { + return std::ceil(collectionIntervalSeconds * 1000 + / minimumPeriodicInterval); +} + +float PeriodicHousekeepingHelper::intervalToIntervalSeconds( + uint32_t collectionInterval) { + return static_cast(collectionInterval * + minimumPeriodicInterval); +} + +void PeriodicHousekeepingHelper::changeCollectionInterval( + float newIntervalSeconds) { + collectionIntervalTicks = intervalSecondsToInterval(newIntervalSeconds); +} diff --git a/housekeeping/PeriodicHousekeepingHelper.h b/housekeeping/PeriodicHousekeepingHelper.h new file mode 100644 index 00000000..d96eae1d --- /dev/null +++ b/housekeeping/PeriodicHousekeepingHelper.h @@ -0,0 +1,32 @@ +#ifndef FSFW_HOUSEKEEPING_PERIODICHOUSEKEEPINGHELPER_H_ +#define FSFW_HOUSEKEEPING_PERIODICHOUSEKEEPINGHELPER_H_ + +#include "../timemanager/Clock.h" +#include + +class LocalPoolDataSetBase; + +class PeriodicHousekeepingHelper { +public: + PeriodicHousekeepingHelper(LocalPoolDataSetBase* owner); + + void initialize(float collectionInterval, + dur_millis_t minimumPeriodicInterval, bool isDiagnostics, + uint8_t nonDiagIntervalFactor); + + void changeCollectionInterval(float newInterval); + float getCollectionIntervalInSeconds(); + bool checkOpNecessary(); +private: + LocalPoolDataSetBase* owner = nullptr; + + uint32_t intervalSecondsToInterval(float collectionIntervalSeconds); + float intervalToIntervalSeconds(uint32_t collectionInterval); + + dur_millis_t minimumPeriodicInterval = 0; + uint32_t internalTickCounter = 1; + uint32_t collectionIntervalTicks = 0; + +}; + +#endif /* FSFW_HOUSEKEEPING_PERIODICHOUSEKEEPINGHELPER_H_ */ From a43e8ea60333627ca085b54c127f0b0acdddd601 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 1 Oct 2020 12:27:28 +0200 Subject: [PATCH 006/119] added srv3 object ID --- objectmanager/frameworkObjects.h | 1 + 1 file changed, 1 insertion(+) diff --git a/objectmanager/frameworkObjects.h b/objectmanager/frameworkObjects.h index 4d08f084..57783286 100644 --- a/objectmanager/frameworkObjects.h +++ b/objectmanager/frameworkObjects.h @@ -6,6 +6,7 @@ enum framework_objects { // Default verification reporter. PUS_SERVICE_1_VERIFICATION = 0x53000001, PUS_SERVICE_2_DEVICE_ACCESS = 0x53000002, + PUS_SERVICE_3_HOUSEKEEPING = 0x53000003, PUS_SERVICE_5_EVENT_REPORTING = 0x53000005, PUS_SERVICE_8_FUNCTION_MGMT = 0x53000008, PUS_SERVICE_9_TIME_MGMT = 0x53000009, From 322ff54f58d1ce3c214e1b240f418d543e93a320 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 1 Oct 2020 12:28:25 +0200 Subject: [PATCH 007/119] added new message types --- ipc/FwMessageTypes.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ipc/FwMessageTypes.h b/ipc/FwMessageTypes.h index e820b1df..8b49c122 100644 --- a/ipc/FwMessageTypes.h +++ b/ipc/FwMessageTypes.h @@ -3,7 +3,7 @@ namespace messagetypes { //Remember to add new Message Types to the clearCommandMessage function! -enum FW_MESSAGE_TYPE { +enum FsfwMessageTypes { COMMAND = 0, MODE_COMMAND, HEALTH_COMMAND, @@ -14,7 +14,10 @@ enum FW_MESSAGE_TYPE { MONITORING, MEMORY, PARAMETER, - FW_MESSAGES_COUNT + FILE_SYSTEM_MESSAGE, + HOUSEKEEPING, + + FW_MESSAGES_COUNT, }; } From 196cde4075e92bd1053c136720acb3a3c6cd62f2 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 1 Oct 2020 12:30:53 +0200 Subject: [PATCH 008/119] DHB update --- devicehandlers/DeviceHandlerBase.cpp | 171 +++++++++++++++++++-------- devicehandlers/DeviceHandlerBase.h | 170 ++++++++++++++------------ 2 files changed, 214 insertions(+), 127 deletions(-) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 6fcba64b..227d5b6d 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -2,15 +2,17 @@ #include "AcceptsDeviceResponsesIF.h" #include "DeviceTmReportingWrapper.h" +#include "../serviceinterface/ServiceInterfaceStream.h" +#include "../datapoolglob/GlobalDataSet.h" +#include "../datapoolglob/GlobalPoolVariable.h" #include "../objectmanager/ObjectManager.h" #include "../storagemanager/StorageManagerIF.h" #include "../thermal/ThermalComponentIF.h" -#include "../datapoolglob/GlobalDataSet.h" -#include "../datapoolglob/GlobalPoolVariable.h" #include "../globalfunctions/CRC.h" -#include "../subsystem/SubsystemBase.h" +#include "../housekeeping/HousekeepingMessage.h" +#include "../ipc/MessageQueueMessage.h" #include "../ipc/QueueFactory.h" -#include "../serviceinterface/ServiceInterfaceStream.h" +#include "../subsystem/SubsystemBase.h" #include @@ -25,10 +27,11 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, wiretappingMode(OFF), storedRawData(StorageManagerIF::INVALID_ADDRESS), deviceCommunicationId(deviceCommunication), comCookie(comCookie), healthHelper(this,setObjectId), modeHelper(this), parameterHelper(this), - actionHelper(this, nullptr), childTransitionFailure(RETURN_OK), - fdirInstance(fdirInstance), hkSwitcher(this), - defaultFDIRUsed(fdirInstance == nullptr), switchOffWasReported(false), - childTransitionDelay(5000), transitionSourceMode(_MODE_POWER_DOWN), + actionHelper(this, nullptr), hkManager(this, nullptr), + childTransitionFailure(RETURN_OK), fdirInstance(fdirInstance), + hkSwitcher(this), defaultFDIRUsed(fdirInstance == nullptr), + switchOffWasReported(false), childTransitionDelay(5000), + transitionSourceMode(_MODE_POWER_DOWN), transitionSourceSubMode(SUBMODE_NONE) { commandQueue = QueueFactory::instance()->createMessageQueue(cmdQueueSize, MessageQueueMessage::MAX_MESSAGE_SIZE); @@ -48,6 +51,10 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, } } +void DeviceHandlerBase::setHkDestination(object_id_t hkDestination) { + this->hkDestination = hkDestination; +} + void DeviceHandlerBase::setThermalStateRequestPoolIds( uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId) { this->deviceThermalRequestPoolId = thermalStatePoolId; @@ -74,6 +81,7 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { decrementDeviceReplyMap(); fdirInstance->checkForFailures(); hkSwitcher.performOperation(); + hkManager.performHkOperation(); performOperationHook(); } if (mode == MODE_OFF) { @@ -120,7 +128,9 @@ ReturnValue_t DeviceHandlerBase::initialize() { result = communicationInterface->initializeInterface(comCookie); if (result != RETURN_OK) { - return result; + sif::error << "DeviceHandlerBase::initialize: Initializing " + "communication interface failed!" << std::endl; + return result; } IPCStore = objectManager->get(objects::IPC_STORE); @@ -183,11 +193,16 @@ ReturnValue_t DeviceHandlerBase::initialize() { return result; } + result = hkManager.initialize(commandQueue); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + fillCommandAndReplyMap(); //Set temperature target state to NON_OP. GlobDataSet mySet; - gp_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, + gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_WRITE); mySet.read(); thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; @@ -245,10 +260,10 @@ void DeviceHandlerBase::readCommandQueue() { return; } -// result = hkManager.handleHousekeepingMessage(&command); -// if (result == RETURN_OK) { -// return; -// } + result = hkManager.handleHousekeepingMessage(&command); + if (result == RETURN_OK) { + return; + } result = handleDeviceHandlerMessage(&command); if (result == RETURN_OK) { @@ -376,24 +391,28 @@ ReturnValue_t DeviceHandlerBase::isModeCombinationValid(Mode_t mode, ReturnValue_t DeviceHandlerBase::insertInCommandAndReplyMap( DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, - size_t replyLen, bool periodic, bool hasDifferentReplyId, - DeviceCommandId_t replyId) { + LocalPoolDataSetBase* replyDataSet, size_t replyLen, bool periodic, + bool hasDifferentReplyId, DeviceCommandId_t replyId) { //No need to check, as we may try to insert multiple times. insertInCommandMap(deviceCommand); if (hasDifferentReplyId) { - return insertInReplyMap(replyId, maxDelayCycles, replyLen, periodic); + return insertInReplyMap(replyId, maxDelayCycles, + replyDataSet, replyLen, periodic); } else { - return insertInReplyMap(deviceCommand, maxDelayCycles, replyLen, periodic); + return insertInReplyMap(deviceCommand, maxDelayCycles, + replyDataSet, replyLen, periodic); } } ReturnValue_t DeviceHandlerBase::insertInReplyMap(DeviceCommandId_t replyId, - uint16_t maxDelayCycles, size_t replyLen, bool periodic) { + uint16_t maxDelayCycles, LocalPoolDataSetBase* dataSet, + size_t replyLen, bool periodic) { DeviceReplyInfo info; info.maxDelayCycles = maxDelayCycles; info.periodic = periodic; info.delayCycles = 0; info.replyLen = replyLen; + info.dataSet = dataSet; info.command = deviceCommandMap.end(); auto resultPair = deviceReplyMap.emplace(replyId, info); if (resultPair.second) { @@ -419,13 +438,12 @@ ReturnValue_t DeviceHandlerBase::insertInCommandMap( ReturnValue_t DeviceHandlerBase::updateReplyMapEntry(DeviceCommandId_t deviceReply, uint16_t delayCycles, uint16_t maxDelayCycles, bool periodic) { - std::map::iterator iter = - deviceReplyMap.find(deviceReply); - if (iter == deviceReplyMap.end()) { + auto replyIter = deviceReplyMap.find(deviceReply); + if (replyIter == deviceReplyMap.end()) { triggerEvent(INVALID_DEVICE_COMMAND, deviceReply); return RETURN_FAILED; } else { - DeviceReplyInfo *info = &(iter->second); + DeviceReplyInfo *info = &(replyIter->second); if (maxDelayCycles != 0) { info->maxDelayCycles = maxDelayCycles; } @@ -435,6 +453,17 @@ ReturnValue_t DeviceHandlerBase::updateReplyMapEntry(DeviceCommandId_t deviceRep } } + +ReturnValue_t DeviceHandlerBase::setReplyDataset(DeviceCommandId_t replyId, + LocalPoolDataSetBase *dataSet) { + auto replyIter = deviceReplyMap.find(replyId); + if(replyIter == deviceReplyMap.end()) { + return HasReturnvaluesIF::RETURN_FAILED; + } + replyIter->second.dataSet = dataSet; + return HasReturnvaluesIF::RETURN_OK; +} + void DeviceHandlerBase::callChildStatemachine() { if (mode == _MODE_START_UP) { doStartUp(); @@ -470,7 +499,7 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) { if (mode == MODE_OFF) { GlobDataSet mySet; - gp_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, + gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_READ_WRITE); mySet.read(); if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { @@ -649,7 +678,7 @@ void DeviceHandlerBase::doGetRead() { void DeviceHandlerBase::parseReply(const uint8_t* receivedData, size_t receivedDataLen) { ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; - DeviceCommandId_t foundId = 0xFFFFFFFF; + DeviceCommandId_t foundId = 0xffffffff; size_t foundLen = 0; // The loop may not execute more often than the number of received bytes // (worst case). This approach avoids infinite loops due to buggy @@ -661,18 +690,26 @@ void DeviceHandlerBase::parseReply(const uint8_t* receivedData, switch (result) { case RETURN_OK: handleReply(receivedData, foundId, foundLen); + if(foundLen == 0) { + sif::warning << "DeviceHandlerBase::parseReply: foundLen is 0!" + " Packet parsing will be stuck." << std::endl; + } break; case APERIODIC_REPLY: { result = interpretDeviceReply(foundId, receivedData); if (result != RETURN_OK) { - replyRawReplyIfnotWiretapped(receivedData, foundLen); - triggerEvent(DEVICE_INTERPRETING_REPLY_FAILED, result, - foundId); + replyRawReplyIfnotWiretapped(receivedData, foundLen); + triggerEvent(DEVICE_INTERPRETING_REPLY_FAILED, result, + foundId); + } + if(foundLen == 0) { + sif::warning << "DeviceHandlerBase::parseReply: foundLen is 0!" + " Packet parsing will be stuck." << std::endl; } - } - break; - case IGNORE_REPLY_DATA: break; + } + case IGNORE_REPLY_DATA: + continue; case IGNORE_FULL_PACKET: return; default: @@ -704,16 +741,19 @@ void DeviceHandlerBase::handleReply(const uint8_t* receivedData, DeviceReplyInfo *info = &(iter->second); if (info->delayCycles != 0) { + result = interpretDeviceReply(foundId, receivedData); - if (info->periodic != false) { + if(result == IGNORE_REPLY_DATA) { + return; + } + + if (info->periodic) { info->delayCycles = info->maxDelayCycles; } else { info->delayCycles = 0; } - result = interpretDeviceReply(foundId, receivedData); - if (result != RETURN_OK) { // Report failed interpretation to FDIR. replyRawReplyIfnotWiretapped(receivedData, foundLen); @@ -927,9 +967,9 @@ ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, if ((commandedMode == MODE_ON) && (mode == MODE_OFF) && (deviceThermalStatePoolId != PoolVariableIF::NO_PARAMETER)) { GlobDataSet mySet; - gp_int8_t thermalState(deviceThermalStatePoolId, &mySet, + gp_uint8_t thermalState(deviceThermalStatePoolId, &mySet, PoolVariableIF::VAR_READ); - gp_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, + gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_READ); mySet.read(); if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { @@ -1089,19 +1129,6 @@ ReturnValue_t DeviceHandlerBase::handleDeviceHandlerMessage( } replyReturnvalueToCommand(RETURN_OK); return RETURN_OK; -// case DeviceHandlerMessage::CMD_SWITCH_IOBOARD: -// if (mode != MODE_OFF) { -// replyReturnvalueToCommand(WRONG_MODE_FOR_COMMAND); -// } else { -// result = switchCookieChannel( -// DeviceHandlerMessage::getIoBoardObjectId(message)); -// if (result == RETURN_OK) { -// replyReturnvalueToCommand(RETURN_OK); -// } else { -// replyReturnvalueToCommand(CANT_SWITCH_IO_ADDRESS); -// } -// } -// return RETURN_OK; case DeviceHandlerMessage::CMD_RAW: if ((mode != MODE_RAW)) { DeviceHandlerMessage::clear(message); @@ -1248,10 +1275,14 @@ void DeviceHandlerBase::buildInternalCommand(void) { if (iter == deviceCommandMap.end()) { result = COMMAND_NOT_SUPPORTED; } else if (iter->second.isExecuting) { + //so we can track misconfigurations sif::debug << std::hex << getObjectId() << ": DHB::buildInternalCommand: Command " - << deviceCommandId << " isExecuting" << std::endl; //so we can track misconfigurations - return; //this is an internal command, no need to report a failure here, missed reply will track if a reply is too late, otherwise, it's ok + << deviceCommandId << " isExecuting" << std::dec + << std::endl; + // this is an internal command, no need to report a failure here, + // missed reply will track if a reply is too late, otherwise, it's ok + return; } else { iter->second.sendReplyTo = NO_COMMANDER; iter->second.isExecuting = true; @@ -1340,12 +1371,50 @@ void DeviceHandlerBase::debugInterface(uint8_t positionTracker, void DeviceHandlerBase::performOperationHook() { } +ReturnValue_t DeviceHandlerBase::initializeLocalDataPool( + LocalDataPool &localDataPoolMap, + LocalDataPoolManager& poolManager) { + return RETURN_OK; +} + +LocalDataPoolManager* DeviceHandlerBase::getHkManagerHandle() { + return &hkManager; +} + + ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() { // In this function, the task handle should be valid if the task // was implemented correctly. We still check to be 1000 % sure :-) if(executingTask != nullptr) { pstIntervalMs = executingTask->getPeriodMs(); } + this->hkManager.initializeAfterTaskCreation(); + + if(setStartupImmediately) { + startTransition(MODE_ON, SUBMODE_NONE); + } return HasReturnvaluesIF::RETURN_OK; } +LocalPoolDataSetBase* DeviceHandlerBase::getDataSetHandle(sid_t sid) { + auto iter = deviceReplyMap.find(sid.ownerSetId); + if(iter != deviceReplyMap.end()) { + return iter->second.dataSet; + } + else { + return nullptr; + } +} + +object_id_t DeviceHandlerBase::getObjectId() const { + return SystemObject::getObjectId(); +} + +void DeviceHandlerBase::setStartUpImmediately() { + this->setStartupImmediately = true; +} + +dur_millis_t DeviceHandlerBase::getPeriodicOperationFrequency() const { + return pstIntervalMs; +} + diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index 5666f35e..eda318cb 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -1,12 +1,11 @@ -#ifndef FRAMEWORK_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ -#define FRAMEWORK_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ +#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ +#define FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ #include "DeviceHandlerIF.h" #include "DeviceCommunicationIF.h" #include "DeviceHandlerFailureIsolation.h" #include "../objectmanager/SystemObject.h" -#include "../tasks/PeriodicTaskIF.h" #include "../tasks/ExecutableObjectIF.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../action/HasActionsIF.h" @@ -14,10 +13,13 @@ #include "../modes/HasModesIF.h" #include "../power/PowerSwitchIF.h" #include "../ipc/MessageQueueIF.h" +#include "../tasks/PeriodicTaskIF.h" #include "../action/ActionHelper.h" #include "../health/HealthHelper.h" #include "../parameters/ParameterHelper.h" #include "../datapool/HkSwitchHelper.h" +#include "../datapoollocal/HasLocalDataPoolIF.h" +#include "../datapoollocal/LocalDataPoolManager.h" #include @@ -38,17 +40,16 @@ class StorageManagerIF; * Documentation: Dissertation Baetz p.138,139, p.141-149 * * 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. + * 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 (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. + * This class uses the opcode of ExecutableObjectIF to perform a + * step-wise execution. For each step a different action is selected and + * executed. Currently, the device handler base performs a 4-step + * execution related to 4 communication steps (based on RMAP). + * NOTE: RMAP is a standard which is used for Flying Laptop. * 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. @@ -69,9 +70,6 @@ class StorageManagerIF; * * Other important virtual methods with a default implementation * are the getTransitionDelayMs() function and the getSwitches() function. - * Please ensure that getSwitches() returns DeviceHandlerIF::NO_SWITCHES if - * power switches are not implemented yet. Otherwise, the device handler will - * not transition to MODE_ON, even if setMode(MODE_ON) is called. * If a transition to MODE_ON is desired without commanding, override the * intialize() function and call setMode(_MODE_START_UP) before calling * DeviceHandlerBase::initialize(). @@ -85,20 +83,18 @@ class DeviceHandlerBase: public DeviceHandlerIF, public HasModesIF, public HasHealthIF, public HasActionsIF, - public ReceivesParameterMessagesIF { + public ReceivesParameterMessagesIF, + public HasLocalDataPoolIF { friend void (Factory::setStaticFrameworkObjectIds)(); 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 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 comCookie This object will be passed to the communication inter- + * face and can contain user-defined information about the communication. * @param fdirInstance * @param cmdQueueSize */ @@ -106,8 +102,21 @@ public: CookieIF * comCookie, FailureIsolationBase* fdirInstance = nullptr, size_t cmdQueueSize = 20); + void setHkDestination(object_id_t hkDestination); void setThermalStateRequestPoolIds(uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId); + /** + * @brief Helper function to ease device handler development. + * This will instruct the transition to MODE_ON immediately + * (leading to doStartUp() being called for the transition to the ON mode), + * so external mode commanding is not necessary anymore. + * + * This has to be called before the task is started! + * (e.g. in the task factory). This is only a helper function for + * development. Regular mode commanding should be performed by commanding + * the AssemblyBase or Subsystem objects resposible for the device handler. + */ + void setStartUpImmediately(); /** * @brief This function is the device handler base core component and is @@ -153,6 +162,14 @@ public: * @return */ virtual ReturnValue_t initialize(); + + /** + * @brief Intialization steps performed after all tasks have been created. + * This function will be called by the executing task. + * @return + */ + virtual ReturnValue_t initializeAfterTaskCreation() override; + /** Destructor. */ virtual ~DeviceHandlerBase(); @@ -320,6 +337,8 @@ protected: * @param packet * @return * - @c RETURN_OK when the reply was interpreted. + * - @c IGNORE_REPLY_DATA Ignore the reply and don't reset reply cycle + * counter. * - @c RETURN_FAILED when the reply could not be interpreted, * e.g. logical errors or range violations occurred */ @@ -347,22 +366,10 @@ protected: * 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). - * - * (Robin) This part confuses me. "must do as soon as" implies that - * the developer must do something somewhere else in the code. Is - * that really the case? If I understood correctly, DHB performs - * almost everything (e.g. in erirm function) as long as the commands - * are inserted correctly. - * - * As soon as the replies are enabled, DeviceCommandInfo.periodic must - * be set to true, 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 @@ -376,13 +383,17 @@ protected: * @param deviceCommand Identifier of the command to add. * @param maxDelayCycles The maximum number of delay cycles the command * waits until it times out. + * @param replyLen Will be supplied to the requestReceiveMessage call of + * the communication interface. * @param periodic Indicates if the command is periodic (i.e. it is sent * by the device repeatedly without request) or not. Default is aperiodic (0) * @return - @c RETURN_OK when the command was successfully inserted, * - @c RETURN_FAILED else. */ ReturnValue_t insertInCommandAndReplyMap(DeviceCommandId_t deviceCommand, - uint16_t maxDelayCycles, size_t replyLen = 0, bool periodic = false, + uint16_t maxDelayCycles, + LocalPoolDataSetBase* replyDataSet = nullptr, + size_t replyLen = 0, bool periodic = false, bool hasDifferentReplyId = false, DeviceCommandId_t replyId = 0); /** @@ -396,7 +407,8 @@ protected: * - @c RETURN_FAILED else. */ ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand, - uint16_t maxDelayCycles, size_t replyLen = 0, bool periodic = false); + uint16_t maxDelayCycles, LocalPoolDataSetBase* dataSet = nullptr, + size_t replyLen = 0, bool periodic = false); /** * @brief A simple command to add a command to the commandList. @@ -424,6 +436,9 @@ protected: uint16_t delayCycles, uint16_t maxDelayCycles, bool periodic = false); + ReturnValue_t setReplyDataset(DeviceCommandId_t replyId, + LocalPoolDataSetBase* dataset); + /** * @brief Can be implemented by child handler to * perform debugging @@ -477,18 +492,22 @@ protected: * @param localDataPoolMap * @return */ - //virtual ReturnValue_t initializePoolEntries( - // LocalDataPool& localDataPoolMap) override; + virtual ReturnValue_t initializeLocalDataPool(LocalDataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; /** Get the HK manager object handle */ - //virtual LocalDataPoolManager* getHkManagerHandle() override; + virtual LocalDataPoolManager* getHkManagerHandle() override; /** * @brief Hook function for child handlers which is called once per * performOperation(). Default implementation is empty. */ virtual void performOperationHook(); + public: + /** Explicit interface implementation of getObjectId */ + virtual object_id_t getObjectId() const override; + /** * @param parentQueueId */ @@ -608,7 +627,7 @@ protected: /** Action helper for HasActionsIF */ ActionHelper actionHelper; /** Housekeeping Manager */ - //LocalDataPoolManager hkManager; + LocalDataPoolManager hkManager; /** * @brief Information about commands @@ -647,7 +666,7 @@ protected: //! The dataset used to access housekeeping data related to the //! respective device reply. Will point to a dataset held by //! the child handler (if one is specified) - // DataSetIF* dataSet = nullptr; + LocalPoolDataSetBase* dataSet; //! The command that expects this reply. DeviceCommandMap::iterator command; }; @@ -689,14 +708,18 @@ protected: uint32_t deviceThermalRequestPoolId = PoolVariableIF::NO_PARAMETER; /** - * Optional Error code - * Can be set in doStartUp(), doShutDown() and doTransition() to signal cause for Transition failure. + * Optional Error code. Can be set in doStartUp(), doShutDown() and + * doTransition() to signal cause for Transition failure. */ ReturnValue_t childTransitionFailure; - uint32_t ignoreMissedRepliesCount = 0; //!< Counts if communication channel lost a reply, so some missed replys can be ignored. + /** Counts if communication channel lost a reply, so some missed + * replys can be ignored. */ + uint32_t ignoreMissedRepliesCount = 0; - FailureIsolationBase* fdirInstance; //!< Pointer to the used FDIR instance. If not provided by child, default class is instantiated. + /** Pointer to the used FDIR instance. If not provided by child, + * default class is instantiated. */ + FailureIsolationBase* fdirInstance; HkSwitchHelper hkSwitcher; @@ -944,14 +967,17 @@ protected: virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode); - virtual void startTransition(Mode_t mode, Submode_t submode); - virtual void setToExternalControl(); - virtual void announceMode(bool recursive); + + /* HasModesIF overrides */ + virtual void startTransition(Mode_t mode, Submode_t submode) override; + virtual void setToExternalControl() override; + virtual void announceMode(bool recursive) override; virtual ReturnValue_t letChildHandleMessage(CommandMessage *message); /** - * Overwrites SystemObject::triggerEvent in order to inform FDIR"Helper" faster about executed events. + * Overwrites SystemObject::triggerEvent in order to inform FDIR"Helper" + * faster about executed events. * This is a bit sneaky, but improves responsiveness of the device FDIR. * @param event The event to be thrown * @param parameter1 Optional parameter 1 @@ -1034,12 +1060,17 @@ private: /** the object used to set power switches */ PowerSwitchIF *powerSwitcher = nullptr; + /** HK destination can also be set individually */ + object_id_t hkDestination = objects::NO_OBJECT; + /** * @brief Used for timing out mode transitions. * Set when setMode() is called. */ uint32_t timeoutStart = 0; + bool setStartupImmediately = false; + /** * Delay for the current mode transition, used for time out */ @@ -1080,11 +1111,6 @@ private: 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. * @@ -1111,10 +1137,14 @@ private: /** * Build and send a command to the device. * - * This routine checks whether a raw or direct command has been received, checks the content of the received command and - * calls buildCommandFromCommand() for direct commands or sets #rawpacket to the received raw packet. - * If no external command is received or the received command is invalid and the current mode is @c MODE_NORMAL or a transitional mode, - * it asks the child class to build a command (via getNormalDeviceCommand() or getTransitionalDeviceCommand() and buildCommand()) and + * This routine checks whether a raw or direct command has been received, + * checks the content of the received command and calls + * buildCommandFromCommand() for direct commands or sets #rawpacket + * to the received raw packet. + * If no external command is received or the received command is invalid and + * the current mode is @c MODE_NORMAL or a transitional mode, it asks the + * child class to build a command (via getNormalDeviceCommand() or + * getTransitionalDeviceCommand() and buildCommand()) and * sends the command via RMAP. */ void doSendWrite(void); @@ -1159,7 +1189,6 @@ private: ReturnValue_t getStorageData(store_address_t storageAddress, uint8_t **data, uint32_t *len); - /** * @param modeTo either @c MODE_ON, MODE_NORMAL or MODE_RAW NOTHING ELSE!!! */ @@ -1170,25 +1199,14 @@ private: */ void callChildStatemachine(); - /** - * Switches the channel of the cookie used for the communication - * - * - * @param newChannel the object Id of the channel to switch to - * @return - * - @c RETURN_OK when cookie was changed - * - @c RETURN_FAILED when cookies could not be changed, eg because the newChannel is not enabled - * - @c returnvalues of RMAPChannelIF::isActive() - */ - ReturnValue_t switchCookieChannel(object_id_t newChannelId); - ReturnValue_t handleDeviceHandlerMessage(CommandMessage *message); - virtual ReturnValue_t initializeAfterTaskCreation() override; + virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; - void parseReply(const uint8_t* receivedData, - size_t receivedDataLen); + virtual dur_millis_t getPeriodicOperationFrequency() const override; + + void parseReply(const uint8_t* receivedData, + size_t receivedDataLen); }; -#endif /* FRAMEWORK_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */ - +#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */ From b6a23727f2dcbe498276b3c86306d24a8e1462f8 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 12 Oct 2020 15:40:27 +0200 Subject: [PATCH 009/119] added extended controller base --- controller/ExtendedControllerBase.cpp | 114 ++++++++++++++++++++++++++ controller/ExtendedControllerBase.h | 72 ++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 controller/ExtendedControllerBase.cpp create mode 100644 controller/ExtendedControllerBase.h diff --git a/controller/ExtendedControllerBase.cpp b/controller/ExtendedControllerBase.cpp new file mode 100644 index 00000000..f69d2ea1 --- /dev/null +++ b/controller/ExtendedControllerBase.cpp @@ -0,0 +1,114 @@ +#include "ExtendedControllerBase.h" + + +ExtendedControllerBase::ExtendedControllerBase(object_id_t objectId, + object_id_t parentId, size_t commandQueueDepth): + ControllerBase(objectId, parentId, commandQueueDepth), + localPoolManager(this, commandQueue), + actionHelper(this, commandQueue) { +} + +ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId, + MessageQueueId_t commandedBy, const uint8_t *data, size_t size) { + // needs to be overriden and implemented by child class. + return HasReturnvaluesIF::RETURN_OK; +} + + + +ReturnValue_t ExtendedControllerBase::initializeLocalDataPool( + LocalDataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { + // needs to be overriden and implemented by child class. + return HasReturnvaluesIF::RETURN_OK; +} + +object_id_t ExtendedControllerBase::getObjectId() const { + return SystemObject::getObjectId(); +} + +LocalDataPoolManager* ExtendedControllerBase::getHkManagerHandle() { + return &localPoolManager; +} + +uint32_t ExtendedControllerBase::getPeriodicOperationFrequency() const { + return this->executingTask->getPeriodMs(); +} + +ReturnValue_t ExtendedControllerBase::handleCommandMessage( + CommandMessage *message) { + ReturnValue_t result = actionHelper.handleActionMessage(message); + if(result == HasReturnvaluesIF::RETURN_OK) { + return result; + } + return localPoolManager.handleHousekeepingMessage(message); +} + +void ExtendedControllerBase::handleQueue() { + CommandMessage command; + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + for (result = commandQueue->receiveMessage(&command); + result == RETURN_OK; + result = commandQueue->receiveMessage(&command)) { + result = actionHelper.handleActionMessage(&command); + if (result == RETURN_OK) { + continue; + } + + result = modeHelper.handleModeCommand(&command); + if (result == RETURN_OK) { + continue; + } + + result = healthHelper.handleHealthCommand(&command); + if (result == RETURN_OK) { + continue; + } + + result = localPoolManager.handleHousekeepingMessage(&command); + if (result == RETURN_OK) { + continue; + } + + result = handleCommandMessage(&command); + if (result == RETURN_OK) { + continue; + } + command.setToUnknownCommand(); + commandQueue->reply(&command); + } +} + +ReturnValue_t ExtendedControllerBase::initialize() { + ReturnValue_t result = ControllerBase::initialize(); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = actionHelper.initialize(commandQueue); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + return localPoolManager.initialize(commandQueue); +} + +ReturnValue_t ExtendedControllerBase::initializeAfterTaskCreation() { + return localPoolManager.initializeAfterTaskCreation(); +} + +ReturnValue_t ExtendedControllerBase::performOperation(uint8_t opCode) { + handleQueue(); + hkSwitcher.performOperation(); + localPoolManager.performHkOperation(); + performControlOperation(); + return RETURN_OK; +} + +MessageQueueId_t ExtendedControllerBase::getCommandQueue() const { + return commandQueue->getId(); +} + +LocalPoolDataSetBase* ExtendedControllerBase::getDataSetHandle(sid_t sid) { + sif::warning << "ExtendedControllerBase::getDataSetHandle: No child " + << " implementation provided, returning nullptr!" << std::endl; + return nullptr; +} diff --git a/controller/ExtendedControllerBase.h b/controller/ExtendedControllerBase.h new file mode 100644 index 00000000..02c5728e --- /dev/null +++ b/controller/ExtendedControllerBase.h @@ -0,0 +1,72 @@ +#ifndef FSFW_CONTROLLER_EXTENDEDCONTROLLERBASE_H_ +#define FSFW_CONTROLLER_EXTENDEDCONTROLLERBASE_H_ + +#include "ControllerBase.h" + +#include "../action/HasActionsIF.h" +#include "../datapoollocal/HasLocalDataPoolIF.h" +#include "../action/ActionHelper.h" +#include "../datapoollocal/LocalDataPoolManager.h" + +/** + * @brief Extendes the basic ControllerBase with the common components + * HasActionsIF for commandability and HasLocalDataPoolIF to keep + * a pool of local data pool variables. + * @details + * Default implementations required for the interfaces will be empty and have + * to be implemented by child class. + */ +class ExtendedControllerBase: public ControllerBase, + public HasActionsIF, + public HasLocalDataPoolIF { +public: + ExtendedControllerBase(object_id_t objectId, object_id_t parentId, + size_t commandQueueDepth = 3); + + /** SystemObjectIF overrides */ + virtual ReturnValue_t initialize() override; + + virtual MessageQueueId_t getCommandQueue() const override; + + /** ExecutableObjectIF overrides */ + virtual ReturnValue_t performOperation(uint8_t opCode) override; + virtual ReturnValue_t initializeAfterTaskCreation() override; + +protected: + LocalDataPoolManager localPoolManager; + ActionHelper actionHelper; + + /** + * Implemented by child class. Handle all command messages which are + * not health, mode, action or housekeeping messages. + * @param message + * @return + */ + virtual ReturnValue_t handleCommandMessage(CommandMessage *message) = 0; + + /** + * Periodic helper from ControllerBase, implemented by child class. + */ + virtual void performControlOperation() = 0; + + /** Handle the four messages mentioned above */ + void handleQueue() override; + + /** HasActionsIF overrides */ + virtual ReturnValue_t executeAction(ActionId_t actionId, + MessageQueueId_t commandedBy, const uint8_t* data, + size_t size) override; + + /** HasLocalDatapoolIF overrides */ + virtual object_id_t getObjectId() const override; + virtual ReturnValue_t initializeLocalDataPool( + LocalDataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; + virtual LocalDataPoolManager* getHkManagerHandle() override; + virtual uint32_t getPeriodicOperationFrequency() const override; + virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; +}; + + + +#endif /* FSFW_CONTROLLER_EXTENDEDCONTROLLERBASE_H_ */ From 140aa3ab422cc220dbbcf16f1d24242a73b3c933 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 12 Oct 2020 18:18:41 +0200 Subject: [PATCH 010/119] form improvements DHB --- devicehandlers/AcceptsDeviceResponsesIF.h | 22 +-- devicehandlers/AssemblyBase.cpp | 13 +- devicehandlers/AssemblyBase.h | 90 ++++++--- devicehandlers/ChildHandlerBase.cpp | 10 +- devicehandlers/ChildHandlerBase.h | 14 +- devicehandlers/ChildHandlerFDIR.cpp | 3 +- devicehandlers/ChildHandlerFDIR.h | 4 +- devicehandlers/CookieIF.h | 10 +- devicehandlers/DeviceCommunicationIF.h | 43 ++-- devicehandlers/DeviceHandlerBase.cpp | 183 ++++++++++++------ devicehandlers/DeviceHandlerBase.h | 170 ++++++++-------- .../DeviceHandlerFailureIsolation.cpp | 8 + devicehandlers/DeviceHandlerIF.h | 20 +- devicehandlers/DeviceHandlerMessage.cpp | 19 +- devicehandlers/DeviceHandlerMessage.h | 78 +++----- devicehandlers/DeviceTmReportingWrapper.cpp | 1 - devicehandlers/DeviceTmReportingWrapper.h | 6 +- devicehandlers/HealthDevice.cpp | 6 +- devicehandlers/HealthDevice.h | 6 +- 19 files changed, 403 insertions(+), 303 deletions(-) diff --git a/devicehandlers/AcceptsDeviceResponsesIF.h b/devicehandlers/AcceptsDeviceResponsesIF.h index 8dc69de4..8e13ba0c 100644 --- a/devicehandlers/AcceptsDeviceResponsesIF.h +++ b/devicehandlers/AcceptsDeviceResponsesIF.h @@ -1,23 +1,19 @@ -/** - * @file AcceptsDeviceResponsesIF.h - * @brief This file defines the AcceptsDeviceResponsesIF class. - * @date 15.05.2013 - * @author baetz - */ - -#ifndef ACCEPTSDEVICERESPONSESIF_H_ -#define ACCEPTSDEVICERESPONSESIF_H_ +#ifndef FSFW_DEVICEHANDLERS_ACCEPTSDEVICERESPONSESIF_H_ +#define FSFW_DEVICEHANDLERS_ACCEPTSDEVICERESPONSESIF_H_ #include "../ipc/MessageQueueSenderIF.h" +/** + * This interface is used by the device handler to send a device response + * to the queue ID, which is returned in the implemented abstract method. + */ class AcceptsDeviceResponsesIF { public: /** * Default empty virtual destructor. */ - virtual ~AcceptsDeviceResponsesIF() { -} -virtual MessageQueueId_t getDeviceQueue() = 0; + virtual ~AcceptsDeviceResponsesIF() {} + virtual MessageQueueId_t getDeviceQueue() = 0; }; -#endif /* ACCEPTSDEVICERESPONSESIF_H_ */ +#endif /* FSFW_DEVICEHANDLERS_ACCEPTSDEVICERESPONSESIF_H_ */ diff --git a/devicehandlers/AssemblyBase.cpp b/devicehandlers/AssemblyBase.cpp index 8b3f7f4d..46b2211a 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); } @@ -165,9 +165,8 @@ ReturnValue_t AssemblyBase::checkChildrenState() { } ReturnValue_t AssemblyBase::checkChildrenStateOff() { - for (std::map::iterator iter = childrenMap.begin(); - iter != childrenMap.end(); iter++) { - if (checkChildOff(iter->first) != RETURN_OK) { + for (const auto& childIter: childrenMap) { + if (checkChildOff(childIter.first) != RETURN_OK) { return NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE; } } diff --git a/devicehandlers/AssemblyBase.h b/devicehandlers/AssemblyBase.h index bd2e8fad..353d5f89 100644 --- a/devicehandlers/AssemblyBase.h +++ b/devicehandlers/AssemblyBase.h @@ -1,10 +1,30 @@ -#ifndef ASSEMBLYBASE_H_ -#define ASSEMBLYBASE_H_ +#ifndef FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ +#define FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ -#include "../container/FixedArrayList.h" #include "DeviceHandlerBase.h" +#include "../container/FixedArrayList.h" #include "../subsystem/SubsystemBase.h" +/** + * @brief Base class to implement reconfiguration and failure handling for + * redundant devices by monitoring their modes health states. + * @details + * Documentation: Dissertation Baetz p.156, 157. + * + * This class reduces the complexity of controller components which would + * otherwise be needed for the handling of redundant devices. + * + * The template class monitors mode and health state of its children + * and checks availability of devices on every detected change. + * AssemblyBase does not implement any redundancy logic by itself, but provides + * adaptation points for implementations to do so. Since most monitoring + * activities rely on mode and health state only and are therefore + * generic, it is sufficient for subclasses to provide: + * + * 1. check logic when active-> checkChildrenStateOn + * 2. transition logic to change the mode -> commandChildren + * + */ class AssemblyBase: public SubsystemBase { public: static const uint8_t INTERFACE_ID = CLASS_ID::ASSEMBLY_BASE; @@ -16,10 +36,41 @@ public: static const ReturnValue_t NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE = MAKE_RETURN_CODE(0xa1); - AssemblyBase(object_id_t objectId, object_id_t parentId, uint16_t commandQueueDepth = 8); + AssemblyBase(object_id_t objectId, object_id_t parentId, + uint16_t commandQueueDepth = 8); virtual ~AssemblyBase(); protected: + + // SHOULDDO: Change that OVERWRITE_HEALTH may be returned + // (or return internalState directly?) + /** + * Command children to reach [mode,submode] combination + * Can be done by setting #commandsOutstanding correctly, + * or using executeTable() + * @param mode + * @param submode + * @return + * - @c RETURN_OK if ok + * - @c NEED_SECOND_STEP if children need to be commanded again + */ + virtual ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) = 0; + + /** + * Check whether desired assembly mode was achieved by checking the modes + * or/and health states of child device handlers. + * The assembly template class will also call this function if a health + * or mode change of a child device handler was detected. + * @param wantedMode + * @param wantedSubmode + * @return + */ + virtual ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, + Submode_t wantedSubmode) = 0; + + virtual ReturnValue_t isModeCombinationValid(Mode_t mode, + Submode_t submode) = 0; + enum InternalState { STATE_NONE, STATE_OVERWRITE_HEALTH, @@ -36,6 +87,7 @@ protected: RECOVERY_WAIT } recoveryState; //!< Indicates if one of the children requested a recovery. ChildrenMap::iterator recoveringDevice; + /** * the mode the current transition is trying to achieve. * Can be different from the modehelper.commandedMode! @@ -61,8 +113,8 @@ protected: bool handleChildrenChanged(); /** - * This method is called if the children changed its mode in a way that the current - * mode can't be kept. + * This method is called if the children changed its mode in a way that + * the current mode can't be kept. * Default behavior is to go to MODE_OFF. * @param result The failure code which was returned by checkChildrenState. */ @@ -75,9 +127,6 @@ protected: ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode); - virtual ReturnValue_t isModeCombinationValid(Mode_t mode, - Submode_t submode) = 0; - virtual void startTransition(Mode_t mode, Submode_t submode); virtual void doStartTransition(Mode_t mode, Submode_t submode); @@ -90,24 +139,6 @@ protected: void sendHealthCommand(MessageQueueId_t sendTo, HealthState health); - //SHOULDDO: Change that OVERWRITE_HEALTH may be returned (or return internalState directly?) - /** - * command children to reach mode,submode - * - * set #commandsOutstanding correctly, or use executeTable() - * - * @param mode - * @param submode - * @return - * - @c RETURN_OK if ok - * - @c NEED_SECOND_STEP if children need to be commanded again - */ - virtual ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) = 0; - - //SHOULDDO: Remove wantedMode, wantedSubmode, as targetMode/submode is available? - virtual ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, - Submode_t wantedSubmode) = 0; - virtual ReturnValue_t checkChildrenStateOff(); ReturnValue_t checkChildrenState(); @@ -125,8 +156,9 @@ protected: * Also sets state to STATE_OVERWRITE_HEATH. * @param objectId Must be a registered child. */ - void overwriteDeviceHealth(object_id_t objectId, HasHealthIF::HealthState oldHealth); + void overwriteDeviceHealth(object_id_t objectId, + HasHealthIF::HealthState oldHealth); }; -#endif /* ASSEMBLYBASE_H_ */ +#endif /* FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ */ diff --git a/devicehandlers/ChildHandlerBase.cpp b/devicehandlers/ChildHandlerBase.cpp index e800cd56..d4ef67ad 100644 --- a/devicehandlers/ChildHandlerBase.cpp +++ b/devicehandlers/ChildHandlerBase.cpp @@ -1,16 +1,18 @@ +#include "ChildHandlerBase.h" #include "../subsystem/SubsystemBase.h" -#include "../devicehandlers/ChildHandlerBase.h" #include "../subsystem/SubsystemBase.h" ChildHandlerBase::ChildHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication, CookieIF * cookie, - uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId, - object_id_t parent, FailureIsolationBase* customFdir, - size_t cmdQueueSize) : + object_id_t hkDestination, uint32_t thermalStatePoolId, + uint32_t thermalRequestPoolId, + object_id_t parent, + FailureIsolationBase* customFdir, size_t cmdQueueSize) : DeviceHandlerBase(setObjectId, deviceCommunication, cookie, (customFdir == nullptr? &childHandlerFdir : customFdir), cmdQueueSize), parentId(parent), childHandlerFdir(setObjectId) { + this->setHkDestination(hkDestination); this->setThermalStateRequestPoolIds(thermalStatePoolId, thermalRequestPoolId); diff --git a/devicehandlers/ChildHandlerBase.h b/devicehandlers/ChildHandlerBase.h index f6bf318a..eed4c95e 100644 --- a/devicehandlers/ChildHandlerBase.h +++ b/devicehandlers/ChildHandlerBase.h @@ -1,17 +1,18 @@ -#ifndef FSFW_DEVICES_CHILDHANDLERBASE_H_ -#define FSFW_DEVICES_CHILDHANDLERBASE_H_ +#ifndef FSFW_DEVICEHANDLER_CHILDHANDLERBASE_H_ +#define FSFW_DEVICEHANDLER_CHILDHANDLERBASE_H_ -#include "ChildHandlerFDIR.h" #include "DeviceHandlerBase.h" +#include "ChildHandlerFDIR.h" class ChildHandlerBase: public DeviceHandlerBase { public: ChildHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication, - CookieIF * cookie, uint32_t thermalStatePoolId, - uint32_t thermalRequestPoolId, + CookieIF * cookie, object_id_t hkDestination, + uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId, object_id_t parent = objects::NO_OBJECT, FailureIsolationBase* customFdir = nullptr, size_t cmdQueueSize = 20); + virtual ~ChildHandlerBase(); virtual ReturnValue_t initialize(); @@ -22,5 +23,4 @@ protected: }; -#endif /* FSFW_DEVICES_CHILDHANDLERBASE_H_ */ - +#endif /* FSFW_DEVICEHANDLER_CHILDHANDLERBASE_H_ */ diff --git a/devicehandlers/ChildHandlerFDIR.cpp b/devicehandlers/ChildHandlerFDIR.cpp index 14043a77..cb4c75ea 100644 --- a/devicehandlers/ChildHandlerFDIR.cpp +++ b/devicehandlers/ChildHandlerFDIR.cpp @@ -1,6 +1,7 @@ #include "ChildHandlerFDIR.h" -ChildHandlerFDIR::ChildHandlerFDIR(object_id_t owner, object_id_t faultTreeParent, uint32_t recoveryCount) : +ChildHandlerFDIR::ChildHandlerFDIR(object_id_t owner, + object_id_t faultTreeParent, uint32_t recoveryCount) : DeviceHandlerFailureIsolation(owner, faultTreeParent) { recoveryCounter.setFailureThreshold(recoveryCount); } diff --git a/devicehandlers/ChildHandlerFDIR.h b/devicehandlers/ChildHandlerFDIR.h index ce844518..13d1345c 100644 --- a/devicehandlers/ChildHandlerFDIR.h +++ b/devicehandlers/ChildHandlerFDIR.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_DEVICEHANDLERS_CHILDHANDLERFDIR_H_ -#define FRAMEWORK_DEVICEHANDLERS_CHILDHANDLERFDIR_H_ +#ifndef FSFW_DEVICEHANDLERS_CHILDHANDLERFDIR_H_ +#define FSFW_DEVICEHANDLERS_CHILDHANDLERFDIR_H_ #include "DeviceHandlerFailureIsolation.h" diff --git a/devicehandlers/CookieIF.h b/devicehandlers/CookieIF.h index 496cf0d2..5911bfdb 100644 --- a/devicehandlers/CookieIF.h +++ b/devicehandlers/CookieIF.h @@ -1,11 +1,12 @@ -#ifndef COOKIE_H_ -#define COOKIE_H_ +#ifndef FSFW_DEVICEHANDLER_COOKIE_H_ +#define FSFW_DEVICEHANDLER_COOKIE_H_ + #include /** * @brief Physical address type */ -typedef std::uint32_t address_t; +using address_t = uint32_t; /** * @brief This datatype is used to identify different connection over a @@ -16,7 +17,6 @@ typedef std::uint32_t address_t; * calling @code{.cpp} CookieIF* childCookie = new ChildCookie(...) * @endcode . * - * [not implemented yet] * This cookie is then passed to the child device handlers, which stores the * pointer and passes it to the communication interface functions. * @@ -31,4 +31,4 @@ public: virtual ~CookieIF() {}; }; -#endif /* COOKIE_H_ */ +#endif /* FSFW_DEVICEHANDLER_COOKIE_H_ */ diff --git a/devicehandlers/DeviceCommunicationIF.h b/devicehandlers/DeviceCommunicationIF.h index 11960d8e..e0b473d3 100644 --- a/devicehandlers/DeviceCommunicationIF.h +++ b/devicehandlers/DeviceCommunicationIF.h @@ -1,9 +1,10 @@ -#ifndef DEVICECOMMUNICATIONIF_H_ -#define DEVICECOMMUNICATIONIF_H_ +#ifndef FSFW_DEVICES_DEVICECOMMUNICATIONIF_H_ +#define FSFW_DEVICES_DEVICECOMMUNICATIONIF_H_ #include "CookieIF.h" +#include "DeviceHandlerIF.h" + #include "../returnvalues/HasReturnvaluesIF.h" -#include /** * @defgroup interfaces Interfaces * @brief Interfaces for flight software objects @@ -19,8 +20,8 @@ * 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: + * 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 @@ -38,24 +39,20 @@ class DeviceCommunicationIF: public HasReturnvaluesIF { public: static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_COMMUNICATION_IF; - //! Standard Error Codes + //! This is returned in readReceivedMessage() if no reply was reived. + static const ReturnValue_t NO_REPLY_RECEIVED = MAKE_RETURN_CODE(0x01); + //! General protocol error. Define more concrete errors in child handler - static const ReturnValue_t PROTOCOL_ERROR = MAKE_RETURN_CODE(0x01); + static const ReturnValue_t PROTOCOL_ERROR = MAKE_RETURN_CODE(0x02); //! If cookie is a null pointer - static const ReturnValue_t NULLPOINTER = MAKE_RETURN_CODE(0x02); - static const ReturnValue_t INVALID_COOKIE_TYPE = MAKE_RETURN_CODE(0x03); + static const ReturnValue_t NULLPOINTER = MAKE_RETURN_CODE(0x03); + static const ReturnValue_t INVALID_COOKIE_TYPE = MAKE_RETURN_CODE(0x04); // is this needed if there is no open/close call? static const ReturnValue_t NOT_ACTIVE = MAKE_RETURN_CODE(0x05); - static const ReturnValue_t INVALID_ADDRESS = MAKE_RETURN_CODE(0x06); - static const ReturnValue_t TOO_MUCH_DATA = MAKE_RETURN_CODE(0x07); - static const ReturnValue_t CANT_CHANGE_REPLY_LEN = MAKE_RETURN_CODE(0x08); - - //! Can be used in readReceivedMessage() if no reply was received. - static const ReturnValue_t NO_REPLY_RECEIVED = MAKE_RETURN_CODE(0xA1); + static const ReturnValue_t TOO_MUCH_DATA = MAKE_RETURN_CODE(0x06); virtual ~DeviceCommunicationIF() {} - /** * @brief Device specific initialization, using the cookie. * @details @@ -76,13 +73,13 @@ public: * by implementing and calling related drivers or wrapper functions. * @param cookie * @param data - * @param len + * @param len If this is 0, nothing shall be sent. * @return * - @c RETURN_OK for successfull send * - Everything else triggers failure event with returnvalue as parameter 1 */ - virtual ReturnValue_t sendMessage(CookieIF *cookie, const uint8_t * sendData, - size_t sendLen) = 0; + virtual ReturnValue_t sendMessage(CookieIF *cookie, + const uint8_t * sendData, size_t sendLen) = 0; /** * Called by DHB in the GET_WRITE doGetWrite(). @@ -108,7 +105,7 @@ public: * returnvalue as parameter 1 */ virtual ReturnValue_t requestReceiveMessage(CookieIF *cookie, - size_t requestLen) = 0; + size_t requestLen) = 0; /** * Called by DHB in the GET_WRITE doGetRead(). @@ -124,8 +121,8 @@ public: * - Everything else triggers failure event with * returnvalue as parameter 1 */ - virtual ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, - size_t *size) = 0; + virtual ReturnValue_t readReceivedMessage(CookieIF *cookie, + uint8_t **buffer, size_t *size) = 0; }; -#endif /* DEVICECOMMUNICATIONIF_H_ */ +#endif /* FSFW_DEVICES_DEVICECOMMUNICATIONIF_H_ */ diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 017ea8d2..227d5b6d 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -2,15 +2,17 @@ #include "AcceptsDeviceResponsesIF.h" #include "DeviceTmReportingWrapper.h" +#include "../serviceinterface/ServiceInterfaceStream.h" +#include "../datapoolglob/GlobalDataSet.h" +#include "../datapoolglob/GlobalPoolVariable.h" #include "../objectmanager/ObjectManager.h" #include "../storagemanager/StorageManagerIF.h" #include "../thermal/ThermalComponentIF.h" -#include "../datapool/DataSet.h" -#include "../datapool/PoolVariable.h" #include "../globalfunctions/CRC.h" -#include "../subsystem/SubsystemBase.h" +#include "../housekeeping/HousekeepingMessage.h" +#include "../ipc/MessageQueueMessage.h" #include "../ipc/QueueFactory.h" -#include "../serviceinterface/ServiceInterfaceStream.h" +#include "../subsystem/SubsystemBase.h" #include @@ -25,10 +27,11 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, wiretappingMode(OFF), storedRawData(StorageManagerIF::INVALID_ADDRESS), deviceCommunicationId(deviceCommunication), comCookie(comCookie), healthHelper(this,setObjectId), modeHelper(this), parameterHelper(this), - actionHelper(this, nullptr), childTransitionFailure(RETURN_OK), - fdirInstance(fdirInstance), hkSwitcher(this), - defaultFDIRUsed(fdirInstance == nullptr), switchOffWasReported(false), - childTransitionDelay(5000), transitionSourceMode(_MODE_POWER_DOWN), + actionHelper(this, nullptr), hkManager(this, nullptr), + childTransitionFailure(RETURN_OK), fdirInstance(fdirInstance), + hkSwitcher(this), defaultFDIRUsed(fdirInstance == nullptr), + switchOffWasReported(false), childTransitionDelay(5000), + transitionSourceMode(_MODE_POWER_DOWN), transitionSourceSubMode(SUBMODE_NONE) { commandQueue = QueueFactory::instance()->createMessageQueue(cmdQueueSize, MessageQueueMessage::MAX_MESSAGE_SIZE); @@ -48,6 +51,10 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, } } +void DeviceHandlerBase::setHkDestination(object_id_t hkDestination) { + this->hkDestination = hkDestination; +} + void DeviceHandlerBase::setThermalStateRequestPoolIds( uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId) { this->deviceThermalRequestPoolId = thermalStatePoolId; @@ -74,6 +81,7 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { decrementDeviceReplyMap(); fdirInstance->checkForFailures(); hkSwitcher.performOperation(); + hkManager.performHkOperation(); performOperationHook(); } if (mode == MODE_OFF) { @@ -120,7 +128,9 @@ ReturnValue_t DeviceHandlerBase::initialize() { result = communicationInterface->initializeInterface(comCookie); if (result != RETURN_OK) { - return result; + sif::error << "DeviceHandlerBase::initialize: Initializing " + "communication interface failed!" << std::endl; + return result; } IPCStore = objectManager->get(objects::IPC_STORE); @@ -183,11 +193,16 @@ ReturnValue_t DeviceHandlerBase::initialize() { return result; } + result = hkManager.initialize(commandQueue); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + fillCommandAndReplyMap(); //Set temperature target state to NON_OP. - DataSet mySet; - db_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, + GlobDataSet mySet; + gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_WRITE); mySet.read(); thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; @@ -245,10 +260,10 @@ void DeviceHandlerBase::readCommandQueue() { return; } -// result = hkManager.handleHousekeepingMessage(&command); -// if (result == RETURN_OK) { -// return; -// } + result = hkManager.handleHousekeepingMessage(&command); + if (result == RETURN_OK) { + return; + } result = handleDeviceHandlerMessage(&command); if (result == RETURN_OK) { @@ -376,24 +391,28 @@ ReturnValue_t DeviceHandlerBase::isModeCombinationValid(Mode_t mode, ReturnValue_t DeviceHandlerBase::insertInCommandAndReplyMap( DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, - size_t replyLen, bool periodic, bool hasDifferentReplyId, - DeviceCommandId_t replyId) { + LocalPoolDataSetBase* replyDataSet, size_t replyLen, bool periodic, + bool hasDifferentReplyId, DeviceCommandId_t replyId) { //No need to check, as we may try to insert multiple times. insertInCommandMap(deviceCommand); if (hasDifferentReplyId) { - return insertInReplyMap(replyId, maxDelayCycles, replyLen, periodic); + return insertInReplyMap(replyId, maxDelayCycles, + replyDataSet, replyLen, periodic); } else { - return insertInReplyMap(deviceCommand, maxDelayCycles, replyLen, periodic); + return insertInReplyMap(deviceCommand, maxDelayCycles, + replyDataSet, replyLen, periodic); } } ReturnValue_t DeviceHandlerBase::insertInReplyMap(DeviceCommandId_t replyId, - uint16_t maxDelayCycles, size_t replyLen, bool periodic) { + uint16_t maxDelayCycles, LocalPoolDataSetBase* dataSet, + size_t replyLen, bool periodic) { DeviceReplyInfo info; info.maxDelayCycles = maxDelayCycles; info.periodic = periodic; info.delayCycles = 0; info.replyLen = replyLen; + info.dataSet = dataSet; info.command = deviceCommandMap.end(); auto resultPair = deviceReplyMap.emplace(replyId, info); if (resultPair.second) { @@ -419,13 +438,12 @@ ReturnValue_t DeviceHandlerBase::insertInCommandMap( ReturnValue_t DeviceHandlerBase::updateReplyMapEntry(DeviceCommandId_t deviceReply, uint16_t delayCycles, uint16_t maxDelayCycles, bool periodic) { - std::map::iterator iter = - deviceReplyMap.find(deviceReply); - if (iter == deviceReplyMap.end()) { + auto replyIter = deviceReplyMap.find(deviceReply); + if (replyIter == deviceReplyMap.end()) { triggerEvent(INVALID_DEVICE_COMMAND, deviceReply); return RETURN_FAILED; } else { - DeviceReplyInfo *info = &(iter->second); + DeviceReplyInfo *info = &(replyIter->second); if (maxDelayCycles != 0) { info->maxDelayCycles = maxDelayCycles; } @@ -435,6 +453,17 @@ ReturnValue_t DeviceHandlerBase::updateReplyMapEntry(DeviceCommandId_t deviceRep } } + +ReturnValue_t DeviceHandlerBase::setReplyDataset(DeviceCommandId_t replyId, + LocalPoolDataSetBase *dataSet) { + auto replyIter = deviceReplyMap.find(replyId); + if(replyIter == deviceReplyMap.end()) { + return HasReturnvaluesIF::RETURN_FAILED; + } + replyIter->second.dataSet = dataSet; + return HasReturnvaluesIF::RETURN_OK; +} + void DeviceHandlerBase::callChildStatemachine() { if (mode == _MODE_START_UP) { doStartUp(); @@ -469,8 +498,8 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) { Clock::getUptime(&timeoutStart); if (mode == MODE_OFF) { - DataSet mySet; - db_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, + GlobDataSet mySet; + gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_READ_WRITE); mySet.read(); if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { @@ -649,7 +678,7 @@ void DeviceHandlerBase::doGetRead() { void DeviceHandlerBase::parseReply(const uint8_t* receivedData, size_t receivedDataLen) { ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; - DeviceCommandId_t foundId = 0xFFFFFFFF; + DeviceCommandId_t foundId = 0xffffffff; size_t foundLen = 0; // The loop may not execute more often than the number of received bytes // (worst case). This approach avoids infinite loops due to buggy @@ -661,18 +690,26 @@ void DeviceHandlerBase::parseReply(const uint8_t* receivedData, switch (result) { case RETURN_OK: handleReply(receivedData, foundId, foundLen); + if(foundLen == 0) { + sif::warning << "DeviceHandlerBase::parseReply: foundLen is 0!" + " Packet parsing will be stuck." << std::endl; + } break; case APERIODIC_REPLY: { result = interpretDeviceReply(foundId, receivedData); if (result != RETURN_OK) { - replyRawReplyIfnotWiretapped(receivedData, foundLen); - triggerEvent(DEVICE_INTERPRETING_REPLY_FAILED, result, - foundId); + replyRawReplyIfnotWiretapped(receivedData, foundLen); + triggerEvent(DEVICE_INTERPRETING_REPLY_FAILED, result, + foundId); + } + if(foundLen == 0) { + sif::warning << "DeviceHandlerBase::parseReply: foundLen is 0!" + " Packet parsing will be stuck." << std::endl; } - } - break; - case IGNORE_REPLY_DATA: break; + } + case IGNORE_REPLY_DATA: + continue; case IGNORE_FULL_PACKET: return; default: @@ -704,16 +741,19 @@ void DeviceHandlerBase::handleReply(const uint8_t* receivedData, DeviceReplyInfo *info = &(iter->second); if (info->delayCycles != 0) { + result = interpretDeviceReply(foundId, receivedData); - if (info->periodic != false) { + if(result == IGNORE_REPLY_DATA) { + return; + } + + if (info->periodic) { info->delayCycles = info->maxDelayCycles; } else { info->delayCycles = 0; } - result = interpretDeviceReply(foundId, receivedData); - if (result != RETURN_OK) { // Report failed interpretation to FDIR. replyRawReplyIfnotWiretapped(receivedData, foundLen); @@ -926,10 +966,10 @@ ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, if ((commandedMode == MODE_ON) && (mode == MODE_OFF) && (deviceThermalStatePoolId != PoolVariableIF::NO_PARAMETER)) { - DataSet mySet; - db_int8_t thermalState(deviceThermalStatePoolId, &mySet, + GlobDataSet mySet; + gp_uint8_t thermalState(deviceThermalStatePoolId, &mySet, PoolVariableIF::VAR_READ); - db_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, + gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_READ); mySet.read(); if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { @@ -956,8 +996,8 @@ void DeviceHandlerBase::startTransition(Mode_t commandedMode, childTransitionDelay = getTransitionDelayMs(_MODE_START_UP, MODE_ON); triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); - DataSet mySet; - db_int8_t thermalRequest(deviceThermalRequestPoolId, + GlobDataSet mySet; + gp_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, PoolVariableIF::VAR_READ_WRITE); mySet.read(); if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { @@ -1089,19 +1129,6 @@ ReturnValue_t DeviceHandlerBase::handleDeviceHandlerMessage( } replyReturnvalueToCommand(RETURN_OK); return RETURN_OK; -// case DeviceHandlerMessage::CMD_SWITCH_IOBOARD: -// if (mode != MODE_OFF) { -// replyReturnvalueToCommand(WRONG_MODE_FOR_COMMAND); -// } else { -// result = switchCookieChannel( -// DeviceHandlerMessage::getIoBoardObjectId(message)); -// if (result == RETURN_OK) { -// replyReturnvalueToCommand(RETURN_OK); -// } else { -// replyReturnvalueToCommand(CANT_SWITCH_IO_ADDRESS); -// } -// } -// return RETURN_OK; case DeviceHandlerMessage::CMD_RAW: if ((mode != MODE_RAW)) { DeviceHandlerMessage::clear(message); @@ -1185,7 +1212,7 @@ void DeviceHandlerBase::handleDeviceTM(SerializeIF* data, } //Try to cast to GlobDataSet and commit data. if (!neverInDataPool) { - DataSet* dataSet = dynamic_cast(data); + GlobDataSet* dataSet = dynamic_cast(data); if (dataSet != NULL) { dataSet->commit(PoolVariableIF::VALID); } @@ -1248,10 +1275,14 @@ void DeviceHandlerBase::buildInternalCommand(void) { if (iter == deviceCommandMap.end()) { result = COMMAND_NOT_SUPPORTED; } else if (iter->second.isExecuting) { + //so we can track misconfigurations sif::debug << std::hex << getObjectId() << ": DHB::buildInternalCommand: Command " - << deviceCommandId << " isExecuting" << std::endl; //so we can track misconfigurations - return; //this is an internal command, no need to report a failure here, missed reply will track if a reply is too late, otherwise, it's ok + << deviceCommandId << " isExecuting" << std::dec + << std::endl; + // this is an internal command, no need to report a failure here, + // missed reply will track if a reply is too late, otherwise, it's ok + return; } else { iter->second.sendReplyTo = NO_COMMANDER; iter->second.isExecuting = true; @@ -1340,12 +1371,50 @@ void DeviceHandlerBase::debugInterface(uint8_t positionTracker, void DeviceHandlerBase::performOperationHook() { } +ReturnValue_t DeviceHandlerBase::initializeLocalDataPool( + LocalDataPool &localDataPoolMap, + LocalDataPoolManager& poolManager) { + return RETURN_OK; +} + +LocalDataPoolManager* DeviceHandlerBase::getHkManagerHandle() { + return &hkManager; +} + + ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() { // In this function, the task handle should be valid if the task // was implemented correctly. We still check to be 1000 % sure :-) if(executingTask != nullptr) { pstIntervalMs = executingTask->getPeriodMs(); } + this->hkManager.initializeAfterTaskCreation(); + + if(setStartupImmediately) { + startTransition(MODE_ON, SUBMODE_NONE); + } return HasReturnvaluesIF::RETURN_OK; } +LocalPoolDataSetBase* DeviceHandlerBase::getDataSetHandle(sid_t sid) { + auto iter = deviceReplyMap.find(sid.ownerSetId); + if(iter != deviceReplyMap.end()) { + return iter->second.dataSet; + } + else { + return nullptr; + } +} + +object_id_t DeviceHandlerBase::getObjectId() const { + return SystemObject::getObjectId(); +} + +void DeviceHandlerBase::setStartUpImmediately() { + this->setStartupImmediately = true; +} + +dur_millis_t DeviceHandlerBase::getPeriodicOperationFrequency() const { + return pstIntervalMs; +} + diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index 5666f35e..eda318cb 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -1,12 +1,11 @@ -#ifndef FRAMEWORK_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ -#define FRAMEWORK_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ +#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ +#define FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ #include "DeviceHandlerIF.h" #include "DeviceCommunicationIF.h" #include "DeviceHandlerFailureIsolation.h" #include "../objectmanager/SystemObject.h" -#include "../tasks/PeriodicTaskIF.h" #include "../tasks/ExecutableObjectIF.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../action/HasActionsIF.h" @@ -14,10 +13,13 @@ #include "../modes/HasModesIF.h" #include "../power/PowerSwitchIF.h" #include "../ipc/MessageQueueIF.h" +#include "../tasks/PeriodicTaskIF.h" #include "../action/ActionHelper.h" #include "../health/HealthHelper.h" #include "../parameters/ParameterHelper.h" #include "../datapool/HkSwitchHelper.h" +#include "../datapoollocal/HasLocalDataPoolIF.h" +#include "../datapoollocal/LocalDataPoolManager.h" #include @@ -38,17 +40,16 @@ class StorageManagerIF; * Documentation: Dissertation Baetz p.138,139, p.141-149 * * 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. + * 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 (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. + * This class uses the opcode of ExecutableObjectIF to perform a + * step-wise execution. For each step a different action is selected and + * executed. Currently, the device handler base performs a 4-step + * execution related to 4 communication steps (based on RMAP). + * NOTE: RMAP is a standard which is used for Flying Laptop. * 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. @@ -69,9 +70,6 @@ class StorageManagerIF; * * Other important virtual methods with a default implementation * are the getTransitionDelayMs() function and the getSwitches() function. - * Please ensure that getSwitches() returns DeviceHandlerIF::NO_SWITCHES if - * power switches are not implemented yet. Otherwise, the device handler will - * not transition to MODE_ON, even if setMode(MODE_ON) is called. * If a transition to MODE_ON is desired without commanding, override the * intialize() function and call setMode(_MODE_START_UP) before calling * DeviceHandlerBase::initialize(). @@ -85,20 +83,18 @@ class DeviceHandlerBase: public DeviceHandlerIF, public HasModesIF, public HasHealthIF, public HasActionsIF, - public ReceivesParameterMessagesIF { + public ReceivesParameterMessagesIF, + public HasLocalDataPoolIF { friend void (Factory::setStaticFrameworkObjectIds)(); 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 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 comCookie This object will be passed to the communication inter- + * face and can contain user-defined information about the communication. * @param fdirInstance * @param cmdQueueSize */ @@ -106,8 +102,21 @@ public: CookieIF * comCookie, FailureIsolationBase* fdirInstance = nullptr, size_t cmdQueueSize = 20); + void setHkDestination(object_id_t hkDestination); void setThermalStateRequestPoolIds(uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId); + /** + * @brief Helper function to ease device handler development. + * This will instruct the transition to MODE_ON immediately + * (leading to doStartUp() being called for the transition to the ON mode), + * so external mode commanding is not necessary anymore. + * + * This has to be called before the task is started! + * (e.g. in the task factory). This is only a helper function for + * development. Regular mode commanding should be performed by commanding + * the AssemblyBase or Subsystem objects resposible for the device handler. + */ + void setStartUpImmediately(); /** * @brief This function is the device handler base core component and is @@ -153,6 +162,14 @@ public: * @return */ virtual ReturnValue_t initialize(); + + /** + * @brief Intialization steps performed after all tasks have been created. + * This function will be called by the executing task. + * @return + */ + virtual ReturnValue_t initializeAfterTaskCreation() override; + /** Destructor. */ virtual ~DeviceHandlerBase(); @@ -320,6 +337,8 @@ protected: * @param packet * @return * - @c RETURN_OK when the reply was interpreted. + * - @c IGNORE_REPLY_DATA Ignore the reply and don't reset reply cycle + * counter. * - @c RETURN_FAILED when the reply could not be interpreted, * e.g. logical errors or range violations occurred */ @@ -347,22 +366,10 @@ protected: * 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). - * - * (Robin) This part confuses me. "must do as soon as" implies that - * the developer must do something somewhere else in the code. Is - * that really the case? If I understood correctly, DHB performs - * almost everything (e.g. in erirm function) as long as the commands - * are inserted correctly. - * - * As soon as the replies are enabled, DeviceCommandInfo.periodic must - * be set to true, 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 @@ -376,13 +383,17 @@ protected: * @param deviceCommand Identifier of the command to add. * @param maxDelayCycles The maximum number of delay cycles the command * waits until it times out. + * @param replyLen Will be supplied to the requestReceiveMessage call of + * the communication interface. * @param periodic Indicates if the command is periodic (i.e. it is sent * by the device repeatedly without request) or not. Default is aperiodic (0) * @return - @c RETURN_OK when the command was successfully inserted, * - @c RETURN_FAILED else. */ ReturnValue_t insertInCommandAndReplyMap(DeviceCommandId_t deviceCommand, - uint16_t maxDelayCycles, size_t replyLen = 0, bool periodic = false, + uint16_t maxDelayCycles, + LocalPoolDataSetBase* replyDataSet = nullptr, + size_t replyLen = 0, bool periodic = false, bool hasDifferentReplyId = false, DeviceCommandId_t replyId = 0); /** @@ -396,7 +407,8 @@ protected: * - @c RETURN_FAILED else. */ ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand, - uint16_t maxDelayCycles, size_t replyLen = 0, bool periodic = false); + uint16_t maxDelayCycles, LocalPoolDataSetBase* dataSet = nullptr, + size_t replyLen = 0, bool periodic = false); /** * @brief A simple command to add a command to the commandList. @@ -424,6 +436,9 @@ protected: uint16_t delayCycles, uint16_t maxDelayCycles, bool periodic = false); + ReturnValue_t setReplyDataset(DeviceCommandId_t replyId, + LocalPoolDataSetBase* dataset); + /** * @brief Can be implemented by child handler to * perform debugging @@ -477,18 +492,22 @@ protected: * @param localDataPoolMap * @return */ - //virtual ReturnValue_t initializePoolEntries( - // LocalDataPool& localDataPoolMap) override; + virtual ReturnValue_t initializeLocalDataPool(LocalDataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; /** Get the HK manager object handle */ - //virtual LocalDataPoolManager* getHkManagerHandle() override; + virtual LocalDataPoolManager* getHkManagerHandle() override; /** * @brief Hook function for child handlers which is called once per * performOperation(). Default implementation is empty. */ virtual void performOperationHook(); + public: + /** Explicit interface implementation of getObjectId */ + virtual object_id_t getObjectId() const override; + /** * @param parentQueueId */ @@ -608,7 +627,7 @@ protected: /** Action helper for HasActionsIF */ ActionHelper actionHelper; /** Housekeeping Manager */ - //LocalDataPoolManager hkManager; + LocalDataPoolManager hkManager; /** * @brief Information about commands @@ -647,7 +666,7 @@ protected: //! The dataset used to access housekeeping data related to the //! respective device reply. Will point to a dataset held by //! the child handler (if one is specified) - // DataSetIF* dataSet = nullptr; + LocalPoolDataSetBase* dataSet; //! The command that expects this reply. DeviceCommandMap::iterator command; }; @@ -689,14 +708,18 @@ protected: uint32_t deviceThermalRequestPoolId = PoolVariableIF::NO_PARAMETER; /** - * Optional Error code - * Can be set in doStartUp(), doShutDown() and doTransition() to signal cause for Transition failure. + * Optional Error code. Can be set in doStartUp(), doShutDown() and + * doTransition() to signal cause for Transition failure. */ ReturnValue_t childTransitionFailure; - uint32_t ignoreMissedRepliesCount = 0; //!< Counts if communication channel lost a reply, so some missed replys can be ignored. + /** Counts if communication channel lost a reply, so some missed + * replys can be ignored. */ + uint32_t ignoreMissedRepliesCount = 0; - FailureIsolationBase* fdirInstance; //!< Pointer to the used FDIR instance. If not provided by child, default class is instantiated. + /** Pointer to the used FDIR instance. If not provided by child, + * default class is instantiated. */ + FailureIsolationBase* fdirInstance; HkSwitchHelper hkSwitcher; @@ -944,14 +967,17 @@ protected: virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode); - virtual void startTransition(Mode_t mode, Submode_t submode); - virtual void setToExternalControl(); - virtual void announceMode(bool recursive); + + /* HasModesIF overrides */ + virtual void startTransition(Mode_t mode, Submode_t submode) override; + virtual void setToExternalControl() override; + virtual void announceMode(bool recursive) override; virtual ReturnValue_t letChildHandleMessage(CommandMessage *message); /** - * Overwrites SystemObject::triggerEvent in order to inform FDIR"Helper" faster about executed events. + * Overwrites SystemObject::triggerEvent in order to inform FDIR"Helper" + * faster about executed events. * This is a bit sneaky, but improves responsiveness of the device FDIR. * @param event The event to be thrown * @param parameter1 Optional parameter 1 @@ -1034,12 +1060,17 @@ private: /** the object used to set power switches */ PowerSwitchIF *powerSwitcher = nullptr; + /** HK destination can also be set individually */ + object_id_t hkDestination = objects::NO_OBJECT; + /** * @brief Used for timing out mode transitions. * Set when setMode() is called. */ uint32_t timeoutStart = 0; + bool setStartupImmediately = false; + /** * Delay for the current mode transition, used for time out */ @@ -1080,11 +1111,6 @@ private: 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. * @@ -1111,10 +1137,14 @@ private: /** * Build and send a command to the device. * - * This routine checks whether a raw or direct command has been received, checks the content of the received command and - * calls buildCommandFromCommand() for direct commands or sets #rawpacket to the received raw packet. - * If no external command is received or the received command is invalid and the current mode is @c MODE_NORMAL or a transitional mode, - * it asks the child class to build a command (via getNormalDeviceCommand() or getTransitionalDeviceCommand() and buildCommand()) and + * This routine checks whether a raw or direct command has been received, + * checks the content of the received command and calls + * buildCommandFromCommand() for direct commands or sets #rawpacket + * to the received raw packet. + * If no external command is received or the received command is invalid and + * the current mode is @c MODE_NORMAL or a transitional mode, it asks the + * child class to build a command (via getNormalDeviceCommand() or + * getTransitionalDeviceCommand() and buildCommand()) and * sends the command via RMAP. */ void doSendWrite(void); @@ -1159,7 +1189,6 @@ private: ReturnValue_t getStorageData(store_address_t storageAddress, uint8_t **data, uint32_t *len); - /** * @param modeTo either @c MODE_ON, MODE_NORMAL or MODE_RAW NOTHING ELSE!!! */ @@ -1170,25 +1199,14 @@ private: */ void callChildStatemachine(); - /** - * Switches the channel of the cookie used for the communication - * - * - * @param newChannel the object Id of the channel to switch to - * @return - * - @c RETURN_OK when cookie was changed - * - @c RETURN_FAILED when cookies could not be changed, eg because the newChannel is not enabled - * - @c returnvalues of RMAPChannelIF::isActive() - */ - ReturnValue_t switchCookieChannel(object_id_t newChannelId); - ReturnValue_t handleDeviceHandlerMessage(CommandMessage *message); - virtual ReturnValue_t initializeAfterTaskCreation() override; + virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; - void parseReply(const uint8_t* receivedData, - size_t receivedDataLen); + virtual dur_millis_t getPeriodicOperationFrequency() const override; + + void parseReply(const uint8_t* receivedData, + size_t receivedDataLen); }; -#endif /* FRAMEWORK_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */ - +#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */ diff --git a/devicehandlers/DeviceHandlerFailureIsolation.cpp b/devicehandlers/DeviceHandlerFailureIsolation.cpp index 9fbe71d8..24fe15a0 100644 --- a/devicehandlers/DeviceHandlerFailureIsolation.cpp +++ b/devicehandlers/DeviceHandlerFailureIsolation.cpp @@ -247,6 +247,14 @@ bool DeviceHandlerFailureIsolation::isFdirInActionOrAreWeFaulty( } return true; } + + if (owner == nullptr) { + // Configuration error. + sif::error << "DeviceHandlerFailureIsolation::" + << "isFdirInActionOrAreWeFaulty: Owner not set!" << std::endl; + return false; + } + if (owner->getHealth() == HasHealthIF::FAULTY || owner->getHealth() == HasHealthIF::PERMANENT_FAULTY) { //Ignore all events in case device is already faulty. diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index 52a3be4d..9c8eb098 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -1,12 +1,19 @@ -#ifndef DEVICEHANDLERIF_H_ -#define DEVICEHANDLERIF_H_ +#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERIF_H_ +#define FSFW_DEVICEHANDLERS_DEVICEHANDLERIF_H_ + +#include "DeviceHandlerMessage.h" #include "../action/HasActionsIF.h" -#include "DeviceHandlerMessage.h" #include "../events/Event.h" #include "../modes/HasModesIF.h" #include "../ipc/MessageQueueSenderIF.h" +/** + * This is used to uniquely identify commands that are sent to a device + * The values are defined in the device-specific implementations + */ +using DeviceCommandId_t = uint32_t; + /** * @brief This is the Interface used to communicate with a device handler. * @details Includes all expected return values, events and modes. @@ -15,6 +22,7 @@ class DeviceHandlerIF { public: + static const uint8_t TRANSITION_MODE_CHILD_ACTION_MASK = 0x20; static const uint8_t TRANSITION_MODE_BASE_ACTION_MASK = 0x10; @@ -47,6 +55,8 @@ public: //! 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. + //! It is possible to set the mode to _MODE_SHUT_DOWN to use the to off + //! transition if available. static const Mode_t _MODE_SHUT_DOWN = TRANSITION_MODE_CHILD_ACTION_MASK | 6; //! It is possible to set the mode to _MODE_TO_ON to use the to on //! transition if available. @@ -96,7 +106,7 @@ public: static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_IF; // Standard codes used when building commands. - static const ReturnValue_t NO_COMMAND_DATA = MAKE_RETURN_CODE(0xA0); //!< If the command size is 0. Checked in DHB + static const ReturnValue_t NO_COMMAND_DATA = MAKE_RETURN_CODE(0xA0); //!< If no command data was given when expected. static const ReturnValue_t COMMAND_NOT_SUPPORTED = MAKE_RETURN_CODE(0xA1); //!< Command ID not in commandMap. Checked in DHB static const ReturnValue_t COMMAND_ALREADY_SENT = MAKE_RETURN_CODE(0xA2); //!< Command was already executed. Checked in DHB static const ReturnValue_t COMMAND_WAS_NOT_SENT = MAKE_RETURN_CODE(0xA3); @@ -150,4 +160,4 @@ public: }; -#endif /* DEVICEHANDLERIF_H_ */ +#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERIF_H_ */ diff --git a/devicehandlers/DeviceHandlerMessage.cpp b/devicehandlers/DeviceHandlerMessage.cpp index 564fae21..cb9043db 100644 --- a/devicehandlers/DeviceHandlerMessage.cpp +++ b/devicehandlers/DeviceHandlerMessage.cpp @@ -1,10 +1,6 @@ -#include "../objectmanager/ObjectManagerIF.h" #include "DeviceHandlerMessage.h" #include "../objectmanager/ObjectManagerIF.h" -DeviceHandlerMessage::DeviceHandlerMessage() { -} - store_address_t DeviceHandlerMessage::getStoreAddress( const CommandMessage* message) { return store_address_t(message->getParameter2()); @@ -25,14 +21,6 @@ uint8_t DeviceHandlerMessage::getWiretappingMode( return message->getParameter(); } -//void DeviceHandlerMessage::setDeviceHandlerDirectCommandMessage( -// CommandMessage* message, DeviceCommandId_t deviceCommand, -// store_address_t commandParametersStoreId) { -// message->setCommand(CMD_DIRECT); -// message->setParameter(deviceCommand); -// message->setParameter2(commandParametersStoreId.raw); -//} - void DeviceHandlerMessage::setDeviceHandlerRawCommandMessage( CommandMessage* message, store_address_t rawPacketStoreId) { message->setCommand(CMD_RAW); @@ -47,7 +35,7 @@ void DeviceHandlerMessage::setDeviceHandlerWiretappingMessage( void DeviceHandlerMessage::setDeviceHandlerSwitchIoBoardMessage( CommandMessage* message, uint32_t ioBoardIdentifier) { - message->setCommand(CMD_SWITCH_IOBOARD); + message->setCommand(CMD_SWITCH_ADDRESS); message->setParameter(ioBoardIdentifier); } @@ -79,18 +67,17 @@ void DeviceHandlerMessage::setDeviceHandlerDirectCommandReply( void DeviceHandlerMessage::clear(CommandMessage* message) { switch (message->getCommand()) { case CMD_RAW: -// case CMD_DIRECT: case REPLY_RAW_COMMAND: case REPLY_RAW_REPLY: case REPLY_DIRECT_COMMAND_DATA: { StorageManagerIF *ipcStore = objectManager->get( objects::IPC_STORE); - if (ipcStore != NULL) { + if (ipcStore != nullptr) { ipcStore->deleteData(getStoreAddress(message)); } } /* NO BREAK falls through*/ - case CMD_SWITCH_IOBOARD: + case CMD_SWITCH_ADDRESS: case CMD_WIRETAPPING: message->setCommand(CommandMessage::CMD_NONE); message->setParameter(0); diff --git a/devicehandlers/DeviceHandlerMessage.h b/devicehandlers/DeviceHandlerMessage.h index 8d1c94f4..e5da01c8 100644 --- a/devicehandlers/DeviceHandlerMessage.h +++ b/devicehandlers/DeviceHandlerMessage.h @@ -1,69 +1,56 @@ -#ifndef DEVICEHANDLERMESSAGE_H_ -#define DEVICEHANDLERMESSAGE_H_ +#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERMESSAGE_H_ +#define FSFW_DEVICEHANDLERS_DEVICEHANDLERMESSAGE_H_ #include "../action/ActionMessage.h" #include "../ipc/CommandMessage.h" #include "../objectmanager/SystemObjectIF.h" #include "../storagemanager/StorageManagerIF.h" -//SHOULDDO: rework the static constructors to name the type of command they are building, maybe even hide setting the commandID. +// SHOULDDO: rework the static constructors to name the type of command +// they are building, maybe even hide setting the commandID. + /** - * This is used to uniquely identify commands that are sent to a device - * - * The values are defined in the device-specific implementations - */ -typedef uint32_t DeviceCommandId_t; - -/** - * The DeviceHandlerMessage is used to send Commands to a DeviceHandlerIF + * @brief The DeviceHandlerMessage is used to send commands to classes + * implementing DeviceHandlerIF */ class DeviceHandlerMessage { -private: - DeviceHandlerMessage(); public: + /** + * Instantiation forbidden. Instead, use static functions to operate + * on messages. + */ + DeviceHandlerMessage() = delete; + virtual ~DeviceHandlerMessage() {} /** * These are the commands that can be sent to a DeviceHandlerBase */ static const uint8_t MESSAGE_ID = messagetypes::DEVICE_HANDLER_COMMAND; - static const Command_t CMD_RAW = MAKE_COMMAND_ID( 1 ); //!< Sends a raw command, setParameter is a ::store_id_t containing the raw packet to send -// static const Command_t CMD_DIRECT = MAKE_COMMAND_ID( 2 ); //!< Sends a direct command, setParameter is a ::DeviceCommandId_t, setParameter2 is a ::store_id_t containing the data needed for the command - static const Command_t CMD_SWITCH_IOBOARD = MAKE_COMMAND_ID( 3 ); //!< Requests a IO-Board switch, setParameter() is the IO-Board identifier - static const Command_t CMD_WIRETAPPING = MAKE_COMMAND_ID( 4 ); //!< (De)Activates the monitoring of all raw traffic in DeviceHandlers, setParameter is 0 to deactivate, 1 to activate + //! Sends a raw command, setParameter is a storeId containing the + //! raw packet to send + static const Command_t CMD_RAW = MAKE_COMMAND_ID(1); + //! Requests a IO-Board switch, setParameter() is the IO-Board identifier + static const Command_t CMD_SWITCH_ADDRESS = MAKE_COMMAND_ID(3); + //! (De)Activates the monitoring of all raw traffic in DeviceHandlers, + //! setParameter is 0 to deactivate, 1 to activate + static const Command_t CMD_WIRETAPPING = MAKE_COMMAND_ID(4); - /*static const Command_t REPLY_SWITCHED_IOBOARD = MAKE_COMMAND_ID(1 );//!< Reply to a @c CMD_SWITCH_IOBOARD, indicates switch was successful, getParameter() contains the board switched to (0: nominal, 1: redundant) - static const Command_t REPLY_CANT_SWITCH_IOBOARD = MAKE_COMMAND_ID( 2); //!< Reply to a @c CMD_SWITCH_IOBOARD, indicating the switch could not be performed, getParameter() contains the error message - static const Command_t REPLY_WIRETAPPING = MAKE_COMMAND_ID( 3); //!< Reply to a @c CMD_WIRETAPPING, getParameter() is the current state, 1 enabled, 0 disabled - - static const Command_t REPLY_COMMAND_WAS_SENT = MAKE_COMMAND_ID(4 );//!< Reply to a @c CMD_RAW or @c CMD_DIRECT, indicates the command was successfully sent to the device, getParameter() contains the ::DeviceCommandId_t - static const Command_t REPLY_COMMAND_NOT_SUPPORTED = MAKE_COMMAND_ID(5 );//!< Reply to a @c CMD_DIRECT, the requested ::DeviceCommand_t is not supported, getParameter() contains the requested ::DeviceCommand_t, getParameter2() contains the ::DeviceCommandId_t - static const Command_t REPLY_COMMAND_WAS_NOT_SENT = MAKE_COMMAND_ID(6 );//!< Reply to a @c CMD_RAW or @c CMD_DIRECT, indicates the command was not sent, getParameter contains the RMAP Return code (@see rmap.h), getParameter2() contains the ::DeviceCommandId_t - - static const Command_t REPLY_COMMAND_ALREADY_SENT = MAKE_COMMAND_ID(7 );//!< Reply to a @c CMD_DIRECT, the requested ::DeviceCommand_t has already been sent to the device and not ye been answered - static const Command_t REPLY_WRONG_MODE_FOR_CMD = MAKE_COMMAND_ID(8 );//!< Reply to a @c CMD_RAW or @c CMD_DIRECT, indicates that the requested command can not be sent in the curent mode, getParameter() contains the DeviceHandlerCommand_t - static const Command_t REPLY_NO_DATA = MAKE_COMMAND_ID(9 ); //!< Reply to a CMD_RAW or @c CMD_DIRECT, indicates that the ::store_id_t was invalid, getParameter() contains the ::DeviceCommandId_t, getPrameter2() contains the error code - */ - static const Command_t REPLY_DIRECT_COMMAND_SENT = ActionMessage::STEP_SUCCESS; //!< Signals that a direct command was sent - static const Command_t REPLY_RAW_COMMAND = MAKE_COMMAND_ID(0x11 ); //!< Contains a raw command sent to the Device - static const Command_t REPLY_RAW_REPLY = MAKE_COMMAND_ID( 0x12); //!< Contains a raw reply from the Device, getParameter() is the ObjcetId of the sender, getParameter2() is a ::store_id_t containing the raw packet received + //! Signals that a direct command was sent + static const Command_t REPLY_DIRECT_COMMAND_SENT = ActionMessage::STEP_SUCCESS; + //! Contains a raw command sent to the Device + static const Command_t REPLY_RAW_COMMAND = MAKE_COMMAND_ID(0x11); + //! Contains a raw reply from the Device, getParameter() is the ObjcetId + //! of the sender, getParameter2() is a ::store_id_t containing the + //! raw packet received + static const Command_t REPLY_RAW_REPLY = MAKE_COMMAND_ID(0x12); static const Command_t REPLY_DIRECT_COMMAND_DATA = ActionMessage::DATA_REPLY; - /** - * Default Destructor - */ - virtual ~DeviceHandlerMessage() { - } - static store_address_t getStoreAddress(const CommandMessage* message); static uint32_t getDeviceCommandId(const CommandMessage* message); static object_id_t getDeviceObjectId(const CommandMessage *message); static object_id_t getIoBoardObjectId(const CommandMessage* message); static uint8_t getWiretappingMode(const CommandMessage* message); -// static void setDeviceHandlerDirectCommandMessage(CommandMessage* message, -// DeviceCommandId_t deviceCommand, -// store_address_t commandParametersStoreId); - static void setDeviceHandlerDirectCommandReply(CommandMessage* message, object_id_t deviceObjectid, store_address_t commandParametersStoreId); @@ -75,11 +62,6 @@ public: object_id_t deviceObjectid, store_address_t rawPacketStoreId, bool isCommand); -// static void setDeviceHandlerMessage(CommandMessage* message, -// Command_t command, DeviceCommandId_t deviceCommand, -// store_address_t commandParametersStoreId); -// static void setDeviceHandlerMessage(CommandMessage* message, -// Command_t command, store_address_t rawPacketStoreId); static void setDeviceHandlerWiretappingMessage(CommandMessage* message, uint8_t wiretappingMode); static void setDeviceHandlerSwitchIoBoardMessage(CommandMessage* message, @@ -88,4 +70,4 @@ public: static void clear(CommandMessage* message); }; -#endif /* DEVICEHANDLERMESSAGE_H_ */ +#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERMESSAGE_H_ */ diff --git a/devicehandlers/DeviceTmReportingWrapper.cpp b/devicehandlers/DeviceTmReportingWrapper.cpp index 84f926f0..c70f57d6 100644 --- a/devicehandlers/DeviceTmReportingWrapper.cpp +++ b/devicehandlers/DeviceTmReportingWrapper.cpp @@ -1,4 +1,3 @@ -#include "../serialize/SerializeAdapter.h" #include "DeviceTmReportingWrapper.h" #include "../serialize/SerializeAdapter.h" diff --git a/devicehandlers/DeviceTmReportingWrapper.h b/devicehandlers/DeviceTmReportingWrapper.h index a14c4261..447e95f2 100644 --- a/devicehandlers/DeviceTmReportingWrapper.h +++ b/devicehandlers/DeviceTmReportingWrapper.h @@ -1,5 +1,5 @@ -#ifndef DEVICETMREPORTINGWRAPPER_H_ -#define DEVICETMREPORTINGWRAPPER_H_ +#ifndef FSFW_DEVICEHANDLERS_DEVICETMREPORTINGWRAPPER_H_ +#define FSFW_DEVICEHANDLERS_DEVICETMREPORTINGWRAPPER_H_ #include "../action/HasActionsIF.h" #include "../objectmanager/SystemObjectIF.h" @@ -24,4 +24,4 @@ private: SerializeIF *data; }; -#endif /* DEVICETMREPORTINGWRAPPER_H_ */ +#endif /* FSFW_DEVICEHANDLERS_DEVICETMREPORTINGWRAPPER_H_ */ diff --git a/devicehandlers/HealthDevice.cpp b/devicehandlers/HealthDevice.cpp index b15e5d2b..418ed257 100644 --- a/devicehandlers/HealthDevice.cpp +++ b/devicehandlers/HealthDevice.cpp @@ -13,10 +13,10 @@ HealthDevice::~HealthDevice() { } ReturnValue_t HealthDevice::performOperation(uint8_t opCode) { - CommandMessage message; - ReturnValue_t result = commandQueue->receiveMessage(&message); + CommandMessage command; + ReturnValue_t result = commandQueue->receiveMessage(&command); if (result == HasReturnvaluesIF::RETURN_OK) { - healthHelper.handleHealthCommand(&message); + healthHelper.handleHealthCommand(&command); } return HasReturnvaluesIF::RETURN_OK; } diff --git a/devicehandlers/HealthDevice.h b/devicehandlers/HealthDevice.h index 53b0ab09..738f0c7e 100644 --- a/devicehandlers/HealthDevice.h +++ b/devicehandlers/HealthDevice.h @@ -1,5 +1,5 @@ -#ifndef HEALTHDEVICE_H_ -#define HEALTHDEVICE_H_ +#ifndef FSFW_DEVICEHANDLERS_HEALTHDEVICE_H_ +#define FSFW_DEVICEHANDLERS_HEALTHDEVICE_H_ #include "../health/HasHealthIF.h" #include "../health/HealthHelper.h" @@ -37,4 +37,4 @@ public: HealthHelper healthHelper; }; -#endif /* HEALTHDEVICE_H_ */ +#endif /* FSFW_DEVICEHANDLERS_HEALTHDEVICE_H_ */ From dbcd06527ef728c2620c4141eab58bbf40994339 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 12 Oct 2020 18:28:07 +0200 Subject: [PATCH 011/119] separate step for performOp now --- devicehandlers/DeviceHandlerBase.cpp | 39 ++++++++++++++++++---------- devicehandlers/DeviceHandlerBase.h | 3 ++- devicehandlers/DeviceHandlerIF.h | 3 ++- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 227d5b6d..d67e47d8 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -72,8 +72,13 @@ DeviceHandlerBase::~DeviceHandlerBase() { ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { this->pstStep = counter; + this->lastStep = this->pstStep; - if (getComAction() == SEND_WRITE) { + if (getComAction() == CommunicationAction::NOTHING) { + return HasReturnvaluesIF::RETURN_OK; + } + + if (getComAction() == CommunicationAction::PERFORM_OPERATION) { cookieInfo.state = COOKIE_UNUSED; readCommandQueue(); doStateMachine(); @@ -83,26 +88,29 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { hkSwitcher.performOperation(); hkManager.performHkOperation(); performOperationHook(); + return RETURN_OK; } + if (mode == MODE_OFF) { return RETURN_OK; } + switch (getComAction()) { - case SEND_WRITE: - if ((cookieInfo.state == COOKIE_UNUSED)) { + case CommunicationAction::SEND_WRITE: + if (cookieInfo.state == COOKIE_UNUSED) { + // if no external command was specified, build internal command. buildInternalCommand(); } doSendWrite(); break; - case GET_WRITE: + case CommunicationAction::GET_WRITE: doGetWrite(); break; - case SEND_READ: + case CommunicationAction::SEND_READ: doSendRead(); break; - case GET_READ: + case CommunicationAction::GET_READ: doGetRead(); - cookieInfo.state = COOKIE_UNUSED; break; default: break; @@ -821,24 +829,27 @@ void DeviceHandlerBase::replyRawData(const uint8_t *data, size_t len, } //Default child implementations -DeviceHandlerIF::CommunicationAction_t DeviceHandlerBase::getComAction() { +DeviceHandlerIF::CommunicationAction DeviceHandlerBase::getComAction() { switch (pstStep) { case 0: - return SEND_WRITE; + return CommunicationAction::PERFORM_OPERATION; break; case 1: - return GET_WRITE; - break; + return CommunicationAction::SEND_WRITE; + break; case 2: - return SEND_READ; + return CommunicationAction::GET_WRITE; break; case 3: - return GET_READ; + return CommunicationAction::SEND_READ; + break; + case 4: + return CommunicationAction::GET_READ; break; default: break; } - return NOTHING; + return CommunicationAction::NOTHING; } MessageQueueId_t DeviceHandlerBase::getCommandQueue() const { diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index eda318cb..627a6423 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -577,6 +577,7 @@ protected: /** This is the counter value from performOperation(). */ uint8_t pstStep = 0; + uint8_t lastStep = 0; uint32_t pstIntervalMs = 0; /** @@ -841,7 +842,7 @@ protected: * @return The Rmap action to execute in this step */ - virtual CommunicationAction_t getComAction(); + virtual CommunicationAction getComAction(); /** * Build the device command to send for raw mode. diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index 9c8eb098..088c1b45 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -139,7 +139,8 @@ public: * * This is used by the child class to tell the base class what to do. */ - enum CommunicationAction_t: uint8_t { + enum CommunicationAction: uint8_t { + PERFORM_OPERATION, SEND_WRITE,//!< Send write GET_WRITE, //!< Get write SEND_READ, //!< Send read From 73215baf118066c2eb5278416f302216a243cd9d Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 15 Oct 2020 13:43:23 +0200 Subject: [PATCH 012/119] init commit --- storagemanager/ConstStorageAccessor.h | 2 - storagemanager/LocalPool.cpp | 346 ++++++++++++++++++++++++++ storagemanager/LocalPool.h | 233 ++++++++++------- storagemanager/PoolManager.cpp | 59 +++++ storagemanager/PoolManager.h | 41 ++- storagemanager/StorageAccessor.h | 2 - storagemanager/StorageManagerIF.h | 23 +- storagemanager/storeAddress.h | 13 +- 8 files changed, 612 insertions(+), 107 deletions(-) create mode 100644 storagemanager/LocalPool.cpp create mode 100644 storagemanager/PoolManager.cpp diff --git a/storagemanager/ConstStorageAccessor.h b/storagemanager/ConstStorageAccessor.h index 96d2dca2..570c20ce 100644 --- a/storagemanager/ConstStorageAccessor.h +++ b/storagemanager/ConstStorageAccessor.h @@ -20,9 +20,7 @@ class StorageManagerIF; */ class ConstStorageAccessor { //! StorageManager classes have exclusive access to private variables. - template friend class PoolManager; - template friend class LocalPool; public: /** diff --git a/storagemanager/LocalPool.cpp b/storagemanager/LocalPool.cpp new file mode 100644 index 00000000..8e5f4d48 --- /dev/null +++ b/storagemanager/LocalPool.cpp @@ -0,0 +1,346 @@ +#include "LocalPool.h" +#include + +LocalPool::LocalPool(object_id_t setObjectId, const LocalPoolConfig& poolConfig, + bool registered, bool spillsToHigherPools): + SystemObject(setObjectId, registered), + NUMBER_OF_POOLS(poolConfig.size()), + spillsToHigherPools(spillsToHigherPools) { + if(NUMBER_OF_POOLS == 0) { + sif::error << "LocalPool::LocalPool: Passed pool configuration is " + << " invalid!" << std::endl; + } + max_pools_t index = 0; + for (const auto& currentPoolConfig: poolConfig) { + this->numberOfElements[index] = currentPoolConfig.first; + this->elementSizes[index] = currentPoolConfig.second; + store[index] = std::vector( + numberOfElements[index] * elementSizes[index]); + sizeLists[index] = std::vector(numberOfElements[index]); + for(auto& size: sizeLists[index]) { + size = STORAGE_FREE; + } + index++; + } +} + +LocalPool::~LocalPool(void) {} + + +ReturnValue_t LocalPool::addData(store_address_t* storageId, + const uint8_t* data, size_t size, bool ignoreFault) { + ReturnValue_t status = reserveSpace(size, storageId, ignoreFault); + if (status == RETURN_OK) { + write(*storageId, data, size); + } + return status; +} + +ReturnValue_t LocalPool::getData(store_address_t packetId, + const uint8_t **packetPtr, size_t *size) { + uint8_t* tempData = nullptr; + ReturnValue_t status = modifyData(packetId, &tempData, size); + *packetPtr = tempData; + return status; +} + +ReturnValue_t LocalPool::getData(store_address_t storeId, + ConstStorageAccessor& storeAccessor) { + uint8_t* tempData = nullptr; + ReturnValue_t status = modifyData(storeId, &tempData, + &storeAccessor.size_); + storeAccessor.assignStore(this); + storeAccessor.constDataPointer = tempData; + return status; +} + +ConstAccessorPair LocalPool::getData(store_address_t storeId) { + uint8_t* tempData = nullptr; + ConstStorageAccessor constAccessor(storeId, this); + ReturnValue_t status = modifyData(storeId, &tempData, &constAccessor.size_); + constAccessor.constDataPointer = tempData; + return ConstAccessorPair(status, std::move(constAccessor)); +} + +ReturnValue_t LocalPool::getFreeElement(store_address_t *storageId, + const size_t size, uint8_t **pData, bool ignoreFault) { + ReturnValue_t status = reserveSpace(size, storageId, ignoreFault); + if (status == RETURN_OK) { + *pData = &store[storageId->poolIndex][getRawPosition(*storageId)]; + } + else { + *pData = nullptr; + } + return status; +} + + +AccessorPair LocalPool::modifyData(store_address_t storeId) { + StorageAccessor accessor(storeId, this); + ReturnValue_t status = modifyData(storeId, &accessor.dataPointer, + &accessor.size_); + accessor.assignConstPointer(); + return AccessorPair(status, std::move(accessor)); +} + +ReturnValue_t LocalPool::modifyData(store_address_t storeId, + StorageAccessor& storeAccessor) { + storeAccessor.assignStore(this); + ReturnValue_t status = modifyData(storeId, &storeAccessor.dataPointer, + &storeAccessor.size_); + storeAccessor.assignConstPointer(); + return status; +} + +ReturnValue_t LocalPool::modifyData(store_address_t storeId, + uint8_t **packetPtr, size_t *size) { + ReturnValue_t status = RETURN_FAILED; + if (storeId.poolIndex >= NUMBER_OF_POOLS) { + return ILLEGAL_STORAGE_ID; + } + if ((storeId.packetIndex >= numberOfElements[storeId.poolIndex])) { + return ILLEGAL_STORAGE_ID; + } + + if (sizeLists[storeId.poolIndex][storeId.packetIndex] + != STORAGE_FREE) { + size_type packetPosition = getRawPosition(storeId); + *packetPtr = &store[storeId.poolIndex][packetPosition]; + *size = sizeLists[storeId.poolIndex][storeId.packetIndex]; + status = RETURN_OK; + } + else { + status = DATA_DOES_NOT_EXIST; + } + return status; +} + +ReturnValue_t LocalPool::deleteData(store_address_t storeId) { +#if FSFW_DEBUGGING == 1 + sif::debug << "Delete: Pool: " << std::dec << storeId.poolIndex + << " Index: " << storeId.packetIndex << std::endl; + +#endif + ReturnValue_t status = RETURN_OK; + size_type pageSize = getPageSize(storeId.poolIndex); + if ((pageSize != 0) and + (storeId.packetIndex < numberOfElements[storeId.poolIndex])) { + uint16_t packetPosition = getRawPosition(storeId); + uint8_t* ptr = &store[storeId.poolIndex][packetPosition]; + std::memset(ptr, 0, pageSize); + //Set free list + sizeLists[storeId.poolIndex][storeId.packetIndex] = STORAGE_FREE; + } + else { + //pool_index or packet_index is too large + sif::error << "LocalPool::deleteData: Illegal store ID, no deletion!" + << std::endl; + status = ILLEGAL_STORAGE_ID; + } + return status; +} + +ReturnValue_t LocalPool::deleteData(uint8_t *ptr, size_t size, + store_address_t *storeId) { + store_address_t localId; + ReturnValue_t result = ILLEGAL_ADDRESS; + for (uint16_t n = 0; n < NUMBER_OF_POOLS; n++) { + //Not sure if new allocates all stores in order. so better be careful. + if ((store[n].data() <= ptr) and + (&store[n][numberOfElements[n]*elementSizes[n]] > ptr)) { + localId.poolIndex = n; + uint32_t deltaAddress = ptr - store[n].data(); + // Getting any data from the right "block" is ok. + // This is necessary, as IF's sometimes don't point to the first + // element of an object. + localId.packetIndex = deltaAddress / elementSizes[n]; + result = deleteData(localId); +#if FSFW_DEBUGGING == 1 + if (deltaAddress % elementSizes[n] != 0) { + sif::error << "LocalPool::deleteData: Address not aligned!" + << std::endl; + } +#endif + break; + } + } + if (storeId != nullptr) { + *storeId = localId; + } + return result; +} + + +ReturnValue_t LocalPool::initialize() { + ReturnValue_t result = SystemObject::initialize(); + if (result != RETURN_OK) { + return result; + } + internalErrorReporter = objectManager->get( + objects::INTERNAL_ERROR_REPORTER); + if (internalErrorReporter == nullptr){ + return ObjectManagerIF::INTERNAL_ERR_REPORTER_UNINIT; + } + + //Check if any pool size is large than the maximum allowed. + for (uint8_t count = 0; count < NUMBER_OF_POOLS; count++) { + if (elementSizes[count] >= STORAGE_FREE) { + sif::error << "LocalPool::initialize: Pool is too large! " + "Max. allowed size is: " << (STORAGE_FREE - 1) << std::endl; + return StorageManagerIF::POOL_TOO_LARGE; + } + } + return HasReturnvaluesIF::RETURN_OK; +} + +void LocalPool::clearStore() { + for(auto& sizeList: sizeLists) { + for(auto& size: sizeList) { + size = STORAGE_FREE; + } +// std::memset(sizeList[index], 0xff, +// numberOfElements[index] * sizeof(size_type)); + } + +} + +ReturnValue_t LocalPool::reserveSpace(const size_t size, + store_address_t *storeId, bool ignoreFault) { + ReturnValue_t status = getPoolIndex(size, &storeId->poolIndex); + if (status != RETURN_OK) { + sif::error << "LocalPool( " << std::hex << getObjectId() << std::dec + << " )::reserveSpace: Packet too large." << std::endl; + return status; + } + status = findEmpty(storeId->poolIndex, &storeId->packetIndex); + while (status != RETURN_OK && spillsToHigherPools) { + status = getPoolIndex(size, &storeId->poolIndex, storeId->poolIndex + 1); + if (status != RETURN_OK) { + //We don't find any fitting pool anymore. + break; + } + status = findEmpty(storeId->poolIndex, &storeId->packetIndex); + } + if (status == RETURN_OK) { +#if FSFW_DEBUGGING == 1 + sif::debug << "Reserve: Pool: " << std::dec + << storeId->poolIndex << " Index: " << storeId->packetIndex + << std::endl; +#endif + sizeLists[storeId->poolIndex][storeId->packetIndex] = size; + } + else { + if ((not ignoreFault) and (internalErrorReporter != nullptr)) { + internalErrorReporter->storeFull(); + } + } + return status; +} + +void LocalPool::write(store_address_t storeId, const uint8_t *data, + size_t size) { + uint8_t* ptr = nullptr; + size_type packetPosition = getRawPosition(storeId); + + // Size was checked before calling this function. + ptr = &store[storeId.poolIndex][packetPosition]; + std::memcpy(ptr, data, size); + sizeLists[storeId.poolIndex][storeId.packetIndex] = size; +} + +LocalPool::size_type LocalPool::getPageSize(max_pools_t poolIndex) { + if (poolIndex < NUMBER_OF_POOLS) { + return elementSizes[poolIndex]; + } + else { + return 0; + } +} + +void LocalPool::setToSpillToHigherPools(bool enable) { + this->spillsToHigherPools = enable; +} + +ReturnValue_t LocalPool::getPoolIndex(size_t packetSize, uint16_t *poolIndex, + uint16_t startAtIndex) { + for (uint16_t n = startAtIndex; n < NUMBER_OF_POOLS; n++) { +#if FSFW_DEBUGGING == 1 + sif::debug << "LocalPool " << getObjectId() << "::getPoolIndex: Pool: " + << n << ", Element Size: " << elementSizes[n] << std::endl; +#endif + if (elementSizes[n] >= packetSize) { + *poolIndex = n; + return RETURN_OK; + } + } + return DATA_TOO_LARGE; +} + +LocalPool::size_type LocalPool::getRawPosition(store_address_t storeId) { + return storeId.packetIndex * elementSizes[storeId.poolIndex]; +} + +ReturnValue_t LocalPool::findEmpty(n_pool_elem_t poolIndex, uint16_t *element) { + ReturnValue_t status = DATA_STORAGE_FULL; + for (uint16_t foundElement = 0; foundElement < numberOfElements[poolIndex]; + foundElement++) { + if (sizeLists[poolIndex][foundElement] == STORAGE_FREE) { + *element = foundElement; + status = RETURN_OK; + break; + } + } + return status; +} + +size_t LocalPool::getTotalSize(size_t* additionalSize) { + size_t totalSize = 0; + size_t sizesSize = 0; + for(uint8_t idx = 0; idx < NUMBER_OF_POOLS; idx ++) { + totalSize += elementSizes[idx] * numberOfElements[idx]; + sizesSize += numberOfElements[idx] * sizeof(size_type); + } + if(additionalSize != nullptr) { + *additionalSize = sizesSize; + } + return totalSize; +} + +void LocalPool::getFillCount(uint8_t *buffer, uint8_t *bytesWritten) { + if(bytesWritten == nullptr or buffer == nullptr) { + return; + } + + uint16_t reservedHits = 0; + uint8_t idx = 0; + uint16_t sum = 0; + for(; idx < NUMBER_OF_POOLS; idx ++) { + for(const auto& size: sizeLists[idx]) { + if(size != STORAGE_FREE) { + reservedHits++; + } + } + buffer[idx] = static_cast(reservedHits) / + numberOfElements[idx] * 100; + *bytesWritten += 1; + sum += buffer[idx]; + reservedHits = 0; + } + buffer[idx] = sum / NUMBER_OF_POOLS; + *bytesWritten += 1; +} + + +void LocalPool::clearPage(max_pools_t pageIndex) { + if(pageIndex >= NUMBER_OF_POOLS) { + return; + } + + // Mark the storage as free + for(auto& size: sizeLists[pageIndex]) { + size = STORAGE_FREE; + } + + // Set all the page content to 0. + std::memset(store[pageIndex].data(), 0, elementSizes[pageIndex]); +} diff --git a/storagemanager/LocalPool.h b/storagemanager/LocalPool.h index 3a94c03d..db771152 100644 --- a/storagemanager/LocalPool.h +++ b/storagemanager/LocalPool.h @@ -7,57 +7,93 @@ #include "../serviceinterface/ServiceInterfaceStream.h" #include "../internalError/InternalErrorReporterIF.h" #include "../storagemanager/StorageAccessor.h" -#include + +#include +#include +#include +#include /** - * @brief The LocalPool class provides an intermediate data storage with - * a fixed pool size policy. - * @details The class implements the StorageManagerIF interface. While the - * total number of pools is fixed, the element sizes in one pool and - * the number of pool elements per pool are set on construction. - * The full amount of memory is allocated on construction. - * The overhead is 4 byte per pool element to store the size - * information of each stored element. - * To maintain an "empty" information, the pool size is limited to - * 0xFFFF-1 bytes. - * It is possible to store empty packets in the pool. - * The local pool is NOT thread-safe. - * @author Bastian Baetz + * @brief The LocalPool class provides an intermediate data storage with + * a fixed pool size policy. + * @details + * The class implements the StorageManagerIF interface. While the total number + * of pools is fixed, the element sizes in one pool and the number of pool + * elements per pool are set on construction. The full amount of memory is + * allocated on construction. + * The overhead is 4 byte per pool element to store the size information of + * each stored element. To maintain an "empty" information, the pool size is + * limited to 0xFFFF-1 bytes. + * It is possible to store empty packets in the pool. + * The local pool is NOT thread-safe. */ -template class LocalPool: public SystemObject, public StorageManagerIF { 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; - /** - * @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); + using pool_elem_size_t = size_type; + using n_pool_elem_t = uint16_t; + using LocalPoolCfgPair = std::pair; + + // The configuration needs to be provided with the pool sizes ascending + // but the number of pool elements as the first value is more intuitive. + // Therefore, a custom comparator was provided. + struct LocalPoolConfigCmp + { + bool operator ()(const LocalPoolCfgPair &a, + const LocalPoolCfgPair &b) const + { + if(a.second < b.second) { + return true; + } + else if(a.second > b.second) { + return false; + } + else { + if(a.first < b.first) { + return true; + } + else { + return false; + } + } + } + }; + using LocalPoolConfig = std::multiset; + + /** + * @brief This definition generally sets the number of + * different sized pools. It is derived from the number of pairs + * inside the LocalPoolConfig set on object creation. + * @details + * This must be less than the maximum number of pools (currently 0xff). + */ + const max_pools_t NUMBER_OF_POOLS; + + /** + * @brief This is the default constructor for a pool manager instance. + * @details + * The pool is configured by passing a set of pairs into the constructor. + * The first value of that pair determines the number of one elements on + * the respective page of the pool while the second value determines how + * many elements with that size are created on that page. + * All regions are to zero on start up. + * @param setObjectId The object identifier to be set. This allows for + * multiple instances of LocalPool in the system. + * @param poolConfig + * This is a set of pairs to configure the number of pages in the pool, + * the size of an element on a page, the number of elements on a page + * and the total size of the pool at once while also implicitely + * sorting the pairs in the right order. + * @param registered + * Determines whether the pool is registered in the object manager or not. + * @param spillsToHigherPools A variable to determine whether + * higher n pools are used if the store is full. + */ + LocalPool(object_id_t setObjectId, const LocalPoolConfig& poolConfig, + bool registered = false, bool spillsToHigherPools = false); + + void setToSpillToHigherPools(bool enable); + /** * @brief In the LocalPool's destructor all allocated memory is freed. */ @@ -66,25 +102,49 @@ public: /** * Documentation: See StorageManagerIF.h */ - ReturnValue_t addData(store_address_t* storageId, const uint8_t * data, + ReturnValue_t addData(store_address_t* storeId, const uint8_t * data, size_t size, bool ignoreFault = false) override; - ReturnValue_t getFreeElement(store_address_t* storageId,const size_t size, - uint8_t** p_data, bool ignoreFault = false) override; + ReturnValue_t getFreeElement(store_address_t* storeId,const size_t size, + uint8_t** pData, bool ignoreFault = false) override; - ConstAccessorPair getData(store_address_t packet_id) override; - ReturnValue_t getData(store_address_t packet_id, ConstStorageAccessor&) override; - ReturnValue_t getData(store_address_t packet_id, const uint8_t** packet_ptr, + ConstAccessorPair getData(store_address_t storeId) override; + ReturnValue_t getData(store_address_t storeId, + ConstStorageAccessor& constAccessor) override; + ReturnValue_t getData(store_address_t storeId, + const uint8_t** packet_ptr, size_t * size) override; + + AccessorPair modifyData(store_address_t storeId) override; + ReturnValue_t modifyData(store_address_t storeId, + StorageAccessor& storeAccessor) override; + ReturnValue_t modifyData(store_address_t storeId, uint8_t** packet_ptr, size_t * size) override; - AccessorPair modifyData(store_address_t packet_id) override; - ReturnValue_t modifyData(store_address_t packet_id, StorageAccessor&) override; - ReturnValue_t modifyData(store_address_t packet_id, uint8_t** packet_ptr, - size_t * size) override; - - virtual ReturnValue_t deleteData(store_address_t) override; + virtual ReturnValue_t deleteData(store_address_t storeId) override; virtual ReturnValue_t deleteData(uint8_t* ptr, size_t size, - store_address_t* storeId = NULL) override; + store_address_t* storeId = nullptr) override; + + /** + * Get the total size of allocated memory for pool data. + * There is an additional overhead of the sizes of elements which will + * be assigned to additionalSize + * @return + */ + size_t getTotalSize(size_t* additionalSize) override; + + /** + * Get the fill count of the pool. Each character inside the provided + * buffer will be assigned to a rounded percentage fill count for each + * page. The last written byte (at the index bytesWritten - 1) + * will contain the total fill count of the pool as a mean of the + * percentages of single pages. + * @param buffer + * @param maxSize + */ + void getFillCount(uint8_t* buffer, uint8_t* bytesWritten) override; + void clearStore() override; + void clearPage(max_pools_t pageIndex) override; + ReturnValue_t initialize() override; protected: /** @@ -94,43 +154,48 @@ protected: * @return - #RETURN_OK on success, * - the return codes of #getPoolIndex or #findEmpty otherwise. */ - virtual ReturnValue_t reserveSpace(const uint32_t size, + virtual ReturnValue_t reserveSpace(const size_t size, store_address_t* address, bool ignoreFault); - InternalErrorReporterIF *internalErrorReporter; private: - /** - * Indicates that this element is free. - * This value limits the maximum size of a pool. Change to larger data type - * if increase is required. - */ - static const uint32_t STORAGE_FREE = 0xFFFFFFFF; + /** + * Indicates that this element is free. + * This value limits the maximum size of a pool. + * Change to larger data type if increase is required. + */ + static const size_type STORAGE_FREE = std::numeric_limits::max(); /** * @brief In this array, the element sizes of each pool is stored. * @details The sizes are maintained for internal pool management. The sizes * must be set in ascending order on construction. */ - uint32_t element_sizes[NUMBER_OF_POOLS]; + std::vector elementSizes = + std::vector(NUMBER_OF_POOLS); /** * @brief n_elements stores the number of elements per pool. * @details These numbers are maintained for internal pool management. */ - uint16_t n_elements[NUMBER_OF_POOLS]; + std::vector numberOfElements = + std::vector(NUMBER_OF_POOLS); /** * @brief store represents the actual memory pool. * @details It is an array of pointers to memory, which was allocated with * a @c new call on construction. */ - uint8_t* store[NUMBER_OF_POOLS]; + std::vector> store = + std::vector>(NUMBER_OF_POOLS); + /** * @brief The size_list attribute stores the size values of every pool element. * @details As the number of elements is determined on construction, the size list * is also dynamically allocated there. */ - uint32_t* size_list[NUMBER_OF_POOLS]; + std::vector> sizeLists = + std::vector>(NUMBER_OF_POOLS); + //! A variable to determine whether higher n pools are used if //! the store is full. - bool spillsToHigherPools; + bool spillsToHigherPools = false; /** * @brief This method safely stores the given data in the given packet_id. * @details It also sets the size in size_list. The method does not perform @@ -139,30 +204,24 @@ private: * @param data The data to be stored. * @param size The size of the data to be stored. */ - void write(store_address_t packet_id, const uint8_t* data, size_t size); + void write(store_address_t packetId, const uint8_t* data, size_t size); /** * @brief A helper method to read the element size of a certain pool. * @param pool_index The pool in which to look. * @return Returns the size of an element or 0. */ - uint32_t getPageSize(uint16_t pool_index); - /** - * @brief This helper method looks up a fitting pool for a given size. - * @details The pools are looked up in ascending order, so the first that - * fits is used. - * @param packet_size The size of the data to be stored. - * @return Returns the pool that fits or StorageManagerIF::INVALID_ADDRESS. - */ + size_type getPageSize(max_pools_t poolIndex); + /** * @brief This helper method looks up a fitting pool for a given size. * @details The pools are looked up in ascending order, so the first that * fits is used. * @param packet_size The size of the data to be stored. * @param[out] poolIndex The fitting pool index found. - * @return - #RETURN_OK on success, - * - #DATA_TOO_LARGE otherwise. + * @return - @c RETURN_OK on success, + * - @c DATA_TOO_LARGE otherwise. */ - ReturnValue_t getPoolIndex(size_t packet_size, uint16_t* poolIndex, + ReturnValue_t getPoolIndex(size_t packetSize, uint16_t* poolIndex, uint16_t startAtIndex = 0); /** * @brief This helper method calculates the true array position in store @@ -172,7 +231,7 @@ private: * @param packet_id The packet id to look up. * @return Returns the position of the data in store. */ - uint32_t getRawPosition(store_address_t packet_id); + size_type getRawPosition(store_address_t storeId); /** * @brief This is a helper method to find an empty element in a given pool. * @details The method searches size_list for the first empty element, so @@ -182,9 +241,9 @@ private: * @return - #RETURN_OK on success, * - #DATA_STORAGE_FULL if the store is full */ - ReturnValue_t findEmpty(uint16_t pool_index, uint16_t* element); + ReturnValue_t findEmpty(n_pool_elem_t poolIndex, uint16_t* element); + + InternalErrorReporterIF *internalErrorReporter = nullptr; }; -#include "LocalPool.tpp" - #endif /* FSFW_STORAGEMANAGER_LOCALPOOL_H_ */ diff --git a/storagemanager/PoolManager.cpp b/storagemanager/PoolManager.cpp new file mode 100644 index 00000000..9c801a3d --- /dev/null +++ b/storagemanager/PoolManager.cpp @@ -0,0 +1,59 @@ +#include "PoolManager.h" + +PoolManager::PoolManager(object_id_t setObjectId, + const LocalPoolConfig& localPoolConfig): + LocalPool(setObjectId, localPoolConfig, true) { + mutex = MutexFactory::instance()->createMutex(); +} + + +PoolManager::~PoolManager(void) { + MutexFactory::instance()->deleteMutex(mutex); +} + + +ReturnValue_t PoolManager::reserveSpace(const size_t size, + store_address_t* address, bool ignoreFault) { + MutexHelper mutexHelper(mutex, MutexIF::TimeoutType::WAITING, + mutexTimeoutMs); + ReturnValue_t status = LocalPool::reserveSpace(size, + address,ignoreFault); + return status; +} + + +ReturnValue_t PoolManager::deleteData( + store_address_t storeId) { +#if FSFW_DEBUGGING == 1 + sif::debug << "PoolManager( " << translateObject(getObjectId()) << + " )::deleteData from store " << storeId.poolIndex << + ". id is "<< storeId.packetIndex << std::endl; +#endif + MutexHelper mutexHelper(mutex, MutexIF::TimeoutType::WAITING, + mutexTimeoutMs); + return LocalPool::deleteData(storeId); +} + + +ReturnValue_t PoolManager::deleteData(uint8_t* buffer, + size_t size, store_address_t* storeId) { + MutexHelper mutexHelper(mutex, MutexIF::TimeoutType::WAITING, 20); + ReturnValue_t status = LocalPool::deleteData(buffer, + size, storeId); + return status; +} + + +void PoolManager::setMutexTimeout( + uint32_t mutexTimeoutMs) { + this->mutexTimeoutMs = mutexTimeoutMs; +} + +ReturnValue_t PoolManager::lockMutex(MutexIF::TimeoutType timeoutType, + uint32_t timeoutMs) { + return mutex->lockMutex(timeoutType, timeoutMs); +} + +ReturnValue_t PoolManager::unlockMutex() { + return mutex->unlockMutex(); +} diff --git a/storagemanager/PoolManager.h b/storagemanager/PoolManager.h index 8cc6c065..5786a225 100644 --- a/storagemanager/PoolManager.h +++ b/storagemanager/PoolManager.h @@ -9,16 +9,20 @@ /** * @brief The PoolManager class provides an intermediate data storage with * a fixed pool size policy for inter-process communication. - * @details Uses local pool calls but is thread safe by protecting the call - * with a lock. + * @details + * Uses local pool calls but is thread safe by protecting most calls + * with a lock. The developer can lock the pool with the provided API + * if the lock needs to persists beyond the function call. + * + * Other than that, the class provides the same interface as the LocalPool + * class. The class is always registered as a system object as it is assumed + * it will always be used concurrently (if this is not the case, it is + * recommended to use the LocalPool class instead). * @author Bastian Baetz */ -template -class PoolManager : public LocalPool { +class PoolManager: public LocalPool { public: - PoolManager(object_id_t setObjectId, - const uint16_t element_sizes[NUMBER_OF_POOLS], - const uint16_t n_elements[NUMBER_OF_POOLS]); + PoolManager(object_id_t setObjectId, const LocalPoolConfig& poolConfig); /** * @brief In the PoolManager's destructor all allocated memory @@ -26,6 +30,12 @@ public: */ virtual ~PoolManager(); + /** + * Set the default mutex timeout for internal calls. + * @param mutexTimeoutMs + */ + void setMutexTimeout(uint32_t mutexTimeoutMs); + /** * @brief LocalPool overrides for thread-safety. Decorator function * which wraps LocalPool calls with a mutex protection. @@ -34,12 +44,23 @@ public: ReturnValue_t deleteData(uint8_t* buffer, size_t size, store_address_t* storeId = nullptr) override; - void setMutexTimeout(uint32_t mutexTimeoutMs); + /** + * The developer is allowed to lock the mutex in case the lock needs + * to persist beyond the function calls which are not protected by the + * class. + * @param timeoutType + * @param timeoutMs + * @return + */ + ReturnValue_t lockMutex(MutexIF::TimeoutType timeoutType, + uint32_t timeoutMs); + ReturnValue_t unlockMutex(); + protected: //! Default mutex timeout value to prevent permanent blocking. uint32_t mutexTimeoutMs = 20; - ReturnValue_t reserveSpace(const uint32_t size, store_address_t* address, + ReturnValue_t reserveSpace(const size_t size, store_address_t* address, bool ignoreFault) override; /** @@ -51,6 +72,4 @@ protected: MutexIF* mutex; }; -#include "PoolManager.tpp" - #endif /* FSFW_STORAGEMANAGER_POOLMANAGER_H_ */ diff --git a/storagemanager/StorageAccessor.h b/storagemanager/StorageAccessor.h index 5cf15d50..d5b383eb 100644 --- a/storagemanager/StorageAccessor.h +++ b/storagemanager/StorageAccessor.h @@ -10,9 +10,7 @@ class StorageManagerIF; */ class StorageAccessor: public ConstStorageAccessor { //! StorageManager classes have exclusive access to private variables. - template friend class PoolManager; - template friend class LocalPool; public: StorageAccessor(store_address_t storeId); diff --git a/storagemanager/StorageManagerIF.h b/storagemanager/StorageManagerIF.h index 834e7563..769616d7 100644 --- a/storagemanager/StorageManagerIF.h +++ b/storagemanager/StorageManagerIF.h @@ -28,6 +28,9 @@ using ConstAccessorPair = std::pair; */ class StorageManagerIF : public HasReturnvaluesIF { public: + using size_type = size_t; + using max_pools_t = uint8_t; + static const uint8_t INTERFACE_ID = CLASS_ID::STORAGE_MANAGER_IF; //!< The unique ID for return codes for this interface. static const ReturnValue_t DATA_TOO_LARGE = MAKE_RETURN_CODE(1); //!< This return code indicates that the data to be stored is too large for the store. static const ReturnValue_t DATA_STORAGE_FULL = MAKE_RETURN_CODE(2); //!< This return code indicates that a data storage is full. @@ -40,7 +43,9 @@ public: static const Event GET_DATA_FAILED = MAKE_EVENT(0, SEVERITY::LOW); static const Event STORE_DATA_FAILED = MAKE_EVENT(1, SEVERITY::LOW); - static const uint32_t INVALID_ADDRESS = 0xFFFFFFFF; //!< Indicates an invalid (i.e unused) storage address. + //!< Indicates an invalid (i.e unused) storage address. + static const uint32_t INVALID_ADDRESS = 0xFFFFFFFF; + /** * @brief This is the empty virtual destructor as required for C++ interfaces. */ @@ -164,6 +169,22 @@ public: * Use with care! */ virtual void clearStore() = 0; + + /** + * Clears a page in the store. Use with care! + * @param pageIndex + */ + virtual void clearPage(uint8_t pageIndex) = 0; + + /** + * Get the fill count of the pool. The exact form will be implementation + * dependant. + * @param buffer + * @param bytesWritten + */ + virtual void getFillCount(uint8_t* buffer, uint8_t* bytesWritten) = 0; + + virtual size_t getTotalSize(size_t* additionalSize) = 0; }; #endif /* FSFW_STORAGEMANAGER_STORAGEMANAGERIF_H_ */ diff --git a/storagemanager/storeAddress.h b/storagemanager/storeAddress.h index 044c0790..ea72f6f8 100644 --- a/storagemanager/storeAddress.h +++ b/storagemanager/storeAddress.h @@ -3,16 +3,21 @@ #include +namespace storeId { +static constexpr uint32_t INVALID_STORE_ADDRESS = 0xffffffff; +} + /** * This union defines the type that identifies where a data packet is * stored in the store. It comprises of a raw part to read it as raw value and * a structured part to use it in pool-like stores. */ union store_address_t { + /** * Default Constructor, initializing to INVALID_ADDRESS */ - store_address_t():raw(0xFFFFFFFF){} + store_address_t(): raw(storeId::INVALID_STORE_ADDRESS){} /** * Constructor to create an address object using the raw address * @@ -28,7 +33,7 @@ union store_address_t { * @param packetIndex */ store_address_t(uint16_t poolIndex, uint16_t packetIndex): - pool_index(poolIndex),packet_index(packetIndex){} + poolIndex(poolIndex), packetIndex(packetIndex){} /** * A structure with two elements to access the store address pool-like. */ @@ -36,11 +41,11 @@ union store_address_t { /** * The index in which pool the packet lies. */ - uint16_t pool_index; + uint16_t poolIndex; /** * The position in the chosen pool. */ - uint16_t packet_index; + uint16_t packetIndex; }; /** * Alternative access to the raw value. From 67e33918e0fb07fe3dd6afbfda9eb5bbfcd044fe Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 15 Oct 2020 13:44:09 +0200 Subject: [PATCH 013/119] event manager uses refactored pool now --- events/EventManager.cpp | 18 +++++++++--------- events/EventManager.h | 10 +++++++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/events/EventManager.cpp b/events/EventManager.cpp index e71951e3..57bda13f 100644 --- a/events/EventManager.cpp +++ b/events/EventManager.cpp @@ -1,23 +1,23 @@ #include "EventManager.h" -#include "EventMessage.h" #include "../serviceinterface/ServiceInterfaceStream.h" #include "../ipc/QueueFactory.h" #include "../ipc/MutexFactory.h" -const uint16_t EventManager::POOL_SIZES[N_POOLS] = { - sizeof(EventMatchTree::Node), sizeof(EventIdRangeMatcher), - sizeof(ReporterRangeMatcher) }; // If one checks registerListener calls, there are around 40 (to max 50) // objects registering for certain events. // Each listener requires 1 or 2 EventIdMatcher and 1 or 2 ReportRangeMatcher. // So a good guess is 75 to a max of 100 pools required for each, which fits well. -// SHOULDDO: Shouldn't this be in the config folder and passed via ctor? -const uint16_t EventManager::N_ELEMENTS[N_POOLS] = { 240, 120, 120 }; +// This should be configurable.. +const LocalPool::LocalPoolConfig EventManager::poolConfig = { + {240, sizeof(EventMatchTree::Node)}, + {120, sizeof(EventIdRangeMatcher)}, + {120, sizeof(ReporterRangeMatcher)} +}; EventManager::EventManager(object_id_t setObjectId) : SystemObject(setObjectId), - factoryBackend(0, POOL_SIZES, N_ELEMENTS, false, true) { + factoryBackend(0, poolConfig, false, true) { mutex = MutexFactory::instance()->createMutex(); eventReportQueue = QueueFactory::instance()->createMessageQueue( MAX_EVENTS_PER_CYCLE, EventMessage::EVENT_MESSAGE_SIZE); @@ -109,13 +109,13 @@ ReturnValue_t EventManager::unsubscribeFromEventRange(MessageQueueId_t listener, return result; } -#ifdef DEBUG +#if FSFW_DEBUG_OUTPUT == 1 void EventManager::printEvent(EventMessage* message) { const char *string = 0; switch (message->getSeverity()) { case SEVERITY::INFO: -#ifdef DEBUG_INFO_EVENT +#if DEBUG_INFO_EVENT == 1 string = translateObject(message->getReporter()); sif::info << "EVENT: "; if (string != 0) { diff --git a/events/EventManager.h b/events/EventManager.h index 2602aeb2..fe35d9d3 100644 --- a/events/EventManager.h +++ b/events/EventManager.h @@ -8,9 +8,11 @@ #include "../tasks/ExecutableObjectIF.h" #include "../ipc/MessageQueueIF.h" #include "../ipc/MutexIF.h" +#include + #include -#ifdef DEBUG +#if FSFW_DEBUG_OUTPUT == 1 // forward declaration, should be implemented by mission extern const char* translateObject(object_id_t object); extern const char* translateEvents(Event event); @@ -49,13 +51,15 @@ protected: MutexIF* mutex = nullptr; static const uint8_t N_POOLS = 3; - LocalPool factoryBackend; + LocalPool factoryBackend; + static const LocalPool::LocalPoolConfig poolConfig; + static const uint16_t POOL_SIZES[N_POOLS]; static const uint16_t N_ELEMENTS[N_POOLS]; void notifyListeners(EventMessage *message); -#ifdef DEBUG +#if FSFW_DEBUG_OUTPUT == 1 void printEvent(EventMessage *message); #endif From 7d358483dd1f684609d52f144e64aa02e1e82425 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 15 Oct 2020 13:44:59 +0200 Subject: [PATCH 014/119] added back config includes --- storagemanager/LocalPool.cpp | 1 + storagemanager/PoolManager.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/storagemanager/LocalPool.cpp b/storagemanager/LocalPool.cpp index 8e5f4d48..48934a0c 100644 --- a/storagemanager/LocalPool.cpp +++ b/storagemanager/LocalPool.cpp @@ -1,4 +1,5 @@ #include "LocalPool.h" +#include #include LocalPool::LocalPool(object_id_t setObjectId, const LocalPoolConfig& poolConfig, diff --git a/storagemanager/PoolManager.cpp b/storagemanager/PoolManager.cpp index 9c801a3d..85376b28 100644 --- a/storagemanager/PoolManager.cpp +++ b/storagemanager/PoolManager.cpp @@ -1,4 +1,5 @@ #include "PoolManager.h" +#include PoolManager::PoolManager(object_id_t setObjectId, const LocalPoolConfig& localPoolConfig): From bd2f8b3e8ce09c6328519a9047d7144ff7932d37 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 15 Oct 2020 13:45:47 +0200 Subject: [PATCH 015/119] packet matcher uses refactored pool now --- tmtcpacket/packetmatcher/PacketMatchTree.cpp | 28 ++++++++++++-------- tmtcpacket/packetmatcher/PacketMatchTree.h | 3 ++- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/tmtcpacket/packetmatcher/PacketMatchTree.cpp b/tmtcpacket/packetmatcher/PacketMatchTree.cpp index a4579657..ac72b3e7 100644 --- a/tmtcpacket/packetmatcher/PacketMatchTree.cpp +++ b/tmtcpacket/packetmatcher/PacketMatchTree.cpp @@ -3,19 +3,30 @@ #include "ServiceMatcher.h" #include "SubserviceMatcher.h" +// This should be configurable.. +const LocalPool::LocalPoolConfig PacketMatchTree::poolConfig = { + {10, sizeof(ServiceMatcher)}, + {20, sizeof(SubServiceMatcher)}, + {2, sizeof(ApidMatcher)}, + {40, sizeof(PacketMatchTree::Node)} +}; + PacketMatchTree::PacketMatchTree(Node* root) : - MatchTree(root, 2), factoryBackend(0, POOL_SIZES, - N_ELEMENTS, false, true), factory(&factoryBackend) { + MatchTree(root, 2), + factoryBackend(0, poolConfig, false, true), + factory(&factoryBackend) { } PacketMatchTree::PacketMatchTree(iterator root) : - MatchTree(root.element, 2), factoryBackend(0, - POOL_SIZES, N_ELEMENTS, false, true), factory(&factoryBackend) { + MatchTree(root.element, 2), + factoryBackend(0, poolConfig, false, true), + factory(&factoryBackend) { } PacketMatchTree::PacketMatchTree() : - MatchTree((Node*) NULL, 2), factoryBackend(0, - POOL_SIZES, N_ELEMENTS, false, true), factory(&factoryBackend) { + MatchTree((Node*) NULL, 2), + factoryBackend(0, poolConfig, false, true), + factory(&factoryBackend) { } PacketMatchTree::~PacketMatchTree() { @@ -172,11 +183,6 @@ ReturnValue_t PacketMatchTree::initialize() { return factoryBackend.initialize(); } -const uint16_t PacketMatchTree::POOL_SIZES[N_POOLS] = { sizeof(ServiceMatcher), - sizeof(SubServiceMatcher), sizeof(ApidMatcher), - sizeof(PacketMatchTree::Node) }; -//Maximum number of types and subtypes to filter should be more than sufficient. -const uint16_t PacketMatchTree::N_ELEMENTS[N_POOLS] = { 10, 20, 2, 40 }; ReturnValue_t PacketMatchTree::changeMatch(bool addToMatch, uint16_t apid, uint8_t type, uint8_t subtype) { diff --git a/tmtcpacket/packetmatcher/PacketMatchTree.h b/tmtcpacket/packetmatcher/PacketMatchTree.h index 86fb087e..54fc856c 100644 --- a/tmtcpacket/packetmatcher/PacketMatchTree.h +++ b/tmtcpacket/packetmatcher/PacketMatchTree.h @@ -23,8 +23,9 @@ protected: ReturnValue_t cleanUpElement(iterator position); private: static const uint8_t N_POOLS = 4; - LocalPool factoryBackend; + LocalPool factoryBackend; PlacementFactory factory; + static const LocalPool::LocalPoolConfig poolConfig; static const uint16_t POOL_SIZES[N_POOLS]; static const uint16_t N_ELEMENTS[N_POOLS]; template From 6ea933492368e31217851717f9d880c033cfce17 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 29 Oct 2020 17:57:27 +0100 Subject: [PATCH 016/119] parameter updates --- parameters/HasParametersIF.h | 42 ++++++++++------- parameters/ParameterHelper.cpp | 80 +++++++++++++++++---------------- parameters/ParameterMessage.cpp | 20 ++++++++- parameters/ParameterMessage.h | 35 ++++++++++++++- parameters/ParameterWrapper.cpp | 55 ++++++++++++++++++++--- parameters/ParameterWrapper.h | 23 +++++++--- 6 files changed, 186 insertions(+), 69 deletions(-) diff --git a/parameters/HasParametersIF.h b/parameters/HasParametersIF.h index 005403fa..c4fd81b3 100644 --- a/parameters/HasParametersIF.h +++ b/parameters/HasParametersIF.h @@ -1,12 +1,16 @@ #ifndef FSFW_PARAMETERS_HASPARAMETERSIF_H_ #define FSFW_PARAMETERS_HASPARAMETERSIF_H_ -#include "../parameters/ParameterWrapper.h" #include "../returnvalues/HasReturnvaluesIF.h" -#include +#include "ParameterWrapper.h" +#include -/** Each parameter is identified with a unique parameter ID */ -typedef uint32_t ParameterId_t; +/** + * Each parameter is identified with a unique parameter ID + * The first byte of the parameter ID will denote the domain ID. + * The second and third byte will be the unique identifier number. + */ +using ParameterId_t = uint32_t; /** * @brief This interface is used by components which have modifiable @@ -19,13 +23,13 @@ typedef uint32_t ParameterId_t; * The second and third byte represent the matrix ID, which can represent * a 8-bit row and column number and the last byte... * - * Yeah, it it matrix ID oder parameter ID now and is index a 16 bit number + * Yeah, is it matrix ID or parameter ID now and is index a 16 bit number * of a 8 bit number now? */ class HasParametersIF { public: static const uint8_t INTERFACE_ID = CLASS_ID::HAS_PARAMETERS_IF; - static const ReturnValue_t INVALID_MATRIX_ID = MAKE_RETURN_CODE(0x01); + static const ReturnValue_t INVALID_IDENTIFIER_ID = MAKE_RETURN_CODE(0x01); static const ReturnValue_t INVALID_DOMAIN_ID = MAKE_RETURN_CODE(0x02); static const ReturnValue_t INVALID_VALUE = MAKE_RETURN_CODE(0x03); static const ReturnValue_t READ_ONLY = MAKE_RETURN_CODE(0x05); @@ -34,17 +38,24 @@ public: return id >> 24; } - static uint16_t getMatrixId(ParameterId_t id) { - return id >> 8; + static uint8_t getUniqueIdentifierId(ParameterId_t id) { + return id >> 16; } - static uint8_t getIndex(ParameterId_t id) { + /** + * Get the index of a parameter. Please note that the index is always a + * linear index. For a vector, this is straightforward. + * For a matrix, the linear indexing run from left to right, top to bottom. + * @param id + * @return + */ + static uint16_t getIndex(ParameterId_t id) { return id; } - static uint32_t getFullParameterId(uint8_t domainId, uint16_t parameterId, - uint8_t index) { - return (domainId << 24) + (parameterId << 8) + index; + static uint32_t getFullParameterId(uint8_t domainId, + uint8_t uniqueIdentifier, uint16_t linearIndex) { + return (domainId << 24) + (uniqueIdentifier << 16) + linearIndex; } virtual ~HasParametersIF() {} @@ -56,11 +67,12 @@ public: * @param parameterId * @param parameterWrapper * @param newValues - * @param startAtIndex + * @param startAtIndex Linear index, runs left to right, top to bottom for + * matrix indexes. * @return */ - virtual ReturnValue_t getParameter(uint8_t domainId, uint16_t parameterId, - ParameterWrapper *parameterWrapper, + virtual ReturnValue_t getParameter(uint8_t domainId, + uint16_t uniqueIdentifier, ParameterWrapper *parameterWrapper, const ParameterWrapper *newValues, uint16_t startAtIndex) = 0; }; diff --git a/parameters/ParameterHelper.cpp b/parameters/ParameterHelper.cpp index 659b00de..d2193b28 100644 --- a/parameters/ParameterHelper.cpp +++ b/parameters/ParameterHelper.cpp @@ -9,15 +9,20 @@ ParameterHelper::~ParameterHelper() { } ReturnValue_t ParameterHelper::handleParameterMessage(CommandMessage *message) { + if(storage == nullptr) { + // ParameterHelper was not initialized + return HasReturnvaluesIF::RETURN_FAILED; + } + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; switch (message->getCommand()) { case ParameterMessage::CMD_PARAMETER_DUMP: { ParameterWrapper description; uint8_t domain = HasParametersIF::getDomain( ParameterMessage::getParameterId(message)); - uint16_t parameterId = HasParametersIF::getMatrixId( + uint8_t uniqueIdentifier = HasParametersIF::getUniqueIdentifierId( ParameterMessage::getParameterId(message)); - result = owner->getParameter(domain, parameterId, + result = owner->getParameter(domain, uniqueIdentifier, &description, &description, 0); if (result == HasReturnvaluesIF::RETURN_OK) { result = sendParameter(message->getSender(), @@ -26,49 +31,48 @@ ReturnValue_t ParameterHelper::handleParameterMessage(CommandMessage *message) { } break; case ParameterMessage::CMD_PARAMETER_LOAD: { - uint8_t domain = HasParametersIF::getDomain( - ParameterMessage::getParameterId(message)); - uint16_t parameterId = HasParametersIF::getMatrixId( - ParameterMessage::getParameterId(message)); - uint8_t index = HasParametersIF::getIndex( - ParameterMessage::getParameterId(message)); + ParameterId_t parameterId = 0; + uint8_t ptc = 0; + uint8_t pfc = 0; + uint8_t rows = 0; + uint8_t columns = 0; + store_address_t storeId = ParameterMessage::getParameterLoadCommand( + message, ¶meterId, &ptc, &pfc, &rows, &columns); + Type type(Type::getActualType(ptc, pfc)); - const uint8_t *storedStream = nullptr; - size_t storedStreamSize = 0; - result = storage->getData( - ParameterMessage::getStoreId(message), &storedStream, - &storedStreamSize); + uint8_t domain = HasParametersIF::getDomain(parameterId); + uint8_t uniqueIdentifier = HasParametersIF::getUniqueIdentifierId( + parameterId); + uint16_t linearIndex = HasParametersIF::getIndex(parameterId); + + ConstStorageAccessor accessor(storeId); + result = storage->getData(storeId, accessor); if (result != HasReturnvaluesIF::RETURN_OK) { sif::error << "ParameterHelper::handleParameterMessage: Getting" - " store data failed for load command." << std::endl; + << " store data failed for load command." << std::endl; break; } ParameterWrapper streamWrapper; - result = streamWrapper.set(storedStream, storedStreamSize); - if (result != HasReturnvaluesIF::RETURN_OK) { - storage->deleteData(ParameterMessage::getStoreId(message)); - break; + result = streamWrapper.set(type, rows, columns, accessor.data(), + accessor.size()); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; } ParameterWrapper ownerWrapper; - result = owner->getParameter(domain, parameterId, &ownerWrapper, - &streamWrapper, index); + result = owner->getParameter(domain, uniqueIdentifier, &ownerWrapper, + &streamWrapper, linearIndex); + + result = ownerWrapper.copyFrom(&streamWrapper, linearIndex); if (result != HasReturnvaluesIF::RETURN_OK) { - storage->deleteData(ParameterMessage::getStoreId(message)); - break; + return result; } - result = ownerWrapper.copyFrom(&streamWrapper, index); - - storage->deleteData(ParameterMessage::getStoreId(message)); - - if (result == HasReturnvaluesIF::RETURN_OK) { - result = sendParameter(message->getSender(), - ParameterMessage::getParameterId(message), &ownerWrapper); - } - } + result = sendParameter(message->getSender(), + ParameterMessage::getParameterId(message), &ownerWrapper); break; + } default: return HasReturnvaluesIF::RETURN_FAILED; } @@ -113,15 +117,13 @@ ReturnValue_t ParameterHelper::sendParameter(MessageQueueId_t to, uint32_t id, } ReturnValue_t ParameterHelper::initialize() { - ownerQueueId = owner->getCommandQueue(); + ownerQueueId = owner->getCommandQueue(); - - storage = objectManager->get(objects::IPC_STORE); - if (storage == NULL) { - return HasReturnvaluesIF::RETURN_FAILED; - } else { - return HasReturnvaluesIF::RETURN_OK; - } + storage = objectManager->get(objects::IPC_STORE); + if (storage == nullptr) { + return ObjectManagerIF::CHILD_INIT_FAILED; + } + return HasReturnvaluesIF::RETURN_OK; } void ParameterHelper::rejectCommand(MessageQueueId_t to, ReturnValue_t reason, diff --git a/parameters/ParameterMessage.cpp b/parameters/ParameterMessage.cpp index e9f3191f..af1f9ce8 100644 --- a/parameters/ParameterMessage.cpp +++ b/parameters/ParameterMessage.cpp @@ -25,10 +25,26 @@ void ParameterMessage::setParameterDumpReply(CommandMessage* message, } void ParameterMessage::setParameterLoadCommand(CommandMessage* message, - ParameterId_t id, store_address_t storageID) { + ParameterId_t id, store_address_t storeId, uint8_t ptc, uint8_t pfc, + uint8_t rows = 1, uint8_t columns = 1) { message->setCommand(CMD_PARAMETER_LOAD); message->setParameter(id); - message->setParameter2(storageID.raw); + message->setParameter2(storeId.raw); + uint32_t packedParameterSettings = (ptc << 24) | (pfc << 16) | + (rows << 8) | columns; + message->setParameter3(packedParameterSettings); +} + +store_address_t ParameterMessage::getParameterLoadCommand( + const CommandMessage *message, ParameterId_t* parameterId, uint8_t *ptc, + uint8_t *pfc, uint8_t *rows, uint8_t *columns) { + *parameterId = message->getParameter2(); + uint32_t packedParamSettings = message->getParameter3(); + *ptc = packedParamSettings >> 24 & 0xff; + *pfc = packedParamSettings >> 16 & 0xff; + *rows = packedParamSettings >> 8 & 0xff; + *columns = packedParamSettings & 0xff; + return message->getParameter2(); } void ParameterMessage::clear(CommandMessage* message) { diff --git a/parameters/ParameterMessage.h b/parameters/ParameterMessage.h index 31d7fe0b..fd4481ee 100644 --- a/parameters/ParameterMessage.h +++ b/parameters/ParameterMessage.h @@ -5,6 +5,20 @@ #include "../ipc/CommandMessage.h" #include "../storagemanager/StorageManagerIF.h" +/** + * @brief ParameterMessage interface + * @details + * General structure of a parameter message: + * 1. 4-byte Object ID + * 2. 4-byte Parameter ID, first byte is Domain ID, second byte is unique + * identifier, third and fourth byte is linear index to start from + * 3. 4-byte Parameter Settings. First byte and second byte are the PTC and PFC + * ECSS type identifiers (see ECSS-E-ST-70-41C15 p.428 or Type class in + * globalfunctions). Third byte is the number of rows and fourth byte + * is the number of columns. For single variable parameters, this will + * be [1, 1]. + * + */ class ParameterMessage { private: ParameterMessage(); @@ -20,8 +34,27 @@ public: ParameterId_t id); static void setParameterDumpReply(CommandMessage* message, ParameterId_t id, store_address_t storageID); + + /** + * Command to set a load parameter message. The CCSDS / ECSS type in + * form of a PTC and a PFC is expected. See ECSS-E-ST-70-41C15 p.428 + * for all types or the Type class in globalfunctions. + * @param message + * @param id + * @param storeId + * @param ptc Type information according to CCSDS/ECSS standards + * @param pfc Type information according to CCSDS/ECSS standards + * @param rows Set number of rows in parameter set, minimum one. + * @param columns Set number of columns in parameter set, minimum one + */ static void setParameterLoadCommand(CommandMessage* message, - ParameterId_t id, store_address_t storageID); + ParameterId_t id, store_address_t storeId, uint8_t ptc, + uint8_t pfc, uint8_t rows, uint8_t columns); + + static store_address_t getParameterLoadCommand( + const CommandMessage* message, ParameterId_t* parameterId, + uint8_t* ptc, uint8_t* pfc, uint8_t* rows, uint8_t* columns) ; + static void clear(CommandMessage* message); }; diff --git a/parameters/ParameterWrapper.cpp b/parameters/ParameterWrapper.cpp index 6adc6857..23955516 100644 --- a/parameters/ParameterWrapper.cpp +++ b/parameters/ParameterWrapper.cpp @@ -115,7 +115,7 @@ ReturnValue_t ParameterWrapper::deSerializeData(uint8_t startingRow, uint8_t fromColumns) { //treat from as a continuous Stream as we copy all of it - const uint8_t *fromAsStream = (const uint8_t*) from; + const uint8_t *fromAsStream = reinterpret_cast(from); size_t streamSize = fromRows * fromColumns * sizeof(T); ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; @@ -123,8 +123,8 @@ ReturnValue_t ParameterWrapper::deSerializeData(uint8_t startingRow, for (uint8_t fromRow = 0; fromRow < fromRows; fromRow++) { //get the start element of this row in data - T *dataWithDataType = ((T*) data) - + (((startingRow + fromRow) * columns) + startingColumn); + uint16_t offset = (((startingRow + fromRow) * columns) + startingColumn); + T *dataWithDataType = static_cast(data) + offset; for (uint8_t fromColumn = 0; fromColumn < fromColumns; fromColumn++) { result = SerializeAdapter::deSerialize( @@ -159,6 +159,23 @@ ReturnValue_t ParameterWrapper::deSerialize(const uint8_t **buffer, return copyFrom(&streamDescription, startWritingAtIndex); } +ReturnValue_t ParameterWrapper::set(Type type, uint8_t rows, uint8_t columns, + const void *data, size_t dataSize) { + this->type = type; + this->rows = rows; + this->columns = columns; + + size_t expectedSize = type.getSize() * rows * columns; + if (expectedSize < dataSize) { + return SerializeIF::STREAM_TOO_SHORT; + } + + this->data = nullptr; + this->readonlyData = data; + pointsToStream = true; + return HasReturnvaluesIF::RETURN_OK; +} + ReturnValue_t ParameterWrapper::set(const uint8_t *stream, size_t streamSize, const uint8_t **remainingStream, size_t *remainingSize) { ReturnValue_t result = SerializeAdapter::deSerialize(&type, &stream, @@ -202,11 +219,13 @@ ReturnValue_t ParameterWrapper::set(const uint8_t *stream, size_t streamSize, ReturnValue_t ParameterWrapper::copyFrom(const ParameterWrapper *from, uint16_t startWritingAtIndex) { - if (data == NULL) { + // TODO: Optional diagnostic output (which can be disabled in FSFWConfig) + // to determined faulty implementations and configuration errors quickly. + if (data == nullptr) { return READONLY; } - if (from->readonlyData == NULL) { + if (from->readonlyData == nullptr) { return SOURCE_NOT_SET; } @@ -214,9 +233,16 @@ ReturnValue_t ParameterWrapper::copyFrom(const ParameterWrapper *from, return DATATYPE_MISSMATCH; } + // The smallest allowed value for rows and columns is one. + if(rows == 0 or columns == 0) { + return COLUMN_OR_ROWS_ZERO; + } + //check if from fits into this - uint8_t startingRow = startWritingAtIndex / columns; - uint8_t startingColumn = startWritingAtIndex % columns; + uint8_t startingRow = 0; + uint8_t startingColumn = 0; + ParameterWrapper::convertLinearIndexToRowAndColumn(startWritingAtIndex, + &startingRow, &startingColumn); if ((from->rows > (rows - startingRow)) || (from->columns > (columns - startingColumn))) { @@ -279,3 +305,18 @@ ReturnValue_t ParameterWrapper::copyFrom(const ParameterWrapper *from, return result; } + +void ParameterWrapper::convertLinearIndexToRowAndColumn(uint16_t index, + uint8_t *row, uint8_t *column) { + if(row == nullptr or column == nullptr) { + return; + } + // Integer division. + *row = index / columns; + *column = index % columns; +} + +uint16_t ParameterWrapper::convertRowAndColumnToLinearIndex(uint8_t row, + uint8_t column) { + return row * columns + column; +} diff --git a/parameters/ParameterWrapper.h b/parameters/ParameterWrapper.h index f07205d4..168a535f 100644 --- a/parameters/ParameterWrapper.h +++ b/parameters/ParameterWrapper.h @@ -22,6 +22,7 @@ public: static const ReturnValue_t SOURCE_NOT_SET = MAKE_RETURN_CODE(0x05); static const ReturnValue_t OUT_OF_BOUNDS = MAKE_RETURN_CODE(0x06); static const ReturnValue_t NOT_SET = MAKE_RETURN_CODE(0x07); + static const ReturnValue_t COLUMN_OR_ROWS_ZERO = MAKE_RETURN_CODE(0x08); ParameterWrapper(); ParameterWrapper(Type type, uint8_t rows, uint8_t columns, void *data); @@ -68,7 +69,7 @@ public: template void set(const T *readonlyData, uint8_t rows, uint8_t columns) { - this->data = NULL; + this->data = nullptr; this->readonlyData = readonlyData; this->type = PodTypeConversion::type; this->rows = rows; @@ -97,14 +98,19 @@ public: } template void setMatrix(T& member) { - this->set(member[0], sizeof(member)/sizeof(member[0]), sizeof(member[0])/sizeof(member[0][0])); + this->set(member[0], sizeof(member)/sizeof(member[0]), + sizeof(member[0])/sizeof(member[0][0])); } template void setMatrix(const T& member) { - this->set(member[0], sizeof(member)/sizeof(member[0]), sizeof(member[0])/sizeof(member[0][0])); + this->set(member[0], sizeof(member)/sizeof(member[0]), + sizeof(member[0])/sizeof(member[0][0])); } + ReturnValue_t set(Type type, uint8_t rows, uint8_t columns, + const void *data, size_t dataSize); + ReturnValue_t set(const uint8_t *stream, size_t streamSize, const uint8_t **remainingStream = nullptr, size_t *remainingSize = nullptr); @@ -113,6 +119,13 @@ public: uint16_t startWritingAtIndex); private: + + void convertLinearIndexToRowAndColumn(uint16_t index, + uint8_t *row, uint8_t *column); + + uint16_t convertRowAndColumnToLinearIndex(uint8_t row, + uint8_t column); + bool pointsToStream = false; Type type; @@ -148,9 +161,9 @@ inline ReturnValue_t ParameterWrapper::getElement(T *value, uint8_t row, if (pointsToStream) { const uint8_t *streamWithType = static_cast(readonlyData); streamWithType += (row * columns + column) * type.getSize(); - int32_t size = type.getSize(); + size_t size = type.getSize(); return SerializeAdapter::deSerialize(value, &streamWithType, - &size, true); + &size, SerializeIF::Endianness::BIG); } else { const T *dataWithType = static_cast(readonlyData); From 2f993cf39a91bce9cbebc3845476578ba39727e5 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 2 Nov 2020 15:51:52 +0100 Subject: [PATCH 017/119] some minor form stuff --- action/ActionHelper.cpp | 1 + action/ActionMessage.h | 18 +++++++++++------- action/CommandsActionsIF.h | 15 +++++++++------ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index 0d3baa88..28557916 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -1,5 +1,6 @@ #include "ActionHelper.h" #include "HasActionsIF.h" + #include "../ipc/MessageQueueSenderIF.h" #include "../objectmanager/ObjectManagerIF.h" diff --git a/action/ActionMessage.h b/action/ActionMessage.h index 0a718aab..7a859de0 100644 --- a/action/ActionMessage.h +++ b/action/ActionMessage.h @@ -1,5 +1,5 @@ -#ifndef ACTIONMESSAGE_H_ -#define ACTIONMESSAGE_H_ +#ifndef FSFW_ACTION_ACTIONMESSAGE_H_ +#define FSFW_ACTION_ACTIONMESSAGE_H_ #include "../ipc/CommandMessage.h" #include "../objectmanager/ObjectManagerIF.h" @@ -18,15 +18,19 @@ public: static const Command_t COMPLETION_SUCCESS = MAKE_COMMAND_ID(5); static const Command_t COMPLETION_FAILED = MAKE_COMMAND_ID(6); virtual ~ActionMessage(); - static void setCommand(CommandMessage* message, ActionId_t fid, store_address_t parameters); + static void setCommand(CommandMessage* message, ActionId_t fid, + store_address_t parameters); static ActionId_t getActionId(const CommandMessage* message ); static store_address_t getStoreId(const CommandMessage* message ); - static void setStepReply(CommandMessage* message, ActionId_t fid, uint8_t step, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + static void setStepReply(CommandMessage* message, ActionId_t fid, + uint8_t step, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); static uint8_t getStep(const CommandMessage* message ); static ReturnValue_t getReturnCode(const CommandMessage* message ); - static void setDataReply(CommandMessage* message, ActionId_t actionId, store_address_t data); - static void setCompletionReply(CommandMessage* message, ActionId_t fid, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + static void setDataReply(CommandMessage* message, ActionId_t actionId, + store_address_t data); + static void setCompletionReply(CommandMessage* message, ActionId_t fid, + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); static void clear(CommandMessage* message); }; -#endif /* ACTIONMESSAGE_H_ */ +#endif /* FSFW_ACTION_ACTIONMESSAGE_H_ */ diff --git a/action/CommandsActionsIF.h b/action/CommandsActionsIF.h index 3d04015d..491bfc70 100644 --- a/action/CommandsActionsIF.h +++ b/action/CommandsActionsIF.h @@ -1,5 +1,5 @@ -#ifndef COMMANDSACTIONSIF_H_ -#define COMMANDSACTIONSIF_H_ +#ifndef FSFW_ACTION_COMMANDSACTIONSIF_H_ +#define FSFW_ACTION_COMMANDSACTIONSIF_H_ #include "CommandActionHelper.h" #include "../returnvalues/HasReturnvaluesIF.h" @@ -24,11 +24,14 @@ public: virtual MessageQueueIF* getCommandQueuePtr() = 0; protected: virtual void stepSuccessfulReceived(ActionId_t actionId, uint8_t step) = 0; - virtual void stepFailedReceived(ActionId_t actionId, uint8_t step, ReturnValue_t returnCode) = 0; - virtual void dataReceived(ActionId_t actionId, const uint8_t* data, uint32_t size) = 0; + virtual void stepFailedReceived(ActionId_t actionId, uint8_t step, + ReturnValue_t returnCode) = 0; + virtual void dataReceived(ActionId_t actionId, const uint8_t* data, + uint32_t size) = 0; virtual void completionSuccessfulReceived(ActionId_t actionId) = 0; - virtual void completionFailedReceived(ActionId_t actionId, ReturnValue_t returnCode) = 0; + virtual void completionFailedReceived(ActionId_t actionId, + ReturnValue_t returnCode) = 0; }; -#endif /* COMMANDSACTIONSIF_H_ */ +#endif /* FSFW_ACTION_COMMANDSACTIONSIF_H_ */ From 83568e11d11e66f023ede3b7701df884c2129317 Mon Sep 17 00:00:00 2001 From: "Jakob.Meier" Date: Sat, 7 Nov 2020 20:37:48 +0100 Subject: [PATCH 018/119] removed unwanted changes --- devicehandlers/DeviceHandlerIF.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index 10c9b89a..52a3be4d 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -18,8 +18,6 @@ public: static const uint8_t TRANSITION_MODE_CHILD_ACTION_MASK = 0x20; static const uint8_t TRANSITION_MODE_BASE_ACTION_MASK = 0x10; - static constexpr DeviceCommandId_t NO_COMMAND = 0xffffffff; - /** * @brief This is the mode the device handler is in. * From 210d2de11e25bc2d521687aef35cf5f764df08ce Mon Sep 17 00:00:00 2001 From: "Jakob.Meier" Date: Sat, 7 Nov 2020 20:40:01 +0100 Subject: [PATCH 019/119] fetched fsfw.mk from upstream master --- fsfw.mk | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/fsfw.mk b/fsfw.mk index f2cdd7ab..c2c6e747 100644 --- a/fsfw.mk +++ b/fsfw.mk @@ -28,25 +28,12 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/*.cpp) # select the OS ifeq ($(OS_FSFW),rtems) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/rtems/*.cpp) - else ifeq ($(OS_FSFW),linux) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/linux/*.cpp) - else ifeq ($(OS_FSFW),freeRTOS) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/FreeRTOS/*.cpp) - else ifeq ($(OS_FSFW),host) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/host/*.cpp) -ifeq ($(OS),Windows_NT) -CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/windows/*.cpp) -else -# For now, the linux UDP bridge sources needs to be included manually by upper makefile -# for host OS because we can't be sure the OS is linux. -# Following lines can be used to do this: -# CXXSRC += $(FRAMEWORK_PATH)/osal/linux/TcUnixUdpPollingTask.cpp -# CXXSRC += $(FRAMEWORK_PATH)/osal/linux/TmTcUnixUdpBridge.cpp -endif - else $(error invalid OS_FSFW specified, valid OS_FSFW are rtems, linux, freeRTOS, host) endif @@ -70,5 +57,3 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcpacket/packetmatcher/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcpacket/pus/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcservices/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/pus/*.cpp) - -INCLUDES += $(CURRENTPATH) From 3eefcd3a2d52b7420c33dd34bc334093a37c3b03 Mon Sep 17 00:00:00 2001 From: "Jakob.Meier" Date: Sat, 7 Nov 2020 20:45:00 +0100 Subject: [PATCH 020/119] fsfw.mk adjusted to upstream master --- fsfw.mk | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/fsfw.mk b/fsfw.mk index c2c6e747..3639717b 100644 --- a/fsfw.mk +++ b/fsfw.mk @@ -28,12 +28,25 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/*.cpp) # select the OS ifeq ($(OS_FSFW),rtems) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/rtems/*.cpp) + else ifeq ($(OS_FSFW),linux) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/linux/*.cpp) + else ifeq ($(OS_FSFW),freeRTOS) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/FreeRTOS/*.cpp) + else ifeq ($(OS_FSFW),host) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/host/*.cpp) +ifeq ($(OS),Windows_NT) +CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/windows/*.cpp) +else +# For now, the linux UDP bridge sources needs to be included manually by upper makefile +# for host OS because we can't be sure the OS is linux. +# Following lines can be used to do this: +# CXXSRC += $(FRAMEWORK_PATH)/osal/linux/TcUnixUdpPollingTask.cpp +# CXXSRC += $(FRAMEWORK_PATH)/osal/linux/TmTcUnixUdpBridge.cpp +endif + else $(error invalid OS_FSFW specified, valid OS_FSFW are rtems, linux, freeRTOS, host) endif From bc81b5893c93cd6a68361bb2f1235220edc055da Mon Sep 17 00:00:00 2001 From: "Jakob.Meier" Date: Mon, 9 Nov 2020 17:34:53 +0100 Subject: [PATCH 021/119] removed debug output --- osal/linux/TmTcUnixUdpBridge.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osal/linux/TmTcUnixUdpBridge.cpp b/osal/linux/TmTcUnixUdpBridge.cpp index 71a39384..ab28623e 100644 --- a/osal/linux/TmTcUnixUdpBridge.cpp +++ b/osal/linux/TmTcUnixUdpBridge.cpp @@ -73,9 +73,9 @@ ReturnValue_t TmTcUnixUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { clientAddressLen = sizeof(serverAddress); } - char ipAddress [15]; - sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, - &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; +// char ipAddress [15]; +// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, +// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; ssize_t bytesSent = sendto(serverSocket, data, dataLen, flags, reinterpret_cast(&clientAddress), clientAddressLen); From 37fc22a11785d6363954169ba6bae3d40a4be493 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 9 Nov 2020 21:21:56 +0100 Subject: [PATCH 022/119] config folder renamed and improved --- .../{config => fsfwconfig}/FSFWConfig.h | 1 + .../{config => fsfwconfig}/OBSWConfig.h | 0 .../{config => fsfwconfig}/OBSWVersion.h | 0 .../devices/logicalAddresses.cpp | 0 .../devices/logicalAddresses.h | 2 +- .../devices/powerSwitcherList.cpp | 0 .../devices/powerSwitcherList.h | 0 .../events/subsystemIdRanges.h | 0 .../config.mk => fsfwconfig/fsfwconfig.mk} | 0 .../ipc/missionMessageTypes.cpp | 3 +- .../ipc/missionMessageTypes.h | 0 .../objects/Factory.cpp | 14 +- .../{config => fsfwconfig}/objects/Factory.h | 0 .../objects/systemObjectList.h | 0 .../fsfwconfig/objects/translateObjects.cpp | 271 ++++++++++++++++++ .../fsfwconfig/objects/translateObjects.h | 9 + .../PollingSequenceFactory.cpp | 0 .../pollingsequence/PollingSequenceFactory.h | 0 .../returnvalues/classIds.h | 0 defaultcfg/{config => fsfwconfig}/tmtc/apid.h | 0 .../{config => fsfwconfig}/tmtc/pusIds.h | 0 21 files changed, 291 insertions(+), 9 deletions(-) rename defaultcfg/{config => fsfwconfig}/FSFWConfig.h (98%) rename defaultcfg/{config => fsfwconfig}/OBSWConfig.h (100%) rename defaultcfg/{config => fsfwconfig}/OBSWVersion.h (100%) rename defaultcfg/{config => fsfwconfig}/devices/logicalAddresses.cpp (100%) rename defaultcfg/{config => fsfwconfig}/devices/logicalAddresses.h (89%) rename defaultcfg/{config => fsfwconfig}/devices/powerSwitcherList.cpp (100%) rename defaultcfg/{config => fsfwconfig}/devices/powerSwitcherList.h (100%) rename defaultcfg/{config => fsfwconfig}/events/subsystemIdRanges.h (100%) rename defaultcfg/{config/config.mk => fsfwconfig/fsfwconfig.mk} (100%) rename defaultcfg/{config => fsfwconfig}/ipc/missionMessageTypes.cpp (79%) rename defaultcfg/{config => fsfwconfig}/ipc/missionMessageTypes.h (100%) rename defaultcfg/{config => fsfwconfig}/objects/Factory.cpp (75%) rename defaultcfg/{config => fsfwconfig}/objects/Factory.h (100%) rename defaultcfg/{config => fsfwconfig}/objects/systemObjectList.h (100%) create mode 100644 defaultcfg/fsfwconfig/objects/translateObjects.cpp create mode 100644 defaultcfg/fsfwconfig/objects/translateObjects.h rename defaultcfg/{config => fsfwconfig}/pollingsequence/PollingSequenceFactory.cpp (100%) rename defaultcfg/{config => fsfwconfig}/pollingsequence/PollingSequenceFactory.h (100%) rename defaultcfg/{config => fsfwconfig}/returnvalues/classIds.h (100%) rename defaultcfg/{config => fsfwconfig}/tmtc/apid.h (100%) rename defaultcfg/{config => fsfwconfig}/tmtc/pusIds.h (100%) diff --git a/defaultcfg/config/FSFWConfig.h b/defaultcfg/fsfwconfig/FSFWConfig.h similarity index 98% rename from defaultcfg/config/FSFWConfig.h rename to defaultcfg/fsfwconfig/FSFWConfig.h index ea86152c..be3717b4 100644 --- a/defaultcfg/config/FSFWConfig.h +++ b/defaultcfg/fsfwconfig/FSFWConfig.h @@ -3,6 +3,7 @@ #include #include +#include //! Used to determine whether C++ ostreams are used //! Those can lead to code bloat. diff --git a/defaultcfg/config/OBSWConfig.h b/defaultcfg/fsfwconfig/OBSWConfig.h similarity index 100% rename from defaultcfg/config/OBSWConfig.h rename to defaultcfg/fsfwconfig/OBSWConfig.h diff --git a/defaultcfg/config/OBSWVersion.h b/defaultcfg/fsfwconfig/OBSWVersion.h similarity index 100% rename from defaultcfg/config/OBSWVersion.h rename to defaultcfg/fsfwconfig/OBSWVersion.h diff --git a/defaultcfg/config/devices/logicalAddresses.cpp b/defaultcfg/fsfwconfig/devices/logicalAddresses.cpp similarity index 100% rename from defaultcfg/config/devices/logicalAddresses.cpp rename to defaultcfg/fsfwconfig/devices/logicalAddresses.cpp diff --git a/defaultcfg/config/devices/logicalAddresses.h b/defaultcfg/fsfwconfig/devices/logicalAddresses.h similarity index 89% rename from defaultcfg/config/devices/logicalAddresses.h rename to defaultcfg/fsfwconfig/devices/logicalAddresses.h index 174fa788..e0827ba3 100644 --- a/defaultcfg/config/devices/logicalAddresses.h +++ b/defaultcfg/fsfwconfig/devices/logicalAddresses.h @@ -1,8 +1,8 @@ #ifndef CONFIG_DEVICES_LOGICALADDRESSES_H_ #define CONFIG_DEVICES_LOGICALADDRESSES_H_ -#include #include +#include "../objects/systemObjectList.h" #include /** diff --git a/defaultcfg/config/devices/powerSwitcherList.cpp b/defaultcfg/fsfwconfig/devices/powerSwitcherList.cpp similarity index 100% rename from defaultcfg/config/devices/powerSwitcherList.cpp rename to defaultcfg/fsfwconfig/devices/powerSwitcherList.cpp diff --git a/defaultcfg/config/devices/powerSwitcherList.h b/defaultcfg/fsfwconfig/devices/powerSwitcherList.h similarity index 100% rename from defaultcfg/config/devices/powerSwitcherList.h rename to defaultcfg/fsfwconfig/devices/powerSwitcherList.h diff --git a/defaultcfg/config/events/subsystemIdRanges.h b/defaultcfg/fsfwconfig/events/subsystemIdRanges.h similarity index 100% rename from defaultcfg/config/events/subsystemIdRanges.h rename to defaultcfg/fsfwconfig/events/subsystemIdRanges.h diff --git a/defaultcfg/config/config.mk b/defaultcfg/fsfwconfig/fsfwconfig.mk similarity index 100% rename from defaultcfg/config/config.mk rename to defaultcfg/fsfwconfig/fsfwconfig.mk diff --git a/defaultcfg/config/ipc/missionMessageTypes.cpp b/defaultcfg/fsfwconfig/ipc/missionMessageTypes.cpp similarity index 79% rename from defaultcfg/config/ipc/missionMessageTypes.cpp rename to defaultcfg/fsfwconfig/ipc/missionMessageTypes.cpp index e2edbf9c..b41b87ba 100644 --- a/defaultcfg/config/ipc/missionMessageTypes.cpp +++ b/defaultcfg/fsfwconfig/ipc/missionMessageTypes.cpp @@ -1,4 +1,5 @@ -#include +#include "missionMessageTypes.h" + #include void messagetypes::clearMissionMessage(CommandMessage* message) { diff --git a/defaultcfg/config/ipc/missionMessageTypes.h b/defaultcfg/fsfwconfig/ipc/missionMessageTypes.h similarity index 100% rename from defaultcfg/config/ipc/missionMessageTypes.h rename to defaultcfg/fsfwconfig/ipc/missionMessageTypes.h diff --git a/defaultcfg/config/objects/Factory.cpp b/defaultcfg/fsfwconfig/objects/Factory.cpp similarity index 75% rename from defaultcfg/config/objects/Factory.cpp rename to defaultcfg/fsfwconfig/objects/Factory.cpp index 51dd6130..41333b1c 100644 --- a/defaultcfg/config/objects/Factory.cpp +++ b/defaultcfg/fsfwconfig/objects/Factory.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -31,15 +32,15 @@ void Factory::produce(void) { setStaticFrameworkObjectIds(); new EventManager(objects::EVENT_MANAGER); new HealthTable(objects::HEALTH_TABLE); - //new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); + new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); } void Factory::setStaticFrameworkObjectIds() { - PusServiceBase::packetSource = objects::PUS_PACKET_DISTRIBUTOR; - PusServiceBase::packetDestination = objects::TM_FUNNEL; + PusServiceBase::packetSource = objects::NO_OBJECT; + PusServiceBase::packetDestination = objects::NO_OBJECT; - CommandingServiceBase::defaultPacketSource = objects::PUS_PACKET_DISTRIBUTOR; - CommandingServiceBase::defaultPacketDestination = objects::TM_FUNNEL; + CommandingServiceBase::defaultPacketSource = objects::NO_OBJECT; + CommandingServiceBase::defaultPacketDestination = objects::NO_OBJECT; VerificationReporter::messageReceiver = objects::PUS_SERVICE_1_VERIFICATION; @@ -48,7 +49,6 @@ void Factory::setStaticFrameworkObjectIds() { DeviceHandlerFailureIsolation::powerConfirmationId = objects::NO_OBJECT; - TmPacketStored::timeStamperId = objects::PUS_TIME; - //TmFunnel::downlinkDestination = objects::NO_OBJECT; + TmPacketStored::timeStamperId = objects::NO_OBJECT; } diff --git a/defaultcfg/config/objects/Factory.h b/defaultcfg/fsfwconfig/objects/Factory.h similarity index 100% rename from defaultcfg/config/objects/Factory.h rename to defaultcfg/fsfwconfig/objects/Factory.h diff --git a/defaultcfg/config/objects/systemObjectList.h b/defaultcfg/fsfwconfig/objects/systemObjectList.h similarity index 100% rename from defaultcfg/config/objects/systemObjectList.h rename to defaultcfg/fsfwconfig/objects/systemObjectList.h diff --git a/defaultcfg/fsfwconfig/objects/translateObjects.cpp b/defaultcfg/fsfwconfig/objects/translateObjects.cpp new file mode 100644 index 00000000..73cd02bf --- /dev/null +++ b/defaultcfg/fsfwconfig/objects/translateObjects.cpp @@ -0,0 +1,271 @@ +/** + * @brief Auto-generated object translation file. Contains 86 translations. + * Generated on: 2020-08-25 00:57:14 + **/ +#include "translateObjects.h" + +const char *AT91_UART2_TEST_TASK_STRING = "AT91_UART2_TEST_TASK"; +const char *ARDUINO_0_STRING = "ARDUINO_0"; +const char *ARDUINO_1_STRING = "ARDUINO_1"; +const char *ARDUINO_2_STRING = "ARDUINO_2"; +const char *ARDUINO_3_STRING = "ARDUINO_3"; +const char *ARDUINO_4_STRING = "ARDUINO_4"; +const char *AT91_I2C_TEST_TASK_STRING = "AT91_I2C_TEST_TASK"; +const char *LED_TASK_STRING = "LED_TASK"; +const char *TEST_TASK_STRING = "TEST_TASK"; +const char *PCDU_HANDLER_STRING = "PCDU_HANDLER"; +const char *DUMMY_HANDLER_STRING = "DUMMY_HANDLER"; +const char *SuS_ADC1_DEC2_STRING = "SuS_ADC1_DEC2"; +const char *GPS0_HANDLER_STRING = "GPS0_HANDLER"; +const char *DLR_PVCH_STRING = "DLR_PVCH"; +const char *GYRO1_STRING = "GYRO1"; +const char *DLR_IRAS_STRING = "DLR_IRAS"; +const char *SPI_Test_PT1000_STRING = "SPI_Test_PT1000"; +const char *PT1000_Syrlinks_DEC1_O1_STRING = "PT1000_Syrlinks_DEC1_O1"; +const char *PT1000_Camera_DEC1_O2_STRING = "PT1000_Camera_DEC1_O2"; +const char *PT1000_SuS1_DEC1_O3_STRING = "PT1000_SuS1_DEC1_O3"; +const char *PT1000_SuS2_DEC1_O4_STRING = "PT1000_SuS2_DEC1_O4"; +const char *PT1000_SuS3_DEC1_O5_STRING = "PT1000_SuS3_DEC1_O5"; +const char *PT1000_PVHC_DEC1_O6_STRING = "PT1000_PVHC_DEC1_O6"; +const char *SPI_Test_Gyro_STRING = "SPI_Test_Gyro"; +const char *PT1000_CCSDS1_DEC2_STRING = "PT1000_CCSDS1_DEC2"; +const char *PT1000_MGT1_DEC2_STRING = "PT1000_MGT1_DEC2"; +const char *PT1000_SuS4_DEC2_STRING = "PT1000_SuS4_DEC2"; +const char *PT1000_SuS5_DEC2_STRING = "PT1000_SuS5_DEC2"; +const char *PT1000_SuS6_DEC2_STRING = "PT1000_SuS6_DEC2"; +const char *PT1000_PVCH_DEC2_STRING = "PT1000_PVCH_DEC2"; +const char *PT1000_Iridium_DEC3_STRING = "PT1000_Iridium_DEC3"; +const char *PT1000_CCSDS2_DEC3_STRING = "PT1000_CCSDS2_DEC3"; +const char *PT1000_SuS7_DEC3_STRING = "PT1000_SuS7_DEC3"; +const char *PT1000_SuS8_DEC3_STRING = "PT1000_SuS8_DEC3"; +const char *PT1000_PVCH_DEC3_STRING = "PT1000_PVCH_DEC3"; +const char *GYRO2_STRING = "GYRO2"; +const char *PT1000_PLOC_DEC4_STRING = "PT1000_PLOC_DEC4"; +const char *PT1000_SuS9_DEC4_STRING = "PT1000_SuS9_DEC4"; +const char *PT1000_SuS10_DEC4_STRING = "PT1000_SuS10_DEC4"; +const char *PT1000_PVHC_DEC4_STRING = "PT1000_PVHC_DEC4"; +const char *SuS_ADC_DEC4_STRING = "SuS_ADC_DEC4"; +const char *GPS1_HANDLER_STRING = "GPS1_HANDLER"; +const char *DUMMY_GPS_COM_IF_STRING = "DUMMY_GPS_COM_IF"; +const char *RS232_DEVICE_COM_IF_STRING = "RS232_DEVICE_COM_IF"; +const char *I2C_DEVICE_COM_IF_STRING = "I2C_DEVICE_COM_IF"; +const char *GPIO_DEVICE_COM_IF_STRING = "GPIO_DEVICE_COM_IF"; +const char *SPI_POLLING_TASK_STRING = "SPI_POLLING_TASK"; +const char *SPI_DEVICE_COM_IF_STRING = "SPI_DEVICE_COM_IF"; +const char *DUMMY_ECHO_COM_IF_STRING = "DUMMY_ECHO_COM_IF"; +const char *SD_CARD_HANDLER_STRING = "SD_CARD_HANDLER"; +const char *CCSDS_PACKET_DISTRIBUTOR_STRING = "CCSDS_PACKET_DISTRIBUTOR"; +const char *PUS_PACKET_DISTRIBUTOR_STRING = "PUS_PACKET_DISTRIBUTOR"; +const char *UDP_TMTC_BRIDGE_STRING = "UDP_TMTC_BRIDGE"; +const char *EMAC_POLLING_TASK_STRING = "EMAC_POLLING_TASK"; +const char *SERIAL_TMTC_BRIDGE_STRING = "SERIAL_TMTC_BRIDGE"; +const char *SERIAL_RING_BUFFER_STRING = "SERIAL_RING_BUFFER"; +const char *SERIAL_POLLING_TASK_STRING = "SERIAL_POLLING_TASK"; +const char *PUS_SERVICE_1_STRING = "PUS_SERVICE_1"; +const char *PUS_SERVICE_2_STRING = "PUS_SERVICE_2"; +const char *PUS_SERVICE_3_STRING = "PUS_SERVICE_3"; +const char *PUS_SERVICE_3_PSB_STRING = "PUS_SERVICE_3_PSB"; +const char *PUS_SERVICE_5_STRING = "PUS_SERVICE_5"; +const char *PUS_SERVICE_6_STRING = "PUS_SERVICE_6"; +const char *PUS_SERVICE_8_STRING = "PUS_SERVICE_8"; +const char *PUS_SERVICE_9_STRING = "PUS_SERVICE_9"; +const char *PUS_SERVICE_17_STRING = "PUS_SERVICE_17"; +const char *PUS_SERVICE_20_STRING = "PUS_SERVICE_20"; +const char *PUS_SERVICE_23_STRING = "PUS_SERVICE_23"; +const char *PUS_SERVICE_200_STRING = "PUS_SERVICE_200"; +const char *PUS_SERVICE_201_STRING = "PUS_SERVICE_201"; +const char *PUS_TIME_STRING = "PUS_TIME"; +const char *PUS_FUNNEL_STRING = "PUS_FUNNEL"; +const char *FREERTOS_TASK_MONITOR_STRING = "FREERTOS_TASK_MONITOR"; +const char *HEALTH_TABLE_STRING = "HEALTH_TABLE"; +const char *MODE_STORE_STRING = "MODE_STORE"; +const char *EVENT_MANAGER_STRING = "EVENT_MANAGER"; +const char *INTERNAL_ERROR_REPORTER_STRING = "INTERNAL_ERROR_REPORTER"; +const char *TC_STORE_STRING = "TC_STORE"; +const char *TM_STORE_STRING = "TM_STORE"; +const char *IPC_STORE_STRING = "IPC_STORE"; +const char *AT91_SPI_TEST_TASK_STRING = "AT91_SPI_TEST_TASK"; +const char *STM32_TEST_TASK_STRING = "STM32_TEST_TASK"; +const char *AT91_UART0_TEST_TASK_STRING = "AT91_UART0_TEST_TASK"; +const char *TC_INJECTOR_STRING = "TC_INJECTOR"; +const char *NO_OBJECT_STRING = "NO_OBJECT"; + +const char* translateObject(object_id_t object){ + switch((object&0xFFFFFFFF)){ + case 0x000123336: + return AT91_UART2_TEST_TASK_STRING; + case 0x01010100: + return ARDUINO_0_STRING; + case 0x01010101: + return ARDUINO_1_STRING; + case 0x01010102: + return ARDUINO_2_STRING; + case 0x01010103: + return ARDUINO_3_STRING; + case 0x01010104: + return ARDUINO_4_STRING; + case 0x12345678: + return AT91_I2C_TEST_TASK_STRING; + case 0x12345777: + return LED_TASK_STRING; + case 0x42694269: + return TEST_TASK_STRING; + case 0x44003200: + return PCDU_HANDLER_STRING; + case 0x4400AFFE: + return DUMMY_HANDLER_STRING; + case 0x44020108: + return SuS_ADC1_DEC2_STRING; + case 0x44101F00: + return GPS0_HANDLER_STRING; + case 0x44104000: + return DLR_PVCH_STRING; + case 0x44105000: + return GYRO1_STRING; + case 0x44106000: + return DLR_IRAS_STRING; + case 0x44115400: + return SPI_Test_PT1000_STRING; + case 0x44115401: + return PT1000_Syrlinks_DEC1_O1_STRING; + case 0x44115402: + return PT1000_Camera_DEC1_O2_STRING; + case 0x44115404: + return PT1000_SuS1_DEC1_O3_STRING; + case 0x44115405: + return PT1000_SuS2_DEC1_O4_STRING; + case 0x44115406: + return PT1000_SuS3_DEC1_O5_STRING; + case 0x44115407: + return PT1000_PVHC_DEC1_O6_STRING; + case 0x44115500: + return SPI_Test_Gyro_STRING; + case 0x44125401: + return PT1000_CCSDS1_DEC2_STRING; + case 0x44125403: + return PT1000_MGT1_DEC2_STRING; + case 0x44125404: + return PT1000_SuS4_DEC2_STRING; + case 0x44125405: + return PT1000_SuS5_DEC2_STRING; + case 0x44125406: + return PT1000_SuS6_DEC2_STRING; + case 0x44125407: + return PT1000_PVCH_DEC2_STRING; + case 0x44130301: + return PT1000_Iridium_DEC3_STRING; + case 0x44130302: + return PT1000_CCSDS2_DEC3_STRING; + case 0x44130305: + return PT1000_SuS7_DEC3_STRING; + case 0x44130306: + return PT1000_SuS8_DEC3_STRING; + case 0x44130307: + return PT1000_PVCH_DEC3_STRING; + case 0x44130308: + return GYRO2_STRING; + case 0x44145401: + return PT1000_PLOC_DEC4_STRING; + case 0x44145404: + return PT1000_SuS9_DEC4_STRING; + case 0x44145405: + return PT1000_SuS10_DEC4_STRING; + case 0x44145406: + return PT1000_PVHC_DEC4_STRING; + case 0x44145407: + return SuS_ADC_DEC4_STRING; + case 0x44202000: + return GPS1_HANDLER_STRING; + case 0x49001F00: + return DUMMY_GPS_COM_IF_STRING; + case 0x49005200: + return RS232_DEVICE_COM_IF_STRING; + case 0x49005300: + return I2C_DEVICE_COM_IF_STRING; + case 0x49005400: + return GPIO_DEVICE_COM_IF_STRING; + case 0x49005410: + return SPI_POLLING_TASK_STRING; + case 0x49005600: + return SPI_DEVICE_COM_IF_STRING; + case 0x4900AFFE: + return DUMMY_ECHO_COM_IF_STRING; + case 0x4D0073AD: + return SD_CARD_HANDLER_STRING; + case 0x50000100: + return CCSDS_PACKET_DISTRIBUTOR_STRING; + case 0x50000200: + return PUS_PACKET_DISTRIBUTOR_STRING; + case 0x50000300: + return UDP_TMTC_BRIDGE_STRING; + case 0x50000400: + return EMAC_POLLING_TASK_STRING; + case 0x50000500: + return SERIAL_TMTC_BRIDGE_STRING; + case 0x50000550: + return SERIAL_RING_BUFFER_STRING; + case 0x50000600: + return SERIAL_POLLING_TASK_STRING; + case 0x51000100: + return PUS_SERVICE_1_STRING; + case 0x51000200: + return PUS_SERVICE_2_STRING; + case 0x51000300: + return PUS_SERVICE_3_STRING; + case 0x51000310: + return PUS_SERVICE_3_PSB_STRING; + case 0x51000400: + return PUS_SERVICE_5_STRING; + case 0x51000500: + return PUS_SERVICE_6_STRING; + case 0x51000800: + return PUS_SERVICE_8_STRING; + case 0x51000900: + return PUS_SERVICE_9_STRING; + case 0x51001700: + return PUS_SERVICE_17_STRING; + case 0x51002000: + return PUS_SERVICE_20_STRING; + case 0x51002300: + return PUS_SERVICE_23_STRING; + case 0x51020000: + return PUS_SERVICE_200_STRING; + case 0x51020100: + return PUS_SERVICE_201_STRING; + case 0x52000001: + return PUS_TIME_STRING; + case 0x52000002: + return PUS_FUNNEL_STRING; + case 0x53000000: + return PUS_SERVICE_1_STRING; + case 0x53000003: + return FREERTOS_TASK_MONITOR_STRING; + case 0x53010000: + return HEALTH_TABLE_STRING; + case 0x53010100: + return MODE_STORE_STRING; + case 0x53030000: + return EVENT_MANAGER_STRING; + case 0x53040000: + return INTERNAL_ERROR_REPORTER_STRING; + case 0x534f0100: + return TC_STORE_STRING; + case 0x534f0200: + return TM_STORE_STRING; + case 0x534f0300: + return IPC_STORE_STRING; + case 0x66666666: + return AT91_SPI_TEST_TASK_STRING; + case 0x77777777: + return STM32_TEST_TASK_STRING; + case 0x87654321: + return AT91_UART0_TEST_TASK_STRING; + case 0x99000001: + return TC_INJECTOR_STRING; + case 0xFFFFFFFF: + return NO_OBJECT_STRING; + default: + return "UNKNOWN_OBJECT"; + } + return 0; +} diff --git a/defaultcfg/fsfwconfig/objects/translateObjects.h b/defaultcfg/fsfwconfig/objects/translateObjects.h new file mode 100644 index 00000000..5b656940 --- /dev/null +++ b/defaultcfg/fsfwconfig/objects/translateObjects.h @@ -0,0 +1,9 @@ +#ifndef CONFIG_OBJECTS_TRANSLATEOBJECTS_H_ +#define CONFIG_OBJECTS_TRANSLATEOBJECTS_H_ + +#include + +const char* translateObject(object_id_t object); + + +#endif /* CONFIG_OBJECTS_TRANSLATEOBJECTS_H_ */ diff --git a/defaultcfg/config/pollingsequence/PollingSequenceFactory.cpp b/defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.cpp similarity index 100% rename from defaultcfg/config/pollingsequence/PollingSequenceFactory.cpp rename to defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.cpp diff --git a/defaultcfg/config/pollingsequence/PollingSequenceFactory.h b/defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.h similarity index 100% rename from defaultcfg/config/pollingsequence/PollingSequenceFactory.h rename to defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.h diff --git a/defaultcfg/config/returnvalues/classIds.h b/defaultcfg/fsfwconfig/returnvalues/classIds.h similarity index 100% rename from defaultcfg/config/returnvalues/classIds.h rename to defaultcfg/fsfwconfig/returnvalues/classIds.h diff --git a/defaultcfg/config/tmtc/apid.h b/defaultcfg/fsfwconfig/tmtc/apid.h similarity index 100% rename from defaultcfg/config/tmtc/apid.h rename to defaultcfg/fsfwconfig/tmtc/apid.h diff --git a/defaultcfg/config/tmtc/pusIds.h b/defaultcfg/fsfwconfig/tmtc/pusIds.h similarity index 100% rename from defaultcfg/config/tmtc/pusIds.h rename to defaultcfg/fsfwconfig/tmtc/pusIds.h From 136f04a5c6ce99b15a6f2c9d9c8c7eb409dafd43 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 9 Nov 2020 21:23:51 +0100 Subject: [PATCH 023/119] trnaslation files removed --- .../fsfwconfig/objects/translateObjects.cpp | 271 ------------------ .../fsfwconfig/objects/translateObjects.h | 9 - 2 files changed, 280 deletions(-) delete mode 100644 defaultcfg/fsfwconfig/objects/translateObjects.cpp delete mode 100644 defaultcfg/fsfwconfig/objects/translateObjects.h diff --git a/defaultcfg/fsfwconfig/objects/translateObjects.cpp b/defaultcfg/fsfwconfig/objects/translateObjects.cpp deleted file mode 100644 index 73cd02bf..00000000 --- a/defaultcfg/fsfwconfig/objects/translateObjects.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/** - * @brief Auto-generated object translation file. Contains 86 translations. - * Generated on: 2020-08-25 00:57:14 - **/ -#include "translateObjects.h" - -const char *AT91_UART2_TEST_TASK_STRING = "AT91_UART2_TEST_TASK"; -const char *ARDUINO_0_STRING = "ARDUINO_0"; -const char *ARDUINO_1_STRING = "ARDUINO_1"; -const char *ARDUINO_2_STRING = "ARDUINO_2"; -const char *ARDUINO_3_STRING = "ARDUINO_3"; -const char *ARDUINO_4_STRING = "ARDUINO_4"; -const char *AT91_I2C_TEST_TASK_STRING = "AT91_I2C_TEST_TASK"; -const char *LED_TASK_STRING = "LED_TASK"; -const char *TEST_TASK_STRING = "TEST_TASK"; -const char *PCDU_HANDLER_STRING = "PCDU_HANDLER"; -const char *DUMMY_HANDLER_STRING = "DUMMY_HANDLER"; -const char *SuS_ADC1_DEC2_STRING = "SuS_ADC1_DEC2"; -const char *GPS0_HANDLER_STRING = "GPS0_HANDLER"; -const char *DLR_PVCH_STRING = "DLR_PVCH"; -const char *GYRO1_STRING = "GYRO1"; -const char *DLR_IRAS_STRING = "DLR_IRAS"; -const char *SPI_Test_PT1000_STRING = "SPI_Test_PT1000"; -const char *PT1000_Syrlinks_DEC1_O1_STRING = "PT1000_Syrlinks_DEC1_O1"; -const char *PT1000_Camera_DEC1_O2_STRING = "PT1000_Camera_DEC1_O2"; -const char *PT1000_SuS1_DEC1_O3_STRING = "PT1000_SuS1_DEC1_O3"; -const char *PT1000_SuS2_DEC1_O4_STRING = "PT1000_SuS2_DEC1_O4"; -const char *PT1000_SuS3_DEC1_O5_STRING = "PT1000_SuS3_DEC1_O5"; -const char *PT1000_PVHC_DEC1_O6_STRING = "PT1000_PVHC_DEC1_O6"; -const char *SPI_Test_Gyro_STRING = "SPI_Test_Gyro"; -const char *PT1000_CCSDS1_DEC2_STRING = "PT1000_CCSDS1_DEC2"; -const char *PT1000_MGT1_DEC2_STRING = "PT1000_MGT1_DEC2"; -const char *PT1000_SuS4_DEC2_STRING = "PT1000_SuS4_DEC2"; -const char *PT1000_SuS5_DEC2_STRING = "PT1000_SuS5_DEC2"; -const char *PT1000_SuS6_DEC2_STRING = "PT1000_SuS6_DEC2"; -const char *PT1000_PVCH_DEC2_STRING = "PT1000_PVCH_DEC2"; -const char *PT1000_Iridium_DEC3_STRING = "PT1000_Iridium_DEC3"; -const char *PT1000_CCSDS2_DEC3_STRING = "PT1000_CCSDS2_DEC3"; -const char *PT1000_SuS7_DEC3_STRING = "PT1000_SuS7_DEC3"; -const char *PT1000_SuS8_DEC3_STRING = "PT1000_SuS8_DEC3"; -const char *PT1000_PVCH_DEC3_STRING = "PT1000_PVCH_DEC3"; -const char *GYRO2_STRING = "GYRO2"; -const char *PT1000_PLOC_DEC4_STRING = "PT1000_PLOC_DEC4"; -const char *PT1000_SuS9_DEC4_STRING = "PT1000_SuS9_DEC4"; -const char *PT1000_SuS10_DEC4_STRING = "PT1000_SuS10_DEC4"; -const char *PT1000_PVHC_DEC4_STRING = "PT1000_PVHC_DEC4"; -const char *SuS_ADC_DEC4_STRING = "SuS_ADC_DEC4"; -const char *GPS1_HANDLER_STRING = "GPS1_HANDLER"; -const char *DUMMY_GPS_COM_IF_STRING = "DUMMY_GPS_COM_IF"; -const char *RS232_DEVICE_COM_IF_STRING = "RS232_DEVICE_COM_IF"; -const char *I2C_DEVICE_COM_IF_STRING = "I2C_DEVICE_COM_IF"; -const char *GPIO_DEVICE_COM_IF_STRING = "GPIO_DEVICE_COM_IF"; -const char *SPI_POLLING_TASK_STRING = "SPI_POLLING_TASK"; -const char *SPI_DEVICE_COM_IF_STRING = "SPI_DEVICE_COM_IF"; -const char *DUMMY_ECHO_COM_IF_STRING = "DUMMY_ECHO_COM_IF"; -const char *SD_CARD_HANDLER_STRING = "SD_CARD_HANDLER"; -const char *CCSDS_PACKET_DISTRIBUTOR_STRING = "CCSDS_PACKET_DISTRIBUTOR"; -const char *PUS_PACKET_DISTRIBUTOR_STRING = "PUS_PACKET_DISTRIBUTOR"; -const char *UDP_TMTC_BRIDGE_STRING = "UDP_TMTC_BRIDGE"; -const char *EMAC_POLLING_TASK_STRING = "EMAC_POLLING_TASK"; -const char *SERIAL_TMTC_BRIDGE_STRING = "SERIAL_TMTC_BRIDGE"; -const char *SERIAL_RING_BUFFER_STRING = "SERIAL_RING_BUFFER"; -const char *SERIAL_POLLING_TASK_STRING = "SERIAL_POLLING_TASK"; -const char *PUS_SERVICE_1_STRING = "PUS_SERVICE_1"; -const char *PUS_SERVICE_2_STRING = "PUS_SERVICE_2"; -const char *PUS_SERVICE_3_STRING = "PUS_SERVICE_3"; -const char *PUS_SERVICE_3_PSB_STRING = "PUS_SERVICE_3_PSB"; -const char *PUS_SERVICE_5_STRING = "PUS_SERVICE_5"; -const char *PUS_SERVICE_6_STRING = "PUS_SERVICE_6"; -const char *PUS_SERVICE_8_STRING = "PUS_SERVICE_8"; -const char *PUS_SERVICE_9_STRING = "PUS_SERVICE_9"; -const char *PUS_SERVICE_17_STRING = "PUS_SERVICE_17"; -const char *PUS_SERVICE_20_STRING = "PUS_SERVICE_20"; -const char *PUS_SERVICE_23_STRING = "PUS_SERVICE_23"; -const char *PUS_SERVICE_200_STRING = "PUS_SERVICE_200"; -const char *PUS_SERVICE_201_STRING = "PUS_SERVICE_201"; -const char *PUS_TIME_STRING = "PUS_TIME"; -const char *PUS_FUNNEL_STRING = "PUS_FUNNEL"; -const char *FREERTOS_TASK_MONITOR_STRING = "FREERTOS_TASK_MONITOR"; -const char *HEALTH_TABLE_STRING = "HEALTH_TABLE"; -const char *MODE_STORE_STRING = "MODE_STORE"; -const char *EVENT_MANAGER_STRING = "EVENT_MANAGER"; -const char *INTERNAL_ERROR_REPORTER_STRING = "INTERNAL_ERROR_REPORTER"; -const char *TC_STORE_STRING = "TC_STORE"; -const char *TM_STORE_STRING = "TM_STORE"; -const char *IPC_STORE_STRING = "IPC_STORE"; -const char *AT91_SPI_TEST_TASK_STRING = "AT91_SPI_TEST_TASK"; -const char *STM32_TEST_TASK_STRING = "STM32_TEST_TASK"; -const char *AT91_UART0_TEST_TASK_STRING = "AT91_UART0_TEST_TASK"; -const char *TC_INJECTOR_STRING = "TC_INJECTOR"; -const char *NO_OBJECT_STRING = "NO_OBJECT"; - -const char* translateObject(object_id_t object){ - switch((object&0xFFFFFFFF)){ - case 0x000123336: - return AT91_UART2_TEST_TASK_STRING; - case 0x01010100: - return ARDUINO_0_STRING; - case 0x01010101: - return ARDUINO_1_STRING; - case 0x01010102: - return ARDUINO_2_STRING; - case 0x01010103: - return ARDUINO_3_STRING; - case 0x01010104: - return ARDUINO_4_STRING; - case 0x12345678: - return AT91_I2C_TEST_TASK_STRING; - case 0x12345777: - return LED_TASK_STRING; - case 0x42694269: - return TEST_TASK_STRING; - case 0x44003200: - return PCDU_HANDLER_STRING; - case 0x4400AFFE: - return DUMMY_HANDLER_STRING; - case 0x44020108: - return SuS_ADC1_DEC2_STRING; - case 0x44101F00: - return GPS0_HANDLER_STRING; - case 0x44104000: - return DLR_PVCH_STRING; - case 0x44105000: - return GYRO1_STRING; - case 0x44106000: - return DLR_IRAS_STRING; - case 0x44115400: - return SPI_Test_PT1000_STRING; - case 0x44115401: - return PT1000_Syrlinks_DEC1_O1_STRING; - case 0x44115402: - return PT1000_Camera_DEC1_O2_STRING; - case 0x44115404: - return PT1000_SuS1_DEC1_O3_STRING; - case 0x44115405: - return PT1000_SuS2_DEC1_O4_STRING; - case 0x44115406: - return PT1000_SuS3_DEC1_O5_STRING; - case 0x44115407: - return PT1000_PVHC_DEC1_O6_STRING; - case 0x44115500: - return SPI_Test_Gyro_STRING; - case 0x44125401: - return PT1000_CCSDS1_DEC2_STRING; - case 0x44125403: - return PT1000_MGT1_DEC2_STRING; - case 0x44125404: - return PT1000_SuS4_DEC2_STRING; - case 0x44125405: - return PT1000_SuS5_DEC2_STRING; - case 0x44125406: - return PT1000_SuS6_DEC2_STRING; - case 0x44125407: - return PT1000_PVCH_DEC2_STRING; - case 0x44130301: - return PT1000_Iridium_DEC3_STRING; - case 0x44130302: - return PT1000_CCSDS2_DEC3_STRING; - case 0x44130305: - return PT1000_SuS7_DEC3_STRING; - case 0x44130306: - return PT1000_SuS8_DEC3_STRING; - case 0x44130307: - return PT1000_PVCH_DEC3_STRING; - case 0x44130308: - return GYRO2_STRING; - case 0x44145401: - return PT1000_PLOC_DEC4_STRING; - case 0x44145404: - return PT1000_SuS9_DEC4_STRING; - case 0x44145405: - return PT1000_SuS10_DEC4_STRING; - case 0x44145406: - return PT1000_PVHC_DEC4_STRING; - case 0x44145407: - return SuS_ADC_DEC4_STRING; - case 0x44202000: - return GPS1_HANDLER_STRING; - case 0x49001F00: - return DUMMY_GPS_COM_IF_STRING; - case 0x49005200: - return RS232_DEVICE_COM_IF_STRING; - case 0x49005300: - return I2C_DEVICE_COM_IF_STRING; - case 0x49005400: - return GPIO_DEVICE_COM_IF_STRING; - case 0x49005410: - return SPI_POLLING_TASK_STRING; - case 0x49005600: - return SPI_DEVICE_COM_IF_STRING; - case 0x4900AFFE: - return DUMMY_ECHO_COM_IF_STRING; - case 0x4D0073AD: - return SD_CARD_HANDLER_STRING; - case 0x50000100: - return CCSDS_PACKET_DISTRIBUTOR_STRING; - case 0x50000200: - return PUS_PACKET_DISTRIBUTOR_STRING; - case 0x50000300: - return UDP_TMTC_BRIDGE_STRING; - case 0x50000400: - return EMAC_POLLING_TASK_STRING; - case 0x50000500: - return SERIAL_TMTC_BRIDGE_STRING; - case 0x50000550: - return SERIAL_RING_BUFFER_STRING; - case 0x50000600: - return SERIAL_POLLING_TASK_STRING; - case 0x51000100: - return PUS_SERVICE_1_STRING; - case 0x51000200: - return PUS_SERVICE_2_STRING; - case 0x51000300: - return PUS_SERVICE_3_STRING; - case 0x51000310: - return PUS_SERVICE_3_PSB_STRING; - case 0x51000400: - return PUS_SERVICE_5_STRING; - case 0x51000500: - return PUS_SERVICE_6_STRING; - case 0x51000800: - return PUS_SERVICE_8_STRING; - case 0x51000900: - return PUS_SERVICE_9_STRING; - case 0x51001700: - return PUS_SERVICE_17_STRING; - case 0x51002000: - return PUS_SERVICE_20_STRING; - case 0x51002300: - return PUS_SERVICE_23_STRING; - case 0x51020000: - return PUS_SERVICE_200_STRING; - case 0x51020100: - return PUS_SERVICE_201_STRING; - case 0x52000001: - return PUS_TIME_STRING; - case 0x52000002: - return PUS_FUNNEL_STRING; - case 0x53000000: - return PUS_SERVICE_1_STRING; - case 0x53000003: - return FREERTOS_TASK_MONITOR_STRING; - case 0x53010000: - return HEALTH_TABLE_STRING; - case 0x53010100: - return MODE_STORE_STRING; - case 0x53030000: - return EVENT_MANAGER_STRING; - case 0x53040000: - return INTERNAL_ERROR_REPORTER_STRING; - case 0x534f0100: - return TC_STORE_STRING; - case 0x534f0200: - return TM_STORE_STRING; - case 0x534f0300: - return IPC_STORE_STRING; - case 0x66666666: - return AT91_SPI_TEST_TASK_STRING; - case 0x77777777: - return STM32_TEST_TASK_STRING; - case 0x87654321: - return AT91_UART0_TEST_TASK_STRING; - case 0x99000001: - return TC_INJECTOR_STRING; - case 0xFFFFFFFF: - return NO_OBJECT_STRING; - default: - return "UNKNOWN_OBJECT"; - } - return 0; -} diff --git a/defaultcfg/fsfwconfig/objects/translateObjects.h b/defaultcfg/fsfwconfig/objects/translateObjects.h deleted file mode 100644 index 5b656940..00000000 --- a/defaultcfg/fsfwconfig/objects/translateObjects.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CONFIG_OBJECTS_TRANSLATEOBJECTS_H_ -#define CONFIG_OBJECTS_TRANSLATEOBJECTS_H_ - -#include - -const char* translateObject(object_id_t object); - - -#endif /* CONFIG_OBJECTS_TRANSLATEOBJECTS_H_ */ From 6c22fab2082deb5f0e781694a3265cabb2b3d5f8 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 9 Nov 2020 21:33:09 +0100 Subject: [PATCH 024/119] pus service improvements --- pus/CService200ModeCommanding.cpp | 5 +++-- pus/CService200ModeCommanding.h | 11 +++++------ pus/Service1TelecommandVerification.cpp | 8 +++----- pus/Service1TelecommandVerification.h | 11 ++++++----- pus/Service2DeviceAccess.cpp | 21 +++++++++------------ pus/Service2DeviceAccess.h | 18 +++++++++++------- pus/Service5EventReporting.cpp | 5 +++-- pus/Service5EventReporting.h | 9 +++++---- pus/Service8FunctionManagement.cpp | 8 ++++---- pus/Service8FunctionManagement.h | 12 +++++++----- pus/servicepackets/Service1Packets.h | 6 +++--- pus/servicepackets/Service200Packets.h | 6 +++--- pus/servicepackets/Service2Packets.h | 6 +++--- pus/servicepackets/Service5Packets.h | 6 +++--- pus/servicepackets/Service8Packets.h | 6 +++--- 15 files changed, 71 insertions(+), 67 deletions(-) diff --git a/pus/CService200ModeCommanding.cpp b/pus/CService200ModeCommanding.cpp index c63b47a6..c4e99359 100644 --- a/pus/CService200ModeCommanding.cpp +++ b/pus/CService200ModeCommanding.cpp @@ -7,9 +7,10 @@ #include "../modes/ModeMessage.h" CService200ModeCommanding::CService200ModeCommanding(object_id_t objectId, - uint16_t apid, uint8_t serviceId): + uint16_t apid, uint8_t serviceId, uint8_t numParallelCommands, + uint16_t commandTimeoutSeconds): CommandingServiceBase(objectId, apid, serviceId, - NUMBER_OF_PARALLEL_COMMANDS,COMMAND_TIMEOUT_SECONDS) {} + numParallelCommands, commandTimeoutSeconds) {} CService200ModeCommanding::~CService200ModeCommanding() {} diff --git a/pus/CService200ModeCommanding.h b/pus/CService200ModeCommanding.h index 89347dbd..84040212 100644 --- a/pus/CService200ModeCommanding.h +++ b/pus/CService200ModeCommanding.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_PUS_CSERVICE200MODECOMMANDING_H_ -#define FRAMEWORK_PUS_CSERVICE200MODECOMMANDING_H_ +#ifndef FSFW_PUS_CSERVICE200MODECOMMANDING_H_ +#define FSFW_PUS_CSERVICE200MODECOMMANDING_H_ #include "../tmtcservices/CommandingServiceBase.h" @@ -15,11 +15,10 @@ */ class CService200ModeCommanding: public CommandingServiceBase { public: - static constexpr uint8_t NUMBER_OF_PARALLEL_COMMANDS = 4; - static constexpr uint16_t COMMAND_TIMEOUT_SECONDS = 60; CService200ModeCommanding(object_id_t objectId, - uint16_t apid, uint8_t serviceId); + uint16_t apid, uint8_t serviceId, uint8_t numParallelCommands = 4, + uint16_t commandTimeoutSeconds = 60); virtual~ CService200ModeCommanding(); protected: @@ -82,4 +81,4 @@ private: }; }; -#endif /* FRAMEWORK_PUS_CSERVICE200MODECOMMANDING_H_ */ +#endif /* FSFW_PUS_CSERVICE200MODECOMMANDING_H_ */ diff --git a/pus/Service1TelecommandVerification.cpp b/pus/Service1TelecommandVerification.cpp index 578eb02d..86b0dcde 100644 --- a/pus/Service1TelecommandVerification.cpp +++ b/pus/Service1TelecommandVerification.cpp @@ -6,15 +6,13 @@ #include "../tmtcpacket/pus/TmPacketStored.h" #include "../serviceinterface/ServiceInterfaceStream.h" #include "../tmtcservices/AcceptsTelemetryIF.h" -#include "../serviceinterface/ServiceInterfaceStream.h" - Service1TelecommandVerification::Service1TelecommandVerification( object_id_t objectId, uint16_t apid, uint8_t serviceId, - object_id_t targetDestination): + object_id_t targetDestination, uint16_t messageQueueDepth): SystemObject(objectId), apid(apid), serviceId(serviceId), targetDestination(targetDestination) { - tmQueue = QueueFactory::instance()->createMessageQueue(); + tmQueue = QueueFactory::instance()->createMessageQueue(messageQueueDepth); } Service1TelecommandVerification::~Service1TelecommandVerification() {} @@ -53,7 +51,7 @@ ReturnValue_t Service1TelecommandVerification::sendVerificationReport( result = generateSuccessReport(message); } if(result != HasReturnvaluesIF::RETURN_OK){ - sif::error << "Service1TelecommandVerification::initialize: " + sif::error << "Service1TelecommandVerification::sendVerificationReport: " "Sending verification packet failed !" << std::endl; } return result; diff --git a/pus/Service1TelecommandVerification.h b/pus/Service1TelecommandVerification.h index 37562d1c..3d68a4e0 100644 --- a/pus/Service1TelecommandVerification.h +++ b/pus/Service1TelecommandVerification.h @@ -1,5 +1,5 @@ -#ifndef MISSION_PUS_SERVICE1TELECOMMANDVERIFICATION_H_ -#define MISSION_PUS_SERVICE1TELECOMMANDVERIFICATION_H_ +#ifndef FSFW_PUS_SERVICE1TELECOMMANDVERIFICATION_H_ +#define FSFW_PUS_SERVICE1TELECOMMANDVERIFICATION_H_ #include "../objectmanager/SystemObject.h" #include "../returnvalues/HasReturnvaluesIF.h" @@ -44,14 +44,15 @@ public: static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PUS_SERVICE_1; Service1TelecommandVerification(object_id_t objectId, - uint16_t apid, uint8_t serviceId, object_id_t targetDestination); + uint16_t apid, uint8_t serviceId, object_id_t targetDestination, + uint16_t messageQueueDepth); virtual ~Service1TelecommandVerification(); /** * * @return ID of Verification Queue */ - virtual MessageQueueId_t getVerificationQueue(); + virtual MessageQueueId_t getVerificationQueue() override; /** * Performs the service periodically as specified in init_mission(). @@ -91,4 +92,4 @@ private: }; }; -#endif /* MISSION_PUS_SERVICE1TELECOMMANDVERIFICATION_H_ */ +#endif /* FSFW_PUS_SERVICE1TELECOMMANDVERIFICATION_H_ */ diff --git a/pus/Service2DeviceAccess.cpp b/pus/Service2DeviceAccess.cpp index 2093a617..3648b7eb 100644 --- a/pus/Service2DeviceAccess.cpp +++ b/pus/Service2DeviceAccess.cpp @@ -21,8 +21,8 @@ Service2DeviceAccess::~Service2DeviceAccess() {} ReturnValue_t Service2DeviceAccess::isValidSubservice(uint8_t subservice) { switch(static_cast(subservice)){ - case Subservice::RAW_COMMANDING: - case Subservice::TOGGLE_WIRETAPPING: + case Subservice::COMMAND_RAW_COMMANDING: + case Subservice::COMMAND_TOGGLE_WIRETAPPING: return HasReturnvaluesIF::RETURN_OK; default: sif::error << "Invalid Subservice" << std::endl; @@ -39,8 +39,7 @@ ReturnValue_t Service2DeviceAccess::getMessageQueueAndObject( SerializeAdapter::deSerialize(objectId, &tcData, &tcDataLen, SerializeIF::Endianness::BIG); - ReturnValue_t result = checkInterfaceAndAcquireMessageQueue(id,objectId); - return result; + return checkInterfaceAndAcquireMessageQueue(id,objectId); } ReturnValue_t Service2DeviceAccess::checkInterfaceAndAcquireMessageQueue( @@ -59,14 +58,12 @@ ReturnValue_t Service2DeviceAccess::prepareCommand(CommandMessage* message, uint8_t subservice, const uint8_t* tcData, size_t tcDataLen, uint32_t* state, object_id_t objectId) { switch(static_cast(subservice)){ - case Subservice::RAW_COMMANDING: { - return prepareRawCommand(dynamic_cast(message), - tcData, tcDataLen); + case Subservice::COMMAND_RAW_COMMANDING: { + return prepareRawCommand(message, tcData, tcDataLen); } break; - case Subservice::TOGGLE_WIRETAPPING: { - return prepareWiretappingCommand(dynamic_cast(message), - tcData, tcDataLen); + case Subservice::COMMAND_TOGGLE_WIRETAPPING: { + return prepareWiretappingCommand(message, tcData, tcDataLen); } break; default: @@ -121,11 +118,11 @@ void Service2DeviceAccess::handleUnrequestedReply(CommandMessage* reply) { switch(reply->getCommand()) { case DeviceHandlerMessage::REPLY_RAW_COMMAND: sendWiretappingTm(reply, - static_cast(Subservice::WIRETAPPING_RAW_TC)); + static_cast(Subservice::REPLY_WIRETAPPING_RAW_TC)); break; case DeviceHandlerMessage::REPLY_RAW_REPLY: sendWiretappingTm(reply, - static_cast(Subservice::RAW_REPLY)); + static_cast(Subservice::REPLY_RAW)); break; default: sif::error << "Unknown message in Service2DeviceAccess::" diff --git a/pus/Service2DeviceAccess.h b/pus/Service2DeviceAccess.h index f6aa8b52..b62e6854 100644 --- a/pus/Service2DeviceAccess.h +++ b/pus/Service2DeviceAccess.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_PUS_SERVICE2DEVICEACCESS_H_ -#define FRAMEWORK_PUS_SERVICE2DEVICEACCESS_H_ +#ifndef FSFW_PUS_SERVICE2DEVICEACCESS_H_ +#define FSFW_PUS_SERVICE2DEVICEACCESS_H_ #include "../objectmanager/SystemObjectIF.h" #include "../devicehandlers/AcceptsDeviceResponsesIF.h" @@ -81,12 +81,16 @@ private: const uint8_t* tcData, size_t tcDataLen); enum class Subservice { - RAW_COMMANDING = 128, //!< [EXPORT] : [COMMAND] Command in device native protocol - TOGGLE_WIRETAPPING = 129, //!< [EXPORT] : [COMMAND] Toggle wiretapping of raw communication - RAW_REPLY = 130, //!< [EXPORT] : [REPLY] Includes wiretapping TM and normal TM raw replies from device - WIRETAPPING_RAW_TC = 131 //!< [EXPORT] : [REPLY] Wiretapping packets of commands built by device handler + //!< [EXPORT] : [COMMAND] Command in device native protocol + COMMAND_RAW_COMMANDING = 128, + //!< [EXPORT] : [COMMAND] Toggle wiretapping of raw communication + COMMAND_TOGGLE_WIRETAPPING = 129, + //!< [EXPORT] : [REPLY] Includes wiretapping TM and normal TM raw replies from device + REPLY_RAW = 130, + //!< [EXPORT] : [REPLY] Wiretapping packets of commands built by device handler + REPLY_WIRETAPPING_RAW_TC = 131 }; }; -#endif /* MISSION_PUS_DEVICE2DEVICECOMMANDING_H_ */ +#endif /* FSFW_PUS_DEVICE2DEVICECOMMANDING_H_ */ diff --git a/pus/Service5EventReporting.cpp b/pus/Service5EventReporting.cpp index 829d04bd..e0b34a5b 100644 --- a/pus/Service5EventReporting.cpp +++ b/pus/Service5EventReporting.cpp @@ -8,10 +8,11 @@ Service5EventReporting::Service5EventReporting(object_id_t objectId, - uint16_t apid, uint8_t serviceId, size_t maxNumberReportsPerCycle): + uint16_t apid, uint8_t serviceId, size_t maxNumberReportsPerCycle, + uint32_t messageQueueDepth): PusServiceBase(objectId, apid, serviceId), maxNumberReportsPerCycle(maxNumberReportsPerCycle) { - eventQueue = QueueFactory::instance()->createMessageQueue(); + eventQueue = QueueFactory::instance()->createMessageQueue(messageQueueDepth); } Service5EventReporting::~Service5EventReporting(){} diff --git a/pus/Service5EventReporting.h b/pus/Service5EventReporting.h index 0b6ee9a8..69242801 100644 --- a/pus/Service5EventReporting.h +++ b/pus/Service5EventReporting.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_PUS_SERVICE5EVENTREPORTING_H_ -#define FRAMEWORK_PUS_SERVICE5EVENTREPORTING_H_ +#ifndef FSFW_PUS_SERVICE5EVENTREPORTING_H_ +#define FSFW_PUS_SERVICE5EVENTREPORTING_H_ #include "../tmtcservices/PusServiceBase.h" #include "../events/EventMessage.h" @@ -42,7 +42,8 @@ class Service5EventReporting: public PusServiceBase { public: Service5EventReporting(object_id_t objectId, uint16_t apid, - uint8_t serviceId, size_t maxNumberReportsPerCycle = 10); + uint8_t serviceId, size_t maxNumberReportsPerCycle = 10, + uint32_t messageQueueDepth = 10); virtual ~Service5EventReporting(); /*** @@ -83,4 +84,4 @@ private: ReturnValue_t generateEventReport(EventMessage message); }; -#endif /* MISSION_PUS_SERVICE5EVENTREPORTING_H_ */ +#endif /* FSFW_PUS_SERVICE5EVENTREPORTING_H_ */ diff --git a/pus/Service8FunctionManagement.cpp b/pus/Service8FunctionManagement.cpp index 2c2e590b..d710c56e 100644 --- a/pus/Service8FunctionManagement.cpp +++ b/pus/Service8FunctionManagement.cpp @@ -7,10 +7,10 @@ #include "../serialize/SerializeAdapter.h" #include "../serviceinterface/ServiceInterfaceStream.h" -Service8FunctionManagement::Service8FunctionManagement(object_id_t object_id, +Service8FunctionManagement::Service8FunctionManagement(object_id_t objectId, uint16_t apid, uint8_t serviceId, uint8_t numParallelCommands, uint16_t commandTimeoutSeconds): - CommandingServiceBase(object_id, apid, serviceId, numParallelCommands, + CommandingServiceBase(objectId, apid, serviceId, numParallelCommands, commandTimeoutSeconds) {} Service8FunctionManagement::~Service8FunctionManagement() {} @@ -19,7 +19,7 @@ Service8FunctionManagement::~Service8FunctionManagement() {} ReturnValue_t Service8FunctionManagement::isValidSubservice( uint8_t subservice) { switch(static_cast(subservice)) { - case Subservice::DIRECT_COMMANDING: + case Subservice::COMMAND_DIRECT_COMMANDING: return HasReturnvaluesIF::RETURN_OK; default: return AcceptsTelecommandsIF::INVALID_SUBSERVICE; @@ -131,7 +131,7 @@ ReturnValue_t Service8FunctionManagement::handleDataReply( } DataReply dataReply(objectId, actionId, buffer, size); result = sendTmPacket(static_cast( - Subservice::DIRECT_COMMANDING_DATA_REPLY), &dataReply); + Subservice::REPLY_DIRECT_COMMANDING_DATA), &dataReply); auto deletionResult = IPCStore->deleteData(storeId); if(deletionResult != HasReturnvaluesIF::RETURN_OK) { diff --git a/pus/Service8FunctionManagement.h b/pus/Service8FunctionManagement.h index b5ebcda8..00153cfb 100644 --- a/pus/Service8FunctionManagement.h +++ b/pus/Service8FunctionManagement.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_PUS_SERVICE8FUNCTIONMANAGEMENT_H_ -#define FRAMEWORK_PUS_SERVICE8FUNCTIONMANAGEMENT_H_ +#ifndef FSFW_PUS_SERVICE8FUNCTIONMANAGEMENT_H_ +#define FSFW_PUS_SERVICE8FUNCTIONMANAGEMENT_H_ #include "../action/ActionMessage.h" #include "../tmtcservices/CommandingServiceBase.h" @@ -52,8 +52,10 @@ protected: private: enum class Subservice { - DIRECT_COMMANDING = 128, //!< [EXPORT] : [COMMAND] Functional commanding - DIRECT_COMMANDING_DATA_REPLY = 130, //!< [EXPORT] : [REPLY] Data reply + //!< [EXPORT] : [COMMAND] Functional commanding + COMMAND_DIRECT_COMMANDING = 128, + //!< [EXPORT] : [REPLY] Data reply + REPLY_DIRECT_COMMANDING_DATA = 130, }; ReturnValue_t checkInterfaceAndAcquireMessageQueue( @@ -64,4 +66,4 @@ private: object_id_t objectId, ActionId_t actionId); }; -#endif /* FRAMEWORK_PUS_SERVICE8FUNCTIONMANAGEMENT_H_ */ +#endif /* FSFW_PUS_SERVICE8FUNCTIONMANAGEMENT_H_ */ diff --git a/pus/servicepackets/Service1Packets.h b/pus/servicepackets/Service1Packets.h index dbd31028..ecb62693 100644 --- a/pus/servicepackets/Service1Packets.h +++ b/pus/servicepackets/Service1Packets.h @@ -1,3 +1,6 @@ +#ifndef FSFW_PUS_SERVICEPACKETS_SERVICE1PACKETS_H_ +#define FSFW_PUS_SERVICEPACKETS_SERVICE1PACKETS_H_ + /** * @defgroup spacepackets PUS Packet Definitions * This group contains all implemented TM or TM packages that are sent to @@ -5,9 +8,6 @@ * packet structures in Mission Information Base (MIB). */ -#ifndef MISSION_PUS_SERVICEPACKETS_SERVICE1PACKETS_H_ -#define MISSION_PUS_SERVICEPACKETS_SERVICE1PACKETS_H_ - #include "../../serialize/SerializeAdapter.h" #include "../../tmtcservices/VerificationCodes.h" diff --git a/pus/servicepackets/Service200Packets.h b/pus/servicepackets/Service200Packets.h index efcf65fc..cb9ad4a7 100644 --- a/pus/servicepackets/Service200Packets.h +++ b/pus/servicepackets/Service200Packets.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_PUS_SERVICEPACKETS_SERVICE200PACKETS_H_ -#define FRAMEWORK_PUS_SERVICEPACKETS_SERVICE200PACKETS_H_ +#ifndef FSFW_PUS_SERVICEPACKETS_SERVICE200PACKETS_H_ +#define FSFW_PUS_SERVICEPACKETS_SERVICE200PACKETS_H_ #include "../../serialize/SerialLinkedListAdapter.h" #include "../../modes/ModeMessage.h" @@ -60,4 +60,4 @@ public: SerializeElement reason; //!< [EXPORT] : [COMMENT] Reason the mode could not be reached }; -#endif /* FRAMEWORK_PUS_SERVICEPACKETS_SERVICE200PACKETS_H_ */ +#endif /* FSFW_PUS_SERVICEPACKETS_SERVICE200PACKETS_H_ */ diff --git a/pus/servicepackets/Service2Packets.h b/pus/servicepackets/Service2Packets.h index d4f3fb17..285a8f9f 100644 --- a/pus/servicepackets/Service2Packets.h +++ b/pus/servicepackets/Service2Packets.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_PUS_SERVICEPACKETS_SERVICE2PACKETS_H_ -#define FRAMEWORK_PUS_SERVICEPACKETS_SERVICE2PACKETS_H_ +#ifndef FSFW_PUS_SERVICEPACKETS_SERVICE2PACKETS_H_ +#define FSFW_PUS_SERVICEPACKETS_SERVICE2PACKETS_H_ #include "../../action/ActionMessage.h" #include "../../objectmanager/SystemObjectIF.h" @@ -73,4 +73,4 @@ public: } }; -#endif /* FRAMEWORK_PUS_SERVICEPACKETS_SERVICE2PACKETS_H_ */ +#endif /* FSFW_PUS_SERVICEPACKETS_SERVICE2PACKETS_H_ */ diff --git a/pus/servicepackets/Service5Packets.h b/pus/servicepackets/Service5Packets.h index 9655608a..f903d6a2 100644 --- a/pus/servicepackets/Service5Packets.h +++ b/pus/servicepackets/Service5Packets.h @@ -1,5 +1,5 @@ -#ifndef MISSION_PUS_SERVICEPACKETS_SERVICE5PACKETS_H_ -#define MISSION_PUS_SERVICEPACKETS_SERVICE5PACKETS_H_ +#ifndef FSFW_PUS_SERVICEPACKETS_SERVICE5PACKETS_H_ +#define FSFW_PUS_SERVICEPACKETS_SERVICE5PACKETS_H_ #include "../../serialize/SerializeAdapter.h" #include "../../tmtcservices/VerificationCodes.h" @@ -73,4 +73,4 @@ private: }; -#endif /* MISSION_PUS_SERVICEPACKETS_SERVICE5PACKETS_H_ */ +#endif /* FSFW_PUS_SERVICEPACKETS_SERVICE5PACKETS_H_ */ diff --git a/pus/servicepackets/Service8Packets.h b/pus/servicepackets/Service8Packets.h index 14f8b6e6..b026edf5 100644 --- a/pus/servicepackets/Service8Packets.h +++ b/pus/servicepackets/Service8Packets.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_PUS_SERVICEPACKETS_SERVICE8PACKETS_H_ -#define FRAMEWORK_PUS_SERVICEPACKETS_SERVICE8PACKETS_H_ +#ifndef FSFW_PUS_SERVICEPACKETS_SERVICE8PACKETS_H_ +#define FSFW_PUS_SERVICEPACKETS_SERVICE8PACKETS_H_ #include "../../action/ActionMessage.h" #include "../../objectmanager/SystemObjectIF.h" @@ -118,4 +118,4 @@ private: }; -#endif /* FRAMEWORK_PUS_SERVICEPACKETS_SERVICE8PACKETS_H_ */ +#endif /* FSFW_PUS_SERVICEPACKETS_SERVICE8PACKETS_H_ */ From 0304f61bdeb3035137bea029b145bacd34b5dd75 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 10 Nov 2020 11:00:13 +0100 Subject: [PATCH 025/119] has parametersIF doc improved --- parameters/HasParametersIF.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/parameters/HasParametersIF.h b/parameters/HasParametersIF.h index c4fd81b3..19c12d81 100644 --- a/parameters/HasParametersIF.h +++ b/parameters/HasParametersIF.h @@ -1,8 +1,8 @@ #ifndef FSFW_PARAMETERS_HASPARAMETERSIF_H_ #define FSFW_PARAMETERS_HASPARAMETERSIF_H_ -#include "../returnvalues/HasReturnvaluesIF.h" #include "ParameterWrapper.h" +#include "../returnvalues/HasReturnvaluesIF.h" #include /** @@ -20,11 +20,10 @@ using ParameterId_t = uint32_t; * ID is the domain ID which can be used to identify unqiue spacecraft domains * (e.g. control and sensor domain in the AOCS controller). * - * The second and third byte represent the matrix ID, which can represent - * a 8-bit row and column number and the last byte... + * The second byte is a unique identfier ID. * - * Yeah, is it matrix ID or parameter ID now and is index a 16 bit number - * of a 8 bit number now? + * The third and fourth byte can be used as a linear index for matrix or array + * parameter entries. */ class HasParametersIF { public: @@ -61,6 +60,10 @@ public: virtual ~HasParametersIF() {} /** + * This is the generic function overriden by child classes to set + * parameters. To set a parameter, the parameter wrapper is used with + * a variety of set functions. The provided values can be checked with + * newValues. * Always set parameter before checking newValues! * * @param domainId From 7c75c6e8cc22a4b8540f8866a5425f101bcfea6e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 10 Nov 2020 11:14:07 +0100 Subject: [PATCH 026/119] parameter helper update --- parameters/ParameterHelper.cpp | 166 ++++++++++++++++----------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/parameters/ParameterHelper.cpp b/parameters/ParameterHelper.cpp index d2193b28..22465ca3 100644 --- a/parameters/ParameterHelper.cpp +++ b/parameters/ParameterHelper.cpp @@ -3,7 +3,7 @@ #include "../objectmanager/ObjectManagerIF.h" ParameterHelper::ParameterHelper(ReceivesParameterMessagesIF* owner) : - owner(owner) {} +owner(owner) {} ParameterHelper::~ParameterHelper() { } @@ -14,106 +14,106 @@ ReturnValue_t ParameterHelper::handleParameterMessage(CommandMessage *message) { return HasReturnvaluesIF::RETURN_FAILED; } - ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; - switch (message->getCommand()) { - case ParameterMessage::CMD_PARAMETER_DUMP: { - ParameterWrapper description; - uint8_t domain = HasParametersIF::getDomain( - ParameterMessage::getParameterId(message)); - uint8_t uniqueIdentifier = HasParametersIF::getUniqueIdentifierId( - ParameterMessage::getParameterId(message)); - result = owner->getParameter(domain, uniqueIdentifier, - &description, &description, 0); - if (result == HasReturnvaluesIF::RETURN_OK) { - result = sendParameter(message->getSender(), - ParameterMessage::getParameterId(message), &description); - } - } - break; - case ParameterMessage::CMD_PARAMETER_LOAD: { - ParameterId_t parameterId = 0; - uint8_t ptc = 0; - uint8_t pfc = 0; - uint8_t rows = 0; - uint8_t columns = 0; - store_address_t storeId = ParameterMessage::getParameterLoadCommand( - message, ¶meterId, &ptc, &pfc, &rows, &columns); - Type type(Type::getActualType(ptc, pfc)); + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + switch (message->getCommand()) { + case ParameterMessage::CMD_PARAMETER_DUMP: { + ParameterWrapper description; + uint8_t domain = HasParametersIF::getDomain( + ParameterMessage::getParameterId(message)); + uint8_t uniqueIdentifier = HasParametersIF::getUniqueIdentifierId( + ParameterMessage::getParameterId(message)); + result = owner->getParameter(domain, uniqueIdentifier, + &description, &description, 0); + if (result == HasReturnvaluesIF::RETURN_OK) { + result = sendParameter(message->getSender(), + ParameterMessage::getParameterId(message), &description); + } + } + break; + case ParameterMessage::CMD_PARAMETER_LOAD: { + ParameterId_t parameterId = 0; + uint8_t ptc = 0; + uint8_t pfc = 0; + uint8_t rows = 0; + uint8_t columns = 0; + store_address_t storeId = ParameterMessage::getParameterLoadCommand( + message, ¶meterId, &ptc, &pfc, &rows, &columns); + Type type(Type::getActualType(ptc, pfc)); - uint8_t domain = HasParametersIF::getDomain(parameterId); - uint8_t uniqueIdentifier = HasParametersIF::getUniqueIdentifierId( - parameterId); - uint16_t linearIndex = HasParametersIF::getIndex(parameterId); + uint8_t domain = HasParametersIF::getDomain(parameterId); + uint8_t uniqueIdentifier = HasParametersIF::getUniqueIdentifierId( + parameterId); + uint16_t linearIndex = HasParametersIF::getIndex(parameterId); - ConstStorageAccessor accessor(storeId); - result = storage->getData(storeId, accessor); - if (result != HasReturnvaluesIF::RETURN_OK) { - sif::error << "ParameterHelper::handleParameterMessage: Getting" - << " store data failed for load command." << std::endl; - break; - } + ConstStorageAccessor accessor(storeId); + result = storage->getData(storeId, accessor); + if (result != HasReturnvaluesIF::RETURN_OK) { + sif::error << "ParameterHelper::handleParameterMessage: Getting" + << " store data failed for load command." << std::endl; + break; + } - ParameterWrapper streamWrapper; - result = streamWrapper.set(type, rows, columns, accessor.data(), - accessor.size()); - if(result != HasReturnvaluesIF::RETURN_OK) { - return result; - } + ParameterWrapper streamWrapper; + result = streamWrapper.set(type, rows, columns, accessor.data(), + accessor.size()); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } - ParameterWrapper ownerWrapper; - result = owner->getParameter(domain, uniqueIdentifier, &ownerWrapper, - &streamWrapper, linearIndex); + ParameterWrapper ownerWrapper; + result = owner->getParameter(domain, uniqueIdentifier, &ownerWrapper, + &streamWrapper, linearIndex); - result = ownerWrapper.copyFrom(&streamWrapper, linearIndex); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } + result = ownerWrapper.copyFrom(&streamWrapper, linearIndex); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } result = sendParameter(message->getSender(), ParameterMessage::getParameterId(message), &ownerWrapper); - break; - } - default: - return HasReturnvaluesIF::RETURN_FAILED; - } + break; + } + default: + return HasReturnvaluesIF::RETURN_FAILED; + } - if (result != HasReturnvaluesIF::RETURN_OK) { - rejectCommand(message->getSender(), result, message->getCommand()); - } + if (result != HasReturnvaluesIF::RETURN_OK) { + rejectCommand(message->getSender(), result, message->getCommand()); + } - return HasReturnvaluesIF::RETURN_OK; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t ParameterHelper::sendParameter(MessageQueueId_t to, uint32_t id, - const ParameterWrapper* description) { - size_t serializedSize = description->getSerializedSize(); + const ParameterWrapper* description) { + size_t serializedSize = description->getSerializedSize(); - uint8_t *storeElement; - store_address_t address; + uint8_t *storeElement; + store_address_t address; - ReturnValue_t result = storage->getFreeElement(&address, serializedSize, - &storeElement); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } + ReturnValue_t result = storage->getFreeElement(&address, serializedSize, + &storeElement); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } - size_t storeElementSize = 0; + size_t storeElementSize = 0; - result = description->serialize(&storeElement, &storeElementSize, - serializedSize, SerializeIF::Endianness::BIG); + result = description->serialize(&storeElement, &storeElementSize, + serializedSize, SerializeIF::Endianness::BIG); - if (result != HasReturnvaluesIF::RETURN_OK) { - storage->deleteData(address); - return result; - } + if (result != HasReturnvaluesIF::RETURN_OK) { + storage->deleteData(address); + return result; + } - CommandMessage reply; + CommandMessage reply; - ParameterMessage::setParameterDumpReply(&reply, id, address); + ParameterMessage::setParameterDumpReply(&reply, id, address); - MessageQueueSenderIF::sendMessage(to, &reply, ownerQueueId); + MessageQueueSenderIF::sendMessage(to, &reply, ownerQueueId); - return HasReturnvaluesIF::RETURN_OK; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t ParameterHelper::initialize() { @@ -127,8 +127,8 @@ ReturnValue_t ParameterHelper::initialize() { } void ParameterHelper::rejectCommand(MessageQueueId_t to, ReturnValue_t reason, - Command_t initialCommand) { - CommandMessage reply; - reply.setReplyRejected(reason, initialCommand); - MessageQueueSenderIF::sendMessage(to, &reply, ownerQueueId); + Command_t initialCommand) { + CommandMessage reply; + reply.setReplyRejected(reason, initialCommand); + MessageQueueSenderIF::sendMessage(to, &reply, ownerQueueId); } From 5865e56c54209ffe3076fda86cf94e02a706df25 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 10 Nov 2020 11:15:42 +0100 Subject: [PATCH 027/119] indentation fixed --- parameters/ParameterHelper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parameters/ParameterHelper.cpp b/parameters/ParameterHelper.cpp index 22465ca3..23d1a1f3 100644 --- a/parameters/ParameterHelper.cpp +++ b/parameters/ParameterHelper.cpp @@ -2,8 +2,8 @@ #include "ParameterMessage.h" #include "../objectmanager/ObjectManagerIF.h" -ParameterHelper::ParameterHelper(ReceivesParameterMessagesIF* owner) : -owner(owner) {} +ParameterHelper::ParameterHelper(ReceivesParameterMessagesIF* owner): + owner(owner) {} ParameterHelper::~ParameterHelper() { } From 520409822e58c07c77bbdc163b232e4ac1c6ed76 Mon Sep 17 00:00:00 2001 From: "Jakob.Meier" Date: Tue, 10 Nov 2020 15:06:51 +0100 Subject: [PATCH 028/119] now all debug output removed --- osal/linux/TcUnixUdpPollingTask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osal/linux/TcUnixUdpPollingTask.cpp b/osal/linux/TcUnixUdpPollingTask.cpp index 7df56a95..af99ec91 100644 --- a/osal/linux/TcUnixUdpPollingTask.cpp +++ b/osal/linux/TcUnixUdpPollingTask.cpp @@ -45,8 +45,8 @@ ReturnValue_t TcUnixUdpPollingTask::performOperation(uint8_t opCode) { continue; } - sif::debug << "TcSocketPollingTask::performOperation: " << bytesReceived - << " bytes received" << std::endl; +// sif::debug << "TcSocketPollingTask::performOperation: " << bytesReceived +// << " bytes received" << std::endl; ReturnValue_t result = handleSuccessfullTcRead(bytesReceived); if(result != HasReturnvaluesIF::RETURN_FAILED) { From 752601e85f6d3d182c0f7b35e784e3226aa18db4 Mon Sep 17 00:00:00 2001 From: Steffen Gaisser Date: Fri, 13 Nov 2020 14:31:30 +0100 Subject: [PATCH 029/119] WIP readme --- README.md | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fc86fca7..c75af4ee 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,152 @@ Flight Software Framework (FSFW) ====== -I want to be written! +The Flight Software Framework is a C++ Object Oriented Framework for unmanned, +automated systems like Satellites. + +The initial version of the Flight Software Framework was developed during +the Flying Laptop Project by the University of Stuttgart in cooperation +with Airbus Defence and Space GmbH. + +## Intended Use + +The framework is designed for systems which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. Therefore, a mode and health system provides control over the states of the software and the controlled devices. In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. + + +## Structure + +The general structure is driven by the usage of interfaces provided by objects. The FSFW uses C++11 as baseline. The intention behind this is that this C++ Standard should be widely available, even with older compilers. +The FSFW uses dynamic allocation during the initialization but provides static containers during runtime. +This simplifies the instantiation of objects and allows the usage of some standard containers. +Dynamic Allocation after initialization is discouraged and different solutions are provided in the FSFW to achieve that. +The fsfw uses Run-time type information. +Exceptions are not allowed. + +### Failure Handling + +Functions should return a defined ReturnValue_t to signal to the caller that something is gone wrong. +Returnvalues must be unique. For this the function HasReturnvaluesIF::makeReturnCode or the Macro MAKE_RETURN can be used. +The CLASS_ID is a unique id for that type of object. See returnvalues/FwClassIds. + +### OSAL +The FSFW provides operation system abstraction layers for Linux, FreeRTOS and RTEMS. A independent OSAL called "host" is currently not finished. This aims to be running on windows as well. +The OSAL provides periodic tasks, message queues, clocks and Semaphores as well as Mutexes. + +### Core Components + +Clock: + * This is a class of static functions that can be used at anytime + * Leap Seconds must be set if any time conversions from UTC to other times is used + +ObjectManager (must be created): + +* The component which handles all references. All SystemObjects register at this component. +* Any SystemObject needs to have a unique ObjectId. Those can be managed like objects::framework_objects. +* A reference to an object can be get by calling the following function. T must be the specific Interface you want to call. +A nullptr check of the returning Pointer must be done. This function is based on Run-time type information. + +``` c++ + template T* ObjectManagerIF::get( object_id_t id ) + +``` +* A typical way to create all objects on startup is a handing a static produce function to the ObjectManager on creation. +By calling objectManager->initialize() the produce function will be called and all SystemObjects will be initialized afterwards. + +Event Manager: + +* Component which allows routing of events +* Other objects can subscribe to specific events, ranges of events or all events of an object. +* Subscriptions can be done during runtime but should be done during initialization +* Amounts of allowed subscriptions must be configured by setting this parameters: + +``` c++ +namespace fsfwconfig { +//! Configure the allocated pool sizes for the event manager. +static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240; +static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120; +static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120; +} +``` + + +Health Table: + +* A component which holds every health state +* Provides a thread safe way to access all health states without the need of message exchanges + +Stores + +* The message based communication can only exchange a few bytes of information inside the message itself. Therefore, additional information can be exchanged with Stores. With this, only the store address must be exchanged in the message. +* Internally, the FSFW uses an IPC Store to exchange data between processes. For incoming TCs a TC Store is used. For outgoing TM a TM store is used. +* All of them should use the Thread Safe Class storagemanager/PoolManager + +Tasks + +* There are two different types of tasks +** The PeriodicTask just executes objects that are of type ExecutableObjectIF in the order of the insertion to the Tasks. +** FixedTimeslotTask executes a list of calls in the order of the given list. This is intended for DeviceHandlers, where polling should be in a defined order. An example can be found in defaultcfg/fsfwconfig/pollingSequence + + +### Static Ids in the framework + +Some parts of the framework use a static routing address for communication. +An example setup of ids can be found in the example config in "defaultcft/fsfwconfig/objects/Factory::setStaticFrameworkObjectIds()". + +### Events + +Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT. This works analog to the returnvalues. +Every object that needs own EventIds has to get a unique SUBSYSTEM_ID. +Every SystemObject can call triggerEvent from the parent class. +Therefore, event messages contain the specific EventId and the objectId of the object that has triggered. + +### Internal Communication + +Components communicate mostly over Message through Queues. +Those queues are created by calling the singleton QueueFactory::instance()->create(). + +### External Communication + +The external communication with the mission control system is mostly up to the user implementation. +The FSFW provides PUS Services which can be used to but don't need to be used. +The services can be seen as a conversion from a TC to a message based communication and back. + +#### CCSDS Frames, CCSDS Space Packets and PUS + +If the communication is based on CCSDS Frames and Space Packets, several classes can be used to distributed the packets to the corresponding services. Those can be found in tcdistribution. +If Space Packets are used, a timestamper must be created. +An example can be found in the timemanager folder, this uses CCSDSTime::CDS_short. + +#### DeviceHandling + +DeviceHandlers are a core component of the FSFW. +The idea is, to have a software counterpart of every physical device to provide a simple mode, health and commanding interface. +By separating the underlying Communication Interface with DeviceCommunicationIF, a DH can be tested on different hardware. +The DH has mechanisms to monitor the communication with the physical device which allow for FDIR reaction. +A standard FDIR component for the DH will be created automatically but can be overwritten by the user. + +#### Modes, Health + +The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components. +On-board Modemangement is implement in hierarchy system. +DeviceHandlers and Controllers are the lowest part of the hierarchy. +The next layer are Assemblies. Those assemblies act as a component which handle redunandcies of handlers. +Assemblies share a common core with the next level which are the Subsystems. +Those are intended to act as auto-generated components from a database which describes the subsystem modes. +The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded. +Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a switch into any higher AOCS mode might first turn on the sensors, than the actuators and the controller as last component. +The target table is used to describe the state that is check continuously by the subsystem. +All of this allows System Modes to be generated as Subsystem object as well from the same database. +This System contains list of subsystem modes in the transition and target tables. +Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be command. +The health state represents if the component is able to perform its tasks. +This can be used to signal the system to avoid using this component instead of a redundant one. +The on-board FDIR uses the health state for isolation and recovery. + +## Example config + +A example config can be found in defaultcfg/fsfwconfig. + +## Unit Tests + +Unit Tests are provided in the unittest folder. Those use the catch2 framework but do not include catch2 itself. +See README.md in the unittest Folder. \ No newline at end of file From 8aef4b9b997fbd7f4be929384e4d5695c6796be1 Mon Sep 17 00:00:00 2001 From: Steffen Gaisser Date: Fri, 13 Nov 2020 14:36:32 +0100 Subject: [PATCH 030/119] Updated formatting --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c75af4ee..91383d48 100644 --- a/README.md +++ b/README.md @@ -82,9 +82,9 @@ Stores Tasks -* There are two different types of tasks -** The PeriodicTask just executes objects that are of type ExecutableObjectIF in the order of the insertion to the Tasks. -** FixedTimeslotTask executes a list of calls in the order of the given list. This is intended for DeviceHandlers, where polling should be in a defined order. An example can be found in defaultcfg/fsfwconfig/pollingSequence +There are two different types of tasks: + * The PeriodicTask just executes objects that are of type ExecutableObjectIF in the order of the insertion to the Tasks. + * FixedTimeslotTask executes a list of calls in the order of the given list. This is intended for DeviceHandlers, where polling should be in a defined order. An example can be found in defaultcfg/fsfwconfig/pollingSequence ### Static Ids in the framework @@ -127,17 +127,19 @@ A standard FDIR component for the DH will be created automatically but can be ov #### Modes, Health The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components. -On-board Modemangement is implement in hierarchy system. +On-board Mode Management is implement in hierarchy system. DeviceHandlers and Controllers are the lowest part of the hierarchy. The next layer are Assemblies. Those assemblies act as a component which handle redunandcies of handlers. Assemblies share a common core with the next level which are the Subsystems. -Those are intended to act as auto-generated components from a database which describes the subsystem modes. + +Those Assemblies are intended to act as auto-generated components from a database which describes the subsystem modes. The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded. Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a switch into any higher AOCS mode might first turn on the sensors, than the actuators and the controller as last component. The target table is used to describe the state that is check continuously by the subsystem. All of this allows System Modes to be generated as Subsystem object as well from the same database. This System contains list of subsystem modes in the transition and target tables. Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be command. + The health state represents if the component is able to perform its tasks. This can be used to signal the system to avoid using this component instead of a redundant one. The on-board FDIR uses the health state for isolation and recovery. From 51443d7a68afc4fed57bc236a47815eec313c534 Mon Sep 17 00:00:00 2001 From: Steffen Gaisser Date: Fri, 13 Nov 2020 15:09:00 +0100 Subject: [PATCH 031/119] Version number --- FSFWVersion.h | 3 ++- README.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/FSFWVersion.h b/FSFWVersion.h index dcb592dc..11a60891 100644 --- a/FSFWVersion.h +++ b/FSFWVersion.h @@ -1,10 +1,11 @@ #ifndef FSFW_DEFAULTCFG_VERSION_H_ #define FSFW_DEFAULTCFG_VERSION_H_ -const char* const FSFW_VERSION_NAME = "fsfw"; +const char* const FSFW_VERSION_NAME = "ASTP"; #define FSFW_VERSION 0 #define FSFW_SUBVERSION 0 +#define FSFW_REVISION 1 diff --git a/README.md b/README.md index 91383d48..13f7b95d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ with Airbus Defence and Space GmbH. ## Intended Use -The framework is designed for systems which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. Therefore, a mode and health system provides control over the states of the software and the controlled devices. In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. +The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. Therefore, a mode and health system provides control over the states of the software and the controlled devices. In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. ## Structure From 5b5f2f3e1d40b3109821ff334483e8a02701a963 Mon Sep 17 00:00:00 2001 From: Steffen Gaisser Date: Tue, 17 Nov 2020 09:42:38 +0100 Subject: [PATCH 032/119] Added hardware recommendations --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 13f7b95d..b4db3c19 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,12 @@ with Airbus Defence and Space GmbH. ## Intended Use -The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. Therefore, a mode and health system provides control over the states of the software and the controlled devices. In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. +The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. +Therefore, a mode and health system provides control over the states of the software and the controlled devices. +In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. + +The recommended hardware is a microprocessor with more than 2 MB of RAM and 1 MB of non-volatile Memory. +For reference, current Applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zync 7000 SoC. ## Structure From 273ddf9061c53e5ab73a7ce39443b81773fef57b Mon Sep 17 00:00:00 2001 From: Steffen Gaisser Date: Tue, 17 Nov 2020 19:25:57 +0100 Subject: [PATCH 033/119] Rtems compiles again fixes #176 --- events/Event.h | 2 +- osal/rtems/Clock.cpp | 18 +++---- osal/rtems/CpuUsage.cpp | 2 +- osal/rtems/InternalErrorCodes.cpp | 4 +- osal/rtems/Interrupt.cpp | 86 ------------------------------- osal/rtems/Interrupt.h | 50 ------------------ osal/rtems/MessageQueue.cpp | 27 +++++----- osal/rtems/MessageQueue.h | 14 ++--- osal/rtems/MultiObjectTask.cpp | 4 +- osal/rtems/Mutex.cpp | 11 ++-- osal/rtems/PollingTask.cpp | 21 ++++---- osal/rtems/PollingTask.h | 2 +- osal/rtems/QueueFactory.cpp | 5 +- osal/rtems/TaskBase.cpp | 2 +- timemanager/Clock.h | 2 +- 15 files changed, 57 insertions(+), 193 deletions(-) delete mode 100644 osal/rtems/Interrupt.cpp delete mode 100644 osal/rtems/Interrupt.h diff --git a/events/Event.h b/events/Event.h index f8410f32..aba156f2 100644 --- a/events/Event.h +++ b/events/Event.h @@ -4,7 +4,7 @@ #include #include "fwSubsystemIdRanges.h" //could be move to more suitable location -#include +#include typedef uint16_t EventId_t; typedef uint8_t EventSeverity_t; diff --git a/osal/rtems/Clock.cpp b/osal/rtems/Clock.cpp index eeffd7f3..9460fd2d 100644 --- a/osal/rtems/Clock.cpp +++ b/osal/rtems/Clock.cpp @@ -157,30 +157,24 @@ ReturnValue_t Clock::setLeapSeconds(const uint16_t leapSeconds_) { if(checkOrCreateClockMutex()!=HasReturnvaluesIF::RETURN_OK){ return HasReturnvaluesIF::RETURN_FAILED; } - ReturnValue_t result = timeMutex->lockMutex(MutexIF::NO_TIMEOUT); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } + MutexHelper helper(timeMutex); + leapSeconds = leapSeconds_; - result = timeMutex->unlockMutex(); - return result; + + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) { if(timeMutex==NULL){ return HasReturnvaluesIF::RETURN_FAILED; } - ReturnValue_t result = timeMutex->lockMutex(MutexIF::NO_TIMEOUT); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } + MutexHelper helper(timeMutex); *leapSeconds_ = leapSeconds; - result = timeMutex->unlockMutex(); - return result; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t Clock::checkOrCreateClockMutex(){ diff --git a/osal/rtems/CpuUsage.cpp b/osal/rtems/CpuUsage.cpp index d49de4ad..6655c69b 100644 --- a/osal/rtems/CpuUsage.cpp +++ b/osal/rtems/CpuUsage.cpp @@ -158,7 +158,7 @@ uint32_t CpuUsage::ThreadData::getSerializedSize() const { } ReturnValue_t CpuUsage::ThreadData::deSerialize(const uint8_t** buffer, - int32_t* size, Endianness streamEndianness) { + size_t* size, Endianness streamEndianness) { ReturnValue_t result = SerializeAdapter::deSerialize(&id, buffer, size, streamEndianness); if (result != HasReturnvaluesIF::RETURN_OK) { diff --git a/osal/rtems/InternalErrorCodes.cpp b/osal/rtems/InternalErrorCodes.cpp index 9346cf15..ddf365d5 100644 --- a/osal/rtems/InternalErrorCodes.cpp +++ b/osal/rtems/InternalErrorCodes.cpp @@ -12,8 +12,8 @@ ReturnValue_t InternalErrorCodes::translate(uint8_t code) { // return INVALID_WORKSPACE_ADDRESS; case INTERNAL_ERROR_TOO_LITTLE_WORKSPACE: return TOO_LITTLE_WORKSPACE; - case INTERNAL_ERROR_WORKSPACE_ALLOCATION: - return WORKSPACE_ALLOCATION; +// case INTERNAL_ERROR_WORKSPACE_ALLOCATION: +// return WORKSPACE_ALLOCATION; // case INTERNAL_ERROR_INTERRUPT_STACK_TOO_SMALL: // return INTERRUPT_STACK_TOO_SMALL; case INTERNAL_ERROR_THREAD_EXITTED: diff --git a/osal/rtems/Interrupt.cpp b/osal/rtems/Interrupt.cpp deleted file mode 100644 index b740f1ca..00000000 --- a/osal/rtems/Interrupt.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "Interrupt.h" -extern "C" { -#include -#include -} -#include "RtemsBasic.h" - - -ReturnValue_t Interrupt::enableInterrupt(InterruptNumber_t interruptNumber) { - volatile uint32_t* irqMask = hw_irq_mask; - uint32_t expectedValue = *irqMask | (1 << interruptNumber); - *irqMask = expectedValue; - uint32_t tempValue = *irqMask; - if (tempValue == expectedValue) { - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } -} - -ReturnValue_t Interrupt::setInterruptServiceRoutine(IsrHandler_t handler, - InterruptNumber_t interrupt, IsrHandler_t* oldHandler) { - IsrHandler_t oldHandler_local; - if (oldHandler == NULL) { - oldHandler = &oldHandler_local; - } - //+ 0x10 comes because of trap type assignment to IRQs in UT699 processor - rtems_status_code status = rtems_interrupt_catch(handler, interrupt + 0x10, - oldHandler); - switch(status){ - case RTEMS_SUCCESSFUL: - //ISR established successfully - return HasReturnvaluesIF::RETURN_OK; - case RTEMS_INVALID_NUMBER: - //illegal vector number - return HasReturnvaluesIF::RETURN_FAILED; - case RTEMS_INVALID_ADDRESS: - //illegal ISR entry point or invalid old_isr_handler - return HasReturnvaluesIF::RETURN_FAILED; - default: - return HasReturnvaluesIF::RETURN_FAILED; - } - -} - -ReturnValue_t Interrupt::disableInterrupt(InterruptNumber_t interruptNumber) { - //TODO Not implemented - return HasReturnvaluesIF::RETURN_FAILED; -} - -//SHOULDDO: Make default values (edge, polarity) settable? -ReturnValue_t Interrupt::enableGpioInterrupt(InterruptNumber_t interrupt) { - volatile uint32_t* irqMask = hw_irq_mask; - uint32_t expectedValue = *irqMask | (1 << interrupt); - *irqMask = expectedValue; - uint32_t tempValue = *irqMask; - if (tempValue == expectedValue) { - volatile hw_gpio_port_t* ioPorts = hw_gpio_port; - ioPorts->direction &= ~(1 << interrupt); //Direction In - ioPorts->interrupt_edge |= 1 << interrupt; //Edge triggered - ioPorts->interrupt_polarity |= 1 << interrupt; //Trigger on rising edge - ioPorts->interrupt_mask |= 1 << interrupt; //Enable - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } -} - -ReturnValue_t Interrupt::disableGpioInterrupt(InterruptNumber_t interrupt) { - volatile uint32_t* irqMask = hw_irq_mask; - uint32_t expectedValue = *irqMask & ~(1 << interrupt); - *irqMask = expectedValue; - uint32_t tempValue = *irqMask; - if (tempValue == expectedValue) { - //Disable gpio IRQ - volatile hw_gpio_port_t* ioPorts = hw_gpio_port; - ioPorts->interrupt_mask &= ~(1 << interrupt); - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } -} - -bool Interrupt::isInterruptInProgress() { - return rtems_interrupt_is_in_progress(); -} diff --git a/osal/rtems/Interrupt.h b/osal/rtems/Interrupt.h deleted file mode 100644 index 2152e2f0..00000000 --- a/osal/rtems/Interrupt.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef OS_RTEMS_INTERRUPT_H_ -#define OS_RTEMS_INTERRUPT_H_ - -#include "../../returnvalues/HasReturnvaluesIF.h" -#include -#include - -typedef rtems_isr_entry IsrHandler_t; -typedef rtems_isr IsrReturn_t; -typedef rtems_vector_number InterruptNumber_t; - -class Interrupt { -public: - virtual ~Interrupt(){}; - - /** - * Establishes a new interrupt service routine. - * @param handler The service routine to establish - * @param interrupt The interrupt (NOT trap type) the routine shall react to. - * @return RETURN_OK on success. Otherwise, the OS failure code is returned. - */ - static ReturnValue_t setInterruptServiceRoutine(IsrHandler_t handler, - InterruptNumber_t interrupt, IsrHandler_t *oldHandler = NULL); - static ReturnValue_t enableInterrupt(InterruptNumber_t interruptNumber); - static ReturnValue_t disableInterrupt(InterruptNumber_t interruptNumber); - /** - * Enables the interrupt given. - * The function tests, if the InterruptMask register was written successfully. - * @param interrupt The interrupt to enable. - * @return RETURN_OK if the interrupt was set successfully. RETURN_FAILED else. - */ - static ReturnValue_t enableGpioInterrupt(InterruptNumber_t interrupt); - /** - * Disables the interrupt given. - * @param interrupt The interrupt to disable. - * @return RETURN_OK if the interrupt was set successfully. RETURN_FAILED else. - */ - static ReturnValue_t disableGpioInterrupt(InterruptNumber_t interrupt); - - - /** - * Checks if the current executing context is an ISR. - * @return true if handling an interrupt, false else. - */ - static bool isInterruptInProgress(); - -}; - - -#endif /* OS_RTEMS_INTERRUPT_H_ */ diff --git a/osal/rtems/MessageQueue.cpp b/osal/rtems/MessageQueue.cpp index 700db444..bc7e864d 100644 --- a/osal/rtems/MessageQueue.cpp +++ b/osal/rtems/MessageQueue.cpp @@ -1,4 +1,5 @@ #include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../objectmanager/ObjectManagerIF.h" #include "MessageQueue.h" #include "RtemsBasic.h" #include @@ -8,7 +9,7 @@ MessageQueue::MessageQueue(size_t message_depth, size_t max_message_size) : rtems_status_code status = rtems_message_queue_create(name, message_depth, max_message_size, 0, &(this->id)); if (status != RTEMS_SUCCESSFUL) { - error << "MessageQueue::MessageQueue: Creating Queue " << std::hex + sif::error << "MessageQueue::MessageQueue: Creating Queue " << std::hex << name << std::dec << " failed with status:" << (uint32_t) status << std::endl; this->id = 0; @@ -20,15 +21,15 @@ MessageQueue::~MessageQueue() { } ReturnValue_t MessageQueue::sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, bool ignoreFault) { + MessageQueueMessageIF* message, bool ignoreFault) { return sendMessageFrom(sendTo, message, this->getId(), ignoreFault); } -ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessage* message) { +ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessageIF* message) { return sendToDefaultFrom(message, this->getId()); } -ReturnValue_t MessageQueue::reply(MessageQueueMessage* message) { +ReturnValue_t MessageQueue::reply(MessageQueueMessageIF* message) { if (this->lastPartner != 0) { return sendMessage(this->lastPartner, message, this->getId()); } else { @@ -36,27 +37,29 @@ ReturnValue_t MessageQueue::reply(MessageQueueMessage* message) { } } -ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessage* message, +ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message, MessageQueueId_t* receivedFrom) { ReturnValue_t status = this->receiveMessage(message); *receivedFrom = this->lastPartner; return status; } -ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessage* message) { +ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) { + size_t size = 0; rtems_status_code status = rtems_message_queue_receive(id, - message->getBuffer(), &(message->messageSize), + message->getBuffer(),&size, RTEMS_NO_WAIT, 1); if (status == RTEMS_SUCCESSFUL) { + message->setMessageSize(size); this->lastPartner = message->getSender(); //Check size of incoming message. - if (message->messageSize < message->getMinimumMessageSize()) { + if (message->getMessageSize() < message->getMinimumMessageSize()) { return HasReturnvaluesIF::RETURN_FAILED; } } else { //No message was received. Keep lastPartner anyway, I might send something later. //But still, delete packet content. - memset(message->getData(), 0, message->MAX_DATA_SIZE); + memset(message->getData(), 0, message->getMaximumMessageSize()); } return convertReturnCode(status); } @@ -79,12 +82,12 @@ void MessageQueue::setDefaultDestination(MessageQueueId_t defaultDestination) { } ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, - MessageQueueMessage* message, MessageQueueId_t sentFrom, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, bool ignoreFault) { message->setSender(sentFrom); rtems_status_code result = rtems_message_queue_send(sendTo, - message->getBuffer(), message->messageSize); + message->getBuffer(), message->getMessageSize()); //TODO: Check if we're in ISR. if (result != RTEMS_SUCCESSFUL && !ignoreFault) { @@ -105,7 +108,7 @@ ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, return returnCode; } -ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessage* message, +ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessageIF* message, MessageQueueId_t sentFrom, bool ignoreFault) { return sendMessageFrom(defaultDestination, message, sentFrom, ignoreFault); } diff --git a/osal/rtems/MessageQueue.h b/osal/rtems/MessageQueue.h index c6fc62d5..78a1e5ca 100644 --- a/osal/rtems/MessageQueue.h +++ b/osal/rtems/MessageQueue.h @@ -60,14 +60,14 @@ public: * @param ignoreFault If set to true, the internal software fault counter is not incremented if queue is full. */ ReturnValue_t sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, bool ignoreFault = false ); + MessageQueueMessageIF* message, bool ignoreFault = false ); /** * @brief This operation sends a message to the default destination. * @details As in the sendMessage method, this function uses the sendToDefault call of the * MessageQueueSender parent class and adds its queue id as "sentFrom" information. * @param message A pointer to a previously created message, which is sent. */ - ReturnValue_t sendToDefault( MessageQueueMessage* message ); + ReturnValue_t sendToDefault( MessageQueueMessageIF* message ); /** * @brief This operation sends a message to the last communication partner. * @details This operation simplifies answering an incoming message by using the stored @@ -75,7 +75,7 @@ public: * (i.e. lastPartner is zero), an error code is returned. * @param message A pointer to a previously created message, which is sent. */ - ReturnValue_t reply( MessageQueueMessage* message ); + ReturnValue_t reply( MessageQueueMessageIF* message ); /** * @brief This function reads available messages from the message queue and returns the sender. @@ -84,7 +84,7 @@ public: * @param message A pointer to a message in which the received data is stored. * @param receivedFrom A pointer to a queue id in which the sender's id is stored. */ - ReturnValue_t receiveMessage(MessageQueueMessage* message, + ReturnValue_t receiveMessage(MessageQueueMessageIF* message, MessageQueueId_t *receivedFrom); /** @@ -95,7 +95,7 @@ public: * message's content is cleared and the function returns immediately. * @param message A pointer to a message in which the received data is stored. */ - ReturnValue_t receiveMessage(MessageQueueMessage* message); + ReturnValue_t receiveMessage(MessageQueueMessageIF* message); /** * Deletes all pending messages in the queue. * @param count The number of flushed messages. @@ -121,7 +121,7 @@ public: * This variable is set to zero by default. * \param ignoreFault If set to true, the internal software fault counter is not incremented if queue is full. */ - virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, MessageQueueMessage* message, MessageQueueId_t sentFrom = NO_QUEUE, bool ignoreFault = false ); + virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, MessageQueueMessageIF* 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. @@ -129,7 +129,7 @@ public: * \param sentFrom The sentFrom information can be set to inject the sender's queue id into the message. * This variable is set to zero by default. */ - virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessage* message, MessageQueueId_t sentFrom = NO_QUEUE, bool ignoreFault = false ); + virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, MessageQueueId_t sentFrom = NO_QUEUE, bool ignoreFault = false ); /** * \brief This method is a simple setter for the default destination. */ diff --git a/osal/rtems/MultiObjectTask.cpp b/osal/rtems/MultiObjectTask.cpp index 2342c24c..a239d9cb 100644 --- a/osal/rtems/MultiObjectTask.cpp +++ b/osal/rtems/MultiObjectTask.cpp @@ -30,7 +30,7 @@ ReturnValue_t MultiObjectTask::startTask() { rtems_status_code status = rtems_task_start(id, MultiObjectTask::taskEntryPoint, rtems_task_argument((void *) this)); if (status != RTEMS_SUCCESSFUL) { - error << "ObjectTask::startTask for " << std::hex << this->getId() + sif::error << "ObjectTask::startTask for " << std::hex << this->getId() << std::dec << " failed." << std::endl; } switch(status){ @@ -63,7 +63,7 @@ void MultiObjectTask::taskFunctionality() { char nameSpace[8] = { 0 }; char* ptr = rtems_object_get_name(getId(), sizeof(nameSpace), nameSpace); - error << "ObjectTask: " << ptr << " Deadline missed." << std::endl; + sif::error << "ObjectTask: " << ptr << " Deadline missed." << std::endl; if (this->deadlineMissedFunc != NULL) { this->deadlineMissedFunc(); } diff --git a/osal/rtems/Mutex.cpp b/osal/rtems/Mutex.cpp index 9553ac79..a5ec9635 100644 --- a/osal/rtems/Mutex.cpp +++ b/osal/rtems/Mutex.cpp @@ -10,7 +10,7 @@ Mutex::Mutex() : RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY, 0, &mutexId); if (status != RTEMS_SUCCESSFUL) { - error << "Mutex: creation with name, id " << mutexName << ", " << mutexId + sif::error << "Mutex: creation with name, id " << mutexName << ", " << mutexId << " failed with " << status << std::endl; } } @@ -18,24 +18,25 @@ Mutex::Mutex() : Mutex::~Mutex() { rtems_status_code status = rtems_semaphore_delete(mutexId); if (status != RTEMS_SUCCESSFUL) { - error << "Mutex: deletion for id " << mutexId + sif::error << "Mutex: deletion for id " << mutexId << " failed with " << status << std::endl; } } ReturnValue_t Mutex::lockMutex(TimeoutType timeoutType = TimeoutType::BLOCKING, uint32_t timeoutMs) { + rtems_status_code status = RTEMS_INVALID_ID; if(timeoutMs == MutexIF::TimeoutType::BLOCKING) { - rtems_status_code status = rtems_semaphore_obtain(mutexId, + status = rtems_semaphore_obtain(mutexId, RTEMS_WAIT, RTEMS_NO_TIMEOUT); } else if(timeoutMs == MutexIF::TimeoutType::POLLING) { timeoutMs = RTEMS_NO_TIMEOUT; - rtems_status_code status = rtems_semaphore_obtain(mutexId, + status = rtems_semaphore_obtain(mutexId, RTEMS_NO_WAIT, 0); } else { - rtems_status_code status = rtems_semaphore_obtain(mutexId, + status = rtems_semaphore_obtain(mutexId, RTEMS_WAIT, timeoutMs); } diff --git a/osal/rtems/PollingTask.cpp b/osal/rtems/PollingTask.cpp index 04191515..1329638d 100644 --- a/osal/rtems/PollingTask.cpp +++ b/osal/rtems/PollingTask.cpp @@ -1,5 +1,6 @@ -#include "../../devicehandlers/FixedSequenceSlot.h" +#include "../../tasks/FixedSequenceSlot.h" #include "../../objectmanager/SystemObjectIF.h" +#include "../../objectmanager/ObjectManagerIF.h" #include "PollingTask.h" #include "RtemsBasic.h" #include "../../returnvalues/HasReturnvaluesIF.h" @@ -34,14 +35,14 @@ rtems_task PollingTask::taskEntryPoint(rtems_task_argument argument) { PollingTask *originalTask(reinterpret_cast(argument)); //The task's functionality is called. originalTask->taskFunctionality(); - debug << "Polling task " << originalTask->getId() + sif::debug << "Polling task " << originalTask->getId() << " returned from taskFunctionality." << std::endl; } void PollingTask::missedDeadlineCounter() { PollingTask::deadlineMissedCount++; if (PollingTask::deadlineMissedCount % 10 == 0) { - error << "PST missed " << PollingTask::deadlineMissedCount + sif::error << "PST missed " << PollingTask::deadlineMissedCount << " deadlines." << std::endl; } } @@ -50,7 +51,7 @@ ReturnValue_t PollingTask::startTask() { rtems_status_code status = rtems_task_start(id, PollingTask::taskEntryPoint, rtems_task_argument((void *) this)); if (status != RTEMS_SUCCESSFUL) { - error << "PollingTask::startTask for " << std::hex << this->getId() + sif::error << "PollingTask::startTask for " << std::hex << this->getId() << std::dec << " failed." << std::endl; } switch(status){ @@ -68,12 +69,13 @@ ReturnValue_t PollingTask::startTask() { ReturnValue_t PollingTask::addSlot(object_id_t componentId, uint32_t slotTimeMs, int8_t executionStep) { - if (objectManager->get(componentId) != nullptr) { - pst.addSlot(componentId, slotTimeMs, executionStep, this); + ExecutableObjectIF* object = objectManager->get(componentId); + if (object != nullptr) { + pst.addSlot(componentId, slotTimeMs, executionStep, object, this); return HasReturnvaluesIF::RETURN_OK; } - error << "Component " << std::hex << componentId << + sif::error << "Component " << std::hex << componentId << " not found, not adding it to pst" << std::endl; return HasReturnvaluesIF::RETURN_FAILED; } @@ -90,11 +92,10 @@ ReturnValue_t PollingTask::checkSequence() const { void PollingTask::taskFunctionality() { // A local iterator for the Polling Sequence Table is created to find the start time for the first entry. - std::list::iterator it = pst.current; + FixedSlotSequence::SlotListIter it = pst.current; //The start time for the first entry is read. - rtems_interval interval = RtemsBasic::convertMsToTicks( - (*it)->pollingTimeMs); + rtems_interval interval = RtemsBasic::convertMsToTicks(it->pollingTimeMs); TaskBase::setAndStartPeriod(interval,&periodId); //The task's "infinite" inner loop is entered. while (1) { diff --git a/osal/rtems/PollingTask.h b/osal/rtems/PollingTask.h index 199c34e7..5656e354 100644 --- a/osal/rtems/PollingTask.h +++ b/osal/rtems/PollingTask.h @@ -1,7 +1,7 @@ #ifndef POLLINGTASK_H_ #define POLLINGTASK_H_ -#include "../../devicehandlers/FixedSlotSequence.h" +#include "../../tasks/FixedSlotSequence.h" #include "../../tasks/FixedTimeslotTaskIF.h" #include "TaskBase.h" diff --git a/osal/rtems/QueueFactory.cpp b/osal/rtems/QueueFactory.cpp index fce55a0e..b35e7a39 100644 --- a/osal/rtems/QueueFactory.cpp +++ b/osal/rtems/QueueFactory.cpp @@ -1,4 +1,5 @@ #include "../../ipc/QueueFactory.h" +#include "../../ipc/MessageQueueSenderIF.h" #include "MessageQueue.h" #include "RtemsBasic.h" @@ -6,11 +7,11 @@ QueueFactory* QueueFactory::factoryInstance = NULL; ReturnValue_t MessageQueueSenderIF::sendMessage(MessageQueueId_t sendTo, - MessageQueueMessage* message, MessageQueueId_t sentFrom,bool ignoreFault) { + MessageQueueMessageIF* message, MessageQueueId_t sentFrom,bool ignoreFault) { //TODO add ignoreFault functionality message->setSender(sentFrom); rtems_status_code result = rtems_message_queue_send(sendTo, message->getBuffer(), - message->messageSize); + message->getMessageSize()); switch(result){ case RTEMS_SUCCESSFUL: //message sent successfully diff --git a/osal/rtems/TaskBase.cpp b/osal/rtems/TaskBase.cpp index adf6ee70..4e0c8f00 100644 --- a/osal/rtems/TaskBase.cpp +++ b/osal/rtems/TaskBase.cpp @@ -22,7 +22,7 @@ TaskBase::TaskBase(rtems_task_priority set_priority, size_t stack_size, } ReturnValue_t result = convertReturnCode(status); if (result != HasReturnvaluesIF::RETURN_OK) { - error << "TaskBase::TaskBase: createTask with name " << std::hex + sif::error << "TaskBase::TaskBase: createTask with name " << std::hex << osalName << std::dec << " failed with return code " << (uint32_t) status << std::endl; this->id = 0; diff --git a/timemanager/Clock.h b/timemanager/Clock.h index acb68e2e..bc112388 100644 --- a/timemanager/Clock.h +++ b/timemanager/Clock.h @@ -2,7 +2,7 @@ #define FRAMEWORK_TIMEMANAGER_CLOCK_H_ #include "../returnvalues/HasReturnvaluesIF.h" -#include "../ipc/MutexFactory.h" +#include "../ipc/MutexHelper.h" #include "../globalfunctions/timevalOperations.h" #include From 6489246c4b52d57b0a1fce6368787adea0115128 Mon Sep 17 00:00:00 2001 From: Steffen Gaisser Date: Tue, 17 Nov 2020 19:35:37 +0100 Subject: [PATCH 034/119] Updated include guards --- osal/rtems/Clock.cpp | 14 +++++++------- osal/rtems/MessageQueue.cpp | 6 +++--- osal/rtems/MessageQueue.h | 15 +++------------ osal/rtems/MultiObjectTask.cpp | 4 ++-- osal/rtems/MultiObjectTask.h | 2 +- osal/rtems/Mutex.h | 6 +++--- osal/rtems/MutexFactory.cpp | 1 - osal/rtems/PollingTask.cpp | 2 +- osal/rtems/PollingTask.h | 6 +++--- osal/rtems/QueueFactory.cpp | 4 ++-- osal/rtems/RtemsBasic.h | 6 +++--- osal/rtems/TaskBase.h | 6 +++--- 12 files changed, 31 insertions(+), 41 deletions(-) diff --git a/osal/rtems/Clock.cpp b/osal/rtems/Clock.cpp index 9460fd2d..e5f37ec6 100644 --- a/osal/rtems/Clock.cpp +++ b/osal/rtems/Clock.cpp @@ -3,7 +3,7 @@ #include uint16_t Clock::leapSeconds = 0; -MutexIF* Clock::timeMutex = NULL; +MutexIF* Clock::timeMutex = nullptr; uint32_t Clock::getTicksPerSecond(void){ rtems_interval ticks_per_second = rtems_clock_get_ticks_per_second(); @@ -40,7 +40,7 @@ ReturnValue_t Clock::setClock(const timeval* time) { //SHOULDDO: Not sure if we need to protect this call somehow (by thread lock or something). //Uli: rtems docu says you can call this from an ISR, not sure if this means no protetion needed //TODO Second parameter is ISR_lock_Context - _TOD_Set(&newTime,NULL); + _TOD_Set(&newTime,nullptr); return HasReturnvaluesIF::RETURN_OK; } @@ -131,7 +131,7 @@ ReturnValue_t Clock::convertTimevalToJD2000(timeval time, double* JD2000) { ReturnValue_t Clock::convertUTCToTT(timeval utc, timeval* tt) { //SHOULDDO: works not for dates in the past (might have less leap seconds) - if (timeMutex == NULL) { + if (timeMutex == nullptr) { return HasReturnvaluesIF::RETURN_FAILED; } @@ -167,7 +167,7 @@ ReturnValue_t Clock::setLeapSeconds(const uint16_t leapSeconds_) { } ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) { - if(timeMutex==NULL){ + if(timeMutex==nullptr){ return HasReturnvaluesIF::RETURN_FAILED; } MutexHelper helper(timeMutex); @@ -178,13 +178,13 @@ ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) { } ReturnValue_t Clock::checkOrCreateClockMutex(){ - if(timeMutex==NULL){ + if(timeMutex==nullptr){ MutexFactory* mutexFactory = MutexFactory::instance(); - if (mutexFactory == NULL) { + if (mutexFactory == nullptr) { return HasReturnvaluesIF::RETURN_FAILED; } timeMutex = mutexFactory->createMutex(); - if (timeMutex == NULL) { + if (timeMutex == nullptr) { return HasReturnvaluesIF::RETURN_FAILED; } } diff --git a/osal/rtems/MessageQueue.cpp b/osal/rtems/MessageQueue.cpp index bc7e864d..839182a6 100644 --- a/osal/rtems/MessageQueue.cpp +++ b/osal/rtems/MessageQueue.cpp @@ -4,7 +4,7 @@ #include "RtemsBasic.h" #include MessageQueue::MessageQueue(size_t message_depth, size_t max_message_size) : - id(0), lastPartner(0), defaultDestination(NO_QUEUE), internalErrorReporter(NULL) { + id(0), lastPartner(0), defaultDestination(NO_QUEUE), internalErrorReporter(nullptr) { rtems_name name = ('Q' << 24) + (queueCounter++ << 8); rtems_status_code status = rtems_message_queue_create(name, message_depth, max_message_size, 0, &(this->id)); @@ -91,11 +91,11 @@ ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, //TODO: Check if we're in ISR. if (result != RTEMS_SUCCESSFUL && !ignoreFault) { - if (internalErrorReporter == NULL) { + if (internalErrorReporter == nullptr) { internalErrorReporter = objectManager->get( objects::INTERNAL_ERROR_REPORTER); } - if (internalErrorReporter != NULL) { + if (internalErrorReporter != nullptr) { internalErrorReporter->queueMessageNotSent(); } } diff --git a/osal/rtems/MessageQueue.h b/osal/rtems/MessageQueue.h index 78a1e5ca..342f1e30 100644 --- a/osal/rtems/MessageQueue.h +++ b/osal/rtems/MessageQueue.h @@ -1,14 +1,5 @@ -/** - * @file MessageQueue.h - * - * @date 10/02/2012 - * @author Bastian Baetz - * - * @brief This file contains the definition of the MessageQueue class. - */ - -#ifndef MESSAGEQUEUE_H_ -#define MESSAGEQUEUE_H_ +#ifndef FSFW_OSAL_RTEMS_MESSAGEQUEUE_H_ +#define FSFW_OSAL_RTEMS_MESSAGEQUEUE_H_ #include "../../internalError/InternalErrorReporterIF.h" #include "../../ipc/MessageQueueIF.h" @@ -178,4 +169,4 @@ private: static ReturnValue_t convertReturnCode(rtems_status_code inValue); }; -#endif /* MESSAGEQUEUE_H_ */ +#endif /* FSFW_OSAL_RTEMS_MESSAGEQUEUE_H_ */ diff --git a/osal/rtems/MultiObjectTask.cpp b/osal/rtems/MultiObjectTask.cpp index a239d9cb..970d01e1 100644 --- a/osal/rtems/MultiObjectTask.cpp +++ b/osal/rtems/MultiObjectTask.cpp @@ -64,7 +64,7 @@ void MultiObjectTask::taskFunctionality() { char* ptr = rtems_object_get_name(getId(), sizeof(nameSpace), nameSpace); sif::error << "ObjectTask: " << ptr << " Deadline missed." << std::endl; - if (this->deadlineMissedFunc != NULL) { + if (this->deadlineMissedFunc != nullptr) { this->deadlineMissedFunc(); } } @@ -74,7 +74,7 @@ void MultiObjectTask::taskFunctionality() { ReturnValue_t MultiObjectTask::addComponent(object_id_t object) { ExecutableObjectIF* newObject = objectManager->get( object); - if (newObject == NULL) { + if (newObject == nullptr) { return HasReturnvaluesIF::RETURN_FAILED; } objectList.push_back(newObject); diff --git a/osal/rtems/MultiObjectTask.h b/osal/rtems/MultiObjectTask.h index 736e79dd..04d122a3 100644 --- a/osal/rtems/MultiObjectTask.h +++ b/osal/rtems/MultiObjectTask.h @@ -80,7 +80,7 @@ protected: /** * @brief The pointer to the deadline-missed function. * @details This pointer stores the function that is executed if the task's deadline is missed. - * So, each may react individually on a timing failure. The pointer may be NULL, + * So, each may react individually on a timing failure. The pointer may be nullptr, * then nothing happens on missing the deadline. The deadline is equal to the next execution * of the periodic task. */ diff --git a/osal/rtems/Mutex.h b/osal/rtems/Mutex.h index 72368210..4c861318 100644 --- a/osal/rtems/Mutex.h +++ b/osal/rtems/Mutex.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_OSAL_RTEMS_MUTEX_H_ -#define FRAMEWORK_OSAL_RTEMS_MUTEX_H_ +#ifndef FSFW_OSAL_RTEMS_MUTEX_H_ +#define FSFW_OSAL_RTEMS_MUTEX_H_ #include "../../ipc/MutexIF.h" #include "RtemsBasic.h" @@ -15,4 +15,4 @@ private: static uint8_t count; }; -#endif /* OS_RTEMS_MUTEX_H_ */ +#endif /* FSFW_OSAL_RTEMS_MUTEX_H_ */ diff --git a/osal/rtems/MutexFactory.cpp b/osal/rtems/MutexFactory.cpp index ea594789..24af5fa9 100644 --- a/osal/rtems/MutexFactory.cpp +++ b/osal/rtems/MutexFactory.cpp @@ -2,7 +2,6 @@ #include "Mutex.h" #include "RtemsBasic.h" -//TODO: Different variant than the lazy loading in QueueFactory. What's better and why? MutexFactory* MutexFactory::factoryInstance = new MutexFactory(); MutexFactory::MutexFactory() { diff --git a/osal/rtems/PollingTask.cpp b/osal/rtems/PollingTask.cpp index 1329638d..db7864fe 100644 --- a/osal/rtems/PollingTask.cpp +++ b/osal/rtems/PollingTask.cpp @@ -108,7 +108,7 @@ void PollingTask::taskFunctionality() { //If the deadline was missed, the deadlineMissedFunc is called. rtems_status_code status = TaskBase::restartPeriod(interval,periodId); if (status == RTEMS_TIMEOUT) { - if (this->deadlineMissedFunc != NULL) { + if (this->deadlineMissedFunc != nullptr) { this->deadlineMissedFunc(); } } diff --git a/osal/rtems/PollingTask.h b/osal/rtems/PollingTask.h index 5656e354..42deaf09 100644 --- a/osal/rtems/PollingTask.h +++ b/osal/rtems/PollingTask.h @@ -1,5 +1,5 @@ -#ifndef POLLINGTASK_H_ -#define POLLINGTASK_H_ +#ifndef FSFW_OSAL_RTEMS_POLLINGTASK_H_ +#define FSFW_OSAL_RTEMS_POLLINGTASK_H_ #include "../../tasks/FixedSlotSequence.h" #include "../../tasks/FixedTimeslotTaskIF.h" @@ -82,4 +82,4 @@ protected: void taskFunctionality( void ); }; -#endif /* POLLINGTASK_H_ */ +#endif /* FSFW_OSAL_RTEMS_POLLINGTASK_H_ */ diff --git a/osal/rtems/QueueFactory.cpp b/osal/rtems/QueueFactory.cpp index b35e7a39..ff561304 100644 --- a/osal/rtems/QueueFactory.cpp +++ b/osal/rtems/QueueFactory.cpp @@ -3,7 +3,7 @@ #include "MessageQueue.h" #include "RtemsBasic.h" -QueueFactory* QueueFactory::factoryInstance = NULL; +QueueFactory* QueueFactory::factoryInstance = nullptr; ReturnValue_t MessageQueueSenderIF::sendMessage(MessageQueueId_t sendTo, @@ -38,7 +38,7 @@ ReturnValue_t MessageQueueSenderIF::sendMessage(MessageQueueId_t sendTo, } QueueFactory* QueueFactory::instance() { - if (factoryInstance == NULL) { + if (factoryInstance == nullptr) { factoryInstance = new QueueFactory; } return factoryInstance; diff --git a/osal/rtems/RtemsBasic.h b/osal/rtems/RtemsBasic.h index 78e0d46e..d0ca5aba 100644 --- a/osal/rtems/RtemsBasic.h +++ b/osal/rtems/RtemsBasic.h @@ -1,5 +1,5 @@ -#ifndef OS_RTEMS_RTEMSBASIC_H_ -#define OS_RTEMS_RTEMSBASIC_H_ +#ifndef FSFW_OSAL_RTEMS_RTEMSBASIC_H_ +#define FSFW_OSAL_RTEMS_RTEMSBASIC_H_ #include "../../returnvalues/HasReturnvaluesIF.h" #include @@ -22,4 +22,4 @@ public: } }; -#endif /* OS_RTEMS_RTEMSBASIC_H_ */ +#endif /* FSFW_OSAL_RTEMS_RTEMSBASIC_H_ */ diff --git a/osal/rtems/TaskBase.h b/osal/rtems/TaskBase.h index 410d9110..0e186e67 100644 --- a/osal/rtems/TaskBase.h +++ b/osal/rtems/TaskBase.h @@ -1,5 +1,5 @@ -#ifndef TASKBASE_H_ -#define TASKBASE_H_ +#ifndef FSFW_OSAL_RTEMS_TASKBASE_H_ +#define FSFW_OSAL_RTEMS_TASKBASE_H_ #include "RtemsBasic.h" #include "../../tasks/PeriodicTaskIF.h" @@ -44,4 +44,4 @@ private: }; -#endif /* TASKBASE_H_ */ +#endif /* FSFW_OSAL_RTEMS_TASKBASE_H_ */ From 1cc0847bb512bc5df7a79097da726bf7f45739db Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 21 Nov 2020 00:15:31 +0100 Subject: [PATCH 035/119] small bugfix --- devicehandlers/DeviceHandlerIF.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index 10907a89..35bbe376 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -18,7 +18,7 @@ public: static const uint8_t TRANSITION_MODE_CHILD_ACTION_MASK = 0x20; static const uint8_t TRANSITION_MODE_BASE_ACTION_MASK = 0x10; - static constexpr Command_t NO_COMMAND = 0xffffffff; + static constexpr Command_t NO_COMMAND = -1; /** * @brief This is the mode the device handler is in. From 8c294729d0dce83c95f34b849ec8bbe8ef369133 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 22 Nov 2020 12:34:16 +0100 Subject: [PATCH 036/119] extern c definition added --- osal/FreeRTOS/TaskManagement.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osal/FreeRTOS/TaskManagement.h b/osal/FreeRTOS/TaskManagement.h index 4b7fe3eb..eb735185 100644 --- a/osal/FreeRTOS/TaskManagement.h +++ b/osal/FreeRTOS/TaskManagement.h @@ -13,7 +13,7 @@ extern "C" { * Architecture dependant portmacro.h function call. * Should be implemented in bsp. */ -extern void vRequestContextSwitchFromISR(); +extern "C" void vRequestContextSwitchFromISR(); /*! * Used by functions to tell if they are being called from From 767f1057628f96019ba1836a83a43c94383cb0f4 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 24 Nov 2020 13:45:34 +0100 Subject: [PATCH 037/119] added changelog --- CHANGELOG | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 CHANGELOG diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 00000000..96f5703c --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,7 @@ +## Changes from ASTP 0.0.1 to 0.0.2 + + +### FreeRTOS OSAL + +- vRequestContextSwitchFromISR is declared extern "C" so it can be defined in +a C file without issues From 227ec25e89c9f95e3bb16f1efe856761cdd7dc4e Mon Sep 17 00:00:00 2001 From: "Jakob.Meier" Date: Fri, 27 Nov 2020 11:44:11 +0100 Subject: [PATCH 038/119] some minor corrections in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b4db3c19..52fec27e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Therefore, a mode and health system provides control over the states of the soft In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. The recommended hardware is a microprocessor with more than 2 MB of RAM and 1 MB of non-volatile Memory. -For reference, current Applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zync 7000 SoC. +For reference, current Applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. ## Structure @@ -134,16 +134,16 @@ A standard FDIR component for the DH will be created automatically but can be ov The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components. On-board Mode Management is implement in hierarchy system. DeviceHandlers and Controllers are the lowest part of the hierarchy. -The next layer are Assemblies. Those assemblies act as a component which handle redunandcies of handlers. +The next layer are Assemblies. Those assemblies act as a component which handle redundancies of handlers. Assemblies share a common core with the next level which are the Subsystems. Those Assemblies are intended to act as auto-generated components from a database which describes the subsystem modes. The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded. Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a switch into any higher AOCS mode might first turn on the sensors, than the actuators and the controller as last component. -The target table is used to describe the state that is check continuously by the subsystem. +The target table is used to describe the state that is checked continuously by the subsystem. All of this allows System Modes to be generated as Subsystem object as well from the same database. This System contains list of subsystem modes in the transition and target tables. -Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be command. +Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be commanded. The health state represents if the component is able to perform its tasks. This can be used to signal the system to avoid using this component instead of a redundant one. From c182171b096d1beaa25edda4ff365b5f2fdd64d9 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 12:32:38 +0100 Subject: [PATCH 039/119] updated make file --- fsfw.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fsfw.mk b/fsfw.mk index 3639717b..f2cdd7ab 100644 --- a/fsfw.mk +++ b/fsfw.mk @@ -70,3 +70,5 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcpacket/packetmatcher/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcpacket/pus/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcservices/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/pus/*.cpp) + +INCLUDES += $(CURRENTPATH) From 49479faa1a4e2b8e43fc1ff55fb4fce7cff75bbb Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 12:37:22 +0100 Subject: [PATCH 040/119] better solution --- defaultcfg/fsfwconfig/FSFWConfig.h | 1 - fsfw.mk | 2 -- 2 files changed, 3 deletions(-) diff --git a/defaultcfg/fsfwconfig/FSFWConfig.h b/defaultcfg/fsfwconfig/FSFWConfig.h index be3717b4..8c6c754a 100644 --- a/defaultcfg/fsfwconfig/FSFWConfig.h +++ b/defaultcfg/fsfwconfig/FSFWConfig.h @@ -1,7 +1,6 @@ #ifndef CONFIG_FSFWCONFIG_H_ #define CONFIG_FSFWCONFIG_H_ -#include #include #include diff --git a/fsfw.mk b/fsfw.mk index f2cdd7ab..3639717b 100644 --- a/fsfw.mk +++ b/fsfw.mk @@ -70,5 +70,3 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcpacket/packetmatcher/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcpacket/pus/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcservices/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/pus/*.cpp) - -INCLUDES += $(CURRENTPATH) From 4241c00448e57c7a05b5ba26248cbb23bc72a4c0 Mon Sep 17 00:00:00 2001 From: Steffen Gaisser Date: Mon, 30 Nov 2020 18:30:58 +0100 Subject: [PATCH 041/119] Added the new logos, colors are WIP at the moment --- NOTICE | 2 + README.md | 3 +- logo/FSFW_Logo_V3.png | Bin 0 -> 137423 bytes logo/FSFW_Logo_V3.svg | 711 +++++++++++++++++++++++++++++++++++++++ logo/FSFW_Logo_V3_bw.png | Bin 0 -> 13369 bytes 5 files changed, 715 insertions(+), 1 deletion(-) create mode 100644 logo/FSFW_Logo_V3.png create mode 100644 logo/FSFW_Logo_V3.svg create mode 100644 logo/FSFW_Logo_V3_bw.png diff --git a/NOTICE b/NOTICE index e1663282..be1a37c4 100644 --- a/NOTICE +++ b/NOTICE @@ -4,6 +4,8 @@ The initial version of the Flight Software Framework was developed during the Flying Laptop Project by the Universität Stuttgart in coorporation with Airbus Defence and Space GmbH. +The supreme FSFW Logo was designed by Markus Koller and Luise Trilsbach. + Copyrights in the Flight Software Framework are retained by their contributors. No copyright assignment is required to contribute to the Flight Software Framework. diff --git a/README.md b/README.md index 52fec27e..a59f4ddd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -Flight Software Framework (FSFW) +![FSFW Logo](logo/FSFW_Logo_V3_bw.png) +# Flight Software Framework (FSFW) ====== The Flight Software Framework is a C++ Object Oriented Framework for unmanned, diff --git a/logo/FSFW_Logo_V3.png b/logo/FSFW_Logo_V3.png new file mode 100644 index 0000000000000000000000000000000000000000..2ac710dd2069446e8f10108716f4c0e97bc961b9 GIT binary patch literal 137423 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

e=h(U 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 + + + + + + + + + + + + + + + + + + + + + + + fs fw + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + fs fw + + + + + + + + + + + + + fs fw + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logo/FSFW_Logo_V3_bw.png b/logo/FSFW_Logo_V3_bw.png new file mode 100644 index 0000000000000000000000000000000000000000..99b9b4f9608f4e53b86ff83747a114477004c9f1 GIT binary patch literal 13369 zcmXwA1ymF6*WMW2&1h-qP6sF{ASxv&T>{cDx}{qrCS4**4;3aLj2s~;>gbW1NP~2I z^Y=gB;cRDTZ0~#D``&o&bMKNLKh&inXCnsy091PSwTuA(f;Rl?K2l=*vycAE7ytkm z=ccLoSWi>)mXDwJb2krX0N{33M3(CP-n$(Aww6^DxzvHQbVHAe#gdqOr$rdcHQqBl z3y}J9L{T8eFD#Mq(!IMov%e-b^Nzz?C)JTO6;zwWRQ;qh?f;b+_}DRirF@>-F$5^R z3mJj-@-E&gS^NYYyXd2P@|tnQf5kUe;{N4ez|6Yhn0De7MApG+>adT_p&Z@qNAmG? zIsGF#T_&^2@Ti~8@rTSVpN$>s!wy&y4rYgCez{TeC2Nb_ivzIA=NQGrgX`IZ1*blh z?(NYiM`l^cd>OV|v$)G7!jGc4Ch2OIr`Xq16wU#(*?L?!GLr5PjJA13anA)F5|Tnd z=FjGAJpr<%Z-31_J0d0Z2C@9|G+YEp1VwiXSxd;ma0Ji{oTK7zYXSa((F1f$| zKrMCI9-EPlz9hZ(-r7UEAnCQ7!-<8Zfu)0+k#-?(GCJkGxOn{&)x3)PTZBCDU4F)7 zfjm+Nsu{kaLsT*3rS0pmjXQ@|i}@EJT{ed)eZ^;sVLSQxve=R^YEqs!T8;mIx=kEF zA4BV)Jf370Gj=!3Zhg&y2uw47n=VLkyJHl!Aq+F7>zc} zcHCaW<#CL{{KjnkWT1auM;;L>5dS1n5g-oqFXMUx4FBA>FTD4Y9>s50@$R$S5izyR z@J%6cM;1q;5Vzep0k@uq1}STsMxUOposgZVU20#G$Fo(n01{$?3xO+3=Iz&xZXNmH z!HUbvTgok0V_jW^;EqQGGysNZZF(533^bm0-3esJVh7mcD{_$|Yy)6{VI>rbcQ)HEz5%DIkP*4JJU z6s<$wh*yh|SRkPefxo{L??)M^|ByH|0ySFc7llIosaTF&8>(< z8ko+5q=}~_u<~WUyb^&GmHoZ^ZVrm;mN7a;WWW2{bwNAtq~17t_E~G zq;Gvg+G=a-1#;6gXcfJT%tKxywgfbi2+CoxwEd2{nCE4l8C`JNR_KMt0d{1lwA9pR zGy|EFGhWO>-+S$AMN~%%CMS$j`nlUtLMcZ{eBVHb!x1}N|^nGH+ElmdhF zF8`@n?dTC@N1ch-C`k!m3~&weW6qlRhL|E$pI z+XfQaY*-LQ;RBv1C%@{NMt3RJPD>g+Yy%1rWb$+SJzY>dj8+w}!GD_fjtXek>8K)- z4c6Ndk&BZ0i5`!T-{$4}*|Y8ysotB}$3X~-g&M$~W zMn#&d$(j`RATy?>mPWixc!5P$`9%&P#Rh*r%7(*2J=T*xFYB)9aY_MNXPW69Pi*O{ z@XKYD>>$%}B$uU;=P_xO#qeF2+om>?Yh3DT@zWUioPh%)ex)ht1gJeZ35h~R#(;ED z)dvv!33l~q7~5krQT+RffoXGB&QR+15i!J77;>(C&{u3l#%z4)dPnLz)*wm z{|xEWw+?=#i6l)nOiBsiRubtCNz{kprRqCy%dG#lYh|~JL`^frOC&k?HX6COyn6K8 zgGY;Kuu@bjm;)du|EY`Z%`G@tmavczah;^NPyg+>|4euUgH32jjiwNg;gNA6@TuJ| zy5d*hal(=F`{|>nM*f z=rP9Z zL?h{lBm2%H;sJc5h`U#;bxZ4U2SwfH%&{7#T7DDTg3P4cHYX%zomGF8CP@|5yB*y~ zT%4_p=E*{_376J#ujo&xWu;xj_en8-cHzXxYhLI+e)!MoR>JC%#fvA)*p358f9qF0 zS+x+y!WD_A2i7G@O22~_K?w|zvj~e|GRp$#cp`E-(l*w3(nPBCZSL&B8hsN?u$|1F zBnG9drg;a{)gKaw0^HHNlwY^koMT3GO#T)HM?7#32RvWUpG`3;aVsUv?z-W! z!{>|g!1EQMkgT4GuO<$)BA(G+Ll;);+auj#7=MhR)tfhz){jC75-|#|pgCN*d5u1W zVO)edz((e>ac0ShKLsoEPstFhIHt#?1wgC-x3;e7!5)j?BJp#AdOrkAJG2du2x?z# zE_TgrEBc07Pvs_=kDudSw*D+7SY<)enjI$Q^CoTncIQqIU<@g)tX>?Bk)uM4TT^)o zxuy6!Yg>7EG~T@klG>v&H2GoS)Pen(HRu=BX-oKk@H&a&cUG3AO##OEl9M8BpvswJ z!yVOM0ahm|F`(q86H<*9&4&Hx25v@*merSET@*~syCzr$Qmd|Zk4G=Lwbcx0X<8~9 zA2{}|MO?G@1X?9s;s}dgQn)FxVBg^5qbfOq+*M+M7P*PwJj}#&T=Fa+nBTdH_^%R7 znYW%3nAh(FTfK|>97ByLJ{8Ztu_3`x6cNsAIKzMTzB*}o_CWg6_ATG_9vy_)j2C_U z{jD|zUwf)Blt>(CNOHU?V};umNJ3aqtC}9MYuOYAuk{Vrd@((4BOLHM#7=RZzA#q- zIf>ABbBnxq@pZSRhio9kjW5f2covZ>_O%@#U56(0l?(YF7IlDFTZ(-Mdi) zTjY=zkVap>l0n#`^lj%HXO#ABt9z%D6tMhq?HQ_tRNr;e_&sN-G1(`g1*~)9KXRPM z=mw6lfD@=zuh;Pqbk8|NR@Uk`X|-zXpj=wt791CiIxa!_{r zOgMvsw5M-Kkh`<%Q;V|S!#UzHKNF5y*Dm3{?eME-uId_#7Uo7dzpMR-%TN8YN|x^) zHmic8v`@cCYMb(MXvPp?cN#WeJknYr}VbVA9|hnW>rF}tFL_KD<356WTEqvyn6c&#$KuAzOMI$ zTU)+W3L0Spw^g)QC{q70D}PkVTIxn_{i@uO`KM5Ft)V+!13?m_ zUmh{Yi^sov$a}wE2dS2D%mS3u^Dx>|v;xO!t9Rb+H8!`z1SK3-MkiKDPVO=JbnO@I z(_2Crw1-YvYNPvPPqojw(>dZQ@oGgw|jZSFQS6!@a~+C7VQ^V zS?OoQLWxTNk-Yn4S!C6#+z0z)do0OfD%3KcipkT&+RDQ;9JbDzXq8M<=sCMkRV9b^ zolyQ~HV@BYz5~JOqI(mT4vf6=mVhg&?Qz}3c{I)~e5?fYO}M$dT}HyA8}=tlQBgq! zqzj(4O&<9mw&F;)S4uv%L<9zPfUJnOsOos(8N8+3CAvv_qLvK`Wi4(Kr%(&@=d~tC7g2_GpsCDuS?u;e+ag6J6;%?ubw7M zzb`dsyKqyr83?I3u2QIaryZ&wGxnF8U&fnDZt$;XPNkAgi4<;73v=@w%PgO{bIZdQ z=3<~*;V9Km)JXHuci%?ilDaj-_is@{SEL{1gKIT9*p3e#)R`K_B&8Kc+u(&Mhn&LU zcbam`ucR=4cxez6UPC>O*2gXBtT;CPia9C2vUc+N-EK=+=am37@`( zwFBZ@h76{v*Qd#61+I{koV?pzUBTL>xsR7Ac}2o;4H|0`1+=o7@D|~B$1ni5 z-lGZ!e~)#(L{KRUwC(`+B(ptc2l49Vko?ZbGh{t7;I!JDaYo9PcQEa|@Am$>Gv;}+ zxJUswVC^ZUp-R}&2%Hc#zYa}d4H6dlMQ3m}NJqp~OS!Pgu51qYBVdz2^4_!P=o$gN zV=X6d*4vgcb)VN#gao5-a^}Z23!`OEBv_!_eq=9PE1@k7RqPY@KlP*Y`t>O(QQViT zDnc2yl9yQ&(_!#=#l3UpRts^1N12L0y9gSpxl&t6_4OdEbxMFEN&TyHph68o5$ z4B^h=A!5F2j0Hxg)p>7-KjoHap{|HI^It}BZM?A8lYG)A({C;OFcdEL327Fnaz|Rt z#rSKE)A-Q)8gfxq>^da5?p~x@j-xH!S0f##vuw1veKPw}SMFf4tN11-zXzLeaALq? zibprXtZngo_b*A6fCwts4BsP1^Yz-pYA);#GwPG!om53inQ#l<)C^{1J2B^%R?65; zloW<0=I2y4E_kqvFPrT9ML@niWzf)sI7a}2w$2vB=PSMPl&$TfZ#g5c?K`%OHR9wy zVH}>*e-`PVLW^9dvpiGOFxzP$wTF1)j!0KI7`cn`{cKMe1uM4<-t?_Ci;X5>(8LoA zK?p9AFNYQOY6E&!OBKozhNX>{JFvluO8LE*d%9kty=5pc@gG^sYgx}r--~?AB{*j3$|W4XBGng{Zg&P%vsHLwCe5=@+S6e=KeeI zZO-J3g)|kv$4JMJ5vdGSXIx%h=)BkJnQ;07SE$(9uq~CpGz+$$ae}^nKd<``YbcSN_k9)yHL+5 znqm?jyxH=4u)^`pJ>3n*kzNk|t8|)dP5h0(8;x|2$%4hcQm($ymNwBFn)vK*K(2YB zkt}Y0&Wc^7O-^HKa^^H5`gCf+dfHRQXKpFM<9i$dhnH& z6hA7%D39fAP^RU!B=2zMlByy#J^KS8AtbJ>S>a;w@_4BXsw!4RVl%?^0yGEwmhZ1> zR%Sx^RUXQ14l2ZCy1ey;+z5C73VWZh^Zc!7>zp7;?Z>i)w^+n^io=G51h1&T!4ogs z?nh#&xmz%GgW?Lug}#v0T0R%%2;^vXqxBLnGdn{|*WCkN*Gi;T;%EYXa%XsAG(8m4 z%joa-G$TSqPCH6x)5ypSp94z2+#M%f^ApGl*^J9)r&Kv$y~g`n|1)DF1OI(r@|J@v z9@xVC%&9;-AovrZKe)M^qJ>e>fU6oi0kgsD;b-U3_sWVIk0ei%*%jJ^F@d}xl>zLW zvN7L+;)wD?(er4_vp(k(KHshNv1P=6OkkFzuwq=DecxA)u^ zpzkI}_munHd(}<8{cwLfuWAEmkR7yFks!)XN;Seq)r5Mxhp&n}!tZ*B<70t6@ztA0 zh@qK_tTBV2Fue_FsOGP8!1aza(J9q$7`kO3e*W#%RcEN_betI+wJCx0>|88!YDNPJ ztG(V&3#o*HImv}sB9Qg){{HmWc#VZ&{9`k$@bPx@bzy)_3T(ML$3@YPj&flK!HJ;| zQF;z+1qcD}VDdg#M(A?S61aoHKrX8t@Z8G{dHgOptHxtu z?}FfvslI0tY7!DN;mrLL^#&!16y!b~+2Ie8XTOsLx4-;y34j%Tal41|mOfISLOsn% zlg15(-A%2aITfoEa*Dt&W)avu4*W@3HlFV5Rxak80?Xs7&!Su?m@FEG?Eg>_TnR*^ z`k;|Gq#G`nLaD|s0h5RB_|lbIqovEe8X?t$1KF_7jUCqFaoQy7+(11Y@F+;CW!Q6M zSj>2UTwhxT+HL0EA^4ath*BcbIt~?x=qSw{+s7gW5Iu_J=D@GqbMHp;jI(iI_e-?< zvm+o4YU9HnLHuQpP^rjd&%WLKoigWqbL+Q4fjrd8Jb}G~KVioq6W%yj;V zpvlwW$Vz*?oNP(;`D)R|p3 zEz3{y5vl6LG~#xBQc6|ZZPYL` z&Qw)FUl!X$ILkmL5-<>Y~bZIM3WBN#;2%&#UaGvT(Q<@#WJYTuN*Nl$qz`4c$tBX{KP$nh)?$HD-dgT!s+qYCNmLL zCX4sK(^G5-+*xODq$h8Bn(0~)BPRcfmHJ6XUn{2b95Y!@P6)MSi zI0^}{tHZ_2&a$)11D#uM-7+#((^L*tD`CPHE;npCvwS&asnRvg6(8a{4`A;uE+x%d&=B zjnNS9?^-0?Oepiu0p~+BHJZ4!yqcn+CSOD@`NbqF?77q~c5thY)klZg(KVx<@!Zug z4EkvxkJsCYmDuycwQ)VdfQ~u<$$%FmeKtqanhs5GUKv5yu=3*@hmG-Jw5-w4FS%)u zYD_sY;#9KOu(#^-SrBhnNSW5pEsel4Uziw!D$v|#MG>*r_;9WL_a7}s?$*g^U6frV zwYTvRaM!6rm>QO$g(|^EgIi^tiYRf-l9)NOqIH9BA*pk^W<{r|VHOdL!v3RPwr1NT2Wc+>^_h;+h%0&27B>Ukmw8(k- z&tJTo0|yGv?C>hx@}AC8rI2gc_BB{Od_Rx&flF~kg)FAATv_nZRYns!awZ!&#GNsmy1p{6G$mnEnXE$DVCME zhd&H}v1y^I&{InR;^)ycGK=%=|ElydBi-{HkIO`YkH#IOLUbnyql>0OrEp5?ti0O$ zEHyqFWvjoFMop^2gw)!=OD-%-5y@+`_cP@nNX$de-7wBp@*ik?mjyH|z9_~&~Wf5Z54uR{z-35^A1nJV84;yn_pzr{LjMA{EM zN;MS6X~G}C91o}K1?7`6YVjGcn0_r@eKt7zBoA-ELpq!@Dg^n{z}9N1MF-hgFFpJF zMF~^PDVsj^Mv5q+NSdI2PqqWjaUZ;eK~Z_uP8Rqm$q|e%3@`o_mu!~xCq{Hb?+W*; zngQmo1OyjlhOuG`eiiz4X@{=on;T+1jidp&_7GbF&bP#q``mcMN?d1p1iblqN^e~7 z_Ed>#PGZfFFMFJ5Anf)#t6a~t=6szuZUtJ(aOOlX+iCF6e&(PLnZu`O(z%jk6TCe= zLDw)Y2;ua7Jb)6_?NVYnb8+P_y2&~ZGN*$g27?`Zq zqAoCInh2MjE#un-iQ`?8CjmH)sye0f^;K(uR4?%6nTdLYh@!N4VW6b;*CnJdI^wV4 za(i$K3Y$xt@7$a}pqbJmyGIs*e4%g#)MPOd8Kp7Rfu^#8$+hVnt8mrT1$L9ZX0Y!`RX{vw> zAh3Jm>%1aCKF3Q)8!x0q2Dj$XV-vyZ2p-Al8)Wg_#}~Sb{1bI|nenvP<)P^qYP*fa z*SBW5Ta85N5iiKD10E5S{2-~oZpK0(-{-4k#J{vBD?P6bp#-!@f8tKDtP-y)pJP?( z@mUb{HA~mBl8la}+SbJcx{zbV;bLUN6HL6}nu=>dWdhphJFf8x3Y$$#%K2KzFMAPG z3nu@FCIg7@tG-t5B16dB7}AW0VM*%8M9arM0j<4+ov@gU~RO5{dq6AxY=f} z@2=3}J5FjcF2v{?)qe*sj>ea_EY!%nr)zR{_E}U50nw$b4MD;!0v1N$3O)a5C1Aso zw);26R%Br>?(%4@e^UHJ<;nLf!XfAMz!sxp7-A|%@KE|w6g7hH=$`nlvod4UqXkOL zIG(Oi`fdk}CQYpZ7{EgpRnD7dhozX;H>n7v-ibhwy+_G)A}0-lCAv-3y|wY>?Q|`WMyg37 zbkp$F;&*iCljPs2FL2ic79$hajO4UI!ZPc(?eN`Bd+n&FUdaZxw@1Fj&og>$dU+f^q}KG!q1cVrX-f&Y2kux`rAq15qSJkV@w=^5Hs zS3Gq)n#l>rT=E2u?{`7?&aV-iz~^Q8RXqZ{gWsRo;L4gYh?PA6U$$i*InWE(1COBw zbC19JjfOb|*AF1}-gf*3TntVz{R4AhC3{n+M@x0>d{y5cF+PTFdI}4diFWZ0A zIlav~OO|AqeQGuP%VAw|RIuYk2AUq0Nz!?LQqn*GRT#~emJw$iU0ZW&NmaJfChDNk zw(CxdVeZXO&ox7LJocZ9spfBa?^+6O>zseHqRhpSxmv33$=gr)jpg?TlqbRdkF5RM zveEIf@O7!JXMQ0Z4mrAwB1a$S_R8V5e6~Y2ja3gN^VcyO|FWZ}^vlO>^FF(-ZZ*;?fr~ zWY`|&{RvW~9~0Ng^TuyzoqG!+%9)xp$081>EKk9fB7=~Rl9b36k>Fy}@j{0EM$AGzIL>4<+yO%Pg{%; z*L_iv&>-+GnAxY$bUc+AnU!$7>hmR88v^ZV`GwIZ?Bc79En;*gr=5TdduC9_+1EY z@4P+tQr(hu&O1|q3e~5AT*mW?7=?Gtixb-(5bS+B%iji!(4IU+Z>s|triyQAL~8OS z*}A!MsV+xeRvRx*VqlbAlj@0)yUwl`tj~Cw?IKDWNs4&k+P$P@`7U!$k)^{@xKO)y z`$fz{nuk&mWO)TGNGh}l|A%m=P{tFRx-ZQMl56(3owP0&=rsLJMCQI?bNHB|G~2jh zC8av^^LI)vxA%UnYj&9Hx&LotVb=@; zQ7EM8-Ezl8jIKcE^!rnNID+i7s{HC5auXTGdTcKl>m}|szerKE6|ysoSl<(p-8`g5 zP(eFydvp&N2#>RqZovjGfL1(ARf%G1e-TB0bO8~R*ipckWGXG&>ZWCNdb>4^<&2%t zLuLm9bwRwl7AN{FQ{;_41Nqpz7IS?$x33J;V0<%on{n!869l$X1cSq8UaxW_;5 zX=M_R8H9MI;mL+1^RaC%NwxnJw#_B5d%* z!-Bndv1Z9jQrP);qLIPb$p>U`ulMt;=ieM~2?(;$bKj>UuaG7&?a)+m(0AXNPx|IR zphHz}PNF$|Lzv?&xNP|q;{!LI_v#KDi{5v-XGdj=d$hj)qtl)u)gq8h&fv7?kxwD) zd$Y3FNvn64H>poUA$abW-&qi_snIt>d!B=$!(<>PLUF^@ctt!|-(MoH8-fAMH@F(H zYqrK}F%!}_7}TRnhADPVLv>2+XUx*YmMOMHwoXbCS~7{wd#oqXoj;ZDWBJ=ERFxb; ztX|HU+UT>9H$-v6ygil3U0If}?pB@s7yRU`Z7@HegV zC55&nR~~pQJ^F{=mMkK-;t{CWWkh5c71zqELZw8G24nk8t}hWw@)sD$mnz#YC!-Wf zY-r&SC7J>8kl(dUm){<3M4DSsPbAapHFSt;wlg91^%q(2qY?O?HwM<#N==P`zgH z3fsD4tc?h#?x607Hc;lR>5cMjpNp^nxh7MD(0;}WVrVPN-A4mnNQ1rqdYtg=@iyhV zTwE)8mr2%wEQ4E$_0aZ43+X3*xCbN+`WXY^3=UJ$AuUOMQWP+=KSZBmtVoR`5Dc~Eg#yAuaKusj((#qIko8k9g97-@DN{Xq z#AoymsN|Od`pKi56qEl(+|q1b-|%G-#mk3x3yq=ym$GYb4Ow#n3g9gW@r1{u7aoiW|_#I;%)btncy9>~RDhZD}`4^yYP_#P;l^N2vngoto2CKa_ zqf;~FJ8-i0**NNdV|}AH4yiCJveU{wX37&s(GP8Pdqhhf6#i)mFEkZx6EszzKRuSU>)d@`!bz6BXQ>aFdQC)^HT>Af9%oHuR zB%>G-9DUR1+ewPx!ME&WTZ|Evz5==b8C)Sd$In-)KLMz93mmp`u;e&hCW285 zqD|L|y-a}p?VDmMt5v^of9y(+d9xjpm9pbf&!gGT(zE( z%i1TzECRLHO+S{X2pJ!$gnFs&WtA_bD%uxgXRaxFEIeH)IY>K9x~~EnxQzad@H%@C ze5=7Ycid9Gaj-LEZ8LKYT2+c9^w8N}g^Ta^@A6|G9C+L`vW@G>Vz6&h`>hXxa~%>% z$ne{D@70pq4yR_GAq4(L_;87{ww+CJV7revQ8UQj3CDldrguT<2RHfU+YjXnQpI0q zesyD=*R^fhni|FL94F;xuQRZ-;waDL+`Fp>Eju9StMWqOur)cCYV7gO@+NWQId9x;+H?T|9`b%(jF#1A)5w`%qTA29 zQjXN8q#f}TL2n2;C?KSe8sVoB{?YBxS27(NPMH3piwH?6x!yltV@8w<6F8%}LGFNB zkUP-2Rw?mgOzfaqZsEVP6Db{uutY>}oG8{#-by?>9Li3urZQ|8_EgP3O|G?^} zZAATeJidfnCuP=Pq-9E@tw22J@#Me50FyM28~KVLxb@W6o$0)T6jshB+8~MPuue5N zIeu$u@n3k@lw~*n;l_vIO6J3bw24-_M`)?V^Rn@Z7Kc*5u_!wWr2t1;K0Vdn6pf>_ z=F##DkqwbP!Ixtk_xwzBetqF&k+r7U=@nlxAMT=%(d7okMpOM=zWv{4gXd|EfT09I z=99PRN3dMYWVYa^{c2G<=TuI3)y#uW=R4x(*1iZfqFl--YfgVkW%LDfs3e-HvkEgIj3-y2b_+h@i;F!-%&c`Rkf43*z(lqwO zSJ`{%e%hQ%G@0oVNaBA7PWynUd1UY;@}?z8|zNUVE)S5#6l&K=M^x z;{zwx^_jlM4Md9kZFcLstXJ3V$!ZS_G9JBNjhNSIR(u>@kMG4?( Date: Tue, 1 Dec 2020 13:24:29 +0100 Subject: [PATCH 042/119] added new tests --- unittest/tests/storagemanager/TestPool.cpp | 591 ++++++++++----------- 1 file changed, 295 insertions(+), 296 deletions(-) diff --git a/unittest/tests/storagemanager/TestPool.cpp b/unittest/tests/storagemanager/TestPool.cpp index f278c40c..daeb6bb5 100644 --- a/unittest/tests/storagemanager/TestPool.cpp +++ b/unittest/tests/storagemanager/TestPool.cpp @@ -1,296 +1,295 @@ -//#include "CatchDefinitions.h" -// -//#include -//#include -//#include -// -//#include -//#include -// -//#include -// -// -//TEST_CASE( "Local Pool Simple Tests [1 Pool]" , "[TestPool]") { -//// uint16_t numberOfElements[1] = {1}; -//// uint16_t sizeofElements[1] = {10}; -// LocalPool::LocalPoolConfig config = {{1, 10}}; -// LocalPool simplePool(0, config); -// std::array testDataArray; -// std::array receptionArray; -// store_address_t testStoreId; -// ReturnValue_t result = retval::CATCH_FAILED; -// uint8_t *pointer = nullptr; -// const uint8_t * constPointer = nullptr; -// -// for(size_t i = 0; i < testDataArray.size(); i++) { -// testDataArray[i] = i; -// } -// size_t size = 10; -// -// SECTION ( "Basic tests") { -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// result = simplePool.getData(testStoreId, &constPointer, &size); -// REQUIRE(result == retval::CATCH_OK); -// memcpy(receptionArray.data(), constPointer, size); -// for(size_t i = 0; i < size; i++) { -// CHECK(receptionArray[i] == i ); -// } -// memset(receptionArray.data(), 0, size); -// result = simplePool.modifyData(testStoreId, &pointer, &size); -// memcpy(receptionArray.data(), pointer, size); -// REQUIRE(result == retval::CATCH_OK); -// for(size_t i = 0; i < size; i++) { -// CHECK(receptionArray[i] == i ); -// } -// result = simplePool.deleteData(testStoreId); -// REQUIRE(result == retval::CATCH_OK); -// result = simplePool.addData(&testStoreId, testDataArray.data(), 15); -// CHECK (result == (int) StorageManagerIF::DATA_TOO_LARGE); -// } -// -// SECTION ( "Reservation Tests ") { -// pointer = nullptr; -// result = simplePool.getFreeElement(&testStoreId, size, &pointer); -// REQUIRE (result == retval::CATCH_OK); -// memcpy(pointer, testDataArray.data(), size); -// constPointer = nullptr; -// result = simplePool.getData(testStoreId, &constPointer, &size); -// -// REQUIRE (result == retval::CATCH_OK); -// memcpy(receptionArray.data(), constPointer, size); -// for(size_t i = 0; i < size; i++) { -// CHECK(receptionArray[i] == i ); -// } -// } -// -// SECTION ( "Add, delete, add, add when full") { -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// result = simplePool.getData(testStoreId, &constPointer, &size); -// REQUIRE( result == retval::CATCH_OK); -// memcpy(receptionArray.data(), constPointer, size); -// for(size_t i = 0; i < size; i++) { -// CHECK(receptionArray[i] == i ); -// } -// -// result = simplePool.deleteData(testStoreId); -// REQUIRE(result == retval::CATCH_OK); -// -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// result = simplePool.getData(testStoreId, &constPointer, &size); -// REQUIRE( result == retval::CATCH_OK); -// memcpy(receptionArray.data(), constPointer, size); -// for(size_t i = 0; i < size; i++) { -// CHECK(receptionArray[i] == i ); -// } -// -// store_address_t newAddress; -// result = simplePool.addData(&newAddress, testDataArray.data(), size); -// REQUIRE(result == (int) StorageManagerIF::DATA_STORAGE_FULL); -// -// // Packet Index to high intentionally -// newAddress.packetIndex = 2; -// pointer = testDataArray.data(); -// result = simplePool.modifyData(newAddress, &pointer, &size); -// REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); -// -// result = simplePool.deleteData(newAddress); -// REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); -// -// newAddress.packetIndex = 0; -// newAddress.poolIndex = 2; -// result = simplePool.deleteData(newAddress); -// REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); -// } -// -// SECTION ( "Initialize and clear store, delete with pointer") { -// result = simplePool.initialize(); -// REQUIRE(result == retval::CATCH_OK); -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// simplePool.clearStore(); -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// result = simplePool.modifyData(testStoreId, &pointer, &size); -// REQUIRE(result == retval::CATCH_OK); -// store_address_t newId; -// result = simplePool.deleteData(pointer, size, &testStoreId); -// REQUIRE(result == retval::CATCH_OK); -// REQUIRE(testStoreId.raw != (uint32_t) StorageManagerIF::INVALID_ADDRESS); -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// } -//} -// -//int runIdx = 0; -// -//TEST_CASE( "Local Pool Extended Tests [3 Pools]" , "[TestPool2]") { -// LocalPool::LocalPoolConfig* config; -// if(runIdx == 0) { -// config = new LocalPool::LocalPoolConfig{{10, 5}, {5, 10}, {2, 20}}; -// } -// else { -// // shufle the order, they should be sort implictely so that the -// // order is ascending for the page sizes. -// config = new LocalPool::LocalPoolConfig{{5, 10}, {2, 20}, {10, 5}}; -// size_t lastSize = 0; -// for(const auto& pair: *config) { -// CHECK(pair.second > lastSize); -// lastSize = pair.second; -// } -// } -// runIdx++; -// -// LocalPool simplePool(0, *config); -// std::array testDataArray; -// std::array receptionArray; -// store_address_t testStoreId; -// ReturnValue_t result = retval::CATCH_FAILED; -// for(size_t i = 0; i < testDataArray.size(); i++) { -// testDataArray[i] = i; -// } -// size_t size = 0; -// -// SECTION ("Basic tests") { -// size = 8; -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// // Should be on second page of the pool now for 8 bytes -// CHECK(testStoreId.poolIndex == 1); -// CHECK(testStoreId.packetIndex == 0); -// -// size = 15; -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// // Should be on third page of the pool now for 15 bytes -// CHECK(testStoreId.poolIndex == 2); -// CHECK(testStoreId.packetIndex == 0); -// -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// // Should be on third page of the pool now for 15 bytes -// CHECK(testStoreId.poolIndex == 2); -// CHECK(testStoreId.packetIndex == 1); -// -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// // Should be on third page of the pool now for 15 bytes -// REQUIRE(result == (int) LocalPool::DATA_STORAGE_FULL); -// -// size = 8; -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// // Should still work -// CHECK(testStoreId.poolIndex == 1); -// CHECK(testStoreId.packetIndex == 1); -// -// // fill the rest of the pool -// for(uint8_t idx = 2; idx < 5; idx++) { -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// CHECK(testStoreId.poolIndex == 1); -// CHECK(testStoreId.packetIndex == idx); -// } -// } -// -// SECTION ("Fill Count and Clearing") { -// //SECTION("Basic tests"); -// uint8_t bytesWritten = 0; -// simplePool.getFillCount(receptionArray.data(), &bytesWritten); -// // fill count should be all zeros now. -// CHECK(bytesWritten == 4); -// CHECK(receptionArray[0] == 0); -// CHECK(receptionArray[1] == 0); -// CHECK(receptionArray[2] == 0); -// CHECK(receptionArray[3] == 0); -// -// // now fill the store completely. -// size = 5; -// for(uint8_t idx = 0; idx < 10; idx++) { -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// CHECK(testStoreId.poolIndex == 0); -// CHECK(testStoreId.packetIndex == idx); -// } -// size = 10; -// for(uint8_t idx = 0; idx < 5; idx++) { -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// CHECK(testStoreId.poolIndex == 1); -// CHECK(testStoreId.packetIndex == idx); -// } -// size = 20; -// for(uint8_t idx = 0; idx < 2; idx++) { -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// CHECK(testStoreId.poolIndex == 2); -// CHECK(testStoreId.packetIndex == idx); -// } -// bytesWritten = 0; -// simplePool.getFillCount(receptionArray.data(), &bytesWritten); -// // fill count should be all 100 now. -// CHECK(bytesWritten == 4); -// CHECK(receptionArray[0] == 100); -// CHECK(receptionArray[1] == 100); -// CHECK(receptionArray[2] == 100); -// CHECK(receptionArray[3] == 100); -// -// // now clear the store -// simplePool.clearStore(); -// bytesWritten = 0; -// simplePool.getFillCount(receptionArray.data(), &bytesWritten); -// CHECK(bytesWritten == 4); -// CHECK(receptionArray[0] == 0); -// CHECK(receptionArray[1] == 0); -// CHECK(receptionArray[2] == 0); -// CHECK(receptionArray[3] == 0); -// -// // now fill one page -// size = 5; -// for(uint8_t idx = 0; idx < 10; idx++) { -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// CHECK(testStoreId.poolIndex == 0); -// CHECK(testStoreId.packetIndex == idx); -// } -// bytesWritten = 0; -// simplePool.getFillCount(receptionArray.data(), &bytesWritten); -// // First page full, median fill count is 33 % -// CHECK(bytesWritten == 4); -// CHECK(receptionArray[0] == 100); -// CHECK(receptionArray[1] == 0); -// CHECK(receptionArray[2] == 0); -// CHECK(receptionArray[3] == 33); -// -// // now fill second page -// size = 10; -// for(uint8_t idx = 0; idx < 5; idx++) { -// result = simplePool.addData(&testStoreId, testDataArray.data(), size); -// REQUIRE(result == retval::CATCH_OK); -// CHECK(testStoreId.poolIndex == 1); -// CHECK(testStoreId.packetIndex == idx); -// } -// bytesWritten = 0; -// simplePool.getFillCount(receptionArray.data(), &bytesWritten); -// // First and second page full, median fill count is 66 % -// CHECK(bytesWritten == 4); -// CHECK(receptionArray[0] == 100); -// CHECK(receptionArray[1] == 100); -// CHECK(receptionArray[2] == 0); -// CHECK(receptionArray[3] == 66); -// -// // now clear first page -// simplePool.clearPage(0); -// bytesWritten = 0; -// simplePool.getFillCount(receptionArray.data(), &bytesWritten); -// // Second page full, median fill count is 33 % -// CHECK(bytesWritten == 4); -// CHECK(receptionArray[0] == 0); -// CHECK(receptionArray[1] == 100); -// CHECK(receptionArray[2] == 0); -// CHECK(receptionArray[3] == 33); -// } -// -// delete(config); -//} +#include "CatchDefinitions.h" + +#include +#include + +#include +#include + +#include + + +TEST_CASE( "Local Pool Simple Tests [1 Pool]" , "[TestPool]") { +// uint16_t numberOfElements[1] = {1}; +// uint16_t sizeofElements[1] = {10}; + LocalPool::LocalPoolConfig config = {{1, 10}}; + LocalPool simplePool(0, config); + std::array testDataArray; + std::array receptionArray; + store_address_t testStoreId; + ReturnValue_t result = retval::CATCH_FAILED; + uint8_t *pointer = nullptr; + const uint8_t * constPointer = nullptr; + + for(size_t i = 0; i < testDataArray.size(); i++) { + testDataArray[i] = i; + } + size_t size = 10; + + SECTION ( "Basic tests") { + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + result = simplePool.getData(testStoreId, &constPointer, &size); + REQUIRE(result == retval::CATCH_OK); + memcpy(receptionArray.data(), constPointer, size); + for(size_t i = 0; i < size; i++) { + CHECK(receptionArray[i] == i ); + } + memset(receptionArray.data(), 0, size); + result = simplePool.modifyData(testStoreId, &pointer, &size); + memcpy(receptionArray.data(), pointer, size); + REQUIRE(result == retval::CATCH_OK); + for(size_t i = 0; i < size; i++) { + CHECK(receptionArray[i] == i ); + } + result = simplePool.deleteData(testStoreId); + REQUIRE(result == retval::CATCH_OK); + result = simplePool.addData(&testStoreId, testDataArray.data(), 15); + CHECK (result == (int) StorageManagerIF::DATA_TOO_LARGE); + } + + SECTION ( "Reservation Tests ") { + pointer = nullptr; + result = simplePool.getFreeElement(&testStoreId, size, &pointer); + REQUIRE (result == retval::CATCH_OK); + memcpy(pointer, testDataArray.data(), size); + constPointer = nullptr; + result = simplePool.getData(testStoreId, &constPointer, &size); + + REQUIRE (result == retval::CATCH_OK); + memcpy(receptionArray.data(), constPointer, size); + for(size_t i = 0; i < size; i++) { + CHECK(receptionArray[i] == i ); + } + } + + SECTION ( "Add, delete, add, add when full") { + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + result = simplePool.getData(testStoreId, &constPointer, &size); + REQUIRE( result == retval::CATCH_OK); + memcpy(receptionArray.data(), constPointer, size); + for(size_t i = 0; i < size; i++) { + CHECK(receptionArray[i] == i ); + } + + result = simplePool.deleteData(testStoreId); + REQUIRE(result == retval::CATCH_OK); + + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + result = simplePool.getData(testStoreId, &constPointer, &size); + REQUIRE( result == retval::CATCH_OK); + memcpy(receptionArray.data(), constPointer, size); + for(size_t i = 0; i < size; i++) { + CHECK(receptionArray[i] == i ); + } + + store_address_t newAddress; + result = simplePool.addData(&newAddress, testDataArray.data(), size); + REQUIRE(result == (int) StorageManagerIF::DATA_STORAGE_FULL); + + // Packet Index to high intentionally + newAddress.packetIndex = 2; + pointer = testDataArray.data(); + result = simplePool.modifyData(newAddress, &pointer, &size); + REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); + + result = simplePool.deleteData(newAddress); + REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); + + newAddress.packetIndex = 0; + newAddress.poolIndex = 2; + result = simplePool.deleteData(newAddress); + REQUIRE(result == (int) StorageManagerIF::ILLEGAL_STORAGE_ID); + } + + SECTION ( "Initialize and clear store, delete with pointer") { + result = simplePool.initialize(); + REQUIRE(result == retval::CATCH_OK); + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + simplePool.clearStore(); + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + result = simplePool.modifyData(testStoreId, &pointer, &size); + REQUIRE(result == retval::CATCH_OK); + store_address_t newId; + result = simplePool.deleteData(pointer, size, &testStoreId); + REQUIRE(result == retval::CATCH_OK); + REQUIRE(testStoreId.raw != (uint32_t) StorageManagerIF::INVALID_ADDRESS); + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + } +} + +int runIdx = 0; + +TEST_CASE( "Local Pool Extended Tests [3 Pools]" , "[TestPool2]") { + LocalPool::LocalPoolConfig* config; + if(runIdx == 0) { + config = new LocalPool::LocalPoolConfig{{10, 5}, {5, 10}, {2, 20}}; + } + else { + // shufle the order, they should be sort implictely so that the + // order is ascending for the page sizes. + config = new LocalPool::LocalPoolConfig{{5, 10}, {2, 20}, {10, 5}}; + size_t lastSize = 0; + for(const auto& pair: *config) { + CHECK(pair.second > lastSize); + lastSize = pair.second; + } + } + runIdx++; + + LocalPool simplePool(0, *config); + std::array testDataArray; + std::array receptionArray; + store_address_t testStoreId; + ReturnValue_t result = retval::CATCH_FAILED; + for(size_t i = 0; i < testDataArray.size(); i++) { + testDataArray[i] = i; + } + size_t size = 0; + + SECTION ("Basic tests") { + size = 8; + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + // Should be on second page of the pool now for 8 bytes + CHECK(testStoreId.poolIndex == 1); + CHECK(testStoreId.packetIndex == 0); + + size = 15; + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + // Should be on third page of the pool now for 15 bytes + CHECK(testStoreId.poolIndex == 2); + CHECK(testStoreId.packetIndex == 0); + + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + // Should be on third page of the pool now for 15 bytes + CHECK(testStoreId.poolIndex == 2); + CHECK(testStoreId.packetIndex == 1); + + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + // Should be on third page of the pool now for 15 bytes + REQUIRE(result == (int) LocalPool::DATA_STORAGE_FULL); + + size = 8; + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + // Should still work + CHECK(testStoreId.poolIndex == 1); + CHECK(testStoreId.packetIndex == 1); + + // fill the rest of the pool + for(uint8_t idx = 2; idx < 5; idx++) { + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + CHECK(testStoreId.poolIndex == 1); + CHECK(testStoreId.packetIndex == idx); + } + } + + SECTION ("Fill Count and Clearing") { + //SECTION("Basic tests"); + uint8_t bytesWritten = 0; + simplePool.getFillCount(receptionArray.data(), &bytesWritten); + // fill count should be all zeros now. + CHECK(bytesWritten == 4); + CHECK(receptionArray[0] == 0); + CHECK(receptionArray[1] == 0); + CHECK(receptionArray[2] == 0); + CHECK(receptionArray[3] == 0); + + // now fill the store completely. + size = 5; + for(uint8_t idx = 0; idx < 10; idx++) { + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + CHECK(testStoreId.poolIndex == 0); + CHECK(testStoreId.packetIndex == idx); + } + size = 10; + for(uint8_t idx = 0; idx < 5; idx++) { + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + CHECK(testStoreId.poolIndex == 1); + CHECK(testStoreId.packetIndex == idx); + } + size = 20; + for(uint8_t idx = 0; idx < 2; idx++) { + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + CHECK(testStoreId.poolIndex == 2); + CHECK(testStoreId.packetIndex == idx); + } + bytesWritten = 0; + simplePool.getFillCount(receptionArray.data(), &bytesWritten); + // fill count should be all 100 now. + CHECK(bytesWritten == 4); + CHECK(receptionArray[0] == 100); + CHECK(receptionArray[1] == 100); + CHECK(receptionArray[2] == 100); + CHECK(receptionArray[3] == 100); + + // now clear the store + simplePool.clearStore(); + bytesWritten = 0; + simplePool.getFillCount(receptionArray.data(), &bytesWritten); + CHECK(bytesWritten == 4); + CHECK(receptionArray[0] == 0); + CHECK(receptionArray[1] == 0); + CHECK(receptionArray[2] == 0); + CHECK(receptionArray[3] == 0); + + // now fill one page + size = 5; + for(uint8_t idx = 0; idx < 10; idx++) { + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + CHECK(testStoreId.poolIndex == 0); + CHECK(testStoreId.packetIndex == idx); + } + bytesWritten = 0; + simplePool.getFillCount(receptionArray.data(), &bytesWritten); + // First page full, median fill count is 33 % + CHECK(bytesWritten == 4); + CHECK(receptionArray[0] == 100); + CHECK(receptionArray[1] == 0); + CHECK(receptionArray[2] == 0); + CHECK(receptionArray[3] == 33); + + // now fill second page + size = 10; + for(uint8_t idx = 0; idx < 5; idx++) { + result = simplePool.addData(&testStoreId, testDataArray.data(), size); + REQUIRE(result == retval::CATCH_OK); + CHECK(testStoreId.poolIndex == 1); + CHECK(testStoreId.packetIndex == idx); + } + bytesWritten = 0; + simplePool.getFillCount(receptionArray.data(), &bytesWritten); + // First and second page full, median fill count is 66 % + CHECK(bytesWritten == 4); + CHECK(receptionArray[0] == 100); + CHECK(receptionArray[1] == 100); + CHECK(receptionArray[2] == 0); + CHECK(receptionArray[3] == 66); + + // now clear first page + simplePool.clearPage(0); + bytesWritten = 0; + simplePool.getFillCount(receptionArray.data(), &bytesWritten); + // Second page full, median fill count is 33 % + CHECK(bytesWritten == 4); + CHECK(receptionArray[0] == 0); + CHECK(receptionArray[1] == 100); + CHECK(receptionArray[2] == 0); + CHECK(receptionArray[3] == 33); + } + + delete(config); +} From 378cbab64d0449c78aafc97922ea20118f842d57 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 13:25:00 +0100 Subject: [PATCH 043/119] removed config includes --- storagemanager/LocalPool.cpp | 1 - storagemanager/PoolManager.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/storagemanager/LocalPool.cpp b/storagemanager/LocalPool.cpp index 48934a0c..8e5f4d48 100644 --- a/storagemanager/LocalPool.cpp +++ b/storagemanager/LocalPool.cpp @@ -1,5 +1,4 @@ #include "LocalPool.h" -#include #include LocalPool::LocalPool(object_id_t setObjectId, const LocalPoolConfig& poolConfig, diff --git a/storagemanager/PoolManager.cpp b/storagemanager/PoolManager.cpp index 85376b28..9c801a3d 100644 --- a/storagemanager/PoolManager.cpp +++ b/storagemanager/PoolManager.cpp @@ -1,5 +1,4 @@ #include "PoolManager.h" -#include PoolManager::PoolManager(object_id_t setObjectId, const LocalPoolConfig& localPoolConfig): From 64b6edab6fb8e9a851c958a8ff5ae55c37403328 Mon Sep 17 00:00:00 2001 From: gaisser Date: Tue, 1 Dec 2020 13:32:50 +0100 Subject: [PATCH 044/119] Update 'README.md' --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a59f4ddd..4aabe36a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ ![FSFW Logo](logo/FSFW_Logo_V3_bw.png) # Flight Software Framework (FSFW) -====== The Flight Software Framework is a C++ Object Oriented Framework for unmanned, automated systems like Satellites. From 98dae18430f6a19ee1047d5c0b47d16cf4ee6bc2 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 13:46:29 +0100 Subject: [PATCH 045/119] update defaultcfg folder --- defaultcfg/{config => fsfwconfig}/FSFWConfig.h | 15 +++++++++------ defaultcfg/{config => fsfwconfig}/OBSWConfig.h | 0 defaultcfg/{config => fsfwconfig}/OBSWVersion.h | 0 .../devices/logicalAddresses.cpp | 0 .../devices/logicalAddresses.h | 2 +- .../devices/powerSwitcherList.cpp | 0 .../devices/powerSwitcherList.h | 0 .../events/subsystemIdRanges.h | 0 .../config.mk => fsfwconfig/fsfwconfig.mk} | 0 .../ipc/missionMessageTypes.cpp | 3 ++- .../ipc/missionMessageTypes.h | 0 .../{config => fsfwconfig}/objects/Factory.cpp | 14 +++++++------- .../{config => fsfwconfig}/objects/Factory.h | 0 .../objects/systemObjectList.h | 0 .../pollingsequence/PollingSequenceFactory.cpp | 0 .../pollingsequence/PollingSequenceFactory.h | 0 .../returnvalues/classIds.h | 0 defaultcfg/{config => fsfwconfig}/tmtc/apid.h | 0 defaultcfg/{config => fsfwconfig}/tmtc/pusIds.h | 0 19 files changed, 19 insertions(+), 15 deletions(-) rename defaultcfg/{config => fsfwconfig}/FSFWConfig.h (81%) rename defaultcfg/{config => fsfwconfig}/OBSWConfig.h (100%) rename defaultcfg/{config => fsfwconfig}/OBSWVersion.h (100%) rename defaultcfg/{config => fsfwconfig}/devices/logicalAddresses.cpp (100%) rename defaultcfg/{config => fsfwconfig}/devices/logicalAddresses.h (89%) rename defaultcfg/{config => fsfwconfig}/devices/powerSwitcherList.cpp (100%) rename defaultcfg/{config => fsfwconfig}/devices/powerSwitcherList.h (100%) rename defaultcfg/{config => fsfwconfig}/events/subsystemIdRanges.h (100%) rename defaultcfg/{config/config.mk => fsfwconfig/fsfwconfig.mk} (100%) rename defaultcfg/{config => fsfwconfig}/ipc/missionMessageTypes.cpp (79%) rename defaultcfg/{config => fsfwconfig}/ipc/missionMessageTypes.h (100%) rename defaultcfg/{config => fsfwconfig}/objects/Factory.cpp (75%) rename defaultcfg/{config => fsfwconfig}/objects/Factory.h (100%) rename defaultcfg/{config => fsfwconfig}/objects/systemObjectList.h (100%) rename defaultcfg/{config => fsfwconfig}/pollingsequence/PollingSequenceFactory.cpp (100%) rename defaultcfg/{config => fsfwconfig}/pollingsequence/PollingSequenceFactory.h (100%) rename defaultcfg/{config => fsfwconfig}/returnvalues/classIds.h (100%) rename defaultcfg/{config => fsfwconfig}/tmtc/apid.h (100%) rename defaultcfg/{config => fsfwconfig}/tmtc/pusIds.h (100%) diff --git a/defaultcfg/config/FSFWConfig.h b/defaultcfg/fsfwconfig/FSFWConfig.h similarity index 81% rename from defaultcfg/config/FSFWConfig.h rename to defaultcfg/fsfwconfig/FSFWConfig.h index ea86152c..1386bf66 100644 --- a/defaultcfg/config/FSFWConfig.h +++ b/defaultcfg/fsfwconfig/FSFWConfig.h @@ -1,19 +1,19 @@ #ifndef CONFIG_FSFWCONFIG_H_ #define CONFIG_FSFWCONFIG_H_ -#include #include +#include //! Used to determine whether C++ ostreams are used //! Those can lead to code bloat. #define FSFW_CPP_OSTREAM_ENABLED 1 -//! Reduced printout to further decrese code size +//! Reduced printout to further decrease code size //! Be careful, this also turns off most diagnostic prinouts! -#define FSFW_REDUCED_PRINTOUT 0 +#define FSFW_ENHANCED_PRINTOUT 0 -//! Can be used to enable debugging printouts for developing the FSFW -#define FSFW_DEBUGGING 0 +//! Can be used to enable additional debugging printouts for developing the FSFW +#define FSFW_PRINT_VERBOSITY_LEVEL 0 //! Defines the FIFO depth of each commanding service base which //! also determines how many commands a CSB service can handle in one cycle @@ -38,7 +38,10 @@ //! When using the newlib nano library, C99 support for stdio facilities //! will not be provided. This define should be set to 1 if this is the case. -#define FSFW_NO_C99_IO 1 +#define FSFW_NO_C99_IO 1 + +//! Specify whether a special mode store is used for Subsystem components. +#define FSFW_USE_MODESTORE 0 namespace fsfwconfig { //! Default timestamp size. The default timestamp will be an eight byte CDC diff --git a/defaultcfg/config/OBSWConfig.h b/defaultcfg/fsfwconfig/OBSWConfig.h similarity index 100% rename from defaultcfg/config/OBSWConfig.h rename to defaultcfg/fsfwconfig/OBSWConfig.h diff --git a/defaultcfg/config/OBSWVersion.h b/defaultcfg/fsfwconfig/OBSWVersion.h similarity index 100% rename from defaultcfg/config/OBSWVersion.h rename to defaultcfg/fsfwconfig/OBSWVersion.h diff --git a/defaultcfg/config/devices/logicalAddresses.cpp b/defaultcfg/fsfwconfig/devices/logicalAddresses.cpp similarity index 100% rename from defaultcfg/config/devices/logicalAddresses.cpp rename to defaultcfg/fsfwconfig/devices/logicalAddresses.cpp diff --git a/defaultcfg/config/devices/logicalAddresses.h b/defaultcfg/fsfwconfig/devices/logicalAddresses.h similarity index 89% rename from defaultcfg/config/devices/logicalAddresses.h rename to defaultcfg/fsfwconfig/devices/logicalAddresses.h index 174fa788..e0827ba3 100644 --- a/defaultcfg/config/devices/logicalAddresses.h +++ b/defaultcfg/fsfwconfig/devices/logicalAddresses.h @@ -1,8 +1,8 @@ #ifndef CONFIG_DEVICES_LOGICALADDRESSES_H_ #define CONFIG_DEVICES_LOGICALADDRESSES_H_ -#include #include +#include "../objects/systemObjectList.h" #include /** diff --git a/defaultcfg/config/devices/powerSwitcherList.cpp b/defaultcfg/fsfwconfig/devices/powerSwitcherList.cpp similarity index 100% rename from defaultcfg/config/devices/powerSwitcherList.cpp rename to defaultcfg/fsfwconfig/devices/powerSwitcherList.cpp diff --git a/defaultcfg/config/devices/powerSwitcherList.h b/defaultcfg/fsfwconfig/devices/powerSwitcherList.h similarity index 100% rename from defaultcfg/config/devices/powerSwitcherList.h rename to defaultcfg/fsfwconfig/devices/powerSwitcherList.h diff --git a/defaultcfg/config/events/subsystemIdRanges.h b/defaultcfg/fsfwconfig/events/subsystemIdRanges.h similarity index 100% rename from defaultcfg/config/events/subsystemIdRanges.h rename to defaultcfg/fsfwconfig/events/subsystemIdRanges.h diff --git a/defaultcfg/config/config.mk b/defaultcfg/fsfwconfig/fsfwconfig.mk similarity index 100% rename from defaultcfg/config/config.mk rename to defaultcfg/fsfwconfig/fsfwconfig.mk diff --git a/defaultcfg/config/ipc/missionMessageTypes.cpp b/defaultcfg/fsfwconfig/ipc/missionMessageTypes.cpp similarity index 79% rename from defaultcfg/config/ipc/missionMessageTypes.cpp rename to defaultcfg/fsfwconfig/ipc/missionMessageTypes.cpp index e2edbf9c..b41b87ba 100644 --- a/defaultcfg/config/ipc/missionMessageTypes.cpp +++ b/defaultcfg/fsfwconfig/ipc/missionMessageTypes.cpp @@ -1,4 +1,5 @@ -#include +#include "missionMessageTypes.h" + #include void messagetypes::clearMissionMessage(CommandMessage* message) { diff --git a/defaultcfg/config/ipc/missionMessageTypes.h b/defaultcfg/fsfwconfig/ipc/missionMessageTypes.h similarity index 100% rename from defaultcfg/config/ipc/missionMessageTypes.h rename to defaultcfg/fsfwconfig/ipc/missionMessageTypes.h diff --git a/defaultcfg/config/objects/Factory.cpp b/defaultcfg/fsfwconfig/objects/Factory.cpp similarity index 75% rename from defaultcfg/config/objects/Factory.cpp rename to defaultcfg/fsfwconfig/objects/Factory.cpp index 51dd6130..41333b1c 100644 --- a/defaultcfg/config/objects/Factory.cpp +++ b/defaultcfg/fsfwconfig/objects/Factory.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -31,15 +32,15 @@ void Factory::produce(void) { setStaticFrameworkObjectIds(); new EventManager(objects::EVENT_MANAGER); new HealthTable(objects::HEALTH_TABLE); - //new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); + new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); } void Factory::setStaticFrameworkObjectIds() { - PusServiceBase::packetSource = objects::PUS_PACKET_DISTRIBUTOR; - PusServiceBase::packetDestination = objects::TM_FUNNEL; + PusServiceBase::packetSource = objects::NO_OBJECT; + PusServiceBase::packetDestination = objects::NO_OBJECT; - CommandingServiceBase::defaultPacketSource = objects::PUS_PACKET_DISTRIBUTOR; - CommandingServiceBase::defaultPacketDestination = objects::TM_FUNNEL; + CommandingServiceBase::defaultPacketSource = objects::NO_OBJECT; + CommandingServiceBase::defaultPacketDestination = objects::NO_OBJECT; VerificationReporter::messageReceiver = objects::PUS_SERVICE_1_VERIFICATION; @@ -48,7 +49,6 @@ void Factory::setStaticFrameworkObjectIds() { DeviceHandlerFailureIsolation::powerConfirmationId = objects::NO_OBJECT; - TmPacketStored::timeStamperId = objects::PUS_TIME; - //TmFunnel::downlinkDestination = objects::NO_OBJECT; + TmPacketStored::timeStamperId = objects::NO_OBJECT; } diff --git a/defaultcfg/config/objects/Factory.h b/defaultcfg/fsfwconfig/objects/Factory.h similarity index 100% rename from defaultcfg/config/objects/Factory.h rename to defaultcfg/fsfwconfig/objects/Factory.h diff --git a/defaultcfg/config/objects/systemObjectList.h b/defaultcfg/fsfwconfig/objects/systemObjectList.h similarity index 100% rename from defaultcfg/config/objects/systemObjectList.h rename to defaultcfg/fsfwconfig/objects/systemObjectList.h diff --git a/defaultcfg/config/pollingsequence/PollingSequenceFactory.cpp b/defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.cpp similarity index 100% rename from defaultcfg/config/pollingsequence/PollingSequenceFactory.cpp rename to defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.cpp diff --git a/defaultcfg/config/pollingsequence/PollingSequenceFactory.h b/defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.h similarity index 100% rename from defaultcfg/config/pollingsequence/PollingSequenceFactory.h rename to defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.h diff --git a/defaultcfg/config/returnvalues/classIds.h b/defaultcfg/fsfwconfig/returnvalues/classIds.h similarity index 100% rename from defaultcfg/config/returnvalues/classIds.h rename to defaultcfg/fsfwconfig/returnvalues/classIds.h diff --git a/defaultcfg/config/tmtc/apid.h b/defaultcfg/fsfwconfig/tmtc/apid.h similarity index 100% rename from defaultcfg/config/tmtc/apid.h rename to defaultcfg/fsfwconfig/tmtc/apid.h diff --git a/defaultcfg/config/tmtc/pusIds.h b/defaultcfg/fsfwconfig/tmtc/pusIds.h similarity index 100% rename from defaultcfg/config/tmtc/pusIds.h rename to defaultcfg/fsfwconfig/tmtc/pusIds.h From 9ebf259a9e0ac9c10e501182bd18916bfef41a56 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 13:50:33 +0100 Subject: [PATCH 046/119] added back config --- storagemanager/LocalPool.cpp | 9 +++++---- storagemanager/PoolManager.cpp | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/storagemanager/LocalPool.cpp b/storagemanager/LocalPool.cpp index 8e5f4d48..74e362d4 100644 --- a/storagemanager/LocalPool.cpp +++ b/storagemanager/LocalPool.cpp @@ -1,4 +1,5 @@ #include "LocalPool.h" +#include #include LocalPool::LocalPool(object_id_t setObjectId, const LocalPoolConfig& poolConfig, @@ -116,7 +117,7 @@ ReturnValue_t LocalPool::modifyData(store_address_t storeId, } ReturnValue_t LocalPool::deleteData(store_address_t storeId) { -#if FSFW_DEBUGGING == 1 +#if FSFW_VERBOSE_PRINTOUT == 2 sif::debug << "Delete: Pool: " << std::dec << storeId.poolIndex << " Index: " << storeId.packetIndex << std::endl; @@ -155,7 +156,7 @@ ReturnValue_t LocalPool::deleteData(uint8_t *ptr, size_t size, // element of an object. localId.packetIndex = deltaAddress / elementSizes[n]; result = deleteData(localId); -#if FSFW_DEBUGGING == 1 +#if FSFW_VERBOSE_PRINTOUT == 2 if (deltaAddress % elementSizes[n] != 0) { sif::error << "LocalPool::deleteData: Address not aligned!" << std::endl; @@ -222,7 +223,7 @@ ReturnValue_t LocalPool::reserveSpace(const size_t size, status = findEmpty(storeId->poolIndex, &storeId->packetIndex); } if (status == RETURN_OK) { -#if FSFW_DEBUGGING == 1 +#if FSFW_VERBOSE_PRINTOUT == 2 sif::debug << "Reserve: Pool: " << std::dec << storeId->poolIndex << " Index: " << storeId->packetIndex << std::endl; @@ -264,7 +265,7 @@ void LocalPool::setToSpillToHigherPools(bool enable) { ReturnValue_t LocalPool::getPoolIndex(size_t packetSize, uint16_t *poolIndex, uint16_t startAtIndex) { for (uint16_t n = startAtIndex; n < NUMBER_OF_POOLS; n++) { -#if FSFW_DEBUGGING == 1 +#if FSFW_VERBOSE_PRINTOUT == 2 sif::debug << "LocalPool " << getObjectId() << "::getPoolIndex: Pool: " << n << ", Element Size: " << elementSizes[n] << std::endl; #endif diff --git a/storagemanager/PoolManager.cpp b/storagemanager/PoolManager.cpp index 9c801a3d..3b7b549b 100644 --- a/storagemanager/PoolManager.cpp +++ b/storagemanager/PoolManager.cpp @@ -1,4 +1,5 @@ #include "PoolManager.h" +#include PoolManager::PoolManager(object_id_t setObjectId, const LocalPoolConfig& localPoolConfig): @@ -24,7 +25,7 @@ ReturnValue_t PoolManager::reserveSpace(const size_t size, ReturnValue_t PoolManager::deleteData( store_address_t storeId) { -#if FSFW_DEBUGGING == 1 +#if FSFW_VERBOSE_PRINTOUT == 2 sif::debug << "PoolManager( " << translateObject(getObjectId()) << " )::deleteData from store " << storeId.poolIndex << ". id is "<< storeId.packetIndex << std::endl; From 2930b2dd5ac533188d3a8969ddf70f44a587f169 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 13:52:40 +0100 Subject: [PATCH 047/119] changelog update --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 96f5703c..7637700c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,3 +5,7 @@ - vRequestContextSwitchFromISR is declared extern "C" so it can be defined in a C file without issues + +### Local Pool + +- Interface of LocalPools has change. LocalPool is not a template anymore. Instead the size and bucket number of the pools per page and the number of pages are passed to the ctor instead of two ctor arguments and a template parameter From 1f78496cd3512d2f8f1cd080a9ff9e161d2cc280 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 14:01:04 +0100 Subject: [PATCH 048/119] changelog update --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 96f5703c..ed73248d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,3 +5,7 @@ - vRequestContextSwitchFromISR is declared extern "C" so it can be defined in a C file without issues + +### EnhancedControllerBase + +- New base class for a controller which also implements HasActionsIF and HasLocalDataPoolIF From 802e24e2933be81eb6f7a35af89692dfff5bc159 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 14:43:36 +0100 Subject: [PATCH 049/119] change log update --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 96f5703c..d5e083dc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -## Changes from ASTP 0.0.1 to 0.0.2 +## Changes from ASTP 1.0.0 to 1.1.0 ### FreeRTOS OSAL From 45df446ab80e1303738889c21e15921c4b807fd6 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 14:44:03 +0100 Subject: [PATCH 050/119] change log update --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index d5e083dc..4937b5f8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -## Changes from ASTP 1.0.0 to 1.1.0 +## Changes from ASTP 0.0.1 to 1.0.0 ### FreeRTOS OSAL From 3e74c18d88d0fadc82f7b60be926d2582d5ec7f0 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 14:59:35 +0100 Subject: [PATCH 051/119] health servicd --- pus/CService201HealthCommanding.cpp | 102 ++++++++++++++++++++++++++++ pus/CService201HealthCommanding.h | 63 +++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 pus/CService201HealthCommanding.cpp create mode 100644 pus/CService201HealthCommanding.h diff --git a/pus/CService201HealthCommanding.cpp b/pus/CService201HealthCommanding.cpp new file mode 100644 index 00000000..db43db89 --- /dev/null +++ b/pus/CService201HealthCommanding.cpp @@ -0,0 +1,102 @@ +#include "CService201HealthCommanding.h" + +#include "../health/HasHealthIF.h" +#include "../serviceinterface/ServiceInterfaceStream.h" +#include "../health/HealthMessage.h" +#include "servicepackets/Service201Packets.h" + +CService201HealthCommanding::CService201HealthCommanding(object_id_t objectId, + uint16_t apid, uint8_t serviceId, uint8_t numParallelCommands, + uint16_t commandTimeoutSeconds): + CommandingServiceBase(objectId, apid, serviceId, + numParallelCommands, commandTimeoutSeconds) { +} + +CService201HealthCommanding::~CService201HealthCommanding() { +} + +ReturnValue_t CService201HealthCommanding::isValidSubservice(uint8_t subservice) { + switch(subservice) { + case(Subservice::COMMAND_SET_HEALTH): + case(Subservice::COMMAND_ANNOUNCE_HEALTH): + case(Subservice::COMMAND_ANNOUNCE_HEALTH_ALL): + return RETURN_OK; + default: + sif::error << "Invalid Subservice" << std::endl; + return AcceptsTelecommandsIF::INVALID_SUBSERVICE; + } +} + +ReturnValue_t CService201HealthCommanding::getMessageQueueAndObject( + uint8_t subservice, const uint8_t *tcData, size_t tcDataLen, + MessageQueueId_t *id, object_id_t *objectId) { + if(tcDataLen < sizeof(object_id_t)) { + return CommandingServiceBase::INVALID_TC; + } + SerializeAdapter::deSerialize(objectId, &tcData, &tcDataLen, + SerializeIF::Endianness::BIG); + + return checkInterfaceAndAcquireMessageQueue(id,objectId); +} + +ReturnValue_t CService201HealthCommanding::checkInterfaceAndAcquireMessageQueue( + MessageQueueId_t* messageQueueToSet, object_id_t* objectId) { + HasHealthIF * destination = objectManager->get(*objectId); + if(destination == nullptr) { + return CommandingServiceBase::INVALID_OBJECT; + } + + *messageQueueToSet = destination->getCommandQueue(); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t CService201HealthCommanding::prepareCommand( + CommandMessage* message, uint8_t subservice, const uint8_t *tcData, + size_t tcDataLen, uint32_t *state, object_id_t objectId) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + switch(subservice) { + case(Subservice::COMMAND_SET_HEALTH): { + HealthSetCommand healthCommand; + result = healthCommand.deSerialize(&tcData, &tcDataLen, + SerializeIF::Endianness::BIG); + if (result != RETURN_OK) { + break; + } + HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_SET, + healthCommand.getHealth()); + break; + } + case(Subservice::COMMAND_ANNOUNCE_HEALTH): { + HealthMessage::setHealthMessage(message, + HealthMessage::HEALTH_ANNOUNCE); + break; + } + case(Subservice::COMMAND_ANNOUNCE_HEALTH_ALL): { + HealthMessage::setHealthMessage(message, + HealthMessage::HEALTH_ANNOUNCE_ALL); + break; + } + } + return result; +} + +ReturnValue_t CService201HealthCommanding::handleReply + (const CommandMessage* reply, Command_t previousCommand, + uint32_t *state, CommandMessage* optionalNextCommand, + object_id_t objectId, bool *isStep) { + Command_t replyId = reply->getCommand(); + if (replyId == HealthMessage::REPLY_HEALTH_SET) { + return prepareHealthSetReply(reply); + } + return CommandingServiceBase::INVALID_REPLY; +} + +ReturnValue_t CService201HealthCommanding::prepareHealthSetReply( + const CommandMessage* reply) { + prepareHealthSetReply(reply); + uint8_t health = static_cast(HealthMessage::getHealth(reply)); + uint8_t oldHealth = static_cast(HealthMessage::getOldHealth(reply)); + HealthSetReply healthSetReply(health, oldHealth); + return sendTmPacket(Subservice::REPLY_HEALTH_SET, &healthSetReply); +} + diff --git a/pus/CService201HealthCommanding.h b/pus/CService201HealthCommanding.h new file mode 100644 index 00000000..124e2ac7 --- /dev/null +++ b/pus/CService201HealthCommanding.h @@ -0,0 +1,63 @@ +#ifndef FSFW_PUS_CSERVICE201HEALTHCOMMANDING_H_ +#define FSFW_PUS_CSERVICE201HEALTHCOMMANDING_H_ + +#include "../tmtcservices/CommandingServiceBase.h" + +/** + * @brief Custom PUS service to set health of all objects + * implementing hasHealthIF. + * + * Examples: Device Handlers, Assemblies or Subsystems. + * Full Documentation: ECSS-E-ST-70-41C or ECSS-E-70-41A + * Dissertation Baetz p. 115, 116, 165-167. + * + * This is a gateway service. It relays device commands using the software bus. + * This service is very closely tied to the Commanding Service Base template + * class. There is constant interaction between this Service Base und a + * child class like this service + * + */ +class CService201HealthCommanding: public CommandingServiceBase { +public: + + CService201HealthCommanding(object_id_t objectId, uint16_t apid, + uint8_t serviceId, uint8_t numParallelCommands = 4, + uint16_t commandTimeoutSeconds = 60); + virtual~ CService201HealthCommanding(); +protected: + /* CSB abstract function implementations */ + ReturnValue_t isValidSubservice(uint8_t subservice) override; + ReturnValue_t getMessageQueueAndObject(uint8_t subservice, + const uint8_t *tcData, size_t tcDataLen, MessageQueueId_t *id, + object_id_t *objectId) override; + /** Prepare health command */ + ReturnValue_t prepareCommand(CommandMessage* message, + uint8_t subservice, const uint8_t *tcData, size_t tcDataLen, + uint32_t *state, object_id_t objectId) override; + /** Handle health reply */ + ReturnValue_t handleReply(const CommandMessage* reply, + Command_t previousCommand, uint32_t *state, + CommandMessage* optionalNextCommand, object_id_t objectId, + bool *isStep) override; + +private: + ReturnValue_t checkAndAcquireTargetID(object_id_t* objectIdToSet, + const uint8_t* tcData, size_t tcDataLen); + ReturnValue_t checkInterfaceAndAcquireMessageQueue( + MessageQueueId_t* MessageQueueToSet, object_id_t* objectId); + + ReturnValue_t prepareHealthSetReply(const CommandMessage *reply); + + enum Subservice { + //! [EXPORT] : [TC] Set health of target object + COMMAND_SET_HEALTH = 1, + //! [EXPORT] : [TM] Reply to health set command which also provides old health + REPLY_HEALTH_SET = 2, + //! [EXPORT] : [TC] Commands object to announce their health as an event + COMMAND_ANNOUNCE_HEALTH = 3, + //! [EXPORT] : [TC] Commands all objects in the health map to announce their health + COMMAND_ANNOUNCE_HEALTH_ALL = 4 + }; +}; + +#endif /* FSFW_PUS_CSERVICE201HEALTHCOMMANDING_H_ */ From 257e3e2db9297afc460b2590111033c3e3b25e32 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 15:03:49 +0100 Subject: [PATCH 052/119] change log update --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 4937b5f8..a4951a41 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,3 +5,5 @@ - vRequestContextSwitchFromISR is declared extern "C" so it can be defined in a C file without issues + +- PUS Health Service added, which allows to command and retrieve health via PUS packets From c72ba92247a2d1d13b5d12291a304c46ac56cfa5 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 15:05:54 +0100 Subject: [PATCH 053/119] health service update --- pus/CService201HealthCommanding.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pus/CService201HealthCommanding.cpp b/pus/CService201HealthCommanding.cpp index db43db89..edacd73a 100644 --- a/pus/CService201HealthCommanding.cpp +++ b/pus/CService201HealthCommanding.cpp @@ -86,11 +86,15 @@ ReturnValue_t CService201HealthCommanding::handleReply object_id_t objectId, bool *isStep) { Command_t replyId = reply->getCommand(); if (replyId == HealthMessage::REPLY_HEALTH_SET) { - return prepareHealthSetReply(reply); + return EXECUTION_COMPLETE; + } + else if(replyId == CommandMessageIF::REPLY_REJECTED) { + return reply->getReplyRejectedReason(); } return CommandingServiceBase::INVALID_REPLY; } +// Not used for now, health state already reported by event ReturnValue_t CService201HealthCommanding::prepareHealthSetReply( const CommandMessage* reply) { prepareHealthSetReply(reply); From 0b32d3dac838aab62979031c5d33c51f0086b632 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 15:10:23 +0100 Subject: [PATCH 054/119] change log update --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 4937b5f8..c877c1ed 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,3 +5,7 @@ - vRequestContextSwitchFromISR is declared extern "C" so it can be defined in a C file without issues + +### PUS Services + +- It is now possible to change the message queue depth for the telecommand verification service (PUS1) From c9bfc0bbfd87797d75b6d1049e066e649e1714dd Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 15:22:18 +0100 Subject: [PATCH 055/119] change log update --- CHANGELOG | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 4937b5f8..c0c826a7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,3 +5,8 @@ - vRequestContextSwitchFromISR is declared extern "C" so it can be defined in a C file without issues + +### Device Handler Base + +- There is an additional `PERFORM_OPERATION` step for the device handler base. It is important +that DHB users adapt their polling sequence tables to perform this step. This steps allows for aclear distinction between operation and communication steps From 0a016f391a7d6ae6eb062e29e4dd494987a16799 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 15:26:06 +0100 Subject: [PATCH 056/119] changelog update --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index fc2fe7e5..a6686478 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,4 +9,5 @@ a C file without issues ### PUS Services - It is now possible to change the message queue depth for the telecommand verification service (PUS1) +- The same is possible for the event reporting service (PUS5) - PUS Health Service added, which allows to command and retrieve health via PUS packets From 8c14cdfbd84654b6761f96769cdefdc98f542043 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 15:34:56 +0100 Subject: [PATCH 057/119] changelog update --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 35547c2c..16275d7a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,4 +14,4 @@ a C file without issues ### Local Pool -- Interface of LocalPools has change. LocalPool is not a template anymore. Instead the size and bucket number of the pools per page and the number of pages are passed to the ctor instead of two ctor arguments and a template parameter +- Interface of LocalPools has changed. LocalPool is not a template anymore. Instead the size and bucket number of the pools per page and the number of pages are passed to the ctor instead of two ctor arguments and a template parameter From b198a6c13ca705dc4ab17f14b4503da6e92f2337 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 16:04:40 +0100 Subject: [PATCH 058/119] added packet --- pus/servicepackets/Service201Packets.h | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 pus/servicepackets/Service201Packets.h diff --git a/pus/servicepackets/Service201Packets.h b/pus/servicepackets/Service201Packets.h new file mode 100644 index 00000000..aed821f9 --- /dev/null +++ b/pus/servicepackets/Service201Packets.h @@ -0,0 +1,48 @@ +#ifndef FSFW_PUS_SERVICEPACKETS_SERVICE201PACKETS_H_ +#define FSFW_PUS_SERVICEPACKETS_SERVICE201PACKETS_H_ + +#include "../../serialize/SerialLinkedListAdapter.h" +#include "../../serialize/SerializeIF.h" +#include "../../health/HasHealthIF.h" + +class HealthSetCommand: public SerialLinkedListAdapter { //!< [EXPORT] : [SUBSERVICE] 1 +public: + + HealthSetCommand() { + setLinks(); + } + + HasHealthIF::HealthState getHealth() { + return static_cast(health.entry); + } +private: + void setLinks() { + setStart(&objectId); + objectId.setNext(&health); + } + SerializeElement objectId; //!< [EXPORT] : [COMMENT] Target object Id + SerializeElement health; //!< [EXPORT] : [COMMENT] Health to set +}; + + +class HealthSetReply: public SerialLinkedListAdapter { //!< [EXPORT] : [SUBSERVICE] 2 +public: + HealthSetReply(uint8_t health_, uint8_t oldHealth_): + health(health_), oldHealth(oldHealth_) + { + setLinks(); + } + +private: + HealthSetReply(const HealthSetReply &reply); + void setLinks() { + setStart(&objectId); + objectId.setNext(&health); + health.setNext(&oldHealth); + } + SerializeElement objectId; //!< [EXPORT] : [COMMENT] Source object ID + SerializeElement health; //!< [EXPORT] : [COMMENT] New Health + SerializeElement oldHealth; //!< [EXPORT] : [COMMENT] Old Health +}; + +#endif /* FSFW_PUS_SERVICEPACKETS_SERVICE201PACKETS_H_ */ From 29eb7200711c771182e14ea770a5f074f44d8093 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 16:22:59 +0100 Subject: [PATCH 059/119] introducing relative includes --- unittest/internal/osal/IntTestMutex.cpp | 2 +- unittest/internal/osal/IntTestSemaphore.cpp | 2 +- unittest/internal/serialize/IntTestSerialization.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/unittest/internal/osal/IntTestMutex.cpp b/unittest/internal/osal/IntTestMutex.cpp index 3fd668df..2899535a 100644 --- a/unittest/internal/osal/IntTestMutex.cpp +++ b/unittest/internal/osal/IntTestMutex.cpp @@ -1,7 +1,7 @@ #include "IntTestMutex.h" #include -#include +#include "../UnittDefinitions.h" #if defined(hosted) #include diff --git a/unittest/internal/osal/IntTestSemaphore.cpp b/unittest/internal/osal/IntTestSemaphore.cpp index 534a6a6d..130fc8ae 100644 --- a/unittest/internal/osal/IntTestSemaphore.cpp +++ b/unittest/internal/osal/IntTestSemaphore.cpp @@ -1,6 +1,6 @@ #include "IntTestSemaphore.h" #include -#include +#include "../UnittDefinitions.h" #include #include diff --git a/unittest/internal/serialize/IntTestSerialization.cpp b/unittest/internal/serialize/IntTestSerialization.cpp index 3f231a41..031fd6fe 100644 --- a/unittest/internal/serialize/IntTestSerialization.cpp +++ b/unittest/internal/serialize/IntTestSerialization.cpp @@ -1,7 +1,7 @@ #include "IntTestSerialization.h" #include #include -#include +#include "../UnittDefinitions.h" #include #include From 8cddf8f509c62ebc0481fba1487efc658e0c95ee Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 16:24:38 +0100 Subject: [PATCH 060/119] more relative includes --- unittest/internal/InternalUnitTester.h | 2 +- unittest/internal/serialize/IntTestSerialization.cpp | 7 ++++--- unittest/internal/serialize/IntTestSerialization.h | 9 +++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/unittest/internal/InternalUnitTester.h b/unittest/internal/InternalUnitTester.h index b301b923..d0b1c106 100644 --- a/unittest/internal/InternalUnitTester.h +++ b/unittest/internal/InternalUnitTester.h @@ -2,7 +2,7 @@ #define FRAMEWORK_TEST_UNITTESTCLASS_H_ #include "UnittDefinitions.h" -#include +#include "../../returnvalues/HasReturnvaluesIF.h" /** * @brief Can be used for internal testing, for example for hardware specific diff --git a/unittest/internal/serialize/IntTestSerialization.cpp b/unittest/internal/serialize/IntTestSerialization.cpp index 031fd6fe..1e33ff33 100644 --- a/unittest/internal/serialize/IntTestSerialization.cpp +++ b/unittest/internal/serialize/IntTestSerialization.cpp @@ -1,8 +1,9 @@ #include "IntTestSerialization.h" -#include -#include #include "../UnittDefinitions.h" -#include +#include "../../serialize/SerializeElement.h" +#include "../../serialize/SerialBufferAdapter.h" +#include "../../serialize/SerializeIF.h" + #include using retval = HasReturnvaluesIF; diff --git a/unittest/internal/serialize/IntTestSerialization.h b/unittest/internal/serialize/IntTestSerialization.h index f8841b82..e8dbd35a 100644 --- a/unittest/internal/serialize/IntTestSerialization.h +++ b/unittest/internal/serialize/IntTestSerialization.h @@ -1,6 +1,7 @@ -#ifndef UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ -#define UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ -#include +#ifndef FSFW_UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ +#define FSFW_UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ + +#include "../../returnvalues/HasReturnvaluesIF.h" #include namespace testserialize { @@ -12,4 +13,4 @@ ReturnValue_t test_serial_buffer_adapter(); extern std::array test_array; } -#endif /* UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ */ +#endif /* FSFW_UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ */ From c741ef3945b1536f45a7cac4fd08c5029c536083 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 16:50:02 +0100 Subject: [PATCH 061/119] using relative includes now --- unittest/internal/UnittDefinitions.cpp | 2 +- unittest/internal/osal/IntTestMq.cpp | 9 +++++---- unittest/internal/osal/IntTestMutex.cpp | 6 +++--- unittest/internal/osal/IntTestSemaphore.cpp | 7 ++++--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/unittest/internal/UnittDefinitions.cpp b/unittest/internal/UnittDefinitions.cpp index 0bdbfcc7..6265e9fd 100644 --- a/unittest/internal/UnittDefinitions.cpp +++ b/unittest/internal/UnittDefinitions.cpp @@ -1,4 +1,4 @@ -#include +#include "UnittDefinitions.h" ReturnValue_t unitt::put_error(std::string errorId) { sif::error << "Unit Tester error: Failed at test ID " diff --git a/unittest/internal/osal/IntTestMq.cpp b/unittest/internal/osal/IntTestMq.cpp index 63016374..8d95f51e 100644 --- a/unittest/internal/osal/IntTestMq.cpp +++ b/unittest/internal/osal/IntTestMq.cpp @@ -1,7 +1,8 @@ -#include -#include -#include -#include +#include "IntTestMq.h" +#include "../UnittDefinitions.h" + +#include "../../ipc/MessageQueueIF.h" +#include "../../ipc/QueueFactory.h" #include diff --git a/unittest/internal/osal/IntTestMutex.cpp b/unittest/internal/osal/IntTestMutex.cpp index 2899535a..01581347 100644 --- a/unittest/internal/osal/IntTestMutex.cpp +++ b/unittest/internal/osal/IntTestMutex.cpp @@ -1,10 +1,10 @@ #include "IntTestMutex.h" -#include +#include "../../ipc/MutexFactory.h" #include "../UnittDefinitions.h" #if defined(hosted) -#include +#include "../../osal/hosted/Mutex.h" #include #include #endif @@ -20,7 +20,7 @@ void testmutex::testMutex() { // timed_mutex from the C++ library specifies undefined behaviour if // the timed mutex is locked twice from the same thread. #if defined(hosted) - // hold on, this actually worked ? :-D This calls the function from + // This calls the function from // another thread and stores the returnvalue in a future. auto future = std::async(&MutexIF::lockMutex, mutex, 1); result = future.get(); diff --git a/unittest/internal/osal/IntTestSemaphore.cpp b/unittest/internal/osal/IntTestSemaphore.cpp index 130fc8ae..f260b6a5 100644 --- a/unittest/internal/osal/IntTestSemaphore.cpp +++ b/unittest/internal/osal/IntTestSemaphore.cpp @@ -1,8 +1,9 @@ #include "IntTestSemaphore.h" -#include #include "../UnittDefinitions.h" -#include -#include + +#include "../../tasks/SemaphoreFactory.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../timemanager/Stopwatch.h" void testsemaph::testBinSemaph() { From a0689320d1e72cb41a1059a4c1254d3533cb7d8e Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 16:55:24 +0100 Subject: [PATCH 062/119] more include fixes --- unittest/tests/container/RingBufferTest.cpp | 4 ++-- unittest/tests/container/TestArrayList.cpp | 5 +++-- unittest/tests/container/TestDynamicFifo.cpp | 6 +++--- unittest/tests/container/TestFifo.cpp | 6 +++--- unittest/tests/container/TestFixedArrayList.cpp | 4 ++-- unittest/tests/container/TestFixedOrderedMultimap.cpp | 4 ++-- unittest/tests/osal/TestMessageQueue.cpp | 8 ++++---- unittest/tests/serialize/TestSerialLinkedPacket.cpp | 7 ++++--- unittest/tests/serialize/TestSerialization.cpp | 2 +- 9 files changed, 24 insertions(+), 22 deletions(-) diff --git a/unittest/tests/container/RingBufferTest.cpp b/unittest/tests/container/RingBufferTest.cpp index 8b82d407..9c1c8a23 100644 --- a/unittest/tests/container/RingBufferTest.cpp +++ b/unittest/tests/container/RingBufferTest.cpp @@ -1,7 +1,7 @@ -#include -#include #include "../../core/CatchDefinitions.h" +#include "../../container/SimpleRingBuffer.h" +#include #include TEST_CASE("Ring Buffer Test" , "[RingBufferTest]") { diff --git a/unittest/tests/container/TestArrayList.cpp b/unittest/tests/container/TestArrayList.cpp index 914188cb..2f884276 100644 --- a/unittest/tests/container/TestArrayList.cpp +++ b/unittest/tests/container/TestArrayList.cpp @@ -1,5 +1,6 @@ -#include -#include +#include "../../container/ArrayList.h" +#include "../../returnvalues/HasReturnvaluesIF.h" + #include #include "../../core/CatchDefinitions.h" diff --git a/unittest/tests/container/TestDynamicFifo.cpp b/unittest/tests/container/TestDynamicFifo.cpp index 6c9b7415..bb19131e 100644 --- a/unittest/tests/container/TestDynamicFifo.cpp +++ b/unittest/tests/container/TestDynamicFifo.cpp @@ -1,7 +1,7 @@ -#include -#include -#include +#include "../../container/DynamicFIFO.h" +#include "../../container/FIFO.h" +#include "../../returnvalues/HasReturnvaluesIF.h" #include #include diff --git a/unittest/tests/container/TestFifo.cpp b/unittest/tests/container/TestFifo.cpp index 3775f424..bd727e00 100644 --- a/unittest/tests/container/TestFifo.cpp +++ b/unittest/tests/container/TestFifo.cpp @@ -1,7 +1,7 @@ -#include -#include -#include +#include "../../container/DynamicFIFO.h" +#include "../../container/FIFO.h" +#include "../../returnvalues/HasReturnvaluesIF.h" #include #include "../../core/CatchDefinitions.h" diff --git a/unittest/tests/container/TestFixedArrayList.cpp b/unittest/tests/container/TestFixedArrayList.cpp index 737932e3..5a1bd280 100644 --- a/unittest/tests/container/TestFixedArrayList.cpp +++ b/unittest/tests/container/TestFixedArrayList.cpp @@ -1,7 +1,7 @@ #include "../../core/CatchDefinitions.h" -#include -#include +#include "../../container/FixedArrayList.h" +#include "../../returnvalues/HasReturnvaluesIF.h" #include diff --git a/unittest/tests/container/TestFixedOrderedMultimap.cpp b/unittest/tests/container/TestFixedOrderedMultimap.cpp index 95194cb5..e625b559 100644 --- a/unittest/tests/container/TestFixedOrderedMultimap.cpp +++ b/unittest/tests/container/TestFixedOrderedMultimap.cpp @@ -1,5 +1,5 @@ -#include -#include +#include "../../container/FixedOrderedMultimap.h" +#include "../../returnvalues/HasReturnvaluesIF.h" #include #include "../../core/CatchDefinitions.h" diff --git a/unittest/tests/osal/TestMessageQueue.cpp b/unittest/tests/osal/TestMessageQueue.cpp index 8e59fa08..441d32e7 100644 --- a/unittest/tests/osal/TestMessageQueue.cpp +++ b/unittest/tests/osal/TestMessageQueue.cpp @@ -1,8 +1,8 @@ -#include -#include -#include "catch.hpp" +#include "../../ipc/MessageQueueIF.h" +#include "../../ipc/QueueFactory.h" +#include #include -#include "core/CatchDefinitions.h" +#include "../../core/CatchDefinitions.h" TEST_CASE("MessageQueue Basic Test","[TestMq]") { MessageQueueIF* testSenderMq = diff --git a/unittest/tests/serialize/TestSerialLinkedPacket.cpp b/unittest/tests/serialize/TestSerialLinkedPacket.cpp index 0a09e430..fbe48894 100644 --- a/unittest/tests/serialize/TestSerialLinkedPacket.cpp +++ b/unittest/tests/serialize/TestSerialLinkedPacket.cpp @@ -1,8 +1,9 @@ -#include +#include "TestSerialLinkedPacket.h" +#include "../../core/CatchDefinitions.h" + +#include "../../globalfunctions/arrayprinter.h" #include -#include "../../core/CatchDefinitions.h" -#include "TestSerialLinkedPacket.h" TEST_CASE("Serial Linked Packet" , "[SerLinkPacket]") { diff --git a/unittest/tests/serialize/TestSerialization.cpp b/unittest/tests/serialize/TestSerialization.cpp index 4c9ba181..6e31170a 100644 --- a/unittest/tests/serialize/TestSerialization.cpp +++ b/unittest/tests/serialize/TestSerialization.cpp @@ -1,4 +1,4 @@ -#include +#include "../../serialize/SerializeAdapter.h" #include "catch.hpp" #include From 36b76fa07ca7aac38815a315d04cb6c0aa14ef10 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:04:33 +0100 Subject: [PATCH 063/119] testcfg update --- unittest/testcfg/FSFWConfig.h | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/unittest/testcfg/FSFWConfig.h b/unittest/testcfg/FSFWConfig.h index 4fb224c1..1386bf66 100644 --- a/unittest/testcfg/FSFWConfig.h +++ b/unittest/testcfg/FSFWConfig.h @@ -1,18 +1,19 @@ #ifndef CONFIG_FSFWCONFIG_H_ #define CONFIG_FSFWCONFIG_H_ -#include +#include +#include //! Used to determine whether C++ ostreams are used //! Those can lead to code bloat. #define FSFW_CPP_OSTREAM_ENABLED 1 -//! Reduced printout to further decrese code size +//! Reduced printout to further decrease code size //! Be careful, this also turns off most diagnostic prinouts! -#define FSFW_REDUCED_PRINTOUT 0 +#define FSFW_ENHANCED_PRINTOUT 0 -//! Can be used to enable debugging printouts for developing the FSFW -#define FSFW_DEBUGGING 0 +//! Can be used to enable additional debugging printouts for developing the FSFW +#define FSFW_PRINT_VERBOSITY_LEVEL 0 //! Defines the FIFO depth of each commanding service base which //! also determines how many commands a CSB service can handle in one cycle @@ -25,9 +26,6 @@ //! and translateEvents (and their compiled source files) #define FSFW_OBJ_EVENT_TRANSLATION 0 -//! If -DDEBUG is supplied in the build defines, there will be -//! additional output which requires the translation files translateObjects -//! and translateEvents (and their compiles source files) #if FSFW_OBJ_EVENT_TRANSLATION == 1 #define FSFW_DEBUG_OUTPUT 1 //! Specify whether info events are printed too. @@ -40,7 +38,20 @@ //! When using the newlib nano library, C99 support for stdio facilities //! will not be provided. This define should be set to 1 if this is the case. -#define FSFW_NO_C99_IO 1 +#define FSFW_NO_C99_IO 1 +//! Specify whether a special mode store is used for Subsystem components. +#define FSFW_USE_MODESTORE 0 + +namespace fsfwconfig { +//! Default timestamp size. The default timestamp will be an eight byte CDC +//! short timestamp. +static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 8; + +//! Configure the allocated pool sizes for the event manager. +static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240; +static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120; +static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120; +} #endif /* CONFIG_FSFWCONFIG_H_ */ From 51b9e0074b027b1982fc6354c8c6e8c13cb68458 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:06:05 +0100 Subject: [PATCH 064/119] Revert "testcfg update" This reverts commit 36b76fa07ca7aac38815a315d04cb6c0aa14ef10. --- unittest/testcfg/FSFWConfig.h | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/unittest/testcfg/FSFWConfig.h b/unittest/testcfg/FSFWConfig.h index 1386bf66..4fb224c1 100644 --- a/unittest/testcfg/FSFWConfig.h +++ b/unittest/testcfg/FSFWConfig.h @@ -1,19 +1,18 @@ #ifndef CONFIG_FSFWCONFIG_H_ #define CONFIG_FSFWCONFIG_H_ -#include -#include +#include //! Used to determine whether C++ ostreams are used //! Those can lead to code bloat. #define FSFW_CPP_OSTREAM_ENABLED 1 -//! Reduced printout to further decrease code size +//! Reduced printout to further decrese code size //! Be careful, this also turns off most diagnostic prinouts! -#define FSFW_ENHANCED_PRINTOUT 0 +#define FSFW_REDUCED_PRINTOUT 0 -//! Can be used to enable additional debugging printouts for developing the FSFW -#define FSFW_PRINT_VERBOSITY_LEVEL 0 +//! Can be used to enable debugging printouts for developing the FSFW +#define FSFW_DEBUGGING 0 //! Defines the FIFO depth of each commanding service base which //! also determines how many commands a CSB service can handle in one cycle @@ -26,6 +25,9 @@ //! and translateEvents (and their compiled source files) #define FSFW_OBJ_EVENT_TRANSLATION 0 +//! If -DDEBUG is supplied in the build defines, there will be +//! additional output which requires the translation files translateObjects +//! and translateEvents (and their compiles source files) #if FSFW_OBJ_EVENT_TRANSLATION == 1 #define FSFW_DEBUG_OUTPUT 1 //! Specify whether info events are printed too. @@ -38,20 +40,7 @@ //! When using the newlib nano library, C99 support for stdio facilities //! will not be provided. This define should be set to 1 if this is the case. -#define FSFW_NO_C99_IO 1 +#define FSFW_NO_C99_IO 1 -//! Specify whether a special mode store is used for Subsystem components. -#define FSFW_USE_MODESTORE 0 - -namespace fsfwconfig { -//! Default timestamp size. The default timestamp will be an eight byte CDC -//! short timestamp. -static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 8; - -//! Configure the allocated pool sizes for the event manager. -static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240; -static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120; -static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120; -} #endif /* CONFIG_FSFWCONFIG_H_ */ From 5eb2cbde0eb117fff237d531d113f3a79b89f15a Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:08:03 +0100 Subject: [PATCH 065/119] some additional ionclude fixes --- unittest/tests/container/TestFixedMap.cpp | 4 ++-- unittest/tests/serialize/TestSerialBufferAdapter.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/unittest/tests/container/TestFixedMap.cpp b/unittest/tests/container/TestFixedMap.cpp index 079062f0..da0c84e3 100644 --- a/unittest/tests/container/TestFixedMap.cpp +++ b/unittest/tests/container/TestFixedMap.cpp @@ -1,5 +1,5 @@ -#include -#include +#include "../../container/FixedMap.h" +#include "../../returnvalues/HasReturnvaluesIF.h" #include #include "../../core/CatchDefinitions.h" diff --git a/unittest/tests/serialize/TestSerialBufferAdapter.cpp b/unittest/tests/serialize/TestSerialBufferAdapter.cpp index 9919ed84..07cd3f9c 100644 --- a/unittest/tests/serialize/TestSerialBufferAdapter.cpp +++ b/unittest/tests/serialize/TestSerialBufferAdapter.cpp @@ -1,4 +1,4 @@ -#include +#include "../../serialize/SerialBufferAdapter.h" #include #include "../../core/CatchDefinitions.h" From 29e701d14df9ffe6e4d2c891a26facbc3a4e7af2 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:08:46 +0100 Subject: [PATCH 066/119] op divider additional features --- globalfunctions/PeriodicOperationDivider.cpp | 14 ++++++++++++-- globalfunctions/PeriodicOperationDivider.h | 12 +++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/globalfunctions/PeriodicOperationDivider.cpp b/globalfunctions/PeriodicOperationDivider.cpp index ad3b8bbd..28e98feb 100644 --- a/globalfunctions/PeriodicOperationDivider.cpp +++ b/globalfunctions/PeriodicOperationDivider.cpp @@ -7,16 +7,26 @@ PeriodicOperationDivider::PeriodicOperationDivider(uint32_t divider, } bool PeriodicOperationDivider::checkAndIncrement() { - if(counter >= divider) { + bool opNecessary = check(); + if(opNecessary) { if(resetAutomatically) { counter = 0; } - return true; + return opNecessary; } counter ++; + return opNecessary; +} + +bool PeriodicOperationDivider::check() { + if(counter >= divider) { + return true; + } return false; } + + void PeriodicOperationDivider::resetCounter() { counter = 0; } diff --git a/globalfunctions/PeriodicOperationDivider.h b/globalfunctions/PeriodicOperationDivider.h index dd970fb8..7f7fb469 100644 --- a/globalfunctions/PeriodicOperationDivider.h +++ b/globalfunctions/PeriodicOperationDivider.h @@ -21,17 +21,27 @@ public: */ PeriodicOperationDivider(uint32_t divider, bool resetAutomatically = true); + /** * Check whether operation is necessary. * If an operation is necessary and the class has been * configured to be reset automatically, the counter will be reset. - * If not, the counter will be incremented. + * * @return * -@c true if the counter is larger or equal to the divider * -@c false otherwise */ bool checkAndIncrement(); + /** + * Checks whether an operation is necessary. + * This function will not increment the counter! + * @return + * -@c true if the counter is larger or equal to the divider + * -@c false otherwise + */ + bool check(); + /** * Can be used to reset the counter to 0 manually. */ From 09caeffbc7a82ee262723ec13933fe65fc10be0f Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:15:25 +0100 Subject: [PATCH 067/119] added new interface for file systems --- memory/HasFileSystemIF.h | 80 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 memory/HasFileSystemIF.h diff --git a/memory/HasFileSystemIF.h b/memory/HasFileSystemIF.h new file mode 100644 index 00000000..dcec6346 --- /dev/null +++ b/memory/HasFileSystemIF.h @@ -0,0 +1,80 @@ +#ifndef FSFW_MEMORY_HASFILESYSTEMIF_H_ +#define FSFW_MEMORY_HASFILESYSTEMIF_H_ + +#include "../returnvalues/HasReturnvaluesIF.h" +#include "../returnvalues/FwClassIds.h" +#include "../ipc/messageQueueDefinitions.h" + +#include + +/** + * @brief Generic interface for objects which expose a file system to enable + * message based file handling. + * @author J. Meier, R. Mueller + */ +class HasFileSystemIF { +public: + static constexpr uint8_t INTERFACE_ID = CLASS_ID::FILE_SYSTEM; + + static constexpr ReturnValue_t FILE_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x00); + static constexpr ReturnValue_t FILE_ALREADY_EXISTS = MAKE_RETURN_CODE(0x01); + static constexpr ReturnValue_t FILE_LOCKED = MAKE_RETURN_CODE(0x02); + + static constexpr ReturnValue_t DIRECTORY_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x03); + static constexpr ReturnValue_t DIRECTORY_ALREADY_EXISTS = MAKE_RETURN_CODE(0x04); + static constexpr ReturnValue_t DIRECTORY_NOT_EMPTY = MAKE_RETURN_CODE(0x05); + + + static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_WRITE = MAKE_RETURN_CODE(0x06); //! P1: Sequence number missing + static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_READ = MAKE_RETURN_CODE(0x07); //! P1: Sequence number missing + + virtual ~HasFileSystemIF() {} + /** + * Function to get the MessageQueueId_t of the implementing object + * @return MessageQueueId_t of the object + */ + virtual MessageQueueId_t getCommandQueue() const = 0; + + /** + * Generic function to append to file. + * @param dirname Directory of the file + * @param filename The filename of the file + * @param data The data to write to the file + * @param size The size of the data to write + * @param packetNumber Current packet number. Can be used to verify that + * there are no missing packets. + * @param args Any other arguments which an implementation might require. + * @param bytesWritten Actual bytes written to file + * For large files the write procedure must be split in multiple calls + * to writeToFile + */ + virtual ReturnValue_t appendToFile(const char* repositoryPath, + const char* filename, const uint8_t* data, size_t size, + uint16_t packetNumber, void* args = nullptr) = 0; + + /** + * Generic function to create a new file. + * @param repositoryPath + * @param filename + * @param data + * @param size + * @param args Any other arguments which an implementation might require. + * @return + */ + virtual ReturnValue_t createFile(const char* repositoryPath, + const char* filename, const uint8_t* data = nullptr, + size_t size = 0, void* args = nullptr) = 0; + + /** + * Generic function to delete a file. + * @param repositoryPath + * @param filename + * @param args + * @return + */ + virtual ReturnValue_t deleteFile(const char* repositoryPath, + const char* filename, void* args = nullptr) = 0; +}; + + +#endif /* FSFW_MEMORY_HASFILESYSTEMIF_H_ */ From f1c5f192134656c7bf2683cb2497cef81cbc747b Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:17:53 +0100 Subject: [PATCH 068/119] changelog update --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index a6686478..2791c761 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,3 +11,7 @@ a C file without issues - It is now possible to change the message queue depth for the telecommand verification service (PUS1) - The same is possible for the event reporting service (PUS5) - PUS Health Service added, which allows to command and retrieve health via PUS packets + +### File System Interface + +- A new interfaces specifies the functions for a software object which exposes the file system of a given hardware to use message based file handling (e.g. PUS commanding) From 606aac687b7d4eec45fa285f16d37958dc7a618a Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:44:36 +0100 Subject: [PATCH 069/119] make event api change --- events/Event.cpp | 6 ++++-- events/Event.h | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/events/Event.cpp b/events/Event.cpp index ea3d46fe..0f76b438 100644 --- a/events/Event.cpp +++ b/events/Event.cpp @@ -1,4 +1,5 @@ #include "Event.h" + namespace EVENT { EventId_t getEventId(Event event) { return (event & 0xFFFF); @@ -8,7 +9,8 @@ EventSeverity_t getSeverity(Event event) { return ((event >> 16) & 0xFF); } -Event makeEvent(EventId_t eventId, EventSeverity_t eventSeverity) { - return (eventSeverity << 16) + (eventId & 0xFFFF); +Event makeEvent(uint8_t subsystemId, uint8_t uniqueEventId, + EventSeverity_t eventSeverity) { + return (eventSeverity << 16) + (subsystemId * 100) + uniqueEventId; } } diff --git a/events/Event.h b/events/Event.h index aba156f2..90ff1a90 100644 --- a/events/Event.h +++ b/events/Event.h @@ -18,9 +18,10 @@ EventId_t getEventId(Event event); EventSeverity_t getSeverity(Event event); -Event makeEvent(EventId_t eventId, EventSeverity_t eventSeverity); - +Event makeEvent(uint8_t subsystemId, uint8_t uniqueEventId, + EventSeverity_t eventSeverity); } + namespace SEVERITY { static const EventSeverity_t INFO = 1; static const EventSeverity_t LOW = 2; From 94b108e03df766c50dd3178b95264b6a3c663d9c Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:45:23 +0100 Subject: [PATCH 070/119] changelog update --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index a6686478..c221c811 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,3 +11,7 @@ a C file without issues - It is now possible to change the message queue depth for the telecommand verification service (PUS1) - The same is possible for the event reporting service (PUS5) - PUS Health Service added, which allows to command and retrieve health via PUS packets + +### Events + +- makeEvent function: Not takes three input parameters instead of two and allows setting a unique ID From 37196c8e3b5e2795dc75243aa38412464e79de9e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 1 Dec 2020 23:47:11 +0100 Subject: [PATCH 071/119] atomic fix --- osal/host/FixedTimeslotTask.h | 2 +- osal/host/PeriodicTask.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osal/host/FixedTimeslotTask.h b/osal/host/FixedTimeslotTask.h index 9985e2ee..f3fffd0f 100644 --- a/osal/host/FixedTimeslotTask.h +++ b/osal/host/FixedTimeslotTask.h @@ -74,7 +74,7 @@ protected: //!< Typedef for the List of objects. typedef std::vector ObjectList; std::thread mainThread; - std::atomic terminateThread = false; + std::atomic terminateThread { false }; //! Polling sequence table which contains the object to execute //! and information like the timeslots and the passed execution step. diff --git a/osal/host/PeriodicTask.h b/osal/host/PeriodicTask.h index 7689788a..00cb6a24 100644 --- a/osal/host/PeriodicTask.h +++ b/osal/host/PeriodicTask.h @@ -69,7 +69,7 @@ protected: //!< Typedef for the List of objects. typedef std::vector ObjectList; std::thread mainThread; - std::atomic terminateThread = false; + std::atomic terminateThread { false }; /** * @brief This attribute holds a list of objects to be executed. From a92f6c5d8e06c3cee2328671d1e3138211ba49e4 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Dec 2020 00:05:54 +0100 Subject: [PATCH 072/119] mutex bugfix for host --- osal/host/Mutex.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osal/host/Mutex.cpp b/osal/host/Mutex.cpp index 8471cab8..fa3aa24c 100644 --- a/osal/host/Mutex.cpp +++ b/osal/host/Mutex.cpp @@ -4,12 +4,12 @@ Mutex::Mutex() {} ReturnValue_t Mutex::lockMutex(TimeoutType timeoutType, uint32_t timeoutMs) { - if(timeoutMs == MutexIF::BLOCKING) { + if(timeoutType == MutexIF::BLOCKING) { mutex.lock(); locked = true; return HasReturnvaluesIF::RETURN_OK; } - else if(timeoutMs == MutexIF::POLLING) { + else if(timeoutType == MutexIF::POLLING) { if(mutex.try_lock()) { locked = true; return HasReturnvaluesIF::RETURN_OK; From 206d4b58b287a6a4a56df478d010225bfefe583d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Dec 2020 00:17:12 +0100 Subject: [PATCH 073/119] removed boolean var --- osal/host/Mutex.cpp | 7 ------- osal/host/Mutex.h | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/osal/host/Mutex.cpp b/osal/host/Mutex.cpp index fa3aa24c..ad8873df 100644 --- a/osal/host/Mutex.cpp +++ b/osal/host/Mutex.cpp @@ -6,19 +6,16 @@ Mutex::Mutex() {} ReturnValue_t Mutex::lockMutex(TimeoutType timeoutType, uint32_t timeoutMs) { if(timeoutType == MutexIF::BLOCKING) { mutex.lock(); - locked = true; return HasReturnvaluesIF::RETURN_OK; } else if(timeoutType == MutexIF::POLLING) { if(mutex.try_lock()) { - locked = true; return HasReturnvaluesIF::RETURN_OK; } } else if(timeoutMs > MutexIF::POLLING){ auto chronoMs = std::chrono::milliseconds(timeoutMs); if(mutex.try_lock_for(chronoMs)) { - locked = true; return HasReturnvaluesIF::RETURN_OK; } } @@ -26,11 +23,7 @@ ReturnValue_t Mutex::lockMutex(TimeoutType timeoutType, uint32_t timeoutMs) { } ReturnValue_t Mutex::unlockMutex() { - if(not locked) { - return MutexIF::CURR_THREAD_DOES_NOT_OWN_MUTEX; - } mutex.unlock(); - locked = false; return HasReturnvaluesIF::RETURN_OK; } diff --git a/osal/host/Mutex.h b/osal/host/Mutex.h index 24dafbbd..c0fa19b7 100644 --- a/osal/host/Mutex.h +++ b/osal/host/Mutex.h @@ -22,7 +22,7 @@ public: std::timed_mutex* getMutexHandle(); private: - bool locked = false; + //bool locked = false; std::timed_mutex mutex; }; From f5c384de14da3275fbd00487a2bc481de9cd99ea Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Dec 2020 00:27:53 +0100 Subject: [PATCH 074/119] some bugfixes --- osal/windows/TmTcWinUdpBridge.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osal/windows/TmTcWinUdpBridge.cpp b/osal/windows/TmTcWinUdpBridge.cpp index 7e283c2a..1f66290f 100644 --- a/osal/windows/TmTcWinUdpBridge.cpp +++ b/osal/windows/TmTcWinUdpBridge.cpp @@ -6,6 +6,7 @@ TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, uint16_t serverPort, uint16_t clientPort): TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) { mutex = MutexFactory::instance()->createMutex(); + communicationLinkUp = false; // Initiates Winsock DLL. WSAData wsaData; @@ -90,7 +91,6 @@ ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { // sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were" // " sent." << std::endl; return HasReturnvaluesIF::RETURN_OK; - return HasReturnvaluesIF::RETURN_OK; } void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in newAddress) { @@ -101,6 +101,7 @@ void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in newAddress) { // &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; // sif::debug << "IP Address Old: " << inet_ntop(AF_INET, // &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; + registerCommConnect(); // Set new IP address if it has changed. if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) { @@ -114,7 +115,7 @@ void TmTcWinUdpBridge::handleSocketError() { switch(errCode) { case(WSANOTINITIALISED): { sif::info << "TmTcWinUdpBridge::handleSocketError: WSANOTINITIALISED: " - << "WSAStartup(...) call " << "necessary" << std::endl; + << "WSAStartup(...) call necessary" << std::endl; break; } default: { @@ -154,11 +155,11 @@ void TmTcWinUdpBridge::handleSendError() { switch(errCode) { case(WSANOTINITIALISED): { sif::info << "TmTcWinUdpBridge::handleSendError: WSANOTINITIALISED: " - << "WSAStartup(...) call " << "necessary" << std::endl; + << "WSAStartup(...) call necessary" << std::endl; break; } case(WSAEADDRNOTAVAIL): { - sif::info << "TmTcWinUdpBridge::handleReadError: WSAEADDRNOTAVAIL: " + sif::info << "TmTcWinUdpBridge::handleSendError: WSAEADDRNOTAVAIL: " << "Check target address. " << std::endl; break; } From 73d3defe9f1edc043911e17884ca09d8dcdda48a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Dec 2020 00:32:07 +0100 Subject: [PATCH 075/119] error output improved --- osal/host/QueueMapManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osal/host/QueueMapManager.cpp b/osal/host/QueueMapManager.cpp index 1b2094e9..621f46bc 100644 --- a/osal/host/QueueMapManager.cpp +++ b/osal/host/QueueMapManager.cpp @@ -44,7 +44,7 @@ MessageQueueIF* QueueMapManager::getMessageQueue( return queueIter->second; } else { - sif::warning << "QueueMapManager::getQueueHandle: The ID" << + sif::warning << "QueueMapManager::getQueueHandle: The ID " << messageQueueId << " does not exists in the map" << std::endl; return nullptr; } From 8ad30d489a35cdd6ee27c2e14cfd907d85b62ee0 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Dec 2020 00:58:13 +0100 Subject: [PATCH 076/119] important bugfix for host osal --- osal/host/PeriodicTask.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osal/host/PeriodicTask.cpp b/osal/host/PeriodicTask.cpp index f4ee079b..fb236963 100644 --- a/osal/host/PeriodicTask.cpp +++ b/osal/host/PeriodicTask.cpp @@ -89,6 +89,10 @@ ReturnValue_t PeriodicTask::sleepFor(uint32_t ms) { } void PeriodicTask::taskFunctionality() { + for (const auto& object: objectList) { + object->initializeAfterTaskCreation(); + } + std::chrono::milliseconds periodChrono(static_cast(period*1000)); auto currentStartTime { std::chrono::duration_cast( @@ -101,9 +105,8 @@ void PeriodicTask::taskFunctionality() { if(terminateThread.load()) { break; } - for (ObjectList::iterator it = objectList.begin(); - it != objectList.end(); ++it) { - (*it)->performOperation(); + for (const auto& object: objectList) { + object->performOperation(); } if(not delayForInterval(¤tStartTime, periodChrono)) { sif::warning << "PeriodicTask: " << taskName << From 7b742a2e6775d8f2b5e8b0121d39a84d293cdd94 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Dec 2020 01:00:07 +0100 Subject: [PATCH 077/119] warning removed from osal --- osal/host/PeriodicTask.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osal/host/PeriodicTask.cpp b/osal/host/PeriodicTask.cpp index fb236963..5662c7cc 100644 --- a/osal/host/PeriodicTask.cpp +++ b/osal/host/PeriodicTask.cpp @@ -98,7 +98,7 @@ void PeriodicTask::taskFunctionality() { std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) }; - auto nextStartTime{ currentStartTime }; + auto nextStartTime { currentStartTime }; /* Enter the loop that defines the task behavior. */ for (;;) { @@ -109,8 +109,6 @@ void PeriodicTask::taskFunctionality() { object->performOperation(); } if(not delayForInterval(¤tStartTime, periodChrono)) { - sif::warning << "PeriodicTask: " << taskName << - " missed deadline!\n" << std::flush; if(deadlineMissedFunc != nullptr) { this->deadlineMissedFunc(); } From 558220643e05a6eaaad0fce5107c3a1c12eea9ae Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Dec 2020 01:01:31 +0100 Subject: [PATCH 078/119] small form improvements --- osal/host/FixedTimeslotTask.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osal/host/FixedTimeslotTask.cpp b/osal/host/FixedTimeslotTask.cpp index e78c974a..7000024e 100644 --- a/osal/host/FixedTimeslotTask.cpp +++ b/osal/host/FixedTimeslotTask.cpp @@ -115,8 +115,9 @@ void FixedTimeslotTask::taskFunctionality() { this->pollingSeqTable.executeAndAdvance(); if (not pollingSeqTable.slotFollowsImmediately()) { // we need to wait before executing the current slot - //this gives us the time to wait: - interval = chron_ms(this->pollingSeqTable.getIntervalToPreviousSlotMs()); + // this gives us the time to wait: + interval = chron_ms( + this->pollingSeqTable.getIntervalToPreviousSlotMs()); delayForInterval(¤tStartTime, interval); //TODO deadline missed check } From 2deb7037b0e58156a8fc04095b57c9eee0003395 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Dec 2020 01:07:40 +0100 Subject: [PATCH 079/119] todo added --- osal/host/FixedTimeslotTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osal/host/FixedTimeslotTask.cpp b/osal/host/FixedTimeslotTask.cpp index 7000024e..9e892bb5 100644 --- a/osal/host/FixedTimeslotTask.cpp +++ b/osal/host/FixedTimeslotTask.cpp @@ -46,7 +46,7 @@ FixedTimeslotTask::FixedTimeslotTask(const char *name, TaskPriority setPriority, << GetLastError() << std::endl; } #elif defined(LINUX) - // we can just copy and paste the code from linux here. + // TODO: we can just copy and paste the code from the linux OSAL here. #endif } From 464988ca61570bf6464d2e1e1228744659fff9c0 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Dec 2020 13:00:04 +0100 Subject: [PATCH 080/119] update --- datapoollocal/HasLocalDataPoolIF.h | 51 +- datapoollocal/LocalDataPoolManager.cpp | 805 ++++++++++++++------ datapoollocal/LocalDataPoolManager.h | 163 +++- datapoollocal/LocalPoolDataSetBase.cpp | 97 ++- datapoollocal/LocalPoolDataSetBase.h | 16 +- datapoollocal/LocalPoolObjectBase.cpp | 73 ++ datapoollocal/LocalPoolObjectBase.h | 63 ++ datapoollocal/LocalPoolVariable.h | 54 +- datapoollocal/LocalPoolVariable.tpp | 150 ++-- datapoollocal/LocalPoolVector.h | 68 +- datapoollocal/LocalPoolVector.tpp | 85 +-- datapoollocal/MarkChangedIF.h | 17 + datapoollocal/locPoolDefinitions.h | 93 +++ housekeeping/AcceptsHkPacketsIF.h | 1 + housekeeping/HousekeepingMessage.cpp | 57 +- housekeeping/HousekeepingMessage.h | 76 +- housekeeping/HousekeepingPacketUpdate.h | 51 +- housekeeping/PeriodicHousekeepingHelper.cpp | 3 +- 18 files changed, 1346 insertions(+), 577 deletions(-) create mode 100644 datapoollocal/LocalPoolObjectBase.cpp create mode 100644 datapoollocal/LocalPoolObjectBase.h create mode 100644 datapoollocal/MarkChangedIF.h create mode 100644 datapoollocal/locPoolDefinitions.h diff --git a/datapoollocal/HasLocalDataPoolIF.h b/datapoollocal/HasLocalDataPoolIF.h index f8f4ef4c..7f1f202e 100644 --- a/datapoollocal/HasLocalDataPoolIF.h +++ b/datapoollocal/HasLocalDataPoolIF.h @@ -1,6 +1,8 @@ #ifndef FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ #define FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ +#include "locPoolDefinitions.h" + #include "../datapool/PoolEntryIF.h" #include "../ipc/MessageQueueSenderIF.h" #include "../housekeeping/HousekeepingMessage.h" @@ -9,11 +11,8 @@ class LocalDataPoolManager; class LocalPoolDataSetBase; +class LocalPoolObjectBase; -/** - * @brief Type definition for local pool entries. - */ -using lp_id_t = uint32_t; using LocalDataPool = std::map; using LocalDataPoolMapIter = LocalDataPool::iterator; @@ -44,7 +43,8 @@ public: virtual~ HasLocalDataPoolIF() {}; static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF; - static constexpr lp_id_t NO_POOL_ID = 0xffffffff; + + static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID; virtual object_id_t getObjectId() const = 0; @@ -78,6 +78,47 @@ public: */ virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) = 0; + /** + * Similar to the function above, but used to get a local pool variable + * handle. This is only needed for update notifications, so it is not + * defined as abstract. + * @param localPoolId + * @return + */ + virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) { + sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden" + << ". Returning nullptr!" << std::endl; + return nullptr; + } + + /** + * @brief This function will be called by the manager if an update + * notification is received. + * @details + * Can be overriden by the child class to handle changed datasets. + * @param sid + * @param storeId If a snapshot was requested, data will be located inside + * the IPC store with this store ID. + */ + virtual void handleChangedDataset(sid_t sid, + store_address_t storeId = storeId::INVALID_STORE_ADDRESS) { + return; + } + + /** + * @brief This function will be called by the manager if an update + * notification is received. + * @details + * Can be overriden by the child class to handle changed pool IDs. + * @param sid + * @param storeId If a snapshot was requested, data will be located inside + * the IPC store with this store ID. + */ + virtual void handleChangedPoolVariable(lp_id_t poolId, + store_address_t storeId = storeId::INVALID_STORE_ADDRESS) { + return; + } + /* These function can be implemented by pool owner, as they are required * by the housekeeping message interface */ virtual ReturnValue_t addDataSet(sid_t sid) { diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index c23f183b..f649a362 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -1,22 +1,25 @@ #include "LocalDataPoolManager.h" +#include "LocalPoolObjectBase.h" #include "LocalPoolDataSetBase.h" +#include "../housekeeping/HousekeepingPacketUpdate.h" #include "../housekeeping/HousekeepingSetPacket.h" #include "../housekeeping/AcceptsHkPacketsIF.h" + +#include "../timemanager/CCSDSTime.h" #include "../ipc/MutexFactory.h" #include "../ipc/MutexHelper.h" #include "../ipc/QueueFactory.h" -#include "../objectmanager/frameworkObjects.h" #include #include object_id_t LocalDataPoolManager::defaultHkDestination = - objects::PUS_SERVICE_3_HOUSEKEEPING; + objects::PUS_SERVICE_3_HOUSEKEEPING; LocalDataPoolManager::LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQueueIF* queueToUse, bool appendValidityBuffer): - appendValidityBuffer(appendValidityBuffer) { + appendValidityBuffer(appendValidityBuffer) { if(owner == nullptr) { sif::error << "LocalDataPoolManager::LocalDataPoolManager: " << "Invalid supplied owner!" << std::endl; @@ -59,7 +62,7 @@ ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) { } else { sif::error << "LocalDataPoolManager::LocalDataPoolManager: " - << "Default HK destination object is invalid!" << std::endl; + << "Default HK destination object is invalid!" << std::endl; return HasReturnvaluesIF::RETURN_FAILED; } } @@ -74,23 +77,22 @@ ReturnValue_t LocalDataPoolManager::initializeAfterTaskCreation( } ReturnValue_t LocalDataPoolManager::initializeHousekeepingPoolEntriesOnce() { - if(not mapInitialized) { - ReturnValue_t result = owner->initializeLocalDataPool(localPoolMap, - *this); - if(result == HasReturnvaluesIF::RETURN_OK) { - mapInitialized = true; - } - return result; - } - sif::warning << "HousekeepingManager: The map should only be initialized " - << "once!" << std::endl; - return HasReturnvaluesIF::RETURN_OK; + if(not mapInitialized) { + ReturnValue_t result = owner->initializeLocalDataPool(localPoolMap, + *this); + if(result == HasReturnvaluesIF::RETURN_OK) { + mapInitialized = true; + } + return result; + } + sif::warning << "HousekeepingManager: The map should only be initialized " + << "once!" << std::endl; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t LocalDataPoolManager::performHkOperation() { + ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; for(auto& receiver: hkReceiversMap) { - //HkReceiver* receiver = &hkReceiversIter.second; - switch(receiver.reportingType) { case(ReportingType::PERIODIC): { if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { @@ -100,8 +102,16 @@ ReturnValue_t LocalDataPoolManager::performHkOperation() { performPeriodicHkGeneration(receiver); break; } + case(ReportingType::UPDATE_HK): { + handleHkUpdate(receiver, status); + break; + } + case(ReportingType::UPDATE_NOTIFICATION): { + handleNotificationUpdate(receiver, status); + break; + } case(ReportingType::UPDATE_SNAPSHOT): { - // check whether data has changed and send messages in case it has. + handleNotificationSnapshot(receiver, status); break; } default: @@ -109,95 +119,441 @@ ReturnValue_t LocalDataPoolManager::performHkOperation() { return HasReturnvaluesIF::RETURN_FAILED; } } + resetHkUpdateResetHelper(); + return status; +} + +ReturnValue_t LocalDataPoolManager::handleHkUpdate(HkReceiver& receiver, + ReturnValue_t& status) { + if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { + // Update packets shall only be generated from datasets. + return HasReturnvaluesIF::RETURN_FAILED; + } + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle( + receiver.dataId.sid); + if(dataSet->hasChanged()) { + // prepare and send update notification + ReturnValue_t result = generateHousekeepingPacket( + receiver.dataId.sid, dataSet, true); + if(result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + } + handleChangeResetLogic(receiver.dataType, receiver.dataId, + dataSet); return HasReturnvaluesIF::RETURN_OK; } +ReturnValue_t LocalDataPoolManager::handleNotificationUpdate( + HkReceiver& receiver, ReturnValue_t& status) { + MarkChangedIF* toReset = nullptr; + if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { + LocalPoolObjectBase* poolObj = owner->getPoolObjectHandle( + receiver.dataId.localPoolId); + if(poolObj == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + if(poolObj->hasChanged()) { + // prepare and send update notification. + CommandMessage notification; + HousekeepingMessage::setUpdateNotificationVariableCommand( + ¬ification, receiver.dataId.localPoolId); + ReturnValue_t result = hkQueue->sendMessage( + receiver.destinationQueue, ¬ification); + if(result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + toReset = poolObj; + } + + } + else { + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle( + receiver.dataId.sid); + if(dataSet == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + if(dataSet->hasChanged()) { + // prepare and send update notification + CommandMessage notification; + HousekeepingMessage::setUpdateNotificationSetCommand( + ¬ification, receiver.dataId.sid); + ReturnValue_t result = hkQueue->sendMessage( + receiver.destinationQueue, ¬ification); + if(result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + toReset = dataSet; + } + } + if(toReset != nullptr) { + handleChangeResetLogic(receiver.dataType, + receiver.dataId, toReset); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::handleNotificationSnapshot( + HkReceiver& receiver, ReturnValue_t& status) { + MarkChangedIF* toReset = nullptr; + // check whether data has changed and send messages in case it has. + if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { + LocalPoolObjectBase* poolObj = owner->getPoolObjectHandle( + receiver.dataId.localPoolId); + if(poolObj == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + if (not poolObj->hasChanged()) { + return HasReturnvaluesIF::RETURN_OK; + } + + // prepare and send update snapshot. + timeval now; + Clock::getClock_timeval(&now); + CCSDSTime::CDS_short cds; + CCSDSTime::convertToCcsds(&cds, &now); + HousekeepingPacketUpdate updatePacket(reinterpret_cast(&cds), + sizeof(cds), owner->getPoolObjectHandle( + receiver.dataId.localPoolId)); + + store_address_t storeId; + ReturnValue_t result = addUpdateToStore(updatePacket, storeId); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + CommandMessage notification; + HousekeepingMessage::setUpdateSnapshotVariableCommand(¬ification, + receiver.dataId.localPoolId, storeId); + result = hkQueue->sendMessage(receiver.destinationQueue, + ¬ification); + if (result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + toReset = poolObj; + } + else { + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle( + receiver.dataId.sid); + if(dataSet == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + if(not dataSet->hasChanged()) { + return HasReturnvaluesIF::RETURN_OK; + } + + // prepare and send update snapshot. + timeval now; + Clock::getClock_timeval(&now); + CCSDSTime::CDS_short cds; + CCSDSTime::convertToCcsds(&cds, &now); + HousekeepingPacketUpdate updatePacket(reinterpret_cast(&cds), + sizeof(cds), owner->getDataSetHandle(receiver.dataId.sid)); + + store_address_t storeId; + ReturnValue_t result = addUpdateToStore(updatePacket, storeId); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + CommandMessage notification; + HousekeepingMessage::setUpdateSnapshotSetCommand( + ¬ification, receiver.dataId.sid, storeId); + result = hkQueue->sendMessage(receiver.destinationQueue, ¬ification); + if(result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + toReset = dataSet; + + } + if(toReset != nullptr) { + handleChangeResetLogic(receiver.dataType, + receiver.dataId, toReset); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::addUpdateToStore( + HousekeepingPacketUpdate& updatePacket, store_address_t& storeId) { + size_t updatePacketSize = updatePacket.getSerializedSize(); + uint8_t *storePtr = nullptr; + ReturnValue_t result = ipcStore->getFreeElement(&storeId, + updatePacket.getSerializedSize(), &storePtr); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + size_t serializedSize = 0; + result = updatePacket.serialize(&storePtr, &serializedSize, + updatePacketSize, SerializeIF::Endianness::MACHINE); + return result;; +} + +void LocalDataPoolManager::handleChangeResetLogic( + DataType type, DataId dataId, MarkChangedIF* toReset) { + if(hkUpdateResetList == nullptr) { + // config error! + return; + } + + for(auto& changeInfo: *hkUpdateResetList) { + if(changeInfo.dataType != type) { + continue; + } + if((changeInfo.dataType == DataType::DATA_SET) and + (changeInfo.dataId.sid != dataId.sid)) { + continue; + } + if((changeInfo.dataType == DataType::LOCAL_POOL_VARIABLE) and + (changeInfo.dataId.localPoolId != dataId.localPoolId)) { + continue; + } + + if(changeInfo.updateCounter <= 1) { + toReset->setChanged(false); + } + if(changeInfo.currentUpdateCounter == 0) { + toReset->setChanged(false); + } + else { + changeInfo.currentUpdateCounter--; + } + return; + } +} + +void LocalDataPoolManager::resetHkUpdateResetHelper() { + if(hkUpdateResetList == nullptr) { + return; + } + + for(auto& changeInfo: *hkUpdateResetList) { + changeInfo.currentUpdateCounter = changeInfo.updateCounter; + } +} + ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(sid_t sid, - bool enableReporting, float collectionInterval, bool isDiagnostics, - object_id_t packetDestination) { - AcceptsHkPacketsIF* hkReceiverObject = - objectManager->get(packetDestination); - if(hkReceiverObject == nullptr) { - sif::error << "LocalDataPoolManager::subscribeForPeriodicPacket:" - << " Invalid receiver!"<< std::endl; - return HasReturnvaluesIF::RETURN_OK; - } + bool enableReporting, float collectionInterval, bool isDiagnostics, + object_id_t packetDestination) { + AcceptsHkPacketsIF* hkReceiverObject = + objectManager->get(packetDestination); + if(hkReceiverObject == nullptr) { + sif::error << "LocalDataPoolManager::subscribeForPeriodicPacket:" + << " Invalid receiver!"<< std::endl; + return HasReturnvaluesIF::RETURN_OK; + } - struct HkReceiver hkReceiver; - hkReceiver.dataId.sid = sid; - hkReceiver.reportingType = ReportingType::PERIODIC; - hkReceiver.destinationQueue = hkReceiverObject->getHkQueue(); + struct HkReceiver hkReceiver; + hkReceiver.dataId.sid = sid; + hkReceiver.reportingType = ReportingType::PERIODIC; + hkReceiver.dataType = DataType::DATA_SET; + hkReceiver.destinationQueue = hkReceiverObject->getHkQueue(); - LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); - if(dataSet != nullptr) { - dataSet->setReportingEnabled(enableReporting); - dataSet->setDiagnostic(isDiagnostics); - dataSet->initializePeriodicHelper(collectionInterval, - owner->getPeriodicOperationFrequency(), isDiagnostics); - } + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + if(dataSet != nullptr) { + dataSet->setReportingEnabled(enableReporting); + dataSet->setDiagnostic(isDiagnostics); + dataSet->initializePeriodicHelper(collectionInterval, + owner->getPeriodicOperationFrequency(), isDiagnostics); + } - hkReceiversMap.push_back(hkReceiver); - return HasReturnvaluesIF::RETURN_OK; + hkReceiversMap.push_back(hkReceiver); + return HasReturnvaluesIF::RETURN_OK; +} + + +ReturnValue_t LocalDataPoolManager::subscribeForUpdatePackets(sid_t sid, + bool isDiagnostics, bool reportingEnabled, + object_id_t packetDestination) { + AcceptsHkPacketsIF* hkReceiverObject = + objectManager->get(packetDestination); + if(hkReceiverObject == nullptr) { + sif::error << "LocalDataPoolManager::subscribeForPeriodicPacket:" + << " Invalid receiver!"<< std::endl; + return HasReturnvaluesIF::RETURN_OK; + } + + struct HkReceiver hkReceiver; + hkReceiver.dataId.sid = sid; + hkReceiver.reportingType = ReportingType::UPDATE_HK; + hkReceiver.dataType = DataType::DATA_SET; + hkReceiver.destinationQueue = hkReceiverObject->getHkQueue(); + + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + if(dataSet != nullptr) { + dataSet->setReportingEnabled(true); + dataSet->setDiagnostic(isDiagnostics); + } + + hkReceiversMap.push_back(hkReceiver); + + handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessages( + const uint32_t setId, object_id_t destinationObject, + MessageQueueId_t targetQueueId, bool generateSnapshot) { + struct HkReceiver hkReceiver; + hkReceiver.dataType = DataType::DATA_SET; + hkReceiver.dataId.sid = sid_t(this->getOwner()->getObjectId(), setId); + hkReceiver.destinationQueue = targetQueueId; + hkReceiver.objectId = destinationObject; + if(generateSnapshot) { + hkReceiver.reportingType = ReportingType::UPDATE_SNAPSHOT; + } + else { + hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION; + } + + hkReceiversMap.push_back(hkReceiver); + + handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::subscribeForVariableUpdateMessages( + const lp_id_t localPoolId, object_id_t destinationObject, + MessageQueueId_t targetQueueId, bool generateSnapshot) { + struct HkReceiver hkReceiver; + hkReceiver.dataType = DataType::LOCAL_POOL_VARIABLE; + hkReceiver.dataId.localPoolId = localPoolId; + hkReceiver.destinationQueue = targetQueueId; + hkReceiver.objectId = destinationObject; + if(generateSnapshot) { + hkReceiver.reportingType = ReportingType::UPDATE_SNAPSHOT; + } + else { + hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION; + } + + hkReceiversMap.push_back(hkReceiver); + + handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); + return HasReturnvaluesIF::RETURN_OK; +} + +void LocalDataPoolManager::handleHkUpdateResetListInsertion(DataType dataType, + DataId dataId) { + if(hkUpdateResetList == nullptr) { + hkUpdateResetList = new std::vector(); + } + + for(auto& updateResetStruct: *hkUpdateResetList) { + if(dataType == DataType::DATA_SET) { + if(updateResetStruct.dataId.sid == dataId.sid) { + updateResetStruct.updateCounter++; + updateResetStruct.currentUpdateCounter++; + return; + } + } + else { + if(updateResetStruct.dataId.localPoolId == dataId.localPoolId) { + updateResetStruct.updateCounter++; + updateResetStruct.currentUpdateCounter++; + return; + } + } + + } + HkUpdateResetHelper hkUpdateResetHelper; + hkUpdateResetHelper.currentUpdateCounter = 1; + hkUpdateResetHelper.updateCounter = 1; + hkUpdateResetHelper.dataType = dataType; + if(dataType == DataType::DATA_SET) { + hkUpdateResetHelper.dataId.sid = dataId.sid; + } + else { + hkUpdateResetHelper.dataId.localPoolId = dataId.localPoolId; + } + hkUpdateResetList->push_back(hkUpdateResetHelper); } ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage( - CommandMessage* message) { + CommandMessage* message) { Command_t command = message->getCommand(); sid_t sid = HousekeepingMessage::getSid(message); ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; switch(command) { + // Houskeeping interface handling. case(HousekeepingMessage::ENABLE_PERIODIC_DIAGNOSTICS_GENERATION): { - result = togglePeriodicGeneration(sid, true, true); - break; + result = togglePeriodicGeneration(sid, true, true); + break; } case(HousekeepingMessage::DISABLE_PERIODIC_DIAGNOSTICS_GENERATION): { - result = togglePeriodicGeneration(sid, false, true); - break; + result = togglePeriodicGeneration(sid, false, true); + break; } case(HousekeepingMessage::ENABLE_PERIODIC_HK_REPORT_GENERATION): { - result = togglePeriodicGeneration(sid, true, false); - break; + result = togglePeriodicGeneration(sid, true, false); + break; } case(HousekeepingMessage::DISABLE_PERIODIC_HK_REPORT_GENERATION): { - result = togglePeriodicGeneration(sid, false, false); - break; + result = togglePeriodicGeneration(sid, false, false); + break; } case(HousekeepingMessage::REPORT_DIAGNOSTICS_REPORT_STRUCTURES): - return generateSetStructurePacket(sid, true); + return generateSetStructurePacket(sid, true); case(HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): - return generateSetStructurePacket(sid, false); + return generateSetStructurePacket(sid, false); case(HousekeepingMessage::MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL): case(HousekeepingMessage::MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL): { - float newCollIntvl = 0; - HousekeepingMessage::getCollectionIntervalModificationCommand(message, - &newCollIntvl); - if(command == HousekeepingMessage:: - MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL) { - result = changeCollectionInterval(sid, newCollIntvl, true); - } - else { - result = changeCollectionInterval(sid, newCollIntvl, false); - } - break; + float newCollIntvl = 0; + HousekeepingMessage::getCollectionIntervalModificationCommand(message, + &newCollIntvl); + if(command == HousekeepingMessage:: + MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL) { + result = changeCollectionInterval(sid, newCollIntvl, true); + } + else { + result = changeCollectionInterval(sid, newCollIntvl, false); + } + break; } case(HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT): case(HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT): { - LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); - if(command == HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT - and dataSet->isDiagnostics()) { - return WRONG_HK_PACKET_TYPE; - } - else if(command == HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT - and not dataSet->isDiagnostics()) { - return WRONG_HK_PACKET_TYPE; - } - return generateHousekeepingPacket(HousekeepingMessage::getSid(message), - dataSet, true); + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + if(command == HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT + and dataSet->isDiagnostics()) { + return WRONG_HK_PACKET_TYPE; + } + else if(command == HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT + and not dataSet->isDiagnostics()) { + return WRONG_HK_PACKET_TYPE; + } + return generateHousekeepingPacket(HousekeepingMessage::getSid(message), + dataSet, true); + } + + // Notification handling. + case(HousekeepingMessage::UPDATE_NOTIFICATION_SET): { + owner->handleChangedDataset(sid); + return HasReturnvaluesIF::RETURN_OK; + } + case(HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE): { + lp_id_t locPoolId = HousekeepingMessage:: + getUpdateNotificationVariableCommand(message); + owner->handleChangedPoolVariable(locPoolId); + return HasReturnvaluesIF::RETURN_OK; + } + case(HousekeepingMessage::UPDATE_SNAPSHOT_SET): { + store_address_t storeId; + HousekeepingMessage::getUpdateSnapshotSetCommand(message, &storeId); + owner->handleChangedDataset(sid, storeId); + return HasReturnvaluesIF::RETURN_OK; + } + case(HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE): { + store_address_t storeId; + lp_id_t localPoolId = HousekeepingMessage:: + getUpdateSnapshotVariableCommand(message, &storeId); + owner->handleChangedPoolVariable(localPoolId, storeId); + return HasReturnvaluesIF::RETURN_OK; } default: @@ -205,30 +561,30 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage( } CommandMessage reply; - if(result != HasReturnvaluesIF::RETURN_OK) { - HousekeepingMessage::setHkRequestFailureReply(&reply, sid, result); - } - else { - HousekeepingMessage::setHkRequestSuccessReply(&reply, sid); - } - hkQueue->sendMessage(hkDestinationId, &reply); - return result; + if(result != HasReturnvaluesIF::RETURN_OK) { + HousekeepingMessage::setHkRequestFailureReply(&reply, sid, result); + } + else { + HousekeepingMessage::setHkRequestSuccessReply(&reply, sid); + } + hkQueue->sendMessage(hkDestinationId, &reply); + return result; } ReturnValue_t LocalDataPoolManager::printPoolEntry( - lp_id_t localPoolId) { - auto poolIter = localPoolMap.find(localPoolId); - if (poolIter == localPoolMap.end()) { - sif::debug << "HousekeepingManager::fechPoolEntry:" - << " Pool entry not found." << std::endl; - return POOL_ENTRY_NOT_FOUND; - } - poolIter->second->print(); - return HasReturnvaluesIF::RETURN_OK; + lp_id_t localPoolId) { + auto poolIter = localPoolMap.find(localPoolId); + if (poolIter == localPoolMap.end()) { + sif::debug << "HousekeepingManager::fechPoolEntry:" + << " Pool entry not found." << std::endl; + return POOL_ENTRY_NOT_FOUND; + } + poolIter->second->print(); + return HasReturnvaluesIF::RETURN_OK; } MutexIF* LocalDataPoolManager::getMutexHandle() { - return mutex; + return mutex; } HasLocalDataPoolIF* LocalDataPoolManager::getOwner() { @@ -236,51 +592,51 @@ HasLocalDataPoolIF* LocalDataPoolManager::getOwner() { } ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid, - LocalPoolDataSetBase* dataSet, bool forDownlink, - MessageQueueId_t destination) { - if(dataSet == nullptr) { - // Configuration error. - sif::warning << "HousekeepingManager::generateHousekeepingPacket:" - << " Set ID not found or dataset not assigned!" << std::endl; - return HasReturnvaluesIF::RETURN_FAILED; - } + LocalPoolDataSetBase* dataSet, bool forDownlink, + MessageQueueId_t destination) { + if(dataSet == nullptr) { + // Configuration error. + sif::warning << "HousekeepingManager::generateHousekeepingPacket:" + << " Set ID not found or dataset not assigned!" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } - store_address_t storeId; - HousekeepingPacketDownlink hkPacket(sid, dataSet); - size_t serializedSize = 0; - ReturnValue_t result = serializeHkPacketIntoStore(hkPacket, storeId, - forDownlink, &serializedSize); - if(result != HasReturnvaluesIF::RETURN_OK or serializedSize == 0) { - return result; - } + store_address_t storeId; + HousekeepingPacketDownlink hkPacket(sid, dataSet); + size_t serializedSize = 0; + ReturnValue_t result = serializeHkPacketIntoStore(hkPacket, storeId, + forDownlink, &serializedSize); + if(result != HasReturnvaluesIF::RETURN_OK or serializedSize == 0) { + return result; + } - // and now we set a HK message and send it the HK packet destination. - CommandMessage hkMessage; - if(dataSet->isDiagnostics()) { - HousekeepingMessage::setHkDiagnosticsReply(&hkMessage, sid, storeId); - } - else { - HousekeepingMessage::setHkReportReply(&hkMessage, sid, storeId); - } + // and now we set a HK message and send it the HK packet destination. + CommandMessage hkMessage; + if(dataSet->isDiagnostics()) { + HousekeepingMessage::setHkDiagnosticsReply(&hkMessage, sid, storeId); + } + else { + HousekeepingMessage::setHkReportReply(&hkMessage, sid, storeId); + } - if(hkQueue == nullptr) { - return QUEUE_OR_DESTINATION_NOT_SET; - } - if(destination == MessageQueueIF::NO_QUEUE) { - if(hkDestinationId == MessageQueueIF::NO_QUEUE) { - // error, all destinations invalid - return HasReturnvaluesIF::RETURN_FAILED; - } - destination = hkDestinationId; - } + if(hkQueue == nullptr) { + return QUEUE_OR_DESTINATION_NOT_SET; + } + if(destination == MessageQueueIF::NO_QUEUE) { + if(hkDestinationId == MessageQueueIF::NO_QUEUE) { + // error, all destinations invalid + return HasReturnvaluesIF::RETURN_FAILED; + } + destination = hkDestinationId; + } - return hkQueue->sendMessage(destination, &hkMessage); + return hkQueue->sendMessage(destination, &hkMessage); } ReturnValue_t LocalDataPoolManager::serializeHkPacketIntoStore( HousekeepingPacketDownlink& hkPacket, store_address_t& storeId, bool forDownlink, - size_t* serializedSize) { + size_t* serializedSize) { uint8_t* dataPtr = nullptr; const size_t maxSize = hkPacket.getSerializedSize(); ReturnValue_t result = ipcStore->getFreeElement(&storeId, @@ -290,8 +646,8 @@ ReturnValue_t LocalDataPoolManager::serializeHkPacketIntoStore( } if(forDownlink) { - return hkPacket.serialize(&dataPtr, serializedSize, maxSize, - SerializeIF::Endianness::BIG); + return hkPacket.serialize(&dataPtr, serializedSize, maxSize, + SerializeIF::Endianness::BIG); } return hkPacket.serialize(&dataPtr, serializedSize, maxSize, SerializeIF::Endianness::MACHINE); @@ -303,125 +659,124 @@ void LocalDataPoolManager::setNonDiagnosticIntervalFactor( } void LocalDataPoolManager::performPeriodicHkGeneration(HkReceiver& receiver) { - sid_t sid = receiver.dataId.sid; - LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); - if(not dataSet->getReportingEnabled()) { - return; - } + sid_t sid = receiver.dataId.sid; + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + if(not dataSet->getReportingEnabled()) { + return; + } - if(dataSet->periodicHelper == nullptr) { - // Configuration error. - return; - } + if(dataSet->periodicHelper == nullptr) { + // Configuration error. + return; + } - if(not dataSet->periodicHelper->checkOpNecessary()) { - return; - } + if(not dataSet->periodicHelper->checkOpNecessary()) { + return; + } - ReturnValue_t result = generateHousekeepingPacket( - sid, dataSet, true); - if(result != HasReturnvaluesIF::RETURN_OK) { - // configuration error - sif::debug << "LocalDataPoolManager::performHkOperation:" - << "0x" << std::hex << std::setfill('0') << std::setw(8) - << owner->getObjectId() << " Error generating " - << "HK packet" << std::setfill(' ') << std::dec << std::endl; - } + ReturnValue_t result = generateHousekeepingPacket( + sid, dataSet, true); + if(result != HasReturnvaluesIF::RETURN_OK) { + // configuration error + sif::debug << "LocalDataPoolManager::performHkOperation:" + << "0x" << std::hex << std::setfill('0') << std::setw(8) + << owner->getObjectId() << " Error generating " + << "HK packet" << std::setfill(' ') << std::dec << std::endl; + } } ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid, - bool enable, bool isDiagnostics) { - LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); - if((dataSet->isDiagnostics() and not isDiagnostics) or - (not dataSet->isDiagnostics() and isDiagnostics)) { - return WRONG_HK_PACKET_TYPE; - } + bool enable, bool isDiagnostics) { + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + if((dataSet->isDiagnostics() and not isDiagnostics) or + (not dataSet->isDiagnostics() and isDiagnostics)) { + return WRONG_HK_PACKET_TYPE; + } - if((dataSet->getReportingEnabled() and enable) or - (not dataSet->getReportingEnabled() and not enable)) { - return REPORTING_STATUS_UNCHANGED; - } + if((dataSet->getReportingEnabled() and enable) or + (not dataSet->getReportingEnabled() and not enable)) { + return REPORTING_STATUS_UNCHANGED; + } - dataSet->setReportingEnabled(enable); - return HasReturnvaluesIF::RETURN_OK; + dataSet->setReportingEnabled(enable); + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid, - float newCollectionInterval, bool isDiagnostics) { - LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); - bool targetIsDiagnostics = dataSet->isDiagnostics(); - if((targetIsDiagnostics and not isDiagnostics) or - (not targetIsDiagnostics and isDiagnostics)) { - return WRONG_HK_PACKET_TYPE; - } + float newCollectionInterval, bool isDiagnostics) { + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + bool targetIsDiagnostics = dataSet->isDiagnostics(); + if((targetIsDiagnostics and not isDiagnostics) or + (not targetIsDiagnostics and isDiagnostics)) { + return WRONG_HK_PACKET_TYPE; + } - if(dataSet->periodicHelper == nullptr) { - // config error - return PERIODIC_HELPER_INVALID; - } + if(dataSet->periodicHelper == nullptr) { + // config error + return PERIODIC_HELPER_INVALID; + } - dataSet->periodicHelper->changeCollectionInterval(newCollectionInterval); - return HasReturnvaluesIF::RETURN_OK; + dataSet->periodicHelper->changeCollectionInterval(newCollectionInterval); + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid, - bool isDiagnostics) { + bool isDiagnostics) { // Get and check dataset first. - LocalPoolDataSetBase* dataSet = dynamic_cast( - owner->getDataSetHandle(sid)); - if(dataSet == nullptr) { - sif::warning << "HousekeepingManager::generateHousekeepingPacket:" - << " Set ID not found" << std::endl; - return HasReturnvaluesIF::RETURN_FAILED; - } + LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + if(dataSet == nullptr) { + sif::warning << "HousekeepingManager::generateHousekeepingPacket:" + << " Set ID not found" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } - bool targetIsDiagnostics = dataSet->isDiagnostics(); - if((targetIsDiagnostics and not isDiagnostics) or - (not targetIsDiagnostics and isDiagnostics)) { - return WRONG_HK_PACKET_TYPE; - } + bool targetIsDiagnostics = dataSet->isDiagnostics(); + if((targetIsDiagnostics and not isDiagnostics) or + (not targetIsDiagnostics and isDiagnostics)) { + return WRONG_HK_PACKET_TYPE; + } - bool valid = dataSet->isValid(); - bool reportingEnabled = dataSet->getReportingEnabled(); - float collectionInterval = - dataSet->periodicHelper->getCollectionIntervalInSeconds(); + bool valid = dataSet->isValid(); + bool reportingEnabled = dataSet->getReportingEnabled(); + float collectionInterval = + dataSet->periodicHelper->getCollectionIntervalInSeconds(); - // Generate set packet which can be serialized. - HousekeepingSetPacket setPacket = HousekeepingSetPacket(sid, - reportingEnabled, valid, collectionInterval, dataSet); - size_t expectedSize = setPacket.getSerializedSize(); - uint8_t* storePtr = nullptr; - store_address_t storeId; - ReturnValue_t result = ipcStore->getFreeElement(&storeId, - expectedSize,&storePtr); - if(result != HasReturnvaluesIF::RETURN_OK) { - sif::error << "HousekeepingManager::generateHousekeepingPacket: " - << "Could not get free element from IPC store." << std::endl; - return result; - } + // Generate set packet which can be serialized. + HousekeepingSetPacket setPacket(sid, + reportingEnabled, valid, collectionInterval, dataSet); + size_t expectedSize = setPacket.getSerializedSize(); + uint8_t* storePtr = nullptr; + store_address_t storeId; + ReturnValue_t result = ipcStore->getFreeElement(&storeId, + expectedSize,&storePtr); + if(result != HasReturnvaluesIF::RETURN_OK) { + sif::error << "HousekeepingManager::generateHousekeepingPacket: " + << "Could not get free element from IPC store." << std::endl; + return result; + } - // Serialize set packet into store. - size_t size = 0; - result = setPacket.serialize(&storePtr, &size, expectedSize, - SerializeIF::Endianness::BIG); - if(expectedSize != size) { - sif::error << "HousekeepingManager::generateSetStructurePacket: " - << "Expected size is not equal to serialized size" << std::endl; - } + // Serialize set packet into store. + size_t size = 0; + result = setPacket.serialize(&storePtr, &size, expectedSize, + SerializeIF::Endianness::BIG); + if(expectedSize != size) { + sif::error << "HousekeepingManager::generateSetStructurePacket: " + << "Expected size is not equal to serialized size" << std::endl; + } - // Send structure reporting reply. - CommandMessage reply; - if(isDiagnostics) { - HousekeepingMessage::setDiagnosticsStuctureReportReply(&reply, - sid, storeId); - } - else { - HousekeepingMessage::setHkStuctureReportReply(&reply, - sid, storeId); - } + // Send structure reporting reply. + CommandMessage reply; + if(isDiagnostics) { + HousekeepingMessage::setDiagnosticsStuctureReportReply(&reply, + sid, storeId); + } + else { + HousekeepingMessage::setHkStuctureReportReply(&reply, + sid, storeId); + } - hkQueue->reply(&reply); - return result; + hkQueue->reply(&reply); + return result; } diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h index 779e3050..95d48303 100644 --- a/datapoollocal/LocalDataPoolManager.h +++ b/datapoollocal/LocalDataPoolManager.h @@ -20,16 +20,23 @@ namespace Factory { void setStaticFrameworkObjectIds(); } -class LocalDataSetBase; - +class LocalPoolDataSetBase; +class HousekeepingPacketUpdate; /** * @brief This class is the managing instance for the local data pool. * @details * The actual data pool structure is a member of this class. Any class which - * has a local data pool shall have this class as a member and implement + * has a local data pool shall have this manager class as a member and implement * the HasLocalDataPoolIF. * + * The manager offers some adaption points and functions which can be used + * by the owning class to simplify data handling significantly. + * + * Please ensure that both initialize and initializeAfterTaskCreation are + * called at some point by the owning class in the respective functions of the + * same name! + * * Users of the data pool use the helper classes LocalDataSet, * LocalPoolVariable and LocalPoolVector to access pool entries in * a thread-safe and efficient way. @@ -41,10 +48,8 @@ class LocalDataSetBase; * @author R. Mueller */ class LocalDataPoolManager { - template - friend class LocalPoolVar; - template - friend class LocalPoolVector; + template friend class LocalPoolVar; + template friend class LocalPoolVector; friend class LocalPoolDataSetBase; friend void (Factory::setStaticFrameworkObjectIds)(); public: @@ -67,14 +72,16 @@ public: * initialize() has to be called in any case before using the object! * @param owner * @param queueToUse - * @param appendValidityBuffer + * @param appendValidityBuffer Specify whether a buffer containing the + * validity state is generated when serializing or deserializing packets. */ LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQueueIF* queueToUse, bool appendValidityBuffer = true); virtual~ LocalDataPoolManager(); /** - * Assigns the queue to use. + * Assigns the queue to use. Make sure to call this in the #initialize + * function of the owner. * @param queueToUse * @param nonDiagInvlFactor See #setNonDiagnosticIntervalFactor doc * @return @@ -84,27 +91,88 @@ public: /** * Initializes the map by calling the map initialization function and * setting the periodic factor for non-diagnostic packets. - * Don't forget to call this, otherwise the map will be invalid! + * Don't forget to call this in the #initializeAfterTaskCreation call of + * the owner, otherwise the map will be invalid! * @param nonDiagInvlFactor * @return */ - ReturnValue_t initializeAfterTaskCreation(uint8_t nonDiagInvlFactor = 5); + ReturnValue_t initializeAfterTaskCreation( + uint8_t nonDiagInvlFactor = 5); /** - * This should be called in the periodic handler of the owner. + * @brief This should be called in the periodic handler of the owner. + * @details + * This in generally called in the #performOperation function of the owner. * It performs all the periodic functionalities of the data pool manager, * for example generating periodic HK packets. + * Marked virtual as an adaption point for custom data pool managers. * @return */ - ReturnValue_t performHkOperation(); + virtual ReturnValue_t performHkOperation(); /** + * @brief Subscribe for the generation of periodic packets. + * @details + * This subscription mechanism will generally be used by the data creator + * to generate housekeeping packets which are downlinked directly. * @return */ ReturnValue_t subscribeForPeriodicPacket(sid_t sid, bool enableReporting, float collectionInterval, bool isDiagnostics, object_id_t packetDestination = defaultHkDestination); + /** + * @brief Subscribe for the generation of packets if the dataset + * is marked as changed. + * @details + * This subscription mechanism will generally be used by the data creator. + * @param sid + * @param isDiagnostics + * @param packetDestination + * @return + */ + ReturnValue_t subscribeForUpdatePackets(sid_t sid, bool reportingEnabled, + bool isDiagnostics, + object_id_t packetDestination = defaultHkDestination); + + /** + * @brief Subscribe for a notification message which will be sent + * if a dataset has changed. + * @details + * This subscription mechanism will generally be used internally by + * other software components. + * @param setId Set ID of the set to receive update messages from. + * @param destinationObject + * @param targetQueueId + * @param generateSnapshot If this is set to true, a copy of the current + * data with a timestamp will be generated and sent via message. + * Otherwise, only an notification message is sent. + * @return + */ + ReturnValue_t subscribeForSetUpdateMessages(const uint32_t setId, + object_id_t destinationObject, + MessageQueueId_t targetQueueId, + bool generateSnapshot); + + /** + * @brief Subscribe for an notification message which will be sent if a + * pool variable has changed. + * @details + * This subscription mechanism will generally be used internally by + * other software components. + * @param localPoolId Pool ID of the pool variable + * @param destinationObject + * @param targetQueueId + * @param generateSnapshot If this is set to true, a copy of the current + * data with a timestamp will be generated and sent via message. + * Otherwise, only an notification message is sent. + * @return + */ + ReturnValue_t subscribeForVariableUpdateMessages(const lp_id_t localPoolId, + object_id_t destinationObject, + MessageQueueId_t targetQueueId, + bool generateSnapshot); + /** * Non-Diagnostics packets usually have a lower minimum sampling frequency * than diagnostic packets. @@ -116,6 +184,19 @@ public: */ void setNonDiagnosticIntervalFactor(uint8_t nonDiagInvlFactor); + /** + * @brief The manager is also able to handle housekeeping messages. + * @details + * This most commonly is used to handle messages for the housekeeping + * interface, but the manager is also able to handle update notifications + * and calls a special function which can be overriden by a child class + * to handle data set or pool variable updates. This is relevant + * for classes like controllers which have their own local datapool + * but pull their data from other local datapools. + * @param message + * @return + */ + virtual ReturnValue_t handleHousekeepingMessage(CommandMessage* message); /** * Generate a housekeeping packet with a given SID. @@ -126,16 +207,6 @@ public: LocalPoolDataSetBase* dataSet, bool forDownlink, MessageQueueId_t destination = MessageQueueIF::NO_QUEUE); - ReturnValue_t handleHousekeepingMessage(CommandMessage* message); - - /** - * This function is used to fill the local data pool map with pool - * entries. It should only be called once by the pool owner. - * @param localDataPoolMap - * @return - */ - ReturnValue_t initializeHousekeepingPoolEntriesOnce(); - HasLocalDataPoolIF* getOwner(); ReturnValue_t printPoolEntry(lp_id_t localPoolId); @@ -194,17 +265,18 @@ private: static object_id_t defaultHkDestination; MessageQueueId_t hkDestinationId = MessageQueueIF::NO_QUEUE; + union DataId { + DataId(): sid() {}; + sid_t sid; + lp_id_t localPoolId; + }; + /** The data pool manager will keep an internal map of HK receivers. */ struct HkReceiver { /** Object ID of receiver */ object_id_t objectId = objects::NO_OBJECT; DataType dataType = DataType::DATA_SET; - union DataId { - DataId(): sid() {}; - sid_t sid; - lp_id_t localPoolId; - }; DataId dataId; ReportingType reportingType = ReportingType::PERIODIC; @@ -216,6 +288,17 @@ private: HkReceivers hkReceiversMap; + struct HkUpdateResetHelper { + DataType dataType = DataType::DATA_SET; + DataId dataId; + uint8_t updateCounter; + uint8_t currentUpdateCounter; + }; + + using HkUpdateResetList = std::vector; + // Will only be created when needed. + HkUpdateResetList* hkUpdateResetList = nullptr; + /** This is the map holding the actual data. Should only be initialized * once ! */ bool mapInitialized = false; @@ -234,7 +317,7 @@ private: StorageManagerIF* ipcStore = nullptr; /** * Get the pointer to the mutex. Can be used to lock the data pool - * eternally. Use with care and don't forget to unlock locked mutexes! + * externally. Use with care and don't forget to unlock locked mutexes! * For now, only friend classes can accss this function. * @return */ @@ -255,6 +338,14 @@ private: template ReturnValue_t fetchPoolEntry(lp_id_t localPoolId, PoolEntry **poolEntry); + /** + * This function is used to fill the local data pool map with pool + * entries. It should only be called once by the pool owner. + * @param localDataPoolMap + * @return + */ + ReturnValue_t initializeHousekeepingPoolEntriesOnce(); + ReturnValue_t serializeHkPacketIntoStore( HousekeepingPacketDownlink& hkPacket, store_address_t& storeId, bool forDownlink, size_t* serializedSize); @@ -265,6 +356,20 @@ private: ReturnValue_t changeCollectionInterval(sid_t sid, float newCollectionInterval, bool isDiagnostics); ReturnValue_t generateSetStructurePacket(sid_t sid, bool isDiagnostics); + + void handleHkUpdateResetListInsertion(DataType dataType, DataId dataId); + void handleChangeResetLogic(DataType type, + DataId dataId, MarkChangedIF* toReset); + void resetHkUpdateResetHelper(); + + ReturnValue_t handleHkUpdate(HkReceiver& hkReceiver, + ReturnValue_t& status); + ReturnValue_t handleNotificationUpdate(HkReceiver& hkReceiver, + ReturnValue_t& status); + ReturnValue_t handleNotificationSnapshot(HkReceiver& hkReceiver, + ReturnValue_t& status); + ReturnValue_t addUpdateToStore(HousekeepingPacketUpdate& updatePacket, + store_address_t& storeId); }; diff --git a/datapoollocal/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp index da0a86b2..640956db 100644 --- a/datapoollocal/LocalPoolDataSetBase.cpp +++ b/datapoollocal/LocalPoolDataSetBase.cpp @@ -7,22 +7,24 @@ #include LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner, - uint32_t setId, PoolVariableIF** registeredVariablesArray, - const size_t maxNumberOfVariables, bool noPeriodicHandling): - PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { - if(hkOwner == nullptr) { - // Configuration error. - sif::error << "LocalPoolDataSetBase::LocalPoolDataSetBase: Owner " - << "invalid!" << std::endl; - return; - } + uint32_t setId, PoolVariableIF** registeredVariablesArray, + const size_t maxNumberOfVariables, bool noPeriodicHandling): + PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { + if(hkOwner == nullptr) { + // Configuration error. + sif::error << "LocalPoolDataSetBase::LocalPoolDataSetBase: Owner " + << "invalid!" << std::endl; + return; + } hkManager = hkOwner->getHkManagerHandle(); this->sid.objectId = hkOwner->getObjectId(); this->sid.ownerSetId = setId; + mutex = MutexFactory::instance()->createMutex(); + // Data creators get a periodic helper for periodic HK data generation. if(not noPeriodicHandling) { - periodicHelper = new PeriodicHousekeepingHelper(this); + periodicHelper = new PeriodicHousekeepingHelper(this); } } @@ -33,21 +35,23 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, HasLocalDataPoolIF* hkOwner = objectManager->get( sid.objectId); if(hkOwner == nullptr) { - // Configuration error. + // Configuration error. sif::error << "LocalPoolDataSetBase::LocalPoolDataSetBase: Owner " - << "invalid!" << std::endl; + << "invalid!" << std::endl; return; } hkManager = hkOwner->getHkManagerHandle(); this->sid = sid; + + mutex = MutexFactory::instance()->createMutex(); } LocalPoolDataSetBase::~LocalPoolDataSetBase() { } ReturnValue_t LocalPoolDataSetBase::lockDataPool(uint32_t timeoutMs) { - MutexIF* mutex = hkManager->getMutexHandle(); - return mutex->lockMutex(MutexIF::TimeoutType::WAITING, timeoutMs); + MutexIF* mutex = hkManager->getMutexHandle(); + return mutex->lockMutex(MutexIF::TimeoutType::WAITING, timeoutMs); } ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer, @@ -77,6 +81,10 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer return result; } } + + if(*size + validityMaskSize > maxSize) { + return SerializeIF::BUFFER_TOO_SHORT; + } // copy validity buffer to end std::memcpy(*buffer, validityMask, validityMaskSize); *size += validityMaskSize; @@ -89,14 +97,18 @@ ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer( ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; for (uint16_t count = 0; count < fillCount; count++) { result = registeredVariables[count]->deSerialize(buffer, size, - streamEndianness); + streamEndianness); if(result != HasReturnvaluesIF::RETURN_OK) { return result; } } + + if(*size < std::ceil(static_cast(fillCount) / 8.0)) { + return SerializeIF::STREAM_TOO_SHORT; + } + uint8_t validBufferIndex = 0; uint8_t validBufferIndexBit = 0; - // could be made more efficient but make it work first for (uint16_t count = 0; count < fillCount; count++) { // set validity buffer here. bool nextVarValid = this->bitGetter(*buffer + @@ -113,9 +125,10 @@ ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer( } return result; } + ReturnValue_t LocalPoolDataSetBase::unlockDataPool() { - MutexIF* mutex = hkManager->getMutexHandle(); - return mutex->unlockMutex(); + MutexIF* mutex = hkManager->getMutexHandle(); + return mutex->unlockMutex(); } ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t** buffer, @@ -192,7 +205,7 @@ ReturnValue_t LocalPoolDataSetBase::serialize(uint8_t **buffer, size_t *size, void LocalPoolDataSetBase::bitSetter(uint8_t* byte, uint8_t position) const { if(position > 7) { sif::debug << "Pool Raw Access: Bit setting invalid position" - << std::endl; + << std::endl; return; } uint8_t shiftNumber = position + (7 - 2 * position); @@ -200,45 +213,49 @@ void LocalPoolDataSetBase::bitSetter(uint8_t* byte, uint8_t position) const { } void LocalPoolDataSetBase::setDiagnostic(bool isDiagnostics) { - this->diagnostic = isDiagnostics; + this->diagnostic = isDiagnostics; } bool LocalPoolDataSetBase::isDiagnostics() const { - return diagnostic; + return diagnostic; } void LocalPoolDataSetBase::setReportingEnabled(bool reportingEnabled) { - this->reportingEnabled = reportingEnabled; + this->reportingEnabled = reportingEnabled; } bool LocalPoolDataSetBase::getReportingEnabled() const { - return reportingEnabled; + return reportingEnabled; } void LocalPoolDataSetBase::initializePeriodicHelper( - float collectionInterval, dur_millis_t minimumPeriodicInterval, - bool isDiagnostics, uint8_t nonDiagIntervalFactor) { - periodicHelper->initialize(collectionInterval, minimumPeriodicInterval, - isDiagnostics, nonDiagIntervalFactor); + float collectionInterval, dur_millis_t minimumPeriodicInterval, + bool isDiagnostics, uint8_t nonDiagIntervalFactor) { + periodicHelper->initialize(collectionInterval, minimumPeriodicInterval, + isDiagnostics, nonDiagIntervalFactor); } void LocalPoolDataSetBase::setChanged(bool changed) { - this->changed = changed; + // TODO: Make this configurable? + MutexHelper(mutex, MutexIF::TimeoutType::WAITING, 20); + this->changed = changed; } -bool LocalPoolDataSetBase::isChanged() const { - return changed; +bool LocalPoolDataSetBase::hasChanged() const { + // TODO: Make this configurable? + MutexHelper(mutex, MutexIF::TimeoutType::WAITING, 20); + return changed; } sid_t LocalPoolDataSetBase::getSid() const { - return sid; + return sid; } bool LocalPoolDataSetBase::bitGetter(const uint8_t* byte, - uint8_t position) const { + uint8_t position) const { if(position > 7) { sif::debug << "Pool Raw Access: Bit setting invalid position" - << std::endl; + << std::endl; return false; } uint8_t shiftNumber = position + (7 - 2 * position); @@ -246,14 +263,16 @@ bool LocalPoolDataSetBase::bitGetter(const uint8_t* byte, } bool LocalPoolDataSetBase::isValid() const { + MutexHelper(mutex, MutexIF::TimeoutType::WAITING, 5); return this->valid; } void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) { - if(setEntriesRecursively) { - for(size_t idx = 0; idx < this->getFillCount(); idx++) { - registeredVariables[idx] -> setValid(valid); - } - } - this->valid = valid; + MutexHelper(mutex, MutexIF::TimeoutType::WAITING, 5); + if(setEntriesRecursively) { + for(size_t idx = 0; idx < this->getFillCount(); idx++) { + registeredVariables[idx] -> setValid(valid); + } + } + this->valid = valid; } diff --git a/datapoollocal/LocalPoolDataSetBase.h b/datapoollocal/LocalPoolDataSetBase.h index d00af992..aa155bf1 100644 --- a/datapoollocal/LocalPoolDataSetBase.h +++ b/datapoollocal/LocalPoolDataSetBase.h @@ -2,6 +2,8 @@ #define FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_ #include "HasLocalDataPoolIF.h" +#include "MarkChangedIF.h" + #include "../datapool/DataSetIF.h" #include "../datapool/PoolDataSetBase.h" #include "../serialize/SerializeIF.h" @@ -39,7 +41,8 @@ class PeriodicHousekeepingHelper; * * @ingroup data_pool */ -class LocalPoolDataSetBase: public PoolDataSetBase { +class LocalPoolDataSetBase: public PoolDataSetBase, + public MarkChangedIF { friend class LocalDataPoolManager; friend class PeriodicHousekeepingHelper; public: @@ -109,18 +112,23 @@ public: uint8_t getLocalPoolIdsSerializedSize(bool serializeFillCount = true) const; /** - * Set the dataset valid or invalid + * Set the dataset valid or invalid. These calls are mutex protected. * @param setEntriesRecursively * If this is true, all contained datasets will also be set recursively. */ void setValidity(bool valid, bool setEntriesRecursively); bool isValid() const override; - void setChanged(bool changed); - bool isChanged() const; + /** + * These calls are mutex protected. + * @param changed + */ + void setChanged(bool changed) override; + bool hasChanged() const override; protected: sid_t sid; + MutexIF* mutex = nullptr; bool diagnostic = false; void setDiagnostic(bool diagnostics); diff --git a/datapoollocal/LocalPoolObjectBase.cpp b/datapoollocal/LocalPoolObjectBase.cpp new file mode 100644 index 00000000..b4d0e306 --- /dev/null +++ b/datapoollocal/LocalPoolObjectBase.cpp @@ -0,0 +1,73 @@ +#include "LocalPoolObjectBase.h" + +LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, + HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, + pool_rwm_t setReadWriteMode): localPoolId(poolId), + readWriteMode(setReadWriteMode) { + if(poolId == PoolVariableIF::NO_PARAMETER) { + sif::warning << "LocalPoolVar::LocalPoolVar: 0 passed as pool ID, " + << "which is the NO_PARAMETER value!" << std::endl; + } + if(hkOwner == nullptr) { + sif::error << "LocalPoolVar::LocalPoolVar: The supplied pool " + << "owner is a invalid!" << std::endl; + return; + } + hkManager = hkOwner->getHkManagerHandle(); + if (dataSet != nullptr) { + dataSet->registerVariable(this); + } +} + +LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, + DataSetIF *dataSet, pool_rwm_t setReadWriteMode): localPoolId(poolId), + readWriteMode(setReadWriteMode) { + if(poolId == PoolVariableIF::NO_PARAMETER) { + sif::warning << "LocalPoolVar::LocalPoolVar: 0 passed as pool ID, " + << "which is the NO_PARAMETER value!" << std::endl; + } + HasLocalDataPoolIF* hkOwner = + objectManager->get(poolOwner); + if(hkOwner == nullptr) { + sif::error << "LocalPoolVariable: The supplied pool owner did not " + << "implement the correct interface" + << " HasLocalDataPoolIF!" << std::endl; + return; + } + hkManager = hkOwner->getHkManagerHandle(); + if(dataSet != nullptr) { + dataSet->registerVariable(this); + } +} + +pool_rwm_t LocalPoolObjectBase::getReadWriteMode() const { + return readWriteMode; +} + +bool LocalPoolObjectBase::isValid() const { + return valid; +} + +void LocalPoolObjectBase::setValid(bool valid) { + this->valid = valid; +} + +lp_id_t LocalPoolObjectBase::getDataPoolId() const { + return localPoolId; +} + +void LocalPoolObjectBase::setDataPoolId(lp_id_t poolId) { + this->localPoolId = poolId; +} + +void LocalPoolObjectBase::setChanged(bool changed) { + this->changed = changed; +} + +bool LocalPoolObjectBase::hasChanged() const { + return changed; +} + +void LocalPoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) { + this->readWriteMode = newReadWriteMode; +} diff --git a/datapoollocal/LocalPoolObjectBase.h b/datapoollocal/LocalPoolObjectBase.h new file mode 100644 index 00000000..7165fc24 --- /dev/null +++ b/datapoollocal/LocalPoolObjectBase.h @@ -0,0 +1,63 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_ + +#include "MarkChangedIF.h" +#include "../datapoollocal/LocalDataPoolManager.h" +#include "../datapool/PoolVariableIF.h" + + +class LocalPoolObjectBase: public PoolVariableIF, + public HasReturnvaluesIF, + public MarkChangedIF { +public: + LocalPoolObjectBase(lp_id_t poolId, + HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, + pool_rwm_t setReadWriteMode); + + LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, + DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + + void setReadWriteMode(pool_rwm_t newReadWriteMode); + pool_rwm_t getReadWriteMode() const; + + bool isValid() const override; + void setValid(bool valid) override; + + void setChanged(bool changed) override; + bool hasChanged() const override; + + lp_id_t getDataPoolId() const override; + void setDataPoolId(lp_id_t poolId); + +protected: + /** + * @brief To access the correct data pool entry on read and commit calls, + * the data pool id is stored. + */ + uint32_t localPoolId = PoolVariableIF::NO_PARAMETER; + /** + * @brief The valid information as it was stored in the data pool + * is copied to this attribute. + */ + bool valid = false; + + /** + * @brief A local pool variable can be marked as changed. + */ + bool changed = false; + + /** + * @brief The information whether the class is read-write or + * read-only is stored here. + */ + ReadWriteMode_t readWriteMode = pool_rwm_t::VAR_READ_WRITE; + + //! @brief Pointer to the class which manages the HK pool. + LocalDataPoolManager* hkManager; + +}; + + + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_ */ diff --git a/datapoollocal/LocalPoolVariable.h b/datapoollocal/LocalPoolVariable.h index ec5c8cd1..c18d5443 100644 --- a/datapoollocal/LocalPoolVariable.h +++ b/datapoollocal/LocalPoolVariable.h @@ -1,6 +1,7 @@ #ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ #define FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ +#include "LocalPoolObjectBase.h" #include "HasLocalDataPoolIF.h" #include "LocalDataPoolManager.h" @@ -21,7 +22,7 @@ * @ingroup data_pool */ template -class LocalPoolVar: public PoolVariableIF, HasReturnvaluesIF { +class LocalPoolVar: public LocalPoolObjectBase { public: //! Default ctor is forbidden. LocalPoolVar() = delete; @@ -42,7 +43,7 @@ public: * If nullptr, the variable is not registered. * @param setReadWriteMode Specify the read-write mode of the pool variable. */ - LocalPoolVar(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, + LocalPoolVar(HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr, pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); @@ -63,9 +64,17 @@ public: * @param setReadWriteMode Specify the read-write mode of the pool variable. * */ - LocalPoolVar(lp_id_t poolId, object_id_t poolOwner, + LocalPoolVar(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr, pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + /** + * Variation which takes the global unique identifier of a pool variable. + * @param globalPoolId + * @param dataSet + * @param setReadWriteMode + */ + LocalPoolVar(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); virtual~ LocalPoolVar() {}; @@ -76,15 +85,6 @@ public: */ T value = 0; - pool_rwm_t getReadWriteMode() const override; - - lp_id_t getDataPoolId() const override; - void setDataPoolId(lp_id_t poolId); - - bool isValid() const override; - void setValid(bool validity) override; - uint8_t getValid() const; - ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize, SerializeIF::Endianness streamEndianness) const override; virtual size_t getSerializedSize() const override; @@ -118,7 +118,25 @@ public: ReturnValue_t commit(dur_millis_t lockTimeout = MutexIF::BLOCKING) override; - LocalPoolVar &operator=(T newValue); + LocalPoolVar &operator=(const T& newValue); + LocalPoolVar &operator=(const LocalPoolVar& newPoolVariable); + + //! Explicit type conversion operator. Allows casting the class to + //! its template type to perform operations on value. + explicit operator T() const; + + bool operator==(const LocalPoolVar& other) const; + bool operator==(const T& other) const; + + bool operator!=(const LocalPoolVar& other) const; + bool operator!=(const T& other) const; + + bool operator<(const LocalPoolVar& other) const; + bool operator<(const T& other) const; + + bool operator>(const LocalPoolVar& other) const; + bool operator>(const T& other) const; + protected: /** * @brief Like #read, but without a lock protection of the global pool. @@ -145,15 +163,6 @@ protected: const LocalPoolVar &var); private: - //! @brief Pool ID of pool entry inside the used local pool. - lp_id_t localPoolId = PoolVariableIF::NO_PARAMETER; - //! @brief Read-write mode of the pool variable - pool_rwm_t readWriteMode = pool_rwm_t::VAR_READ_WRITE; - //! @brief Specifies whether the entry is valid or invalid. - bool valid = false; - - //! Pointer to the class which manages the HK pool. - LocalDataPoolManager* hkManager; }; #include "LocalPoolVariable.tpp" @@ -173,5 +182,4 @@ using lp_int64_t = LocalPoolVar; using lp_float_t = LocalPoolVar; using lp_double_t = LocalPoolVar; - #endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ */ diff --git a/datapoollocal/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp index b0bdd7b9..b9f7b906 100644 --- a/datapoollocal/LocalPoolVariable.tpp +++ b/datapoollocal/LocalPoolVariable.tpp @@ -6,46 +6,22 @@ #endif template -inline LocalPoolVar::LocalPoolVar(lp_id_t poolId, - HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, - pool_rwm_t setReadWriteMode): - localPoolId(poolId), readWriteMode(setReadWriteMode) { - if(poolId == PoolVariableIF::NO_PARAMETER) { - sif::warning << "LocalPoolVar::LocalPoolVar: 0 passed as pool ID, " - << "which is the NO_PARAMETER value!" << std::endl; - } - if(hkOwner == nullptr) { - sif::error << "LocalPoolVar::LocalPoolVar: The supplied pool " - << "owner is a invalid!" << std::endl; - return; - } - hkManager = hkOwner->getHkManagerHandle(); - if(dataSet != nullptr) { - dataSet->registerVariable(this); - } -} +inline LocalPoolVar::LocalPoolVar(HasLocalDataPoolIF* hkOwner, + lp_id_t poolId, DataSetIF* dataSet, pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {} template -inline LocalPoolVar::LocalPoolVar(lp_id_t poolId, object_id_t poolOwner, +inline LocalPoolVar::LocalPoolVar(object_id_t poolOwner, lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode): - localPoolId(poolId), readWriteMode(setReadWriteMode) { - if(poolId == PoolVariableIF::NO_PARAMETER) { - sif::warning << "LocalPoolVar::LocalPoolVar: 0 passed as pool ID, " - << "which is the NO_PARAMETER value!" << std::endl; - } - HasLocalDataPoolIF* hkOwner = - objectManager->get(poolOwner); - if(hkOwner == nullptr) { - sif::error << "LocalPoolVariable: The supplied pool owner did not " - << "implement the correct interface " - << "HasLocalDataPoolIF!" << std::endl; - return; - } - hkManager = hkOwner->getHkManagerHandle(); - if(dataSet != nullptr) { - dataSet->registerVariable(this); - } -} + LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {} + + +template +inline LocalPoolVar::LocalPoolVar(gp_id_t globalPoolId, DataSetIF *dataSet, + pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, + dataSet, setReadWriteMode){} + template inline ReturnValue_t LocalPoolVar::read(dur_millis_t lockTimeout) { @@ -104,43 +80,6 @@ inline ReturnValue_t LocalPoolVar::commitWithoutLock() { return RETURN_OK; } -template -inline LocalPoolVar & LocalPoolVar::operator =(T newValue) { - value = newValue; - return *this; -} - - -template -inline pool_rwm_t LocalPoolVar::getReadWriteMode() const { - return readWriteMode; -} - -template -inline lp_id_t LocalPoolVar::getDataPoolId() const { - return localPoolId; -} - -template -inline void LocalPoolVar::setDataPoolId(lp_id_t poolId) { - this->localPoolId = poolId; -} - -template -inline bool LocalPoolVar::isValid() const { - return valid; -} - -template -inline void LocalPoolVar::setValid(bool validity) { - this->valid = validity; -} - -template -inline uint8_t LocalPoolVar::getValid() const { - return valid; -} - template inline ReturnValue_t LocalPoolVar::serialize(uint8_t** buffer, size_t* size, const size_t max_size, SerializeIF::Endianness streamEndianness) const { @@ -166,4 +105,65 @@ inline std::ostream& operator<< (std::ostream &out, return out; } -#endif +template +inline LocalPoolVar::operator T() const { + return value; +} + +template +inline LocalPoolVar & LocalPoolVar::operator=(const T& newValue) { + value = newValue; + return *this; +} + +template +inline LocalPoolVar& LocalPoolVar::operator =( + const LocalPoolVar& newPoolVariable) { + value = newPoolVariable.value; + return *this; +} + +template +inline bool LocalPoolVar::operator ==(const LocalPoolVar &other) const { + return this->value == other.value; +} + +template +inline bool LocalPoolVar::operator ==(const T &other) const { + return this->value == other; +} + + +template +inline bool LocalPoolVar::operator !=(const LocalPoolVar &other) const { + return not (*this == other); +} + +template +inline bool LocalPoolVar::operator !=(const T &other) const { + return not (*this == other); +} + + +template +inline bool LocalPoolVar::operator <(const LocalPoolVar &other) const { + return this->value < other.value; +} + +template +inline bool LocalPoolVar::operator <(const T &other) const { + return this->value < other; +} + + +template +inline bool LocalPoolVar::operator >(const LocalPoolVar &other) const { + return not (*this < other); +} + +template +inline bool LocalPoolVar::operator >(const T &other) const { + return not (*this < other); +} + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_ */ diff --git a/datapoollocal/LocalPoolVector.h b/datapoollocal/LocalPoolVector.h index 57c4b90b..58face3c 100644 --- a/datapoollocal/LocalPoolVector.h +++ b/datapoollocal/LocalPoolVector.h @@ -1,6 +1,7 @@ -#ifndef FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ -#define FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#include "LocalPoolObjectBase.h" #include "../datapool/DataSetIF.h" #include "../datapool/PoolEntry.h" #include "../datapool/PoolVariableIF.h" @@ -30,7 +31,7 @@ * @ingroup data_pool */ template -class LocalPoolVector: public PoolVariableIF, public HasReturnvaluesIF { +class LocalPoolVector: public LocalPoolObjectBase { public: LocalPoolVector() = delete; /** @@ -46,10 +47,9 @@ public: * @param dataSet The data set in which the variable shall register itself. * If nullptr, the variable is not registered. */ - LocalPoolVector(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, + LocalPoolVector(HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr, - pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE - ); + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); /** * This constructor is used by data users like controllers to have @@ -65,10 +65,19 @@ public: * @param dataSet The data set in which the variable shall register itself. * If nullptr, the variable is not registered. */ - LocalPoolVector(lp_id_t poolId, object_id_t poolOwner, + LocalPoolVector(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr, - pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE - ); + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + /** + * Variation which takes the unique global identifier of a local pool + * vector. + * @param globalPoolId + * @param dataSet + * @param setReadWriteMode + */ + LocalPoolVector(gp_id_t globalPoolId, + DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); /** * @brief This is the local copy of the data pool entry. @@ -91,27 +100,6 @@ public: return vectorSize; } - uint32_t getDataPoolId() const override; - /** - * @brief This operation sets the data pool ID of the variable. - * @details - * The method is necessary to set id's of data pool member variables - * with bad initialization. - */ - void setDataPoolId(uint32_t poolId); - - /** - * This method returns if the variable is write-only, read-write or read-only. - */ - pool_rwm_t getReadWriteMode() const; - - /** - * @brief With this call, the valid information of the variable is returned. - */ - bool isValid() const override; - void setValid(bool valid) override; - uint8_t getValid() const; - T& operator [](int i); const T &operator [](int i) const; @@ -168,23 +156,7 @@ protected: ReturnValue_t commitWithoutLock() override; private: - /** - * @brief To access the correct data pool entry on read and commit calls, - * the data pool id is stored. - */ - uint32_t localPoolId; - /** - * @brief The valid information as it was stored in the data pool - * is copied to this attribute. - */ - bool valid; - /** - * @brief The information whether the class is read-write or - * read-only is stored here. - */ - ReadWriteMode_t readWriteMode; - //! @brief Pointer to the class which manages the HK pool. - LocalDataPoolManager* hkManager; + // std::ostream is the type for object std::cout template @@ -199,4 +171,4 @@ private: template using lp_vec_t = LocalPoolVector; -#endif /* FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ */ +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ */ diff --git a/datapoollocal/LocalPoolVector.tpp b/datapoollocal/LocalPoolVector.tpp index 2aa6fbb5..46123ccc 100644 --- a/datapoollocal/LocalPoolVector.tpp +++ b/datapoollocal/LocalPoolVector.tpp @@ -1,47 +1,27 @@ -#ifndef FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ -#define FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ -#ifndef FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ #error Include LocalPoolVector.h before LocalPoolVector.tpp! #endif template -inline LocalPoolVector::LocalPoolVector(lp_id_t poolId, - HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, +inline LocalPoolVector::LocalPoolVector( + HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet, pool_rwm_t setReadWriteMode): - localPoolId(poolId), valid(false), readWriteMode(setReadWriteMode) { - if(poolId == PoolVariableIF::NO_PARAMETER) { - sif::warning << "LocalPoolVector: PoolVariableIF::NO_PARAMETER passed " - << "as pool ID, which is the NO_PARAMETER value!" << std::endl; - } - std::memset(this->value, 0, vectorSize * sizeof(T)); - hkManager = hkOwner->getHkManagerHandle(); - if (dataSet != nullptr) { - dataSet->registerVariable(this); - } -} + LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {} template -inline LocalPoolVector::LocalPoolVector(lp_id_t poolId, - object_id_t poolOwner, DataSetIF *dataSet, pool_rwm_t setReadWriteMode): - readWriteMode(setReadWriteMode) { - if(poolId == PoolVariableIF::NO_PARAMETER) { - sif::warning << "LocalPoolVector: PoolVariableIF::NO_PARAMETER passed " - << "as pool ID, which is the NO_PARAMETER value!" << std::endl; - } - HasLocalDataPoolIF* hkOwner = - objectManager->get(poolOwner); - if(hkOwner == nullptr) { - sif::error << "LocalPoolVariable: The supplied pool owner did not " - << "implement the correct interface HasHkPoolParametersIF!" - << std::endl; - return; - } - hkManager = hkOwner->getHkManagerHandle(); - if(dataSet != nullptr) { - dataSet->registerVariable(this); - } -} +inline LocalPoolVector::LocalPoolVector(object_id_t poolOwner, + lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {} + + +template +inline LocalPoolVector::LocalPoolVector(gp_id_t globalPoolId, + DataSetIF *dataSet, pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, + dataSet, setReadWriteMode) {} template inline ReturnValue_t LocalPoolVector::read(uint32_t lockTimeout) { @@ -161,37 +141,6 @@ inline ReturnValue_t LocalPoolVector::deSerialize( return result; } -template -inline pool_rwm_t LocalPoolVector::getReadWriteMode() const { - return this->readWriteMode; -} - - -template -inline uint32_t LocalPoolVector::getDataPoolId() const { - return localPoolId; -} - -template -inline void LocalPoolVector::setDataPoolId(uint32_t poolId) { - this->localPoolId = poolId; -} - -template -inline void LocalPoolVector::setValid(bool valid) { - this->valid = valid; -} - -template -inline uint8_t LocalPoolVector::getValid() const { - return valid; -} - -template -inline bool LocalPoolVector::isValid() const { - return valid; -} - template inline std::ostream& operator<< (std::ostream &out, const LocalPoolVector &var) { @@ -206,4 +155,4 @@ inline std::ostream& operator<< (std::ostream &out, return out; } -#endif +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ */ diff --git a/datapoollocal/MarkChangedIF.h b/datapoollocal/MarkChangedIF.h new file mode 100644 index 00000000..e575d1d3 --- /dev/null +++ b/datapoollocal/MarkChangedIF.h @@ -0,0 +1,17 @@ +#ifndef FSFW_DATAPOOLLOCAL_MARKCHANGEDIF_H_ +#define FSFW_DATAPOOLLOCAL_MARKCHANGEDIF_H_ + +/** + * Common interface for local pool entities which can be marked as changed. + */ +class MarkChangedIF { +public: + virtual~ MarkChangedIF() {}; + + virtual bool hasChanged() const = 0; + virtual void setChanged(bool changed) = 0; +}; + + + +#endif /* FSFW_DATAPOOLLOCAL_MARKCHANGEDIF_H_ */ diff --git a/datapoollocal/locPoolDefinitions.h b/datapoollocal/locPoolDefinitions.h new file mode 100644 index 00000000..6e74d349 --- /dev/null +++ b/datapoollocal/locPoolDefinitions.h @@ -0,0 +1,93 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCPOOLDEFINITIONS_H_ +#define FSFW_DATAPOOLLOCAL_LOCPOOLDEFINITIONS_H_ + +#include +#include "../objectmanager/SystemObjectIF.h" +#include "../objectmanager/frameworkObjects.h" + +/** + * @brief Type definition for local pool entries. + */ +using lp_id_t = uint32_t; + +namespace localpool { +static constexpr uint32_t INVALID_LPID = -1; +} + +/** + * Used as a unique identifier for data sets. + */ +union sid_t { + static constexpr uint64_t INVALID_SID = -1; + static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT; + static constexpr uint32_t INVALID_SET_ID = -1; + + + sid_t(): raw(INVALID_SID) {} + + sid_t(object_id_t objectId, uint32_t setId): + objectId(objectId), + ownerSetId(setId) {} + + struct { + object_id_t objectId ; + /** + * A generic 32 bit ID to identify unique HK packets for a single + * object. For example, the DeviceCommandId_t is used for + * DeviceHandlers + */ + uint32_t ownerSetId; + }; + /** + * Alternative access to the raw value. This is also the size of the type. + */ + uint64_t raw; + + bool notSet() const { + return raw == INVALID_SID; + } + + bool operator==(const sid_t& other) const { + return raw == other.raw; + } + + bool operator!=(const sid_t& other) const { + return not (raw == other.raw); + } +}; + +/** + * Used as a global unique identifier for local pool variables. + */ +union gp_id_t { + static constexpr uint64_t INVALID_GPID = -1; + static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT; + static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID; + + gp_id_t(): raw(INVALID_GPID) {} + + gp_id_t(object_id_t objectId, lp_id_t localPoolId): + objectId(objectId), + localPoolId(localPoolId) {} + + struct { + object_id_t objectId; + lp_id_t localPoolId; + }; + + uint64_t raw; + + bool notSet() const { + return raw == INVALID_GPID; + } + + bool operator==(const sid_t& other) const { + return raw == other.raw; + } + + bool operator!=(const sid_t& other) const { + return not (raw == other.raw); + } +}; + +#endif /* FSFW_DATAPOOLLOCAL_LOCPOOLDEFINITIONS_H_ */ diff --git a/housekeeping/AcceptsHkPacketsIF.h b/housekeeping/AcceptsHkPacketsIF.h index b3ba6e7c..6fa151b1 100644 --- a/housekeeping/AcceptsHkPacketsIF.h +++ b/housekeeping/AcceptsHkPacketsIF.h @@ -1,5 +1,6 @@ #ifndef FRAMEWORK_HOUSEKEEPING_ACCEPTSHKPACKETSIF_H_ #define FRAMEWORK_HOUSEKEEPING_ACCEPTSHKPACKETSIF_H_ + #include "../ipc/MessageQueueMessageIF.h" class AcceptsHkPacketsIF { diff --git a/housekeeping/HousekeepingMessage.cpp b/housekeeping/HousekeepingMessage.cpp index d2ab546b..221f0a6f 100644 --- a/housekeeping/HousekeepingMessage.cpp +++ b/housekeeping/HousekeepingMessage.cpp @@ -1,5 +1,6 @@ -#include #include "HousekeepingMessage.h" + +#include "../objectmanager/ObjectManagerIF.h" #include HousekeepingMessage::~HousekeepingMessage() {} @@ -148,7 +149,7 @@ void HousekeepingMessage::clear(CommandMessage* message) { case(DIAGNOSTICS_REPORT): case(HK_DEFINITIONS_REPORT): case(DIAGNOSTICS_DEFINITION_REPORT): - case(UPDATE_SNAPSHOT): { + case(UPDATE_SNAPSHOT_SET): { store_address_t storeId; getHkDataReply(message, &storeId); StorageManagerIF *ipcStore = objectManager->get( @@ -160,3 +161,55 @@ void HousekeepingMessage::clear(CommandMessage* message) { } message->setCommand(CommandMessage::CMD_NONE); } + +void HousekeepingMessage::setUpdateNotificationSetCommand( + CommandMessage *command, sid_t sid) { + command->setCommand(UPDATE_NOTIFICATION_SET); + setSid(command, sid); +} + +void HousekeepingMessage::setUpdateNotificationVariableCommand( + CommandMessage *command, lp_id_t localPoolId) { + command->setCommand(UPDATE_NOTIFICATION_VARIABLE); + command->setParameter(localPoolId); +} + +void HousekeepingMessage::setUpdateSnapshotSetCommand(CommandMessage *command, + sid_t sid, store_address_t storeId) { + command->setCommand(UPDATE_SNAPSHOT_VARIABLE); + setSid(command, sid); + command->setParameter3(storeId.raw); +} + +void HousekeepingMessage::setUpdateSnapshotVariableCommand( + CommandMessage *command, lp_id_t localPoolId, store_address_t storeId) { + command->setCommand(UPDATE_SNAPSHOT_VARIABLE); + command->setParameter(localPoolId); + command->setParameter3(storeId.raw); +} + +sid_t HousekeepingMessage::getUpdateNotificationSetCommand( + const CommandMessage *command) { + return getSid(command); +} + +lp_id_t HousekeepingMessage::getUpdateNotificationVariableCommand( + const CommandMessage *command) { + return command->getParameter(); +} + +sid_t HousekeepingMessage::getUpdateSnapshotSetCommand( + const CommandMessage *command, store_address_t *storeId) { + if(storeId != nullptr) { + *storeId = command->getParameter3(); + } + return getSid(command); +} + +lp_id_t HousekeepingMessage::getUpdateSnapshotVariableCommand( + const CommandMessage *command, store_address_t *storeId) { + if(storeId != nullptr) { + *storeId = command->getParameter3(); + } + return command->getParameter(); +} diff --git a/housekeeping/HousekeepingMessage.h b/housekeeping/HousekeepingMessage.h index 6dc95f54..90bbe594 100644 --- a/housekeeping/HousekeepingMessage.h +++ b/housekeeping/HousekeepingMessage.h @@ -1,49 +1,12 @@ #ifndef FSFW_HOUSEKEEPING_HOUSEKEEPINGMESSAGE_H_ #define FSFW_HOUSEKEEPING_HOUSEKEEPINGMESSAGE_H_ +#include "../datapoollocal/locPoolDefinitions.h" #include "../ipc/CommandMessage.h" #include "../ipc/FwMessageTypes.h" #include "../objectmanager/frameworkObjects.h" -#include "../objectmanager/SystemObjectIF.h" #include "../storagemanager/StorageManagerIF.h" -union sid_t { - static constexpr uint64_t INVALID_SID = -1; - static constexpr uint32_t INVALID_SET_ID = -1; - static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT; - sid_t(): raw(INVALID_SID) {} - - sid_t(object_id_t objectId, uint32_t setId): - objectId(objectId), - ownerSetId(setId) {} - - struct { - object_id_t objectId ; - /** - * A generic 32 bit ID to identify unique HK packets for a single - * object. For example, the DeviceCommandId_t is used for - * DeviceHandlers - */ - uint32_t ownerSetId; - }; - /** - * Alternative access to the raw value. This is also the size of the type. - */ - uint64_t raw; - - bool notSet() const { - return raw == INVALID_SID; - } - - bool operator==(const sid_t& other) const { - return raw == other.raw; - } - - bool operator!=(const sid_t& other) const { - return not (raw == other.raw); - } -}; - /** * @brief Special command message type for housekeeping messages @@ -101,14 +64,20 @@ public: static constexpr Command_t HK_REQUEST_FAILURE = MAKE_COMMAND_ID(129); - static constexpr Command_t UPDATE_NOTIFICATION = MAKE_COMMAND_ID(130); - static constexpr Command_t UPDATE_SNAPSHOT = MAKE_COMMAND_ID(131); + static constexpr Command_t UPDATE_NOTIFICATION_SET = + MAKE_COMMAND_ID(130); + static constexpr Command_t UPDATE_NOTIFICATION_VARIABLE = + MAKE_COMMAND_ID(131); - static constexpr Command_t UPDATE_HK_REPORT = MAKE_COMMAND_ID(132); + static constexpr Command_t UPDATE_SNAPSHOT_SET = MAKE_COMMAND_ID(132); + static constexpr Command_t UPDATE_SNAPSHOT_VARIABLE = MAKE_COMMAND_ID(133); + + //static constexpr Command_t UPDATE_HK_REPORT = MAKE_COMMAND_ID(134); static sid_t getSid(const CommandMessage* message); - /** Setter functions */ + /* Housekeeping Interface Messages */ + static void setToggleReportingCommand(CommandMessage* command, sid_t sid, bool enableReporting, bool isDiagnostics); static void setStructureReportingCommand(CommandMessage* command, sid_t sid, @@ -148,6 +117,29 @@ public: static sid_t getCollectionIntervalModificationCommand( const CommandMessage* command, float* newCollectionInterval); + + /* Update Notification Messages */ + + static void setUpdateNotificationSetCommand(CommandMessage* command, + sid_t sid); + static void setUpdateNotificationVariableCommand(CommandMessage* command, + lp_id_t localPoolId); + + static void setUpdateSnapshotSetCommand(CommandMessage* command, sid_t sid, + store_address_t storeId); + static void setUpdateSnapshotVariableCommand(CommandMessage* command, + lp_id_t localPoolId, store_address_t storeId); + + static sid_t getUpdateNotificationSetCommand(const CommandMessage* command); + static lp_id_t getUpdateNotificationVariableCommand( + const CommandMessage* command); + + static sid_t getUpdateSnapshotSetCommand(const CommandMessage* command, + store_address_t* storeId); + static lp_id_t getUpdateSnapshotVariableCommand(const CommandMessage* command, + store_address_t* storeId); + + /** Utility */ static void clear(CommandMessage* message); private: static void setSid(CommandMessage* message, sid_t sid); diff --git a/housekeeping/HousekeepingPacketUpdate.h b/housekeeping/HousekeepingPacketUpdate.h index eebdc11c..43ec0619 100644 --- a/housekeeping/HousekeepingPacketUpdate.h +++ b/housekeeping/HousekeepingPacketUpdate.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_ -#define FRAMEWORK_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_ +#ifndef FSFW_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_ +#define FSFW_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_ #include "../serialize/SerialBufferAdapter.h" #include "../serialize/SerialLinkedListAdapter.h" @@ -12,6 +12,7 @@ class HousekeepingPacketUpdate: public SerializeIF { public: /** + * Update packet constructor for datasets * @param timeStamp * @param timeStampSize * @param hkData @@ -19,8 +20,19 @@ public: */ HousekeepingPacketUpdate(uint8_t* timeStamp, size_t timeStampSize, LocalPoolDataSetBase* dataSetPtr): - timeStamp(timeStamp), timeStampSize(timeStampSize), - dataSetPtr(dataSetPtr) {}; + timeStamp(timeStamp), timeStampSize(timeStampSize), + updateData(dataSetPtr) {}; + + /** + * Update packet constructor for pool variables. + * @param timeStamp + * @param timeStampSize + * @param dataSetPtr + */ + HousekeepingPacketUpdate(uint8_t* timeStamp, size_t timeStampSize, + LocalPoolObjectBase* dataSetPtr): + timeStamp(timeStamp), timeStampSize(timeStampSize), + updateData(dataSetPtr) {}; virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize, Endianness streamEndianness) const { @@ -31,43 +43,50 @@ public: *size += timeStampSize; *buffer += timeStampSize; } - if(dataSetPtr == nullptr) { + if(updateData == nullptr) { return HasReturnvaluesIF::RETURN_FAILED; } - return dataSetPtr->serialize(buffer, size, maxSize, streamEndianness); + return updateData->serialize(buffer, size, maxSize, streamEndianness); } virtual size_t getSerializedSize() const { - if(dataSetPtr == nullptr) { + if(updateData == nullptr) { return 0; } - return timeStampSize + dataSetPtr->getSerializedSize(); + return timeStampSize + updateData->getSerializedSize(); } virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, SerializeIF::Endianness streamEndianness) override { + if(*size < timeStampSize) { + return SerializeIF::STREAM_TOO_SHORT; + } + if(timeStamp != nullptr) { /* Endianness will always be MACHINE, so we can simply use memcpy here. */ std::memcpy(timeStamp, *buffer, timeStampSize); - *size += timeStampSize; + *size -= timeStampSize; *buffer += timeStampSize; } - if(dataSetPtr == nullptr) { + if(updateData == nullptr) { return HasReturnvaluesIF::RETURN_FAILED; } - return dataSetPtr->deSerialize(buffer, size, streamEndianness); + if(*size < updateData->getSerializedSize()) { + return SerializeIF::STREAM_TOO_SHORT; + } + + return updateData->deSerialize(buffer, size, streamEndianness); } private: - uint8_t* timeStamp; - size_t timeStampSize; + uint8_t* timeStamp = nullptr; + size_t timeStampSize = 0; - LocalPoolDataSetBase* dataSetPtr = nullptr; + SerializeIF* updateData = nullptr; }; - -#endif /* FRAMEWORK_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_ */ +#endif /* FSFW_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_ */ diff --git a/housekeeping/PeriodicHousekeepingHelper.cpp b/housekeeping/PeriodicHousekeepingHelper.cpp index 37349f81..365f0004 100644 --- a/housekeeping/PeriodicHousekeepingHelper.cpp +++ b/housekeeping/PeriodicHousekeepingHelper.cpp @@ -1,5 +1,6 @@ -#include "../datapoollocal/LocalPoolDataSetBase.h" #include "PeriodicHousekeepingHelper.h" + +#include "../datapoollocal/LocalPoolDataSetBase.h" #include PeriodicHousekeepingHelper::PeriodicHousekeepingHelper( From 74ba8b76644c31405d67878421b2081306eab453 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Dec 2020 13:10:33 +0100 Subject: [PATCH 081/119] changel og updated --- CHANGELOG | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index a6686478..62a6b0a9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,3 +11,19 @@ a C file without issues - It is now possible to change the message queue depth for the telecommand verification service (PUS1) - The same is possible for the event reporting service (PUS5) - PUS Health Service added, which allows to command and retrieve health via PUS packets + +### Parameter Service + +- The API of the parameter service has been changed to prevent incosistencies +and clarify usage when inspecting the code. +- The parameter ID now consists of: + 1. Domain ID (1 byte) + 2. Unique Identifier (1 byte) + 3. Linear Index (2 bytes) +The linear index can be used for arrays as well as matrices. +The parameter load command now explicitely expects the ECSS PTC and PFC +information as well as the rows and column number. Rows and column will +default to one, which is equivalent to one scalar parameter (the most +important use-case) + + From 3f9f062435061c32818c4c4ddb078cb328ea8f4d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Dec 2020 13:11:52 +0100 Subject: [PATCH 082/119] changelog update --- CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 62a6b0a9..8341e138 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,8 +14,8 @@ a C file without issues ### Parameter Service -- The API of the parameter service has been changed to prevent incosistencies -and clarify usage when inspecting the code. +- The API of the parameter service has been changed to prevent inconsistencies +between documentation and actual code and to clarify usage. - The parameter ID now consists of: 1. Domain ID (1 byte) 2. Unique Identifier (1 byte) From 795ff585f858f7ca4a155eaf7bf704964ad6f13d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Dec 2020 13:14:30 +0100 Subject: [PATCH 083/119] controller base update --- controller/ControllerBase.cpp | 41 ++++++++++-------- controller/ControllerBase.h | 78 +++++++++++++++++++++-------------- 2 files changed, 71 insertions(+), 48 deletions(-) diff --git a/controller/ControllerBase.cpp b/controller/ControllerBase.cpp index 69551731..45e678eb 100644 --- a/controller/ControllerBase.cpp +++ b/controller/ControllerBase.cpp @@ -1,16 +1,16 @@ -#include "../subsystem/SubsystemBase.h" #include "ControllerBase.h" + #include "../subsystem/SubsystemBase.h" #include "../ipc/QueueFactory.h" #include "../action/HasActionsIF.h" -ControllerBase::ControllerBase(uint32_t setObjectId, uint32_t parentId, +ControllerBase::ControllerBase(object_id_t setObjectId, object_id_t parentId, size_t commandQueueDepth) : - SystemObject(setObjectId), parentId(parentId), mode(MODE_OFF), submode( - SUBMODE_NONE), commandQueue(NULL), modeHelper( - this), healthHelper(this, setObjectId),hkSwitcher(this),executingTask(NULL) { - commandQueue = QueueFactory::instance()->createMessageQueue(commandQueueDepth); - + SystemObject(setObjectId), parentId(parentId), mode(MODE_OFF), + submode(SUBMODE_NONE), modeHelper(this), + healthHelper(this, setObjectId), hkSwitcher(this) { + commandQueue = QueueFactory::instance()->createMessageQueue( + commandQueueDepth); } ControllerBase::~ControllerBase() { @@ -24,9 +24,9 @@ ReturnValue_t ControllerBase::initialize() { } MessageQueueId_t parentQueue = 0; - if (parentId != 0) { + if (parentId != objects::NO_OBJECT) { SubsystemBase *parent = objectManager->get(parentId); - if (parent == NULL) { + if (parent == nullptr) { return RETURN_FAILED; } parentQueue = parent->getCommandQueue(); @@ -56,26 +56,27 @@ MessageQueueId_t ControllerBase::getCommandQueue() const { } void ControllerBase::handleQueue() { - CommandMessage message; - ReturnValue_t result; - for (result = commandQueue->receiveMessage(&message); result == RETURN_OK; - result = commandQueue->receiveMessage(&message)) { + CommandMessage command; + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + for (result = commandQueue->receiveMessage(&command); + result == RETURN_OK; + result = commandQueue->receiveMessage(&command)) { - result = modeHelper.handleModeCommand(&message); + result = modeHelper.handleModeCommand(&command); if (result == RETURN_OK) { continue; } - result = healthHelper.handleHealthCommand(&message); + result = healthHelper.handleHealthCommand(&command); if (result == RETURN_OK) { continue; } - result = handleCommandMessage(&message); + result = handleCommandMessage(&command); if (result == RETURN_OK) { continue; } - message.setToUnknownCommand(); - commandQueue->reply(&message); + command.setToUnknownCommand(); + commandQueue->reply(&command); } } @@ -135,3 +136,7 @@ void ControllerBase::setTaskIF(PeriodicTaskIF* task_){ void ControllerBase::changeHK(Mode_t mode, Submode_t submode, bool enable) { } + +ReturnValue_t ControllerBase::initializeAfterTaskCreation() { + return HasReturnvaluesIF::RETURN_OK; +} diff --git a/controller/ControllerBase.h b/controller/ControllerBase.h index f5182ceb..25d3ab1f 100644 --- a/controller/ControllerBase.h +++ b/controller/ControllerBase.h @@ -1,5 +1,5 @@ -#ifndef CONTROLLERBASE_H_ -#define CONTROLLERBASE_H_ +#ifndef FSFW_CONTROLLER_CONTROLLERBASE_H_ +#define FSFW_CONTROLLER_CONTROLLERBASE_H_ #include "../health/HasHealthIF.h" #include "../health/HealthHelper.h" @@ -7,73 +7,91 @@ #include "../modes/ModeHelper.h" #include "../objectmanager/SystemObject.h" #include "../tasks/ExecutableObjectIF.h" +#include "../tasks/PeriodicTaskIF.h" #include "../datapool/HkSwitchHelper.h" - +/** + * @brief Generic base class for controller classes + * @details + * Implements common interfaces for controllers, which generally have + * a mode and a health state. This avoids boilerplate code. + */ class ControllerBase: public HasModesIF, public HasHealthIF, public ExecutableObjectIF, public SystemObject, public HasReturnvaluesIF { public: - static const Mode_t MODE_NORMAL = 2; - ControllerBase(uint32_t setObjectId, uint32_t parentId, + ControllerBase(object_id_t setObjectId, object_id_t parentId, size_t commandQueueDepth = 3); virtual ~ControllerBase(); - ReturnValue_t initialize(); + /** SystemObject override */ + virtual ReturnValue_t initialize() override; - virtual MessageQueueId_t getCommandQueue() const; + virtual MessageQueueId_t getCommandQueue() const override; - virtual ReturnValue_t performOperation(uint8_t opCode); - - virtual ReturnValue_t setHealth(HealthState health); - - virtual HasHealthIF::HealthState getHealth(); - - /** - * Implementation of ExecutableObjectIF function - * - * Used to setup the reference of the task, that executes this component - * @param task_ Pointer to the taskIF of this task - */ - virtual void setTaskIF(PeriodicTaskIF* task_); + /** HasHealthIF overrides */ + virtual ReturnValue_t setHealth(HealthState health) override; + virtual HasHealthIF::HealthState getHealth() override; + /** ExecutableObjectIF overrides */ + virtual ReturnValue_t performOperation(uint8_t opCode) override; + virtual void setTaskIF(PeriodicTaskIF* task) override; + virtual ReturnValue_t initializeAfterTaskCreation() override; protected: - const uint32_t parentId; + + /** + * Implemented by child class. Handle command messages which are not + * mode or health messages. + * @param message + * @return + */ + virtual ReturnValue_t handleCommandMessage(CommandMessage *message) = 0; + + /** + * Periodic helper, implemented by child class. + */ + virtual void performControlOperation() = 0; + + virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, + uint32_t *msToReachTheMode) = 0; + + const object_id_t parentId; Mode_t mode; Submode_t submode; - MessageQueueIF* commandQueue; + MessageQueueIF* commandQueue = nullptr; ModeHelper modeHelper; HealthHelper healthHelper; + // Is this still needed? HkSwitchHelper hkSwitcher; /** - * Pointer to the task which executes this component, is invalid before setTaskIF was called. + * Pointer to the task which executes this component, + * is invalid before setTaskIF was called. */ - PeriodicTaskIF* executingTask; + PeriodicTaskIF* executingTask = nullptr; - void handleQueue(); + /** Handle mode and health messages */ + virtual void handleQueue(); - virtual ReturnValue_t handleCommandMessage(CommandMessage *message) = 0; - virtual void performControlOperation() = 0; - virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, - uint32_t *msToReachTheMode) = 0; + /** Mode helpers */ virtual void modeChanged(Mode_t mode, Submode_t submode); virtual void startTransition(Mode_t mode, Submode_t submode); virtual void getMode(Mode_t *mode, Submode_t *submode); virtual void setToExternalControl(); virtual void announceMode(bool recursive); + /** HK helpers */ virtual void changeHK(Mode_t mode, Submode_t submode, bool enable); }; -#endif /* CONTROLLERBASE_H_ */ +#endif /* FSFW_CONTROLLER_CONTROLLERBASE_H_ */ From 5e092778edcce53ba6bdc89c8b5e0b9a7b9482b8 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Dec 2020 13:17:28 +0100 Subject: [PATCH 084/119] some minor changes --- datalinklayer/MapPacketExtraction.cpp | 17 +++++------------ datalinklayer/MapPacketExtraction.h | 8 +------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/datalinklayer/MapPacketExtraction.cpp b/datalinklayer/MapPacketExtraction.cpp index d64348e1..45f7d3f8 100644 --- a/datalinklayer/MapPacketExtraction.cpp +++ b/datalinklayer/MapPacketExtraction.cpp @@ -1,10 +1,3 @@ -/** - * @file MapPacketExtraction.cpp - * @brief This file defines the MapPacketExtraction class. - * @date 26.03.2013 - * @author baetz - */ - #include "MapPacketExtraction.h" #include "../ipc/QueueFactory.h" #include "../serviceinterface/ServiceInterfaceStream.h" @@ -12,14 +5,14 @@ #include "../tmtcpacket/SpacePacketBase.h" #include "../tmtcservices/AcceptsTelecommandsIF.h" #include "../tmtcservices/TmTcMessage.h" -#include +#include MapPacketExtraction::MapPacketExtraction(uint8_t setMapId, object_id_t setPacketDestination) : - lastSegmentationFlag(NO_SEGMENTATION), mapId(setMapId), packetLength(0), bufferPosition( - packetBuffer), packetDestination(setPacketDestination), packetStore( - NULL), tcQueueId(MessageQueueIF::NO_QUEUE) { - memset(packetBuffer, 0, sizeof(packetBuffer)); + lastSegmentationFlag(NO_SEGMENTATION), mapId(setMapId), packetLength(0), + bufferPosition(packetBuffer), packetDestination(setPacketDestination), + packetStore(NULL), tcQueueId(MessageQueueIF::NO_QUEUE) { + std::memset(packetBuffer, 0, sizeof(packetBuffer)); } ReturnValue_t MapPacketExtraction::extractPackets(TcTransferFrame* frame) { diff --git a/datalinklayer/MapPacketExtraction.h b/datalinklayer/MapPacketExtraction.h index 507f13db..eff9b7c3 100644 --- a/datalinklayer/MapPacketExtraction.h +++ b/datalinklayer/MapPacketExtraction.h @@ -1,10 +1,3 @@ -/** - * @file MapPacketExtraction.h - * @brief This file defines the MapPacketExtraction class. - * @date 26.03.2013 - * @author baetz - */ - #ifndef MAPPACKETEXTRACTION_H_ #define MAPPACKETEXTRACTION_H_ @@ -20,6 +13,7 @@ class StorageManagerIF; * The class implements the full MAP Packet Extraction functionality as described in the CCSDS * TC Space Data Link Protocol. It internally stores incomplete segmented packets until they are * fully received. All found packets are forwarded to a single distribution entity. + * @author B. Baetz */ class MapPacketExtraction: public MapPacketExtractionIF { private: From f7f062570e63a249eea95d51312b0dbb0dd104d3 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Dec 2020 13:21:44 +0100 Subject: [PATCH 085/119] new internal error reporter using local pools --- internalError/InternalErrorDataset.h | 34 +++++++ internalError/InternalErrorReporter.cpp | 127 ++++++++++++++++++------ internalError/InternalErrorReporter.h | 66 +++++++++--- 3 files changed, 182 insertions(+), 45 deletions(-) create mode 100644 internalError/InternalErrorDataset.h diff --git a/internalError/InternalErrorDataset.h b/internalError/InternalErrorDataset.h new file mode 100644 index 00000000..fa91116d --- /dev/null +++ b/internalError/InternalErrorDataset.h @@ -0,0 +1,34 @@ +#ifndef FSFW_INTERNALERROR_INTERNALERRORDATASET_H_ +#define FSFW_INTERNALERROR_INTERNALERRORDATASET_H_ + +#include +#include + +enum errorPoolIds { + TM_HITS, + QUEUE_HITS, + STORE_HITS +}; + + +class InternalErrorDataset: public StaticLocalDataSet<3 * sizeof(uint32_t)> { +public: + static constexpr uint8_t ERROR_SET_ID = 0; + + InternalErrorDataset(HasLocalDataPoolIF* owner): + StaticLocalDataSet(owner, ERROR_SET_ID) {} + + InternalErrorDataset(object_id_t objectId): + StaticLocalDataSet(sid_t(objectId , ERROR_SET_ID)) {} + + lp_var_t tmHits = lp_var_t(hkManager->getOwner(), + TM_HITS, this); + lp_var_t queueHits = lp_var_t(hkManager->getOwner(), + QUEUE_HITS, this); + lp_var_t storeHits = lp_var_t(hkManager->getOwner(), + STORE_HITS, this); +}; + + + +#endif /* FSFW_INTERNALERROR_INTERNALERRORDATASET_H_ */ diff --git a/internalError/InternalErrorReporter.cpp b/internalError/InternalErrorReporter.cpp index 861e1595..8d5c792b 100644 --- a/internalError/InternalErrorReporter.cpp +++ b/internalError/InternalErrorReporter.cpp @@ -1,17 +1,17 @@ #include "InternalErrorReporter.h" -#include "../datapool/DataSet.h" -#include "../datapool/PoolVariable.h" +#include "../ipc/QueueFactory.h" #include "../ipc/MutexFactory.h" - #include "../serviceinterface/ServiceInterfaceStream.h" 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) { + uint32_t messageQueueDepth) : + SystemObject(setObjectId), + commandQueue(QueueFactory::instance()-> + createMessageQueue(messageQueueDepth)), + poolManager(this, commandQueue), + internalErrorSid(setObjectId, InternalErrorDataset::ERROR_SET_ID), + internalErrorDataset(this) { mutex = MutexFactory::instance()->createMutex(); } @@ -19,28 +19,42 @@ InternalErrorReporter::~InternalErrorReporter() { MutexFactory::instance()->deleteMutex(mutex); } +void InternalErrorReporter::setDiagnosticPrintout(bool enable) { + this->diagnosticPrintout = enable; +} + ReturnValue_t InternalErrorReporter::performOperation(uint8_t opCode) { - - DataSet mySet; - PoolVariable queueHitsInPool(queuePoolId, &mySet, - PoolVariableIF::VAR_READ_WRITE); - PoolVariable tmHitsInPool(tmPoolId, &mySet, - PoolVariableIF::VAR_READ_WRITE); - - PoolVariable storeHitsInPool(storePoolId, &mySet, - PoolVariableIF::VAR_READ_WRITE); - mySet.read(); + internalErrorDataset.read(INTERNAL_ERROR_MUTEX_TIMEOUT); uint32_t newQueueHits = getAndResetQueueHits(); uint32_t newTmHits = getAndResetTmHits(); uint32_t newStoreHits = getAndResetStoreHits(); - queueHitsInPool.value += newQueueHits; - tmHitsInPool.value += newTmHits; - storeHitsInPool.value += newStoreHits; +#ifdef DEBUG + if(diagnosticPrintout) { + if((newQueueHits > 0) or (newTmHits > 0) or (newStoreHits > 0)) { + sif::debug << "InternalErrorReporter::performOperation: Errors " + << "occured!" << std::endl; + sif::debug << "Queue errors: " << newQueueHits << std::endl; + sif::debug << "TM errors: " << newTmHits << std::endl; + sif::debug << "Store errors: " << newStoreHits << std::endl; + } + } +#endif - mySet.commit(PoolVariableIF::VALID); + internalErrorDataset.queueHits.value += newQueueHits; + internalErrorDataset.storeHits.value += newStoreHits; + internalErrorDataset.tmHits.value += newTmHits; + internalErrorDataset.commit(INTERNAL_ERROR_MUTEX_TIMEOUT); + + poolManager.performHkOperation(); + + CommandMessage message; + ReturnValue_t result = commandQueue->receiveMessage(&message); + if(result != MessageQueueIF::EMPTY) { + poolManager.handleHousekeepingMessage(&message); + } return HasReturnvaluesIF::RETURN_OK; } @@ -54,7 +68,7 @@ void InternalErrorReporter::lostTm() { uint32_t InternalErrorReporter::getAndResetQueueHits() { uint32_t value; - mutex->lockMutex(MutexIF::BLOCKING); + mutex->lockMutex(MutexIF::WAITING, INTERNAL_ERROR_MUTEX_TIMEOUT); value = queueHits; queueHits = 0; mutex->unlockMutex(); @@ -63,21 +77,21 @@ uint32_t InternalErrorReporter::getAndResetQueueHits() { uint32_t InternalErrorReporter::getQueueHits() { uint32_t value; - mutex->lockMutex(MutexIF::BLOCKING); + mutex->lockMutex(MutexIF::WAITING, INTERNAL_ERROR_MUTEX_TIMEOUT); value = queueHits; mutex->unlockMutex(); return value; } void InternalErrorReporter::incrementQueueHits() { - mutex->lockMutex(MutexIF::BLOCKING); + mutex->lockMutex(MutexIF::WAITING, INTERNAL_ERROR_MUTEX_TIMEOUT); queueHits++; mutex->unlockMutex(); } uint32_t InternalErrorReporter::getAndResetTmHits() { uint32_t value; - mutex->lockMutex(MutexIF::BLOCKING); + mutex->lockMutex(MutexIF::WAITING, INTERNAL_ERROR_MUTEX_TIMEOUT); value = tmHits; tmHits = 0; mutex->unlockMutex(); @@ -86,14 +100,14 @@ uint32_t InternalErrorReporter::getAndResetTmHits() { uint32_t InternalErrorReporter::getTmHits() { uint32_t value; - mutex->lockMutex(MutexIF::BLOCKING); + mutex->lockMutex(MutexIF::WAITING, INTERNAL_ERROR_MUTEX_TIMEOUT); value = tmHits; mutex->unlockMutex(); return value; } void InternalErrorReporter::incrementTmHits() { - mutex->lockMutex(MutexIF::BLOCKING); + mutex->lockMutex(MutexIF::WAITING, INTERNAL_ERROR_MUTEX_TIMEOUT); tmHits++; mutex->unlockMutex(); } @@ -104,7 +118,7 @@ void InternalErrorReporter::storeFull() { uint32_t InternalErrorReporter::getAndResetStoreHits() { uint32_t value; - mutex->lockMutex(MutexIF::BLOCKING); + mutex->lockMutex(MutexIF::WAITING, INTERNAL_ERROR_MUTEX_TIMEOUT); value = storeHits; storeHits = 0; mutex->unlockMutex(); @@ -113,14 +127,65 @@ uint32_t InternalErrorReporter::getAndResetStoreHits() { uint32_t InternalErrorReporter::getStoreHits() { uint32_t value; - mutex->lockMutex(MutexIF::BLOCKING); + mutex->lockMutex(MutexIF::WAITING, INTERNAL_ERROR_MUTEX_TIMEOUT); value = storeHits; mutex->unlockMutex(); return value; } void InternalErrorReporter::incrementStoreHits() { - mutex->lockMutex(MutexIF::BLOCKING); + mutex->lockMutex(MutexIF::WAITING, INTERNAL_ERROR_MUTEX_TIMEOUT); storeHits++; mutex->unlockMutex(); } + +object_id_t InternalErrorReporter::getObjectId() const { + return SystemObject::getObjectId(); +} + +MessageQueueId_t InternalErrorReporter::getCommandQueue() const { + return this->commandQueue->getId(); +} + +ReturnValue_t InternalErrorReporter::initializeLocalDataPool( + LocalDataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { + localDataPoolMap.emplace(errorPoolIds::TM_HITS, + new PoolEntry()); + localDataPoolMap.emplace(errorPoolIds::QUEUE_HITS, + new PoolEntry()); + localDataPoolMap.emplace(errorPoolIds::STORE_HITS, + new PoolEntry()); + poolManager.subscribeForPeriodicPacket(internalErrorSid, false, + getPeriodicOperationFrequency(), true); + internalErrorDataset.setValidity(true, true); + return HasReturnvaluesIF::RETURN_OK; +} + +LocalDataPoolManager* InternalErrorReporter::getHkManagerHandle() { + return &poolManager; +} + +dur_millis_t InternalErrorReporter::getPeriodicOperationFrequency() const { + return this->executingTask->getPeriodMs(); +} + +LocalPoolDataSetBase* InternalErrorReporter::getDataSetHandle(sid_t sid) { + return &internalErrorDataset; +} + +void InternalErrorReporter::setTaskIF(PeriodicTaskIF *task) { + this->executingTask = task; +} + +ReturnValue_t InternalErrorReporter::initialize() { + ReturnValue_t result = poolManager.initialize(commandQueue); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + return SystemObject::initialize(); +} + +ReturnValue_t InternalErrorReporter::initializeAfterTaskCreation() { + return poolManager.initializeAfterTaskCreation(); +} + diff --git a/internalError/InternalErrorReporter.h b/internalError/InternalErrorReporter.h index 9aa24a0f..8d33c06e 100644 --- a/internalError/InternalErrorReporter.h +++ b/internalError/InternalErrorReporter.h @@ -1,37 +1,75 @@ -#ifndef INTERNALERRORREPORTER_H_ -#define INTERNALERRORREPORTER_H_ +#ifndef FSFW_INTERNALERROR_INTERNALERRORREPORTER_H_ +#define FSFW_INTERNALERROR_INTERNALERRORREPORTER_H_ #include "InternalErrorReporterIF.h" +#include "../tasks/PeriodicTaskIF.h" +#include "../internalError/InternalErrorDataset.h" +#include "../datapoollocal/LocalDataPoolManager.h" #include "../tasks/ExecutableObjectIF.h" #include "../objectmanager/SystemObject.h" #include "../ipc/MutexIF.h" +/** + * @brief This class is used to track internal errors like lost telemetry, + * failed message sending or a full store. + * @details + * All functions were kept virtual so this class can be extended easily + * to store custom internal errors (e.g. communication interface errors). + */ class InternalErrorReporter: public SystemObject, public ExecutableObjectIF, - public InternalErrorReporterIF { + public InternalErrorReporterIF, + public HasLocalDataPoolIF { public: - InternalErrorReporter(object_id_t setObjectId, uint32_t queuePoolId, - uint32_t tmPoolId, uint32_t storePoolId); + static constexpr uint8_t INTERNAL_ERROR_MUTEX_TIMEOUT = 20; + + InternalErrorReporter(object_id_t setObjectId, + uint32_t messageQueueDepth = 5); + + /** + * Enable diagnostic printout. Please note that this feature will + * only work if DEBUG has been supplied to the build defines. + * @param enable + */ + void setDiagnosticPrintout(bool enable); + virtual ~InternalErrorReporter(); - virtual ReturnValue_t performOperation(uint8_t opCode); + virtual object_id_t getObjectId() const override; + virtual MessageQueueId_t getCommandQueue() const override; + virtual ReturnValue_t initializeLocalDataPool( + LocalDataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; + virtual LocalDataPoolManager* getHkManagerHandle() override; + virtual dur_millis_t getPeriodicOperationFrequency() const override; + virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; + + virtual ReturnValue_t initialize() override; + virtual ReturnValue_t initializeAfterTaskCreation() override; + virtual ReturnValue_t performOperation(uint8_t opCode) override; virtual void queueMessageNotSent(); virtual void lostTm(); virtual void storeFull(); + + virtual void setTaskIF(PeriodicTaskIF* task) override; protected: - MutexIF* mutex; + MessageQueueIF* commandQueue; + LocalDataPoolManager poolManager; - uint32_t queuePoolId; - uint32_t tmPoolId; - uint32_t storePoolId; + PeriodicTaskIF* executingTask = nullptr; + MutexIF* mutex = nullptr; + sid_t internalErrorSid; + InternalErrorDataset internalErrorDataset; - uint32_t queueHits; - uint32_t tmHits; - uint32_t storeHits; + bool diagnosticPrintout = true; + + uint32_t queueHits = 0; + uint32_t tmHits = 0; + uint32_t storeHits = 0; uint32_t getAndResetQueueHits(); uint32_t getQueueHits(); @@ -47,4 +85,4 @@ protected: }; -#endif /* INTERNALERRORREPORTER_H_ */ +#endif /* FSFW_INTERNALERROR_INTERNALERRORREPORTER_H_ */ From c6db9944fd57d934fed5a54f0816feeea346ebf1 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Dec 2020 13:26:50 +0100 Subject: [PATCH 086/119] changelog update --- CHANGELOG | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index a6686478..b8f589f8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,3 +11,10 @@ a C file without issues - It is now possible to change the message queue depth for the telecommand verification service (PUS1) - The same is possible for the event reporting service (PUS5) - PUS Health Service added, which allows to command and retrieve health via PUS packets + +### Internal Error Reporter + +- The new internal error reporter uses the local data pools. The pool IDs for +the exisiting three error values and the new error set will be hardcoded for +now, the the constructor for the internal error reporter just takes an object +ID for now. From 03aacea4ddd5f0ab293dd319ddad2860c99cd6aa Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 3 Dec 2020 14:44:11 +0100 Subject: [PATCH 087/119] fixed unittest --- unittest/core/CatchFactory.cpp | 60 ++++ .../objects/Factory.h => core/CatchFactory.h} | 0 unittest/core/CatchSetup.cpp | 3 +- unittest/testcfg/objects/Factory.cpp | 34 --- unittest/tests/action/TestActionHelper.cpp | 213 +++++++------- unittest/tests/action/TestActionHelper.h | 263 +++++++++--------- 6 files changed, 300 insertions(+), 273 deletions(-) create mode 100644 unittest/core/CatchFactory.cpp rename unittest/{testcfg/objects/Factory.h => core/CatchFactory.h} (100%) delete mode 100644 unittest/testcfg/objects/Factory.cpp diff --git a/unittest/core/CatchFactory.cpp b/unittest/core/CatchFactory.cpp new file mode 100644 index 00000000..cf10e94a --- /dev/null +++ b/unittest/core/CatchFactory.cpp @@ -0,0 +1,60 @@ +#include "CatchFactory.h" + +#include "../../events/EventManager.h" +#include "../../health/HealthTable.h" + +#include "../../internalError/InternalErrorReporter.h" +#include "../../objectmanager/frameworkObjects.h" +#include "../../storagemanager/PoolManager.h" + +/** + * @brief Produces system objects. + * @details + * Build tasks by using SystemObject Interface (Interface). + * Header files of all tasks must be included + * Please note that an object has to implement the system object interface + * if the interface validity is checked or retrieved later by using the + * get(object_id) function from the ObjectManagerIF. + * + * Framework objects are created first. + * + * @ingroup init + */ +void Factory::produce(void) { + setStaticFrameworkObjectIds(); + new EventManager(objects::EVENT_MANAGER); + new HealthTable(objects::HEALTH_TABLE); + //new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); + + { + static constexpr uint8_t NUMBER_OF_POOLS = 5; + const uint16_t element_sizes[NUMBER_OF_POOLS] = {16, 32, 64, 128, 1024}; + const uint16_t n_elements[NUMBER_OF_POOLS] = {100, 50, 25, 15, 5}; + new PoolManager(objects::TC_STORE, element_sizes, + n_elements); + } + + { + static constexpr uint8_t NUMBER_OF_POOLS = 5; + const uint16_t element_sizes[NUMBER_OF_POOLS] = {16, 32, 64, 128, 1024}; + const uint16_t n_elements[NUMBER_OF_POOLS] = {100, 50, 25, 15, 5}; + new PoolManager(objects::TM_STORE, element_sizes, + n_elements); + } + + { + static constexpr uint8_t NUMBER_OF_POOLS = 6; + const uint16_t element_sizes[NUMBER_OF_POOLS] = {32, 64, 512, + 1024, 2048, 4096}; + const uint16_t n_elements[NUMBER_OF_POOLS] = {200, 100, 50, 25, 15, 5}; + new PoolManager(objects::IPC_STORE, element_sizes, + n_elements); + } + +} + +void Factory::setStaticFrameworkObjectIds() { + +} + + diff --git a/unittest/testcfg/objects/Factory.h b/unittest/core/CatchFactory.h similarity index 100% rename from unittest/testcfg/objects/Factory.h rename to unittest/core/CatchFactory.h diff --git a/unittest/core/CatchSetup.cpp b/unittest/core/CatchSetup.cpp index f8543fd2..cb5bd33e 100644 --- a/unittest/core/CatchSetup.cpp +++ b/unittest/core/CatchSetup.cpp @@ -1,8 +1,7 @@ #include "CatchDefinitions.h" +#include "CatchFactory.h" #include -#include - #ifdef GCOV #include diff --git a/unittest/testcfg/objects/Factory.cpp b/unittest/testcfg/objects/Factory.cpp deleted file mode 100644 index e05b7942..00000000 --- a/unittest/testcfg/objects/Factory.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "Factory.h" - -#include -#include - -#include -#include - -/** - * @brief Produces system objects. - * @details - * Build tasks by using SystemObject Interface (Interface). - * Header files of all tasks must be included - * Please note that an object has to implement the system object interface - * if the interface validity is checked or retrieved later by using the - * get(object_id) function from the ObjectManagerIF. - * - * Framework objects are created first. - * - * @ingroup init - */ -void Factory::produce(void) { - setStaticFrameworkObjectIds(); - new EventManager(objects::EVENT_MANAGER); - new HealthTable(objects::HEALTH_TABLE); - new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); - -} - -void Factory::setStaticFrameworkObjectIds() { - -} - - diff --git a/unittest/tests/action/TestActionHelper.cpp b/unittest/tests/action/TestActionHelper.cpp index d5b2e467..944ad705 100644 --- a/unittest/tests/action/TestActionHelper.cpp +++ b/unittest/tests/action/TestActionHelper.cpp @@ -1,106 +1,107 @@ -//#include "TestActionHelper.h" -//#include -//#include -//#include -//#include "../../core/CatchDefinitions.h" -// -// -//TEST_CASE( "Action Helper" , "[ActionHelper]") { -// ActionHelperOwnerMockBase testDhMock; -// MessageQueueMockBase testMqMock; -// ActionHelper actionHelper = ActionHelper( -// &testDhMock, dynamic_cast(&testMqMock)); -// CommandMessage actionMessage; -// ActionId_t testActionId = 777; -// std::array testParams {1, 2, 3}; -// store_address_t paramAddress; -// StorageManagerIF *ipcStore = tglob::getIpcStoreHandle(); -// ipcStore->addData(¶mAddress, testParams.data(), 3); -// REQUIRE(actionHelper.initialize() == retval::CATCH_OK); -// -// SECTION ("Simple tests") { -// ActionMessage::setCommand(&actionMessage, testActionId, paramAddress); -// CHECK(not testDhMock.executeActionCalled); -// REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK); -// CHECK(testDhMock.executeActionCalled); -// // No message is sent if everything is alright. -// CHECK(not testMqMock.wasMessageSent()); -// store_address_t invalidAddress; -// ActionMessage::setCommand(&actionMessage, testActionId, invalidAddress); -// actionHelper.handleActionMessage(&actionMessage); -// CHECK(testMqMock.wasMessageSent()); -// const uint8_t* ptr = nullptr; -// size_t size = 0; -// REQUIRE(ipcStore->getData(paramAddress, &ptr, &size) == static_cast(StorageManagerIF::DATA_DOES_NOT_EXIST)); -// REQUIRE(ptr == nullptr); -// REQUIRE(size == 0); -// testDhMock.getBuffer(&ptr, &size); -// REQUIRE(size == 3); -// for(uint8_t i = 0; i<3;i++){ -// REQUIRE(ptr[i] == (i+1)); -// } -// testDhMock.clearBuffer(); -// } -// -// SECTION("Handle failures"){ -// actionMessage.setCommand(1234); -// REQUIRE(actionHelper.handleActionMessage(&actionMessage) == static_cast(CommandMessage::UNKNOWN_COMMAND)); -// CHECK(not testMqMock.wasMessageSent()); -// uint16_t step = 5; -// ReturnValue_t status = 0x1234; -// actionHelper.step(step, testMqMock.getId(), testActionId, status); -// step += 1; -// CHECK(testMqMock.wasMessageSent()); -// CommandMessage testMessage; -// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); -// REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); -// REQUIRE(testMessage.getParameter() == static_cast(testActionId)); -// uint32_t parameter2 = ((uint32_t)step << 16) | (uint32_t)status; -// REQUIRE(testMessage.getParameter2() == parameter2); -// REQUIRE(ActionMessage::getStep(&testMessage) == step); -// } -// -// SECTION("Handle finish"){ -// CHECK(not testMqMock.wasMessageSent()); -// ReturnValue_t status = 0x9876; -// actionHelper.finish(testMqMock.getId(), testActionId, status); -// CHECK(testMqMock.wasMessageSent()); -// CommandMessage testMessage; -// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); -// REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::COMPLETION_FAILED)); -// REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId); -// REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast(status)); -// } -// -// SECTION("Handle failed"){ -// store_address_t toLongParamAddress = StorageManagerIF::INVALID_ADDRESS; -// std::array toLongData = {5, 4, 3, 2, 1}; -// REQUIRE(ipcStore->addData(&toLongParamAddress, toLongData.data(), 5) == retval::CATCH_OK); -// ActionMessage::setCommand(&actionMessage, testActionId, toLongParamAddress); -// CHECK(not testDhMock.executeActionCalled); -// REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK); -// REQUIRE(ipcStore->getData(toLongParamAddress).first == static_cast(StorageManagerIF::DATA_DOES_NOT_EXIST)); -// CommandMessage testMessage; -// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); -// REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); -// REQUIRE(ActionMessage::getReturnCode(&testMessage) == 0xAFFE); -// REQUIRE(ActionMessage::getStep(&testMessage) == 0); -// REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId); -// } -// -// SECTION("Missing IPC Data"){ -// ActionMessage::setCommand(&actionMessage, testActionId, StorageManagerIF::INVALID_ADDRESS); -// CHECK(not testDhMock.executeActionCalled); -// REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK); -// CommandMessage testMessage; -// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); -// REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); -// REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast(StorageManagerIF::ILLEGAL_STORAGE_ID)); -// REQUIRE(ActionMessage::getStep(&testMessage) == 0); -// } -// -// -// SECTION("Data Reply"){ -// -// } -//} +#include "TestActionHelper.h" +#include +#include +#include +#include "../../core/CatchDefinitions.h" + + +TEST_CASE( "Action Helper" , "[ActionHelper]") { + ActionHelperOwnerMockBase testDhMock; + MessageQueueMockBase testMqMock; + ActionHelper actionHelper = ActionHelper( + &testDhMock, dynamic_cast(&testMqMock)); + CommandMessage actionMessage; + ActionId_t testActionId = 777; + std::array testParams {1, 2, 3}; + store_address_t paramAddress; + StorageManagerIF *ipcStore = tglob::getIpcStoreHandle(); + REQUIRE(ipcStore != nullptr); + ipcStore->addData(¶mAddress, testParams.data(), 3); + REQUIRE(actionHelper.initialize() == retval::CATCH_OK); + + SECTION ("Simple tests") { + ActionMessage::setCommand(&actionMessage, testActionId, paramAddress); + CHECK(not testDhMock.executeActionCalled); + REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK); + CHECK(testDhMock.executeActionCalled); + // No message is sent if everything is alright. + CHECK(not testMqMock.wasMessageSent()); + store_address_t invalidAddress; + ActionMessage::setCommand(&actionMessage, testActionId, invalidAddress); + actionHelper.handleActionMessage(&actionMessage); + CHECK(testMqMock.wasMessageSent()); + const uint8_t* ptr = nullptr; + size_t size = 0; + REQUIRE(ipcStore->getData(paramAddress, &ptr, &size) == static_cast(StorageManagerIF::DATA_DOES_NOT_EXIST)); + REQUIRE(ptr == nullptr); + REQUIRE(size == 0); + testDhMock.getBuffer(&ptr, &size); + REQUIRE(size == 3); + for(uint8_t i = 0; i<3;i++){ + REQUIRE(ptr[i] == (i+1)); + } + testDhMock.clearBuffer(); + } + + SECTION("Handle failures"){ + actionMessage.setCommand(1234); + REQUIRE(actionHelper.handleActionMessage(&actionMessage) == static_cast(CommandMessage::UNKNOWN_COMMAND)); + CHECK(not testMqMock.wasMessageSent()); + uint16_t step = 5; + ReturnValue_t status = 0x1234; + actionHelper.step(step, testMqMock.getId(), testActionId, status); + step += 1; + CHECK(testMqMock.wasMessageSent()); + CommandMessage testMessage; + REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); + REQUIRE(testMessage.getParameter() == static_cast(testActionId)); + uint32_t parameter2 = ((uint32_t)step << 16) | (uint32_t)status; + REQUIRE(testMessage.getParameter2() == parameter2); + REQUIRE(ActionMessage::getStep(&testMessage) == step); + } + + SECTION("Handle finish"){ + CHECK(not testMqMock.wasMessageSent()); + ReturnValue_t status = 0x9876; + actionHelper.finish(testMqMock.getId(), testActionId, status); + CHECK(testMqMock.wasMessageSent()); + CommandMessage testMessage; + REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::COMPLETION_FAILED)); + REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId); + REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast(status)); + } + + SECTION("Handle failed"){ + store_address_t toLongParamAddress = StorageManagerIF::INVALID_ADDRESS; + std::array toLongData = {5, 4, 3, 2, 1}; + REQUIRE(ipcStore->addData(&toLongParamAddress, toLongData.data(), 5) == retval::CATCH_OK); + ActionMessage::setCommand(&actionMessage, testActionId, toLongParamAddress); + CHECK(not testDhMock.executeActionCalled); + REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK); + REQUIRE(ipcStore->getData(toLongParamAddress).first == static_cast(StorageManagerIF::DATA_DOES_NOT_EXIST)); + CommandMessage testMessage; + REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); + REQUIRE(ActionMessage::getReturnCode(&testMessage) == 0xAFFE); + REQUIRE(ActionMessage::getStep(&testMessage) == 0); + REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId); + } + + SECTION("Missing IPC Data"){ + ActionMessage::setCommand(&actionMessage, testActionId, StorageManagerIF::INVALID_ADDRESS); + CHECK(not testDhMock.executeActionCalled); + REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK); + CommandMessage testMessage; + REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); + REQUIRE(testMessage.getCommand() == static_cast(ActionMessage::STEP_FAILED)); + REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast(StorageManagerIF::ILLEGAL_STORAGE_ID)); + REQUIRE(ActionMessage::getStep(&testMessage) == 0); + } + + + SECTION("Data Reply"){ + + } +} diff --git a/unittest/tests/action/TestActionHelper.h b/unittest/tests/action/TestActionHelper.h index 9bc93d3e..4ba417eb 100644 --- a/unittest/tests/action/TestActionHelper.h +++ b/unittest/tests/action/TestActionHelper.h @@ -1,131 +1,132 @@ -//#ifndef UNITTEST_HOSTED_TESTACTIONHELPER_H_ -//#define UNITTEST_HOSTED_TESTACTIONHELPER_H_ -// -//#include -//#include -//#include -//#include -// -// -//class ActionHelperOwnerMockBase: public HasActionsIF { -//public: -// bool getCommandQueueCalled = false; -// bool executeActionCalled = false; -// static const size_t MAX_SIZE = 3; -// uint8_t buffer[MAX_SIZE] = {0, 0, 0}; -// size_t size = 0; -// -// MessageQueueId_t getCommandQueue() const override { -// return tconst::testQueueId; -// } -// -// ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, -// const uint8_t* data, size_t size) override { -// executeActionCalled = true; -// if(size > MAX_SIZE){ -// return 0xAFFE; -// } -// this->size = size; -// memcpy(buffer, data, size); -// return HasReturnvaluesIF::RETURN_OK; -// } -// -// void clearBuffer(){ -// this->size = 0; -// for(size_t i = 0; isize; -// } -// if(ptr != nullptr){ -// *ptr = buffer; -// } -// } -//}; -// -// -//class MessageQueueMockBase: public MessageQueueIF { -//public: -// MessageQueueId_t myQueueId = 0; -// bool defaultDestSet = false; -// bool messageSent = false; -// -// -// -// bool wasMessageSent() { -// bool tempMessageSent = messageSent; -// messageSent = false; -// return tempMessageSent; -// } -// -// virtual ReturnValue_t reply( MessageQueueMessage* message ) { -// messageSent = true; -// lastMessage = (*message); -// return HasReturnvaluesIF::RETURN_OK; -// }; -// virtual ReturnValue_t receiveMessage(MessageQueueMessage* message, -// MessageQueueId_t *receivedFrom) { -// (*message) = lastMessage; -// lastMessage.clear(); -// return HasReturnvaluesIF::RETURN_OK; -// } -// virtual ReturnValue_t receiveMessage(MessageQueueMessage* message) { -// (*message) = lastMessage; -// lastMessage.clear(); -// return HasReturnvaluesIF::RETURN_OK; -// } -// virtual ReturnValue_t flush(uint32_t* count) { -// return HasReturnvaluesIF::RETURN_OK; -// } -// virtual MessageQueueId_t getLastPartner() const { -// return tconst::testQueueId; -// } -// virtual MessageQueueId_t getId() const { -// return tconst::testQueueId; -// } -// virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, -// MessageQueueMessage* message, MessageQueueId_t sentFrom, -// bool ignoreFault = false ) { -// messageSent = true; -// lastMessage = (*message); -// return HasReturnvaluesIF::RETURN_OK; -// } -// virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo, -// MessageQueueMessage* message, bool ignoreFault = false ) override { -// messageSent = true; -// lastMessage = (*message); -// return HasReturnvaluesIF::RETURN_OK; -// } -// virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessage* message, -// MessageQueueId_t sentFrom, bool ignoreFault = false ) { -// messageSent = true; -// lastMessage = (*message); -// return HasReturnvaluesIF::RETURN_OK; -// } -// virtual ReturnValue_t sendToDefault( MessageQueueMessage* message ) { -// messageSent = true; -// lastMessage = (*message); -// return HasReturnvaluesIF::RETURN_OK; -// } -// virtual void setDefaultDestination(MessageQueueId_t defaultDestination) { -// myQueueId = defaultDestination; -// defaultDestSet = true; -// } -// -// virtual MessageQueueId_t getDefaultDestination() const { -// return myQueueId; -// } -// virtual bool isDefaultDestinationSet() const { -// return defaultDestSet; -// } -//private: -// MessageQueueMessage lastMessage; -// -//}; -// -// -//#endif /* UNITTEST_TESTFW_NEWTESTS_TESTACTIONHELPER_H_ */ +#ifndef UNITTEST_HOSTED_TESTACTIONHELPER_H_ +#define UNITTEST_HOSTED_TESTACTIONHELPER_H_ + +#include +#include +#include +#include + + +class ActionHelperOwnerMockBase: public HasActionsIF { +public: + bool getCommandQueueCalled = false; + bool executeActionCalled = false; + static const size_t MAX_SIZE = 3; + uint8_t buffer[MAX_SIZE] = {0, 0, 0}; + size_t size = 0; + + MessageQueueId_t getCommandQueue() const override { + return tconst::testQueueId; + } + + ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, + const uint8_t* data, size_t size) override { + executeActionCalled = true; + if(size > MAX_SIZE){ + return 0xAFFE; + } + this->size = size; + memcpy(buffer, data, size); + return HasReturnvaluesIF::RETURN_OK; + } + + void clearBuffer(){ + this->size = 0; + for(size_t i = 0; isize; + } + if(ptr != nullptr){ + *ptr = buffer; + } + } +}; + + +class MessageQueueMockBase: public MessageQueueIF { +public: + MessageQueueId_t myQueueId = 0; + bool defaultDestSet = false; + bool messageSent = false; + + + + bool wasMessageSent() { + bool tempMessageSent = messageSent; + messageSent = false; + return tempMessageSent; + } + + virtual ReturnValue_t reply( MessageQueueMessageIF* message ) { + messageSent = true; + lastMessage = *(dynamic_cast(message)); + return HasReturnvaluesIF::RETURN_OK; + }; + virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message, + MessageQueueId_t *receivedFrom) { + (*message) = lastMessage; + lastMessage.clear(); + return HasReturnvaluesIF::RETURN_OK; + } + virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) { + memcpy(message->getBuffer(), lastMessage.getBuffer(), + message->getMessageSize()); + lastMessage.clear(); + return HasReturnvaluesIF::RETURN_OK; + } + virtual ReturnValue_t flush(uint32_t* count) { + return HasReturnvaluesIF::RETURN_OK; + } + virtual MessageQueueId_t getLastPartner() const { + return tconst::testQueueId; + } + virtual MessageQueueId_t getId() const { + return tconst::testQueueId; + } + virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault = false ) { + messageSent = true; + lastMessage = *(dynamic_cast(message)); + return HasReturnvaluesIF::RETURN_OK; + } + virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo, + MessageQueueMessageIF* message, bool ignoreFault = false ) override { + messageSent = true; + lastMessage = *(dynamic_cast(message)); + return HasReturnvaluesIF::RETURN_OK; + } + virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, + MessageQueueId_t sentFrom, bool ignoreFault = false ) { + messageSent = true; + lastMessage = *(dynamic_cast(message)); + return HasReturnvaluesIF::RETURN_OK; + } + virtual ReturnValue_t sendToDefault( MessageQueueMessageIF* message ) { + messageSent = true; + lastMessage = *(dynamic_cast(message)); + return HasReturnvaluesIF::RETURN_OK; + } + virtual void setDefaultDestination(MessageQueueId_t defaultDestination) { + myQueueId = defaultDestination; + defaultDestSet = true; + } + + virtual MessageQueueId_t getDefaultDestination() const { + return myQueueId; + } + virtual bool isDefaultDestinationSet() const { + return defaultDestSet; + } +private: + MessageQueueMessage lastMessage; + +}; + + +#endif /* UNITTEST_TESTFW_NEWTESTS_TESTACTIONHELPER_H_ */ From 9781105ad0378ee66d14d93c5994f01602a091ae Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 3 Dec 2020 14:50:28 +0100 Subject: [PATCH 088/119] moved catch factory again --- unittest/{core => testcfg}/CatchFactory.cpp | 10 +++++----- unittest/{core => testcfg}/CatchFactory.h | 0 unittest/testcfg/testcfg.mk | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) rename unittest/{core => testcfg}/CatchFactory.cpp (87%) rename unittest/{core => testcfg}/CatchFactory.h (100%) diff --git a/unittest/core/CatchFactory.cpp b/unittest/testcfg/CatchFactory.cpp similarity index 87% rename from unittest/core/CatchFactory.cpp rename to unittest/testcfg/CatchFactory.cpp index cf10e94a..c74a8126 100644 --- a/unittest/core/CatchFactory.cpp +++ b/unittest/testcfg/CatchFactory.cpp @@ -1,11 +1,11 @@ #include "CatchFactory.h" -#include "../../events/EventManager.h" -#include "../../health/HealthTable.h" +#include +#include -#include "../../internalError/InternalErrorReporter.h" -#include "../../objectmanager/frameworkObjects.h" -#include "../../storagemanager/PoolManager.h" +#include +#include +#include /** * @brief Produces system objects. diff --git a/unittest/core/CatchFactory.h b/unittest/testcfg/CatchFactory.h similarity index 100% rename from unittest/core/CatchFactory.h rename to unittest/testcfg/CatchFactory.h diff --git a/unittest/testcfg/testcfg.mk b/unittest/testcfg/testcfg.mk index 64fa87f6..31d3b60a 100644 --- a/unittest/testcfg/testcfg.mk +++ b/unittest/testcfg/testcfg.mk @@ -3,6 +3,7 @@ CXXSRC += $(wildcard $(CURRENTPATH)/ipc/*.cpp) CXXSRC += $(wildcard $(CURRENTPATH)/objects/*.cpp) CXXSRC += $(wildcard $(CURRENTPATH)/pollingsequence/*.cpp) CXXSRC += $(wildcard $(CURRENTPATH)/events/*.cpp) +CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp) INCLUDES += $(CURRENTPATH) INCLUDES += $(CURRENTPATH)/objects From ec3c83bcc1e9cbf587b4602fa3d65d9f797c781e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Dec 2020 18:24:51 +0100 Subject: [PATCH 089/119] monitoring update --- monitoring/AbsLimitMonitor.h | 28 +++++++----- monitoring/HasMonitorsIF.h | 12 ++--- monitoring/LimitMonitor.h | 27 ++++++----- monitoring/MonitorBase.h | 64 +++++++++++++++++---------- monitoring/MonitorReporter.h | 37 +++++++++------- monitoring/MonitoringIF.h | 8 ++-- monitoring/MonitoringMessageContent.h | 37 ++++++++++------ monitoring/TriplexMonitor.h | 2 +- 8 files changed, 127 insertions(+), 88 deletions(-) diff --git a/monitoring/AbsLimitMonitor.h b/monitoring/AbsLimitMonitor.h index 2e60f6f8..5feb369c 100644 --- a/monitoring/AbsLimitMonitor.h +++ b/monitoring/AbsLimitMonitor.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_MONITORING_ABSLIMITMONITOR_H_ -#define FRAMEWORK_MONITORING_ABSLIMITMONITOR_H_ +#ifndef FSFW_MONITORING_ABSLIMITMONITOR_H_ +#define FSFW_MONITORING_ABSLIMITMONITOR_H_ #include "MonitorBase.h" #include @@ -7,9 +7,14 @@ template class AbsLimitMonitor: public MonitorBase { public: - AbsLimitMonitor(object_id_t reporterId, uint8_t monitorId, uint32_t parameterId, - uint16_t confirmationLimit, T limit, Event violationEvent = MonitoringIF::VALUE_OUT_OF_RANGE, bool aboveIsViolation = true) : - MonitorBase(reporterId, monitorId, parameterId, confirmationLimit), limit(limit), violationEvent(violationEvent), aboveIsViolation(aboveIsViolation) { + AbsLimitMonitor(object_id_t reporterId, uint8_t monitorId, + gp_id_t globalPoolId, uint16_t confirmationLimit, T limit, + Event violationEvent = MonitoringIF::VALUE_OUT_OF_RANGE, + bool aboveIsViolation = true) : + MonitorBase(reporterId, monitorId, globalPoolId, + confirmationLimit), + limit(limit), violationEvent(violationEvent), + aboveIsViolation(aboveIsViolation) { } virtual ~AbsLimitMonitor() { } @@ -32,8 +37,9 @@ public: const ParameterWrapper *newValues, uint16_t startAtIndex) { ReturnValue_t result = this->MonitorBase::getParameter(domainId, parameterId, parameterWrapper, newValues, startAtIndex); - //We'll reuse the DOMAIN_ID of MonitorReporter, as we know the parameterIds used there. - if (result != this->INVALID_MATRIX_ID) { + // We'll reuse the DOMAIN_ID of MonitorReporter, + // as we know the parameterIds used there. + if (result != this->INVALID_IDENTIFIER_ID) { return result; } switch (parameterId) { @@ -41,7 +47,7 @@ public: parameterWrapper->set(this->limit); break; default: - return this->INVALID_MATRIX_ID; + return this->INVALID_IDENTIFIER_ID; } return HasReturnvaluesIF::RETURN_OK; } @@ -59,7 +65,9 @@ protected: void sendTransitionEvent(T currentValue, ReturnValue_t state) { switch (state) { case MonitoringIF::OUT_OF_RANGE: - EventManagerIF::triggerEvent(this->reportingId, violationEvent, this->parameterId); + EventManagerIF::triggerEvent(this->reportingId, + violationEvent, this->globalPoolId.objectId, + this->globalPoolId.localPoolId); break; default: break; @@ -70,4 +78,4 @@ protected: const bool aboveIsViolation; }; -#endif /* FRAMEWORK_MONITORING_ABSLIMITMONITOR_H_ */ +#endif /* FSFW_MONITORING_ABSLIMITMONITOR_H_ */ diff --git a/monitoring/HasMonitorsIF.h b/monitoring/HasMonitorsIF.h index 85d92b6b..04f63437 100644 --- a/monitoring/HasMonitorsIF.h +++ b/monitoring/HasMonitorsIF.h @@ -1,11 +1,5 @@ -/** - * @file HasMonitorsIF.h - * @brief This file defines the HasMonitorsIF class. - * @date 28.07.2014 - * @author baetz - */ -#ifndef HASMONITORSIF_H_ -#define HASMONITORSIF_H_ +#ifndef FSFW_MONITORING_HASMONITORSIF_H_ +#define FSFW_MONITORING_HASMONITORSIF_H_ #include "../events/EventReportingProxyIF.h" #include "../objectmanager/ObjectManagerIF.h" @@ -27,4 +21,4 @@ public: } }; -#endif /* HASMONITORSIF_H_ */ +#endif /* FSFW_MONITORING_HASMONITORSIF_H_ */ diff --git a/monitoring/LimitMonitor.h b/monitoring/LimitMonitor.h index 66e6725e..c4448ced 100644 --- a/monitoring/LimitMonitor.h +++ b/monitoring/LimitMonitor.h @@ -12,13 +12,15 @@ template class LimitMonitor: public MonitorBase { public: - LimitMonitor(object_id_t reporterId, uint8_t monitorId, uint32_t parameterId, - uint16_t confirmationLimit, T lowerLimit, T upperLimit, - Event belowLowEvent = MonitoringIF::VALUE_BELOW_LOW_LIMIT, + LimitMonitor(object_id_t reporterId, uint8_t monitorId, + gp_id_t globalPoolId, 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) { + MonitorBase(reporterId, monitorId, globalPoolId, + confirmationLimit), + lowerLimit(lowerLimit), upperLimit(upperLimit), + belowLowEvent(belowLowEvent), aboveHighEvent(aboveHighEvent) { } virtual ~LimitMonitor() { } @@ -41,7 +43,7 @@ public: ReturnValue_t result = this->MonitorBase::getParameter(domainId, parameterId, parameterWrapper, newValues, startAtIndex); //We'll reuse the DOMAIN_ID of MonitorReporter, as we know the parameterIds used there. - if (result != this->INVALID_MATRIX_ID) { + if (result != this->INVALID_IDENTIFIER_ID) { return result; } switch (parameterId) { @@ -52,12 +54,13 @@ public: parameterWrapper->set(this->upperLimit); break; default: - return this->INVALID_MATRIX_ID; + return this->INVALID_IDENTIFIER_ID; } return HasReturnvaluesIF::RETURN_OK; } bool isOutOfLimits() { - if (this->oldState == MonitoringIF::ABOVE_HIGH_LIMIT || this->oldState == MonitoringIF::BELOW_LOW_LIMIT) { + if (this->oldState == MonitoringIF::ABOVE_HIGH_LIMIT or + this->oldState == MonitoringIF::BELOW_LOW_LIMIT) { return true; } else { return false; @@ -76,10 +79,12 @@ protected: void sendTransitionEvent(T currentValue, ReturnValue_t state) { switch (state) { case MonitoringIF::BELOW_LOW_LIMIT: - EventManagerIF::triggerEvent(this->reportingId, belowLowEvent, this->parameterId); + EventManagerIF::triggerEvent(this->reportingId, belowLowEvent, + this->globalPoolId.objectId, this->globalPoolId.localPoolId); break; case MonitoringIF::ABOVE_HIGH_LIMIT: - EventManagerIF::triggerEvent(this->reportingId, aboveHighEvent, this->parameterId); + EventManagerIF::triggerEvent(this->reportingId, aboveHighEvent, + this->globalPoolId.objectId, this->globalPoolId.localPoolId); break; default: break; diff --git a/monitoring/MonitorBase.h b/monitoring/MonitorBase.h index b2d0e6cb..530a3840 100644 --- a/monitoring/MonitorBase.h +++ b/monitoring/MonitorBase.h @@ -1,39 +1,50 @@ -#ifndef MONITORBASE_H_ -#define MONITORBASE_H_ +#ifndef FSFW_MONITORING_MONITORBASE_H_ +#define FSFW_MONITORING_MONITORBASE_H_ -#include "../datapool/DataSet.h" -#include "../datapool/PIDReader.h" #include "LimitViolationReporter.h" #include "MonitoringIF.h" #include "MonitoringMessageContent.h" #include "MonitorReporter.h" +#include "../datapoollocal/LocalPoolVariable.h" + + /** - * Base class for monitoring of parameters. - * Can be used anywhere, specializations need to implement checkSample and should override sendTransitionEvent. - * Manages state handling, enabling and disabling of events/reports and forwarding of transition - * reports via MonitorReporter. In addition, it provides default implementations for fetching the parameter sample from - * the data pool and a simple confirmation counter. + * @brief Base class for monitoring of parameters. + * @details + * Can be used anywhere, specializations need to implement checkSample and + * should override sendTransitionEvent. + * Manages state handling, enabling and disabling of events/reports and + * forwarding of transition reports via MonitorReporter. + * + * In addition, it provides default implementations for fetching the + * parameter sample from the data pool and a simple confirmation counter. */ template class MonitorBase: public MonitorReporter { public: + MonitorBase(object_id_t reporterId, uint8_t monitorId, - uint32_t parameterId, uint16_t confirmationLimit) : - MonitorReporter(reporterId, monitorId, parameterId, confirmationLimit) { + gp_id_t globalPoolId, uint16_t confirmationLimit): + MonitorReporter(reporterId, monitorId, globalPoolId, + confirmationLimit), + poolVariable(globalPoolId) { } + virtual ~MonitorBase() { } + virtual ReturnValue_t check() { - //1. Fetch sample of type T, return validity. + // 1. Fetch sample of type T, return validity. T sample = 0; ReturnValue_t validity = fetchSample(&sample); - //2. If returning from fetch != OK, parameter is invalid. Report (if oldState is != invalidity). + // 2. If returning from fetch != OK, parameter is invalid. + // Report (if oldState is != invalidity). if (validity != HasReturnvaluesIF::RETURN_OK) { this->monitorStateIs(validity, sample, 0); - //3. Otherwise, check sample. } else { + //3. Otherwise, check sample. this->oldState = doCheck(sample); } return this->oldState; @@ -43,20 +54,25 @@ public: ReturnValue_t currentState = checkSample(sample, &crossedLimit); return this->monitorStateIs(currentState,sample, crossedLimit); } - //Abstract or default. + + // Abstract or default. virtual ReturnValue_t checkSample(T sample, T* crossedLimit) = 0; protected: + virtual ReturnValue_t fetchSample(T* sample) { - DataSet mySet; - PIDReader parameter(this->parameterId, &mySet); - mySet.read(); - if (!parameter.isValid()) { - return MonitoringIF::INVALID; - } - *sample = parameter.value; - return HasReturnvaluesIF::RETURN_OK; + ReturnValue_t result = poolVariable.read(); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + if (not poolVariable.isValid()) { + return MonitoringIF::INVALID; + } + *sample = poolVariable.value; + return HasReturnvaluesIF::RETURN_OK; } + + LocalPoolVar poolVariable; }; -#endif /* MONITORBASE_H_ */ +#endif /* FSFW_MONITORING_MONITORBASE_H_ */ diff --git a/monitoring/MonitorReporter.h b/monitoring/MonitorReporter.h index ca2b534b..9028e7e4 100644 --- a/monitoring/MonitorReporter.h +++ b/monitoring/MonitorReporter.h @@ -1,10 +1,12 @@ -#ifndef FRAMEWORK_MONITORING_MONITORREPORTER_H_ -#define FRAMEWORK_MONITORING_MONITORREPORTER_H_ +#ifndef FSFW_MONITORING_MONITORREPORTER_H_ +#define FSFW_MONITORING_MONITORREPORTER_H_ -#include "../events/EventManagerIF.h" #include "LimitViolationReporter.h" #include "MonitoringIF.h" #include "MonitoringMessageContent.h" + +#include "../datapoollocal/locPoolDefinitions.h" +#include "../events/EventManagerIF.h" #include "../parameters/HasParametersIF.h" template @@ -14,11 +16,14 @@ public: static const uint8_t ENABLED = 1; static const uint8_t DISABLED = 0; - MonitorReporter(object_id_t reportingId, uint8_t monitorId, uint32_t parameterId, uint16_t confirmationLimit) : - monitorId(monitorId), parameterId(parameterId), reportingId( - reportingId), oldState(MonitoringIF::UNCHECKED), reportingEnabled( - ENABLED), eventEnabled(ENABLED), currentCounter(0), confirmationLimit( - confirmationLimit) { + // TODO: Adapt to use SID instead of parameter ID. + + MonitorReporter(object_id_t reportingId, uint8_t monitorId, + gp_id_t globalPoolId, uint16_t confirmationLimit) : + monitorId(monitorId), globalPoolId(globalPoolId), + reportingId(reportingId), oldState(MonitoringIF::UNCHECKED), + reportingEnabled(ENABLED), eventEnabled(ENABLED), currentCounter(0), + confirmationLimit(confirmationLimit) { } virtual ~MonitorReporter() { @@ -63,7 +68,7 @@ public: parameterWrapper->set(this->eventEnabled); break; default: - return INVALID_MATRIX_ID; + return INVALID_IDENTIFIER_ID; } return HasReturnvaluesIF::RETURN_OK; } @@ -91,7 +96,7 @@ public: protected: const uint8_t monitorId; - const uint32_t parameterId; + const gp_id_t globalPoolId; object_id_t reportingId; ReturnValue_t oldState; @@ -148,7 +153,8 @@ protected: case HasReturnvaluesIF::RETURN_OK: break; default: - EventManagerIF::triggerEvent(reportingId, MonitoringIF::MONITOR_CHANGED_STATE, state); + EventManagerIF::triggerEvent(reportingId, + MonitoringIF::MONITOR_CHANGED_STATE, state); break; } } @@ -159,14 +165,15 @@ protected: * @param crossedLimit The limit crossed (if applicable). * @param state Current state the monitor is in. */ - virtual void sendTransitionReport(T parameterValue, T crossedLimit, ReturnValue_t state) { - MonitoringReportContent report(parameterId, + virtual void sendTransitionReport(T parameterValue, T crossedLimit, + ReturnValue_t state) { + MonitoringReportContent report(globalPoolId, parameterValue, crossedLimit, oldState, state); LimitViolationReporter::sendLimitViolationReport(&report); } ReturnValue_t setToState(ReturnValue_t state) { if (oldState != state && reportingEnabled) { - MonitoringReportContent report(parameterId, 0, 0, oldState, + MonitoringReportContent report(globalPoolId, 0, 0, oldState, state); LimitViolationReporter::sendLimitViolationReport(&report); oldState = state; @@ -175,4 +182,4 @@ protected: } }; -#endif /* FRAMEWORK_MONITORING_MONITORREPORTER_H_ */ +#endif /* FSFW_MONITORING_MONITORREPORTER_H_ */ diff --git a/monitoring/MonitoringIF.h b/monitoring/MonitoringIF.h index 44218c36..aa266f33 100644 --- a/monitoring/MonitoringIF.h +++ b/monitoring/MonitoringIF.h @@ -1,8 +1,8 @@ -#ifndef MONITORINGIF_H_ -#define MONITORINGIF_H_ +#ifndef FSFW_MONITORING_MONITORINGIF_H_ +#define FSFW_MONITORING_MONITORINGIF_H_ -#include "../memory/HasMemoryIF.h" #include "MonitoringMessage.h" +#include "../memory/HasMemoryIF.h" #include "../serialize/SerializeIF.h" class MonitoringIF : public SerializeIF { @@ -64,4 +64,4 @@ public: -#endif /* MONITORINGIF_H_ */ +#endif /* FSFW_MONITORING_MONITORINGIF_H_ */ diff --git a/monitoring/MonitoringMessageContent.h b/monitoring/MonitoringMessageContent.h index c82506f3..44d32656 100644 --- a/monitoring/MonitoringMessageContent.h +++ b/monitoring/MonitoringMessageContent.h @@ -3,6 +3,7 @@ #include "HasMonitorsIF.h" #include "MonitoringIF.h" +#include "../datapoollocal/locPoolDefinitions.h" #include "../objectmanager/ObjectManagerIF.h" #include "../serialize/SerialBufferAdapter.h" #include "../serialize/SerialFixedArrayListAdapter.h" @@ -16,12 +17,17 @@ void setStaticFrameworkObjectIds(); } //PID(uint32_t), TYPE, LIMIT_ID, value,limitValue, previous, later, timestamp +/** + * @brief Does magic. + * @tparam T + */ template class MonitoringReportContent: public SerialLinkedListAdapter { friend void (Factory::setStaticFrameworkObjectIds)(); public: SerializeElement monitorId; - SerializeElement parameterId; + SerializeElement parameterObjectId; + SerializeElement localPoolId; SerializeElement parameterValue; SerializeElement limitValue; SerializeElement oldState; @@ -30,20 +36,23 @@ public: SerializeElement> timestampSerializer; TimeStamperIF* timeStamper; MonitoringReportContent() : - SerialLinkedListAdapter( - LinkedElement::Iterator(¶meterId)), monitorId(0), parameterId( - 0), parameterValue(0), limitValue(0), oldState(0), newState( - 0), rawTimestamp( { 0 }), timestampSerializer(rawTimestamp, + SerialLinkedListAdapter(¶meterObjectId), + monitorId(0), parameterObjectId(0), + localPoolId(0), parameterValue(0), + limitValue(0), oldState(0), newState(0), + rawTimestamp( { 0 }), timestampSerializer(rawTimestamp, sizeof(rawTimestamp)), timeStamper(NULL) { setAllNext(); } - MonitoringReportContent(uint32_t setPID, T value, T limitValue, + MonitoringReportContent(gp_id_t globalPoolId, T value, T limitValue, ReturnValue_t oldState, ReturnValue_t newState) : - SerialLinkedListAdapter( - LinkedElement::Iterator(¶meterId)), monitorId(0), parameterId( - setPID), parameterValue(value), limitValue(limitValue), oldState( - oldState), newState(newState), timestampSerializer(rawTimestamp, - sizeof(rawTimestamp)), timeStamper(NULL) { + SerialLinkedListAdapter(¶meterObjectId), + monitorId(0), parameterObjectId(globalPoolId.objectId), + localPoolId(globalPoolId.localPoolId), + parameterValue(value), limitValue(limitValue), + oldState(oldState), newState(newState), + timestampSerializer(rawTimestamp, sizeof(rawTimestamp)), + timeStamper(NULL) { setAllNext(); if (checkAndSetStamper()) { timeStamper->addTimeStamp(rawTimestamp, sizeof(rawTimestamp)); @@ -53,16 +62,16 @@ private: static object_id_t timeStamperId; void setAllNext() { - parameterId.setNext(¶meterValue); + parameterObjectId.setNext(¶meterValue); parameterValue.setNext(&limitValue); limitValue.setNext(&oldState); oldState.setNext(&newState); newState.setNext(×tampSerializer); } bool checkAndSetStamper() { - if (timeStamper == NULL) { + if (timeStamper == nullptr) { timeStamper = objectManager->get( timeStamperId ); - if ( timeStamper == NULL ) { + if ( timeStamper == nullptr ) { sif::error << "MonitoringReportContent::checkAndSetStamper: " "Stamper not found!" << std::endl; return false; diff --git a/monitoring/TriplexMonitor.h b/monitoring/TriplexMonitor.h index 9b60aeb6..c4b3c537 100644 --- a/monitoring/TriplexMonitor.h +++ b/monitoring/TriplexMonitor.h @@ -82,7 +82,7 @@ public: parameterWrapper->set(limit); break; default: - return INVALID_MATRIX_ID; + return INVALID_IDENTIFIER_ID; } return HasReturnvaluesIF::RETURN_OK; } From 9ba8ef1ae212f9058671399933cf29335cd436f5 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Dec 2020 18:29:28 +0100 Subject: [PATCH 090/119] power update --- power/Fuse.cpp | 33 +++++++++++++++------------- power/Fuse.h | 47 ++++++++++++++++++++++------------------ power/PowerComponent.cpp | 27 ++++++++++------------- power/PowerComponent.h | 24 +++++++++++--------- power/PowerComponentIF.h | 19 ++++++++-------- power/PowerSensor.cpp | 30 ++++++++++++++----------- power/PowerSensor.h | 38 ++++++++++++++++++-------------- power/PowerSwitchIF.h | 22 +++++++++---------- power/PowerSwitcher.cpp | 28 +++++++++++++++--------- power/PowerSwitcher.h | 17 ++++++++++----- 10 files changed, 156 insertions(+), 129 deletions(-) diff --git a/power/Fuse.cpp b/power/Fuse.cpp index 9986ab6b..1c30d83f 100644 --- a/power/Fuse.cpp +++ b/power/Fuse.cpp @@ -1,22 +1,25 @@ +#include "Fuse.h" + #include "../monitoring/LimitViolationReporter.h" #include "../monitoring/MonitoringMessageContent.h" #include "../objectmanager/ObjectManagerIF.h" -#include "Fuse.h" #include "../serialize/SerialFixedArrayListAdapter.h" #include "../ipc/QueueFactory.h" object_id_t Fuse::powerSwitchId = 0; -Fuse::Fuse(object_id_t fuseObjectId, uint8_t fuseId, VariableIds ids, +Fuse::Fuse(object_id_t fuseObjectId, uint8_t fuseId, + sid_t variableSet, VariableIds ids, float maxCurrent, uint16_t confirmationCount) : - SystemObject(fuseObjectId), oldFuseState(0), fuseId(fuseId), powerIF( - NULL), currentLimit(fuseObjectId, 1, ids.pidCurrent, confirmationCount, - maxCurrent, FUSE_CURRENT_HIGH), powerMonitor(fuseObjectId, 2, - DataPool::poolIdAndPositionToPid(ids.poolIdPower, 0), - confirmationCount), set(), voltage(ids.pidVoltage, &set), current( - ids.pidCurrent, &set), state(ids.pidState, &set), power( - ids.poolIdPower, &set, PoolVariableIF::VAR_READ_WRITE), commandQueue( - NULL), parameterHelper(this), healthHelper(this, fuseObjectId) { + SystemObject(fuseObjectId), oldFuseState(0), fuseId(fuseId), + currentLimit(fuseObjectId, 1, ids.pidCurrent, confirmationCount, + maxCurrent, FUSE_CURRENT_HIGH), + powerMonitor(fuseObjectId, 2, ids.poolIdPower, + confirmationCount), + set(variableSet), voltage(ids.pidVoltage, &set), + current(ids.pidCurrent, &set), state(ids.pidState, &set), + power(ids.poolIdPower, &set, PoolVariableIF::VAR_READ_WRITE), + parameterHelper(this), healthHelper(this, fuseObjectId) { commandQueue = QueueFactory::instance()->createMessageQueue(); } @@ -75,7 +78,7 @@ ReturnValue_t Fuse::check() { float lowLimit = 0.0; float highLimit = RESIDUAL_POWER; calculatePowerLimits(&lowLimit, &highLimit); - result = powerMonitor.checkPower(power, lowLimit, highLimit); + result = powerMonitor.checkPower(power.value, lowLimit, highLimit); if (result == MonitoringIF::BELOW_LOW_LIMIT) { reportEvents(POWER_BELOW_LOW_LIMIT); } else if (result == MonitoringIF::ABOVE_HIGH_LIMIT) { @@ -109,7 +112,7 @@ size_t Fuse::getSerializedSize() const { } ReturnValue_t Fuse::deSerialize(const uint8_t** buffer, size_t* size, -Endianness streamEndianness) { + Endianness streamEndianness) { ReturnValue_t result = RETURN_FAILED; for (DeviceList::iterator iter = devices.begin(); iter != devices.end(); iter++) { @@ -132,7 +135,7 @@ void Fuse::calculateFusePower() { return; } //Calculate fuse power. - power = current * voltage; + power.value = current.value * voltage.value; power.setValid(PoolVariableIF::VALID); } @@ -190,12 +193,12 @@ void Fuse::checkFuseState() { reportEvents(FUSE_WENT_OFF); } } - oldFuseState = state; + oldFuseState = state.value; } float Fuse::getPower() { if (power.isValid()) { - return power; + return power.value; } else { return 0.0; } diff --git a/power/Fuse.h b/power/Fuse.h index 279642be..e9e9290b 100644 --- a/power/Fuse.h +++ b/power/Fuse.h @@ -1,16 +1,16 @@ -#ifndef FUSE_H_ -#define FUSE_H_ +#ifndef FSFW_POWER_FUSE_H_ +#define FSFW_POWER_FUSE_H_ -#include "../datapool/DataSet.h" -#include "../datapool/PIDReader.h" -#include "../devicehandlers/HealthDevice.h" -#include "../monitoring/AbsLimitMonitor.h" #include "PowerComponentIF.h" #include "PowerSwitchIF.h" + +#include "../devicehandlers/HealthDevice.h" +#include "../monitoring/AbsLimitMonitor.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../parameters/ParameterHelper.h" #include +#include "../datapoollocal/StaticLocalDataSet.h" namespace Factory { void setStaticFrameworkObjectIds(); } @@ -25,10 +25,10 @@ private: static constexpr float RESIDUAL_POWER = 0.005 * 28.5; //!< This is the upper limit of residual power lost by fuses and switches. Worst case is Fuse and one of two switches on. See PCDU ICD 1.9 p29 bottom public: struct VariableIds { - uint32_t pidVoltage; - uint32_t pidCurrent; - uint32_t pidState; - uint32_t poolIdPower; + gp_id_t pidVoltage; + gp_id_t pidCurrent; + gp_id_t pidState; + gp_id_t poolIdPower; }; static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PCDU_1; @@ -38,8 +38,8 @@ public: static const Event POWER_BELOW_LOW_LIMIT = MAKE_EVENT(5, SEVERITY::LOW); //!< PSS detected a fuse that violates its limits. typedef std::list DeviceList; - Fuse(object_id_t fuseObjectId, uint8_t fuseId, VariableIds ids, - float maxCurrent, uint16_t confirmationCount = 2); + Fuse(object_id_t fuseObjectId, uint8_t fuseId, sid_t variableSet, + VariableIds ids, float maxCurrent, uint16_t confirmationCount = 2); virtual ~Fuse(); void addDevice(PowerComponentIF *set); float getPower(); @@ -69,12 +69,12 @@ public: private: uint8_t oldFuseState; uint8_t fuseId; - PowerSwitchIF *powerIF; //could be static in our case. + PowerSwitchIF *powerIF = nullptr; //could be static in our case. AbsLimitMonitor currentLimit; class PowerMonitor: public MonitorReporter { public: template - PowerMonitor(Args ... args) : + PowerMonitor(Args ... args): MonitorReporter(std::forward(args)...) { } ReturnValue_t checkPower(float sample, float lowerLimit, @@ -84,12 +84,17 @@ private: }; PowerMonitor powerMonitor; - DataSet set; - PIDReader voltage; - PIDReader current; - PIDReader state; - db_float_t power; - MessageQueueIF *commandQueue; + StaticLocalDataSet<3> set; + //LocalPoolDataSetBase* set = nullptr; + //PIDReader voltage; + //PIDReader current; + //PIDReader state; + lp_var_t voltage; + lp_var_t current; + lp_var_t state; + + lp_var_t power; + MessageQueueIF* commandQueue = nullptr; ParameterHelper parameterHelper; HealthHelper healthHelper; static object_id_t powerSwitchId; @@ -102,4 +107,4 @@ private: bool areSwitchesOfComponentOn(DeviceList::iterator iter); }; -#endif /* FUSE_H_ */ +#endif /* FSFW_POWER_FUSE_H_ */ diff --git a/power/PowerComponent.cpp b/power/PowerComponent.cpp index 6012ad4c..c31b9a85 100644 --- a/power/PowerComponent.cpp +++ b/power/PowerComponent.cpp @@ -1,20 +1,15 @@ -/** - * @file PowerComponent.cpp - * @brief This file defines the PowerComponent class. - * @date 28.08.2014 - * @author baetz - */ - #include "PowerComponent.h" +#include "../serialize/SerializeAdapter.h" -PowerComponent::PowerComponent() : - deviceObjectId(0), switchId1(0xFF), switchId2(0xFF), doIHaveTwoSwitches( - false), min(0.0), max(0.0), moduleId(0) { +PowerComponent::PowerComponent(): switchId1(0xFF), switchId2(0xFF), + doIHaveTwoSwitches(false) { } -PowerComponent::PowerComponent(object_id_t setId, uint8_t moduleId, float min, float max, - uint8_t switchId1, bool twoSwitches, uint8_t switchId2) : - deviceObjectId(setId), switchId1(switchId1), switchId2(switchId2), doIHaveTwoSwitches( - twoSwitches), min(min), max(max), moduleId(moduleId) { + +PowerComponent::PowerComponent(object_id_t setId, uint8_t moduleId, float min, + float max, uint8_t switchId1, bool twoSwitches, uint8_t switchId2) : + deviceObjectId(setId), switchId1(switchId1), switchId2(switchId2), + doIHaveTwoSwitches(twoSwitches), min(min), max(max), + moduleId(moduleId) { } ReturnValue_t PowerComponent::serialize(uint8_t** buffer, size_t* size, @@ -57,7 +52,7 @@ float PowerComponent::getMax() { } ReturnValue_t PowerComponent::deSerialize(const uint8_t** buffer, size_t* size, -Endianness streamEndianness) { + Endianness streamEndianness) { ReturnValue_t result = SerializeAdapter::deSerialize(&min, buffer, size, streamEndianness); if (result != HasReturnvaluesIF::RETURN_OK) { @@ -80,7 +75,7 @@ ReturnValue_t PowerComponent::getParameter(uint8_t domainId, parameterWrapper->set<>(max); break; default: - return INVALID_MATRIX_ID; + return INVALID_IDENTIFIER_ID; } return HasReturnvaluesIF::RETURN_OK; } diff --git a/power/PowerComponent.h b/power/PowerComponent.h index 6d1c9c06..c1add3b9 100644 --- a/power/PowerComponent.h +++ b/power/PowerComponent.h @@ -1,13 +1,17 @@ -#ifndef POWERCOMPONENT_H_ -#define POWERCOMPONENT_H_ +#ifndef FSFW_POWER_POWERCOMPONENT_H_ +#define FSFW_POWER_POWERCOMPONENT_H_ -#include "../objectmanager/SystemObjectIF.h" #include "PowerComponentIF.h" +#include "../objectmanager/frameworkObjects.h" +#include "../objectmanager/SystemObjectIF.h" + + class PowerComponent: public PowerComponentIF { public: - PowerComponent(object_id_t setId, uint8_t moduleId, float min, float max, uint8_t switchId1, - bool twoSwitches = false, uint8_t switchId2 = 0xFF); + PowerComponent(object_id_t setId, uint8_t moduleId, float min, float max, + uint8_t switchId1, bool twoSwitches = false, + uint8_t switchId2 = 0xFF); virtual object_id_t getDeviceObjectId(); @@ -31,18 +35,18 @@ public: ParameterWrapper *parameterWrapper, const ParameterWrapper *newValues, uint16_t startAtIndex); private: - const object_id_t deviceObjectId; + const object_id_t deviceObjectId = objects::NO_OBJECT; const uint8_t switchId1; const uint8_t switchId2; const bool doIHaveTwoSwitches; - float min; - float max; + float min = 0.0; + float max = 0.0; - uint8_t moduleId; + uint8_t moduleId = 0; PowerComponent(); }; -#endif /* POWERCOMPONENT_H_ */ +#endif /* FSFW_POWER_POWERCOMPONENT_H_ */ diff --git a/power/PowerComponentIF.h b/power/PowerComponentIF.h index c2e3a6a5..f35b4d1d 100644 --- a/power/PowerComponentIF.h +++ b/power/PowerComponentIF.h @@ -1,24 +1,23 @@ -#ifndef POWERCOMPONENTIF_H_ -#define POWERCOMPONENTIF_H_ +#ifndef FSFW_POWER_POWERCOMPONENTIF_H_ +#define FSFW_POWER_POWERCOMPONENTIF_H_ #include "../serialize/SerializeIF.h" #include "../parameters/HasParametersIF.h" +#include "../objectmanager/SystemObjectIF.h" class PowerComponentIF : public SerializeIF, public HasParametersIF { public: - virtual ~PowerComponentIF() { + virtual ~PowerComponentIF() {} - } + virtual object_id_t getDeviceObjectId() = 0; - virtual object_id_t getDeviceObjectId()=0; - - virtual uint8_t getSwitchId1()=0; - virtual uint8_t getSwitchId2()=0; - virtual bool hasTwoSwitches()=0; + virtual uint8_t getSwitchId1() = 0; + virtual uint8_t getSwitchId2() = 0; + virtual bool hasTwoSwitches() = 0; virtual float getMin() = 0; virtual float getMax() = 0; }; -#endif /* POWERCOMPONENTIF_H_ */ +#endif /* FSFW_POWER_POWERCOMPONENTIF_H_ */ diff --git a/power/PowerSensor.cpp b/power/PowerSensor.cpp index 50cb6acd..1ef041e3 100644 --- a/power/PowerSensor.cpp +++ b/power/PowerSensor.cpp @@ -1,14 +1,18 @@ #include "PowerSensor.h" + #include "../ipc/QueueFactory.h" -PowerSensor::PowerSensor(object_id_t setId, VariableIds ids, +PowerSensor::PowerSensor(object_id_t objectId, sid_t setId, VariableIds ids, DefaultLimits limits, SensorEvents events, uint16_t confirmationCount) : - SystemObject(setId), commandQueue(NULL), parameterHelper(this), healthHelper(this, setId), set(), current( - ids.pidCurrent, &set), voltage(ids.pidVoltage, &set), power( - ids.poolIdPower, &set, PoolVariableIF::VAR_WRITE), currentLimit( - setId, MODULE_ID_CURRENT, ids.pidCurrent, confirmationCount, + SystemObject(objectId), parameterHelper(this), + healthHelper(this, objectId), + powerSensorSet(setId), current(ids.pidCurrent, &powerSensorSet), + voltage(ids.pidVoltage, &powerSensorSet), + power(ids.poolIdPower, &powerSensorSet, PoolVariableIF::VAR_WRITE), + currentLimit(objectId, MODULE_ID_CURRENT, ids.pidCurrent, confirmationCount, limits.currentMin, limits.currentMax, events.currentLow, - events.currentHigh), voltageLimit(setId, MODULE_ID_VOLTAGE, + events.currentHigh), + voltageLimit(objectId, MODULE_ID_VOLTAGE, ids.pidVoltage, confirmationCount, limits.voltageMin, limits.voltageMax, events.voltageLow, events.voltageHigh) { commandQueue = QueueFactory::instance()->createMessageQueue(); @@ -19,13 +23,13 @@ PowerSensor::~PowerSensor() { } ReturnValue_t PowerSensor::calculatePower() { - set.read(); + powerSensorSet.read(); ReturnValue_t result1 = HasReturnvaluesIF::RETURN_FAILED; ReturnValue_t result2 = HasReturnvaluesIF::RETURN_FAILED; if (healthHelper.healthTable->isHealthy(getObjectId()) && voltage.isValid() && current.isValid()) { - result1 = voltageLimit.doCheck(voltage); - result2 = currentLimit.doCheck(current); + result1 = voltageLimit.doCheck(voltage.value); + result2 = currentLimit.doCheck(current.value); } else { voltageLimit.setToInvalid(); currentLimit.setToInvalid(); @@ -37,9 +41,9 @@ ReturnValue_t PowerSensor::calculatePower() { power.setValid(PoolVariableIF::INVALID); } else { power.setValid(PoolVariableIF::VALID); - power = current * voltage; + power.value = current.value * voltage.value; } - set.commit(); + powerSensorSet.commit(); return result1; } @@ -92,8 +96,8 @@ void PowerSensor::checkCommandQueue() { } void PowerSensor::setDataPoolEntriesInvalid() { - set.read(); - set.commit(PoolVariableIF::INVALID); + powerSensorSet.read(); + powerSensorSet.commit(PoolVariableIF::INVALID); } float PowerSensor::getPower() { diff --git a/power/PowerSensor.h b/power/PowerSensor.h index 0f973e45..a0ccb1ca 100644 --- a/power/PowerSensor.h +++ b/power/PowerSensor.h @@ -1,9 +1,7 @@ -#ifndef POWERSENSOR_H_ -#define POWERSENSOR_H_ +#ifndef FSFW_POWER_POWERSENSOR_H_ +#define FSFW_POWER_POWERSENSOR_H_ -#include "../datapool/DataSet.h" -#include "../datapool/PIDReader.h" -#include "../datapool/PoolVariable.h" +#include "../datapoollocal/StaticLocalDataSet.h" #include "../devicehandlers/HealthDevice.h" #include "../monitoring/LimitMonitor.h" #include "../parameters/ParameterHelper.h" @@ -12,15 +10,18 @@ class PowerController; +/** + * @brief Does magic. + */ class PowerSensor: public SystemObject, public ReceivesParameterMessagesIF, public HasHealthIF { friend class PowerController; public: struct VariableIds { - uint32_t pidCurrent; - uint32_t pidVoltage; - uint32_t poolIdPower; + gp_id_t pidCurrent; + gp_id_t pidVoltage; + gp_id_t poolIdPower; }; struct DefaultLimits { float currentMin; @@ -34,8 +35,9 @@ public: Event voltageLow; Event voltageHigh; }; - PowerSensor(object_id_t setId, VariableIds setIds, DefaultLimits limits, - SensorEvents events, uint16_t confirmationCount = 0); + PowerSensor(object_id_t objectId, sid_t sid, VariableIds setIds, + DefaultLimits limits, SensorEvents events, + uint16_t confirmationCount = 0); virtual ~PowerSensor(); ReturnValue_t calculatePower(); ReturnValue_t performOperation(uint8_t opCode); @@ -50,15 +52,19 @@ public: ParameterWrapper *parameterWrapper, const ParameterWrapper *newValues, uint16_t startAtIndex); private: - MessageQueueIF* commandQueue; + MessageQueueIF* commandQueue = nullptr; ParameterHelper parameterHelper; HealthHelper healthHelper; - DataSet set; + //GlobDataSet set; + StaticLocalDataSet<3> powerSensorSet; //Variables in - PIDReader current; - PIDReader voltage; + lp_var_t current; + lp_var_t voltage; + //PIDReader current; + //PIDReader voltage; //Variables out - db_float_t power; + lp_var_t power; + //gp_float_t power; static const uint8_t MODULE_ID_CURRENT = 1; static const uint8_t MODULE_ID_VOLTAGE = 2; @@ -68,4 +74,4 @@ protected: LimitMonitor voltageLimit; }; -#endif /* POWERSENSOR_H_ */ +#endif /* FSFW_POWER_POWERSENSOR_H_ */ diff --git a/power/PowerSwitchIF.h b/power/PowerSwitchIF.h index 40220fb6..1422baeb 100644 --- a/power/PowerSwitchIF.h +++ b/power/PowerSwitchIF.h @@ -1,18 +1,16 @@ -/** - * @file PowerSwitchIF.h - * @brief This file defines the PowerSwitchIF class. - * @date 20.03.2013 - * @author baetz - */ - -#ifndef POWERSWITCHIF_H_ -#define POWERSWITCHIF_H_ +#ifndef FSFW_POWER_POWERSWITCHIF_H_ +#define FSFW_POWER_POWERSWITCHIF_H_ #include "../events/Event.h" #include "../returnvalues/HasReturnvaluesIF.h" /** - * 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: @@ -72,4 +70,4 @@ public: }; -#endif /* POWERSWITCHIF_H_ */ +#endif /* FSFW_POWER_POWERSWITCHIF_H_ */ diff --git a/power/PowerSwitcher.cpp b/power/PowerSwitcher.cpp index 6296a04c..ed37998e 100644 --- a/power/PowerSwitcher.cpp +++ b/power/PowerSwitcher.cpp @@ -1,15 +1,17 @@ -#include "../objectmanager/ObjectManagerIF.h" #include "PowerSwitcher.h" + +#include "../objectmanager/ObjectManagerIF.h" #include "../serviceinterface/ServiceInterfaceStream.h" PowerSwitcher::PowerSwitcher(uint8_t setSwitch1, uint8_t setSwitch2, - PowerSwitcher::State_t setStartState) : - state(setStartState), firstSwitch(setSwitch1), secondSwitch(setSwitch2), power(NULL) { + PowerSwitcher::State_t setStartState): + state(setStartState), firstSwitch(setSwitch1), + secondSwitch(setSwitch2) { } ReturnValue_t PowerSwitcher::initialize(object_id_t powerSwitchId) { power = objectManager->get(powerSwitchId); - if (power == NULL) { + if (power == nullptr) { return HasReturnvaluesIF::RETURN_FAILED; } return HasReturnvaluesIF::RETURN_OK; @@ -17,19 +19,25 @@ ReturnValue_t PowerSwitcher::initialize(object_id_t powerSwitchId) { ReturnValue_t PowerSwitcher::getStateOfSwitches() { SwitchReturn_t result = howManySwitches(); + switch (result) { case ONE_SWITCH: return power->getSwitchState(firstSwitch); - case TWO_SWITCHES: - if ((power->getSwitchState(firstSwitch) == PowerSwitchIF::SWITCH_ON) - && (power->getSwitchState(secondSwitch) == PowerSwitchIF::SWITCH_ON)) { + case TWO_SWITCHES: { + ReturnValue_t firstSwitchState = power->getSwitchState(firstSwitch); + ReturnValue_t secondSwitchState = power->getSwitchState(firstSwitch); + if ((firstSwitchState == PowerSwitchIF::SWITCH_ON) + && (secondSwitchState == PowerSwitchIF::SWITCH_ON)) { return PowerSwitchIF::SWITCH_ON; - } else if ((power->getSwitchState(firstSwitch) == PowerSwitchIF::SWITCH_OFF) - && (power->getSwitchState(secondSwitch) == PowerSwitchIF::SWITCH_OFF)) { + } + else if ((firstSwitchState == PowerSwitchIF::SWITCH_OFF) + && (secondSwitchState == PowerSwitchIF::SWITCH_OFF)) { return PowerSwitchIF::SWITCH_OFF; - } else { + } + else { return HasReturnvaluesIF::RETURN_FAILED; } + } default: return HasReturnvaluesIF::RETURN_FAILED; } diff --git a/power/PowerSwitcher.h b/power/PowerSwitcher.h index 0f1c02de..f4e2138d 100644 --- a/power/PowerSwitcher.h +++ b/power/PowerSwitcher.h @@ -1,10 +1,13 @@ -#ifndef POWERSWITCHER_H_ -#define POWERSWITCHER_H_ +#ifndef FSFW_POWER_POWERSWITCHER_H_ +#define FSFW_POWER_POWERSWITCHER_H_ + #include "PowerSwitchIF.h" + +#include "../objectmanager/SystemObjectIF.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../timemanager/Countdown.h" -class PowerSwitcher : public HasReturnvaluesIF { +class PowerSwitcher: public HasReturnvaluesIF { public: enum State_t { WAIT_OFF, @@ -16,7 +19,8 @@ public: static const uint8_t INTERFACE_ID = CLASS_ID::POWER_SWITCHER; static const ReturnValue_t IN_POWER_TRANSITION = MAKE_RETURN_CODE(1); static const ReturnValue_t SWITCH_STATE_MISMATCH = MAKE_RETURN_CODE(2); - PowerSwitcher( uint8_t setSwitch1, uint8_t setSwitch2 = NO_SWITCH, State_t setStartState = SWITCH_IS_OFF ); + PowerSwitcher( uint8_t setSwitch1, uint8_t setSwitch2 = NO_SWITCH, + State_t setStartState = SWITCH_IS_OFF ); ReturnValue_t initialize(object_id_t powerSwitchId); void turnOn(); void turnOff(); @@ -29,7 +33,8 @@ public: private: uint8_t firstSwitch; uint8_t secondSwitch; - PowerSwitchIF* power; + PowerSwitchIF* power = nullptr; + static const uint8_t NO_SWITCH = 0xFF; enum SwitchReturn_t { ONE_SWITCH = 1, @@ -42,4 +47,4 @@ private: -#endif /* POWERSWITCHER_H_ */ +#endif /* FSFW_POWER_POWERSWITCHER_H_ */ From 64cf0d0a7028313a5f5ad9429d9738964902762e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Dec 2020 18:32:32 +0100 Subject: [PATCH 091/119] thermal update --- thermal/AbstractTemperatureSensor.cpp | 12 +- thermal/AbstractTemperatureSensor.h | 10 + thermal/AcceptsThermalMessagesIF.h | 22 ++ thermal/Heater.cpp | 10 +- thermal/RedundantHeater.h | 13 +- thermal/TemperatureSensor.h | 138 +++++++++---- thermal/ThermalComponent.cpp | 59 +++--- thermal/ThermalComponent.h | 49 ++++- thermal/ThermalComponentCore.cpp | 285 ++++++++++++++++++++++++++ thermal/ThermalComponentCore.h | 117 +++++++++++ thermal/ThermalComponentIF.h | 12 +- thermal/ThermalModule.cpp | 84 ++++---- thermal/ThermalModule.h | 49 +++-- thermal/ThermalMonitorReporter.cpp | 75 +++++++ thermal/ThermalMonitorReporter.h | 28 +++ thermal/tcsDefinitions.h | 2 +- 16 files changed, 796 insertions(+), 169 deletions(-) create mode 100644 thermal/AcceptsThermalMessagesIF.h create mode 100644 thermal/ThermalComponentCore.cpp create mode 100644 thermal/ThermalComponentCore.h create mode 100644 thermal/ThermalMonitorReporter.cpp create mode 100644 thermal/ThermalMonitorReporter.h diff --git a/thermal/AbstractTemperatureSensor.cpp b/thermal/AbstractTemperatureSensor.cpp index 45ebe4a2..40b305af 100644 --- a/thermal/AbstractTemperatureSensor.cpp +++ b/thermal/AbstractTemperatureSensor.cpp @@ -44,19 +44,19 @@ ReturnValue_t AbstractTemperatureSensor::performHealthOp() { } void AbstractTemperatureSensor::handleCommandQueue() { - CommandMessage message; - ReturnValue_t result = commandQueue->receiveMessage(&message); + CommandMessage command; + ReturnValue_t result = commandQueue->receiveMessage(&command); if (result == HasReturnvaluesIF::RETURN_OK) { - result = healthHelper.handleHealthCommand(&message); + result = healthHelper.handleHealthCommand(&command); if (result == HasReturnvaluesIF::RETURN_OK) { return; } - result = parameterHelper.handleParameterMessage(&message); + result = parameterHelper.handleParameterMessage(&command); if (result == HasReturnvaluesIF::RETURN_OK) { return; } - message.setToUnknownCommand(); - commandQueue->reply(&message); + command.setToUnknownCommand(); + commandQueue->reply(&command); } } diff --git a/thermal/AbstractTemperatureSensor.h b/thermal/AbstractTemperatureSensor.h index 726ab9f4..cc064ce4 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 00000000..5fbd6bb3 --- /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 "../ipc/MessageQueueSenderIF.h" + +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/Heater.cpp b/thermal/Heater.cpp index 1301e2e0..ce965d5e 100644 --- a/thermal/Heater.cpp +++ b/thermal/Heater.cpp @@ -279,14 +279,14 @@ ReturnValue_t Heater::initialize() { } void Heater::handleQueue() { - CommandMessage message; - ReturnValue_t result = commandQueue->receiveMessage(&message); + CommandMessage command; + ReturnValue_t result = commandQueue->receiveMessage(&command); if (result == HasReturnvaluesIF::RETURN_OK) { - result = healthHelper.handleHealthCommand(&message); + result = healthHelper.handleHealthCommand(&command); if (result == HasReturnvaluesIF::RETURN_OK) { return; } - parameterHelper.handleParameterMessage(&message); + parameterHelper.handleParameterMessage(&command); } } @@ -301,7 +301,7 @@ ReturnValue_t Heater::getParameter(uint8_t domainId, uint16_t parameterId, parameterWrapper->set(heaterOnCountdown.timeout); break; default: - return INVALID_MATRIX_ID; + return INVALID_IDENTIFIER_ID; } return HasReturnvaluesIF::RETURN_OK; } diff --git a/thermal/RedundantHeater.h b/thermal/RedundantHeater.h index ab745a69..76537542 100644 --- a/thermal/RedundantHeater.h +++ b/thermal/RedundantHeater.h @@ -1,7 +1,7 @@ #ifndef REDUNDANTHEATER_H_ #define REDUNDANTHEATER_H_ -#include "Heater.h" +#include "../thermal/Heater.h" class RedundantHeater { public: @@ -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 356ca722..f41b3761 100644 --- a/thermal/TemperatureSensor.h +++ b/thermal/TemperatureSensor.h @@ -1,40 +1,101 @@ #ifndef TEMPERATURESENSOR_H_ #define TEMPERATURESENSOR_H_ -#include "../datapool/DataSet.h" -#include "AbstractTemperatureSensor.h" +#include "../thermal/AbstractTemperatureSensor.h" +#include "../datapoolglob/GlobalDataSet.h" +#include "../datapoolglob/GlobalPoolVariable.h" #include "../monitoring/LimitMonitor.h" -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}, + GlobDataSet *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, + GlobalDataPool::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 +108,17 @@ protected: UsedParameters parameters; - T *inputTemperature; + inputType * inputValue; PoolVariableIF *poolVariable; - PoolVariable outputTemperature; + gp_float_t 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 +126,7 @@ protected: return; } - outputTemperature = calculateOutputTemperature(*inputTemperature); + outputTemperature = calculateOutputTemperature(*inputValue); outputTemperature.setValid(PoolVariableIF::VALID); timeval uptime; @@ -78,7 +134,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 +152,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 +166,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 +174,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) { @@ -160,7 +208,7 @@ public: parameterWrapper->set(parameters.gradient); break; default: - return INVALID_MATRIX_ID; + return INVALID_IDENTIFIER_ID; } return HasReturnvaluesIF::RETURN_OK; } diff --git a/thermal/ThermalComponent.cpp b/thermal/ThermalComponent.cpp index 5dcd0bc6..084201dd 100644 --- a/thermal/ThermalComponent.cpp +++ b/thermal/ThermalComponent.cpp @@ -1,48 +1,46 @@ #include "ThermalComponent.h" ThermalComponent::ThermalComponent(object_id_t reportingObjectId, - uint8_t domainId, uint32_t temperaturePoolId, - uint32_t targetStatePoolId, uint32_t currentStatePoolId, - uint32_t requestPoolId, DataSet* dataSet, + uint8_t domainId, gp_id_t temperaturePoolId, + gp_id_t targetStatePoolId, gp_id_t currentStatePoolId, + gp_id_t requestPoolId, LocalPoolDataSetBase* dataSet, AbstractTemperatureSensor* sensor, AbstractTemperatureSensor* firstRedundantSensor, AbstractTemperatureSensor* secondRedundantSensor, ThermalModuleIF* thermalModule, Parameters parameters, Priority priority) : - CoreComponent(reportingObjectId, domainId, temperaturePoolId, + ThermalComponentCore(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 }) { + parameters.heaterOn, parameters.hysteresis, + parameters.heaterSwitchoff }, + ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL), + nopParameters({ parameters.lowerNopLimit, parameters.upperNopLimit }) { } ThermalComponent::~ThermalComponent() { } ReturnValue_t ThermalComponent::setTargetState(int8_t newState) { - DataSet mySet; - PoolVariable writableTargetState(targetState.getDataPoolId(), - &mySet, PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - if ((writableTargetState == STATE_REQUEST_OPERATIONAL) - && (newState != STATE_REQUEST_IGNORE)) { + targetState.setReadWriteMode(pool_rwm_t::VAR_READ_WRITE); + targetState.read(); + if ((targetState == STATE_REQUEST_OPERATIONAL) + and (newState != STATE_REQUEST_IGNORE)) { return HasReturnvaluesIF::RETURN_FAILED; } switch (newState) { case STATE_REQUEST_NON_OPERATIONAL: - writableTargetState = newState; - mySet.commit(PoolVariableIF::VALID); + targetState = newState; + targetState.setValid(true); + targetState.commit(PoolVariableIF::VALID); return HasReturnvaluesIF::RETURN_OK; default: - return CoreComponent::setTargetState(newState); + return ThermalComponentCore::setTargetState(newState); } + return HasReturnvaluesIF::RETURN_OK; } -ReturnValue_t ThermalComponent::setLimits(const uint8_t* data, uint32_t size) { +ReturnValue_t ThermalComponent::setLimits(const uint8_t* data, size_t size) { if (size != 4 * sizeof(parameters.lowerOpLimit)) { return MonitoringIF::INVALID_SIZE; } @@ -59,11 +57,11 @@ ReturnValue_t ThermalComponent::setLimits(const uint8_t* data, uint32_t size) { } ThermalComponentIF::State ThermalComponent::getState(float temperature, - CoreComponent::Parameters parameters, int8_t targetState) { + ThermalComponentCore::Parameters parameters, int8_t targetState) { if (temperature < nopParameters.lowerNopLimit) { return OUT_OF_RANGE_LOW; } else { - State state = CoreComponent::getState(temperature, parameters, + State state = ThermalComponentCore::getState(temperature, parameters, targetState); if (state != NON_OPERATIONAL_HIGH && state != NON_OPERATIONAL_HIGH_IGNORED) { @@ -80,18 +78,19 @@ ThermalComponentIF::State ThermalComponent::getState(float temperature, } void ThermalComponent::checkLimits(ThermalComponentIF::State state) { - if (targetState == STATE_REQUEST_OPERATIONAL || targetState == STATE_REQUEST_IGNORE) { - CoreComponent::checkLimits(state); + if ((targetState == STATE_REQUEST_OPERATIONAL) or + (targetState == STATE_REQUEST_IGNORE)) { + ThermalComponentCore::checkLimits(state); return; } - //If component is not operational, it checks the NOP limits. + // If component is not operational, it checks the NOP limits. temperatureMonitor.translateState(state, temperature.value, nopParameters.lowerNopLimit, nopParameters.upperNopLimit, false); } ThermalComponentIF::HeaterRequest ThermalComponent::getHeaterRequest( int8_t targetState, float temperature, - CoreComponent::Parameters parameters) { + ThermalComponentCore::Parameters parameters) { if (targetState == STATE_REQUEST_IGNORE) { isHeating = false; return HEATER_DONT_CARE; @@ -144,16 +143,16 @@ ThermalComponentIF::State ThermalComponent::getIgnoredState(int8_t state) { case OUT_OF_RANGE_HIGH_IGNORED: return OUT_OF_RANGE_HIGH_IGNORED; default: - return CoreComponent::getIgnoredState(state); + return ThermalComponentCore::getIgnoredState(state); } } ReturnValue_t ThermalComponent::getParameter(uint8_t domainId, uint16_t parameterId, ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues, uint16_t startAtIndex) { - ReturnValue_t result = CoreComponent::getParameter(domainId, parameterId, + ReturnValue_t result = ThermalComponentCore::getParameter(domainId, parameterId, parameterWrapper, newValues, startAtIndex); - if (result != INVALID_MATRIX_ID) { + if (result != INVALID_IDENTIFIER_ID) { return result; } switch (parameterId) { @@ -164,7 +163,7 @@ ReturnValue_t ThermalComponent::getParameter(uint8_t domainId, parameterWrapper->set(nopParameters.upperNopLimit); break; default: - return INVALID_MATRIX_ID; + return INVALID_IDENTIFIER_ID; } return HasReturnvaluesIF::RETURN_OK; } diff --git a/thermal/ThermalComponent.h b/thermal/ThermalComponent.h index 93243868..0785a914 100644 --- a/thermal/ThermalComponent.h +++ b/thermal/ThermalComponent.h @@ -1,9 +1,14 @@ -#ifndef THERMALCOMPONENT_H_ -#define THERMALCOMPONENT_H_ +#ifndef FSFW_THERMAL_THERMALCOMPONENT_H_ +#define FSFW_THERMAL_THERMALCOMPONENT_H_ -#include "CoreComponent.h" +#include "ThermalComponentCore.h" -class ThermalComponent: public CoreComponent { +/** + * @brief + * @details + * Some more documentation. + */ +class ThermalComponent: public ThermalComponentCore { public: struct Parameters { float lowerNopLimit; @@ -14,13 +19,35 @@ public: float hysteresis; float heaterSwitchoff; }; + + /** + * Non-Operational Temperatures + */ struct NopParameters { float lowerNopLimit; float upperNopLimit; }; - ThermalComponent(object_id_t reportingObjectId, uint8_t domainId, uint32_t temperaturePoolId, - uint32_t targetStatePoolId, uint32_t currentStatePoolId, uint32_t requestPoolId, - DataSet *dataSet, AbstractTemperatureSensor *sensor, + + /** + * 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, + gp_id_t temperaturePoolId, gp_id_t targetStatePoolId, + gp_id_t currentStatePoolId, gp_id_t requestPoolId, + LocalPoolDataSetBase *dataSet, AbstractTemperatureSensor *sensor, AbstractTemperatureSensor *firstRedundantSensor, AbstractTemperatureSensor *secondRedundantSensor, ThermalModuleIF *thermalModule, Parameters parameters, @@ -29,7 +56,7 @@ public: ReturnValue_t setTargetState(int8_t newState); - virtual ReturnValue_t setLimits( const uint8_t* data, uint32_t size); + virtual ReturnValue_t setLimits( const uint8_t* data, size_t size); virtual ReturnValue_t getParameter(uint8_t domainId, uint16_t parameterId, ParameterWrapper *parameterWrapper, @@ -39,15 +66,15 @@ protected: NopParameters nopParameters; - State getState(float temperature, CoreComponent::Parameters parameters, + State getState(float temperature, ThermalComponentCore::Parameters parameters, int8_t targetState); virtual void checkLimits(State state); virtual HeaterRequest getHeaterRequest(int8_t targetState, float temperature, - CoreComponent::Parameters parameters); + ThermalComponentCore::Parameters parameters); State getIgnoredState(int8_t state); }; -#endif /* THERMALCOMPONENT_H_ */ +#endif /* FSFW_THERMAL_THERMALCOMPONENT_H_ */ diff --git a/thermal/ThermalComponentCore.cpp b/thermal/ThermalComponentCore.cpp new file mode 100644 index 00000000..7b594d0c --- /dev/null +++ b/thermal/ThermalComponentCore.cpp @@ -0,0 +1,285 @@ +#include "ThermalComponentCore.h" + +ThermalComponentCore::ThermalComponentCore(object_id_t reportingObjectId, + uint8_t domainId, gp_id_t temperaturePoolId, + gp_id_t targetStatePoolId, gp_id_t currentStatePoolId, + gp_id_t requestPoolId, LocalPoolDataSetBase* dataSet, + Parameters parameters, StateRequest initialTargetState) : + temperature(temperaturePoolId, dataSet, PoolVariableIF::VAR_WRITE), + targetState(targetStatePoolId, dataSet, PoolVariableIF::VAR_READ), + currentState(currentStatePoolId, dataSet, PoolVariableIF::VAR_WRITE), + heaterRequest(requestPoolId, dataSet, PoolVariableIF::VAR_WRITE), + parameters(parameters), domainId(domainId), + temperatureMonitor(reportingObjectId, domainId + 1,temperaturePoolId, + COMPONENT_TEMP_CONFIRMATION) { + //Set thermal state once, then leave to operator. + targetState.setReadWriteMode(PoolVariableIF::VAR_WRITE); + ReturnValue_t result = targetState.read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + targetState = initialTargetState; + targetState.setValid(true); + targetState.commit(); + } + targetState.setReadWriteMode(PoolVariableIF::VAR_READ); +} + +void ThermalComponentCore::addSensor(AbstractTemperatureSensor* sensor) { + this->sensor = sensor; +} + +void ThermalComponentCore::addFirstRedundantSensor( + AbstractTemperatureSensor *firstRedundantSensor) { + this->firstRedundantSensor = firstRedundantSensor; +} + +void ThermalComponentCore::addSecondRedundantSensor( + AbstractTemperatureSensor *secondRedundantSensor) { + this->secondRedundantSensor = secondRedundantSensor; +} + +void ThermalComponentCore::addThermalModule(ThermalModule *thermalModule, + Priority priority) { + this->thermalModule = thermalModule; + if(thermalModule != nullptr) { + thermalModule->registerComponent(this, priority); + } +} + +void ThermalComponentCore::setPriority(Priority priority) { + if(priority == SAFE) { + this->isSafeComponent = true; + } +} + +ThermalComponentCore::~ThermalComponentCore() { +} + +ThermalComponentIF::HeaterRequest ThermalComponentCore::performOperation( + uint8_t opCode) { + HeaterRequest request = HEATER_DONT_CARE; + //SHOULDDO: Better pass db_float_t* to getTemperature and set it invalid if invalid. + temperature = getTemperature(); + updateMinMaxTemp(); + if (temperature != INVALID_TEMPERATURE) { + temperature.setValid(PoolVariableIF::VALID); + State state = getState(temperature.value, getParameters(), + targetState.value); + currentState = state; + checkLimits(state); + request = getHeaterRequest(targetState.value, temperature.value, + getParameters()); + } else { + temperatureMonitor.setToInvalid(); + temperature.setValid(PoolVariableIF::INVALID); + currentState = UNKNOWN; + request = HEATER_DONT_CARE; + } + currentState.setValid(PoolVariableIF::VALID); + heaterRequest = request; + heaterRequest.setValid(PoolVariableIF::VALID); + return request; +} + +void ThermalComponentCore::markStateIgnored() { + currentState = getIgnoredState(currentState.value); +} + +object_id_t ThermalComponentCore::getObjectId() { + return temperatureMonitor.getReporterId(); + return 0; +} + +float ThermalComponentCore::getLowerOpLimit() { + return parameters.lowerOpLimit; +} + + + +ReturnValue_t ThermalComponentCore::setTargetState(int8_t newState) { + targetState.setReadWriteMode(pool_rwm_t::VAR_READ_WRITE); + targetState.read(); + if((targetState == STATE_REQUEST_OPERATIONAL) and + (newState != STATE_REQUEST_IGNORE)) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + switch (newState) { + case STATE_REQUEST_HEATING: + case STATE_REQUEST_IGNORE: + case STATE_REQUEST_OPERATIONAL: + targetState = newState; + break; + case STATE_REQUEST_NON_OPERATIONAL: + default: + return INVALID_TARGET_STATE; + } + targetState.setValid(true); + targetState.commit(); + return HasReturnvaluesIF::RETURN_OK; +} + +void ThermalComponentCore::setOutputInvalid() { + temperature = INVALID_TEMPERATURE; + temperature.setValid(PoolVariableIF::INVALID); + currentState.setValid(PoolVariableIF::INVALID); + heaterRequest = HEATER_DONT_CARE; + heaterRequest.setValid(PoolVariableIF::INVALID); + temperatureMonitor.setToUnchecked(); +} + +float ThermalComponentCore::getTemperature() { + if ((sensor != nullptr) && (sensor->isValid())) { + return sensor->getTemperature(); + } + + if ((firstRedundantSensor != nullptr) && + (firstRedundantSensor->isValid())) { + return firstRedundantSensor->getTemperature(); + } + + if ((secondRedundantSensor != nullptr) && + (secondRedundantSensor->isValid())) { + return secondRedundantSensor->getTemperature(); + } + + if (thermalModule != nullptr) { + float temperature = thermalModule->getTemperature(); + if (temperature != ThermalModuleIF::INVALID_TEMPERATURE) { + return temperature; + } else { + return INVALID_TEMPERATURE; + } + } else { + return INVALID_TEMPERATURE; + } +} + +ThermalComponentIF::State ThermalComponentCore::getState(float temperature, + Parameters parameters, int8_t targetState) { + ThermalComponentIF::State state; + + if (temperature < parameters.lowerOpLimit) { + state = NON_OPERATIONAL_LOW; + } else if (temperature < parameters.upperOpLimit) { + state = OPERATIONAL; + } else { + state = NON_OPERATIONAL_HIGH; + } + if (targetState == STATE_REQUEST_IGNORE) { + state = getIgnoredState(state); + } + + return state; +} + +void ThermalComponentCore::checkLimits(ThermalComponentIF::State state) { + //Checks operational limits only. + temperatureMonitor.translateState(state, temperature.value, + getParameters().lowerOpLimit, getParameters().upperOpLimit); + +} + +ThermalComponentIF::HeaterRequest ThermalComponentCore::getHeaterRequest( + int8_t targetState, float temperature, Parameters parameters) { + if (targetState == STATE_REQUEST_IGNORE) { + isHeating = false; + return HEATER_DONT_CARE; + } + + if (temperature > parameters.upperOpLimit - parameters.heaterSwitchoff) { + isHeating = false; + return HEATER_REQUEST_EMERGENCY_OFF; + } + + float opHeaterLimit = parameters.lowerOpLimit + parameters.heaterOn; + + if (isHeating) { + opHeaterLimit += parameters.hysteresis; + } + + if (temperature < opHeaterLimit) { + isHeating = true; + return HEATER_REQUEST_EMERGENCY_ON; + } + isHeating = false; + return HEATER_DONT_CARE; +} + +ThermalComponentIF::State ThermalComponentCore::getIgnoredState(int8_t state) { + switch (state) { + case NON_OPERATIONAL_LOW: + return NON_OPERATIONAL_LOW_IGNORED; + case OPERATIONAL: + return OPERATIONAL_IGNORED; + case NON_OPERATIONAL_HIGH: + return NON_OPERATIONAL_HIGH_IGNORED; + case NON_OPERATIONAL_LOW_IGNORED: + return NON_OPERATIONAL_LOW_IGNORED; + case OPERATIONAL_IGNORED: + return OPERATIONAL_IGNORED; + case NON_OPERATIONAL_HIGH_IGNORED: + return NON_OPERATIONAL_HIGH_IGNORED; + default: + case UNKNOWN: + return UNKNOWN; + } +} + +void ThermalComponentCore::updateMinMaxTemp() { + if (temperature == INVALID_TEMPERATURE) { + return; + } + if (temperature < minTemp) { + minTemp = static_cast(temperature); + } + if (temperature > maxTemp) { + maxTemp = static_cast(temperature); + } +} + +uint8_t ThermalComponentCore::getDomainId() const { + return domainId; +} + +ThermalComponentCore::Parameters ThermalComponentCore::getParameters() { + return parameters; +} + +ReturnValue_t ThermalComponentCore::getParameter(uint8_t domainId, + uint16_t parameterId, ParameterWrapper* parameterWrapper, + const ParameterWrapper* newValues, uint16_t startAtIndex) { + ReturnValue_t result = temperatureMonitor.getParameter(domainId, + parameterId, parameterWrapper, newValues, startAtIndex); + if (result != INVALID_DOMAIN_ID) { + return result; + } + if (domainId != this->domainId) { + return INVALID_DOMAIN_ID; + } + switch (parameterId) { + case 0: + parameterWrapper->set(parameters.heaterOn); + break; + case 1: + parameterWrapper->set(parameters.hysteresis); + break; + case 2: + parameterWrapper->set(parameters.heaterSwitchoff); + break; + case 3: + parameterWrapper->set(minTemp); + break; + case 4: + parameterWrapper->set(maxTemp); + break; + case 10: + parameterWrapper->set(parameters.lowerOpLimit); + break; + case 11: + parameterWrapper->set(parameters.upperOpLimit); + break; + default: + return INVALID_IDENTIFIER_ID; + } + return HasReturnvaluesIF::RETURN_OK; +} diff --git a/thermal/ThermalComponentCore.h b/thermal/ThermalComponentCore.h new file mode 100644 index 00000000..da9424e6 --- /dev/null +++ b/thermal/ThermalComponentCore.h @@ -0,0 +1,117 @@ +#ifndef FSFW_THERMAL_THERMALCOMPONENTCORE_H_ +#define FSFW_THERMAL_THERMALCOMPONENTCORE_H_ + +#include "ThermalMonitorReporter.h" +#include "ThermalComponentIF.h" +#include "AbstractTemperatureSensor.h" +#include "ThermalModule.h" + +#include "../datapoollocal/LocalPoolVariable.h" + +/** + * @brief + * @details + */ +class ThermalComponentCore: public ThermalComponentIF { +public: + struct Parameters { + float lowerOpLimit; + float upperOpLimit; + float heaterOn; + float hysteresis; + float heaterSwitchoff; + }; + + static const uint16_t COMPONENT_TEMP_CONFIRMATION = 5; + + /** + * Some documentation + * @param reportingObjectId + * @param domainId + * @param temperaturePoolId + * @param targetStatePoolId + * @param currentStatePoolId + * @param requestPoolId + * @param dataSet + * @param parameters + * @param initialTargetState + */ + ThermalComponentCore(object_id_t reportingObjectId, uint8_t domainId, + gp_id_t temperaturePoolId, gp_id_t targetStatePoolId, + gp_id_t currentStatePoolId, gp_id_t requestPoolId, + LocalPoolDataSetBase* dataSet, Parameters parameters, + StateRequest initialTargetState = + ThermalComponentIF::STATE_REQUEST_OPERATIONAL); + + void addSensor(AbstractTemperatureSensor* firstRedundantSensor); + void addFirstRedundantSensor( + AbstractTemperatureSensor* firstRedundantSensor); + void addSecondRedundantSensor( + AbstractTemperatureSensor* secondRedundantSensor); + void addThermalModule(ThermalModule* thermalModule, Priority priority); + + void setPriority(Priority priority); + + virtual ~ThermalComponentCore(); + + virtual HeaterRequest performOperation(uint8_t opCode); + + void markStateIgnored(); + + object_id_t getObjectId(); + + uint8_t getDomainId() const; + + virtual float getLowerOpLimit(); + + ReturnValue_t setTargetState(int8_t newState); + + virtual void setOutputInvalid(); + + virtual ReturnValue_t getParameter(uint8_t domainId, uint16_t parameterId, + ParameterWrapper *parameterWrapper, + const ParameterWrapper *newValues, uint16_t startAtIndex); + +protected: + + AbstractTemperatureSensor *sensor = nullptr; + AbstractTemperatureSensor *firstRedundantSensor = nullptr; + AbstractTemperatureSensor *secondRedundantSensor = nullptr; + ThermalModuleIF *thermalModule = nullptr; + + lp_var_t temperature; + lp_var_t targetState; + lp_var_t currentState; + lp_var_t heaterRequest; + + bool isHeating = false; + + bool isSafeComponent = false; + + float minTemp = 999; + + float maxTemp = AbstractTemperatureSensor::ZERO_KELVIN_C; + + Parameters parameters; + + const uint8_t domainId; + + ThermalMonitorReporter temperatureMonitor; + + virtual float getTemperature(); + virtual State getState(float temperature, Parameters parameters, + int8_t targetState); + + virtual void checkLimits(State state); + + virtual HeaterRequest getHeaterRequest(int8_t targetState, + float temperature, Parameters parameters); + + virtual State getIgnoredState(int8_t state); + + void updateMinMaxTemp(); + + virtual Parameters getParameters(); +}; + +#endif /* FSFW_THERMAL_THERMALCOMPONENT_CORE_H_ */ diff --git a/thermal/ThermalComponentIF.h b/thermal/ThermalComponentIF.h index 522d4e44..16739e36 100644 --- a/thermal/ThermalComponentIF.h +++ b/thermal/ThermalComponentIF.h @@ -49,18 +49,18 @@ public: SAFE = 0, //!< SAFE IDLE, //!< IDLE PAYLOAD, //!< PAYLOAD - NUMBER_OF_PRIORITIES //!< MAX_PRIORITY + NUMBER_OF_PRIORITIES //!< MAX_PRIORITY }; /** * The elements are ordered by priority, lowest have highest priority */ enum HeaterRequest { - HEATER_REQUEST_EMERGENCY_OFF = 0, //!< REQUEST_EMERGENCY_OFF - HEATER_REQUEST_EMERGENCY_ON, //!< REQUEST_EMERGENCY_ON - HEATER_REQUEST_OFF, //!< REQUEST_OFF - HEATER_REQUEST_ON, //!< REQUEST_ON - HEATER_DONT_CARE //!< DONT_CARE + HEATER_REQUEST_EMERGENCY_OFF = 0, //!< REQUEST_EMERGENCY_OFF + HEATER_REQUEST_EMERGENCY_ON, //!< REQUEST_EMERGENCY_ON + HEATER_REQUEST_OFF, //!< REQUEST_OFF + HEATER_REQUEST_ON, //!< REQUEST_ON + HEATER_DONT_CARE //!< DONT_CARE }; virtual ~ThermalComponentIF() { diff --git a/thermal/ThermalModule.cpp b/thermal/ThermalModule.cpp index c573008e..2bc1741f 100644 --- a/thermal/ThermalModule.cpp +++ b/thermal/ThermalModule.cpp @@ -1,28 +1,31 @@ -#include "../monitoring/LimitViolationReporter.h" -#include "../monitoring/MonitoringMessageContent.h" #include "ThermalModule.h" - #include "AbstractTemperatureSensor.h" -ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, - uint32_t currentStatePoolId, uint32_t targetStatePoolId, - DataSet *dataSet, Parameters parameters, +#include "../monitoring/LimitViolationReporter.h" +#include "../monitoring/MonitoringMessageContent.h" + + +ThermalModule::ThermalModule(gp_id_t moduleTemperaturePoolId, + gp_id_t currentStatePoolId, gp_id_t targetStatePoolId, + LocalPoolDataSetBase *dataSet, Parameters parameters, RedundantHeater::Parameters heaterParameters) : - oldStrategy(ACTIVE_SINGLE), survivalTargetTemp(0), targetTemp(0), heating( - false), parameters(parameters), moduleTemperature( - moduleTemperaturePoolId, dataSet, PoolVariableIF::VAR_WRITE), currentState( - currentStatePoolId, dataSet, PoolVariableIF::VAR_WRITE), targetState( - targetStatePoolId, dataSet, PoolVariableIF::VAR_READ) { + oldStrategy(ACTIVE_SINGLE), parameters(parameters), + moduleTemperature(moduleTemperaturePoolId, dataSet, + PoolVariableIF::VAR_WRITE), + currentState(currentStatePoolId, dataSet, PoolVariableIF::VAR_WRITE), + targetState(targetStatePoolId, dataSet, PoolVariableIF::VAR_READ) { heater = new RedundantHeater(heaterParameters); } -ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, DataSet* dataSet) : - oldStrategy(ACTIVE_SINGLE), survivalTargetTemp(0), targetTemp(0), heating( - false), parameters( { 0, 0 }), moduleTemperature( - moduleTemperaturePoolId, dataSet, PoolVariableIF::VAR_WRITE), heater( - NULL), currentState(PoolVariableIF::INVALID, dataSet, - PoolVariableIF::VAR_WRITE), targetState(PoolVariableIF::INVALID, - dataSet, PoolVariableIF::VAR_READ) { +ThermalModule::ThermalModule(gp_id_t moduleTemperaturePoolId, + LocalPoolDataSetBase* dataSet) : + oldStrategy(ACTIVE_SINGLE), parameters( { 0, 0 }), + moduleTemperature(moduleTemperaturePoolId, dataSet, + PoolVariableIF::VAR_WRITE), + currentState(gp_id_t(), dataSet, + PoolVariableIF::VAR_WRITE), + targetState(gp_id_t(), dataSet, + PoolVariableIF::VAR_READ) { } ThermalModule::~ThermalModule() { @@ -30,7 +33,7 @@ ThermalModule::~ThermalModule() { } void ThermalModule::performOperation(uint8_t opCode) { - if (heater != NULL) { + if (heater != nullptr) { heater->performOperation(0); } } @@ -42,7 +45,7 @@ void ThermalModule::performMode(Strategy strategy) { ThermalComponentIF::HeaterRequest componentHeaterRequest = letComponentsPerformAndDeciceIfWeNeedToHeat(safeOnly); - if (heater == NULL) { + if (heater == nullptr) { informComponentsAboutHeaterState(false, NONE); return; } @@ -53,8 +56,8 @@ void ThermalModule::performMode(Strategy strategy) { //Components overwrite the module request. heating = ((componentHeaterRequest == ThermalComponentIF::HEATER_REQUEST_ON) - || (componentHeaterRequest - == ThermalComponentIF::HEATER_REQUEST_EMERGENCY_ON)); + or (componentHeaterRequest + == ThermalComponentIF::HEATER_REQUEST_EMERGENCY_ON)); } bool dual = (strategy == ACTIVE_DUAL); @@ -76,7 +79,7 @@ void ThermalModule::performMode(Strategy strategy) { } float ThermalModule::getTemperature() { - return moduleTemperature; + return moduleTemperature.value; } void ThermalModule::registerSensor(AbstractTemperatureSensor * sensor) { @@ -85,7 +88,8 @@ void ThermalModule::registerSensor(AbstractTemperatureSensor * sensor) { void ThermalModule::registerComponent(ThermalComponentIF* component, ThermalComponentIF::Priority priority) { - components.push_back(ComponentData( { component, priority, ThermalComponentIF::HEATER_DONT_CARE })); + components.push_back(ComponentData( { component, priority, + ThermalComponentIF::HEATER_DONT_CARE })); } void ThermalModule::calculateTemperature() { @@ -94,12 +98,13 @@ void ThermalModule::calculateTemperature() { std::list::iterator iter = sensors.begin(); for (; iter != sensors.end(); iter++) { if ((*iter)->isValid()) { - moduleTemperature = moduleTemperature + (*iter)->getTemperature(); + moduleTemperature = moduleTemperature.value + + (*iter)->getTemperature(); numberOfValidSensors++; } } if (numberOfValidSensors != 0) { - moduleTemperature = moduleTemperature / numberOfValidSensors; + moduleTemperature = moduleTemperature.value / numberOfValidSensors; moduleTemperature.setValid(PoolVariableIF::VALID); } else { moduleTemperature = INVALID_TEMPERATURE; @@ -117,9 +122,10 @@ ThermalComponentIF* ThermalModule::findComponent(object_id_t objectId) { return NULL; } -ThermalComponentIF::HeaterRequest ThermalModule::letComponentsPerformAndDeciceIfWeNeedToHeat( - bool safeOnly) { - ThermalComponentIF::HeaterRequest heaterRequests[ThermalComponentIF::NUMBER_OF_PRIORITIES]; +ThermalComponentIF::HeaterRequest +ThermalModule::letComponentsPerformAndDeciceIfWeNeedToHeat(bool safeOnly) { + ThermalComponentIF::HeaterRequest + heaterRequests[ThermalComponentIF::NUMBER_OF_PRIORITIES]; survivalTargetTemp = -999; targetTemp = -999; @@ -224,7 +230,7 @@ bool ThermalModule::calculateModuleHeaterRequestAndSetModuleStatus( limit = survivalTargetTemp; } - if (moduleTemperature >= limit) { + if (moduleTemperature.value >= limit) { currentState = OPERATIONAL; } else { currentState = NON_OPERATIONAL; @@ -250,15 +256,15 @@ bool ThermalModule::calculateModuleHeaterRequestAndSetModuleStatus( } void ThermalModule::setHeating(bool on) { - DataSet mySet; - PoolVariable writableTargetState(targetState.getDataPoolId(), - &mySet, PoolVariableIF::VAR_WRITE); - if (on) { - writableTargetState = STATE_REQUEST_HEATING; - } else { - writableTargetState = STATE_REQUEST_PASSIVE; - } - mySet.commit(PoolVariableIF::VALID); +// GlobDataSet mySet; +// gp_int8_t writableTargetState(targetState.getDataPoolId(), +// &mySet, PoolVariableIF::VAR_WRITE); +// if (on) { +// writableTargetState = STATE_REQUEST_HEATING; +// } else { +// writableTargetState = STATE_REQUEST_PASSIVE; +// } +// mySet.commit(PoolVariableIF::VALID); } void ThermalModule::updateTargetTemperatures(ThermalComponentIF* component, diff --git a/thermal/ThermalModule.h b/thermal/ThermalModule.h index 19ab9a54..d1e4fccb 100644 --- a/thermal/ThermalModule.h +++ b/thermal/ThermalModule.h @@ -1,16 +1,25 @@ -#ifndef THERMALMODULE_H_ -#define THERMALMODULE_H_ +#ifndef FSFW_THERMAL_THERMALMODULE_H_ +#define FSFW_THERMAL_THERMALMODULE_H_ -#include "../datapool/DataSet.h" -#include "../datapool/PoolVariable.h" -#include "../devicehandlers/HealthDevice.h" -#include "../events/EventReportingProxyIF.h" #include "ThermalModuleIF.h" -#include #include "tcsDefinitions.h" #include "RedundantHeater.h" + +//#include "../datapoolglob/GlobalDataSet.h" +//#include "../datapoolglob/GlobalPoolVariable.h" +#include "../datapoollocal/LocalPoolDataSetBase.h" +#include "../datapoollocal/LocalPoolVariable.h" +#include "../devicehandlers/HealthDevice.h" +#include "../events/EventReportingProxyIF.h" + +#include + + class PowerSwitchIF; +/** + * @brief Allows creation of different thermal control domains within a system. + */ class ThermalModule: public ThermalModuleIF { friend class ThermalController; public: @@ -19,11 +28,12 @@ public: float hysteresis; }; - ThermalModule(uint32_t moduleTemperaturePoolId, uint32_t currentStatePoolId, - uint32_t targetStatePoolId, DataSet *dataSet, Parameters parameters, - RedundantHeater::Parameters heaterParameters); + ThermalModule(gp_id_t moduleTemperaturePoolId, gp_id_t currentStatePoolId, + gp_id_t targetStatePoolId, LocalPoolDataSetBase *dataSet, + Parameters parameters, RedundantHeater::Parameters heaterParameters); - ThermalModule(uint32_t moduleTemperaturePoolId, DataSet *dataSet); + ThermalModule(gp_id_t moduleTemperaturePoolId, + LocalPoolDataSetBase *dataSet); virtual ~ThermalModule(); @@ -59,20 +69,21 @@ protected: Strategy oldStrategy; - float survivalTargetTemp; + float survivalTargetTemp = 0.0; - float targetTemp; + float targetTemp = 0.0; - bool heating; + bool heating = false; Parameters parameters; - db_float_t moduleTemperature; + lp_var_t moduleTemperature; + //gp_float_t moduleTemperature; - RedundantHeater *heater; + RedundantHeater *heater = nullptr; - db_int8_t currentState; - db_int8_t targetState; + lp_var_t currentState; + lp_var_t targetState; std::list sensors; std::list components; @@ -89,4 +100,4 @@ protected: void updateTargetTemperatures(ThermalComponentIF *component, bool isSafe); }; -#endif /* THERMALMODULE_H_ */ +#endif /* FSFW_THERMAL_THERMALMODULE_H_ */ diff --git a/thermal/ThermalMonitorReporter.cpp b/thermal/ThermalMonitorReporter.cpp new file mode 100644 index 00000000..cefc6110 --- /dev/null +++ b/thermal/ThermalMonitorReporter.cpp @@ -0,0 +1,75 @@ +#include "ThermalMonitorReporter.h" +#include "ThermalComponentIF.h" + +#include "../monitoring/MonitoringIF.h" + +ThermalMonitorReporter::~ThermalMonitorReporter() { +} + +void ThermalMonitorReporter::sendTransitionEvent(float currentValue, + ReturnValue_t state) { + switch (state) { + case MonitoringIF::BELOW_LOW_LIMIT: + EventManagerIF::triggerEvent(reportingId, + ThermalComponentIF::COMPONENT_TEMP_OOL_LOW, state); + break; + case MonitoringIF::ABOVE_HIGH_LIMIT: + EventManagerIF::triggerEvent(reportingId, + ThermalComponentIF::COMPONENT_TEMP_OOL_HIGH, state); + break; + case ThermalComponentIF::BELOW_OPERATIONAL_LIMIT: + EventManagerIF::triggerEvent(reportingId, + ThermalComponentIF::COMPONENT_TEMP_LOW, state); + break; + case ThermalComponentIF::ABOVE_OPERATIONAL_LIMIT: + EventManagerIF::triggerEvent(reportingId, + ThermalComponentIF::COMPONENT_TEMP_HIGH, state); + break; + default: + break; + } +} + +bool ThermalMonitorReporter::isAboveHighLimit() { + if (oldState == ThermalComponentIF::ABOVE_OPERATIONAL_LIMIT) { + return true; + } else { + return false; + } +} + +ReturnValue_t ThermalMonitorReporter::translateState( + ThermalComponentIF::State state, float sample, float lowerLimit, + float upperLimit, bool componentIsOperational) { + if (ThermalComponentIF::isIgnoredState(state)) { + setToUnchecked(); + return MonitoringIF::UNCHECKED; + } + switch (state) { + case ThermalComponentIF::OUT_OF_RANGE_LOW: + return monitorStateIs(MonitoringIF::BELOW_LOW_LIMIT, sample, + lowerLimit); + case ThermalComponentIF::NON_OPERATIONAL_LOW: + if (componentIsOperational) { + return monitorStateIs(ThermalComponentIF::BELOW_OPERATIONAL_LIMIT, + sample, lowerLimit); + } else { + return monitorStateIs(HasReturnvaluesIF::RETURN_OK, sample, 0.0); + } + case ThermalComponentIF::OPERATIONAL: + return monitorStateIs(HasReturnvaluesIF::RETURN_OK, sample, 0.0); + case ThermalComponentIF::NON_OPERATIONAL_HIGH: + if (componentIsOperational) { + return monitorStateIs(ThermalComponentIF::ABOVE_OPERATIONAL_LIMIT, + sample, upperLimit); + } else { + return monitorStateIs(HasReturnvaluesIF::RETURN_OK, sample, 0.0); + } + case ThermalComponentIF::OUT_OF_RANGE_HIGH: + return monitorStateIs(MonitoringIF::ABOVE_HIGH_LIMIT, sample, + upperLimit); + default: + //Never reached, all states covered. + return HasReturnvaluesIF::RETURN_FAILED; + } +} diff --git a/thermal/ThermalMonitorReporter.h b/thermal/ThermalMonitorReporter.h new file mode 100644 index 00000000..4fb68a99 --- /dev/null +++ b/thermal/ThermalMonitorReporter.h @@ -0,0 +1,28 @@ +#ifndef FSFW_THERMAL_THERMALMONITORREPORTER_H_ +#define FSFW_THERMAL_THERMALMONITORREPORTER_H_ + +#include "ThermalComponentIF.h" +#include "../monitoring/MonitorReporter.h" + + +/** + * @brief Monitor Reporter implementation for thermal components. + */ +class ThermalMonitorReporter: public MonitorReporter { +public: + template + ThermalMonitorReporter(Args ... args) : + MonitorReporter(std::forward(args)...) { + } + ~ThermalMonitorReporter(); + ReturnValue_t translateState(ThermalComponentIF::State state, float sample, + float lowerLimit, float upperLimit, + bool componentIsOperational = true); + + bool isAboveHighLimit(); +protected: + virtual void sendTransitionEvent(float currentValue, ReturnValue_t state); + +}; + +#endif /* FSFW_THERMAL_THERMALMONITORREPORTERREPORTER_H_ */ diff --git a/thermal/tcsDefinitions.h b/thermal/tcsDefinitions.h index ad258ced..37e5b849 100644 --- a/thermal/tcsDefinitions.h +++ b/thermal/tcsDefinitions.h @@ -2,7 +2,7 @@ #define TCSDEFINITIONS_H_ -static const uint32_t INVALID_TEMPERATURE = 999; +static const float INVALID_TEMPERATURE = 999; #endif /* TCSDEFINITIONS_H_ */ From 983556b3b3c70b9d4c337ebfc44cbe0290da96ec Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 5 Dec 2020 01:21:04 +0100 Subject: [PATCH 092/119] evil bug fixed --- osal/host/MessageQueue.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osal/host/MessageQueue.cpp b/osal/host/MessageQueue.cpp index bced3713..f1f5867b 100644 --- a/osal/host/MessageQueue.cpp +++ b/osal/host/MessageQueue.cpp @@ -34,7 +34,7 @@ ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessageIF* message, } ReturnValue_t MessageQueue::reply(MessageQueueMessageIF* message) { - if (this->lastPartner != 0) { + if (this->lastPartner != MessageQueueIF::NO_QUEUE) { return sendMessageFrom(this->lastPartner, message, this->getId()); } else { return MessageQueueIF::NO_REPLY_PARTNER; @@ -106,6 +106,7 @@ bool MessageQueue::isDefaultDestinationSet() const { ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, MessageQueueMessageIF* message, MessageQueueId_t sentFrom, bool ignoreFault) { + message->setSender(sentFrom); if(message->getMessageSize() > message->getMaximumMessageSize()) { // Actually, this should never happen or an error will be emitted // in MessageQueueMessage. @@ -126,7 +127,6 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, // TODO: Better returnvalue return HasReturnvaluesIF::RETURN_FAILED; } - if(targetQueue->messageQueue.size() < targetQueue->messageDepth) { MutexHelper mutexLock(targetQueue->queueLock, MutexIF::TimeoutType::WAITING, 20); @@ -145,7 +145,6 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, else { return MessageQueueIF::FULL; } - message->setSender(sentFrom); return HasReturnvaluesIF::RETURN_OK; } From c0e9c22f3442086eb773c9f1029f0ad0bcbaa8da Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 5 Dec 2020 10:40:53 +0100 Subject: [PATCH 093/119] hotfix for message queue --- osal/host/MessageQueue.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osal/host/MessageQueue.cpp b/osal/host/MessageQueue.cpp index bced3713..d662d782 100644 --- a/osal/host/MessageQueue.cpp +++ b/osal/host/MessageQueue.cpp @@ -34,7 +34,7 @@ ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessageIF* message, } ReturnValue_t MessageQueue::reply(MessageQueueMessageIF* message) { - if (this->lastPartner != 0) { + if (this->lastPartner != MessageQueueIF::NO_QUEUE) { return sendMessageFrom(this->lastPartner, message, this->getId()); } else { return MessageQueueIF::NO_REPLY_PARTNER; @@ -106,6 +106,7 @@ bool MessageQueue::isDefaultDestinationSet() const { ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, MessageQueueMessageIF* message, MessageQueueId_t sentFrom, bool ignoreFault) { + message->setSender(sentFrom); if(message->getMessageSize() > message->getMaximumMessageSize()) { // Actually, this should never happen or an error will be emitted // in MessageQueueMessage. @@ -126,7 +127,6 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, // TODO: Better returnvalue return HasReturnvaluesIF::RETURN_FAILED; } - if(targetQueue->messageQueue.size() < targetQueue->messageDepth) { MutexHelper mutexLock(targetQueue->queueLock, MutexIF::TimeoutType::WAITING, 20); @@ -145,7 +145,6 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, else { return MessageQueueIF::FULL; } - message->setSender(sentFrom); return HasReturnvaluesIF::RETURN_OK; } From 6766abc0fa4dcd4bf74e95aa939e36291ed0bced Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 5 Dec 2020 10:41:37 +0100 Subject: [PATCH 094/119] changelog update --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index a6686478..07899769 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ ## Changes from ASTP 0.0.1 to 1.0.0 +### Host OSAL + +- Bugfix in MessageQueue, which caused the sender not to be set properly ### FreeRTOS OSAL From bb11bc5685e54366c819803c8ec5655ba4acdc4d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 5 Dec 2020 17:11:34 +0100 Subject: [PATCH 095/119] task if is set now --- osal/host/PeriodicTask.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/osal/host/PeriodicTask.cpp b/osal/host/PeriodicTask.cpp index 5662c7cc..bfa6c3fd 100644 --- a/osal/host/PeriodicTask.cpp +++ b/osal/host/PeriodicTask.cpp @@ -122,6 +122,7 @@ ReturnValue_t PeriodicTask::addComponent(object_id_t object) { if (newObject == nullptr) { return HasReturnvaluesIF::RETURN_FAILED; } + newObject->setTaskIF(this); objectList.push_back(newObject); return HasReturnvaluesIF::RETURN_OK; } From 5e234f1e23e82c98a18f7f82c8ff37d66ed98f5e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 7 Dec 2020 01:40:10 +0100 Subject: [PATCH 096/119] cmake init, printChar tests --- CMakeLists.txt | 58 +++++++++++++++++++++ action/CMakeLists.txt | 7 +++ container/CMakeLists.txt | 5 ++ controller/CMakeLists.txt | 4 ++ coordinates/CMakeLists.txt | 5 ++ datalinklayer/CMakeLists.txt | 12 +++++ datapool/CMakeLists.txt | 11 ++++ defaultcfg/fsfwconfig/CMakeLists.txt | 15 ++++++ devicehandlers/CMakeLists.txt | 11 ++++ events/CMakeLists.txt | 8 +++ events/eventmatching/CMakeLists.txt | 7 +++ fdir/CMakeLists.txt | 6 +++ globalfunctions/CMakeLists.txt | 12 +++++ globalfunctions/math/CMakeLists.txt | 4 ++ health/CMakeLists.txt | 6 +++ internalError/CMakeLists.txt | 4 ++ ipc/CMakeLists.txt | 6 +++ memory/CMakeLists.txt | 5 ++ modes/CMakeLists.txt | 5 ++ monitoring/CMakeLists.txt | 5 ++ objectmanager/CMakeLists.txt | 5 ++ osal/CMakeLists.txt | 28 ++++++++++ osal/host/CMakeLists.txt | 13 +++++ osal/windows/CMakeLists.txt | 11 ++++ parameters/CMakeLists.txt | 6 +++ power/CMakeLists.txt | 7 +++ pus/CMakeLists.txt | 11 ++++ rmap/CMakeLists.txt | 7 +++ serialize/CMakeLists.txt | 4 ++ serviceinterface/CMakeLists.txt | 5 ++ serviceinterface/ServiceInterfaceBuffer.cpp | 2 +- storagemanager/CMakeLists.txt | 5 ++ subsystem/CMakeLists.txt | 7 +++ subsystem/modes/CMakeLists.txt | 5 ++ tasks/CMakeLists.txt | 5 ++ tcdistribution/CMakeLists.txt | 7 +++ thermal/CMakeLists.txt | 10 ++++ timemanager/CMakeLists.txt | 8 +++ tmstorage/CMakeLists.txt | 4 ++ tmtcpacket/CMakeLists.txt | 8 +++ tmtcpacket/packetmatcher/CMakeLists.txt | 4 ++ tmtcpacket/pus/CMakeLists.txt | 8 +++ tmtcservices/CMakeLists.txt | 9 ++++ 43 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 CMakeLists.txt create mode 100644 action/CMakeLists.txt create mode 100644 container/CMakeLists.txt create mode 100644 controller/CMakeLists.txt create mode 100644 coordinates/CMakeLists.txt create mode 100644 datalinklayer/CMakeLists.txt create mode 100644 datapool/CMakeLists.txt create mode 100644 defaultcfg/fsfwconfig/CMakeLists.txt create mode 100644 devicehandlers/CMakeLists.txt create mode 100644 events/CMakeLists.txt create mode 100644 events/eventmatching/CMakeLists.txt create mode 100644 fdir/CMakeLists.txt create mode 100644 globalfunctions/CMakeLists.txt create mode 100644 globalfunctions/math/CMakeLists.txt create mode 100644 health/CMakeLists.txt create mode 100644 internalError/CMakeLists.txt create mode 100644 ipc/CMakeLists.txt create mode 100644 memory/CMakeLists.txt create mode 100644 modes/CMakeLists.txt create mode 100644 monitoring/CMakeLists.txt create mode 100644 objectmanager/CMakeLists.txt create mode 100644 osal/CMakeLists.txt create mode 100644 osal/host/CMakeLists.txt create mode 100644 osal/windows/CMakeLists.txt create mode 100644 parameters/CMakeLists.txt create mode 100644 power/CMakeLists.txt create mode 100644 pus/CMakeLists.txt create mode 100644 rmap/CMakeLists.txt create mode 100644 serialize/CMakeLists.txt create mode 100644 serviceinterface/CMakeLists.txt create mode 100644 storagemanager/CMakeLists.txt create mode 100644 subsystem/CMakeLists.txt create mode 100644 subsystem/modes/CMakeLists.txt create mode 100644 tasks/CMakeLists.txt create mode 100644 tcdistribution/CMakeLists.txt create mode 100644 thermal/CMakeLists.txt create mode 100644 timemanager/CMakeLists.txt create mode 100644 tmstorage/CMakeLists.txt create mode 100644 tmtcpacket/CMakeLists.txt create mode 100644 tmtcpacket/packetmatcher/CMakeLists.txt create mode 100644 tmtcpacket/pus/CMakeLists.txt create mode 100644 tmtcservices/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..eff272df --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.13) + +set(LIB_FSFW_NAME fsfw) +add_library(${LIB_FSFW_NAME}) + +option(FSFW_USE_RMAP "Compile with RMAP" ON) + +add_subdirectory(action) +add_subdirectory(container) +add_subdirectory(controller) +add_subdirectory(coordinates) +add_subdirectory(datalinklayer) +add_subdirectory(datapool) +add_subdirectory(devicehandlers) +add_subdirectory(events) +add_subdirectory(fdir) +add_subdirectory(globalfunctions) +add_subdirectory(health) +add_subdirectory(internalError) +add_subdirectory(ipc) +add_subdirectory(memory) +add_subdirectory(modes) +add_subdirectory(monitoring) +add_subdirectory(objectmanager) +add_subdirectory(osal) +add_subdirectory(parameters) +add_subdirectory(power) +add_subdirectory(pus) + +if(FSFW_USE_RMAP) +add_subdirectory(rmap) +endif() + +add_subdirectory(serialize) +add_subdirectory(serviceinterface) +add_subdirectory(storagemanager) +add_subdirectory(subsystem) +add_subdirectory(tasks) +add_subdirectory(tcdistribution) +add_subdirectory(thermal) +add_subdirectory(timemanager) +add_subdirectory(tmstorage) +add_subdirectory(tmtcpacket) +add_subdirectory(tmtcservices) + +# The project CMakeLists file has to set the FSFW_CONFIG_PATH and add it. +# If this is not given, we include the default configuration and emit a warning. +if(NOT FSFW_CONFIG_PATH) +message(WARNING "Flight Software Framework configuration path not set!") +message(WARNING "Setting default configuration!") +add_subdirectory(defaultcfg/fsfwconfig) +endif() + +# Required include paths to compile the FSFW +target_include_directories(${LIB_FSFW_NAME} + INTERFACE + ${FSFW_CONFIG_PATH} +) diff --git a/action/CMakeLists.txt b/action/CMakeLists.txt new file mode 100644 index 00000000..a62d4044 --- /dev/null +++ b/action/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + ActionHelper.cpp + ActionMessage.cpp + CommandActionHelper.cpp + SimpleActionHelper.cpp +) \ No newline at end of file diff --git a/container/CMakeLists.txt b/container/CMakeLists.txt new file mode 100644 index 00000000..904cde55 --- /dev/null +++ b/container/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + SharedRingBuffer.cpp + SimpleRingBuffer.cpp +) \ No newline at end of file diff --git a/controller/CMakeLists.txt b/controller/CMakeLists.txt new file mode 100644 index 00000000..6f660738 --- /dev/null +++ b/controller/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + ControllerBase.cpp +) \ No newline at end of file diff --git a/coordinates/CMakeLists.txt b/coordinates/CMakeLists.txt new file mode 100644 index 00000000..a1fa1e52 --- /dev/null +++ b/coordinates/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + CoordinateTransformations.cpp + Sgp4Propagator.cpp +) \ No newline at end of file diff --git a/datalinklayer/CMakeLists.txt b/datalinklayer/CMakeLists.txt new file mode 100644 index 00000000..148e7c5d --- /dev/null +++ b/datalinklayer/CMakeLists.txt @@ -0,0 +1,12 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + Clcw.cpp + DataLinkLayer.cpp + Farm1StateLockout.cpp + Farm1StateOpen.cpp + Farm1StateWait.cpp + MapPacketExtraction.cpp + TcTransferFrame.cpp + TcTransferFrameLocal.cpp + VirtualChannelReception.cpp +) \ No newline at end of file diff --git a/datapool/CMakeLists.txt b/datapool/CMakeLists.txt new file mode 100644 index 00000000..e741f6a1 --- /dev/null +++ b/datapool/CMakeLists.txt @@ -0,0 +1,11 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + ControllerSet.cpp + DataPool.cpp + DataPoolAdmin.cpp + DataPoolParameterWrapper.cpp + DataSet.cpp + HkSwitchHelper.cpp + PoolEntry.cpp + PoolRawAccess.cpp +) \ No newline at end of file diff --git a/defaultcfg/fsfwconfig/CMakeLists.txt b/defaultcfg/fsfwconfig/CMakeLists.txt new file mode 100644 index 00000000..b8b41c93 --- /dev/null +++ b/defaultcfg/fsfwconfig/CMakeLists.txt @@ -0,0 +1,15 @@ +target_sources(${TARGET_NAME} + PRIVATE +) + +# Add include paths for the executable +target_include_directories(${TARGET_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +# Add include paths for the FSFW library +target_include_directories(${LIB_FSFW_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) \ No newline at end of file diff --git a/devicehandlers/CMakeLists.txt b/devicehandlers/CMakeLists.txt new file mode 100644 index 00000000..50c1008f --- /dev/null +++ b/devicehandlers/CMakeLists.txt @@ -0,0 +1,11 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + AssemblyBase.cpp + ChildHandlerBase.cpp + ChildHandlerFDIR.cpp + DeviceHandlerBase.cpp + DeviceHandlerFailureIsolation.cpp + DeviceHandlerMessage.cpp + DeviceTmReportingWrapper.cpp + HealthDevice.cpp +) \ No newline at end of file diff --git a/events/CMakeLists.txt b/events/CMakeLists.txt new file mode 100644 index 00000000..9e63deb8 --- /dev/null +++ b/events/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + Event.cpp + EventManager.cpp + EventMessage.cpp +) + +add_subdirectory(eventmatching) \ No newline at end of file diff --git a/events/eventmatching/CMakeLists.txt b/events/eventmatching/CMakeLists.txt new file mode 100644 index 00000000..81ff9ed8 --- /dev/null +++ b/events/eventmatching/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + EventIdRangeMatcher.cpp + EventMatchTree.cpp + ReporterRangeMatcher.cpp + SeverityRangeMatcher.cpp +) \ No newline at end of file diff --git a/fdir/CMakeLists.txt b/fdir/CMakeLists.txt new file mode 100644 index 00000000..f5ffbba8 --- /dev/null +++ b/fdir/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + EventCorrelation.cpp + FailureIsolationBase.cpp + FaultCounter.cpp +) \ No newline at end of file diff --git a/globalfunctions/CMakeLists.txt b/globalfunctions/CMakeLists.txt new file mode 100644 index 00000000..2b3dcf8e --- /dev/null +++ b/globalfunctions/CMakeLists.txt @@ -0,0 +1,12 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + arrayprinter.cpp + AsciiConverter.cpp + CRC.cpp + DleEncoder.cpp + PeriodicOperationDivider.cpp + timevalOperations.cpp + Type.cpp +) + +add_subdirectory(math) \ No newline at end of file diff --git a/globalfunctions/math/CMakeLists.txt b/globalfunctions/math/CMakeLists.txt new file mode 100644 index 00000000..a9c4ded7 --- /dev/null +++ b/globalfunctions/math/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + QuaternionOperations.cpp +) diff --git a/health/CMakeLists.txt b/health/CMakeLists.txt new file mode 100644 index 00000000..d5f3ccd3 --- /dev/null +++ b/health/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + HealthHelper.cpp + HealthMessage.cpp + HealthTable.cpp +) \ No newline at end of file diff --git a/internalError/CMakeLists.txt b/internalError/CMakeLists.txt new file mode 100644 index 00000000..2b383914 --- /dev/null +++ b/internalError/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + InternalErrorReporter.cpp +) \ No newline at end of file diff --git a/ipc/CMakeLists.txt b/ipc/CMakeLists.txt new file mode 100644 index 00000000..6a3afe33 --- /dev/null +++ b/ipc/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + CommandMessage.cpp + CommandMessageCleaner.cpp + MessageQueueMessage.cpp +) \ No newline at end of file diff --git a/memory/CMakeLists.txt b/memory/CMakeLists.txt new file mode 100644 index 00000000..9edb9031 --- /dev/null +++ b/memory/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + MemoryHelper.cpp + MemoryMessage.cpp +) \ No newline at end of file diff --git a/modes/CMakeLists.txt b/modes/CMakeLists.txt new file mode 100644 index 00000000..8e5c719b --- /dev/null +++ b/modes/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + ModeHelper.cpp + ModeMessage.cpp +) \ No newline at end of file diff --git a/monitoring/CMakeLists.txt b/monitoring/CMakeLists.txt new file mode 100644 index 00000000..d26e807c --- /dev/null +++ b/monitoring/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + LimitViolationReporter.cpp + MonitoringMessage.cpp +) \ No newline at end of file diff --git a/objectmanager/CMakeLists.txt b/objectmanager/CMakeLists.txt new file mode 100644 index 00000000..72aaec89 --- /dev/null +++ b/objectmanager/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + ObjectManager.cpp + SystemObject.cpp +) \ No newline at end of file diff --git a/osal/CMakeLists.txt b/osal/CMakeLists.txt new file mode 100644 index 00000000..d7aa1303 --- /dev/null +++ b/osal/CMakeLists.txt @@ -0,0 +1,28 @@ +# Check the OS_FSFW variable +if(${OS_FSFW} STREQUAL "freertos") + add_subdirectory(FreeRTOS) +elseif(${OS_FSFW} STREQUAL "rtems") + add_subdirectory(rtems) +elseif(${OS_FSFW} STREQUAL "linux") + add_subdirectory(linux) +elseif(${OS_FSFW} STREQUAL "host") + add_subdirectory(host) + if (WIN32) + add_subdirectory(windows) + endif () + +else() + + message(WARNING "The OS_FSFW variable was not set. Assuming host OS..") + # Not set. Assumuing this is a host build, try to determine host OS + if (WIN32) + add_subdirectory(host) + add_subdirectory(windows) + elseif (UNIX) + add_subdirectory(linux) + else () + # MacOS or other OSes have not been tested yet / are not supported. + message(FATAL_ERROR "The host OS could not be determined! Aborting.") + endif() + +endif() \ No newline at end of file diff --git a/osal/host/CMakeLists.txt b/osal/host/CMakeLists.txt new file mode 100644 index 00000000..3efd91d2 --- /dev/null +++ b/osal/host/CMakeLists.txt @@ -0,0 +1,13 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + Clock.cpp + FixedTimeslotTask.cpp + MessageQueue.cpp + Mutex.cpp + MutexFactory.cpp + PeriodicTask.cpp + QueueFactory.cpp + QueueMapManager.cpp + SemaphoreFactory.cpp + TaskFactory.cpp +) \ No newline at end of file diff --git a/osal/windows/CMakeLists.txt b/osal/windows/CMakeLists.txt new file mode 100644 index 00000000..b6e76d6a --- /dev/null +++ b/osal/windows/CMakeLists.txt @@ -0,0 +1,11 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + TcWinUdpPollingTask.cpp + TmTcWinUdpBridge.cpp +) + +target_link_libraries(${LIB_FSFW_NAME} + PRIVATE + wsock32 + ws2_32 +) \ No newline at end of file diff --git a/parameters/CMakeLists.txt b/parameters/CMakeLists.txt new file mode 100644 index 00000000..fb5e4590 --- /dev/null +++ b/parameters/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + ParameterHelper.cpp + ParameterMessage.cpp + ParameterWrapper.cpp +) \ No newline at end of file diff --git a/power/CMakeLists.txt b/power/CMakeLists.txt new file mode 100644 index 00000000..1c625db1 --- /dev/null +++ b/power/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + Fuse.cpp + PowerComponent.cpp + PowerSensor.cpp + PowerSwitcher.cpp +) \ No newline at end of file diff --git a/pus/CMakeLists.txt b/pus/CMakeLists.txt new file mode 100644 index 00000000..758c2629 --- /dev/null +++ b/pus/CMakeLists.txt @@ -0,0 +1,11 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + CService200ModeCommanding.cpp + CService201HealthCommanding.cpp + Service17Test.cpp + Service1TelecommandVerification.cpp + Service2DeviceAccess.cpp + Service5EventReporting.cpp + Service8FunctionManagement.cpp + Service9TimeManagement.cpp +) \ No newline at end of file diff --git a/rmap/CMakeLists.txt b/rmap/CMakeLists.txt new file mode 100644 index 00000000..78c99e42 --- /dev/null +++ b/rmap/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + RMAP.cpp + RMAPCookie.cpp + RmapDeviceCommunicationIF.cpp +) + diff --git a/serialize/CMakeLists.txt b/serialize/CMakeLists.txt new file mode 100644 index 00000000..fc2387e8 --- /dev/null +++ b/serialize/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + SerialBufferAdapter.cpp +) \ No newline at end of file diff --git a/serviceinterface/CMakeLists.txt b/serviceinterface/CMakeLists.txt new file mode 100644 index 00000000..d84adbeb --- /dev/null +++ b/serviceinterface/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + ServiceInterfaceStream.cpp + ServiceInterfaceBuffer.cpp +) \ No newline at end of file diff --git a/serviceinterface/ServiceInterfaceBuffer.cpp b/serviceinterface/ServiceInterfaceBuffer.cpp index 68fc4dec..bc360334 100644 --- a/serviceinterface/ServiceInterfaceBuffer.cpp +++ b/serviceinterface/ServiceInterfaceBuffer.cpp @@ -4,7 +4,7 @@ #include // to be implemented by bsp -extern "C" void printChar(const char*, bool errStream); +/*extern "C" */void printChar(const char*, bool errStream); #ifndef UT699 diff --git a/storagemanager/CMakeLists.txt b/storagemanager/CMakeLists.txt new file mode 100644 index 00000000..57c92195 --- /dev/null +++ b/storagemanager/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + ConstStorageAccessor.cpp + StorageAccessor.cpp +) \ No newline at end of file diff --git a/subsystem/CMakeLists.txt b/subsystem/CMakeLists.txt new file mode 100644 index 00000000..5c98ee70 --- /dev/null +++ b/subsystem/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + Subsystem.cpp + SubsystemBase.cpp +) + +add_subdirectory(modes) \ No newline at end of file diff --git a/subsystem/modes/CMakeLists.txt b/subsystem/modes/CMakeLists.txt new file mode 100644 index 00000000..6ac6a293 --- /dev/null +++ b/subsystem/modes/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + ModeSequenceMessage.cpp + ModeStore.cpp +) diff --git a/tasks/CMakeLists.txt b/tasks/CMakeLists.txt new file mode 100644 index 00000000..1964bb4e --- /dev/null +++ b/tasks/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + FixedSequenceSlot.cpp + FixedSlotSequence.cpp +) \ No newline at end of file diff --git a/tcdistribution/CMakeLists.txt b/tcdistribution/CMakeLists.txt new file mode 100644 index 00000000..17dc186c --- /dev/null +++ b/tcdistribution/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + CCSDSDistributor.cpp + PUSDistributor.cpp + TcDistributor.cpp + TcPacketCheck.cpp +) diff --git a/thermal/CMakeLists.txt b/thermal/CMakeLists.txt new file mode 100644 index 00000000..67664fbe --- /dev/null +++ b/thermal/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + AbstractTemperatureSensor.cpp + CoreComponent.cpp + Heater.cpp + RedundantHeater.cpp + ThermalComponent.cpp + ThermalModule.cpp + ThermalMonitor.cpp +) diff --git a/timemanager/CMakeLists.txt b/timemanager/CMakeLists.txt new file mode 100644 index 00000000..3367775f --- /dev/null +++ b/timemanager/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + CCSDSTime.cpp + Countdown.cpp + Stopwatch.cpp + TimeMessage.cpp + TimeStamper.cpp +) diff --git a/tmstorage/CMakeLists.txt b/tmstorage/CMakeLists.txt new file mode 100644 index 00000000..7990d85a --- /dev/null +++ b/tmstorage/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + TmStoreMessage.cpp +) diff --git a/tmtcpacket/CMakeLists.txt b/tmtcpacket/CMakeLists.txt new file mode 100644 index 00000000..fe3d2a4d --- /dev/null +++ b/tmtcpacket/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + SpacePacket.cpp + SpacePacketBase.cpp +) + +add_subdirectory(packetmatcher) +add_subdirectory(pus) \ No newline at end of file diff --git a/tmtcpacket/packetmatcher/CMakeLists.txt b/tmtcpacket/packetmatcher/CMakeLists.txt new file mode 100644 index 00000000..e9a8d03b --- /dev/null +++ b/tmtcpacket/packetmatcher/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + PacketMatchTree.cpp +) diff --git a/tmtcpacket/pus/CMakeLists.txt b/tmtcpacket/pus/CMakeLists.txt new file mode 100644 index 00000000..fcfc82d2 --- /dev/null +++ b/tmtcpacket/pus/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + TcPacketBase.cpp + TcPacketStored.cpp + TmPacketBase.cpp + TmPacketMinimal.cpp + TmPacketStored.cpp +) diff --git a/tmtcservices/CMakeLists.txt b/tmtcservices/CMakeLists.txt new file mode 100644 index 00000000..c30af214 --- /dev/null +++ b/tmtcservices/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + CommandingServiceBase.cpp + PusServiceBase.cpp + PusVerificationReport.cpp + TmTcBridge.cpp + TmTcMessage.cpp + VerificationReporter.cpp +) \ No newline at end of file From 33d4b574001e89828c044eff295ca0a20295e549 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 7 Dec 2020 02:03:42 +0100 Subject: [PATCH 097/119] additional build option --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index eff272df..9973c0ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ set(LIB_FSFW_NAME fsfw) add_library(${LIB_FSFW_NAME}) option(FSFW_USE_RMAP "Compile with RMAP" ON) +option(FSFW_USE_DATALINKLAYER "Compile with Data Link Layer" ON) add_subdirectory(action) add_subdirectory(container) From c4ba243885cf912eb940e0442bb2728802eac46b Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 7 Dec 2020 12:17:02 +0100 Subject: [PATCH 098/119] small bugfixes --- osal/host/CMakeLists.txt | 10 +++++++++- serviceinterface/ServiceInterfaceBuffer.cpp | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osal/host/CMakeLists.txt b/osal/host/CMakeLists.txt index 3efd91d2..d54cfac8 100644 --- a/osal/host/CMakeLists.txt +++ b/osal/host/CMakeLists.txt @@ -10,4 +10,12 @@ target_sources(${LIB_FSFW_NAME} QueueMapManager.cpp SemaphoreFactory.cpp TaskFactory.cpp -) \ No newline at end of file +) + +if(UNIX) + target_link_libraries(${LIB_FSFW_NAME} + PRIVATE + pthread + rt + ) +endif() \ No newline at end of file diff --git a/serviceinterface/ServiceInterfaceBuffer.cpp b/serviceinterface/ServiceInterfaceBuffer.cpp index bc360334..68fc4dec 100644 --- a/serviceinterface/ServiceInterfaceBuffer.cpp +++ b/serviceinterface/ServiceInterfaceBuffer.cpp @@ -4,7 +4,7 @@ #include // to be implemented by bsp -/*extern "C" */void printChar(const char*, bool errStream); +extern "C" void printChar(const char*, bool errStream); #ifndef UT699 From 489a2097b850313154866cf4d7bbfbc09320a5b5 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 7 Dec 2020 12:25:30 +0100 Subject: [PATCH 099/119] fix for host osal linux --- osal/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osal/CMakeLists.txt b/osal/CMakeLists.txt index d7aa1303..02ff2405 100644 --- a/osal/CMakeLists.txt +++ b/osal/CMakeLists.txt @@ -9,6 +9,12 @@ elseif(${OS_FSFW} STREQUAL "host") add_subdirectory(host) if (WIN32) add_subdirectory(windows) + elseif(UNIX) + target_sources(${LIB_FSFW_NAME} + PUBLIC + linux/TcUnixUdpPollingTask.cpp + linux/TmTcUnixUdpBridge.cpp + ) endif () else() From d93a8692e08abbe7926955ffdaa95ad569c372f9 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 7 Dec 2020 12:59:09 +0100 Subject: [PATCH 100/119] cmakelists update --- CMakeLists.txt | 26 ++++++++++++++++++++++++++ osal/host/CMakeLists.txt | 2 +- osal/linux/CMakeLists.txt | 25 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 osal/linux/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 9973c0ec..669283c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,32 @@ cmake_minimum_required(VERSION 3.13) set(LIB_FSFW_NAME fsfw) add_library(${LIB_FSFW_NAME}) +# Set options for FSFW OSAL selection. +if(UNIX) +set(OS_FSFW "linux" CACHE STRING "OS abstraction layer used in the FSFW") +elseif(WIN32) +set(OS_FSFW "host" CACHE STRING "OS abstraction layer used in the FSFW") +endif() + +set_property(CACHE OS_FSFW PROPERTY STRINGS host linux rtems freertos) + +if(${OS_FSFW} STREQUAL host) +set(OS_FSFW_NAME "Host") +elseif(${OS_FSFW} STREQUAL linux) +set(OS_FSFW_NAME "Linux") +elseif(${OS_FSFW} STREQUAL freertos) +set(OS_FSFW_NAME "FreeRTOS") +elseif(${OS_FSFW} STREQUAL rtems) +set(OS_FSFW_NAME "RTEMS") +else() +message(WARNING "Invalid operating system for FSFW specified! Setting to host..") +set(OS_FSFW_NAME "Host") +set(OS_FSFW "host") +endif() + +message(STATUS "Compiling FSFW for the ${OS_FSFW_NAME} operating system") + +# Options to exclude parts of the FSFW from compilation. option(FSFW_USE_RMAP "Compile with RMAP" ON) option(FSFW_USE_DATALINKLAYER "Compile with Data Link Layer" ON) diff --git a/osal/host/CMakeLists.txt b/osal/host/CMakeLists.txt index d54cfac8..d73e6f27 100644 --- a/osal/host/CMakeLists.txt +++ b/osal/host/CMakeLists.txt @@ -13,9 +13,9 @@ target_sources(${LIB_FSFW_NAME} ) if(UNIX) + add_definitions(-pthread) target_link_libraries(${LIB_FSFW_NAME} PRIVATE - pthread rt ) endif() \ No newline at end of file diff --git a/osal/linux/CMakeLists.txt b/osal/linux/CMakeLists.txt new file mode 100644 index 00000000..c0096e42 --- /dev/null +++ b/osal/linux/CMakeLists.txt @@ -0,0 +1,25 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + Clock.cpp + BinarySemaphore.cpp + CountingSemaphore.cpp + FixedTimeslotTask.cpp + InternalErrorCodes.cpp + MessageQueue.cpp + Mutex.cpp + MutexFactory.cpp + PeriodicPosixTask.cpp + PosixThread.cpp + QueueFactory.cpp + SemaphoreFactory.cpp + TaskFactory.cpp + TcUnixUdpPollingTask.cpp + TmTcUnixUdpBridge.cpp + Timer.cpp +) + +target_link_libraries(${LIB_FSFW_NAME} + PRIVATE + rt + pthread +) From 2a025de321396315ac1119d0f5e46d9abdc95249 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 14:19:53 +0100 Subject: [PATCH 101/119] changelog update --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index c221c811..2d1152c7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,4 +14,4 @@ a C file without issues ### Events -- makeEvent function: Not takes three input parameters instead of two and allows setting a unique ID +- makeEvent function: Now takes three input parameters instead of two and allows setting a unique ID From fdbe5408b7b5924ee431206d5cdc32c47a3374b8 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 14:43:44 +0100 Subject: [PATCH 102/119] renamed namespaces --- datalinklayer/DataLinkLayer.h | 12 +++--- datapool/HkSwitchHelper.h | 2 +- .../DeviceHandlerFailureIsolation.cpp | 6 +-- devicehandlers/DeviceHandlerIF.h | 22 +++++----- events/Event.h | 43 ++++++++----------- events/EventManager.cpp | 2 +- fdir/FailureIsolationBase.cpp | 4 +- fdir/FailureIsolationBase.h | 6 +-- health/HasHealthIF.h | 14 +++--- modes/HasModesIF.h | 16 +++---- monitoring/MonitoringIF.h | 8 ++-- power/Fuse.h | 8 ++-- power/PowerSwitchIF.h | 2 +- pus/Service17Test.h | 2 +- pus/Service9TimeManagement.h | 4 +- storagemanager/StorageManagerIF.h | 4 +- thermal/AbstractTemperatureSensor.h | 6 +-- thermal/Heater.h | 10 ++--- thermal/ThermalComponentIF.h | 10 ++--- tmstorage/TmStoreBackendIF.h | 32 +++++++------- 20 files changed, 104 insertions(+), 109 deletions(-) diff --git a/datalinklayer/DataLinkLayer.h b/datalinklayer/DataLinkLayer.h index 5a900099..17a57d61 100644 --- a/datalinklayer/DataLinkLayer.h +++ b/datalinklayer/DataLinkLayer.h @@ -19,12 +19,12 @@ class VirtualChannelReception; class DataLinkLayer : public CCSDSReturnValuesIF { public: static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SYSTEM_1; - static const Event RF_AVAILABLE = MAKE_EVENT(0, SEVERITY::INFO); //!< A RF available signal was detected. P1: raw RFA state, P2: 0 - static const Event RF_LOST = MAKE_EVENT(1, SEVERITY::INFO); //!< A previously found RF available signal was lost. P1: raw RFA state, P2: 0 - static const Event BIT_LOCK = MAKE_EVENT(2, SEVERITY::INFO); //!< A Bit Lock signal. Was detected. P1: raw BLO state, P2: 0 - static const Event BIT_LOCK_LOST = MAKE_EVENT(3, SEVERITY::INFO); //!< A previously found Bit Lock signal was lost. P1: raw BLO state, P2: 0 -// static const Event RF_CHAIN_LOST = MAKE_EVENT(4, SEVERITY::INFO); //!< The CCSDS Board detected that either bit lock or RF available or both are lost. No parameters. - static const Event FRAME_PROCESSING_FAILED = MAKE_EVENT(5, SEVERITY::LOW); //!< The CCSDS Board could not interpret a TC + static const Event RF_AVAILABLE = MAKE_EVENT(0, severity::INFO); //!< A RF available signal was detected. P1: raw RFA state, P2: 0 + static const Event RF_LOST = MAKE_EVENT(1, severity::INFO); //!< A previously found RF available signal was lost. P1: raw RFA state, P2: 0 + static const Event BIT_LOCK = MAKE_EVENT(2, severity::INFO); //!< A Bit Lock signal. Was detected. P1: raw BLO state, P2: 0 + static const Event BIT_LOCK_LOST = MAKE_EVENT(3, severity::INFO); //!< A previously found Bit Lock signal was lost. P1: raw BLO state, P2: 0 +// static const Event RF_CHAIN_LOST = MAKE_EVENT(4, severity::INFO); //!< The CCSDS Board detected that either bit lock or RF available or both are lost. No parameters. + static const Event FRAME_PROCESSING_FAILED = MAKE_EVENT(5, severity::LOW); //!< The CCSDS Board could not interpret a TC /** * The Constructor sets the passed parameters and nothing else. * @param set_frame_buffer The buffer in which incoming frame candidates are stored. diff --git a/datapool/HkSwitchHelper.h b/datapool/HkSwitchHelper.h index 385b2047..bb9e7dc6 100644 --- a/datapool/HkSwitchHelper.h +++ b/datapool/HkSwitchHelper.h @@ -13,7 +13,7 @@ class HkSwitchHelper: public ExecutableObjectIF, public CommandsActionsIF { public: static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::HK; - static const Event SWITCHING_TM_FAILED = MAKE_EVENT(1, SEVERITY::LOW); //!< Commanding the HK Service failed, p1: error code, p2 action: 0 disable / 1 enable + static const Event SWITCHING_TM_FAILED = MAKE_EVENT(1, severity::LOW); //!< Commanding the HK Service failed, p1: error code, p2 action: 0 disable / 1 enable HkSwitchHelper(EventReportingProxyIF *eventProxy); virtual ~HkSwitchHelper(); diff --git a/devicehandlers/DeviceHandlerFailureIsolation.cpp b/devicehandlers/DeviceHandlerFailureIsolation.cpp index 9fbe71d8..f4966351 100644 --- a/devicehandlers/DeviceHandlerFailureIsolation.cpp +++ b/devicehandlers/DeviceHandlerFailureIsolation.cpp @@ -191,7 +191,7 @@ void DeviceHandlerFailureIsolation::triggerEvent(Event event, uint32_t parameter uint32_t parameter2) { //Do not throw error events if fdirState != none. //This will still forward MODE and HEALTH INFO events in any case. - if (fdirState == NONE || EVENT::getSeverity(event) == SEVERITY::INFO) { + if (fdirState == NONE || event::getSeverity(event) == severity::INFO) { FailureIsolationBase::triggerEvent(event, parameter1, parameter2); } } @@ -201,7 +201,7 @@ bool DeviceHandlerFailureIsolation::isFdirActionInProgress() { } void DeviceHandlerFailureIsolation::startRecovery(Event reason) { - throwFdirEvent(FDIR_STARTS_RECOVERY, EVENT::getEventId(reason)); + throwFdirEvent(FDIR_STARTS_RECOVERY, event::getEventId(reason)); setOwnerHealth(HasHealthIF::NEEDS_RECOVERY); setFdirState(RECOVERY_ONGOING); } @@ -228,7 +228,7 @@ ReturnValue_t DeviceHandlerFailureIsolation::getParameter(uint8_t domainId, } void DeviceHandlerFailureIsolation::setFaulty(Event reason) { - throwFdirEvent(FDIR_TURNS_OFF_DEVICE, EVENT::getEventId(reason)); + throwFdirEvent(FDIR_TURNS_OFF_DEVICE, event::getEventId(reason)); setOwnerHealth(HasHealthIF::FAULTY); setFdirState(AWAIT_SHUTDOWN); } diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index 35bbe376..39a0e39a 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -83,17 +83,17 @@ public: static const Mode_t _MODE_SWITCH_IS_OFF = TRANSITION_MODE_BASE_ACTION_MASK | 5; 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); + 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); static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_IF; diff --git a/events/Event.h b/events/Event.h index 90ff1a90..aebc4bc5 100644 --- a/events/Event.h +++ b/events/Event.h @@ -3,7 +3,7 @@ #include #include "fwSubsystemIdRanges.h" -//could be move to more suitable location +// could be moved to more suitable location #include typedef uint16_t EventId_t; @@ -13,33 +13,28 @@ typedef uint8_t EventSeverity_t; typedef uint32_t Event; -namespace EVENT { -EventId_t getEventId(Event event); +namespace event { -EventSeverity_t getSeverity(Event event); - -Event makeEvent(uint8_t subsystemId, uint8_t uniqueEventId, - EventSeverity_t eventSeverity); +constexpr EventId_t getEventId(Event event) { + return (event & 0xFFFF); } -namespace SEVERITY { - static const EventSeverity_t INFO = 1; - static const EventSeverity_t LOW = 2; - static const EventSeverity_t MEDIUM = 3; - static const EventSeverity_t HIGH = 4; +constexpr EventSeverity_t getSeverity(Event event) { + return ((event >> 16) & 0xFF); } -//Unfortunately, this does not work nicely because of the inability to define static classes in headers. -//struct Event { -// Event(uint8_t domain, uint8_t counter, EventSeverity_t severity) : -// id(domain*100+counter), severity(severity) { -// } -// EventId_t id; -// EventSeverity_t severity; -// static const EventSeverity_t INFO = 1; -// static const EventSeverity_t LOW = 2; -// static const EventSeverity_t MEDIUM = 3; -// static const EventSeverity_t HIGH = 4; -//}; +constexpr Event makeEvent(uint8_t subsystemId, uint8_t uniqueEventId, + EventSeverity_t eventSeverity) { + return (eventSeverity << 16) + (subsystemId * 100) + uniqueEventId; +} + +} + +namespace severity { + static constexpr EventSeverity_t INFO = 1; + static constexpr EventSeverity_t LOW = 2; + static constexpr EventSeverity_t MEDIUM = 3; + static constexpr EventSeverity_t HIGH = 4; +} #endif /* EVENTOBJECT_EVENT_H_ */ diff --git a/events/EventManager.cpp b/events/EventManager.cpp index f60a8a66..71d1e2e6 100644 --- a/events/EventManager.cpp +++ b/events/EventManager.cpp @@ -118,7 +118,7 @@ ReturnValue_t EventManager::unsubscribeFromEventRange(MessageQueueId_t listener, void EventManager::printEvent(EventMessage* message) { const char *string = 0; switch (message->getSeverity()) { - case SEVERITY::INFO: + case severity::INFO: #ifdef DEBUG_INFO_EVENT string = translateObject(message->getReporter()); sif::info << "EVENT: "; diff --git a/fdir/FailureIsolationBase.cpp b/fdir/FailureIsolationBase.cpp index f3b34f0f..a04a0313 100644 --- a/fdir/FailureIsolationBase.cpp +++ b/fdir/FailureIsolationBase.cpp @@ -139,7 +139,7 @@ void FailureIsolationBase::triggerEvent(Event event, uint32_t parameter1, uint32_t parameter2) { //With this mechanism, all events are disabled for a certain device. //That's not so good for visibility. - if (isFdirDisabledForSeverity(EVENT::getSeverity(event))) { + if (isFdirDisabledForSeverity(event::getSeverity(event))) { return; } EventMessage message(event, ownerId, parameter1, parameter2); @@ -148,7 +148,7 @@ void FailureIsolationBase::triggerEvent(Event event, uint32_t parameter1, } bool FailureIsolationBase::isFdirDisabledForSeverity(EventSeverity_t severity) { - if ((owner != NULL) && (severity != SEVERITY::INFO)) { + if ((owner != NULL) && (severity != severity::INFO)) { if (owner->getHealth() == HasHealthIF::EXTERNAL_CONTROL) { //External control disables handling of fault messages. return true; diff --git a/fdir/FailureIsolationBase.h b/fdir/FailureIsolationBase.h index 5b2c099a..4a3f5adf 100644 --- a/fdir/FailureIsolationBase.h +++ b/fdir/FailureIsolationBase.h @@ -14,9 +14,9 @@ class FailureIsolationBase: public HasReturnvaluesIF, public HasParametersIF { public: static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::FDIR_1; - static const Event FDIR_CHANGED_STATE = MAKE_EVENT(1, SEVERITY::INFO); //!< FDIR has an internal state, which changed from par2 (oldState) to par1 (newState). - static const Event FDIR_STARTS_RECOVERY = MAKE_EVENT(2, SEVERITY::MEDIUM); //!< FDIR tries to restart device. Par1: event that caused recovery. - static const Event FDIR_TURNS_OFF_DEVICE = MAKE_EVENT(3, SEVERITY::MEDIUM); //!< FDIR turns off device. Par1: event that caused recovery. + static const Event FDIR_CHANGED_STATE = MAKE_EVENT(1, severity::INFO); //!< FDIR has an internal state, which changed from par2 (oldState) to par1 (newState). + static const Event FDIR_STARTS_RECOVERY = MAKE_EVENT(2, severity::MEDIUM); //!< FDIR tries to restart device. Par1: event that caused recovery. + static const Event FDIR_TURNS_OFF_DEVICE = MAKE_EVENT(3, severity::MEDIUM); //!< FDIR turns off device. Par1: event that caused recovery. FailureIsolationBase(object_id_t owner, object_id_t parent = objects::NO_OBJECT, diff --git a/health/HasHealthIF.h b/health/HasHealthIF.h index 86863ea8..a6254e39 100644 --- a/health/HasHealthIF.h +++ b/health/HasHealthIF.h @@ -21,13 +21,13 @@ public: static const ReturnValue_t INVALID_HEALTH_STATE = MAKE_RETURN_CODE(2); static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SYSTEM_MANAGER_1; - static const Event HEALTH_INFO = MAKE_EVENT(6, SEVERITY::INFO); - static const Event CHILD_CHANGED_HEALTH = MAKE_EVENT(7, SEVERITY::INFO); - static const Event CHILD_PROBLEMS = MAKE_EVENT(8, SEVERITY::LOW); - static const Event OVERWRITING_HEALTH = MAKE_EVENT(9, SEVERITY::LOW); //!< Assembly overwrites health information of children to keep satellite alive. - static const Event TRYING_RECOVERY = MAKE_EVENT(10, SEVERITY::MEDIUM); //!< Someone starts a recovery of a component (typically power-cycle). No parameters. - static const Event RECOVERY_STEP = MAKE_EVENT(11, SEVERITY::MEDIUM); //!< Recovery is ongoing. Comes twice during recovery. P1: 0 for the first, 1 for the second event. P2: 0 - static const Event RECOVERY_DONE = MAKE_EVENT(12, SEVERITY::MEDIUM); //!< Recovery was completed. Not necessarily successful. No parameters. + static const Event HEALTH_INFO = MAKE_EVENT(6, severity::INFO); + static const Event CHILD_CHANGED_HEALTH = MAKE_EVENT(7, severity::INFO); + static const Event CHILD_PROBLEMS = MAKE_EVENT(8, severity::LOW); + static const Event OVERWRITING_HEALTH = MAKE_EVENT(9, severity::LOW); //!< Assembly overwrites health information of children to keep satellite alive. + static const Event TRYING_RECOVERY = MAKE_EVENT(10, severity::MEDIUM); //!< Someone starts a recovery of a component (typically power-cycle). No parameters. + static const Event RECOVERY_STEP = MAKE_EVENT(11, severity::MEDIUM); //!< Recovery is ongoing. Comes twice during recovery. P1: 0 for the first, 1 for the second event. P2: 0 + static const Event RECOVERY_DONE = MAKE_EVENT(12, severity::MEDIUM); //!< Recovery was completed. Not necessarily successful. No parameters. virtual ~HasHealthIF() { } diff --git a/modes/HasModesIF.h b/modes/HasModesIF.h index 34a15937..c29a0a96 100644 --- a/modes/HasModesIF.h +++ b/modes/HasModesIF.h @@ -18,14 +18,14 @@ public: static const ReturnValue_t INVALID_SUBMODE = MAKE_RETURN_CODE(0x04); static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SYSTEM_MANAGER; - static const Event CHANGING_MODE = MAKE_EVENT(0, SEVERITY::INFO); //!< An object announces changing the mode. p1: target mode. p2: target submode - static const Event MODE_INFO = MAKE_EVENT(1, SEVERITY::INFO); //!< An Object announces its mode; parameter1 is mode, parameter2 is submode - static const Event FALLBACK_FAILED = MAKE_EVENT(2, SEVERITY::HIGH); - static const Event MODE_TRANSITION_FAILED = MAKE_EVENT(3, SEVERITY::LOW); - static const Event CANT_KEEP_MODE = MAKE_EVENT(4, SEVERITY::HIGH); - static const Event OBJECT_IN_INVALID_MODE = MAKE_EVENT(5, SEVERITY::LOW); //!< Indicates a bug or configuration failure: Object is in a mode it should never be in. - static const Event FORCING_MODE = MAKE_EVENT(6, SEVERITY::MEDIUM); //!< The mode is changed, but for some reason, the change is forced, i.e. EXTERNAL_CONTROL ignored. p1: target mode. p2: target submode - static const Event MODE_CMD_REJECTED = MAKE_EVENT(7, SEVERITY::LOW); //!< A mode command was rejected by the called object. Par1: called object id, Par2: return code. + static const Event CHANGING_MODE = MAKE_EVENT(0, severity::INFO); //!< An object announces changing the mode. p1: target mode. p2: target submode + static const Event MODE_INFO = MAKE_EVENT(1, severity::INFO); //!< An Object announces its mode; parameter1 is mode, parameter2 is submode + static const Event FALLBACK_FAILED = MAKE_EVENT(2, severity::HIGH); + static const Event MODE_TRANSITION_FAILED = MAKE_EVENT(3, severity::LOW); + static const Event CANT_KEEP_MODE = MAKE_EVENT(4, severity::HIGH); + static const Event OBJECT_IN_INVALID_MODE = MAKE_EVENT(5, severity::LOW); //!< Indicates a bug or configuration failure: Object is in a mode it should never be in. + static const Event FORCING_MODE = MAKE_EVENT(6, severity::MEDIUM); //!< The mode is changed, but for some reason, the change is forced, i.e. EXTERNAL_CONTROL ignored. p1: target mode. p2: target submode + static const Event MODE_CMD_REJECTED = MAKE_EVENT(7, severity::LOW); //!< A mode command was rejected by the called object. Par1: called object id, Par2: return code. static const Mode_t MODE_ON = 1; //!< The device is powered and ready to perform operations. In this mode, no commands are sent by the device handler itself, but direct commands van be commanded and will be interpreted static const Mode_t MODE_OFF = 0; //!< The device is powered off. The only command accepted in this mode is a mode change to on. diff --git a/monitoring/MonitoringIF.h b/monitoring/MonitoringIF.h index 44218c36..040539ef 100644 --- a/monitoring/MonitoringIF.h +++ b/monitoring/MonitoringIF.h @@ -15,10 +15,10 @@ public: static const uint8_t LIMIT_TYPE_OBJECT = 128; static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::FDIR_2; - static const Event MONITOR_CHANGED_STATE = MAKE_EVENT(1, SEVERITY::LOW); - static const Event VALUE_BELOW_LOW_LIMIT = MAKE_EVENT(2, SEVERITY::LOW); - static const Event VALUE_ABOVE_HIGH_LIMIT = MAKE_EVENT(3, SEVERITY::LOW); - static const Event VALUE_OUT_OF_RANGE = MAKE_EVENT(4, SEVERITY::LOW); + static const Event MONITOR_CHANGED_STATE = MAKE_EVENT(1, severity::LOW); + static const Event VALUE_BELOW_LOW_LIMIT = MAKE_EVENT(2, severity::LOW); + static const Event VALUE_ABOVE_HIGH_LIMIT = MAKE_EVENT(3, severity::LOW); + static const Event VALUE_OUT_OF_RANGE = MAKE_EVENT(4, severity::LOW); static const uint8_t INTERFACE_ID = CLASS_ID::LIMITS_IF; static const ReturnValue_t UNCHECKED = MAKE_RETURN_CODE(1); diff --git a/power/Fuse.h b/power/Fuse.h index 279642be..c67f69cc 100644 --- a/power/Fuse.h +++ b/power/Fuse.h @@ -32,10 +32,10 @@ public: }; static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PCDU_1; - static const Event FUSE_CURRENT_HIGH = MAKE_EVENT(1, SEVERITY::LOW); //!< PSS detected that current on a fuse is totally out of bounds. - static const Event FUSE_WENT_OFF = MAKE_EVENT(2, SEVERITY::LOW); //!< PSS detected a fuse that went off. - static const Event POWER_ABOVE_HIGH_LIMIT = MAKE_EVENT(4, SEVERITY::LOW); //!< PSS detected a fuse that violates its limits. - static const Event POWER_BELOW_LOW_LIMIT = MAKE_EVENT(5, SEVERITY::LOW); //!< PSS detected a fuse that violates its limits. + static const Event FUSE_CURRENT_HIGH = MAKE_EVENT(1, severity::LOW); //!< PSS detected that current on a fuse is totally out of bounds. + static const Event FUSE_WENT_OFF = MAKE_EVENT(2, severity::LOW); //!< PSS detected a fuse that went off. + static const Event POWER_ABOVE_HIGH_LIMIT = MAKE_EVENT(4, severity::LOW); //!< PSS detected a fuse that violates its limits. + static const Event POWER_BELOW_LOW_LIMIT = MAKE_EVENT(5, severity::LOW); //!< PSS detected a fuse that violates its limits. typedef std::list DeviceList; Fuse(object_id_t fuseObjectId, uint8_t fuseId, VariableIds ids, diff --git a/power/PowerSwitchIF.h b/power/PowerSwitchIF.h index 40220fb6..c6e4b257 100644 --- a/power/PowerSwitchIF.h +++ b/power/PowerSwitchIF.h @@ -32,7 +32,7 @@ public: static const ReturnValue_t FUSE_ON = MAKE_RETURN_CODE(3); static const ReturnValue_t FUSE_OFF = MAKE_RETURN_CODE(4); static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PCDU_2; - static const Event SWITCH_WENT_OFF = MAKE_EVENT(0, SEVERITY::LOW); //!< Someone detected that a switch went off which shouldn't. Severity: Low, Parameter1: switchId1, Parameter2: switchId2 + static const Event SWITCH_WENT_OFF = MAKE_EVENT(0, severity::LOW); //!< Someone detected that a switch went off which shouldn't. Severity: Low, Parameter1: switchId1, Parameter2: switchId2 /** * send a direct command to the Power Unit to enable/disable the specified switch. * diff --git a/pus/Service17Test.h b/pus/Service17Test.h index e2681865..83d436ea 100644 --- a/pus/Service17Test.h +++ b/pus/Service17Test.h @@ -21,7 +21,7 @@ class Service17Test: public PusServiceBase { public: // Custom events which can be triggered static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PUS_SERVICE_17; - static constexpr Event TEST = MAKE_EVENT(0, SEVERITY::INFO); + static constexpr Event TEST = MAKE_EVENT(0, severity::INFO); enum Subservice: uint8_t { //! [EXPORT] : [COMMAND] Perform connection test diff --git a/pus/Service9TimeManagement.h b/pus/Service9TimeManagement.h index 4802cdec..70b86966 100644 --- a/pus/Service9TimeManagement.h +++ b/pus/Service9TimeManagement.h @@ -7,8 +7,8 @@ class Service9TimeManagement: public PusServiceBase { public: static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PUS_SERVICE_9; - static constexpr Event CLOCK_SET = MAKE_EVENT(0, SEVERITY::INFO); //!< Clock has been set. P1: New Uptime. P2: Old Uptime - static constexpr Event CLOCK_SET_FAILURE = MAKE_EVENT(1, SEVERITY::LOW); //!< Clock could not be set. P1: Returncode. + static constexpr Event CLOCK_SET = MAKE_EVENT(0, severity::INFO); //!< Clock has been set. P1: New Uptime. P2: Old Uptime + static constexpr Event CLOCK_SET_FAILURE = MAKE_EVENT(1, severity::LOW); //!< Clock could not be set. P1: Returncode. static constexpr uint8_t CLASS_ID = CLASS_ID::PUS_SERVICE_9; diff --git a/storagemanager/StorageManagerIF.h b/storagemanager/StorageManagerIF.h index 834e7563..ab1998bf 100644 --- a/storagemanager/StorageManagerIF.h +++ b/storagemanager/StorageManagerIF.h @@ -37,8 +37,8 @@ public: static const ReturnValue_t POOL_TOO_LARGE = MAKE_RETURN_CODE(6); //!< Pool size too large on initialization. static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::OBSW; - static const Event GET_DATA_FAILED = MAKE_EVENT(0, SEVERITY::LOW); - static const Event STORE_DATA_FAILED = MAKE_EVENT(1, SEVERITY::LOW); + static const Event GET_DATA_FAILED = MAKE_EVENT(0, severity::LOW); + static const Event STORE_DATA_FAILED = MAKE_EVENT(1, severity::LOW); static const uint32_t INVALID_ADDRESS = 0xFFFFFFFF; //!< Indicates an invalid (i.e unused) storage address. /** diff --git a/thermal/AbstractTemperatureSensor.h b/thermal/AbstractTemperatureSensor.h index 726ab9f4..eaca48fb 100644 --- a/thermal/AbstractTemperatureSensor.h +++ b/thermal/AbstractTemperatureSensor.h @@ -17,9 +17,9 @@ class AbstractTemperatureSensor: public HasHealthIF, public: static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::T_SENSORS; - static const Event TEMP_SENSOR_HIGH = MAKE_EVENT(0, SEVERITY::LOW); - static const Event TEMP_SENSOR_LOW = MAKE_EVENT(1, SEVERITY::LOW); - static const Event TEMP_SENSOR_GRADIENT = MAKE_EVENT(2, SEVERITY::LOW); + static const Event TEMP_SENSOR_HIGH = MAKE_EVENT(0, severity::LOW); + static const Event TEMP_SENSOR_LOW = MAKE_EVENT(1, severity::LOW); + static const Event TEMP_SENSOR_GRADIENT = MAKE_EVENT(2, severity::LOW); static constexpr float ZERO_KELVIN_C = -273.15; AbstractTemperatureSensor(object_id_t setObjectid, diff --git a/thermal/Heater.h b/thermal/Heater.h index 63fe2066..b034dfee 100644 --- a/thermal/Heater.h +++ b/thermal/Heater.h @@ -14,11 +14,11 @@ class Heater: public HealthDevice, public ReceivesParameterMessagesIF { public: static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::HEATER; - static const Event HEATER_ON = MAKE_EVENT(0, SEVERITY::INFO); - static const Event HEATER_OFF = MAKE_EVENT(1, SEVERITY::INFO); - static const Event HEATER_TIMEOUT = MAKE_EVENT(2, SEVERITY::LOW); - static const Event HEATER_STAYED_ON = MAKE_EVENT(3, SEVERITY::LOW); - static const Event HEATER_STAYED_OFF = MAKE_EVENT(4, SEVERITY::LOW); + static const Event HEATER_ON = MAKE_EVENT(0, severity::INFO); + static const Event HEATER_OFF = MAKE_EVENT(1, severity::INFO); + static const Event HEATER_TIMEOUT = MAKE_EVENT(2, severity::LOW); + static const Event HEATER_STAYED_ON = MAKE_EVENT(3, severity::LOW); + static const Event HEATER_STAYED_OFF = MAKE_EVENT(4, severity::LOW); Heater(uint32_t objectId, uint8_t switch0, uint8_t switch1); virtual ~Heater(); diff --git a/thermal/ThermalComponentIF.h b/thermal/ThermalComponentIF.h index 522d4e44..e542c842 100644 --- a/thermal/ThermalComponentIF.h +++ b/thermal/ThermalComponentIF.h @@ -10,11 +10,11 @@ class ThermalComponentIF : public HasParametersIF { public: static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::TCS_1; - static const Event COMPONENT_TEMP_LOW = MAKE_EVENT(1, SEVERITY::LOW); - static const Event COMPONENT_TEMP_HIGH = MAKE_EVENT(2, SEVERITY::LOW); - static const Event COMPONENT_TEMP_OOL_LOW = MAKE_EVENT(3, SEVERITY::LOW); - static const Event COMPONENT_TEMP_OOL_HIGH = MAKE_EVENT(4, SEVERITY::LOW); - static const Event TEMP_NOT_IN_OP_RANGE = MAKE_EVENT(5, SEVERITY::LOW); //!< Is thrown when a device should start-up, but the temperature is out of OP range. P1: thermalState of the component, P2: 0 + static const Event COMPONENT_TEMP_LOW = MAKE_EVENT(1, severity::LOW); + static const Event COMPONENT_TEMP_HIGH = MAKE_EVENT(2, severity::LOW); + static const Event COMPONENT_TEMP_OOL_LOW = MAKE_EVENT(3, severity::LOW); + static const Event COMPONENT_TEMP_OOL_HIGH = MAKE_EVENT(4, severity::LOW); + static const Event TEMP_NOT_IN_OP_RANGE = MAKE_EVENT(5, severity::LOW); //!< Is thrown when a device should start-up, but the temperature is out of OP range. P1: thermalState of the component, P2: 0 static const uint8_t INTERFACE_ID = CLASS_ID::THERMAL_COMPONENT_IF; static const ReturnValue_t INVALID_TARGET_STATE = MAKE_RETURN_CODE(1); diff --git a/tmstorage/TmStoreBackendIF.h b/tmstorage/TmStoreBackendIF.h index a441808e..4b2a8836 100644 --- a/tmstorage/TmStoreBackendIF.h +++ b/tmstorage/TmStoreBackendIF.h @@ -31,22 +31,22 @@ public: static const ReturnValue_t INVALID_REQUEST = MAKE_RETURN_CODE(15); static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::MEMORY; - static const Event STORE_SEND_WRITE_FAILED = MAKE_EVENT(0, SEVERITY::LOW); //!< Initiating sending data to store failed. Low, par1: returnCode, par2: integer (debug info) - static const Event STORE_WRITE_FAILED = MAKE_EVENT(1, SEVERITY::LOW); //!< Data was sent, but writing failed. Low, par1: returnCode, par2: 0 - static const Event STORE_SEND_READ_FAILED = MAKE_EVENT(2, SEVERITY::LOW); //!< Initiating reading data from store failed. Low, par1: returnCode, par2: 0 - static const Event STORE_READ_FAILED = MAKE_EVENT(3, SEVERITY::LOW); //!< Data was requested, but access failed. Low, par1: returnCode, par2: 0 - static const Event UNEXPECTED_MSG = MAKE_EVENT(4, SEVERITY::LOW); //!< An unexpected TM packet or data message occurred. Low, par1: 0, par2: integer (debug info) - static const Event STORING_FAILED = MAKE_EVENT(5, SEVERITY::LOW); //!< Storing data failed. May simply be a full store. Low, par1: returnCode, par2: integer (sequence count of failed packet). - static const Event TM_DUMP_FAILED = MAKE_EVENT(6, SEVERITY::LOW); //!< Dumping retrieved data failed. Low, par1: returnCode, par2: integer (sequence count of failed packet). - static const Event STORE_INIT_FAILED = MAKE_EVENT(7, SEVERITY::LOW); //!< Corrupted init data or read error. Low, par1: returnCode, par2: integer (debug info) - static const Event STORE_INIT_EMPTY = MAKE_EVENT(8, SEVERITY::INFO); //!< Store was not initialized. Starts empty. Info, parameters both zero. - static const Event STORE_CONTENT_CORRUPTED = MAKE_EVENT(9, SEVERITY::LOW); //!< Data was read out, but it is inconsistent. Low par1: Memory address of corruption, par2: integer (debug info) - static const Event STORE_INITIALIZE = MAKE_EVENT(10, SEVERITY::INFO); //!< Info event indicating the store will be initialized, either at boot or after IOB switch. Info. pars: 0 - static const Event INIT_DONE = MAKE_EVENT(11, SEVERITY::INFO); //!< Info event indicating the store was successfully initialized, either at boot or after IOB switch. Info. pars: 0 - static const Event DUMP_FINISHED = MAKE_EVENT(12, SEVERITY::INFO); //!< Info event indicating that dumping finished successfully. par1: Number of dumped packets. par2: APID/SSC (16bits each) - static const Event DELETION_FINISHED = MAKE_EVENT(13, SEVERITY::INFO); //!< Info event indicating that deletion finished successfully. par1: Number of deleted packets. par2: APID/SSC (16bits each) - static const Event DELETION_FAILED = MAKE_EVENT(14, SEVERITY::LOW); //!< Info event indicating that something went wrong during deletion. pars: 0 - static const Event AUTO_CATALOGS_SENDING_FAILED = MAKE_EVENT(15, SEVERITY::INFO);//!< Info that the a auto catalog report failed + static const Event STORE_SEND_WRITE_FAILED = MAKE_EVENT(0, severity::LOW); //!< Initiating sending data to store failed. Low, par1: returnCode, par2: integer (debug info) + static const Event STORE_WRITE_FAILED = MAKE_EVENT(1, severity::LOW); //!< Data was sent, but writing failed. Low, par1: returnCode, par2: 0 + static const Event STORE_SEND_READ_FAILED = MAKE_EVENT(2, severity::LOW); //!< Initiating reading data from store failed. Low, par1: returnCode, par2: 0 + static const Event STORE_READ_FAILED = MAKE_EVENT(3, severity::LOW); //!< Data was requested, but access failed. Low, par1: returnCode, par2: 0 + static const Event UNEXPECTED_MSG = MAKE_EVENT(4, severity::LOW); //!< An unexpected TM packet or data message occurred. Low, par1: 0, par2: integer (debug info) + static const Event STORING_FAILED = MAKE_EVENT(5, severity::LOW); //!< Storing data failed. May simply be a full store. Low, par1: returnCode, par2: integer (sequence count of failed packet). + static const Event TM_DUMP_FAILED = MAKE_EVENT(6, severity::LOW); //!< Dumping retrieved data failed. Low, par1: returnCode, par2: integer (sequence count of failed packet). + static const Event STORE_INIT_FAILED = MAKE_EVENT(7, severity::LOW); //!< Corrupted init data or read error. Low, par1: returnCode, par2: integer (debug info) + static const Event STORE_INIT_EMPTY = MAKE_EVENT(8, severity::INFO); //!< Store was not initialized. Starts empty. Info, parameters both zero. + static const Event STORE_CONTENT_CORRUPTED = MAKE_EVENT(9, severity::LOW); //!< Data was read out, but it is inconsistent. Low par1: Memory address of corruption, par2: integer (debug info) + static const Event STORE_INITIALIZE = MAKE_EVENT(10, severity::INFO); //!< Info event indicating the store will be initialized, either at boot or after IOB switch. Info. pars: 0 + static const Event INIT_DONE = MAKE_EVENT(11, severity::INFO); //!< Info event indicating the store was successfully initialized, either at boot or after IOB switch. Info. pars: 0 + static const Event DUMP_FINISHED = MAKE_EVENT(12, severity::INFO); //!< Info event indicating that dumping finished successfully. par1: Number of dumped packets. par2: APID/SSC (16bits each) + static const Event DELETION_FINISHED = MAKE_EVENT(13, severity::INFO); //!< Info event indicating that deletion finished successfully. par1: Number of deleted packets. par2: APID/SSC (16bits each) + static const Event DELETION_FAILED = MAKE_EVENT(14, severity::LOW); //!< Info event indicating that something went wrong during deletion. pars: 0 + static const Event AUTO_CATALOGS_SENDING_FAILED = MAKE_EVENT(15, severity::INFO);//!< Info that the a auto catalog report failed virtual ~TmStoreBackendIF() {} virtual ReturnValue_t performOperation(uint8_t opCode) = 0; From 7d70cce4dec572ca0db9012dfed4ddee31767a48 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 14:45:43 +0100 Subject: [PATCH 103/119] namepsace fix --- events/EventMessage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/events/EventMessage.cpp b/events/EventMessage.cpp index b911abab..bbc41e10 100644 --- a/events/EventMessage.cpp +++ b/events/EventMessage.cpp @@ -48,7 +48,7 @@ void EventMessage::setMessageId(uint8_t id) { EventSeverity_t EventMessage::getSeverity() { Event event; memcpy(&event, getData(), sizeof(Event)); - return EVENT::getSeverity(event); + return event::getSeverity(event); } void EventMessage::setSeverity(EventSeverity_t severity) { @@ -61,7 +61,7 @@ void EventMessage::setSeverity(EventSeverity_t severity) { EventId_t EventMessage::getEventId() { Event event; memcpy(&event, getData(), sizeof(Event)); - return EVENT::getEventId(event); + return event::getEventId(event); } void EventMessage::setEventId(EventId_t eventId) { From f0b13427e63ea7dfa2da89efe0894b78c1c546c9 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 14:51:36 +0100 Subject: [PATCH 104/119] changelog update --- CHANGELOG | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 33665e60..634eb2a7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,4 +17,7 @@ a C file without issues ### Events -- makeEvent function: Now takes three input parameters instead of two and allows setting a unique ID +- makeEvent function: Now takes three input parameters instead of two and +allows setting a unique ID. Event.cpp source file removed, functions now +defined in header directly. Namespaces renamed. Functions declared `constexpr` +now From 6ee4ceeb65c53ddaefe30c2c9001d6691c032182 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 14:58:24 +0100 Subject: [PATCH 105/119] deleted event.cpp source file --- events/Event.cpp | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 events/Event.cpp diff --git a/events/Event.cpp b/events/Event.cpp deleted file mode 100644 index 0f76b438..00000000 --- a/events/Event.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "Event.h" - -namespace EVENT { -EventId_t getEventId(Event event) { - return (event & 0xFFFF); -} - -EventSeverity_t getSeverity(Event event) { - return ((event >> 16) & 0xFF); -} - -Event makeEvent(uint8_t subsystemId, uint8_t uniqueEventId, - EventSeverity_t eventSeverity) { - return (eventSeverity << 16) + (subsystemId * 100) + uniqueEventId; -} -} From 4a4634928620b68fc0aa253cf9ccd1e977a81318 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 15:14:39 +0100 Subject: [PATCH 106/119] minor imrpovements --- datalinklayer/MapPacketExtraction.cpp | 4 ++-- datalinklayer/MapPacketExtraction.h | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/datalinklayer/MapPacketExtraction.cpp b/datalinklayer/MapPacketExtraction.cpp index 45f7d3f8..845ed7c1 100644 --- a/datalinklayer/MapPacketExtraction.cpp +++ b/datalinklayer/MapPacketExtraction.cpp @@ -9,9 +9,9 @@ MapPacketExtraction::MapPacketExtraction(uint8_t setMapId, object_id_t setPacketDestination) : - lastSegmentationFlag(NO_SEGMENTATION), mapId(setMapId), packetLength(0), + lastSegmentationFlag(NO_SEGMENTATION), mapId(setMapId), bufferPosition(packetBuffer), packetDestination(setPacketDestination), - packetStore(NULL), tcQueueId(MessageQueueIF::NO_QUEUE) { + tcQueueId(MessageQueueIF::NO_QUEUE) { std::memset(packetBuffer, 0, sizeof(packetBuffer)); } diff --git a/datalinklayer/MapPacketExtraction.h b/datalinklayer/MapPacketExtraction.h index eff9b7c3..ddb867fb 100644 --- a/datalinklayer/MapPacketExtraction.h +++ b/datalinklayer/MapPacketExtraction.h @@ -1,5 +1,5 @@ -#ifndef MAPPACKETEXTRACTION_H_ -#define MAPPACKETEXTRACTION_H_ +#ifndef FSFW_DATALINKLAYER_MAPPACKETEXTRACTION_H_ +#define FSFW_DATALINKLAYER_MAPPACKETEXTRACTION_H_ #include "MapPacketExtractionIF.h" #include "../objectmanager/ObjectManagerIF.h" @@ -20,11 +20,12 @@ private: static const uint32_t MAX_PACKET_SIZE = 4096; uint8_t lastSegmentationFlag; //!< The segmentation flag of the last received frame. uint8_t mapId; //!< MAP ID of this MAP Channel. - uint32_t packetLength; //!< Complete length of the current Space Packet. + uint32_t packetLength = 0; //!< Complete length of the current Space Packet. uint8_t* bufferPosition; //!< Position to write to in the internal Packet buffer. uint8_t packetBuffer[MAX_PACKET_SIZE]; //!< The internal Space Packet Buffer. object_id_t packetDestination; - StorageManagerIF* packetStore; //!< Pointer to the store where full TC packets are stored. + //!< Pointer to the store where full TC packets are stored. + StorageManagerIF* packetStore = nullptr; MessageQueueId_t tcQueueId; //!< QueueId to send found packets to the distributor. /** * Debug method to print the packet Buffer's content. @@ -69,4 +70,4 @@ public: uint8_t getMapId() const; }; -#endif /* MAPPACKETEXTRACTION_H_ */ +#endif /* FSFW_DATALINKLAYER_MAPPACKETEXTRACTION_H_ */ From bd1137d089539288fbfe3840ded67bea858ebc9d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 15:33:32 +0100 Subject: [PATCH 107/119] replaced hardcoded value with `NO_COMMAND` --- devicehandlers/DeviceHandlerBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index e4bb6d71..e6c15f9e 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -678,7 +678,7 @@ void DeviceHandlerBase::doGetRead() { void DeviceHandlerBase::parseReply(const uint8_t* receivedData, size_t receivedDataLen) { ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; - DeviceCommandId_t foundId = 0xffffffff; + DeviceCommandId_t foundId = DeviceHandlerIF::NO_COMMAND; size_t foundLen = 0; // The loop may not execute more often than the number of received bytes // (worst case). This approach avoids infinite loops due to buggy From ac5446b2b145eee8a3a5183913989b66d20fcc63 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 15:43:58 +0100 Subject: [PATCH 108/119] removed hk switcher helper member --- controller/ControllerBase.cpp | 7 +------ controller/ControllerBase.h | 3 --- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/controller/ControllerBase.cpp b/controller/ControllerBase.cpp index 45e678eb..2a402468 100644 --- a/controller/ControllerBase.cpp +++ b/controller/ControllerBase.cpp @@ -8,7 +8,7 @@ ControllerBase::ControllerBase(object_id_t setObjectId, object_id_t parentId, size_t commandQueueDepth) : SystemObject(setObjectId), parentId(parentId), mode(MODE_OFF), submode(SUBMODE_NONE), modeHelper(this), - healthHelper(this, setObjectId), hkSwitcher(this) { + healthHelper(this, setObjectId) { commandQueue = QueueFactory::instance()->createMessageQueue( commandQueueDepth); } @@ -44,10 +44,6 @@ ReturnValue_t ControllerBase::initialize() { return result; } - result = hkSwitcher.initialize(); - if (result != RETURN_OK) { - return result; - } return RETURN_OK; } @@ -107,7 +103,6 @@ void ControllerBase::announceMode(bool recursive) { ReturnValue_t ControllerBase::performOperation(uint8_t opCode) { handleQueue(); - hkSwitcher.performOperation(); performControlOperation(); return RETURN_OK; } diff --git a/controller/ControllerBase.h b/controller/ControllerBase.h index 25d3ab1f..6e83fe80 100644 --- a/controller/ControllerBase.h +++ b/controller/ControllerBase.h @@ -72,9 +72,6 @@ protected: HealthHelper healthHelper; - // Is this still needed? - HkSwitchHelper hkSwitcher; - /** * Pointer to the task which executes this component, * is invalid before setTaskIF was called. From a612e19e94a3edcbd3a5997410e8c4b87800cfb3 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 15:49:46 +0100 Subject: [PATCH 109/119] ndentation --- internalError/InternalErrorReporter.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internalError/InternalErrorReporter.cpp b/internalError/InternalErrorReporter.cpp index 12f36f33..818b00cf 100644 --- a/internalError/InternalErrorReporter.cpp +++ b/internalError/InternalErrorReporter.cpp @@ -6,8 +6,7 @@ #include "../serviceinterface/ServiceInterfaceStream.h" InternalErrorReporter::InternalErrorReporter(object_id_t setObjectId, - uint32_t messageQueueDepth) : - SystemObject(setObjectId), + uint32_t messageQueueDepth): SystemObject(setObjectId), commandQueue(QueueFactory::instance()-> createMessageQueue(messageQueueDepth)), poolManager(this, commandQueue), From 7f40566031f40d8ebe214bf29a7958731d936cd0 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 15:50:21 +0100 Subject: [PATCH 110/119] include removed --- internalError/InternalErrorReporter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/internalError/InternalErrorReporter.cpp b/internalError/InternalErrorReporter.cpp index 818b00cf..bfb67289 100644 --- a/internalError/InternalErrorReporter.cpp +++ b/internalError/InternalErrorReporter.cpp @@ -1,4 +1,3 @@ -#include "../datapoolglob/GlobalDataSet.h" #include "InternalErrorReporter.h" #include "../ipc/QueueFactory.h" From 5e2e18ac078b3ea78ab1328adda5116d532b222c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 15:59:30 +0100 Subject: [PATCH 111/119] device handler uses local pools now --- devicehandlers/DeviceHandlerBase.cpp | 148 +++++++++++++---------- devicehandlers/DeviceHandlerBase.h | 169 ++++++++++++++++----------- 2 files changed, 193 insertions(+), 124 deletions(-) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index b5d43d77..733106e5 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -3,8 +3,6 @@ #include "DeviceTmReportingWrapper.h" #include "../serviceinterface/ServiceInterfaceStream.h" -#include "../datapoolglob/GlobalDataSet.h" -#include "../datapoolglob/GlobalPoolVariable.h" #include "../objectmanager/ObjectManager.h" #include "../storagemanager/StorageManagerIF.h" #include "../thermal/ThermalComponentIF.h" @@ -13,9 +11,11 @@ #include "../ipc/MessageQueueMessage.h" #include "../ipc/QueueFactory.h" #include "../subsystem/SubsystemBase.h" +#include "../datapoollocal/LocalPoolVariable.h" #include + object_id_t DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT; object_id_t DeviceHandlerBase::rawDataReceiverId = objects::NO_OBJECT; object_id_t DeviceHandlerBase::defaultFdirParentId = objects::NO_OBJECT; @@ -56,9 +56,10 @@ void DeviceHandlerBase::setHkDestination(object_id_t hkDestination) { } void DeviceHandlerBase::setThermalStateRequestPoolIds( - uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId) { - this->deviceThermalRequestPoolId = thermalStatePoolId; - this->deviceThermalRequestPoolId = thermalRequestPoolId; + lp_id_t thermalStatePoolId, lp_id_t heaterRequestPoolId, + uint32_t thermalSetId) { + thermalSet = new DeviceHandlerThermalSet(this, thermalSetId, + thermalStatePoolId, heaterRequestPoolId); } @@ -86,7 +87,6 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { decrementDeviceReplyMap(); fdirInstance->checkForFailures(); hkSwitcher.performOperation(); - hkManager.performHkOperation(); performOperationHook(); return RETURN_OK; } @@ -111,6 +111,9 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { break; case CommunicationAction::GET_READ: doGetRead(); + // This will be performed after datasets have been updated by the + // custom device implementation. + hkManager.performHkOperation(); break; default: break; @@ -208,16 +211,18 @@ ReturnValue_t DeviceHandlerBase::initialize() { fillCommandAndReplyMap(); - //Set temperature target state to NON_OP. - GlobDataSet mySet; - gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, - PoolVariableIF::VAR_WRITE); - mySet.read(); - thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; - mySet.commit(PoolVariableIF::VALID); + if(thermalSet != nullptr) { + //Set temperature target state to NON_OP. + result = thermalSet->read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + thermalSet->heaterRequest.value = + ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; + thermalSet->commit(PoolVariableIF::VALID); + } + + } return RETURN_OK; - } void DeviceHandlerBase::decrementDeviceReplyMap() { @@ -505,15 +510,17 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) { } Clock::getUptime(&timeoutStart); - if (mode == MODE_OFF) { - GlobDataSet mySet; - gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, - PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { - thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; + if (mode == MODE_OFF and thermalSet != nullptr) { + ReturnValue_t result = thermalSet->read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + if (thermalSet->heaterRequest.value != + ThermalComponentIF::STATE_REQUEST_IGNORE) { + thermalSet->heaterRequest.value = ThermalComponentIF:: + STATE_REQUEST_NON_OPERATIONAL; + } + thermalSet->heaterRequest.commit(PoolVariableIF::VALID); } - mySet.commit(PoolVariableIF::VALID); + } changeHK(mode, submode, true); } @@ -976,17 +983,15 @@ ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, } if ((commandedMode == MODE_ON) && (mode == MODE_OFF) - && (deviceThermalStatePoolId != PoolVariableIF::NO_PARAMETER)) { - GlobDataSet mySet; - gp_uint8_t thermalState(deviceThermalStatePoolId, &mySet, - PoolVariableIF::VAR_READ); - gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, - PoolVariableIF::VAR_READ); - mySet.read(); - if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { - if (!ThermalComponentIF::isOperational(thermalState)) { + and (thermalSet != nullptr)) { + ReturnValue_t result = thermalSet->read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + if((thermalSet->heaterRequest.value != + ThermalComponentIF::STATE_REQUEST_IGNORE) and (not + ThermalComponentIF::isOperational( + thermalSet->thermalState.value))) { triggerEvent(ThermalComponentIF::TEMP_NOT_IN_OP_RANGE, - thermalState); + thermalSet->thermalState.value); return NON_OP_TEMPERATURE; } } @@ -999,32 +1004,15 @@ void DeviceHandlerBase::startTransition(Mode_t commandedMode, Submode_t commandedSubmode) { switch (commandedMode) { case MODE_ON: - if (mode == MODE_OFF) { - transitionSourceMode = _MODE_POWER_DOWN; - transitionSourceSubMode = SUBMODE_NONE; - setMode(_MODE_POWER_ON, commandedSubmode); - //already set the delay for the child transition so we don't need to call it twice - childTransitionDelay = getTransitionDelayMs(_MODE_START_UP, - MODE_ON); - triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); - GlobDataSet mySet; - gp_int8_t thermalRequest(deviceThermalRequestPoolId, - &mySet, PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { - thermalRequest = ThermalComponentIF::STATE_REQUEST_OPERATIONAL; - mySet.commit(PoolVariableIF::VALID); - } - } else { - setTransition(MODE_ON, commandedSubmode); - } + handleTransitionToOnMode(commandedMode, commandedSubmode); break; case MODE_OFF: if (mode == MODE_OFF) { triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); setMode(_MODE_POWER_DOWN, commandedSubmode); } else { - //already set the delay for the child transition so we don't need to call it twice + // already set the delay for the child transition + // so we don't need to call it twice childTransitionDelay = getTransitionDelayMs(mode, _MODE_POWER_DOWN); transitionSourceMode = _MODE_POWER_DOWN; transitionSourceSubMode = commandedSubmode; @@ -1050,6 +1038,33 @@ void DeviceHandlerBase::startTransition(Mode_t commandedMode, } } +void DeviceHandlerBase::handleTransitionToOnMode(Mode_t commandedMode, + Submode_t commandedSubmode) { + if (mode == MODE_OFF) { + transitionSourceMode = _MODE_POWER_DOWN; + transitionSourceSubMode = SUBMODE_NONE; + setMode(_MODE_POWER_ON, commandedSubmode); + // already set the delay for the child transition so we don't + // need to call it twice + childTransitionDelay = getTransitionDelayMs(_MODE_START_UP, + MODE_ON); + triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); + if(thermalSet != nullptr) { + ReturnValue_t result = thermalSet->read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + if(thermalSet->heaterRequest != + ThermalComponentIF::STATE_REQUEST_IGNORE) { + thermalSet->heaterRequest = + ThermalComponentIF::STATE_REQUEST_OPERATIONAL; + thermalSet->commit(); + } + } + } + } else { + setTransition(MODE_ON, commandedSubmode); + } +} + void DeviceHandlerBase::getMode(Mode_t* mode, Submode_t* submode) { *mode = this->mode; *submode = this->submode; @@ -1222,10 +1237,12 @@ void DeviceHandlerBase::handleDeviceTM(SerializeIF* data, } } //Try to cast to GlobDataSet and commit data. - if (!neverInDataPool) { - GlobDataSet* dataSet = dynamic_cast(data); - if (dataSet != NULL) { - dataSet->commit(PoolVariableIF::VALID); + if (not neverInDataPool) { + LocalPoolDataSetBase* dataSet = + dynamic_cast(data); + if (dataSet != nullptr) { + dataSet->setValidity(true, true); + dataSet->commit(); } } } @@ -1262,7 +1279,8 @@ void DeviceHandlerBase::buildInternalCommand(void) { if (result == BUSY) { //so we can track misconfigurations sif::debug << std::hex << getObjectId() - << ": DHB::buildInternalCommand: Busy" << std::dec << std::endl; + << ": DHB::buildInternalCommand: Busy" << std::dec + << std::endl; result = NOTHING_TO_SEND; //no need to report this } } @@ -1371,8 +1389,8 @@ bool DeviceHandlerBase::commandIsExecuting(DeviceCommandId_t commandId) { void DeviceHandlerBase::changeHK(Mode_t mode, Submode_t submode, bool enable) { } -void DeviceHandlerBase::setTaskIF(PeriodicTaskIF* task_){ - executingTask = task_; +void DeviceHandlerBase::setTaskIF(PeriodicTaskIF* task){ + executingTask = task; } // Default implementations empty. @@ -1385,6 +1403,12 @@ void DeviceHandlerBase::performOperationHook() { ReturnValue_t DeviceHandlerBase::initializeLocalDataPool( LocalDataPool &localDataPoolMap, LocalDataPoolManager& poolManager) { + if(thermalSet != nullptr) { + localDataPoolMap.emplace(thermalSet->thermalStatePoolId, + new PoolEntry); + localDataPoolMap.emplace(thermalSet->heaterRequestPoolId, + new PoolEntry); + } return RETURN_OK; } @@ -1429,3 +1453,9 @@ dur_millis_t DeviceHandlerBase::getPeriodicOperationFrequency() const { return pstIntervalMs; } +DeviceCommandId_t DeviceHandlerBase::getPendingCommand() const { + if(cookieInfo.pendingCommand != deviceCommandMap.end()) { + return cookieInfo.pendingCommand->first; + } + return DeviceHandlerIF::NO_COMMAND; +} diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index 627a6423..2bfa62a6 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -4,6 +4,7 @@ #include "DeviceHandlerIF.h" #include "DeviceCommunicationIF.h" #include "DeviceHandlerFailureIsolation.h" +#include "DeviceHandlerThermalSet.h" #include "../objectmanager/SystemObject.h" #include "../tasks/ExecutableObjectIF.h" @@ -103,8 +104,21 @@ public: size_t cmdQueueSize = 20); void setHkDestination(object_id_t hkDestination); - void setThermalStateRequestPoolIds(uint32_t thermalStatePoolId, - uint32_t thermalRequestPoolId); + + /** + * If the device handler is controlled by the FSFW thermal building blocks, + * this function should be called to initialize all required components. + * The device handler will then take care of creating local pool entries + * for the device thermal state and device heating request. + * Custom local pool IDs can be assigned as well. + * @param thermalStatePoolId + * @param thermalRequestPoolId + */ + void setThermalStateRequestPoolIds(lp_id_t thermalStatePoolId = + DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID, + lp_id_t thermalRequestPoolId = + DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID, + uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID); /** * @brief Helper function to ease device handler development. * This will instruct the transition to MODE_ON immediately @@ -694,19 +708,7 @@ protected: //! and to send replies. MessageQueueIF* commandQueue = nullptr; - /** - * this is the datapool variable with the thermal state of the device - * - * can be set to PoolVariableIF::NO_PARAMETER to deactivate thermal checking - */ - uint32_t deviceThermalStatePoolId = PoolVariableIF::NO_PARAMETER; - - /** - * this is the datapool variable with the thermal request of the device - * - * can be set to PoolVariableIF::NO_PARAMETER to deactivate thermal checking - */ - uint32_t deviceThermalRequestPoolId = PoolVariableIF::NO_PARAMETER; + DeviceHandlerThermalSet* thermalSet = nullptr; /** * Optional Error code. Can be set in doStartUp(), doShutDown() and @@ -732,15 +734,27 @@ protected: //! before setTaskIF was called. PeriodicTaskIF* executingTask = nullptr; - static object_id_t powerSwitcherId; //!< Object which switches power on and off. + //!< Object which switches power on and off. + static object_id_t powerSwitcherId; - static object_id_t rawDataReceiverId; //!< Object which receives RAW data by default. + //!< Object which receives RAW data by default. + static object_id_t rawDataReceiverId; + + //!< Object which may be the root cause of an identified fault. + static object_id_t defaultFdirParentId; + + /** + * Helper function to get pending command. This is useful for devices + * like SPI sensors to identify the last sent command. + * @return + */ + DeviceCommandId_t getPendingCommand() const; - static object_id_t defaultFdirParentId; //!< Object which may be the root cause of an identified fault. /** * Helper function to report a missed reply * - * Can be overwritten by children to act on missed replies or to fake reporting Id. + * Can be overwritten by children to act on missed replies or to fake + * reporting Id. * * @param id of the missed reply */ @@ -847,15 +861,18 @@ protected: /** * Build the device command to send for raw mode. * - * This is only called in @c MODE_RAW. It is for the rare case that in raw mode packets - * are to be sent by the handler itself. It is NOT needed for the raw commanding service. - * Its only current use is in the STR handler which gets its raw packets from a different - * source. - * Also it can be used for transitional commands, to get the device ready for @c MODE_RAW + * This is only called in @c MODE_RAW. It is for the rare case that in + * raw mode packets are to be sent by the handler itself. It is NOT needed + * for the raw commanding service. Its only current use is in the STR + * handler which gets its raw packets from a different source. + * Also it can be used for transitional commands, to get the device ready + * for @c MODE_RAW * - * As it is almost never used, there is a default implementation returning @c NOTHING_TO_SEND. + * As it is almost never used, there is a default implementation + * returning @c NOTHING_TO_SEND. * - * #rawPacket and #rawPacketLen must be set by this method to the packet to be sent. + * #rawPacket and #rawPacketLen must be set by this method to the packet + * to be sent. * * @param[out] id the device command id built * @return @@ -868,7 +885,9 @@ protected: * Returns the delay cycle count of a reply. * A count != 0 indicates that the command is already executed. * @param deviceCommand The command to look for - * @return The current delay count. If the command does not exist (should never happen) it returns 0. + * @return + * The current delay count. If the command does not exist (should never + * happen) it returns 0. */ uint8_t getReplyDelayCycles(DeviceCommandId_t deviceCommand); @@ -878,20 +897,22 @@ protected: * It gets space in the #IPCStore, copies data there, then sends a raw reply * containing the store address. * - * This method is virtual, as the STR has a different channel to send raw replies - * and overwrites it accordingly. + * This method is virtual, as the STR has a different channel to send + * raw replies and overwrites it accordingly. * * @param data data to send * @param len length of @c data * @param sendTo the messageQueueId of the one to send to - * @param isCommand marks the raw data as a command, the message then will be of type raw_command + * @param isCommand marks the raw data as a command, the message then + * will be of type raw_command */ virtual void replyRawData(const uint8_t *data, size_t len, MessageQueueId_t sendTo, bool isCommand = false); /** - * Calls replyRawData() with #defaultRawReceiver, but checks if wiretapping is active and if so, - * does not send the Data as the wiretapping will have sent it already + * Calls replyRawData() with #defaultRawReceiver, but checks if wiretapping + * is active and if so, does not send the data as the wiretapping will have + * sent it already */ void replyRawReplyIfnotWiretapped(const uint8_t *data, size_t len); @@ -903,17 +924,19 @@ protected: /** * Enable the reply checking for a command * - * Is only called, if the command was sent (ie the getWriteReply was successful). - * Must ensure that all replies are activated and correctly linked to the command that initiated it. - * The default implementation looks for a reply with the same id as the command id in the replyMap or - * uses the alternativeReplyId if flagged so. - * When found, copies maxDelayCycles to delayCycles in the reply information and sets the command to - * expect one reply. + * Is only called, if the command was sent (i.e. the getWriteReply was + * successful). Must ensure that all replies are activated and correctly + * linked to the command that initiated it. + * The default implementation looks for a reply with the same id as the + * command id in the replyMap or uses the alternativeReplyId if flagged so. + * When found, copies maxDelayCycles to delayCycles in the reply information + * and sets the command to expect one reply. * * Can be overwritten by the child, if a command activates multiple replies * or replyId differs from commandId. * Notes for child implementations: - * - If the command was not found in the reply map, NO_REPLY_EXPECTED MUST be returned. + * - If the command was not found in the reply map, + * NO_REPLY_EXPECTED MUST be returned. * - A failure code may be returned if something went fundamentally wrong. * * @param deviceCommand @@ -929,17 +952,20 @@ protected: * get the state of the PCDU switches in the datapool * * @return - * - @c PowerSwitchIF::SWITCH_ON if all switches specified by #switches are on - * - @c PowerSwitchIF::SWITCH_OFF one of the switches specified by #switches are off - * - @c PowerSwitchIF::RETURN_FAILED if an error occured + * - @c PowerSwitchIF::SWITCH_ON if all switches specified + * by #switches are on + * - @c PowerSwitchIF::SWITCH_OFF one of the switches specified by + * #switches are off + * - @c PowerSwitchIF::RETURN_FAILED if an error occured */ 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. + * @brief Set all datapool variables that are update periodically in + * normal mode invalid + * @details TODO: Use local pools + * Child classes should provide an implementation which sets all those + * variables invalid which are set periodically during any normal mode. */ virtual void setNormalDatapoolEntriesInvalid() = 0; @@ -949,11 +975,12 @@ protected: virtual void changeHK(Mode_t mode, Submode_t submode, bool enable); /** - * Children can overwrite this function to suppress checking of the command Queue + * Children can overwrite this function to suppress checking of the + * command Queue * - * This can be used when the child does not want to receive a command in a certain - * situation. Care must be taken that checking is not permanentely disabled as this - * would render the handler unusable. + * This can be used when the child does not want to receive a command in + * a certain situation. Care must be taken that checking is not + * permanentely disabled as this would render the handler unusable. * * @return whether checking the queue should NOT be done */ @@ -992,17 +1019,20 @@ protected: virtual void forwardEvent(Event event, uint32_t parameter1 = 0, uint32_t parameter2 = 0) const; /** - * Checks state of switches in conjunction with mode and triggers an event if they don't fit. + * Checks state of switches in conjunction with mode and triggers an event + * if they don't fit. */ virtual void checkSwitchState(); /** - * Reserved for the rare case where a device needs to perform additional operation cyclically in OFF mode. + * Reserved for the rare case where a device needs to perform additional + * operation cyclically in OFF mode. */ virtual void doOffActivity(); /** - * Reserved for the rare case where a device needs to perform additional operation cyclically in ON mode. + * Reserved for the rare case where a device needs to perform additional + * operation cyclically in ON mode. */ virtual void doOnActivity(); @@ -1043,9 +1073,10 @@ private: /** * Information about a cookie. * - * This is stored in a map for each cookie, to not only track the state, but also information - * about the sent command. Tracking this information is needed as - * the state of a commandId (waiting for reply) is done when a RMAP write reply is received. + * This is stored in a map for each cookie, to not only track the state, + * but also information about the sent command. Tracking this information + * is needed as the state of a commandId (waiting for reply) is done when a + * write reply is received. */ struct CookieInfo { CookieState_t state; @@ -1102,10 +1133,14 @@ private: /** * Handle the device handler mode. * - * - checks whether commands are valid for the current mode, rejects them accordingly - * - 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() + * - checks whether commands are valid for the current mode, rejects + * them accordingly + * - 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 (e.g. setting a timeout) are + * handled in setMode() */ void doStateMachine(void); @@ -1115,16 +1150,17 @@ private: /** * 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). + * 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). */ void decrementDeviceReplyMap(void); /** * Convenience function to handle a reply. * - * Called after scanForReply() has found a packet. Checks if the found id is in the #deviceCommandMap, if so, - * calls interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) for further action. + * Called after scanForReply() has found a packet. Checks if the found ID + * is in the #deviceCommandMap, if so, calls + * #interpretDeviceReply for further action. * * It also resets the timeout counter for the command id. * @@ -1184,7 +1220,7 @@ private: * @param[out] len * @return * - @c RETURN_OK @c data is valid - * - @c RETURN_FAILED IPCStore is NULL + * - @c RETURN_FAILED IPCStore is nullptr * - the return value from the IPCStore if it was not @c RETURN_OK */ ReturnValue_t getStorageData(store_address_t storageAddress, uint8_t **data, @@ -1208,6 +1244,9 @@ private: void parseReply(const uint8_t* receivedData, size_t receivedDataLen); + + void handleTransitionToOnMode(Mode_t commandedMode, + Submode_t commandedSubmode); }; #endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */ From 46596f9397d7cea1a82a16f093117e64e133c919 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Dec 2020 16:01:48 +0100 Subject: [PATCH 112/119] tab changed --- devicehandlers/DeviceHandlerBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 733106e5..f095834e 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -113,7 +113,7 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { doGetRead(); // This will be performed after datasets have been updated by the // custom device implementation. - hkManager.performHkOperation(); + hkManager.performHkOperation(); break; default: break; From ff62ab8427f84442cb14759e81fb03972caa7bfc Mon Sep 17 00:00:00 2001 From: Spacefish Date: Thu, 10 Dec 2020 16:32:52 +0100 Subject: [PATCH 113/119] removed core component --- thermal/CoreComponent.cpp | 257 -------------------------------------- thermal/CoreComponent.h | 96 -------------- 2 files changed, 353 deletions(-) delete mode 100644 thermal/CoreComponent.cpp delete mode 100644 thermal/CoreComponent.h diff --git a/thermal/CoreComponent.cpp b/thermal/CoreComponent.cpp deleted file mode 100644 index 657e79cb..00000000 --- a/thermal/CoreComponent.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include "CoreComponent.h" - -CoreComponent::CoreComponent(object_id_t reportingObjectId, uint8_t domainId, - uint32_t temperaturePoolId, uint32_t targetStatePoolId, - uint32_t currentStatePoolId, uint32_t requestPoolId, GlobDataSet* dataSet, - AbstractTemperatureSensor* sensor, - AbstractTemperatureSensor* firstRedundantSensor, - AbstractTemperatureSensor* secondRedundantSensor, - ThermalModuleIF* thermalModule, Parameters parameters, - Priority priority, StateRequest initialTargetState) : - sensor(sensor), firstRedundantSensor(firstRedundantSensor), secondRedundantSensor( - secondRedundantSensor), thermalModule(thermalModule), temperature( - temperaturePoolId, dataSet, PoolVariableIF::VAR_WRITE), targetState( - targetStatePoolId, dataSet, PoolVariableIF::VAR_READ), currentState( - currentStatePoolId, dataSet, PoolVariableIF::VAR_WRITE), heaterRequest( - requestPoolId, dataSet, PoolVariableIF::VAR_WRITE), isHeating( - false), isSafeComponent(priority == SAFE), minTemp(999), maxTemp( - AbstractTemperatureSensor::ZERO_KELVIN_C), parameters( - parameters), temperatureMonitor(reportingObjectId, - domainId + 1, - GlobalDataPool::poolIdAndPositionToPid(temperaturePoolId, 0), - COMPONENT_TEMP_CONFIRMATION), domainId(domainId) { - if (thermalModule != NULL) { - thermalModule->registerComponent(this, priority); - } - //Set thermal state once, then leave to operator. - GlobDataSet mySet; - gp_uint8_t writableTargetState(targetStatePoolId, &mySet, - PoolVariableIF::VAR_WRITE); - writableTargetState = initialTargetState; - mySet.commit(PoolVariableIF::VALID); -} - -CoreComponent::~CoreComponent() { -} - -ThermalComponentIF::HeaterRequest CoreComponent::performOperation(uint8_t opCode) { - HeaterRequest request = HEATER_DONT_CARE; - //SHOULDDO: Better pass db_float_t* to getTemperature and set it invalid if invalid. - temperature = getTemperature(); - updateMinMaxTemp(); - if ((temperature != INVALID_TEMPERATURE)) { - temperature.setValid(PoolVariableIF::VALID); - State state = getState(temperature, getParameters(), targetState); - currentState = state; - checkLimits(state); - request = getHeaterRequest(targetState, temperature, getParameters()); - } else { - temperatureMonitor.setToInvalid(); - temperature.setValid(PoolVariableIF::INVALID); - currentState = UNKNOWN; - request = HEATER_DONT_CARE; - } - currentState.setValid(PoolVariableIF::VALID); - heaterRequest = request; - heaterRequest.setValid(PoolVariableIF::VALID); - return request; -} - -void CoreComponent::markStateIgnored() { - currentState = getIgnoredState(currentState); -} - -object_id_t CoreComponent::getObjectId() { - return temperatureMonitor.getReporterId(); -} - -float CoreComponent::getLowerOpLimit() { - return parameters.lowerOpLimit; -} - -ReturnValue_t CoreComponent::setTargetState(int8_t newState) { - GlobDataSet mySet; - gp_uint8_t writableTargetState(targetState.getDataPoolId(), - &mySet, PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - if ((writableTargetState == STATE_REQUEST_OPERATIONAL) - && (newState != STATE_REQUEST_IGNORE)) { - return HasReturnvaluesIF::RETURN_FAILED; - } - switch (newState) { - case STATE_REQUEST_HEATING: - case STATE_REQUEST_IGNORE: - case STATE_REQUEST_OPERATIONAL: - writableTargetState = newState; - break; - case STATE_REQUEST_NON_OPERATIONAL: - default: - return INVALID_TARGET_STATE; - } - mySet.commit(PoolVariableIF::VALID); - return HasReturnvaluesIF::RETURN_OK; -} - -void CoreComponent::setOutputInvalid() { - temperature = INVALID_TEMPERATURE; - temperature.setValid(PoolVariableIF::INVALID); - currentState.setValid(PoolVariableIF::INVALID); - heaterRequest = HEATER_DONT_CARE; - heaterRequest.setValid(PoolVariableIF::INVALID); - temperatureMonitor.setToUnchecked(); -} - -float CoreComponent::getTemperature() { - if ((sensor != NULL) && (sensor->isValid())) { - return sensor->getTemperature(); - } - - if ((firstRedundantSensor != NULL) && (firstRedundantSensor->isValid())) { - return firstRedundantSensor->getTemperature(); - } - - if ((secondRedundantSensor != NULL) && (secondRedundantSensor->isValid())) { - return secondRedundantSensor->getTemperature(); - } - - if (thermalModule != NULL) { - float temperature = thermalModule->getTemperature(); - if (temperature != ThermalModuleIF::INVALID_TEMPERATURE) { - return temperature; - } else { - return INVALID_TEMPERATURE; - } - } else { - return INVALID_TEMPERATURE; - } -} - -ThermalComponentIF::State CoreComponent::getState(float temperature, - Parameters parameters, int8_t targetState) { - ThermalComponentIF::State state; - - if (temperature < parameters.lowerOpLimit) { - state = NON_OPERATIONAL_LOW; - } else if (temperature < parameters.upperOpLimit) { - state = OPERATIONAL; - } else { - state = NON_OPERATIONAL_HIGH; - } - if (targetState == STATE_REQUEST_IGNORE) { - state = getIgnoredState(state); - } - - return state; -} - -void CoreComponent::checkLimits(ThermalComponentIF::State state) { - //Checks operational limits only. - temperatureMonitor.translateState(state, temperature.value, - getParameters().lowerOpLimit, getParameters().upperOpLimit); - -} - -ThermalComponentIF::HeaterRequest CoreComponent::getHeaterRequest( - int8_t targetState, float temperature, Parameters parameters) { - if (targetState == STATE_REQUEST_IGNORE) { - isHeating = false; - return HEATER_DONT_CARE; - } - - if (temperature > parameters.upperOpLimit - parameters.heaterSwitchoff) { - isHeating = false; - return HEATER_REQUEST_EMERGENCY_OFF; - } - - float opHeaterLimit = parameters.lowerOpLimit + parameters.heaterOn; - - if (isHeating) { - opHeaterLimit += parameters.hysteresis; - } - - if (temperature < opHeaterLimit) { - isHeating = true; - return HEATER_REQUEST_EMERGENCY_ON; - } - isHeating = false; - return HEATER_DONT_CARE; -} - -ThermalComponentIF::State CoreComponent::getIgnoredState(int8_t state) { - switch (state) { - case NON_OPERATIONAL_LOW: - return NON_OPERATIONAL_LOW_IGNORED; - case OPERATIONAL: - return OPERATIONAL_IGNORED; - case NON_OPERATIONAL_HIGH: - return NON_OPERATIONAL_HIGH_IGNORED; - case NON_OPERATIONAL_LOW_IGNORED: - return NON_OPERATIONAL_LOW_IGNORED; - case OPERATIONAL_IGNORED: - return OPERATIONAL_IGNORED; - case NON_OPERATIONAL_HIGH_IGNORED: - return NON_OPERATIONAL_HIGH_IGNORED; - default: - case UNKNOWN: - return UNKNOWN; - } -} - -void CoreComponent::updateMinMaxTemp() { - if (temperature == INVALID_TEMPERATURE) { - return; - } - if (temperature < minTemp) { - minTemp = temperature; - } - if (temperature > maxTemp) { - maxTemp = temperature; - } -} - -uint8_t CoreComponent::getDomainId() const { - return domainId; -} - -CoreComponent::Parameters CoreComponent::getParameters() { - return parameters; -} - -ReturnValue_t CoreComponent::getParameter(uint8_t domainId, - uint16_t parameterId, ParameterWrapper* parameterWrapper, - const ParameterWrapper* newValues, uint16_t startAtIndex) { - ReturnValue_t result = temperatureMonitor.getParameter(domainId, - parameterId, parameterWrapper, newValues, startAtIndex); - if (result != INVALID_DOMAIN_ID) { - return result; - } - if (domainId != this->domainId) { - return INVALID_DOMAIN_ID; - } - switch (parameterId) { - case 0: - parameterWrapper->set(parameters.heaterOn); - break; - case 1: - parameterWrapper->set(parameters.hysteresis); - break; - case 2: - parameterWrapper->set(parameters.heaterSwitchoff); - break; - case 3: - parameterWrapper->set(minTemp); - break; - case 4: - parameterWrapper->set(maxTemp); - break; - case 10: - parameterWrapper->set(parameters.lowerOpLimit); - break; - case 11: - parameterWrapper->set(parameters.upperOpLimit); - break; - default: - return INVALID_MATRIX_ID; - } - return HasReturnvaluesIF::RETURN_OK; -} diff --git a/thermal/CoreComponent.h b/thermal/CoreComponent.h deleted file mode 100644 index 8093711d..00000000 --- a/thermal/CoreComponent.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef MISSION_CONTROLLERS_TCS_CORECOMPONENT_H_ -#define MISSION_CONTROLLERS_TCS_CORECOMPONENT_H_ - -#include "../datapoolglob/GlobalDataSet.h" -#include "../datapoolglob/GlobalPoolVariable.h" -#include "../thermal/ThermalComponentIF.h" -#include "../thermal/AbstractTemperatureSensor.h" -#include "../thermal/ThermalModule.h" -#include "../thermal/ThermalMonitor.h" - -// TODO: Documentaiton, how to use this? only use Thermal Component, which inherits core component? -class CoreComponent: public ThermalComponentIF { -public: - struct Parameters { - float lowerOpLimit; - float upperOpLimit; - float heaterOn; - float hysteresis; - float heaterSwitchoff; - }; - - static const uint16_t COMPONENT_TEMP_CONFIRMATION = 5; - - CoreComponent(object_id_t reportingObjectId, uint8_t domainId, uint32_t temperaturePoolId, - uint32_t targetStatePoolId, uint32_t currentStatePoolId, - uint32_t requestPoolId, GlobDataSet *dataSet, - AbstractTemperatureSensor *sensor, - AbstractTemperatureSensor *firstRedundantSensor, - AbstractTemperatureSensor *secondRedundantSensor, - ThermalModuleIF *thermalModule, Parameters parameters, - Priority priority, StateRequest initialTargetState = - ThermalComponentIF::STATE_REQUEST_OPERATIONAL); - - virtual ~CoreComponent(); - - virtual HeaterRequest performOperation(uint8_t opCode); - - void markStateIgnored(); - - object_id_t getObjectId(); - - uint8_t getDomainId() const; - - virtual float getLowerOpLimit(); - - ReturnValue_t setTargetState(int8_t newState); - - virtual void setOutputInvalid(); - - virtual ReturnValue_t getParameter(uint8_t domainId, uint16_t parameterId, - ParameterWrapper *parameterWrapper, - const ParameterWrapper *newValues, uint16_t startAtIndex); - -protected: - - AbstractTemperatureSensor *sensor; - AbstractTemperatureSensor *firstRedundantSensor; - AbstractTemperatureSensor *secondRedundantSensor; - ThermalModuleIF *thermalModule; - - gp_float_t temperature; - gp_int8_t targetState; - gp_int8_t currentState; - gp_uint8_t heaterRequest; - - bool isHeating; - - bool isSafeComponent; - - float minTemp; - - float maxTemp; - - Parameters parameters; - - ThermalMonitor temperatureMonitor; - - const uint8_t domainId; - - virtual float getTemperature(); - virtual State getState(float temperature, Parameters parameters, - int8_t targetState); - - virtual void checkLimits(State state); - - virtual HeaterRequest getHeaterRequest(int8_t targetState, - float temperature, Parameters parameters); - - virtual State getIgnoredState(int8_t state); - - void updateMinMaxTemp(); - - virtual Parameters getParameters(); -}; - -#endif /* MISSION_CONTROLLERS_TCS_CORECOMPONENT_H_ */ From 0a5099e293acc10407dcfe8078fddca303b901a3 Mon Sep 17 00:00:00 2001 From: Spacefish Date: Thu, 10 Dec 2020 16:34:25 +0100 Subject: [PATCH 114/119] deleted file which was renamed --- thermal/ThermalMonitor.cpp | 68 -------------------------------------- thermal/ThermalMonitor.h | 23 ------------- 2 files changed, 91 deletions(-) delete mode 100644 thermal/ThermalMonitor.cpp delete mode 100644 thermal/ThermalMonitor.h diff --git a/thermal/ThermalMonitor.cpp b/thermal/ThermalMonitor.cpp deleted file mode 100644 index 11abfbe5..00000000 --- a/thermal/ThermalMonitor.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "ThermalMonitor.h" -#include "ThermalComponentIF.h" -#include "../monitoring/MonitoringIF.h" -ThermalMonitor::~ThermalMonitor() { -} - -void ThermalMonitor::sendTransitionEvent(float currentValue, - ReturnValue_t state) { - switch (state) { - case MonitoringIF::BELOW_LOW_LIMIT: - EventManagerIF::triggerEvent(reportingId, - ThermalComponentIF::COMPONENT_TEMP_OOL_LOW, state); - break; - case MonitoringIF::ABOVE_HIGH_LIMIT: - EventManagerIF::triggerEvent(reportingId, - ThermalComponentIF::COMPONENT_TEMP_OOL_HIGH, state); - break; - case ThermalComponentIF::BELOW_OPERATIONAL_LIMIT: - EventManagerIF::triggerEvent(reportingId, - ThermalComponentIF::COMPONENT_TEMP_LOW, state); - break; - case ThermalComponentIF::ABOVE_OPERATIONAL_LIMIT: - EventManagerIF::triggerEvent(reportingId, - ThermalComponentIF::COMPONENT_TEMP_HIGH, state); - break; - default: - break; - } -} - -bool ThermalMonitor::isAboveHighLimit() { - if (oldState == ThermalComponentIF::ABOVE_OPERATIONAL_LIMIT) { - return true; - } else { - return false; - } -} - -ReturnValue_t ThermalMonitor::translateState(ThermalComponentIF::State state, float sample, float lowerLimit, - float upperLimit, bool componentIsOperational) { - if (ThermalComponentIF::isIgnoredState(state)) { - setToUnchecked(); - return MonitoringIF::UNCHECKED; - } - switch (state) { - case ThermalComponentIF::OUT_OF_RANGE_LOW: - return monitorStateIs(MonitoringIF::BELOW_LOW_LIMIT, sample, lowerLimit); - case ThermalComponentIF::NON_OPERATIONAL_LOW: - if (componentIsOperational) { - return monitorStateIs(ThermalComponentIF::BELOW_OPERATIONAL_LIMIT, sample, lowerLimit); - } else { - return monitorStateIs(HasReturnvaluesIF::RETURN_OK, sample, 0.0); - } - case ThermalComponentIF::OPERATIONAL: - return monitorStateIs(HasReturnvaluesIF::RETURN_OK, sample, 0.0); - case ThermalComponentIF::NON_OPERATIONAL_HIGH: - if (componentIsOperational) { - return monitorStateIs(ThermalComponentIF::ABOVE_OPERATIONAL_LIMIT, sample, upperLimit); - } else { - return monitorStateIs(HasReturnvaluesIF::RETURN_OK, sample, 0.0); - } - case ThermalComponentIF::OUT_OF_RANGE_HIGH: - return monitorStateIs(MonitoringIF::ABOVE_HIGH_LIMIT, sample, upperLimit); - default: - //Never reached, all states covered. - return HasReturnvaluesIF::RETURN_FAILED; - } -} diff --git a/thermal/ThermalMonitor.h b/thermal/ThermalMonitor.h deleted file mode 100644 index 5c6806c1..00000000 --- a/thermal/ThermalMonitor.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef FRAMEWORK_THERMAL_THERMALMONITOR_H_ -#define FRAMEWORK_THERMAL_THERMALMONITOR_H_ - -#include "../monitoring/MonitorReporter.h" -#include "ThermalComponentIF.h" - -class ThermalMonitor: public MonitorReporter { -public: - template - ThermalMonitor(Args ... args) : - MonitorReporter(std::forward(args)...) { - } - ~ThermalMonitor(); - ReturnValue_t translateState(ThermalComponentIF::State state, float sample, - float lowerLimit, float upperLimit, bool componentIsOperational = true); - - bool isAboveHighLimit(); -protected: - virtual void sendTransitionEvent(float currentValue, ReturnValue_t state); - -}; - -#endif /* FRAMEWORK_THERMAL_THERMALMONITOR_H_ */ From f81c9d7ecc54442eeabfae066f4fafc7feae7698 Mon Sep 17 00:00:00 2001 From: Spacefish Date: Thu, 10 Dec 2020 16:36:09 +0100 Subject: [PATCH 115/119] resolved merge conflicts --- thermal/ThermalModule.cpp | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/thermal/ThermalModule.cpp b/thermal/ThermalModule.cpp index cc541c8c..2bc1741f 100644 --- a/thermal/ThermalModule.cpp +++ b/thermal/ThermalModule.cpp @@ -5,15 +5,9 @@ #include "../monitoring/MonitoringMessageContent.h" -<<<<<<< HEAD ThermalModule::ThermalModule(gp_id_t moduleTemperaturePoolId, gp_id_t currentStatePoolId, gp_id_t targetStatePoolId, LocalPoolDataSetBase *dataSet, Parameters parameters, -======= -ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, - uint32_t currentStatePoolId, uint32_t targetStatePoolId, - GlobDataSet *dataSet, Parameters parameters, ->>>>>>> upstream/development RedundantHeater::Parameters heaterParameters) : oldStrategy(ACTIVE_SINGLE), parameters(parameters), moduleTemperature(moduleTemperaturePoolId, dataSet, @@ -23,7 +17,6 @@ ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, heater = new RedundantHeater(heaterParameters); } -<<<<<<< HEAD ThermalModule::ThermalModule(gp_id_t moduleTemperaturePoolId, LocalPoolDataSetBase* dataSet) : oldStrategy(ACTIVE_SINGLE), parameters( { 0, 0 }), @@ -33,15 +26,6 @@ ThermalModule::ThermalModule(gp_id_t moduleTemperaturePoolId, PoolVariableIF::VAR_WRITE), targetState(gp_id_t(), dataSet, PoolVariableIF::VAR_READ) { -======= -ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, GlobDataSet* dataSet) : - oldStrategy(ACTIVE_SINGLE), survivalTargetTemp(0), targetTemp(0), heating( - false), parameters( { 0, 0 }), moduleTemperature( - moduleTemperaturePoolId, dataSet, PoolVariableIF::VAR_WRITE), heater( - NULL), currentState(PoolVariableIF::INVALID, dataSet, - PoolVariableIF::VAR_WRITE), targetState(PoolVariableIF::INVALID, - dataSet, PoolVariableIF::VAR_READ) { ->>>>>>> upstream/development } ThermalModule::~ThermalModule() { @@ -272,7 +256,6 @@ bool ThermalModule::calculateModuleHeaterRequestAndSetModuleStatus( } void ThermalModule::setHeating(bool on) { -<<<<<<< HEAD // GlobDataSet mySet; // gp_int8_t writableTargetState(targetState.getDataPoolId(), // &mySet, PoolVariableIF::VAR_WRITE); @@ -282,17 +265,6 @@ void ThermalModule::setHeating(bool on) { // writableTargetState = STATE_REQUEST_PASSIVE; // } // mySet.commit(PoolVariableIF::VALID); -======= - GlobDataSet mySet; - gp_int8_t writableTargetState(targetState.getDataPoolId(), - &mySet, PoolVariableIF::VAR_WRITE); - if (on) { - writableTargetState = STATE_REQUEST_HEATING; - } else { - writableTargetState = STATE_REQUEST_PASSIVE; - } - mySet.commit(PoolVariableIF::VALID); ->>>>>>> upstream/development } void ThermalModule::updateTargetTemperatures(ThermalComponentIF* component, From be9913f262e879f8c264f54550d61e983fddc1e5 Mon Sep 17 00:00:00 2001 From: Spacefish Date: Thu, 10 Dec 2020 16:40:22 +0100 Subject: [PATCH 116/119] themral module update --- thermal/ThermalModule.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/thermal/ThermalModule.cpp b/thermal/ThermalModule.cpp index 2bc1741f..457a6743 100644 --- a/thermal/ThermalModule.cpp +++ b/thermal/ThermalModule.cpp @@ -256,15 +256,16 @@ bool ThermalModule::calculateModuleHeaterRequestAndSetModuleStatus( } void ThermalModule::setHeating(bool on) { -// GlobDataSet mySet; -// gp_int8_t writableTargetState(targetState.getDataPoolId(), -// &mySet, PoolVariableIF::VAR_WRITE); -// if (on) { -// writableTargetState = STATE_REQUEST_HEATING; -// } else { -// writableTargetState = STATE_REQUEST_PASSIVE; -// } -// mySet.commit(PoolVariableIF::VALID); + ReturnValue_t result = targetState.read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + if(on) { + targetState.value = STATE_REQUEST_HEATING; + } + else { + targetState.value = STATE_REQUEST_PASSIVE; + } + } + targetState.setValid(true); } void ThermalModule::updateTargetTemperatures(ThermalComponentIF* component, From 50ad2c03809502d9494e37bff99a54bf39fc024c Mon Sep 17 00:00:00 2001 From: Spacefish Date: Thu, 10 Dec 2020 16:57:43 +0100 Subject: [PATCH 117/119] resolved merge conflicts --- power/Fuse.h | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/power/Fuse.h b/power/Fuse.h index 8a1b29f0..b892611b 100644 --- a/power/Fuse.h +++ b/power/Fuse.h @@ -1,21 +1,11 @@ #ifndef FSFW_POWER_FUSE_H_ #define FSFW_POWER_FUSE_H_ -<<<<<<< HEAD #include "PowerComponentIF.h" #include "PowerSwitchIF.h" #include "../devicehandlers/HealthDevice.h" #include "../monitoring/AbsLimitMonitor.h" -======= -#include "../datapoolglob/GlobalDataSet.h" -#include "../datapoolglob/GlobalPoolVariable.h" -#include "../datapoolglob/PIDReader.h" -#include "../devicehandlers/HealthDevice.h" -#include "../monitoring/AbsLimitMonitor.h" -#include "../power/PowerComponentIF.h" -#include "../power/PowerSwitchIF.h" ->>>>>>> upstream/development #include "../returnvalues/HasReturnvaluesIF.h" #include "../parameters/ParameterHelper.h" #include @@ -94,26 +84,14 @@ private: }; PowerMonitor powerMonitor; -<<<<<<< HEAD StaticLocalDataSet<3> set; - //LocalPoolDataSetBase* set = nullptr; - //PIDReader voltage; - //PIDReader current; - //PIDReader state; + lp_var_t voltage; lp_var_t current; lp_var_t state; lp_var_t power; MessageQueueIF* commandQueue = nullptr; -======= - GlobDataSet set; - PIDReader voltage; - PIDReader current; - PIDReader state; - gp_float_t power; - MessageQueueIF* commandQueue; ->>>>>>> upstream/development ParameterHelper parameterHelper; HealthHelper healthHelper; static object_id_t powerSwitchId; From a2823835dd8f6708fd004aef2d95d74547c7cdf7 Mon Sep 17 00:00:00 2001 From: Spacefish Date: Thu, 10 Dec 2020 17:05:41 +0100 Subject: [PATCH 118/119] explicit cast added --- parameters/ParameterWrapper.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/parameters/ParameterWrapper.cpp b/parameters/ParameterWrapper.cpp index 23955516..d9f1ef68 100644 --- a/parameters/ParameterWrapper.cpp +++ b/parameters/ParameterWrapper.cpp @@ -123,7 +123,8 @@ ReturnValue_t ParameterWrapper::deSerializeData(uint8_t startingRow, for (uint8_t fromRow = 0; fromRow < fromRows; fromRow++) { //get the start element of this row in data - uint16_t offset = (((startingRow + fromRow) * columns) + startingColumn); + uint16_t offset = (((startingRow + fromRow) * + static_cast(columns)) + startingColumn); T *dataWithDataType = static_cast(data) + offset; for (uint8_t fromColumn = 0; fromColumn < fromColumns; fromColumn++) { @@ -296,8 +297,8 @@ ReturnValue_t ParameterWrapper::copyFrom(const ParameterWrapper *from, //need a type to do arithmetic uint8_t* typedData = static_cast(data); for (uint8_t fromRow = 0; fromRow < from->rows; fromRow++) { - size_t offset = (((startingRow + fromRow) * columns) + - startingColumn) * typeSize; + size_t offset = (((startingRow + fromRow) * static_cast( + columns)) + startingColumn) * typeSize; std::memcpy(typedData + offset, from->readonlyData, typeSize * from->columns); } From 7986584d17d65da8ed06fc217b216472e0cc12da Mon Sep 17 00:00:00 2001 From: Spacefish Date: Thu, 10 Dec 2020 17:18:46 +0100 Subject: [PATCH 119/119] updated comment --- devicehandlers/DeviceHandlerBase.h | 1 + 1 file changed, 1 insertion(+) diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index 2bfa62a6..9a5287e0 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -746,6 +746,7 @@ protected: /** * Helper function to get pending command. This is useful for devices * like SPI sensors to identify the last sent command. + * This only returns the command sent in the last SEND_WRITE cycle. * @return */ DeviceCommandId_t getPendingCommand() const;