Merge branch 'development' of https://egit.irs.uni-stuttgart.de/eive/fsfw into development

This commit is contained in:
Jakob Meier 2021-03-19 15:38:06 +01:00
commit fa108f0a3b
54 changed files with 2130 additions and 1532 deletions

View File

@ -22,7 +22,9 @@ a C file without issues
### Local Pool ### Local Pool
- 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 - 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
### Parameter Service ### Parameter Service
@ -40,7 +42,8 @@ important use-case)
### File System Interface ### 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) - 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)
### Internal Error Reporter ### Internal Error Reporter
@ -52,7 +55,8 @@ ID for now.
### Device Handler Base ### Device Handler Base
- There is an additional `PERFORM_OPERATION` step for the device handler base. It is important - 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 that DHB users adapt their polling sequence tables to perform this step. This steps allows for
a clear distinction between operation and communication steps
- setNormalDatapoolEntriesInvalid is not an abstract method and a default implementation was provided - setNormalDatapoolEntriesInvalid is not an abstract method and a default implementation was provided
- getTransitionDelayMs is now an abstract method - getTransitionDelayMs is now an abstract method
@ -69,7 +73,8 @@ now
### Commanding Service Base ### Commanding Service Base
- CSB uses the new fsfwconfig::FSFW_CSB_FIFO_DEPTH variable to determine the FIFO depth for each CSB instance. This variable has to be set in the FSFWConfig.h file - CSB uses the new fsfwconfig::FSFW_CSB_FIFO_DEPTH variable to determine the FIFO depth for each
CSB instance. This variable has to be set in the FSFWConfig.h file
### Service Interface ### Service Interface
@ -82,6 +87,12 @@ now
For mission code, developers need to replace sif:: calls by the printf counterparts, but only if the CPP stream are excluded. For mission code, developers need to replace sif:: calls by the printf counterparts, but only if the CPP stream are excluded.
If this is not the case, everything should work as usual. If this is not the case, everything should work as usual.
### ActionHelper and ActionMessage
- ActionHelper finish function and ActionMessage::setCompletionReply now expects explicit
information whether to report a success or failure message instead of deriving it implicitely
from returnvalue
### PUS Parameter Service 20 ### PUS Parameter Service 20
Added PUS parameter service 20 (only custom subservices available). Added PUS parameter service 20 (only custom subservices available).

View File

