Merge pull request 'FSFW Update' (#4) from mueller/master into eive/develop

Reviewed-on: #4
This commit is contained in:
Jakob Meier 2021-03-19 15:40:15 +01:00
commit 32b289cbec
50 changed files with 2222 additions and 1667 deletions

View File

@ -22,7 +22,9 @@ a C file without issues
### 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
@ -40,7 +42,8 @@ important use-case)
### 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
@ -52,7 +55,8 @@ ID for now.
### Device Handler Base
- There is an additional `PERFORM_OPERATION` step for the device handler base. It is important
that DHB users adapt their polling sequence tables to perform this step. This steps allows for aclear distinction between operation and communication steps
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
- getTransitionDelayMs is now an abstract method
@ -69,7 +73,8 @@ now
### 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
@ -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.
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
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);
}
void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId,
void ActionHelper::finish(bool success, MessageQueueId_t reportTo, ActionId_t commandId,
ReturnValue_t result) {
CommandMessage reply;
ActionMessage::setCompletionReply(&reply, commandId, result);
ActionMessage::setCompletionReply(&reply, commandId, success, result);
queueToUse->sendMessage(reportTo, &reply);
}
@ -69,7 +69,7 @@ void ActionHelper::prepareExecution(MessageQueueId_t commandedBy,
ipcStore->deleteData(dataAddress);
if(result == HasActionsIF::EXECUTION_FINISHED) {
CommandMessage reply;
ActionMessage::setCompletionReply(&reply, actionId, result);
ActionMessage::setCompletionReply(&reply, actionId, true, result);
queueToUse->sendMessage(commandedBy, &reply);
}
if (result != HasReturnvaluesIF::RETURN_OK) {

View File

@ -62,12 +62,12 @@ public:
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
/**
* 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 commandId ID of the executed command
* @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);
/**
* 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,
ActionId_t fid, ReturnValue_t result) {
if (result == HasReturnvaluesIF::RETURN_OK or result == HasActionsIF::EXECUTION_FINISHED) {
ActionId_t fid, bool success, ReturnValue_t result) {
if (success) {
message->setCommand(COMPLETION_SUCCESS);
} else {
}
else {
message->setCommand(COMPLETION_FAILED);
}
message->setParameter(fid);

View File

@ -24,11 +24,14 @@ public:
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_FAILED = MAKE_COMMAND_ID(6);
virtual ~ActionMessage();
static void setCommand(CommandMessage* message, ActionId_t fid,
store_address_t parameters);
static ActionId_t getActionId(const CommandMessage* message );
static store_address_t getStoreId(const CommandMessage* message );
static store_address_t getStoreId(const CommandMessage* message);
static void setStepReply(CommandMessage* message, ActionId_t fid,
uint8_t step, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
static uint8_t getStep(const CommandMessage* message );
@ -36,7 +39,8 @@ public:
static void setDataReply(CommandMessage* message, ActionId_t actionId,
store_address_t data);
static void setCompletionReply(CommandMessage* message, ActionId_t fid,
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
bool success, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
static void clear(CommandMessage* message);
};

View File

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

View File

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

View File

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

View File

@ -1,5 +1,8 @@
#include "PoolDataSetBase.h"
#include "../serviceinterface/ServiceInterfaceStream.h"
#include "ReadCommitIFAttorney.h"
#include "../serviceinterface/ServiceInterface.h"
#include <cstring>
PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
@ -11,62 +14,66 @@ PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
PoolDataSetBase::~PoolDataSetBase() {}
ReturnValue_t PoolDataSetBase::registerVariable(
PoolVariableIF *variable) {
if (state != States::STATE_SET_UNINITIALISED) {
ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF *variable) {
if (state != States::STATE_SET_UNINITIALISED) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::registerVariable: "
"Call made in wrong position." << std::endl;
sif::error << "DataSet::registerVariable: Call made in wrong position." << std::endl;
#else
sif::printError("DataSet::registerVariable: Call made in wrong position.");
#endif
return DataSetIF::DATA_SET_UNINITIALISED;
}
if (variable == nullptr) {
return DataSetIF::DATA_SET_UNINITIALISED;
}
if (variable == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::registerVariable: "
"Pool variable is nullptr." << std::endl;
sif::error << "DataSet::registerVariable: Pool variable is nullptr." << std::endl;
#else
sif::printError("DataSet::registerVariable: Pool variable is nullptr.\n");
#endif
return DataSetIF::POOL_VAR_NULL;
}
if (fillCount >= maxFillCount) {
return DataSetIF::POOL_VAR_NULL;
}
if (fillCount >= maxFillCount) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::registerVariable: "
"DataSet is full." << std::endl;
sif::error << "DataSet::registerVariable: DataSet is full." << std::endl;
#else
sif::printError("DataSet::registerVariable: DataSet is full.\n");
#endif
return DataSetIF::DATA_SET_FULL;
}
registeredVariables[fillCount] = variable;
fillCount++;
return HasReturnvaluesIF::RETURN_OK;
return DataSetIF::DATA_SET_FULL;
}
registeredVariables[fillCount] = variable;
fillCount++;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t PoolDataSetBase::read(MutexIF::TimeoutType timeoutType,
uint32_t lockTimeout) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
ReturnValue_t error = result;
if (state == States::STATE_SET_UNINITIALISED) {
lockDataPool(timeoutType, lockTimeout);
for (uint16_t count = 0; count < fillCount; count++) {
result = readVariable(count);
if(result != RETURN_OK) {
error = result;
}
}
state = States::STATE_SET_WAS_READ;
unlockDataPool();
}
else {
uint32_t lockTimeout) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
ReturnValue_t error = result;
if (state == States::STATE_SET_UNINITIALISED) {
lockDataPool(timeoutType, lockTimeout);
for (uint16_t count = 0; count < fillCount; count++) {
result = readVariable(count);
if(result != RETURN_OK) {
error = result;
}
}
state = States::STATE_SET_WAS_READ;
unlockDataPool();
}
else {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::read(): "
"Call made in wrong position. Don't forget to commit"
" member datasets!" << std::endl;
#endif
result = SET_WAS_ALREADY_READ;
}
sif::error << "DataSet::read(): Call made in wrong position. Don't forget to commit"
" member datasets!" << std::endl;
#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;
}
if(error != HasReturnvaluesIF::RETURN_OK) {
result = error;
}
return result;
if(error != HasReturnvaluesIF::RETURN_OK) {
result = error;
}
return result;
}
uint16_t PoolDataSetBase::getFillCount() const {
@ -74,144 +81,136 @@ uint16_t PoolDataSetBase::getFillCount() const {
}
ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
if(registeredVariables[count] == nullptr) {
// configuration error.
return HasReturnvaluesIF::RETURN_FAILED;
}
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
if(registeredVariables[count] == nullptr) {
/* Configuration error. */
return HasReturnvaluesIF::RETURN_FAILED;
}
// These checks are often performed by the respective
// variable implementation too, but I guess a double check does not hurt.
if (registeredVariables[count]->getReadWriteMode() !=
PoolVariableIF::VAR_WRITE and
registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER)
{
if(protectEveryReadCommitCall) {
result = registeredVariables[count]->read(
timeoutTypeForSingleVars,
mutexTimeoutForSingleVars);
}
else {
result = registeredVariables[count]->readWithoutLock();
}
/* These checks are often performed by the respective variable implementation too, but I guess
a double check does not hurt. */
if (registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_WRITE and
registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER) {
if(protectEveryReadCommitCall) {
result = registeredVariables[count]->read(timeoutTypeForSingleVars,
mutexTimeoutForSingleVars);
}
else {
/* The readWithoutLock function is protected, so we use the attorney here */
result = ReadCommitIFAttorney::readWithoutLock(registeredVariables[count]);
}
if(result != HasReturnvaluesIF::RETURN_OK) {
result = INVALID_PARAMETER_DEFINITION;
}
}
return result;
if(result != HasReturnvaluesIF::RETURN_OK) {
result = INVALID_PARAMETER_DEFINITION;
}
}
return result;
}
ReturnValue_t PoolDataSetBase::commit(MutexIF::TimeoutType timeoutType,
uint32_t lockTimeout) {
if (state == States::STATE_SET_WAS_READ) {
handleAlreadyReadDatasetCommit(timeoutType, lockTimeout);
return HasReturnvaluesIF::RETURN_OK;
}
else {
return handleUnreadDatasetCommit(timeoutType, lockTimeout);
}
uint32_t lockTimeout) {
if (state == States::STATE_SET_WAS_READ) {
handleAlreadyReadDatasetCommit(timeoutType, lockTimeout);
return HasReturnvaluesIF::RETURN_OK;
}
else {
return handleUnreadDatasetCommit(timeoutType, lockTimeout);
}
}
void PoolDataSetBase::handleAlreadyReadDatasetCommit(
MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) {
lockDataPool(timeoutType, lockTimeout);
for (uint16_t count = 0; count < fillCount; count++) {
if (registeredVariables[count]->getReadWriteMode()
!= PoolVariableIF::VAR_READ
&& registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER) {
if(protectEveryReadCommitCall) {
registeredVariables[count]->commit(
timeoutTypeForSingleVars,
mutexTimeoutForSingleVars);
}
else {
registeredVariables[count]->commitWithoutLock();
}
}
}
state = States::STATE_SET_UNINITIALISED;
unlockDataPool();
MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) {
lockDataPool(timeoutType, lockTimeout);
for (uint16_t count = 0; count < fillCount; count++) {
if ((registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_READ) and
(registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER)) {
if(protectEveryReadCommitCall) {
registeredVariables[count]->commit(timeoutTypeForSingleVars,
mutexTimeoutForSingleVars);
}
else {
/* The commitWithoutLock function is protected, so we use the attorney here */
ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]);
}
}
}
state = States::STATE_SET_UNINITIALISED;
unlockDataPool();
}
ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit(
MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
lockDataPool(timeoutType, lockTimeout);
for (uint16_t count = 0; count < fillCount; count++) {
if (registeredVariables[count]->getReadWriteMode()
== PoolVariableIF::VAR_WRITE
&& registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER) {
if(protectEveryReadCommitCall) {
result = registeredVariables[count]->commit(
timeoutTypeForSingleVars,
mutexTimeoutForSingleVars);
}
else {
result = registeredVariables[count]->commitWithoutLock();
}
MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
lockDataPool(timeoutType, lockTimeout);
for (uint16_t count = 0; count < fillCount; count++) {
if ((registeredVariables[count]->getReadWriteMode() == PoolVariableIF::VAR_WRITE) and
(registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER)) {
if(protectEveryReadCommitCall) {
result = registeredVariables[count]->commit(timeoutTypeForSingleVars,
mutexTimeoutForSingleVars);
}
else {
/* The commitWithoutLock function is protected, so we use the attorney here */
ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]);
}
} else if (registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER) {
if (result != COMMITING_WITHOUT_READING) {
} else if (registeredVariables[count]->getDataPoolId()
!= PoolVariableIF::NO_PARAMETER) {
if (result != COMMITING_WITHOUT_READING) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::commit(): commit-without-read call made "
"with non write-only variable." << std::endl;
sif::error << "DataSet::commit(): commit-without-read call made "
"with non write-only variable." << std::endl;
#endif
result = COMMITING_WITHOUT_READING;
}
}
}
state = States::STATE_SET_UNINITIALISED;
unlockDataPool();
return result;
result = COMMITING_WITHOUT_READING;
}
}
}
state = States::STATE_SET_UNINITIALISED;
unlockDataPool();
return result;
}
ReturnValue_t PoolDataSetBase::lockDataPool(MutexIF::TimeoutType timeoutType,
uint32_t lockTimeout) {
return HasReturnvaluesIF::RETURN_OK;
uint32_t lockTimeout) {
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t PoolDataSetBase::unlockDataPool() {
return HasReturnvaluesIF::RETURN_OK;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size,
const size_t maxSize, SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
for (uint16_t count = 0; count < fillCount; count++) {
result = registeredVariables[count]->serialize(buffer, size, maxSize,
streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
}
return result;
const size_t maxSize, SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
for (uint16_t count = 0; count < fillCount; count++) {
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
}
return result;
}
ReturnValue_t PoolDataSetBase::deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
for (uint16_t count = 0; count < fillCount; count++) {
result = registeredVariables[count]->deSerialize(buffer, size,
streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
}
return result;
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
for (uint16_t count = 0; count < fillCount; count++) {
result = registeredVariables[count]->deSerialize(buffer, size,
streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
}
return result;
}
size_t PoolDataSetBase::getSerializedSize() const {
uint32_t size = 0;
for (uint16_t count = 0; count < fillCount; count++) {
size += registeredVariables[count]->getSerializedSize();
}
return size;
uint32_t size = 0;
for (uint16_t count = 0; count < fillCount; count++) {
size += registeredVariables[count]->getSerializedSize();
}
return size;
}
void PoolDataSetBase::setContainer(PoolVariableIF **variablesContainer) {
@ -219,13 +218,13 @@ void PoolDataSetBase::setContainer(PoolVariableIF **variablesContainer) {
}
PoolVariableIF** PoolDataSetBase::getContainer() const {
return registeredVariables;
return registeredVariables;
}
void PoolDataSetBase::setReadCommitProtectionBehaviour(
bool protectEveryReadCommit, MutexIF::TimeoutType timeoutType,
uint32_t mutexTimeout) {
this->protectEveryReadCommitCall = protectEveryReadCommit;
this->timeoutTypeForSingleVars = timeoutType;
this->mutexTimeoutForSingleVars = mutexTimeout;
bool protectEveryReadCommit, MutexIF::TimeoutType timeoutType,
uint32_t mutexTimeout) {
this->protectEveryReadCommitCall = protectEveryReadCommit;
this->timeoutTypeForSingleVars = timeoutType;
this->mutexTimeoutForSingleVars = mutexTimeout;
}

View File

@ -29,98 +29,99 @@
* @author Bastian Baetz
* @ingroup data_pool
*/
class PoolDataSetBase: public PoolDataSetIF,
public SerializeIF,
public HasReturnvaluesIF {
class PoolDataSetBase:
public PoolDataSetIF,
public SerializeIF,
public HasReturnvaluesIF {
public:
/**
* @brief Creates an empty dataset. Use registerVariable or
* supply a pointer to this dataset to PoolVariable
* initializations to register pool variables.
*/
PoolDataSetBase(PoolVariableIF** registeredVariablesArray, const size_t maxFillCount);
/**
* @brief Creates an empty dataset. Use registerVariable or
* supply a pointer to this dataset to PoolVariable
* initializations to register pool variables.
*/
PoolDataSetBase(PoolVariableIF** registeredVariablesArray, const size_t maxFillCount);
/* Forbidden for now */
PoolDataSetBase(const PoolDataSetBase& otherSet) = delete;
const PoolDataSetBase& operator=(const PoolDataSetBase& otherSet) = delete;
/* Forbidden for now */
PoolDataSetBase(const PoolDataSetBase& otherSet) = delete;
const PoolDataSetBase& operator=(const PoolDataSetBase& otherSet) = delete;
virtual~ PoolDataSetBase();
virtual~ PoolDataSetBase();
/**
* @brief The read call initializes reading out all registered variables.
* It is mandatory to call commit after every read call!
* @details
* It iterates through the list of registered variables and calls all read()
* functions of the registered pool variables (which read out their values
* from the data pool) which are not write-only.
* In case of an error (e.g. a wrong data type, or an invalid data pool id),
* the operation is aborted and @c INVALID_PARAMETER_DEFINITION returned.
*
* The data pool is locked during the whole read operation and
* freed afterwards. It is mandatory to call commit after a read call,
* even if the read operation is not successful!
* @return
* - @c RETURN_OK if all variables were read successfully.
* - @c INVALID_PARAMETER_DEFINITION if a pool entry does not exist or there
* is a type conflict.
* - @c SET_WAS_ALREADY_READ if read() is called twice without calling
* commit() in between
*/
virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t lockTimeout = 20) override;
/**
* @brief The commit call initializes writing back the registered variables.
* @details
* It iterates through the list of registered variables and calls the
* commit() method of the remaining registered variables (which write back
* their values to the pool).
*
* The data pool is locked during the whole commit operation and
* freed afterwards. The state changes to "was committed" after this operation.
*
* If the set does contain at least one variable which is not write-only
* commit() can only be called after read(). If the set only contains
* variables which are write only, commit() can be called without a
* preceding read() call. Every read call must be followed by a commit call!
* @return - @c RETURN_OK if all variables were read successfully.
* - @c COMMITING_WITHOUT_READING if set was not read yet and
* contains non write-only variables
*/
virtual ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t lockTimeout = 20) override;
/**
* @brief The read call initializes reading out all registered variables.
* It is mandatory to call commit after every read call!
* @details
* It iterates through the list of registered variables and calls all read()
* functions of the registered pool variables (which read out their values
* from the data pool) which are not write-only.
* In case of an error (e.g. a wrong data type, or an invalid data pool id),
* the operation is aborted and @c INVALID_PARAMETER_DEFINITION returned.
*
* The data pool is locked during the whole read operation and
* freed afterwards. It is mandatory to call commit after a read call,
* even if the read operation is not successful!
* @return
* - @c RETURN_OK if all variables were read successfully.
* - @c INVALID_PARAMETER_DEFINITION if a pool entry does not exist or there
* is a type conflict.
* - @c SET_WAS_ALREADY_READ if read() is called twice without calling
* commit() in between
*/
virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t lockTimeout = 20) override;
/**
* @brief The commit call initializes writing back the registered variables.
* @details
* It iterates through the list of registered variables and calls the
* commit() method of the remaining registered variables (which write back
* their values to the pool).
*
* The data pool is locked during the whole commit operation and
* freed afterwards. The state changes to "was committed" after this operation.
*
* If the set does contain at least one variable which is not write-only
* commit() can only be called after read(). If the set only contains
* variables which are write only, commit() can be called without a
* preceding read() call. Every read call must be followed by a commit call!
* @return - @c RETURN_OK if all variables were read successfully.
* - @c COMMITING_WITHOUT_READING if set was not read yet and
* contains non write-only variables
*/
virtual ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t lockTimeout = 20) override;
/**
* Register the passed pool variable instance into the data set.
* @param variable
* @return
*/
virtual ReturnValue_t registerVariable( PoolVariableIF* variable) override;
/**
* Register the passed pool variable instance into the data set.
* @param variable
* @return
*/
virtual ReturnValue_t registerVariable( PoolVariableIF* variable) override;
/**
* Provides the means to lock the underlying data structure to ensure
* thread-safety. Default implementation is empty
* @return Always returns -@c RETURN_OK
*/
virtual ReturnValue_t lockDataPool(
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* Provides the means to unlock the underlying data structure to ensure
* thread-safety. Default implementation is empty
* @return Always returns -@c RETURN_OK
*/
virtual ReturnValue_t unlockDataPool() override;
/**
* Provides the means to lock the underlying data structure to ensure
* thread-safety. Default implementation is empty
* @return Always returns -@c RETURN_OK
*/
virtual ReturnValue_t lockDataPool(
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* Provides the means to unlock the underlying data structure to ensure
* thread-safety. Default implementation is empty
* @return Always returns -@c RETURN_OK
*/
virtual ReturnValue_t unlockDataPool() override;
virtual uint16_t getFillCount() const;
virtual uint16_t getFillCount() const;
/* SerializeIF implementations */
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size,
const size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
virtual size_t getSerializedSize() const override;
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
/* SerializeIF implementations */
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size,
const size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
virtual size_t getSerializedSize() const override;
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
/**
* Can be used to individually protect every read and commit call.
@ -132,48 +133,48 @@ public:
uint32_t mutexTimeout = 20);
protected:
/**
* @brief The fill_count attribute ensures that the variables
* register in the correct array position and that the maximum
* number of variables is not exceeded.
*/
uint16_t fillCount = 0;
/**
* States of the seet.
*/
enum class States {
STATE_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED
STATE_SET_WAS_READ //!< DATA_SET_WAS_READ
};
/**
* @brief state manages the internal state of the data set,
* which is important e.g. for the behavior on destruction.
*/
States state = States::STATE_SET_UNINITIALISED;
/**
* @brief The fill_count attribute ensures that the variables
* register in the correct array position and that the maximum
* number of variables is not exceeded.
*/
uint16_t fillCount = 0;
/**
* States of the seet.
*/
enum class States {
STATE_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED
STATE_SET_WAS_READ //!< DATA_SET_WAS_READ
};
/**
* @brief state manages the internal state of the data set,
* which is important e.g. for the behavior on destruction.
*/
States state = States::STATE_SET_UNINITIALISED;
/**
* @brief This array represents all pool variables registered in this set.
* Child classes can use a static or dynamic container to create
* an array of registered variables and assign the first entry here.
*/
PoolVariableIF** registeredVariables = nullptr;
const size_t maxFillCount = 0;
/**
* @brief This array represents all pool variables registered in this set.
* Child classes can use a static or dynamic container to create
* an array of registered variables and assign the first entry here.
*/
PoolVariableIF** registeredVariables = nullptr;
const size_t maxFillCount = 0;
void setContainer(PoolVariableIF** variablesContainer);
PoolVariableIF** getContainer() const;
void setContainer(PoolVariableIF** variablesContainer);
PoolVariableIF** getContainer() const;
private:
bool protectEveryReadCommitCall = false;
MutexIF::TimeoutType timeoutTypeForSingleVars = MutexIF::TimeoutType::WAITING;
uint32_t mutexTimeoutForSingleVars = 20;
bool protectEveryReadCommitCall = false;
MutexIF::TimeoutType timeoutTypeForSingleVars = MutexIF::TimeoutType::WAITING;
uint32_t mutexTimeoutForSingleVars = 20;
ReturnValue_t readVariable(uint16_t count);
void handleAlreadyReadDatasetCommit(
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20);
ReturnValue_t handleUnreadDatasetCommit(
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20);
ReturnValue_t readVariable(uint16_t count);
void handleAlreadyReadDatasetCommit(
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20);
ReturnValue_t handleUnreadDatasetCommit(
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20);
};
#endif /* FSFW_DATAPOOL_POOLDATASETBASE_H_ */

View File

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

View File

@ -8,9 +8,9 @@
/**
* @brief Helper class to read data sets or pool variables
*/
class PoolReadHelper {
class PoolReadGuard {
public:
PoolReadHelper(ReadCommitIF* readObject,
PoolReadGuard(ReadCommitIF* readObject,
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t mutexTimeout = 20):
readObject(readObject), mutexTimeout(mutexTimeout) {
@ -32,8 +32,18 @@ public:
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);
}
@ -42,6 +52,7 @@ public:
private:
ReadCommitIF* readObject = nullptr;
ReturnValue_t readResult = HasReturnvaluesIF::RETURN_OK;
bool noCommit = false;
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
uint32_t mutexTimeout = 20;
};

View File

@ -1,9 +1,10 @@
#ifndef FSFW_DATAPOOL_POOLVARIABLEIF_H_
#define FSFW_DATAPOOL_POOLVARIABLEIF_H_
#include "ReadCommitIF.h"
#include "../returnvalues/HasReturnvaluesIF.h"
#include "../serialize/SerializeIF.h"
#include "ReadCommitIF.h"
/**
* @brief This interface is used to control data pool
@ -18,47 +19,48 @@
* @author Bastian Baetz
* @ingroup data_pool
*/
class PoolVariableIF : public SerializeIF,
public ReadCommitIF {
friend class PoolDataSetBase;
friend class LocalPoolDataSetBase;
class PoolVariableIF :
public SerializeIF,
public ReadCommitIF {
public:
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_POOL_ENTRY = MAKE_RETURN_CODE(0xA1);
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_POOL_ENTRY = MAKE_RETURN_CODE(0xA1);
static constexpr bool VALID = 1;
static constexpr bool INVALID = 0;
static constexpr uint32_t NO_PARAMETER = 0xffffffff;
static constexpr bool VALID = 1;
static constexpr bool INVALID = 0;
static constexpr uint32_t NO_PARAMETER = 0xffffffff;
enum ReadWriteMode_t {
VAR_READ, VAR_WRITE, VAR_READ_WRITE
};
enum ReadWriteMode_t {
VAR_READ, VAR_WRITE, VAR_READ_WRITE
};
/**
* @brief This is an empty virtual destructor,
* as it is proposed for C++ interfaces.
*/
virtual ~PoolVariableIF() {}
/**
* @brief This method returns if the variable is write-only,
* read-write or read-only.
*/
virtual ReadWriteMode_t getReadWriteMode() const = 0;
/**
* @brief This operation shall return the data pool id of the variable.
*/
virtual uint32_t getDataPoolId() const = 0;
/**
* @brief With this call, the valid information of the
* variable is returned.
*/
virtual bool isValid() const = 0;
/**
* @brief With this call, the valid information of the variable is set.
*/
virtual void setValid(bool validity) = 0;
/**
* @brief This is an empty virtual destructor,
* as it is proposed for C++ interfaces.
*/
virtual ~PoolVariableIF() {}
/**
* @brief This method returns if the variable is write-only,
* read-write or read-only.
*/
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.
*/
virtual uint32_t getDataPoolId() const = 0;
/**
* @brief With this call, the valid information of the
* variable is returned.
*/
virtual bool isValid() const = 0;
/**
* @brief With this call, the valid information of the variable is set.
*/
virtual void setValid(bool validity) = 0;
};
using pool_rwm_t = PoolVariableIF::ReadWriteMode_t;

View File

@ -9,6 +9,7 @@
* semantics.
*/
class ReadCommitIF {
friend class ReadCommitIFAttorney;
public:
virtual ~ReadCommitIF() {}
virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType,
@ -18,9 +19,8 @@ public:
protected:
//! Optional and protected because this is interesting for classes grouping
//! members with commit and read semantics where the lock is only necessary
//! once.
/* Optional and protected because this is interesting for classes grouping members with commit
and read semantics where the lock is only necessary once. */
virtual ReturnValue_t readWithoutLock() {
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
* 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.
*
* The local data pool can be accessed using helper classes by using the
* 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
* 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 {
friend class HasLocalDpIFManagerAttorney;
@ -152,8 +162,8 @@ protected:
*/
virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden"
<< ". Returning nullptr!" << std::endl;
sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden. "
"Returning nullptr!" << std::endl;
#else
sif::printWarning("HasLocalDataPoolIF::getPoolObjectHandle: "
"Not overriden. Returning nullptr!\n");

View File

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

View File

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

View File

@ -3,6 +3,7 @@
#include "internal/HasLocalDpIFUserAttorney.h"
#include "../serviceinterface/ServiceInterface.h"
#include "../globalfunctions/bitutility.h"
#include "../datapoollocal/LocalDataPoolManager.h"
#include "../housekeeping/PeriodicHousekeepingHelper.h"
#include "../serialize/SerializeAdapter.h"
@ -35,14 +36,13 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
this->sid.objectId = hkOwner->getObjectId();
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) {
periodicHelper = new PeriodicHousekeepingHelper(this);
}
}
LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid,
PoolVariableIF** registeredVariablesArray,
LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables):
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(
@ -96,22 +96,22 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer
SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
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 validBufferIndexBit = 0;
for (uint16_t count = 0; count < fillCount; count++) {
if(registeredVariables[count]->isValid()) {
// set validity buffer here.
this->bitSetter(validityMask + validBufferIndex,
validBufferIndexBit);
if(validBufferIndexBit == 7) {
validBufferIndex ++;
validBufferIndexBit = 0;
}
else {
validBufferIndexBit ++;
}
/* Set bit at correct position */
bitutil::bitSet(validityMask + validBufferIndex, validBufferIndexBit);
}
if(validBufferIndexBit == 7) {
validBufferIndex ++;
validBufferIndexBit = 0;
}
else {
validBufferIndexBit ++;
}
result = registeredVariables[count]->serialize(buffer, size, maxSize,
streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) {
@ -148,7 +148,7 @@ ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer(
uint8_t validBufferIndexBit = 0;
for (uint16_t count = 0; count < fillCount; count++) {
// set validity buffer here.
bool nextVarValid = this->bitGetter(*buffer +
bool nextVarValid = bitutil::bitGet(*buffer +
validBufferIndex, validBufferIndexBit);
registeredVariables[count]->setValid(nextVarValid);
@ -173,7 +173,7 @@ ReturnValue_t LocalPoolDataSetBase::unlockDataPool() {
ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t** buffer,
size_t* size, size_t maxSize,SerializeIF::Endianness streamEndianness,
bool serializeFillCount) const {
// Serialize as uint8_t
/* Serialize fill count as uint8_t */
uint8_t fillCount = this->fillCount;
if(serializeFillCount) {
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) {
this->diagnostic = isDiagnostics;
}
@ -296,19 +281,6 @@ sid_t LocalPoolDataSetBase::getSid() const {
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 {
return this->valid;
}
@ -316,7 +288,7 @@ bool LocalPoolDataSetBase::isValid() const {
void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) {
if(setEntriesRecursively) {
for(size_t idx = 0; idx < this->getFillCount(); idx++) {
registeredVariables[idx] -> setValid(valid);
registeredVariables[idx]->setValid(valid);
}
}
this->valid = valid;
@ -328,3 +300,9 @@ object_id_t LocalPoolDataSetBase::getCreatorObjectId() {
}
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
* to access data created by one (!) HK manager.
* to access data created by one HK manager.
* @details
* Unlike the first constructor, no component for periodic handling
* will be initiated.
@ -109,6 +109,12 @@ public:
LocalPoolDataSetBase(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);
sid_t getSid() const;
@ -218,13 +224,6 @@ protected:
*/
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;
LocalDataPoolManager* poolManager = nullptr;

View File

@ -1,10 +1,12 @@
#include "LocalPoolObjectBase.h"
#include "LocalDataPoolManager.h"
#include "internal/HasLocalDpIFUserAttorney.h"
#include "AccessLocalPoolF.h"
#include "HasLocalDataPoolIF.h"
#include "internal/HasLocalDpIFUserAttorney.h"
#include "../objectmanager/ObjectManagerIF.h"
LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner,
DataSetIF* dataSet, pool_rwm_t 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 FSFW_CPP_OSTREAM_ENABLED == 1
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
}
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(poolOwner);
if(hkOwner == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPoolVariable: The supplied pool owner did not "
<< "implement the correct interface"
<< " HasLocalDataPoolIF!" << std::endl;
sif::error << "LocalPoolVariable: The supplied pool owner did not implement the correct "
"interface HasLocalDataPoolIF!" << std::endl;
#else
sif::printError( "LocalPoolVariable: The supplied pool owner did not implement the correct "
"interface HasLocalDataPoolIF!\n");
#endif
return;
}
@ -93,6 +100,10 @@ void LocalPoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) {
void LocalPoolObjectBase::reportReadCommitError(const char* variableType,
ReturnValue_t error, bool read, object_id_t objectId, lp_id_t lpId) {
#if FSFW_DISABLE_PRINTOUT == 0
const char* variablePrintout = variableType;
if(variablePrintout == nullptr) {
variablePrintout = "Unknown Type";
}
const char* type = nullptr;
if(read) {
type = "read";
@ -119,12 +130,12 @@ void LocalPoolObjectBase::reportReadCommitError(const char* variableType,
}
#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
<< " LPID: " << lpId << std::endl;
#else
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_DISABLE_PRINTOUT == 0 */
}

View File

@ -26,8 +26,17 @@ inline LocalPoolVariable<T>::LocalPoolVariable(gp_id_t globalPoolId,
template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::read(
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
return readWithoutLock();
if(hkManager == nullptr) {
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>
@ -43,7 +52,6 @@ inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() {
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId,
&poolEntry);
//ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry);
if(result != RETURN_OK) {
object_id_t ownerObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVariable", result,
@ -51,15 +59,6 @@ inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() {
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->valid = poolEntry->getValid();
return RETURN_OK;
@ -75,8 +74,17 @@ inline ReturnValue_t LocalPoolVariable<T>::commit(bool setValid,
template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::commit(
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
return commitWithoutLock();
if(hkManager == nullptr) {
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>
@ -90,7 +98,6 @@ inline ReturnValue_t LocalPoolVariable<T>::commitWithoutLock() {
}
PoolEntry<T>* poolEntry = nullptr;
//ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry);
ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId,
&poolEntry);
if(result != RETURN_OK) {

View File

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

View File

@ -24,7 +24,6 @@ private:
return manager.getMutexHandle();
}
template<typename T> friend class LocalPoolVariable;
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_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01);
//! This is the core data structure of the local data pools. Users should insert all desired
//! pool variables, using the std::map interface.
/** This is the core data structure of the local data pools. Users should insert all desired
pool variables, using the std::map interface. */
using DataPool = std::map<lp_id_t, PoolEntryIF*>;
using DataPoolMapIter = DataPool::iterator;

View File

@ -308,6 +308,14 @@ void DeviceHandlerBase::doStateMachine() {
uint32_t currentUptime;
Clock::getUptime(&currentUptime);
if (currentUptime - timeoutStart >= childTransitionDelay) {
#if FSFW_VERBOSE_LEVEL >= 1
char printout[60];
sprintf(printout, "Transition timeout (%lu) occured !",
static_cast<unsigned long>(childTransitionDelay));
/* Very common configuration error, so print it */
printWarningOrError(sif::OutputTypes::OUT_WARNING, "doStateMachine",
RETURN_FAILED, printout);
#endif
triggerEvent(MODE_TRANSITION_FAILED, childTransitionFailure, 0);
setMode(transitionSourceMode, transitionSourceSubMode);
break;
@ -558,7 +566,7 @@ void DeviceHandlerBase::replyToCommand(ReturnValue_t status,
if (cookieInfo.pendingCommand->second.sendReplyTo != NO_COMMANDER) {
MessageQueueId_t queueId = cookieInfo.pendingCommand->second.sendReplyTo;
if (status == NO_REPLY_EXPECTED) {
actionHelper.finish(queueId, cookieInfo.pendingCommand->first,
actionHelper.finish(true, queueId, cookieInfo.pendingCommand->first,
RETURN_OK);
} else {
actionHelper.step(1, queueId, cookieInfo.pendingCommand->first,
@ -581,7 +589,11 @@ void DeviceHandlerBase::replyToReply(DeviceReplyMap::iterator iter,
// Check if it was transition or internal command.
// Don't send any replies in that case.
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;
}
@ -1494,10 +1506,9 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType,
if(errorType == sif::OutputTypes::OUT_WARNING) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "DeviceHandlerBase::" << functionName << ": Object ID "
<< std::hex << std::setw(8) << std::setfill('0')
<< this->getObjectId() << " | " << errorPrint << std::dec
<< std::setfill(' ') << std::endl;
sif::warning << "DeviceHandlerBase::" << functionName << ": Object ID 0x" << std::hex <<
std::setw(8) << std::setfill('0') << this->getObjectId() << " | " << errorPrint <<
std::dec << std::setfill(' ') << std::endl;
#else
sif::printWarning("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n",
this->getObjectId(), errorPrint);

View File

@ -119,7 +119,7 @@ public:
DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID,
lp_id_t thermalRequestPoolId =
DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID,
uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID);
uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID);
/**
* @brief Helper function to ease device handler development.
* This will instruct the transition to MODE_ON immediately

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
timevalOperations.cpp
Type.cpp
bitutility.cpp
)
add_subdirectory(math)

View File

@ -5,6 +5,15 @@
void arrayprinter::print(const uint8_t *data, size_t size, OutputType type,
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(printInfo) {
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.
* @details
* 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> {
public:

View File

@ -13,60 +13,62 @@ public:
/**
* Header consists of sender ID and command ID.
*/
static constexpr size_t HEADER_SIZE = MessageQueueMessageIF::HEADER_SIZE +
sizeof(Command_t);
/**
* This minimum size is derived from the interface requirement to be able
static constexpr size_t HEADER_SIZE = MessageQueueMessageIF::HEADER_SIZE + sizeof(Command_t);
/**
* This minimum size is derived from the interface requirement to be able
* to set a rejected reply, which contains a returnvalue and the initial
* command.
*/
static constexpr size_t MINIMUM_COMMAND_MESSAGE_SIZE =
CommandMessageIF::HEADER_SIZE + sizeof(ReturnValue_t) +
sizeof(Command_t);
*/
static constexpr size_t MINIMUM_COMMAND_MESSAGE_SIZE = CommandMessageIF::HEADER_SIZE +
sizeof(ReturnValue_t) + sizeof(Command_t);
static const uint8_t INTERFACE_ID = CLASS_ID::COMMAND_MESSAGE;
static const ReturnValue_t UNKNOWN_COMMAND = MAKE_RETURN_CODE(0x01);
static constexpr Command_t makeCommandId(uint8_t messageId, uint8_t uniqueId) {
return ((messageId << 8) | uniqueId);
}
static const uint8_t MESSAGE_ID = messagetypes::COMMAND;
//! Used internally, shall be ignored
static const Command_t CMD_NONE = MAKE_COMMAND_ID( 0 );
static const Command_t REPLY_COMMAND_OK = MAKE_COMMAND_ID( 1 );
//! Reply indicating that the current command was rejected,
//! par1 should contain the error code
static const Command_t REPLY_REJECTED = MAKE_COMMAND_ID( 2 );
static const uint8_t INTERFACE_ID = CLASS_ID::COMMAND_MESSAGE;
static const ReturnValue_t UNKNOWN_COMMAND = MAKE_RETURN_CODE(1);
virtual ~CommandMessageIF() {};
static const uint8_t MESSAGE_ID = messagetypes::COMMAND;
//! Used internally, shall be ignored
static const Command_t CMD_NONE = MAKE_COMMAND_ID( 0 );
static const Command_t REPLY_COMMAND_OK = MAKE_COMMAND_ID( 1 );
//! Reply indicating that the current command was rejected,
//! par1 should contain the error code
static const Command_t REPLY_REJECTED = MAKE_COMMAND_ID( 2 );
/**
* A command message shall have a uint16_t command ID field.
* @return
*/
virtual Command_t getCommand() const = 0;
/**
* A command message shall have a uint8_t message type ID field.
* @return
*/
virtual uint8_t getMessageType() const = 0;
virtual ~CommandMessageIF() {};
/**
* A command message can be rejected and needs to offer a function
* to set a rejected reply
* @param reason
* @param initialCommand
*/
virtual void setReplyRejected(ReturnValue_t reason,
Command_t initialCommand) = 0;
/**
* Corrensonding getter function.
* @param initialCommand
* @return
*/
virtual ReturnValue_t getReplyRejectedReason(
Command_t* initialCommand = nullptr) const = 0;
/**
* A command message shall have a uint16_t command ID field.
* @return
*/
virtual Command_t getCommand() const = 0;
/**
* A command message shall have a uint8_t message type ID field.
* @return
*/
virtual uint8_t getMessageType() const = 0;
virtual void setToUnknownCommand() = 0;
/**
* A command message can be rejected and needs to offer a function
* to set a rejected reply
* @param reason
* @param initialCommand
*/
virtual void setReplyRejected(ReturnValue_t reason,
Command_t initialCommand) = 0;
/**
* Corrensonding getter function.
* @param initialCommand
* @return
*/
virtual ReturnValue_t getReplyRejectedReason(
Command_t* initialCommand = nullptr) const = 0;
virtual void clear() = 0;
virtual void setToUnknownCommand() = 0;
virtual void clear() = 0;
};

View File

@ -39,6 +39,9 @@ public:
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 */
}

View File

@ -16,17 +16,27 @@ class HasFileSystemIF {
public:
static constexpr uint8_t INTERFACE_ID = CLASS_ID::FILE_SYSTEM;
static constexpr ReturnValue_t FILE_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x00);
static constexpr ReturnValue_t FILE_ALREADY_EXISTS = MAKE_RETURN_CODE(0x01);
static constexpr ReturnValue_t FILE_LOCKED = MAKE_RETURN_CODE(0x02);
//! [EXPORT] : P1: Can be file system specific error code
static constexpr ReturnValue_t GENERIC_FILE_ERROR = MAKE_RETURN_CODE(0);
//! [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 DIRECTORY_ALREADY_EXISTS = MAKE_RETURN_CODE(0x04);
static constexpr ReturnValue_t DIRECTORY_NOT_EMPTY = MAKE_RETURN_CODE(0x05);
static constexpr ReturnValue_t FILE_DOES_NOT_EXIST = MAKE_RETURN_CODE(5);
static constexpr ReturnValue_t FILE_ALREADY_EXISTS = MAKE_RETURN_CODE(6);
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() {}
/**

View File

@ -16,56 +16,56 @@ uint16_t Clock::leapSeconds = 0;
MutexIF* Clock::timeMutex = nullptr;
uint32_t Clock::getTicksPerSecond(void) {
return 1000;
return 1000;
}
ReturnValue_t Clock::setClock(const TimeOfDay_t* time) {
timeval time_timeval;
timeval time_timeval;
ReturnValue_t result = convertTimeOfDayToTimeval(time, &time_timeval);
if (result != HasReturnvaluesIF::RETURN_OK){
return result;
}
ReturnValue_t result = convertTimeOfDayToTimeval(time, &time_timeval);
if (result != HasReturnvaluesIF::RETURN_OK){
return result;
}
return setClock(&time_timeval);
return setClock(&time_timeval);
}
ReturnValue_t Clock::setClock(const timeval* time) {
timeval uptime = getUptime();
timeval uptime = getUptime();
timeval offset = *time - uptime;
timeval offset = *time - uptime;
Timekeeper::instance()->setOffset(offset);
Timekeeper::instance()->setOffset(offset);
return HasReturnvaluesIF::RETURN_OK;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::getClock_timeval(timeval* time) {
timeval uptime = getUptime();
timeval uptime = getUptime();
timeval offset = Timekeeper::instance()->getOffset();
timeval offset = Timekeeper::instance()->getOffset();
*time = offset + uptime;
*time = offset + uptime;
return HasReturnvaluesIF::RETURN_OK;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::getUptime(timeval* uptime) {
*uptime = getUptime();
*uptime = getUptime();
return HasReturnvaluesIF::RETURN_OK;
return HasReturnvaluesIF::RETURN_OK;
}
timeval Clock::getUptime() {
TickType_t ticksSinceStart = xTaskGetTickCount();
return Timekeeper::ticksToTimeval(ticksSinceStart);
TickType_t ticksSinceStart = xTaskGetTickCount();
return Timekeeper::ticksToTimeval(ticksSinceStart);
}
ReturnValue_t Clock::getUptime(uint32_t* uptimeMs) {
timeval uptime = getUptime();
*uptimeMs = uptime.tv_sec * 1000 + uptime.tv_usec / 1000;
return HasReturnvaluesIF::RETURN_OK;
timeval uptime = getUptime();
*uptimeMs = uptime.tv_sec * 1000 + uptime.tv_usec / 1000;
return HasReturnvaluesIF::RETURN_OK;
}
@ -76,129 +76,129 @@ ReturnValue_t Clock::getUptime(uint32_t* uptimeMs) {
ReturnValue_t Clock::getClock_usecs(uint64_t* time) {
timeval time_timeval;
ReturnValue_t result = getClock_timeval(&time_timeval);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
*time = time_timeval.tv_sec * 1000000 + time_timeval.tv_usec;
return HasReturnvaluesIF::RETURN_OK;
timeval time_timeval;
ReturnValue_t result = getClock_timeval(&time_timeval);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
*time = time_timeval.tv_sec * 1000000 + time_timeval.tv_usec;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
timeval time_timeval;
ReturnValue_t result = getClock_timeval(&time_timeval);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
struct tm time_tm;
timeval time_timeval;
ReturnValue_t result = getClock_timeval(&time_timeval);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
struct tm time_tm;
gmtime_r(&time_timeval.tv_sec,&time_tm);
gmtime_r(&time_timeval.tv_sec,&time_tm);
time->year = time_tm.tm_year + 1900;
time->month = time_tm.tm_mon + 1;
time->day = time_tm.tm_mday;
time->year = time_tm.tm_year + 1900;
time->month = time_tm.tm_mon + 1;
time->day = time_tm.tm_mday;
time->hour = time_tm.tm_hour;
time->minute = time_tm.tm_min;
time->second = time_tm.tm_sec;
time->hour = time_tm.tm_hour;
time->minute = time_tm.tm_min;
time->second = time_tm.tm_sec;
time->usecond = time_timeval.tv_usec;
time->usecond = time_timeval.tv_usec;
return HasReturnvaluesIF::RETURN_OK;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from,
timeval* to) {
struct tm time_tm;
timeval* to) {
struct tm time_tm;
time_tm.tm_year = from->year - 1900;
time_tm.tm_mon = from->month - 1;
time_tm.tm_mday = from->day;
time_tm.tm_year = from->year - 1900;
time_tm.tm_mon = from->month - 1;
time_tm.tm_mday = from->day;
time_tm.tm_hour = from->hour;
time_tm.tm_min = from->minute;
time_tm.tm_sec = from->second;
time_tm.tm_hour = from->hour;
time_tm.tm_min = from->minute;
time_tm.tm_sec = from->second;
time_t seconds = mktime(&time_tm);
time_t seconds = mktime(&time_tm);
to->tv_sec = seconds;
to->tv_usec = from->usecond;
//Fails in 2038..
return HasReturnvaluesIF::RETURN_OK;
to->tv_sec = seconds;
to->tv_usec = from->usecond;
//Fails in 2038..
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::convertTimevalToJD2000(timeval time, double* JD2000) {
*JD2000 = (time.tv_sec - 946728000. + time.tv_usec / 1000000.) / 24.
/ 3600.;
return HasReturnvaluesIF::RETURN_OK;
*JD2000 = (time.tv_sec - 946728000. + time.tv_usec / 1000000.) / 24.
/ 3600.;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::convertUTCToTT(timeval utc, timeval* tt) {
//SHOULDDO: works not for dates in the past (might have less leap seconds)
if (timeMutex == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
//SHOULDDO: works not for dates in the past (might have less leap seconds)
if (timeMutex == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
uint16_t leapSeconds;
ReturnValue_t result = getLeapSeconds(&leapSeconds);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
timeval leapSeconds_timeval = { 0, 0 };
leapSeconds_timeval.tv_sec = leapSeconds;
uint16_t leapSeconds;
ReturnValue_t result = getLeapSeconds(&leapSeconds);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
timeval leapSeconds_timeval = { 0, 0 };
leapSeconds_timeval.tv_sec = leapSeconds;
//initial offset between UTC and TAI
timeval UTCtoTAI1972 = { 10, 0 };
//initial offset between UTC and TAI
timeval UTCtoTAI1972 = { 10, 0 };
timeval TAItoTT = { 32, 184000 };
timeval TAItoTT = { 32, 184000 };
*tt = utc + leapSeconds_timeval + UTCtoTAI1972 + TAItoTT;
*tt = utc + leapSeconds_timeval + UTCtoTAI1972 + TAItoTT;
return HasReturnvaluesIF::RETURN_OK;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::setLeapSeconds(const uint16_t leapSeconds_) {
if (checkOrCreateClockMutex() != HasReturnvaluesIF::RETURN_OK) {
return HasReturnvaluesIF::RETURN_FAILED;
}
ReturnValue_t result = timeMutex->lockMutex(MutexIF::TimeoutType::BLOCKING);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
if (checkOrCreateClockMutex() != HasReturnvaluesIF::RETURN_OK) {
return HasReturnvaluesIF::RETURN_FAILED;
}
ReturnValue_t result = timeMutex->lockMutex(MutexIF::TimeoutType::BLOCKING);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
leapSeconds = leapSeconds_;
leapSeconds = leapSeconds_;
result = timeMutex->unlockMutex();
return result;
result = timeMutex->unlockMutex();
return result;
}
ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) {
if (timeMutex == NULL) {
return HasReturnvaluesIF::RETURN_FAILED;
}
ReturnValue_t result = timeMutex->lockMutex(MutexIF::TimeoutType::BLOCKING);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
if (timeMutex == NULL) {
return HasReturnvaluesIF::RETURN_FAILED;
}
ReturnValue_t result = timeMutex->lockMutex(MutexIF::TimeoutType::BLOCKING);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
*leapSeconds_ = leapSeconds;
*leapSeconds_ = leapSeconds;
result = timeMutex->unlockMutex();
return result;
result = timeMutex->unlockMutex();
return result;
}
ReturnValue_t Clock::checkOrCreateClockMutex() {
if (timeMutex == NULL) {
MutexFactory* mutexFactory = MutexFactory::instance();
if (mutexFactory == NULL) {
return HasReturnvaluesIF::RETURN_FAILED;
}
timeMutex = mutexFactory->createMutex();
if (timeMutex == NULL) {
return HasReturnvaluesIF::RETURN_FAILED;
}
}
return HasReturnvaluesIF::RETURN_OK;
if (timeMutex == NULL) {
MutexFactory* mutexFactory = MutexFactory::instance();
if (mutexFactory == NULL) {
return HasReturnvaluesIF::RETURN_FAILED;
}
timeMutex = mutexFactory->createMutex();
if (timeMutex == NULL) {
return HasReturnvaluesIF::RETURN_FAILED;
}
}
return HasReturnvaluesIF::RETURN_OK;
}

View File

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

View File

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

View File

@ -6,252 +6,250 @@
#include <errno.h>
PosixThread::PosixThread(const char* name_, int priority_, size_t stackSize_):
thread(0),priority(priority_),stackSize(stackSize_) {
thread(0),priority(priority_),stackSize(stackSize_) {
name[0] = '\0';
std::strncat(name, name_, PTHREAD_MAX_NAMELEN - 1);
}
PosixThread::~PosixThread() {
//No deletion and no free of Stack Pointer
//No deletion and no free of Stack Pointer
}
ReturnValue_t PosixThread::sleep(uint64_t ns) {
//TODO sleep might be better with timer instead of sleep()
timespec time;
time.tv_sec = ns/1000000000;
time.tv_nsec = ns - time.tv_sec*1e9;
//TODO sleep might be better with timer instead of sleep()
timespec time;
time.tv_sec = ns/1000000000;
time.tv_nsec = ns - time.tv_sec*1e9;
//Remaining Time is not set here
int status = nanosleep(&time,NULL);
if(status != 0){
switch(errno){
case EINTR:
//The nanosleep() function was interrupted by a signal.
return HasReturnvaluesIF::RETURN_FAILED;
case EINVAL:
//The rqtp argument specified a nanosecond value less than zero or
// greater than or equal to 1000 million.
return HasReturnvaluesIF::RETURN_FAILED;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
//Remaining Time is not set here
int status = nanosleep(&time,NULL);
if(status != 0){
switch(errno){
case EINTR:
//The nanosleep() function was interrupted by a signal.
return HasReturnvaluesIF::RETURN_FAILED;
case EINVAL:
//The rqtp argument specified a nanosecond value less than zero or
// greater than or equal to 1000 million.
return HasReturnvaluesIF::RETURN_FAILED;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
}
return HasReturnvaluesIF::RETURN_OK;
}
return HasReturnvaluesIF::RETURN_OK;
}
void PosixThread::suspend() {
//Wait for SIGUSR1
int caughtSig = 0;
sigset_t waitSignal;
sigemptyset(&waitSignal);
sigaddset(&waitSignal, SIGUSR1);
sigwait(&waitSignal, &caughtSig);
if (caughtSig != SIGUSR1) {
//Wait for SIGUSR1
int caughtSig = 0;
sigset_t waitSignal;
sigemptyset(&waitSignal);
sigaddset(&waitSignal, SIGUSR1);
sigwait(&waitSignal, &caughtSig);
if (caughtSig != SIGUSR1) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "FixedTimeslotTask: Unknown Signal received: " <<
caughtSig << std::endl;
sif::error << "FixedTimeslotTask: Unknown Signal received: " <<
caughtSig << std::endl;
#endif
}
}
}
void PosixThread::resume(){
/* Signal the thread to start. Makes sense to call kill to start or? ;)
*
* According to Posix raise(signal) will call pthread_kill(pthread_self(), sig),
* but as the call must be done from the thread itsself this is not possible here
*/
pthread_kill(thread,SIGUSR1);
/* Signal the thread to start. Makes sense to call kill to start or? ;)
According to POSIX raise(signal) will call pthread_kill(pthread_self(), sig),
but as the call must be done from the thread itself this is not possible here */
pthread_kill(thread,SIGUSR1);
}
bool PosixThread::delayUntil(uint64_t* const prevoiusWakeTime_ms,
const uint64_t delayTime_ms) {
uint64_t nextTimeToWake_ms;
bool shouldDelay = false;
//Get current Time
const uint64_t currentTime_ms = getCurrentMonotonicTimeMs();
/* Generate the tick time at which the task wants to wake. */
nextTimeToWake_ms = (*prevoiusWakeTime_ms) + delayTime_ms;
const uint64_t delayTime_ms) {
uint64_t nextTimeToWake_ms;
bool shouldDelay = false;
/* Get current Time */
const uint64_t currentTime_ms = getCurrentMonotonicTimeMs();
/* Generate the tick time at which the task wants to wake. */
nextTimeToWake_ms = (*prevoiusWakeTime_ms) + delayTime_ms;
if (currentTime_ms < *prevoiusWakeTime_ms) {
/* The tick count has overflowed since this function was
lasted called. In this case the only time we should ever
actually delay is if the wake time has also overflowed,
and the wake time is greater than the tick time. When this
if (currentTime_ms < *prevoiusWakeTime_ms) {
/* The tick count has overflowed since this function was
lasted called. In this case the only time we should ever
actually delay is if the wake time has also overflowed,
and the wake time is greater than the tick time. When this
is the case it is as if neither time had overflowed. */
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
&& (nextTimeToWake_ms > currentTime_ms)) {
shouldDelay = true;
}
} else {
/* The tick time has not overflowed. In this case we will
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
&& (nextTimeToWake_ms > currentTime_ms)) {
shouldDelay = true;
}
} else {
/* The tick time has not overflowed. In this case we will
delay if either the wake time has overflowed, and/or the
tick time is less than the wake time. */
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
|| (nextTimeToWake_ms > currentTime_ms)) {
shouldDelay = true;
}
}
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
|| (nextTimeToWake_ms > currentTime_ms)) {
shouldDelay = true;
}
}
/* Update the wake time ready for the next call. */
/* Update the wake time ready for the next call. */
(*prevoiusWakeTime_ms) = nextTimeToWake_ms;
(*prevoiusWakeTime_ms) = nextTimeToWake_ms;
if (shouldDelay) {
uint64_t sleepTime = nextTimeToWake_ms - currentTime_ms;
PosixThread::sleep(sleepTime * 1000000ull);
return true;
}
//We are shifting the time in case the deadline was missed like rtems
(*prevoiusWakeTime_ms) = currentTime_ms;
return false;
if (shouldDelay) {
uint64_t sleepTime = nextTimeToWake_ms - currentTime_ms;
PosixThread::sleep(sleepTime * 1000000ull);
return true;
}
/* We are shifting the time in case the deadline was missed like RTEMS */
(*prevoiusWakeTime_ms) = currentTime_ms;
return false;
}
uint64_t PosixThread::getCurrentMonotonicTimeMs(){
timespec timeNow;
clock_gettime(CLOCK_MONOTONIC_RAW, &timeNow);
uint64_t currentTime_ms = (uint64_t) timeNow.tv_sec * 1000
+ timeNow.tv_nsec / 1000000;
timespec timeNow;
clock_gettime(CLOCK_MONOTONIC_RAW, &timeNow);
uint64_t currentTime_ms = (uint64_t) timeNow.tv_sec * 1000
+ timeNow.tv_nsec / 1000000;
return currentTime_ms;
return currentTime_ms;
}
void PosixThread::createTask(void* (*fnc_)(void*), void* arg_) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
//sif::debug << "PosixThread::createTask" << std::endl;
//sif::debug << "PosixThread::createTask" << std::endl;
#endif
/*
* The attr argument points to a pthread_attr_t structure whose contents
are used at thread creation time to determine attributes for the new
thread; this structure is initialized using pthread_attr_init(3) and
related functions. If attr is NULL, then the thread is created with
default attributes.
*/
pthread_attr_t attributes;
int status = pthread_attr_init(&attributes);
if(status != 0){
/*
* The attr argument points to a pthread_attr_t structure whose contents
* are used at thread creation time to determine attributes for the new
* thread; this structure is initialized using pthread_attr_init(3) and
* related functions. If attr is NULL, then the thread is created with
* default attributes.
*/
pthread_attr_t attributes;
int status = pthread_attr_init(&attributes);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread attribute init failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread attribute init failed with: " <<
strerror(status) << std::endl;
#endif
}
void* stackPointer;
status = posix_memalign(&stackPointer, sysconf(_SC_PAGESIZE), stackSize);
if(status != 0){
}
void* stackPointer;
status = posix_memalign(&stackPointer, sysconf(_SC_PAGESIZE), stackSize);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Stack init failed with: " <<
strerror(status) << std::endl;
sif::error << "PosixThread::createTask: Stack init failed with: " <<
strerror(status) << std::endl;
#endif
if(errno == ENOMEM) {
size_t stackMb = stackSize/10e6;
if(errno == ENOMEM) {
size_t stackMb = stackSize/10e6;
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Insufficient memory for"
" the requested " << stackMb << " MB" << std::endl;
sif::error << "PosixThread::createTask: Insufficient memory for"
" the requested " << stackMb << " MB" << std::endl;
#else
sif::printError("PosixThread::createTask: Insufficient memory for "
"the requested %lu MB\n", static_cast<unsigned long>(stackMb));
sif::printError("PosixThread::createTask: Insufficient memory for "
"the requested %lu MB\n", static_cast<unsigned long>(stackMb));
#endif
}
else if(errno == EINVAL) {
}
else if(errno == EINVAL) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Wrong alignment argument!"
<< std::endl;
sif::error << "PosixThread::createTask: Wrong alignment argument!"
<< std::endl;
#else
sif::printError("PosixThread::createTask: "
"Wrong alignment argument!\n");
sif::printError("PosixThread::createTask: "
"Wrong alignment argument!\n");
#endif
}
return;
}
}
return;
}
status = pthread_attr_setstack(&attributes, stackPointer, stackSize);
if(status != 0){
status = pthread_attr_setstack(&attributes, stackPointer, stackSize);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: pthread_attr_setstack "
" failed with: " << strerror(status) << std::endl;
sif::error << "Make sure the specified stack size is valid and is "
"larger than the minimum allowed stack size." << std::endl;
sif::error << "PosixThread::createTask: pthread_attr_setstack "
" failed with: " << strerror(status) << std::endl;
sif::error << "Make sure the specified stack size is valid and is "
"larger than the minimum allowed stack size." << std::endl;
#endif
}
}
status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED);
if(status != 0){
status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread attribute setinheritsched failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread attribute setinheritsched failed with: " <<
strerror(status) << std::endl;
#endif
}
}
// TODO FIFO -> This needs root privileges for the process
status = pthread_attr_setschedpolicy(&attributes,SCHED_FIFO);
if(status != 0){
// TODO FIFO -> This needs root privileges for the process
status = pthread_attr_setschedpolicy(&attributes,SCHED_FIFO);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread attribute schedule policy failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread attribute schedule policy failed with: " <<
strerror(status) << std::endl;
#endif
}
}
sched_param scheduleParams;
scheduleParams.__sched_priority = priority;
status = pthread_attr_setschedparam(&attributes, &scheduleParams);
if(status != 0){
sched_param scheduleParams;
scheduleParams.__sched_priority = priority;
status = pthread_attr_setschedparam(&attributes, &scheduleParams);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread attribute schedule params failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread attribute schedule params failed with: " <<
strerror(status) << std::endl;
#endif
}
}
//Set Signal Mask for suspend until startTask is called
sigset_t waitSignal;
sigemptyset(&waitSignal);
sigaddset(&waitSignal, SIGUSR1);
status = pthread_sigmask(SIG_BLOCK, &waitSignal, NULL);
if(status != 0){
//Set Signal Mask for suspend until startTask is called
sigset_t waitSignal;
sigemptyset(&waitSignal);
sigaddset(&waitSignal, SIGUSR1);
status = pthread_sigmask(SIG_BLOCK, &waitSignal, NULL);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread sigmask failed failed with: " <<
strerror(status) << " errno: " << strerror(errno) << std::endl;
sif::error << "Posix Thread sigmask failed failed with: " <<
strerror(status) << " errno: " << strerror(errno) << std::endl;
#endif
}
}
status = pthread_create(&thread,&attributes,fnc_,arg_);
if(status != 0){
status = pthread_create(&thread,&attributes,fnc_,arg_);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread create failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread create failed with: " <<
strerror(status) << std::endl;
#endif
}
}
status = pthread_setname_np(thread,name);
if(status != 0){
status = pthread_setname_np(thread,name);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: setname failed with: " <<
strerror(status) << std::endl;
sif::error << "PosixThread::createTask: setname failed with: " <<
strerror(status) << std::endl;
#endif
if(status == ERANGE) {
if(status == ERANGE) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Task name length longer"
" than 16 chars. Truncating.." << std::endl;
sif::error << "PosixThread::createTask: Task name length longer"
" than 16 chars. Truncating.." << std::endl;
#endif
name[15] = '\0';
status = pthread_setname_np(thread,name);
if(status != 0){
name[15] = '\0';
status = pthread_setname_np(thread,name);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Setting name"
" did not work.." << std::endl;
sif::error << "PosixThread::createTask: Setting name"
" did not work.." << std::endl;
#endif
}
}
}
}
}
}
status = pthread_attr_destroy(&attributes);
if(status!=0){
status = pthread_attr_destroy(&attributes);
if(status!=0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread attribute destroy failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread attribute destroy failed with: " <<
strerror(status) << std::endl;
#endif
}
}
}

View File

@ -10,201 +10,209 @@ uint16_t Clock::leapSeconds = 0;
MutexIF* Clock::timeMutex = nullptr;
uint32_t Clock::getTicksPerSecond(void){
rtems_interval ticks_per_second = rtems_clock_get_ticks_per_second();
return static_cast<uint32_t>(ticks_per_second);
rtems_interval ticks_per_second = rtems_clock_get_ticks_per_second();
return static_cast<uint32_t>(ticks_per_second);
}
ReturnValue_t Clock::setClock(const TimeOfDay_t* time) {
rtems_time_of_day timeRtems;
timeRtems.year = time->year;
timeRtems.month = time->month;
timeRtems.day = time->day;
timeRtems.hour = time->hour;
timeRtems.minute = time->minute;
timeRtems.second = time->second;
timeRtems.ticks = time->usecond * getTicksPerSecond() / 1e6;
rtems_status_code status = rtems_clock_set(&timeRtems);
switch(status){
case RTEMS_SUCCESSFUL:
return HasReturnvaluesIF::RETURN_OK;
case RTEMS_INVALID_ADDRESS:
return HasReturnvaluesIF::RETURN_FAILED;
case RTEMS_INVALID_CLOCK:
return HasReturnvaluesIF::RETURN_FAILED;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
rtems_time_of_day timeRtems;
timeRtems.year = time->year;
timeRtems.month = time->month;
timeRtems.day = time->day;
timeRtems.hour = time->hour;
timeRtems.minute = time->minute;
timeRtems.second = time->second;
timeRtems.ticks = time->usecond * getTicksPerSecond() / 1e6;
rtems_status_code status = rtems_clock_set(&timeRtems);
switch(status){
case RTEMS_SUCCESSFUL:
return HasReturnvaluesIF::RETURN_OK;
case RTEMS_INVALID_ADDRESS:
return HasReturnvaluesIF::RETURN_FAILED;
case RTEMS_INVALID_CLOCK:
return HasReturnvaluesIF::RETURN_FAILED;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
}
ReturnValue_t Clock::setClock(const timeval* time) {
timespec newTime;
newTime.tv_sec = time->tv_sec;
if(time->tv_usec < 0) {
// better returnvalue.
return HasReturnvaluesIF::RETURN_FAILED;
}
newTime.tv_nsec = time->tv_usec * TOD_NANOSECONDS_PER_MICROSECOND;
timespec newTime;
newTime.tv_sec = time->tv_sec;
if(time->tv_usec < 0) {
// better returnvalue.
return HasReturnvaluesIF::RETURN_FAILED;
}
newTime.tv_nsec = time->tv_usec * TOD_NANOSECONDS_PER_MICROSECOND;
ISR_lock_Context context;
_TOD_Lock();
_TOD_Acquire(&context);
Status_Control status = _TOD_Set(&newTime, &context);
_TOD_Unlock();
if(status == STATUS_SUCCESSFUL) {
return HasReturnvaluesIF::RETURN_OK;
}
// better returnvalue
return HasReturnvaluesIF::RETURN_FAILED;
ISR_lock_Context context;
_TOD_Lock();
_TOD_Acquire(&context);
Status_Control status = _TOD_Set(&newTime, &context);
_TOD_Unlock();
if(status == STATUS_SUCCESSFUL) {
return HasReturnvaluesIF::RETURN_OK;
}
// better returnvalue
return HasReturnvaluesIF::RETURN_FAILED;
}
ReturnValue_t Clock::getClock_timeval(timeval* time) {
//Callable from ISR
rtems_status_code status = rtems_clock_get_tod_timeval(time);
switch(status){
case RTEMS_SUCCESSFUL:
return HasReturnvaluesIF::RETURN_OK;
case RTEMS_NOT_DEFINED:
return HasReturnvaluesIF::RETURN_FAILED;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
//Callable from ISR
rtems_status_code status = rtems_clock_get_tod_timeval(time);
switch(status){
case RTEMS_SUCCESSFUL:
return HasReturnvaluesIF::RETURN_OK;
case RTEMS_NOT_DEFINED:
return HasReturnvaluesIF::RETURN_FAILED;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
}
ReturnValue_t Clock::getUptime(timeval* uptime) {
//According to docs.rtems.org for rtems 5 this method is more accurate than rtems_clock_get_ticks_since_boot
timespec time;
rtems_status_code status = rtems_clock_get_uptime(&time);
uptime->tv_sec = time.tv_sec;
time.tv_nsec = time.tv_nsec / 1000;
uptime->tv_usec = time.tv_nsec;
switch(status){
case RTEMS_SUCCESSFUL:
return HasReturnvaluesIF::RETURN_OK;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
//According to docs.rtems.org for rtems 5 this method is more accurate than rtems_clock_get_ticks_since_boot
timespec time;
rtems_status_code status = rtems_clock_get_uptime(&time);
uptime->tv_sec = time.tv_sec;
time.tv_nsec = time.tv_nsec / 1000;
uptime->tv_usec = time.tv_nsec;
switch(status){
case RTEMS_SUCCESSFUL:
return HasReturnvaluesIF::RETURN_OK;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
}
ReturnValue_t Clock::getUptime(uint32_t* uptimeMs) {
//This counter overflows after 50 days
*uptimeMs = rtems_clock_get_ticks_since_boot();
return HasReturnvaluesIF::RETURN_OK;
//This counter overflows after 50 days
*uptimeMs = rtems_clock_get_ticks_since_boot();
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::getClock_usecs(uint64_t* time) {
timeval temp_time;
rtems_status_code returnValue = rtems_clock_get_tod_timeval(&temp_time);
*time = ((uint64_t) temp_time.tv_sec * 1000000) + temp_time.tv_usec;
switch(returnValue){
case RTEMS_SUCCESSFUL:
return HasReturnvaluesIF::RETURN_OK;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
timeval temp_time;
rtems_status_code returnValue = rtems_clock_get_tod_timeval(&temp_time);
*time = ((uint64_t) temp_time.tv_sec * 1000000) + temp_time.tv_usec;
switch(returnValue){
case RTEMS_SUCCESSFUL:
return HasReturnvaluesIF::RETURN_OK;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
}
ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
/* 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_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) {
case RTEMS_SUCCESSFUL:
return HasReturnvaluesIF::RETURN_OK;
case RTEMS_NOT_DEFINED:
//system date and time is not set
return HasReturnvaluesIF::RETURN_FAILED;
case RTEMS_INVALID_ADDRESS:
//time_buffer is NULL
return HasReturnvaluesIF::RETURN_FAILED;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
/* For all but the last field, the struct will be filled with the correct values */
rtems_time_of_day timeRtems;
rtems_status_code status = rtems_clock_get_tod(&timeRtems);
switch (status) {
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;
}
case RTEMS_NOT_DEFINED:
/* System date and time is not set */
return HasReturnvaluesIF::RETURN_FAILED;
case RTEMS_INVALID_ADDRESS:
/* time_buffer is NULL */
return HasReturnvaluesIF::RETURN_FAILED;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
}
ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from,
timeval* to) {
//Fails in 2038..
rtems_time_of_day timeRtems;
timeRtems.year = from->year;
timeRtems.month = from->month;
timeRtems.day = from->day;
timeRtems.hour = from->hour;
timeRtems.minute = from->minute;
timeRtems.second = from->second;
timeRtems.ticks = from->usecond * getTicksPerSecond() / 1e6;
to->tv_sec = _TOD_To_seconds(&timeRtems);
to->tv_usec = from->usecond;
return HasReturnvaluesIF::RETURN_OK;
timeval* to) {
//Fails in 2038..
rtems_time_of_day timeRtems;
timeRtems.year = from->year;
timeRtems.month = from->month;
timeRtems.day = from->day;
timeRtems.hour = from->hour;
timeRtems.minute = from->minute;
timeRtems.second = from->second;
timeRtems.ticks = from->usecond * getTicksPerSecond() / 1e6;
to->tv_sec = _TOD_To_seconds(&timeRtems);
to->tv_usec = from->usecond;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::convertTimevalToJD2000(timeval time, double* JD2000) {
*JD2000 = (time.tv_sec - 946728000. + time.tv_usec / 1000000.) / 24.
/ 3600.;
return HasReturnvaluesIF::RETURN_OK;
*JD2000 = (time.tv_sec - 946728000. + time.tv_usec / 1000000.) / 24.
/ 3600.;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::convertUTCToTT(timeval utc, timeval* tt) {
//SHOULDDO: works not for dates in the past (might have less leap seconds)
if (timeMutex == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
//SHOULDDO: works not for dates in the past (might have less leap seconds)
if (timeMutex == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
uint16_t leapSeconds;
ReturnValue_t result = getLeapSeconds(&leapSeconds);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
timeval leapSeconds_timeval = { 0, 0 };
leapSeconds_timeval.tv_sec = leapSeconds;
uint16_t leapSeconds;
ReturnValue_t result = getLeapSeconds(&leapSeconds);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
timeval leapSeconds_timeval = { 0, 0 };
leapSeconds_timeval.tv_sec = leapSeconds;
//initial offset between UTC and TAI
timeval UTCtoTAI1972 = { 10, 0 };
//initial offset between UTC and TAI
timeval UTCtoTAI1972 = { 10, 0 };
timeval TAItoTT = { 32, 184000 };
timeval TAItoTT = { 32, 184000 };
*tt = utc + leapSeconds_timeval + UTCtoTAI1972 + TAItoTT;
*tt = utc + leapSeconds_timeval + UTCtoTAI1972 + TAItoTT;
return HasReturnvaluesIF::RETURN_OK;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::setLeapSeconds(const uint16_t leapSeconds_) {
if(checkOrCreateClockMutex()!=HasReturnvaluesIF::RETURN_OK){
return HasReturnvaluesIF::RETURN_FAILED;
}
MutexHelper helper(timeMutex);
if(checkOrCreateClockMutex()!=HasReturnvaluesIF::RETURN_OK){
return HasReturnvaluesIF::RETURN_FAILED;
}
MutexHelper helper(timeMutex);
leapSeconds = leapSeconds_;
leapSeconds = leapSeconds_;
return HasReturnvaluesIF::RETURN_OK;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) {
if(timeMutex==nullptr){
return HasReturnvaluesIF::RETURN_FAILED;
}
MutexHelper helper(timeMutex);
if(timeMutex==nullptr){
return HasReturnvaluesIF::RETURN_FAILED;
}
MutexHelper helper(timeMutex);
*leapSeconds_ = leapSeconds;
*leapSeconds_ = leapSeconds;
return HasReturnvaluesIF::RETURN_OK;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::checkOrCreateClockMutex(){
if(timeMutex==nullptr){
MutexFactory* mutexFactory = MutexFactory::instance();
if (mutexFactory == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
timeMutex = mutexFactory->createMutex();
if (timeMutex == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
}
return HasReturnvaluesIF::RETURN_OK;
if(timeMutex==nullptr){
MutexFactory* mutexFactory = MutexFactory::instance();
if (mutexFactory == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
timeMutex = mutexFactory->createMutex();
if (timeMutex == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
}
return HasReturnvaluesIF::RETURN_OK;
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -21,215 +21,215 @@ bool operator==(const timeval& lhs, const timeval& rhs);
*/
class CCSDSTime: public HasReturnvaluesIF {
public:
/**
* The Time code identifications, bits 4-6 in the P-Field
*/
enum TimeCodeIdentification {
CCS = 0b101,
CUC_LEVEL1 = 0b001,
CUC_LEVEL2 = 0b010,
CDS = 0b100,
AGENCY_DEFINED = 0b110
};
static const uint8_t P_FIELD_CUC_6B_CCSDS = (CUC_LEVEL1 << 4) + (3 << 2)
+ 2;
static const uint8_t P_FIELD_CUC_6B_AGENCY = (CUC_LEVEL2 << 4) + (3 << 2)
+ 2;
static const uint8_t P_FIELD_CDS_SHORT = (CDS << 4);
/**
* Struct for CDS day-segmented format.
*/
struct CDS_short {
uint8_t pField;
uint8_t dayMSB;
uint8_t dayLSB;
uint8_t msDay_hh;
uint8_t msDay_h;
uint8_t msDay_l;
uint8_t msDay_ll;
};
/**
* Struct for the CCS fromat in day of month variation with max resolution
*/
struct Ccs_seconds {
uint8_t pField;
uint8_t yearMSB;
uint8_t yearLSB;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
};
/**
* The Time code identifications, bits 4-6 in the P-Field
*/
enum TimeCodeIdentification {
CCS = 0b101,
CUC_LEVEL1 = 0b001,
CUC_LEVEL2 = 0b010,
CDS = 0b100,
AGENCY_DEFINED = 0b110
};
static const uint8_t P_FIELD_CUC_6B_CCSDS = (CUC_LEVEL1 << 4) + (3 << 2)
+ 2;
static const uint8_t P_FIELD_CUC_6B_AGENCY = (CUC_LEVEL2 << 4) + (3 << 2)
+ 2;
static const uint8_t P_FIELD_CDS_SHORT = (CDS << 4);
/**
* Struct for CDS day-segmented format.
*/
struct CDS_short {
uint8_t pField;
uint8_t dayMSB;
uint8_t dayLSB;
uint8_t msDay_hh;
uint8_t msDay_h;
uint8_t msDay_l;
uint8_t msDay_ll;
};
/**
* Struct for the CCS fromat in day of month variation with max resolution
*/
struct Ccs_seconds {
uint8_t pField;
uint8_t yearMSB;
uint8_t yearLSB;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
};
/**
* Struct for the CCS fromat in day of month variation with 10E-4 seconds resolution
*/
struct Ccs_mseconds {
uint8_t pField;
uint8_t yearMSB;
uint8_t yearLSB;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t secondEminus2;
uint8_t secondEminus4;
};
/**
* Struct for the CCS fromat in day of month variation with 10E-4 seconds resolution
*/
struct Ccs_mseconds {
uint8_t pField;
uint8_t yearMSB;
uint8_t yearLSB;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t secondEminus2;
uint8_t secondEminus4;
};
struct OBT_FLP {
uint8_t pFiled;
uint8_t seconds_hh;
uint8_t seconds_h;
uint8_t seconds_l;
uint8_t seconds_ll;
uint8_t subsecondsMSB;
uint8_t subsecondsLSB;
};
struct OBT_FLP {
uint8_t pFiled;
uint8_t seconds_hh;
uint8_t seconds_h;
uint8_t seconds_l;
uint8_t seconds_ll;
uint8_t subsecondsMSB;
uint8_t subsecondsLSB;
};
struct TimevalLess {
bool operator()(const timeval& lhs, const timeval& rhs) const {
return (lhs < rhs);
}
};
struct TimevalLess {
bool operator()(const timeval& lhs, const timeval& rhs) const {
return (lhs < rhs);
}
};
static const uint8_t INTERFACE_ID = CLASS_ID::CCSDS_TIME_HELPER_CLASS;
static const ReturnValue_t UNSUPPORTED_TIME_FORMAT = MAKE_RETURN_CODE(0);
static const ReturnValue_t NOT_ENOUGH_INFORMATION_FOR_TARGET_FORMAT =
MAKE_RETURN_CODE(1);
static const ReturnValue_t LENGTH_MISMATCH = MAKE_RETURN_CODE(2);
static const ReturnValue_t INVALID_TIME_FORMAT = MAKE_RETURN_CODE(3);
static const ReturnValue_t INVALID_DAY_OF_YEAR = MAKE_RETURN_CODE(4);
static const ReturnValue_t TIME_DOES_NOT_FIT_FORMAT = MAKE_RETURN_CODE(5);
static const uint8_t INTERFACE_ID = CLASS_ID::CCSDS_TIME_HELPER_CLASS;
static const ReturnValue_t UNSUPPORTED_TIME_FORMAT = MAKE_RETURN_CODE(0);
static const ReturnValue_t NOT_ENOUGH_INFORMATION_FOR_TARGET_FORMAT =
MAKE_RETURN_CODE(1);
static const ReturnValue_t LENGTH_MISMATCH = MAKE_RETURN_CODE(2);
static const ReturnValue_t INVALID_TIME_FORMAT = MAKE_RETURN_CODE(3);
static const ReturnValue_t INVALID_DAY_OF_YEAR = MAKE_RETURN_CODE(4);
static const ReturnValue_t TIME_DOES_NOT_FIT_FORMAT = MAKE_RETURN_CODE(5);
/**
* convert a TimeofDay struct to ccs with seconds resolution
*
* @param to pointer to a CCS struct
* @param from pointer to a TimeOfDay Struct
* @return
* - @c RETURN_OK if OK
* - @c INVALID_TIMECODE if not OK
*/
static ReturnValue_t convertToCcsds(Ccs_seconds *to,
Clock::TimeOfDay_t const *from);
/**
* convert a TimeofDay struct to ccs with seconds resolution
*
* @param to pointer to a CCS struct
* @param from pointer to a TimeOfDay Struct
* @return
* - @c RETURN_OK if OK
* - @c INVALID_TIMECODE if not OK
*/
static ReturnValue_t convertToCcsds(Ccs_seconds *to,
Clock::TimeOfDay_t const *from);
/**
* Converts to CDS format from timeval.
* @param to pointer to the CDS struct to generate
* @param from pointer to a timeval struct which comprises a time of day since UNIX epoch.
* @return
* - @c RETURN_OK as it assumes a valid timeval.
*/
static ReturnValue_t convertToCcsds(CDS_short* to, timeval const *from);
/**
* Converts to CDS format from timeval.
* @param to pointer to the CDS struct to generate
* @param from pointer to a timeval struct which comprises a time of day since UNIX epoch.
* @return
* - @c RETURN_OK as it assumes a valid timeval.
*/
static ReturnValue_t convertToCcsds(CDS_short* to, timeval const *from);
static ReturnValue_t convertToCcsds(OBT_FLP* to, timeval const *from);
static ReturnValue_t convertToCcsds(OBT_FLP* to, timeval const *from);
/**
* convert a TimeofDay struct to ccs with 10E-3 seconds resolution
*
* The 10E-4 seconds in the CCS Struct are 0 as the TimeOfDay only has ms resolution
*
* @param to pointer to a CCS struct
* @param from pointer to a TimeOfDay Struct
* @return
* - @c RETURN_OK if OK
* - @c INVALID_TIMECODE if not OK
*/
static ReturnValue_t convertToCcsds(Ccs_mseconds *to,
Clock::TimeOfDay_t const *from);
/**
* convert a TimeofDay struct to ccs with 10E-3 seconds resolution
*
* The 10E-4 seconds in the CCS Struct are 0 as the TimeOfDay only has ms resolution
*
* @param to pointer to a CCS struct
* @param from pointer to a TimeOfDay Struct
* @return
* - @c RETURN_OK if OK
* - @c INVALID_TIMECODE if not OK
*/
static ReturnValue_t convertToCcsds(Ccs_mseconds *to,
Clock::TimeOfDay_t const *from);
/**
* SHOULDDO: can this be modified to recognize padding?
* Tries to interpret a Level 1 CCSDS time code
*
* It assumes binary formats contain a valid P Field and recognizes the ASCII format
* by the lack of one.
*
* @param to an empty TimeOfDay struct
* @param from pointer to an CCSDS Time code
* @param length length of the Time code
* @return
* - @c RETURN_OK if successful
* - @c UNSUPPORTED_TIME_FORMAT if a (possibly valid) time code is not supported
* - @c LENGTH_MISMATCH if the length does not match the P Field
* - @c INVALID_TIME_FORMAT if the format or a value is invalid
*/
static ReturnValue_t convertFromCcsds(Clock::TimeOfDay_t *to,
uint8_t const *from, size_t length);
/**
* SHOULDDO: can this be modified to recognize padding?
* Tries to interpret a Level 1 CCSDS time code
*
* It assumes binary formats contain a valid P Field and recognizes the ASCII format
* by the lack of one.
*
* @param to an empty TimeOfDay struct
* @param from pointer to an CCSDS Time code
* @param length length of the Time code
* @return
* - @c RETURN_OK if successful
* - @c UNSUPPORTED_TIME_FORMAT if a (possibly valid) time code is not supported
* - @c LENGTH_MISMATCH if the length does not match the P Field
* - @c INVALID_TIME_FORMAT if the format or a value is invalid
*/
static ReturnValue_t convertFromCcsds(Clock::TimeOfDay_t *to,
uint8_t const *from, size_t length);
/**
* not implemented yet
*
* @param to
* @param from
* @return
*/
static ReturnValue_t convertFromCcsds(timeval *to, uint8_t const *from,
size_t* foundLength, size_t maxLength);
/**
* not implemented yet
*
* @param to
* @param from
* @return
*/
static ReturnValue_t convertFromCcsds(timeval *to, uint8_t const *from,
size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCUC(Clock::TimeOfDay_t *to,
uint8_t const *from, uint8_t length);
static ReturnValue_t convertFromCUC(Clock::TimeOfDay_t *to,
uint8_t const *from, uint8_t length);
static ReturnValue_t convertFromCUC(timeval *to, uint8_t const *from,
size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCUC(timeval *to, uint8_t const *from,
size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCUC(timeval *to, uint8_t pField,
uint8_t const *from, size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCUC(timeval *to, uint8_t pField,
uint8_t const *from, size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCCS(timeval *to, uint8_t const *from,
size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCCS(timeval *to, uint8_t const *from,
size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCCS(timeval *to, uint8_t pField,
uint8_t const *from, size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCCS(timeval *to, uint8_t pField,
uint8_t const *from, size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCDS(Clock::TimeOfDay_t *to,
uint8_t const *from, uint8_t length);
static ReturnValue_t convertFromCDS(Clock::TimeOfDay_t *to,
uint8_t const *from, uint8_t length);
static ReturnValue_t convertFromCDS(timeval *to, uint8_t const *from,
size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCDS(timeval *to, uint8_t const *from,
size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCCS(Clock::TimeOfDay_t *to,
uint8_t const *from, size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromCCS(Clock::TimeOfDay_t *to,
uint8_t const *from, size_t* foundLength, size_t maxLength);
static ReturnValue_t convertFromASCII(Clock::TimeOfDay_t *to,
uint8_t const *from, uint8_t length);
static ReturnValue_t convertFromASCII(Clock::TimeOfDay_t *to,
uint8_t const *from, uint8_t length);
static uint32_t subsecondsToMicroseconds(uint16_t subseconds);
static uint32_t subsecondsToMicroseconds(uint16_t subseconds);
private:
CCSDSTime();
virtual ~CCSDSTime();
/**
* checks a ccs time stream for validity
*
* Stream may be longer than the actual timecode
*
* @param time pointer to an Ccs stream
* @param length length of stream
* @return
*/
static ReturnValue_t checkCcs(const uint8_t* time, uint8_t length);
CCSDSTime();
virtual ~CCSDSTime();
/**
* checks a ccs time stream for validity
*
* Stream may be longer than the actual timecode
*
* @param time pointer to an Ccs stream
* @param length length of stream
* @return
*/
static ReturnValue_t checkCcs(const uint8_t* time, uint8_t length);
static ReturnValue_t checkTimeOfDay(const Clock::TimeOfDay_t *time);
static ReturnValue_t checkTimeOfDay(const Clock::TimeOfDay_t *time);
static const uint32_t SECONDS_PER_DAY = 24 * 60 * 60;
static const uint32_t SECONDS_PER_NON_LEAP_YEAR = SECONDS_PER_DAY * 365;
static const uint32_t DAYS_CCSDS_TO_UNIX_EPOCH = 4383; //!< Time difference between CCSDS and POSIX epoch. This is exact, because leap-seconds where not introduced before 1972.
static const uint32_t SECONDS_CCSDS_TO_UNIX_EPOCH = DAYS_CCSDS_TO_UNIX_EPOCH
* SECONDS_PER_DAY;
/**
* @param dayofYear
* @param year
* @param month
* @param day
*/
static ReturnValue_t convertDaysOfYear(uint16_t dayofYear, uint16_t year,
uint8_t *month, uint8_t *day);
static const uint32_t SECONDS_PER_DAY = 24 * 60 * 60;
static const uint32_t SECONDS_PER_NON_LEAP_YEAR = SECONDS_PER_DAY * 365;
static const uint32_t DAYS_CCSDS_TO_UNIX_EPOCH = 4383; //!< Time difference between CCSDS and POSIX epoch. This is exact, because leap-seconds where not introduced before 1972.
static const uint32_t SECONDS_CCSDS_TO_UNIX_EPOCH = DAYS_CCSDS_TO_UNIX_EPOCH
* SECONDS_PER_DAY;
/**
* @param dayofYear
* @param year
* @param month
* @param day
*/
static ReturnValue_t convertDaysOfYear(uint16_t dayofYear, uint16_t year,
uint8_t *month, uint8_t *day);
static bool isLeapYear(uint32_t year);
static ReturnValue_t convertTimevalToTimeOfDay(Clock::TimeOfDay_t* to,
timeval* from);
static bool isLeapYear(uint32_t year);
static ReturnValue_t convertTimevalToTimeOfDay(Clock::TimeOfDay_t* to,
timeval* from);
};
#endif /* FSFW_TIMEMANAGER_CCSDSTIME_H_ */

View File

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

View File

@ -1,8 +1,13 @@
#include "LocalPoolOwnerBase.h"
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/datapool/PoolReadHelper.h>
#include <fsfw/globalfunctions/bitutility.h>
#include <unittest/core/CatchDefinitions.h>
TEST_CASE("LocalDataSet" , "[LocDataSetTest]") {
@ -12,11 +17,193 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") {
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation()
== retval::CATCH_OK);
const uint32_t setId = 0;
LocalPoolStaticTestDataSet localSet;
SECTION("BasicTest") {
StaticLocalDataSet<3> localSet = StaticLocalDataSet<3>(
sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE, setId));
/* Test some basic functions */
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>(
HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE));
/* 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);
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
is a global object. */
poolOwner->resetSubscriptionList();
CHECK(poolOwner->reset() == retval::CATCH_OK);
mqMock->clearMessages(true);
}

View File

@ -10,6 +10,7 @@
#include <testcfg/objects/systemObjectList.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/unittest/tests/mocks/MessageQueueMockBase.h>
#include "../../../datapool/PoolReadHelper.h"
namespace lpool {
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 {
public:
LocalPoolTestDataSet():
@ -41,19 +59,6 @@ public:
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<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);
@ -164,25 +169,62 @@ public:
}
ReturnValue_t subscribeWrapperSetUpdate() {
return poolManager.subscribeForSetUpdateMessages(lpool::testSetId,
return poolManager.subscribeForSetUpdateMessage(lpool::testSetId,
objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, false);
}
ReturnValue_t subscribeWrapperSetUpdateSnapshot() {
return poolManager.subscribeForSetUpdateMessages(lpool::testSetId,
return poolManager.subscribeForSetUpdateMessage(lpool::testSetId,
objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, true);
}
ReturnValue_t subscribeWrapperSetUpdateHk(bool diagnostics = false) {
return poolManager.subscribeForUpdatePackets(lpool::testSid, diagnostics,
return poolManager.subscribeForUpdatePacket(lpool::testSid, diagnostics,
false, objects::HK_RECEIVER_MOCK);
}
ReturnValue_t subscribeWrapperVariableUpdate(lp_id_t localPoolId) {
return poolManager.subscribeForVariableUpdateMessages(localPoolId,
return poolManager.subscribeForVariableUpdateMessage(localPoolId,
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() {
poolManager.clearReceiversList();
}
@ -191,14 +233,12 @@ public:
LocalPoolTestDataSet dataset;
private:
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,
&dataset);
lp_var_t<uint8_t> testUint8 = lp_var_t<uint8_t>(this, lpool::uint8VarId);
lp_var_t<float> testFloat = lp_var_t<float>(this, lpool::floatVarId);
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,
lpool::uint16Vec3Id, &dataset);
lpool::uint16Vec3Id);
lp_vec_t<int64_t, 2> testInt64Vec = lp_vec_t<int64_t, 2>(this,
lpool::int64Vec2Id);

View File

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