diff --git a/CHANGELOG b/CHANGELOG index fc46ee02..add8e1a5 100644 --- a/CHANGELOG +++ b/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). \ No newline at end of file +Added PUS parameter service 20 (only custom subservices available). diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index e32be68b..42ee4022 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -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(success, &reply, commandId, 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(true, &reply, actionId, result); queueToUse->sendMessage(commandedBy, &reply); } if (result != HasReturnvaluesIF::RETURN_OK) { diff --git a/action/ActionHelper.h b/action/ActionHelper.h index 35ac41d1..c9024747 100644 --- a/action/ActionHelper.h +++ b/action/ActionHelper.h @@ -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. diff --git a/action/ActionMessage.cpp b/action/ActionMessage.cpp index c3eb4710..15b59b9d 100644 --- a/action/ActionMessage.cpp +++ b/action/ActionMessage.cpp @@ -53,11 +53,12 @@ void ActionMessage::setDataReply(CommandMessage* message, ActionId_t actionId, message->setParameter2(data.raw); } -void ActionMessage::setCompletionReply(CommandMessage* message, +void ActionMessage::setCompletionReply(bool success, CommandMessage* message, ActionId_t fid, ReturnValue_t result) { - if (result == HasReturnvaluesIF::RETURN_OK or result == HasActionsIF::EXECUTION_FINISHED) { + if (success) { message->setCommand(COMPLETION_SUCCESS); - } else { + } + else { message->setCommand(COMPLETION_FAILED); } message->setParameter(fid); diff --git a/action/ActionMessage.h b/action/ActionMessage.h index 246ac601..df97485b 100644 --- a/action/ActionMessage.h +++ b/action/ActionMessage.h @@ -24,19 +24,23 @@ 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 ); static ReturnValue_t getReturnCode(const CommandMessage* message ); static void setDataReply(CommandMessage* message, ActionId_t actionId, 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); + static void clear(CommandMessage* message); }; diff --git a/action/SimpleActionHelper.cpp b/action/SimpleActionHelper.cpp index f77d0147..979ee3ad 100644 --- a/action/SimpleActionHelper.cpp +++ b/action/SimpleActionHelper.cpp @@ -62,7 +62,7 @@ void SimpleActionHelper::prepareExecution(MessageQueueId_t commandedBy, stepCount++; break; case HasActionsIF::EXECUTION_FINISHED: - ActionMessage::setCompletionReply(&reply, actionId, + ActionMessage::setCompletionReply(true, &reply, actionId, HasReturnvaluesIF::RETURN_OK); queueToUse->sendMessage(commandedBy, &reply); break; diff --git a/controller/ExtendedControllerBase.cpp b/controller/ExtendedControllerBase.cpp index cc93efa8..95ba012e 100644 --- a/controller/ExtendedControllerBase.cpp +++ b/controller/ExtendedControllerBase.cpp @@ -13,7 +13,7 @@ ExtendedControllerBase::~ExtendedControllerBase() { ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t *data, size_t size) { - // needs to be overriden and implemented by child class. + /* Needs to be overriden and implemented by child class. */ return HasReturnvaluesIF::RETURN_OK; } @@ -21,7 +21,7 @@ ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId, ReturnValue_t ExtendedControllerBase::initializeLocalDataPool( localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { - // needs to be overriden and implemented by child class. + /* Needs to be overriden and implemented by child class. */ return HasReturnvaluesIF::RETURN_OK; } @@ -96,8 +96,10 @@ ReturnValue_t ExtendedControllerBase::initializeAfterTaskCreation() { ReturnValue_t ExtendedControllerBase::performOperation(uint8_t opCode) { handleQueue(); - poolManager.performHkOperation(); performControlOperation(); + /* We do this after performing control operation because variables will be set changed + in this function. */ + poolManager.performHkOperation(); return RETURN_OK; } diff --git a/datapool/PoolReadHelper.h b/datapool/PoolReadGuard.h similarity index 94% rename from datapool/PoolReadHelper.h rename to datapool/PoolReadGuard.h index 9825e83c..eb014f1e 100644 --- a/datapool/PoolReadHelper.h +++ b/datapool/PoolReadGuard.h @@ -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) { @@ -42,7 +42,7 @@ public: /** * @brief Default destructor which will take care of commiting changed values. */ - ~PoolReadHelper() { + ~PoolReadGuard() { if(readObject != nullptr and not noCommit) { readObject->commit(timeoutType, mutexTimeout); } diff --git a/datapool/PoolVariableIF.h b/datapool/PoolVariableIF.h index dead6844..dbb9db15 100644 --- a/datapool/PoolVariableIF.h +++ b/datapool/PoolVariableIF.h @@ -46,6 +46,8 @@ public: * 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. */ @@ -59,7 +61,6 @@ public: * @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; diff --git a/datapoollocal/HasLocalDataPoolIF.h b/datapoollocal/HasLocalDataPoolIF.h index 49f2db7d..d52c72b6 100644 --- a/datapoollocal/HasLocalDataPoolIF.h +++ b/datapoollocal/HasLocalDataPoolIF.h @@ -162,8 +162,8 @@ protected: */ virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden" - << ". Returning nullptr!" << std::endl; + sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden. " + "Returning nullptr!" << std::endl; #else sif::printWarning("HasLocalDataPoolIF::getPoolObjectHandle: " "Not overriden. Returning nullptr!\n"); diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index c15182d2..72873d99 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -42,15 +42,15 @@ LocalDataPoolManager::~LocalDataPoolManager() {} ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) { if(queueToUse == nullptr) { - // error, all destinations invalid - printWarningOrError(sif::OutputTypes::OUT_ERROR, - "initialize", QUEUE_OR_DESTINATION_INVALID); + /* Error, all destinations invalid */ + printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", + QUEUE_OR_DESTINATION_INVALID); } hkQueue = queueToUse; ipcStore = objectManager->get(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."); diff --git a/datapoollocal/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp index 75a3ab7c..daa54fb3 100644 --- a/datapoollocal/LocalPoolDataSetBase.cpp +++ b/datapoollocal/LocalPoolDataSetBase.cpp @@ -42,8 +42,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner, } } -LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, - PoolVariableIF** registeredVariablesArray, +LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray, const size_t maxNumberOfVariables): PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { HasLocalDataPoolIF* hkOwner = objectManager->get( @@ -289,7 +288,7 @@ bool LocalPoolDataSetBase::isValid() const { void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) { if(setEntriesRecursively) { for(size_t idx = 0; idx < this->getFillCount(); idx++) { - registeredVariables[idx] -> setValid(valid); + registeredVariables[idx]->setValid(valid); } } this->valid = valid; @@ -302,3 +301,8 @@ 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); + } +} diff --git a/datapoollocal/LocalPoolDataSetBase.h b/datapoollocal/LocalPoolDataSetBase.h index 8a3a6339..404509ae 100644 --- a/datapoollocal/LocalPoolDataSetBase.h +++ b/datapoollocal/LocalPoolDataSetBase.h @@ -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; diff --git a/datapoollocal/LocalPoolObjectBase.cpp b/datapoollocal/LocalPoolObjectBase.cpp index a64ed2b4..b6db0608 100644 --- a/datapoollocal/LocalPoolObjectBase.cpp +++ b/datapoollocal/LocalPoolObjectBase.cpp @@ -37,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::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::LocalPoolVar: 0 passed as pool ID, " + "which is the NO_PARAMETER value!\n"); #endif } HasLocalDataPoolIF* hkOwner = objectManager->get(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; } diff --git a/datapoollocal/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp index 35de4ebd..ba7a413d 100644 --- a/datapoollocal/LocalPoolVariable.tpp +++ b/datapoollocal/LocalPoolVariable.tpp @@ -26,8 +26,17 @@ inline LocalPoolVariable::LocalPoolVariable(gp_id_t globalPoolId, template inline ReturnValue_t LocalPoolVariable::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 @@ -43,7 +52,6 @@ inline ReturnValue_t LocalPoolVariable::readWithoutLock() { PoolEntry* 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::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::commit(bool setValid, template inline ReturnValue_t LocalPoolVariable::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 diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 35d34bf9..15eac11f 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -558,7 +558,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 +581,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; } diff --git a/doc/README-localpools.md b/doc/README-localpools.md index 8dd83385..96ae2d0a 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -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. + +
+ +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 angVelocityX = lp_var_t(sid.objectId, + gyrodefs::ANGULAR_VELOCITY_X, this); + lp_var_t angVelocityY = lp_var_t(sid.objectId, + gyrodefs::ANGULAR_VELOCITY_Y, this); + lp_var_t angVelocityZ = lp_var_t(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({0.0})); + localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Y, + new PoolEntry({0.0})); + localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Z, + new PoolEntry({0.0})); + localDataPoolMap.emplace(gyrodefs::GENERAL_CONFIG_REG42, + new PoolEntry({0})); + localDataPoolMap.emplace(gyrodefs::RANGE_CONFIG_REG43, + new PoolEntry({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 diff --git a/doc/images/PoolArchitecture.png b/doc/images/PoolArchitecture.png new file mode 100644 index 00000000..13ea85c8 Binary files /dev/null and b/doc/images/PoolArchitecture.png differ diff --git a/osal/FreeRTOS/Clock.cpp b/osal/FreeRTOS/Clock.cpp index 9c0a0267..806edcc7 100644 --- a/osal/FreeRTOS/Clock.cpp +++ b/osal/FreeRTOS/Clock.cpp @@ -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; } diff --git a/timemanager/CCSDSTime.cpp b/timemanager/CCSDSTime.cpp index aefcac2e..192c1f57 100644 --- a/timemanager/CCSDSTime.cpp +++ b/timemanager/CCSDSTime.cpp @@ -12,595 +12,603 @@ CCSDSTime::~CCSDSTime() { } ReturnValue_t CCSDSTime::convertToCcsds(Ccs_seconds* to, - const Clock::TimeOfDay_t* from) { - ReturnValue_t result = checkTimeOfDay(from); - if (result != RETURN_OK) { - return result; - } + const Clock::TimeOfDay_t* from) { + ReturnValue_t result = checkTimeOfDay(from); + if (result != RETURN_OK) { + return result; + } - to->pField = (CCS << 4); + to->pField = (CCS << 4); - to->yearMSB = (from->year >> 8); - to->yearLSB = from->year & 0xff; - to->month = from->month; - to->day = from->day; - to->hour = from->hour; - to->minute = from->minute; - to->second = from->second; + to->yearMSB = (from->year >> 8); + to->yearLSB = from->year & 0xff; + to->month = from->month; + to->day = from->day; + to->hour = from->hour; + to->minute = from->minute; + to->second = from->second; - return RETURN_OK; + return RETURN_OK; } ReturnValue_t CCSDSTime::convertToCcsds(Ccs_mseconds* to, - const Clock::TimeOfDay_t* from) { - ReturnValue_t result = checkTimeOfDay(from); - if (result != RETURN_OK) { - return result; - } + const Clock::TimeOfDay_t* from) { + ReturnValue_t result = checkTimeOfDay(from); + if (result != RETURN_OK) { + return result; + } - to->pField = (CCS << 4) + 2; + to->pField = (CCS << 4) + 2; - to->yearMSB = (from->year >> 8); - to->yearLSB = from->year & 0xff; - to->month = from->month; - to->day = from->day; - to->hour = from->hour; - to->minute = from->minute; - to->second = from->second; - to->secondEminus2 = from->usecond / 10000; - to->secondEminus4 = (from->usecond % 10000) / 100; + to->yearMSB = (from->year >> 8); + to->yearLSB = from->year & 0xff; + to->month = from->month; + to->day = from->day; + to->hour = from->hour; + to->minute = from->minute; + to->second = from->second; + to->secondEminus2 = from->usecond / 10000; + to->secondEminus4 = (from->usecond % 10000) / 100; - return RETURN_OK; + return RETURN_OK; } ReturnValue_t CCSDSTime::convertFromCcsds(Clock::TimeOfDay_t* to, const uint8_t* from, size_t length) { - ReturnValue_t result; - if (length > 0xFF) { - return LENGTH_MISMATCH; - } - result = convertFromASCII(to, from, length); //Try to parse it as ASCII - if (result == RETURN_OK) { - return RETURN_OK; - } + ReturnValue_t result; + if (length > 0xFF) { + return LENGTH_MISMATCH; + } + result = convertFromASCII(to, from, length); //Try to parse it as ASCII + if (result == RETURN_OK) { + return RETURN_OK; + } - //Seems to be no ascii, try the other formats - uint8_t codeIdentification = (*from >> 4); - switch (codeIdentification) { - case CUC_LEVEL1: //CUC_LEVEL2 can not be converted to TimeOfDay (ToD is Level 1) <- Well, if we know the epoch, we can... <- see bug 1133 - return convertFromCUC(to, from, length); - case CDS: - return convertFromCDS(to, from, length); - case CCS: { - size_t temp = 0; - return convertFromCCS(to, from, &temp, length); - } + //Seems to be no ascii, try the other formats + uint8_t codeIdentification = (*from >> 4); + switch (codeIdentification) { + case CUC_LEVEL1: //CUC_LEVEL2 can not be converted to TimeOfDay (ToD is Level 1) <- Well, if we know the epoch, we can... <- see bug 1133 + return convertFromCUC(to, from, length); + case CDS: + return convertFromCDS(to, from, length); + case CCS: { + size_t temp = 0; + return convertFromCCS(to, from, &temp, length); + } - default: - return UNSUPPORTED_TIME_FORMAT; - } + default: + return UNSUPPORTED_TIME_FORMAT; + } } ReturnValue_t CCSDSTime::convertFromCUC(Clock::TimeOfDay_t* to, const uint8_t* from, uint8_t length) { - return UNSUPPORTED_TIME_FORMAT; + return UNSUPPORTED_TIME_FORMAT; } ReturnValue_t CCSDSTime::convertFromCDS(Clock::TimeOfDay_t* to, const uint8_t* from, uint8_t length) { - timeval time; - ReturnValue_t result = convertFromCDS(&time, from, NULL, length); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - return convertTimevalToTimeOfDay(to, &time); + timeval time; + ReturnValue_t result = convertFromCDS(&time, from, NULL, length); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + return convertTimevalToTimeOfDay(to, &time); } ReturnValue_t CCSDSTime::convertFromCCS(Clock::TimeOfDay_t* to, const uint8_t* from, size_t* foundLength, size_t maxLength) { - uint8_t subsecondsLength = *from & 0b111; - uint32_t totalLength = subsecondsLength + 8; - if (maxLength < totalLength) { - return LENGTH_MISMATCH; - } + uint8_t subsecondsLength = *from & 0b111; + uint32_t totalLength = subsecondsLength + 8; + if (maxLength < totalLength) { + return LENGTH_MISMATCH; + } - *foundLength = totalLength; + *foundLength = totalLength; - ReturnValue_t result = checkCcs(from, maxLength); + ReturnValue_t result = checkCcs(from, maxLength); - if (result != RETURN_OK) { - return result; - } + if (result != RETURN_OK) { + return result; + } - Ccs_mseconds *temp = (Ccs_mseconds *) from; + Ccs_mseconds *temp = (Ccs_mseconds *) from; - to->year = (temp->yearMSB << 8) + temp->yearLSB; - to->hour = temp->hour; - to->minute = temp->minute; - to->second = temp->second; + to->year = (temp->yearMSB << 8) + temp->yearLSB; + to->hour = temp->hour; + to->minute = temp->minute; + to->second = temp->second; - if (temp->pField & (1 << 3)) { //day of year variation - uint16_t tempDay = (temp->month << 8) + temp->day; - result = convertDaysOfYear(tempDay, to->year, - &(temp->month), &(temp->day)); - if (result != RETURN_OK) { - return result; - } - } + if (temp->pField & (1 << 3)) { //day of year variation + uint16_t tempDay = (temp->month << 8) + temp->day; + result = convertDaysOfYear(tempDay, to->year, + &(temp->month), &(temp->day)); + if (result != RETURN_OK) { + return result; + } + } - to->month = temp->month; - to->day = temp->day; + to->month = temp->month; + to->day = temp->day; - to->usecond = 0; - if (subsecondsLength > 0) { - *foundLength += 1; - if (temp->secondEminus2 >= 100) { - return INVALID_TIME_FORMAT; - } - to->usecond = temp->secondEminus2 * 10000; - } + to->usecond = 0; + if (subsecondsLength > 0) { + *foundLength += 1; + if (temp->secondEminus2 >= 100) { + return INVALID_TIME_FORMAT; + } + to->usecond = temp->secondEminus2 * 10000; + } - if (subsecondsLength > 1) { - *foundLength += 1; - if (temp->secondEminus4 >= 100) { - return INVALID_TIME_FORMAT; - } - to->usecond += temp->secondEminus4 * 100; - } + if (subsecondsLength > 1) { + *foundLength += 1; + if (temp->secondEminus4 >= 100) { + return INVALID_TIME_FORMAT; + } + to->usecond += temp->secondEminus4 * 100; + } - return RETURN_OK; + return RETURN_OK; } ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t* from, uint8_t length) { - if (length < 19) { - return RETURN_FAILED; - } -// Newlib nano can't parse uint8, see SCNu8 documentation and https://sourceware.org/newlib/README -// Suggestion: use uint16 all the time. This should work on all systems. + if (length < 19) { + return RETURN_FAILED; + } + // Newlib nano can't parse uint8, see SCNu8 documentation and https://sourceware.org/newlib/README + // Suggestion: use uint16 all the time. This should work on all systems. #if FSFW_NO_C99_IO == 1 - uint16_t year; - uint16_t month; - uint16_t day; - uint16_t hour; - uint16_t minute; - float second; - int count = sscanf((char *) from, "%4" SCNu16 "-%2" SCNu16 "-%2" SCNu16 "T%" - "2" SCNu16 ":%2" SCNu16 ":%fZ", &year, &month, &day, &hour, - &minute, &second); - if (count == 6) { - to->year = year; - to->month = month; - to->day = day; - to->hour = hour; - to->minute = minute; - to->second = second; - to->usecond = (second - floor(second)) * 1000000; - return RETURN_OK; - } + uint16_t year; + uint16_t month; + uint16_t day; + uint16_t hour; + uint16_t minute; + float second; + int count = sscanf((char *) from, "%4" SCNu16 "-%2" SCNu16 "-%2" SCNu16 "T%" + "2" SCNu16 ":%2" SCNu16 ":%fZ", &year, &month, &day, &hour, + &minute, &second); + if (count == 6) { + to->year = year; + to->month = month; + to->day = day; + to->hour = hour; + to->minute = minute; + to->second = second; + to->usecond = (second - floor(second)) * 1000000; + return RETURN_OK; + } - // try Code B (yyyy-ddd) - count = sscanf((char *) from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu16 ":%" - "2" SCNu16 ":%fZ", &year, &day, &hour, &minute, &second); - if (count == 5) { - uint8_t tempDay; - ReturnValue_t result = CCSDSTime::convertDaysOfYear(day, year, - reinterpret_cast(&month), - reinterpret_cast(&tempDay)); - if (result != RETURN_OK) { - return RETURN_FAILED; - } - to->year = year; - to->month = month; - to->day = tempDay; - to->hour = hour; - to->minute = minute; - to->second = second; - to->usecond = (second - floor(second)) * 1000000; - return RETURN_OK; - } -// Warning: Compiler/Linker fails ambiguously if library does not implement -// C99 I/O + // try Code B (yyyy-ddd) + count = sscanf((char *) from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu16 ":%" + "2" SCNu16 ":%fZ", &year, &day, &hour, &minute, &second); + if (count == 5) { + uint8_t tempDay; + ReturnValue_t result = CCSDSTime::convertDaysOfYear(day, year, + reinterpret_cast(&month), + reinterpret_cast(&tempDay)); + if (result != RETURN_OK) { + return RETURN_FAILED; + } + to->year = year; + to->month = month; + to->day = tempDay; + to->hour = hour; + to->minute = minute; + to->second = second; + to->usecond = (second - floor(second)) * 1000000; + return RETURN_OK; + } + // Warning: Compiler/Linker fails ambiguously if library does not implement + // C99 I/O #else - uint16_t year; - uint8_t month; - uint16_t day; - uint8_t hour; - uint8_t minute; - float second; + uint16_t year; + uint8_t month; + uint16_t day; + uint8_t hour; + uint8_t minute; + float second; //try Code A (yyyy-mm-dd) int count = sscanf((char *) from, "%4" SCNu16 "-%2" SCNu8 "-%2" SCNu16 "T%2" SCNu8 ":%2" SCNu8 ":%fZ", &year, &month, &day, &hour, &minute, &second); - if (count == 6) { - to->year = year; - to->month = month; - to->day = day; - to->hour = hour; - to->minute = minute; - to->second = second; - to->usecond = (second - floor(second)) * 1000000; - return RETURN_OK; - } + if (count == 6) { + to->year = year; + to->month = month; + to->day = day; + to->hour = hour; + to->minute = minute; + to->second = second; + to->usecond = (second - floor(second)) * 1000000; + return RETURN_OK; + } //try Code B (yyyy-ddd) count = sscanf((char *) from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu8 ":%2" SCNu8 ":%fZ", &year, &day, &hour, &minute, &second); - if (count == 5) { - uint8_t tempDay; - ReturnValue_t result = CCSDSTime::convertDaysOfYear(day, year, &month, - &tempDay); - if (result != RETURN_OK) { - return RETURN_FAILED; - } - to->year = year; - to->month = month; - to->day = tempDay; - to->hour = hour; - to->minute = minute; - to->second = second; - to->usecond = (second - floor(second)) * 1000000; - return RETURN_OK; - } + if (count == 5) { + uint8_t tempDay; + ReturnValue_t result = CCSDSTime::convertDaysOfYear(day, year, &month, + &tempDay); + if (result != RETURN_OK) { + return RETURN_FAILED; + } + to->year = year; + to->month = month; + to->day = tempDay; + to->hour = hour; + to->minute = minute; + to->second = second; + to->usecond = (second - floor(second)) * 1000000; + return RETURN_OK; + } #endif - return UNSUPPORTED_TIME_FORMAT; + return UNSUPPORTED_TIME_FORMAT; } ReturnValue_t CCSDSTime::checkCcs(const uint8_t* time, uint8_t length) { - Ccs_mseconds *time_struct = (Ccs_mseconds *) time; + Ccs_mseconds *time_struct = (Ccs_mseconds *) time; - uint8_t additionalBytes = time_struct->pField & 0b111; - if ((additionalBytes == 0b111) || (length < (additionalBytes + 8))) { - return INVALID_TIME_FORMAT; - } + uint8_t additionalBytes = time_struct->pField & 0b111; + if ((additionalBytes == 0b111) || (length < (additionalBytes + 8))) { + return INVALID_TIME_FORMAT; + } - if (time_struct->pField & (1 << 3)) { //day of year variation - uint16_t day = (time_struct->month << 8) + time_struct->day; - if (day > 366) { - return INVALID_TIME_FORMAT; - } - } else { - if (time_struct->month > 12) { - return INVALID_TIME_FORMAT; - } - if (time_struct->day > 31) { - return INVALID_TIME_FORMAT; - } - } - if (time_struct->hour > 23) { - return INVALID_TIME_FORMAT; - } - if (time_struct->minute > 59) { - return INVALID_TIME_FORMAT; - } - if (time_struct->second > 59) { - return INVALID_TIME_FORMAT; - } + if (time_struct->pField & (1 << 3)) { //day of year variation + uint16_t day = (time_struct->month << 8) + time_struct->day; + if (day > 366) { + return INVALID_TIME_FORMAT; + } + } else { + if (time_struct->month > 12) { + return INVALID_TIME_FORMAT; + } + if (time_struct->day > 31) { + return INVALID_TIME_FORMAT; + } + } + if (time_struct->hour > 23) { + return INVALID_TIME_FORMAT; + } + if (time_struct->minute > 59) { + return INVALID_TIME_FORMAT; + } + if (time_struct->second > 59) { + return INVALID_TIME_FORMAT; + } - uint8_t *additionalByte = &time_struct->secondEminus2; + uint8_t *additionalByte = &time_struct->secondEminus2; - for (; additionalBytes != 0; additionalBytes--) { - if (*additionalByte++ > 99) { - return INVALID_TIME_FORMAT; - } - } - return RETURN_OK; + for (; additionalBytes != 0; additionalBytes--) { + if (*additionalByte++ > 99) { + return INVALID_TIME_FORMAT; + } + } + return RETURN_OK; } ReturnValue_t CCSDSTime::convertDaysOfYear(uint16_t dayofYear, uint16_t year, - uint8_t* month, uint8_t* day) { - if (isLeapYear(year)) { - if (dayofYear > 366) { - return INVALID_DAY_OF_YEAR; - } - } else { - if (dayofYear > 365) { - return INVALID_DAY_OF_YEAR; - } - } - *month = 1; - if (dayofYear <= 31) { - *day = dayofYear; - return RETURN_OK; - } - *month += 1; - dayofYear -= 31; - if (isLeapYear(year)) { - if (dayofYear <= 29) { - *day = dayofYear; - return RETURN_OK; - } - *month += 1; - dayofYear -= 29; - } else { - if (dayofYear <= 28) { - *day = dayofYear; - return RETURN_OK; - } - *month += 1; - dayofYear -= 28; - } - while (*month <= 12) { - if (dayofYear <= 31) { - *day = dayofYear; - return RETURN_OK; - } - *month += 1; - dayofYear -= 31; + uint8_t* month, uint8_t* day) { + if (isLeapYear(year)) { + if (dayofYear > 366) { + return INVALID_DAY_OF_YEAR; + } + } else { + if (dayofYear > 365) { + return INVALID_DAY_OF_YEAR; + } + } + *month = 1; + if (dayofYear <= 31) { + *day = dayofYear; + return RETURN_OK; + } + *month += 1; + dayofYear -= 31; + if (isLeapYear(year)) { + if (dayofYear <= 29) { + *day = dayofYear; + return RETURN_OK; + } + *month += 1; + dayofYear -= 29; + } else { + if (dayofYear <= 28) { + *day = dayofYear; + return RETURN_OK; + } + *month += 1; + dayofYear -= 28; + } + while (*month <= 12) { + if (dayofYear <= 31) { + *day = dayofYear; + return RETURN_OK; + } + *month += 1; + dayofYear -= 31; - if (*month == 8) { - continue; - } + if (*month == 8) { + continue; + } - if (dayofYear <= 30) { - *day = dayofYear; - return RETURN_OK; - } - *month += 1; - dayofYear -= 30; - } - return INVALID_DAY_OF_YEAR; + if (dayofYear <= 30) { + *day = dayofYear; + return RETURN_OK; + } + *month += 1; + dayofYear -= 30; + } + return INVALID_DAY_OF_YEAR; } bool CCSDSTime::isLeapYear(uint32_t year) { - if ((year % 400) == 0) { - return true; - } - if ((year % 100) == 0) { - return false; - } - if ((year % 4) == 0) { - return true; - } - return false; + if ((year % 400) == 0) { + return true; + } + if ((year % 100) == 0) { + return false; + } + if ((year % 4) == 0) { + return true; + } + return false; } ReturnValue_t CCSDSTime::convertToCcsds(CDS_short* to, const timeval* from) { - to->pField = (CDS << 4) + 0; - uint32_t days = ((from->tv_sec) / SECONDS_PER_DAY) - + DAYS_CCSDS_TO_UNIX_EPOCH; - if (days > (1 << 16)) { - //Date is beyond year 2137 - return TIME_DOES_NOT_FIT_FORMAT; - } - to->dayMSB = (days & 0xFF00) >> 8; - to->dayLSB = (days & 0xFF); - uint32_t msDay = ((from->tv_sec % SECONDS_PER_DAY) * 1000) - + (from->tv_usec / 1000); - to->msDay_hh = (msDay & 0xFF000000) >> 24; - to->msDay_h = (msDay & 0xFF0000) >> 16; - to->msDay_l = (msDay & 0xFF00) >> 8; - to->msDay_ll = (msDay & 0xFF); - return RETURN_OK; + to->pField = (CDS << 4) + 0; + uint32_t days = ((from->tv_sec) / SECONDS_PER_DAY) + + DAYS_CCSDS_TO_UNIX_EPOCH; + if (days > (1 << 16)) { + //Date is beyond year 2137 + return TIME_DOES_NOT_FIT_FORMAT; + } + to->dayMSB = (days & 0xFF00) >> 8; + to->dayLSB = (days & 0xFF); + uint32_t msDay = ((from->tv_sec % SECONDS_PER_DAY) * 1000) + + (from->tv_usec / 1000); + to->msDay_hh = (msDay & 0xFF000000) >> 24; + to->msDay_h = (msDay & 0xFF0000) >> 16; + to->msDay_l = (msDay & 0xFF00) >> 8; + to->msDay_ll = (msDay & 0xFF); + return RETURN_OK; } ReturnValue_t CCSDSTime::convertToCcsds(OBT_FLP* to, const timeval* from) { - to->pFiled = (AGENCY_DEFINED << 4) + 5; - to->seconds_hh = (from->tv_sec >> 24) & 0xff; - to->seconds_h = (from->tv_sec >> 16) & 0xff; - to->seconds_l = (from->tv_sec >> 8) & 0xff; - to->seconds_ll = (from->tv_sec >> 0) & 0xff; + to->pFiled = (AGENCY_DEFINED << 4) + 5; + to->seconds_hh = (from->tv_sec >> 24) & 0xff; + to->seconds_h = (from->tv_sec >> 16) & 0xff; + to->seconds_l = (from->tv_sec >> 8) & 0xff; + to->seconds_ll = (from->tv_sec >> 0) & 0xff; - //convert the µs to 2E-16 seconds - uint64_t temp = from->tv_usec; - temp = temp << 16; - temp = temp / 1E6; + //convert the µs to 2E-16 seconds + uint64_t temp = from->tv_usec; + temp = temp << 16; + temp = temp / 1E6; - to->subsecondsMSB = (temp >> 8) & 0xff; - to->subsecondsLSB = temp & 0xff; + to->subsecondsMSB = (temp >> 8) & 0xff; + to->subsecondsLSB = temp & 0xff; - return RETURN_OK; + return RETURN_OK; } ReturnValue_t CCSDSTime::convertFromCcsds(timeval* to, const uint8_t* from, - size_t* foundLength, size_t maxLength) { - //We don't expect ascii here. SHOULDDO - uint8_t codeIdentification = (*from >> 4); - switch (codeIdentification) { - //unsupported, as Leap second correction would have to be applied -// case CUC_LEVEL1: -// return convertFromCUC(to, from, foundLength, maxLength); - case CDS: - return convertFromCDS(to, from, foundLength, maxLength); - case CCS: - return convertFromCCS(to, from, foundLength, maxLength); - default: - return UNSUPPORTED_TIME_FORMAT; - } + size_t* foundLength, size_t maxLength) { + if(maxLength >= 19) { + Clock::TimeOfDay_t timeOfDay; + /* Try to parse it as ASCII */ + ReturnValue_t result = convertFromASCII(&timeOfDay, from, maxLength); + if (result == RETURN_OK) { + return Clock::convertTimeOfDayToTimeval(&timeOfDay, to); + } + } + + uint8_t codeIdentification = (*from >> 4); + switch (codeIdentification) { + /* Unsupported, as Leap second correction would have to be applied */ + case CUC_LEVEL1: + return UNSUPPORTED_TIME_FORMAT; + case CDS: + return convertFromCDS(to, from, foundLength, maxLength); + case CCS: + return convertFromCCS(to, from, foundLength, maxLength); + default: + return UNSUPPORTED_TIME_FORMAT; + } } ReturnValue_t CCSDSTime::convertFromCUC(timeval* to, const uint8_t* from, - size_t* foundLength, size_t maxLength) { - if (maxLength < 1) { - return INVALID_TIME_FORMAT; - } - uint8_t pField = *from; - from++; - ReturnValue_t result = convertFromCUC(to, pField, from, foundLength, - maxLength - 1); - if (result == HasReturnvaluesIF::RETURN_OK) { - if (foundLength != NULL) { - *foundLength += 1; - } - } - return result; + size_t* foundLength, size_t maxLength) { + if (maxLength < 1) { + return INVALID_TIME_FORMAT; + } + uint8_t pField = *from; + from++; + ReturnValue_t result = convertFromCUC(to, pField, from, foundLength, + maxLength - 1); + if (result == HasReturnvaluesIF::RETURN_OK) { + if (foundLength != NULL) { + *foundLength += 1; + } + } + return result; } ReturnValue_t CCSDSTime::checkTimeOfDay(const Clock::TimeOfDay_t* time) { - if ((time->month > 12) || (time->month == 0)) { - return INVALID_TIME_FORMAT; - } + if ((time->month > 12) || (time->month == 0)) { + return INVALID_TIME_FORMAT; + } - if (time->day == 0) { - return INVALID_TIME_FORMAT; - } - switch (time->month) { - case 2: - if (isLeapYear(time->year)) { - if (time->day > 29) { - return INVALID_TIME_FORMAT; - } - } else { - if (time->day > 28) { - return INVALID_TIME_FORMAT; - } - } - break; - case 4: - case 6: - case 9: - case 11: - if (time->day > 30) { - return INVALID_TIME_FORMAT; - } - break; - default: - if (time->day > 31) { - return INVALID_TIME_FORMAT; - } - break; - } + if (time->day == 0) { + return INVALID_TIME_FORMAT; + } + switch (time->month) { + case 2: + if (isLeapYear(time->year)) { + if (time->day > 29) { + return INVALID_TIME_FORMAT; + } + } else { + if (time->day > 28) { + return INVALID_TIME_FORMAT; + } + } + break; + case 4: + case 6: + case 9: + case 11: + if (time->day > 30) { + return INVALID_TIME_FORMAT; + } + break; + default: + if (time->day > 31) { + return INVALID_TIME_FORMAT; + } + break; + } - if (time->hour > 23) { - return INVALID_TIME_FORMAT; - } + if (time->hour > 23) { + return INVALID_TIME_FORMAT; + } - if (time->minute > 59) { - return INVALID_TIME_FORMAT; - } + if (time->minute > 59) { + return INVALID_TIME_FORMAT; + } - if (time->second > 59) { - return INVALID_TIME_FORMAT; - } + if (time->second > 59) { + return INVALID_TIME_FORMAT; + } - if (time->usecond > 999999) { - return INVALID_TIME_FORMAT; - } + if (time->usecond > 999999) { + return INVALID_TIME_FORMAT; + } - return RETURN_OK; + return RETURN_OK; } ReturnValue_t CCSDSTime::convertTimevalToTimeOfDay(Clock::TimeOfDay_t* to, - timeval* from) { -//This is rather tricky. Implement only if needed. Also, if so, move to OSAL. - return UNSUPPORTED_TIME_FORMAT; + timeval* from) { + //This is rather tricky. Implement only if needed. Also, if so, move to OSAL. + return UNSUPPORTED_TIME_FORMAT; } ReturnValue_t CCSDSTime::convertFromCDS(timeval* to, const uint8_t* from, - size_t* foundLength, size_t maxLength) { - uint8_t pField = *from; - from++; -//Check epoch - if (pField & 0b1000) { - return NOT_ENOUGH_INFORMATION_FOR_TARGET_FORMAT; - } -//Check length - uint8_t expectedLength = 7; //Including p-Field. - bool extendedDays = pField & 0b100; - if (extendedDays) { - expectedLength += 1; - } - if ((pField & 0b11) == 0b01) { - expectedLength += 2; - } else if ((pField & 0b11) == 0b10) { - expectedLength += 4; - } - if (foundLength != NULL) { - *foundLength = expectedLength; - } - if (expectedLength > maxLength) { - return LENGTH_MISMATCH; - } -//Check and count days - uint32_t days = 0; - if (extendedDays) { - days = (from[0] << 16) + (from[1] << 8) + from[2]; - from += 3; - } else { - days = (from[0] << 8) + from[1]; - from += 2; - } -//Move to POSIX epoch. - if (days <= DAYS_CCSDS_TO_UNIX_EPOCH) { - return INVALID_TIME_FORMAT; - } - days -= DAYS_CCSDS_TO_UNIX_EPOCH; - to->tv_sec = days * SECONDS_PER_DAY; - uint32_t msDay = (from[0] << 24) + (from[1] << 16) + (from[2] << 8) - + from[3]; - from += 4; - to->tv_sec += (msDay / 1000); - to->tv_usec = (msDay % 1000) * 1000; - if ((pField & 0b11) == 0b01) { - uint16_t usecs = (from[0] << 16) + from[1]; - from += 2; - if (usecs > 999) { - return INVALID_TIME_FORMAT; - } - to->tv_usec += usecs; - } else if ((pField & 0b11) == 0b10) { - uint32_t picosecs = (from[0] << 24) + (from[1] << 16) + (from[2] << 8) - + from[3]; - from += 4; - if (picosecs > 999999) { - return INVALID_TIME_FORMAT; - } - //Not very useful. - to->tv_usec += (picosecs / 1000); - } - return RETURN_OK; + size_t* foundLength, size_t maxLength) { + uint8_t pField = *from; + from++; + //Check epoch + if (pField & 0b1000) { + return NOT_ENOUGH_INFORMATION_FOR_TARGET_FORMAT; + } + //Check length + uint8_t expectedLength = 7; //Including p-Field. + bool extendedDays = pField & 0b100; + if (extendedDays) { + expectedLength += 1; + } + if ((pField & 0b11) == 0b01) { + expectedLength += 2; + } else if ((pField & 0b11) == 0b10) { + expectedLength += 4; + } + if (foundLength != NULL) { + *foundLength = expectedLength; + } + if (expectedLength > maxLength) { + return LENGTH_MISMATCH; + } + //Check and count days + uint32_t days = 0; + if (extendedDays) { + days = (from[0] << 16) + (from[1] << 8) + from[2]; + from += 3; + } else { + days = (from[0] << 8) + from[1]; + from += 2; + } + //Move to POSIX epoch. + if (days <= DAYS_CCSDS_TO_UNIX_EPOCH) { + return INVALID_TIME_FORMAT; + } + days -= DAYS_CCSDS_TO_UNIX_EPOCH; + to->tv_sec = days * SECONDS_PER_DAY; + uint32_t msDay = (from[0] << 24) + (from[1] << 16) + (from[2] << 8) + + from[3]; + from += 4; + to->tv_sec += (msDay / 1000); + to->tv_usec = (msDay % 1000) * 1000; + if ((pField & 0b11) == 0b01) { + uint16_t usecs = (from[0] << 16) + from[1]; + from += 2; + if (usecs > 999) { + return INVALID_TIME_FORMAT; + } + to->tv_usec += usecs; + } else if ((pField & 0b11) == 0b10) { + uint32_t picosecs = (from[0] << 24) + (from[1] << 16) + (from[2] << 8) + + from[3]; + from += 4; + if (picosecs > 999999) { + return INVALID_TIME_FORMAT; + } + //Not very useful. + to->tv_usec += (picosecs / 1000); + } + return RETURN_OK; } ReturnValue_t CCSDSTime::convertFromCUC(timeval* to, uint8_t pField, - const uint8_t* from, size_t* foundLength, size_t maxLength) { - uint32_t secs = 0; - uint32_t subSeconds = 0; - uint8_t nCoarse = ((pField & 0b1100) >> 2) + 1; - uint8_t nFine = (pField & 0b11); - size_t totalLength = nCoarse + nFine; - if (foundLength != NULL) { - *foundLength = totalLength; - } - if (totalLength > maxLength) { - return LENGTH_MISMATCH; - } - for (int count = 0; count < nCoarse; count++) { - secs += *from << ((nCoarse * 8 - 8) * (1 + count)); - from++; - } - for (int count = 0; count < nFine; count++) { - subSeconds += *from << ((nFine * 8 - 8) * (1 + count)); - from++; - } - //Move to POSIX epoch. - to->tv_sec = secs; - if (pField & 0b10000) { - //CCSDS-Epoch - to->tv_sec -= (DAYS_CCSDS_TO_UNIX_EPOCH * SECONDS_PER_DAY); - } - to->tv_usec = subsecondsToMicroseconds(subSeconds); - return RETURN_OK; + const uint8_t* from, size_t* foundLength, size_t maxLength) { + uint32_t secs = 0; + uint32_t subSeconds = 0; + uint8_t nCoarse = ((pField & 0b1100) >> 2) + 1; + uint8_t nFine = (pField & 0b11); + size_t totalLength = nCoarse + nFine; + if (foundLength != NULL) { + *foundLength = totalLength; + } + if (totalLength > maxLength) { + return LENGTH_MISMATCH; + } + for (int count = 0; count < nCoarse; count++) { + secs += *from << ((nCoarse * 8 - 8) * (1 + count)); + from++; + } + for (int count = 0; count < nFine; count++) { + subSeconds += *from << ((nFine * 8 - 8) * (1 + count)); + from++; + } + //Move to POSIX epoch. + to->tv_sec = secs; + if (pField & 0b10000) { + //CCSDS-Epoch + to->tv_sec -= (DAYS_CCSDS_TO_UNIX_EPOCH * SECONDS_PER_DAY); + } + to->tv_usec = subsecondsToMicroseconds(subSeconds); + return RETURN_OK; } uint32_t CCSDSTime::subsecondsToMicroseconds(uint16_t subseconds) { - uint64_t temp = (uint64_t) subseconds * 1000000 - / (1 << (sizeof(subseconds) * 8)); - return temp; + uint64_t temp = (uint64_t) subseconds * 1000000 + / (1 << (sizeof(subseconds) * 8)); + return temp; } ReturnValue_t CCSDSTime::convertFromCCS(timeval* to, const uint8_t* from, - size_t* foundLength, size_t maxLength) { - Clock::TimeOfDay_t tempTime; - ReturnValue_t result = convertFromCCS(&tempTime, from, foundLength, - maxLength); - if (result != RETURN_OK) { - return result; - } + size_t* foundLength, size_t maxLength) { + Clock::TimeOfDay_t tempTime; + ReturnValue_t result = convertFromCCS(&tempTime, from, foundLength, + maxLength); + if (result != RETURN_OK) { + return result; + } - return Clock::convertTimeOfDayToTimeval(&tempTime, to); + return Clock::convertTimeOfDayToTimeval(&tempTime, to); } diff --git a/timemanager/CCSDSTime.h b/timemanager/CCSDSTime.h index d74adc61..de151852 100644 --- a/timemanager/CCSDSTime.h +++ b/timemanager/CCSDSTime.h @@ -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_ */ diff --git a/unittest/tests/action/TestActionHelper.cpp b/unittest/tests/action/TestActionHelper.cpp index 2493a6b8..a7adfc82 100644 --- a/unittest/tests/action/TestActionHelper.cpp +++ b/unittest/tests/action/TestActionHelper.cpp @@ -70,7 +70,7 @@ TEST_CASE( "Action Helper" , "[ActionHelper]") { SECTION("Handle finish"){ CHECK(not testMqMock.wasMessageSent()); ReturnValue_t status = 0x9876; - actionHelper.finish(testMqMock.getId(), testActionId, status); + actionHelper.finish(true, testMqMock.getId(), testActionId, status); CHECK(testMqMock.wasMessageSent()); CommandMessage testMessage; REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index bb090e3b..5c277850 100644 --- a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h @@ -192,7 +192,7 @@ public: resetSubscriptionList(); ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; { - PoolReadHelper readHelper(&dataset); + PoolReadGuard readHelper(&dataset); if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { status = readHelper.getReadResult(); } @@ -205,7 +205,7 @@ public: } { - PoolReadHelper readHelper(&testUint32); + PoolReadGuard readHelper(&testUint32); if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { status = readHelper.getReadResult(); } @@ -214,7 +214,7 @@ public: } { - PoolReadHelper readHelper(&testInt64Vec); + PoolReadGuard readHelper(&testInt64Vec); if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { status = readHelper.getReadResult(); }