@ -43,10 +43,10 @@ void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo,
queueToUse->sendMessage(reportTo, &reply); queueToUse->sendMessage(reportTo, &reply);
} }
void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId, void ActionHelper::finish(bool success, MessageQueueId_t reportTo, ActionId_t commandId,
ReturnValue_t result) { ReturnValue_t result) {
CommandMessage reply; CommandMessage reply;
ActionMessage::setCompletionReply(&reply, commandId, result); ActionMessage::setCompletionReply(&reply, commandId, success, result);
queueToUse->sendMessage(reportTo, &reply); queueToUse->sendMessage(reportTo, &reply);
} }
@ -69,7 +69,7 @@ void ActionHelper::prepareExecution(MessageQueueId_t commandedBy,
ipcStore->deleteData(dataAddress); ipcStore->deleteData(dataAddress);
if(result == HasActionsIF::EXECUTION_FINISHED) { if(result == HasActionsIF::EXECUTION_FINISHED) {
CommandMessage reply; CommandMessage reply;
ActionMessage::setCompletionReply(&reply, actionId, result); ActionMessage::setCompletionReply(&reply, actionId, true, result);
queueToUse->sendMessage(commandedBy, &reply); queueToUse->sendMessage(commandedBy, &reply);
} }
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {

View File

@ -62,12 +62,12 @@ public:
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
/** /**
* Function to be called by the owner to send a action completion message * Function to be called by the owner to send a action completion message
* * @param success Specify whether action was completed successfully or not.
* @param reportTo MessageQueueId_t to report the action completion message to * @param reportTo MessageQueueId_t to report the action completion message to
* @param commandId ID of the executed command * @param commandId ID of the executed command
* @param result Result of the execution * @param result Result of the execution
*/ */
void finish(MessageQueueId_t reportTo, ActionId_t commandId, void finish(bool success, MessageQueueId_t reportTo, ActionId_t commandId,
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
/** /**
* Function to be called by the owner if an action does report data. * Function to be called by the owner if an action does report data.

View File

@ -54,10 +54,11 @@ void ActionMessage::setDataReply(CommandMessage* message, ActionId_t actionId,
} }
void ActionMessage::setCompletionReply(CommandMessage* message, void ActionMessage::setCompletionReply(CommandMessage* message,
ActionId_t fid, ReturnValue_t result) { ActionId_t fid, bool success, ReturnValue_t result) {
if (result == HasReturnvaluesIF::RETURN_OK or result == HasActionsIF::EXECUTION_FINISHED) { if (success) {
message->setCommand(COMPLETION_SUCCESS); message->setCommand(COMPLETION_SUCCESS);
} else { }
else {
message->setCommand(COMPLETION_FAILED); message->setCommand(COMPLETION_FAILED);
} }
message->setParameter(fid); message->setParameter(fid);

View File

@ -24,11 +24,14 @@ public:
static const Command_t DATA_REPLY = MAKE_COMMAND_ID(4); static const Command_t DATA_REPLY = MAKE_COMMAND_ID(4);
static const Command_t COMPLETION_SUCCESS = MAKE_COMMAND_ID(5); static const Command_t COMPLETION_SUCCESS = MAKE_COMMAND_ID(5);
static const Command_t COMPLETION_FAILED = MAKE_COMMAND_ID(6); static const Command_t COMPLETION_FAILED = MAKE_COMMAND_ID(6);
virtual ~ActionMessage(); virtual ~ActionMessage();
static void setCommand(CommandMessage* message, ActionId_t fid, static void setCommand(CommandMessage* message, ActionId_t fid,
store_address_t parameters); store_address_t parameters);
static ActionId_t getActionId(const CommandMessage* message ); static ActionId_t getActionId(const CommandMessage* message );
static store_address_t getStoreId(const CommandMessage* message); static store_address_t getStoreId(const CommandMessage* message);
static void setStepReply(CommandMessage* message, ActionId_t fid, static void setStepReply(CommandMessage* message, ActionId_t fid,
uint8_t step, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); uint8_t step, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
static uint8_t getStep(const CommandMessage* message ); static uint8_t getStep(const CommandMessage* message );
@ -36,7 +39,8 @@ public:
static void setDataReply(CommandMessage* message, ActionId_t actionId, static void setDataReply(CommandMessage* message, ActionId_t actionId,
store_address_t data); store_address_t data);
static void setCompletionReply(CommandMessage* message, ActionId_t fid, static void setCompletionReply(CommandMessage* message, ActionId_t fid,
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); bool success, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
static void clear(CommandMessage* message); static void clear(CommandMessage* message);
}; };

View File

@ -63,7 +63,7 @@ void SimpleActionHelper::prepareExecution(MessageQueueId_t commandedBy,
break; break;
case HasActionsIF::EXECUTION_FINISHED: case HasActionsIF::EXECUTION_FINISHED:
ActionMessage::setCompletionReply(&reply, actionId, ActionMessage::setCompletionReply(&reply, actionId,
HasReturnvaluesIF::RETURN_OK); true, HasReturnvaluesIF::RETURN_OK);
queueToUse->sendMessage(commandedBy, &reply); queueToUse->sendMessage(commandedBy, &reply);
break; break;
default: default:

View File

@ -13,7 +13,7 @@ ExtendedControllerBase::~ExtendedControllerBase() {
ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId, ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId,
MessageQueueId_t commandedBy, const uint8_t *data, size_t size) { MessageQueueId_t commandedBy, const uint8_t *data, size_t size) {
// needs to be overriden and implemented by child class. /* Needs to be overriden and implemented by child class. */
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
@ -21,7 +21,7 @@ ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId,
ReturnValue_t ExtendedControllerBase::initializeLocalDataPool( ReturnValue_t ExtendedControllerBase::initializeLocalDataPool(
localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) {
// needs to be overriden and implemented by child class. /* Needs to be overriden and implemented by child class. */
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
@ -96,8 +96,10 @@ ReturnValue_t ExtendedControllerBase::initializeAfterTaskCreation() {
ReturnValue_t ExtendedControllerBase::performOperation(uint8_t opCode) { ReturnValue_t ExtendedControllerBase::performOperation(uint8_t opCode) {
handleQueue(); handleQueue();
poolManager.performHkOperation();
performControlOperation(); performControlOperation();
/* We do this after performing control operation because variables will be set changed
in this function. */
poolManager.performHkOperation();
return RETURN_OK; return RETURN_OK;
} }

View File

@ -18,15 +18,13 @@ class PoolVariableIF;
class DataSetIF { class DataSetIF {
public: public:
static constexpr uint8_t INTERFACE_ID = CLASS_ID::DATA_SET_CLASS; static constexpr uint8_t INTERFACE_ID = CLASS_ID::DATA_SET_CLASS;
static constexpr ReturnValue_t INVALID_PARAMETER_DEFINITION = static constexpr ReturnValue_t INVALID_PARAMETER_DEFINITION = MAKE_RETURN_CODE(1);
MAKE_RETURN_CODE( 0x01 ); static constexpr ReturnValue_t SET_WAS_ALREADY_READ = MAKE_RETURN_CODE(2);
static constexpr ReturnValue_t SET_WAS_ALREADY_READ = MAKE_RETURN_CODE( 0x02 ); static constexpr ReturnValue_t COMMITING_WITHOUT_READING = MAKE_RETURN_CODE(3);
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_UNINITIALISED = MAKE_RETURN_CODE(4);
static constexpr ReturnValue_t DATA_SET_FULL = MAKE_RETURN_CODE( 0x05 ); static constexpr ReturnValue_t DATA_SET_FULL = MAKE_RETURN_CODE(5);
static constexpr ReturnValue_t POOL_VAR_NULL = MAKE_RETURN_CODE( 0x06 ); static constexpr ReturnValue_t POOL_VAR_NULL = MAKE_RETURN_CODE(6);
/** /**
* @brief This is an empty virtual destructor, * @brief This is an empty virtual destructor,

View File

@ -1,5 +1,8 @@
#include "PoolDataSetBase.h" #include "PoolDataSetBase.h"
#include "../serviceinterface/ServiceInterfaceStream.h" #include "ReadCommitIFAttorney.h"
#include "../serviceinterface/ServiceInterface.h"
#include <cstring> #include <cstring>
PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray, PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
@ -11,26 +14,28 @@ PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
PoolDataSetBase::~PoolDataSetBase() {} PoolDataSetBase::~PoolDataSetBase() {}
ReturnValue_t PoolDataSetBase::registerVariable( ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF *variable) {
PoolVariableIF *variable) {
if (state != States::STATE_SET_UNINITIALISED) { if (state != States::STATE_SET_UNINITIALISED) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::registerVariable: " sif::error << "DataSet::registerVariable: Call made in wrong position." << std::endl;
"Call made in wrong position." << std::endl; #else
sif::printError("DataSet::registerVariable: Call made in wrong position.");
#endif #endif
return DataSetIF::DATA_SET_UNINITIALISED; return DataSetIF::DATA_SET_UNINITIALISED;
} }
if (variable == nullptr) { if (variable == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::registerVariable: " sif::error << "DataSet::registerVariable: Pool variable is nullptr." << std::endl;
"Pool variable is nullptr." << std::endl; #else
sif::printError("DataSet::registerVariable: Pool variable is nullptr.\n");
#endif #endif
return DataSetIF::POOL_VAR_NULL; return DataSetIF::POOL_VAR_NULL;
} }
if (fillCount >= maxFillCount) { if (fillCount >= maxFillCount) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::registerVariable: " sif::error << "DataSet::registerVariable: DataSet is full." << std::endl;
"DataSet is full." << std::endl; #else
sif::printError("DataSet::registerVariable: DataSet is full.\n");
#endif #endif
return DataSetIF::DATA_SET_FULL; return DataSetIF::DATA_SET_FULL;
} }
@ -56,10 +61,12 @@ ReturnValue_t PoolDataSetBase::read(MutexIF::TimeoutType timeoutType,
} }
else { else {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::read(): " sif::error << "DataSet::read(): Call made in wrong position. Don't forget to commit"
"Call made in wrong position. Don't forget to commit"
" member datasets!" << std::endl; " member datasets!" << std::endl;
#endif #else
sif::printError("DataSet::read(): Call made in wrong position. Don't forget to commit"
" member datasets!\n");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
result = SET_WAS_ALREADY_READ; result = SET_WAS_ALREADY_READ;
} }
@ -76,24 +83,21 @@ uint16_t PoolDataSetBase::getFillCount() const {
ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) { ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
if(registeredVariables[count] == nullptr) { if(registeredVariables[count] == nullptr) {
// configuration error. /* Configuration error. */
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
// These checks are often performed by the respective /* These checks are often performed by the respective variable implementation too, but I guess
// variable implementation too, but I guess a double check does not hurt. a double check does not hurt. */
if (registeredVariables[count]->getReadWriteMode() != if (registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_WRITE and
PoolVariableIF::VAR_WRITE and registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER) {
registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER)
{
if(protectEveryReadCommitCall) { if(protectEveryReadCommitCall) {
result = registeredVariables[count]->read( result = registeredVariables[count]->read(timeoutTypeForSingleVars,
timeoutTypeForSingleVars,
mutexTimeoutForSingleVars); mutexTimeoutForSingleVars);
} }
else { else {
result = registeredVariables[count]->readWithoutLock(); /* The readWithoutLock function is protected, so we use the attorney here */
result = ReadCommitIFAttorney::readWithoutLock(registeredVariables[count]);
} }
if(result != HasReturnvaluesIF::RETURN_OK) { if(result != HasReturnvaluesIF::RETURN_OK) {
@ -118,17 +122,15 @@ void PoolDataSetBase::handleAlreadyReadDatasetCommit(
MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) { MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) {
lockDataPool(timeoutType, lockTimeout); lockDataPool(timeoutType, lockTimeout);
for (uint16_t count = 0; count < fillCount; count++) { for (uint16_t count = 0; count < fillCount; count++) {
if (registeredVariables[count]->getReadWriteMode() if ((registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_READ) and
!= PoolVariableIF::VAR_READ (registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER)) {
&& registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER) {
if(protectEveryReadCommitCall) { if(protectEveryReadCommitCall) {
registeredVariables[count]->commit( registeredVariables[count]->commit(timeoutTypeForSingleVars,
timeoutTypeForSingleVars,
mutexTimeoutForSingleVars); mutexTimeoutForSingleVars);
} }
else { else {
registeredVariables[count]->commitWithoutLock(); /* The commitWithoutLock function is protected, so we use the attorney here */
ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]);
} }
} }
} }
@ -141,17 +143,15 @@ ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit(
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
lockDataPool(timeoutType, lockTimeout); lockDataPool(timeoutType, lockTimeout);
for (uint16_t count = 0; count < fillCount; count++) { for (uint16_t count = 0; count < fillCount; count++) {
if (registeredVariables[count]->getReadWriteMode() if ((registeredVariables[count]->getReadWriteMode() == PoolVariableIF::VAR_WRITE) and
== PoolVariableIF::VAR_WRITE (registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER)) {
&& registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER) {
if(protectEveryReadCommitCall) { if(protectEveryReadCommitCall) {
result = registeredVariables[count]->commit( result = registeredVariables[count]->commit(timeoutTypeForSingleVars,
timeoutTypeForSingleVars,
mutexTimeoutForSingleVars); mutexTimeoutForSingleVars);
} }
else { else {
result = registeredVariables[count]->commitWithoutLock(); /* The commitWithoutLock function is protected, so we use the attorney here */
ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]);
} }
} else if (registeredVariables[count]->getDataPoolId() } else if (registeredVariables[count]->getDataPoolId()
@ -184,8 +184,7 @@ ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size,
const size_t maxSize, SerializeIF::Endianness streamEndianness) const { const size_t maxSize, SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
for (uint16_t count = 0; count < fillCount; count++) { for (uint16_t count = 0; count < fillCount; count++) {
result = registeredVariables[count]->serialize(buffer, size, maxSize, result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {
return result; return result;
} }

View File

@ -29,7 +29,8 @@
* @author Bastian Baetz * @author Bastian Baetz
* @ingroup data_pool * @ingroup data_pool
*/ */
class PoolDataSetBase: public PoolDataSetIF, class PoolDataSetBase:
public PoolDataSetIF,
public SerializeIF, public SerializeIF,
public HasReturnvaluesIF { public HasReturnvaluesIF {
public: public:

View File

@ -8,7 +8,9 @@
* @brief Extendes the DataSetIF by adding abstract functions to lock * @brief Extendes the DataSetIF by adding abstract functions to lock
* and unlock a data pool and read/commit semantics. * and unlock a data pool and read/commit semantics.
*/ */
class PoolDataSetIF: public DataSetIF, public ReadCommitIF { class PoolDataSetIF:
public DataSetIF,
public ReadCommitIF {
public: public:
virtual~ PoolDataSetIF() {}; virtual~ PoolDataSetIF() {};

View File

@ -8,9 +8,9 @@
/** /**
* @brief Helper class to read data sets or pool variables * @brief Helper class to read data sets or pool variables
*/ */
class PoolReadHelper { class PoolReadGuard {
public: public:
PoolReadHelper(ReadCommitIF* readObject, PoolReadGuard(ReadCommitIF* readObject,
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t mutexTimeout = 20): uint32_t mutexTimeout = 20):
readObject(readObject), mutexTimeout(mutexTimeout) { readObject(readObject), mutexTimeout(mutexTimeout) {
@ -32,8 +32,18 @@ public:
return readResult; return readResult;
} }
~PoolReadHelper() { /**
if(readObject != nullptr) { * @brief Can be used to suppress commit on destruction.
*/
void setNoCommitMode(bool commit) {
this->noCommit = commit;
}
/**
* @brief Default destructor which will take care of commiting changed values.
*/
~PoolReadGuard() {
if(readObject != nullptr and not noCommit) {
readObject->commit(timeoutType, mutexTimeout); readObject->commit(timeoutType, mutexTimeout);
} }
@ -42,6 +52,7 @@ public:
private: private:
ReadCommitIF* readObject = nullptr; ReadCommitIF* readObject = nullptr;
ReturnValue_t readResult = HasReturnvaluesIF::RETURN_OK; ReturnValue_t readResult = HasReturnvaluesIF::RETURN_OK;
bool noCommit = false;
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING; MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
uint32_t mutexTimeout = 20; uint32_t mutexTimeout = 20;
}; };

View File

@ -1,9 +1,10 @@
#ifndef FSFW_DATAPOOL_POOLVARIABLEIF_H_ #ifndef FSFW_DATAPOOL_POOLVARIABLEIF_H_
#define FSFW_DATAPOOL_POOLVARIABLEIF_H_ #define FSFW_DATAPOOL_POOLVARIABLEIF_H_
#include "ReadCommitIF.h"
#include "../returnvalues/HasReturnvaluesIF.h" #include "../returnvalues/HasReturnvaluesIF.h"
#include "../serialize/SerializeIF.h" #include "../serialize/SerializeIF.h"
#include "ReadCommitIF.h"
/** /**
* @brief This interface is used to control data pool * @brief This interface is used to control data pool
@ -18,10 +19,10 @@
* @author Bastian Baetz * @author Bastian Baetz
* @ingroup data_pool * @ingroup data_pool
*/ */
class PoolVariableIF : public SerializeIF, class PoolVariableIF :
public SerializeIF,
public ReadCommitIF { public ReadCommitIF {
friend class PoolDataSetBase;
friend class LocalPoolDataSetBase;
public: public:
static constexpr uint8_t INTERFACE_ID = CLASS_ID::POOL_VARIABLE_IF; 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 ReturnValue_t INVALID_READ_WRITE_MODE = MAKE_RETURN_CODE(0xA0);
@ -45,6 +46,8 @@ public:
* read-write or read-only. * read-write or read-only.
*/ */
virtual ReadWriteMode_t getReadWriteMode() const = 0; virtual ReadWriteMode_t getReadWriteMode() const = 0;
virtual void setReadWriteMode(ReadWriteMode_t newMode) = 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.
*/ */
@ -58,7 +61,6 @@ public:
* @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(bool validity) = 0; virtual void setValid(bool validity) = 0;
}; };
using pool_rwm_t = PoolVariableIF::ReadWriteMode_t; using pool_rwm_t = PoolVariableIF::ReadWriteMode_t;

View File

@ -9,6 +9,7 @@
* semantics. * semantics.
*/ */
class ReadCommitIF { class ReadCommitIF {
friend class ReadCommitIFAttorney;
public: public:
virtual ~ReadCommitIF() {} virtual ~ReadCommitIF() {}
virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType, virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType,
@ -18,9 +19,8 @@ public:
protected: protected:
//! Optional and protected because this is interesting for classes grouping /* Optional and protected because this is interesting for classes grouping members with commit
//! members with commit and read semantics where the lock is only necessary and read semantics where the lock is only necessary once. */
//! once.
virtual ReturnValue_t readWithoutLock() { virtual ReturnValue_t readWithoutLock() {
return read(MutexIF::TimeoutType::WAITING, 20); return read(MutexIF::TimeoutType::WAITING, 20);
} }

View File

@ -0,0 +1,32 @@
#ifndef FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_
#define FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_
#include <fsfw/datapool/ReadCommitIF.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
/**
* @brief This class determines which members are allowed to access protected members
* of the ReadCommitIF.
*/
class ReadCommitIFAttorney {
private:
static ReturnValue_t readWithoutLock(ReadCommitIF* readCommitIF) {
if(readCommitIF == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
return readCommitIF->readWithoutLock();
}
static ReturnValue_t commitWithoutLock(ReadCommitIF* readCommitIF) {
if(readCommitIF == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
return readCommitIF->commitWithoutLock();
}
friend class PoolDataSetBase;
};
#endif /* FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_ */

View File

@ -23,11 +23,21 @@ class LocalPoolObjectBase;
* @details * @details
* Any class implementing this interface shall also have a LocalDataPoolManager member class which * 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. * contains the actual pool data structure and exposes the public interface for it.
*
* The local data pool can be accessed using helper classes by using the * The local data pool can be accessed using helper classes by using the
* LocalPoolVariable, LocalPoolVector or LocalDataSet classes. Every local pool variable can * LocalPoolVariable, LocalPoolVector or LocalDataSet classes. Every local pool variable can
* be uniquely identified by a global pool ID (gp_id_t) and every dataset tied * be uniquely identified by a global pool ID (gp_id_t) and every dataset tied
* to a pool manager can be uniqely identified by a global structure ID (sid_t). * to a pool manager can be uniqely identified by a global structure ID (sid_t).
* *
* All software objects which want to use the local pool of another object shall also use this
* interface, for example to get a handle to the subscription interface. The interface
* can be retrieved using the object manager, provided the target object is a SystemObject.
* For example, the following line of code can be used to retrieve the interface
*
* HasLocalDataPoolIF* poolIF = objectManager->get<HasLocalDataPoolIF>(objects::SOME_OBJECT);
* if(poolIF != nullptr) {
* doSomething()
* }
*/ */
class HasLocalDataPoolIF { class HasLocalDataPoolIF {
friend class HasLocalDpIFManagerAttorney; friend class HasLocalDpIFManagerAttorney;
@ -152,8 +162,8 @@ protected:
*/ */
virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) { virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden" sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden. "
<< ". Returning nullptr!" << std::endl; "Returning nullptr!" << std::endl;
#else #else
sif::printWarning("HasLocalDataPoolIF::getPoolObjectHandle: " sif::printWarning("HasLocalDataPoolIF::getPoolObjectHandle: "
"Not overriden. Returning nullptr!\n"); "Not overriden. Returning nullptr!\n");

View File

@ -42,15 +42,15 @@ LocalDataPoolManager::~LocalDataPoolManager() {}
ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) { ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) {
if(queueToUse == nullptr) { if(queueToUse == nullptr) {
// error, all destinations invalid /* Error, all destinations invalid */
printWarningOrError(sif::OutputTypes::OUT_ERROR, printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize",
"initialize", QUEUE_OR_DESTINATION_INVALID); QUEUE_OR_DESTINATION_INVALID);
} }
hkQueue = queueToUse; hkQueue = queueToUse;
ipcStore = objectManager->get<StorageManagerIF>(objects::IPC_STORE); ipcStore = objectManager->get<StorageManagerIF>(objects::IPC_STORE);
if(ipcStore == nullptr) { if(ipcStore == nullptr) {
// error, all destinations invalid /* Error, all destinations invalid */
printWarningOrError(sif::OutputTypes::OUT_ERROR, printWarningOrError(sif::OutputTypes::OUT_ERROR,
"initialize", HasReturnvaluesIF::RETURN_FAILED, "initialize", HasReturnvaluesIF::RETURN_FAILED,
"Could not set IPC store."); "Could not set IPC store.");
@ -98,7 +98,7 @@ ReturnValue_t LocalDataPoolManager::initializeHousekeepingPoolEntriesOnce() {
ReturnValue_t LocalDataPoolManager::performHkOperation() { ReturnValue_t LocalDataPoolManager::performHkOperation() {
ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; ReturnValue_t status = HasReturnvaluesIF::RETURN_OK;
for(auto& receiver: hkReceiversMap) { for(auto& receiver: hkReceivers) {
switch(receiver.reportingType) { switch(receiver.reportingType) {
case(ReportingType::PERIODIC): { case(ReportingType::PERIODIC): {
if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
@ -375,12 +375,12 @@ ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(sid_t sid,
owner->getPeriodicOperationFrequency(), isDiagnostics); owner->getPeriodicOperationFrequency(), isDiagnostics);
} }
hkReceiversMap.push_back(hkReceiver); hkReceivers.push_back(hkReceiver);
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
ReturnValue_t LocalDataPoolManager::subscribeForUpdatePackets(sid_t sid, ReturnValue_t LocalDataPoolManager::subscribeForUpdatePacket(sid_t sid,
bool isDiagnostics, bool reportingEnabled, bool isDiagnostics, bool reportingEnabled,
object_id_t packetDestination) { object_id_t packetDestination) {
AcceptsHkPacketsIF* hkReceiverObject = AcceptsHkPacketsIF* hkReceiverObject =
@ -404,13 +404,13 @@ ReturnValue_t LocalDataPoolManager::subscribeForUpdatePackets(sid_t sid,
LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics); LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics);
} }
hkReceiversMap.push_back(hkReceiver); hkReceivers.push_back(hkReceiver);
handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId);
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessages( ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessage(
const uint32_t setId, object_id_t destinationObject, const uint32_t setId, object_id_t destinationObject,
MessageQueueId_t targetQueueId, bool generateSnapshot) { MessageQueueId_t targetQueueId, bool generateSnapshot) {
struct HkReceiver hkReceiver; struct HkReceiver hkReceiver;
@ -425,13 +425,13 @@ ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessages(
hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION; hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION;
} }
hkReceiversMap.push_back(hkReceiver); hkReceivers.push_back(hkReceiver);
handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId);
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
ReturnValue_t LocalDataPoolManager::subscribeForVariableUpdateMessages( ReturnValue_t LocalDataPoolManager::subscribeForVariableUpdateMessage(
const lp_id_t localPoolId, object_id_t destinationObject, const lp_id_t localPoolId, object_id_t destinationObject,
MessageQueueId_t targetQueueId, bool generateSnapshot) { MessageQueueId_t targetQueueId, bool generateSnapshot) {
struct HkReceiver hkReceiver; struct HkReceiver hkReceiver;
@ -446,7 +446,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForVariableUpdateMessages(
hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION; hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION;
} }
hkReceiversMap.push_back(hkReceiver); hkReceivers.push_back(hkReceiver);
handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId);
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
@ -829,8 +829,8 @@ ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid,
} }
void LocalDataPoolManager::clearReceiversList() { void LocalDataPoolManager::clearReceiversList() {
// clear the vector completely and releases allocated memory. /* Clear the vector completely and releases allocated memory. */
HkReceivers().swap(hkReceiversMap); HkReceivers().swap(hkReceivers);
} }
MutexIF* LocalDataPoolManager::getLocalPoolMutex() { MutexIF* LocalDataPoolManager::getLocalPoolMutex() {

View File

@ -137,7 +137,7 @@ public:
* @param packetDestination * @param packetDestination
* @return * @return
*/ */
ReturnValue_t subscribeForUpdatePackets(sid_t sid, bool reportingEnabled, ReturnValue_t subscribeForUpdatePacket(sid_t sid, bool reportingEnabled,
bool isDiagnostics, bool isDiagnostics,
object_id_t packetDestination = defaultHkDestination) override; object_id_t packetDestination = defaultHkDestination) override;
@ -155,7 +155,7 @@ public:
* Otherwise, only an notification message is sent. * Otherwise, only an notification message is sent.
* @return * @return
*/ */
ReturnValue_t subscribeForSetUpdateMessages(const uint32_t setId, ReturnValue_t subscribeForSetUpdateMessage(const uint32_t setId,
object_id_t destinationObject, object_id_t destinationObject,
MessageQueueId_t targetQueueId, MessageQueueId_t targetQueueId,
bool generateSnapshot) override; bool generateSnapshot) override;
@ -174,7 +174,7 @@ public:
* Otherwise, only an notification message is sent. * Otherwise, only an notification message is sent.
* @return * @return
*/ */
ReturnValue_t subscribeForVariableUpdateMessages(const lp_id_t localPoolId, ReturnValue_t subscribeForVariableUpdateMessage(const lp_id_t localPoolId,
object_id_t destinationObject, object_id_t destinationObject,
MessageQueueId_t targetQueueId, MessageQueueId_t targetQueueId,
bool generateSnapshot) override; bool generateSnapshot) override;
@ -271,7 +271,10 @@ public:
MutexIF* getMutexHandle(); MutexIF* getMutexHandle();
virtual LocalDataPoolManager* getPoolManagerHandle() override; virtual LocalDataPoolManager* getPoolManagerHandle() override;
private:
protected:
/** Core data structure for the actual pool data */
localpool::DataPool localPoolMap; localpool::DataPool localPoolMap;
/** Every housekeeping data manager has a mutex to protect access /** Every housekeeping data manager has a mutex to protect access
to it's data pool. */ to it's data pool. */
@ -307,7 +310,7 @@ private:
/** This vector will contain the list of HK receivers. */ /** This vector will contain the list of HK receivers. */
using HkReceivers = std::vector<struct HkReceiver>; using HkReceivers = std::vector<struct HkReceiver>;
HkReceivers hkReceiversMap; HkReceivers hkReceivers;
struct HkUpdateResetHelper { struct HkUpdateResetHelper {
DataType dataType = DataType::DATA_SET; DataType dataType = DataType::DATA_SET;
@ -317,7 +320,8 @@ private:
}; };
using HkUpdateResetList = std::vector<struct HkUpdateResetHelper>; using HkUpdateResetList = std::vector<struct HkUpdateResetHelper>;
// Will only be created when needed. /** This list is used to manage creating multiple update packets and only resetting
the update flag if all of them were created. Will only be created when needed. */
HkUpdateResetList* hkUpdateResetList = nullptr; HkUpdateResetList* hkUpdateResetList = nullptr;
/** This is the map holding the actual data. Should only be initialized /** This is the map holding the actual data. Should only be initialized
@ -341,16 +345,14 @@ private:
* Read a variable by supplying its local pool ID and assign the pool * 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 * entry to the supplied PoolEntry pointer. The type of the pool entry
* is deduced automatically. This call is not thread-safe! * is deduced automatically. This call is not thread-safe!
* For now, only friend classes like LocalPoolVar may access this * For now, only classes designated by the LocalDpManagerAttorney may use this function.
* function.
* @tparam T Type of the pool entry * @tparam T Type of the pool entry
* @param localPoolId Pool ID of the variable to read * @param localPoolId Pool ID of the variable to read
* @param poolVar [out] Corresponding pool entry will be assigned to the * @param poolVar [out] Corresponding pool entry will be assigned to the
* supplied pointer. * supplied pointer.
* @return * @return
*/ */
template <class T> ReturnValue_t fetchPoolEntry(lp_id_t localPoolId, template <class T> ReturnValue_t fetchPoolEntry(lp_id_t localPoolId, PoolEntry<T> **poolEntry);
PoolEntry<T> **poolEntry);
/** /**
* This function is used to fill the local data pool map with pool * This function is used to fill the local data pool map with pool
@ -362,15 +364,13 @@ private:
MutexIF* getLocalPoolMutex() override; MutexIF* getLocalPoolMutex() override;
ReturnValue_t serializeHkPacketIntoStore( ReturnValue_t serializeHkPacketIntoStore(HousekeepingPacketDownlink& hkPacket,
HousekeepingPacketDownlink& hkPacket,
store_address_t& storeId, bool forDownlink, size_t* serializedSize); store_address_t& storeId, bool forDownlink, size_t* serializedSize);
void performPeriodicHkGeneration(HkReceiver& hkReceiver); void performPeriodicHkGeneration(HkReceiver& hkReceiver);
ReturnValue_t togglePeriodicGeneration(sid_t sid, bool enable, ReturnValue_t togglePeriodicGeneration(sid_t sid, bool enable, bool isDiagnostics);
ReturnValue_t changeCollectionInterval(sid_t sid, float newCollectionInterval,
bool isDiagnostics); bool isDiagnostics);
ReturnValue_t changeCollectionInterval(sid_t sid,
float newCollectionInterval, bool isDiagnostics);
ReturnValue_t generateSetStructurePacket(sid_t sid, bool isDiagnostics); ReturnValue_t generateSetStructurePacket(sid_t sid, bool isDiagnostics);
void handleHkUpdateResetListInsertion(DataType dataType, DataId dataId); void handleHkUpdateResetListInsertion(DataType dataType, DataId dataId);
@ -378,25 +378,19 @@ private:
DataId dataId, MarkChangedIF* toReset); DataId dataId, MarkChangedIF* toReset);
void resetHkUpdateResetHelper(); void resetHkUpdateResetHelper();
ReturnValue_t handleHkUpdate(HkReceiver& hkReceiver, ReturnValue_t handleHkUpdate(HkReceiver& hkReceiver, ReturnValue_t& status);
ReturnValue_t& status); ReturnValue_t handleNotificationUpdate(HkReceiver& hkReceiver, ReturnValue_t& status);
ReturnValue_t handleNotificationUpdate(HkReceiver& hkReceiver, ReturnValue_t handleNotificationSnapshot(HkReceiver& hkReceiver, ReturnValue_t& status);
ReturnValue_t& status); ReturnValue_t addUpdateToStore(HousekeepingSnapshot& updatePacket, store_address_t& storeId);
ReturnValue_t handleNotificationSnapshot(HkReceiver& hkReceiver,
ReturnValue_t& status);
ReturnValue_t addUpdateToStore(HousekeepingSnapshot& updatePacket,
store_address_t& storeId);
void printWarningOrError(sif::OutputTypes outputType, void printWarningOrError(sif::OutputTypes outputType, const char* functionName,
const char* functionName,
ReturnValue_t errorCode = HasReturnvaluesIF::RETURN_FAILED, ReturnValue_t errorCode = HasReturnvaluesIF::RETURN_FAILED,
const char* errorPrint = nullptr); const char* errorPrint = nullptr);
}; };
template<class T> inline template<class T> inline
ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, PoolEntry<T> **poolEntry) {
PoolEntry<T> **poolEntry) {
auto poolIter = localPoolMap.find(localPoolId); auto poolIter = localPoolMap.find(localPoolId);
if (poolIter == localPoolMap.end()) { if (poolIter == localPoolMap.end()) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry", printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry",

View File

@ -3,6 +3,7 @@
#include "internal/HasLocalDpIFUserAttorney.h" #include "internal/HasLocalDpIFUserAttorney.h"
#include "../serviceinterface/ServiceInterface.h" #include "../serviceinterface/ServiceInterface.h"
#include "../globalfunctions/bitutility.h"
#include "../datapoollocal/LocalDataPoolManager.h" #include "../datapoollocal/LocalDataPoolManager.h"
#include "../housekeeping/PeriodicHousekeepingHelper.h" #include "../housekeeping/PeriodicHousekeepingHelper.h"
#include "../serialize/SerializeAdapter.h" #include "../serialize/SerializeAdapter.h"
@ -35,14 +36,13 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
this->sid.objectId = hkOwner->getObjectId(); this->sid.objectId = hkOwner->getObjectId();
this->sid.ownerSetId = setId; this->sid.ownerSetId = setId;
// Data creators get a periodic helper for periodic HK data generation. /* Data creators get a periodic helper for periodic HK data generation. */
if(periodicHandling) { if(periodicHandling) {
periodicHelper = new PeriodicHousekeepingHelper(this); periodicHelper = new PeriodicHousekeepingHelper(this);
} }
} }
LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray,
PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables): const size_t maxNumberOfVariables):
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>( HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(
@ -96,14 +96,14 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer
SerializeIF::Endianness streamEndianness) const { SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount)/8.0); uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount)/8.0);
uint8_t validityMask[validityMaskSize]; uint8_t validityMask[validityMaskSize] = {};
uint8_t validBufferIndex = 0; uint8_t validBufferIndex = 0;
uint8_t validBufferIndexBit = 0; uint8_t validBufferIndexBit = 0;
for (uint16_t count = 0; count < fillCount; count++) { for (uint16_t count = 0; count < fillCount; count++) {
if(registeredVariables[count]->isValid()) { if(registeredVariables[count]->isValid()) {
// set validity buffer here. /* Set bit at correct position */
this->bitSetter(validityMask + validBufferIndex, bitutil::bitSet(validityMask + validBufferIndex, validBufferIndexBit);
validBufferIndexBit); }
if(validBufferIndexBit == 7) { if(validBufferIndexBit == 7) {
validBufferIndex ++; validBufferIndex ++;
validBufferIndexBit = 0; validBufferIndexBit = 0;
@ -111,7 +111,7 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer
else { else {
validBufferIndexBit ++; validBufferIndexBit ++;
} }
}
result = registeredVariables[count]->serialize(buffer, size, maxSize, result = registeredVariables[count]->serialize(buffer, size, maxSize,
streamEndianness); streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {
@ -148,7 +148,7 @@ ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer(
uint8_t validBufferIndexBit = 0; uint8_t validBufferIndexBit = 0;
for (uint16_t count = 0; count < fillCount; count++) { for (uint16_t count = 0; count < fillCount; count++) {
// set validity buffer here. // set validity buffer here.
bool nextVarValid = this->bitGetter(*buffer + bool nextVarValid = bitutil::bitGet(*buffer +
validBufferIndex, validBufferIndexBit); validBufferIndex, validBufferIndexBit);
registeredVariables[count]->setValid(nextVarValid); registeredVariables[count]->setValid(nextVarValid);
@ -173,7 +173,7 @@ ReturnValue_t LocalPoolDataSetBase::unlockDataPool() {
ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t** buffer, ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t** buffer,
size_t* size, size_t maxSize,SerializeIF::Endianness streamEndianness, size_t* size, size_t maxSize,SerializeIF::Endianness streamEndianness,
bool serializeFillCount) const { bool serializeFillCount) const {
// Serialize as uint8_t /* Serialize fill count as uint8_t */
uint8_t fillCount = this->fillCount; uint8_t fillCount = this->fillCount;
if(serializeFillCount) { if(serializeFillCount) {
SerializeAdapter::serialize(&fillCount, buffer, size, maxSize, SerializeAdapter::serialize(&fillCount, buffer, size, maxSize,
@ -246,21 +246,6 @@ ReturnValue_t LocalPoolDataSetBase::serialize(uint8_t **buffer, size_t *size,
} }
} }
void LocalPoolDataSetBase::bitSetter(uint8_t* byte, uint8_t position) const {
if(position > 7) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolDataSetBase::bitSetter: Invalid position!"
<< std::endl;
#else
sif::printWarning("LocalPoolDataSetBase::bitSetter: "
"Invalid position!\n\r");
#endif
return;
}
uint8_t shiftNumber = position + (7 - 2 * position);
*byte |= 1 << shiftNumber;
}
void LocalPoolDataSetBase::setDiagnostic(bool isDiagnostics) { void LocalPoolDataSetBase::setDiagnostic(bool isDiagnostics) {
this->diagnostic = isDiagnostics; this->diagnostic = isDiagnostics;
} }
@ -296,19 +281,6 @@ sid_t LocalPoolDataSetBase::getSid() const {
return sid; return sid;
} }
bool LocalPoolDataSetBase::bitGetter(const uint8_t* byte,
uint8_t position) const {
if(position > 7) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "Pool Raw Access: Bit setting invalid position"
<< std::endl;
#endif
return false;
}
uint8_t shiftNumber = position + (7 - 2 * position);
return *byte & (1 << shiftNumber);
}
bool LocalPoolDataSetBase::isValid() const { bool LocalPoolDataSetBase::isValid() const {
return this->valid; return this->valid;
} }
@ -328,3 +300,9 @@ object_id_t LocalPoolDataSetBase::getCreatorObjectId() {
} }
return objects::NO_OBJECT; return objects::NO_OBJECT;
} }
void LocalPoolDataSetBase::setAllVariablesReadOnly() {
for(size_t idx = 0; idx < this->getFillCount(); idx++) {
registeredVariables[idx]->setReadWriteMode(pool_rwm_t::VAR_READ);
}
}

View File

@ -59,7 +59,7 @@ public:
/** /**
* @brief Constructor for users of the local pool data, which need * @brief Constructor for users of the local pool data, which need
* to access data created by one (!) HK manager. * to access data created by one HK manager.
* @details * @details
* Unlike the first constructor, no component for periodic handling * Unlike the first constructor, no component for periodic handling
* will be initiated. * will be initiated.
@ -109,6 +109,12 @@ public:
LocalPoolDataSetBase(const LocalPoolDataSetBase& otherSet) = delete; LocalPoolDataSetBase(const LocalPoolDataSetBase& otherSet) = delete;
const LocalPoolDataSetBase& operator=(const LocalPoolDataSetBase& otherSet) = delete; const LocalPoolDataSetBase& operator=(const LocalPoolDataSetBase& otherSet) = delete;
/**
* Helper functions used to set all currently contained variables to read-only.
* It is recommended to call this in set constructors intended to be used
* by data consumers to prevent accidentally changing pool data.
*/
void setAllVariablesReadOnly();
void setValidityBufferGeneration(bool withValidityBuffer); void setValidityBufferGeneration(bool withValidityBuffer);
sid_t getSid() const; sid_t getSid() const;
@ -218,13 +224,6 @@ protected:
*/ */
ReturnValue_t unlockDataPool() override; ReturnValue_t unlockDataPool() override;
/**
* 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; PeriodicHousekeepingHelper* periodicHelper = nullptr;
LocalDataPoolManager* poolManager = nullptr; LocalDataPoolManager* poolManager = nullptr;

View File

@ -1,10 +1,12 @@
#include "LocalPoolObjectBase.h" #include "LocalPoolObjectBase.h"
#include "LocalDataPoolManager.h" #include "LocalDataPoolManager.h"
#include "internal/HasLocalDpIFUserAttorney.h" #include "AccessLocalPoolF.h"
#include "HasLocalDataPoolIF.h" #include "HasLocalDataPoolIF.h"
#include "internal/HasLocalDpIFUserAttorney.h"
#include "../objectmanager/ObjectManagerIF.h" #include "../objectmanager/ObjectManagerIF.h"
LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner,
DataSetIF* dataSet, pool_rwm_t setReadWriteMode): DataSetIF* dataSet, pool_rwm_t setReadWriteMode):
localPoolId(poolId), readWriteMode(setReadWriteMode) { localPoolId(poolId), readWriteMode(setReadWriteMode) {
@ -35,15 +37,20 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
if(poolId == PoolVariableIF::NO_PARAMETER) { if(poolId == PoolVariableIF::NO_PARAMETER) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolVar<T>::LocalPoolVar: 0 passed as pool ID, " sif::warning << "LocalPoolVar<T>::LocalPoolVar: 0 passed as pool ID, "
<< "which is the NO_PARAMETER value!" << std::endl; "which is the NO_PARAMETER value!" << std::endl;
#else
sif::printWarning("LocalPoolVar<T>::LocalPoolVar: 0 passed as pool ID, "
"which is the NO_PARAMETER value!\n");
#endif #endif
} }
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(poolOwner); HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(poolOwner);
if(hkOwner == nullptr) { if(hkOwner == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPoolVariable: The supplied pool owner did not " sif::error << "LocalPoolVariable: The supplied pool owner did not implement the correct "
<< "implement the correct interface" "interface HasLocalDataPoolIF!" << std::endl;
<< " HasLocalDataPoolIF!" << std::endl; #else
sif::printError( "LocalPoolVariable: The supplied pool owner did not implement the correct "
"interface HasLocalDataPoolIF!\n");
#endif #endif
return; return;
} }
@ -93,6 +100,10 @@ void LocalPoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) {
void LocalPoolObjectBase::reportReadCommitError(const char* variableType, void LocalPoolObjectBase::reportReadCommitError(const char* variableType,
ReturnValue_t error, bool read, object_id_t objectId, lp_id_t lpId) { ReturnValue_t error, bool read, object_id_t objectId, lp_id_t lpId) {
#if FSFW_DISABLE_PRINTOUT == 0 #if FSFW_DISABLE_PRINTOUT == 0
const char* variablePrintout = variableType;
if(variablePrintout == nullptr) {
variablePrintout = "Unknown Type";
}
const char* type = nullptr; const char* type = nullptr;
if(read) { if(read) {
type = "read"; type = "read";
@ -119,12 +130,12 @@ void LocalPoolObjectBase::reportReadCommitError(const char* variableType,
} }
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << variableType << ": " << type << " call | " << errMsg << " | Owner: 0x" sif::warning << variablePrintout << ": " << type << " call | " << errMsg << " | Owner: 0x"
<< std::hex << std::setw(8) << std::setfill('0') << objectId << std::dec << std::hex << std::setw(8) << std::setfill('0') << objectId << std::dec
<< " LPID: " << lpId << std::endl; << " LPID: " << lpId << std::endl;
#else #else
sif::printWarning("%s: %s call | %s | Owner: 0x%08x LPID: %lu\n", sif::printWarning("%s: %s call | %s | Owner: 0x%08x LPID: %lu\n",
variableType, type, errMsg, objectId, lpId); variablePrintout, type, errMsg, objectId, lpId);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif /* FSFW_DISABLE_PRINTOUT == 0 */
} }

View File

@ -26,9 +26,18 @@ inline LocalPoolVariable<T>::LocalPoolVariable(gp_id_t globalPoolId,
template<typename T> template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::read( inline ReturnValue_t LocalPoolVariable<T>::read(
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) { MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs); if(hkManager == nullptr) {
return readWithoutLock(); return readWithoutLock();
} }
MutexIF* mutex = LocalDpManagerAttorney::getMutexHandle(*hkManager);
ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs);
if(result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
result = readWithoutLock();
mutex->unlockMutex();
return result;
}
template<typename T> template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() { inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() {
@ -43,7 +52,6 @@ inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() {
PoolEntry<T>* poolEntry = nullptr; PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId,
&poolEntry); &poolEntry);
//ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry);
if(result != RETURN_OK) { if(result != RETURN_OK) {
object_id_t ownerObjectId = hkManager->getCreatorObjectId(); object_id_t ownerObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVariable", result, reportReadCommitError("LocalPoolVariable", result,
@ -51,15 +59,6 @@ inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() {
return result; return result;
} }
// Actually this should never happen..
// if(poolEntry->address == nullptr) {
// result = PoolVariableIF::INVALID_POOL_ENTRY;
// object_id_t ownerObjectId = hkManager->getOwner()->getObjectId();
// reportReadCommitError("LocalPoolVariable", result,
// false, ownerObjectId, localPoolId);
// return result;
// }
this->value = *(poolEntry->getDataPtr()); this->value = *(poolEntry->getDataPtr());
this->valid = poolEntry->getValid(); this->valid = poolEntry->getValid();
return RETURN_OK; return RETURN_OK;
@ -75,9 +74,18 @@ inline ReturnValue_t LocalPoolVariable<T>::commit(bool setValid,
template<typename T> template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::commit( inline ReturnValue_t LocalPoolVariable<T>::commit(
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) { MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs); if(hkManager == nullptr) {
return commitWithoutLock(); return commitWithoutLock();
} }
MutexIF* mutex = LocalDpManagerAttorney::getMutexHandle(*hkManager);
ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs);
if(result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
result = commitWithoutLock();
mutex->unlockMutex();
return result;
}
template<typename T> template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::commitWithoutLock() { inline ReturnValue_t LocalPoolVariable<T>::commitWithoutLock() {
@ -90,7 +98,6 @@ inline ReturnValue_t LocalPoolVariable<T>::commitWithoutLock() {
} }
PoolEntry<T>* poolEntry = nullptr; PoolEntry<T>* poolEntry = nullptr;
//ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry);
ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId,
&poolEntry); &poolEntry);
if(result != RETURN_OK) { if(result != RETURN_OK) {

View File

@ -17,12 +17,8 @@ public:
* to generate housekeeping packets which are downlinked directly. * to generate housekeeping packets which are downlinked directly.
* @return * @return
*/ */
virtual ReturnValue_t subscribeForPeriodicPacket(sid_t sid, virtual ReturnValue_t subscribeForPeriodicPacket(sid_t sid, bool enableReporting,
bool enableReporting, float collectionInterval, bool isDiagnostics, object_id_t packetDestination) = 0;
float collectionInterval, bool isDiagnostics,
object_id_t packetDestination) = 0;
/** /**
* @brief Subscribe for the generation of packets if the dataset * @brief Subscribe for the generation of packets if the dataset
* is marked as changed. * is marked as changed.
@ -33,11 +29,8 @@ public:
* @param packetDestination * @param packetDestination
* @return * @return
*/ */
virtual ReturnValue_t subscribeForUpdatePackets(sid_t sid, virtual ReturnValue_t subscribeForUpdatePacket(sid_t sid, bool reportingEnabled,
bool reportingEnabled, bool isDiagnostics, object_id_t packetDestination) = 0;
bool isDiagnostics,
object_id_t packetDestination) = 0;
/** /**
* @brief Subscribe for a notification message which will be sent * @brief Subscribe for a notification message which will be sent
* if a dataset has changed. * if a dataset has changed.
@ -52,10 +45,9 @@ public:
* Otherwise, only an notification message is sent. * Otherwise, only an notification message is sent.
* @return * @return
*/ */
virtual ReturnValue_t subscribeForSetUpdateMessages(const uint32_t setId, virtual ReturnValue_t subscribeForSetUpdateMessage(const uint32_t setId,
object_id_t destinationObject, MessageQueueId_t targetQueueId, object_id_t destinationObject, MessageQueueId_t targetQueueId,
bool generateSnapshot) = 0; bool generateSnapshot) = 0;
/** /**
* @brief Subscribe for an notification message which will be sent if a * @brief Subscribe for an notification message which will be sent if a
* pool variable has changed. * pool variable has changed.
@ -70,12 +62,9 @@ public:
* only an notification message is sent. * only an notification message is sent.
* @return * @return
*/ */
virtual ReturnValue_t subscribeForVariableUpdateMessages( virtual ReturnValue_t subscribeForVariableUpdateMessage(const lp_id_t localPoolId,
const lp_id_t localPoolId, object_id_t destinationObject, MessageQueueId_t targetQueueId,
object_id_t destinationObject,
MessageQueueId_t targetQueueId,
bool generateSnapshot) = 0; bool generateSnapshot) = 0;
}; };
#endif /* FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ */ #endif /* FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ */

View File

@ -24,7 +24,6 @@ private:
return manager.getMutexHandle(); return manager.getMutexHandle();
} }
template<typename T> friend class LocalPoolVariable; template<typename T> friend class LocalPoolVariable;
template<typename T, uint16_t vecSize> friend class LocalPoolVector; template<typename T, uint16_t vecSize> friend class LocalPoolVector;
}; };

View File

@ -21,8 +21,8 @@ static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF;
static constexpr ReturnValue_t POOL_ENTRY_NOT_FOUND = MAKE_RETURN_CODE(0x00); 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 POOL_ENTRY_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01);
//! This is the core data structure of the local data pools. Users should insert all desired /** This is the core data structure of the local data pools. Users should insert all desired
//! pool variables, using the std::map interface. pool variables, using the std::map interface. */
using DataPool = std::map<lp_id_t, PoolEntryIF*>; using DataPool = std::map<lp_id_t, PoolEntryIF*>;
using DataPoolMapIter = DataPool::iterator; using DataPoolMapIter = DataPool::iterator;

View File

@ -558,7 +558,7 @@ void DeviceHandlerBase::replyToCommand(ReturnValue_t status,
if (cookieInfo.pendingCommand->second.sendReplyTo != NO_COMMANDER) { if (cookieInfo.pendingCommand->second.sendReplyTo != NO_COMMANDER) {
MessageQueueId_t queueId = cookieInfo.pendingCommand->second.sendReplyTo; MessageQueueId_t queueId = cookieInfo.pendingCommand->second.sendReplyTo;
if (status == NO_REPLY_EXPECTED) { if (status == NO_REPLY_EXPECTED) {
actionHelper.finish(queueId, cookieInfo.pendingCommand->first, actionHelper.finish(true, queueId, cookieInfo.pendingCommand->first,
RETURN_OK); RETURN_OK);
} else { } else {
actionHelper.step(1, queueId, cookieInfo.pendingCommand->first, actionHelper.step(1, queueId, cookieInfo.pendingCommand->first,
@ -581,7 +581,11 @@ void DeviceHandlerBase::replyToReply(DeviceReplyMap::iterator iter,
// Check if it was transition or internal command. // Check if it was transition or internal command.
// Don't send any replies in that case. // Don't send any replies in that case.
if (info->sendReplyTo != NO_COMMANDER) { if (info->sendReplyTo != NO_COMMANDER) {
actionHelper.finish(info->sendReplyTo, iter->first, status); bool success = false;
if(status == HasReturnvaluesIF::RETURN_OK) {
success = true;
}
actionHelper.finish(success, info->sendReplyTo, iter->first, status);
} }
info->isExecuting = false; info->isExecuting = false;
} }
@ -1494,10 +1498,9 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType,
if(errorType == sif::OutputTypes::OUT_WARNING) { if(errorType == sif::OutputTypes::OUT_WARNING) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "DeviceHandlerBase::" << functionName << ": Object ID " sif::warning << "DeviceHandlerBase::" << functionName << ": Object ID 0x" << std::hex <<
<< std::hex << std::setw(8) << std::setfill('0') std::setw(8) << std::setfill('0') << this->getObjectId() << " | " << errorPrint <<
<< this->getObjectId() << " | " << errorPrint << std::dec std::dec << std::setfill(' ') << std::endl;
<< std::setfill(' ') << std::endl;
#else #else
sif::printWarning("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n", sif::printWarning("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n",
this->getObjectId(), errorPrint); this->getObjectId(), errorPrint);

View File

@ -1,3 +1,172 @@
## Local Data Pools ## Local Data Pools Developer Information
The following text is targeted towards mission software developers which would like
to use the local data pools provided by the FSFW to store data like sensor values so they can be
used by other software objects like controllers as well. If a custom class should have a local
pool which can be used by other software objects as well, following steps have to be performed:
1. Create a `LocalDataPoolManager` member object in the custom class
2. Implement the `HasLocalDataPoolIF` with specifies the interface between the local pool manager
and the class owning the local pool.
The local data pool manager is also able to process housekeeping service requests in form
of messages, generate periodic housekeeping packet, generate notification and snapshots of changed
variables and datasets and process notifications and snapshots coming from other objects.
The two former tasks are related to the external interface using telemetry and telecommands (TMTC)
while the later two are related to data consumers like controllers only acting on data change
detected by the data creator instead of checking the data manually each cycle. Two important
framework classes `DeviceHandlerBase` and `ExtendedControllerBase` already perform the two steps
shown above so the steps required are altered slightly.
### Storing and Accessing pool data
The pool manager is responsible for thread-safe access of the pool data, but the actual
access to the pool data from the point of view of a mission software developer happens via proxy
classes like pool variable classes. These classes store a copy
of the pool variable with the matching datatype and copy the actual data from the local pool
on a `read` call. Changed variables can then be written to the local pool with a `commit` call.
The `read` and `commit` calls are thread-safe and can be called concurrently from data creators
and data consumers. Generally, a user will create a dataset class which in turn groups all
cohesive pool variables. These sets simply iterator over the list of variables and call the
`read` and `commit` functions of each variable. The following diagram shows the
high-level architecture of the local data pools.
<img align="center" src="./images/PoolArchitecture.png" width="50%"> <br>
An example is shown for using the local data pools with a Gyroscope.
For example, the following code shows an implementation to access data from a Gyroscope taken
from the SOURCE CubeSat project:
```cpp
class GyroPrimaryDataset: public StaticLocalDataSet<3 * sizeof(float)> {
public:
/**
* Constructor for data users
* @param gyroId
*/
GyroPrimaryDataset(object_id_t gyroId):
StaticLocalDataSet(sid_t(gyroId, gyrodefs::GYRO_DATA_SET_ID)) {
setAllVariablesReadOnly();
}
lp_var_t<float> angVelocityX = lp_var_t<float>(sid.objectId,
gyrodefs::ANGULAR_VELOCITY_X, this);
lp_var_t<float> angVelocityY = lp_var_t<float>(sid.objectId,
gyrodefs::ANGULAR_VELOCITY_Y, this);
lp_var_t<float> angVelocityZ = lp_var_t<float>(sid.objectId,
gyrodefs::ANGULAR_VELOCITY_Z, this);
private:
friend class GyroHandler;
/**
* Constructor for data creator
* @param hkOwner
*/
GyroPrimaryDataset(HasLocalDataPoolIF* hkOwner):
StaticLocalDataSet(hkOwner, gyrodefs::GYRO_DATA_SET_ID) {}
};
```
There is a public constructor for users which sets all variables to read-only and there is a
constructor for the GyroHandler data creator by marking it private and declaring the `GyroHandler`
as a friend class. Both the atittude controller and the `GyroHandler` can now
use the same class definition to access the pool variables with `read` and `commit` semantics
in a thread-safe way. Generally, each class requiring access will have the set class as a member
class. The data creator will also be generally a `DeviceHandlerBase` subclass and some additional
steps are necessary to expose the set for housekeeping purposes.
### Using the local data pools in a `DeviceHandlerBase` subclass
It is very common to store data generated by devices like a sensor into a pool which can
then be used by other objects. Therefore, the `DeviceHandlerBase` already has a
local pool. Using the aforementioned example, our `GyroHandler` will now have the set class
as a member:
```cpp
class GyroHandler: ... {
public:
...
private:
...
GyroPrimaryDataset gyroData;
...
};
```
The constructor used for the creators expects the owner class as a parameter, so we initialize
the object in the `GyroHandler` constructor like this:
```cpp
GyroHandler::GyroHandler(object_id_t objectId, object_id_t comIF,
CookieIF *comCookie, uint8_t switchId):
DeviceHandlerBase(objectId, comIF, comCookie), switchId(switchId),
gyroData(this) {}
```
We need to assign the set to a reply ID used in the `DeviceHandlerBase`.
The combination of the `GyroHandler` object ID and the reply ID will be the 64-bit structure ID
`sid_t` and is used to globally identify the set, for example when requesting housekeeping data or
generating update messages. We need to assign our custom set class in some way so that the local
pool manager can access the custom data sets as well.
By default, the `getDataSetHandle` will take care of this tasks. The default implementation for a
`DeviceHandlerBase` subclass will use the internal command map to retrieve
a handle to a dataset from a given reply ID. Therefore,
we assign the set in the `fillCommandAndReplyMap` function:
```cpp
void GyroHandler::fillCommandAndReplyMap() {
...
this->insertInCommandAndReplyMap(gyrodefs::GYRO_DATA, 3, &gyroData);
...
}
```
Now, we need to create the actual pool entries as well, using the `initializeLocalDataPool`
function. Here, we also immediately subscribe for periodic housekeeping packets
with an interval of 4 seconds. They are still disabled in this example and can be enabled
with a housekeeping service command.
```cpp
ReturnValue_t GyroHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) {
localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_X,
new PoolEntry<float>({0.0}));
localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Y,
new PoolEntry<float>({0.0}));
localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Z,
new PoolEntry<float>({0.0}));
localDataPoolMap.emplace(gyrodefs::GENERAL_CONFIG_REG42,
new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(gyrodefs::RANGE_CONFIG_REG43,
new PoolEntry<uint8_t>({0}));
poolManager.subscribeForPeriodicPacket(gyroData.getSid(), false, 4.0, false);
return HasReturnvaluesIF::RETURN_OK;
}
```
Now, if we receive some sensor data and converted them into the right format,
we can write it into the pool like this, using a guard class to ensure the set is commited back
in any case:
```cpp
PoolReadGuard readHelper(&gyroData);
if(readHelper.getReadResult() == HasReturnvaluesIF::RETURN_OK) {
if(not gyroData.isValid()) {
gyroData.setValidity(true, true);
}
gyroData.angVelocityX = angularVelocityX;
gyroData.angVelocityY = angularVelocityY;
gyroData.angVelocityZ = angularVelocityZ;
}
```
The guard class will commit the changed data on destruction automatically.
### Using the local data pools in a `ExtendedControllerBase` subclass
Coming soon

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -7,6 +7,7 @@ target_sources(${LIB_FSFW_NAME}
PeriodicOperationDivider.cpp PeriodicOperationDivider.cpp
timevalOperations.cpp timevalOperations.cpp
Type.cpp Type.cpp
bitutility.cpp
) )
add_subdirectory(math) add_subdirectory(math)

View File

@ -5,6 +5,15 @@
void arrayprinter::print(const uint8_t *data, size_t size, OutputType type, void arrayprinter::print(const uint8_t *data, size_t size, OutputType type,
bool printInfo, size_t maxCharPerLine) { bool printInfo, size_t maxCharPerLine) {
if(size == 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "Size is zero, nothing to print" << std::endl;
#else
sif::printInfo("Size is zero, nothing to print\n");
#endif
return;
}
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
if(printInfo) { if(printInfo) {
sif::info << "Printing data with size " << size << ": " << std::endl; sif::info << "Printing data with size " << size << ": " << std::endl;

View File

@ -0,0 +1,33 @@
#include "bitutility.h"
void bitutil::bitSet(uint8_t *byte, uint8_t position) {
if(position > 7) {
return;
}
uint8_t shiftNumber = position + (7 - 2 * position);
*byte |= 1 << shiftNumber;
}
void bitutil::bitToggle(uint8_t *byte, uint8_t position) {
if(position > 7) {
return;
}
uint8_t shiftNumber = position + (7 - 2 * position);
*byte ^= 1 << shiftNumber;
}
void bitutil::bitClear(uint8_t *byte, uint8_t position) {
if(position > 7) {
return;
}
uint8_t shiftNumber = position + (7 - 2 * position);
*byte &= ~(1 << shiftNumber);
}
bool bitutil::bitGet(const uint8_t *byte, uint8_t position) {
if(position > 7) {
return false;
}
uint8_t shiftNumber = position + (7 - 2 * position);
return *byte & (1 << shiftNumber);
}

View File

@ -0,0 +1,18 @@
#ifndef FSFW_GLOBALFUNCTIONS_BITUTIL_H_
#define FSFW_GLOBALFUNCTIONS_BITUTIL_H_
#include <cstdint>
namespace bitutil {
/* Helper functions for manipulating the individual bits of a byte.
Position refers to n-th bit of a byte, going from 0 (most significant bit) to
7 (least significant bit) */
void bitSet(uint8_t* byte, uint8_t position);
void bitToggle(uint8_t* byte, uint8_t position);
void bitClear(uint8_t* byte, uint8_t position);
bool bitGet(const uint8_t* byte, uint8_t position);
}
#endif /* FSFW_GLOBALFUNCTIONS_BITUTIL_H_ */

View File

@ -10,7 +10,11 @@
* which are destined to be downlinked into the store. * which are destined to be downlinked into the store.
* @details * @details
* The housekeeping packets are stored into the IPC store and forwarded * The housekeeping packets are stored into the IPC store and forwarded
* to the designated housekeeping handler. * to the designated housekeeping handler. The packet will consist of the following fields
* - SID (8 byte): Structure ID, with the first 4 bytes being the object ID and the last four
* bytes being the set ID
* - Housekeeping Data: The rest of the packet will be the serialized housekeeping data. A validity
* buffer might be appended at the end, depending on the set configuration.
*/ */
class HousekeepingPacketDownlink: public SerialLinkedListAdapter<SerializeIF> { class HousekeepingPacketDownlink: public SerialLinkedListAdapter<SerializeIF> {
public: public:

View File

@ -13,19 +13,21 @@ public:
/** /**
* Header consists of sender ID and command ID. * Header consists of sender ID and command ID.
*/ */
static constexpr size_t HEADER_SIZE = MessageQueueMessageIF::HEADER_SIZE + static constexpr size_t HEADER_SIZE = MessageQueueMessageIF::HEADER_SIZE + sizeof(Command_t);
sizeof(Command_t);
/** /**
* This minimum size is derived from the interface requirement to be able * This minimum size is derived from the interface requirement to be able
* to set a rejected reply, which contains a returnvalue and the initial * to set a rejected reply, which contains a returnvalue and the initial
* command. * command.
*/ */
static constexpr size_t MINIMUM_COMMAND_MESSAGE_SIZE = static constexpr size_t MINIMUM_COMMAND_MESSAGE_SIZE = CommandMessageIF::HEADER_SIZE +
CommandMessageIF::HEADER_SIZE + sizeof(ReturnValue_t) + sizeof(ReturnValue_t) + sizeof(Command_t);
sizeof(Command_t);
static constexpr Command_t makeCommandId(uint8_t messageId, uint8_t uniqueId) {
return ((messageId << 8) | uniqueId);
}
static const uint8_t INTERFACE_ID = CLASS_ID::COMMAND_MESSAGE; static const uint8_t INTERFACE_ID = CLASS_ID::COMMAND_MESSAGE;
static const ReturnValue_t UNKNOWN_COMMAND = MAKE_RETURN_CODE(0x01); static const ReturnValue_t UNKNOWN_COMMAND = MAKE_RETURN_CODE(1);
static const uint8_t MESSAGE_ID = messagetypes::COMMAND; static const uint8_t MESSAGE_ID = messagetypes::COMMAND;
//! Used internally, shall be ignored //! Used internally, shall be ignored

View File

@ -2,33 +2,56 @@
#define FRAMEWORK_IPC_MUTEXHELPER_H_ #define FRAMEWORK_IPC_MUTEXHELPER_H_
#include "MutexFactory.h" #include "MutexFactory.h"
#include "../serviceinterface/ServiceInterfaceStream.h" #include "../serviceinterface/ServiceInterface.h"
class MutexHelper { class MutexHelper {
public: public:
MutexHelper(MutexIF* mutex, MutexIF::TimeoutType timeoutType = MutexHelper(MutexIF* mutex, MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::BLOCKING, uint32_t timeoutMs = 0): MutexIF::TimeoutType::BLOCKING, uint32_t timeoutMs = 0):
internalMutex(mutex) { internalMutex(mutex) {
if(mutex == nullptr) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "MutexHelper: Passed mutex is invalid!" << std::endl;
#else
sif::printError("MutexHelper: Passed mutex is invalid!\n");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
return;
}
ReturnValue_t status = mutex->lockMutex(timeoutType, ReturnValue_t status = mutex->lockMutex(timeoutType,
timeoutMs); timeoutMs);
#if FSFW_VERBOSE_LEVEL >= 1
if(status == MutexIF::MUTEX_TIMEOUT) { if(status == MutexIF::MUTEX_TIMEOUT) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "MutexHelper: Lock of mutex failed with timeout of " sif::error << "MutexHelper: Lock of mutex failed with timeout of "
<< timeoutMs << " milliseconds!" << std::endl; << timeoutMs << " milliseconds!" << std::endl;
#endif #else
sif::printError("MutexHelper: Lock of mutex failed with timeout of %lu milliseconds\n",
timeoutMs);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
} }
else if(status != HasReturnvaluesIF::RETURN_OK) { else if(status != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "MutexHelper: Lock of Mutex failed with code " sif::error << "MutexHelper: Lock of Mutex failed with code " << status << std::endl;
<< status << std::endl; #else
#endif sif::printError("MutexHelper: Lock of Mutex failed with code %d\n", status);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
} }
#else
/* To avoid unused variable warning */
static_cast<void>(status);
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
} }
~MutexHelper() { ~MutexHelper() {
if(internalMutex != nullptr) {
internalMutex->unlockMutex(); internalMutex->unlockMutex();
} }
}
private: private:
MutexIF* internalMutex; MutexIF* internalMutex;
}; };
#endif /* FRAMEWORK_IPC_MUTEXHELPER_H_ */ #endif /* FRAMEWORK_IPC_MUTEXHELPER_H_ */

View File

@ -16,17 +16,27 @@ class HasFileSystemIF {
public: public:
static constexpr uint8_t INTERFACE_ID = CLASS_ID::FILE_SYSTEM; static constexpr uint8_t INTERFACE_ID = CLASS_ID::FILE_SYSTEM;
static constexpr ReturnValue_t FILE_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x00); //! [EXPORT] : P1: Can be file system specific error code
static constexpr ReturnValue_t FILE_ALREADY_EXISTS = MAKE_RETURN_CODE(0x01); static constexpr ReturnValue_t GENERIC_FILE_ERROR = MAKE_RETURN_CODE(0);
static constexpr ReturnValue_t FILE_LOCKED = MAKE_RETURN_CODE(0x02); //! [EXPORT] : File system is currently busy
static constexpr ReturnValue_t IS_BUSY = MAKE_RETURN_CODE(1);
//! [EXPORT] : Invalid parameters like file name or repository path
static constexpr ReturnValue_t INVALID_PARAMETERS = MAKE_RETURN_CODE(2);
static constexpr ReturnValue_t DIRECTORY_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x03); static constexpr ReturnValue_t FILE_DOES_NOT_EXIST = MAKE_RETURN_CODE(5);
static constexpr ReturnValue_t DIRECTORY_ALREADY_EXISTS = MAKE_RETURN_CODE(0x04); static constexpr ReturnValue_t FILE_ALREADY_EXISTS = MAKE_RETURN_CODE(6);
static constexpr ReturnValue_t DIRECTORY_NOT_EMPTY = MAKE_RETURN_CODE(0x05); static constexpr ReturnValue_t FILE_LOCKED = MAKE_RETURN_CODE(7);
static constexpr ReturnValue_t DIRECTORY_DOES_NOT_EXIST = MAKE_RETURN_CODE(10);
static constexpr ReturnValue_t DIRECTORY_ALREADY_EXISTS = MAKE_RETURN_CODE(11);
static constexpr ReturnValue_t DIRECTORY_NOT_EMPTY = MAKE_RETURN_CODE(12);
//! [EXPORT] : P1: Sequence number missing
static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_WRITE = MAKE_RETURN_CODE(15);
//! [EXPORT] : P1: Sequence number missing
static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_READ = MAKE_RETURN_CODE(16);
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() {} virtual ~HasFileSystemIF() {}
/** /**

View File

@ -49,5 +49,11 @@ ReturnValue_t TaskFactory::delayTask(uint32_t delayMs) {
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
void TaskFactory::printMissedDeadline() {
/* TODO: Implement */
return;
}
TaskFactory::TaskFactory() { TaskFactory::TaskFactory() {
} }

View File

@ -1,4 +1,4 @@
#include "../../serviceinterface/ServiceInterfaceStream.h" #include "../../serviceinterface/ServiceInterface.h"
#include "../../timemanager/Clock.h" #include "../../timemanager/Clock.h"
#include <chrono> #include <chrono>

View File

@ -1,5 +1,6 @@
#include "QueueMapManager.h" #include "QueueMapManager.h"
#include "../../serviceinterface/ServiceInterface.h"
#include "../../ipc/MutexFactory.h" #include "../../ipc/MutexFactory.h"
#include "../../ipc/MutexHelper.h" #include "../../ipc/MutexHelper.h"

View File

@ -48,4 +48,9 @@ ReturnValue_t TaskFactory::delayTask(uint32_t delayMs){
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
void TaskFactory::printMissedDeadline() {
/* TODO: Implement */
return;
}

View File

@ -68,20 +68,6 @@ void PeriodicPosixTask::taskFunctionality(void) {
} }
if(not PosixThread::delayUntil(&lastWakeTime, periodMs)){ if(not PosixThread::delayUntil(&lastWakeTime, periodMs)){
char name[20] = {0};
int status = pthread_getname_np(pthread_self(), name, sizeof(name));
if(status == 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PeriodicPosixTask " << name << ": Deadline "
"missed." << std::endl;
#endif
}
else {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PeriodicPosixTask X: Deadline missed. " <<
status << std::endl;
#endif
}
if (this->deadlineMissedFunc != nullptr) { if (this->deadlineMissedFunc != nullptr) {
this->deadlineMissedFunc(); this->deadlineMissedFunc();
} }

View File

@ -39,5 +39,26 @@ ReturnValue_t TaskFactory::delayTask(uint32_t delayMs){
return PosixThread::sleep(delayMs*1000000ull); return PosixThread::sleep(delayMs*1000000ull);
} }
void TaskFactory::printMissedDeadline() {
char name[20] = {0};
int status = pthread_getname_np(pthread_self(), name, sizeof(name));
#if FSFW_CPP_OSTREAM_ENABLED == 1
if(status == 0) {
sif::warning << "task::printMissedDeadline: " << name << "" << std::endl;
}
else {
sif::warning << "task::printMissedDeadline: Unknown task name" << status <<
std::endl;
}
#else
if(status == 0) {
sif::printWarning("task::printMissedDeadline: %s\n", name);
}
else {
sif::printWarning("task::printMissedDeadline: Unknown task name\n", name);
}
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
}
TaskFactory::TaskFactory() { TaskFactory::TaskFactory() {
} }

View File

@ -105,20 +105,28 @@ ReturnValue_t Clock::getClock_usecs(uint64_t* time) {
ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) { ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
/* For all but the last field, the struct will be filled with the correct values */ /* For all but the last field, the struct will be filled with the correct values */
rtems_time_of_day* timeRtems = reinterpret_cast<rtems_time_of_day*>(time); rtems_time_of_day timeRtems;
rtems_status_code status = rtems_clock_get_tod(timeRtems); rtems_status_code status = rtems_clock_get_tod(&timeRtems);
/* The last field now contains the RTEMS ticks of the seconds from 0
to rtems_clock_get_ticks_per_second() minus one. We calculate the microseconds accordingly */
timeRtems->ticks = static_cast<float>(timeRtems->ticks) /
rtems_clock_get_ticks_per_second() * 1e6;
switch (status) { switch (status) {
case RTEMS_SUCCESSFUL: case RTEMS_SUCCESSFUL: {
/* The last field now contains the RTEMS ticks of the seconds from 0
to rtems_clock_get_ticks_per_second() minus one.
We calculate the microseconds accordingly */
time->day = timeRtems.day;
time->hour = timeRtems.hour;
time->minute = timeRtems.minute;
time->month = timeRtems.month;
time->second = timeRtems.second;
time->usecond = static_cast<float>(timeRtems.ticks) /
rtems_clock_get_ticks_per_second() * 1e6;
time->year = timeRtems.year;
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
}
case RTEMS_NOT_DEFINED: case RTEMS_NOT_DEFINED:
//system date and time is not set /* System date and time is not set */
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
case RTEMS_INVALID_ADDRESS: case RTEMS_INVALID_ADDRESS:
//time_buffer is NULL /* time_buffer is NULL */
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
default: default:
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;

View File

@ -44,5 +44,10 @@ ReturnValue_t TaskFactory::delayTask(uint32_t delayMs){
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
void TaskFactory::printMissedDeadline() {
/* TODO: Implement */
return;
}
TaskFactory::TaskFactory() { TaskFactory::TaskFactory() {
} }

View File

@ -65,6 +65,7 @@ enum {
HOUSEKEEPING_MANAGER, //HKM 60 HOUSEKEEPING_MANAGER, //HKM 60
DLE_ENCODER, //DLEE 61 DLE_ENCODER, //DLEE 61
PUS_SERVICE_9, //PUS9 62 PUS_SERVICE_9, //PUS9 62
FILE_SYSTEM, //FILS 63
FW_CLASS_ID_COUNT //is actually count + 1 ! FW_CLASS_ID_COUNT //is actually count + 1 !
}; };

View File

@ -62,6 +62,12 @@ public:
*/ */
static ReturnValue_t delayTask(uint32_t delayMs); static ReturnValue_t delayTask(uint32_t delayMs);
/**
* OS specific implementation to print deadline. In most cases, there is a OS specific
* way to retrieve the task name and print it out as well.
*/
static void printMissedDeadline();
private: private:
/** /**
* External instantiation is not allowed. * External instantiation is not allowed.

View File

@ -396,12 +396,20 @@ ReturnValue_t CCSDSTime::convertToCcsds(OBT_FLP* to, const timeval* from) {
ReturnValue_t CCSDSTime::convertFromCcsds(timeval* to, const uint8_t* from, ReturnValue_t CCSDSTime::convertFromCcsds(timeval* to, const uint8_t* from,
size_t* foundLength, size_t maxLength) { size_t* foundLength, size_t maxLength) {
//We don't expect ascii here. SHOULDDO if(maxLength >= 19) {
Clock::TimeOfDay_t timeOfDay;
/* Try to parse it as ASCII */
ReturnValue_t result = convertFromASCII(&timeOfDay, from, maxLength);
if (result == RETURN_OK) {
return Clock::convertTimeOfDayToTimeval(&timeOfDay, to);
}
}
uint8_t codeIdentification = (*from >> 4); uint8_t codeIdentification = (*from >> 4);
switch (codeIdentification) { switch (codeIdentification) {
//unsupported, as Leap second correction would have to be applied /* Unsupported, as Leap second correction would have to be applied */
// case CUC_LEVEL1: case CUC_LEVEL1:
// return convertFromCUC(to, from, foundLength, maxLength); return UNSUPPORTED_TIME_FORMAT;
case CDS: case CDS:
return convertFromCDS(to, from, foundLength, maxLength); return convertFromCDS(to, from, foundLength, maxLength);
case CCS: case CCS:

View File

@ -70,7 +70,7 @@ TEST_CASE( "Action Helper" , "[ActionHelper]") {
SECTION("Handle finish"){ SECTION("Handle finish"){
CHECK(not testMqMock.wasMessageSent()); CHECK(not testMqMock.wasMessageSent());
ReturnValue_t status = 0x9876; ReturnValue_t status = 0x9876;
actionHelper.finish(testMqMock.getId(), testActionId, status); actionHelper.finish(true, testMqMock.getId(), testActionId, status);
CHECK(testMqMock.wasMessageSent()); CHECK(testMqMock.wasMessageSent());
CommandMessage testMessage; CommandMessage testMessage;
REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK)); REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK));

View File

@ -1,8 +1,13 @@
#include "LocalPoolOwnerBase.h" #include "LocalPoolOwnerBase.h"
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h> #include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h> #include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/datapool/PoolReadHelper.h>
#include <fsfw/globalfunctions/bitutility.h>
#include <unittest/core/CatchDefinitions.h> #include <unittest/core/CatchDefinitions.h>
TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { TEST_CASE("LocalDataSet" , "[LocDataSetTest]") {
@ -12,11 +17,193 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") {
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK); REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation()
== retval::CATCH_OK); == retval::CATCH_OK);
const uint32_t setId = 0; LocalPoolStaticTestDataSet localSet;
SECTION("BasicTest") { SECTION("BasicTest") {
StaticLocalDataSet<3> localSet = StaticLocalDataSet<3>( /* Test some basic functions */
sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE, setId)); CHECK(localSet.getLocalPoolIdsSerializedSize(false) == 3 * sizeof(lp_id_t));
CHECK(localSet.getLocalPoolIdsSerializedSize(true) ==
3 * sizeof(lp_id_t) + sizeof(uint8_t));
CHECK(localSet.getSid() == lpool::testSid);
CHECK(localSet.getCreatorObjectId() == objects::TEST_LOCAL_POOL_OWNER_BASE);
size_t maxSize = localSet.getLocalPoolIdsSerializedSize(true);
uint8_t localPoolIdBuff[maxSize];
/* Skip size field */
lp_id_t* lpIds = reinterpret_cast<lp_id_t*>(localPoolIdBuff + 1);
size_t serSize = 0;
uint8_t *localPoolIdBuffPtr = reinterpret_cast<uint8_t*>(localPoolIdBuff);
/* Test local pool ID serialization */
CHECK(localSet.serializeLocalPoolIds(&localPoolIdBuffPtr, &serSize,
maxSize, SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
CHECK(serSize == maxSize);
CHECK(localPoolIdBuff[0] == 3);
CHECK(lpIds[0] == localSet.localPoolVarUint8.getDataPoolId());
CHECK(lpIds[1] == localSet.localPoolVarFloat.getDataPoolId());
CHECK(lpIds[2] == localSet.localPoolUint16Vec.getDataPoolId());
/* Now serialize without fill count */
lpIds = reinterpret_cast<lp_id_t*>(localPoolIdBuff);
localPoolIdBuffPtr = localPoolIdBuff;
serSize = 0;
CHECK(localSet.serializeLocalPoolIds(&localPoolIdBuffPtr, &serSize,
maxSize, SerializeIF::Endianness::MACHINE, false) == retval::CATCH_OK);
CHECK(serSize == maxSize - sizeof(uint8_t));
CHECK(lpIds[0] == localSet.localPoolVarUint8.getDataPoolId());
CHECK(lpIds[1] == localSet.localPoolVarFloat.getDataPoolId());
CHECK(lpIds[2] == localSet.localPoolUint16Vec.getDataPoolId());
{
/* Test read operation. Values should be all zeros */
PoolReadHelper readHelper(&localSet);
REQUIRE(readHelper.getReadResult() == retval::CATCH_OK);
CHECK(not localSet.isValid());
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(not localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0));
CHECK(not localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolUint16Vec.value[0] == 0);
CHECK(localSet.localPoolUint16Vec.value[1] == 0);
CHECK(localSet.localPoolUint16Vec.value[2] == 0);
CHECK(not localSet.localPoolVarUint8.isValid());
/* Now set new values, commit should be done by read helper automatically */
localSet.localPoolVarUint8 = 232;
localSet.localPoolVarFloat = -2324.322;
localSet.localPoolUint16Vec.value[0] = 232;
localSet.localPoolUint16Vec.value[1] = 23923;
localSet.localPoolUint16Vec.value[2] = 1;
localSet.setValidity(true, true);
} }
/* Zero out some values for next test */
localSet.localPoolVarUint8 = 0;
localSet.localPoolVarFloat = 0;
{
/* Now we read again and check whether our zeroed values were overwritten with
the values in the pool */
PoolReadHelper readHelper(&localSet);
REQUIRE(readHelper.getReadResult() == retval::CATCH_OK);
CHECK(localSet.isValid());
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 */
localSet.setValidityBufferGeneration(false);
maxSize = localSet.getSerializedSize();
CHECK(maxSize == sizeof(uint8_t) + sizeof(uint16_t) * 3 + sizeof(float));
serSize = 0;
/* Already reserve additional space for validity buffer, will be needed later */
uint8_t buffer[maxSize + 1];
uint8_t* buffPtr = buffer;
CHECK(localSet.serialize(&buffPtr, &serSize, maxSize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
uint8_t rawUint8 = buffer[0];
CHECK(rawUint8 == 232);
float rawFloat = 0.0;
std::memcpy(&rawFloat, buffer + sizeof(uint8_t), sizeof(float));
CHECK(rawFloat == Catch::Approx(-2324.322));
uint16_t rawUint16Vec[3];
std::memcpy(&rawUint16Vec, buffer + sizeof(uint8_t) + sizeof(float),
3 * sizeof(uint16_t));
CHECK(rawUint16Vec[0] == 232);
CHECK(rawUint16Vec[1] == 23923);
CHECK(rawUint16Vec[2] == 1);
size_t sizeToDeserialize = maxSize;
/* Now we zeros out the raw entries and deserialize back into the dataset */
std::memset(buffer, 0, sizeof(buffer));
const uint8_t* constBuffPtr = buffer;
CHECK(localSet.deSerialize(&constBuffPtr, &sizeToDeserialize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
/* Check whether deserialization was successfull */
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0));
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(localSet.localPoolUint16Vec.value[0] == 0);
CHECK(localSet.localPoolUint16Vec.value[1] == 0);
CHECK(localSet.localPoolUint16Vec.value[2] == 0);
/* Validity should be unchanged */
CHECK(localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolVarFloat.isValid());
CHECK(localSet.localPoolUint16Vec.isValid());
/* Now we do the same process but with the validity buffer */
localSet.localPoolVarUint8 = 232;
localSet.localPoolVarFloat = -2324.322;
localSet.localPoolUint16Vec.value[0] = 232;
localSet.localPoolUint16Vec.value[1] = 23923;
localSet.localPoolUint16Vec.value[2] = 1;
localSet.localPoolVarUint8.setValid(true);
localSet.localPoolVarFloat.setValid(false);
localSet.localPoolUint16Vec.setValid(true);
localSet.setValidityBufferGeneration(true);
maxSize = localSet.getSerializedSize();
CHECK(maxSize == sizeof(uint8_t) + sizeof(uint16_t) * 3 + sizeof(float) + 1);
serSize = 0;
buffPtr = buffer;
CHECK(localSet.serialize(&buffPtr, &serSize, maxSize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
CHECK(rawUint8 == 232);
std::memcpy(&rawFloat, buffer + sizeof(uint8_t), sizeof(float));
CHECK(rawFloat == Catch::Approx(-2324.322));
std::memcpy(&rawUint16Vec, buffer + sizeof(uint8_t) + sizeof(float),
3 * sizeof(uint16_t));
CHECK(rawUint16Vec[0] == 232);
CHECK(rawUint16Vec[1] == 23923);
CHECK(rawUint16Vec[2] == 1);
/* We can do it like this because the buffer only has one byte for
less than 8 variables */
uint8_t* validityByte = buffer + sizeof(buffer) - 1;
CHECK(bitutil::bitGet(validityByte, 0) == true);
CHECK(bitutil::bitGet(validityByte, 1) == false);
CHECK(bitutil::bitGet(validityByte, 2) == true);
/* Now we manipulate the validity buffer for the deserialization */
bitutil::bitClear(validityByte, 0);
bitutil::bitSet(validityByte, 1);
bitutil::bitClear(validityByte, 2);
/* Zero out everything except validity buffer */
std::memset(buffer, 0, sizeof(buffer) - 1);
sizeToDeserialize = maxSize;
constBuffPtr = buffer;
CHECK(localSet.deSerialize(&constBuffPtr, &sizeToDeserialize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
/* Check whether deserialization was successfull */
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0));
CHECK(localSet.localPoolVarUint8.value == 0);
CHECK(localSet.localPoolUint16Vec.value[0] == 0);
CHECK(localSet.localPoolUint16Vec.value[1] == 0);
CHECK(localSet.localPoolUint16Vec.value[2] == 0);
CHECK(not localSet.localPoolVarUint8.isValid());
CHECK(localSet.localPoolVarFloat.isValid());
CHECK(not localSet.localPoolUint16Vec.isValid());
}
/* Common fault test cases */
LocalPoolObjectBase* variableHandle = poolOwner->getPoolObjectHandle(lpool::uint32VarId);
CHECK(variableHandle != nullptr);
CHECK(localSet.registerVariable(variableHandle) ==
static_cast<int>(DataSetIF::DATA_SET_FULL));
variableHandle = nullptr;
REQUIRE(localSet.registerVariable(variableHandle) ==
static_cast<int>(DataSetIF::POOL_VAR_NULL));
}
/* we need to reset the subscription list because the pool owner
is a global object. */
CHECK(poolOwner->reset() == retval::CATCH_OK);
} }

View File

@ -160,7 +160,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
CHECK(messageSent.getCommand() == static_cast<int>( CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE)); HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE));
/* Now subscribe for the dataset update (HK and update) again with subscription interface */ /* Now subscribe for the dataset update (HK and update) again with subscription interface */
REQUIRE(subscriptionIF->subscribeForSetUpdateMessages(lpool::testSetId, REQUIRE(subscriptionIF->subscribeForSetUpdateMessage(lpool::testSetId,
objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, false) == retval::CATCH_OK); objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, false) == retval::CATCH_OK);
REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK); REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK);
@ -192,7 +192,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
/* we need to reset the subscription list because the pool owner /* we need to reset the subscription list because the pool owner
is a global object. */ is a global object. */
poolOwner->resetSubscriptionList(); CHECK(poolOwner->reset() == retval::CATCH_OK);
mqMock->clearMessages(true); mqMock->clearMessages(true);
} }

View File

@ -10,6 +10,7 @@
#include <testcfg/objects/systemObjectList.h> #include <testcfg/objects/systemObjectList.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h> #include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/unittest/tests/mocks/MessageQueueMockBase.h> #include <fsfw/unittest/tests/mocks/MessageQueueMockBase.h>
#include "../../../datapool/PoolReadHelper.h"
namespace lpool { namespace lpool {
static constexpr lp_id_t uint8VarId = 0; static constexpr lp_id_t uint8VarId = 0;
@ -31,6 +32,23 @@ static const gp_id_t uint64Vec2Id = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE,
} }
class LocalPoolStaticTestDataSet: public StaticLocalDataSet<3> {
public:
LocalPoolStaticTestDataSet():
StaticLocalDataSet(lpool::testSid) {
}
LocalPoolStaticTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId):
StaticLocalDataSet(owner, setId) {
}
lp_var_t<uint8_t> localPoolVarUint8 = lp_var_t<uint8_t>(lpool::uint8VarGpid, this);
lp_var_t<float> localPoolVarFloat = lp_var_t<float>(lpool::floatVarGpid, this);
lp_vec_t<uint16_t, 3> localPoolUint16Vec = lp_vec_t<uint16_t, 3>(lpool::uint16Vec3Gpid, this);
private:
};
class LocalPoolTestDataSet: public LocalDataSet { class LocalPoolTestDataSet: public LocalDataSet {
public: public:
LocalPoolTestDataSet(): LocalPoolTestDataSet():
@ -41,19 +59,6 @@ public:
LocalDataSet(owner, setId, lpool::dataSetMaxVariables) { LocalDataSet(owner, setId, lpool::dataSetMaxVariables) {
} }
// ReturnValue_t assignPointers() {
// PoolVariableIF** rawVarArray = getContainer();
// localPoolVarUint8 = dynamic_cast<lp_var_t<uint8_t>*>(rawVarArray[0]);
// localPoolVarFloat = dynamic_cast<lp_var_t<float>*>(rawVarArray[1]);
// localPoolUint16Vec = dynamic_cast<lp_vec_t<uint16_t, 3>*>(
// rawVarArray[2]);
// if(localPoolVarUint8 == nullptr or localPoolVarFloat == nullptr or
// localPoolUint16Vec == nullptr) {
// return HasReturnvaluesIF::RETURN_FAILED;
// }
// return HasReturnvaluesIF::RETURN_OK;
// }
lp_var_t<uint8_t> localPoolVarUint8 = lp_var_t<uint8_t>(lpool::uint8VarGpid, this); lp_var_t<uint8_t> localPoolVarUint8 = lp_var_t<uint8_t>(lpool::uint8VarGpid, this);
lp_var_t<float> localPoolVarFloat = lp_var_t<float>(lpool::floatVarGpid, this); lp_var_t<float> localPoolVarFloat = lp_var_t<float>(lpool::floatVarGpid, this);
lp_vec_t<uint16_t, 3> localPoolUint16Vec = lp_vec_t<uint16_t, 3>(lpool::uint16Vec3Gpid, this); lp_vec_t<uint16_t, 3> localPoolUint16Vec = lp_vec_t<uint16_t, 3>(lpool::uint16Vec3Gpid, this);
@ -164,25 +169,62 @@ public:
} }
ReturnValue_t subscribeWrapperSetUpdate() { ReturnValue_t subscribeWrapperSetUpdate() {
return poolManager.subscribeForSetUpdateMessages(lpool::testSetId, return poolManager.subscribeForSetUpdateMessage(lpool::testSetId,
objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, false); objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, false);
} }
ReturnValue_t subscribeWrapperSetUpdateSnapshot() { ReturnValue_t subscribeWrapperSetUpdateSnapshot() {
return poolManager.subscribeForSetUpdateMessages(lpool::testSetId, return poolManager.subscribeForSetUpdateMessage(lpool::testSetId,
objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, true); objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, true);
} }
ReturnValue_t subscribeWrapperSetUpdateHk(bool diagnostics = false) { ReturnValue_t subscribeWrapperSetUpdateHk(bool diagnostics = false) {
return poolManager.subscribeForUpdatePackets(lpool::testSid, diagnostics, return poolManager.subscribeForUpdatePacket(lpool::testSid, diagnostics,
false, objects::HK_RECEIVER_MOCK); false, objects::HK_RECEIVER_MOCK);
} }
ReturnValue_t subscribeWrapperVariableUpdate(lp_id_t localPoolId) { ReturnValue_t subscribeWrapperVariableUpdate(lp_id_t localPoolId) {
return poolManager.subscribeForVariableUpdateMessages(localPoolId, return poolManager.subscribeForVariableUpdateMessage(localPoolId,
MessageQueueIF::NO_QUEUE, objects::HK_RECEIVER_MOCK, false); MessageQueueIF::NO_QUEUE, objects::HK_RECEIVER_MOCK, false);
} }
ReturnValue_t reset() {
resetSubscriptionList();
ReturnValue_t status = HasReturnvaluesIF::RETURN_OK;
{
PoolReadGuard readHelper(&dataset);
if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) {
status = readHelper.getReadResult();
}
dataset.localPoolVarUint8.value = 0;
dataset.localPoolVarFloat.value = 0.0;
dataset.localPoolUint16Vec.value[0] = 0;
dataset.localPoolUint16Vec.value[1] = 0;
dataset.localPoolUint16Vec.value[2] = 0;
dataset.setValidity(false, true);
}
{
PoolReadGuard readHelper(&testUint32);
if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) {
status = readHelper.getReadResult();
}
testUint32.value = 0;
testUint32.setValid(false);
}
{
PoolReadGuard readHelper(&testInt64Vec);
if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) {
status = readHelper.getReadResult();
}
testInt64Vec.value[0] = 0;
testInt64Vec.value[1] = 0;
testInt64Vec.setValid(false);
}
return status;
}
void resetSubscriptionList() { void resetSubscriptionList() {
poolManager.clearReceiversList(); poolManager.clearReceiversList();
} }
@ -191,14 +233,12 @@ public:
LocalPoolTestDataSet dataset; LocalPoolTestDataSet dataset;
private: private:
lp_var_t<uint8_t> testUint8 = lp_var_t<uint8_t>(this, lpool::uint8VarId, lp_var_t<uint8_t> testUint8 = lp_var_t<uint8_t>(this, lpool::uint8VarId);
&dataset); lp_var_t<float> testFloat = lp_var_t<float>(this, lpool::floatVarId);
lp_var_t<float> testFloat = lp_var_t<float>(this, lpool::floatVarId,
&dataset);
lp_var_t<uint32_t> testUint32 = lp_var_t<uint32_t>(this, lpool::uint32VarId); lp_var_t<uint32_t> testUint32 = lp_var_t<uint32_t>(this, lpool::uint32VarId);
lp_vec_t<uint16_t, 3> testUint16Vec = lp_vec_t<uint16_t, 3>(this, lp_vec_t<uint16_t, 3> testUint16Vec = lp_vec_t<uint16_t, 3>(this,
lpool::uint16Vec3Id, &dataset); lpool::uint16Vec3Id);
lp_vec_t<int64_t, 2> testInt64Vec = lp_vec_t<int64_t, 2>(this, lp_vec_t<int64_t, 2> testInt64Vec = lp_vec_t<int64_t, 2>(this,
lpool::int64Vec2Id); lpool::int64Vec2Id);

View File

@ -118,6 +118,8 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") {
lpool::uint8VarId); lpool::uint8VarId);
} }
CHECK(poolOwner->reset() == retval::CATCH_OK);
} }