Compare commits

..

28 Commits

Author SHA1 Message Date
faeca1b419 update PUS5 TM format 2025-02-25 16:25:54 +01:00
7b6981e162 update for event definitions 2025-02-25 14:46:36 +01:00
b8979d8f90 Merge pull request 'Improve readability of SubsystemBase::executeTable' (#49) from spahr/SubsystemBase into main
Reviewed-on: #49
2025-02-19 11:26:36 +01:00
9557db7036 Merge branch 'main' into spahr/SubsystemBase 2025-02-19 11:26:26 +01:00
c9fcabccd6 Merge pull request 'Missing valid flag update in readWithoutLock and commitWithoutLock' (#48) from bugfix-poolvariable into main
Reviewed-on: #48
2025-02-19 11:26:04 +01:00
ecd36f5e52 Merge branch 'main' into bugfix-poolvariable 2025-02-19 11:25:52 +01:00
fc19c0838e Merge pull request 'Formatting of debug printout' (#50) from spahr/formattingForDebugPrintout into main
Reviewed-on: #50
2025-02-19 11:24:39 +01:00
spahr@ksat-stuttgart.de
8c3f366d1a formatting for time prinout 2025-02-16 17:36:22 +01:00
spahr@ksat-stuttgart.de
6efb2641a7 Re-write the logic in executeTable() to improve the readabilty of the code. This gets rid of the usage of isFaulty() (which is misleading), and removes nexted if-statements 2025-02-16 17:07:40 +01:00
spahr@ksat-stuttgart.de
260bbad9a0 Modify the hard-coded strings which are printed to into the debug session to enforce alignment of all printouts, regardless of their printlevel (info, debug, warning, error). 2025-02-14 19:02:57 +01:00
9edd6221f8 Missing valid flag update in readWithoutLock and commitWithoutLock 2025-02-11 15:16:27 +01:00
735e341aab Merge pull request 'temperaturesensor-bugfix' (#47) from temperaturesensor-bugfix into main
Reviewed-on: #47
2025-01-21 14:33:29 +01:00
921bfb1e99 Merge branch 'main' into temperaturesensor-bugfix 2025-01-21 14:31:59 +01:00
5b1651e1a6 Merge pull request 'Update HK and datapool handling' (#45) from update-hk-handling-datapools into main
Reviewed-on: #45
2025-01-08 10:38:34 +01:00
e916b9b096 fix for CMakeLists.txt 2025-01-07 10:50:37 +01:00
b14e761bad small typo 2025-01-07 10:50:37 +01:00
33f3ae2434 Update and clean up HK and Local Pool Modules 2025-01-07 10:50:37 +01:00
f0087d5b0d Merge pull request 'FreeRTOS Monotonic Clock' (#46) from meier/freertos-monotonic-clock into main
Reviewed-on: #46
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2025-01-07 10:33:29 +01:00
mikael.senger
69c33587e8 Merge remote-tracking branch 'origin/main' into temperaturesensor-bugfix 2024-12-31 10:51:11 +01:00
Jakob Meier
d1bf04cc29 Merge branch 'main' into meier/freertos-monotonic-clock 2024-12-25 10:20:22 +01:00
Jakob Meier
8e3bc1b8aa updated changelog 2024-12-23 12:07:54 +01:00
Jakob Meier
64f97fc3ba implemented freertos monotonic clock 2024-12-23 12:02:29 +01:00
Jakob Meier
1427fbd2fe added monotonic clock which is independend of clock jumps in the system clock 2024-12-22 18:37:44 +01:00
mikael.senger
81cd8bd290 did not build before 2024-12-15 23:52:32 +01:00
5dfeee08b3 Merge pull request 'some FDHB docs improvements' (#44) from fdhb-docs into main
Reviewed-on: #44
2024-12-11 13:55:18 +01:00
d77dbe44c6 some FDHB docs improvements 2024-12-11 13:54:16 +01:00
2165f8ead8 Merge pull request 'add docs' (#43) from fdhb-docs into main
Reviewed-on: #43
2024-12-11 13:44:15 +01:00
3def5d8d85 add docs 2024-12-11 13:01:48 +01:00
46 changed files with 821 additions and 133 deletions

View File

@@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Added
- FreeRTOS monotonic clock which is not subjected to time jumps of the system clock
- add CFDP subsystem ID
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/742
- `PusTmZcWriter` now exposes API to set message counter field.
@@ -34,8 +35,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Changed
- Complete refactoring of HK subsystem. Replaced local data pool manager by periodic HK
- Complete overhaul of HK subsystem. Replaced local data pool manager by periodic HK
helper. The shared pool and the periodic HK generation are now distinct concepts.
- The local HK manager was replaced by a periodic HK helper which has reduced responsibilities.
It takes care of tracking the HK generation using a set specification provided by the user.n
However, it leaves serialization of the HK data completely to the developer. This removes a major
constraint on the format of the HK data, which was previously constrained to implementors of a
certain base class.
- The former set classes and pool objects are still available for HK set specification and
generation. The API has changed, but the general usage and their architecture has not.
- A new set of set classes and helper objects to specify HK sets and data which does not need to be
shared was added as well. The majority of datasets do not need to be shared anyway.
- The non-shared API retain the capability of appending of a validity blob for each piece of set
data at the end of the HK data. For both non-shared and shared data, this capability can be
specified in the constructor, and defaults to true.
- Improved File System Abstraction to be more in line with normal filesystems.
- CFDP implementation was improved, has now even less dependencies on other FSFW components
and allows one inserted packet per state machine call.

View File

@@ -34,11 +34,12 @@ class ExtendedControllerBase : public ControllerBase,
ActionHelper actionHelper;
// Periodic HK methods, default method assumes that no shared pool is required.
virtual datapool::SharedPool* getOptionalSharedPool() override;
datapool::SharedPool* getOptionalSharedPool() override = 0;
// Periodic HK abstract methods.
ReturnValue_t serializeHkDataset(dp::sid_t structureId, uint8_t* buf, size_t maxSize) = 0;
ReturnValue_t specifyHkDatasets(std::vector<hk::SetSpecification>& setList) = 0;
ReturnValue_t serializeHkDataset(dp::sid_t structureId, uint8_t* buf,
size_t maxSize) override = 0;
ReturnValue_t specifyHkDatasets(std::vector<hk::SetSpecification>& setList) override = 0;
/**
* Implemented by child class. Handle all command messages which are

View File

@@ -48,6 +48,14 @@ PoolObjectBase::PoolObjectBase(object_id_t poolOwner, id_t poolId, DataSetIF* da
return;
}
sharedPool = hkOwner->getOptionalSharedPool();
if (sharedPool == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PoolObjectBase: HK owner 0x" << std::hex << poolOwner << std::dec
<< "does not have a shared pool " << std::endl;
#else
sif::printError("PoolObjectBase: HK owner 0x%08x does not have a shared pool\n", poolOwner);
#endif
}
if (dataSet != nullptr) {
dataSet->registerVariable(this);
@@ -64,6 +72,10 @@ void PoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) {
this->readWriteMode = newReadWriteMode;
}
[[nodiscard]] bool PoolObjectBase::isValid() const { return valid; }
void PoolObjectBase::setValid(bool valid) { this->valid = valid; }
void PoolObjectBase::reportReadCommitError(const char* variableType, ReturnValue_t error, bool read,
object_id_t objectId, id_t lpId) {
#if FSFW_DISABLE_PRINTOUT == 0

View File

@@ -27,6 +27,9 @@ class PoolObjectBase : public PoolVariableIF {
[[nodiscard]] dp::id_t getDataPoolId() const override;
void setDataPoolId(dp::id_t poolId);
[[nodiscard]] bool isValid() const;
void setValid(bool valid);
protected:
/**
* @brief To access the correct data pool entry on read and commit calls,
@@ -40,6 +43,8 @@ class PoolObjectBase : public PoolVariableIF {
*/
ReadWriteMode_t readWriteMode = pool_rwm_t::VAR_READ_WRITE;
bool valid = false;
//! @brief Pointer to the class which manages the HK pool.
dp::SharedPool* sharedPool = nullptr;
@@ -47,4 +52,4 @@ class PoolObjectBase : public PoolVariableIF {
object_id_t objectId, dp::id_t lpId);
};
} // namespace datapool
} // namespace datapool

View File

@@ -216,6 +216,7 @@ class PoolVector : public PoolObjectBase {
if (result != returnvalue::OK) {
return result;
}
this->valid = poolEntry->getValid();
std::memcpy(this->value, poolEntry->getDataPtr(), poolEntry->getByteSize());
return returnvalue::OK;
}
@@ -237,6 +238,7 @@ class PoolVector : public PoolObjectBase {
if (result != returnvalue::OK) {
return result;
}
poolEntry->setValid(this->valid);
std::memcpy(poolEntry->getDataPtr(), this->value, poolEntry->getByteSize());
return returnvalue::OK;
}

View File

@@ -1,15 +1,19 @@
#include "fsfw/datapool/PoolDataSetBase.h"
#include <cmath>
#include <cstring>
#include "fsfw/datapool/ReadCommitIFAttorney.h"
#include "fsfw/globalfunctions/bitutility.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
const size_t maxFillCount)
: registeredVariables(registeredVariablesArray), maxFillCount(maxFillCount) {}
const size_t maxFillCount, bool serializeWithValidityBlob)
: serializeWithValidityBlob(serializeWithValidityBlob),
registeredVariables(registeredVariablesArray),
maxFillCount(maxFillCount) {}
PoolDataSetBase::~PoolDataSetBase() {}
PoolDataSetBase::~PoolDataSetBase() = default;
ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF* variable) {
if (registeredVariables == nullptr) {
@@ -172,6 +176,9 @@ ReturnValue_t PoolDataSetBase::unlockDataPool() { return returnvalue::OK; }
ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
if (this->serializeWithValidityBlob) {
return doSerializeWithValidityBlob(buffer, size, maxSize, streamEndianness);
}
ReturnValue_t result = returnvalue::FAILED;
for (uint16_t count = 0; count < fillCount; count++) {
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
@@ -182,6 +189,51 @@ ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, const s
return result;
}
ReturnValue_t PoolDataSetBase::doSerializeWithValidityBlob(
uint8_t** buffer, size_t* size, const size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = returnvalue::FAILED;
const uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount) / 8.0);
uint8_t* validityPtr = nullptr;
#if defined(_MSC_VER) || defined(__clang__)
// Use a std::vector here because MSVC will (rightly) not create a fixed size array
// with a non constant size specifier. The Apple compiler (LLVM) will not accept
// the initialization of a variable sized array
std::vector<uint8_t> validityMask(validityMaskSize, 0);
validityPtr = validityMask.data();
#else
uint8_t validityMask[validityMaskSize] = {};
validityPtr = validityMask;
#endif
uint8_t validBufferIndex = 0;
uint8_t validBufferIndexBit = 0;
for (uint16_t count = 0; count < fillCount; count++) {
if (registeredVariables[count]->isValid()) {
// Set bit at correct position
bitutil::set(validityPtr + validBufferIndex, validBufferIndexBit);
}
if (validBufferIndexBit == 7) {
validBufferIndex++;
validBufferIndexBit = 0;
} else {
validBufferIndexBit++;
}
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
}
if (*size + validityMaskSize > maxSize) {
return SerializeIF::BUFFER_TOO_SHORT;
}
// copy validity buffer to end
std::memcpy(*buffer, validityPtr, validityMaskSize);
*size += validityMaskSize;
return result;
}
ReturnValue_t PoolDataSetBase::deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) {
ReturnValue_t result = returnvalue::FAILED;
@@ -199,6 +251,9 @@ size_t PoolDataSetBase::getSerializedSize() const {
for (uint16_t count = 0; count < fillCount; count++) {
size += registeredVariables[count]->getSerializedSize();
}
if (serializeWithValidityBlob) {
size += std::ceil(static_cast<float>(fillCount) / 8.0);
}
return size;
}
@@ -215,3 +270,9 @@ void PoolDataSetBase::setReadCommitProtectionBehaviour(bool protectEveryReadComm
this->timeoutTypeForSingleVars = timeoutType;
this->mutexTimeoutForSingleVars = mutexTimeout;
}
void PoolDataSetBase::setChildrenValidity(bool valid) {
for (uint16_t count = 0; count < fillCount; count++) {
registeredVariables[count]->setValid(valid);
}
}

View File

@@ -41,7 +41,8 @@ class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
* supply a pointer to this dataset to PoolVariable
* initializations to register pool variables.
*/
PoolDataSetBase(PoolVariableIF** registeredVariablesArray, size_t maxFillCount);
PoolDataSetBase(PoolVariableIF** registeredVariablesArray, size_t maxFillCount,
bool serializeWithValidityBlob = true);
/* Forbidden for now */
PoolDataSetBase(const PoolDataSetBase& otherSet) = delete;
@@ -115,13 +116,15 @@ class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
[[nodiscard]] uint16_t getFillCount() const override;
/* SerializeIF implementations */
// SerializeIF implementations
ReturnValue_t serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
[[nodiscard]] size_t getSerializedSize() const override;
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
ReturnValue_t doSerializeWithValidityBlob(uint8_t** buffer, size_t* size, const size_t maxSize,
SerializeIF::Endianness streamEndianness) const;
/**
* Can be used to individually protect every read and commit call.
* @param protectEveryReadCommit
@@ -131,6 +134,13 @@ class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
bool protectEveryReadCommit, MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t mutexTimeout = 20);
/**
* Set the validity of all children
*/
void setChildrenValidity(bool valid);
bool serializeWithValidityBlob = false;
protected:
/**
* @brief The fill_count attribute ensures that the variables

View File

@@ -52,6 +52,16 @@ void* PoolEntry<T>::getRawData() {
return this->address;
}
template <typename T>
void PoolEntry<T>::setValid(bool isValid) {
this->valid = isValid;
}
template <typename T>
bool PoolEntry<T>::getValid() {
return valid;
}
template <typename T>
void PoolEntry<T>::print() {
const char* validString = nullptr;

View File

@@ -105,6 +105,17 @@ class PoolEntry : public PoolEntryIF {
*/
void* getRawData();
/**
* @brief This method allows to set the valid information
* of the pool entry.
*/
void setValid(bool isValid) override;
/**
* @brief This method allows to get the valid information
* of the pool entry.
*/
bool getValid() override;
/**
* @brief This is a debug method that prints all values and the valid
* information to the screen. It prints all array entries in a row.

View File

@@ -51,6 +51,15 @@ class PoolEntryIF {
* Returns the type of the entry.
*/
virtual Type getType() = 0;
/**
* @brief This method allows to set the valid information of the pool entry.
*/
virtual void setValid(bool isValid) = 0;
/**
* @brief This method allows to set the valid information of the pool entry.
*/
virtual bool getValid() = 0;
};
#endif /* FSFW_DATAPOOL_POOLENTRYIF_H_ */

View File

@@ -20,9 +20,9 @@ class PoolReadGuard {
if (readResult != returnvalue::OK) {
#if FSFW_VERBOSE_LEVEL == 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PoolReadHelper: Read failed!" << std::endl;
sif::error << "PoolReadGuard: Read failed!" << std::endl;
#else
sif::printError("PoolReadHelper: Read failed!\n");
sif::printError("PoolReadGuard: Read failed!\n");
#endif /* FSFW_PRINT_VERBOSITY_LEVEL == 1 */
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
}

View File

@@ -210,6 +210,7 @@ inline ReturnValue_t PoolVariable<T>::readWithoutLock() {
}
this->value = *(poolEntry->getDataPtr());
this->valid = poolEntry->getValid();
return returnvalue::OK;
}
@@ -241,6 +242,7 @@ ReturnValue_t PoolVariable<T>::commitWithoutLock() {
}
*(poolEntry->getDataPtr()) = this->value;
poolEntry->setValid(this->valid);
return returnvalue::OK;
}

