From dbe326e78743ca64888cea4506acefdbb1bf566b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 9 Jan 2021 13:52:47 +0100 Subject: [PATCH 01/20] minor tweak --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cc5ffd5..587e6701 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,7 +106,7 @@ else() ) endif() -if(CMAKE_COMPILER_IS_GNUCXX) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(WARNING_FLAGS -Wall -Wextra From 7129ea67bdf4c85d496a22c692a84f0f7a6ede97 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sat, 9 Jan 2021 16:06:54 +0100 Subject: [PATCH 02/20] linux fixes --- osal/linux/BinarySemaphore.cpp | 20 +++++++++++++------- osal/linux/CountingSemaphore.cpp | 4 +++- osal/linux/MessageQueue.cpp | 14 ++++++++------ osal/linux/TcUnixUdpPollingTask.cpp | 2 ++ osal/linux/TmTcUnixUdpBridge.cpp | 8 ++++++-- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/osal/linux/BinarySemaphore.cpp b/osal/linux/BinarySemaphore.cpp index 0a6bb29f..c2eb8514 100644 --- a/osal/linux/BinarySemaphore.cpp +++ b/osal/linux/BinarySemaphore.cpp @@ -1,10 +1,11 @@ #include "BinarySemaphore.h" +#include "../../serviceinterface/ServiceInterfacePrinter.h" #include "../../serviceinterface/ServiceInterfaceStream.h" -extern "C" { +#include #include #include -} + BinarySemaphore::BinarySemaphore() { // Using unnamed semaphores for now @@ -113,7 +114,8 @@ uint8_t BinarySemaphore::getSemaphoreCounter(sem_t *handle) { } else if(result != 0 and errno == EINVAL) { // Could be called from interrupt, use lightweight printf - printf("BinarySemaphore::getSemaphoreCounter: Invalid semaphore\n"); + fsfw::printError("BinarySemaphore::getSemaphoreCounter: " + "Invalid semaphore\n"); return 0; } else { @@ -128,13 +130,17 @@ void BinarySemaphore::initSemaphore(uint8_t initCount) { switch(errno) { case(EINVAL): // Value exceeds SEM_VALUE_MAX - case(ENOSYS): - // System does not support process-shared semaphores + case(ENOSYS): { + // System does not support process-shared semaphores #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "BinarySemaphore: Init failed with" << strerror(errno) - << std::endl; + sif::error << "BinarySemaphore: Init failed with " + << strerror(errno) << std::endl; +#else + fsfw::printError("BinarySemaphore: Init failed with %s\n", + strerror(errno)); #endif } + } } } diff --git a/osal/linux/CountingSemaphore.cpp b/osal/linux/CountingSemaphore.cpp index 0bb71831..07c52212 100644 --- a/osal/linux/CountingSemaphore.cpp +++ b/osal/linux/CountingSemaphore.cpp @@ -1,5 +1,7 @@ #include "../../osal/linux/CountingSemaphore.h" -#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../serviceinterface/ServiceInterface.h" + +#include CountingSemaphore::CountingSemaphore(const uint8_t maxCount, uint8_t initCount): maxCount(maxCount), initCount(initCount) { diff --git a/osal/linux/MessageQueue.cpp b/osal/linux/MessageQueue.cpp index e7b09b4f..d93b7f6d 100644 --- a/osal/linux/MessageQueue.cpp +++ b/osal/linux/MessageQueue.cpp @@ -1,5 +1,5 @@ #include "MessageQueue.h" -#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../serviceinterface/ServiceInterface.h" #include "../../objectmanager/ObjectManagerIF.h" #include @@ -121,14 +121,16 @@ ReturnValue_t MessageQueue::handleError(mq_attr* attributes, break; } - default: + default: { // Failed either the first time or the second time #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "MessageQueue::MessageQueue: Creating Queue " << std::hex - << name << std::dec << " failed with status: " - << strerror(errno) << std::endl; + sif::error << "MessageQueue::MessageQueue: Creating Queue " << name + << " failed with status: " << strerror(errno) << std::endl; +#else + fsfw::printError("MessageQueue::MessageQueue: Creating Queue %s" + " failed with status: %s\n", name, strerror(errno)); #endif - + } } return HasReturnvaluesIF::RETURN_FAILED; diff --git a/osal/linux/TcUnixUdpPollingTask.cpp b/osal/linux/TcUnixUdpPollingTask.cpp index a8387e87..11ed7fee 100644 --- a/osal/linux/TcUnixUdpPollingTask.cpp +++ b/osal/linux/TcUnixUdpPollingTask.cpp @@ -1,6 +1,8 @@ #include "TcUnixUdpPollingTask.h" #include "../../globalfunctions/arrayprinter.h" +#include + TcUnixUdpPollingTask::TcUnixUdpPollingTask(object_id_t objectId, object_id_t tmtcUnixUdpBridge, size_t frameSize, double timeoutSeconds): SystemObject(objectId), diff --git a/osal/linux/TmTcUnixUdpBridge.cpp b/osal/linux/TmTcUnixUdpBridge.cpp index 1d318d3b..0bedce0f 100644 --- a/osal/linux/TmTcUnixUdpBridge.cpp +++ b/osal/linux/TmTcUnixUdpBridge.cpp @@ -1,5 +1,5 @@ #include "TmTcUnixUdpBridge.h" -#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../serviceinterface/ServiceInterface.h" #include "../../ipc/MutexHelper.h" #include @@ -188,12 +188,16 @@ void TmTcUnixUdpBridge::handleBindError() { void TmTcUnixUdpBridge::handleSendError() { switch(errno) { - default: + default: { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "TmTcUnixBridge::handleSendError: " << strerror(errno) << std::endl; +#else + fsfw::printError("TmTcUnixBridge::handleSendError: %s\n", + strerror(errno)); #endif } + } } void TmTcUnixUdpBridge::setClientAddressToAny(bool ipAddrAnySet){ From 13ffcfe801c61a5d4cca3ccb55d2d9300e100ffd Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sat, 9 Jan 2021 16:20:35 +0100 Subject: [PATCH 03/20] prints added --- osal/linux/PosixThread.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osal/linux/PosixThread.cpp b/osal/linux/PosixThread.cpp index a0e540cf..8d368c18 100644 --- a/osal/linux/PosixThread.cpp +++ b/osal/linux/PosixThread.cpp @@ -1,6 +1,6 @@ #include "PosixThread.h" -#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../serviceinterface/ServiceInterface.h" #include #include @@ -146,16 +146,22 @@ void PosixThread::createTask(void* (*fnc_)(void*), void* arg_) { strerror(status) << std::endl; #endif if(errno == ENOMEM) { - uint64_t stackMb = stackSize/10e6; + size_t stackMb = stackSize/10e6; #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "PosixThread::createTask: Insufficient memory for" " the requested " << stackMb << " MB" << std::endl; +#else + fsfw::printError("PosixThread::createTask: Insufficient memory for " + "the requested %zu MB\n", stackMb); #endif } else if(errno == EINVAL) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "PosixThread::createTask: Wrong alignment argument!" << std::endl; +#else + fsfw::printError("PosixThread::createTask: " + "Wrong alignment argument!\n"); #endif } return; From f8abf3527dead67938a7c03f39cf63f46ca73ff5 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 11:51:33 +0100 Subject: [PATCH 04/20] fixed indentation --- datapoollocal/LocalDataPoolManager.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index 0ab6f150..8ccc620c 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -509,10 +509,13 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage( break; } - case(HousekeepingMessage::REPORT_DIAGNOSTICS_REPORT_STRUCTURES): - return generateSetStructurePacket(sid, true); - case(HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): - return generateSetStructurePacket(sid, false); + case(HousekeepingMessage::REPORT_DIAGNOSTICS_REPORT_STRUCTURES): { + return generateSetStructurePacket(sid, true); + } + + case(HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): { + return generateSetStructurePacket(sid, false); + } case(HousekeepingMessage::MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL): case(HousekeepingMessage::MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL): { float newCollIntvl = 0; From 918200e88c542a985e9751c1a053dfa37bc4c66e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 12:53:01 +0100 Subject: [PATCH 05/20] added first unit tests for hk manager --- datapool/PoolDataSetBase.cpp | 4 + datapool/PoolDataSetBase.h | 1 + datapoollocal/LocalDataPoolManager.cpp | 6 +- unittest/tests/action/TestActionHelper.cpp | 1 + unittest/tests/action/TestActionHelper.h | 81 ----------------- unittest/tests/datapoollocal/CMakeLists.txt | 1 + .../datapoollocal/LocalPoolManagerTest.cpp | 28 ++++++ .../tests/datapoollocal/LocalPoolOwnerBase.h | 59 ++++++++++-- unittest/tests/mocks/MessageQueueMockBase.h | 90 +++++++++++++++++++ 9 files changed, 179 insertions(+), 92 deletions(-) create mode 100644 unittest/tests/datapoollocal/LocalPoolManagerTest.cpp create mode 100644 unittest/tests/mocks/MessageQueueMockBase.h diff --git a/datapool/PoolDataSetBase.cpp b/datapool/PoolDataSetBase.cpp index 05797012..7731a8df 100644 --- a/datapool/PoolDataSetBase.cpp +++ b/datapool/PoolDataSetBase.cpp @@ -214,6 +214,10 @@ void PoolDataSetBase::setContainer(PoolVariableIF **variablesContainer) { this->registeredVariables = variablesContainer; } +PoolVariableIF** PoolDataSetBase::getContainer() const { + return registeredVariables; +} + void PoolDataSetBase::setReadCommitProtectionBehaviour( bool protectEveryReadCommit, uint32_t mutexTimeout) { this->protectEveryReadCommitCall = protectEveryReadCommit; diff --git a/datapool/PoolDataSetBase.h b/datapool/PoolDataSetBase.h index 0528f40f..665f4b33 100644 --- a/datapool/PoolDataSetBase.h +++ b/datapool/PoolDataSetBase.h @@ -157,6 +157,7 @@ protected: const size_t maxFillCount = 0; void setContainer(PoolVariableIF** variablesContainer); + PoolVariableIF** getContainer() const; private: bool protectEveryReadCommitCall = false; diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index 8ccc620c..564604cd 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -308,8 +308,8 @@ void LocalDataPoolManager::handleChangeResetLogic( // config error! return; } - - for(auto& changeInfo: *hkUpdateResetList) { + HkUpdateResetList& listRef = *hkUpdateResetList; + for(auto& changeInfo: listRef) { if(changeInfo.dataType != type) { continue; } @@ -636,7 +636,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid, } if(hkQueue == nullptr) { - // error, all destinations invalid + // error, no queue available to send packet with. printWarningOrError(fsfw::OutputTypes::OUT_WARNING, "generateHousekeepingPacket", QUEUE_OR_DESTINATION_INVALID); diff --git a/unittest/tests/action/TestActionHelper.cpp b/unittest/tests/action/TestActionHelper.cpp index 3957bba2..2493a6b8 100644 --- a/unittest/tests/action/TestActionHelper.cpp +++ b/unittest/tests/action/TestActionHelper.cpp @@ -4,6 +4,7 @@ #include #include +#include #include diff --git a/unittest/tests/action/TestActionHelper.h b/unittest/tests/action/TestActionHelper.h index 610fb09e..641ea2c6 100644 --- a/unittest/tests/action/TestActionHelper.h +++ b/unittest/tests/action/TestActionHelper.h @@ -48,85 +48,4 @@ public: }; -class MessageQueueMockBase: public MessageQueueIF { -public: - MessageQueueId_t myQueueId = 0; - bool defaultDestSet = false; - bool messageSent = false; - - - - bool wasMessageSent() { - bool tempMessageSent = messageSent; - messageSent = false; - return tempMessageSent; - } - - virtual ReturnValue_t reply( MessageQueueMessageIF* message ) { - messageSent = true; - lastMessage = *(dynamic_cast(message)); - return HasReturnvaluesIF::RETURN_OK; - }; - virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message, - MessageQueueId_t *receivedFrom) { - (*message) = lastMessage; - lastMessage.clear(); - return HasReturnvaluesIF::RETURN_OK; - } - virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) { - memcpy(message->getBuffer(), lastMessage.getBuffer(), - message->getMessageSize()); - lastMessage.clear(); - return HasReturnvaluesIF::RETURN_OK; - } - virtual ReturnValue_t flush(uint32_t* count) { - return HasReturnvaluesIF::RETURN_OK; - } - virtual MessageQueueId_t getLastPartner() const { - return tconst::testQueueId; - } - virtual MessageQueueId_t getId() const { - return tconst::testQueueId; - } - virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, - MessageQueueMessageIF* message, MessageQueueId_t sentFrom, - bool ignoreFault = false ) { - messageSent = true; - lastMessage = *(dynamic_cast(message)); - return HasReturnvaluesIF::RETURN_OK; - } - virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo, - MessageQueueMessageIF* message, bool ignoreFault = false ) override { - messageSent = true; - lastMessage = *(dynamic_cast(message)); - return HasReturnvaluesIF::RETURN_OK; - } - virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, - MessageQueueId_t sentFrom, bool ignoreFault = false ) { - messageSent = true; - lastMessage = *(dynamic_cast(message)); - return HasReturnvaluesIF::RETURN_OK; - } - virtual ReturnValue_t sendToDefault( MessageQueueMessageIF* message ) { - messageSent = true; - lastMessage = *(dynamic_cast(message)); - return HasReturnvaluesIF::RETURN_OK; - } - virtual void setDefaultDestination(MessageQueueId_t defaultDestination) { - myQueueId = defaultDestination; - defaultDestSet = true; - } - - virtual MessageQueueId_t getDefaultDestination() const { - return myQueueId; - } - virtual bool isDefaultDestinationSet() const { - return defaultDestSet; - } -private: - MessageQueueMessage lastMessage; - -}; - - #endif /* UNITTEST_TESTFW_NEWTESTS_TESTACTIONHELPER_H_ */ diff --git a/unittest/tests/datapoollocal/CMakeLists.txt b/unittest/tests/datapoollocal/CMakeLists.txt index fd7f61b8..f196a080 100644 --- a/unittest/tests/datapoollocal/CMakeLists.txt +++ b/unittest/tests/datapoollocal/CMakeLists.txt @@ -2,4 +2,5 @@ target_sources(${TARGET_NAME} PRIVATE LocalPoolVariableTest.cpp LocalPoolVectorTest.cpp DataSetTest.cpp + LocalPoolManagerTest.cpp ) diff --git a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp new file mode 100644 index 00000000..4cea3ed1 --- /dev/null +++ b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp @@ -0,0 +1,28 @@ +#include "LocalPoolOwnerBase.h" + +#include +#include +#include +#include + + +TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { + LocalPoolOwnerBase* poolOwner = objectManager-> + get(objects::TEST_LOCAL_POOL_OWNER_BASE); + REQUIRE(poolOwner != nullptr); + REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK); + REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() + == retval::CATCH_OK); + REQUIRE(poolOwner->dataset.assignPointers() == retval::CATCH_OK); + MessageQueueMockBase* mqMock = poolOwner->getMockQueueHandle(); + REQUIRE(mqMock != nullptr); + + poolOwner->subscribeWrapperSetUpdate(); + SECTION("BasicTest") { + poolOwner->dataset.setChanged(true); + REQUIRE(poolOwner->hkManager.performHkOperation() == retval::CATCH_OK); + REQUIRE(mqMock->wasMessageSent() == true); + + } +} + diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index c665cb7f..c211801a 100644 --- a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include namespace lpool { static constexpr lp_id_t uint8VarId = 0; @@ -14,16 +16,45 @@ static constexpr lp_id_t floatVarId = 1; static constexpr lp_id_t uint32VarId = 2; static constexpr lp_id_t uint16Vec3Id = 3; static constexpr lp_id_t int64Vec2Id = 4; + +static constexpr uint32_t testSetId = 0; } +class LocalPoolTestDataSet: public StaticLocalDataSet<3> { +public: + LocalPoolTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId): + StaticLocalDataSet(owner, setId) { + } + + 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 = nullptr; + lp_var_t* localPoolVarFloat = nullptr; + lp_vec_t* localPoolUint16Vec = nullptr; + +private: +}; + class LocalPoolOwnerBase: public SystemObject, public HasLocalDataPoolIF { public: LocalPoolOwnerBase( object_id_t objectId = objects::TEST_LOCAL_POOL_OWNER_BASE): - SystemObject(objectId), hkManager(this, messageQueue) { - messageQueue = QueueFactory::instance()->createMessageQueue(10); + SystemObject(objectId), hkManager(this, messageQueue), + dataset(this, lpool::testSetId) { + messageQueue = new MessageQueueMockBase(); } ~LocalPoolOwnerBase() { @@ -89,22 +120,34 @@ public: * @return */ virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override { - // empty for now - return nullptr; + return &dataset; } + + MessageQueueMockBase* getMockQueueHandle() const { + return dynamic_cast(messageQueue); + } + + void subscribeWrapperSetUpdate() { + hkManager.subscribeForSetUpdateMessages(lpool::testSetId, + objects::NO_OBJECT, MessageQueueIF::NO_QUEUE, false); + } + + LocalDataPoolManager hkManager; + LocalPoolTestDataSet dataset; private: - lp_var_t testUint8 = lp_var_t(this, lpool::uint8VarId); - lp_var_t testFloat = lp_var_t(this, lpool::floatVarId); + 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 testUint32 = lp_var_t(this, lpool::uint32VarId); lp_vec_t testUint16Vec = lp_vec_t(this, - lpool::uint16Vec3Id); + lpool::uint16Vec3Id, &dataset); lp_vec_t testInt64Vec = lp_vec_t(this, lpool::int64Vec2Id); MessageQueueIF* messageQueue = nullptr; - LocalDataPoolManager hkManager; bool initialized = false; bool initializedAfterTaskCreation = false; diff --git a/unittest/tests/mocks/MessageQueueMockBase.h b/unittest/tests/mocks/MessageQueueMockBase.h new file mode 100644 index 00000000..5a157329 --- /dev/null +++ b/unittest/tests/mocks/MessageQueueMockBase.h @@ -0,0 +1,90 @@ +#ifndef FSFW_UNITTEST_TESTS_MOCKS_MESSAGEQUEUEMOCKBASE_H_ +#define FSFW_UNITTEST_TESTS_MOCKS_MESSAGEQUEUEMOCKBASE_H_ + +#include +#include +#include +#include + + +class MessageQueueMockBase: public MessageQueueIF { +public: + MessageQueueId_t myQueueId = 0; + bool defaultDestSet = false; + bool messageSent = false; + + + bool wasMessageSent() { + bool tempMessageSent = messageSent; + messageSent = false; + return tempMessageSent; + } + + virtual ReturnValue_t reply( MessageQueueMessageIF* message ) { + messageSent = true; + lastMessage = *(dynamic_cast(message)); + return HasReturnvaluesIF::RETURN_OK; + }; + virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message, + MessageQueueId_t *receivedFrom) { + (*message) = lastMessage; + lastMessage.clear(); + return HasReturnvaluesIF::RETURN_OK; + } + virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) { + std::memcpy(message->getBuffer(), lastMessage.getBuffer(), + message->getMessageSize()); + lastMessage.clear(); + return HasReturnvaluesIF::RETURN_OK; + } + virtual ReturnValue_t flush(uint32_t* count) { + return HasReturnvaluesIF::RETURN_OK; + } + virtual MessageQueueId_t getLastPartner() const { + return tconst::testQueueId; + } + virtual MessageQueueId_t getId() const { + return tconst::testQueueId; + } + virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault = false ) { + messageSent = true; + lastMessage = *(dynamic_cast(message)); + return HasReturnvaluesIF::RETURN_OK; + } + virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo, + MessageQueueMessageIF* message, bool ignoreFault = false ) override { + messageSent = true; + lastMessage = *(dynamic_cast(message)); + return HasReturnvaluesIF::RETURN_OK; + } + virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, + MessageQueueId_t sentFrom, bool ignoreFault = false ) { + messageSent = true; + lastMessage = *(dynamic_cast(message)); + return HasReturnvaluesIF::RETURN_OK; + } + virtual ReturnValue_t sendToDefault( MessageQueueMessageIF* message ) { + messageSent = true; + lastMessage = *(dynamic_cast(message)); + return HasReturnvaluesIF::RETURN_OK; + } + virtual void setDefaultDestination(MessageQueueId_t defaultDestination) { + myQueueId = defaultDestination; + defaultDestSet = true; + } + + virtual MessageQueueId_t getDefaultDestination() const { + return myQueueId; + } + virtual bool isDefaultDestinationSet() const { + return defaultDestSet; + } +private: + MessageQueueMessage lastMessage; + +}; + + +#endif /* FSFW_UNITTEST_TESTS_MOCKS_MESSAGEQUEUEMOCKBASE_H_ */ From b570da6467fb11497e14d3d05abd20d386f68e4b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 13:58:33 +0100 Subject: [PATCH 06/20] more tests added --- datapoollocal/HasLocalDataPoolIF.h | 4 + .../datapoollocal/LocalPoolManagerTest.cpp | 40 +++++++++- .../tests/datapoollocal/LocalPoolOwnerBase.h | 21 ++++- unittest/tests/mocks/HkReceiverMock.h | 20 +++++ unittest/tests/mocks/MessageQueueMockBase.h | 76 ++++++++++++------- .../user/testcfg/objects/systemObjectList.h | 1 + 6 files changed, 131 insertions(+), 31 deletions(-) create mode 100644 unittest/tests/mocks/HkReceiverMock.h diff --git a/datapoollocal/HasLocalDataPoolIF.h b/datapoollocal/HasLocalDataPoolIF.h index 7707fab8..07c6882b 100644 --- a/datapoollocal/HasLocalDataPoolIF.h +++ b/datapoollocal/HasLocalDataPoolIF.h @@ -4,6 +4,7 @@ #include "locPoolDefinitions.h" #include "../datapool/PoolEntryIF.h" +#include "../serviceinterface/ServiceInterface.h" #include "../ipc/MessageQueueSenderIF.h" #include "../housekeeping/HousekeepingMessage.h" @@ -92,6 +93,9 @@ public: #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden" << ". Returning nullptr!" << std::endl; +#else + fsfw::printWarning("HasLocalDataPoolIF::getPoolObjectHandle: " + "Not overriden. Returning nullptr!\n"); #endif return nullptr; } diff --git a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp index 4cea3ed1..2c797505 100644 --- a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -17,12 +18,49 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { MessageQueueMockBase* mqMock = poolOwner->getMockQueueHandle(); REQUIRE(mqMock != nullptr); - poolOwner->subscribeWrapperSetUpdate(); + SECTION("BasicTest") { + // Subscribe for message generation on update. + REQUIRE(poolOwner->subscribeWrapperSetUpdate() == retval::CATCH_OK); + // Subscribe for an update message. poolOwner->dataset.setChanged(true); + // Now the update message should be generated. REQUIRE(poolOwner->hkManager.performHkOperation() == retval::CATCH_OK); REQUIRE(mqMock->wasMessageSent() == true); + CommandMessage messageSent; + uint8_t messagesSent = 0; + REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); + CHECK(messageSent.getCommand() == static_cast( + HousekeepingMessage::UPDATE_NOTIFICATION_SET)); + // Should have been reset. + CHECK(poolOwner->dataset.hasChanged() == false); + // Set changed again, result should be the same. + poolOwner->dataset.setChanged(true); + REQUIRE(poolOwner->hkManager.performHkOperation() == retval::CATCH_OK); + + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); + CHECK(messageSent.getCommand() == static_cast( + HousekeepingMessage::UPDATE_NOTIFICATION_SET)); + + // now subscribe for set update HK as well. + REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK); + poolOwner->dataset.setChanged(true); + REQUIRE(poolOwner->hkManager.performHkOperation() == retval::CATCH_OK); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 2); + // first message sent should be the update notification, considering + // the internal list is a vector checked in insertion order. + REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); + CHECK(messageSent.getCommand() == static_cast( + HousekeepingMessage::UPDATE_NOTIFICATION_SET)); + + REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); + CHECK(messageSent.getCommand() == static_cast( + HousekeepingMessage::HK_REPORT)); + CommandMessageCleaner::clearCommandMessage(&messageSent); } } diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index c211801a..5ff14698 100644 --- a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h @@ -18,6 +18,8 @@ static constexpr lp_id_t uint16Vec3Id = 3; static constexpr lp_id_t int64Vec2Id = 4; static constexpr uint32_t testSetId = 0; +static const sid_t testSid = sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE, + testSetId); } @@ -123,12 +125,27 @@ public: return &dataset; } + virtual LocalPoolObjectBase* getPoolObjectHandle( + lp_id_t localPoolId) override { + return &testUint8; + } + MessageQueueMockBase* getMockQueueHandle() const { return dynamic_cast(messageQueue); } - void subscribeWrapperSetUpdate() { - hkManager.subscribeForSetUpdateMessages(lpool::testSetId, + ReturnValue_t subscribeWrapperSetUpdate() { + return hkManager.subscribeForSetUpdateMessages(lpool::testSetId, + objects::NO_OBJECT, MessageQueueIF::NO_QUEUE, false); + } + + ReturnValue_t subscribeWrapperSetUpdateHk(bool diagnostics = false) { + return hkManager.subscribeForUpdatePackets(lpool::testSid, diagnostics, + false, objects::HK_RECEIVER_MOCK); + } + + ReturnValue_t subscribeWrapperVariableUpdate() { + return hkManager.subscribeForSetUpdateMessages(lpool::testSetId, objects::NO_OBJECT, MessageQueueIF::NO_QUEUE, false); } diff --git a/unittest/tests/mocks/HkReceiverMock.h b/unittest/tests/mocks/HkReceiverMock.h new file mode 100644 index 00000000..e7509c59 --- /dev/null +++ b/unittest/tests/mocks/HkReceiverMock.h @@ -0,0 +1,20 @@ +#ifndef FSFW_UNITTEST_TESTS_MOCKS_HKRECEIVERMOCK_H_ +#define FSFW_UNITTEST_TESTS_MOCKS_HKRECEIVERMOCK_H_ + +#include +#include + +class HkReceiverMock: public SystemObject, public AcceptsHkPacketsIF { +public: + HkReceiverMock(object_id_t objectId): SystemObject(objectId) { + + } + + MessageQueueId_t getHkQueue() const { + return MessageQueueIF::NO_QUEUE; + } +}; + + + +#endif /* FSFW_UNITTEST_TESTS_MOCKS_HKRECEIVERMOCK_H_ */ diff --git a/unittest/tests/mocks/MessageQueueMockBase.h b/unittest/tests/mocks/MessageQueueMockBase.h index 5a157329..4792772f 100644 --- a/unittest/tests/mocks/MessageQueueMockBase.h +++ b/unittest/tests/mocks/MessageQueueMockBase.h @@ -6,68 +6,81 @@ #include #include +#include class MessageQueueMockBase: public MessageQueueIF { public: - MessageQueueId_t myQueueId = 0; + MessageQueueId_t myQueueId = tconst::testQueueId; + uint8_t messageSentCounter = 0; bool defaultDestSet = false; bool messageSent = false; - bool wasMessageSent() { + bool wasMessageSent(uint8_t* messageSentCounter = nullptr, + bool resetCounter = true) { bool tempMessageSent = messageSent; messageSent = false; + if(messageSentCounter != nullptr) { + *messageSentCounter = this->messageSentCounter; + } + if(resetCounter) { + this->messageSentCounter = 0; + } return tempMessageSent; } virtual ReturnValue_t reply( MessageQueueMessageIF* message ) { - messageSent = true; - lastMessage = *(dynamic_cast(message)); + //messageSent = true; + //lastMessage = *(dynamic_cast(message)); + return sendMessage(myQueueId, message); return HasReturnvaluesIF::RETURN_OK; }; virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message, MessageQueueId_t *receivedFrom) { - (*message) = lastMessage; - lastMessage.clear(); - return HasReturnvaluesIF::RETURN_OK; + return receiveMessage(message); } virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) { - std::memcpy(message->getBuffer(), lastMessage.getBuffer(), + std::memcpy(message->getBuffer(), messagesSentQueue.front().getBuffer(), message->getMessageSize()); - lastMessage.clear(); + messagesSentQueue.pop(); return HasReturnvaluesIF::RETURN_OK; } virtual ReturnValue_t flush(uint32_t* count) { return HasReturnvaluesIF::RETURN_OK; } virtual MessageQueueId_t getLastPartner() const { - return tconst::testQueueId; + return myQueueId; } virtual MessageQueueId_t getId() const { - return tconst::testQueueId; + return myQueueId; } virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, MessageQueueMessageIF* message, MessageQueueId_t sentFrom, bool ignoreFault = false ) { - messageSent = true; - lastMessage = *(dynamic_cast(message)); - return HasReturnvaluesIF::RETURN_OK; + //messageSent = true; + //lastMessage = *(dynamic_cast(message)); + //return HasReturnvaluesIF::RETURN_OK; + return sendMessage(sendTo, message); + } + virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, + MessageQueueId_t sentFrom, bool ignoreFault = false ) { + //messageSent = true; + //lastMessage = *(dynamic_cast(message)); + //return HasReturnvaluesIF::RETURN_OK; + return sendMessage(myQueueId, message); + } + virtual ReturnValue_t sendToDefault( MessageQueueMessageIF* message ) { + //messageSent = true; + //lastMessage = *(dynamic_cast(message)); + return sendMessage(myQueueId, message); } virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo, MessageQueueMessageIF* message, bool ignoreFault = false ) override { messageSent = true; - lastMessage = *(dynamic_cast(message)); - return HasReturnvaluesIF::RETURN_OK; - } - virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, - MessageQueueId_t sentFrom, bool ignoreFault = false ) { - messageSent = true; - lastMessage = *(dynamic_cast(message)); - return HasReturnvaluesIF::RETURN_OK; - } - virtual ReturnValue_t sendToDefault( MessageQueueMessageIF* message ) { - messageSent = true; - lastMessage = *(dynamic_cast(message)); + messageSentCounter++; + MessageQueueMessage& messageRef = *( + dynamic_cast(message)); + messagesSentQueue.push(messageRef); return HasReturnvaluesIF::RETURN_OK; } virtual void setDefaultDestination(MessageQueueId_t defaultDestination) { @@ -81,9 +94,16 @@ public: virtual bool isDefaultDestinationSet() const { return defaultDestSet; } -private: - MessageQueueMessage lastMessage; + void clearMessages() { + while(not messagesSentQueue.empty()) { + messagesSentQueue.pop(); + } + } + +private: + std::queue messagesSentQueue; + //MessageQueueMessage lastMessage; }; diff --git a/unittest/user/testcfg/objects/systemObjectList.h b/unittest/user/testcfg/objects/systemObjectList.h index da95617c..88b92131 100644 --- a/unittest/user/testcfg/objects/systemObjectList.h +++ b/unittest/user/testcfg/objects/systemObjectList.h @@ -21,6 +21,7 @@ namespace objects { TEST_ECHO_COM_IF = 20, TEST_DEVICE = 21, + HK_RECEIVER_MOCK = 22, TEST_LOCAL_POOL_OWNER_BASE = 25 }; } From 1af28dd45737440a43cf4f8560e9d28ed3b9cd69 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 13:59:40 +0100 Subject: [PATCH 07/20] updated user folder for unittests --- unittest/user/unittest/CMakeLists.txt | 1 + unittest/user/unittest/core/CatchDefinitions.cpp | 4 ++-- unittest/user/unittest/core/CatchFactory.cpp | 7 ++++++- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 unittest/user/unittest/CMakeLists.txt diff --git a/unittest/user/unittest/CMakeLists.txt b/unittest/user/unittest/CMakeLists.txt new file mode 100644 index 00000000..ad6d4787 --- /dev/null +++ b/unittest/user/unittest/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(core) diff --git a/unittest/user/unittest/core/CatchDefinitions.cpp b/unittest/user/unittest/core/CatchDefinitions.cpp index 02a935d2..531ee87b 100644 --- a/unittest/user/unittest/core/CatchDefinitions.cpp +++ b/unittest/user/unittest/core/CatchDefinitions.cpp @@ -9,8 +9,8 @@ StorageManagerIF* tglob::getIpcStoreHandle() { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "Global object manager uninitialized" << std::endl; #else - fsfw::printError("Global object manager uninitialized"); -#endif + fsfw::printError("Global object manager uninitialized\n\r"); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ return nullptr; } } diff --git a/unittest/user/unittest/core/CatchFactory.cpp b/unittest/user/unittest/core/CatchFactory.cpp index 0597f08c..eabaa21d 100644 --- a/unittest/user/unittest/core/CatchFactory.cpp +++ b/unittest/user/unittest/core/CatchFactory.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include /** * @brief Produces system objects. @@ -30,6 +32,9 @@ void Factory::produce(void) { new HealthTable(objects::HEALTH_TABLE); new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); + new LocalPoolOwnerBase (objects::TEST_LOCAL_POOL_OWNER_BASE); + new HkReceiverMock(objects::HK_RECEIVER_MOCK); + { PoolManager::LocalPoolConfig poolCfg = { {100, 16}, {50, 32}, {25, 64} , {15, 128}, {5, 1024} @@ -65,7 +70,7 @@ void Factory::setStaticFrameworkObjectIds() { DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT; DeviceHandlerBase::rawDataReceiverId = objects::PUS_SERVICE_2_DEVICE_ACCESS; - LocalDataPoolManager::defaultHkDestination = objects::NO_OBJECT; + LocalDataPoolManager::defaultHkDestination = objects::HK_RECEIVER_MOCK; DeviceHandlerFailureIsolation::powerConfirmationId = objects::NO_OBJECT; From 69e931c07b8a6dbb7c3c0f665b33d8722cce8223 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 14:27:57 +0100 Subject: [PATCH 08/20] important logic fix --- datapoollocal/LocalDataPoolManager.cpp | 6 +++- .../datapoollocal/LocalPoolManagerTest.cpp | 18 +++++++++++ .../tests/datapoollocal/LocalPoolOwnerBase.h | 31 +++++++++++++++---- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index 564604cd..c12203f7 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -322,12 +322,16 @@ void LocalDataPoolManager::handleChangeResetLogic( continue; } + // only one update recipient, we can reset changes status immediately. if(changeInfo.updateCounter <= 1) { toReset->setChanged(false); } - if(changeInfo.currentUpdateCounter == 0) { + // All recipients have been notified, reset the changed flag. + if(changeInfo.currentUpdateCounter <= 1) { toReset->setChanged(false); + changeInfo.currentUpdateCounter = 0; } + // Not all recipiens have been notified yet, decrement. else { changeInfo.currentUpdateCounter--; } diff --git a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp index 2c797505..bc07c9d9 100644 --- a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp @@ -60,7 +60,25 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); CHECK(messageSent.getCommand() == static_cast( HousekeepingMessage::HK_REPORT)); + // clear message to avoid memory leak, our mock won't do it for us (yet) CommandMessageCleaner::clearCommandMessage(&messageSent); + + // now subscribe for variable update as well + REQUIRE(not poolOwner->dataset.hasChanged()); + REQUIRE(poolOwner->subscribeWrapperVariableUpdate(lpool::uint8VarId) == + retval::CATCH_OK); + lp_var_t* poolVar = dynamic_cast*>( + poolOwner->getPoolObjectHandle(lpool::uint8VarId)); + REQUIRE(poolVar != nullptr); + poolVar->setChanged(true); + REQUIRE(poolOwner->hkManager.performHkOperation() == retval::CATCH_OK); + + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); + CHECK(messageSent.getCommand() == static_cast( + HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE)); + } } diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index 5ff14698..430c8b07 100644 --- a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h @@ -2,6 +2,7 @@ #define FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_ #include +#include #include #include #include @@ -18,15 +19,16 @@ static constexpr lp_id_t uint16Vec3Id = 3; static constexpr lp_id_t int64Vec2Id = 4; static constexpr uint32_t testSetId = 0; +static constexpr uint8_t dataSetMaxVariables = 10; static const sid_t testSid = sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE, testSetId); } -class LocalPoolTestDataSet: public StaticLocalDataSet<3> { +class LocalPoolTestDataSet: public LocalDataSet { public: LocalPoolTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId): - StaticLocalDataSet(owner, setId) { + LocalDataSet(owner, setId, lpool::dataSetMaxVariables) { } ReturnValue_t assignPointers() { @@ -127,7 +129,24 @@ public: virtual LocalPoolObjectBase* getPoolObjectHandle( lp_id_t localPoolId) override { - return &testUint8; + if(localPoolId == lpool::uint8VarId) { + return &testUint8; + } + else if(localPoolId == lpool::uint16Vec3Id) { + return &testUint16Vec; + } + else if(localPoolId == lpool::floatVarId) { + return &testFloat; + } + else if(localPoolId == lpool::int64Vec2Id) { + return &testInt64Vec; + } + else if(localPoolId == lpool::uint32VarId) { + return &testUint32; + } + else { + return &testUint8; + } } MessageQueueMockBase* getMockQueueHandle() const { @@ -144,9 +163,9 @@ public: false, objects::HK_RECEIVER_MOCK); } - ReturnValue_t subscribeWrapperVariableUpdate() { - return hkManager.subscribeForSetUpdateMessages(lpool::testSetId, - objects::NO_OBJECT, MessageQueueIF::NO_QUEUE, false); + ReturnValue_t subscribeWrapperVariableUpdate(lp_id_t localPoolId) { + return hkManager.subscribeForVariableUpdateMessages(localPoolId, + MessageQueueIF::NO_QUEUE, objects::NO_OBJECT, false); } LocalDataPoolManager hkManager; From 4fa9a1fe1934694999a031fc802119721f05bd93 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 14:33:02 +0100 Subject: [PATCH 09/20] added more tests --- .../datapoollocal/LocalPoolManagerTest.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp index bc07c9d9..ef8f7f33 100644 --- a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp @@ -17,6 +17,8 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { REQUIRE(poolOwner->dataset.assignPointers() == retval::CATCH_OK); MessageQueueMockBase* mqMock = poolOwner->getMockQueueHandle(); REQUIRE(mqMock != nullptr); + CommandMessage messageSent; + uint8_t messagesSent = 0; SECTION("BasicTest") { @@ -27,8 +29,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { // Now the update message should be generated. REQUIRE(poolOwner->hkManager.performHkOperation() == retval::CATCH_OK); REQUIRE(mqMock->wasMessageSent() == true); - CommandMessage messageSent; - uint8_t messagesSent = 0; + REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); CHECK(messageSent.getCommand() == static_cast( HousekeepingMessage::UPDATE_NOTIFICATION_SET)); @@ -62,8 +63,10 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { HousekeepingMessage::HK_REPORT)); // clear message to avoid memory leak, our mock won't do it for us (yet) CommandMessageCleaner::clearCommandMessage(&messageSent); + } - // now subscribe for variable update as well + SECTION("AdvancedTests") { + // Subscribe for variable update as well REQUIRE(not poolOwner->dataset.hasChanged()); REQUIRE(poolOwner->subscribeWrapperVariableUpdate(lpool::uint8VarId) == retval::CATCH_OK); @@ -73,12 +76,20 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { poolVar->setChanged(true); REQUIRE(poolOwner->hkManager.performHkOperation() == retval::CATCH_OK); + // Check update notification was sent. REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); CHECK(messagesSent == 1); + // Should have been reset. + CHECK(poolVar->hasChanged() == false); REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); CHECK(messageSent.getCommand() == static_cast( HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE)); + // now subscribe for the dataset update (HK and update) again + REQUIRE(poolOwner->subscribeWrapperSetUpdate() == retval::CATCH_OK); + REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK); + + } } From 6993415873b88b68e987ef7796e5f371a21fbef4 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 14:54:05 +0100 Subject: [PATCH 10/20] more tests --- datapoollocal/LocalDataPoolManager.cpp | 5 ++++ datapoollocal/LocalDataPoolManager.h | 6 +++++ .../datapoollocal/LocalPoolManagerTest.cpp | 25 +++++++++++++++++++ .../tests/datapoollocal/LocalPoolOwnerBase.h | 4 +++ unittest/tests/mocks/MessageQueueMockBase.h | 13 +++++++++- 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index c12203f7..da6e91a8 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -820,6 +820,11 @@ ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid, return result; } +void LocalDataPoolManager::clearReceiversList() { + // clear the vector completely and releases allocated memory. + HkReceivers().swap(hkReceiversMap); +} + void LocalDataPoolManager::printWarningOrError(fsfw::OutputTypes outputType, const char* functionName, ReturnValue_t error, const char* errorPrint) { if(errorPrint == nullptr) { diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h index eee4c593..2ced2efd 100644 --- a/datapoollocal/LocalDataPoolManager.h +++ b/datapoollocal/LocalDataPoolManager.h @@ -250,6 +250,12 @@ public: LocalDataPoolManager(const LocalDataPoolManager &) = delete; LocalDataPoolManager operator=(const LocalDataPoolManager&) = delete; + /** + * This function can be used to clear the receivers list. This is + * intended for test functions and not for regular operations, because + * the insertion operations allocate dynamically. + */ + void clearReceiversList(); private: LocalDataPool localPoolMap; //! Every housekeeping data manager has a mutex to protect access diff --git a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp index ef8f7f33..2ea169b2 100644 --- a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp @@ -66,6 +66,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { } SECTION("AdvancedTests") { + poolOwner->resetSubscriptionList(); // Subscribe for variable update as well REQUIRE(not poolOwner->dataset.hasChanged()); REQUIRE(poolOwner->subscribeWrapperVariableUpdate(lpool::uint8VarId) == @@ -89,7 +90,31 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { REQUIRE(poolOwner->subscribeWrapperSetUpdate() == retval::CATCH_OK); REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK); + poolOwner->dataset.setChanged(true); + REQUIRE(poolOwner->hkManager.performHkOperation() == retval::CATCH_OK); + // now two messages should be sent. + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 2); + mqMock->clearMessages(true); + poolOwner->dataset.setChanged(true); + poolVar->setChanged(true); + REQUIRE(poolOwner->hkManager.performHkOperation() == retval::CATCH_OK); + // now three messages should be sent. + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 3); + REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); + CHECK(messageSent.getCommand() == static_cast( + HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE)); + REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); + CHECK(messageSent.getCommand() == static_cast( + HousekeepingMessage::UPDATE_NOTIFICATION_SET)); + REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); + CHECK(messageSent.getCommand() == static_cast( + HousekeepingMessage::HK_REPORT)); + CommandMessageCleaner::clearCommandMessage(&messageSent); + REQUIRE(mqMock->receiveMessage(&messageSent) == + static_cast(MessageQueueIF::EMPTY)); } } diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index 430c8b07..a5389812 100644 --- a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h @@ -168,6 +168,10 @@ public: MessageQueueIF::NO_QUEUE, objects::NO_OBJECT, false); } + void resetSubscriptionList() { + hkManager.clearReceiversList(); + } + LocalDataPoolManager hkManager; LocalPoolTestDataSet dataset; private: diff --git a/unittest/tests/mocks/MessageQueueMockBase.h b/unittest/tests/mocks/MessageQueueMockBase.h index 4792772f..7b810b41 100644 --- a/unittest/tests/mocks/MessageQueueMockBase.h +++ b/unittest/tests/mocks/MessageQueueMockBase.h @@ -40,6 +40,10 @@ public: return receiveMessage(message); } virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) { + if(messagesSentQueue.empty()) { + return MessageQueueIF::EMPTY; + } + std::memcpy(message->getBuffer(), messagesSentQueue.front().getBuffer(), message->getMessageSize()); messagesSentQueue.pop(); @@ -95,8 +99,15 @@ public: return defaultDestSet; } - void clearMessages() { + void clearMessages(bool clearCommandMessages = true) { while(not messagesSentQueue.empty()) { + if(clearCommandMessages) { + CommandMessage message; + std::memcpy(message.getBuffer(), + messagesSentQueue.front().getBuffer(), + message.getMessageSize()); + message.clear(); + } messagesSentQueue.pop(); } } From 15b65b78e730d73a840d9708658a7d0d2817a19e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 14:57:56 +0100 Subject: [PATCH 11/20] ndentiation --- datapoollocal/LocalDataPoolManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index da6e91a8..19c94049 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -48,7 +48,7 @@ ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) { ipcStore = objectManager->get(objects::IPC_STORE); if(ipcStore == nullptr) { - // error, all destinations invalid + // error, all destinations invalid printWarningOrError(fsfw::OutputTypes::OUT_ERROR, "initialize", HasReturnvaluesIF::RETURN_FAILED, "Could not set IPC store."); From 63ce87acab188be447f820c421661487fda7b5a9 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 14:59:03 +0100 Subject: [PATCH 12/20] more indentation corrections --- datapoollocal/LocalDataPoolManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index 19c94049..2a0ec923 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -65,7 +65,7 @@ ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) { else { printWarningOrError(fsfw::OutputTypes::OUT_ERROR, "initialize", QUEUE_OR_DESTINATION_INVALID); - return QUEUE_OR_DESTINATION_INVALID; + return QUEUE_OR_DESTINATION_INVALID; } } From 4bfbeead0024898b1e2139ec6ed4a877e60980c3 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 15:04:47 +0100 Subject: [PATCH 13/20] additional doc --- unittest/tests/datapoollocal/LocalPoolManagerTest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp index 2ea169b2..f497e610 100644 --- a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp @@ -66,6 +66,8 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { } SECTION("AdvancedTests") { + // we need to reset the subscription list because the pool owner + // is a global object. poolOwner->resetSubscriptionList(); // Subscribe for variable update as well REQUIRE(not poolOwner->dataset.hasChanged()); From 7a053b6f72b50a3868019bcbc9af4f80c5f03d55 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 17:24:19 +0100 Subject: [PATCH 14/20] updated README --- README.md | 130 +++++++--------------------------------- doc/README-highlevel.md | 99 ++++++++++++++++++++++++++++++ doc/README-osal.md | 32 ++++++++++ 3 files changed, 153 insertions(+), 108 deletions(-) create mode 100644 doc/README-highlevel.md create mode 100644 doc/README-osal.md diff --git a/README.md b/README.md index 8552e0c8..f308ac9f 100644 --- a/README.md +++ b/README.md @@ -9,121 +9,35 @@ The initial version of the Flight Software Framework was developed during the Flying Laptop Project by the University of Stuttgart in cooperation with Airbus Defence and Space GmbH. -## Intended Use +## Quick facts -The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. -Therefore, a mode and health system provides control over the states of the software and the controlled devices. -In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. +The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. Therefore, a mode and health system provides control over the states of the software and the controlled devices. In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. -The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile Memory. -For reference, current Applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. -The `fsfw` was also tested on the STM32H743ZI-Nucleo board. +The FSFW provides abstraction layers for operating systems to provide a uniform operating system abstraction layer (OSAL). Some components of this OSAL are required internally by the FSFW but is also very useful for developers to implement the same application logic on different operating systems with a uniform interface. -## How to Use +Currently, the FSFW provides the following OSALs: + +- Linux +- Host +- FreeRTOS +- RTEMS + +The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile Memory. For reference, current applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. The `fsfw` was also successfully run on the STM32H743ZI-Nucleo board and on a Raspberry Pi. + +## Getting started + +The [FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example) provides a good starting point and a demo to see the FSFW capabilities and build it with the Make or the CMake build system. It is recommended to evaluate the FSFW by building and playing around with the demo application. -The [FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example) provides a good starting point and a demo -to see the FSFW capabilities and build it with the Make or the CMake build system. Generally, the FSFW is included in a project by compiling the FSFW sources and providing -a configuration folder and adding it to the include path. +a configuration folder and adding it to the include path. There are some functions like `printChar` which are different depending on the target architecture and need to be implemented by the mission developer. + A template configuration folder was provided and can be copied into the project root to have -a starting point. The [configuration section](doc/README-config.md#top) provides more specific information about -the possible options. +a starting point. The [configuration section](doc/README-config.md#top) provides more specific information about the possible options. -## Structure +## Index -The general structure is driven by the usage of interfaces provided by objects. -The FSFW uses C++11 as baseline. The intention behind this is that this C++ Standard should be widely available, even with older compilers. -The FSFW uses dynamic allocation during the initialization but provides static containers during runtime. -This simplifies the instantiation of objects and allows the usage of some standard containers. -Dynamic Allocation after initialization is discouraged and different solutions are provided in the FSFW to achieve that. -The fsfw uses run-time type information but exceptions are not allowed. +[1. High-level overview](doc/README-highlevel.md#top) +[2. Core components](doc/README-core.md#top) +[3. OSAL overview](doc/README-osal.md#top) -### Failure Handling -Functions should return a defined ReturnValue_t to signal to the caller that something has gone wrong. -Returnvalues must be unique. For this the function HasReturnvaluesIF::makeReturnCode or the Macro MAKE_RETURN can be used. -The CLASS_ID is a unique id for that type of object. See returnvalues/FwClassIds. - -### OSAL - -The FSFW provides operation system abstraction layers for Linux, FreeRTOS and RTEMS. -A independent Host OSAL is in development which will provide abstraction for common type of -host OSes (tested for Linux and Windows, not for MacOS yet). -The OSAL provides periodic tasks, message queues, clocks and semaphores as well as mutexes. - -### Core Components - -The FSFW has following core components. More detailed informations can be found in the -[core component section](doc/README-core.md#top): - -1. Tasks: Abstraction for different (periodic) task types like periodic tasks or tasks with fixed timeslots -2. ObjectManager: This module stores all `SystemObjects` by mapping a provided unique object ID to the object handles. -3. Static Stores: Different stores are provided to store data of variable size (like telecommands or small telemetry) in a pool structure without - using dynamic memory allocation. These pools are allocated up front. -3. Clock: This module provided common time related functions -4. EventManager: This module allows routing of events generated by `SystemObjects` -5. HealthTable: A component which stores the health states of objects - -### Static Ids in the framework - -Some parts of the framework use a static routing address for communication. -An example setup of ids can be found in the example config in "defaultcft/fsfwconfig/objects/Factory::setStaticFrameworkObjectIds()". - -### Events - -Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT. This works analog to the returnvalues. -Every object that needs own EventIds has to get a unique SUBSYSTEM_ID. -Every SystemObject can call triggerEvent from the parent class. -Therefore, event messages contain the specific EventId and the objectId of the object that has triggered. - -### Internal Communication - -Components communicate mostly over Message through Queues. -Those queues are created by calling the singleton QueueFactory::instance()->create(). - -### External Communication - -The external communication with the mission control system is mostly up to the user implementation. -The FSFW provides PUS Services which can be used to but don't need to be used. -The services can be seen as a conversion from a TC to a message based communication and back. - -#### CCSDS Frames, CCSDS Space Packets and PUS - -If the communication is based on CCSDS Frames and Space Packets, several classes can be used to distributed the packets to the corresponding services. Those can be found in tcdistribution. -If Space Packets are used, a timestamper must be created. -An example can be found in the timemanager folder, this uses CCSDSTime::CDS_short. - -#### Device Handlers - -DeviceHandlers are another important component of the FSFW. -The idea is, to have a software counterpart of every physical device to provide a simple mode, health and commanding interface. -By separating the underlying Communication Interface with DeviceCommunicationIF, a device handler (DH) can be tested on different hardware. -The DH has mechanisms to monitor the communication with the physical device which allow for FDIR reaction. -Device Handlers can be created by overriding `DeviceHandlerBase`. -A standard FDIR component for the DH will be created automatically but can be overwritten by the user. -More information on DeviceHandlers can be found in the related [documentation section](doc/README-devicehandlers.md#top). - -#### Modes, Health - -The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components. -On-board Mode Management is implement in hierarchy system. -DeviceHandlers and Controllers are the lowest part of the hierarchy. -The next layer are Assemblies. Those assemblies act as a component which handle redundancies of handlers. -Assemblies share a common core with the next level which are the Subsystems. - -Those Assemblies are intended to act as auto-generated components from a database which describes the subsystem modes. -The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded. -Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a switch into any higher AOCS mode might first turn on the sensors, than the actuators and the controller as last component. -The target table is used to describe the state that is checked continuously by the subsystem. -All of this allows System Modes to be generated as Subsystem object as well from the same database. -This System contains list of subsystem modes in the transition and target tables. -Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be commanded. - -The health state represents if the component is able to perform its tasks. -This can be used to signal the system to avoid using this component instead of a redundant one. -The on-board FDIR uses the health state for isolation and recovery. - -## Unit Tests - -Unit Tests are provided in the unittest folder. Those use the catch2 framework but do not include catch2 itself. -See README.md in the unittest Folder. \ No newline at end of file diff --git a/doc/README-highlevel.md b/doc/README-highlevel.md new file mode 100644 index 00000000..fac89c53 --- /dev/null +++ b/doc/README-highlevel.md @@ -0,0 +1,99 @@ +# High-level overview + +## Structure + +The general structure is driven by the usage of interfaces provided by objects. +The FSFW uses C++11 as baseline. The intention behind this is that this C++ Standard should be widely available, even with older compilers. +The FSFW uses dynamic allocation during the initialization but provides static containers during runtime. +This simplifies the instantiation of objects and allows the usage of some standard containers. +Dynamic Allocation after initialization is discouraged and different solutions are provided in the FSFW to achieve that. +The fsfw uses run-time type information but exceptions are not allowed. + +### Failure Handling + +Functions should return a defined ReturnValue_t to signal to the caller that something has gone wrong. +Returnvalues must be unique. For this the function HasReturnvaluesIF::makeReturnCode or the Macro MAKE_RETURN can be used. +The CLASS_ID is a unique id for that type of object. See returnvalues/FwClassIds. + +### OSAL + +The FSFW provides operation system abstraction layers for Linux, FreeRTOS and RTEMS. +The OSAL provides periodic tasks, message queues, clocks and semaphores as well as mutexes. +The [OSAL README](doc/README-osal.md#top) provides more detailed information on provided components and how to use them. + +### Core Components + +The FSFW has following core components. More detailed informations can be found in the +[core component section](doc/README-core.md#top): + +1. Tasks: Abstraction for different (periodic) task types like periodic tasks or tasks with fixed timeslots +2. ObjectManager: This module stores all `SystemObjects` by mapping a provided unique object ID to the object handles. +3. Static Stores: Different stores are provided to store data of variable size (like telecommands or small telemetry) in a pool structure without + using dynamic memory allocation. These pools are allocated up front. +3. Clock: This module provided common time related functions +4. EventManager: This module allows routing of events generated by `SystemObjects` +5. HealthTable: A component which stores the health states of objects + +### Static IDs in the framework + +Some parts of the framework use a static routing address for communication. +An example setup of ids can be found in the example config in "defaultcft/fsfwconfig/objects/Factory::setStaticFrameworkObjectIds()". + +### Events + +Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT. This works analog to the returnvalues. +Every object that needs own EventIds has to get a unique SUBSYSTEM_ID. +Every SystemObject can call triggerEvent from the parent class. +Therefore, event messages contain the specific EventId and the objectId of the object that has triggered. + +### Internal Communication + +Components communicate mostly over Message through Queues. +Those queues are created by calling the singleton QueueFactory::instance()->create(). + +### External Communication + +The external communication with the mission control system is mostly up to the user implementation. +The FSFW provides PUS Services which can be used to but don't need to be used. +The services can be seen as a conversion from a TC to a message based communication and back. + +#### CCSDS Frames, CCSDS Space Packets and PUS + +If the communication is based on CCSDS Frames and Space Packets, several classes can be used to distributed the packets to the corresponding services. Those can be found in tcdistribution. +If Space Packets are used, a timestamper must be created. +An example can be found in the timemanager folder, this uses CCSDSTime::CDS_short. + +#### Device Handlers + +DeviceHandlers are another important component of the FSFW. +The idea is, to have a software counterpart of every physical device to provide a simple mode, health and commanding interface. +By separating the underlying Communication Interface with DeviceCommunicationIF, a device handler (DH) can be tested on different hardware. +The DH has mechanisms to monitor the communication with the physical device which allow for FDIR reaction. +Device Handlers can be created by overriding `DeviceHandlerBase`. +A standard FDIR component for the DH will be created automatically but can be overwritten by the user. +More information on DeviceHandlers can be found in the related [documentation section](doc/README-devicehandlers.md#top). + +#### Modes, Health + +The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components. +On-board Mode Management is implement in hierarchy system. +DeviceHandlers and Controllers are the lowest part of the hierarchy. +The next layer are Assemblies. Those assemblies act as a component which handle redundancies of handlers. +Assemblies share a common core with the next level which are the Subsystems. + +Those Assemblies are intended to act as auto-generated components from a database which describes the subsystem modes. +The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded. +Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a switch into any higher AOCS mode might first turn on the sensors, than the actuators and the controller as last component. +The target table is used to describe the state that is checked continuously by the subsystem. +All of this allows System Modes to be generated as Subsystem object as well from the same database. +This System contains list of subsystem modes in the transition and target tables. +Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be commanded. + +The health state represents if the component is able to perform its tasks. +This can be used to signal the system to avoid using this component instead of a redundant one. +The on-board FDIR uses the health state for isolation and recovery. + +## Unit Tests + +Unit Tests are provided in the unittest folder. Those use the catch2 framework but do not include catch2 itself. More information on how to run these tests can be found in the separate +[`fsfw_tests` reposoitory](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_tests) diff --git a/doc/README-osal.md b/doc/README-osal.md new file mode 100644 index 00000000..206596ac --- /dev/null +++ b/doc/README-osal.md @@ -0,0 +1,32 @@ +# Operating System Abstraction Layer (OSAL) + +Some specific information on the provided OSALs are provided. + +## Linux OSAL + +This OSAL can be used to compile for Linux host systems like Ubuntu 20.04 or for +embedded Linux targets like the Raspberry Pi. This OSAL generally requires threading support +and real-time functionalities. This is generally done by adding `-lrt` and `-lpthread` to the linked libraries in the compilation process. The CMake build support provided will do this automatically for the `fsfw` target. It should be noted that most UNIX systems need to be configured specifically to allow the real-time functionalities required by the FSFW. + +More information on how to set up a Linux system accordingly can be found in the +[Linux README of the FSFW example application](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example/src/branch/master/doc/README-linux.md#top) + +## Hosted OSAL + +This is the newest OSAL. Support for Semaphores has not been implemented yet and will propably be implemented as soon as C++20 with Semaphore support has matured. This OSAL can be used to run the FSFW on any host system, but currently has only been tested on Windows 10 and Ubuntu 20.04. Unlike the other OSALs, it uses dynamic memory allocation (e.g. for the message queue implementation). Cross-platform serial port (USB) support might be added soon. + +## FreeRTOS OSAL + +FreeRTOS is not included and the developer needs to take care of compiling the FreeRTOS sources and adding the `FreeRTOSConfig.h` file location to the include path. This OSAL has only been tested extensively with the pre-emptive scheduler configuration so far but it should in principle also be possible to use a cooperative scheduler. It is recommended to use the `heap_4` allocation scheme. When using newlib (nano), it is also recommended to add `#define configUSE_NEWLIB_REENTRANT` to the FreeRTOS configuration file to ensure thread-safety. + +When using this OSAL, developers also need to provide an implementation for the `vRequestContextSwitchFromISR` function. This has been done because the call to request a context switch from an ISR is generally located in the `portmacro.h` header and is different depending on the target architecture or device. + +## RTEMS OSAL + +The RTEMS OSAL was the first implemented OSAL which is also used on the active satellite Flying Laptop. + +## TCP/IP socket abstraction + +The Linux and Host OSAL provide abstraction layers for the socket API. Currently, only UDP sockets have been imlemented. This is very useful to test TMTC handling either on the host computer directly (targeting localhost with a TMTC application) or on embedded Linux devices, sending TMTC packets via Ethernet. + + From 952438f2528aa792638348fb5cce90a36e116837 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 17:25:19 +0100 Subject: [PATCH 15/20] README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f308ac9f..d74c4c76 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Currently, the FSFW provides the following OSALs: - FreeRTOS - RTEMS -The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile Memory. For reference, current applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. The `fsfw` was also successfully run on the STM32H743ZI-Nucleo board and on a Raspberry Pi. +The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile Memory. For reference, current applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. The `fsfw` was also successfully run on the STM32H743ZI-Nucleo board and on a Raspberry Pi and is currently running on the active satellite mission Flying Laptop. ## Getting started From 6dde0ab8f66dc144b0323437f93e3e8d689eb41f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 17:25:51 +0100 Subject: [PATCH 16/20] readme update --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d74c4c76..ba64f757 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ a starting point. The [configuration section](doc/README-config.md#top) provides ## Index -[1. High-level overview](doc/README-highlevel.md#top) -[2. Core components](doc/README-core.md#top) -[3. OSAL overview](doc/README-osal.md#top) +[1. High-level overview](doc/README-highlevel.md#top)
+[2. Core components](doc/README-core.md#top)
+[3. OSAL overview](doc/README-osal.md#top)
From 368b42c7b031cbc9c4689ed41576249b51c396f2 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 17:26:48 +0100 Subject: [PATCH 17/20] formatting improvements --- doc/README-core.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/README-core.md b/doc/README-core.md index c47ae0f2..6431b831 100644 --- a/doc/README-core.md +++ b/doc/README-core.md @@ -16,9 +16,8 @@ Flight Software Framework * A reference to an object can be get by calling the following function. T must be the specific Interface you want to call. A nullptr check of the returning Pointer must be done. This function is based on Run-time type information. -``` c++ - template T* ObjectManagerIF::get( object_id_t id ) - +```cpp +template T* ObjectManagerIF::get( object_id_t id ) ``` * A typical way to create all objects on startup is a handing a static produce function to the ObjectManager on creation. By calling objectManager->initialize() the produce function will be called and all SystemObjects will be initialized afterwards. From 1f901a14ffcb8af674dfc7703b79eefb78bb5cf9 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 17:28:13 +0100 Subject: [PATCH 18/20] updated osal readme --- doc/README-osal.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README-osal.md b/doc/README-osal.md index 206596ac..6f8ce60f 100644 --- a/doc/README-osal.md +++ b/doc/README-osal.md @@ -6,7 +6,7 @@ Some specific information on the provided OSALs are provided. This OSAL can be used to compile for Linux host systems like Ubuntu 20.04 or for embedded Linux targets like the Raspberry Pi. This OSAL generally requires threading support -and real-time functionalities. This is generally done by adding `-lrt` and `-lpthread` to the linked libraries in the compilation process. The CMake build support provided will do this automatically for the `fsfw` target. It should be noted that most UNIX systems need to be configured specifically to allow the real-time functionalities required by the FSFW. +and real-time functionalities. For most UNIX systems, this is done by adding `-lrt` and `-lpthread` to the linked libraries in the compilation process. The CMake build support provided will do this automatically for the `fsfw` target. It should be noted that most UNIX systems need to be configured specifically to allow the real-time functionalities required by the FSFW. More information on how to set up a Linux system accordingly can be found in the [Linux README of the FSFW example application](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example/src/branch/master/doc/README-linux.md#top) From c956feafd6fc5932570181cde9e51b525f20980c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 17:30:31 +0100 Subject: [PATCH 19/20] added new readme which will be filled later --- doc/README-devicehandlers.md | 2 +- doc/README-localpools.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 doc/README-localpools.md diff --git a/doc/README-devicehandlers.md b/doc/README-devicehandlers.md index 6e737cc5..8b6551aa 100644 --- a/doc/README-devicehandlers.md +++ b/doc/README-devicehandlers.md @@ -1 +1 @@ -## FSFW DeviceHandlers \ No newline at end of file +## Device Handlers diff --git a/doc/README-localpools.md b/doc/README-localpools.md new file mode 100644 index 00000000..8dd83385 --- /dev/null +++ b/doc/README-localpools.md @@ -0,0 +1,3 @@ +## Local Data Pools + + From e497c4ab1505f7438c96283a391f3b71de81fd8e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 10 Jan 2021 17:34:21 +0100 Subject: [PATCH 20/20] added new reamde file stubs --- README.md | 5 +++++ doc/README-controllers.md | 1 + doc/README-pus.md | 1 + 3 files changed, 7 insertions(+) create mode 100644 doc/README-controllers.md create mode 100644 doc/README-pus.md diff --git a/README.md b/README.md index ba64f757..fb3be429 100644 --- a/README.md +++ b/README.md @@ -39,5 +39,10 @@ a starting point. The [configuration section](doc/README-config.md#top) provides [1. High-level overview](doc/README-highlevel.md#top)
[2. Core components](doc/README-core.md#top)
[3. OSAL overview](doc/README-osal.md#top)
+[4. PUS services](doc/README-pus.md#top)
+[5. Device Handler overview](doc/README-devicehandlers.md#top)
+[6. Controller overview](doc/README-controllers.md#top)
+[7. Local Data Pools](doc/README-localpools.md#top)
+ diff --git a/doc/README-controllers.md b/doc/README-controllers.md new file mode 100644 index 00000000..7af0652d --- /dev/null +++ b/doc/README-controllers.md @@ -0,0 +1 @@ +## Controllers diff --git a/doc/README-pus.md b/doc/README-pus.md new file mode 100644 index 00000000..928d2eda --- /dev/null +++ b/doc/README-pus.md @@ -0,0 +1 @@ +## PUS Services