#pragma once #include #include "LocalPoolObjectBase.h" #include "fsfw/datapool/DataSetIF.h" #include "fsfw/datapool/PoolEntry.h" #include "fsfw/datapool/PoolVariableIF.h" #include "fsfw/serialize/SerializeAdapter.h" #include "fsfw/serviceinterface/ServiceInterface.h" namespace datapool { /** * @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 N * 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 PoolObjectBase { public: PoolVector() = 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. */ PoolVector(SharedPool& sharedPool, id_t poolId, DataSetIF* dataSet = nullptr, pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE) : PoolObjectBase(sharedPool, poolId, dataSet, setReadWriteMode) {} /** * 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 poolOwner Owner of the shared pool. * @param poolId ID of the local pool entry. * @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. */ PoolVector(object_id_t poolOwner, id_t poolId, DataSetIF* dataSet = nullptr, pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE) : PoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {} /** * Variation which takes the unique global identifier of a local pool * vector. * @param globalPoolId * @param dataSet * @param setReadWriteMode */ PoolVector(g_id_t globalPoolId, DataSetIF* dataSet = nullptr, pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE) : PoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, dataSet, 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[N] = {}; /** * @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() override {}; /** * @brief The operation returns the number of array entries * in this variable. */ size_t getSize() { return N; } T& operator[](size_t i) { if (i < N) { return value[i]; } // If this happens, I have to set some value. I consider this // a configuration error, but I wont exit here. #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "PoolVector: Invalid index. Setting or returning" " last value!" << std::endl; #else sif::printWarning( "PoolVector: Invalid index. Setting or returning" " last value!\n"); #endif return value[N - 1]; } const T& operator[](size_t i) const { if (i < N) { return value[i]; } // If this happens, I have to set some value. I consider this // a configuration error, but I wont exit here. #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "PoolVector: Invalid index. Setting or returning" " last value!" << std::endl; #else sif::printWarning( "PoolVector: Invalid index. Setting or returning" " last value!\n"); #endif return value[N - 1]; } ReturnValue_t serialize(uint8_t** buffer, size_t* size, const size_t maxSize, SerializeIF::Endianness streamEndianness) const override { ReturnValue_t result = returnvalue::FAILED; for (uint16_t i = 0; i < N; i++) { result = SerializeAdapter::serialize(&(value[i]), buffer, size, maxSize, streamEndianness); if (result != returnvalue::OK) { break; } } return result; } [[nodiscard]] size_t getSerializedSize() const override { return N * SerializeAdapter::getSerializedSize(value); } ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, SerializeIF::Endianness streamEndianness) override { ReturnValue_t result = returnvalue::FAILED; for (uint16_t i = 0; i < N; i++) { result = SerializeAdapter::deSerialize(&(value[i]), buffer, size, streamEndianness); if (result != returnvalue::OK) { break; } } return result; } /** * @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(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, uint32_t timeoutMs = 20) override { MutexGuard mg(sharedPool->getPoolMutex(), timeoutType, timeoutMs); return readWithoutLock(); } /** * @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(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) override { MutexGuard mg(sharedPool->getPoolMutex(), timeoutType, timeoutMs); return commitWithoutLock(); } ReturnValue_t commit() { return commit(MutexIF::TimeoutType::WAITING, 20); } 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 { if (readWriteMode == pool_rwm_t::VAR_WRITE) { return PoolVariableIF::INVALID_READ_WRITE_MODE; } PoolEntry* poolEntry = nullptr; ReturnValue_t result = sharedPool->fetchPoolEntry(localPoolId, &poolEntry); memset(this->value, 0, N * sizeof(T)); if (result != returnvalue::OK) { return result; } this->valid = poolEntry->getValid(); std::memcpy(this->value, poolEntry->getDataPtr(), poolEntry->getByteSize()); return returnvalue::OK; } /** * @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 { if (readWriteMode == pool_rwm_t::VAR_READ) { return PoolVariableIF::INVALID_READ_WRITE_MODE; } PoolEntry* poolEntry = nullptr; ReturnValue_t result = sharedPool->fetchPoolEntry(localPoolId, &poolEntry); if (result != returnvalue::OK) { return result; } poolEntry->setValid(this->valid); std::memcpy(poolEntry->getDataPtr(), this->value, poolEntry->getByteSize()); return returnvalue::OK; } private: #if FSFW_CPP_OSTREAM_ENABLED == 1 std::ostream& operator<<(std::ostream& out, const PoolVector& var) { out << "Vector: ["; for (int i = 0; i < N; i++) { out << var.value[i]; if (i < N - 1) { out << ", "; } } out << "]"; return out; } #endif }; template using vec_t = PoolVector; } // namespace datapool