From 9998de086fc2e469dd370b4b36dcb854c5d233d8 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 8 Feb 2021 14:20:36 +0100 Subject: [PATCH 01/59] added printouts for action helper --- action/ActionHelper.cpp | 36 +++++++++++++++++++++++------------- action/ActionHelper.h | 3 ++- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index 23c9d7329..e32be68b2 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -3,6 +3,7 @@ #include "../ipc/MessageQueueSenderIF.h" #include "../objectmanager/ObjectManagerIF.h" +#include "../serviceinterface/ServiceInterface.h" ActionHelper::ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue) : @@ -86,13 +87,20 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, uint8_t *dataPtr; size_t maxSize = data->getSerializedSize(); if (maxSize == 0) { - //No error, there's simply nothing to report. + /* No error, there's simply nothing to report. */ return HasReturnvaluesIF::RETURN_OK; } size_t size = 0; ReturnValue_t result = ipcStore->getFreeElement(&storeAddress, maxSize, &dataPtr); if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "ActionHelper::reportData: Getting free element from IPC store failed!" << + std::endl; +#else + sif::printWarning("ActionHelper::reportData: Getting free element from IPC " + "store failed!\n"); +#endif return result; } result = data->serialize(&dataPtr, &size, maxSize, @@ -101,14 +109,13 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, ipcStore->deleteData(storeAddress); return result; } - // We don't need to report the objectId, as we receive REQUESTED data - // before the completion success message. - // True aperiodic replies need to be reported with - // another dedicated message. + + /* We don't need to report the objectId, as we receive REQUESTED data before the completion + success message. True aperiodic replies need to be reported with another dedicated message. */ ActionMessage::setDataReply(&reply, replyId, storeAddress); - // If the sender needs to be hidden, for example to handle packet - // as unrequested reply, this will be done here. + /* If the sender needs to be hidden, for example to handle packet + as unrequested reply, this will be done here. */ if (hideSender) { result = MessageQueueSenderIF::sendMessage(reportTo, &reply); } @@ -132,6 +139,11 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, store_address_t storeAddress; ReturnValue_t result = ipcStore->addData(&storeAddress, data, dataSize); if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "ActionHelper::reportData: Adding data to IPC store failed!" << std::endl; +#else + sif::printWarning("ActionHelper::reportData: Adding data to IPC store failed!\n"); +#endif return result; } @@ -140,14 +152,12 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, return result; } - // We don't need to report the objectId, as we receive REQUESTED data - // before the completion success message. - // True aperiodic replies need to be reported with - // another dedicated message. + /* We don't need to report the objectId, as we receive REQUESTED data before the completion + success message. True aperiodic replies need to be reported with another dedicated message. */ ActionMessage::setDataReply(&reply, replyId, storeAddress); - // If the sender needs to be hidden, for example to handle packet - // as unrequested reply, this will be done here. + /* If the sender needs to be hidden, for example to handle packet + as unrequested reply, this will be done here. */ if (hideSender) { result = MessageQueueSenderIF::sendMessage(reportTo, &reply); } diff --git a/action/ActionHelper.h b/action/ActionHelper.h index ae971d382..35ac41d1c 100644 --- a/action/ActionHelper.h +++ b/action/ActionHelper.h @@ -101,7 +101,8 @@ public: protected: //! Increase of value of this per step static const uint8_t STEP_OFFSET = 1; - HasActionsIF* owner;//!< Pointer to the owner + //! Pointer to the owner + HasActionsIF* owner; //! Queue to be used as response sender, has to be set in ctor or with //! setQueueToUse MessageQueueIF* queueToUse; From f3cc664d4f9fdadc2f6e2ebf814dd0c924e5ae33 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 23 Feb 2021 22:07:32 +0100 Subject: [PATCH 02/59] small printout tweak --- devicehandlers/DeviceHandlerBase.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 35d34bf9b..2471a5a40 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -1494,10 +1494,9 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType, if(errorType == sif::OutputTypes::OUT_WARNING) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "DeviceHandlerBase::" << functionName << ": Object ID " - << std::hex << std::setw(8) << std::setfill('0') - << this->getObjectId() << " | " << errorPrint << std::dec - << std::setfill(' ') << std::endl; + sif::warning << "DeviceHandlerBase::" << functionName << ": Object ID 0x" << std::hex << + std::setw(8) << std::setfill('0') << this->getObjectId() << " | " << errorPrint << + std::dec << std::setfill(' ') << std::endl; #else sif::printWarning("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n", this->getObjectId(), errorPrint); From 92f249dc62cf02a5052fee4e9877bc89b2be1ab5 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 24 Feb 2021 00:23:48 +0100 Subject: [PATCH 03/59] zero size handling --- globalfunctions/arrayprinter.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/globalfunctions/arrayprinter.cpp b/globalfunctions/arrayprinter.cpp index 7dc056b08..1404035ff 100644 --- a/globalfunctions/arrayprinter.cpp +++ b/globalfunctions/arrayprinter.cpp @@ -5,6 +5,15 @@ void arrayprinter::print(const uint8_t *data, size_t size, OutputType type, bool printInfo, size_t maxCharPerLine) { + if(size == 0) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "Size is zero, nothing to print" << std::endl; +#else + sif::printInfo("Size is zero, nothing to print\n"); +#endif + return; + } + #if FSFW_CPP_OSTREAM_ENABLED == 1 if(printInfo) { sif::info << "Printing data with size " << size << ": " << std::endl; From c5ee2260d14008c2afd7f260ee86240011efb156 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 27 Feb 2021 12:59:37 +0100 Subject: [PATCH 04/59] renamed abstract function, removed plural --- datapoollocal/LocalDataPoolManager.cpp | 2 +- datapoollocal/LocalDataPoolManager.h | 2 +- datapoollocal/ProvidesDataPoolSubscriptionIF.h | 2 +- unittest/tests/datapoollocal/LocalPoolOwnerBase.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index 447691632..01c5bb9aa 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -380,7 +380,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(sid_t sid, } -ReturnValue_t LocalDataPoolManager::subscribeForUpdatePackets(sid_t sid, +ReturnValue_t LocalDataPoolManager::subscribeForUpdatePacket(sid_t sid, bool isDiagnostics, bool reportingEnabled, object_id_t packetDestination) { AcceptsHkPacketsIF* hkReceiverObject = diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h index 05c6adfd6..a53e45b58 100644 --- a/datapoollocal/LocalDataPoolManager.h +++ b/datapoollocal/LocalDataPoolManager.h @@ -137,7 +137,7 @@ public: * @param packetDestination * @return */ - ReturnValue_t subscribeForUpdatePackets(sid_t sid, bool reportingEnabled, + ReturnValue_t subscribeForUpdatePacket(sid_t sid, bool reportingEnabled, bool isDiagnostics, object_id_t packetDestination = defaultHkDestination) override; diff --git a/datapoollocal/ProvidesDataPoolSubscriptionIF.h b/datapoollocal/ProvidesDataPoolSubscriptionIF.h index ead479cc2..642e8ee1d 100644 --- a/datapoollocal/ProvidesDataPoolSubscriptionIF.h +++ b/datapoollocal/ProvidesDataPoolSubscriptionIF.h @@ -33,7 +33,7 @@ public: * @param packetDestination * @return */ - virtual ReturnValue_t subscribeForUpdatePackets(sid_t sid, + virtual ReturnValue_t subscribeForUpdatePacket(sid_t sid, bool reportingEnabled, bool isDiagnostics, object_id_t packetDestination) = 0; diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index 876ae5f5b..68bccbb81 100644 --- a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h @@ -174,7 +174,7 @@ public: } ReturnValue_t subscribeWrapperSetUpdateHk(bool diagnostics = false) { - return poolManager.subscribeForUpdatePackets(lpool::testSid, diagnostics, + return poolManager.subscribeForUpdatePacket(lpool::testSid, diagnostics, false, objects::HK_RECEIVER_MOCK); } From f45d19a9619ad787ee00efd6f46934f67b4785f0 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 27 Feb 2021 13:06:55 +0100 Subject: [PATCH 05/59] better documentation --- housekeeping/HousekeepingPacketDownlink.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/housekeeping/HousekeepingPacketDownlink.h b/housekeeping/HousekeepingPacketDownlink.h index ae0cc988d..aeea16c3c 100644 --- a/housekeeping/HousekeepingPacketDownlink.h +++ b/housekeeping/HousekeepingPacketDownlink.h @@ -10,7 +10,11 @@ * which are destined to be downlinked into the store. * @details * The housekeeping packets are stored into the IPC store and forwarded - * to the designated housekeeping handler. + * to the designated housekeeping handler. The packet will consist of the following fields + * - SID (8 byte): Structure ID, with the first 4 bytes being the object ID and the last four + * bytes being the set ID + * - Housekeeping Data: The rest of the packet will be the serialized housekeeping data. A validity + * buffer might be appended at the end, depending on the set configuration. */ class HousekeepingPacketDownlink: public SerialLinkedListAdapter { public: From 788dbe4eca1bd8f2daa212ec14af95a6b333b4d9 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 27 Feb 2021 13:56:48 +0100 Subject: [PATCH 06/59] removed plural --- datapoollocal/LocalDataPoolManager.cpp | 4 ++-- datapoollocal/LocalDataPoolManager.h | 4 ++-- datapoollocal/ProvidesDataPoolSubscriptionIF.h | 4 ++-- unittest/tests/datapoollocal/LocalPoolManagerTest.cpp | 2 +- unittest/tests/datapoollocal/LocalPoolOwnerBase.h | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index 01c5bb9aa..19e235881 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -410,7 +410,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForUpdatePacket(sid_t sid, return HasReturnvaluesIF::RETURN_OK; } -ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessages( +ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessage( const uint32_t setId, object_id_t destinationObject, MessageQueueId_t targetQueueId, bool generateSnapshot) { struct HkReceiver hkReceiver; @@ -431,7 +431,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessages( return HasReturnvaluesIF::RETURN_OK; } -ReturnValue_t LocalDataPoolManager::subscribeForVariableUpdateMessages( +ReturnValue_t LocalDataPoolManager::subscribeForVariableUpdateMessage( const lp_id_t localPoolId, object_id_t destinationObject, MessageQueueId_t targetQueueId, bool generateSnapshot) { struct HkReceiver hkReceiver; diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h index a53e45b58..fdfd9d233 100644 --- a/datapoollocal/LocalDataPoolManager.h +++ b/datapoollocal/LocalDataPoolManager.h @@ -155,7 +155,7 @@ public: * Otherwise, only an notification message is sent. * @return */ - ReturnValue_t subscribeForSetUpdateMessages(const uint32_t setId, + ReturnValue_t subscribeForSetUpdateMessage(const uint32_t setId, object_id_t destinationObject, MessageQueueId_t targetQueueId, bool generateSnapshot) override; @@ -174,7 +174,7 @@ public: * Otherwise, only an notification message is sent. * @return */ - ReturnValue_t subscribeForVariableUpdateMessages(const lp_id_t localPoolId, + ReturnValue_t subscribeForVariableUpdateMessage(const lp_id_t localPoolId, object_id_t destinationObject, MessageQueueId_t targetQueueId, bool generateSnapshot) override; diff --git a/datapoollocal/ProvidesDataPoolSubscriptionIF.h b/datapoollocal/ProvidesDataPoolSubscriptionIF.h index 642e8ee1d..3394c98bd 100644 --- a/datapoollocal/ProvidesDataPoolSubscriptionIF.h +++ b/datapoollocal/ProvidesDataPoolSubscriptionIF.h @@ -52,7 +52,7 @@ public: * Otherwise, only an notification message is sent. * @return */ - virtual ReturnValue_t subscribeForSetUpdateMessages(const uint32_t setId, + virtual ReturnValue_t subscribeForSetUpdateMessage(const uint32_t setId, object_id_t destinationObject, MessageQueueId_t targetQueueId, bool generateSnapshot) = 0; @@ -70,7 +70,7 @@ public: * only an notification message is sent. * @return */ - virtual ReturnValue_t subscribeForVariableUpdateMessages( + virtual ReturnValue_t subscribeForVariableUpdateMessage( const lp_id_t localPoolId, object_id_t destinationObject, MessageQueueId_t targetQueueId, diff --git a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp index c8b10442a..e621f4d87 100644 --- a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp @@ -160,7 +160,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { CHECK(messageSent.getCommand() == static_cast( HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE)); /* Now subscribe for the dataset update (HK and update) again with subscription interface */ - REQUIRE(subscriptionIF->subscribeForSetUpdateMessages(lpool::testSetId, + REQUIRE(subscriptionIF->subscribeForSetUpdateMessage(lpool::testSetId, objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, false) == retval::CATCH_OK); REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK); diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index 68bccbb81..acbb2b736 100644 --- a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h @@ -164,12 +164,12 @@ public: } ReturnValue_t subscribeWrapperSetUpdate() { - return poolManager.subscribeForSetUpdateMessages(lpool::testSetId, + return poolManager.subscribeForSetUpdateMessage(lpool::testSetId, objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, false); } ReturnValue_t subscribeWrapperSetUpdateSnapshot() { - return poolManager.subscribeForSetUpdateMessages(lpool::testSetId, + return poolManager.subscribeForSetUpdateMessage(lpool::testSetId, objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, true); } @@ -179,7 +179,7 @@ public: } ReturnValue_t subscribeWrapperVariableUpdate(lp_id_t localPoolId) { - return poolManager.subscribeForVariableUpdateMessages(localPoolId, + return poolManager.subscribeForVariableUpdateMessage(localPoolId, MessageQueueIF::NO_QUEUE, objects::HK_RECEIVER_MOCK, false); } From 110159eea1dfcbe74d9a91f6d8c2546d3a3b5019 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 27 Feb 2021 13:57:58 +0100 Subject: [PATCH 07/59] formatting --- .../ProvidesDataPoolSubscriptionIF.h | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/datapoollocal/ProvidesDataPoolSubscriptionIF.h b/datapoollocal/ProvidesDataPoolSubscriptionIF.h index 3394c98bd..1c34099ac 100644 --- a/datapoollocal/ProvidesDataPoolSubscriptionIF.h +++ b/datapoollocal/ProvidesDataPoolSubscriptionIF.h @@ -17,12 +17,8 @@ public: * to generate housekeeping packets which are downlinked directly. * @return */ - virtual ReturnValue_t subscribeForPeriodicPacket(sid_t sid, - bool enableReporting, - float collectionInterval, bool isDiagnostics, - object_id_t packetDestination) = 0; - - + virtual ReturnValue_t subscribeForPeriodicPacket(sid_t sid, bool enableReporting, + float collectionInterval, bool isDiagnostics, object_id_t packetDestination) = 0; /** * @brief Subscribe for the generation of packets if the dataset * is marked as changed. @@ -33,11 +29,8 @@ public: * @param packetDestination * @return */ - virtual ReturnValue_t subscribeForUpdatePacket(sid_t sid, - bool reportingEnabled, - bool isDiagnostics, - object_id_t packetDestination) = 0; - + virtual ReturnValue_t subscribeForUpdatePacket(sid_t sid, bool reportingEnabled, + bool isDiagnostics, object_id_t packetDestination) = 0; /** * @brief Subscribe for a notification message which will be sent * if a dataset has changed. @@ -55,7 +48,6 @@ public: virtual ReturnValue_t subscribeForSetUpdateMessage(const uint32_t setId, object_id_t destinationObject, MessageQueueId_t targetQueueId, bool generateSnapshot) = 0; - /** * @brief Subscribe for an notification message which will be sent if a * pool variable has changed. @@ -70,12 +62,9 @@ public: * only an notification message is sent. * @return */ - virtual ReturnValue_t subscribeForVariableUpdateMessage( - const lp_id_t localPoolId, - object_id_t destinationObject, - MessageQueueId_t targetQueueId, + virtual ReturnValue_t subscribeForVariableUpdateMessage(const lp_id_t localPoolId, + object_id_t destinationObject, MessageQueueId_t targetQueueId, bool generateSnapshot) = 0; - }; #endif /* FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ */ From ea6ee7e79c546454ae60ef6c498a42bf5ef761ac Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 27 Feb 2021 14:08:30 +0100 Subject: [PATCH 08/59] added instructions on how to retrieve the interface --- datapoollocal/HasLocalDataPoolIF.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/datapoollocal/HasLocalDataPoolIF.h b/datapoollocal/HasLocalDataPoolIF.h index 055701750..3c31c5cd8 100644 --- a/datapoollocal/HasLocalDataPoolIF.h +++ b/datapoollocal/HasLocalDataPoolIF.h @@ -23,11 +23,20 @@ class LocalPoolObjectBase; * @details * Any class implementing this interface shall also have a LocalDataPoolManager member class which * contains the actual pool data structure and exposes the public interface for it. + * * The local data pool can be accessed using helper classes by using the * LocalPoolVariable, LocalPoolVector or LocalDataSet classes. Every local pool variable can * be uniquely identified by a global pool ID (gp_id_t) and every dataset tied * to a pool manager can be uniqely identified by a global structure ID (sid_t). * + * All software objects which want to use the local pool of another object shall also use this + * interface, for example to get a handle to the subscription interface. + * For example, the following line of code can be used to retrieve the interface + * + * HasLocalDataPoolIF* poolIF = objectManager->get(objects::SOME_OBJECT); + * if(poolIF != nullptr) { + * doSomething() + * } */ class HasLocalDataPoolIF { friend class HasLocalDpIFManagerAttorney; From fcff06c83fe6d8c0bcd864b9276b039fe6845f04 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 27 Feb 2021 14:09:44 +0100 Subject: [PATCH 09/59] some more details --- datapoollocal/HasLocalDataPoolIF.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/datapoollocal/HasLocalDataPoolIF.h b/datapoollocal/HasLocalDataPoolIF.h index 3c31c5cd8..49f2db7d3 100644 --- a/datapoollocal/HasLocalDataPoolIF.h +++ b/datapoollocal/HasLocalDataPoolIF.h @@ -30,7 +30,8 @@ class LocalPoolObjectBase; * to a pool manager can be uniqely identified by a global structure ID (sid_t). * * All software objects which want to use the local pool of another object shall also use this - * interface, for example to get a handle to the subscription interface. + * interface, for example to get a handle to the subscription interface. The interface + * can be retrieved using the object manager, provided the target object is a SystemObject. * For example, the following line of code can be used to retrieve the interface * * HasLocalDataPoolIF* poolIF = objectManager->get(objects::SOME_OBJECT); From a65211be51be8d2c998396444649edb4eeef1719 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 13:48:53 +0100 Subject: [PATCH 10/59] new attorney for ReadCommitIF --- datapool/PoolDataSetBase.cpp | 315 ++++++++++++++++---------------- datapool/PoolDataSetBase.h | 241 ++++++++++++------------ datapool/PoolVariableIF.h | 75 ++++---- datapool/ReadCommitIF.h | 8 +- datapool/ReadCommitIFAttorney.h | 32 ++++ 5 files changed, 354 insertions(+), 317 deletions(-) create mode 100644 datapool/ReadCommitIFAttorney.h diff --git a/datapool/PoolDataSetBase.cpp b/datapool/PoolDataSetBase.cpp index 2fd519661..1474aff25 100644 --- a/datapool/PoolDataSetBase.cpp +++ b/datapool/PoolDataSetBase.cpp @@ -1,5 +1,8 @@ #include "PoolDataSetBase.h" +#include "ReadCommitIFAttorney.h" + #include "../serviceinterface/ServiceInterfaceStream.h" + #include PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray, @@ -12,61 +15,61 @@ PoolDataSetBase::~PoolDataSetBase() {} ReturnValue_t PoolDataSetBase::registerVariable( - PoolVariableIF *variable) { - if (state != States::STATE_SET_UNINITIALISED) { + PoolVariableIF *variable) { + if (state != States::STATE_SET_UNINITIALISED) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "DataSet::registerVariable: " - "Call made in wrong position." << std::endl; + sif::error << "DataSet::registerVariable: " + "Call made in wrong position." << std::endl; #endif - return DataSetIF::DATA_SET_UNINITIALISED; - } - if (variable == nullptr) { + return DataSetIF::DATA_SET_UNINITIALISED; + } + if (variable == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "DataSet::registerVariable: " - "Pool variable is nullptr." << std::endl; + sif::error << "DataSet::registerVariable: " + "Pool variable is nullptr." << std::endl; #endif - return DataSetIF::POOL_VAR_NULL; - } - if (fillCount >= maxFillCount) { + return DataSetIF::POOL_VAR_NULL; + } + if (fillCount >= maxFillCount) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "DataSet::registerVariable: " - "DataSet is full." << std::endl; + sif::error << "DataSet::registerVariable: " + "DataSet is full." << std::endl; #endif - return DataSetIF::DATA_SET_FULL; - } - registeredVariables[fillCount] = variable; - fillCount++; - return HasReturnvaluesIF::RETURN_OK; + return DataSetIF::DATA_SET_FULL; + } + registeredVariables[fillCount] = variable; + fillCount++; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t PoolDataSetBase::read(MutexIF::TimeoutType timeoutType, - uint32_t lockTimeout) { - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; - ReturnValue_t error = result; - if (state == States::STATE_SET_UNINITIALISED) { - lockDataPool(timeoutType, lockTimeout); - for (uint16_t count = 0; count < fillCount; count++) { - result = readVariable(count); - if(result != RETURN_OK) { - error = result; - } - } - state = States::STATE_SET_WAS_READ; - unlockDataPool(); - } - else { + uint32_t lockTimeout) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + ReturnValue_t error = result; + if (state == States::STATE_SET_UNINITIALISED) { + lockDataPool(timeoutType, lockTimeout); + for (uint16_t count = 0; count < fillCount; count++) { + result = readVariable(count); + if(result != RETURN_OK) { + error = result; + } + } + state = States::STATE_SET_WAS_READ; + unlockDataPool(); + } + else { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "DataSet::read(): " - "Call made in wrong position. Don't forget to commit" - " member datasets!" << std::endl; + sif::error << "DataSet::read(): " + "Call made in wrong position. Don't forget to commit" + " member datasets!" << std::endl; #endif - result = SET_WAS_ALREADY_READ; - } + result = SET_WAS_ALREADY_READ; + } - if(error != HasReturnvaluesIF::RETURN_OK) { - result = error; - } - return result; + if(error != HasReturnvaluesIF::RETURN_OK) { + result = error; + } + return result; } uint16_t PoolDataSetBase::getFillCount() const { @@ -74,144 +77,144 @@ uint16_t PoolDataSetBase::getFillCount() const { } ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) { - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; - if(registeredVariables[count] == nullptr) { - // configuration error. - return HasReturnvaluesIF::RETURN_FAILED; - } + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + if(registeredVariables[count] == nullptr) { + /* Configuration error. */ + return HasReturnvaluesIF::RETURN_FAILED; + } - // These checks are often performed by the respective - // variable implementation too, but I guess a double check does not hurt. - if (registeredVariables[count]->getReadWriteMode() != - PoolVariableIF::VAR_WRITE and - registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) - { - if(protectEveryReadCommitCall) { - result = registeredVariables[count]->read( - timeoutTypeForSingleVars, - mutexTimeoutForSingleVars); - } - else { - result = registeredVariables[count]->readWithoutLock(); - } + /* These checks are often performed by the respective variable implementation too, but I guess + a double check does not hurt. */ + if (registeredVariables[count]->getReadWriteMode() != + PoolVariableIF::VAR_WRITE and + registeredVariables[count]->getDataPoolId() + != PoolVariableIF::NO_PARAMETER) + { + if(protectEveryReadCommitCall) { + result = registeredVariables[count]->read( + timeoutTypeForSingleVars, + mutexTimeoutForSingleVars); + } + else { + result = ReadCommitIFAttorney::readWithoutLock(registeredVariables[count]); + } - if(result != HasReturnvaluesIF::RETURN_OK) { - result = INVALID_PARAMETER_DEFINITION; - } - } - return result; + if(result != HasReturnvaluesIF::RETURN_OK) { + result = INVALID_PARAMETER_DEFINITION; + } + } + return result; } ReturnValue_t PoolDataSetBase::commit(MutexIF::TimeoutType timeoutType, - uint32_t lockTimeout) { - if (state == States::STATE_SET_WAS_READ) { - handleAlreadyReadDatasetCommit(timeoutType, lockTimeout); - return HasReturnvaluesIF::RETURN_OK; - } - else { - return handleUnreadDatasetCommit(timeoutType, lockTimeout); - } + uint32_t lockTimeout) { + if (state == States::STATE_SET_WAS_READ) { + handleAlreadyReadDatasetCommit(timeoutType, lockTimeout); + return HasReturnvaluesIF::RETURN_OK; + } + else { + return handleUnreadDatasetCommit(timeoutType, lockTimeout); + } } void PoolDataSetBase::handleAlreadyReadDatasetCommit( - MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) { - lockDataPool(timeoutType, lockTimeout); - for (uint16_t count = 0; count < fillCount; count++) { - if (registeredVariables[count]->getReadWriteMode() - != PoolVariableIF::VAR_READ - && registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - if(protectEveryReadCommitCall) { - registeredVariables[count]->commit( - timeoutTypeForSingleVars, - mutexTimeoutForSingleVars); - } - else { - registeredVariables[count]->commitWithoutLock(); - } - } - } - state = States::STATE_SET_UNINITIALISED; - unlockDataPool(); + MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) { + lockDataPool(timeoutType, lockTimeout); + for (uint16_t count = 0; count < fillCount; count++) { + if (registeredVariables[count]->getReadWriteMode() + != PoolVariableIF::VAR_READ + && registeredVariables[count]->getDataPoolId() + != PoolVariableIF::NO_PARAMETER) { + if(protectEveryReadCommitCall) { + registeredVariables[count]->commit( + timeoutTypeForSingleVars, + mutexTimeoutForSingleVars); + } + else { + ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]); + } + } + } + state = States::STATE_SET_UNINITIALISED; + unlockDataPool(); } ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit( - MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) { - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; - lockDataPool(timeoutType, lockTimeout); - for (uint16_t count = 0; count < fillCount; count++) { - if (registeredVariables[count]->getReadWriteMode() - == PoolVariableIF::VAR_WRITE - && registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - if(protectEveryReadCommitCall) { - result = registeredVariables[count]->commit( - timeoutTypeForSingleVars, - mutexTimeoutForSingleVars); - } - else { - result = registeredVariables[count]->commitWithoutLock(); - } + MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + lockDataPool(timeoutType, lockTimeout); + for (uint16_t count = 0; count < fillCount; count++) { + if (registeredVariables[count]->getReadWriteMode() + == PoolVariableIF::VAR_WRITE + && registeredVariables[count]->getDataPoolId() + != PoolVariableIF::NO_PARAMETER) { + if(protectEveryReadCommitCall) { + result = registeredVariables[count]->commit( + timeoutTypeForSingleVars, + mutexTimeoutForSingleVars); + } + else { + result = registeredVariables[count]->commitWithoutLock(); + } - } else if (registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - if (result != COMMITING_WITHOUT_READING) { + } else if (registeredVariables[count]->getDataPoolId() + != PoolVariableIF::NO_PARAMETER) { + if (result != COMMITING_WITHOUT_READING) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "DataSet::commit(): commit-without-read call made " - "with non write-only variable." << std::endl; + sif::error << "DataSet::commit(): commit-without-read call made " + "with non write-only variable." << std::endl; #endif - result = COMMITING_WITHOUT_READING; - } - } - } - state = States::STATE_SET_UNINITIALISED; - unlockDataPool(); - return result; + result = COMMITING_WITHOUT_READING; + } + } + } + state = States::STATE_SET_UNINITIALISED; + unlockDataPool(); + return result; } ReturnValue_t PoolDataSetBase::lockDataPool(MutexIF::TimeoutType timeoutType, - uint32_t lockTimeout) { - return HasReturnvaluesIF::RETURN_OK; + uint32_t lockTimeout) { + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t PoolDataSetBase::unlockDataPool() { - return HasReturnvaluesIF::RETURN_OK; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, - const size_t maxSize, SerializeIF::Endianness streamEndianness) const { - ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; - for (uint16_t count = 0; count < fillCount; count++) { - result = registeredVariables[count]->serialize(buffer, size, maxSize, - streamEndianness); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - } - return result; + const size_t maxSize, SerializeIF::Endianness streamEndianness) const { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t count = 0; count < fillCount; count++) { + result = registeredVariables[count]->serialize(buffer, size, maxSize, + streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return result; } ReturnValue_t PoolDataSetBase::deSerialize(const uint8_t** buffer, size_t* size, SerializeIF::Endianness streamEndianness) { - ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; - for (uint16_t count = 0; count < fillCount; count++) { - result = registeredVariables[count]->deSerialize(buffer, size, - streamEndianness); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - } - return result; + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t count = 0; count < fillCount; count++) { + result = registeredVariables[count]->deSerialize(buffer, size, + streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return result; } size_t PoolDataSetBase::getSerializedSize() const { - uint32_t size = 0; - for (uint16_t count = 0; count < fillCount; count++) { - size += registeredVariables[count]->getSerializedSize(); - } - return size; + uint32_t size = 0; + for (uint16_t count = 0; count < fillCount; count++) { + size += registeredVariables[count]->getSerializedSize(); + } + return size; } void PoolDataSetBase::setContainer(PoolVariableIF **variablesContainer) { @@ -219,13 +222,13 @@ void PoolDataSetBase::setContainer(PoolVariableIF **variablesContainer) { } PoolVariableIF** PoolDataSetBase::getContainer() const { - return registeredVariables; + return registeredVariables; } void PoolDataSetBase::setReadCommitProtectionBehaviour( - bool protectEveryReadCommit, MutexIF::TimeoutType timeoutType, - uint32_t mutexTimeout) { - this->protectEveryReadCommitCall = protectEveryReadCommit; - this->timeoutTypeForSingleVars = timeoutType; - this->mutexTimeoutForSingleVars = mutexTimeout; + bool protectEveryReadCommit, MutexIF::TimeoutType timeoutType, + uint32_t mutexTimeout) { + this->protectEveryReadCommitCall = protectEveryReadCommit; + this->timeoutTypeForSingleVars = timeoutType; + this->mutexTimeoutForSingleVars = mutexTimeout; } diff --git a/datapool/PoolDataSetBase.h b/datapool/PoolDataSetBase.h index ab8954557..043c860a7 100644 --- a/datapool/PoolDataSetBase.h +++ b/datapool/PoolDataSetBase.h @@ -29,98 +29,99 @@ * @author Bastian Baetz * @ingroup data_pool */ -class PoolDataSetBase: public PoolDataSetIF, - public SerializeIF, - public HasReturnvaluesIF { +class PoolDataSetBase: + public PoolDataSetIF, + public SerializeIF, + public HasReturnvaluesIF { public: - /** - * @brief Creates an empty dataset. Use registerVariable or - * supply a pointer to this dataset to PoolVariable - * initializations to register pool variables. - */ - PoolDataSetBase(PoolVariableIF** registeredVariablesArray, const size_t maxFillCount); + /** + * @brief Creates an empty dataset. Use registerVariable or + * supply a pointer to this dataset to PoolVariable + * initializations to register pool variables. + */ + PoolDataSetBase(PoolVariableIF** registeredVariablesArray, const size_t maxFillCount); - /* Forbidden for now */ - PoolDataSetBase(const PoolDataSetBase& otherSet) = delete; - const PoolDataSetBase& operator=(const PoolDataSetBase& otherSet) = delete; + /* Forbidden for now */ + PoolDataSetBase(const PoolDataSetBase& otherSet) = delete; + const PoolDataSetBase& operator=(const PoolDataSetBase& otherSet) = delete; - virtual~ PoolDataSetBase(); + virtual~ PoolDataSetBase(); - /** - * @brief The read call initializes reading out all registered variables. - * It is mandatory to call commit after every read call! - * @details - * It iterates through the list of registered variables and calls all read() - * functions of the registered pool variables (which read out their values - * from the data pool) which are not write-only. - * In case of an error (e.g. a wrong data type, or an invalid data pool id), - * the operation is aborted and @c INVALID_PARAMETER_DEFINITION returned. - * - * The data pool is locked during the whole read operation and - * freed afterwards. It is mandatory to call commit after a read call, - * even if the read operation is not successful! - * @return - * - @c RETURN_OK if all variables were read successfully. - * - @c INVALID_PARAMETER_DEFINITION if a pool entry does not exist or there - * is a type conflict. - * - @c SET_WAS_ALREADY_READ if read() is called twice without calling - * commit() in between - */ - virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, - uint32_t lockTimeout = 20) override; - /** - * @brief The commit call initializes writing back the registered variables. - * @details - * It iterates through the list of registered variables and calls the - * commit() method of the remaining registered variables (which write back - * their values to the pool). - * - * The data pool is locked during the whole commit operation and - * freed afterwards. The state changes to "was committed" after this operation. - * - * If the set does contain at least one variable which is not write-only - * commit() can only be called after read(). If the set only contains - * variables which are write only, commit() can be called without a - * preceding read() call. Every read call must be followed by a commit call! - * @return - @c RETURN_OK if all variables were read successfully. - * - @c COMMITING_WITHOUT_READING if set was not read yet and - * contains non write-only variables - */ - virtual ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, - uint32_t lockTimeout = 20) override; + /** + * @brief The read call initializes reading out all registered variables. + * It is mandatory to call commit after every read call! + * @details + * It iterates through the list of registered variables and calls all read() + * functions of the registered pool variables (which read out their values + * from the data pool) which are not write-only. + * In case of an error (e.g. a wrong data type, or an invalid data pool id), + * the operation is aborted and @c INVALID_PARAMETER_DEFINITION returned. + * + * The data pool is locked during the whole read operation and + * freed afterwards. It is mandatory to call commit after a read call, + * even if the read operation is not successful! + * @return + * - @c RETURN_OK if all variables were read successfully. + * - @c INVALID_PARAMETER_DEFINITION if a pool entry does not exist or there + * is a type conflict. + * - @c SET_WAS_ALREADY_READ if read() is called twice without calling + * commit() in between + */ + virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t lockTimeout = 20) override; + /** + * @brief The commit call initializes writing back the registered variables. + * @details + * It iterates through the list of registered variables and calls the + * commit() method of the remaining registered variables (which write back + * their values to the pool). + * + * The data pool is locked during the whole commit operation and + * freed afterwards. The state changes to "was committed" after this operation. + * + * If the set does contain at least one variable which is not write-only + * commit() can only be called after read(). If the set only contains + * variables which are write only, commit() can be called without a + * preceding read() call. Every read call must be followed by a commit call! + * @return - @c RETURN_OK if all variables were read successfully. + * - @c COMMITING_WITHOUT_READING if set was not read yet and + * contains non write-only variables + */ + virtual ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t lockTimeout = 20) override; - /** - * Register the passed pool variable instance into the data set. - * @param variable - * @return - */ - virtual ReturnValue_t registerVariable( PoolVariableIF* variable) override; + /** + * Register the passed pool variable instance into the data set. + * @param variable + * @return + */ + virtual ReturnValue_t registerVariable( PoolVariableIF* variable) override; - /** - * Provides the means to lock the underlying data structure to ensure - * thread-safety. Default implementation is empty - * @return Always returns -@c RETURN_OK - */ - virtual ReturnValue_t lockDataPool( - MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, - uint32_t timeoutMs = 20) override; - /** - * Provides the means to unlock the underlying data structure to ensure - * thread-safety. Default implementation is empty - * @return Always returns -@c RETURN_OK - */ - virtual ReturnValue_t unlockDataPool() override; + /** + * Provides the means to lock the underlying data structure to ensure + * thread-safety. Default implementation is empty + * @return Always returns -@c RETURN_OK + */ + virtual ReturnValue_t lockDataPool( + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20) override; + /** + * Provides the means to unlock the underlying data structure to ensure + * thread-safety. Default implementation is empty + * @return Always returns -@c RETURN_OK + */ + virtual ReturnValue_t unlockDataPool() override; - virtual uint16_t getFillCount() const; + virtual uint16_t getFillCount() const; - /* SerializeIF implementations */ - virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, - const size_t maxSize, - SerializeIF::Endianness streamEndianness) const override; - virtual size_t getSerializedSize() const override; - virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, - SerializeIF::Endianness streamEndianness) override; + /* SerializeIF implementations */ + virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, + const size_t maxSize, + SerializeIF::Endianness streamEndianness) const override; + virtual size_t getSerializedSize() const override; + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) override; /** * Can be used to individually protect every read and commit call. @@ -132,48 +133,48 @@ public: uint32_t mutexTimeout = 20); protected: - /** - * @brief The fill_count attribute ensures that the variables - * register in the correct array position and that the maximum - * number of variables is not exceeded. - */ - uint16_t fillCount = 0; - /** - * States of the seet. - */ - enum class States { - STATE_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED - STATE_SET_WAS_READ //!< DATA_SET_WAS_READ - }; - /** - * @brief state manages the internal state of the data set, - * which is important e.g. for the behavior on destruction. - */ - States state = States::STATE_SET_UNINITIALISED; + /** + * @brief The fill_count attribute ensures that the variables + * register in the correct array position and that the maximum + * number of variables is not exceeded. + */ + uint16_t fillCount = 0; + /** + * States of the seet. + */ + enum class States { + STATE_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED + STATE_SET_WAS_READ //!< DATA_SET_WAS_READ + }; + /** + * @brief state manages the internal state of the data set, + * which is important e.g. for the behavior on destruction. + */ + States state = States::STATE_SET_UNINITIALISED; - /** - * @brief This array represents all pool variables registered in this set. - * Child classes can use a static or dynamic container to create - * an array of registered variables and assign the first entry here. - */ - PoolVariableIF** registeredVariables = nullptr; - const size_t maxFillCount = 0; + /** + * @brief This array represents all pool variables registered in this set. + * Child classes can use a static or dynamic container to create + * an array of registered variables and assign the first entry here. + */ + PoolVariableIF** registeredVariables = nullptr; + const size_t maxFillCount = 0; - void setContainer(PoolVariableIF** variablesContainer); - PoolVariableIF** getContainer() const; + void setContainer(PoolVariableIF** variablesContainer); + PoolVariableIF** getContainer() const; private: - bool protectEveryReadCommitCall = false; - MutexIF::TimeoutType timeoutTypeForSingleVars = MutexIF::TimeoutType::WAITING; - uint32_t mutexTimeoutForSingleVars = 20; + bool protectEveryReadCommitCall = false; + MutexIF::TimeoutType timeoutTypeForSingleVars = MutexIF::TimeoutType::WAITING; + uint32_t mutexTimeoutForSingleVars = 20; - ReturnValue_t readVariable(uint16_t count); - void handleAlreadyReadDatasetCommit( - MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, - uint32_t timeoutMs = 20); - ReturnValue_t handleUnreadDatasetCommit( - MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, - uint32_t timeoutMs = 20); + ReturnValue_t readVariable(uint16_t count); + void handleAlreadyReadDatasetCommit( + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20); + ReturnValue_t handleUnreadDatasetCommit( + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20); }; #endif /* FSFW_DATAPOOL_POOLDATASETBASE_H_ */ diff --git a/datapool/PoolVariableIF.h b/datapool/PoolVariableIF.h index 444e19d00..dead6844a 100644 --- a/datapool/PoolVariableIF.h +++ b/datapool/PoolVariableIF.h @@ -1,9 +1,10 @@ #ifndef FSFW_DATAPOOL_POOLVARIABLEIF_H_ #define FSFW_DATAPOOL_POOLVARIABLEIF_H_ +#include "ReadCommitIF.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../serialize/SerializeIF.h" -#include "ReadCommitIF.h" + /** * @brief This interface is used to control data pool @@ -18,46 +19,46 @@ * @author Bastian Baetz * @ingroup data_pool */ -class PoolVariableIF : public SerializeIF, - public ReadCommitIF { - friend class PoolDataSetBase; - friend class LocalPoolDataSetBase; +class PoolVariableIF : + public SerializeIF, + public ReadCommitIF { + public: - static constexpr uint8_t INTERFACE_ID = CLASS_ID::POOL_VARIABLE_IF; - static constexpr ReturnValue_t INVALID_READ_WRITE_MODE = MAKE_RETURN_CODE(0xA0); - static constexpr ReturnValue_t INVALID_POOL_ENTRY = MAKE_RETURN_CODE(0xA1); + static constexpr uint8_t INTERFACE_ID = CLASS_ID::POOL_VARIABLE_IF; + static constexpr ReturnValue_t INVALID_READ_WRITE_MODE = MAKE_RETURN_CODE(0xA0); + static constexpr ReturnValue_t INVALID_POOL_ENTRY = MAKE_RETURN_CODE(0xA1); - static constexpr bool VALID = 1; - static constexpr bool INVALID = 0; - static constexpr uint32_t NO_PARAMETER = 0xffffffff; + static constexpr bool VALID = 1; + static constexpr bool INVALID = 0; + static constexpr uint32_t NO_PARAMETER = 0xffffffff; - enum ReadWriteMode_t { - VAR_READ, VAR_WRITE, VAR_READ_WRITE - }; + enum ReadWriteMode_t { + VAR_READ, VAR_WRITE, VAR_READ_WRITE + }; - /** - * @brief This is an empty virtual destructor, - * as it is proposed for C++ interfaces. - */ - virtual ~PoolVariableIF() {} - /** - * @brief This method returns if the variable is write-only, - * read-write or read-only. - */ - virtual ReadWriteMode_t getReadWriteMode() const = 0; - /** - * @brief This operation shall return the data pool id of the variable. - */ - virtual uint32_t getDataPoolId() const = 0; - /** - * @brief With this call, the valid information of the - * variable is returned. - */ - virtual bool isValid() const = 0; - /** - * @brief With this call, the valid information of the variable is set. - */ - virtual void setValid(bool validity) = 0; + /** + * @brief This is an empty virtual destructor, + * as it is proposed for C++ interfaces. + */ + virtual ~PoolVariableIF() {} + /** + * @brief This method returns if the variable is write-only, + * read-write or read-only. + */ + virtual ReadWriteMode_t getReadWriteMode() const = 0; + /** + * @brief This operation shall return the data pool id of the variable. + */ + virtual uint32_t getDataPoolId() const = 0; + /** + * @brief With this call, the valid information of the + * variable is returned. + */ + virtual bool isValid() const = 0; + /** + * @brief With this call, the valid information of the variable is set. + */ + virtual void setValid(bool validity) = 0; }; diff --git a/datapool/ReadCommitIF.h b/datapool/ReadCommitIF.h index e6355e824..d8bc5a66e 100644 --- a/datapool/ReadCommitIF.h +++ b/datapool/ReadCommitIF.h @@ -9,6 +9,7 @@ * semantics. */ class ReadCommitIF { + friend class ReadCommitIFAttorney; public: virtual ~ReadCommitIF() {} virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType, @@ -16,11 +17,10 @@ public: virtual ReturnValue_t commit(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) = 0; -protected: +public: - //! Optional and protected because this is interesting for classes grouping - //! members with commit and read semantics where the lock is only necessary - //! once. + /* Optional and protected because this is interesting for classes grouping members with commit + and read semantics where the lock is only necessary once. */ virtual ReturnValue_t readWithoutLock() { return read(MutexIF::TimeoutType::WAITING, 20); } diff --git a/datapool/ReadCommitIFAttorney.h b/datapool/ReadCommitIFAttorney.h new file mode 100644 index 000000000..0291cf5cc --- /dev/null +++ b/datapool/ReadCommitIFAttorney.h @@ -0,0 +1,32 @@ +#ifndef FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_ +#define FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_ + +#include +#include + +/** + * @brief This class determines which members are allowed to access protected members + * of the ReadCommitIF. + */ +class ReadCommitIFAttorney { +private: + static ReturnValue_t readWithoutLock(ReadCommitIF* readCommitIF) { + if(readCommitIF == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return readCommitIF->readWithoutLock(); + } + + static ReturnValue_t commitWithoutLock(ReadCommitIF* readCommitIF) { + if(readCommitIF == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return readCommitIF->commitWithoutLock(); + } + + friend class PoolDataSetBase; +}; + + + +#endif /* FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_ */ From 36039266ee703e86d828a8edf499895bc2d7529a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 13:52:07 +0100 Subject: [PATCH 11/59] some small formatting stuff --- datapool/PoolDataSetBase.cpp | 42 ++++++++++++++---------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/datapool/PoolDataSetBase.cpp b/datapool/PoolDataSetBase.cpp index 1474aff25..176676ce9 100644 --- a/datapool/PoolDataSetBase.cpp +++ b/datapool/PoolDataSetBase.cpp @@ -14,8 +14,7 @@ PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray, PoolDataSetBase::~PoolDataSetBase() {} -ReturnValue_t PoolDataSetBase::registerVariable( - PoolVariableIF *variable) { +ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF *variable) { if (state != States::STATE_SET_UNINITIALISED) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "DataSet::registerVariable: " @@ -59,10 +58,12 @@ ReturnValue_t PoolDataSetBase::read(MutexIF::TimeoutType timeoutType, } else { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "DataSet::read(): " - "Call made in wrong position. Don't forget to commit" + sif::error << "DataSet::read(): Call made in wrong position. Don't forget to commit" " member datasets!" << std::endl; -#endif +#else + sif::printError("DataSet::read(): Call made in wrong position. Don't forget to commit" + " member datasets!\n"); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ result = SET_WAS_ALREADY_READ; } @@ -85,14 +86,10 @@ ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) { /* These checks are often performed by the respective variable implementation too, but I guess a double check does not hurt. */ - if (registeredVariables[count]->getReadWriteMode() != - PoolVariableIF::VAR_WRITE and - registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) - { + if (registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_WRITE and + registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER) { if(protectEveryReadCommitCall) { - result = registeredVariables[count]->read( - timeoutTypeForSingleVars, + result = registeredVariables[count]->read(timeoutTypeForSingleVars, mutexTimeoutForSingleVars); } else { @@ -121,13 +118,10 @@ void PoolDataSetBase::handleAlreadyReadDatasetCommit( MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) { lockDataPool(timeoutType, lockTimeout); for (uint16_t count = 0; count < fillCount; count++) { - if (registeredVariables[count]->getReadWriteMode() - != PoolVariableIF::VAR_READ - && registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { + if ((registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_READ) and + (registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER)) { if(protectEveryReadCommitCall) { - registeredVariables[count]->commit( - timeoutTypeForSingleVars, + registeredVariables[count]->commit(timeoutTypeForSingleVars, mutexTimeoutForSingleVars); } else { @@ -144,13 +138,10 @@ ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit( ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; lockDataPool(timeoutType, lockTimeout); for (uint16_t count = 0; count < fillCount; count++) { - if (registeredVariables[count]->getReadWriteMode() - == PoolVariableIF::VAR_WRITE - && registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { + if ((registeredVariables[count]->getReadWriteMode() == PoolVariableIF::VAR_WRITE) and + (registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER)) { if(protectEveryReadCommitCall) { - result = registeredVariables[count]->commit( - timeoutTypeForSingleVars, + result = registeredVariables[count]->commit(timeoutTypeForSingleVars, mutexTimeoutForSingleVars); } else { @@ -187,8 +178,7 @@ ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, const size_t maxSize, SerializeIF::Endianness streamEndianness) const { ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; for (uint16_t count = 0; count < fillCount; count++) { - result = registeredVariables[count]->serialize(buffer, size, maxSize, - streamEndianness); + result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness); if (result != HasReturnvaluesIF::RETURN_OK) { return result; } From 35d8453b485b394b0add0883c3117cd37dbc7598 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 13:56:16 +0100 Subject: [PATCH 12/59] fixes for unit tests --- datapool/PoolDataSetBase.cpp | 2 +- osal/host/Clock.cpp | 2 +- osal/host/QueueMapManager.cpp | 1 + unittest/tests/datapoollocal/DataSetTest.cpp | 3 +-- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/datapool/PoolDataSetBase.cpp b/datapool/PoolDataSetBase.cpp index 176676ce9..88470152d 100644 --- a/datapool/PoolDataSetBase.cpp +++ b/datapool/PoolDataSetBase.cpp @@ -1,7 +1,7 @@ #include "PoolDataSetBase.h" #include "ReadCommitIFAttorney.h" -#include "../serviceinterface/ServiceInterfaceStream.h" +#include "../serviceinterface/ServiceInterface.h" #include diff --git a/osal/host/Clock.cpp b/osal/host/Clock.cpp index f0543c790..c097f6199 100644 --- a/osal/host/Clock.cpp +++ b/osal/host/Clock.cpp @@ -1,4 +1,4 @@ -#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../serviceinterface/ServiceInterface.h" #include "../../timemanager/Clock.h" #include diff --git a/osal/host/QueueMapManager.cpp b/osal/host/QueueMapManager.cpp index 9fb8b7a30..2a54f8134 100644 --- a/osal/host/QueueMapManager.cpp +++ b/osal/host/QueueMapManager.cpp @@ -1,5 +1,6 @@ #include "QueueMapManager.h" +#include "../../serviceinterface/ServiceInterface.h" #include "../../ipc/MutexFactory.h" #include "../../ipc/MutexHelper.h" diff --git a/unittest/tests/datapoollocal/DataSetTest.cpp b/unittest/tests/datapoollocal/DataSetTest.cpp index 5c2428ae1..370a1e276 100644 --- a/unittest/tests/datapoollocal/DataSetTest.cpp +++ b/unittest/tests/datapoollocal/DataSetTest.cpp @@ -14,8 +14,7 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { == retval::CATCH_OK); const uint32_t setId = 0; SECTION("BasicTest") { - StaticLocalDataSet<3> localSet = StaticLocalDataSet<3>( - sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE, setId)); + StaticLocalDataSet<3> localSet(sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE, setId)); } } From d79f0e1172fbf6c6702084777815660c573bceec Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 14:04:31 +0100 Subject: [PATCH 13/59] some more bugfixes for tests --- datapoollocal/LocalPoolObjectBase.cpp | 4 ++- unittest/tests/datapoollocal/DataSetTest.cpp | 4 +-- .../tests/datapoollocal/LocalPoolOwnerBase.h | 30 +++++++++++-------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/datapoollocal/LocalPoolObjectBase.cpp b/datapoollocal/LocalPoolObjectBase.cpp index 5b9bd34f6..7bc91dcb3 100644 --- a/datapoollocal/LocalPoolObjectBase.cpp +++ b/datapoollocal/LocalPoolObjectBase.cpp @@ -1,10 +1,12 @@ #include "LocalPoolObjectBase.h" #include "LocalDataPoolManager.h" -#include "internal/HasLocalDpIFUserAttorney.h" +#include "AccessLocalPoolF.h" #include "HasLocalDataPoolIF.h" +#include "internal/HasLocalDpIFUserAttorney.h" #include "../objectmanager/ObjectManagerIF.h" + LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, pool_rwm_t setReadWriteMode): localPoolId(poolId), readWriteMode(setReadWriteMode) { diff --git a/unittest/tests/datapoollocal/DataSetTest.cpp b/unittest/tests/datapoollocal/DataSetTest.cpp index 370a1e276..6ccb5122e 100644 --- a/unittest/tests/datapoollocal/DataSetTest.cpp +++ b/unittest/tests/datapoollocal/DataSetTest.cpp @@ -12,9 +12,9 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK); REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() == retval::CATCH_OK); - const uint32_t setId = 0; + //const uint32_t setId = 0; SECTION("BasicTest") { - StaticLocalDataSet<3> localSet(sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE, setId)); + LocalPoolStaticTestDataSet localSet; } } diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index acbb2b736..6b66f1f0a 100644 --- a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h @@ -31,6 +31,23 @@ static const gp_id_t uint64Vec2Id = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, } +class LocalPoolStaticTestDataSet: public StaticLocalDataSet<3> { +public: + LocalPoolStaticTestDataSet(): + StaticLocalDataSet(lpool::testSid) { + } + + LocalPoolStaticTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId): + StaticLocalDataSet(owner, setId) { + } + + lp_var_t localPoolVarUint8 = lp_var_t(lpool::uint8VarGpid, this); + lp_var_t localPoolVarFloat = lp_var_t(lpool::floatVarGpid, this); + lp_vec_t localPoolUint16Vec = lp_vec_t(lpool::uint16Vec3Gpid, this); + +private: +}; + class LocalPoolTestDataSet: public LocalDataSet { public: LocalPoolTestDataSet(): @@ -41,19 +58,6 @@ public: LocalDataSet(owner, setId, lpool::dataSetMaxVariables) { } -// ReturnValue_t assignPointers() { -// PoolVariableIF** rawVarArray = getContainer(); -// localPoolVarUint8 = dynamic_cast*>(rawVarArray[0]); -// localPoolVarFloat = dynamic_cast*>(rawVarArray[1]); -// localPoolUint16Vec = dynamic_cast*>( -// rawVarArray[2]); -// if(localPoolVarUint8 == nullptr or localPoolVarFloat == nullptr or -// localPoolUint16Vec == nullptr) { -// return HasReturnvaluesIF::RETURN_FAILED; -// } -// return HasReturnvaluesIF::RETURN_OK; -// } - lp_var_t localPoolVarUint8 = lp_var_t(lpool::uint8VarGpid, this); lp_var_t localPoolVarFloat = lp_var_t(lpool::floatVarGpid, this); lp_vec_t localPoolUint16Vec = lp_vec_t(lpool::uint16Vec3Gpid, this); From 828115a566f69cfe99f64872f448585eab62ba28 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 14:35:10 +0100 Subject: [PATCH 14/59] test bugfixes and new reset function --- unittest/tests/datapoollocal/DataSetTest.cpp | 44 +++++++++++++++++- .../datapoollocal/LocalPoolManagerTest.cpp | 2 +- .../tests/datapoollocal/LocalPoolOwnerBase.h | 46 +++++++++++++++++-- .../datapoollocal/LocalPoolVariableTest.cpp | 2 + 4 files changed, 87 insertions(+), 7 deletions(-) diff --git a/unittest/tests/datapoollocal/DataSetTest.cpp b/unittest/tests/datapoollocal/DataSetTest.cpp index 6ccb5122e..7ba568775 100644 --- a/unittest/tests/datapoollocal/DataSetTest.cpp +++ b/unittest/tests/datapoollocal/DataSetTest.cpp @@ -1,8 +1,11 @@ #include "LocalPoolOwnerBase.h" #include +#include + #include #include +#include #include TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { @@ -12,10 +15,49 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK); REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() == retval::CATCH_OK); + LocalPoolStaticTestDataSet localSet; //const uint32_t setId = 0; SECTION("BasicTest") { - LocalPoolStaticTestDataSet localSet; + { + PoolReadHelper readHelper(&localSet); + REQUIRE(readHelper.getReadResult() == retval::CATCH_OK); + CHECK(not localSet.isValid()); + CHECK(localSet.localPoolVarUint8.value == 0); + CHECK(not localSet.localPoolVarUint8.isValid()); + CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0)); + CHECK(not localSet.localPoolVarUint8.isValid()); + CHECK(localSet.localPoolUint16Vec.value[0] == 0); + CHECK(localSet.localPoolUint16Vec.value[1] == 0); + CHECK(localSet.localPoolUint16Vec.value[2] == 0); + CHECK(not localSet.localPoolVarUint8.isValid()); + + localSet.localPoolVarUint8 = 232; + localSet.localPoolVarFloat = -2324.322; + localSet.localPoolUint16Vec.value[0] = 232; + localSet.localPoolUint16Vec.value[1] = 23923; + localSet.localPoolUint16Vec.value[2] = 1; + localSet.setValidity(true, true); + } + + { + PoolReadHelper readHelper(&localSet); + REQUIRE(readHelper.getReadResult() == retval::CATCH_OK); + CHECK(localSet.isValid()); + CHECK(localSet.localPoolVarUint8.value == 232); + CHECK(localSet.localPoolVarUint8.isValid()); + CHECK(localSet.localPoolVarFloat.value == Catch::Approx(-2324.322)); + CHECK(localSet.localPoolVarUint8.isValid()); + CHECK(localSet.localPoolUint16Vec.value[0] == 232); + CHECK(localSet.localPoolUint16Vec.value[1] == 23923); + CHECK(localSet.localPoolUint16Vec.value[2] == 1); + CHECK(localSet.localPoolVarUint8.isValid()); + } + } + + /* we need to reset the subscription list because the pool owner + is a global object. */ + CHECK(poolOwner->reset() == retval::CATCH_OK); } diff --git a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp index e621f4d87..a10b44999 100644 --- a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp @@ -192,7 +192,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { /* we need to reset the subscription list because the pool owner is a global object. */ - poolOwner->resetSubscriptionList(); + CHECK(poolOwner->reset() == retval::CATCH_OK); mqMock->clearMessages(true); } diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index 6b66f1f0a..bb090e3b3 100644 --- a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h @@ -10,6 +10,7 @@ #include #include #include +#include "../../../datapool/PoolReadHelper.h" namespace lpool { static constexpr lp_id_t uint8VarId = 0; @@ -187,6 +188,43 @@ public: MessageQueueIF::NO_QUEUE, objects::HK_RECEIVER_MOCK, false); } + ReturnValue_t reset() { + resetSubscriptionList(); + ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; + { + PoolReadHelper readHelper(&dataset); + if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { + status = readHelper.getReadResult(); + } + dataset.localPoolVarUint8.value = 0; + dataset.localPoolVarFloat.value = 0.0; + dataset.localPoolUint16Vec.value[0] = 0; + dataset.localPoolUint16Vec.value[1] = 0; + dataset.localPoolUint16Vec.value[2] = 0; + dataset.setValidity(false, true); + } + + { + PoolReadHelper readHelper(&testUint32); + if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { + status = readHelper.getReadResult(); + } + testUint32.value = 0; + testUint32.setValid(false); + } + + { + PoolReadHelper readHelper(&testInt64Vec); + if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { + status = readHelper.getReadResult(); + } + testInt64Vec.value[0] = 0; + testInt64Vec.value[1] = 0; + testInt64Vec.setValid(false); + } + return status; + } + void resetSubscriptionList() { poolManager.clearReceiversList(); } @@ -195,14 +233,12 @@ public: LocalPoolTestDataSet dataset; private: - lp_var_t testUint8 = lp_var_t(this, lpool::uint8VarId, - &dataset); - lp_var_t testFloat = lp_var_t(this, lpool::floatVarId, - &dataset); + lp_var_t testUint8 = lp_var_t(this, lpool::uint8VarId); + lp_var_t testFloat = lp_var_t(this, lpool::floatVarId); lp_var_t testUint32 = lp_var_t(this, lpool::uint32VarId); lp_vec_t testUint16Vec = lp_vec_t(this, - lpool::uint16Vec3Id, &dataset); + lpool::uint16Vec3Id); lp_vec_t testInt64Vec = lp_vec_t(this, lpool::int64Vec2Id); diff --git a/unittest/tests/datapoollocal/LocalPoolVariableTest.cpp b/unittest/tests/datapoollocal/LocalPoolVariableTest.cpp index eb99e58df..e5a5d3645 100644 --- a/unittest/tests/datapoollocal/LocalPoolVariableTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolVariableTest.cpp @@ -118,6 +118,8 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") { lpool::uint8VarId); } + CHECK(poolOwner->reset() == retval::CATCH_OK); + } From fb5a1b93fce6c3e107050395d31303021928d79b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 14:38:01 +0100 Subject: [PATCH 15/59] unneeded variable removed --- unittest/tests/datapoollocal/DataSetTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/tests/datapoollocal/DataSetTest.cpp b/unittest/tests/datapoollocal/DataSetTest.cpp index 7ba568775..18503f05f 100644 --- a/unittest/tests/datapoollocal/DataSetTest.cpp +++ b/unittest/tests/datapoollocal/DataSetTest.cpp @@ -16,7 +16,7 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() == retval::CATCH_OK); LocalPoolStaticTestDataSet localSet; - //const uint32_t setId = 0; + SECTION("BasicTest") { { PoolReadHelper readHelper(&localSet); From 68415853b5040733ff9f5d2739dd1bc704769bd1 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 14:41:43 +0100 Subject: [PATCH 16/59] read commit IF functions protected again --- datapool/PoolDataSetBase.cpp | 5 ++++- datapool/ReadCommitIF.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/datapool/PoolDataSetBase.cpp b/datapool/PoolDataSetBase.cpp index 88470152d..e7572b27e 100644 --- a/datapool/PoolDataSetBase.cpp +++ b/datapool/PoolDataSetBase.cpp @@ -93,6 +93,7 @@ ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) { mutexTimeoutForSingleVars); } else { + /* The readWithoutLock function is protected, so we use the attorney here */ result = ReadCommitIFAttorney::readWithoutLock(registeredVariables[count]); } @@ -125,6 +126,7 @@ void PoolDataSetBase::handleAlreadyReadDatasetCommit( mutexTimeoutForSingleVars); } else { + /* The commitWithoutLock function is protected, so we use the attorney here */ ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]); } } @@ -145,7 +147,8 @@ ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit( mutexTimeoutForSingleVars); } else { - result = registeredVariables[count]->commitWithoutLock(); + /* The commitWithoutLock function is protected, so we use the attorney here */ + ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]); } } else if (registeredVariables[count]->getDataPoolId() diff --git a/datapool/ReadCommitIF.h b/datapool/ReadCommitIF.h index d8bc5a66e..3ad2b3c02 100644 --- a/datapool/ReadCommitIF.h +++ b/datapool/ReadCommitIF.h @@ -17,7 +17,7 @@ public: virtual ReturnValue_t commit(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) = 0; -public: +protected: /* Optional and protected because this is interesting for classes grouping members with commit and read semantics where the lock is only necessary once. */ From 16566a5690159a84a88bdfb5f8a276feb1050da3 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 14:45:09 +0100 Subject: [PATCH 17/59] nullptr check added --- datapoollocal/LocalPoolObjectBase.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/datapoollocal/LocalPoolObjectBase.cpp b/datapoollocal/LocalPoolObjectBase.cpp index 7bc91dcb3..a64ed2b4a 100644 --- a/datapoollocal/LocalPoolObjectBase.cpp +++ b/datapoollocal/LocalPoolObjectBase.cpp @@ -95,6 +95,10 @@ void LocalPoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) { void LocalPoolObjectBase::reportReadCommitError(const char* variableType, ReturnValue_t error, bool read, object_id_t objectId, lp_id_t lpId) { #if FSFW_DISABLE_PRINTOUT == 0 + const char* variablePrintout = variableType; + if(variablePrintout == nullptr) { + variablePrintout = "Unknown Type"; + } const char* type = nullptr; if(read) { type = "read"; @@ -121,12 +125,12 @@ void LocalPoolObjectBase::reportReadCommitError(const char* variableType, } #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << variableType << ": " << type << " call | " << errMsg << " | Owner: 0x" + sif::warning << variablePrintout << ": " << type << " call | " << errMsg << " | Owner: 0x" << std::hex << std::setw(8) << std::setfill('0') << objectId << std::dec << " LPID: " << lpId << std::endl; #else sif::printWarning("%s: %s call | %s | Owner: 0x%08x LPID: %lu\n", - variableType, type, errMsg, objectId, lpId); + variablePrintout, type, errMsg, objectId, lpId); #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ #endif /* FSFW_DISABLE_PRINTOUT == 0 */ } From 304773f7a7dbf84ab98c2b4c62c60f792cd07f1d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 14:54:03 +0100 Subject: [PATCH 18/59] added some failure test cases --- datapool/DataSetIF.h | 14 ++++++-------- datapool/PoolDataSetBase.cpp | 15 +++++++++------ unittest/tests/datapoollocal/DataSetTest.cpp | 8 ++++++++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/datapool/DataSetIF.h b/datapool/DataSetIF.h index a6634a5c7..ea1a7126e 100644 --- a/datapool/DataSetIF.h +++ b/datapool/DataSetIF.h @@ -18,15 +18,13 @@ class PoolVariableIF; class DataSetIF { public: static constexpr uint8_t INTERFACE_ID = CLASS_ID::DATA_SET_CLASS; - static constexpr ReturnValue_t INVALID_PARAMETER_DEFINITION = - MAKE_RETURN_CODE( 0x01 ); - static constexpr ReturnValue_t SET_WAS_ALREADY_READ = MAKE_RETURN_CODE( 0x02 ); - static constexpr ReturnValue_t COMMITING_WITHOUT_READING = - MAKE_RETURN_CODE(0x03); + static constexpr ReturnValue_t INVALID_PARAMETER_DEFINITION = MAKE_RETURN_CODE(1); + static constexpr ReturnValue_t SET_WAS_ALREADY_READ = MAKE_RETURN_CODE(2); + static constexpr ReturnValue_t COMMITING_WITHOUT_READING = MAKE_RETURN_CODE(3); - static constexpr ReturnValue_t DATA_SET_UNINITIALISED = MAKE_RETURN_CODE( 0x04 ); - static constexpr ReturnValue_t DATA_SET_FULL = MAKE_RETURN_CODE( 0x05 ); - static constexpr ReturnValue_t POOL_VAR_NULL = MAKE_RETURN_CODE( 0x06 ); + static constexpr ReturnValue_t DATA_SET_UNINITIALISED = MAKE_RETURN_CODE(4); + static constexpr ReturnValue_t DATA_SET_FULL = MAKE_RETURN_CODE(5); + static constexpr ReturnValue_t POOL_VAR_NULL = MAKE_RETURN_CODE(6); /** * @brief This is an empty virtual destructor, diff --git a/datapool/PoolDataSetBase.cpp b/datapool/PoolDataSetBase.cpp index e7572b27e..bdca22c3d 100644 --- a/datapool/PoolDataSetBase.cpp +++ b/datapool/PoolDataSetBase.cpp @@ -17,22 +17,25 @@ PoolDataSetBase::~PoolDataSetBase() {} ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF *variable) { if (state != States::STATE_SET_UNINITIALISED) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "DataSet::registerVariable: " - "Call made in wrong position." << std::endl; + sif::error << "DataSet::registerVariable: Call made in wrong position." << std::endl; +#else + sif::printError("DataSet::registerVariable: Call made in wrong position."); #endif return DataSetIF::DATA_SET_UNINITIALISED; } if (variable == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "DataSet::registerVariable: " - "Pool variable is nullptr." << std::endl; + sif::error << "DataSet::registerVariable: Pool variable is nullptr." << std::endl; +#else + sif::printError("DataSet::registerVariable: Pool variable is nullptr.\n"); #endif return DataSetIF::POOL_VAR_NULL; } if (fillCount >= maxFillCount) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "DataSet::registerVariable: " - "DataSet is full." << std::endl; + sif::error << "DataSet::registerVariable: DataSet is full." << std::endl; +#else + sif::printError("DataSet::registerVariable: DataSet is full.\n"); #endif return DataSetIF::DATA_SET_FULL; } diff --git a/unittest/tests/datapoollocal/DataSetTest.cpp b/unittest/tests/datapoollocal/DataSetTest.cpp index 18503f05f..d829e7754 100644 --- a/unittest/tests/datapoollocal/DataSetTest.cpp +++ b/unittest/tests/datapoollocal/DataSetTest.cpp @@ -53,6 +53,14 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { CHECK(localSet.localPoolVarUint8.isValid()); } + /* Common fault test cases */ + LocalPoolObjectBase* variableHandle = poolOwner->getPoolObjectHandle(lpool::uint32VarId); + CHECK(variableHandle != nullptr); + CHECK(localSet.registerVariable(variableHandle) == + static_cast(DataSetIF::DATA_SET_FULL)); + variableHandle = nullptr; + REQUIRE(localSet.registerVariable(variableHandle) == + static_cast(DataSetIF::POOL_VAR_NULL)); } /* we need to reset the subscription list because the pool owner From 50ba3773809a170b858f4eece60f69ad1c65c7af Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 15:34:04 +0100 Subject: [PATCH 19/59] more set tests and new function to suppress commits --- datapool/PoolReadHelper.h | 13 ++++- datapoollocal/LocalPoolDataSetBase.cpp | 2 +- unittest/tests/datapoollocal/DataSetTest.cpp | 55 ++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/datapool/PoolReadHelper.h b/datapool/PoolReadHelper.h index 5c3153bb6..9825e83c1 100644 --- a/datapool/PoolReadHelper.h +++ b/datapool/PoolReadHelper.h @@ -32,8 +32,18 @@ public: return readResult; } + /** + * @brief Can be used to suppress commit on destruction. + */ + void setNoCommitMode(bool commit) { + this->noCommit = commit; + } + + /** + * @brief Default destructor which will take care of commiting changed values. + */ ~PoolReadHelper() { - if(readObject != nullptr) { + if(readObject != nullptr and not noCommit) { readObject->commit(timeoutType, mutexTimeout); } @@ -42,6 +52,7 @@ public: private: ReadCommitIF* readObject = nullptr; ReturnValue_t readResult = HasReturnvaluesIF::RETURN_OK; + bool noCommit = false; MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING; uint32_t mutexTimeout = 20; }; diff --git a/datapoollocal/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp index d1d95d8a8..9f23fc6cb 100644 --- a/datapoollocal/LocalPoolDataSetBase.cpp +++ b/datapoollocal/LocalPoolDataSetBase.cpp @@ -173,7 +173,7 @@ ReturnValue_t LocalPoolDataSetBase::unlockDataPool() { ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t** buffer, size_t* size, size_t maxSize,SerializeIF::Endianness streamEndianness, bool serializeFillCount) const { - // Serialize as uint8_t + /* Serialize fill count as uint8_t */ uint8_t fillCount = this->fillCount; if(serializeFillCount) { SerializeAdapter::serialize(&fillCount, buffer, size, maxSize, diff --git a/unittest/tests/datapoollocal/DataSetTest.cpp b/unittest/tests/datapoollocal/DataSetTest.cpp index d829e7754..eb8e9b603 100644 --- a/unittest/tests/datapoollocal/DataSetTest.cpp +++ b/unittest/tests/datapoollocal/DataSetTest.cpp @@ -18,7 +18,40 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { LocalPoolStaticTestDataSet localSet; SECTION("BasicTest") { + /* Test some basic functions */ + CHECK(localSet.getLocalPoolIdsSerializedSize(false) == 3 * sizeof(lp_id_t)); + CHECK(localSet.getLocalPoolIdsSerializedSize(true) == + 3 * sizeof(lp_id_t) + sizeof(uint8_t)); + CHECK(localSet.getSid() == lpool::testSid); + CHECK(localSet.getCreatorObjectId() == objects::TEST_LOCAL_POOL_OWNER_BASE); + size_t maxSize = localSet.getLocalPoolIdsSerializedSize(true); + uint8_t localPoolIdBuff[maxSize]; + /* Skip size field */ + lp_id_t* lpIds = reinterpret_cast(localPoolIdBuff + 1); + size_t serSize = 0; + uint8_t *localPoolIdBuffPtr = reinterpret_cast(localPoolIdBuff); + + /* Test local pool ID serialization */ + CHECK(localSet.serializeLocalPoolIds(&localPoolIdBuffPtr, &serSize, + maxSize, SerializeIF::Endianness::MACHINE) == retval::CATCH_OK); + CHECK(serSize == maxSize); + CHECK(localPoolIdBuff[0] == 3); + CHECK(lpIds[0] == localSet.localPoolVarUint8.getDataPoolId()); + CHECK(lpIds[1] == localSet.localPoolVarFloat.getDataPoolId()); + CHECK(lpIds[2] == localSet.localPoolUint16Vec.getDataPoolId()); + /* Now serialize without fill count */ + lpIds = reinterpret_cast(localPoolIdBuff); + localPoolIdBuffPtr = localPoolIdBuff; + serSize = 0; + CHECK(localSet.serializeLocalPoolIds(&localPoolIdBuffPtr, &serSize, + maxSize, SerializeIF::Endianness::MACHINE, false) == retval::CATCH_OK); + CHECK(serSize == maxSize - sizeof(uint8_t)); + CHECK(lpIds[0] == localSet.localPoolVarUint8.getDataPoolId()); + CHECK(lpIds[1] == localSet.localPoolVarFloat.getDataPoolId()); + CHECK(lpIds[2] == localSet.localPoolUint16Vec.getDataPoolId()); + { + /* Test read operation. Values should be all zeros */ PoolReadHelper readHelper(&localSet); REQUIRE(readHelper.getReadResult() == retval::CATCH_OK); CHECK(not localSet.isValid()); @@ -31,6 +64,7 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { CHECK(localSet.localPoolUint16Vec.value[2] == 0); CHECK(not localSet.localPoolVarUint8.isValid()); + /* Now set new values, commit should be done by read helper automatically */ localSet.localPoolVarUint8 = 232; localSet.localPoolVarFloat = -2324.322; localSet.localPoolUint16Vec.value[0] = 232; @@ -51,6 +85,27 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { CHECK(localSet.localPoolUint16Vec.value[1] == 23923); CHECK(localSet.localPoolUint16Vec.value[2] == 1); CHECK(localSet.localPoolVarUint8.isValid()); + + localSet.setValidityBufferGeneration(false); + maxSize = localSet.getSerializedSize(); + CHECK(maxSize == sizeof(uint8_t) + sizeof(uint16_t) * 3 + sizeof(float)); + serSize = 0; + uint8_t buffer[maxSize]; + uint8_t* buffPtr = buffer; + CHECK(localSet.serialize(&buffPtr, &serSize, maxSize, + SerializeIF::Endianness::MACHINE) == retval::CATCH_OK); + uint8_t rawUint8 = buffer[0]; + CHECK(rawUint8 == 232); + float rawFloat = 0.0; + std::memcpy(&rawFloat, buffer + sizeof(uint8_t), sizeof(float)); + CHECK(rawFloat == Catch::Approx(-2324.322)); + + uint16_t rawUint16Vec[3]; + std::memcpy(&rawUint16Vec, buffer + sizeof(uint8_t) + sizeof(float), + 3 * sizeof(uint16_t)); + CHECK(rawUint16Vec[0] == 232); + CHECK(rawUint16Vec[1] == 23923); + CHECK(rawUint16Vec[2] == 1); } /* Common fault test cases */ From ffce336801d157c2ee9df2dc6feb682a5d125228 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 15:44:05 +0100 Subject: [PATCH 20/59] set tests continued --- unittest/tests/datapoollocal/DataSetTest.cpp | 35 ++++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/unittest/tests/datapoollocal/DataSetTest.cpp b/unittest/tests/datapoollocal/DataSetTest.cpp index eb8e9b603..929e5d765 100644 --- a/unittest/tests/datapoollocal/DataSetTest.cpp +++ b/unittest/tests/datapoollocal/DataSetTest.cpp @@ -73,24 +73,32 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { localSet.setValidity(true, true); } + /* Zero out some values for next test */ + localSet.localPoolVarUint8 = 0; + localSet.localPoolVarFloat = 0; + { + /* Now we read again and check whether our zeroed values were overwritten with + the values in the pool */ PoolReadHelper readHelper(&localSet); REQUIRE(readHelper.getReadResult() == retval::CATCH_OK); CHECK(localSet.isValid()); CHECK(localSet.localPoolVarUint8.value == 232); CHECK(localSet.localPoolVarUint8.isValid()); CHECK(localSet.localPoolVarFloat.value == Catch::Approx(-2324.322)); - CHECK(localSet.localPoolVarUint8.isValid()); + CHECK(localSet.localPoolVarFloat.isValid()); CHECK(localSet.localPoolUint16Vec.value[0] == 232); CHECK(localSet.localPoolUint16Vec.value[1] == 23923); CHECK(localSet.localPoolUint16Vec.value[2] == 1); - CHECK(localSet.localPoolVarUint8.isValid()); + CHECK(localSet.localPoolUint16Vec.isValid()); + /* Now we serialize these values into a buffer without the validity buffer */ localSet.setValidityBufferGeneration(false); maxSize = localSet.getSerializedSize(); CHECK(maxSize == sizeof(uint8_t) + sizeof(uint16_t) * 3 + sizeof(float)); serSize = 0; - uint8_t buffer[maxSize]; + /* Already reserve additional space for validity buffer, will be needed later */ + uint8_t buffer[maxSize + 1]; uint8_t* buffPtr = buffer; CHECK(localSet.serialize(&buffPtr, &serSize, maxSize, SerializeIF::Endianness::MACHINE) == retval::CATCH_OK); @@ -106,6 +114,27 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { CHECK(rawUint16Vec[0] == 232); CHECK(rawUint16Vec[1] == 23923); CHECK(rawUint16Vec[2] == 1); + + size_t sizeToDeserialize = maxSize; + /* Now we zeros out the raw entries and deserialize back into the dataset */ + std::memset(buffer, 0, sizeof(buffer)); + const uint8_t* constBuffPtr = buffer; + CHECK(localSet.deSerialize(&constBuffPtr, &sizeToDeserialize, + SerializeIF::Endianness::MACHINE) == retval::CATCH_OK); + /* Check whether deserialization was successfull */ + CHECK(localSet.localPoolVarUint8.value == 0); + CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0)); + CHECK(localSet.localPoolVarUint8.value == 0); + CHECK(localSet.localPoolUint16Vec.value[0] == 0); + CHECK(localSet.localPoolUint16Vec.value[1] == 0); + CHECK(localSet.localPoolUint16Vec.value[2] == 0); + /* Validity should be unchanged */ + CHECK(localSet.localPoolVarUint8.isValid()); + CHECK(localSet.localPoolVarFloat.isValid()); + CHECK(localSet.localPoolUint16Vec.isValid()); + + /* Now we do the same process but with the validity buffer */ + localSet.setValidityBufferGeneration(true); } /* Common fault test cases */ From 714f11f1170ac3669e155c9fdd68632349464d1d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 16:17:07 +0100 Subject: [PATCH 21/59] more tests and validity buffer bugfix --- datapoollocal/LocalPoolDataSetBase.cpp | 77 ++++++++++---------- datapoollocal/LocalPoolDataSetBase.h | 14 ++-- unittest/tests/datapoollocal/DataSetTest.cpp | 33 +++++++++ 3 files changed, 78 insertions(+), 46 deletions(-) diff --git a/datapoollocal/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp index 9f23fc6cb..085ae2275 100644 --- a/datapoollocal/LocalPoolDataSetBase.cpp +++ b/datapoollocal/LocalPoolDataSetBase.cpp @@ -96,22 +96,22 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer SerializeIF::Endianness streamEndianness) const { ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; uint8_t validityMaskSize = std::ceil(static_cast(fillCount)/8.0); - uint8_t validityMask[validityMaskSize]; + uint8_t validityMask[validityMaskSize] = {}; uint8_t validBufferIndex = 0; uint8_t validBufferIndexBit = 0; for (uint16_t count = 0; count < fillCount; count++) { if(registeredVariables[count]->isValid()) { - // set validity buffer here. - this->bitSetter(validityMask + validBufferIndex, - validBufferIndexBit); - if(validBufferIndexBit == 7) { - validBufferIndex ++; - validBufferIndexBit = 0; - } - else { - validBufferIndexBit ++; - } + /* Set bit at correct position */ + this->bitSetter(validityMask + validBufferIndex, validBufferIndexBit); } + if(validBufferIndexBit == 7) { + validBufferIndex ++; + validBufferIndexBit = 0; + } + else { + validBufferIndexBit ++; + } + result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness); if (result != HasReturnvaluesIF::RETURN_OK) { @@ -246,21 +246,6 @@ ReturnValue_t LocalPoolDataSetBase::serialize(uint8_t **buffer, size_t *size, } } -void LocalPoolDataSetBase::bitSetter(uint8_t* byte, uint8_t position) const { - if(position > 7) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "LocalPoolDataSetBase::bitSetter: Invalid position!" - << std::endl; -#else - sif::printWarning("LocalPoolDataSetBase::bitSetter: " - "Invalid position!\n\r"); -#endif - return; - } - uint8_t shiftNumber = position + (7 - 2 * position); - *byte |= 1 << shiftNumber; -} - void LocalPoolDataSetBase::setDiagnostic(bool isDiagnostics) { this->diagnostic = isDiagnostics; } @@ -296,19 +281,6 @@ sid_t LocalPoolDataSetBase::getSid() const { return sid; } -bool LocalPoolDataSetBase::bitGetter(const uint8_t* byte, - uint8_t position) const { - if(position > 7) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::debug << "Pool Raw Access: Bit setting invalid position" - << std::endl; -#endif - return false; - } - uint8_t shiftNumber = position + (7 - 2 * position); - return *byte & (1 << shiftNumber); -} - bool LocalPoolDataSetBase::isValid() const { return this->valid; } @@ -328,3 +300,30 @@ object_id_t LocalPoolDataSetBase::getCreatorObjectId() { } return objects::NO_OBJECT; } + +void LocalPoolDataSetBase::bitSetter(uint8_t* byte, uint8_t position) { + if(position > 7) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "LocalPoolDataSetBase::bitSetter: Invalid position!" + << std::endl; +#else + sif::printWarning("LocalPoolDataSetBase::bitSetter: " + "Invalid position!\n\r"); +#endif + return; + } + uint8_t shiftNumber = position + (7 - 2 * position); + *byte |= 1 << shiftNumber; +} + +bool LocalPoolDataSetBase::bitGetter(const uint8_t* byte, uint8_t position) { + if(position > 7) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::debug << "Pool Raw Access: Bit setting invalid position" + << std::endl; +#endif + return false; + } + uint8_t shiftNumber = position + (7 - 2 * position); + return *byte & (1 << shiftNumber); +} diff --git a/datapoollocal/LocalPoolDataSetBase.h b/datapoollocal/LocalPoolDataSetBase.h index ca9074311..c676285c6 100644 --- a/datapoollocal/LocalPoolDataSetBase.h +++ b/datapoollocal/LocalPoolDataSetBase.h @@ -160,6 +160,13 @@ public: object_id_t getCreatorObjectId(); + /* Static helper functions for manipulating validity buffers */ + /** + * Set n-th bit of a byte, with n being the position from 0 + * (most significant bit) to 7 (least significant bit) + */ + static void bitSetter(uint8_t* byte, uint8_t position); + static bool bitGetter(const uint8_t* byte, uint8_t position); protected: sid_t sid; //! This mutex is used if the data is created by one object only. @@ -218,13 +225,6 @@ protected: */ ReturnValue_t unlockDataPool() override; - /** - * Set n-th bit of a byte, with n being the position from 0 - * (most significant bit) to 7 (least significant bit) - */ - void bitSetter(uint8_t* byte, uint8_t position) const; - bool bitGetter(const uint8_t* byte, uint8_t position) const; - PeriodicHousekeepingHelper* periodicHelper = nullptr; LocalDataPoolManager* poolManager = nullptr; diff --git a/unittest/tests/datapoollocal/DataSetTest.cpp b/unittest/tests/datapoollocal/DataSetTest.cpp index 929e5d765..7b2632c2c 100644 --- a/unittest/tests/datapoollocal/DataSetTest.cpp +++ b/unittest/tests/datapoollocal/DataSetTest.cpp @@ -134,7 +134,39 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { CHECK(localSet.localPoolUint16Vec.isValid()); /* Now we do the same process but with the validity buffer */ + localSet.localPoolVarUint8 = 232; + localSet.localPoolVarFloat = -2324.322; + localSet.localPoolUint16Vec.value[0] = 232; + localSet.localPoolUint16Vec.value[1] = 23923; + localSet.localPoolUint16Vec.value[2] = 1; + localSet.localPoolVarUint8.setValid(true); + localSet.localPoolVarFloat.setValid(false); + localSet.localPoolUint16Vec.setValid(true); localSet.setValidityBufferGeneration(true); + maxSize = localSet.getSerializedSize(); + CHECK(maxSize == sizeof(uint8_t) + sizeof(uint16_t) * 3 + sizeof(float) + 1); + serSize = 0; + buffPtr = buffer; + CHECK(localSet.serialize(&buffPtr, &serSize, maxSize, + SerializeIF::Endianness::MACHINE) == retval::CATCH_OK); + CHECK(rawUint8 == 232); + std::memcpy(&rawFloat, buffer + sizeof(uint8_t), sizeof(float)); + CHECK(rawFloat == Catch::Approx(-2324.322)); + + std::memcpy(&rawUint16Vec, buffer + sizeof(uint8_t) + sizeof(float), + 3 * sizeof(uint16_t)); + CHECK(rawUint16Vec[0] == 232); + CHECK(rawUint16Vec[1] == 23923); + CHECK(rawUint16Vec[2] == 1); + /* We can do it like this because the buffer only has one byte for + less than 8 variables */ + uint8_t validityByte = buffer[sizeof(buffer) - 1]; + CHECK(LocalPoolDataSetBase::bitGetter(&validityByte, 0) == true); + CHECK(LocalPoolDataSetBase::bitGetter(&validityByte, 1) == false); + CHECK(LocalPoolDataSetBase::bitGetter(&validityByte, 2) == true); + + /* Now we manipulate the validity buffer for the deserialization */ + } /* Common fault test cases */ @@ -145,6 +177,7 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { variableHandle = nullptr; REQUIRE(localSet.registerVariable(variableHandle) == static_cast(DataSetIF::POOL_VAR_NULL)); + } /* we need to reset the subscription list because the pool owner From 940bbf47e425dc0bdd70d1cba1c95a981d4d2db9 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 16:34:11 +0100 Subject: [PATCH 22/59] set deser test complete new bitutility file --- datapoollocal/LocalPoolDataSetBase.cpp | 31 ++---------------- datapoollocal/LocalPoolDataSetBase.h | 7 ----- globalfunctions/CMakeLists.txt | 3 +- globalfunctions/bitutility.cpp | 33 ++++++++++++++++++++ globalfunctions/bitutility.h | 18 +++++++++++ unittest/tests/datapoollocal/DataSetTest.cpp | 29 ++++++++++++++--- 6 files changed, 81 insertions(+), 40 deletions(-) create mode 100644 globalfunctions/bitutility.cpp create mode 100644 globalfunctions/bitutility.h diff --git a/datapoollocal/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp index 085ae2275..75a3ab7c2 100644 --- a/datapoollocal/LocalPoolDataSetBase.cpp +++ b/datapoollocal/LocalPoolDataSetBase.cpp @@ -3,6 +3,7 @@ #include "internal/HasLocalDpIFUserAttorney.h" #include "../serviceinterface/ServiceInterface.h" +#include "../globalfunctions/bitutility.h" #include "../datapoollocal/LocalDataPoolManager.h" #include "../housekeeping/PeriodicHousekeepingHelper.h" #include "../serialize/SerializeAdapter.h" @@ -102,7 +103,7 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer for (uint16_t count = 0; count < fillCount; count++) { if(registeredVariables[count]->isValid()) { /* Set bit at correct position */ - this->bitSetter(validityMask + validBufferIndex, validBufferIndexBit); + bitutil::bitSet(validityMask + validBufferIndex, validBufferIndexBit); } if(validBufferIndexBit == 7) { validBufferIndex ++; @@ -148,7 +149,7 @@ ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer( uint8_t validBufferIndexBit = 0; for (uint16_t count = 0; count < fillCount; count++) { // set validity buffer here. - bool nextVarValid = this->bitGetter(*buffer + + bool nextVarValid = bitutil::bitGet(*buffer + validBufferIndex, validBufferIndexBit); registeredVariables[count]->setValid(nextVarValid); @@ -301,29 +302,3 @@ object_id_t LocalPoolDataSetBase::getCreatorObjectId() { return objects::NO_OBJECT; } -void LocalPoolDataSetBase::bitSetter(uint8_t* byte, uint8_t position) { - if(position > 7) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "LocalPoolDataSetBase::bitSetter: Invalid position!" - << std::endl; -#else - sif::printWarning("LocalPoolDataSetBase::bitSetter: " - "Invalid position!\n\r"); -#endif - return; - } - uint8_t shiftNumber = position + (7 - 2 * position); - *byte |= 1 << shiftNumber; -} - -bool LocalPoolDataSetBase::bitGetter(const uint8_t* byte, uint8_t position) { - if(position > 7) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::debug << "Pool Raw Access: Bit setting invalid position" - << std::endl; -#endif - return false; - } - uint8_t shiftNumber = position + (7 - 2 * position); - return *byte & (1 << shiftNumber); -} diff --git a/datapoollocal/LocalPoolDataSetBase.h b/datapoollocal/LocalPoolDataSetBase.h index c676285c6..8a3a63394 100644 --- a/datapoollocal/LocalPoolDataSetBase.h +++ b/datapoollocal/LocalPoolDataSetBase.h @@ -160,13 +160,6 @@ public: object_id_t getCreatorObjectId(); - /* Static helper functions for manipulating validity buffers */ - /** - * Set n-th bit of a byte, with n being the position from 0 - * (most significant bit) to 7 (least significant bit) - */ - static void bitSetter(uint8_t* byte, uint8_t position); - static bool bitGetter(const uint8_t* byte, uint8_t position); protected: sid_t sid; //! This mutex is used if the data is created by one object only. diff --git a/globalfunctions/CMakeLists.txt b/globalfunctions/CMakeLists.txt index 2b3dcf8ed..5ccd3c4cc 100644 --- a/globalfunctions/CMakeLists.txt +++ b/globalfunctions/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(${LIB_FSFW_NAME} PeriodicOperationDivider.cpp timevalOperations.cpp Type.cpp + bitutility.cpp ) -add_subdirectory(math) \ No newline at end of file +add_subdirectory(math) diff --git a/globalfunctions/bitutility.cpp b/globalfunctions/bitutility.cpp new file mode 100644 index 000000000..5cc92dac5 --- /dev/null +++ b/globalfunctions/bitutility.cpp @@ -0,0 +1,33 @@ +#include "bitutility.h" + +void bitutil::bitSet(uint8_t *byte, uint8_t position) { + if(position > 7) { + return; + } + uint8_t shiftNumber = position + (7 - 2 * position); + *byte |= 1 << shiftNumber; +} + +void bitutil::bitToggle(uint8_t *byte, uint8_t position) { + if(position > 7) { + return; + } + uint8_t shiftNumber = position + (7 - 2 * position); + *byte ^= 1 << shiftNumber; +} + +void bitutil::bitClear(uint8_t *byte, uint8_t position) { + if(position > 7) { + return; + } + uint8_t shiftNumber = position + (7 - 2 * position); + *byte &= ~(1 << shiftNumber); +} + +bool bitutil::bitGet(const uint8_t *byte, uint8_t position) { + if(position > 7) { + return false; + } + uint8_t shiftNumber = position + (7 - 2 * position); + return *byte & (1 << shiftNumber); +} diff --git a/globalfunctions/bitutility.h b/globalfunctions/bitutility.h new file mode 100644 index 000000000..1fc1290dc --- /dev/null +++ b/globalfunctions/bitutility.h @@ -0,0 +1,18 @@ +#ifndef FSFW_GLOBALFUNCTIONS_BITUTIL_H_ +#define FSFW_GLOBALFUNCTIONS_BITUTIL_H_ + +#include + +namespace bitutil { + +/* Helper functions for manipulating the individual bits of a byte. +Position refers to n-th bit of a byte, going from 0 (most significant bit) to +7 (least significant bit) */ +void bitSet(uint8_t* byte, uint8_t position); +void bitToggle(uint8_t* byte, uint8_t position); +void bitClear(uint8_t* byte, uint8_t position); +bool bitGet(const uint8_t* byte, uint8_t position); + +} + +#endif /* FSFW_GLOBALFUNCTIONS_BITUTIL_H_ */ diff --git a/unittest/tests/datapoollocal/DataSetTest.cpp b/unittest/tests/datapoollocal/DataSetTest.cpp index 7b2632c2c..561345951 100644 --- a/unittest/tests/datapoollocal/DataSetTest.cpp +++ b/unittest/tests/datapoollocal/DataSetTest.cpp @@ -6,6 +6,8 @@ #include #include #include +#include + #include TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { @@ -160,12 +162,31 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { CHECK(rawUint16Vec[2] == 1); /* We can do it like this because the buffer only has one byte for less than 8 variables */ - uint8_t validityByte = buffer[sizeof(buffer) - 1]; - CHECK(LocalPoolDataSetBase::bitGetter(&validityByte, 0) == true); - CHECK(LocalPoolDataSetBase::bitGetter(&validityByte, 1) == false); - CHECK(LocalPoolDataSetBase::bitGetter(&validityByte, 2) == true); + uint8_t* validityByte = buffer + sizeof(buffer) - 1; + CHECK(bitutil::bitGet(validityByte, 0) == true); + CHECK(bitutil::bitGet(validityByte, 1) == false); + CHECK(bitutil::bitGet(validityByte, 2) == true); /* Now we manipulate the validity buffer for the deserialization */ + bitutil::bitClear(validityByte, 0); + bitutil::bitSet(validityByte, 1); + bitutil::bitClear(validityByte, 2); + /* Zero out everything except validity buffer */ + std::memset(buffer, 0, sizeof(buffer) - 1); + sizeToDeserialize = maxSize; + constBuffPtr = buffer; + CHECK(localSet.deSerialize(&constBuffPtr, &sizeToDeserialize, + SerializeIF::Endianness::MACHINE) == retval::CATCH_OK); + /* Check whether deserialization was successfull */ + CHECK(localSet.localPoolVarUint8.value == 0); + CHECK(localSet.localPoolVarFloat.value == Catch::Approx(0.0)); + CHECK(localSet.localPoolVarUint8.value == 0); + CHECK(localSet.localPoolUint16Vec.value[0] == 0); + CHECK(localSet.localPoolUint16Vec.value[1] == 0); + CHECK(localSet.localPoolUint16Vec.value[2] == 0); + CHECK(not localSet.localPoolVarUint8.isValid()); + CHECK(localSet.localPoolVarFloat.isValid()); + CHECK(not localSet.localPoolUint16Vec.isValid()); } From 5cf2197c06b15472eacb07eabc974d10491f49a8 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Feb 2021 17:33:54 +0100 Subject: [PATCH 23/59] minor formatting stuff --- datapool/PoolDataSetBase.h | 7 ++++--- datapool/PoolDataSetIF.h | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/datapool/PoolDataSetBase.h b/datapool/PoolDataSetBase.h index ab8954557..a551b815b 100644 --- a/datapool/PoolDataSetBase.h +++ b/datapool/PoolDataSetBase.h @@ -29,9 +29,10 @@ * @author Bastian Baetz * @ingroup data_pool */ -class PoolDataSetBase: public PoolDataSetIF, - public SerializeIF, - public HasReturnvaluesIF { +class PoolDataSetBase: + public PoolDataSetIF, + public SerializeIF, + public HasReturnvaluesIF { public: /** diff --git a/datapool/PoolDataSetIF.h b/datapool/PoolDataSetIF.h index 1f52871d9..9151f2f8e 100644 --- a/datapool/PoolDataSetIF.h +++ b/datapool/PoolDataSetIF.h @@ -8,7 +8,9 @@ * @brief Extendes the DataSetIF by adding abstract functions to lock * and unlock a data pool and read/commit semantics. */ -class PoolDataSetIF: public DataSetIF, public ReadCommitIF { +class PoolDataSetIF: + public DataSetIF, + public ReadCommitIF { public: virtual~ PoolDataSetIF() {}; From 2b6ccbc17f106a95a95fc04524c1334af10bd524 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 1 Mar 2021 12:26:16 +0100 Subject: [PATCH 24/59] renamed receivers list, functions protected --- datapoollocal/LocalDataPoolManager.cpp | 14 +++++++------- datapoollocal/LocalDataPoolManager.h | 6 ++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index 19e235881..c15182d26 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -98,7 +98,7 @@ ReturnValue_t LocalDataPoolManager::initializeHousekeepingPoolEntriesOnce() { ReturnValue_t LocalDataPoolManager::performHkOperation() { ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; - for(auto& receiver: hkReceiversMap) { + for(auto& receiver: hkReceivers) { switch(receiver.reportingType) { case(ReportingType::PERIODIC): { if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { @@ -375,7 +375,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(sid_t sid, owner->getPeriodicOperationFrequency(), isDiagnostics); } - hkReceiversMap.push_back(hkReceiver); + hkReceivers.push_back(hkReceiver); return HasReturnvaluesIF::RETURN_OK; } @@ -404,7 +404,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForUpdatePacket(sid_t sid, LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics); } - hkReceiversMap.push_back(hkReceiver); + hkReceivers.push_back(hkReceiver); handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); return HasReturnvaluesIF::RETURN_OK; @@ -425,7 +425,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessage( hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION; } - hkReceiversMap.push_back(hkReceiver); + hkReceivers.push_back(hkReceiver); handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); return HasReturnvaluesIF::RETURN_OK; @@ -446,7 +446,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForVariableUpdateMessage( hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION; } - hkReceiversMap.push_back(hkReceiver); + hkReceivers.push_back(hkReceiver); handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); return HasReturnvaluesIF::RETURN_OK; @@ -829,8 +829,8 @@ ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid, } void LocalDataPoolManager::clearReceiversList() { - // clear the vector completely and releases allocated memory. - HkReceivers().swap(hkReceiversMap); + /* Clear the vector completely and releases allocated memory. */ + HkReceivers().swap(hkReceivers); } MutexIF* LocalDataPoolManager::getLocalPoolMutex() { diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h index fdfd9d233..241472581 100644 --- a/datapoollocal/LocalDataPoolManager.h +++ b/datapoollocal/LocalDataPoolManager.h @@ -271,7 +271,9 @@ public: MutexIF* getMutexHandle(); virtual LocalDataPoolManager* getPoolManagerHandle() override; -private: + +protected: + localpool::DataPool localPoolMap; /** Every housekeeping data manager has a mutex to protect access to it's data pool. */ @@ -307,7 +309,7 @@ private: /** This vector will contain the list of HK receivers. */ using HkReceivers = std::vector; - HkReceivers hkReceiversMap; + HkReceivers hkReceivers; struct HkUpdateResetHelper { DataType dataType = DataType::DATA_SET; From 9f09c190bb09e5b368a6f4779b840264e15dfabd Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 1 Mar 2021 12:32:54 +0100 Subject: [PATCH 25/59] formatting change --- datapoollocal/LocalDataPoolManager.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h index 241472581..2e673f977 100644 --- a/datapoollocal/LocalDataPoolManager.h +++ b/datapoollocal/LocalDataPoolManager.h @@ -397,8 +397,7 @@ protected: template inline -ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, - PoolEntry **poolEntry) { +ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, PoolEntry **poolEntry) { auto poolIter = localPoolMap.find(localPoolId); if (poolIter == localPoolMap.end()) { printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry", From 9e9113912b6f6f906adcce501a7c7f08461967f9 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 1 Mar 2021 12:36:18 +0100 Subject: [PATCH 26/59] minor formatting stuff --- datapoollocal/LocalDataPoolManager.h | 33 ++++++++----------- .../internal/LocalDpManagerAttorney.h | 1 - datapoollocal/localPoolDefinitions.h | 4 +-- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h index 2e673f977..ff6edb95d 100644 --- a/datapoollocal/LocalDataPoolManager.h +++ b/datapoollocal/LocalDataPoolManager.h @@ -274,6 +274,7 @@ public: protected: + /** Core data structure for the actual pool data */ localpool::DataPool localPoolMap; /** Every housekeeping data manager has a mutex to protect access to it's data pool. */ @@ -319,7 +320,8 @@ protected: }; using HkUpdateResetList = std::vector; - // Will only be created when needed. + /** This list is used to manage creating multiple update packets and only resetting + the update flag if all of them were created. Will only be created when needed. */ HkUpdateResetList* hkUpdateResetList = nullptr; /** This is the map holding the actual data. Should only be initialized @@ -343,16 +345,14 @@ protected: * Read a variable by supplying its local pool ID and assign the pool * entry to the supplied PoolEntry pointer. The type of the pool entry * is deduced automatically. This call is not thread-safe! - * For now, only friend classes like LocalPoolVar may access this - * function. + * For now, only classes designated by the LocalDpManagerAttorney may use this function. * @tparam T Type of the pool entry * @param localPoolId Pool ID of the variable to read * @param poolVar [out] Corresponding pool entry will be assigned to the * supplied pointer. * @return */ - template ReturnValue_t fetchPoolEntry(lp_id_t localPoolId, - PoolEntry **poolEntry); + template ReturnValue_t fetchPoolEntry(lp_id_t localPoolId, PoolEntry **poolEntry); /** * This function is used to fill the local data pool map with pool @@ -364,15 +364,13 @@ protected: MutexIF* getLocalPoolMutex() override; - ReturnValue_t serializeHkPacketIntoStore( - HousekeepingPacketDownlink& hkPacket, + ReturnValue_t serializeHkPacketIntoStore(HousekeepingPacketDownlink& hkPacket, store_address_t& storeId, bool forDownlink, size_t* serializedSize); void performPeriodicHkGeneration(HkReceiver& hkReceiver); - ReturnValue_t togglePeriodicGeneration(sid_t sid, bool enable, + ReturnValue_t togglePeriodicGeneration(sid_t sid, bool enable, bool isDiagnostics); + ReturnValue_t changeCollectionInterval(sid_t sid, float newCollectionInterval, bool isDiagnostics); - ReturnValue_t changeCollectionInterval(sid_t sid, - float newCollectionInterval, bool isDiagnostics); ReturnValue_t generateSetStructurePacket(sid_t sid, bool isDiagnostics); void handleHkUpdateResetListInsertion(DataType dataType, DataId dataId); @@ -380,17 +378,12 @@ protected: DataId dataId, MarkChangedIF* toReset); void resetHkUpdateResetHelper(); - ReturnValue_t handleHkUpdate(HkReceiver& hkReceiver, - ReturnValue_t& status); - ReturnValue_t handleNotificationUpdate(HkReceiver& hkReceiver, - ReturnValue_t& status); - ReturnValue_t handleNotificationSnapshot(HkReceiver& hkReceiver, - ReturnValue_t& status); - ReturnValue_t addUpdateToStore(HousekeepingSnapshot& updatePacket, - store_address_t& storeId); + ReturnValue_t handleHkUpdate(HkReceiver& hkReceiver, ReturnValue_t& status); + ReturnValue_t handleNotificationUpdate(HkReceiver& hkReceiver, ReturnValue_t& status); + ReturnValue_t handleNotificationSnapshot(HkReceiver& hkReceiver, ReturnValue_t& status); + ReturnValue_t addUpdateToStore(HousekeepingSnapshot& updatePacket, store_address_t& storeId); - void printWarningOrError(sif::OutputTypes outputType, - const char* functionName, + void printWarningOrError(sif::OutputTypes outputType, const char* functionName, ReturnValue_t errorCode = HasReturnvaluesIF::RETURN_FAILED, const char* errorPrint = nullptr); }; diff --git a/datapoollocal/internal/LocalDpManagerAttorney.h b/datapoollocal/internal/LocalDpManagerAttorney.h index bb64fb422..994f0613b 100644 --- a/datapoollocal/internal/LocalDpManagerAttorney.h +++ b/datapoollocal/internal/LocalDpManagerAttorney.h @@ -24,7 +24,6 @@ private: return manager.getMutexHandle(); } - template friend class LocalPoolVariable; template friend class LocalPoolVector; }; diff --git a/datapoollocal/localPoolDefinitions.h b/datapoollocal/localPoolDefinitions.h index ff44fb8e7..af8ce7115 100644 --- a/datapoollocal/localPoolDefinitions.h +++ b/datapoollocal/localPoolDefinitions.h @@ -21,8 +21,8 @@ static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF; static constexpr ReturnValue_t POOL_ENTRY_NOT_FOUND = MAKE_RETURN_CODE(0x00); static constexpr ReturnValue_t POOL_ENTRY_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01); -//! This is the core data structure of the local data pools. Users should insert all desired -//! pool variables, using the std::map interface. +/** This is the core data structure of the local data pools. Users should insert all desired +pool variables, using the std::map interface. */ using DataPool = std::map; using DataPoolMapIter = DataPool::iterator; From cc84d542c8a1b44bf16650aa9aaf2343d848c817 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 1 Mar 2021 16:46:45 +0100 Subject: [PATCH 27/59] added static function to create command id --- ipc/CommandMessageIF.h | 93 ++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/ipc/CommandMessageIF.h b/ipc/CommandMessageIF.h index aafa40ef8..898f69827 100644 --- a/ipc/CommandMessageIF.h +++ b/ipc/CommandMessageIF.h @@ -13,60 +13,63 @@ public: /** * Header consists of sender ID and command ID. */ - static constexpr size_t HEADER_SIZE = MessageQueueMessageIF::HEADER_SIZE + - sizeof(Command_t); - /** - * This minimum size is derived from the interface requirement to be able + static constexpr size_t HEADER_SIZE = MessageQueueMessageIF::HEADER_SIZE + + sizeof(Command_t); + /** + * This minimum size is derived from the interface requirement to be able * to set a rejected reply, which contains a returnvalue and the initial * command. - */ - static constexpr size_t MINIMUM_COMMAND_MESSAGE_SIZE = - CommandMessageIF::HEADER_SIZE + sizeof(ReturnValue_t) + - sizeof(Command_t); + */ + static constexpr size_t MINIMUM_COMMAND_MESSAGE_SIZE = CommandMessageIF::HEADER_SIZE + + sizeof(ReturnValue_t) + sizeof(Command_t); - static const uint8_t INTERFACE_ID = CLASS_ID::COMMAND_MESSAGE; - static const ReturnValue_t UNKNOWN_COMMAND = MAKE_RETURN_CODE(0x01); + static Command_t makeCommandId(uint8_t messageId, uint8_t uniqueId) { + return ((messageId << 8) | uniqueId); + } - static const uint8_t MESSAGE_ID = messagetypes::COMMAND; - //! Used internally, shall be ignored - static const Command_t CMD_NONE = MAKE_COMMAND_ID( 0 ); - static const Command_t REPLY_COMMAND_OK = MAKE_COMMAND_ID( 1 ); - //! Reply indicating that the current command was rejected, - //! par1 should contain the error code - static const Command_t REPLY_REJECTED = MAKE_COMMAND_ID( 2 ); + static const uint8_t INTERFACE_ID = CLASS_ID::COMMAND_MESSAGE; + static const ReturnValue_t UNKNOWN_COMMAND = MAKE_RETURN_CODE(0x01); - virtual ~CommandMessageIF() {}; + static const uint8_t MESSAGE_ID = messagetypes::COMMAND; + //! Used internally, shall be ignored + static const Command_t CMD_NONE = MAKE_COMMAND_ID( 0 ); + static const Command_t REPLY_COMMAND_OK = MAKE_COMMAND_ID( 1 ); + //! Reply indicating that the current command was rejected, + //! par1 should contain the error code + static const Command_t REPLY_REJECTED = MAKE_COMMAND_ID( 2 ); - /** - * A command message shall have a uint16_t command ID field. - * @return - */ - virtual Command_t getCommand() const = 0; - /** - * A command message shall have a uint8_t message type ID field. - * @return - */ - virtual uint8_t getMessageType() const = 0; + virtual ~CommandMessageIF() {}; - /** - * A command message can be rejected and needs to offer a function - * to set a rejected reply - * @param reason - * @param initialCommand - */ - virtual void setReplyRejected(ReturnValue_t reason, - Command_t initialCommand) = 0; - /** - * Corrensonding getter function. - * @param initialCommand - * @return - */ - virtual ReturnValue_t getReplyRejectedReason( - Command_t* initialCommand = nullptr) const = 0; + /** + * A command message shall have a uint16_t command ID field. + * @return + */ + virtual Command_t getCommand() const = 0; + /** + * A command message shall have a uint8_t message type ID field. + * @return + */ + virtual uint8_t getMessageType() const = 0; - virtual void setToUnknownCommand() = 0; + /** + * A command message can be rejected and needs to offer a function + * to set a rejected reply + * @param reason + * @param initialCommand + */ + virtual void setReplyRejected(ReturnValue_t reason, + Command_t initialCommand) = 0; + /** + * Corrensonding getter function. + * @param initialCommand + * @return + */ + virtual ReturnValue_t getReplyRejectedReason( + Command_t* initialCommand = nullptr) const = 0; - virtual void clear() = 0; + virtual void setToUnknownCommand() = 0; + + virtual void clear() = 0; }; From 1d3438bb7de151cbcc27663ffa58dc58e6821b39 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 2 Mar 2021 01:04:51 +0100 Subject: [PATCH 28/59] updated HasFileSystemIF --- memory/HasFileSystemIF.h | 20 ++++++++++++-------- returnvalues/FwClassIds.h | 1 + 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/memory/HasFileSystemIF.h b/memory/HasFileSystemIF.h index dcec6346e..52360f0c7 100644 --- a/memory/HasFileSystemIF.h +++ b/memory/HasFileSystemIF.h @@ -16,17 +16,21 @@ class HasFileSystemIF { public: static constexpr uint8_t INTERFACE_ID = CLASS_ID::FILE_SYSTEM; - static constexpr ReturnValue_t FILE_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x00); - static constexpr ReturnValue_t FILE_ALREADY_EXISTS = MAKE_RETURN_CODE(0x01); - static constexpr ReturnValue_t FILE_LOCKED = MAKE_RETURN_CODE(0x02); + //! [EXPORT] : P1: Can be file system specific error code + static constexpr ReturnValue_t GENERIC_FILE_ERROR = MAKE_RETURN_CODE(0); - static constexpr ReturnValue_t DIRECTORY_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x03); - static constexpr ReturnValue_t DIRECTORY_ALREADY_EXISTS = MAKE_RETURN_CODE(0x04); - static constexpr ReturnValue_t DIRECTORY_NOT_EMPTY = MAKE_RETURN_CODE(0x05); + static constexpr ReturnValue_t FILE_DOES_NOT_EXIST = MAKE_RETURN_CODE(1); + static constexpr ReturnValue_t FILE_ALREADY_EXISTS = MAKE_RETURN_CODE(2); + static constexpr ReturnValue_t FILE_LOCKED = MAKE_RETURN_CODE(3); + static constexpr ReturnValue_t DIRECTORY_DOES_NOT_EXIST = MAKE_RETURN_CODE(4); + static constexpr ReturnValue_t DIRECTORY_ALREADY_EXISTS = MAKE_RETURN_CODE(5); + static constexpr ReturnValue_t DIRECTORY_NOT_EMPTY = MAKE_RETURN_CODE(6); - static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_WRITE = MAKE_RETURN_CODE(0x06); //! P1: Sequence number missing - static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_READ = MAKE_RETURN_CODE(0x07); //! P1: Sequence number missing + //! [EXPORT] : P1: Sequence number missing + static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_WRITE = MAKE_RETURN_CODE(7); + //! [EXPORT] : P1: Sequence number missing + static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_READ = MAKE_RETURN_CODE(8); virtual ~HasFileSystemIF() {} /** diff --git a/returnvalues/FwClassIds.h b/returnvalues/FwClassIds.h index 53add6a0a..4c9f022ba 100644 --- a/returnvalues/FwClassIds.h +++ b/returnvalues/FwClassIds.h @@ -65,6 +65,7 @@ enum { HOUSEKEEPING_MANAGER, //HKM 60 DLE_ENCODER, //DLEE 61 PUS_SERVICE_9, //PUS9 62 + FILE_SYSTEM, //FILS 63 FW_CLASS_ID_COUNT //is actually count + 1 ! }; From 91db9c362eb214741b46969864bd63f8fd9fedfb Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 2 Mar 2021 12:35:19 +0100 Subject: [PATCH 29/59] added additional returnvalues --- memory/HasFileSystemIF.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/memory/HasFileSystemIF.h b/memory/HasFileSystemIF.h index 52360f0c7..cb4ca9309 100644 --- a/memory/HasFileSystemIF.h +++ b/memory/HasFileSystemIF.h @@ -32,6 +32,11 @@ public: //! [EXPORT] : P1: Sequence number missing static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_READ = MAKE_RETURN_CODE(8); + //! [EXPORT] : File system is currently busy + static constexpr ReturnValue_t IS_BUSY = MAKE_RETURN_CODE(9); + //! [EXPORT] : Invalid parameters like file name or repository path + static constexpr ReturnValue_t INVALID_PARAMETERS = MAKE_RETURN_CODE(10); + virtual ~HasFileSystemIF() {} /** * Function to get the MessageQueueId_t of the implementing object From 3f47db9c18d35a6d01a7a087ad41759eb3f409a2 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 2 Mar 2021 12:36:13 +0100 Subject: [PATCH 30/59] reordered returnvalues --- memory/HasFileSystemIF.h | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/memory/HasFileSystemIF.h b/memory/HasFileSystemIF.h index cb4ca9309..73c934105 100644 --- a/memory/HasFileSystemIF.h +++ b/memory/HasFileSystemIF.h @@ -18,24 +18,25 @@ public: //! [EXPORT] : P1: Can be file system specific error code static constexpr ReturnValue_t GENERIC_FILE_ERROR = MAKE_RETURN_CODE(0); - - static constexpr ReturnValue_t FILE_DOES_NOT_EXIST = MAKE_RETURN_CODE(1); - static constexpr ReturnValue_t FILE_ALREADY_EXISTS = MAKE_RETURN_CODE(2); - static constexpr ReturnValue_t FILE_LOCKED = MAKE_RETURN_CODE(3); - - static constexpr ReturnValue_t DIRECTORY_DOES_NOT_EXIST = MAKE_RETURN_CODE(4); - static constexpr ReturnValue_t DIRECTORY_ALREADY_EXISTS = MAKE_RETURN_CODE(5); - static constexpr ReturnValue_t DIRECTORY_NOT_EMPTY = MAKE_RETURN_CODE(6); - - //! [EXPORT] : P1: Sequence number missing - static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_WRITE = MAKE_RETURN_CODE(7); - //! [EXPORT] : P1: Sequence number missing - static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_READ = MAKE_RETURN_CODE(8); - //! [EXPORT] : File system is currently busy - static constexpr ReturnValue_t IS_BUSY = MAKE_RETURN_CODE(9); + static constexpr ReturnValue_t IS_BUSY = MAKE_RETURN_CODE(1); //! [EXPORT] : Invalid parameters like file name or repository path - static constexpr ReturnValue_t INVALID_PARAMETERS = MAKE_RETURN_CODE(10); + static constexpr ReturnValue_t INVALID_PARAMETERS = MAKE_RETURN_CODE(2); + + static constexpr ReturnValue_t FILE_DOES_NOT_EXIST = MAKE_RETURN_CODE(5); + static constexpr ReturnValue_t FILE_ALREADY_EXISTS = MAKE_RETURN_CODE(6); + static constexpr ReturnValue_t FILE_LOCKED = MAKE_RETURN_CODE(7); + + static constexpr ReturnValue_t DIRECTORY_DOES_NOT_EXIST = MAKE_RETURN_CODE(10); + static constexpr ReturnValue_t DIRECTORY_ALREADY_EXISTS = MAKE_RETURN_CODE(11); + static constexpr ReturnValue_t DIRECTORY_NOT_EMPTY = MAKE_RETURN_CODE(12); + + //! [EXPORT] : P1: Sequence number missing + static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_WRITE = MAKE_RETURN_CODE(15); + //! [EXPORT] : P1: Sequence number missing + static constexpr ReturnValue_t SEQUENCE_PACKET_MISSING_READ = MAKE_RETURN_CODE(16); + + virtual ~HasFileSystemIF() {} /** From 1caa45118b9bdeec471086b15da5893a23a82ca0 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 2 Mar 2021 14:58:49 +0100 Subject: [PATCH 31/59] makecommandid is constexpr now --- ipc/CommandMessageIF.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ipc/CommandMessageIF.h b/ipc/CommandMessageIF.h index 898f69827..468e24693 100644 --- a/ipc/CommandMessageIF.h +++ b/ipc/CommandMessageIF.h @@ -13,8 +13,7 @@ public: /** * Header consists of sender ID and command ID. */ - static constexpr size_t HEADER_SIZE = MessageQueueMessageIF::HEADER_SIZE + - sizeof(Command_t); + static constexpr size_t HEADER_SIZE = MessageQueueMessageIF::HEADER_SIZE + sizeof(Command_t); /** * This minimum size is derived from the interface requirement to be able * to set a rejected reply, which contains a returnvalue and the initial @@ -23,12 +22,12 @@ public: static constexpr size_t MINIMUM_COMMAND_MESSAGE_SIZE = CommandMessageIF::HEADER_SIZE + sizeof(ReturnValue_t) + sizeof(Command_t); - static Command_t makeCommandId(uint8_t messageId, uint8_t uniqueId) { + static constexpr Command_t makeCommandId(uint8_t messageId, uint8_t uniqueId) { return ((messageId << 8) | uniqueId); } static const uint8_t INTERFACE_ID = CLASS_ID::COMMAND_MESSAGE; - static const ReturnValue_t UNKNOWN_COMMAND = MAKE_RETURN_CODE(0x01); + static const ReturnValue_t UNKNOWN_COMMAND = MAKE_RETURN_CODE(1); static const uint8_t MESSAGE_ID = messagetypes::COMMAND; //! Used internally, shall be ignored From 9a3cd1d7fc5b069c655ba1bf53a8273df34b9bc8 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Wed, 3 Mar 2021 15:13:03 +0100 Subject: [PATCH 32/59] success flag not explicitely expected for actions --- action/ActionHelper.cpp | 6 +++--- action/ActionHelper.h | 4 ++-- action/ActionMessage.cpp | 7 ++++--- action/ActionMessage.h | 8 ++++++-- action/SimpleActionHelper.cpp | 2 +- devicehandlers/DeviceHandlerBase.cpp | 8 ++++++-- unittest/tests/action/TestActionHelper.cpp | 2 +- 7 files changed, 23 insertions(+), 14 deletions(-) diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index e32be68b2..42ee4022f 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 35ac41d1c..c90247478 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 c3eb47109..15b59b9dc 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 246ac601d..df97485b9 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 f77d01473..979ee3ad4 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/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 35d34bf9b..15eac11f4 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/unittest/tests/action/TestActionHelper.cpp b/unittest/tests/action/TestActionHelper.cpp index 2493a6b8e..a7adfc82d 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)); From 6580aa73bf587afddcdfd908844dabe4add36489 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Wed, 3 Mar 2021 17:13:37 +0100 Subject: [PATCH 33/59] updated clock module --- osal/FreeRTOS/Clock.cpp | 214 +++++++++++++++++++------------------- timemanager/CCSDSTime.cpp | 16 ++- 2 files changed, 119 insertions(+), 111 deletions(-) diff --git a/osal/FreeRTOS/Clock.cpp b/osal/FreeRTOS/Clock.cpp index 9c0a02679..806edcc7c 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 aefcac2eb..8f0d1c314 100644 --- a/timemanager/CCSDSTime.cpp +++ b/timemanager/CCSDSTime.cpp @@ -396,12 +396,20 @@ ReturnValue_t CCSDSTime::convertToCcsds(OBT_FLP* to, const timeval* from) { ReturnValue_t CCSDSTime::convertFromCcsds(timeval* to, const uint8_t* from, size_t* foundLength, size_t maxLength) { - //We don't expect ascii here. SHOULDDO + if(maxLength >= 19) { + Clock::TimeOfDay_t timeOfDay; + /* Try to parse it as ASCII */ + ReturnValue_t result = convertFromASCII(&timeOfDay, from, maxLength); + if (result == RETURN_OK) { + return Clock::convertTimeOfDayToTimeval(&timeOfDay, to); + } + } + uint8_t codeIdentification = (*from >> 4); switch (codeIdentification) { - //unsupported, as Leap second correction would have to be applied -// case CUC_LEVEL1: -// return convertFromCUC(to, from, foundLength, maxLength); + /* 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: From bff8d103ba77837b63b91577ff30c52926d625ef Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 3 Mar 2021 22:45:44 +0100 Subject: [PATCH 34/59] form stuff and cleaning up --- datapoollocal/LocalDataPoolManager.cpp | 8 ++++---- datapoollocal/LocalPoolVariable.tpp | 10 ---------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index c15182d26..72873d993 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/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp index 35de4ebdf..aa789a6dd 100644 --- a/datapoollocal/LocalPoolVariable.tpp +++ b/datapoollocal/LocalPoolVariable.tpp @@ -43,7 +43,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 +50,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; From d8d18c93335f3e850deda56aa10ace757d8e6c2a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 3 Mar 2021 23:49:27 +0100 Subject: [PATCH 35/59] fixed order --- controller/ExtendedControllerBase.cpp | 8 +++++--- datapoollocal/LocalPoolDataSetBase.cpp | 3 +-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/controller/ExtendedControllerBase.cpp b/controller/ExtendedControllerBase.cpp index cc93efa86..95ba012eb 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/datapoollocal/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp index 75a3ab7c2..d043d18f5 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( From dae4a5fa7447dbd9bf49242facbd4c20509e8dc4 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 16:36:34 +0100 Subject: [PATCH 36/59] small form stuff --- datapoollocal/HasLocalDataPoolIF.h | 4 ++-- datapoollocal/LocalPoolDataSetBase.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/datapoollocal/HasLocalDataPoolIF.h b/datapoollocal/HasLocalDataPoolIF.h index 49f2db7d3..d52c72b6c 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/LocalPoolDataSetBase.h b/datapoollocal/LocalPoolDataSetBase.h index 8a3a63394..9501f7821 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. From 21a7fd621d69d1058a2a55ddc6a4867021b0fb85 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 16:38:35 +0100 Subject: [PATCH 37/59] renamed guard class --- datapool/{PoolReadHelper.h => PoolReadGuard.h} | 6 +++--- unittest/tests/datapoollocal/LocalPoolOwnerBase.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) rename datapool/{PoolReadHelper.h => PoolReadGuard.h} (94%) diff --git a/datapool/PoolReadHelper.h b/datapool/PoolReadGuard.h similarity index 94% rename from datapool/PoolReadHelper.h rename to datapool/PoolReadGuard.h index 9825e83c1..eb014f1e7 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/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index bb090e3b3..5c277850f 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(); } From d66e486f166f3a9d926be4f270a22caf37f6243e Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 16:44:17 +0100 Subject: [PATCH 38/59] whitespaces instead of tabs --- timemanager/CCSDSTime.cpp | 954 +++++++++++++++++++------------------- timemanager/CCSDSTime.h | 370 +++++++-------- 2 files changed, 662 insertions(+), 662 deletions(-) diff --git a/timemanager/CCSDSTime.cpp b/timemanager/CCSDSTime.cpp index 8f0d1c314..192c1f57a 100644 --- a/timemanager/CCSDSTime.cpp +++ b/timemanager/CCSDSTime.cpp @@ -12,390 +12,390 @@ 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) { + size_t* foundLength, size_t maxLength) { if(maxLength >= 19) { Clock::TimeOfDay_t timeOfDay; /* Try to parse it as ASCII */ @@ -405,210 +405,210 @@ ReturnValue_t CCSDSTime::convertFromCcsds(timeval* to, const uint8_t* from, } } - 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; - } + 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 d74adc61d..de151852a 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_ */ From 4250c7e022ede038d756d4a88e7b206377d93d3d Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 16:45:35 +0100 Subject: [PATCH 39/59] updated changelog --- CHANGELOG | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index fc46ee021..bd41de5a1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -82,6 +82,10 @@ 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 + +- ActionHelper finish function 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). From bd903b844776f80718ad74d6132c74046aae7607 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 16:46:39 +0100 Subject: [PATCH 40/59] changelog update --- CHANGELOG | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bd41de5a1..235ed7d37 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,9 +87,11 @@ 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 +### ActionHelper and ActionMessage -- ActionHelper finish function now expects explicit information whether to report a success or failure message instead of deriving it implicitely from returnvalue +- 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 From 043d47e5e43348a79fc50a7550ddcc0caeab2dba Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 16:48:21 +0100 Subject: [PATCH 41/59] removing whitespaces --- CHANGELOG | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 235ed7d37..add8e1a5b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -22,8 +22,8 @@ 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 +- 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 @@ -42,7 +42,7 @@ important use-case) ### File System Interface -- A new interfaces specifies the functions for a software object which exposes the file system of +- 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 From 35825a6561379506e092a02266efe833743fc862 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 17:27:03 +0100 Subject: [PATCH 42/59] new functions to set all vars read only --- datapool/PoolVariableIF.h | 3 ++- datapoollocal/LocalPoolDataSetBase.cpp | 7 ++++++- datapoollocal/LocalPoolDataSetBase.h | 6 ++++++ doc/README-localpools.md | 26 ++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/datapool/PoolVariableIF.h b/datapool/PoolVariableIF.h index dead6844a..dbb9db15c 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/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp index d043d18f5..daa54fb33 100644 --- a/datapoollocal/LocalPoolDataSetBase.cpp +++ b/datapoollocal/LocalPoolDataSetBase.cpp @@ -288,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; @@ -301,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 9501f7821..404509ae5 100644 --- a/datapoollocal/LocalPoolDataSetBase.h +++ b/datapoollocal/LocalPoolDataSetBase.h @@ -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/doc/README-localpools.md b/doc/README-localpools.md index 8dd83385e..57a41aa14 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -1,3 +1,29 @@ ## Local Data Pools +The local data pools can be used to store data like sensor values so they can be used +by other software objects like controllers as well. If a 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 class +2. Implement the `HasLocalDataPoolIF` + +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. +Two important framework classes 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 is done via proxy classes like pool variable classes or dataset classes. +Generally, a user will create a dataset class which in turn groups all cohesive pool variables. + +The user can then use this set class to `read` the variables and `commit` changed variables +back into the pool. + +### Using the local data pools of the `DeviceHandlerBase` + +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. The From 8de33f13014f059f53767968eb11f8fe8a1ad32d Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 17:46:42 +0100 Subject: [PATCH 43/59] added local pool doc --- doc/README-localpools.md | 134 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 4 deletions(-) diff --git a/doc/README-localpools.md b/doc/README-localpools.md index 57a41aa14..9e7ddeacf 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -1,4 +1,4 @@ -## Local Data Pools +## Local Data Pools Developer Information The local data pools can be used to store data like sensor values so they can be used by other software objects like controllers as well. If a class should have a local pool which @@ -20,10 +20,136 @@ access to the pool data is done via proxy classes like pool variable classes or Generally, a user will create a dataset class which in turn groups all cohesive pool variables. The user can then use this set class to `read` the variables and `commit` changed variables -back into the pool. +back into the pool. The general approach is that a user will create a header containing the set +class. For example, the following code shows an implementation to access data from a Gyroscope: -### Using the local data pools of the `DeviceHandlerBase` +```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 constructor for users which sets all variables to read-only and there is the constructor +for the GyroHandler data creator. 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. The +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 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, we have received 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; +} +``` + +### Using the local data pools in a `ExtendedControllerBase` subclass + +Coming soon + + From 23873f6bc60cdef68e022e6bfd9d2ddd65b1723e Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 17:58:07 +0100 Subject: [PATCH 44/59] improved readme --- doc/README-localpools.md | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/doc/README-localpools.md b/doc/README-localpools.md index 9e7ddeacf..399400a4b 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -1,27 +1,37 @@ ## Local Data Pools Developer Information The local data pools can be used to store data like sensor values so they can be used -by other software objects like controllers as well. If a class should have a local pool which +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 class -2. Implement the `HasLocalDataPoolIF` +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. -Two important framework classes already perform the two steps shown above so the steps required -are altered slightly. +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 is done via proxy classes like pool variable classes or dataset classes. -Generally, a user will create a dataset class which in turn groups all cohesive pool variables. +access to the pool data from the point of a mission software developer is given in form of +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 user can then use this set class to `read` the variables and `commit` changed variables -back into the pool. The general approach is that a user will create a header containing the set -class. For example, the following code shows an implementation to access data from a Gyroscope: +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)> { @@ -53,8 +63,9 @@ private: }; ``` -There is a constructor for users which sets all variables to read-only and there is the constructor -for the GyroHandler data creator. Both the atittude controller and the `GyroHandler` can now +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 makring 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 @@ -148,6 +159,8 @@ if(readHelper.getReadResult() == HasReturnvaluesIF::RETURN_OK) { } ``` +The guard class will commit the changed data on destruction automatically. + ### Using the local data pools in a `ExtendedControllerBase` subclass Coming soon From a7878aaf0446b85a71da8522a7e3f6807407cbe1 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 18:06:11 +0100 Subject: [PATCH 45/59] readme update --- doc/README-localpools.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/README-localpools.md b/doc/README-localpools.md index 399400a4b..223b9d2b4 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -103,8 +103,8 @@ GyroHandler::GyroHandler(object_id_t objectId, object_id_t comIF, 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 in some way so that the local pool manager -can access the custom data sets as well. +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, From d84003d62a08ddc722e0363bf4cfe381d3f44785 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 18:07:10 +0100 Subject: [PATCH 46/59] updated READMe --- doc/README-localpools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README-localpools.md b/doc/README-localpools.md index 223b9d2b4..8b1fc45bb 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -142,7 +142,7 @@ ReturnValue_t GyroHandler::initializeLocalDataPool(localpool::DataPool &localDat } ``` -Now, we have received some sensor data and converted them into the right format, +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: From 6df1abf570e57e134e46c50d1b8aeff2f6f6404d Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 18:11:10 +0100 Subject: [PATCH 47/59] added graph --- doc/README-localpools.md | 6 +++++- doc/images/PoolArchitecture.png | Bin 0 -> 53460 bytes 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 doc/images/PoolArchitecture.png diff --git a/doc/README-localpools.md b/doc/README-localpools.md index 8b1fc45bb..041167975 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -15,7 +15,11 @@ The two former tasks are related to the external interface using telemetry and t 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. +shown above so the steps required are altered slightly. The following diagram shows the +high-level architecture of the local data pools. + + + ### Storing and Accessing pool data diff --git a/doc/images/PoolArchitecture.png b/doc/images/PoolArchitecture.png new file mode 100644 index 0000000000000000000000000000000000000000..13ea85c8886ee64629152d6ea4a2b3e58f7085be GIT binary patch literal 53460 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 A;s5{u literal 0 HcmV?d00001 From 69b428222ac26611acbf9ef8938516de0c0e6884 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 18:12:34 +0100 Subject: [PATCH 48/59] updated readme --- doc/README-localpools.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/doc/README-localpools.md b/doc/README-localpools.md index 041167975..2f9bba7f8 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -15,11 +15,7 @@ The two former tasks are related to the external interface using telemetry and t 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. The following diagram shows the -high-level architecture of the local data pools. - - - +shown above so the steps required are altered slightly. ### Storing and Accessing pool data @@ -31,7 +27,10 @@ on a `read` call. Changed variables can then be written to the local pool with a 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. +`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 e0d7363eed3bac2e2d1535be77c1b8f6e94025cc Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 18:14:29 +0100 Subject: [PATCH 49/59] better entry text --- doc/README-localpools.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/README-localpools.md b/doc/README-localpools.md index 2f9bba7f8..548fa2728 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -1,8 +1,9 @@ ## Local Data Pools Developer Information -The local data pools can be used 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: +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 @@ -67,7 +68,7 @@ private: ``` 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 makring it private and declaring the `GyroHandler` +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 From e501390d7be09ece647d829b63b81e2bc2dc6277 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 18:20:58 +0100 Subject: [PATCH 50/59] readme typo --- doc/README-localpools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README-localpools.md b/doc/README-localpools.md index 548fa2728..4ac2a4d8b 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -21,7 +21,7 @@ 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 a mission software developer is given in form of +access to the pool data from the point of view of a mission software developer is in form of 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. From a56cf4389754b577b86ca14d47a5dc7bf97ff2d1 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 18:22:32 +0100 Subject: [PATCH 51/59] another rwadme update --- doc/README-localpools.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/README-localpools.md b/doc/README-localpools.md index 4ac2a4d8b..96ae2d0af 100644 --- a/doc/README-localpools.md +++ b/doc/README-localpools.md @@ -21,8 +21,8 @@ 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 is in form of -proxy classes like pool variable classes. These classes store a copy +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 From 227074fd4d165a8433d55a4baeac18b5ef84d6da Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 18:45:32 +0100 Subject: [PATCH 52/59] increaed exception safety --- datapoollocal/LocalPoolObjectBase.cpp | 13 +++++++++---- datapoollocal/LocalPoolVariable.tpp | 26 ++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/datapoollocal/LocalPoolObjectBase.cpp b/datapoollocal/LocalPoolObjectBase.cpp index a64ed2b4a..b6db06089 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 aa789a6dd..ba7a413de 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 @@ -65,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 From bc7d956899f382ad9012e4e132908d4b569ae653 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 19:45:13 +0100 Subject: [PATCH 53/59] tiny form stuff --- datapoollocal/LocalPoolDataSetBase.cpp | 2 +- datapoollocal/LocalPoolVariable.tpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/datapoollocal/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp index daa54fb33..e5ea15983 100644 --- a/datapoollocal/LocalPoolDataSetBase.cpp +++ b/datapoollocal/LocalPoolDataSetBase.cpp @@ -36,7 +36,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner, this->sid.objectId = hkOwner->getObjectId(); this->sid.ownerSetId = setId; - // Data creators get a periodic helper for periodic HK data generation. + /* Data creators get a periodic helper for periodic HK data generation. */ if(periodicHandling) { periodicHelper = new PeriodicHousekeepingHelper(this); } diff --git a/datapoollocal/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp index ba7a413de..9bb30611b 100644 --- a/datapoollocal/LocalPoolVariable.tpp +++ b/datapoollocal/LocalPoolVariable.tpp @@ -98,7 +98,6 @@ inline ReturnValue_t LocalPoolVariable::commitWithoutLock() { } PoolEntry* poolEntry = nullptr; - //ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry); ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, &poolEntry); if(result != RETURN_OK) { From cb514e7493b8d6b0a56f93c41974d4245b9a529d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 4 Mar 2021 20:43:08 +0100 Subject: [PATCH 54/59] small tweak to avoid warning --- ipc/MutexHelper.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipc/MutexHelper.h b/ipc/MutexHelper.h index bc5730850..bc744d3f9 100644 --- a/ipc/MutexHelper.h +++ b/ipc/MutexHelper.h @@ -39,6 +39,9 @@ public: sif::printError("MutexHelper: Lock of Mutex failed with code %d\n", status); #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ } +#else + /* To avoid unused variable warning */ + static_cast(status); #endif /* FSFW_VERBOSE_LEVEL >= 1 */ } From 2d069896a59a642dd381205059f00fd692490f0b Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 4 Mar 2021 23:24:57 +0100 Subject: [PATCH 55/59] properly implemented getDataAndTime --- osal/rtems/Clock.cpp | 300 ++++++++++++++++++++++--------------------- 1 file changed, 154 insertions(+), 146 deletions(-) diff --git a/osal/rtems/Clock.cpp b/osal/rtems/Clock.cpp index 9f4634cca..aef71fe15 100644 --- a/osal/rtems/Clock.cpp +++ b/osal/rtems/Clock.cpp @@ -10,201 +10,209 @@ uint16_t Clock::leapSeconds = 0; MutexIF* Clock::timeMutex = nullptr; uint32_t Clock::getTicksPerSecond(void){ - rtems_interval ticks_per_second = rtems_clock_get_ticks_per_second(); - return static_cast(ticks_per_second); + rtems_interval ticks_per_second = rtems_clock_get_ticks_per_second(); + return static_cast(ticks_per_second); } ReturnValue_t Clock::setClock(const TimeOfDay_t* time) { - rtems_time_of_day timeRtems; - timeRtems.year = time->year; - timeRtems.month = time->month; - timeRtems.day = time->day; - timeRtems.hour = time->hour; - timeRtems.minute = time->minute; - timeRtems.second = time->second; - timeRtems.ticks = time->usecond * getTicksPerSecond() / 1e6; - rtems_status_code status = rtems_clock_set(&timeRtems); - switch(status){ - case RTEMS_SUCCESSFUL: - return HasReturnvaluesIF::RETURN_OK; - case RTEMS_INVALID_ADDRESS: - return HasReturnvaluesIF::RETURN_FAILED; - case RTEMS_INVALID_CLOCK: - return HasReturnvaluesIF::RETURN_FAILED; - default: - return HasReturnvaluesIF::RETURN_FAILED; - } + rtems_time_of_day timeRtems; + timeRtems.year = time->year; + timeRtems.month = time->month; + timeRtems.day = time->day; + timeRtems.hour = time->hour; + timeRtems.minute = time->minute; + timeRtems.second = time->second; + timeRtems.ticks = time->usecond * getTicksPerSecond() / 1e6; + rtems_status_code status = rtems_clock_set(&timeRtems); + switch(status){ + case RTEMS_SUCCESSFUL: + return HasReturnvaluesIF::RETURN_OK; + case RTEMS_INVALID_ADDRESS: + return HasReturnvaluesIF::RETURN_FAILED; + case RTEMS_INVALID_CLOCK: + return HasReturnvaluesIF::RETURN_FAILED; + default: + return HasReturnvaluesIF::RETURN_FAILED; + } } ReturnValue_t Clock::setClock(const timeval* time) { - timespec newTime; - newTime.tv_sec = time->tv_sec; - if(time->tv_usec < 0) { - // better returnvalue. - return HasReturnvaluesIF::RETURN_FAILED; - } - newTime.tv_nsec = time->tv_usec * TOD_NANOSECONDS_PER_MICROSECOND; + timespec newTime; + newTime.tv_sec = time->tv_sec; + if(time->tv_usec < 0) { + // better returnvalue. + return HasReturnvaluesIF::RETURN_FAILED; + } + newTime.tv_nsec = time->tv_usec * TOD_NANOSECONDS_PER_MICROSECOND; - ISR_lock_Context context; - _TOD_Lock(); - _TOD_Acquire(&context); - Status_Control status = _TOD_Set(&newTime, &context); - _TOD_Unlock(); - if(status == STATUS_SUCCESSFUL) { - return HasReturnvaluesIF::RETURN_OK; - } - // better returnvalue - return HasReturnvaluesIF::RETURN_FAILED; + ISR_lock_Context context; + _TOD_Lock(); + _TOD_Acquire(&context); + Status_Control status = _TOD_Set(&newTime, &context); + _TOD_Unlock(); + if(status == STATUS_SUCCESSFUL) { + return HasReturnvaluesIF::RETURN_OK; + } + // better returnvalue + return HasReturnvaluesIF::RETURN_FAILED; } ReturnValue_t Clock::getClock_timeval(timeval* time) { - //Callable from ISR - rtems_status_code status = rtems_clock_get_tod_timeval(time); - switch(status){ - case RTEMS_SUCCESSFUL: - return HasReturnvaluesIF::RETURN_OK; - case RTEMS_NOT_DEFINED: - return HasReturnvaluesIF::RETURN_FAILED; - default: - return HasReturnvaluesIF::RETURN_FAILED; - } + //Callable from ISR + rtems_status_code status = rtems_clock_get_tod_timeval(time); + switch(status){ + case RTEMS_SUCCESSFUL: + return HasReturnvaluesIF::RETURN_OK; + case RTEMS_NOT_DEFINED: + return HasReturnvaluesIF::RETURN_FAILED; + default: + return HasReturnvaluesIF::RETURN_FAILED; + } } ReturnValue_t Clock::getUptime(timeval* uptime) { - //According to docs.rtems.org for rtems 5 this method is more accurate than rtems_clock_get_ticks_since_boot - timespec time; - rtems_status_code status = rtems_clock_get_uptime(&time); - uptime->tv_sec = time.tv_sec; - time.tv_nsec = time.tv_nsec / 1000; - uptime->tv_usec = time.tv_nsec; - switch(status){ - case RTEMS_SUCCESSFUL: - return HasReturnvaluesIF::RETURN_OK; - default: - return HasReturnvaluesIF::RETURN_FAILED; - } + //According to docs.rtems.org for rtems 5 this method is more accurate than rtems_clock_get_ticks_since_boot + timespec time; + rtems_status_code status = rtems_clock_get_uptime(&time); + uptime->tv_sec = time.tv_sec; + time.tv_nsec = time.tv_nsec / 1000; + uptime->tv_usec = time.tv_nsec; + switch(status){ + case RTEMS_SUCCESSFUL: + return HasReturnvaluesIF::RETURN_OK; + default: + return HasReturnvaluesIF::RETURN_FAILED; + } } ReturnValue_t Clock::getUptime(uint32_t* uptimeMs) { - //This counter overflows after 50 days - *uptimeMs = rtems_clock_get_ticks_since_boot(); - return HasReturnvaluesIF::RETURN_OK; + //This counter overflows after 50 days + *uptimeMs = rtems_clock_get_ticks_since_boot(); + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t Clock::getClock_usecs(uint64_t* time) { - timeval temp_time; - rtems_status_code returnValue = rtems_clock_get_tod_timeval(&temp_time); - *time = ((uint64_t) temp_time.tv_sec * 1000000) + temp_time.tv_usec; - switch(returnValue){ - case RTEMS_SUCCESSFUL: - return HasReturnvaluesIF::RETURN_OK; - default: - return HasReturnvaluesIF::RETURN_FAILED; - } + timeval temp_time; + rtems_status_code returnValue = rtems_clock_get_tod_timeval(&temp_time); + *time = ((uint64_t) temp_time.tv_sec * 1000000) + temp_time.tv_usec; + switch(returnValue){ + case RTEMS_SUCCESSFUL: + return HasReturnvaluesIF::RETURN_OK; + default: + return HasReturnvaluesIF::RETURN_FAILED; + } } ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) { - /* For all but the last field, the struct will be filled with the correct values */ - rtems_time_of_day* timeRtems = reinterpret_cast(time); - rtems_status_code status = rtems_clock_get_tod(timeRtems); - /* The last field now contains the RTEMS ticks of the seconds from 0 - to rtems_clock_get_ticks_per_second() minus one. We calculate the microseconds accordingly */ - timeRtems->ticks = static_cast(timeRtems->ticks) / - rtems_clock_get_ticks_per_second() * 1e6; - switch (status) { - case RTEMS_SUCCESSFUL: - return HasReturnvaluesIF::RETURN_OK; - case RTEMS_NOT_DEFINED: - //system date and time is not set - return HasReturnvaluesIF::RETURN_FAILED; - case RTEMS_INVALID_ADDRESS: - //time_buffer is NULL - return HasReturnvaluesIF::RETURN_FAILED; - default: - return HasReturnvaluesIF::RETURN_FAILED; - } + /* For all but the last field, the struct will be filled with the correct values */ + rtems_time_of_day timeRtems; + rtems_status_code status = rtems_clock_get_tod(&timeRtems); + switch (status) { + case RTEMS_SUCCESSFUL: { + /* The last field now contains the RTEMS ticks of the seconds from 0 + to rtems_clock_get_ticks_per_second() minus one. + We calculate the microseconds accordingly */ + time->day = timeRtems.day; + time->hour = timeRtems.hour; + time->minute = timeRtems.minute; + time->month = timeRtems.month; + time->second = timeRtems.second; + time->usecond = static_cast(timeRtems.ticks) / + rtems_clock_get_ticks_per_second() * 1e6; + time->year = timeRtems.year; + return HasReturnvaluesIF::RETURN_OK; + } + case RTEMS_NOT_DEFINED: + /* System date and time is not set */ + return HasReturnvaluesIF::RETURN_FAILED; + case RTEMS_INVALID_ADDRESS: + /* time_buffer is NULL */ + return HasReturnvaluesIF::RETURN_FAILED; + default: + return HasReturnvaluesIF::RETURN_FAILED; + } } ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from, - timeval* to) { - //Fails in 2038.. - rtems_time_of_day timeRtems; - timeRtems.year = from->year; - timeRtems.month = from->month; - timeRtems.day = from->day; - timeRtems.hour = from->hour; - timeRtems.minute = from->minute; - timeRtems.second = from->second; - timeRtems.ticks = from->usecond * getTicksPerSecond() / 1e6; - to->tv_sec = _TOD_To_seconds(&timeRtems); - to->tv_usec = from->usecond; - return HasReturnvaluesIF::RETURN_OK; + timeval* to) { + //Fails in 2038.. + rtems_time_of_day timeRtems; + timeRtems.year = from->year; + timeRtems.month = from->month; + timeRtems.day = from->day; + timeRtems.hour = from->hour; + timeRtems.minute = from->minute; + timeRtems.second = from->second; + timeRtems.ticks = from->usecond * getTicksPerSecond() / 1e6; + to->tv_sec = _TOD_To_seconds(&timeRtems); + to->tv_usec = from->usecond; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t Clock::convertTimevalToJD2000(timeval time, double* JD2000) { - *JD2000 = (time.tv_sec - 946728000. + time.tv_usec / 1000000.) / 24. - / 3600.; - return HasReturnvaluesIF::RETURN_OK; + *JD2000 = (time.tv_sec - 946728000. + time.tv_usec / 1000000.) / 24. + / 3600.; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t Clock::convertUTCToTT(timeval utc, timeval* tt) { - //SHOULDDO: works not for dates in the past (might have less leap seconds) - if (timeMutex == nullptr) { - return HasReturnvaluesIF::RETURN_FAILED; - } + //SHOULDDO: works not for dates in the past (might have less leap seconds) + if (timeMutex == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } - uint16_t leapSeconds; - ReturnValue_t result = getLeapSeconds(&leapSeconds); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - timeval leapSeconds_timeval = { 0, 0 }; - leapSeconds_timeval.tv_sec = leapSeconds; + uint16_t leapSeconds; + ReturnValue_t result = getLeapSeconds(&leapSeconds); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + timeval leapSeconds_timeval = { 0, 0 }; + leapSeconds_timeval.tv_sec = leapSeconds; - //initial offset between UTC and TAI - timeval UTCtoTAI1972 = { 10, 0 }; + //initial offset between UTC and TAI + timeval UTCtoTAI1972 = { 10, 0 }; - timeval TAItoTT = { 32, 184000 }; + timeval TAItoTT = { 32, 184000 }; - *tt = utc + leapSeconds_timeval + UTCtoTAI1972 + TAItoTT; + *tt = utc + leapSeconds_timeval + UTCtoTAI1972 + TAItoTT; - return HasReturnvaluesIF::RETURN_OK; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t Clock::setLeapSeconds(const uint16_t leapSeconds_) { - if(checkOrCreateClockMutex()!=HasReturnvaluesIF::RETURN_OK){ - return HasReturnvaluesIF::RETURN_FAILED; - } - MutexHelper helper(timeMutex); + if(checkOrCreateClockMutex()!=HasReturnvaluesIF::RETURN_OK){ + return HasReturnvaluesIF::RETURN_FAILED; + } + MutexHelper helper(timeMutex); - leapSeconds = leapSeconds_; + leapSeconds = leapSeconds_; - return HasReturnvaluesIF::RETURN_OK; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) { - if(timeMutex==nullptr){ - return HasReturnvaluesIF::RETURN_FAILED; - } - MutexHelper helper(timeMutex); + if(timeMutex==nullptr){ + return HasReturnvaluesIF::RETURN_FAILED; + } + MutexHelper helper(timeMutex); - *leapSeconds_ = leapSeconds; + *leapSeconds_ = leapSeconds; - return HasReturnvaluesIF::RETURN_OK; + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t Clock::checkOrCreateClockMutex(){ - if(timeMutex==nullptr){ - MutexFactory* mutexFactory = MutexFactory::instance(); - if (mutexFactory == nullptr) { - return HasReturnvaluesIF::RETURN_FAILED; - } - timeMutex = mutexFactory->createMutex(); - if (timeMutex == nullptr) { - return HasReturnvaluesIF::RETURN_FAILED; - } - } - return HasReturnvaluesIF::RETURN_OK; + if(timeMutex==nullptr){ + MutexFactory* mutexFactory = MutexFactory::instance(); + if (mutexFactory == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + timeMutex = mutexFactory->createMutex(); + if (timeMutex == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + } + return HasReturnvaluesIF::RETURN_OK; } From b3ca7b8667efde7dfa25bcbf8fd5354ab8e8075c Mon Sep 17 00:00:00 2001 From: Spacefish Date: Fri, 5 Mar 2021 09:51:08 +0100 Subject: [PATCH 56/59] setCompletionReply ActionMessage success flag is now third parameter --- action/ActionHelper.cpp | 4 ++-- action/ActionMessage.cpp | 4 ++-- action/ActionMessage.h | 4 ++-- action/SimpleActionHelper.cpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index 42ee4022f..4b64a40ca 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -46,7 +46,7 @@ void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo, void ActionHelper::finish(bool success, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) { CommandMessage reply; - ActionMessage::setCompletionReply(success, &reply, commandId, result); + ActionMessage::setCompletionReply(&reply, commandId, success, result); queueToUse->sendMessage(reportTo, &reply); } @@ -69,7 +69,7 @@ void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ipcStore->deleteData(dataAddress); if(result == HasActionsIF::EXECUTION_FINISHED) { CommandMessage reply; - ActionMessage::setCompletionReply(true, &reply, actionId, result); + ActionMessage::setCompletionReply(&reply, actionId, true, result); queueToUse->sendMessage(commandedBy, &reply); } if (result != HasReturnvaluesIF::RETURN_OK) { diff --git a/action/ActionMessage.cpp b/action/ActionMessage.cpp index 15b59b9dc..66c7f0581 100644 --- a/action/ActionMessage.cpp +++ b/action/ActionMessage.cpp @@ -53,8 +53,8 @@ void ActionMessage::setDataReply(CommandMessage* message, ActionId_t actionId, message->setParameter2(data.raw); } -void ActionMessage::setCompletionReply(bool success, CommandMessage* message, - ActionId_t fid, ReturnValue_t result) { +void ActionMessage::setCompletionReply(CommandMessage* message, + ActionId_t fid, bool success, ReturnValue_t result) { if (success) { message->setCommand(COMPLETION_SUCCESS); } diff --git a/action/ActionMessage.h b/action/ActionMessage.h index df97485b9..0c64588ca 100644 --- a/action/ActionMessage.h +++ b/action/ActionMessage.h @@ -38,8 +38,8 @@ public: static ReturnValue_t getReturnCode(const CommandMessage* message ); static void setDataReply(CommandMessage* message, ActionId_t actionId, store_address_t data); - static void setCompletionReply(bool success, CommandMessage* message, ActionId_t fid, - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + static void setCompletionReply(CommandMessage* message, ActionId_t fid, + bool success, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); static void clear(CommandMessage* message); }; diff --git a/action/SimpleActionHelper.cpp b/action/SimpleActionHelper.cpp index 979ee3ad4..8d34f40f2 100644 --- a/action/SimpleActionHelper.cpp +++ b/action/SimpleActionHelper.cpp @@ -62,8 +62,8 @@ void SimpleActionHelper::prepareExecution(MessageQueueId_t commandedBy, stepCount++; break; case HasActionsIF::EXECUTION_FINISHED: - ActionMessage::setCompletionReply(true, &reply, actionId, - HasReturnvaluesIF::RETURN_OK); + ActionMessage::setCompletionReply(&reply, actionId, + true, HasReturnvaluesIF::RETURN_OK); queueToUse->sendMessage(commandedBy, &reply); break; default: From caa3cf538b5e6a1ba390c71d863ed9a85de0dc20 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Fri, 5 Mar 2021 13:34:06 +0100 Subject: [PATCH 57/59] formatting correction --- devicehandlers/DeviceHandlerBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index a19e2cbc0..d61b0407d 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -119,7 +119,7 @@ public: DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID, lp_id_t thermalRequestPoolId = DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID, - uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID); + uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID); /** * @brief Helper function to ease device handler development. * This will instruct the transition to MODE_ON immediately From 17b8d3fed05cae6e208dc3f14b6ba0d44c9ec5ef Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sat, 6 Mar 2021 18:12:41 +0100 Subject: [PATCH 58/59] printout for trans timeout --- devicehandlers/DeviceHandlerBase.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 10de57bd4..0649aaa04 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -308,6 +308,14 @@ void DeviceHandlerBase::doStateMachine() { uint32_t currentUptime; Clock::getUptime(¤tUptime); if (currentUptime - timeoutStart >= childTransitionDelay) { +#if FSFW_VERBOSE_LEVEL >= 1 + char printout[60]; + sprintf(printout, "Transition timeout (%lu) occured !", + static_cast(childTransitionDelay)); + /* Very common configuration error, so print it */ + printWarningOrError(sif::OutputTypes::OUT_WARNING, "doStateMachine", + RETURN_FAILED, printout); +#endif triggerEvent(MODE_TRANSITION_FAILED, childTransitionFailure, 0); setMode(transitionSourceMode, transitionSourceSubMode); break; From 778ef4ef230019ca4b155a77518156793cf5d308 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sat, 6 Mar 2021 20:36:54 +0100 Subject: [PATCH 59/59] cleaned up a bit, no functional change --- osal/linux/PosixThread.cpp | 336 ++++++++++++++++++------------------- 1 file changed, 167 insertions(+), 169 deletions(-) diff --git a/osal/linux/PosixThread.cpp b/osal/linux/PosixThread.cpp index bd8e72583..bc5cacee4 100644 --- a/osal/linux/PosixThread.cpp +++ b/osal/linux/PosixThread.cpp @@ -6,252 +6,250 @@ #include PosixThread::PosixThread(const char* name_, int priority_, size_t stackSize_): - thread(0),priority(priority_),stackSize(stackSize_) { + thread(0),priority(priority_),stackSize(stackSize_) { name[0] = '\0'; std::strncat(name, name_, PTHREAD_MAX_NAMELEN - 1); } PosixThread::~PosixThread() { - //No deletion and no free of Stack Pointer + //No deletion and no free of Stack Pointer } ReturnValue_t PosixThread::sleep(uint64_t ns) { - //TODO sleep might be better with timer instead of sleep() - timespec time; - time.tv_sec = ns/1000000000; - time.tv_nsec = ns - time.tv_sec*1e9; + //TODO sleep might be better with timer instead of sleep() + timespec time; + time.tv_sec = ns/1000000000; + time.tv_nsec = ns - time.tv_sec*1e9; - //Remaining Time is not set here - int status = nanosleep(&time,NULL); - if(status != 0){ - switch(errno){ - case EINTR: - //The nanosleep() function was interrupted by a signal. - return HasReturnvaluesIF::RETURN_FAILED; - case EINVAL: - //The rqtp argument specified a nanosecond value less than zero or - // greater than or equal to 1000 million. - return HasReturnvaluesIF::RETURN_FAILED; - default: - return HasReturnvaluesIF::RETURN_FAILED; - } + //Remaining Time is not set here + int status = nanosleep(&time,NULL); + if(status != 0){ + switch(errno){ + case EINTR: + //The nanosleep() function was interrupted by a signal. + return HasReturnvaluesIF::RETURN_FAILED; + case EINVAL: + //The rqtp argument specified a nanosecond value less than zero or + // greater than or equal to 1000 million. + return HasReturnvaluesIF::RETURN_FAILED; + default: + return HasReturnvaluesIF::RETURN_FAILED; + } - } - return HasReturnvaluesIF::RETURN_OK; + } + return HasReturnvaluesIF::RETURN_OK; } void PosixThread::suspend() { - //Wait for SIGUSR1 - int caughtSig = 0; - sigset_t waitSignal; - sigemptyset(&waitSignal); - sigaddset(&waitSignal, SIGUSR1); - sigwait(&waitSignal, &caughtSig); - if (caughtSig != SIGUSR1) { + //Wait for SIGUSR1 + int caughtSig = 0; + sigset_t waitSignal; + sigemptyset(&waitSignal); + sigaddset(&waitSignal, SIGUSR1); + sigwait(&waitSignal, &caughtSig); + if (caughtSig != SIGUSR1) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "FixedTimeslotTask: Unknown Signal received: " << - caughtSig << std::endl; + sif::error << "FixedTimeslotTask: Unknown Signal received: " << + caughtSig << std::endl; #endif - } + } } void PosixThread::resume(){ - /* Signal the thread to start. Makes sense to call kill to start or? ;) - * - * According to Posix raise(signal) will call pthread_kill(pthread_self(), sig), - * but as the call must be done from the thread itsself this is not possible here - */ - pthread_kill(thread,SIGUSR1); + /* Signal the thread to start. Makes sense to call kill to start or? ;) + According to POSIX raise(signal) will call pthread_kill(pthread_self(), sig), + but as the call must be done from the thread itself this is not possible here */ + pthread_kill(thread,SIGUSR1); } bool PosixThread::delayUntil(uint64_t* const prevoiusWakeTime_ms, - const uint64_t delayTime_ms) { - uint64_t nextTimeToWake_ms; - bool shouldDelay = false; - //Get current Time - const uint64_t currentTime_ms = getCurrentMonotonicTimeMs(); - /* Generate the tick time at which the task wants to wake. */ - nextTimeToWake_ms = (*prevoiusWakeTime_ms) + delayTime_ms; + const uint64_t delayTime_ms) { + uint64_t nextTimeToWake_ms; + bool shouldDelay = false; + /* Get current Time */ + const uint64_t currentTime_ms = getCurrentMonotonicTimeMs(); + /* Generate the tick time at which the task wants to wake. */ + nextTimeToWake_ms = (*prevoiusWakeTime_ms) + delayTime_ms; - if (currentTime_ms < *prevoiusWakeTime_ms) { - /* The tick count has overflowed since this function was - lasted called. In this case the only time we should ever - actually delay is if the wake time has also overflowed, - and the wake time is greater than the tick time. When this + if (currentTime_ms < *prevoiusWakeTime_ms) { + /* The tick count has overflowed since this function was + lasted called. In this case the only time we should ever + actually delay is if the wake time has also overflowed, + and the wake time is greater than the tick time. When this is the case it is as if neither time had overflowed. */ - if ((nextTimeToWake_ms < *prevoiusWakeTime_ms) - && (nextTimeToWake_ms > currentTime_ms)) { - shouldDelay = true; - } - } else { - /* The tick time has not overflowed. In this case we will + if ((nextTimeToWake_ms < *prevoiusWakeTime_ms) + && (nextTimeToWake_ms > currentTime_ms)) { + shouldDelay = true; + } + } else { + /* The tick time has not overflowed. In this case we will delay if either the wake time has overflowed, and/or the tick time is less than the wake time. */ - if ((nextTimeToWake_ms < *prevoiusWakeTime_ms) - || (nextTimeToWake_ms > currentTime_ms)) { - shouldDelay = true; - } - } + if ((nextTimeToWake_ms < *prevoiusWakeTime_ms) + || (nextTimeToWake_ms > currentTime_ms)) { + shouldDelay = true; + } + } - /* Update the wake time ready for the next call. */ + /* Update the wake time ready for the next call. */ - (*prevoiusWakeTime_ms) = nextTimeToWake_ms; + (*prevoiusWakeTime_ms) = nextTimeToWake_ms; - if (shouldDelay) { - uint64_t sleepTime = nextTimeToWake_ms - currentTime_ms; - PosixThread::sleep(sleepTime * 1000000ull); - return true; - } - //We are shifting the time in case the deadline was missed like rtems - (*prevoiusWakeTime_ms) = currentTime_ms; - return false; + if (shouldDelay) { + uint64_t sleepTime = nextTimeToWake_ms - currentTime_ms; + PosixThread::sleep(sleepTime * 1000000ull); + return true; + } + /* We are shifting the time in case the deadline was missed like RTEMS */ + (*prevoiusWakeTime_ms) = currentTime_ms; + return false; } uint64_t PosixThread::getCurrentMonotonicTimeMs(){ - timespec timeNow; - clock_gettime(CLOCK_MONOTONIC_RAW, &timeNow); - uint64_t currentTime_ms = (uint64_t) timeNow.tv_sec * 1000 - + timeNow.tv_nsec / 1000000; + timespec timeNow; + clock_gettime(CLOCK_MONOTONIC_RAW, &timeNow); + uint64_t currentTime_ms = (uint64_t) timeNow.tv_sec * 1000 + + timeNow.tv_nsec / 1000000; - return currentTime_ms; + return currentTime_ms; } void PosixThread::createTask(void* (*fnc_)(void*), void* arg_) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - //sif::debug << "PosixThread::createTask" << std::endl; + //sif::debug << "PosixThread::createTask" << std::endl; #endif - /* - * The attr argument points to a pthread_attr_t structure whose contents - are used at thread creation time to determine attributes for the new - thread; this structure is initialized using pthread_attr_init(3) and - related functions. If attr is NULL, then the thread is created with - default attributes. - */ - pthread_attr_t attributes; - int status = pthread_attr_init(&attributes); - if(status != 0){ + /* + * The attr argument points to a pthread_attr_t structure whose contents + * are used at thread creation time to determine attributes for the new + * thread; this structure is initialized using pthread_attr_init(3) and + * related functions. If attr is NULL, then the thread is created with + * default attributes. + */ + pthread_attr_t attributes; + int status = pthread_attr_init(&attributes); + if(status != 0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "Posix Thread attribute init failed with: " << - strerror(status) << std::endl; + sif::error << "Posix Thread attribute init failed with: " << + strerror(status) << std::endl; #endif - } - void* stackPointer; - status = posix_memalign(&stackPointer, sysconf(_SC_PAGESIZE), stackSize); - if(status != 0){ + } + void* stackPointer; + status = posix_memalign(&stackPointer, sysconf(_SC_PAGESIZE), stackSize); + if(status != 0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PosixThread::createTask: Stack init failed with: " << - strerror(status) << std::endl; + sif::error << "PosixThread::createTask: Stack init failed with: " << + strerror(status) << std::endl; #endif - if(errno == ENOMEM) { - size_t stackMb = stackSize/10e6; + if(errno == ENOMEM) { + size_t stackMb = stackSize/10e6; #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PosixThread::createTask: Insufficient memory for" - " the requested " << stackMb << " MB" << std::endl; + sif::error << "PosixThread::createTask: Insufficient memory for" + " the requested " << stackMb << " MB" << std::endl; #else - sif::printError("PosixThread::createTask: Insufficient memory for " - "the requested %lu MB\n", static_cast(stackMb)); + sif::printError("PosixThread::createTask: Insufficient memory for " + "the requested %lu MB\n", static_cast(stackMb)); #endif - } - else if(errno == EINVAL) { + } + else if(errno == EINVAL) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PosixThread::createTask: Wrong alignment argument!" - << std::endl; + sif::error << "PosixThread::createTask: Wrong alignment argument!" + << std::endl; #else - sif::printError("PosixThread::createTask: " - "Wrong alignment argument!\n"); + sif::printError("PosixThread::createTask: " + "Wrong alignment argument!\n"); #endif - } - return; - } + } + return; + } - status = pthread_attr_setstack(&attributes, stackPointer, stackSize); - if(status != 0){ + status = pthread_attr_setstack(&attributes, stackPointer, stackSize); + if(status != 0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PosixThread::createTask: pthread_attr_setstack " - " failed with: " << strerror(status) << std::endl; - sif::error << "Make sure the specified stack size is valid and is " - "larger than the minimum allowed stack size." << std::endl; + sif::error << "PosixThread::createTask: pthread_attr_setstack " + " failed with: " << strerror(status) << std::endl; + sif::error << "Make sure the specified stack size is valid and is " + "larger than the minimum allowed stack size." << std::endl; #endif - } + } - status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED); - if(status != 0){ + status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED); + if(status != 0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "Posix Thread attribute setinheritsched failed with: " << - strerror(status) << std::endl; + sif::error << "Posix Thread attribute setinheritsched failed with: " << + strerror(status) << std::endl; #endif - } + } - // TODO FIFO -> This needs root privileges for the process - status = pthread_attr_setschedpolicy(&attributes,SCHED_FIFO); - if(status != 0){ + // TODO FIFO -> This needs root privileges for the process + status = pthread_attr_setschedpolicy(&attributes,SCHED_FIFO); + if(status != 0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "Posix Thread attribute schedule policy failed with: " << - strerror(status) << std::endl; + sif::error << "Posix Thread attribute schedule policy failed with: " << + strerror(status) << std::endl; #endif - } + } - sched_param scheduleParams; - scheduleParams.__sched_priority = priority; - status = pthread_attr_setschedparam(&attributes, &scheduleParams); - if(status != 0){ + sched_param scheduleParams; + scheduleParams.__sched_priority = priority; + status = pthread_attr_setschedparam(&attributes, &scheduleParams); + if(status != 0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "Posix Thread attribute schedule params failed with: " << - strerror(status) << std::endl; + sif::error << "Posix Thread attribute schedule params failed with: " << + strerror(status) << std::endl; #endif - } + } - //Set Signal Mask for suspend until startTask is called - sigset_t waitSignal; - sigemptyset(&waitSignal); - sigaddset(&waitSignal, SIGUSR1); - status = pthread_sigmask(SIG_BLOCK, &waitSignal, NULL); - if(status != 0){ + //Set Signal Mask for suspend until startTask is called + sigset_t waitSignal; + sigemptyset(&waitSignal); + sigaddset(&waitSignal, SIGUSR1); + status = pthread_sigmask(SIG_BLOCK, &waitSignal, NULL); + if(status != 0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "Posix Thread sigmask failed failed with: " << - strerror(status) << " errno: " << strerror(errno) << std::endl; + sif::error << "Posix Thread sigmask failed failed with: " << + strerror(status) << " errno: " << strerror(errno) << std::endl; #endif - } + } - status = pthread_create(&thread,&attributes,fnc_,arg_); - if(status != 0){ + status = pthread_create(&thread,&attributes,fnc_,arg_); + if(status != 0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "Posix Thread create failed with: " << - strerror(status) << std::endl; + sif::error << "Posix Thread create failed with: " << + strerror(status) << std::endl; #endif - } + } - status = pthread_setname_np(thread,name); - if(status != 0){ + status = pthread_setname_np(thread,name); + if(status != 0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PosixThread::createTask: setname failed with: " << - strerror(status) << std::endl; + sif::error << "PosixThread::createTask: setname failed with: " << + strerror(status) << std::endl; #endif - if(status == ERANGE) { + if(status == ERANGE) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PosixThread::createTask: Task name length longer" - " than 16 chars. Truncating.." << std::endl; + sif::error << "PosixThread::createTask: Task name length longer" + " than 16 chars. Truncating.." << std::endl; #endif - name[15] = '\0'; - status = pthread_setname_np(thread,name); - if(status != 0){ + name[15] = '\0'; + status = pthread_setname_np(thread,name); + if(status != 0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PosixThread::createTask: Setting name" - " did not work.." << std::endl; + sif::error << "PosixThread::createTask: Setting name" + " did not work.." << std::endl; #endif - } - } - } + } + } + } - status = pthread_attr_destroy(&attributes); - if(status!=0){ + status = pthread_attr_destroy(&attributes); + if(status!=0){ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "Posix Thread attribute destroy failed with: " << - strerror(status) << std::endl; + sif::error << "Posix Thread attribute destroy failed with: " << + strerror(status) << std::endl; #endif - } + } }