View File

@@ -47,6 +47,9 @@ class PoolVariableIF : public SerializeIF, public ReadCommitIF {
* @brief This operation shall return the data pool id of the variable.
*/
virtual uint32_t getDataPoolId() const = 0;
virtual bool isValid() const = 0;
virtual void setValid(bool valid) = 0;
};
using pool_rwm_t = PoolVariableIF::ReadWriteMode_t;

View File

@@ -2,14 +2,17 @@
using namespace dp;
SharedSet::SharedSet(dp::SharedPool& sharedPool, uint32_t setId, const size_t maxNumberOfVariables)
: SharedSetBase(sharedPool, setId, nullptr, maxNumberOfVariables),
SharedSet::SharedSet(dp::SharedPool& sharedPool, uint32_t setId, const size_t maxNumberOfVariables,
bool serializeWithValidityBlob)
: SharedSetBase(sharedPool, setId, nullptr, maxNumberOfVariables, serializeWithValidityBlob),
poolVarList(maxNumberOfVariables) {
this->setContainer(poolVarList.data());
}
SharedSet::SharedSet(dp::structure_id_t sid, const size_t maxNumberOfVariables)
: SharedSetBase(sid, nullptr, maxNumberOfVariables), poolVarList(maxNumberOfVariables) {
SharedSet::SharedSet(dp::structure_id_t sid, const size_t maxNumberOfVariables,
bool serializeWithValidityBlob)
: SharedSetBase(sid, nullptr, maxNumberOfVariables, serializeWithValidityBlob),
poolVarList(maxNumberOfVariables) {
this->setContainer(poolVarList.data());
}

View File

@@ -21,9 +21,10 @@ namespace datapool {
*/
class SharedSet : public SharedSetBase {
public:
SharedSet(SharedPool& sharedPool, uint32_t setId, size_t maxSize);
SharedSet(SharedPool& sharedPool, uint32_t setId, size_t maxSize,
bool serializeWithValidityBlob = true);
SharedSet(sid_t sid, size_t maxSize);
SharedSet(sid_t sid, size_t maxSize, bool serializeWithValidityBlob = true);
~SharedSet() override;

View File

@@ -14,8 +14,9 @@ using namespace datapool;
SharedSetBase::SharedSetBase(SharedPool &sharedPool, uint32_t setId,
PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables)
: base(registeredVariablesArray, maxNumberOfVariables), sharedPool(&sharedPool) {
const size_t maxNumberOfVariables, bool serializeWithValidityBlob)
: base(registeredVariablesArray, maxNumberOfVariables, serializeWithValidityBlob),
sharedPool(&sharedPool) {
mutexIfSingleDataCreator = sharedPool.getPoolMutex();
this->sid.objectId = sharedPool.getOwnerId();
@@ -23,8 +24,8 @@ SharedSetBase::SharedSetBase(SharedPool &sharedPool, uint32_t setId,
}
SharedSetBase::SharedSetBase(structure_id_t sid, PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables)
: base(registeredVariablesArray, maxNumberOfVariables) {
const size_t maxNumberOfVariables, bool serializeWithValidityBlob)
: base(registeredVariablesArray, maxNumberOfVariables, serializeWithValidityBlob) {
auto *hkOwner = ObjectManager::instance()->get<hk::GeneratesPeriodicHkIF>(sid.objectId);
if (hkOwner != nullptr) {
sharedPool = hkOwner->getOptionalSharedPool();
@@ -37,8 +38,9 @@ SharedSetBase::SharedSetBase(structure_id_t sid, PoolVariableIF **registeredVari
}
SharedSetBase::SharedSetBase(PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables, bool protectEveryReadCommitCall)
: base(registeredVariablesArray, maxNumberOfVariables) {
const size_t maxNumberOfVariables, bool protectEveryReadCommitCall,
bool serializeWithValidityBlob)
: base(registeredVariablesArray, maxNumberOfVariables, serializeWithValidityBlob) {
base.setReadCommitProtectionBehaviour(protectEveryReadCommitCall);
}
@@ -120,6 +122,8 @@ bool SharedSetBase::getReportingEnabled() const { return reportingEnabled; }
structure_id_t SharedSetBase::getStructureId() const { return sid; }
void SharedSetBase::setChildrenValidity(bool valid) { return base.setChildrenValidity(valid); }
object_id_t SharedSetBase::getCreatorObjectId() {
if (sharedPool != nullptr) {
return sharedPool->getOwnerId();
@@ -136,10 +140,17 @@ void SharedSetBase::setAllVariablesReadOnly() {
void SharedSetBase::printSet() { return; }
ReturnValue_t SharedSetBase::read(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
return base.read(timeoutType, timeoutMs);
lockDataPool(timeoutType, timeoutMs);
ReturnValue_t result = base.read(timeoutType, timeoutMs);
unlockDataPool();
return result;
}
ReturnValue_t SharedSetBase::commit(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
return base.commit(timeoutType, timeoutMs);
lockDataPool(timeoutType, timeoutMs);
ReturnValue_t result = base.commit(timeoutType, timeoutMs);
unlockDataPool();
return result;
}
uint16_t SharedSetBase::getFillCount() const { return base.getFillCount(); }
@@ -151,3 +162,8 @@ void SharedSetBase::setContainer(PoolVariableIF **variablesContainer) {
return base.setContainer(variablesContainer);
}
PoolVariableIF **SharedSetBase::getContainer() const { return base.getContainer(); }
void SharedSetBase::updateValidityBlobSerialization(bool enable) {
base.serializeWithValidityBlob = enable;
}
bool SharedSetBase::getValidityBlobSerialization() const { return base.serializeWithValidityBlob; }

View File

@@ -40,8 +40,6 @@ namespace datapool {
* @ingroup data_pool
*/
class SharedSetBase : public SerializeIF, public PoolDataSetIF {
// friend class PeriodicHousekeepingHelper;
public:
/**
* @brief Constructor for the creator of local pool data.
@@ -50,7 +48,7 @@ class SharedSetBase : public SerializeIF, public PoolDataSetIF {
* periodic handling.
*/
SharedSetBase(SharedPool& sharedPool, uint32_t setId, PoolVariableIF** registeredVariablesArray,
size_t maxNumberOfVariables);
size_t maxNumberOfVariables, bool serializeWithValidityBlob = true);
/**
* @brief Constructor for users of the local pool data, which need
@@ -63,7 +61,8 @@ class SharedSetBase : public SerializeIF, public PoolDataSetIF {
* @param registeredVariablesArray
* @param maxNumberOfVariables
*/
SharedSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray, size_t maxNumberOfVariables);
SharedSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray, size_t maxNumberOfVariables,
bool serializeWithValidityBlob = true);
/**
* @brief Simple constructor, if the dataset is not the owner by
@@ -84,7 +83,7 @@ class SharedSetBase : public SerializeIF, public PoolDataSetIF {
* commit calls separately.
*/
SharedSetBase(PoolVariableIF** registeredVariablesArray, size_t maxNumberOfVariables,
bool protectEveryReadCommitCall = true);
bool serializeWithValidityBlob, bool protectEveryReadCommitCall = true);
/**
* @brief The destructor automatically manages writing the valid
@@ -109,14 +108,14 @@ class SharedSetBase : public SerializeIF, public PoolDataSetIF {
*/
void setAllVariablesReadOnly();
[[nodiscard]] ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override;
[[nodiscard]] virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override;
[[nodiscard]] ReturnValue_t serialize(uint8_t* buffer, size_t& serLen, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
Endianness streamEndianness) override;
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
Endianness streamEndianness) override;
[[nodiscard]] dp::sid_t getStructureId() const;
@@ -155,6 +154,14 @@ class SharedSetBase : public SerializeIF, public PoolDataSetIF {
void setContainer(PoolVariableIF** variablesContainer);
PoolVariableIF** getContainer() const;
/**
* Set the validity of all children
*/
void setChildrenValidity(bool valid);
void updateValidityBlobSerialization(bool enable);
bool getValidityBlobSerialization() const;
protected:
PoolDataSetBase base;
@@ -169,12 +176,6 @@ class SharedSetBase : public SerializeIF, public PoolDataSetIF {
void initializePeriodicHelper(float collectionInterval, dur_millis_t minimumPeriodicInterval);
/**
* 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.

View File

@@ -28,8 +28,9 @@ class StaticSharedSet : public datapool::SharedSetBase {
* @param sharedPool Shared pool this dataset will read from or write to.
* @param setId
*/
StaticSharedSet(SharedPool& sharedPool, const uint32_t setId)
: SharedSetBase(sharedPool, setId, nullptr, NUM_VARIABLES) {
StaticSharedSet(SharedPool& sharedPool, const uint32_t setId,
bool serializeWithValidityBlob = true)
: SharedSetBase(sharedPool, setId, nullptr, NUM_VARIABLES, serializeWithValidityBlob) {
this->setContainer(poolVarList.data());
}
@@ -37,7 +38,8 @@ class StaticSharedSet : public datapool::SharedSetBase {
* Constructor used by data users like controllers.
* @param sid
*/
explicit StaticSharedSet(const structure_id_t sid) : SharedSetBase(sid, nullptr, NUM_VARIABLES) {
explicit StaticSharedSet(const structure_id_t sid, bool serializeWithValidityBlob = true)
: SharedSetBase(sid, nullptr, NUM_VARIABLES, serializeWithValidityBlob) {
this->setContainer(poolVarList.data());
}

View File

@@ -13,7 +13,7 @@ class DeviceHandlerThermalSet : public dp::StaticSharedSet<2> {
: DeviceHandlerThermalSet(hkOwner->getObjectId(), cfg) {}
DeviceHandlerThermalSet(object_id_t deviceHandler, ThermalStateCfg cfg)
: StaticSharedSet(dp::structure_id_t(deviceHandler, cfg.thermalSetId)),
: StaticSharedSet(dp::structure_id_t(deviceHandler, cfg.thermalSetId), true),
thermalStatePoolId(cfg.thermalStatePoolId),
heaterRequestPoolId(cfg.thermalRequestPoolId) {}

View File

@@ -26,6 +26,33 @@ struct DhbConfig {
uint32_t msgQueueDepth = 10;
};
/**
* @brief
* This is a base class which can be used for handler classes which drive physical devices.
*
* @details
* This class performs some boilerplate tasks to make it compatible to the FSFW ecosystem.
* For example, it adds all required interfaces and helpers which are required for the majority
* of physical devices:
*
* - SystemObject to become globally addressable
* - HasModesIF and the ModeHelper: Expose modes. It is expected that most device handlers
* use the default device handler modes OFF, ON and NORMAL at the very least.
* - HasHealthIF, HealthHelper and a FDIR object: Physical devices can break down or require
* power cycling as the most common FDIR reaction. The FDHB implements the necessary interface
* and is also composed of a FDIR object which can set the health state to FAULTY or
* NEEDS_RECOVERY to cause FDIR reactions.
* - ModeTreeChildIF: Many device handlers are part of a mode tree.
* - HasActionsIF and the ActionHelper: Most device handlers expose executable commands, for example
* to re-configure the physical device or to poll data from the physical device.
* - ReceivesParameterMessagesIF and the ParameterHelper: Most device handlers have tweakable
* parameters.
*
* This class is used to extending it in a concrete device handler class and then implementing
* all abstract methods. The abstract methods provide adaption points for users which were
* deemed sufficient for most use-cases. The behaviour of the base-class can also be further
* configured by implementing other virtual functions.
*/
class FreshDeviceHandlerBase : public SystemObject,
public DeviceHandlerIF,
public HasModesIF,
@@ -41,7 +68,20 @@ class FreshDeviceHandlerBase : public SystemObject,
~FreshDeviceHandlerBase() override;
/**
* @brief
* Periodic helper executed function, implemented by child class.
*
* @details
* This will be called by the default performOperation call. This is the main adaption point
* for the actual device handling logic. It is expected that the following common tasks are
* performed in this method:
*
* 1. Performing periodic polling of the physical device in NORMAL mode. This is usually required
* to poll housekeeping data from the device periodically.
* 2. Handle on-going mode transitions if the mode transitions can only be handled asynchronously.
* This might also involve communication with the physical device and power switch
* handling/polling for transition to OFF or ON/NORMAL.
* 3. Perform other periodic tasks which always need to be done irrespective of mode.
*/
virtual void performDeviceOperation(uint8_t opCode) = 0;
@@ -89,13 +129,17 @@ class FreshDeviceHandlerBase : public SystemObject,
// Mode Helpers.
virtual void modeChanged(Mode_t mode, Submode_t submode);
/**
* This method is called when a mode message to set a new mode is received.
*
* The default implementation sets the new mode immediately. If this is not applicable for
* certain modes, the user should provide a custom implementation, which performs roughly
* the same functionality of this function, when all the steps have been taken to reach the
* new mode.
*/
void startTransition(Mode_t mode, Submode_t submode) override;
virtual void setMode(Mode_t newMode, Submode_t newSubmode);
virtual void setMode(Mode_t newMode);
void getMode(Mode_t* mode, Submode_t* submode) override;
@@ -104,12 +148,23 @@ class FreshDeviceHandlerBase : public SystemObject,
// System Object overrides.
ReturnValue_t initialize() override;
// Default implementation assumes that no optional shared pool is required.
virtual datapool::SharedPool* getOptionalSharedPool();
/**
* This function is implemented to serialize a housekeeping packet when a HK message to
* generate the packet is received, or periodic generation is necessary. The user should serialize
* the HK set into the provided buffer, which will have the size specified in the set
* specification.
*/
ReturnValue_t serializeHkDataset(dp::sid_t structureId, uint8_t* buf,
size_t maxSize) override = 0;
/**
* This function is implemented by the user to specify the available housekeeping sets.
*/
ReturnValue_t specifyHkDatasets(std::vector<hk::SetSpecification>& setList) override = 0;
// Implemented by child, required for periodic HK.
ReturnValue_t serializeHkDataset(dp::sid_t structureId, uint8_t* buf, size_t maxSize) = 0;
ReturnValue_t specifyHkDatasets(std::vector<hk::SetSpecification>& setList) = 0;
/*
* If this device handler has an optional pool, return it. Otherwise, nullptr can be returned.
*/
datapool::SharedPool* getOptionalSharedPool() override = 0;
/**
* Implemented by child class. Handle all command messages which are
@@ -124,11 +179,33 @@ class FreshDeviceHandlerBase : public SystemObject,
uint32_t* msToReachTheMode) override = 0;
// Health Overrides.
ReturnValue_t setHealth(HealthState health) override;
// Action override. Forward to user.
/**
* This is called when an ActionMessage is received to execute it.
* @param actionId
* @param commandedBy
* @param data
* @param size
* @return
*/
ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
const uint8_t* data, size_t size) override = 0;
// Executable overrides.
ReturnValue_t performOperation(uint8_t opCode) override;
/**
* This is the default periodic operation handler.
*
* Currently, it performs the following tasks:
*
* 1. Handle the message queue and all message types covered by the base class as specified
* in the class documentation. It uses the composed helper classes for this.
* 2. Poll the FDIR instance to check for failures. The FDIR works by checking all events
* received by the FDHB. An FDIR reaction sets the health state of the FDHB, which might
* in turn trigger FDIR reactions.
* 3. Call the performDeviceOperation user hook.
* 4. Handle periodic housekeeping data generation.
* @param opCode
* @return
*/
virtual ReturnValue_t performOperation(uint8_t opCode) override;
/**
* This calls the FDIR instance event trigger function.

View File

@@ -5,28 +5,28 @@
#include "fwSubsystemIdRanges.h"
using EventId_t = uint16_t;
using EventSeverity_t = uint8_t;
using UniqueEventId_t = uint8_t;
using GroupId_t = uint16_t;
using UniqueEventId_t = uint16_t;
namespace severity {
enum Severity : EventSeverity_t { INFO = 1, LOW = 2, MEDIUM = 3, HIGH = 4 };
enum Severity : EventSeverity_t { INFO = 0, LOW = 1, MEDIUM = 2, HIGH = 3 };
} // namespace severity
#define MAKE_EVENT(id, severity) (((severity) << 16) + (SUBSYSTEM_ID * 100) + (id))
#define MAKE_EVENT(id, severity) (((severity) << 30) | (SUBSYSTEM_ID << 16) | id)
typedef uint32_t Event;
namespace event {
constexpr EventId_t getEventId(Event event) { return (event & 0xFFFF); }
constexpr UniqueEventId_t getEventId(Event event) { return (event & 0xFFFF); }
constexpr EventSeverity_t getSeverity(Event event) { return ((event >> 16) & 0xFF); }
constexpr EventSeverity_t getSeverity(Event event) { return ((event >> 30) & 0b11); }
constexpr Event makeEvent(uint8_t subsystemId, UniqueEventId_t uniqueEventId,
constexpr Event makeEvent(GroupId_t groupId, UniqueEventId_t uniqueEventId,
EventSeverity_t eventSeverity) {
return (eventSeverity << 16) + (subsystemId * 100) + uniqueEventId;
return (eventSeverity << 30) | (groupId << 16) | uniqueEventId;
}
} // namespace event

View File

@@ -1,9 +1,4 @@
#ifndef FSFW_INC_FSFW_HOUSEKEEPING_H_
#define FSFW_INC_FSFW_HOUSEKEEPING_H_
#pragma once
#include "src/core/housekeeping/HousekeepingMessage.h"
#include "src/core/housekeeping/HousekeepingPacketDownlink.h"
#include "src/core/housekeeping/HousekeepingSetPacket.h"
#include "src/core/housekeeping/HousekeepingSnapshot.h"
#endif /* FSFW_INC_FSFW_HOUSEKEEPING_H_ */
#include "fsfw/housekeeping/Dataset.h"
#include "fsfw/housekeeping/DatasetElement.h"

View File

@@ -0,0 +1,128 @@
#pragma once
#include <cmath>
#include <cstring>
#include <functional>
#include <vector>
#include "SerializableWithValidityIF.h"
#include "definitions.h"
#include "fsfw/globalfunctions/bitutility.h"
namespace hk {
class Dataset : public SerializeIF {
public:
explicit Dataset(dp::structure_id_t sid, bool serializeWithValidityBlob = true)
: serializeWithValidityBlob(serializeWithValidityBlob), sid(sid) {}
[[nodiscard]] dp::structure_id_t getStructureId() const { return sid; }
void addSerializable(const std::reference_wrapper<SerializableWithValidityIF> serializable) {
serializables.push_back(serializable);
}
[[nodiscard]] size_t getNumberOfSerializables() const { return serializables.size(); }
[[nodiscard]] ReturnValue_t doSerializeWithValidityBlob(uint8_t **buffer, size_t *size,
size_t maxSize,
Endianness streamEndianness) const {
ReturnValue_t result = returnvalue::FAILED;
const uint8_t validityMaskSize = std::ceil(static_cast<float>(serializables.size()) / 8.0);
uint8_t *validityPtr = nullptr;
#if defined(_MSC_VER) || defined(__clang__)
// Use a std::vector here because MSVC will (rightly) not create a fixed size array
// with a non constant size specifier. The Apple compiler (LLVM) will not accept
// the initialization of a variable sized array
std::vector<uint8_t> validityMask(validityMaskSize, 0);
validityPtr = validityMask.data();
#else
uint8_t validityMask[validityMaskSize] = {};
validityPtr = validityMask;
#endif
uint8_t validBufferIndex = 0;
uint8_t validBufferIndexBit = 0;
for (auto &serializable : serializables) {
if (serializable.get().isValid()) {
// Set bit at correct position
bitutil::set(validityPtr + validBufferIndex, validBufferIndexBit);
}
if (validBufferIndexBit == 7) {
validBufferIndex++;
validBufferIndexBit = 0;
} else {
validBufferIndexBit++;
}
result = serializable.get().serialize(buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
}
if (*size + validityMaskSize > maxSize) {
return SerializeIF::BUFFER_TOO_SHORT;
}
// copy validity buffer to end
std::memcpy(*buffer, validityPtr, validityMaskSize);
*size += validityMaskSize;
*buffer += validityMaskSize;
return result;
}
[[nodiscard]] ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize,
Endianness streamEndianness) const override {
if (serializeWithValidityBlob) {
return doSerializeWithValidityBlob(buffer, size, maxSize, streamEndianness);
}
ReturnValue_t result = returnvalue::OK;
for (auto &serializable : serializables) {
result = serializable.get().serialize(buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
}
return result;
}
[[nodiscard]] ReturnValue_t serialize(uint8_t *buffer, size_t &serSize, size_t maxSize,
Endianness streamEndianness) const override {
return SerializeIF::serialize(buffer, serSize, maxSize, streamEndianness);
}
void setChildrenValidity(bool valid) {
for (auto &serializable : serializables) {
serializable.get().setValid(true);
}
}
[[nodiscard]] size_t getSerializedSize() const override {
size_t size = 0;
for (auto &serializable : serializables) {
size += serializable.get().getSerializedSize();
}
if (serializeWithValidityBlob) {
size += std::ceil(static_cast<float>(serializables.size()) / 8.0);
}
return size;
}
ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size,
const Endianness streamEndianness) override {
ReturnValue_t result = returnvalue::OK;
for (auto &serializable : serializables) {
result = serializable.get().deSerialize(buffer, size, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
}
return result;
}
bool serializeWithValidityBlob = false;
private:
dp::structure_id_t sid;
std::vector<std::reference_wrapper<SerializableWithValidityIF>> serializables;
};
} // namespace hk

View File

@@ -0,0 +1,165 @@
#pragma once
#include <utility>
#include "Dataset.h"
#include "SerializableWithValidityIF.h"
#include "fsfw/serialize/SerializeAdapter.h"
namespace hk {
template <typename T>
class ListVariable : public SerializableWithValidityIF {
public:
template <typename... Args>
explicit ListVariable(Dataset& list, Args... args) : entry(std::forward<Args>(args)...) {
list.addSerializable(*this);
}
explicit ListVariable(Dataset& list) : entry() { list.addSerializable(*this); }
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override {
return SerializeAdapter::serialize(&entry, buffer, size, maxSize, streamEndianness);
}
[[nodiscard]] size_t getSerializedSize() const override {
return SerializeAdapter::getSerializedSize(&entry);
}
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
Endianness streamEndianness) override {
return SerializeAdapter::deSerialize(&entry, buffer, size, streamEndianness);
}
explicit operator T() { return entry; }
ListVariable& operator=(T newValue) {
entry = newValue;
return *this;
}
[[nodiscard]] bool isValid() const override { return valid; };
void setValid(bool _valid) override { this->valid = _valid; }
T* operator->() { return &entry; }
T get() const { return entry; }
bool valid = false;
T entry{};
};
template <typename T>
using LVar = ListVariable<T>;
using lvar_u8 = LVar<uint8_t>;
using lvar_u16 = LVar<uint16_t>;
using lvar_u32 = LVar<uint32_t>;
using lvar_u64 = LVar<uint64_t>;
using lvar_i8 = LVar<int8_t>;
using lvar_i16 = LVar<int16_t>;
using lvar_i32 = LVar<int32_t>;
using lvar_i64 = LVar<int64_t>;
using lvar_f32 = LVar<float>;
using lvar_f64 = LVar<double>;
template <typename T, size_t N>
class ListVector : public SerializableWithValidityIF {
public:
template <typename... Args>
explicit ListVector(Dataset& list, Args... args) : entry(std::forward<Args>(args)...) {
list.addSerializable(*this);
}
explicit ListVector(Dataset& list) : entry() { list.addSerializable(*this); }
const T* get() const { return entry; }
T* getMut() { return entry; }
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override {
ReturnValue_t result = returnvalue::OK;
for (size_t i = 0; i < N; i++) {
result = SerializeAdapter::serialize(entry + i, buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
}
return result;
}
[[nodiscard]] size_t getSerializedSize() const override {
return SerializeAdapter::getSerializedSize(entry) * N;
}
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
Endianness streamEndianness) override {
ReturnValue_t result = returnvalue::OK;
for (size_t i = 0; i < N; i++) {
result = SerializeAdapter::deSerialize(entry + i, buffer, size, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
}
return result;
}
explicit operator T() { return entry; }
ListVector& operator=(T newValue[N]) {
entry = newValue;
return *this;
}
// Array subscript operator for access
T& operator[](size_t index) {
// No exceptions, so user takes care of index validation..
return entry[index];
}
const T& operator[](size_t index) const { return entry[index]; }
// Conversion operator
explicit operator T*() { return entry; }
explicit operator const T*() const { return entry; }
// Iterators for range-based for loops and STL compatibility
T* begin() { return std::begin(entry); }
T* end() { return std::end(entry); }
const T* begin() const { return std::begin(entry); }
const T* end() const { return std::end(entry); }
const T* cbegin() const { return std::begin(entry); }
const T* cend() const { return std::end(entry); }
[[nodiscard]] bool isValid() const override { return valid; };
void setValid(bool _valid) override { this->valid = _valid; }
// Additional utility methods
[[nodiscard]] constexpr size_t size() const { return N; }
bool valid = false;
T entry[N];
};
template <typename T, size_t N>
using LVec = ListVector<T, N>;
template <size_t N>
using lvec_u8 = LVec<uint8_t, N>;
template <size_t N>
using lvec_u16 = LVec<uint16_t, N>;
template <size_t N>
using lvec_u32 = LVec<uint32_t, N>;
template <size_t N>
using lvec_i8 = LVec<int8_t, N>;
template <size_t N>
using lvec_i16 = LVec<int16_t, N>;
template <size_t N>
using lvec_i32 = LVec<int32_t, N>;
template <size_t N>
using lvec_f32 = LVec<float, N>;
template <size_t N>
using lvec_f64 = LVec<double, N>;
} // namespace hk

View File

@@ -17,7 +17,7 @@ PeriodicHelper::PeriodicHelper(GeneratesPeriodicHkIF* owner, MessageQueueIF* que
MessageQueueId_t hkDestQueue)
: hkDestinationId(hkDestQueue) {
if (owner == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "LocalDataPoolManager", returnvalue::FAILED,
printWarningOrError(sif::OutputTypes::OUT_WARNING, "PeriodicHkHelper", returnvalue::FAILED,
"Invalid supplied owner");
return;
}
@@ -200,11 +200,10 @@ void PeriodicHelper::performPeriodicHkGeneration(SetSpecification& setSpec, time
if (result != returnvalue::OK) {
// Configuration error
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalDataPoolManager::performPeriodicHkOperation: HK generation failed."
sif::warning << "hk::PeriodicHelper::performPeriodicHkOperation: HK generation failed."
<< std::endl;
#else
sif::printWarning(
"LocalDataPoolManager::performPeriodicHkOperation: HK generation failed.\n");
sif::printWarning("hk::PeriodicHelper::performPeriodicHkOperation: HK generation failed.\n");
#endif
return;
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include "fsfw/serialize/SerializeIF.h"
class SerializableWithValidityIF : public SerializeIF {
public:
[[nodiscard]] virtual bool isValid() const = 0;
virtual void setValid(bool valid) = 0;
};

View File

@@ -10,15 +10,16 @@ class InternalErrorDataset : public dp::StaticSharedSet<4> {
public:
static constexpr uint8_t ERROR_SET_ID = 0;
InternalErrorDataset(dp::SharedPool& sharedPool) : StaticSharedSet(sharedPool, ERROR_SET_ID) {}
InternalErrorDataset(dp::SharedPool& sharedPool)
: StaticSharedSet(sharedPool, ERROR_SET_ID, false) {}
InternalErrorDataset(object_id_t objectId)
: StaticSharedSet(dp::structure_id_t(objectId, ERROR_SET_ID)) {}
: StaticSharedSet(dp::structure_id_t(objectId, ERROR_SET_ID), false) {}
dp::var_t<uint8_t> valid = dp::var_t<uint8_t>(sid.objectId, VALID, this);
dp::var_t<uint32_t> tmHits = dp::var_t<uint32_t>(sid.objectId, TM_HITS, this);
dp::var_t<uint32_t> queueHits = dp::var_t<uint32_t>(sid.objectId, QUEUE_HITS, this);
dp::var_t<uint32_t> storeHits = dp::var_t<uint32_t>(sid.objectId, STORE_HITS, this);
dp::var_t<uint8_t> valid = dp::var_t<uint8_t>(sid.objectId, VALID, this);
};
#endif /* FSFW_INTERNALERROR_INTERNALERRORDATASET_H_ */

View File

@@ -59,6 +59,9 @@ class MonitorBase : public MonitorReporter<T> {
if (result != returnvalue::OK) {
return result;
}
if (not poolVariable.isValid()) {
return MonitoringIF::INVALID;
}
*sample = poolVariable.value;
return returnvalue::OK;
}

View File

@@ -5,6 +5,7 @@
#include "FreeRTOS.h"
#include "fsfw/globalfunctions/timevalOperations.h"
#include "fsfw/serviceinterface/ServiceInterfacePrinter.h"
#include "fsfw/osal/freertos/Timekeeper.h"
#include "task.h"
@@ -47,8 +48,8 @@ ReturnValue_t Clock::getClock(timeval* time) {
}
ReturnValue_t Clock::getClockMonotonic(timeval* time) {
// TODO: I don't actually know if the timekeeper is monotonic..
return getClock_timeval(time);
*time = Timekeeper::instance()->getMonotonicClockOffset() + getUptime();
return returnvalue::OK;
}
ReturnValue_t Clock::getUptime(timeval* uptime) {
@@ -58,7 +59,7 @@ ReturnValue_t Clock::getUptime(timeval* uptime) {
}
timeval Clock::getUptime() {
TickType_t ticksSinceStart = xTaskGetTickCount();
TickType_t ticksSinceStart = Timekeeper::instance()->getTicks();
return Timekeeper::ticksToTimeval(ticksSinceStart);
}

View File

@@ -17,7 +17,13 @@ Timekeeper* Timekeeper::instance() {
return myinstance;
}
void Timekeeper::setOffset(const timeval& offset) { this->offset = offset; }
void Timekeeper::setOffset(const timeval& offset) {
if (not monotonicClockInitialized) {
this->monotonicClockOffset = offset;
monotonicClockInitialized = true;
}
this->offset = offset;
}
timeval Timekeeper::ticksToTimeval(TickType_t ticks) {
timeval uptime;
@@ -33,3 +39,7 @@ timeval Timekeeper::ticksToTimeval(TickType_t ticks) {
}
TickType_t Timekeeper::getTicks() { return xTaskGetTickCount(); }
const timeval Timekeeper::getMonotonicClockOffset() const {
return monotonicClockOffset;
}

View File

@@ -18,9 +18,14 @@ class Timekeeper {
Timekeeper();
timeval offset;
// Set when offset is initialized the first time
timeval monotonicClockOffset;
bool monotonicClockInitialized = false;
static Timekeeper* myinstance;
void setMonotonicClockOffset(const timeval& monotonicClockOffset);
public:
static Timekeeper* instance();
virtual ~Timekeeper();
@@ -34,6 +39,7 @@ class Timekeeper {
const timeval& getOffset() const;
void setOffset(const timeval& offset);
const timeval getMonotonicClockOffset() const;
};
#endif /* FRAMEWORK_OSAL_FREERTOS_TIMEKEEPER_H_ */

View File

@@ -60,14 +60,14 @@ ReturnValue_t Fuse::check() {
set.read();
if (!healthHelper.healthTable->isHealthy(getObjectId())) {
setAllMonitorsToUnchecked();
set.setValid = false;
set.setChildrenValidity(false);
return set.commit();
}
ReturnValue_t result = returnvalue::OK;
checkFuseState();
calculateFusePower();
// Check if power is valid and if fuse state is off or invalid.
if (!set.setValid.value || (set.state == 0)) {
if (!set.power.isValid() || (set.state == 0) || !set.state.isValid()) {
result = powerMonitor.setToInvalid();
} else {
float lowLimit = 0.0;
@@ -87,7 +87,7 @@ ReturnValue_t Fuse::check() {
ReturnValue_t Fuse::serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const {
ReturnValue_t result = returnvalue::FAILED;
for (DeviceList::const_iterator iter = devices.begin(); iter != devices.end(); iter++) {
for (auto iter = devices.begin(); iter != devices.end(); iter++) {
result = (*iter)->serialize(buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
return result;
@@ -98,7 +98,7 @@ ReturnValue_t Fuse::serialize(uint8_t** buffer, size_t* size, size_t maxSize,
size_t Fuse::getSerializedSize() const {
uint32_t size = 0;
for (DeviceList::const_iterator iter = devices.begin(); iter != devices.end(); iter++) {
for (auto iter = devices.begin(); iter != devices.end(); iter++) {
size += (*iter)->getSerializedSize();
}
return size;
@@ -119,12 +119,12 @@ uint8_t Fuse::getFuseId() const { return fuseId; }
void Fuse::calculateFusePower() {
ReturnValue_t result1 = currentLimit.check();
if (result1 != returnvalue::OK || !(set.setValid.value)) {
set.powerValid = false;
if (result1 != returnvalue::OK || !(set.voltage.isValid())) {
set.power.setValid(false);
}
// Calculate fuse power.
set.power = set.current.value * set.voltage.value;
set.powerValid = true;
set.power.setValid(true);
}
ReturnValue_t Fuse::performOperation(uint8_t opCode) {
@@ -169,7 +169,7 @@ void Fuse::checkCommandQueue() {
}
void Fuse::checkFuseState() {
if (!set.setValid.value) {
if (!set.state.isValid()) {
oldFuseState = 0;
return;
}
@@ -182,7 +182,7 @@ void Fuse::checkFuseState() {
}
float Fuse::getPower() {
if (set.powerValid.value) {
if (set.power.isValid()) {
return set.power.value;
} else {
return 0.0;
@@ -191,8 +191,7 @@ float Fuse::getPower() {
void Fuse::setDataPoolEntriesInvalid() {
set.read();
set.setValid = false;
set.powerValid = false;
set.setChildrenValidity(false);
set.commit();
}
@@ -220,7 +219,7 @@ bool Fuse::areSwitchesOfComponentOn(DeviceList::iterator iter) {
return true;
}
bool Fuse::isPowerValid() { return set.powerValid.value; }
bool Fuse::isPowerValid() { return set.power.isValid(); }
ReturnValue_t Fuse::setHealth(HealthState health) {
healthHelper.setHealth(health);

View File

@@ -29,16 +29,14 @@ class FuseSet : public datapool::StaticSharedSet<6> {
public:
static constexpr uint8_t FUSE_SET_ID = 0;
FuseSet(dp::SharedPool &sharedPool) : StaticSharedSet(sharedPool, FUSE_SET_ID) {}
FuseSet(dp::SharedPool &sharedPool) : StaticSharedSet(sharedPool, FUSE_SET_ID, true) {}
FuseSet(object_id_t objectId) : StaticSharedSet(dp::sid_t(objectId, FUSE_SET_ID)) {}
FuseSet(object_id_t objectId) : StaticSharedSet(dp::sid_t(objectId, FUSE_SET_ID), true) {}
dp::f32_t voltage = dp::f32_t(sid.objectId, FusePoolId::VOLTAGE, this);
dp::f32_t current = dp::f32_t(sid.objectId, FusePoolId::CURRENT, this);
dp::u8_t state = dp::u8_t(sid.objectId, FusePoolId::STATE, this);
dp::f32_t power = dp::f32_t(sid.objectId, FusePoolId::POWER, this);
dp::u8_t powerValid = dp::u8_t(sid.objectId, FusePoolId::POWER_VALID, this);
dp::u8_t setValid = dp::u8_t(sid.objectId, FusePoolId::SET_VALID, this);
};
class Fuse : public SystemObject,

View File

@@ -21,10 +21,11 @@ class PowerSensorSet : public dp::StaticSharedSet<6> {
public:
static constexpr uint8_t POWER_SENSOR_SET_ID = 0;
PowerSensorSet(dp::SharedPool &sharedPool) : StaticSharedSet(sharedPool, POWER_SENSOR_SET_ID) {}
PowerSensorSet(dp::SharedPool &sharedPool)
: StaticSharedSet(sharedPool, POWER_SENSOR_SET_ID, true) {}
PowerSensorSet(object_id_t objectId)
: StaticSharedSet(dp::sid_t(objectId, POWER_SENSOR_SET_ID)) {}
: StaticSharedSet(dp::sid_t(objectId, POWER_SENSOR_SET_ID), true) {}
dp::f32_t current{sid.objectId, PowerSensorPoolId::CURRENT, this};
dp::f32_t voltage{sid.objectId, PowerSensorPoolId::VOLTAGE, this};

View File

@@ -44,7 +44,7 @@ ReturnValue_t Service5EventReporting::performService() {
}
ReturnValue_t Service5EventReporting::generateEventReport(EventMessage message) {
EventReport report(message.getEventId(), message.getReporter(), message.getParameter1(),
EventReport report(message.getEvent(), message.getReporter(), message.getParameter1(),
message.getParameter2());
storeHelper.preparePacket(psbParams.serviceId, message.getSeverity(), tmHelper.sendCounter);
storeHelper.setSourceDataSerializable(report);

View File

@@ -17,17 +17,13 @@
*/
class EventReport : public SerializeIF { //!< [EXPORT] : [SUBSERVICE] 1, 2, 3, 4
public:
EventReport(EventId_t reportId_, object_id_t objectId_, uint32_t parameter1_,
uint32_t parameter2_)
: reportId(reportId_),
objectId(objectId_),
parameter1(parameter1_),
parameter2(parameter2_) {}
EventReport(Event event_, object_id_t objectId_, uint32_t parameter1_, uint32_t parameter2_)
: event(event_), objectId(objectId_), parameter1(parameter1_), parameter2(parameter2_) {}
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override {
ReturnValue_t result =
SerializeAdapter::serialize(&reportId, buffer, size, maxSize, streamEndianness);
SerializeAdapter::serialize(&event, buffer, size, maxSize, streamEndianness);
if (result != returnvalue::OK) {
return result;
}
@@ -48,7 +44,7 @@ class EventReport : public SerializeIF { //!< [EXPORT] : [SUBSERVICE] 1, 2, 3,
size_t getSerializedSize() const override {
uint32_t size = 0;
size += SerializeAdapter::getSerializedSize(&reportId);
size += SerializeAdapter::getSerializedSize(&event);
size += SerializeAdapter::getSerializedSize(&objectId);
size += SerializeAdapter::getSerializedSize(&parameter1);
size += SerializeAdapter::getSerializedSize(&parameter2);
@@ -61,7 +57,7 @@ class EventReport : public SerializeIF { //!< [EXPORT] : [SUBSERVICE] 1, 2, 3,
}
private:
EventId_t reportId;
Event event;
object_id_t objectId;
uint32_t parameter1;
uint32_t parameter2;

View File

@@ -68,6 +68,9 @@ class ListVector : public SerializeIF {
}
explicit ListVector(List& list) : entry() { list.addSerializable(*this); }
const T* get() const { return entry; }
T* getMut() { return entry; }
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
Endianness streamEndianness) const override {
ReturnValue_t result = returnvalue::OK;

View File

@@ -53,17 +53,17 @@ void fsfwPrint(sif::PrintLevel printType, const char *fmt, va_list arg) {
#endif
if (printType == sif::PrintLevel::INFO_LEVEL) {
len += sprintf(bufferPosition + len, "INFO");
len += sprintf(bufferPosition + len, "INFO ");
}
if (printType == sif::PrintLevel::DEBUG_LEVEL) {
len += sprintf(bufferPosition + len, "DEBUG");
len += sprintf(bufferPosition + len, "DEBUG ");
}
if (printType == sif::PrintLevel::WARNING_LEVEL) {
len += sprintf(bufferPosition + len, "WARNING");
}
if (printType == sif::PrintLevel::ERROR_LEVEL) {
len += sprintf(bufferPosition + len, "ERROR");
len += sprintf(bufferPosition + len, "ERROR ");
}
#if FSFW_COLORED_OUTPUT == 1
@@ -75,7 +75,7 @@ void fsfwPrint(sif::PrintLevel printType, const char *fmt, va_list arg) {
/*
* Log current time to terminal if desired.
*/
len += sprintf(bufferPosition + len, " | %lu:%02lu:%02lu.%03lu | ", (unsigned long)now.hour,
len += sprintf(bufferPosition + len, " | %02lu:%02lu:%02lu.%03lu | ", (unsigned long)now.hour,
(unsigned long)now.minute, (unsigned long)now.second,
(unsigned long)now.usecond / 1000);

View File

@@ -79,22 +79,36 @@ void SubsystemBase::executeTable(HybridIterator<ModeListEntry> tableIter, Submod
}
if (healthHelper.healthTable->hasHealth(object)) {
if (healthHelper.healthTable->isFaulty(object)) {
ModeMessage::setModeMessage(&command, ModeMessage::CMD_MODE_COMMAND, HasModesIF::MODE_OFF,
SUBMODE_NONE);
} else {
if (modeHelper.isForced()) {
ModeMessage::setModeMessage(&command, ModeMessage::CMD_MODE_COMMAND_FORCED,
tableIter.value->getMode(), submodeToCommand);
} else {
if (healthHelper.healthTable->isCommandable(object)) {
switch (healthHelper.healthTable->getHealth(object)) {
case NEEDS_RECOVERY:
case FAULTY:
case PERMANENT_FAULTY:
ModeMessage::setModeMessage(&command, ModeMessage::CMD_MODE_COMMAND, HasModesIF::MODE_OFF,
SUBMODE_NONE);
break;
case HEALTHY:
if (modeHelper.isForced()) {
ModeMessage::setModeMessage(&command, ModeMessage::CMD_MODE_COMMAND_FORCED,
tableIter.value->getMode(), submodeToCommand);
} else {
ModeMessage::setModeMessage(&command, ModeMessage::CMD_MODE_COMMAND,
tableIter.value->getMode(), submodeToCommand);
}
break;
case EXTERNAL_CONTROL:
if (modeHelper.isForced()) {
ModeMessage::setModeMessage(&command, ModeMessage::CMD_MODE_COMMAND_FORCED,
tableIter.value->getMode(), submodeToCommand);
} else {
continue;
}
}
break;
default:
// This never happens
break;
}
} else {
ModeMessage::setModeMessage(&command, ModeMessage::CMD_MODE_COMMAND,
tableIter.value->getMode(), submodeToCommand);

View File

@@ -142,7 +142,7 @@ class TemperatureSensor : public AbstractTemperatureSensor {
deltaTime = (uptime.tv_sec + uptime.tv_usec / 1000000.) -
(uptimeOfOldTemperature.tv_sec + uptimeOfOldTemperature.tv_usec / 1000000.);
deltaTemp = oldTemperature - outputTemperature;
deltaTemp = oldTemperature - outputTemperature.value;
if (deltaTemp < 0) {
deltaTemp = -deltaTemp;
}
@@ -160,13 +160,13 @@ class TemperatureSensor : public AbstractTemperatureSensor {
outputTemperature.setValid(PoolVariableIF::INVALID);
outputTemperature = thermal::INVALID_TEMPERATURE;
} else {
oldTemperature = outputTemperature;
oldTemperature = outputTemperature.value;
uptimeOfOldTemperature = uptime;
}
}
public:
float getTemperature() { return outputTemperature; }
float getTemperature() { return outputTemperature.value; }
bool isValid() { return outputTemperature.isValid(); }

View File

@@ -192,6 +192,8 @@ class Clock {
static MutexIF *timeMutex;
static uint16_t leapSeconds;
static bool leapSecondsSet;
static bool monotonicClockInitialized;
static timeval monotonicClockOffset;
};
#endif /* FSFW_TIMEMANAGER_CLOCK_H_ */

View File

@@ -1,3 +1,4 @@
target_sources(
${FSFW_TEST_TGT} PRIVATE testLocalPoolVariable.cpp testLocalPoolVector.cpp
testDataSet.cpp testPeriodicHkHelper.cpp)
${FSFW_TEST_TGT}
PRIVATE testLocalPoolVariable.cpp testLocalPoolVector.cpp testSharedSet.cpp
testPeriodicHkHelper.cpp testDataset.cpp)

View File

@@ -0,0 +1,85 @@
#include <fsfw/housekeeping/Dataset.h>
#include <fsfw/housekeeping/DatasetElement.h>
#include <fsfw/serialize/SerializeElement.h>
#include <catch2/catch_test_macros.hpp>
constexpr auto TEST_ID = dp::structure_id_t(1, 2);
class TestDatasetSmall : public hk::Dataset {
public:
TestDatasetSmall() : hk::Dataset(TEST_ID, false) {}
hk::lvar_u8 test0{*this};
hk::lvar_u32 test1{*this};
};
class TestDatasetLarger : public hk::Dataset {
public:
TestDatasetLarger() : hk::Dataset(TEST_ID, false) {}
hk::lvar_u8 test0{*this};
hk::lvar_u32 test1{*this};
hk::lvar_u16 test2{*this};
hk::lvar_i32 test3{*this};
hk::lvar_f32 test4{*this};
hk::lvar_f64 test5{*this};
hk::lvar_u8 test6{*this};
hk::lvar_i16 test7{*this};
hk::lvec_u16<2> test8{*this};
};
TEST_CASE("Pool Dataset Test", "[datapool]") {
TestDatasetSmall dataset;
CHECK(dataset.getStructureId() == TEST_ID);
CHECK(!dataset.test0.isValid());
dataset.test0.setValid(true);
CHECK(dataset.test0.isValid());
SECTION("Pool Dataset Serialization Test 1") {
uint8_t buf[64]{};
dataset.test0 = 55;
dataset.test1 = 502392;
size_t serLen = 0;
CHECK(dataset.serialize(buf, serLen, sizeof(buf), SerializeIF::Endianness::NETWORK) ==
returnvalue::OK);
CHECK(buf[0] == 55);
CHECK(serLen == 5);
uint32_t readBack = 0;
size_t dummy = 0;
CHECK(SerializeAdapter::deSerialize(&readBack, buf + 1, &dummy,
SerializeIF::Endianness::NETWORK) == returnvalue::OK);
CHECK(readBack == 502392);
CHECK(buf[5] == 0);
}
SECTION("Pool Dataset Serialization With Validity") {
uint8_t buf[64]{};
dataset.test0 = 55;
dataset.test1 = 502392;
dataset.test0.setValid(true);
dataset.test1.setValid(true);
size_t serLen = 0;
uint8_t* dataPtr = buf;
dataset.serializeWithValidityBlob = true;
CHECK(dataset.getSerializedSize() == 6);
CHECK(dataset.serialize(&dataPtr, &serLen, sizeof(buf), SerializeIF::Endianness::NETWORK) ==
returnvalue::OK);
CHECK(buf[5] == 0b11000000);
}
SECTION("Larger Pool Dataset Serialization With Validity") {
uint8_t buf[64]{};
TestDatasetLarger datasetLarge;
datasetLarge.setChildrenValidity(true);
size_t serLen = 0;
uint8_t* dataPtr = buf;
datasetLarge.serializeWithValidityBlob = true;
CHECK(datasetLarge.serialize(&dataPtr, &serLen, sizeof(buf),
SerializeIF::Endianness::NETWORK) == returnvalue::OK);
CHECK(serLen == 32);
CHECK(buf[30] == 0b11111111);
CHECK(buf[31] == 0b10000000);
CHECK(buf[32] == 0);
}
}

View File

@@ -14,7 +14,7 @@
using namespace returnvalue;
using namespace lpool;
TEST_CASE("DataSetTest", "[DataSetTest]") {
TEST_CASE("DataSetTest", "[datapool]") {
auto queue = MessageQueueMock(1, MessageQueueIF::NO_QUEUE);
TestPoolOwner poolOwner(queue, objects::TEST_LOCAL_POOL_OWNER_BASE);
poolOwner.initialize();
@@ -23,6 +23,7 @@ TEST_CASE("DataSetTest", "[DataSetTest]") {
SECTION("BasicTest") {
/* Test some basic functions */
CHECK(localSet.getReportingEnabled() == false);
CHECK(localSet.getSerializedSize() == 11);
CHECK(localSet.getLocalPoolIdsSerializedSize() == 3 * sizeof(dp::id_t));
CHECK(localSet.getStructureId() == lpool::testSid1);
CHECK(localSet.getCreatorObjectId() == objects::TEST_LOCAL_POOL_OWNER_BASE);
@@ -57,17 +58,23 @@ TEST_CASE("DataSetTest", "[DataSetTest]") {
PoolReadGuard readHelper(&localSet);
REQUIRE(readHelper.getReadResult() == returnvalue::OK);
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(!localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0));
CHECK(!localSet.localPoolVarFloat.isValid());
CHECK(localSet.localPoolUint16Vec.value[0] == 0);
CHECK(localSet.localPoolUint16Vec.value[1] == 0);
CHECK(localSet.localPoolUint16Vec.value[2] == 0);
CHECK(!localSet.localPoolUint16Vec.isValid());
// Now set new values, commit should be done by read helper automatically
localSet.localPoolVarUint8 = 232;
localSet.localPoolVarUint8.setValid(true);
localSet.localPoolVarFloat = -2324.322;
localSet.localPoolVarFloat.setValid(true);
localSet.localPoolUint16Vec.value[0] = 232;
localSet.localPoolUint16Vec.value[1] = 23923;
localSet.localPoolUint16Vec.value[2] = 1;
localSet.localPoolUint16Vec.setValid(true);
}
// Zero out some values for next test
@@ -85,10 +92,13 @@ TEST_CASE("DataSetTest", "[DataSetTest]") {
PoolReadGuard readHelper(&localSet);
REQUIRE(readHelper.getReadResult() == returnvalue::OK);
CHECK(localSet.localPoolVarUint8.value == 232);
CHECK(localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(-2324.322));
CHECK(localSet.localPoolVarFloat.isValid());
CHECK(localSet.localPoolUint16Vec.value[0] == 232);
CHECK(localSet.localPoolUint16Vec.value[1] == 23923);
CHECK(localSet.localPoolUint16Vec.value[2] == 1);
CHECK(localSet.localPoolUint16Vec.isValid());
// Now we serialize these values into a buffer without the validity buffer
maxSize = localSet.getSerializedSize();
@@ -129,7 +139,7 @@ TEST_CASE("DataSetTest", "[DataSetTest]") {
SECTION("SharedDataSet") {
object_id_t sharedSetId = objects::SHARED_SET_ID;
SharedSet sharedSet(poolOwner.sharedPool, sharedSetId, 5);
SharedSet sharedSet(poolOwner.sharedPool, sharedSetId, 5, false);
localSet.localPoolVarUint8.setReadWriteMode(pool_rwm_t::VAR_WRITE);
localSet.localPoolUint16Vec.setReadWriteMode(pool_rwm_t::VAR_WRITE);
CHECK(sharedSet.registerVariable(&localSet.localPoolVarUint8) == returnvalue::OK);
@@ -145,4 +155,24 @@ TEST_CASE("DataSetTest", "[DataSetTest]") {
CHECK(sharedSet.commit() == returnvalue::OK);
}
}
SECTION("Serialize with Validity Blob") {
localSet.updateValidityBlobSerialization(true);
CHECK(!localSet.localPoolVarUint8.isValid());
CHECK(!localSet.localPoolUint16Vec.isValid());
CHECK(!localSet.localPoolVarFloat.isValid());
localSet.setChildrenValidity(true);
CHECK(localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolUint16Vec.isValid());
CHECK(localSet.localPoolVarFloat.isValid());
size_t serSize = 0;
// Already reserve additional space for validity buffer, will be needed later
uint8_t buffer[128]{};
uint8_t* buffPtr = buffer;
CHECK(localSet.getSerializedSize() == 12);
CHECK(localSet.serialize(&buffPtr, &serSize, sizeof(buffer),
SerializeIF::Endianness::MACHINE) == returnvalue::OK);
CHECK(serSize == 12);
CHECK(buffer[11] == 0b11100000);
}
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include <fsfw/housekeeping/GeneratesPeriodicHkIF.h>
#include <fsfw/housekeeping/PeriodicHkHelper.h>
#include <fsfw/objectmanager/SystemObject.h>
#include "poolDefinitions.h"

View File

@@ -45,9 +45,10 @@ class Dataset : public List {
class StaticTestDataset : public StaticSharedSet<3> {
public:
StaticTestDataset() : StaticSharedSet(lpool::testSid1) {}
StaticTestDataset() : StaticSharedSet(lpool::testSid1, false) {}
StaticTestDataset(SharedPool& sharedPool, uint32_t setId) : StaticSharedSet(sharedPool, setId) {}
StaticTestDataset(SharedPool& sharedPool, uint32_t setId)
: StaticSharedSet(sharedPool, setId, false) {}
u8_t localPoolVarUint8{lpool::uint8VarGpid, this};
f32_t localPoolVarFloat{lpool::floatVarGpid, this};
@@ -58,10 +59,10 @@ class StaticTestDataset : public StaticSharedSet<3> {
class TestDataset : public SharedSet {
public:
TestDataset() : SharedSet(lpool::testSid2, lpool::dataSetMaxVariables) {}
TestDataset() : SharedSet(lpool::testSid2, lpool::dataSetMaxVariables, false) {}
TestDataset(SharedPool& sharedPool, uint32_t setId)
: SharedSet(sharedPool, setId, lpool::dataSetMaxVariables) {}
: SharedSet(sharedPool, setId, lpool::dataSetMaxVariables, false) {}
u8_t localPoolVarUint8{lpool::uint8VarGpid, this};
f32_t localPoolVarFloat{lpool::floatVarGpid, this};