Merge pull request 'Update Package' (#378) from KSat/fsfw:mueller/update-pack into development

Reviewed-on: fsfw/fsfw#378
This commit is contained in:
Steffen Gaisser 2021-03-09 14:16:13 +01:00
commit dbda6fee82
23 changed files with 1051 additions and 828 deletions

View File

@ -22,7 +22,9 @@ a C file without issues
### Local Pool ### Local Pool
- Interface of LocalPools has changed. LocalPool is not a template anymore. Instead the size and bucket number of the pools per page and the number of pages are passed to the ctor instead of two ctor arguments and a template parameter - Interface of LocalPools has changed. LocalPool is not a template anymore. Instead the size and
bucket number of the pools per page and the number of pages are passed to the ctor instead of
two ctor arguments and a template parameter
### Parameter Service ### Parameter Service
@ -40,7 +42,8 @@ important use-case)
### File System Interface ### File System Interface
- A new interfaces specifies the functions for a software object which exposes the file system of a given hardware to use message based file handling (e.g. PUS commanding) - A new interfaces specifies the functions for a software object which exposes the file system of
a given hardware to use message based file handling (e.g. PUS commanding)
### Internal Error Reporter ### Internal Error Reporter
@ -52,7 +55,8 @@ ID for now.
### Device Handler Base ### Device Handler Base
- There is an additional `PERFORM_OPERATION` step for the device handler base. It is important - There is an additional `PERFORM_OPERATION` step for the device handler base. It is important
that DHB users adapt their polling sequence tables to perform this step. This steps allows for aclear distinction between operation and communication steps that DHB users adapt their polling sequence tables to perform this step. This steps allows for
a clear distinction between operation and communication steps
- setNormalDatapoolEntriesInvalid is not an abstract method and a default implementation was provided - setNormalDatapoolEntriesInvalid is not an abstract method and a default implementation was provided
- getTransitionDelayMs is now an abstract method - getTransitionDelayMs is now an abstract method
@ -69,7 +73,8 @@ now
### Commanding Service Base ### Commanding Service Base
- CSB uses the new fsfwconfig::FSFW_CSB_FIFO_DEPTH variable to determine the FIFO depth for each CSB instance. This variable has to be set in the FSFWConfig.h file - CSB uses the new fsfwconfig::FSFW_CSB_FIFO_DEPTH variable to determine the FIFO depth for each
CSB instance. This variable has to be set in the FSFWConfig.h file
### Service Interface ### Service Interface
@ -82,6 +87,12 @@ now
For mission code, developers need to replace sif:: calls by the printf counterparts, but only if the CPP stream are excluded. For mission code, developers need to replace sif:: calls by the printf counterparts, but only if the CPP stream are excluded.
If this is not the case, everything should work as usual. If this is not the case, everything should work as usual.
### ActionHelper and ActionMessage
- ActionHelper finish function and ActionMessage::setCompletionReply now expects explicit
information whether to report a success or failure message instead of deriving it implicitely
from returnvalue
### PUS Parameter Service 20 ### PUS Parameter Service 20
Added PUS parameter service 20 (only custom subservices available). Added PUS parameter service 20 (only custom subservices available).

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,9 +8,9 @@
/** /**
* @brief Helper class to read data sets or pool variables * @brief Helper class to read data sets or pool variables
*/ */
class PoolReadHelper { class PoolReadGuard {
public: public:
PoolReadHelper(ReadCommitIF* readObject, PoolReadGuard(ReadCommitIF* readObject,
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t mutexTimeout = 20): uint32_t mutexTimeout = 20):
readObject(readObject), mutexTimeout(mutexTimeout) { readObject(readObject), mutexTimeout(mutexTimeout) {
@ -42,7 +42,7 @@ public:
/** /**
* @brief Default destructor which will take care of commiting changed values. * @brief Default destructor which will take care of commiting changed values.
*/ */
~PoolReadHelper() { ~PoolReadGuard() {
if(readObject != nullptr and not noCommit) { if(readObject != nullptr and not noCommit) {
readObject->commit(timeoutType, mutexTimeout); readObject->commit(timeoutType, mutexTimeout);
} }

View File

@ -46,6 +46,8 @@ public:
* read-write or read-only. * read-write or read-only.
*/ */
virtual ReadWriteMode_t getReadWriteMode() const = 0; virtual ReadWriteMode_t getReadWriteMode() const = 0;
virtual void setReadWriteMode(ReadWriteMode_t newMode) = 0;
/** /**
* @brief This operation shall return the data pool id of the variable. * @brief This operation shall return the data pool id of the variable.
*/ */
@ -59,7 +61,6 @@ public:
* @brief With this call, the valid information of the variable is set. * @brief With this call, the valid information of the variable is set.
*/ */
virtual void setValid(bool validity) = 0; virtual void setValid(bool validity) = 0;
}; };
using pool_rwm_t = PoolVariableIF::ReadWriteMode_t; using pool_rwm_t = PoolVariableIF::ReadWriteMode_t;

View File

@ -162,8 +162,8 @@ protected:
*/ */
virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) { virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden" sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden. "
<< ". Returning nullptr!" << std::endl; "Returning nullptr!" << std::endl;
#else #else
sif::printWarning("HasLocalDataPoolIF::getPoolObjectHandle: " sif::printWarning("HasLocalDataPoolIF::getPoolObjectHandle: "
"Not overriden. Returning nullptr!\n"); "Not overriden. Returning nullptr!\n");

View File

@ -42,15 +42,15 @@ LocalDataPoolManager::~LocalDataPoolManager() {}
ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) { ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) {
if(queueToUse == nullptr) { if(queueToUse == nullptr) {
// error, all destinations invalid /* Error, all destinations invalid */
printWarningOrError(sif::OutputTypes::OUT_ERROR, printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize",
"initialize", QUEUE_OR_DESTINATION_INVALID); QUEUE_OR_DESTINATION_INVALID);
} }
hkQueue = queueToUse; hkQueue = queueToUse;
ipcStore = objectManager->get<StorageManagerIF>(objects::IPC_STORE); ipcStore = objectManager->get<StorageManagerIF>(objects::IPC_STORE);
if(ipcStore == nullptr) { if(ipcStore == nullptr) {
// error, all destinations invalid /* Error, all destinations invalid */
printWarningOrError(sif::OutputTypes::OUT_ERROR, printWarningOrError(sif::OutputTypes::OUT_ERROR,
"initialize", HasReturnvaluesIF::RETURN_FAILED, "initialize", HasReturnvaluesIF::RETURN_FAILED,
"Could not set IPC store."); "Could not set IPC store.");

View File

@ -42,8 +42,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
} }
} }
LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray,
PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables): const size_t maxNumberOfVariables):
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>( HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(
@ -289,7 +288,7 @@ bool LocalPoolDataSetBase::isValid() const {
void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) { void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) {
if(setEntriesRecursively) { if(setEntriesRecursively) {
for(size_t idx = 0; idx < this->getFillCount(); idx++) { for(size_t idx = 0; idx < this->getFillCount(); idx++) {
registeredVariables[idx] -> setValid(valid); registeredVariables[idx]->setValid(valid);
} }
} }
this->valid = valid; this->valid = valid;
@ -302,3 +301,8 @@ object_id_t LocalPoolDataSetBase::getCreatorObjectId() {
return objects::NO_OBJECT; return objects::NO_OBJECT;
} }
void LocalPoolDataSetBase::setAllVariablesReadOnly() {
for(size_t idx = 0; idx < this->getFillCount(); idx++) {
registeredVariables[idx]->setReadWriteMode(pool_rwm_t::VAR_READ);
}
}

View File

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

View File

@ -37,15 +37,20 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
if(poolId == PoolVariableIF::NO_PARAMETER) { if(poolId == PoolVariableIF::NO_PARAMETER) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolVar<T>::LocalPoolVar: 0 passed as pool ID, " sif::warning << "LocalPoolVar<T>::LocalPoolVar: 0 passed as pool ID, "
<< "which is the NO_PARAMETER value!" << std::endl; "which is the NO_PARAMETER value!" << std::endl;
#else
sif::printWarning("LocalPoolVar<T>::LocalPoolVar: 0 passed as pool ID, "
"which is the NO_PARAMETER value!\n");
#endif #endif
} }
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(poolOwner); HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(poolOwner);
if(hkOwner == nullptr) { if(hkOwner == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPoolVariable: The supplied pool owner did not " sif::error << "LocalPoolVariable: The supplied pool owner did not implement the correct "
<< "implement the correct interface" "interface HasLocalDataPoolIF!" << std::endl;
<< " HasLocalDataPoolIF!" << std::endl; #else
sif::printError( "LocalPoolVariable: The supplied pool owner did not implement the correct "
"interface HasLocalDataPoolIF!\n");
#endif #endif
return; return;
} }

View File

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

View File

@ -558,7 +558,7 @@ void DeviceHandlerBase::replyToCommand(ReturnValue_t status,
if (cookieInfo.pendingCommand->second.sendReplyTo != NO_COMMANDER) { if (cookieInfo.pendingCommand->second.sendReplyTo != NO_COMMANDER) {
MessageQueueId_t queueId = cookieInfo.pendingCommand->second.sendReplyTo; MessageQueueId_t queueId = cookieInfo.pendingCommand->second.sendReplyTo;
if (status == NO_REPLY_EXPECTED) { if (status == NO_REPLY_EXPECTED) {
actionHelper.finish(queueId, cookieInfo.pendingCommand->first, actionHelper.finish(true, queueId, cookieInfo.pendingCommand->first,
RETURN_OK); RETURN_OK);
} else { } else {
actionHelper.step(1, queueId, cookieInfo.pendingCommand->first, actionHelper.step(1, queueId, cookieInfo.pendingCommand->first,
@ -581,7 +581,11 @@ void DeviceHandlerBase::replyToReply(DeviceReplyMap::iterator iter,
// Check if it was transition or internal command. // Check if it was transition or internal command.
// Don't send any replies in that case. // Don't send any replies in that case.
if (info->sendReplyTo != NO_COMMANDER) { if (info->sendReplyTo != NO_COMMANDER) {
actionHelper.finish(info->sendReplyTo, iter->first, status); bool success = false;
if(status == HasReturnvaluesIF::RETURN_OK) {
success = true;
}
actionHelper.finish(success, info->sendReplyTo, iter->first, status);
} }
info->isExecuting = false; info->isExecuting = false;
} }

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -192,7 +192,7 @@ public:
resetSubscriptionList(); resetSubscriptionList();
ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; ReturnValue_t status = HasReturnvaluesIF::RETURN_OK;
{ {
PoolReadHelper readHelper(&dataset); PoolReadGuard readHelper(&dataset);
if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) {
status = readHelper.getReadResult(); status = readHelper.getReadResult();
} }
@ -205,7 +205,7 @@ public:
} }
{ {
PoolReadHelper readHelper(&testUint32); PoolReadGuard readHelper(&testUint32);
if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) {
status = readHelper.getReadResult(); status = readHelper.getReadResult();
} }
@ -214,7 +214,7 @@ public:
} }
{ {
PoolReadHelper readHelper(&testInt64Vec); PoolReadGuard readHelper(&testInt64Vec);
if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) {
status = readHelper.getReadResult(); status = readHelper.getReadResult();
} }