Compare commits
169 Commits
91f69aa34f
...
83d0db8242
Author | SHA1 | Date | |
---|---|---|---|
83d0db8242 | |||
533d164cac | |||
e44f8bfea3 | |||
fe2b3a01cf | |||
0e92fa4046 | |||
aa849894c6 | |||
1b8878a81f | |||
95096d83de | |||
2331188536 | |||
b2937ae510 | |||
3f8fae24dd | |||
12f47fdd0d | |||
b757c5523c | |||
5271375063 | |||
4955015f3f | |||
898e06591b | |||
d62dea442d | |||
66a09b94ac | |||
125dffcf28 | |||
8dec4c9311 | |||
26ce8d7185 | |||
1996f5949f | |||
ca4a0b1bb8 | |||
588f9471d8 | |||
703dfe9854 | |||
7bc04014e8 | |||
5eb6b277ba | |||
2684b0c68e | |||
76c571b969 | |||
c08e2f0bf7 | |||
bceca86da6 | |||
8ab2044c30 | |||
7173d2ecfc | |||
cf120e2d86 | |||
e92d3901f7 | |||
b071c850af | |||
df7434dae5 | |||
6e5b032dbb | |||
67b05fee2e | |||
286a3649cf | |||
9eefd5b95d | |||
36e524abe3 | |||
c527391b10 | |||
9602a3ed6a | |||
33823b445c | |||
e55f74a00e | |||
8b83de6ca9 | |||
3bacc8ec53 | |||
6f78c13dcf | |||
824f272432 | |||
78b6a83285 | |||
620b2ae79e | |||
8d28bc4b6a | |||
dcde177fe3 | |||
4f89fc62ab | |||
c59fa578c7 | |||
6c0972b2d5 | |||
7ad8763b14 | |||
03936fc5c1 | |||
3789663db7 | |||
b2e4438811 | |||
6501c16fd7 | |||
943495117b | |||
30910034f0 | |||
596d3bc68a | |||
1e73302ba2 | |||
9ba7fabdea | |||
676c9ffcf3 | |||
5095fd206f | |||
bb5b7bed40 | |||
e5b3b6d75e | |||
61affafecd | |||
7525c88392 | |||
3e9c19ee11 | |||
a7bf9a6734 | |||
82d7b7ed6f | |||
7598eb6b56 | |||
7b3616c41f | |||
9737d4cf2a | |||
c815905b5a | |||
59028ccc3f | |||
372e8c66a8 | |||
f5ac4368d8 | |||
6d0bc26624 | |||
baafeadcf4 | |||
dbda6fee82 | |||
cad302730e | |||
da2f594a00 | |||
d5a065eaa8 | |||
b695242420 | |||
494dd0db32 | |||
8be4f45969 | |||
4e5e6e145e | |||
e67ff6a937 | |||
bdd66072d1 | |||
1966b33613 | |||
331bbd14e7 | |||
9efc5dbd61 | |||
f82cc1bca4 | |||
e7cd7c8dc3 | |||
778ef4ef23 | |||
17b8d3fed0 | |||
d57955ade7 | |||
18e40d6248 | |||
caa3cf538b | |||
b3ca7b8667 | |||
2d069896a5 | |||
cb514e7493 | |||
bc7d956899 | |||
227074fd4d | |||
a56cf43897 | |||
e501390d7b | |||
e0d7363eed | |||
69b428222a | |||
6df1abf570 | |||
d84003d62a | |||
a7878aaf04 | |||
23873f6bc6 | |||
8de33f1301 | |||
35825a6561 | |||
043d47e5e4 | |||
bd903b8447 | |||
4250c7e022 | |||
d66e486f16 | |||
21a7fd621d | |||
dae4a5fa74 | |||
d8d18c9333 | |||
041461a066 | |||
bff8d103ba | |||
6580aa73bf | |||
9a3cd1d7fc | |||
e64de87af7 | |||
7ea4fedcea | |||
1caa45118b | |||
6e43a70af5 | |||
559c8d0637 | |||
ef5c0d50f3 | |||
3f47db9c18 | |||
91db9c362e | |||
1d3438bb7d | |||
cc84d542c8 | |||
9e9113912b | |||
9f09c190bb | |||
2b6ccbc17f | |||
458d783211 | |||
5cf2197c06 | |||
940bbf47e4 | |||
714f11f117 | |||
ffce336801 | |||
50ba377380 | |||
304773f7a7 | |||
16566a5690 | |||
68415853b5 | |||
fb5a1b93fc | |||
828115a566 | |||
d79f0e1172 | |||
35d8453b48 | |||
36039266ee | |||
a65211be51 | |||
fcff06c83f | |||
ea6ee7e79c | |||
110159eea1 | |||
788dbe4eca | |||
f45d19a961 | |||
c5ee2260d1 | |||
92f249dc62 | |||
f3cc664d4f | |||
4154c06825 | |||
9998de086f |
21
CHANGELOG
21
CHANGELOG
@@ -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).
|
||||
Added PUS parameter service 20 (only custom subservices available).
|
||||
|
@@ -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) {
|
||||
|
@@ -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.
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
};
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -8,7 +8,9 @@
|
||||
*/
|
||||
template<typename T, size_t MAX_SIZE, typename count_t = uint8_t>
|
||||
class FixedArrayList: public ArrayList<T, count_t> {
|
||||
static_assert(MAX_SIZE <= (pow(2,sizeof(count_t)*8)-1), "count_t is not large enough to hold MAX_SIZE");
|
||||
#if !defined(_MSC_VER)
|
||||
static_assert(MAX_SIZE <= (std::pow(2,sizeof(count_t)*8)-1), "count_t is not large enough to hold MAX_SIZE");
|
||||
#endif
|
||||
private:
|
||||
T data[MAX_SIZE];
|
||||
public:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#include "SharedRingBuffer.h"
|
||||
#include "../ipc/MutexFactory.h"
|
||||
#include "../ipc/MutexHelper.h"
|
||||
#include "../ipc/MutexGuard.h"
|
||||
|
||||
SharedRingBuffer::SharedRingBuffer(object_id_t objectId, const size_t size,
|
||||
bool overwriteOld, size_t maxExcessBytes):
|
||||
|
@@ -66,7 +66,7 @@ public:
|
||||
|
||||
/**
|
||||
* The mutex handle can be accessed directly, for example to perform
|
||||
* the lock with the #MutexHelper for a RAII compliant lock operation.
|
||||
* the lock with the #MutexGuard for a RAII compliant lock operation.
|
||||
* @return
|
||||
*/
|
||||
MutexIF* getMutexHandle() const;
|
||||
|
@@ -13,15 +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.
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 +88,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;
|
||||
}
|
||||
|
||||
@@ -105,14 +99,6 @@ MessageQueueId_t ExtendedControllerBase::getCommandQueue() const {
|
||||
return commandQueue->getId();
|
||||
}
|
||||
|
||||
LocalPoolDataSetBase* ExtendedControllerBase::getDataSetHandle(sid_t sid) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "ExtendedControllerBase::getDataSetHandle: No child "
|
||||
<< " implementation provided, returning nullptr!" << std::endl;
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LocalDataPoolManager* ExtendedControllerBase::getHkManagerHandle() {
|
||||
return &poolManager;
|
||||
}
|
||||
|
@@ -61,11 +61,11 @@ protected:
|
||||
/* HasLocalDatapoolIF overrides */
|
||||
virtual LocalDataPoolManager* getHkManagerHandle() override;
|
||||
virtual object_id_t getObjectId() const override;
|
||||
virtual ReturnValue_t initializeLocalDataPool(
|
||||
localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) override;
|
||||
virtual uint32_t getPeriodicOperationFrequency() const override;
|
||||
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
|
||||
|
||||
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) override = 0;
|
||||
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override = 0;
|
||||
};
|
||||
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -1,72 +1,82 @@
|
||||
#include "PoolDataSetBase.h"
|
||||
#include "../serviceinterface/ServiceInterfaceStream.h"
|
||||
#include "ReadCommitIFAttorney.h"
|
||||
|
||||
#include "../serviceinterface/ServiceInterface.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
|
||||
const size_t maxFillCount):
|
||||
registeredVariables(registeredVariablesArray),
|
||||
maxFillCount(maxFillCount) {
|
||||
}
|
||||
maxFillCount(maxFillCount) {}
|
||||
|
||||
PoolDataSetBase::~PoolDataSetBase() {}
|
||||
|
||||
|
||||
ReturnValue_t PoolDataSetBase::registerVariable(
|
||||
PoolVariableIF *variable) {
|
||||
if (state != States::STATE_SET_UNINITIALISED) {
|
||||
ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF *variable) {
|
||||
if(registeredVariables == nullptr) {
|
||||
/* Underlying container invalid */
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
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 +84,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 +221,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;
|
||||
}
|
||||
|
@@ -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_ */
|
||||
|
@@ -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:
|
||||
virtual public DataSetIF,
|
||||
virtual public ReadCommitIF {
|
||||
public:
|
||||
virtual~ PoolDataSetIF() {};
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
template <typename T>
|
||||
PoolEntry<T>::PoolEntry(std::initializer_list<T> initValue, bool setValid ):
|
||||
length(initValue.size()), valid(setValid) {
|
||||
length(static_cast<uint8_t>(initValue.size())), valid(setValid) {
|
||||
this->address = new T[this->length];
|
||||
if(initValue.size() == 0) {
|
||||
std::memset(this->address, 0, this->getByteSize());
|
||||
|
@@ -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;
|
||||
};
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
32
datapool/ReadCommitIFAttorney.h
Normal file
32
datapool/ReadCommitIFAttorney.h
Normal 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_ */
|
@@ -1,13 +1,15 @@
|
||||
#ifndef FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_
|
||||
#define FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_
|
||||
|
||||
#include "PoolDataSetIF.h"
|
||||
|
||||
class SharedDataSetIF: public PoolDataSetIF {
|
||||
class SharedDataSetIF {
|
||||
public:
|
||||
virtual ~SharedDataSetIF() {};
|
||||
|
||||
private:
|
||||
virtual ReturnValue_t lockDataset(dur_millis_t mutexTimeout) = 0;
|
||||
virtual ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType,
|
||||
dur_millis_t mutexTimeout) = 0;
|
||||
virtual ReturnValue_t unlockDataset() = 0;
|
||||
};
|
||||
|
||||
|
@@ -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;
|
||||
@@ -55,34 +65,45 @@ public:
|
||||
* usually be the period the pool owner performs its periodic operation.
|
||||
* @return
|
||||
*/
|
||||
virtual uint32_t getPeriodicOperationFrequency() const = 0;
|
||||
virtual dur_millis_t getPeriodicOperationFrequency() const = 0;
|
||||
|
||||
/**
|
||||
* @brief This function will be called by the manager if an update
|
||||
* notification is received.
|
||||
* @details HasLocalDataPoolIF
|
||||
* Can be overriden by the child class to handle changed datasets.
|
||||
* @param sid
|
||||
* @param storeId If a snapshot was requested, data will be located inside
|
||||
* @param sid SID of the updated set
|
||||
* @param storeId If a snapshot was requested, data will be located inside
|
||||
* the IPC store with this store ID.
|
||||
* @param clearMessage If this is set to true, the pool manager will take care of
|
||||
* clearing the store automatically
|
||||
*/
|
||||
virtual void handleChangedDataset(sid_t sid,
|
||||
store_address_t storeId = storeId::INVALID_STORE_ADDRESS) {
|
||||
return;
|
||||
store_address_t storeId = storeId::INVALID_STORE_ADDRESS,
|
||||
bool* clearMessage = nullptr) {
|
||||
if(clearMessage != nullptr) {
|
||||
*clearMessage = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function will be called by the manager if an update
|
||||
* notification is received.
|
||||
* @details
|
||||
* Can be overriden by the child class to handle changed pool IDs.
|
||||
* @param sid
|
||||
* @param storeId If a snapshot was requested, data will be located inside
|
||||
* Can be overriden by the child class to handle changed pool variables.
|
||||
* @param gpid GPID of the updated variable.
|
||||
* @param storeId If a snapshot was requested, data will be located inside
|
||||
* the IPC store with this store ID.
|
||||
* @param clearMessage Relevant for snapshots. If the boolean this points to is set to true,
|
||||
* the pool manager will take care of clearing the store automatically
|
||||
* after the callback.
|
||||
*/
|
||||
virtual void handleChangedPoolVariable(gp_id_t globPoolId,
|
||||
store_address_t storeId = storeId::INVALID_STORE_ADDRESS) {
|
||||
return;
|
||||
virtual void handleChangedPoolVariable(gp_id_t gpid,
|
||||
store_address_t storeId = storeId::INVALID_STORE_ADDRESS,
|
||||
bool* clearMessage = nullptr) {
|
||||
if(clearMessage != nullptr) {
|
||||
*clearMessage = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,8 +173,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");
|
||||
|
@@ -10,7 +10,7 @@
|
||||
#include "../housekeeping/AcceptsHkPacketsIF.h"
|
||||
#include "../timemanager/CCSDSTime.h"
|
||||
#include "../ipc/MutexFactory.h"
|
||||
#include "../ipc/MutexHelper.h"
|
||||
#include "../ipc/MutexGuard.h"
|
||||
#include "../ipc/QueueFactory.h"
|
||||
|
||||
#include <array>
|
||||
@@ -38,19 +38,23 @@ LocalDataPoolManager::LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQue
|
||||
hkQueue = queueToUse;
|
||||
}
|
||||
|
||||
LocalDataPoolManager::~LocalDataPoolManager() {}
|
||||
LocalDataPoolManager::~LocalDataPoolManager() {
|
||||
if(mutex != nullptr) {
|
||||
MutexFactory::instance()->deleteMutex(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
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 +102,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) {
|
||||
@@ -132,13 +136,16 @@ ReturnValue_t LocalDataPoolManager::performHkOperation() {
|
||||
ReturnValue_t LocalDataPoolManager::handleHkUpdate(HkReceiver& receiver,
|
||||
ReturnValue_t& status) {
|
||||
if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
|
||||
// Update packets shall only be generated from datasets.
|
||||
/* Update packets shall only be generated from datasets. */
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner,
|
||||
receiver.dataId.sid);
|
||||
if(dataSet == nullptr) {
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
if(dataSet->hasChanged()) {
|
||||
// prepare and send update notification
|
||||
/* Prepare and send update notification */
|
||||
ReturnValue_t result = generateHousekeepingPacket(
|
||||
receiver.dataId.sid, dataSet, true);
|
||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
@@ -328,7 +335,7 @@ void LocalDataPoolManager::handleChangeResetLogic(
|
||||
toReset->setChanged(false);
|
||||
}
|
||||
/* All recipients have been notified, reset the changed flag */
|
||||
if(changeInfo.currentUpdateCounter <= 1) {
|
||||
else if(changeInfo.currentUpdateCounter <= 1) {
|
||||
toReset->setChanged(false);
|
||||
changeInfo.currentUpdateCounter = 0;
|
||||
}
|
||||
@@ -372,15 +379,15 @@ ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(sid_t sid,
|
||||
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, enableReporting);
|
||||
LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics);
|
||||
LocalPoolDataSetAttorney::initializePeriodicHelper(*dataSet, collectionInterval,
|
||||
owner->getPeriodicOperationFrequency(), isDiagnostics);
|
||||
owner->getPeriodicOperationFrequency());
|
||||
}
|
||||
|
||||
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 =
|
||||
@@ -398,19 +405,18 @@ ReturnValue_t LocalDataPoolManager::subscribeForUpdatePackets(sid_t sid,
|
||||
hkReceiver.destinationQueue = hkReceiverObject->getHkQueue();
|
||||
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||
//LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid);
|
||||
if(dataSet != nullptr) {
|
||||
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, true);
|
||||
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 +431,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 +452,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;
|
||||
@@ -516,11 +522,19 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(
|
||||
}
|
||||
|
||||
case(HousekeepingMessage::REPORT_DIAGNOSTICS_REPORT_STRUCTURES): {
|
||||
return generateSetStructurePacket(sid, true);
|
||||
result = generateSetStructurePacket(sid, true);
|
||||
if(result == HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case(HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): {
|
||||
return generateSetStructurePacket(sid, false);
|
||||
result = generateSetStructurePacket(sid, false);
|
||||
if(result == HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(HousekeepingMessage::MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL):
|
||||
case(HousekeepingMessage::MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL): {
|
||||
@@ -540,14 +554,15 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(
|
||||
case(HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT):
|
||||
case(HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT): {
|
||||
LocalPoolDataSetBase* dataSet =HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||
//LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid);
|
||||
if(command == HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT
|
||||
and LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
|
||||
return WRONG_HK_PACKET_TYPE;
|
||||
result = WRONG_HK_PACKET_TYPE;
|
||||
break;
|
||||
}
|
||||
else if(command == HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT
|
||||
and not LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
|
||||
return WRONG_HK_PACKET_TYPE;
|
||||
result = WRONG_HK_PACKET_TYPE;
|
||||
break;
|
||||
}
|
||||
return generateHousekeepingPacket(HousekeepingMessage::getSid(message),
|
||||
dataSet, true);
|
||||
@@ -566,14 +581,22 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(
|
||||
case(HousekeepingMessage::UPDATE_SNAPSHOT_SET): {
|
||||
store_address_t storeId;
|
||||
HousekeepingMessage::getUpdateSnapshotSetCommand(message, &storeId);
|
||||
owner->handleChangedDataset(sid, storeId);
|
||||
bool clearMessage = true;
|
||||
owner->handleChangedDataset(sid, storeId, &clearMessage);
|
||||
if(clearMessage) {
|
||||
message->clear();
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
case(HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE): {
|
||||
store_address_t storeId;
|
||||
gp_id_t globPoolId = HousekeepingMessage::getUpdateSnapshotVariableCommand(message,
|
||||
&storeId);
|
||||
owner->handleChangedPoolVariable(globPoolId, storeId);
|
||||
bool clearMessage = true;
|
||||
owner->handleChangedPoolVariable(globPoolId, storeId, &clearMessage);
|
||||
if(clearMessage) {
|
||||
message->clear();
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
@@ -616,7 +639,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
|
||||
LocalPoolDataSetBase* dataSet, bool forDownlink,
|
||||
MessageQueueId_t destination) {
|
||||
if(dataSet == nullptr) {
|
||||
// Configuration error.
|
||||
/* Configuration error. */
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
||||
"generateHousekeepingPacket",
|
||||
DATASET_NOT_FOUND);
|
||||
@@ -632,7 +655,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
|
||||
return result;
|
||||
}
|
||||
|
||||
// and now we set a HK message and send it the HK packet destination.
|
||||
/* Now we set a HK message and send it the HK packet destination. */
|
||||
CommandMessage hkMessage;
|
||||
if(LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
|
||||
HousekeepingMessage::setHkDiagnosticsReply(&hkMessage, sid, storeId);
|
||||
@@ -642,7 +665,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
|
||||
}
|
||||
|
||||
if(hkQueue == nullptr) {
|
||||
// error, no queue available to send packet with.
|
||||
/* Error, no queue available to send packet with. */
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
||||
"generateHousekeepingPacket",
|
||||
QUEUE_OR_DESTINATION_INVALID);
|
||||
@@ -650,7 +673,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
|
||||
}
|
||||
if(destination == MessageQueueIF::NO_QUEUE) {
|
||||
if(hkDestinationId == MessageQueueIF::NO_QUEUE) {
|
||||
// error, all destinations invalid
|
||||
/* Error, all destinations invalid */
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
||||
"generateHousekeepingPacket",
|
||||
QUEUE_OR_DESTINATION_INVALID);
|
||||
@@ -729,6 +752,12 @@ void LocalDataPoolManager::performPeriodicHkGeneration(HkReceiver& receiver) {
|
||||
ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid,
|
||||
bool enable, bool isDiagnostics) {
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||
if(dataSet == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "togglePeriodicGeneration",
|
||||
DATASET_NOT_FOUND);
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
|
||||
if((LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and not isDiagnostics) or
|
||||
(not LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and isDiagnostics)) {
|
||||
return WRONG_HK_PACKET_TYPE;
|
||||
@@ -746,6 +775,12 @@ ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid,
|
||||
ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
|
||||
float newCollectionInterval, bool isDiagnostics) {
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||
if(dataSet == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "changeCollectionInterval",
|
||||
DATASET_NOT_FOUND);
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool targetIsDiagnostics = LocalPoolDataSetAttorney::isDiagnostics(*dataSet);
|
||||
if((targetIsDiagnostics and not isDiagnostics) or
|
||||
(not targetIsDiagnostics and isDiagnostics)) {
|
||||
@@ -756,7 +791,7 @@ ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
|
||||
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet);
|
||||
|
||||
if(periodicHelper == nullptr) {
|
||||
// config error
|
||||
/* Configuration error, set might not have a corresponding pool manager */
|
||||
return PERIODIC_HELPER_INVALID;
|
||||
}
|
||||
|
||||
@@ -766,13 +801,11 @@ ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid,
|
||||
bool isDiagnostics) {
|
||||
// Get and check dataset first.
|
||||
//LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid);
|
||||
/* Get and check dataset first. */
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||
if(dataSet == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
||||
"performPeriodicHkGeneration",
|
||||
DATASET_NOT_FOUND);
|
||||
"performPeriodicHkGeneration", DATASET_NOT_FOUND);
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
|
||||
@@ -829,8 +862,12 @@ 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);
|
||||
/* Also clear the reset helper if it exists */
|
||||
if(hkUpdateResetList != nullptr) {
|
||||
HkUpdateResetList().swap(*hkUpdateResetList);
|
||||
}
|
||||
}
|
||||
|
||||
MutexIF* LocalDataPoolManager::getLocalPoolMutex() {
|
||||
@@ -843,6 +880,7 @@ object_id_t LocalDataPoolManager::getCreatorObjectId() const {
|
||||
|
||||
void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
|
||||
const char* functionName, ReturnValue_t error, const char* errorPrint) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
if(errorPrint == nullptr) {
|
||||
if(error == DATASET_NOT_FOUND) {
|
||||
errorPrint = "Dataset not found";
|
||||
@@ -873,7 +911,6 @@ void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
|
||||
}
|
||||
|
||||
if(outputType == sif::OutputTypes::OUT_WARNING) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "LocalDataPoolManager::" << functionName
|
||||
<< ": Object ID 0x" << std::setw(8) << std::setfill('0')
|
||||
@@ -883,10 +920,8 @@ void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
|
||||
sif::printWarning("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n",
|
||||
functionName, owner->getObjectId(), errorPrint);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
}
|
||||
else if(outputType == sif::OutputTypes::OUT_ERROR) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "LocalDataPoolManager::" << functionName
|
||||
<< ": Object ID 0x" << std::setw(8) << std::setfill('0')
|
||||
@@ -896,8 +931,8 @@ void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
|
||||
sif::printError("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n",
|
||||
functionName, owner->getObjectId(), errorPrint);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
}
|
||||
#endif /* #if FSFW_VERBOSE_LEVEL >= 1 */
|
||||
}
|
||||
|
||||
LocalDataPoolManager* LocalDataPoolManager::getPoolManagerHandle() {
|
||||
|
@@ -14,7 +14,7 @@
|
||||
#include "../ipc/MutexIF.h"
|
||||
#include "../ipc/CommandMessage.h"
|
||||
#include "../ipc/MessageQueueIF.h"
|
||||
#include "../ipc/MutexHelper.h"
|
||||
#include "../ipc/MutexGuard.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@@ -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,23 @@ 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) {
|
||||
if(poolEntry == nullptr) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
auto poolIter = localPoolMap.find(localPoolId);
|
||||
if (poolIter == localPoolMap.end()) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry",
|
||||
|
@@ -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,16 +36,15 @@ 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) {
|
||||
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
|
||||
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(
|
||||
sid.objectId);
|
||||
if(hkOwner != nullptr) {
|
||||
@@ -58,8 +58,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid,
|
||||
this->sid = sid;
|
||||
}
|
||||
|
||||
LocalPoolDataSetBase::LocalPoolDataSetBase(
|
||||
PoolVariableIF **registeredVariablesArray,
|
||||
LocalPoolDataSetBase::LocalPoolDataSetBase(PoolVariableIF **registeredVariablesArray,
|
||||
const size_t maxNumberOfVariables, bool protectEveryReadCommitCall):
|
||||
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
|
||||
this->setReadCommitProtectionBehaviour(protectEveryReadCommitCall);
|
||||
@@ -95,23 +94,32 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer
|
||||
size_t *size, size_t maxSize,
|
||||
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];
|
||||
const uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount)/8.0);
|
||||
uint8_t* validityPtr = nullptr;
|
||||
#ifdef _MSC_VER
|
||||
/* Use a std::vector here because MSVC will (rightly) not create a fixed size array
|
||||
with a non constant size specifier */
|
||||
std::vector<uint8_t> validityMask(validityMaskSize);
|
||||
validityPtr = validityMask.data();
|
||||
#else
|
||||
uint8_t validityMask[validityMaskSize] = {0};
|
||||
validityPtr = validityMask;
|
||||
#endif
|
||||
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(validityPtr + validBufferIndex, validBufferIndexBit);
|
||||
}
|
||||
if(validBufferIndexBit == 7) {
|
||||
validBufferIndex ++;
|
||||
validBufferIndexBit = 0;
|
||||
}
|
||||
else {
|
||||
validBufferIndexBit ++;
|
||||
}
|
||||
|
||||
result = registeredVariables[count]->serialize(buffer, size, maxSize,
|
||||
streamEndianness);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
@@ -123,7 +131,7 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer
|
||||
return SerializeIF::BUFFER_TOO_SHORT;
|
||||
}
|
||||
// copy validity buffer to end
|
||||
std::memcpy(*buffer, validityMask, validityMaskSize);
|
||||
std::memcpy(*buffer, validityPtr, validityMaskSize);
|
||||
*size += validityMaskSize;
|
||||
return result;
|
||||
}
|
||||
@@ -148,7 +156,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 +181,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 +254,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;
|
||||
}
|
||||
@@ -277,11 +270,9 @@ bool LocalPoolDataSetBase::getReportingEnabled() const {
|
||||
return reportingEnabled;
|
||||
}
|
||||
|
||||
void LocalPoolDataSetBase::initializePeriodicHelper(
|
||||
float collectionInterval, dur_millis_t minimumPeriodicInterval,
|
||||
bool isDiagnostics, uint8_t nonDiagIntervalFactor) {
|
||||
periodicHelper->initialize(collectionInterval, minimumPeriodicInterval,
|
||||
isDiagnostics, nonDiagIntervalFactor);
|
||||
void LocalPoolDataSetBase::initializePeriodicHelper(float collectionInterval,
|
||||
dur_millis_t minimumPeriodicInterval, uint8_t nonDiagIntervalFactor) {
|
||||
periodicHelper->initialize(collectionInterval, minimumPeriodicInterval, nonDiagIntervalFactor);
|
||||
}
|
||||
|
||||
void LocalPoolDataSetBase::setChanged(bool changed) {
|
||||
@@ -296,19 +287,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 +294,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 +306,18 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
float LocalPoolDataSetBase::getCollectionInterval() const {
|
||||
if(periodicHelper != nullptr) {
|
||||
return periodicHelper->getCollectionIntervalInSeconds();
|
||||
}
|
||||
else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
@@ -160,6 +166,16 @@ public:
|
||||
|
||||
object_id_t getCreatorObjectId();
|
||||
|
||||
bool getReportingEnabled() const;
|
||||
|
||||
/**
|
||||
* Returns the current periodic HK generation interval this set
|
||||
* belongs to a HK manager and the interval is not 0. Otherwise,
|
||||
* returns 0.0
|
||||
* @return
|
||||
*/
|
||||
float getCollectionInterval() const;
|
||||
|
||||
protected:
|
||||
sid_t sid;
|
||||
//! This mutex is used if the data is created by one object only.
|
||||
@@ -174,11 +190,9 @@ protected:
|
||||
*/
|
||||
bool reportingEnabled = false;
|
||||
void setReportingEnabled(bool enabled);
|
||||
bool getReportingEnabled() const;
|
||||
|
||||
void initializePeriodicHelper(float collectionInterval,
|
||||
dur_millis_t minimumPeriodicInterval,
|
||||
bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5);
|
||||
void initializePeriodicHelper(float collectionInterval, dur_millis_t minimumPeriodicInterval,
|
||||
uint8_t nonDiagIntervalFactor = 5);
|
||||
|
||||
/**
|
||||
* If the valid state of a dataset is always relevant to the whole
|
||||
@@ -218,13 +232,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;
|
||||
|
||||
|
@@ -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 */
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -25,7 +25,7 @@ inline LocalPoolVector<T, vectorSize>::LocalPoolVector(gp_id_t globalPoolId,
|
||||
template<typename T, uint16_t vectorSize>
|
||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::read(
|
||||
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
||||
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
|
||||
MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
|
||||
return readWithoutLock();
|
||||
}
|
||||
template<typename T, uint16_t vectorSize>
|
||||
@@ -64,7 +64,7 @@ inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(bool valid,
|
||||
template<typename T, uint16_t vectorSize>
|
||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(
|
||||
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
||||
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
|
||||
MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
|
||||
return commitWithoutLock();
|
||||
}
|
||||
|
||||
|
@@ -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_ */
|
||||
|
@@ -1,16 +1,37 @@
|
||||
#include "SharedLocalDataSet.h"
|
||||
|
||||
|
||||
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, sid_t sid,
|
||||
const size_t maxSize): SystemObject(objectId),
|
||||
LocalPoolDataSetBase(sid, nullptr, maxSize) {
|
||||
LocalPoolDataSetBase(sid, nullptr, maxSize), poolVarVector(maxSize) {
|
||||
this->setContainer(poolVarVector.data());
|
||||
datasetLock = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
ReturnValue_t SharedLocalDataSet::lockDataset(dur_millis_t mutexTimeout) {
|
||||
return datasetLock->lockMutex(MutexIF::TimeoutType::WAITING, mutexTimeout);
|
||||
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId,
|
||||
HasLocalDataPoolIF *owner, uint32_t setId,
|
||||
const size_t maxSize): SystemObject(objectId),
|
||||
LocalPoolDataSetBase(owner, setId, nullptr, maxSize), poolVarVector(maxSize) {
|
||||
this->setContainer(poolVarVector.data());
|
||||
datasetLock = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
ReturnValue_t SharedLocalDataSet::lockDataset(MutexIF::TimeoutType timeoutType,
|
||||
dur_millis_t mutexTimeout) {
|
||||
if(datasetLock != nullptr) {
|
||||
return datasetLock->lockMutex(timeoutType, mutexTimeout);
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
|
||||
SharedLocalDataSet::~SharedLocalDataSet() {
|
||||
MutexFactory::instance()->deleteMutex(datasetLock);
|
||||
}
|
||||
|
||||
ReturnValue_t SharedLocalDataSet::unlockDataset() {
|
||||
return datasetLock->unlockMutex();
|
||||
if(datasetLock != nullptr) {
|
||||
return datasetLock->unlockMutex();
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
@@ -11,16 +11,22 @@
|
||||
* multiple threads. It provides a lock in addition to all other functionalities provided
|
||||
* by the LocalPoolDataSetBase class.
|
||||
*
|
||||
* TODO: override and protect read, commit and some other calls used by pool manager.
|
||||
* The user is completely responsible for lockingand unlocking the dataset when using the
|
||||
* shared dataset.
|
||||
*/
|
||||
class SharedLocalDataSet:
|
||||
public SystemObject,
|
||||
public LocalPoolDataSetBase,
|
||||
public SharedDataSetIF {
|
||||
public:
|
||||
SharedLocalDataSet(object_id_t objectId, sid_t sid,
|
||||
SharedLocalDataSet(object_id_t objectId, HasLocalDataPoolIF* owner, uint32_t setId,
|
||||
const size_t maxSize);
|
||||
ReturnValue_t lockDataset(dur_millis_t mutexTimeout) override;
|
||||
SharedLocalDataSet(object_id_t objectId, sid_t sid, const size_t maxSize);
|
||||
|
||||
virtual~ SharedLocalDataSet();
|
||||
|
||||
ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
dur_millis_t mutexTimeout = 20) override;
|
||||
ReturnValue_t unlockDataset() override;
|
||||
private:
|
||||
|
||||
|
12
datapoollocal/datapoollocal.h
Normal file
12
datapoollocal/datapoollocal.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||
#define FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||
|
||||
/* Collected related headers */
|
||||
#include "LocalPoolVariable.h"
|
||||
#include "LocalPoolVector.h"
|
||||
#include "StaticLocalDataSet.h"
|
||||
#include "LocalDataSet.h"
|
||||
#include "SharedLocalDataSet.h"
|
||||
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ */
|
@@ -24,7 +24,6 @@ private:
|
||||
return manager.getMutexHandle();
|
||||
}
|
||||
|
||||
|
||||
template<typename T> friend class LocalPoolVariable;
|
||||
template<typename T, uint16_t vecSize> friend class LocalPoolVector;
|
||||
};
|
||||
|
@@ -14,9 +14,8 @@ private:
|
||||
}
|
||||
|
||||
static void initializePeriodicHelper(LocalPoolDataSetBase& set, float collectionInterval,
|
||||
uint32_t minimumPeriodicIntervalMs,
|
||||
bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5) {
|
||||
set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs, isDiagnostics,
|
||||
uint32_t minimumPeriodicIntervalMs, uint8_t nonDiagIntervalFactor = 5) {
|
||||
set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs,
|
||||
nonDiagIntervalFactor);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
@@ -96,11 +96,11 @@ union gp_id_t {
|
||||
return raw == INVALID_GPID;
|
||||
}
|
||||
|
||||
bool operator==(const sid_t& other) const {
|
||||
bool operator==(const gp_id_t& other) const {
|
||||
return raw == other.raw;
|
||||
}
|
||||
|
||||
bool operator!=(const sid_t& other) const {
|
||||
bool operator!=(const gp_id_t& other) const {
|
||||
return not (raw == other.raw);
|
||||
}
|
||||
};
|
||||
|
@@ -308,6 +308,14 @@ void DeviceHandlerBase::doStateMachine() {
|
||||
uint32_t currentUptime;
|
||||
Clock::getUptime(¤tUptime);
|
||||
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);
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
||||
|
BIN
doc/images/PoolArchitecture.png
Normal file
BIN
doc/images/PoolArchitecture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
@@ -7,6 +7,7 @@ target_sources(${LIB_FSFW_NAME}
|
||||
PeriodicOperationDivider.cpp
|
||||
timevalOperations.cpp
|
||||
Type.cpp
|
||||
bitutility.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(math)
|
||||
add_subdirectory(math)
|
||||
|
@@ -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;
|
||||
@@ -67,7 +76,9 @@ void arrayprinter::printHex(const uint8_t *data, size_t size,
|
||||
}
|
||||
}
|
||||
}
|
||||
#if FSFW_DISABLE_PRINTOUT == 0
|
||||
printf("[%s]\n", printBuffer);
|
||||
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -108,7 +119,9 @@ void arrayprinter::printDec(const uint8_t *data, size_t size,
|
||||
}
|
||||
}
|
||||
}
|
||||
#if FSFW_DISABLE_PRINTOUT == 0
|
||||
printf("[%s]\n", printBuffer);
|
||||
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
|
||||
#endif
|
||||
}
|
||||
|
||||
|
33
globalfunctions/bitutility.cpp
Normal file
33
globalfunctions/bitutility.cpp
Normal 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);
|
||||
}
|
18
globalfunctions/bitutility.h
Normal file
18
globalfunctions/bitutility.h
Normal 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_ */
|
@@ -1,5 +1,5 @@
|
||||
#include "HealthTable.h"
|
||||
#include "../ipc/MutexHelper.h"
|
||||
#include "../ipc/MutexGuard.h"
|
||||
#include "../ipc/MutexFactory.h"
|
||||
#include "../serialize/SerializeAdapter.h"
|
||||
|
||||
@@ -31,7 +31,7 @@ ReturnValue_t HealthTable::registerObject(object_id_t object,
|
||||
|
||||
void HealthTable::setHealth(object_id_t object,
|
||||
HasHealthIF::HealthState newState) {
|
||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
||||
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||
HealthMap::iterator iter = healthMap.find(object);
|
||||
if (iter != healthMap.end()) {
|
||||
iter->second = newState;
|
||||
@@ -40,7 +40,7 @@ void HealthTable::setHealth(object_id_t object,
|
||||
|
||||
HasHealthIF::HealthState HealthTable::getHealth(object_id_t object) {
|
||||
HasHealthIF::HealthState state = HasHealthIF::HEALTHY;
|
||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
||||
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||
HealthMap::iterator iter = healthMap.find(object);
|
||||
if (iter != healthMap.end()) {
|
||||
state = iter->second;
|
||||
@@ -49,7 +49,7 @@ HasHealthIF::HealthState HealthTable::getHealth(object_id_t object) {
|
||||
}
|
||||
|
||||
bool HealthTable::hasHealth(object_id_t object) {
|
||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
||||
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||
HealthMap::iterator iter = healthMap.find(object);
|
||||
if (iter != healthMap.end()) {
|
||||
return true;
|
||||
@@ -58,14 +58,14 @@ bool HealthTable::hasHealth(object_id_t object) {
|
||||
}
|
||||
|
||||
size_t HealthTable::getPrintSize() {
|
||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
||||
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||
uint32_t size = healthMap.size() * sizeof(object_id_t) +
|
||||
sizeof(HasHealthIF::HealthState) + sizeof(uint16_t);
|
||||
return size;
|
||||
}
|
||||
|
||||
void HealthTable::printAll(uint8_t* pointer, size_t maxSize) {
|
||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
||||
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||
size_t size = 0;
|
||||
uint16_t count = healthMap.size();
|
||||
SerializeAdapter::serialize(&count,
|
||||
@@ -81,7 +81,7 @@ void HealthTable::printAll(uint8_t* pointer, size_t maxSize) {
|
||||
|
||||
ReturnValue_t HealthTable::iterate(HealthEntry *value, bool reset) {
|
||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
||||
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||
if (reset) {
|
||||
mapIterator = healthMap.begin();
|
||||
}
|
||||
|
@@ -84,15 +84,21 @@ void HousekeepingMessage::setCollectionIntervalModificationCommand(
|
||||
else {
|
||||
command->setCommand(MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL);
|
||||
}
|
||||
command->setParameter3(collectionInterval);
|
||||
|
||||
/* Raw storage of the float in the message. Do not use setParameter3, does
|
||||
implicit conversion to integer type! */
|
||||
std::memcpy(command->getData() + 2 * sizeof(uint32_t), &collectionInterval,
|
||||
sizeof(collectionInterval));
|
||||
|
||||
setSid(command, sid);
|
||||
}
|
||||
|
||||
sid_t HousekeepingMessage::getCollectionIntervalModificationCommand(
|
||||
const CommandMessage* command, float* newCollectionInterval) {
|
||||
|
||||
if(newCollectionInterval != nullptr) {
|
||||
*newCollectionInterval = command->getParameter3();
|
||||
std::memcpy(newCollectionInterval, command->getData() + 2 * sizeof(uint32_t),
|
||||
sizeof(*newCollectionInterval));
|
||||
}
|
||||
|
||||
return getSid(command);
|
||||
@@ -151,7 +157,8 @@ void HousekeepingMessage::clear(CommandMessage* message) {
|
||||
case(DIAGNOSTICS_REPORT):
|
||||
case(HK_DEFINITIONS_REPORT):
|
||||
case(DIAGNOSTICS_DEFINITION_REPORT):
|
||||
case(UPDATE_SNAPSHOT_SET): {
|
||||
case(UPDATE_SNAPSHOT_SET):
|
||||
case(UPDATE_SNAPSHOT_VARIABLE): {
|
||||
store_address_t storeId;
|
||||
getHkDataReply(message, &storeId);
|
||||
StorageManagerIF *ipcStore = objectManager->get<StorageManagerIF>(
|
||||
|
@@ -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:
|
||||
|
@@ -11,7 +11,8 @@
|
||||
* @brief This helper class will be used to serialize and deserialize update housekeeping packets
|
||||
* into the store.
|
||||
*/
|
||||
class HousekeepingSnapshot: public SerializeIF {
|
||||
class HousekeepingSnapshot:
|
||||
public SerializeIF {
|
||||
public:
|
||||
|
||||
/**
|
||||
@@ -36,6 +37,17 @@ public:
|
||||
timeStamp(timeStamp), timeStampSize(timeStampSize),
|
||||
updateData(dataSetPtr) {};
|
||||
|
||||
/**
|
||||
* Update packet constructor for pool variables.
|
||||
* @param timeStamp
|
||||
* @param timeStampSize
|
||||
* @param dataSetPtr
|
||||
*/
|
||||
HousekeepingSnapshot(CCSDSTime::CDS_short* cdsShort, LocalPoolObjectBase* dataSetPtr):
|
||||
timeStamp(reinterpret_cast<uint8_t*>(cdsShort)),
|
||||
timeStampSize(sizeof(CCSDSTime::CDS_short)), updateData(dataSetPtr) {};
|
||||
|
||||
|
||||
/**
|
||||
* Update packet constructor for pool variables.
|
||||
* @param timeStamp
|
||||
@@ -47,8 +59,8 @@ public:
|
||||
timeStamp(timeStamp), timeStampSize(timeStampSize),
|
||||
updateData(dataSetPtr) {};
|
||||
|
||||
virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size,
|
||||
size_t maxSize, Endianness streamEndianness) const {
|
||||
virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize,
|
||||
Endianness streamEndianness) const {
|
||||
if(timeStamp != nullptr) {
|
||||
/* Endianness will always be MACHINE, so we can simply use memcpy
|
||||
here. */
|
||||
|
@@ -4,46 +4,87 @@
|
||||
#include <cmath>
|
||||
|
||||
PeriodicHousekeepingHelper::PeriodicHousekeepingHelper(
|
||||
LocalPoolDataSetBase* owner): owner(owner) {}
|
||||
|
||||
LocalPoolDataSetBase* owner): owner(owner) {}
|
||||
|
||||
void PeriodicHousekeepingHelper::initialize(float collectionInterval,
|
||||
dur_millis_t minimumPeriodicInterval, bool isDiagnostics,
|
||||
uint8_t nonDiagIntervalFactor) {
|
||||
this->minimumPeriodicInterval = minimumPeriodicInterval;
|
||||
if(not isDiagnostics) {
|
||||
this->minimumPeriodicInterval = this->minimumPeriodicInterval *
|
||||
nonDiagIntervalFactor;
|
||||
}
|
||||
collectionIntervalTicks = intervalSecondsToInterval(collectionInterval);
|
||||
dur_millis_t minimumPeriodicInterval, uint8_t nonDiagIntervalFactor) {
|
||||
this->minimumPeriodicInterval = minimumPeriodicInterval;
|
||||
this->nonDiagIntervalFactor = nonDiagIntervalFactor;
|
||||
collectionIntervalTicks = intervalSecondsToIntervalTicks(collectionInterval);
|
||||
/* This will cause a checkOpNecessary call to be true immediately. I think it's okay
|
||||
if a HK packet is generated immediately instead of waiting one generation cycle. */
|
||||
internalTickCounter = collectionIntervalTicks;
|
||||
}
|
||||
|
||||
float PeriodicHousekeepingHelper::getCollectionIntervalInSeconds() {
|
||||
return intervalToIntervalSeconds(collectionIntervalTicks);
|
||||
float PeriodicHousekeepingHelper::getCollectionIntervalInSeconds() const {
|
||||
return intervalTicksToSeconds(collectionIntervalTicks);
|
||||
}
|
||||
|
||||
bool PeriodicHousekeepingHelper::checkOpNecessary() {
|
||||
if(internalTickCounter >= collectionIntervalTicks) {
|
||||
internalTickCounter = 1;
|
||||
return true;
|
||||
}
|
||||
internalTickCounter++;
|
||||
return false;
|
||||
if(internalTickCounter >= collectionIntervalTicks) {
|
||||
internalTickCounter = 1;
|
||||
return true;
|
||||
}
|
||||
internalTickCounter++;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t PeriodicHousekeepingHelper::intervalSecondsToInterval(
|
||||
float collectionIntervalSeconds) {
|
||||
return std::ceil(collectionIntervalSeconds * 1000
|
||||
/ minimumPeriodicInterval);
|
||||
uint32_t PeriodicHousekeepingHelper::intervalSecondsToIntervalTicks(
|
||||
float collectionIntervalSeconds) {
|
||||
if(owner == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
bool isDiagnostics = owner->isDiagnostics();
|
||||
|
||||
/* Avoid division by zero */
|
||||
if(minimumPeriodicInterval == 0) {
|
||||
if(isDiagnostics) {
|
||||
/* Perform operation each cycle */
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return nonDiagIntervalFactor;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dur_millis_t intervalInMs = collectionIntervalSeconds * 1000;
|
||||
uint32_t divisor = minimumPeriodicInterval;
|
||||
if(not isDiagnostics) {
|
||||
/* We need to multiply the divisor because non-diagnostics only
|
||||
allow a multiple of the minimum periodic interval */
|
||||
divisor *= nonDiagIntervalFactor;
|
||||
}
|
||||
uint32_t ticks = std::ceil(static_cast<float>(intervalInMs) / divisor);
|
||||
if(not isDiagnostics) {
|
||||
/* Now we need to multiply the calculated ticks with the factor as as well
|
||||
because the minimum tick count to generate a non-diagnostic is the factor itself.
|
||||
|
||||
Example calculation for non-diagnostic with
|
||||
0.4 second interval and 0.2 second task interval.
|
||||
Resultant tick count of 5 is equal to operation each second.
|
||||
|
||||
Examle calculation for non-diagnostic with 2.0 second interval and 0.2 second
|
||||
task interval.
|
||||
Resultant tick count of 10 is equal to operatin every 2 seconds.
|
||||
|
||||
Example calculation for diagnostic with 0.4 second interval and 0.3
|
||||
second task interval. Resulting tick count of 2 is equal to operation
|
||||
every 0.6 seconds. */
|
||||
ticks *= nonDiagIntervalFactor;
|
||||
}
|
||||
return ticks;
|
||||
}
|
||||
}
|
||||
|
||||
float PeriodicHousekeepingHelper::intervalToIntervalSeconds(
|
||||
uint32_t collectionInterval) {
|
||||
return static_cast<float>(collectionInterval *
|
||||
minimumPeriodicInterval);
|
||||
float PeriodicHousekeepingHelper::intervalTicksToSeconds(
|
||||
uint32_t collectionInterval) const {
|
||||
/* Number of ticks times the minimum interval is in milliseconds, so we divide by 1000 to get
|
||||
the value in seconds */
|
||||
return static_cast<float>(collectionInterval * minimumPeriodicInterval / 1000.0);
|
||||
}
|
||||
|
||||
void PeriodicHousekeepingHelper::changeCollectionInterval(
|
||||
float newIntervalSeconds) {
|
||||
collectionIntervalTicks = intervalSecondsToInterval(newIntervalSeconds);
|
||||
float newIntervalSeconds) {
|
||||
collectionIntervalTicks = intervalSecondsToIntervalTicks(newIntervalSeconds);
|
||||
}
|
||||
|
||||
|
@@ -10,18 +10,19 @@ class PeriodicHousekeepingHelper {
|
||||
public:
|
||||
PeriodicHousekeepingHelper(LocalPoolDataSetBase* owner);
|
||||
|
||||
void initialize(float collectionInterval,
|
||||
dur_millis_t minimumPeriodicInterval, bool isDiagnostics,
|
||||
uint8_t nonDiagIntervalFactor);
|
||||
void initialize(float collectionInterval, dur_millis_t minimumPeriodicInterval,
|
||||
uint8_t nonDiagIntervalFactor);
|
||||
|
||||
void changeCollectionInterval(float newInterval);
|
||||
float getCollectionIntervalInSeconds();
|
||||
float getCollectionIntervalInSeconds() const;
|
||||
bool checkOpNecessary();
|
||||
|
||||
private:
|
||||
LocalPoolDataSetBase* owner = nullptr;
|
||||
uint8_t nonDiagIntervalFactor = 0;
|
||||
|
||||
uint32_t intervalSecondsToInterval(float collectionIntervalSeconds);
|
||||
float intervalToIntervalSeconds(uint32_t collectionInterval);
|
||||
uint32_t intervalSecondsToIntervalTicks(float collectionIntervalSeconds);
|
||||
float intervalTicksToSeconds(uint32_t collectionInterval) const;
|
||||
|
||||
dur_millis_t minimumPeriodicInterval = 0;
|
||||
uint32_t internalTickCounter = 1;
|
||||
|
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
60
ipc/MutexGuard.h
Normal file
60
ipc/MutexGuard.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef FRAMEWORK_IPC_MUTEXGUARD_H_
|
||||
#define FRAMEWORK_IPC_MUTEXGUARD_H_
|
||||
|
||||
#include "MutexFactory.h"
|
||||
#include "../serviceinterface/ServiceInterface.h"
|
||||
|
||||
class MutexGuard {
|
||||
public:
|
||||
MutexGuard(MutexIF* mutex, MutexIF::TimeoutType timeoutType =
|
||||
MutexIF::TimeoutType::BLOCKING, uint32_t timeoutMs = 0):
|
||||
internalMutex(mutex) {
|
||||
if(mutex == nullptr) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "MutexGuard: Passed mutex is invalid!" << std::endl;
|
||||
#else
|
||||
sif::printError("MutexGuard: Passed mutex is invalid!\n");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
return;
|
||||
}
|
||||
result = mutex->lockMutex(timeoutType,
|
||||
timeoutMs);
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
if(result == MutexIF::MUTEX_TIMEOUT) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "MutexGuard: Lock of mutex failed with timeout of "
|
||||
<< timeoutMs << " milliseconds!" << std::endl;
|
||||
#else
|
||||
sif::printError("MutexGuard: Lock of mutex failed with timeout of %lu milliseconds\n",
|
||||
timeoutMs);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
|
||||
}
|
||||
else if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "MutexGuard: Lock of Mutex failed with code " << result << std::endl;
|
||||
#else
|
||||
sif::printError("MutexGuard: Lock of Mutex failed with code %d\n", result);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
}
|
||||
#else
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
}
|
||||
|
||||
ReturnValue_t getLockResult() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
~MutexGuard() {
|
||||
if(internalMutex != nullptr) {
|
||||
internalMutex->unlockMutex();
|
||||
}
|
||||
}
|
||||
private:
|
||||
MutexIF* internalMutex;
|
||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
|
||||
};
|
||||
|
||||
#endif /* FRAMEWORK_IPC_MUTEXGUARD_H_ */
|
@@ -1,54 +0,0 @@
|
||||
#ifndef FRAMEWORK_IPC_MUTEXHELPER_H_
|
||||
#define FRAMEWORK_IPC_MUTEXHELPER_H_
|
||||
|
||||
#include "MutexFactory.h"
|
||||
#include "../serviceinterface/ServiceInterface.h"
|
||||
|
||||
class MutexHelper {
|
||||
public:
|
||||
MutexHelper(MutexIF* mutex, MutexIF::TimeoutType timeoutType =
|
||||
MutexIF::TimeoutType::BLOCKING, uint32_t timeoutMs = 0):
|
||||
internalMutex(mutex) {
|
||||
if(mutex == nullptr) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "MutexHelper: Passed mutex is invalid!" << std::endl;
|
||||
#else
|
||||
sif::printError("MutexHelper: Passed mutex is invalid!\n");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
return;
|
||||
}
|
||||
ReturnValue_t status = mutex->lockMutex(timeoutType,
|
||||
timeoutMs);
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
if(status == MutexIF::MUTEX_TIMEOUT) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "MutexHelper: Lock of mutex failed with timeout of "
|
||||
<< timeoutMs << " milliseconds!" << std::endl;
|
||||
#else
|
||||
sif::printError("MutexHelper: Lock of mutex failed with timeout of %lu milliseconds\n",
|
||||
timeoutMs);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
|
||||
}
|
||||
else if(status != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "MutexHelper: Lock of Mutex failed with code " << status << std::endl;
|
||||
#else
|
||||
sif::printError("MutexHelper: Lock of Mutex failed with code %d\n", status);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
}
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
}
|
||||
|
||||
~MutexHelper() {
|
||||
if(internalMutex != nullptr) {
|
||||
internalMutex->unlockMutex();
|
||||
}
|
||||
}
|
||||
private:
|
||||
MutexIF* internalMutex;
|
||||
};
|
||||
|
||||
#endif /* FRAMEWORK_IPC_MUTEXHELPER_H_ */
|
@@ -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() {}
|
||||
/**
|
||||
|
@@ -31,4 +31,6 @@ else()
|
||||
message(FATAL_ERROR "The host OS could not be determined! Aborting.")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(common)
|
@@ -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;
|
||||
}
|
||||
|
3
osal/common/CMakeLists.txt
Normal file
3
osal/common/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
tcpipCommon.cpp
|
||||
)
|
36
osal/common/tcpipCommon.cpp
Normal file
36
osal/common/tcpipCommon.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "tcpipCommon.h"
|
||||
|
||||
void tcpip::determineErrorStrings(Protocol protocol, ErrorSources errorSrc, std::string &protStr,
|
||||
std::string &srcString) {
|
||||
if(protocol == Protocol::TCP) {
|
||||
protStr = "TCP";
|
||||
}
|
||||
else if(protocol == Protocol::UDP) {
|
||||
protStr = "UDP";
|
||||
}
|
||||
else {
|
||||
protStr = "Unknown protocol";
|
||||
}
|
||||
|
||||
if(errorSrc == ErrorSources::SETSOCKOPT_CALL) {
|
||||
srcString = "setsockopt call";
|
||||
}
|
||||
else if(errorSrc == ErrorSources::SOCKET_CALL) {
|
||||
srcString = "socket call";
|
||||
}
|
||||
else if(errorSrc == ErrorSources::LISTEN_CALL) {
|
||||
srcString = "listen call";
|
||||
}
|
||||
else if(errorSrc == ErrorSources::ACCEPT_CALL) {
|
||||
srcString = "accept call";
|
||||
}
|
||||
else if(errorSrc == ErrorSources::RECVFROM_CALL) {
|
||||
srcString = "recvfrom call";
|
||||
}
|
||||
else if(errorSrc == ErrorSources::GETADDRINFO_CALL) {
|
||||
srcString = "getaddrinfo call";
|
||||
}
|
||||
else {
|
||||
srcString = "unknown call";
|
||||
}
|
||||
}
|
36
osal/common/tcpipCommon.h
Normal file
36
osal/common/tcpipCommon.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef FSFW_OSAL_COMMON_TCPIPCOMMON_H_
|
||||
#define FSFW_OSAL_COMMON_TCPIPCOMMON_H_
|
||||
|
||||
#include "../../timemanager/clockDefinitions.h"
|
||||
#include <string>
|
||||
|
||||
namespace tcpip {
|
||||
|
||||
const char* const DEFAULT_UDP_SERVER_PORT = "7301";
|
||||
const char* const DEFAULT_TCP_SERVER_PORT = "7303";
|
||||
|
||||
enum class Protocol {
|
||||
UDP,
|
||||
TCP
|
||||
};
|
||||
|
||||
enum class ErrorSources {
|
||||
GETADDRINFO_CALL,
|
||||
SOCKET_CALL,
|
||||
SETSOCKOPT_CALL,
|
||||
BIND_CALL,
|
||||
RECV_CALL,
|
||||
RECVFROM_CALL,
|
||||
LISTEN_CALL,
|
||||
ACCEPT_CALL,
|
||||
SENDTO_CALL
|
||||
};
|
||||
|
||||
void determineErrorStrings(Protocol protocol, ErrorSources errorSrc, std::string& protStr,
|
||||
std::string& srcString);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* FSFW_OSAL_COMMON_TCPIPCOMMON_H_ */
|
@@ -1,4 +1,4 @@
|
||||
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
||||
#include "../../serviceinterface/ServiceInterface.h"
|
||||
#include "../../timemanager/Clock.h"
|
||||
|
||||
#include <chrono>
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
||||
#include "../../ipc/MutexFactory.h"
|
||||
#include "../../ipc/MutexHelper.h"
|
||||
#include "../../ipc/MutexGuard.h"
|
||||
|
||||
MessageQueue::MessageQueue(size_t messageDepth, size_t maxMessageSize):
|
||||
messageSize(maxMessageSize), messageDepth(messageDepth) {
|
||||
@@ -65,7 +65,7 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
|
||||
}
|
||||
// not sure this will work..
|
||||
//*message = std::move(messageQueue.front());
|
||||
MutexHelper mutexLock(queueLock, MutexIF::TimeoutType::WAITING, 20);
|
||||
MutexGuard mutexLock(queueLock, MutexIF::TimeoutType::WAITING, 20);
|
||||
MessageQueueMessage* currentMessage = &messageQueue.front();
|
||||
std::copy(currentMessage->getBuffer(),
|
||||
currentMessage->getBuffer() + messageSize, message->getBuffer());
|
||||
@@ -130,7 +130,7 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
if(targetQueue->messageQueue.size() < targetQueue->messageDepth) {
|
||||
MutexHelper mutexLock(targetQueue->queueLock,
|
||||
MutexGuard mutexLock(targetQueue->queueLock,
|
||||
MutexIF::TimeoutType::WAITING, 20);
|
||||
// not ideal, works for now though.
|
||||
MessageQueueMessage* mqmMessage =
|
||||
|
@@ -24,5 +24,7 @@ MutexIF* MutexFactory::createMutex() {
|
||||
}
|
||||
|
||||
void MutexFactory::deleteMutex(MutexIF* mutex) {
|
||||
delete mutex;
|
||||
if(mutex != nullptr) {
|
||||
delete mutex;
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,8 @@
|
||||
#include "QueueMapManager.h"
|
||||
|
||||
#include "../../serviceinterface/ServiceInterface.h"
|
||||
#include "../../ipc/MutexFactory.h"
|
||||
#include "../../ipc/MutexHelper.h"
|
||||
#include "../../ipc/MutexGuard.h"
|
||||
|
||||
QueueMapManager* QueueMapManager::mqManagerInstance = nullptr;
|
||||
|
||||
@@ -42,7 +43,7 @@ ReturnValue_t QueueMapManager::addMessageQueue(
|
||||
|
||||
MessageQueueIF* QueueMapManager::getMessageQueue(
|
||||
MessageQueueId_t messageQueueId) const {
|
||||
MutexHelper(mapLock, MutexIF::TimeoutType::WAITING, 50);
|
||||
MutexGuard(mapLock, MutexIF::TimeoutType::WAITING, 50);
|
||||
auto queueIter = queueMap.find(messageQueueId);
|
||||
if(queueIter != queueMap.end()) {
|
||||
return queueIter->second;
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#include "../../tasks/SemaphoreFactory.h"
|
||||
#include "../../osal/linux/BinarySemaphore.h"
|
||||
#include "../../osal/linux/CountingSemaphore.h"
|
||||
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
||||
#include "../../serviceinterface/ServiceInterface.h"
|
||||
|
||||
SemaphoreFactory* SemaphoreFactory::factoryInstance = nullptr;
|
||||
|
||||
|
@@ -16,6 +16,7 @@ target_sources(${LIB_FSFW_NAME}
|
||||
TcUnixUdpPollingTask.cpp
|
||||
TmTcUnixUdpBridge.cpp
|
||||
Timer.cpp
|
||||
tcpipHelpers.cpp
|
||||
)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@
|
||||
#include "PeriodicPosixTask.h"
|
||||
|
||||
#include "../../tasks/TaskFactory.h"
|
||||
#include "../../serviceinterface/ServiceInterface.h"
|
||||
#include "../../returnvalues/HasReturnvaluesIF.h"
|
||||
|
||||
//TODO: Different variant than the lazy loading in QueueFactory. What's better and why?
|
||||
|
@@ -1,7 +1,9 @@
|
||||
#include "TcUnixUdpPollingTask.h"
|
||||
#include "tcpipHelpers.h"
|
||||
|
||||
#include "../../globalfunctions/arrayprinter.h"
|
||||
|
||||
#include <errno.h>
|
||||
#define FSFW_UDP_RCV_WIRETAPPING_ENABLED 0
|
||||
|
||||
TcUnixUdpPollingTask::TcUnixUdpPollingTask(object_id_t objectId,
|
||||
object_id_t tmtcUnixUdpBridge, size_t frameSize,
|
||||
@@ -15,8 +17,8 @@ TcUnixUdpPollingTask::TcUnixUdpPollingTask(object_id_t objectId,
|
||||
this->frameSize = DEFAULT_MAX_FRAME_SIZE;
|
||||
}
|
||||
|
||||
// Set up reception buffer with specified frame size.
|
||||
// For now, it is assumed that only one frame is held in the buffer!
|
||||
/* Set up reception buffer with specified frame size.
|
||||
For now, it is assumed that only one frame is held in the buffer! */
|
||||
receptionBuffer.reserve(this->frameSize);
|
||||
receptionBuffer.resize(this->frameSize);
|
||||
|
||||
@@ -31,34 +33,37 @@ TcUnixUdpPollingTask::TcUnixUdpPollingTask(object_id_t objectId,
|
||||
TcUnixUdpPollingTask::~TcUnixUdpPollingTask() {}
|
||||
|
||||
ReturnValue_t TcUnixUdpPollingTask::performOperation(uint8_t opCode) {
|
||||
// Poll for new UDP datagrams in permanent loop.
|
||||
while(1) {
|
||||
//! Sender Address is cached here.
|
||||
struct sockaddr_in senderAddress;
|
||||
socklen_t senderSockLen = sizeof(senderAddress);
|
||||
ssize_t bytesReceived = recvfrom(serverUdpSocket,
|
||||
receptionBuffer.data(), frameSize, receptionFlags,
|
||||
reinterpret_cast<sockaddr*>(&senderAddress), &senderSockLen);
|
||||
if(bytesReceived < 0) {
|
||||
// handle error
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TcSocketPollingTask::performOperation: Reception"
|
||||
"error." << std::endl;
|
||||
#endif
|
||||
handleReadError();
|
||||
/* Sender Address is cached here. */
|
||||
struct sockaddr_in senderAddress;
|
||||
socklen_t senderAddressSize = sizeof(senderAddress);
|
||||
|
||||
/* Poll for new UDP datagrams in permanent loop. */
|
||||
while(true) {
|
||||
ssize_t bytesReceived = recvfrom(
|
||||
serverUdpSocket,
|
||||
receptionBuffer.data(),
|
||||
frameSize,
|
||||
receptionFlags,
|
||||
reinterpret_cast<sockaddr*>(&senderAddress),
|
||||
&senderAddressSize
|
||||
);
|
||||
if(bytesReceived < 0) {
|
||||
/* Handle error */
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TcSocketPollingTask::performOperation: Reception error." << std::endl;
|
||||
#endif
|
||||
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::RECVFROM_CALL, 500);
|
||||
continue;
|
||||
}
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
// sif::debug << "TcSocketPollingTask::performOperation: " << bytesReceived
|
||||
// << " bytes received" << std::endl;
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1
|
||||
sif::debug << "TcSocketPollingTask::performOperation: " << bytesReceived
|
||||
<< " bytes received" << std::endl;
|
||||
#endif
|
||||
|
||||
ReturnValue_t result = handleSuccessfullTcRead(bytesReceived);
|
||||
if(result != HasReturnvaluesIF::RETURN_FAILED) {
|
||||
|
||||
}
|
||||
tmtcBridge->registerCommConnect();
|
||||
tmtcBridge->checkAndSetClientAddress(senderAddress);
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
@@ -67,15 +72,21 @@ ReturnValue_t TcUnixUdpPollingTask::performOperation(uint8_t opCode) {
|
||||
|
||||
ReturnValue_t TcUnixUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
|
||||
store_address_t storeId;
|
||||
ReturnValue_t result = tcStore->addData(&storeId,
|
||||
receptionBuffer.data(), bytesRead);
|
||||
// arrayprinter::print(receptionBuffer.data(), bytesRead);
|
||||
|
||||
#if FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1
|
||||
arrayprinter::print(receptionBuffer.data(), bytesRead);
|
||||
#endif
|
||||
|
||||
ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRead);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TcSerialPollingTask::transferPusToSoftwareBus: Data "
|
||||
sif::error << "TcUnixUdpPollingTask::handleSuccessfullTcRead: Data "
|
||||
"storage failed" << std::endl;
|
||||
sif::error << "Packet size: " << bytesRead << std::endl;
|
||||
#endif
|
||||
#else
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
@@ -83,10 +94,13 @@ ReturnValue_t TcUnixUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
|
||||
|
||||
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "Serial Polling: Sending message to queue failed"
|
||||
<< std::endl;
|
||||
#endif
|
||||
sif::error << "TcUnixUdpPollingTask::handleSuccessfullTcRead: Sending message to queue "
|
||||
"failed" << std::endl;
|
||||
#else
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
tcStore->deleteData(storeId);
|
||||
}
|
||||
return result;
|
||||
@@ -111,15 +125,16 @@ ReturnValue_t TcUnixUdpPollingTask::initialize() {
|
||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||
}
|
||||
|
||||
serverUdpSocket = tmtcBridge->serverSocket;
|
||||
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t TcUnixUdpPollingTask::initializeAfterTaskCreation() {
|
||||
// Initialize the destination after task creation. This ensures
|
||||
// that the destination will be set in the TMTC bridge.
|
||||
/* Initialize the destination after task creation. This ensures
|
||||
that the destination has already been set in the TMTC bridge. */
|
||||
targetTcDestination = tmtcBridge->getRequestQueue();
|
||||
/* The server socket is set up in the bridge intialization. Calling this function here
|
||||
ensures that it is set up properly in any case*/
|
||||
serverUdpSocket = tmtcBridge->serverSocket;
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
@@ -135,24 +150,3 @@ void TcUnixUdpPollingTask::setTimeout(double timeoutSeconds) {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: sleep after error detection to prevent spam
|
||||
void TcUnixUdpPollingTask::handleReadError() {
|
||||
switch(errno) {
|
||||
case(EAGAIN): {
|
||||
// todo: When working in timeout mode, this will occur more often
|
||||
// and is not an error.
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TcUnixUdpPollingTask::handleReadError: Timeout."
|
||||
<< std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TcUnixUdpPollingTask::handleReadError: "
|
||||
<< strerror(errno) << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -48,6 +48,7 @@ private:
|
||||
object_id_t tmtcBridgeId = objects::NO_OBJECT;
|
||||
TmTcUnixUdpBridge* tmtcBridge = nullptr;
|
||||
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
|
||||
|
||||
//! Reception flags: https://linux.die.net/man/2/recvfrom.
|
||||
int receptionFlags = 0;
|
||||
|
||||
@@ -61,7 +62,6 @@ private:
|
||||
timeval receptionTimeout;
|
||||
|
||||
ReturnValue_t handleSuccessfullTcRead(size_t bytesRead);
|
||||
void handleReadError();
|
||||
};
|
||||
|
||||
#endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */
|
||||
|
@@ -1,206 +1,157 @@
|
||||
#include "TmTcUnixUdpBridge.h"
|
||||
#include "tcpipHelpers.h"
|
||||
#include "../../serviceinterface/ServiceInterface.h"
|
||||
#include "../../ipc/MutexHelper.h"
|
||||
#include "../../ipc/MutexGuard.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
TmTcUnixUdpBridge::TmTcUnixUdpBridge(object_id_t objectId,
|
||||
object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId,
|
||||
uint16_t serverPort, uint16_t clientPort):
|
||||
//! Debugging preprocessor define.
|
||||
#define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0
|
||||
|
||||
const std::string TmTcUnixUdpBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_UDP_SERVER_PORT;
|
||||
|
||||
TmTcUnixUdpBridge::TmTcUnixUdpBridge(object_id_t objectId, object_id_t tcDestination,
|
||||
object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort):
|
||||
TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) {
|
||||
mutex = MutexFactory::instance()->createMutex();
|
||||
if(udpServerPort == "") {
|
||||
this->udpServerPort = DEFAULT_UDP_SERVER_PORT;
|
||||
}
|
||||
else {
|
||||
this->udpServerPort = udpServerPort;
|
||||
}
|
||||
|
||||
uint16_t setServerPort = DEFAULT_UDP_SERVER_PORT;
|
||||
if(serverPort != 0xFFFF) {
|
||||
setServerPort = serverPort;
|
||||
}
|
||||
mutex = MutexFactory::instance()->createMutex();
|
||||
communicationLinkUp = false;
|
||||
}
|
||||
|
||||
uint16_t setClientPort = DEFAULT_UDP_CLIENT_PORT;
|
||||
if(clientPort != 0xFFFF) {
|
||||
setClientPort = clientPort;
|
||||
}
|
||||
ReturnValue_t TmTcUnixUdpBridge::initialize() {
|
||||
using namespace tcpip;
|
||||
|
||||
// Set up UDP socket: https://man7.org/linux/man-pages/man7/ip.7.html
|
||||
//clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if(serverSocket < 0) {
|
||||
ReturnValue_t result = TmTcBridge::initialize();
|
||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not open"
|
||||
" UDP socket!" << std::endl;
|
||||
sif::error << "TmTcUnixUdpBridge::initialize: TmTcBridge initialization failed!"
|
||||
<< std::endl;
|
||||
#endif
|
||||
handleSocketError();
|
||||
return;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
serverAddress.sin_family = AF_INET;
|
||||
struct addrinfo *addrResult = nullptr;
|
||||
struct addrinfo hints;
|
||||
|
||||
// Accept packets from any interface.
|
||||
//serverAddress.sin_addr.s_addr = inet_addr("127.73.73.0");
|
||||
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serverAddress.sin_port = htons(setServerPort);
|
||||
serverAddressLen = sizeof(serverAddress);
|
||||
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &serverSocketOptions,
|
||||
sizeof(serverSocketOptions));
|
||||
std::memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
clientAddress.sin_family = AF_INET;
|
||||
clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
clientAddress.sin_port = htons(setClientPort);
|
||||
clientAddressLen = sizeof(clientAddress);
|
||||
|
||||
int result = bind(serverSocket,
|
||||
reinterpret_cast<struct sockaddr*>(&serverAddress),
|
||||
serverAddressLen);
|
||||
if(result == -1) {
|
||||
/* Set up UDP socket:
|
||||
https://man7.org/linux/man-pages/man3/getaddrinfo.3.html
|
||||
Passing nullptr as the first parameter and specifying AI_PASSIVE in hints will cause
|
||||
getaddrinfo to assign the address 0.0.0.0 (any address) */
|
||||
int retval = getaddrinfo(nullptr, udpServerPort.c_str(), &hints, &addrResult);
|
||||
if (retval != 0) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not bind "
|
||||
"local port " << setServerPort << " to server socket!"
|
||||
<< std::endl;
|
||||
sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Retrieving address info failed!" <<
|
||||
std::endl;
|
||||
#endif
|
||||
handleBindError();
|
||||
return;
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
/* Set up UDP socket: https://man7.org/linux/man-pages/man7/ip.7.html */
|
||||
serverSocket = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol);
|
||||
if(serverSocket < 0) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not open UDP socket!" <<
|
||||
std::endl;
|
||||
#else
|
||||
sif::printError("TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not open UDP socket!\n");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
freeaddrinfo(addrResult);
|
||||
handleError(Protocol::UDP, ErrorSources::SOCKET_CALL);
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
retval = bind(serverSocket, addrResult->ai_addr, static_cast<int>(addrResult->ai_addrlen));
|
||||
if(retval != 0) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not bind "
|
||||
"local port (" << udpServerPort << ") to server socket!" << std::endl;
|
||||
#endif
|
||||
freeaddrinfo(addrResult);
|
||||
handleError(Protocol::UDP, ErrorSources::BIND_CALL);
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
TmTcUnixUdpBridge::~TmTcUnixUdpBridge() {
|
||||
if(mutex != nullptr) {
|
||||
MutexFactory::instance()->deleteMutex(mutex);
|
||||
}
|
||||
close(serverSocket);
|
||||
}
|
||||
|
||||
ReturnValue_t TmTcUnixUdpBridge::sendTm(const uint8_t *data, size_t dataLen) {
|
||||
int flags = 0;
|
||||
int flags = 0;
|
||||
|
||||
MutexHelper lock(mutex, MutexIF::TimeoutType::WAITING, 10);
|
||||
/* The target address can be set by different threads so this lock ensures thread-safety */
|
||||
MutexGuard lock(mutex, timeoutType, mutexTimeoutMs);
|
||||
|
||||
if(ipAddrAnySet){
|
||||
clientAddress.sin_addr.s_addr = htons(INADDR_ANY);
|
||||
//clientAddress.sin_addr.s_addr = inet_addr("127.73.73.1");
|
||||
clientAddressLen = sizeof(serverAddress);
|
||||
}
|
||||
|
||||
// char ipAddress [15];
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
|
||||
// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
|
||||
char ipAddress [15];
|
||||
sif::debug << "IP Address Sender: "<<
|
||||
inet_ntop(AF_INET,&clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
#endif
|
||||
|
||||
ssize_t bytesSent = sendto(serverSocket, data, dataLen, flags,
|
||||
reinterpret_cast<sockaddr*>(&clientAddress), clientAddressLen);
|
||||
if(bytesSent < 0) {
|
||||
ssize_t bytesSent = sendto(
|
||||
serverSocket,
|
||||
data,
|
||||
dataLen,
|
||||
flags,
|
||||
reinterpret_cast<sockaddr*>(&clientAddress),
|
||||
clientAddressLen
|
||||
);
|
||||
if(bytesSent < 0) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcUnixUdpBridge::sendTm: Send operation failed."
|
||||
<< std::endl;
|
||||
sif::warning << "TmTcUnixUdpBridge::sendTm: Send operation failed." << std::endl;
|
||||
#endif
|
||||
handleSendError();
|
||||
}
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
// sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were"
|
||||
// " sent." << std::endl;
|
||||
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SENDTO_CALL);
|
||||
}
|
||||
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
|
||||
sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were"
|
||||
" sent." << std::endl;
|
||||
#endif
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
void TmTcUnixUdpBridge::checkAndSetClientAddress(sockaddr_in& newAddress) {
|
||||
MutexHelper lock(mutex, MutexIF::TimeoutType::WAITING, 10);
|
||||
/* The target address can be set by different threads so this lock ensures thread-safety */
|
||||
MutexGuard lock(mutex, timeoutType, mutexTimeoutMs);
|
||||
|
||||
// char ipAddress [15];
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
|
||||
// &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
// sif::debug << "IP Address Old: " << inet_ntop(AF_INET,
|
||||
// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1
|
||||
char ipAddress [15];
|
||||
sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
|
||||
&newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
sif::debug << "IP Address Old: " << inet_ntop(AF_INET,
|
||||
&clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
#endif
|
||||
registerCommConnect();
|
||||
|
||||
// Set new IP address if it has changed.
|
||||
if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) {
|
||||
clientAddress.sin_addr.s_addr = newAddress.sin_addr.s_addr;
|
||||
clientAddressLen = sizeof(clientAddress);
|
||||
}
|
||||
/* Set new IP address to reply to. */
|
||||
clientAddress = newAddress;
|
||||
clientAddressLen = sizeof(clientAddress);
|
||||
}
|
||||
|
||||
|
||||
void TmTcUnixUdpBridge::handleSocketError() {
|
||||
// See: https://man7.org/linux/man-pages/man2/socket.2.html
|
||||
switch(errno) {
|
||||
case(EACCES):
|
||||
case(EINVAL):
|
||||
case(EMFILE):
|
||||
case(ENFILE):
|
||||
case(EAFNOSUPPORT):
|
||||
case(ENOBUFS):
|
||||
case(ENOMEM):
|
||||
case(EPROTONOSUPPORT):
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcUnixBridge::handleSocketError: Socket creation failed"
|
||||
<< " with " << strerror(errno) << std::endl;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcUnixBridge::handleSocketError: Unknown error"
|
||||
<< std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
void TmTcUnixUdpBridge::setMutexProperties(MutexIF::TimeoutType timeoutType,
|
||||
dur_millis_t timeoutMs) {
|
||||
this->timeoutType = timeoutType;
|
||||
this->mutexTimeoutMs = timeoutMs;
|
||||
}
|
||||
|
||||
void TmTcUnixUdpBridge::handleBindError() {
|
||||
// See: https://man7.org/linux/man-pages/man2/bind.2.html
|
||||
switch(errno) {
|
||||
case(EACCES): {
|
||||
/*
|
||||
Ephermeral ports can be shown with following command:
|
||||
sysctl -A | grep ip_local_port_range
|
||||
*/
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcUnixBridge::handleBindError: Port access issue."
|
||||
"Ports 1-1024 are reserved on UNIX systems and require root "
|
||||
"rights while ephermeral ports should not be used as well."
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case(EADDRINUSE):
|
||||
case(EBADF):
|
||||
case(EINVAL):
|
||||
case(ENOTSOCK):
|
||||
case(EADDRNOTAVAIL):
|
||||
case(EFAULT):
|
||||
case(ELOOP):
|
||||
case(ENAMETOOLONG):
|
||||
case(ENOENT):
|
||||
case(ENOMEM):
|
||||
case(ENOTDIR):
|
||||
case(EROFS): {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcUnixBridge::handleBindError: Socket creation failed"
|
||||
<< " with " << strerror(errno) << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcUnixBridge::handleBindError: Unknown error"
|
||||
<< std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TmTcUnixUdpBridge::handleSendError() {
|
||||
switch(errno) {
|
||||
default: {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcUnixBridge::handleSendError: "
|
||||
<< strerror(errno) << std::endl;
|
||||
#else
|
||||
sif::printError("TmTcUnixBridge::handleSendError: %s\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmTcUnixUdpBridge::setClientAddressToAny(bool ipAddrAnySet){
|
||||
this->ipAddrAnySet = ipAddrAnySet;
|
||||
}
|
||||
|
||||
|
@@ -7,45 +7,43 @@
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
class TmTcUnixUdpBridge: public TmTcBridge {
|
||||
friend class TcUnixUdpPollingTask;
|
||||
class TmTcUnixUdpBridge:
|
||||
public TmTcBridge {
|
||||
friend class TcUnixUdpPollingTask;
|
||||
public:
|
||||
// The ports chosen here should not be used by any other process.
|
||||
// List of used ports on Linux: /etc/services
|
||||
static constexpr uint16_t DEFAULT_UDP_SERVER_PORT = 7301;
|
||||
static constexpr uint16_t DEFAULT_UDP_CLIENT_PORT = 7302;
|
||||
|
||||
TmTcUnixUdpBridge(object_id_t objectId, object_id_t tcDestination,
|
||||
object_id_t tmStoreId, object_id_t tcStoreId,
|
||||
uint16_t serverPort = 0xFFFF,uint16_t clientPort = 0xFFFF);
|
||||
virtual~ TmTcUnixUdpBridge();
|
||||
/* The ports chosen here should not be used by any other process.
|
||||
List of used ports on Linux: /etc/services */
|
||||
static const std::string DEFAULT_UDP_SERVER_PORT;
|
||||
|
||||
void checkAndSetClientAddress(sockaddr_in& clientAddress);
|
||||
TmTcUnixUdpBridge(object_id_t objectId, object_id_t tcDestination,
|
||||
object_id_t tmStoreId, object_id_t tcStoreId,
|
||||
std::string serverPort = "");
|
||||
virtual~ TmTcUnixUdpBridge();
|
||||
|
||||
/**
|
||||
* Set properties of internal mutex.
|
||||
*/
|
||||
void setMutexProperties(MutexIF::TimeoutType timeoutType, dur_millis_t timeoutMs);
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
|
||||
void checkAndSetClientAddress(sockaddr_in& clientAddress);
|
||||
|
||||
void setClientAddressToAny(bool ipAddrAnySet);
|
||||
protected:
|
||||
virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override;
|
||||
virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override;
|
||||
|
||||
private:
|
||||
int serverSocket = 0;
|
||||
int serverSocket = 0;
|
||||
std::string udpServerPort;
|
||||
|
||||
const int serverSocketOptions = 0;
|
||||
struct sockaddr_in clientAddress;
|
||||
socklen_t clientAddressLen = 0;
|
||||
|
||||
struct sockaddr_in clientAddress;
|
||||
socklen_t clientAddressLen = 0;
|
||||
|
||||
struct sockaddr_in serverAddress;
|
||||
socklen_t serverAddressLen = 0;
|
||||
|
||||
bool ipAddrAnySet = false;
|
||||
|
||||
//! Access to the client address is mutex protected as it is set
|
||||
//! by another task.
|
||||
MutexIF* mutex;
|
||||
|
||||
void handleSocketError();
|
||||
void handleBindError();
|
||||
void handleSendError();
|
||||
//! Access to the client address is mutex protected as it is set by another task.
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
||||
dur_millis_t mutexTimeoutMs = 20;
|
||||
MutexIF* mutex;
|
||||
};
|
||||
|
||||
#endif /* FRAMEWORK_OSAL_LINUX_TMTCUNIXUDPBRIDGE_H_ */
|
||||
|
108
osal/linux/tcpipHelpers.cpp
Normal file
108
osal/linux/tcpipHelpers.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "tcpipHelpers.h"
|
||||
|
||||
#include "../../tasks/TaskFactory.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string>
|
||||
|
||||
void tcpip::handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration) {
|
||||
int errCode = errno;
|
||||
std::string protocolString;
|
||||
std::string errorSrcString;
|
||||
determineErrorStrings(protocol, errorSrc, protocolString, errorSrcString);
|
||||
std::string infoString;
|
||||
switch(errCode) {
|
||||
case(EACCES): {
|
||||
infoString = "EACCES";
|
||||
break;
|
||||
}
|
||||
case(EINVAL): {
|
||||
infoString = "EINVAL";
|
||||
break;
|
||||
}
|
||||
case(EAGAIN): {
|
||||
infoString = "EAGAIN";
|
||||
break;
|
||||
}
|
||||
case(EMFILE): {
|
||||
infoString = "EMFILE";
|
||||
break;
|
||||
}
|
||||
case(ENFILE): {
|
||||
infoString = "ENFILE";
|
||||
break;
|
||||
}
|
||||
case(EAFNOSUPPORT): {
|
||||
infoString = "EAFNOSUPPORT";
|
||||
break;
|
||||
}
|
||||
case(ENOBUFS): {
|
||||
infoString = "ENOBUFS";
|
||||
break;
|
||||
}
|
||||
case(ENOMEM): {
|
||||
infoString = "ENOMEM";
|
||||
break;
|
||||
}
|
||||
case(EPROTONOSUPPORT): {
|
||||
infoString = "EPROTONOSUPPORT";
|
||||
break;
|
||||
}
|
||||
case(EADDRINUSE): {
|
||||
infoString = "EADDRINUSE";
|
||||
break;
|
||||
}
|
||||
case(EBADF): {
|
||||
infoString = "EBADF";
|
||||
break;
|
||||
}
|
||||
case(ENOTSOCK): {
|
||||
infoString = "ENOTSOCK";
|
||||
break;
|
||||
}
|
||||
case(EADDRNOTAVAIL): {
|
||||
infoString = "EADDRNOTAVAIL";
|
||||
break;
|
||||
}
|
||||
case(EFAULT): {
|
||||
infoString = "EFAULT";
|
||||
break;
|
||||
}
|
||||
case(ELOOP): {
|
||||
infoString = "ELOOP";
|
||||
break;
|
||||
}
|
||||
case(ENAMETOOLONG): {
|
||||
infoString = "ENAMETOOLONG";
|
||||
break;
|
||||
}
|
||||
case(ENOENT): {
|
||||
infoString = "ENOENT";
|
||||
break;
|
||||
}
|
||||
case(ENOTDIR): {
|
||||
infoString = "ENOTDIR";
|
||||
break;
|
||||
}
|
||||
case(EROFS): {
|
||||
infoString = "EROFS";
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
infoString = "Error code: " + std::to_string(errCode);
|
||||
}
|
||||
}
|
||||
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "tcpip::handleError: " << protocolString << " | " << errorSrcString <<
|
||||
" | " << infoString << std::endl;
|
||||
#else
|
||||
sif::printWarning("tcpip::handleError: %s | %s | %s\n", protocolString,
|
||||
errorSrcString, infoString);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
|
||||
if(sleepDuration > 0) {
|
||||
TaskFactory::instance()->delayTask(sleepDuration);
|
||||
}
|
||||
}
|
14
osal/linux/tcpipHelpers.h
Normal file
14
osal/linux/tcpipHelpers.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef FSFW_OSAL_LINUX_TCPIPHELPERS_H_
|
||||
#define FSFW_OSAL_LINUX_TCPIPHELPERS_H_
|
||||
|
||||
#include "../../timemanager/clockDefinitions.h"
|
||||
#include "../common/tcpipCommon.h"
|
||||
|
||||
namespace tcpip {
|
||||
|
||||
|
||||
void handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration = 0);
|
||||
|
||||
}
|
||||
|
||||
#endif /* FSFW_OSAL_LINUX_TCPIPHELPERS_H_ */
|
@@ -1,7 +1,7 @@
|
||||
#include "RtemsBasic.h"
|
||||
|
||||
#include "../../timemanager/Clock.h"
|
||||
#include "../../ipc/MutexHelper.h"
|
||||
#include "../../ipc/MutexGuard.h"
|
||||
|
||||
#include <rtems/score/todimpl.h>
|
||||
#include <rtems/rtems/clockimpl.h>
|
||||
@@ -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;
|
||||
}
|
||||
MutexGuard 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;
|
||||
}
|
||||
MutexGuard 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;
|
||||
}
|
||||
|
@@ -1,11 +1,11 @@
|
||||
target_sources(${LIB_FSFW_NAME}
|
||||
PRIVATE
|
||||
TcWinUdpPollingTask.cpp
|
||||
TmTcWinUdpBridge.cpp
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
TcWinUdpPollingTask.cpp
|
||||
TmTcWinUdpBridge.cpp
|
||||
TcWinTcpServer.cpp
|
||||
tcpipHelpers.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${LIB_FSFW_NAME}
|
||||
PRIVATE
|
||||
wsock32
|
||||
ws2_32
|
||||
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
|
||||
wsock32
|
||||
ws2_32
|
||||
)
|
131
osal/windows/TcWinTcpServer.cpp
Normal file
131
osal/windows/TcWinTcpServer.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "TcWinTcpServer.h"
|
||||
#include "tcpipHelpers.h"
|
||||
#include "../../serviceinterface/ServiceInterface.h"
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
const std::string TcWinTcpServer::DEFAULT_TCP_SERVER_PORT = "7301";
|
||||
const std::string TcWinTcpServer::DEFAULT_TCP_CLIENT_PORT = "7302";
|
||||
|
||||
TcWinTcpServer::TcWinTcpServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge,
|
||||
std::string customTcpServerPort):
|
||||
SystemObject(objectId), tcpPort(customTcpServerPort) {
|
||||
if(tcpPort == "") {
|
||||
tcpPort = DEFAULT_TCP_SERVER_PORT;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t TcWinTcpServer::initialize() {
|
||||
using namespace tcpip;
|
||||
int retval = 0;
|
||||
struct addrinfo *addrResult = nullptr;
|
||||
struct addrinfo hints;
|
||||
/* Initiates Winsock DLL. */
|
||||
WSAData wsaData;
|
||||
WORD wVersionRequested = MAKEWORD(2, 2);
|
||||
int err = WSAStartup(wVersionRequested, &wsaData);
|
||||
if (err != 0) {
|
||||
/* Tell the user that we could not find a usable Winsock DLL. */
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: " <<
|
||||
err << std::endl;
|
||||
#endif
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
ZeroMemory(&hints, sizeof (hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
retval = getaddrinfo(nullptr, tcpPort.c_str(), &hints, &addrResult);
|
||||
if (retval != 0) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "TcWinTcpServer::TcWinTcpServer: Retrieving address info failed!" <<
|
||||
std::endl;
|
||||
#endif
|
||||
handleError(Protocol::TCP, ErrorSources::GETADDRINFO_CALL);
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
/* Open TCP (stream) socket */
|
||||
listenerTcpSocket = socket(addrResult->ai_family, addrResult->ai_socktype,
|
||||
addrResult->ai_protocol);
|
||||
if(listenerTcpSocket == INVALID_SOCKET) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "TcWinTcpServer::TcWinTcpServer: Socket creation failed!" << std::endl;
|
||||
#endif
|
||||
freeaddrinfo(addrResult);
|
||||
handleError(Protocol::TCP, ErrorSources::SOCKET_CALL);
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
retval = bind(listenerTcpSocket, addrResult->ai_addr, static_cast<int>(addrResult->ai_addrlen));
|
||||
if(retval == SOCKET_ERROR) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "TcWinTcpServer::TcWinTcpServer: Binding socket failed!" <<
|
||||
std::endl;
|
||||
#endif
|
||||
freeaddrinfo(addrResult);
|
||||
handleError(Protocol::TCP, ErrorSources::BIND_CALL);
|
||||
}
|
||||
|
||||
freeaddrinfo(addrResult);
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
|
||||
TcWinTcpServer::~TcWinTcpServer() {
|
||||
closesocket(listenerTcpSocket);
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
ReturnValue_t TcWinTcpServer::performOperation(uint8_t opCode) {
|
||||
using namespace tcpip;
|
||||
/* If a connection is accepted, the corresponding socket will be assigned to the new socket */
|
||||
SOCKET clientSocket;
|
||||
sockaddr_in clientSockAddr;
|
||||
int connectorSockAddrLen = 0;
|
||||
int retval = 0;
|
||||
|
||||
/* Listen for connection requests permanently for lifetime of program */
|
||||
while(true) {
|
||||
retval = listen(listenerTcpSocket, currentBacklog);
|
||||
if(retval == SOCKET_ERROR) {
|
||||
handleError(Protocol::TCP, ErrorSources::LISTEN_CALL, 500);
|
||||
continue;
|
||||
}
|
||||
|
||||
clientSocket = accept(listenerTcpSocket, reinterpret_cast<sockaddr*>(&clientSockAddr),
|
||||
&connectorSockAddrLen);
|
||||
|
||||
if(clientSocket == INVALID_SOCKET) {
|
||||
handleError(Protocol::TCP, ErrorSources::ACCEPT_CALL, 500);
|
||||
continue;
|
||||
};
|
||||
|
||||
retval = recv(clientSocket, reinterpret_cast<char*>(receptionBuffer.data()),
|
||||
receptionBuffer.size(), 0);
|
||||
if(retval > 0) {
|
||||
#if FSFW_TCP_RCV_WIRETAPPING_ENABLED == 1
|
||||
sif::info << "TcWinTcpServer::performOperation: Received " << retval << " bytes."
|
||||
std::endl;
|
||||
#endif
|
||||
handleError(Protocol::TCP, ErrorSources::RECV_CALL, 500);
|
||||
}
|
||||
else if(retval == 0) {
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
|
||||
/* Done, shut down connection */
|
||||
retval = shutdown(clientSocket, SD_SEND);
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
|
47
osal/windows/TcWinTcpServer.h
Normal file
47
osal/windows/TcWinTcpServer.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_
|
||||
#define FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_
|
||||
|
||||
#include "../../objectmanager/SystemObject.h"
|
||||
#include "../../tasks/ExecutableObjectIF.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
//! Debugging preprocessor define.
|
||||
#define FSFW_TCP_RCV_WIRETAPPING_ENABLED 0
|
||||
|
||||
/**
|
||||
* @brief Windows TCP server used to receive telecommands on a Windows Host
|
||||
* @details
|
||||
* Based on: https://docs.microsoft.com/en-us/windows/win32/winsock/complete-server-code
|
||||
*/
|
||||
class TcWinTcpServer:
|
||||
public SystemObject,
|
||||
public ExecutableObjectIF {
|
||||
public:
|
||||
/* The ports chosen here should not be used by any other process. */
|
||||
static const std::string DEFAULT_TCP_SERVER_PORT;
|
||||
static const std::string DEFAULT_TCP_CLIENT_PORT;
|
||||
|
||||
TcWinTcpServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge,
|
||||
std::string customTcpServerPort = "");
|
||||
virtual~ TcWinTcpServer();
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
ReturnValue_t performOperation(uint8_t opCode) override;
|
||||
|
||||
private:
|
||||
|
||||
std::string tcpPort;
|
||||
SOCKET listenerTcpSocket = 0;
|
||||
struct sockaddr_in tcpAddress;
|
||||
int tcpAddrLen = sizeof(tcpAddress);
|
||||
int currentBacklog = 3;
|
||||
|
||||
std::vector<uint8_t> receptionBuffer;
|
||||
int tcpSockOpt = 0;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_ */
|
@@ -1,9 +1,12 @@
|
||||
#include "TcWinUdpPollingTask.h"
|
||||
#include "tcpipHelpers.h"
|
||||
#include "../../globalfunctions/arrayprinter.h"
|
||||
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
//! Debugging preprocessor define.
|
||||
#define FSFW_UDP_RCV_WIRETAPPING_ENABLED 0
|
||||
|
||||
TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId,
|
||||
object_id_t tmtcUnixUdpBridge, size_t frameSize,
|
||||
@@ -16,8 +19,8 @@ TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId,
|
||||
this->frameSize = DEFAULT_MAX_FRAME_SIZE;
|
||||
}
|
||||
|
||||
// Set up reception buffer with specified frame size.
|
||||
// For now, it is assumed that only one frame is held in the buffer!
|
||||
/* Set up reception buffer with specified frame size.
|
||||
For now, it is assumed that only one frame is held in the buffer! */
|
||||
receptionBuffer.reserve(this->frameSize);
|
||||
receptionBuffer.resize(this->frameSize);
|
||||
|
||||
@@ -32,34 +35,37 @@ TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId,
|
||||
TcWinUdpPollingTask::~TcWinUdpPollingTask() {}
|
||||
|
||||
ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) {
|
||||
// Poll for new UDP datagrams in permanent loop.
|
||||
/* Sender Address is cached here. */
|
||||
struct sockaddr_in senderAddress;
|
||||
int senderAddressSize = sizeof(senderAddress);
|
||||
|
||||
/* Poll for new UDP datagrams in permanent loop. */
|
||||
while(true) {
|
||||
//! Sender Address is cached here.
|
||||
struct sockaddr_in senderAddress;
|
||||
int senderAddressSize = sizeof(senderAddress);
|
||||
ssize_t bytesReceived = recvfrom(serverUdpSocket,
|
||||
reinterpret_cast<char*>(receptionBuffer.data()), frameSize,
|
||||
receptionFlags, reinterpret_cast<sockaddr*>(&senderAddress),
|
||||
&senderAddressSize);
|
||||
int bytesReceived = recvfrom(
|
||||
serverUdpSocket,
|
||||
reinterpret_cast<char*>(receptionBuffer.data()),
|
||||
frameSize,
|
||||
receptionFlags,
|
||||
reinterpret_cast<sockaddr*>(&senderAddress),
|
||||
&senderAddressSize
|
||||
);
|
||||
if(bytesReceived == SOCKET_ERROR) {
|
||||
// handle error
|
||||
/* Handle error */
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TcWinUdpPollingTask::performOperation: Reception"
|
||||
" error." << std::endl;
|
||||
sif::error << "TcWinUdpPollingTask::performOperation: Reception error." << std::endl;
|
||||
#endif
|
||||
handleReadError();
|
||||
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::RECVFROM_CALL, 1000);
|
||||
continue;
|
||||
}
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
//sif::debug << "TcWinUdpPollingTask::performOperation: " << bytesReceived
|
||||
// << " bytes received" << std::endl;
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1
|
||||
sif::debug << "TcWinUdpPollingTask::performOperation: " << bytesReceived <<
|
||||
" bytes received" << std::endl;
|
||||
#endif
|
||||
|
||||
ReturnValue_t result = handleSuccessfullTcRead(bytesReceived);
|
||||
if(result != HasReturnvaluesIF::RETURN_FAILED) {
|
||||
|
||||
}
|
||||
tmtcBridge->registerCommConnect();
|
||||
tmtcBridge->checkAndSetClientAddress(senderAddress);
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
@@ -68,15 +74,20 @@ ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) {
|
||||
|
||||
ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
|
||||
store_address_t storeId;
|
||||
ReturnValue_t result = tcStore->addData(&storeId,
|
||||
receptionBuffer.data(), bytesRead);
|
||||
// arrayprinter::print(receptionBuffer.data(), bytesRead);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TcSerialPollingTask::transferPusToSoftwareBus: Data "
|
||||
"storage failed" << std::endl;
|
||||
sif::error << "Packet size: " << bytesRead << std::endl;
|
||||
|
||||
#if FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1
|
||||
arrayprinter::print(receptionBuffer.data(), bytesRead);
|
||||
#endif
|
||||
|
||||
ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRead);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning<< "TcWinUdpPollingTask::transferPusToSoftwareBus: Data storage failed." <<
|
||||
std::endl;
|
||||
sif::warning << "Packet size: " << bytesRead << std::endl;
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
@@ -84,10 +95,12 @@ ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
|
||||
|
||||
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "Serial Polling: Sending message to queue failed"
|
||||
<< std::endl;
|
||||
#endif
|
||||
sif::warning << "TcWinUdpPollingTask::handleSuccessfullTcRead: "
|
||||
" Sending message to queue failed" << std::endl;
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
tcStore->deleteData(storeId);
|
||||
}
|
||||
return result;
|
||||
@@ -97,8 +110,7 @@ ReturnValue_t TcWinUdpPollingTask::initialize() {
|
||||
tcStore = objectManager->get<StorageManagerIF>(objects::TC_STORE);
|
||||
if (tcStore == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TcSerialPollingTask::initialize: TC Store uninitialized!"
|
||||
<< std::endl;
|
||||
sif::error << "TcWinUdpPollingTask::initialize: TC store uninitialized!" << std::endl;
|
||||
#endif
|
||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||
}
|
||||
@@ -106,25 +118,21 @@ ReturnValue_t TcWinUdpPollingTask::initialize() {
|
||||
tmtcBridge = objectManager->get<TmTcWinUdpBridge>(tmtcBridgeId);
|
||||
if(tmtcBridge == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TcSocketPollingTask::TcSocketPollingTask: Invalid"
|
||||
" TMTC bridge object!" << std::endl;
|
||||
sif::error << "TcWinUdpPollingTask::initialize: Invalid TMTC bridge object!" <<
|
||||
std::endl;
|
||||
#endif
|
||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||
}
|
||||
|
||||
serverUdpSocket = tmtcBridge->serverSocket;
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
//sif::info << "TcWinUdpPollingTask::initialize: Server UDP socket "
|
||||
// << serverUdpSocket << std::endl;
|
||||
#endif
|
||||
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t TcWinUdpPollingTask::initializeAfterTaskCreation() {
|
||||
// Initialize the destination after task creation. This ensures
|
||||
// that the destination has already been set in the TMTC bridge.
|
||||
/* Initialize the destination after task creation. This ensures
|
||||
that the destination has already been set in the TMTC bridge. */
|
||||
targetTcDestination = tmtcBridge->getRequestQueue();
|
||||
/* The server socket is set up in the bridge intialization. Calling this function here
|
||||
ensures that it is set up properly in any case*/
|
||||
serverUdpSocket = tmtcBridge->serverSocket;
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
@@ -139,39 +147,3 @@ void TcWinUdpPollingTask::setTimeout(double timeoutSeconds) {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void TcWinUdpPollingTask::handleReadError() {
|
||||
int error = WSAGetLastError();
|
||||
switch(error) {
|
||||
case(WSANOTINITIALISED): {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TcWinUdpPollingTask::handleReadError: WSANOTINITIALISED: "
|
||||
<< "WSAStartup(...) call " << "necessary" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(WSAEFAULT): {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TcWinUdpPollingTask::handleReadError: WSADEFAULT: "
|
||||
<< "Bad address " << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(WSAEINVAL): {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TcWinUdpPollingTask::handleReadError: WSAEINVAL: "
|
||||
<< "Invalid input parameters. " << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TcWinUdpPollingTask::handleReadError: Error code: "
|
||||
<< error << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
// to prevent spam.
|
||||
Sleep(1000);
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ class TcWinUdpPollingTask: public SystemObject,
|
||||
public:
|
||||
static constexpr size_t DEFAULT_MAX_FRAME_SIZE = 2048;
|
||||
//! 0.5 default milliseconds timeout for now.
|
||||
static constexpr timeval DEFAULT_TIMEOUT = {.tv_sec = 0, .tv_usec = 500};
|
||||
static constexpr timeval DEFAULT_TIMEOUT = {0, 500};
|
||||
|
||||
TcWinUdpPollingTask(object_id_t objectId, object_id_t tmtcUnixUdpBridge,
|
||||
size_t frameSize = 0, double timeoutSeconds = -1);
|
||||
@@ -48,11 +48,12 @@ private:
|
||||
object_id_t tmtcBridgeId = objects::NO_OBJECT;
|
||||
TmTcWinUdpBridge* tmtcBridge = nullptr;
|
||||
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
|
||||
//! Reception flags: https://linux.die.net/man/2/recvfrom.
|
||||
|
||||
//! See: https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom
|
||||
int receptionFlags = 0;
|
||||
|
||||
//! Server socket, which is member of TMTC bridge and is assigned in
|
||||
//! constructor
|
||||
//! Server socket, which is member of TMTC bridge.
|
||||
//! Will be cached shortly after SW intialization.
|
||||
SOCKET serverUdpSocket = 0;
|
||||
|
||||
std::vector<uint8_t> receptionBuffer;
|
||||
@@ -61,7 +62,6 @@ private:
|
||||
timeval receptionTimeout;
|
||||
|
||||
ReturnValue_t handleSuccessfullTcRead(size_t bytesRead);
|
||||
void handleReadError();
|
||||
};
|
||||
|
||||
#endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */
|
||||
|
@@ -1,14 +1,40 @@
|
||||
#include <fsfw/ipc/MutexHelper.h>
|
||||
#include "TmTcWinUdpBridge.h"
|
||||
#include "tcpipHelpers.h"
|
||||
|
||||
TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId,
|
||||
object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId,
|
||||
uint16_t serverPort, uint16_t clientPort):
|
||||
#include <fsfw/serviceinterface/ServiceInterface.h>
|
||||
#include <fsfw/ipc/MutexGuard.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
//! Debugging preprocessor define.
|
||||
#define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0
|
||||
|
||||
const std::string TmTcWinUdpBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_UDP_SERVER_PORT;
|
||||
|
||||
TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, object_id_t tcDestination,
|
||||
object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort):
|
||||
TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) {
|
||||
if(udpServerPort == "") {
|
||||
this->udpServerPort = DEFAULT_UDP_SERVER_PORT;
|
||||
}
|
||||
else {
|
||||
this->udpServerPort = udpServerPort;
|
||||
}
|
||||
|
||||
mutex = MutexFactory::instance()->createMutex();
|
||||
communicationLinkUp = false;
|
||||
}
|
||||
|
||||
// Initiates Winsock DLL.
|
||||
ReturnValue_t TmTcWinUdpBridge::initialize() {
|
||||
ReturnValue_t result = TmTcBridge::initialize();
|
||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcWinUdpBridge::initialize: TmTcBridge initialization failed!"
|
||||
<< std::endl;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Initiates Winsock DLL. */
|
||||
WSAData wsaData;
|
||||
WORD wVersionRequested = MAKEWORD(2, 2);
|
||||
int err = WSAStartup(wVersionRequested, &wsaData);
|
||||
@@ -16,197 +42,125 @@ TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId,
|
||||
/* Tell the user that we could not find a usable */
|
||||
/* Winsock DLL. */
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge:"
|
||||
"WSAStartup failed with error: " << err << std::endl;
|
||||
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: " <<
|
||||
err << std::endl;
|
||||
#else
|
||||
sif::printError("TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: %d\n",
|
||||
err);
|
||||
#endif
|
||||
return;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
uint16_t setServerPort = DEFAULT_UDP_SERVER_PORT;
|
||||
if(serverPort != 0xFFFF) {
|
||||
setServerPort = serverPort;
|
||||
struct addrinfo *addrResult = nullptr;
|
||||
struct addrinfo hints;
|
||||
|
||||
ZeroMemory(&hints, sizeof (hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
/* See:
|
||||
https://docs.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo
|
||||
for information about AI_PASSIVE. */
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
/* Set up UDP socket:
|
||||
https://en.wikipedia.org/wiki/Getaddrinfo
|
||||
Passing nullptr as the first parameter and specifying AI_PASSIVE in hints will cause
|
||||
getaddrinfo to assign the address 0.0.0.0 (any address) */
|
||||
int retval = getaddrinfo(nullptr, udpServerPort.c_str(), &hints, &addrResult);
|
||||
if (retval != 0) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Retrieving address info failed!" <<
|
||||
std::endl;
|
||||
#endif
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
uint16_t setClientPort = DEFAULT_UDP_CLIENT_PORT;
|
||||
if(clientPort != 0xFFFF) {
|
||||
setClientPort = clientPort;
|
||||
}
|
||||
|
||||
// Set up UDP socket: https://man7.org/linux/man-pages/man7/ip.7.html
|
||||
//clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
serverSocket = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol);
|
||||
if(serverSocket == INVALID_SOCKET) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not open"
|
||||
" UDP socket!" << std::endl;
|
||||
sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not open UDP socket!" <<
|
||||
std::endl;
|
||||
#endif
|
||||
handleSocketError();
|
||||
return;
|
||||
freeaddrinfo(addrResult);
|
||||
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SOCKET_CALL);
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
serverAddress.sin_family = AF_INET;
|
||||
|
||||
// Accept packets from any interface. (potentially insecure).
|
||||
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serverAddress.sin_port = htons(setServerPort);
|
||||
serverAddressLen = sizeof(serverAddress);
|
||||
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR,
|
||||
reinterpret_cast<const char*>(&serverSocketOptions),
|
||||
sizeof(serverSocketOptions));
|
||||
|
||||
clientAddress.sin_family = AF_INET;
|
||||
clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
clientAddress.sin_port = htons(setClientPort);
|
||||
clientAddressLen = sizeof(clientAddress);
|
||||
|
||||
int result = bind(serverSocket,
|
||||
reinterpret_cast<struct sockaddr*>(&serverAddress),
|
||||
serverAddressLen);
|
||||
if(result != 0) {
|
||||
retval = bind(serverSocket, addrResult->ai_addr, static_cast<int>(addrResult->ai_addrlen));
|
||||
if(retval != 0) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not bind "
|
||||
"local port " << setServerPort << " to server socket!"
|
||||
<< std::endl;
|
||||
"local port (" << udpServerPort << ") to server socket!" << std::endl;
|
||||
#endif
|
||||
handleBindError();
|
||||
freeaddrinfo(addrResult);
|
||||
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::BIND_CALL);
|
||||
}
|
||||
freeaddrinfo(addrResult);
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
TmTcWinUdpBridge::~TmTcWinUdpBridge() {
|
||||
if(mutex != nullptr) {
|
||||
MutexFactory::instance()->deleteMutex(mutex);
|
||||
}
|
||||
closesocket(serverSocket);
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) {
|
||||
int flags = 0;
|
||||
|
||||
//clientAddress.sin_addr.s_addr = htons(INADDR_ANY);
|
||||
//clientAddressLen = sizeof(serverAddress);
|
||||
/* The target address can be set by different threads so this lock ensures thread-safety */
|
||||
MutexGuard lock(mutex, timeoutType, mutexTimeoutMs);
|
||||
|
||||
// char ipAddress [15];
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
|
||||
// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
|
||||
char ipAddress [15];
|
||||
sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
|
||||
&clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
#endif
|
||||
|
||||
ssize_t bytesSent = sendto(serverSocket,
|
||||
reinterpret_cast<const char*>(data), dataLen, flags,
|
||||
reinterpret_cast<sockaddr*>(&clientAddress), clientAddressLen);
|
||||
int bytesSent = sendto(
|
||||
serverSocket,
|
||||
reinterpret_cast<const char*>(data),
|
||||
dataLen,
|
||||
flags,
|
||||
reinterpret_cast<sockaddr*>(&clientAddress),
|
||||
clientAddressLen
|
||||
);
|
||||
if(bytesSent == SOCKET_ERROR) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "TmTcWinUdpBridge::sendTm: Send operation failed."
|
||||
<< std::endl;
|
||||
sif::warning << "TmTcWinUdpBridge::sendTm: Send operation failed." << std::endl;
|
||||
#endif
|
||||
handleSendError();
|
||||
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SENDTO_CALL);
|
||||
}
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
// sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were"
|
||||
// " sent." << std::endl;
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
|
||||
sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were"
|
||||
" sent." << std::endl;
|
||||
#endif
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in newAddress) {
|
||||
MutexHelper lock(mutex, MutexIF::TimeoutType::WAITING, 10);
|
||||
void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in& newAddress) {
|
||||
/* The target address can be set by different threads so this lock ensures thread-safety */
|
||||
MutexGuard lock(mutex, timeoutType, mutexTimeoutMs);
|
||||
|
||||
// char ipAddress [15];
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
|
||||
// &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
// sif::debug << "IP Address Old: " << inet_ntop(AF_INET,
|
||||
// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
|
||||
char ipAddress [15];
|
||||
sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
|
||||
&newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
sif::debug << "IP Address Old: " << inet_ntop(AF_INET,
|
||||
&clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
||||
#endif
|
||||
registerCommConnect();
|
||||
|
||||
// Set new IP address if it has changed.
|
||||
if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) {
|
||||
clientAddress.sin_addr.s_addr = newAddress.sin_addr.s_addr;
|
||||
clientAddressLen = sizeof(clientAddress);
|
||||
}
|
||||
/* Set new IP address to reply to */
|
||||
clientAddress = newAddress;
|
||||
clientAddressLen = sizeof(clientAddress);
|
||||
}
|
||||
|
||||
void TmTcWinUdpBridge::handleSocketError() {
|
||||
int errCode = WSAGetLastError();
|
||||
switch(errCode) {
|
||||
case(WSANOTINITIALISED): {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TmTcWinUdpBridge::handleSocketError: WSANOTINITIALISED: "
|
||||
<< "WSAStartup(...) call necessary" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/*
|
||||
https://docs.microsoft.com/en-us/windows/win32/winsock/
|
||||
windows-sockets-error-codes-2
|
||||
*/
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TmTcWinUdpBridge::handleSocketError: Error code: "
|
||||
<< errCode << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
void TmTcWinUdpBridge::setMutexProperties(MutexIF::TimeoutType timeoutType,
|
||||
dur_millis_t timeoutMs) {
|
||||
this->timeoutType = timeoutType;
|
||||
this->mutexTimeoutMs = timeoutMs;
|
||||
}
|
||||
|
||||
void TmTcWinUdpBridge::handleBindError() {
|
||||
int errCode = WSAGetLastError();
|
||||
switch(errCode) {
|
||||
case(WSANOTINITIALISED): {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TmTcWinUdpBridge::handleBindError: WSANOTINITIALISED: "
|
||||
<< "WSAStartup(...) call " << "necessary" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(WSAEADDRINUSE): {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "TmTcWinUdpBridge::handleBindError: WSAEADDRINUSE: "
|
||||
<< "Port is already in use!" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/*
|
||||
https://docs.microsoft.com/en-us/windows/win32/winsock/
|
||||
windows-sockets-error-codes-2
|
||||
*/
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TmTcWinUdpBridge::handleBindError: Error code: "
|
||||
<< errCode << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmTcWinUdpBridge::handleSendError() {
|
||||
int errCode = WSAGetLastError();
|
||||
switch(errCode) {
|
||||
case(WSANOTINITIALISED): {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TmTcWinUdpBridge::handleSendError: WSANOTINITIALISED: "
|
||||
<< "WSAStartup(...) call necessary" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(WSAEADDRNOTAVAIL): {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TmTcWinUdpBridge::handleSendError: WSAEADDRNOTAVAIL: "
|
||||
<< "Check target address. " << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/*
|
||||
https://docs.microsoft.com/en-us/windows/win32/winsock/
|
||||
windows-sockets-error-codes-2
|
||||
*/
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "TmTcWinUdpBridge::handleSendError: Error code: "
|
||||
<< errCode << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,47 +3,43 @@
|
||||
|
||||
#include "../../tmtcservices/TmTcBridge.h"
|
||||
|
||||
#include <string>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
class TmTcWinUdpBridge: public TmTcBridge {
|
||||
friend class TcWinUdpPollingTask;
|
||||
public:
|
||||
// The ports chosen here should not be used by any other process.
|
||||
static constexpr uint16_t DEFAULT_UDP_SERVER_PORT = 7301;
|
||||
static constexpr uint16_t DEFAULT_UDP_CLIENT_PORT = 7302;
|
||||
/* The ports chosen here should not be used by any other process. */
|
||||
static const std::string DEFAULT_UDP_SERVER_PORT;
|
||||
|
||||
TmTcWinUdpBridge(object_id_t objectId, object_id_t tcDestination,
|
||||
object_id_t tmStoreId, object_id_t tcStoreId,
|
||||
uint16_t serverPort = 0xFFFF,uint16_t clientPort = 0xFFFF);
|
||||
object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort = "");
|
||||
virtual~ TmTcWinUdpBridge();
|
||||
|
||||
void checkAndSetClientAddress(sockaddr_in clientAddress);
|
||||
/**
|
||||
* Set properties of internal mutex.
|
||||
*/
|
||||
void setMutexProperties(MutexIF::TimeoutType timeoutType, dur_millis_t timeoutMs);
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
|
||||
void checkAndSetClientAddress(sockaddr_in& clientAddress);
|
||||
|
||||
protected:
|
||||
virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override;
|
||||
|
||||
private:
|
||||
SOCKET serverSocket = 0;
|
||||
|
||||
const int serverSocketOptions = 0;
|
||||
std::string udpServerPort;
|
||||
|
||||
struct sockaddr_in clientAddress;
|
||||
int clientAddressLen = 0;
|
||||
|
||||
struct sockaddr_in serverAddress;
|
||||
int serverAddressLen = 0;
|
||||
|
||||
//! Access to the client address is mutex protected as it is set
|
||||
//! by another task.
|
||||
//! Access to the client address is mutex protected as it is set by another task.
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
||||
dur_millis_t mutexTimeoutMs = 20;
|
||||
MutexIF* mutex;
|
||||
|
||||
void handleSocketError();
|
||||
void handleBindError();
|
||||
void handleSendError();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* FSFW_OSAL_HOST_TMTCWINUDPBRIDGE_H_ */
|
||||
|
||||
|
63
osal/windows/tcpipHelpers.cpp
Normal file
63
osal/windows/tcpipHelpers.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "tcpipHelpers.h"
|
||||
#include <FSFWConfig.h>
|
||||
|
||||
#include "../../tasks/TaskFactory.h"
|
||||
#include "../../serviceinterface/ServiceInterface.h"
|
||||
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <string>
|
||||
|
||||
void tcpip::handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
int errCode = WSAGetLastError();
|
||||
std::string protocolString;
|
||||
std::string errorSrcString;
|
||||
determineErrorStrings(protocol, errorSrc, protocolString, errorSrcString);
|
||||
|
||||
std::string infoString;
|
||||
switch(errCode) {
|
||||
case(WSANOTINITIALISED): {
|
||||
infoString = "WSANOTINITIALISED";
|
||||
break;
|
||||
}
|
||||
case(WSAEADDRINUSE): {
|
||||
infoString = "WSAEADDRINUSE";
|
||||
break;
|
||||
}
|
||||
case(WSAEFAULT): {
|
||||
infoString = "WSAEFAULT";
|
||||
break;
|
||||
}
|
||||
case(WSAEADDRNOTAVAIL): {
|
||||
infoString = "WSAEADDRNOTAVAIL";
|
||||
break;
|
||||
}
|
||||
case(WSAEINVAL): {
|
||||
infoString = "WSAEINVAL";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/*
|
||||
https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2
|
||||
*/
|
||||
infoString = "Error code: " + std::to_string(errCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "tcpip::handleError: " << protocolString << " | " << errorSrcString <<
|
||||
" | " << infoString << std::endl;
|
||||
#else
|
||||
sif::printWarning("tcpip::handleError: %s | %s | %s\n", protocolString,
|
||||
errorSrcString, infoString);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
|
||||
if(sleepDuration > 0) {
|
||||
TaskFactory::instance()->delayTask(sleepDuration);
|
||||
}
|
||||
}
|
||||
|
15
osal/windows/tcpipHelpers.h
Normal file
15
osal/windows/tcpipHelpers.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef FSFW_OSAL_WINDOWS_TCPIPHELPERS_H_
|
||||
#define FSFW_OSAL_WINDOWS_TCPIPHELPERS_H_
|
||||
|
||||
#include "../../timemanager/clockDefinitions.h"
|
||||
#include "../common/tcpipCommon.h"
|
||||
|
||||
namespace tcpip {
|
||||
|
||||
void handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration = 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* FSFW_OSAL_WINDOWS_TCPIPHELPERS_H_ */
|
@@ -1,6 +1,7 @@
|
||||
#include "PowerComponent.h"
|
||||
#include "../serialize/SerializeAdapter.h"
|
||||
|
||||
|
||||
PowerComponent::PowerComponent(): switchId1(0xFF), switchId2(0xFF),
|
||||
doIHaveTwoSwitches(false) {
|
||||
}
|
||||
@@ -8,23 +9,23 @@ PowerComponent::PowerComponent(): switchId1(0xFF), switchId2(0xFF),
|
||||
PowerComponent::PowerComponent(object_id_t setId, uint8_t moduleId, float min,
|
||||
float max, uint8_t switchId1, bool twoSwitches, uint8_t switchId2) :
|
||||
deviceObjectId(setId), switchId1(switchId1), switchId2(switchId2),
|
||||
doIHaveTwoSwitches(twoSwitches), min(min), max(max),
|
||||
doIHaveTwoSwitches(twoSwitches), minPower(min), maxPower(max),
|
||||
moduleId(moduleId) {
|
||||
}
|
||||
|
||||
ReturnValue_t PowerComponent::serialize(uint8_t** buffer, size_t* size,
|
||||
size_t maxSize, Endianness streamEndianness) const {
|
||||
ReturnValue_t result = SerializeAdapter::serialize(&min, buffer,
|
||||
ReturnValue_t result = SerializeAdapter::serialize(&minPower, buffer,
|
||||
size, maxSize, streamEndianness);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
return SerializeAdapter::serialize(&max, buffer, size, maxSize,
|
||||
return SerializeAdapter::serialize(&maxPower, buffer, size, maxSize,
|
||||
streamEndianness);
|
||||
}
|
||||
|
||||
size_t PowerComponent::getSerializedSize() const {
|
||||
return sizeof(min) + sizeof(max);
|
||||
return sizeof(minPower) + sizeof(maxPower);
|
||||
}
|
||||
|
||||
object_id_t PowerComponent::getDeviceObjectId() {
|
||||
@@ -44,21 +45,21 @@ bool PowerComponent::hasTwoSwitches() {
|
||||
}
|
||||
|
||||
float PowerComponent::getMin() {
|
||||
return min;
|
||||
return minPower;
|
||||
}
|
||||
|
||||
float PowerComponent::getMax() {
|
||||
return max;
|
||||
return maxPower;
|
||||
}
|
||||
|
||||
ReturnValue_t PowerComponent::deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) {
|
||||
ReturnValue_t result = SerializeAdapter::deSerialize(&min, buffer,
|
||||
ReturnValue_t result = SerializeAdapter::deSerialize(&minPower, buffer,
|
||||
size, streamEndianness);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
return SerializeAdapter::deSerialize(&max, buffer, size, streamEndianness);
|
||||
return SerializeAdapter::deSerialize(&maxPower, buffer, size, streamEndianness);
|
||||
}
|
||||
|
||||
ReturnValue_t PowerComponent::getParameter(uint8_t domainId, uint8_t uniqueId,
|
||||
@@ -69,10 +70,10 @@ ReturnValue_t PowerComponent::getParameter(uint8_t domainId, uint8_t uniqueId,
|
||||
}
|
||||
switch (uniqueId) {
|
||||
case 0:
|
||||
parameterWrapper->set<>(min);
|
||||
parameterWrapper->set<>(minPower);
|
||||
break;
|
||||
case 1:
|
||||
parameterWrapper->set<>(max);
|
||||
parameterWrapper->set<>(maxPower);
|
||||
break;
|
||||
default:
|
||||
return INVALID_IDENTIFIER_ID;
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
class PowerComponent: public PowerComponentIF {
|
||||
public:
|
||||
PowerComponent(object_id_t setId, uint8_t moduleId, float min, float max,
|
||||
PowerComponent(object_id_t setId, uint8_t moduleId, float minPower, float maxPower,
|
||||
uint8_t switchId1, bool twoSwitches = false,
|
||||
uint8_t switchId2 = 0xFF);
|
||||
|
||||
@@ -41,8 +41,8 @@ private:
|
||||
|
||||
const bool doIHaveTwoSwitches;
|
||||
|
||||
float min = 0.0;
|
||||
float max = 0.0;
|
||||
float minPower = 0.0;
|
||||
float maxPower = 0.0;
|
||||
|
||||
uint8_t moduleId = 0;
|
||||
|
||||
|
@@ -159,7 +159,7 @@ ReturnValue_t Service3Housekeeping::prepareCollectionIntervalModificationCommand
|
||||
CommandMessage *command, object_id_t objectId, bool isDiagnostics,
|
||||
const uint8_t *tcData, size_t tcDataLen) {
|
||||
if(tcDataLen < sizeof(sid_t) + sizeof(float)) {
|
||||
// SID plus the size of the new collection intervL.
|
||||
/* SID plus the size of the new collection interval. */
|
||||
return CommandingServiceBase::INVALID_TC;
|
||||
}
|
||||
|
||||
|
@@ -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 !
|
||||
|
||||
};
|
||||
|
@@ -15,7 +15,7 @@ PoolManager::~PoolManager(void) {
|
||||
|
||||
ReturnValue_t PoolManager::reserveSpace(const size_t size,
|
||||
store_address_t* address, bool ignoreFault) {
|
||||
MutexHelper mutexHelper(mutex, MutexIF::TimeoutType::WAITING,
|
||||
MutexGuard mutexHelper(mutex, MutexIF::TimeoutType::WAITING,
|
||||
mutexTimeoutMs);
|
||||
ReturnValue_t status = LocalPool::reserveSpace(size,
|
||||
address,ignoreFault);
|
||||
@@ -32,7 +32,7 @@ ReturnValue_t PoolManager::deleteData(
|
||||
". id is "<< storeId.packetIndex << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
MutexHelper mutexHelper(mutex, MutexIF::TimeoutType::WAITING,
|
||||
MutexGuard mutexHelper(mutex, MutexIF::TimeoutType::WAITING,
|
||||
mutexTimeoutMs);
|
||||
return LocalPool::deleteData(storeId);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ ReturnValue_t PoolManager::deleteData(
|
||||
|
||||
ReturnValue_t PoolManager::deleteData(uint8_t* buffer,
|
||||
size_t size, store_address_t* storeId) {
|
||||
MutexHelper mutexHelper(mutex, MutexIF::TimeoutType::WAITING, 20);
|
||||
MutexGuard mutexHelper(mutex, MutexIF::TimeoutType::WAITING, 20);
|
||||
ReturnValue_t status = LocalPool::deleteData(buffer,
|
||||
size, storeId);
|
||||
return status;
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "LocalPool.h"
|
||||
#include "StorageAccessor.h"
|
||||
#include "../ipc/MutexHelper.h"
|
||||
#include "../ipc/MutexGuard.h"
|
||||
|
||||
|
||||
/**
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -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_ */
|
||||
|
@@ -173,6 +173,9 @@ ReturnValue_t TmTcBridge::handleTmQueue() {
|
||||
|
||||
ReturnValue_t TmTcBridge::storeDownlinkData(TmTcMessage *message) {
|
||||
store_address_t storeId = 0;
|
||||
if(tmFifo == nullptr) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
if(tmFifo->full()) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
|
@@ -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(false, testMqMock.getId(), testActionId, status);
|
||||
CHECK(testMqMock.wasMessageSent());
|
||||
CommandMessage testMessage;
|
||||
REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK));
|
||||
|
@@ -3,4 +3,5 @@ target_sources(${TARGET_NAME} PRIVATE
|
||||
LocalPoolVectorTest.cpp
|
||||
DataSetTest.cpp
|
||||
LocalPoolManagerTest.cpp
|
||||
LocalPoolOwnerBase.cpp
|
||||
)
|
||||
|
@@ -1,22 +1,285 @@
|
||||
#include "LocalPoolOwnerBase.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
|
||||
#include <fsfw/datapoollocal/SharedLocalDataSet.h>
|
||||
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||
#include <fsfw/datapool/PoolReadGuard.h>
|
||||
#include <fsfw/globalfunctions/bitutility.h>
|
||||
|
||||
#include <unittest/core/CatchDefinitions.h>
|
||||
|
||||
TEST_CASE("LocalDataSet" , "[LocDataSetTest]") {
|
||||
TEST_CASE("DataSetTest" , "[DataSetTest]") {
|
||||
LocalPoolOwnerBase* poolOwner = objectManager->
|
||||
get<LocalPoolOwnerBase>(objects::TEST_LOCAL_POOL_OWNER_BASE);
|
||||
REQUIRE(poolOwner != nullptr);
|
||||
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.getReportingEnabled() == false);
|
||||
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 */
|
||||
PoolReadGuard 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;
|
||||
|
||||
localSet.setAllVariablesReadOnly();
|
||||
CHECK(localSet.localPoolUint16Vec.getReadWriteMode() == pool_rwm_t::VAR_READ);
|
||||
CHECK(localSet.localPoolVarUint8.getReadWriteMode() == pool_rwm_t::VAR_READ);
|
||||
CHECK(localSet.localPoolVarFloat.getReadWriteMode() == pool_rwm_t::VAR_READ);
|
||||
|
||||
{
|
||||
/* Now we read again and check whether our zeroed values were overwritten with
|
||||
the values in the pool */
|
||||
PoolReadGuard 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));
|
||||
|
||||
}
|
||||
|
||||
SECTION("MorePoolVariables") {
|
||||
LocalDataSet set(poolOwner, 2, 10);
|
||||
|
||||
/* Register same variables again to get more than 8 registered variables */
|
||||
for(uint8_t idx = 0; idx < 8; idx ++) {
|
||||
REQUIRE(set.registerVariable(&localSet.localPoolVarUint8) == retval::CATCH_OK);
|
||||
}
|
||||
REQUIRE(set.registerVariable(&localSet.localPoolVarUint8) == retval::CATCH_OK);
|
||||
REQUIRE(set.registerVariable(&localSet.localPoolUint16Vec) == retval::CATCH_OK);
|
||||
|
||||
set.setValidityBufferGeneration(true);
|
||||
{
|
||||
PoolReadGuard readHelper(&localSet);
|
||||
localSet.localPoolVarUint8.value = 42;
|
||||
localSet.localPoolVarUint8.setValid(true);
|
||||
localSet.localPoolUint16Vec.setValid(false);
|
||||
}
|
||||
|
||||
size_t maxSize = set.getSerializedSize();
|
||||
CHECK(maxSize == 9 + sizeof(uint16_t) * 3 + 2);
|
||||
size_t serSize = 0;
|
||||
/* Already reserve additional space for validity buffer, will be needed later */
|
||||
uint8_t buffer[maxSize + 1];
|
||||
uint8_t* buffPtr = buffer;
|
||||
CHECK(set.serialize(&buffPtr, &serSize, maxSize,
|
||||
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
|
||||
std::array<uint8_t, 2> validityBuffer;
|
||||
std::memcpy(validityBuffer.data(), buffer + 9 + sizeof(uint16_t) * 3, 2);
|
||||
/* The first 9 variables should be valid */
|
||||
CHECK(validityBuffer[0] == 0xff);
|
||||
CHECK(bitutil::bitGet(validityBuffer.data() + 1, 0) == true);
|
||||
CHECK(bitutil::bitGet(validityBuffer.data() + 1, 1) == false);
|
||||
|
||||
/* Now we invert the validity */
|
||||
validityBuffer[0] = 0;
|
||||
validityBuffer[1] = 0b0100'0000;
|
||||
std::memcpy(buffer + 9 + sizeof(uint16_t) * 3, validityBuffer.data(), 2);
|
||||
const uint8_t* constBuffPtr = buffer;
|
||||
size_t sizeToDeSerialize = serSize;
|
||||
CHECK(set.deSerialize(&constBuffPtr, &sizeToDeSerialize, SerializeIF::Endianness::MACHINE)
|
||||
== retval::CATCH_OK);
|
||||
CHECK(localSet.localPoolVarUint8.isValid() == false);
|
||||
CHECK(localSet.localPoolUint16Vec.isValid() == true);
|
||||
}
|
||||
|
||||
SECTION("SharedDataSet") {
|
||||
object_id_t sharedSetId = objects::SHARED_SET_ID;
|
||||
SharedLocalDataSet sharedSet(sharedSetId, poolOwner, lpool::testSetId, 5);
|
||||
localSet.localPoolVarUint8.setReadWriteMode(pool_rwm_t::VAR_WRITE);
|
||||
localSet.localPoolUint16Vec.setReadWriteMode(pool_rwm_t::VAR_WRITE);
|
||||
CHECK(sharedSet.registerVariable(&localSet.localPoolVarUint8) == retval::CATCH_OK);
|
||||
CHECK(sharedSet.registerVariable(&localSet.localPoolUint16Vec) == retval::CATCH_OK);
|
||||
CHECK(sharedSet.initialize() == retval::CATCH_OK);
|
||||
CHECK(sharedSet.lockDataset() == retval::CATCH_OK);
|
||||
CHECK(sharedSet.unlockDataset() == retval::CATCH_OK);
|
||||
|
||||
{
|
||||
//PoolReadGuard rg(&sharedSet);
|
||||
//CHECK(rg.getReadResult() == retval::CATCH_OK);
|
||||
localSet.localPoolVarUint8.value = 5;
|
||||
localSet.localPoolUint16Vec.value[0] = 1;
|
||||
localSet.localPoolUint16Vec.value[1] = 2;
|
||||
localSet.localPoolUint16Vec.value[2] = 3;
|
||||
CHECK(sharedSet.commit() == retval::CATCH_OK);
|
||||
}
|
||||
|
||||
sharedSet.setReadCommitProtectionBehaviour(true);
|
||||
}
|
||||
|
||||
/* we need to reset the subscription list because the pool owner
|
||||
is a global object. */
|
||||
CHECK(poolOwner->reset() == retval::CATCH_OK);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include <fsfw/datapool/PoolReadHelper.h>
|
||||
#include <fsfw/datapool/PoolReadGuard.h>
|
||||
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
|
||||
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||
#include <fsfw/housekeeping/HousekeepingSnapshot.h>
|
||||
@@ -20,14 +20,21 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
|
||||
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
|
||||
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation()
|
||||
== retval::CATCH_OK);
|
||||
//REQUIRE(poolOwner->dataset.assignPointers() == retval::CATCH_OK);
|
||||
|
||||
MessageQueueMockBase* mqMock = poolOwner->getMockQueueHandle();
|
||||
REQUIRE(mqMock != nullptr);
|
||||
CommandMessage messageSent;
|
||||
uint8_t messagesSent = 0;
|
||||
|
||||
|
||||
SECTION("BasicTest") {
|
||||
{
|
||||
/* For code coverage, should not crash */
|
||||
LocalDataPoolManager manager(nullptr, nullptr);
|
||||
}
|
||||
auto owner = poolOwner->poolManager.getOwner();
|
||||
REQUIRE(owner != nullptr);
|
||||
CHECK(owner->getObjectId() == objects::TEST_LOCAL_POOL_OWNER_BASE);
|
||||
|
||||
/* Subscribe for message generation on update. */
|
||||
REQUIRE(poolOwner->subscribeWrapperSetUpdate() == retval::CATCH_OK);
|
||||
/* Subscribe for an update message. */
|
||||
@@ -72,10 +79,10 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
|
||||
|
||||
}
|
||||
|
||||
SECTION("SnapshotUpdateTests") {
|
||||
SECTION("SetSnapshotUpdateTest") {
|
||||
/* Set the variables in the set to certain values. These are checked later. */
|
||||
{
|
||||
PoolReadHelper readHelper(&poolOwner->dataset);
|
||||
PoolReadGuard readHelper(&poolOwner->dataset);
|
||||
REQUIRE(readHelper.getReadResult() == retval::CATCH_OK);
|
||||
poolOwner->dataset.localPoolVarUint8.value = 5;
|
||||
poolOwner->dataset.localPoolVarFloat.value = -12.242;
|
||||
@@ -137,7 +144,69 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
|
||||
CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1));
|
||||
}
|
||||
|
||||
SECTION("AdvancedTests") {
|
||||
SECTION("VariableSnapshotTest") {
|
||||
/* Acquire subscription interface */
|
||||
ProvidesDataPoolSubscriptionIF* subscriptionIF = poolOwner->getSubscriptionInterface();
|
||||
REQUIRE(subscriptionIF != nullptr);
|
||||
|
||||
/* Subscribe for variable snapshot */
|
||||
REQUIRE(poolOwner->subscribeWrapperVariableSnapshot(lpool::uint8VarId) == retval::CATCH_OK);
|
||||
auto poolVar = dynamic_cast<lp_var_t<uint8_t>*>(
|
||||
poolOwner->getPoolObjectHandle(lpool::uint8VarId));
|
||||
REQUIRE(poolVar != nullptr);
|
||||
|
||||
{
|
||||
PoolReadGuard rg(poolVar);
|
||||
CHECK(rg.getReadResult() == retval::CATCH_OK);
|
||||
poolVar->value = 25;
|
||||
}
|
||||
|
||||
poolVar->setChanged(true);
|
||||
|
||||
/* Store current time, we are going to check the (approximate) time equality later */
|
||||
CCSDSTime::CDS_short timeCdsNow;
|
||||
timeval now;
|
||||
Clock::getClock_timeval(&now);
|
||||
CCSDSTime::convertToCcsds(&timeCdsNow, &now);
|
||||
|
||||
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
|
||||
|
||||
/* Check update snapshot was sent. */
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
|
||||
/* Should have been reset. */
|
||||
CHECK(poolVar->hasChanged() == false);
|
||||
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
|
||||
CHECK(messageSent.getCommand() == static_cast<int>(
|
||||
HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE));
|
||||
/* Now we deserialize the snapshot into a new dataset instance */
|
||||
CCSDSTime::CDS_short cdsShort;
|
||||
lp_var_t<uint8_t> varCopy = lp_var_t<uint8_t>(lpool::uint8VarGpid);
|
||||
HousekeepingSnapshot snapshot(&cdsShort, &varCopy);
|
||||
store_address_t storeId;
|
||||
HousekeepingMessage::getUpdateSnapshotVariableCommand(&messageSent, &storeId);
|
||||
ConstAccessorPair accessorPair = tglob::getIpcStoreHandle()->getData(storeId);
|
||||
REQUIRE(accessorPair.first == retval::CATCH_OK);
|
||||
const uint8_t* readOnlyPtr = accessorPair.second.data();
|
||||
size_t sizeToDeserialize = accessorPair.second.size();
|
||||
CHECK(varCopy.value == 0);
|
||||
/* Fill the dataset and timestamp */
|
||||
REQUIRE(snapshot.deSerialize(&readOnlyPtr, &sizeToDeserialize,
|
||||
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
|
||||
CHECK(varCopy.value == 25);
|
||||
|
||||
/* Now we check that both times are equal */
|
||||
CHECK(cdsShort.pField == timeCdsNow.pField);
|
||||
CHECK(cdsShort.dayLSB == Catch::Approx(timeCdsNow.dayLSB).margin(1));
|
||||
CHECK(cdsShort.dayMSB == Catch::Approx(timeCdsNow.dayMSB).margin(1));
|
||||
CHECK(cdsShort.msDay_h == Catch::Approx(timeCdsNow.msDay_h).margin(1));
|
||||
CHECK(cdsShort.msDay_hh == Catch::Approx(timeCdsNow.msDay_hh).margin(1));
|
||||
CHECK(cdsShort.msDay_l == Catch::Approx(timeCdsNow.msDay_l).margin(1));
|
||||
CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1));
|
||||
}
|
||||
|
||||
SECTION("VariableNotificationTest") {
|
||||
/* Acquire subscription interface */
|
||||
ProvidesDataPoolSubscriptionIF* subscriptionIF = poolOwner->getSubscriptionInterface();
|
||||
REQUIRE(subscriptionIF != nullptr);
|
||||
@@ -149,6 +218,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
|
||||
poolOwner->getPoolObjectHandle(lpool::uint8VarId));
|
||||
REQUIRE(poolVar != nullptr);
|
||||
poolVar->setChanged(true);
|
||||
REQUIRE(poolVar->hasChanged() == true);
|
||||
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
|
||||
|
||||
/* Check update notification was sent. */
|
||||
@@ -160,7 +230,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);
|
||||
|
||||
@@ -190,9 +260,167 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
|
||||
REQUIRE(mqMock->receiveMessage(&messageSent) == static_cast<int>(MessageQueueIF::EMPTY));
|
||||
}
|
||||
|
||||
SECTION("PeriodicHKAndMessaging") {
|
||||
/* Now we subcribe for a HK periodic generation. Even when it's difficult to simulate
|
||||
the temporal behaviour correctly the HK manager should generate a HK packet
|
||||
immediately and the periodic helper depends on HK op function calls anyway instead of
|
||||
using the clock, so we could also just call performHkOperation multiple times */
|
||||
REQUIRE(poolOwner->subscribePeriodicHk(true) == retval::CATCH_OK);
|
||||
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
|
||||
/* Now HK packet should be sent as message immediately. */
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
LocalPoolDataSetBase* setHandle = poolOwner->getDataSetHandle(lpool::testSid);
|
||||
REQUIRE(setHandle != nullptr);
|
||||
CHECK(poolOwner->poolManager.generateHousekeepingPacket(lpool::testSid,
|
||||
setHandle, false) == retval::CATCH_OK);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
CHECK(setHandle->getReportingEnabled() == true);
|
||||
CommandMessage hkCmd;
|
||||
HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, false, false);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
CHECK(setHandle->getReportingEnabled() == false);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, true, false);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
CHECK(setHandle->getReportingEnabled() == true);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, false, false);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
CHECK(setHandle->getReportingEnabled() == false);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd,
|
||||
lpool::testSid, 0.4, false);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
/* For non-diagnostics and a specified minimum frequency of 0.2 seconds, the
|
||||
resulting collection interval should be 1.0 second */
|
||||
CHECK(poolOwner->dataset.getCollectionInterval() == 1.0);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false);
|
||||
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
/* Now HK packet should be sent as message. */
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setOneShotReportCommand(&hkCmd, lpool::testSid, false);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setUpdateNotificationSetCommand(&hkCmd, lpool::testSid);
|
||||
sid_t sidToCheck;
|
||||
store_address_t storeId;
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
CHECK(poolOwner->changedDataSetCallbackWasCalled(sidToCheck, storeId) == true);
|
||||
CHECK(sidToCheck == lpool::testSid);
|
||||
|
||||
/* Now we test the handling is the dataset is set to diagnostic */
|
||||
poolOwner->dataset.setDiagnostic(true);
|
||||
|
||||
HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) ==
|
||||
static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE));
|
||||
/* We still expect a failure message being sent */
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd,
|
||||
lpool::testSid, 0.4, false);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) ==
|
||||
static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE));
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) ==
|
||||
static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE));
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, true);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd, lpool::testSid, 0.4,
|
||||
true);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, true, true);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, false, true);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setOneShotReportCommand(&hkCmd, lpool::testSid, false);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) ==
|
||||
static_cast<int>(LocalDataPoolManager::WRONG_HK_PACKET_TYPE));
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setOneShotReportCommand(&hkCmd, lpool::testSid, true);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
|
||||
CHECK(messagesSent == 1);
|
||||
CHECK(mqMock->popMessage() == retval::CATCH_OK);
|
||||
|
||||
HousekeepingMessage::setUpdateNotificationVariableCommand(&hkCmd, lpool::uint8VarGpid);
|
||||
gp_id_t gpidToCheck;
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
CHECK(poolOwner->changedVariableCallbackWasCalled(gpidToCheck, storeId) == true);
|
||||
CHECK(gpidToCheck == lpool::uint8VarGpid);
|
||||
|
||||
HousekeepingMessage::setUpdateSnapshotSetCommand(&hkCmd, lpool::testSid,
|
||||
storeId::INVALID_STORE_ADDRESS);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
CHECK(poolOwner->changedDataSetCallbackWasCalled(sidToCheck, storeId) == true);
|
||||
CHECK(sidToCheck == lpool::testSid);
|
||||
|
||||
HousekeepingMessage::setUpdateSnapshotVariableCommand(&hkCmd, lpool::uint8VarGpid,
|
||||
storeId::INVALID_STORE_ADDRESS);
|
||||
CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK);
|
||||
CHECK(poolOwner->changedVariableCallbackWasCalled(gpidToCheck, storeId) == true);
|
||||
CHECK(gpidToCheck == lpool::uint8VarGpid);
|
||||
|
||||
poolOwner->poolManager.printPoolEntry(lpool::uint8VarId);
|
||||
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
141
unittest/tests/datapoollocal/LocalPoolOwnerBase.cpp
Normal file
141
unittest/tests/datapoollocal/LocalPoolOwnerBase.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "LocalPoolOwnerBase.h"
|
||||
|
||||
LocalPoolOwnerBase::LocalPoolOwnerBase(object_id_t objectId):
|
||||
SystemObject(objectId), poolManager(this, messageQueue),
|
||||
dataset(this, lpool::testSetId) {
|
||||
messageQueue = new MessageQueueMockBase();
|
||||
}
|
||||
|
||||
LocalPoolOwnerBase::~LocalPoolOwnerBase() {
|
||||
QueueFactory::instance()->deleteMessageQueue(messageQueue);
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolOwnerBase::initializeHkManager() {
|
||||
if(not initialized) {
|
||||
initialized = true;
|
||||
return poolManager.initialize(messageQueue);
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolOwnerBase::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) {
|
||||
|
||||
// Default initialization empty for now.
|
||||
localDataPoolMap.emplace(lpool::uint8VarId,
|
||||
new PoolEntry<uint8_t>({0}));
|
||||
localDataPoolMap.emplace(lpool::floatVarId,
|
||||
new PoolEntry<float>({0}));
|
||||
localDataPoolMap.emplace(lpool::uint32VarId,
|
||||
new PoolEntry<uint32_t>({0}));
|
||||
|
||||
localDataPoolMap.emplace(lpool::uint16Vec3Id,
|
||||
new PoolEntry<uint16_t>({0, 0, 0}));
|
||||
localDataPoolMap.emplace(lpool::int64Vec2Id,
|
||||
new PoolEntry<int64_t>({0, 0}));
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
LocalPoolObjectBase* LocalPoolOwnerBase::getPoolObjectHandle(lp_id_t localPoolId) {
|
||||
if(localPoolId == lpool::uint8VarId) {
|
||||
return &testUint8;
|
||||
}
|
||||
else if(localPoolId == lpool::uint16Vec3Id) {
|
||||
return &testUint16Vec;
|
||||
}
|
||||
else if(localPoolId == lpool::floatVarId) {
|
||||
return &testFloat;
|
||||
}
|
||||
else if(localPoolId == lpool::int64Vec2Id) {
|
||||
return &testInt64Vec;
|
||||
}
|
||||
else if(localPoolId == lpool::uint32VarId) {
|
||||
return &testUint32;
|
||||
}
|
||||
else {
|
||||
return &testUint8;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolOwnerBase::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;
|
||||
}
|
||||
|
||||
bool LocalPoolOwnerBase::changedDataSetCallbackWasCalled(sid_t &sid, store_address_t &storeId) {
|
||||
bool condition = false;
|
||||
if(not this->changedDatasetSid.notSet()) {
|
||||
condition = true;
|
||||
}
|
||||
sid = changedDatasetSid;
|
||||
storeId = storeIdForChangedSet;
|
||||
this->changedDatasetSid.raw = sid_t::INVALID_SID;
|
||||
this->storeIdForChangedSet = storeId::INVALID_STORE_ADDRESS;
|
||||
return condition;
|
||||
}
|
||||
|
||||
void LocalPoolOwnerBase::handleChangedDataset(sid_t sid, store_address_t storeId,
|
||||
bool* clearMessage) {
|
||||
this->changedDatasetSid = sid;
|
||||
this->storeIdForChangedSet = storeId;
|
||||
}
|
||||
|
||||
bool LocalPoolOwnerBase::changedVariableCallbackWasCalled(gp_id_t &gpid, store_address_t &storeId) {
|
||||
bool condition = false;
|
||||
if(not this->changedPoolVariableGpid.notSet()) {
|
||||
condition = true;
|
||||
}
|
||||
gpid = changedPoolVariableGpid;
|
||||
storeId = storeIdForChangedVariable;
|
||||
this->changedPoolVariableGpid.raw = gp_id_t::INVALID_GPID;
|
||||
this->storeIdForChangedVariable = storeId::INVALID_STORE_ADDRESS;
|
||||
return condition;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolOwnerBase::initializeHkManagerAfterTaskCreation() {
|
||||
if(not initializedAfterTaskCreation) {
|
||||
initializedAfterTaskCreation = true;
|
||||
return poolManager.initializeAfterTaskCreation();
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
|
||||
}
|
||||
|
||||
void LocalPoolOwnerBase::handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId,
|
||||
bool* clearMessage) {
|
||||
this->changedPoolVariableGpid = globPoolId;
|
||||
this->storeIdForChangedVariable = storeId;
|
||||
}
|
||||
|
@@ -1,15 +1,17 @@
|
||||
#ifndef FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_
|
||||
#define FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_
|
||||
|
||||
#include <testcfg/objects/systemObjectList.h>
|
||||
|
||||
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
|
||||
#include <fsfw/datapoollocal/LocalDataSet.h>
|
||||
#include <fsfw/objectmanager/SystemObject.h>
|
||||
#include <fsfw/datapoollocal/LocalPoolVariable.h>
|
||||
#include <fsfw/datapoollocal/LocalPoolVector.h>
|
||||
#include <fsfw/ipc/QueueFactory.h>
|
||||
#include <testcfg/objects/systemObjectList.h>
|
||||
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||
#include <fsfw/unittest/tests/mocks/MessageQueueMockBase.h>
|
||||
#include <fsfw/datapool/PoolReadGuard.h>
|
||||
|
||||
namespace lpool {
|
||||
static constexpr lp_id_t uint8VarId = 0;
|
||||
@@ -31,29 +33,17 @@ static const gp_id_t uint64Vec2Id = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE,
|
||||
}
|
||||
|
||||
|
||||
class LocalPoolTestDataSet: public LocalDataSet {
|
||||
class LocalPoolStaticTestDataSet: public StaticLocalDataSet<3> {
|
||||
public:
|
||||
LocalPoolTestDataSet():
|
||||
LocalDataSet(lpool::testSid, lpool::dataSetMaxVariables) {
|
||||
LocalPoolStaticTestDataSet():
|
||||
StaticLocalDataSet(lpool::testSid) {
|
||||
|
||||
}
|
||||
|
||||
LocalPoolTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId):
|
||||
LocalDataSet(owner, setId, lpool::dataSetMaxVariables) {
|
||||
LocalPoolStaticTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId):
|
||||
StaticLocalDataSet(owner, setId) {
|
||||
}
|
||||
|
||||
// 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);
|
||||
@@ -61,39 +51,39 @@ public:
|
||||
private:
|
||||
};
|
||||
|
||||
class LocalPoolTestDataSet: public LocalDataSet {
|
||||
public:
|
||||
LocalPoolTestDataSet():
|
||||
LocalDataSet(lpool::testSid, lpool::dataSetMaxVariables) {}
|
||||
|
||||
LocalPoolTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId):
|
||||
LocalDataSet(owner, setId, lpool::dataSetMaxVariables) {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
void setDiagnostic(bool isDiagnostic) {
|
||||
LocalPoolDataSetBase::setDiagnostic(isDiagnostic);
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
class LocalPoolOwnerBase: public SystemObject, public HasLocalDataPoolIF {
|
||||
public:
|
||||
LocalPoolOwnerBase(
|
||||
object_id_t objectId = objects::TEST_LOCAL_POOL_OWNER_BASE):
|
||||
SystemObject(objectId), poolManager(this, messageQueue),
|
||||
dataset(this, lpool::testSetId) {
|
||||
messageQueue = new MessageQueueMockBase();
|
||||
}
|
||||
LocalPoolOwnerBase(object_id_t objectId = objects::TEST_LOCAL_POOL_OWNER_BASE);
|
||||
|
||||
~LocalPoolOwnerBase() {
|
||||
QueueFactory::instance()->deleteMessageQueue(messageQueue);
|
||||
}
|
||||
~LocalPoolOwnerBase();
|
||||
|
||||
object_id_t getObjectId() const override {
|
||||
return SystemObject::getObjectId();
|
||||
}
|
||||
|
||||
ReturnValue_t initializeHkManager() {
|
||||
if(not initialized) {
|
||||
initialized = true;
|
||||
return poolManager.initialize(messageQueue);
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
ReturnValue_t initializeHkManager();
|
||||
|
||||
ReturnValue_t initializeHkManagerAfterTaskCreation() {
|
||||
if(not initializedAfterTaskCreation) {
|
||||
initializedAfterTaskCreation = true;
|
||||
return poolManager.initializeAfterTaskCreation();
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
ReturnValue_t initializeHkManagerAfterTaskCreation();
|
||||
|
||||
/** Command queue for housekeeping messages. */
|
||||
MessageQueueId_t getCommandQueue() const override {
|
||||
@@ -101,30 +91,15 @@ public:
|
||||
}
|
||||
|
||||
// This is called by initializeAfterTaskCreation of the HK manager.
|
||||
virtual ReturnValue_t initializeLocalDataPool(
|
||||
localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) {
|
||||
// Default initialization empty for now.
|
||||
localDataPoolMap.emplace(lpool::uint8VarId,
|
||||
new PoolEntry<uint8_t>({0}));
|
||||
localDataPoolMap.emplace(lpool::floatVarId,
|
||||
new PoolEntry<float>({0}));
|
||||
localDataPoolMap.emplace(lpool::uint32VarId,
|
||||
new PoolEntry<uint32_t>({0}));
|
||||
|
||||
localDataPoolMap.emplace(lpool::uint16Vec3Id,
|
||||
new PoolEntry<uint16_t>({0, 0, 0}));
|
||||
localDataPoolMap.emplace(lpool::int64Vec2Id,
|
||||
new PoolEntry<int64_t>({0, 0}));
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) override;
|
||||
|
||||
LocalDataPoolManager* getHkManagerHandle() override {
|
||||
return &poolManager;
|
||||
}
|
||||
|
||||
uint32_t getPeriodicOperationFrequency() const override {
|
||||
return 0;
|
||||
dur_millis_t getPeriodicOperationFrequency() const override {
|
||||
return 200;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,68 +112,69 @@ public:
|
||||
return &dataset;
|
||||
}
|
||||
|
||||
virtual LocalPoolObjectBase* getPoolObjectHandle(
|
||||
lp_id_t localPoolId) override {
|
||||
if(localPoolId == lpool::uint8VarId) {
|
||||
return &testUint8;
|
||||
}
|
||||
else if(localPoolId == lpool::uint16Vec3Id) {
|
||||
return &testUint16Vec;
|
||||
}
|
||||
else if(localPoolId == lpool::floatVarId) {
|
||||
return &testFloat;
|
||||
}
|
||||
else if(localPoolId == lpool::int64Vec2Id) {
|
||||
return &testInt64Vec;
|
||||
}
|
||||
else if(localPoolId == lpool::uint32VarId) {
|
||||
return &testUint32;
|
||||
}
|
||||
else {
|
||||
return &testUint8;
|
||||
}
|
||||
}
|
||||
virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) override;
|
||||
|
||||
MessageQueueMockBase* getMockQueueHandle() const {
|
||||
return dynamic_cast<MessageQueueMockBase*>(messageQueue);
|
||||
}
|
||||
|
||||
ReturnValue_t subscribePeriodicHk(bool enableReporting) {
|
||||
return poolManager.subscribeForPeriodicPacket(lpool::testSid, enableReporting, 0.2, false);
|
||||
}
|
||||
|
||||
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 subscribeWrapperVariableSnapshot(lp_id_t localPoolId) {
|
||||
return poolManager.subscribeForVariableUpdateMessage(localPoolId,
|
||||
MessageQueueIF::NO_QUEUE, objects::HK_RECEIVER_MOCK, true);
|
||||
}
|
||||
|
||||
ReturnValue_t reset();
|
||||
|
||||
void resetSubscriptionList() {
|
||||
poolManager.clearReceiversList();
|
||||
}
|
||||
|
||||
bool changedDataSetCallbackWasCalled(sid_t& sid, store_address_t& storeId);
|
||||
bool changedVariableCallbackWasCalled(gp_id_t& gpid, store_address_t& storeId);
|
||||
|
||||
LocalDataPoolManager poolManager;
|
||||
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);
|
||||
void handleChangedDataset(sid_t sid, store_address_t storeId, bool* clearMessage) override;
|
||||
sid_t changedDatasetSid;
|
||||
store_address_t storeIdForChangedSet;
|
||||
|
||||
void handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId,
|
||||
bool* clearMessage) override;
|
||||
gp_id_t changedPoolVariableGpid;
|
||||
store_address_t storeIdForChangedVariable;
|
||||
|
||||
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);
|
||||
|
||||
|
@@ -10,8 +10,7 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") {
|
||||
get<LocalPoolOwnerBase>(objects::TEST_LOCAL_POOL_OWNER_BASE);
|
||||
REQUIRE(poolOwner != nullptr);
|
||||
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
|
||||
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation()
|
||||
== retval::CATCH_OK);
|
||||
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() == retval::CATCH_OK);
|
||||
|
||||
SECTION("Basic Tests") {
|
||||
/* very basic test. */
|
||||
@@ -118,6 +117,8 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") {
|
||||
lpool::uint8VarId);
|
||||
}
|
||||
|
||||
CHECK(poolOwner->reset() == retval::CATCH_OK);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -115,6 +115,7 @@ TEST_CASE("LocalPoolVector" , "[LocPoolVecTest]") {
|
||||
REQUIRE(readOnlyVec.commit() ==
|
||||
static_cast<int>(PoolVariableIF::INVALID_READ_WRITE_MODE));
|
||||
}
|
||||
poolOwner->reset();
|
||||
}
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user