Files
fsfw/src/fsfw/datapool/LocalPoolVector.h
T

266 lines
9.8 KiB
C++

#pragma once
#include <fsfw/ipc/MutexGuard.h>
#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 <typename T, size_t N>
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<T>* 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<T>* 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<T, N>& var) {
out << "Vector: [";
for (int i = 0; i < N; i++) {
out << var.value[i];
if (i < N - 1) {
out << ", ";
}
}
out << "]";
return out;
}
#endif
};
template <typename T, size_t N>
using vec_t = PoolVector<T, N>;
} // namespace datapool