From 6e97bd4db4a51db75c6b5f4fc8998dcc3a731191 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 17 Oct 2021 23:27:31 +0200 Subject: [PATCH 01/23] added integration test code --- tests/src/fsfw_tests/CMakeLists.txt | 1 + .../src/fsfw_tests/integration/CMakeLists.txt | 4 + .../integration/assemblies/CMakeLists.txt | 3 + .../integration/assemblies/TestAssembly.cpp | 201 +++++ .../integration/assemblies/TestAssembly.h | 56 ++ .../integration/controller/CMakeLists.txt | 3 + .../integration/controller/TestController.cpp | 214 +++++ .../integration/controller/TestController.h | 50 ++ .../ctrldefinitions/testCtrlDefinitions.h | 18 + .../integration/devices/CMakeLists.txt | 5 + .../integration/devices/TestCookie.cpp | 14 + .../integration/devices/TestCookie.h | 22 + .../integration/devices/TestDeviceHandler.cpp | 804 ++++++++++++++++++ .../integration/devices/TestDeviceHandler.h | 142 ++++ .../integration/devices/TestEchoComIF.cpp | 86 ++ .../integration/devices/TestEchoComIF.h | 56 ++ .../devicedefinitions/testDeviceDefinitions.h | 100 +++ .../integration/task/CMakeLists.txt | 3 + .../fsfw_tests/integration/task/TestTask.cpp | 80 ++ .../fsfw_tests/integration/task/TestTask.h | 56 ++ 20 files changed, 1918 insertions(+) create mode 100644 tests/src/fsfw_tests/integration/CMakeLists.txt create mode 100644 tests/src/fsfw_tests/integration/assemblies/CMakeLists.txt create mode 100644 tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp create mode 100644 tests/src/fsfw_tests/integration/assemblies/TestAssembly.h create mode 100644 tests/src/fsfw_tests/integration/controller/CMakeLists.txt create mode 100644 tests/src/fsfw_tests/integration/controller/TestController.cpp create mode 100644 tests/src/fsfw_tests/integration/controller/TestController.h create mode 100644 tests/src/fsfw_tests/integration/controller/ctrldefinitions/testCtrlDefinitions.h create mode 100644 tests/src/fsfw_tests/integration/devices/CMakeLists.txt create mode 100644 tests/src/fsfw_tests/integration/devices/TestCookie.cpp create mode 100644 tests/src/fsfw_tests/integration/devices/TestCookie.h create mode 100644 tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp create mode 100644 tests/src/fsfw_tests/integration/devices/TestDeviceHandler.h create mode 100644 tests/src/fsfw_tests/integration/devices/TestEchoComIF.cpp create mode 100644 tests/src/fsfw_tests/integration/devices/TestEchoComIF.h create mode 100644 tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h create mode 100644 tests/src/fsfw_tests/integration/task/CMakeLists.txt create mode 100644 tests/src/fsfw_tests/integration/task/TestTask.cpp create mode 100644 tests/src/fsfw_tests/integration/task/TestTask.h diff --git a/tests/src/fsfw_tests/CMakeLists.txt b/tests/src/fsfw_tests/CMakeLists.txt index e4a6be80..a06bf3fe 100644 --- a/tests/src/fsfw_tests/CMakeLists.txt +++ b/tests/src/fsfw_tests/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(integration) if(FSFW_ADD_INTERNAL_TESTS) add_subdirectory(internal) diff --git a/tests/src/fsfw_tests/integration/CMakeLists.txt b/tests/src/fsfw_tests/integration/CMakeLists.txt new file mode 100644 index 00000000..e44fbee7 --- /dev/null +++ b/tests/src/fsfw_tests/integration/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(assemblies) +add_subdirectory(controller) +add_subdirectory(devices) +add_subdirectory(task) diff --git a/tests/src/fsfw_tests/integration/assemblies/CMakeLists.txt b/tests/src/fsfw_tests/integration/assemblies/CMakeLists.txt new file mode 100644 index 00000000..22c06600 --- /dev/null +++ b/tests/src/fsfw_tests/integration/assemblies/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE + TestAssembly.cpp +) \ No newline at end of file diff --git a/tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp b/tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp new file mode 100644 index 00000000..1c497ebd --- /dev/null +++ b/tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp @@ -0,0 +1,201 @@ +#include "TestAssembly.h" + +#include + + +TestAssembly::TestAssembly(object_id_t objectId, object_id_t parentId, object_id_t testDevice0, + object_id_t testDevice1): + AssemblyBase(objectId, parentId), deviceHandler0Id(testDevice0), + deviceHandler1Id(testDevice1) { + ModeListEntry newModeListEntry; + newModeListEntry.setObject(testDevice0); + newModeListEntry.setMode(MODE_OFF); + newModeListEntry.setSubmode(SUBMODE_NONE); + + commandTable.insert(newModeListEntry); + + newModeListEntry.setObject(testDevice1); + newModeListEntry.setMode(MODE_OFF); + newModeListEntry.setSubmode(SUBMODE_NONE); + + commandTable.insert(newModeListEntry); + +} + +TestAssembly::~TestAssembly() { +} + +ReturnValue_t TestAssembly::commandChildren(Mode_t mode, + Submode_t submode) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestAssembly: Received command to go to mode " << mode << + " submode " << (int) submode << std::endl; +#else + sif::printInfo("TestAssembly: Received command to go to mode %d submode %d\n", mode, submode); +#endif + ReturnValue_t result = RETURN_OK; + if(mode == MODE_OFF){ + commandTable[0].setMode(MODE_OFF); + commandTable[0].setSubmode(SUBMODE_NONE); + commandTable[1].setMode(MODE_OFF); + commandTable[1].setSubmode(SUBMODE_NONE); + } + else if(mode == DeviceHandlerIF::MODE_NORMAL) { + if(submode == submodes::SINGLE){ + commandTable[0].setMode(MODE_OFF); + commandTable[0].setSubmode(SUBMODE_NONE); + commandTable[1].setMode(MODE_OFF); + commandTable[1].setSubmode(SUBMODE_NONE); + // We try to prefer 0 here but we try to switch to 1 even if it might fail + if(isDeviceAvailable(deviceHandler0Id)) { + if (childrenMap[deviceHandler0Id].mode == MODE_ON) { + commandTable[0].setMode(mode); + commandTable[0].setSubmode(SUBMODE_NONE); + } + else { + commandTable[0].setMode(MODE_ON); + commandTable[0].setSubmode(SUBMODE_NONE); + result = NEED_SECOND_STEP; + } + } + else { + if (childrenMap[deviceHandler1Id].mode == MODE_ON) { + commandTable[1].setMode(mode); + commandTable[1].setSubmode(SUBMODE_NONE); + } + else{ + commandTable[1].setMode(MODE_ON); + commandTable[1].setSubmode(SUBMODE_NONE); + result = NEED_SECOND_STEP; + } + } + } + else{ + // Dual Mode Normal + if (childrenMap[deviceHandler0Id].mode == MODE_ON) { + commandTable[0].setMode(mode); + commandTable[0].setSubmode(SUBMODE_NONE); + } + else{ + commandTable[0].setMode(MODE_ON); + commandTable[0].setSubmode(SUBMODE_NONE); + result = NEED_SECOND_STEP; + } + if (childrenMap[deviceHandler1Id].mode == MODE_ON) { + commandTable[1].setMode(mode); + commandTable[1].setSubmode(SUBMODE_NONE); + } + else{ + commandTable[1].setMode(MODE_ON); + commandTable[1].setSubmode(SUBMODE_NONE); + result = NEED_SECOND_STEP; + } + } + } + else{ + //Mode ON + if(submode == submodes::SINGLE){ + commandTable[0].setMode(MODE_OFF); + commandTable[0].setSubmode(SUBMODE_NONE); + commandTable[1].setMode(MODE_OFF); + commandTable[1].setSubmode(SUBMODE_NONE); + // We try to prefer 0 here but we try to switch to 1 even if it might fail + if(isDeviceAvailable(deviceHandler0Id)){ + commandTable[0].setMode(MODE_ON); + commandTable[0].setSubmode(SUBMODE_NONE); + } + else{ + commandTable[1].setMode(MODE_ON); + commandTable[1].setSubmode(SUBMODE_NONE); + } + } + else{ + commandTable[0].setMode(MODE_ON); + commandTable[0].setSubmode(SUBMODE_NONE); + commandTable[1].setMode(MODE_ON); + commandTable[1].setSubmode(SUBMODE_NONE); + } + } + + + + HybridIterator iter(commandTable.begin(), + commandTable.end()); + executeTable(iter); + return result; +} + +ReturnValue_t TestAssembly::isModeCombinationValid(Mode_t mode, + Submode_t submode) { + switch (mode) { + case MODE_OFF: + if (submode == SUBMODE_NONE) { + return RETURN_OK; + } else { + return INVALID_SUBMODE; + } + case DeviceHandlerIF::MODE_NORMAL: + case MODE_ON: + if (submode < 3) { + return RETURN_OK; + } else { + return INVALID_SUBMODE; + } + } + return INVALID_MODE; +} + +ReturnValue_t TestAssembly::initialize() { + ReturnValue_t result = AssemblyBase::initialize(); + if(result != RETURN_OK){ + return result; + } + handler0 = ObjectManager::instance()->get(deviceHandler0Id); + handler1 = ObjectManager::instance()->get(deviceHandler1Id); + if((handler0 == nullptr) or (handler1 == nullptr)){ + return HasReturnvaluesIF::RETURN_FAILED; + } + + handler0->setParentQueue(this->getCommandQueue()); + handler1->setParentQueue(this->getCommandQueue()); + + + result = registerChild(objects::TEST_DEVICE_HANDLER_0); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = registerChild(objects::TEST_DEVICE_HANDLER_1); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + return result; +} + +ReturnValue_t TestAssembly::checkChildrenStateOn( + Mode_t wantedMode, Submode_t wantedSubmode) { + if(submode == submodes::DUAL){ + for(const auto& info:childrenMap) { + if(info.second.mode != wantedMode or info.second.mode != wantedSubmode){ + return NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE; + } + } + return RETURN_OK; + } + else if(submode == submodes::SINGLE) { + for(const auto& info:childrenMap) { + if(info.second.mode == wantedMode and info.second.mode != wantedSubmode){ + return RETURN_OK; + } + } + } + return INVALID_SUBMODE; +} + +bool TestAssembly::isDeviceAvailable(object_id_t object) { + if(healthHelper.healthTable->getHealth(object) == HasHealthIF::HEALTHY){ + return true; + } + else{ + return false; + } +} diff --git a/tests/src/fsfw_tests/integration/assemblies/TestAssembly.h b/tests/src/fsfw_tests/integration/assemblies/TestAssembly.h new file mode 100644 index 00000000..3cc6f450 --- /dev/null +++ b/tests/src/fsfw_tests/integration/assemblies/TestAssembly.h @@ -0,0 +1,56 @@ +#ifndef MISSION_ASSEMBLIES_TESTASSEMBLY_H_ +#define MISSION_ASSEMBLIES_TESTASSEMBLY_H_ + +#include +#include "../devices/TestDeviceHandler.h" + +class TestAssembly: public AssemblyBase { +public: + TestAssembly(object_id_t objectId, object_id_t parentId, object_id_t testDevice0, + object_id_t testDevice1); + virtual ~TestAssembly(); + ReturnValue_t initialize() override; + + enum submodes: Submode_t{ + SINGLE = 0, + DUAL = 1 + }; + +protected: + /** + * Command children to reach [mode,submode] combination + * Can be done by setting #commandsOutstanding correctly, + * or using executeTable() + * @param mode + * @param submode + * @return + * - @c RETURN_OK if ok + * - @c NEED_SECOND_STEP if children need to be commanded again + */ + ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override; + /** + * Check whether desired assembly mode was achieved by checking the modes + * or/and health states of child device handlers. + * The assembly template class will also call this function if a health + * or mode change of a child device handler was detected. + * @param wantedMode + * @param wantedSubmode + * @return + */ + ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) + override; + + ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, + Submode_t wantedSubmode) override; +private: + FixedArrayList commandTable; + object_id_t deviceHandler0Id = 0; + object_id_t deviceHandler1Id = 0; + TestDevice* handler0 = nullptr; + TestDevice* handler1 = nullptr; + + + bool isDeviceAvailable(object_id_t object); +}; + +#endif /* MISSION_ASSEMBLIES_TESTASSEMBLY_H_ */ diff --git a/tests/src/fsfw_tests/integration/controller/CMakeLists.txt b/tests/src/fsfw_tests/integration/controller/CMakeLists.txt new file mode 100644 index 00000000..f5655b71 --- /dev/null +++ b/tests/src/fsfw_tests/integration/controller/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE + TestController.cpp +) \ No newline at end of file diff --git a/tests/src/fsfw_tests/integration/controller/TestController.cpp b/tests/src/fsfw_tests/integration/controller/TestController.cpp new file mode 100644 index 00000000..385d07bd --- /dev/null +++ b/tests/src/fsfw_tests/integration/controller/TestController.cpp @@ -0,0 +1,214 @@ +#include "TestController.h" +#include "OBSWConfig.h" + +#include +#include +#include + +TestController::TestController(object_id_t objectId, size_t commandQueueDepth): + ExtendedControllerBase(objectId, objects::NO_OBJECT, commandQueueDepth), + deviceDataset0(objects::TEST_DEVICE_HANDLER_0), + deviceDataset1(objects::TEST_DEVICE_HANDLER_1) { +} + +TestController::~TestController() { +} + +ReturnValue_t TestController::handleCommandMessage(CommandMessage *message) { + return HasReturnvaluesIF::RETURN_OK; +} + +void TestController::performControlOperation() { + /* We will trace vaiables if we received an update notification or snapshots */ +#if OBSW_CONTROLLER_PRINTOUT == 1 + if(not traceVariable) { + return; + } + + switch(currentTraceType) { + case(NONE): { + break; + } + case(TRACE_DEV_0_UINT8): { + if(traceCounter == 0) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "Tracing finished" << std::endl; +#else + sif::printInfo("Tracing finished\n"); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + traceVariable = false; + traceCounter = traceCycles; + currentTraceType = TraceTypes::NONE; + break; + } + + PoolReadGuard readHelper(&deviceDataset0.testUint8Var); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "Tracing device 0 variable 0 (UINT8), current value: " << + static_cast(deviceDataset0.testUint8Var.value) << std::endl; +#else + sif::printInfo("Tracing device 0 variable 0 (UINT8), current value: %d\n", + deviceDataset0.testUint8Var.value); +#endif + traceCounter--; + break; + } + case(TRACE_DEV_0_VECTOR): { + break; + } + + } +#endif /* OBSW_CONTROLLER_PRINTOUT == 1 */ +} + +void TestController::handleChangedDataset(sid_t sid, store_address_t storeId, bool* clearMessage) { + using namespace std; + +#if OBSW_CONTROLLER_PRINTOUT == 1 + char const* printout = nullptr; + if(storeId == storeId::INVALID_STORE_ADDRESS) { + printout = "Notification"; + } + else { + printout = "Snapshot"; + } +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestController::handleChangedDataset: " << printout << " update from object " + "ID " << setw(8) << setfill('0') << hex << sid.objectId << + " and set ID " << sid.ownerSetId << dec << setfill(' ') << endl; +#else + sif::printInfo("TestController::handleChangedPoolVariable: %s update from object ID 0x%08x and " + "set ID %lu\n", printout, sid.objectId, sid.ownerSetId); +#endif + + if (storeId == storeId::INVALID_STORE_ADDRESS) { + if(sid.objectId == objects::TEST_DEVICE_HANDLER_0) { + PoolReadGuard readHelper(&deviceDataset0.testFloat3Vec); + float floatVec[3]; + floatVec[0] = deviceDataset0.testFloat3Vec.value[0]; + floatVec[1] = deviceDataset0.testFloat3Vec.value[1]; + floatVec[2] = deviceDataset0.testFloat3Vec.value[2]; +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "Current float vector (3) values: [" << floatVec[0] << ", " << + floatVec[1] << ", " << floatVec[2] << "]" << std::endl; +#else + sif::printInfo("Current float vector (3) values: [%f, %f, %f]\n", + floatVec[0], floatVec[1], floatVec[2]); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } + } +#endif /* OBSW_CONTROLLER_PRINTOUT == 1 */ + + /* We will trace the variables for snapshots and update notifications */ + if(not traceVariable) { + traceVariable = true; + traceCounter = traceCycles; + currentTraceType = TraceTypes::TRACE_DEV_0_VECTOR; + } +} + +void TestController::handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId, + bool* clearMessage) { + using namespace std; + +#if OBSW_CONTROLLER_PRINTOUT == 1 + char const* printout = nullptr; + if (storeId == storeId::INVALID_STORE_ADDRESS) { + printout = "Notification"; + } + else { + printout = "Snapshot"; + } + +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestController::handleChangedPoolVariable: " << printout << " update from object " + "ID 0x" << setw(8) << setfill('0') << hex << globPoolId.objectId << + " and local pool ID " << globPoolId.localPoolId << dec << setfill(' ') << endl; +#else + sif::printInfo("TestController::handleChangedPoolVariable: %s update from object ID 0x%08x and " + "local pool ID %lu\n", printout, globPoolId.objectId, globPoolId.localPoolId); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + + if (storeId == storeId::INVALID_STORE_ADDRESS) { + if(globPoolId.objectId == objects::TEST_DEVICE_HANDLER_0) { + PoolReadGuard readHelper(&deviceDataset0.testUint8Var); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "Current test variable 0 (UINT8) value: " << static_cast( + deviceDataset0.testUint8Var.value) << std::endl; +#else + sif::printInfo("Current test variable 0 (UINT8) value %d\n", + deviceDataset0.testUint8Var.value); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } + } +#endif /* OBSW_CONTROLLER_PRINTOUT == 1 */ + + /* We will trace the variables for snapshots and update notifications */ + if(not traceVariable) { + traceVariable = true; + traceCounter = traceCycles; + currentTraceType = TraceTypes::TRACE_DEV_0_UINT8; + } +} + +LocalPoolDataSetBase* TestController::getDataSetHandle(sid_t sid) { + return nullptr; +} + +ReturnValue_t TestController::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, + LocalDataPoolManager &poolManager) { + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t TestController::initializeAfterTaskCreation() { + namespace td = testdevice; + HasLocalDataPoolIF* device0 = ObjectManager::instance()->get( + objects::TEST_DEVICE_HANDLER_0); + if(device0 == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "TestController::initializeAfterTaskCreation: Test device handler 0 " + "handle invalid!" << std::endl; +#else + sif::printWarning("TestController::initializeAfterTaskCreation: Test device handler 0 " + "handle invalid!"); +#endif + return ObjectManagerIF::CHILD_INIT_FAILED; + } + ProvidesDataPoolSubscriptionIF* subscriptionIF = device0->getSubscriptionInterface(); + if(subscriptionIF != nullptr) { + /* For DEVICE_0, we only subscribe for notifications */ + subscriptionIF->subscribeForSetUpdateMessage(td::TEST_SET_ID, getObjectId(), + getCommandQueue(), false); + subscriptionIF->subscribeForVariableUpdateMessage(td::PoolIds::TEST_UINT8_ID, + getObjectId(), getCommandQueue(), false); + } + + + HasLocalDataPoolIF* device1 = ObjectManager::instance()->get( + objects::TEST_DEVICE_HANDLER_1); + if(device1 == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "TestController::initializeAfterTaskCreation: Test device handler 1 " + "handle invalid!" << std::endl; +#else + sif::printWarning("TestController::initializeAfterTaskCreation: Test device handler 1 " + "handle invalid!"); +#endif + } + + subscriptionIF = device1->getSubscriptionInterface(); + if(subscriptionIF != nullptr) { + /* For DEVICE_1, we will subscribe for snapshots */ + subscriptionIF->subscribeForSetUpdateMessage(td::TEST_SET_ID, getObjectId(), + getCommandQueue(), true); + subscriptionIF->subscribeForVariableUpdateMessage(td::PoolIds::TEST_UINT8_ID, + getObjectId(), getCommandQueue(), true); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t TestController::checkModeCommand(Mode_t mode, Submode_t submode, + uint32_t *msToReachTheMode) { + return HasReturnvaluesIF::RETURN_OK; +} + diff --git a/tests/src/fsfw_tests/integration/controller/TestController.h b/tests/src/fsfw_tests/integration/controller/TestController.h new file mode 100644 index 00000000..8092f945 --- /dev/null +++ b/tests/src/fsfw_tests/integration/controller/TestController.h @@ -0,0 +1,50 @@ +#ifndef MISSION_CONTROLLER_TESTCONTROLLER_H_ +#define MISSION_CONTROLLER_TESTCONTROLLER_H_ + +#include "../devices/devicedefinitions/testDeviceDefinitions.h" +#include + + +class TestController: + public ExtendedControllerBase { +public: + TestController(object_id_t objectId, size_t commandQueueDepth = 10); + virtual~ TestController(); +protected: + testdevice::TestDataSet deviceDataset0; + testdevice::TestDataSet deviceDataset1; + + /* Extended Controller Base overrides */ + ReturnValue_t handleCommandMessage(CommandMessage *message) override; + void performControlOperation() override; + + /* HasLocalDatapoolIF callbacks */ + void handleChangedDataset(sid_t sid, store_address_t storeId, bool* clearMessage) override; + void handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId, + bool* clearMessage) override; + + LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; + ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; + + ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, + uint32_t *msToReachTheMode) override; + + ReturnValue_t initializeAfterTaskCreation() override; + +private: + + bool traceVariable = false; + uint8_t traceCycles = 5; + uint8_t traceCounter = traceCycles; + + enum TraceTypes { + NONE, + TRACE_DEV_0_UINT8, + TRACE_DEV_0_VECTOR + }; + TraceTypes currentTraceType = TraceTypes::NONE; +}; + + +#endif /* MISSION_CONTROLLER_TESTCONTROLLER_H_ */ diff --git a/tests/src/fsfw_tests/integration/controller/ctrldefinitions/testCtrlDefinitions.h b/tests/src/fsfw_tests/integration/controller/ctrldefinitions/testCtrlDefinitions.h new file mode 100644 index 00000000..7bf045df --- /dev/null +++ b/tests/src/fsfw_tests/integration/controller/ctrldefinitions/testCtrlDefinitions.h @@ -0,0 +1,18 @@ +#ifndef MISSION_CONTROLLER_CTRLDEFINITIONS_TESTCTRLDEFINITIONS_H_ +#define MISSION_CONTROLLER_CTRLDEFINITIONS_TESTCTRLDEFINITIONS_H_ + +#include +#include + +namespace testcontroller { + +enum sourceObjectIds: object_id_t { + DEVICE_0_ID = objects::TEST_DEVICE_HANDLER_0, + DEVICE_1_ID = objects::TEST_DEVICE_HANDLER_1, +}; + +} + + + +#endif /* MISSION_CONTROLLER_CTRLDEFINITIONS_TESTCTRLDEFINITIONS_H_ */ diff --git a/tests/src/fsfw_tests/integration/devices/CMakeLists.txt b/tests/src/fsfw_tests/integration/devices/CMakeLists.txt new file mode 100644 index 00000000..cfd238d2 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE + TestCookie.cpp + TestDeviceHandler.cpp + TestEchoComIF.cpp +) diff --git a/tests/src/fsfw_tests/integration/devices/TestCookie.cpp b/tests/src/fsfw_tests/integration/devices/TestCookie.cpp new file mode 100644 index 00000000..91098f80 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestCookie.cpp @@ -0,0 +1,14 @@ +#include "TestCookie.h" + +TestCookie::TestCookie(address_t address, size_t replyMaxLen): + address(address), replyMaxLen(replyMaxLen) {} + +TestCookie::~TestCookie() {} + +address_t TestCookie::getAddress() const { + return address; +} + +size_t TestCookie::getReplyMaxLen() const { + return replyMaxLen; +} diff --git a/tests/src/fsfw_tests/integration/devices/TestCookie.h b/tests/src/fsfw_tests/integration/devices/TestCookie.h new file mode 100644 index 00000000..5dac3f25 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestCookie.h @@ -0,0 +1,22 @@ +#ifndef MISSION_DEVICES_TESTCOOKIE_H_ +#define MISSION_DEVICES_TESTCOOKIE_H_ + +#include +#include + +/** + * @brief Really simple cookie which does not do a lot. + */ +class TestCookie: public CookieIF { +public: + TestCookie(address_t address, size_t maxReplyLen); + virtual ~TestCookie(); + + address_t getAddress() const; + size_t getReplyMaxLen() const; +private: + address_t address = 0; + size_t replyMaxLen = 0; +}; + +#endif /* MISSION_DEVICES_TESTCOOKIE_H_ */ diff --git a/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp new file mode 100644 index 00000000..91019487 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp @@ -0,0 +1,804 @@ +#include "TestDeviceHandler.h" +#include "FSFWConfig.h" + +#include "fsfw/datapool/PoolReadGuard.h" + +#include + +TestDevice::TestDevice(object_id_t objectId, object_id_t comIF, + CookieIF * cookie, testdevice::DeviceIndex deviceIdx, bool fullInfoPrintout, + bool changingDataset): + DeviceHandlerBase(objectId, comIF, cookie), deviceIdx(deviceIdx), + dataset(this), fullInfoPrintout(fullInfoPrintout) { +} + +TestDevice::~TestDevice() {} + +void TestDevice::performOperationHook() { + if(periodicPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::performOperationHook: Alive!" << std::endl; +#else + sif::printInfo("TestDevice%d::performOperationHook: Alive!", deviceIdx); +#endif + } + + if(oneShot) { + oneShot = false; + } +} + + +void TestDevice::doStartUp() { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::doStartUp: Switching On" << std::endl; +#else + sif::printInfo("TestDevice%d::doStartUp: Switching On\n", static_cast(deviceIdx)); +#endif + } + + setMode(_MODE_TO_ON); + return; +} + + +void TestDevice::doShutDown() { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::doShutDown: Switching Off" << std::endl; +#else + sif::printInfo("TestDevice%d::doShutDown: Switching Off\n", static_cast(deviceIdx)); +#endif + } + + setMode(_MODE_SHUT_DOWN); + return; +} + + +ReturnValue_t TestDevice::buildNormalDeviceCommand(DeviceCommandId_t* id) { + using namespace testdevice; + *id = TEST_NORMAL_MODE_CMD; + if(DeviceHandlerBase::isAwaitingReply()) { + return NOTHING_TO_SEND; + } + return buildCommandFromCommand(*id, nullptr, 0); +} + +ReturnValue_t TestDevice::buildTransitionDeviceCommand(DeviceCommandId_t* id) { + if(mode == _MODE_TO_ON) { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::buildTransitionDeviceCommand: Was called" + " from _MODE_TO_ON mode" << std::endl; +#else + sif::printInfo("TestDevice%d::buildTransitionDeviceCommand: " + "Was called from _MODE_TO_ON mode\n", deviceIdx); +#endif + } + + } + if(mode == _MODE_TO_NORMAL) { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::buildTransitionDeviceCommand: Was called " + "from _MODE_TO_NORMAL mode" << std::endl; +#else + sif::printInfo("TestDevice%d::buildTransitionDeviceCommand: Was called from " + " _MODE_TO_NORMAL mode\n", deviceIdx); +#endif + } + + setMode(MODE_NORMAL); + } + if(mode == _MODE_SHUT_DOWN) { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::buildTransitionDeviceCommand: Was called " + "from _MODE_SHUT_DOWN mode" << std::endl; +#else + sif::printInfo("TestDevice%d::buildTransitionDeviceCommand: Was called from " + "_MODE_SHUT_DOWN mode\n", deviceIdx); +#endif + } + + setMode(MODE_OFF); + } + return NOTHING_TO_SEND; +} + +void TestDevice::doTransition(Mode_t modeFrom, Submode_t submodeFrom) { + if(mode == _MODE_TO_NORMAL) { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::doTransition: Custom transition to " + "normal mode" << std::endl; +#else + sif::printInfo("TestDevice%d::doTransition: Custom transition to normal mode\n", + deviceIdx); +#endif + } + + } + else { + DeviceHandlerBase::doTransition(modeFrom, submodeFrom); + } +} + +ReturnValue_t TestDevice::buildCommandFromCommand( + DeviceCommandId_t deviceCommand, const uint8_t* commandData, + size_t commandDataLen) { + using namespace testdevice; + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + switch(deviceCommand) { + case(TEST_NORMAL_MODE_CMD): { + commandSent = true; + result = buildNormalModeCommand(deviceCommand, commandData, commandDataLen); + break; + } + + case(TEST_COMMAND_0): { + commandSent = true; + result = buildTestCommand0(deviceCommand, commandData, commandDataLen); + break; + } + + case(TEST_COMMAND_1): { + commandSent = true; + result = buildTestCommand1(deviceCommand, commandData, commandDataLen); + break; + } + case(TEST_NOTIF_SNAPSHOT_VAR): { + if(changingDatasets) { + changingDatasets = false; + } + + PoolReadGuard readHelper(&dataset.testUint8Var); + if(deviceIdx == testdevice::DeviceIndex::DEVICE_0) { + /* This will trigger a variable notification to the demo controller */ + dataset.testUint8Var = 220; + dataset.testUint8Var.setValid(true); + } + else if(deviceIdx == testdevice::DeviceIndex::DEVICE_1) { + /* This will trigger a variable snapshot to the demo controller */ + dataset.testUint8Var = 30; + dataset.testUint8Var.setValid(true); + } + + break; + } + case(TEST_NOTIF_SNAPSHOT_SET): { + if(changingDatasets) { + changingDatasets = false; + } + + PoolReadGuard readHelper(&dataset.testFloat3Vec); + + if(deviceIdx == testdevice::DeviceIndex::DEVICE_0) { + /* This will trigger a variable notification to the demo controller */ + dataset.testFloat3Vec.value[0] = 60; + dataset.testFloat3Vec.value[1] = 70; + dataset.testFloat3Vec.value[2] = 55; + dataset.testFloat3Vec.setValid(true); + } + else if(deviceIdx == testdevice::DeviceIndex::DEVICE_1) { + /* This will trigger a variable notification to the demo controller */ + dataset.testFloat3Vec.value[0] = -60; + dataset.testFloat3Vec.value[1] = -70; + dataset.testFloat3Vec.value[2] = -55; + dataset.testFloat3Vec.setValid(true); + } + break; + } + default: + result = DeviceHandlerIF::COMMAND_NOT_SUPPORTED; + } + return result; +} + + +ReturnValue_t TestDevice::buildNormalModeCommand(DeviceCommandId_t deviceCommand, + const uint8_t* commandData, size_t commandDataLen) { + if(fullInfoPrintout) { +#if OBSW_VERBOSE_LEVEL >= 3 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice::buildTestCommand1: Building normal command" << std::endl; +#else + sif::printInfo("TestDevice::buildTestCommand1: Building command from TEST_COMMAND_1\n"); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* OBSW_VERBOSE_LEVEL >= 3 */ + } + + if(commandDataLen > MAX_BUFFER_SIZE - sizeof(DeviceCommandId_t)) { + return DeviceHandlerIF::INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS; + } + /* The command is passed on in the command buffer as it is */ + passOnCommand(deviceCommand, commandData, commandDataLen); + return RETURN_OK; +} + +ReturnValue_t TestDevice::buildTestCommand0(DeviceCommandId_t deviceCommand, + const uint8_t* commandData, size_t commandDataLen) { + using namespace testdevice; + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::buildTestCommand0: Executing simple command " + " with completion reply" << std::endl; +#else + sif::printInfo("TestDevice%d::buildTestCommand0: Executing simple command with " + "completion reply\n", deviceIdx); +#endif + } + + if(commandDataLen > MAX_BUFFER_SIZE - sizeof(DeviceCommandId_t)) { + return DeviceHandlerIF::INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS; + } + + /* The command is passed on in the command buffer as it is */ + passOnCommand(deviceCommand, commandData, commandDataLen); + return RETURN_OK; +} + +ReturnValue_t TestDevice::buildTestCommand1(DeviceCommandId_t deviceCommand, + const uint8_t* commandData, + size_t commandDataLen) { + using namespace testdevice; + if(commandDataLen < 7) { + return DeviceHandlerIF::INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS; + } + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::buildTestCommand1: Executing command with " + "data reply" << std::endl; +#else + sif::printInfo("TestDevice%d:buildTestCommand1: Executing command with data reply\n", + deviceIdx); +#endif + } + + deviceCommand = EndianConverter::convertBigEndian(deviceCommand); + memcpy(commandBuffer, &deviceCommand, sizeof(deviceCommand)); + + /* Assign and check parameters */ + uint16_t parameter1 = 0; + size_t size = commandDataLen; + ReturnValue_t result = SerializeAdapter::deSerialize(¶meter1, + &commandData, &size, SerializeIF::Endianness::BIG); + if(result == HasReturnvaluesIF::RETURN_FAILED) { + return result; + } + + /* Parameter 1 needs to be correct */ + if(parameter1 != testdevice::COMMAND_1_PARAM1) { + return DeviceHandlerIF::INVALID_COMMAND_PARAMETER; + } + uint64_t parameter2 = 0; + result = SerializeAdapter::deSerialize(¶meter2, + &commandData, &size, SerializeIF::Endianness::BIG); + if(parameter2!= testdevice::COMMAND_1_PARAM2){ + return DeviceHandlerIF::INVALID_COMMAND_PARAMETER; + } + + /* Pass on the parameters to the Echo IF */ + commandBuffer[4] = (parameter1 & 0xFF00) >> 8; + commandBuffer[5] = (parameter1 & 0xFF); + parameter2 = EndianConverter::convertBigEndian(parameter2); + memcpy(commandBuffer + 6, ¶meter2, sizeof(parameter2)); + rawPacket = commandBuffer; + rawPacketLen = sizeof(deviceCommand) + sizeof(parameter1) + + sizeof(parameter2); + return RETURN_OK; +} + +void TestDevice::passOnCommand(DeviceCommandId_t command, const uint8_t *commandData, + size_t commandDataLen) { + DeviceCommandId_t deviceCommandBe = EndianConverter::convertBigEndian(command); + memcpy(commandBuffer, &deviceCommandBe, sizeof(deviceCommandBe)); + memcpy(commandBuffer + 4, commandData, commandDataLen); + rawPacket = commandBuffer; + rawPacketLen = sizeof(deviceCommandBe) + commandDataLen; +} + +void TestDevice::fillCommandAndReplyMap() { + namespace td = testdevice; + insertInCommandAndReplyMap(testdevice::TEST_NORMAL_MODE_CMD, 5, &dataset); + insertInCommandAndReplyMap(testdevice::TEST_COMMAND_0, 5); + insertInCommandAndReplyMap(testdevice::TEST_COMMAND_1, 5); + + /* No reply expected for these commands */ + insertInCommandMap(td::TEST_NOTIF_SNAPSHOT_SET); + insertInCommandMap(td::TEST_NOTIF_SNAPSHOT_VAR); +} + + +ReturnValue_t TestDevice::scanForReply(const uint8_t *start, size_t len, + DeviceCommandId_t *foundId, size_t *foundLen) { + using namespace testdevice; + + /* Unless a command was sent explicitely, we don't expect any replies and ignore this + the packet. On a real device, there might be replies which are sent without a previous + command. */ + if(not commandSent) { + return DeviceHandlerBase::IGNORE_FULL_PACKET; + } + else { + commandSent = false; + } + + if(len < sizeof(object_id_t)) { + return DeviceHandlerIF::LENGTH_MISSMATCH; + } + + size_t size = len; + ReturnValue_t result = SerializeAdapter::deSerialize(foundId, &start, &size, + SerializeIF::Endianness::BIG); + if (result != RETURN_OK) { + return result; + } + + DeviceCommandId_t pendingCmd = this->getPendingCommand(); + + switch(pendingCmd) { + + case(TEST_NORMAL_MODE_CMD): { + if(fullInfoPrintout) { +#if OBSW_VERBOSE_LEVEL >= 3 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice::scanForReply: Reply for normal commnand (ID " << + TEST_NORMAL_MODE_CMD << ") received!" << std::endl; +#else + sif::printInfo("TestDevice%d::scanForReply: Reply for normal command (ID %d) " + "received!\n", deviceIdx, TEST_NORMAL_MODE_CMD); +#endif +#endif + } + + *foundLen = len; + *foundId = pendingCmd; + return RETURN_OK; + } + + case(TEST_COMMAND_0): { + if(len < TEST_COMMAND_0_SIZE) { + return DeviceHandlerIF::LENGTH_MISSMATCH; + } + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::scanForReply: Reply for simple command " + "(ID " << TEST_COMMAND_0 << ") received!" << std::endl; +#else + sif::printInfo("TestDevice%d::scanForReply: Reply for simple command (ID %d) " + "received!\n", deviceIdx, TEST_COMMAND_0); +#endif + } + + *foundLen = TEST_COMMAND_0_SIZE; + *foundId = pendingCmd; + return RETURN_OK; + } + + case(TEST_COMMAND_1): { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::scanForReply: Reply for data command " + "(ID " << TEST_COMMAND_1 << ") received!" << std::endl; +#else + sif::printInfo("TestDevice%d::scanForReply: Reply for data command (ID %d) " + "received\n", deviceIdx, TEST_COMMAND_1); +#endif + } + + *foundLen = len; + *foundId = pendingCmd; + return RETURN_OK; + } + + default: + return DeviceHandlerIF::DEVICE_REPLY_INVALID; + } +} + + +ReturnValue_t TestDevice::interpretDeviceReply(DeviceCommandId_t id, + const uint8_t* packet) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + switch(id) { + /* Periodic replies */ + case testdevice::TEST_NORMAL_MODE_CMD: { + result = interpretingNormalModeReply(); + break; + } + /* Simple reply */ + case testdevice::TEST_COMMAND_0: { + result = interpretingTestReply0(id, packet); + break; + } + /* Data reply */ + case testdevice::TEST_COMMAND_1: { + result = interpretingTestReply1(id, packet); + break; + } + default: + return DeviceHandlerIF::DEVICE_REPLY_INVALID; + } + return result; +} + +ReturnValue_t TestDevice::interpretingNormalModeReply() { + CommandMessage directReplyMessage; + if(changingDatasets) { + PoolReadGuard readHelper(&dataset); + if(dataset.testUint8Var.value == 0) { + dataset.testUint8Var.value = 10; + dataset.testUint32Var.value = 777; + dataset.testFloat3Vec.value[0] = 2.5; + dataset.testFloat3Vec.value[1] = -2.5; + dataset.testFloat3Vec.value[2] = 2.5; + dataset.setValidity(true, true); + } + else { + dataset.testUint8Var.value = 0; + dataset.testUint32Var.value = 0; + dataset.testFloat3Vec.value[0] = 0.0; + dataset.testFloat3Vec.value[1] = 0.0; + dataset.testFloat3Vec.value[2] = 0.0; + dataset.setValidity(false, true); + } + return RETURN_OK; + } + + PoolReadGuard readHelper(&dataset); + if(dataset.testUint8Var.value == 0) { + /* Reset state */ + dataset.testUint8Var.value = 128; + } + else if(dataset.testUint8Var.value > 200) { + if(not resetAfterChange) { + /* This will trigger an update notification to the controller */ + dataset.testUint8Var.setChanged(true); + resetAfterChange = true; + /* Decrement by 30 automatically. This will prevent any additional notifications. */ + dataset.testUint8Var.value -= 30; + } + } + /* If the value is greater than 0, it will be decremented in a linear way */ + else if(dataset.testUint8Var.value > 128) { + size_t sizeToDecrement = 0; + if(dataset.testUint8Var.value > 128 + 30) { + sizeToDecrement = 30; + } + else { + sizeToDecrement = dataset.testUint8Var.value - 128; + resetAfterChange = false; + } + dataset.testUint8Var.value -= sizeToDecrement; + } + else if(dataset.testUint8Var.value < 50) { + if(not resetAfterChange) { + /* This will trigger an update snapshot to the controller */ + dataset.testUint8Var.setChanged(true); + resetAfterChange = true; + } + else { + /* Increment by 30 automatically. */ + dataset.testUint8Var.value += 30; + } + } + /* Increment in linear way */ + else if(dataset.testUint8Var.value < 128) { + size_t sizeToIncrement = 0; + if(dataset.testUint8Var.value < 128 - 20) { + sizeToIncrement = 20; + } + else { + sizeToIncrement = 128 - dataset.testUint8Var.value; + resetAfterChange = false; + } + dataset.testUint8Var.value += sizeToIncrement; + } + + /* TODO: Same for vector */ + float vectorMean = (dataset.testFloat3Vec.value[0] + dataset.testFloat3Vec.value[1] + + dataset.testFloat3Vec.value[2]) / 3.0; + + /* Lambda (private local function) */ + auto sizeToAdd = [](bool tooHigh, float currentVal) { + if(tooHigh) { + if(currentVal - 20.0 > 10.0) { + return -10.0; + } + else { + return 20.0 - currentVal; + } + } + else { + if(std::abs(currentVal + 20.0) > 10.0) { + return 10.0; + } + else { + return -20.0 - currentVal; + } + } + }; + + if(vectorMean > 20.0 and std::abs(vectorMean - 20.0) > 1.0) { + if(not resetAfterChange) { + dataset.testFloat3Vec.setChanged(true); + resetAfterChange = true; + } + else { + float sizeToDecrementVal0 = 0; + float sizeToDecrementVal1 = 0; + float sizeToDecrementVal2 = 0; + + sizeToDecrementVal0 = sizeToAdd(true, dataset.testFloat3Vec.value[0]); + sizeToDecrementVal1 = sizeToAdd(true, dataset.testFloat3Vec.value[1]); + sizeToDecrementVal2 = sizeToAdd(true, dataset.testFloat3Vec.value[2]); + + dataset.testFloat3Vec.value[0] += sizeToDecrementVal0; + dataset.testFloat3Vec.value[1] += sizeToDecrementVal1; + dataset.testFloat3Vec.value[2] += sizeToDecrementVal2; + } + } + else if (vectorMean < -20.0 and std::abs(vectorMean + 20.0) < 1.0) { + if(not resetAfterChange) { + dataset.testFloat3Vec.setChanged(true); + resetAfterChange = true; + } + else { + float sizeToDecrementVal0 = 0; + float sizeToDecrementVal1 = 0; + float sizeToDecrementVal2 = 0; + + sizeToDecrementVal0 = sizeToAdd(false, dataset.testFloat3Vec.value[0]); + sizeToDecrementVal1 = sizeToAdd(false, dataset.testFloat3Vec.value[1]); + sizeToDecrementVal2 = sizeToAdd(false, dataset.testFloat3Vec.value[2]); + + dataset.testFloat3Vec.value[0] += sizeToDecrementVal0; + dataset.testFloat3Vec.value[1] += sizeToDecrementVal1; + dataset.testFloat3Vec.value[2] += sizeToDecrementVal2; + } + } + else { + if(resetAfterChange) { + resetAfterChange = false; + } + } + + return RETURN_OK; +} + +ReturnValue_t TestDevice::interpretingTestReply0(DeviceCommandId_t id, const uint8_t* packet) { + CommandMessage commandMessage; + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice::interpretingTestReply0: Generating step and finish reply" << + std::endl; +#else + sif::printInfo("TestDevice::interpretingTestReply0: Generating step and finish reply\n"); +#endif + } + + MessageQueueId_t commander = getCommanderQueueId(id); + /* Generate one step reply and the finish reply */ + actionHelper.step(1, commander, id); + actionHelper.finish(true, commander, id); + + return RETURN_OK; +} + +ReturnValue_t TestDevice::interpretingTestReply1(DeviceCommandId_t id, + const uint8_t* packet) { + CommandMessage directReplyMessage; + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::interpretingReply1: Setting data reply" << + std::endl; +#else + sif::printInfo("TestDevice%d::interpretingReply1: Setting data reply\n", deviceIdx); +#endif + } + + MessageQueueId_t commander = getCommanderQueueId(id); + /* Send reply with data */ + ReturnValue_t result = actionHelper.reportData(commander, id, packet, + testdevice::TEST_COMMAND_1_SIZE, false); + + if (result != RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TestDevice" << deviceIdx << "::interpretingReply1: Sending data " + "reply failed!" << std::endl; +#else + sif::printError("TestDevice%d::interpretingReply1: Sending data reply failed!\n", + deviceIdx); +#endif + return result; + } + + if(result == HasReturnvaluesIF::RETURN_OK) { + /* Finish reply */ + actionHelper.finish(true, commander, id); + } + else { + /* Finish reply */ + actionHelper.finish(false, commander, id, result); + } + + return RETURN_OK; +} + +uint32_t TestDevice::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { + return 5000; +} + +void TestDevice::enableFullDebugOutput(bool enable) { + this->fullInfoPrintout = enable; +} + +ReturnValue_t TestDevice::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, + LocalDataPoolManager &poolManager) { + namespace td = testdevice; + localDataPoolMap.emplace(td::PoolIds::TEST_UINT8_ID, new PoolEntry({0})); + localDataPoolMap.emplace(td::PoolIds::TEST_UINT32_ID, new PoolEntry({0})); + localDataPoolMap.emplace(td::PoolIds::TEST_FLOAT_VEC_3_ID, + new PoolEntry({0.0, 0.0, 0.0})); + + sid_t sid; + if(deviceIdx == td::DeviceIndex::DEVICE_0) { + sid = td::TEST_SET_DEV_0_SID; + } + else { + sid = td::TEST_SET_DEV_1_SID; + } + /* Subscribe for periodic HK packets but do not enable reporting for now. + Non-diangostic with a period of one second */ + poolManager.subscribeForPeriodicPacket(sid, false, 1.0, false); + return HasReturnvaluesIF::RETURN_OK; +} + + +ReturnValue_t TestDevice::getParameter(uint8_t domainId, uint8_t uniqueId, + ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues, + uint16_t startAtIndex) { + using namespace testdevice; + switch (uniqueId) { + case ParameterUniqueIds::TEST_UINT32_0: { + if(fullInfoPrintout) { + uint32_t newValue = 0; + ReturnValue_t result = newValues->getElement(&newValue, 0, 0); + if(result == HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::getParameter: Setting parameter 0 to " + "new value " << newValue << std::endl; +#else + sif::printInfo("TestDevice%d::getParameter: Setting parameter 0 to new value %lu\n", + deviceIdx, static_cast(newValue)); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } + } + parameterWrapper->set(testParameter0); + break; + } + case ParameterUniqueIds::TEST_INT32_1: { + if(fullInfoPrintout) { + int32_t newValue = 0; + ReturnValue_t result = newValues->getElement(&newValue, 0, 0); + if(result == HasReturnvaluesIF::RETURN_OK) { +#if OBSW_DEVICE_HANDLER_PRINTOUT == 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::getParameter: Setting parameter 1 to " + "new value " << newValue << std::endl; +#else + sif::printInfo("TestDevice%d::getParameter: Setting parameter 1 to new value %lu\n", + deviceIdx, static_cast(newValue)); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* OBSW_DEVICE_HANDLER_PRINTOUT == 1 */ + } + } + parameterWrapper->set(testParameter1); + break; + } + case ParameterUniqueIds::TEST_FLOAT_VEC3_2: { + if(fullInfoPrintout) { + float newVector[3]; + if(newValues->getElement(newVector, 0, 0) != RETURN_OK or + newValues->getElement(newVector + 1, 0, 1) != RETURN_OK or + newValues->getElement(newVector + 2, 0, 2) != RETURN_OK) { + return HasReturnvaluesIF::RETURN_FAILED; + } +#if OBSW_DEVICE_HANDLER_PRINTOUT == 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::getParameter: Setting parameter 3 to " + "(float vector with 3 entries) to new values [" << newVector[0] << ", " << + newVector[1] << ", " << newVector[2] << "]" << std::endl; +#else + sif::printInfo("TestDevice%d::getParameter: Setting parameter 3 to new values " + "[%f, %f, %f]\n", deviceIdx, newVector[0], newVector[1], newVector[2]); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* OBSW_DEVICE_HANDLER_PRINTOUT == 1 */ + } + parameterWrapper->setVector(vectorFloatParams2); + break; + } + case(ParameterUniqueIds::PERIODIC_PRINT_ENABLED): { + if(fullInfoPrintout) { + uint8_t enabled = 0; + ReturnValue_t result = newValues->getElement(&enabled, 0, 0); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + char const* printout = nullptr; + if (enabled) { + printout = "enabled"; + } + else { + printout = "disabled"; + } +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::getParameter: Periodic printout " << + printout << std::endl; +#else + sif::printInfo("TestDevice%d::getParameter: Periodic printout %s", printout); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } + + parameterWrapper->set(periodicPrintout); + break; + } + case(ParameterUniqueIds::CHANGING_DATASETS): { + + uint8_t enabled = 0; + ReturnValue_t result = newValues->getElement(&enabled, 0, 0); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + if(not enabled) { + PoolReadGuard readHelper(&dataset); + dataset.testUint8Var.value = 0; + dataset.testUint32Var.value = 0; + dataset.testFloat3Vec.value[0] = 0.0; + dataset.testFloat3Vec.value[0] = 0.0; + dataset.testFloat3Vec.value[1] = 0.0; + } + + if(fullInfoPrintout) { + char const* printout = nullptr; + if (enabled) { + printout = "enabled"; + } + else { + printout = "disabled"; + } +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::getParameter: Changing datasets " << + printout << std::endl; +#else + sif::printInfo("TestDevice%d::getParameter: Changing datasets %s", printout); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } + + parameterWrapper->set(changingDatasets); + break; + } + default: + return INVALID_IDENTIFIER_ID; + } + return HasReturnvaluesIF::RETURN_OK; +} + +LocalPoolObjectBase* TestDevice::getPoolObjectHandle(lp_id_t localPoolId) { + namespace td = testdevice; + if (localPoolId == td::PoolIds::TEST_UINT8_ID) { + return &dataset.testUint8Var; + } + else if (localPoolId == td::PoolIds::TEST_UINT32_ID) { + return &dataset.testUint32Var; + } + else if(localPoolId == td::PoolIds::TEST_FLOAT_VEC_3_ID) { + return &dataset.testFloat3Vec; + } + else { + return nullptr; + } +} diff --git a/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.h b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.h new file mode 100644 index 00000000..0eb47731 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.h @@ -0,0 +1,142 @@ +#ifndef TEST_TESTDEVICES_TESTDEVICEHANDLER_H_ +#define TEST_TESTDEVICES_TESTDEVICEHANDLER_H_ + +#include "devicedefinitions/testDeviceDefinitions.h" + +#include "fsfw/devicehandlers/DeviceHandlerBase.h" +#include "fsfw/globalfunctions/PeriodicOperationDivider.h" +#include "fsfw/timemanager/Countdown.h" + +/** + * @brief Basic dummy device handler to test device commanding without a physical device. + * @details + * This test device handler provided a basic demo for the device handler object. + * It can also be commanded with the following PUS services, using + * the specified object ID of the test device handler. + * + * 1. PUS Service 8 - Functional commanding + * 2. PUS Service 2 - Device access, raw commanding + * 3. PUS Service 20 - Parameter Management + * 4. PUS Service 3 - Housekeeping + + * @author R. Mueller + * @ingroup devices + */ +class TestDevice: public DeviceHandlerBase { +public: + /** + * Build the test device in the factory. + * @param objectId This ID will be assigned to the test device handler. + * @param comIF The ID of the Communication IF used by test device handler. + * @param cookie Cookie object used by the test device handler. This is + * also used and passed to the comIF object. + * @param onImmediately This will start a transition to MODE_ON immediately + * so the device handler jumps into #doStartUp. Should only be used + * in development to reduce need of commanding while debugging. + * @param changingDataset + * Will be used later to change the local datasets containeds in the device. + */ + TestDevice(object_id_t objectId, object_id_t comIF, CookieIF * cookie, + testdevice::DeviceIndex deviceIdx = testdevice::DeviceIndex::DEVICE_0, + bool fullInfoPrintout = false, bool changingDataset = true); + + /** + * This can be used to enable and disable a lot of demo print output. + * @param enable + */ + void enableFullDebugOutput(bool enable); + + virtual ~ TestDevice(); + + //! Size of internal buffer used for communication. + static constexpr uint8_t MAX_BUFFER_SIZE = 255; + + //! Unique index if the device handler is created multiple times. + testdevice::DeviceIndex deviceIdx = testdevice::DeviceIndex::DEVICE_0; + +protected: + testdevice::TestDataSet dataset; + //! This is used to reset the dataset after a commanded change has been made. + bool resetAfterChange = false; + bool commandSent = false; + + /** DeviceHandlerBase overrides (see DHB documentation) */ + + /** + * Hook into the DHB #performOperation call which is executed + * periodically. + */ + void performOperationHook() override; + + virtual void doStartUp() override; + virtual void doShutDown() override; + + virtual ReturnValue_t buildNormalDeviceCommand( + DeviceCommandId_t * id) override; + virtual ReturnValue_t buildTransitionDeviceCommand( + DeviceCommandId_t * id) override; + virtual ReturnValue_t buildCommandFromCommand(DeviceCommandId_t + deviceCommand, const uint8_t * commandData, + size_t commandDataLen) override; + + virtual void fillCommandAndReplyMap() override; + + virtual ReturnValue_t scanForReply(const uint8_t *start, size_t len, + DeviceCommandId_t *foundId, size_t *foundLen) override; + virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, + const uint8_t *packet) override; + virtual uint32_t getTransitionDelayMs(Mode_t modeFrom, + Mode_t modeTo) override; + + virtual void doTransition(Mode_t modeFrom, Submode_t subModeFrom) override; + + virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; + virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) override; + + /* HasParametersIF overrides */ + virtual ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueId, + ParameterWrapper *parameterWrapper, + const ParameterWrapper *newValues, uint16_t startAtIndex) override; + + uint8_t commandBuffer[MAX_BUFFER_SIZE]; + + bool fullInfoPrintout = false; + bool oneShot = true; + + /* Variables for parameter service */ + uint32_t testParameter0 = 0; + int32_t testParameter1 = -2; + float vectorFloatParams2[3] = {}; + + /* Change device handler functionality, changeable via parameter service */ + uint8_t periodicPrintout = false; + uint8_t changingDatasets = false; + + ReturnValue_t buildNormalModeCommand(DeviceCommandId_t deviceCommand, + const uint8_t* commandData, size_t commandDataLen); + ReturnValue_t buildTestCommand0(DeviceCommandId_t deviceCommand, const uint8_t* commandData, + size_t commandDataLen); + ReturnValue_t buildTestCommand1(DeviceCommandId_t deviceCommand, const uint8_t* commandData, + size_t commandDataLen); + void passOnCommand(DeviceCommandId_t command, const uint8_t* commandData, + size_t commandDataLen); + + ReturnValue_t interpretingNormalModeReply(); + ReturnValue_t interpretingTestReply0(DeviceCommandId_t id, + const uint8_t* packet); + ReturnValue_t interpretingTestReply1(DeviceCommandId_t id, + const uint8_t* packet); + ReturnValue_t interpretingTestReply2(DeviceCommandId_t id, const uint8_t* packet); + + /* Some timer utilities */ + uint8_t divider1 = 2; + PeriodicOperationDivider opDivider1 = PeriodicOperationDivider(divider1); + uint8_t divider2 = 10; + PeriodicOperationDivider opDivider2 = PeriodicOperationDivider(divider2); + static constexpr uint32_t initTimeout = 2000; + Countdown countdown1 = Countdown(initTimeout); +}; + + +#endif /* TEST_TESTDEVICES_TESTDEVICEHANDLER_H_ */ diff --git a/tests/src/fsfw_tests/integration/devices/TestEchoComIF.cpp b/tests/src/fsfw_tests/integration/devices/TestEchoComIF.cpp new file mode 100644 index 00000000..afb23a06 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestEchoComIF.cpp @@ -0,0 +1,86 @@ +#include "TestEchoComIF.h" +#include "TestCookie.h" + +#include +#include +#include +#include + + +TestEchoComIF::TestEchoComIF(object_id_t objectId): + SystemObject(objectId) { +} + +TestEchoComIF::~TestEchoComIF() {} + +ReturnValue_t TestEchoComIF::initializeInterface(CookieIF * cookie) { + TestCookie* dummyCookie = dynamic_cast(cookie); + if(dummyCookie == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "TestEchoComIF::initializeInterface: Invalid cookie!" << std::endl; +#else + sif::printWarning("TestEchoComIF::initializeInterface: Invalid cookie!\n"); +#endif + return NULLPOINTER; + } + + auto resultPair = replyMap.emplace( + dummyCookie->getAddress(), ReplyBuffer(dummyCookie->getReplyMaxLen())); + if(not resultPair.second) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return RETURN_OK; +} + +ReturnValue_t TestEchoComIF::sendMessage(CookieIF *cookie, + const uint8_t * sendData, size_t sendLen) { + TestCookie* dummyCookie = dynamic_cast(cookie); + if(dummyCookie == nullptr) { + return NULLPOINTER; + } + + ReplyBuffer& replyBuffer = replyMap.find(dummyCookie->getAddress())->second; + if(sendLen > replyBuffer.capacity()) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "TestEchoComIF::sendMessage: Send length " << sendLen << " larger than " + "current reply buffer length!" << std::endl; +#else + sif::printWarning("TestEchoComIF::sendMessage: Send length %d larger than current " + "reply buffer length!\n", sendLen); +#endif + return HasReturnvaluesIF::RETURN_FAILED; + } + replyBuffer.resize(sendLen); + memcpy(replyBuffer.data(), sendData, sendLen); + return RETURN_OK; +} + +ReturnValue_t TestEchoComIF::getSendSuccess(CookieIF *cookie) { + return RETURN_OK; +} + +ReturnValue_t TestEchoComIF::requestReceiveMessage(CookieIF *cookie, + size_t requestLen) { + return RETURN_OK; +} + +ReturnValue_t TestEchoComIF::readReceivedMessage(CookieIF *cookie, + uint8_t **buffer, size_t *size) { + TestCookie* dummyCookie = dynamic_cast(cookie); + if(dummyCookie == nullptr) { + return NULLPOINTER; + } + + ReplyBuffer& replyBuffer = replyMap.find(dummyCookie->getAddress())->second; + *buffer = replyBuffer.data(); + *size = replyBuffer.size(); + + dummyReplyCounter ++; + if(dummyReplyCounter == 10) { + // add anything that needs to be read periodically by dummy handler + dummyReplyCounter = 0; + } + return RETURN_OK; + +} + diff --git a/tests/src/fsfw_tests/integration/devices/TestEchoComIF.h b/tests/src/fsfw_tests/integration/devices/TestEchoComIF.h new file mode 100644 index 00000000..38270cfe --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestEchoComIF.h @@ -0,0 +1,56 @@ +#ifndef TEST_TESTDEVICES_TESTECHOCOMIF_H_ +#define TEST_TESTDEVICES_TESTECHOCOMIF_H_ + +#include +#include +#include +#include + +#include + +/** + * @brief Used to simply returned sent data from device handler + * @details Assign this com IF in the factory when creating the device handler + * @ingroup test + */ +class TestEchoComIF: public DeviceCommunicationIF, public SystemObject { +public: + TestEchoComIF(object_id_t objectId); + virtual ~TestEchoComIF(); + + /** + * DeviceCommunicationIF overrides + * (see DeviceCommunicationIF documentation + */ + ReturnValue_t initializeInterface(CookieIF * cookie) override; + ReturnValue_t sendMessage(CookieIF *cookie, const uint8_t * sendData, + size_t sendLen) override; + ReturnValue_t getSendSuccess(CookieIF *cookie) override; + ReturnValue_t requestReceiveMessage(CookieIF *cookie, + size_t requestLen) override; + ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, + size_t *size) override; + +private: + + /** + * Send TM packet which contains received data as TM[17,130]. + * Wiretapping will do the same. + * @param data + * @param len + */ + void sendTmPacket(const uint8_t *data,uint32_t len); + + AcceptsTelemetryIF* funnel = nullptr; + MessageQueueIF* tmQueue = nullptr; + size_t replyMaxLen = 0; + + using ReplyBuffer = std::vector; + std::map replyMap; + + uint8_t dummyReplyCounter = 0; + + uint16_t packetSubCounter = 0; +}; + +#endif /* TEST_TESTDEVICES_TESTECHOCOMIF_H_ */ diff --git a/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h b/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h new file mode 100644 index 00000000..10668b94 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h @@ -0,0 +1,100 @@ +#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_TESTDEVICEDEFINITIONS_H_ +#define MISSION_DEVICES_DEVICEDEFINITIONS_TESTDEVICEDEFINITIONS_H_ + +#include +#include +#include + +namespace testdevice { + +enum ParameterUniqueIds: uint8_t { + TEST_UINT32_0, + TEST_INT32_1, + TEST_FLOAT_VEC3_2, + PERIODIC_PRINT_ENABLED, + CHANGING_DATASETS +}; + +enum DeviceIndex: uint32_t { + DEVICE_0, + DEVICE_1 +}; + +/** Normal mode command. This ID is also used to access the set variable via the housekeeping +service */ +static constexpr DeviceCommandId_t TEST_NORMAL_MODE_CMD = 0; + +//! Test completion reply +static constexpr DeviceCommandId_t TEST_COMMAND_0 = 1; +//! Test data reply +static constexpr DeviceCommandId_t TEST_COMMAND_1 = 2; + +/** + * Can be used to trigger a notification to the demo controller. For DEVICE_0, only notifications + * messages will be generated while for DEVICE_1, snapshot messages will be generated. + * + * DEVICE_0 VAR: Sets the set variable 0 above a treshold (200) to trigger a variable + * notification. + * DEVICE_0 SET: Sets the vector mean values above a treshold (mean larger than 20) to trigger a + * set notification. + * + * DEVICE_1 VAR: Sets the set variable 0 below a treshold (less than 50 but not 0) to trigger a + * variable snapshot. + * DEVICE_1 SET: Sets the set vector mean values below a treshold (mean smaller than -20) to + * trigger a set snapshot message. + */ +static constexpr DeviceCommandId_t TEST_NOTIF_SNAPSHOT_VAR = 3; +static constexpr DeviceCommandId_t TEST_NOTIF_SNAPSHOT_SET = 4; + +/** + * Can be used to trigger a snapshot message to the demo controller. + * Depending on the device index, a notification will be triggered for different set variables. + * + * DEVICE_0: Sets the set variable 0 below a treshold (below 50 but not 0) to trigger + * a variable snapshot + * DEVICE_1: Sets the vector mean values below a treshold (mean less than -20) to trigger a + * set snapshot + */ +static constexpr DeviceCommandId_t TEST_SNAPSHOT = 5; + +//! Generates a random value for variable 1 of the dataset. +static constexpr DeviceCommandId_t GENERATE_SET_VAR_1_RNG_VALUE = 6; + + +/** + * These parameters are sent back with the command ID as a data reply + */ +static constexpr uint16_t COMMAND_1_PARAM1 = 0xBAB0; //!< param1, 2 bytes +//! param2, 8 bytes +static constexpr uint64_t COMMAND_1_PARAM2 = 0x000000524F42494E; + +static constexpr size_t TEST_COMMAND_0_SIZE = sizeof(TEST_COMMAND_0); +static constexpr size_t TEST_COMMAND_1_SIZE = sizeof(TEST_COMMAND_1) + sizeof(COMMAND_1_PARAM1) + + sizeof(COMMAND_1_PARAM2); + +enum PoolIds: lp_id_t { + TEST_UINT8_ID = 0, + TEST_UINT32_ID = 1, + TEST_FLOAT_VEC_3_ID = 2 +}; + +static constexpr uint8_t TEST_SET_ID = TEST_NORMAL_MODE_CMD; +static const sid_t TEST_SET_DEV_0_SID = sid_t(objects::TEST_DEVICE_HANDLER_0, TEST_SET_ID); +static const sid_t TEST_SET_DEV_1_SID = sid_t(objects::TEST_DEVICE_HANDLER_1, TEST_SET_ID); + +class TestDataSet: public StaticLocalDataSet<3> { +public: + TestDataSet(HasLocalDataPoolIF* owner): StaticLocalDataSet(owner, TEST_SET_ID) {} + TestDataSet(object_id_t owner): StaticLocalDataSet(sid_t(owner, TEST_SET_ID)) {} + + lp_var_t testUint8Var = lp_var_t( + gp_id_t(this->getCreatorObjectId(), PoolIds::TEST_UINT8_ID), this); + lp_var_t testUint32Var = lp_var_t( + gp_id_t(this->getCreatorObjectId(), PoolIds::TEST_UINT32_ID), this); + lp_vec_t testFloat3Vec = lp_vec_t( + gp_id_t(this->getCreatorObjectId(), PoolIds::TEST_FLOAT_VEC_3_ID), this); +}; + +} + +#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_TESTDEVICEDEFINITIONS_H_ */ diff --git a/tests/src/fsfw_tests/integration/task/CMakeLists.txt b/tests/src/fsfw_tests/integration/task/CMakeLists.txt new file mode 100644 index 00000000..0402d093 --- /dev/null +++ b/tests/src/fsfw_tests/integration/task/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${TARGET_NAME} PRIVATE + TestTask.cpp +) \ No newline at end of file diff --git a/tests/src/fsfw_tests/integration/task/TestTask.cpp b/tests/src/fsfw_tests/integration/task/TestTask.cpp new file mode 100644 index 00000000..c9910e90 --- /dev/null +++ b/tests/src/fsfw_tests/integration/task/TestTask.cpp @@ -0,0 +1,80 @@ +#include "TestTask.h" + +#include +#include + +bool TestTask::oneShotAction = true; +MutexIF* TestTask::testLock = nullptr; + +TestTask::TestTask(object_id_t objectId, bool periodicPrintout, bool periodicEvent): + SystemObject(objectId), testMode(testModes::A), + periodicPrinout(periodicPrintout), periodicEvent(periodicEvent) { + if(testLock == nullptr) { + testLock = MutexFactory::instance()->createMutex(); + } + IPCStore = ObjectManager::instance()->get(objects::IPC_STORE); +} + +TestTask::~TestTask() { +} + +ReturnValue_t TestTask::performOperation(uint8_t operationCode) { + ReturnValue_t result = RETURN_OK; + testLock->lockMutex(MutexIF::TimeoutType::WAITING, 20); + if(oneShotAction) { + // Add code here which should only be run once + performOneShotAction(); + oneShotAction = false; + } + testLock->unlockMutex(); + + // Add code here which should only be run once per performOperation + performPeriodicAction(); + + // Add code here which should only be run on alternating cycles. + if(testMode == testModes::A) { + performActionA(); + testMode = testModes::B; + } + else if(testMode == testModes::B) { + performActionB(); + testMode = testModes::A; + } + return result; +} + +ReturnValue_t TestTask::performOneShotAction() { + /* Everything here will only be performed once. */ + return HasReturnvaluesIF::RETURN_OK; +} + + +ReturnValue_t TestTask::performPeriodicAction() { + /* This is performed each task cycle */ + ReturnValue_t result = RETURN_OK; + + if(periodicPrinout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestTask::performPeriodicAction: Hello World!" << std::endl; +#else + sif::printInfo("TestTask::performPeriodicAction: Hello World!\n"); +#endif + } + if(periodicEvent) { + triggerEvent(TEST_EVENT, 0x1234, 0x4321); + } + return result; +} + +ReturnValue_t TestTask::performActionA() { + /* This is performed each alternating task cycle */ + ReturnValue_t result = RETURN_OK; + return result; +} + +ReturnValue_t TestTask::performActionB() { + /* This is performed each alternating task cycle */ + ReturnValue_t result = RETURN_OK; + return result; +} + diff --git a/tests/src/fsfw_tests/integration/task/TestTask.h b/tests/src/fsfw_tests/integration/task/TestTask.h new file mode 100644 index 00000000..95f27bec --- /dev/null +++ b/tests/src/fsfw_tests/integration/task/TestTask.h @@ -0,0 +1,56 @@ +#ifndef MISSION_DEMO_TESTTASK_H_ +#define MISSION_DEMO_TESTTASK_H_ + +#include +#include +#include + +#include "fsfw/events/Event.h" +#include "events/subsystemIdRanges.h" + +/** + * @brief Test class for general C++ testing and any other code which will not be part of the + * primary mission software. + * @details + * Should not be used for board specific tests. Instead, a derived board test class should be used. + */ +class TestTask : + public SystemObject, + public ExecutableObjectIF, + public HasReturnvaluesIF { +public: + TestTask(object_id_t objectId, bool periodicPrintout = false, bool periodicEvent = false); + virtual ~TestTask(); + virtual ReturnValue_t performOperation(uint8_t operationCode = 0); + + static constexpr uint8_t subsystemId = SUBSYSTEM_ID::TEST_TASK_ID; + static constexpr Event TEST_EVENT = event::makeEvent(subsystemId, 0, severity::INFO); + +protected: + virtual ReturnValue_t performOneShotAction(); + virtual ReturnValue_t performPeriodicAction(); + virtual ReturnValue_t performActionA(); + virtual ReturnValue_t performActionB(); + + enum testModes: uint8_t { + A, + B + }; + + testModes testMode; + bool periodicPrinout = false; + bool periodicEvent = false; + + bool testFlag = false; + uint8_t counter { 1 }; + uint8_t counterTrigger { 3 }; + + void performPusInjectorTest(); + void examplePacketTest(); +private: + static bool oneShotAction; + static MutexIF* testLock; + StorageManagerIF* IPCStore; +}; + +#endif /* TESTTASK_H_ */ From 7122c37511ebea8e6c586556db6195cf2d91f636 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 18 Oct 2021 18:26:03 +0200 Subject: [PATCH 02/23] updated function names --- src/fsfw/tmtcpacket/SpacePacket.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fsfw/tmtcpacket/SpacePacket.h b/src/fsfw/tmtcpacket/SpacePacket.h index 677ba023..16673319 100644 --- a/src/fsfw/tmtcpacket/SpacePacket.h +++ b/src/fsfw/tmtcpacket/SpacePacket.h @@ -77,12 +77,12 @@ constexpr uint16_t getSpacePacketIdFromApid(bool isTc, uint16_t apid, ((apid >> 8) & 0x07)) << 8 | (apid & 0x00ff); } -constexpr uint16_t getTcSpacketIdFromApid(uint16_t apid, +constexpr uint16_t getTcSpacePacketIdFromApid(uint16_t apid, bool secondaryHeaderFlag = true) { return getSpacePacketIdFromApid(true, apid, secondaryHeaderFlag); } -constexpr uint16_t getTmSpacketIdFromApid(uint16_t apid, +constexpr uint16_t getTmSpacePacketIdFromApid(uint16_t apid, bool secondaryHeaderFlag = true) { return getSpacePacketIdFromApid(false, apid, secondaryHeaderFlag); } From a5a306ff66afad64b19f9e96ecd9ced815b378b6 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 21 Oct 2021 22:36:54 +0200 Subject: [PATCH 03/23] arrayprinter format improvements --- src/fsfw/globalfunctions/arrayprinter.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/fsfw/globalfunctions/arrayprinter.cpp b/src/fsfw/globalfunctions/arrayprinter.cpp index 45a1cb38..82b2a6f5 100644 --- a/src/fsfw/globalfunctions/arrayprinter.cpp +++ b/src/fsfw/globalfunctions/arrayprinter.cpp @@ -45,11 +45,11 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, std::cout << "\r" << std::endl; } - std::cout << "[" << std::hex; + std::cout << "hex [" << std::hex; for(size_t i = 0; i < size; i++) { - std::cout << "0x" << static_cast(data[i]); + std::cout << "" << static_cast(data[i]); if(i < size - 1) { - std::cout << " , "; + std::cout << ","; if(i > 0 and (i + 1) % maxCharPerLine == 0) { std::cout << std::endl; @@ -69,16 +69,16 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, break; } - currentPos += snprintf(printBuffer + currentPos, 6, "0x%02x", data[i]); + currentPos += snprintf(printBuffer + currentPos, 6, "%02x", data[i]); if(i < size - 1) { - currentPos += sprintf(printBuffer + currentPos, ", "); + currentPos += sprintf(printBuffer + currentPos, ","); if(i > 0 and (i + 1) % maxCharPerLine == 0) { currentPos += sprintf(printBuffer + currentPos, "\n"); } } } #if FSFW_DISABLE_PRINTOUT == 0 - printf("[%s]\n", printBuffer); + printf("hex [%s]\n", printBuffer); #endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif } @@ -90,11 +90,11 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, std::cout << "\r" << std::endl; } - std::cout << "[" << std::dec; + std::cout << "dec [" << std::dec; for(size_t i = 0; i < size; i++) { std::cout << static_cast(data[i]); if(i < size - 1){ - std::cout << " , "; + std::cout << ","; if(i > 0 and (i + 1) % maxCharPerLine == 0) { std::cout << std::endl; } @@ -114,14 +114,14 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, currentPos += snprintf(printBuffer + currentPos, 3, "%d", data[i]); if(i < size - 1) { - currentPos += sprintf(printBuffer + currentPos, ", "); + currentPos += sprintf(printBuffer + currentPos, ","); if(i > 0 and (i + 1) % maxCharPerLine == 0) { currentPos += sprintf(printBuffer + currentPos, "\n"); } } } #if FSFW_DISABLE_PRINTOUT == 0 - printf("[%s]\n", printBuffer); + printf("dec [%s]\n", printBuffer); #endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif } From dc6ed40bfb7ff6ecbb472ae09ccf9ca38167c563 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 22 Oct 2021 11:30:00 +0200 Subject: [PATCH 04/23] arrayprinter format improvements --- src/fsfw/globalfunctions/arrayprinter.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/fsfw/globalfunctions/arrayprinter.cpp b/src/fsfw/globalfunctions/arrayprinter.cpp index 82b2a6f5..45a1cb38 100644 --- a/src/fsfw/globalfunctions/arrayprinter.cpp +++ b/src/fsfw/globalfunctions/arrayprinter.cpp @@ -45,11 +45,11 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, std::cout << "\r" << std::endl; } - std::cout << "hex [" << std::hex; + std::cout << "[" << std::hex; for(size_t i = 0; i < size; i++) { - std::cout << "" << static_cast(data[i]); + std::cout << "0x" << static_cast(data[i]); if(i < size - 1) { - std::cout << ","; + std::cout << " , "; if(i > 0 and (i + 1) % maxCharPerLine == 0) { std::cout << std::endl; @@ -69,16 +69,16 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, break; } - currentPos += snprintf(printBuffer + currentPos, 6, "%02x", data[i]); + currentPos += snprintf(printBuffer + currentPos, 6, "0x%02x", data[i]); if(i < size - 1) { - currentPos += sprintf(printBuffer + currentPos, ","); + currentPos += sprintf(printBuffer + currentPos, ", "); if(i > 0 and (i + 1) % maxCharPerLine == 0) { currentPos += sprintf(printBuffer + currentPos, "\n"); } } } #if FSFW_DISABLE_PRINTOUT == 0 - printf("hex [%s]\n", printBuffer); + printf("[%s]\n", printBuffer); #endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif } @@ -90,11 +90,11 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, std::cout << "\r" << std::endl; } - std::cout << "dec [" << std::dec; + std::cout << "[" << std::dec; for(size_t i = 0; i < size; i++) { std::cout << static_cast(data[i]); if(i < size - 1){ - std::cout << ","; + std::cout << " , "; if(i > 0 and (i + 1) % maxCharPerLine == 0) { std::cout << std::endl; } @@ -114,14 +114,14 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, currentPos += snprintf(printBuffer + currentPos, 3, "%d", data[i]); if(i < size - 1) { - currentPos += sprintf(printBuffer + currentPos, ","); + currentPos += sprintf(printBuffer + currentPos, ", "); if(i > 0 and (i + 1) % maxCharPerLine == 0) { currentPos += sprintf(printBuffer + currentPos, "\n"); } } } #if FSFW_DISABLE_PRINTOUT == 0 - printf("dec [%s]\n", printBuffer); + printf("[%s]\n", printBuffer); #endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif } From 07a0dd5331072c536cd30fd0505bfc1b38cc813b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 22 Oct 2021 11:32:28 +0200 Subject: [PATCH 05/23] this is the correct file --- src/fsfw/globalfunctions/arrayprinter.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/fsfw/globalfunctions/arrayprinter.cpp b/src/fsfw/globalfunctions/arrayprinter.cpp index 45a1cb38..964b9d04 100644 --- a/src/fsfw/globalfunctions/arrayprinter.cpp +++ b/src/fsfw/globalfunctions/arrayprinter.cpp @@ -45,18 +45,18 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, std::cout << "\r" << std::endl; } - std::cout << "[" << std::hex; + std::cout << "hex [" << std::setfill('0') << std::hex; for(size_t i = 0; i < size; i++) { - std::cout << "0x" << static_cast(data[i]); + std::cout << std::setw(2) << static_cast(data[i]); if(i < size - 1) { - std::cout << " , "; + std::cout << ","; if(i > 0 and (i + 1) % maxCharPerLine == 0) { std::cout << std::endl; } } } - std::cout << std::dec; + std::cout << std::dec << std::setfill(' '); std::cout << "]" << std::endl; #else // General format: 0x01, 0x02, 0x03 so it is number of chars times 6 @@ -69,16 +69,16 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, break; } - currentPos += snprintf(printBuffer + currentPos, 6, "0x%02x", data[i]); + currentPos += snprintf(printBuffer + currentPos, 6, "%02x", data[i]); if(i < size - 1) { - currentPos += sprintf(printBuffer + currentPos, ", "); + currentPos += sprintf(printBuffer + currentPos, ","); if(i > 0 and (i + 1) % maxCharPerLine == 0) { currentPos += sprintf(printBuffer + currentPos, "\n"); } } } #if FSFW_DISABLE_PRINTOUT == 0 - printf("[%s]\n", printBuffer); + printf("hex [%s]\n", printBuffer); #endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif } @@ -90,11 +90,11 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, std::cout << "\r" << std::endl; } - std::cout << "[" << std::dec; + std::cout << "dec [" << std::dec; for(size_t i = 0; i < size; i++) { std::cout << static_cast(data[i]); if(i < size - 1){ - std::cout << " , "; + std::cout << ","; if(i > 0 and (i + 1) % maxCharPerLine == 0) { std::cout << std::endl; } @@ -114,14 +114,14 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, currentPos += snprintf(printBuffer + currentPos, 3, "%d", data[i]); if(i < size - 1) { - currentPos += sprintf(printBuffer + currentPos, ", "); + currentPos += sprintf(printBuffer + currentPos, ","); if(i > 0 and (i + 1) % maxCharPerLine == 0) { currentPos += sprintf(printBuffer + currentPos, "\n"); } } } #if FSFW_DISABLE_PRINTOUT == 0 - printf("[%s]\n", printBuffer); + printf("dec [%s]\n", printBuffer); #endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif } From 19f9b0280c2ae45b4f029d9d852a8fb8631e79c8 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Mon, 25 Oct 2021 14:59:16 +0200 Subject: [PATCH 06/23] added jenkins integration --- automation/Dockerfile | 8 ++++++++ automation/Jenkinsfile | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 automation/Dockerfile create mode 100644 automation/Jenkinsfile diff --git a/automation/Dockerfile b/automation/Dockerfile new file mode 100644 index 00000000..0526d8f0 --- /dev/null +++ b/automation/Dockerfile @@ -0,0 +1,8 @@ +FROM ubuntu:focal + +RUN apt-get update +RUN apt-get --yes upgrade + +#tzdata is a dependency, won't install otherwise +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get --yes install gcc g++ cmake lcov git nano diff --git a/automation/Jenkinsfile b/automation/Jenkinsfile new file mode 100644 index 00000000..a90037f8 --- /dev/null +++ b/automation/Jenkinsfile @@ -0,0 +1,38 @@ +pipeline { + agent any + stages { + stage('Clean') { + steps { + sh 'rm -rf build-unittests' + } + } + stage('Build') { + agent { + dockerfile { + dir 'automation' + additionalBuildArgs '--no-cache' + reuseNode true + } + } + steps { + dir('build-unittests') { + sh 'cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..' + sh 'cmake --build . -j' + } + } + } + stage('Unittests') { + agent { + dockerfile { + dir 'automation' + reuseNode true + } + } + steps { + dir('build-unittests') { + sh 'cmake --build . -- fsfw-tests_coverage -j' + } + } + } + } +} From 81bae858254d496da5e48d734e8218218cd11eaa Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 26 Oct 2021 17:10:15 +0200 Subject: [PATCH 07/23] hotfix for unittests --- tests/src/fsfw_tests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/fsfw_tests/CMakeLists.txt b/tests/src/fsfw_tests/CMakeLists.txt index fb03f562..f6e1b8ab 100644 --- a/tests/src/fsfw_tests/CMakeLists.txt +++ b/tests/src/fsfw_tests/CMakeLists.txt @@ -1,9 +1,9 @@ -add_subdirectory(integration) - if(FSFW_ADD_INTERNAL_TESTS) add_subdirectory(internal) endif() if(FSFW_BUILD_UNITTESTS) add_subdirectory(unit) +else() + add_subdirectory(integration) endif() From 5f8adc63b70298c0d6163219ad8782de76c22460 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 26 Oct 2021 17:16:21 +0200 Subject: [PATCH 08/23] some more fixes for integration tests --- .../integration/assemblies/TestAssembly.cpp | 4 ++-- .../integration/controller/TestController.cpp | 11 ++++++----- .../integration/controller/TestController.h | 3 ++- .../integration/devices/TestDeviceHandler.cpp | 8 +------- .../devices/devicedefinitions/testDeviceDefinitions.h | 3 --- 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp b/tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp index 1c497ebd..0ead4bfd 100644 --- a/tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp +++ b/tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp @@ -160,11 +160,11 @@ ReturnValue_t TestAssembly::initialize() { handler1->setParentQueue(this->getCommandQueue()); - result = registerChild(objects::TEST_DEVICE_HANDLER_0); + result = registerChild(deviceHandler0Id); if (result != HasReturnvaluesIF::RETURN_OK) { return result; } - result = registerChild(objects::TEST_DEVICE_HANDLER_1); + result = registerChild(deviceHandler1Id); if (result != HasReturnvaluesIF::RETURN_OK) { return result; } diff --git a/tests/src/fsfw_tests/integration/controller/TestController.cpp b/tests/src/fsfw_tests/integration/controller/TestController.cpp index 385d07bd..96da5fe3 100644 --- a/tests/src/fsfw_tests/integration/controller/TestController.cpp +++ b/tests/src/fsfw_tests/integration/controller/TestController.cpp @@ -5,10 +5,11 @@ #include #include -TestController::TestController(object_id_t objectId, size_t commandQueueDepth): +TestController::TestController(object_id_t objectId, object_id_t device0, object_id_t device1, + size_t commandQueueDepth): ExtendedControllerBase(objectId, objects::NO_OBJECT, commandQueueDepth), - deviceDataset0(objects::TEST_DEVICE_HANDLER_0), - deviceDataset1(objects::TEST_DEVICE_HANDLER_1) { + deviceDataset0(device0), + deviceDataset1(device1) { } TestController::~TestController() { @@ -163,7 +164,7 @@ ReturnValue_t TestController::initializeLocalDataPool(localpool::DataPool &local ReturnValue_t TestController::initializeAfterTaskCreation() { namespace td = testdevice; HasLocalDataPoolIF* device0 = ObjectManager::instance()->get( - objects::TEST_DEVICE_HANDLER_0); + deviceDataset0.getCreatorObjectId()); if(device0 == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "TestController::initializeAfterTaskCreation: Test device handler 0 " @@ -185,7 +186,7 @@ ReturnValue_t TestController::initializeAfterTaskCreation() { HasLocalDataPoolIF* device1 = ObjectManager::instance()->get( - objects::TEST_DEVICE_HANDLER_1); + deviceDataset0.getCreatorObjectId()); if(device1 == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "TestController::initializeAfterTaskCreation: Test device handler 1 " diff --git a/tests/src/fsfw_tests/integration/controller/TestController.h b/tests/src/fsfw_tests/integration/controller/TestController.h index 8092f945..475d8703 100644 --- a/tests/src/fsfw_tests/integration/controller/TestController.h +++ b/tests/src/fsfw_tests/integration/controller/TestController.h @@ -8,7 +8,8 @@ class TestController: public ExtendedControllerBase { public: - TestController(object_id_t objectId, size_t commandQueueDepth = 10); + TestController(object_id_t objectId, object_id_t device0, object_id_t device1, + size_t commandQueueDepth = 10); virtual~ TestController(); protected: testdevice::TestDataSet deviceDataset0; diff --git a/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp index 91019487..46138c56 100644 --- a/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp +++ b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp @@ -644,13 +644,7 @@ ReturnValue_t TestDevice::initializeLocalDataPool(localpool::DataPool &localData localDataPoolMap.emplace(td::PoolIds::TEST_FLOAT_VEC_3_ID, new PoolEntry({0.0, 0.0, 0.0})); - sid_t sid; - if(deviceIdx == td::DeviceIndex::DEVICE_0) { - sid = td::TEST_SET_DEV_0_SID; - } - else { - sid = td::TEST_SET_DEV_1_SID; - } + sid_t sid(this->getObjectId(), td::TEST_SET_ID); /* Subscribe for periodic HK packets but do not enable reporting for now. Non-diangostic with a period of one second */ poolManager.subscribeForPeriodicPacket(sid, false, 1.0, false); diff --git a/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h b/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h index 10668b94..1c112e3f 100644 --- a/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h +++ b/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h @@ -3,7 +3,6 @@ #include #include -#include namespace testdevice { @@ -79,8 +78,6 @@ enum PoolIds: lp_id_t { }; static constexpr uint8_t TEST_SET_ID = TEST_NORMAL_MODE_CMD; -static const sid_t TEST_SET_DEV_0_SID = sid_t(objects::TEST_DEVICE_HANDLER_0, TEST_SET_ID); -static const sid_t TEST_SET_DEV_1_SID = sid_t(objects::TEST_DEVICE_HANDLER_1, TEST_SET_ID); class TestDataSet: public StaticLocalDataSet<3> { public: From 2126e6e3754a45f09bc9c557f1703821d7e68789 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 26 Oct 2021 17:24:28 +0200 Subject: [PATCH 09/23] simplified test task --- .../fsfw_tests/integration/task/TestTask.cpp | 16 ++-------------- .../src/fsfw_tests/integration/task/TestTask.h | 17 ++--------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/tests/src/fsfw_tests/integration/task/TestTask.cpp b/tests/src/fsfw_tests/integration/task/TestTask.cpp index c9910e90..b33bd51c 100644 --- a/tests/src/fsfw_tests/integration/task/TestTask.cpp +++ b/tests/src/fsfw_tests/integration/task/TestTask.cpp @@ -6,9 +6,8 @@ bool TestTask::oneShotAction = true; MutexIF* TestTask::testLock = nullptr; -TestTask::TestTask(object_id_t objectId, bool periodicPrintout, bool periodicEvent): - SystemObject(objectId), testMode(testModes::A), - periodicPrinout(periodicPrintout), periodicEvent(periodicEvent) { +TestTask::TestTask(object_id_t objectId): + SystemObject(objectId), testMode(testModes::A) { if(testLock == nullptr) { testLock = MutexFactory::instance()->createMutex(); } @@ -52,17 +51,6 @@ ReturnValue_t TestTask::performOneShotAction() { ReturnValue_t TestTask::performPeriodicAction() { /* This is performed each task cycle */ ReturnValue_t result = RETURN_OK; - - if(periodicPrinout) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::info << "TestTask::performPeriodicAction: Hello World!" << std::endl; -#else - sif::printInfo("TestTask::performPeriodicAction: Hello World!\n"); -#endif - } - if(periodicEvent) { - triggerEvent(TEST_EVENT, 0x1234, 0x4321); - } return result; } diff --git a/tests/src/fsfw_tests/integration/task/TestTask.h b/tests/src/fsfw_tests/integration/task/TestTask.h index 95f27bec..e56b5581 100644 --- a/tests/src/fsfw_tests/integration/task/TestTask.h +++ b/tests/src/fsfw_tests/integration/task/TestTask.h @@ -5,9 +5,6 @@ #include #include -#include "fsfw/events/Event.h" -#include "events/subsystemIdRanges.h" - /** * @brief Test class for general C++ testing and any other code which will not be part of the * primary mission software. @@ -19,12 +16,9 @@ class TestTask : public ExecutableObjectIF, public HasReturnvaluesIF { public: - TestTask(object_id_t objectId, bool periodicPrintout = false, bool periodicEvent = false); + TestTask(object_id_t objectId); virtual ~TestTask(); - virtual ReturnValue_t performOperation(uint8_t operationCode = 0); - - static constexpr uint8_t subsystemId = SUBSYSTEM_ID::TEST_TASK_ID; - static constexpr Event TEST_EVENT = event::makeEvent(subsystemId, 0, severity::INFO); + virtual ReturnValue_t performOperation(uint8_t operationCode = 0) override; protected: virtual ReturnValue_t performOneShotAction(); @@ -38,15 +32,8 @@ protected: }; testModes testMode; - bool periodicPrinout = false; - bool periodicEvent = false; - bool testFlag = false; - uint8_t counter { 1 }; - uint8_t counterTrigger { 3 }; - void performPusInjectorTest(); - void examplePacketTest(); private: static bool oneShotAction; static MutexIF* testLock; From 3c414726499e5729667e6f62a67ad5dd9d5bc7e9 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 26 Oct 2021 20:30:22 +0200 Subject: [PATCH 10/23] tweaking Jenkinsfile --- automation/Jenkinsfile | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/automation/Jenkinsfile b/automation/Jenkinsfile index a90037f8..e5b5ecda 100644 --- a/automation/Jenkinsfile +++ b/automation/Jenkinsfile @@ -1,12 +1,10 @@ pipeline { agent any + environment { + BUILDDIR = 'build-unittests' + } stages { - stage('Clean') { - steps { - sh 'rm -rf build-unittests' - } - } - stage('Build') { + stage('Configure') { agent { dockerfile { dir 'automation' @@ -15,8 +13,21 @@ pipeline { } } steps { - dir('build-unittests') { + sh 'rm -rf $BUILDDIR' + dir($BUILDDIR) { sh 'cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..' + } + } + } + stage('Build') { + agent { + dockerfile { + dir 'automation' + reuseNode true + } + } + steps { + dir($BUILDDIR) { sh 'cmake --build . -j' } } @@ -29,7 +40,7 @@ pipeline { } } steps { - dir('build-unittests') { + dir($BUILDDIR) { sh 'cmake --build . -- fsfw-tests_coverage -j' } } From 1923b339e92772f3f43cbc7942d3c75baca35e02 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 26 Oct 2021 20:47:53 +0200 Subject: [PATCH 11/23] I can not jenkins --- automation/Jenkinsfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/automation/Jenkinsfile b/automation/Jenkinsfile index e5b5ecda..9dc716a6 100644 --- a/automation/Jenkinsfile +++ b/automation/Jenkinsfile @@ -14,7 +14,7 @@ pipeline { } steps { sh 'rm -rf $BUILDDIR' - dir($BUILDDIR) { + dir(env.BUILDDIR) { sh 'cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..' } } @@ -27,7 +27,7 @@ pipeline { } } steps { - dir($BUILDDIR) { + dir(BUILDDIR) { sh 'cmake --build . -j' } } @@ -40,7 +40,7 @@ pipeline { } } steps { - dir($BUILDDIR) { + dir(BUILDDIR) { sh 'cmake --build . -- fsfw-tests_coverage -j' } } From b02f737418b59c1724b9ee8b64defff1f5d19623 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 26 Oct 2021 20:53:08 +0200 Subject: [PATCH 12/23] jenkins cosmetics --- automation/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automation/Jenkinsfile b/automation/Jenkinsfile index 9dc716a6..6a5d94b5 100644 --- a/automation/Jenkinsfile +++ b/automation/Jenkinsfile @@ -14,7 +14,7 @@ pipeline { } steps { sh 'rm -rf $BUILDDIR' - dir(env.BUILDDIR) { + dir(BUILDDIR) { sh 'cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..' } } From da42edcc0cc49a81d1dca5a897aa2ab73accd600 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 26 Oct 2021 20:58:34 +0200 Subject: [PATCH 13/23] Jenkinsfile: added stage to be more verbose --- automation/Jenkinsfile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/automation/Jenkinsfile b/automation/Jenkinsfile index 6a5d94b5..0cb973bd 100644 --- a/automation/Jenkinsfile +++ b/automation/Jenkinsfile @@ -4,7 +4,7 @@ pipeline { BUILDDIR = 'build-unittests' } stages { - stage('Configure') { + stage('Create Docker') { agent { dockerfile { dir 'automation' @@ -14,6 +14,16 @@ pipeline { } steps { sh 'rm -rf $BUILDDIR' + } + } + stage('Configure') { + agent { + dockerfile { + dir 'automation' + reuseNode true + } + } + steps { dir(BUILDDIR) { sh 'cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..' } From cc7250fcf5fb5e9ca890b3688d47d22a8956b14e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 27 Oct 2021 17:08:59 +0200 Subject: [PATCH 14/23] second cmake fix --- src/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a8f139b..e4670807 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,5 +16,3 @@ target_include_directories(${LIB_FSFW_NAME} PRIVATE target_include_directories(${LIB_FSFW_NAME} INTERFACE ${CMAKE_CURRENT_BINARY_DIR} ) - -configure_file(fsfw/FSFW.h.in fsfw/FSFW.h) From 42458725e86352e1fc38b806235ea5e8eb2b318c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 27 Oct 2021 17:10:37 +0200 Subject: [PATCH 15/23] more important fix --- CMakeLists.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 923d5cc5..e78e8929 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,14 +93,6 @@ target_include_directories(${LIB_FSFW_NAME} INTERFACE ${CMAKE_CURRENT_BINARY_DIR} ) -if(FSFW_BUILD_UNITTESTS) - configure_file(src/fsfw/FSFW.h.in fsfw/FSFW.h) - configure_file(src/fsfw/FSFWVersion.h.in fsfw/FSFWVersion.h) -else() - configure_file(src/fsfw/FSFW.h.in FSFW.h) - configure_file(src/fsfw/FSFWVersion.h.in FSFWVersion.h) -endif() - if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) @@ -155,6 +147,14 @@ else() set(OS_FSFW "host") endif() +if(FSFW_BUILD_UNITTESTS) + configure_file(src/fsfw/FSFW.h.in fsfw/FSFW.h) + configure_file(src/fsfw/FSFWVersion.h.in fsfw/FSFWVersion.h) +else() + configure_file(src/fsfw/FSFW.h.in FSFW.h) + configure_file(src/fsfw/FSFWVersion.h.in FSFWVersion.h) +endif() + message(STATUS "Compiling FSFW for the ${FSFW_OS_NAME} operating system.") add_subdirectory(src) From 3448a8c01b4e1cc3171e93f68b600dbfa9501962 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 27 Oct 2021 17:23:14 +0200 Subject: [PATCH 16/23] SPI ComIF updates 1. Make setting a chip select pin optional 2. Make ComIF member functions public --- hal/src/fsfw_hal/stm32h7/spi/SpiComIF.cpp | 38 +++++++++++++++-------- hal/src/fsfw_hal/stm32h7/spi/SpiComIF.h | 3 +- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.cpp b/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.cpp index 1813aac0..4c4f7744 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.cpp +++ b/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.cpp @@ -138,12 +138,14 @@ ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) { spi::setSpiDmaMspFunctions(typedCfg); } - gpio::initializeGpioClock(gpioPort); - GPIO_InitTypeDef chipSelect = {}; - chipSelect.Pin = gpioPin; - chipSelect.Mode = GPIO_MODE_OUTPUT_PP; - HAL_GPIO_Init(gpioPort, &chipSelect); - HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET); + if(gpioPort != nullptr) { + gpio::initializeGpioClock(gpioPort); + GPIO_InitTypeDef chipSelect = {}; + chipSelect.Pin = gpioPin; + chipSelect.Mode = GPIO_MODE_OUTPUT_PP; + HAL_GPIO_Init(gpioPort, &chipSelect); + HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET); + } if(HAL_SPI_Init(&spiHandle) != HAL_OK) { sif::printWarning("SpiComIF::initialize: Error initializing SPI\n"); @@ -259,10 +261,15 @@ ReturnValue_t SpiComIF::handlePollingSendOperation(uint8_t* recvPtr, SPI_HandleT return returnval; } spiCookie.setTransferState(spi::TransferStates::WAIT); - HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_RESET); + if(gpioPort != nullptr) { + HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_RESET); + } + auto result = HAL_SPI_TransmitReceive(&spiHandle, const_cast(sendData), recvPtr, sendLen, defaultPollingTimeout); - HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET); + if(gpioPort != nullptr) { + HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET); + } spiSemaphore->release(); switch(result) { case(HAL_OK): { @@ -392,8 +399,10 @@ ReturnValue_t SpiComIF::genericIrqSendSetup(uint8_t *recvPtr, SPI_HandleTypeDef& // The SPI handle is passed to the default SPI callback as a void argument. This callback // is different from the user callbacks specified above! spi::assignSpiUserArgs(spiCookie.getSpiIdx(), reinterpret_cast(&spiHandle)); - HAL_GPIO_WritePin(spiCookie.getChipSelectGpioPort(), spiCookie.getChipSelectGpioPin(), - GPIO_PIN_RESET); + if(spiCookie.getChipSelectGpioPort() != nullptr) { + HAL_GPIO_WritePin(spiCookie.getChipSelectGpioPort(), spiCookie.getChipSelectGpioPin(), + GPIO_PIN_RESET); + } return HasReturnvaluesIF::RETURN_OK; } @@ -426,9 +435,12 @@ void SpiComIF::genericIrqHandler(void *irqArgsVoid, spi::TransferStates targetSt spiCookie->setTransferState(targetState); - // Pull CS pin high again - HAL_GPIO_WritePin(spiCookie->getChipSelectGpioPort(), spiCookie->getChipSelectGpioPin(), - GPIO_PIN_SET); + if(spiCookie->getChipSelectGpioPort() != nullptr) { + // Pull CS pin high again + HAL_GPIO_WritePin(spiCookie->getChipSelectGpioPort(), spiCookie->getChipSelectGpioPin(), + GPIO_PIN_SET); + } + #if defined FSFW_OSAL_FREERTOS // Release the task semaphore diff --git a/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.h b/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.h index 9548e102..cb6c4cf8 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.h +++ b/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.h @@ -60,7 +60,6 @@ public: void addDmaHandles(DMA_HandleTypeDef* txHandle, DMA_HandleTypeDef* rxHandle); ReturnValue_t initialize() override; -protected: // DeviceCommunicationIF overrides virtual ReturnValue_t initializeInterface(CookieIF * cookie) override; @@ -72,7 +71,7 @@ protected: virtual ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) override; -private: +protected: struct SpiInstance { SpiInstance(size_t maxRecvSize): replyBuffer(std::vector(maxRecvSize)) {} From d675621b73e86449ffe88298bcc09d5582b1c5c9 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 27 Oct 2021 17:31:04 +0200 Subject: [PATCH 17/23] grouping CS gpio definition --- hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp | 10 +++++----- hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp index 88f1e1f1..e9cbac8e 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp +++ b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp @@ -3,10 +3,10 @@ SpiCookie::SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferModes transferMode, spi::MspCfgBase* mspCfg, uint32_t spiSpeed, spi::SpiModes spiMode, - uint16_t chipSelectGpioPin, GPIO_TypeDef* chipSelectGpioPort, size_t maxRecvSize): + size_t maxRecvSize, GpioPair csGpio): deviceAddress(deviceAddress), spiIdx(spiIdx), spiSpeed(spiSpeed), spiMode(spiMode), - transferMode(transferMode), chipSelectGpioPin(chipSelectGpioPin), - chipSelectGpioPort(chipSelectGpioPort), mspCfg(mspCfg), maxRecvSize(maxRecvSize) { + transferMode(transferMode), csGpio(csGpio), + mspCfg(mspCfg), maxRecvSize(maxRecvSize) { spiHandle.Init.DataSize = SPI_DATASIZE_8BIT; spiHandle.Init.FirstBit = SPI_FIRSTBIT_MSB; spiHandle.Init.TIMode = SPI_TIMODE_DISABLE; @@ -24,11 +24,11 @@ SpiCookie::SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferM } uint16_t SpiCookie::getChipSelectGpioPin() const { - return chipSelectGpioPin; + return csGpio.second; } GPIO_TypeDef* SpiCookie::getChipSelectGpioPort() { - return chipSelectGpioPort; + return csGpio.first; } address_t SpiCookie::getDeviceAddress() const { diff --git a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h index 45226b4a..f5698999 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h +++ b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h @@ -8,6 +8,8 @@ #include "stm32h743xx.h" +#include + /** * @brief SPI cookie implementation for the STM32H7 device family * @details @@ -18,6 +20,12 @@ class SpiCookie: public CookieIF { friend class SpiComIF; public: + /** + * Typedef for STM32 GPIO pair where the first entry is the port used (e.g. GPIOA) + * and the second entry is the pin number + */ + using GpioPair = std::pair; + /** * Allows construction of a SPI cookie for a connected SPI device * @param deviceAddress @@ -32,10 +40,11 @@ public: * definitions supplied in the MCU header file! (e.g. GPIO_PIN_X) * @param chipSelectGpioPort GPIO port (e.g. GPIOA) * @param maxRecvSize Maximum expected receive size. Chose as small as possible. + * @param csGpio Optional CS GPIO definition. */ SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferModes transferMode, spi::MspCfgBase* mspCfg, uint32_t spiSpeed, spi::SpiModes spiMode, - uint16_t chipSelectGpioPin, GPIO_TypeDef* chipSelectGpioPort, size_t maxRecvSize); + size_t maxRecvSize, GpioPair csGpio = GpioPair(nullptr, 0)); uint16_t getChipSelectGpioPin() const; GPIO_TypeDef* getChipSelectGpioPort(); @@ -55,8 +64,8 @@ private: spi::SpiModes spiMode; spi::TransferModes transferMode; volatile spi::TransferStates transferState = spi::TransferStates::IDLE; - uint16_t chipSelectGpioPin; - GPIO_TypeDef* chipSelectGpioPort; + GpioPair csGpio; + // The MSP configuration is cached here. Be careful when using this, it is automatically // deleted by the SPI communication interface if it is not required anymore! spi::MspCfgBase* mspCfg = nullptr; From cb7399b9998e9ec2d6439c5eb75c8312641770a4 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 27 Oct 2021 18:05:18 +0200 Subject: [PATCH 18/23] msp init improvements --- hal/src/fsfw_hal/stm32h7/definitions.h | 25 ++++++++++ hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp | 6 +-- hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h | 10 ++-- hal/src/fsfw_hal/stm32h7/spi/mspInit.cpp | 28 +++++------ hal/src/fsfw_hal/stm32h7/spi/mspInit.h | 50 +++++++++++++------ .../fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp | 22 ++++---- 6 files changed, 90 insertions(+), 51 deletions(-) create mode 100644 hal/src/fsfw_hal/stm32h7/definitions.h diff --git a/hal/src/fsfw_hal/stm32h7/definitions.h b/hal/src/fsfw_hal/stm32h7/definitions.h new file mode 100644 index 00000000..af63a541 --- /dev/null +++ b/hal/src/fsfw_hal/stm32h7/definitions.h @@ -0,0 +1,25 @@ +#ifndef FSFW_HAL_STM32H7_DEFINITIONS_H_ +#define FSFW_HAL_STM32H7_DEFINITIONS_H_ + +#include +#include "stm32h7xx.h" + +namespace stm32h7 { + +/** + * Typedef for STM32 GPIO pair where the first entry is the port used (e.g. GPIOA) + * and the second entry is the pin number + */ +struct GpioCfg { + GpioCfg(): port(nullptr), pin(0), altFnc(0) {}; + + GpioCfg(GPIO_TypeDef* port, uint16_t pin, uint8_t altFnc = 0): + port(port), pin(pin), altFnc(altFnc) {}; + GPIO_TypeDef* port; + uint16_t pin; + uint8_t altFnc; +}; + +} + +#endif /* #ifndef FSFW_HAL_STM32H7_DEFINITIONS_H_ */ diff --git a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp index e9cbac8e..200d4651 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp +++ b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp @@ -3,7 +3,7 @@ SpiCookie::SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferModes transferMode, spi::MspCfgBase* mspCfg, uint32_t spiSpeed, spi::SpiModes spiMode, - size_t maxRecvSize, GpioPair csGpio): + size_t maxRecvSize, stm32h7::GpioCfg csGpio): deviceAddress(deviceAddress), spiIdx(spiIdx), spiSpeed(spiSpeed), spiMode(spiMode), transferMode(transferMode), csGpio(csGpio), mspCfg(mspCfg), maxRecvSize(maxRecvSize) { @@ -24,11 +24,11 @@ SpiCookie::SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferM } uint16_t SpiCookie::getChipSelectGpioPin() const { - return csGpio.second; + return csGpio.pin; } GPIO_TypeDef* SpiCookie::getChipSelectGpioPort() { - return csGpio.first; + return csGpio.port; } address_t SpiCookie::getDeviceAddress() const { diff --git a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h index f5698999..56c6e800 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h +++ b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h @@ -3,6 +3,7 @@ #include "spiDefinitions.h" #include "mspInit.h" +#include "../definitions.h" #include "fsfw/devicehandlers/CookieIF.h" @@ -20,11 +21,6 @@ class SpiCookie: public CookieIF { friend class SpiComIF; public: - /** - * Typedef for STM32 GPIO pair where the first entry is the port used (e.g. GPIOA) - * and the second entry is the pin number - */ - using GpioPair = std::pair; /** * Allows construction of a SPI cookie for a connected SPI device @@ -44,7 +40,7 @@ public: */ SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferModes transferMode, spi::MspCfgBase* mspCfg, uint32_t spiSpeed, spi::SpiModes spiMode, - size_t maxRecvSize, GpioPair csGpio = GpioPair(nullptr, 0)); + size_t maxRecvSize, stm32h7::GpioCfg csGpio = stm32h7::GpioCfg(nullptr, 0, 0)); uint16_t getChipSelectGpioPin() const; GPIO_TypeDef* getChipSelectGpioPort(); @@ -64,7 +60,7 @@ private: spi::SpiModes spiMode; spi::TransferModes transferMode; volatile spi::TransferStates transferState = spi::TransferStates::IDLE; - GpioPair csGpio; + stm32h7::GpioCfg csGpio; // The MSP configuration is cached here. Be careful when using this, it is automatically // deleted by the SPI communication interface if it is not required anymore! diff --git a/hal/src/fsfw_hal/stm32h7/spi/mspInit.cpp b/hal/src/fsfw_hal/stm32h7/spi/mspInit.cpp index 4df61f9b..b7ff2f70 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/mspInit.cpp +++ b/hal/src/fsfw_hal/stm32h7/spi/mspInit.cpp @@ -118,40 +118,40 @@ void spi::halMspInitPolling(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) { GPIO_InitTypeDef GPIO_InitStruct = {}; /*##-1- Enable peripherals and GPIO Clocks #################################*/ /* Enable GPIO TX/RX clock */ - cfg->setupMacroWrapper(); + cfg->setupCb(); /*##-2- Configure peripheral GPIO ##########################################*/ /* SPI SCK GPIO pin configuration */ - GPIO_InitStruct.Pin = cfg->sckPin; + GPIO_InitStruct.Pin = cfg->sck.pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = cfg->sckAlternateFunction; - HAL_GPIO_Init(cfg->sckPort, &GPIO_InitStruct); + GPIO_InitStruct.Alternate = cfg->sck.altFnc; + HAL_GPIO_Init(cfg->sck.port, &GPIO_InitStruct); /* SPI MISO GPIO pin configuration */ - GPIO_InitStruct.Pin = cfg->misoPin; - GPIO_InitStruct.Alternate = cfg->misoAlternateFunction; - HAL_GPIO_Init(cfg->misoPort, &GPIO_InitStruct); + GPIO_InitStruct.Pin = cfg->miso.pin; + GPIO_InitStruct.Alternate = cfg->miso.altFnc; + HAL_GPIO_Init(cfg->miso.port, &GPIO_InitStruct); /* SPI MOSI GPIO pin configuration */ - GPIO_InitStruct.Pin = cfg->mosiPin; - GPIO_InitStruct.Alternate = cfg->mosiAlternateFunction; - HAL_GPIO_Init(cfg->mosiPort, &GPIO_InitStruct); + GPIO_InitStruct.Pin = cfg->mosi.pin; + GPIO_InitStruct.Alternate = cfg->mosi.altFnc; + HAL_GPIO_Init(cfg->mosi.port, &GPIO_InitStruct); } void spi::halMspDeinitPolling(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) { auto cfg = reinterpret_cast(cfgBase); // Reset peripherals - cfg->cleanUpMacroWrapper(); + cfg->cleanupCb(); // Disable peripherals and GPIO Clocks /* Configure SPI SCK as alternate function */ - HAL_GPIO_DeInit(cfg->sckPort, cfg->sckPin); + HAL_GPIO_DeInit(cfg->sck.port, cfg->sck.pin); /* Configure SPI MISO as alternate function */ - HAL_GPIO_DeInit(cfg->misoPort, cfg->misoPin); + HAL_GPIO_DeInit(cfg->miso.port, cfg->miso.pin); /* Configure SPI MOSI as alternate function */ - HAL_GPIO_DeInit(cfg->mosiPort, cfg->mosiPin); + HAL_GPIO_DeInit(cfg->mosi.port, cfg->mosi.pin); } void spi::halMspInitInterrupt(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) { diff --git a/hal/src/fsfw_hal/stm32h7/spi/mspInit.h b/hal/src/fsfw_hal/stm32h7/spi/mspInit.h index e6de2f8e..0fb553f7 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/mspInit.h +++ b/hal/src/fsfw_hal/stm32h7/spi/mspInit.h @@ -2,6 +2,7 @@ #define FSFW_HAL_STM32H7_SPI_MSPINIT_H_ #include "spiDefinitions.h" +#include "../definitions.h" #include "../dma.h" #include "stm32h7xx_hal_spi.h" @@ -12,6 +13,8 @@ extern "C" { #endif +using mspCb = void (*) (void); + /** * @brief This file provides MSP implementation for DMA, IRQ and Polling mode for the * SPI peripheral. This configuration is required for the SPI communication to work. @@ -19,27 +22,37 @@ extern "C" { namespace spi { struct MspCfgBase { + MspCfgBase(); + MspCfgBase(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso, + mspCb cleanupCb = nullptr, mspCb setupCb = nullptr): + sck(sck), mosi(mosi), miso(miso), cleanupCb(cleanupCb), + setupCb(setupCb) {} + virtual ~MspCfgBase() = default; - void (* cleanUpMacroWrapper) (void) = nullptr; - void (* setupMacroWrapper) (void) = nullptr; + stm32h7::GpioCfg sck; + stm32h7::GpioCfg mosi; + stm32h7::GpioCfg miso; - GPIO_TypeDef* sckPort = nullptr; - uint32_t sckPin = 0; - uint8_t sckAlternateFunction = 0; - GPIO_TypeDef* mosiPort = nullptr; - uint32_t mosiPin = 0; - uint8_t mosiAlternateFunction = 0; - GPIO_TypeDef* misoPort = nullptr; - uint32_t misoPin = 0; - uint8_t misoAlternateFunction = 0; + mspCb cleanupCb = nullptr; + mspCb setupCb = nullptr; }; -struct MspPollingConfigStruct: public MspCfgBase {}; +struct MspPollingConfigStruct: public MspCfgBase { + MspPollingConfigStruct(): MspCfgBase() {}; + MspPollingConfigStruct(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso, + mspCb cleanupCb = nullptr, mspCb setupCb = nullptr): + MspCfgBase(sck, mosi, miso, cleanupCb, setupCb) {} +}; /* A valid instance of this struct must be passed to the MSP initialization function as a void* argument */ struct MspIrqConfigStruct: public MspPollingConfigStruct { + MspIrqConfigStruct(): MspPollingConfigStruct() {}; + MspIrqConfigStruct(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso, + mspCb cleanupCb = nullptr, mspCb setupCb = nullptr): + MspPollingConfigStruct(sck, mosi, miso, cleanupCb, setupCb) {} + SpiBus spiBus = SpiBus::SPI_1; user_handler_t spiIrqHandler = nullptr; user_args_t spiUserArgs = nullptr; @@ -53,11 +66,16 @@ struct MspIrqConfigStruct: public MspPollingConfigStruct { /* A valid instance of this struct must be passed to the MSP initialization function as a void* argument */ struct MspDmaConfigStruct: public MspIrqConfigStruct { + MspDmaConfigStruct(): MspIrqConfigStruct() {}; + MspDmaConfigStruct(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso, + mspCb cleanupCb = nullptr, mspCb setupCb = nullptr): + MspIrqConfigStruct(sck, mosi, miso, cleanupCb, setupCb) {} void (* dmaClkEnableWrapper) (void) = nullptr; - dma::DMAIndexes txDmaIndex; - dma::DMAIndexes rxDmaIndex; - dma::DMAStreams txDmaStream; - dma::DMAStreams rxDmaStream; + + dma::DMAIndexes txDmaIndex = dma::DMAIndexes::DMA_1; + dma::DMAIndexes rxDmaIndex = dma::DMAIndexes::DMA_1; + dma::DMAStreams txDmaStream = dma::DMAStreams::STREAM_0; + dma::DMAStreams rxDmaStream = dma::DMAStreams::STREAM_0; IRQn_Type txDmaIrqNumber = DMA1_Stream0_IRQn; IRQn_Type rxDmaIrqNumber = DMA1_Stream1_IRQn; // Priorities for NVIC diff --git a/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp b/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp index 43194704..8247d002 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp +++ b/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp @@ -23,17 +23,17 @@ void spiDmaClockEnableWrapper() { } void spi::h743zi::standardPollingCfg(MspPollingConfigStruct& cfg) { - cfg.setupMacroWrapper = &spiSetupWrapper; - cfg.cleanUpMacroWrapper = &spiCleanUpWrapper; - cfg.sckPort = GPIOA; - cfg.sckPin = GPIO_PIN_5; - cfg.misoPort = GPIOA; - cfg.misoPin = GPIO_PIN_6; - cfg.mosiPort = GPIOA; - cfg.mosiPin = GPIO_PIN_7; - cfg.sckAlternateFunction = GPIO_AF5_SPI1; - cfg.mosiAlternateFunction = GPIO_AF5_SPI1; - cfg.misoAlternateFunction = GPIO_AF5_SPI1; + cfg.setupCb = &spiSetupWrapper; + cfg.cleanupCb = &spiCleanUpWrapper; + cfg.sck.port = GPIOA; + cfg.sck.pin = GPIO_PIN_5; + cfg.miso.port = GPIOA; + cfg.miso.pin = GPIO_PIN_6; + cfg.mosi.port = GPIOA; + cfg.mosi.pin = GPIO_PIN_7; + cfg.sck.altFnc = GPIO_AF5_SPI1; + cfg.mosi.altFnc = GPIO_AF5_SPI1; + cfg.miso.altFnc = GPIO_AF5_SPI1; } void spi::h743zi::standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio, From 7c855592d05ecfc017e9806f87e40a11f8c75a8a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 27 Oct 2021 18:11:56 +0200 Subject: [PATCH 19/23] more cleaning up --- hal/src/fsfw_hal/stm32h7/devicetest/GyroL3GD20H.cpp | 8 ++++---- hal/src/fsfw_hal/stm32h7/spi/CMakeLists.txt | 2 +- .../spi/{stm32h743ziSpi.cpp => stm32h743zi.cpp} | 10 +++++----- .../stm32h7/spi/{stm32h743ziSpi.h => stm32h743zi.h} | 11 +++++------ 4 files changed, 15 insertions(+), 16 deletions(-) rename hal/src/fsfw_hal/stm32h7/spi/{stm32h743ziSpi.cpp => stm32h743zi.cpp} (88%) rename hal/src/fsfw_hal/stm32h7/spi/{stm32h743ziSpi.h => stm32h743zi.h} (64%) diff --git a/hal/src/fsfw_hal/stm32h7/devicetest/GyroL3GD20H.cpp b/hal/src/fsfw_hal/stm32h7/devicetest/GyroL3GD20H.cpp index 051be344..d1fdd1e5 100644 --- a/hal/src/fsfw_hal/stm32h7/devicetest/GyroL3GD20H.cpp +++ b/hal/src/fsfw_hal/stm32h7/devicetest/GyroL3GD20H.cpp @@ -4,7 +4,7 @@ #include "fsfw_hal/stm32h7/spi/spiDefinitions.h" #include "fsfw_hal/stm32h7/spi/spiCore.h" #include "fsfw_hal/stm32h7/spi/spiInterrupts.h" -#include "fsfw_hal/stm32h7/spi/stm32h743ziSpi.h" +#include "fsfw_hal/stm32h7/spi/stm32h743zi.h" #include "fsfw/tasks/TaskFactory.h" #include "fsfw/serviceinterface/ServiceInterface.h" @@ -33,20 +33,20 @@ GyroL3GD20H::GyroL3GD20H(SPI_HandleTypeDef *spiHandle, spi::TransferModes transf mspCfg = new spi::MspDmaConfigStruct(); auto typedCfg = dynamic_cast(mspCfg); spi::setDmaHandles(txDmaHandle, rxDmaHandle); - spi::h743zi::standardDmaCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS, + stm32h7::h743zi::standardDmaCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS, IrqPriorities::HIGHEST_FREERTOS, IrqPriorities::HIGHEST_FREERTOS); spi::setSpiDmaMspFunctions(typedCfg); } else if(transferMode == spi::TransferModes::INTERRUPT) { mspCfg = new spi::MspIrqConfigStruct(); auto typedCfg = dynamic_cast(mspCfg); - spi::h743zi::standardInterruptCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS); + stm32h7::h743zi::standardInterruptCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS); spi::setSpiIrqMspFunctions(typedCfg); } else if(transferMode == spi::TransferModes::POLLING) { mspCfg = new spi::MspPollingConfigStruct(); auto typedCfg = dynamic_cast(mspCfg); - spi::h743zi::standardPollingCfg(*typedCfg); + stm32h7::h743zi::standardPollingCfg(*typedCfg); spi::setSpiPollingMspFunctions(typedCfg); } diff --git a/hal/src/fsfw_hal/stm32h7/spi/CMakeLists.txt b/hal/src/fsfw_hal/stm32h7/spi/CMakeLists.txt index e28c35aa..aa5541bc 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/CMakeLists.txt +++ b/hal/src/fsfw_hal/stm32h7/spi/CMakeLists.txt @@ -5,5 +5,5 @@ target_sources(${LIB_FSFW_NAME} PRIVATE mspInit.cpp SpiCookie.cpp SpiComIF.cpp - stm32h743ziSpi.cpp + stm32h743zi.cpp ) diff --git a/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp b/hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.cpp similarity index 88% rename from hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp rename to hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.cpp index 8247d002..1bafccd5 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp +++ b/hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.cpp @@ -1,4 +1,4 @@ -#include "fsfw_hal/stm32h7/spi/stm32h743ziSpi.h" +#include "fsfw_hal/stm32h7/spi/stm32h743zi.h" #include "fsfw_hal/stm32h7/spi/spiCore.h" #include "fsfw_hal/stm32h7/spi/spiInterrupts.h" @@ -22,7 +22,7 @@ void spiDmaClockEnableWrapper() { __HAL_RCC_DMA2_CLK_ENABLE(); } -void spi::h743zi::standardPollingCfg(MspPollingConfigStruct& cfg) { +void stm32h7::h743zi::standardPollingCfg(spi::MspPollingConfigStruct& cfg) { cfg.setupCb = &spiSetupWrapper; cfg.cleanupCb = &spiCleanUpWrapper; cfg.sck.port = GPIOA; @@ -36,13 +36,13 @@ void spi::h743zi::standardPollingCfg(MspPollingConfigStruct& cfg) { cfg.miso.altFnc = GPIO_AF5_SPI1; } -void spi::h743zi::standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio, +void stm32h7::h743zi::standardInterruptCfg(spi::MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio, IrqPriorities spiSubprio) { // High, but works on FreeRTOS as well (priorities range from 0 to 15) cfg.preEmptPriority = spiIrqPrio; cfg.subpriority = spiSubprio; cfg.spiIrqNumber = SPI1_IRQn; - cfg.spiBus = SpiBus::SPI_1; + cfg.spiBus = spi::SpiBus::SPI_1; user_handler_t spiUserHandler = nullptr; user_args_t spiUserArgs = nullptr; getSpiUserHandler(spi::SpiBus::SPI_1, &spiUserHandler, &spiUserArgs); @@ -55,7 +55,7 @@ void spi::h743zi::standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities sp standardPollingCfg(cfg); } -void spi::h743zi::standardDmaCfg(MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio, +void stm32h7::h743zi::standardDmaCfg(spi::MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio, IrqPriorities txIrqPrio, IrqPriorities rxIrqPrio, IrqPriorities spiSubprio, IrqPriorities txSubprio, IrqPriorities rxSubprio) { cfg.dmaClkEnableWrapper = &spiDmaClockEnableWrapper; diff --git a/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.h b/hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.h similarity index 64% rename from hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.h rename to hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.h index 87689add..daa95554 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.h +++ b/hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.h @@ -3,21 +3,20 @@ #include "mspInit.h" -namespace spi { +namespace stm32h7 { namespace h743zi { -void standardPollingCfg(MspPollingConfigStruct& cfg); -void standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio, +void standardPollingCfg(spi::MspPollingConfigStruct& cfg); +void standardInterruptCfg(spi::MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio, IrqPriorities spiSubprio = HIGHEST); -void standardDmaCfg(MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio, +void standardDmaCfg(spi::MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio, IrqPriorities txIrqPrio, IrqPriorities rxIrqPrio, IrqPriorities spiSubprio = HIGHEST, IrqPriorities txSubPrio = HIGHEST, IrqPriorities rxSubprio = HIGHEST); + } } - - #endif /* FSFW_HAL_STM32H7_SPI_STM32H743ZISPI_H_ */ From 36aaf3d75800db7e2d9f7229fdd95068fdb17481 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Wed, 27 Oct 2021 20:41:04 +0200 Subject: [PATCH 20/23] say hi to my new friend valgrind --- tests/src/fsfw_tests/unit/container/RingBufferTest.cpp | 4 ++-- .../fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/src/fsfw_tests/unit/container/RingBufferTest.cpp b/tests/src/fsfw_tests/unit/container/RingBufferTest.cpp index 819401ab..0be8b2a7 100644 --- a/tests/src/fsfw_tests/unit/container/RingBufferTest.cpp +++ b/tests/src/fsfw_tests/unit/container/RingBufferTest.cpp @@ -78,7 +78,7 @@ TEST_CASE("Ring Buffer Test" , "[RingBufferTest]") { TEST_CASE("Ring Buffer Test2" , "[RingBufferTest2]") { uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; - uint8_t* newBuffer = new uint8_t[10]; + uint8_t* newBuffer = new uint8_t[15]; SimpleRingBuffer ringBuffer(newBuffer, 10, true, 5); SECTION("Simple Test") { @@ -168,7 +168,7 @@ TEST_CASE("Ring Buffer Test2" , "[RingBufferTest2]") { TEST_CASE("Ring Buffer Test3" , "[RingBufferTest3]") { uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; - uint8_t* newBuffer = new uint8_t[10]; + uint8_t* newBuffer = new uint8_t[25]; SimpleRingBuffer ringBuffer(newBuffer, 10, true, 15); SECTION("Simple Test") { diff --git a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp index 7b2f9412..b1160254 100644 --- a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp +++ b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp @@ -143,7 +143,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { CHECK(cdsShort.msDay_h == Catch::Approx(timeCdsNow.msDay_h).margin(1)); CHECK(cdsShort.msDay_hh == Catch::Approx(timeCdsNow.msDay_hh).margin(1)); CHECK(cdsShort.msDay_l == Catch::Approx(timeCdsNow.msDay_l).margin(1)); - CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1)); + CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(5)); } SECTION("VariableSnapshotTest") { @@ -205,7 +205,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { CHECK(cdsShort.msDay_h == Catch::Approx(timeCdsNow.msDay_h).margin(1)); CHECK(cdsShort.msDay_hh == Catch::Approx(timeCdsNow.msDay_hh).margin(1)); CHECK(cdsShort.msDay_l == Catch::Approx(timeCdsNow.msDay_l).margin(1)); - CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1)); + CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(5)); } SECTION("VariableNotificationTest") { From a53992fdc9127c435f7bdf1a68cdf1329aab31ff Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Wed, 27 Oct 2021 21:32:40 +0200 Subject: [PATCH 21/23] introducing valgrind --- automation/Dockerfile | 2 +- automation/Jenkinsfile | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/automation/Dockerfile b/automation/Dockerfile index 0526d8f0..93a4fe7d 100644 --- a/automation/Dockerfile +++ b/automation/Dockerfile @@ -5,4 +5,4 @@ RUN apt-get --yes upgrade #tzdata is a dependency, won't install otherwise ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get --yes install gcc g++ cmake lcov git nano +RUN apt-get --yes install gcc g++ cmake make lcov git valgrind nano diff --git a/automation/Jenkinsfile b/automation/Jenkinsfile index 0cb973bd..d4a8e2ab 100644 --- a/automation/Jenkinsfile +++ b/automation/Jenkinsfile @@ -55,5 +55,18 @@ pipeline { } } } + stage('Valgrind') { + agent { + dockerfile { + dir 'automation' + reuseNode true + } + } + steps { + dir(BUILDDIR) { + sh 'valgrind --leak-check=full --error-exitcode=1 ./fsfw-tests' + } + } + } } } From 6d5eb5b387b05c8c9615de0544bd0fc355137a52 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 10 Nov 2021 18:48:02 +0100 Subject: [PATCH 22/23] Op Divider and bitutility updates - Added unittests for `PeriodicOperationDivider` and the `bitutil` helpers - Some API changes: Removed redundant bit part, because these functions are already in a namespace - Some bugfixes for `PeriodicOperationDivider` --- .../devicehandlers/MgmRM3100Handler.cpp | 2 +- .../datapoollocal/LocalPoolDataSetBase.cpp | 6 +- .../PeriodicOperationDivider.cpp | 39 +++++----- .../PeriodicOperationDivider.h | 77 +++++++++---------- src/fsfw/globalfunctions/bitutility.cpp | 11 +-- src/fsfw/globalfunctions/bitutility.h | 37 +++++++-- .../unit/datapoollocal/DataSetTest.cpp | 24 ++++-- .../unit/globalfunctions/CMakeLists.txt | 2 + .../unit/globalfunctions/testBitutil.cpp | 64 +++++++++++++++ .../unit/globalfunctions/testOpDivider.cpp | 64 +++++++++++++++ 10 files changed, 242 insertions(+), 84 deletions(-) create mode 100644 tests/src/fsfw_tests/unit/globalfunctions/testBitutil.cpp create mode 100644 tests/src/fsfw_tests/unit/globalfunctions/testOpDivider.cpp diff --git a/hal/src/fsfw_hal/devicehandlers/MgmRM3100Handler.cpp b/hal/src/fsfw_hal/devicehandlers/MgmRM3100Handler.cpp index 124eebbc..db4ea607 100644 --- a/hal/src/fsfw_hal/devicehandlers/MgmRM3100Handler.cpp +++ b/hal/src/fsfw_hal/devicehandlers/MgmRM3100Handler.cpp @@ -186,7 +186,7 @@ ReturnValue_t MgmRM3100Handler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t cmmValue = packet[1]; // We clear the seventh bit in any case // because this one is zero sometimes for some reason - bitutil::bitClear(&cmmValue, 6); + bitutil::clear(&cmmValue, 6); if(cmmValue == cmmRegValue and internalState == InternalState::READ_CMM) { commandExecuted = true; } diff --git a/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp b/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp index 5422c68a..d1ac0c7f 100644 --- a/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp +++ b/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp @@ -110,7 +110,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 */ - bitutil::bitSet(validityPtr + validBufferIndex, validBufferIndexBit); + bitutil::set(validityPtr + validBufferIndex, validBufferIndexBit); } if(validBufferIndexBit == 7) { validBufferIndex ++; @@ -156,8 +156,8 @@ ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer( uint8_t validBufferIndexBit = 0; for (uint16_t count = 0; count < fillCount; count++) { // set validity buffer here. - bool nextVarValid = bitutil::bitGet(*buffer + - validBufferIndex, validBufferIndexBit); + bool nextVarValid = false; + bitutil::get(*buffer + validBufferIndex, validBufferIndexBit, nextVarValid); registeredVariables[count]->setValid(nextVarValid); if(validBufferIndexBit == 7) { diff --git a/src/fsfw/globalfunctions/PeriodicOperationDivider.cpp b/src/fsfw/globalfunctions/PeriodicOperationDivider.cpp index 62cc6f4c..ac6a78e4 100644 --- a/src/fsfw/globalfunctions/PeriodicOperationDivider.cpp +++ b/src/fsfw/globalfunctions/PeriodicOperationDivider.cpp @@ -2,43 +2,40 @@ PeriodicOperationDivider::PeriodicOperationDivider(uint32_t divider, - bool resetAutomatically): resetAutomatically(resetAutomatically), - counter(divider), divider(divider) { + bool resetAutomatically): resetAutomatically(resetAutomatically), + divider(divider) { } bool PeriodicOperationDivider::checkAndIncrement() { - bool opNecessary = check(); - if(opNecessary) { - if(resetAutomatically) { - counter = 0; - } - return opNecessary; - } - counter ++; - return opNecessary; + bool opNecessary = check(); + if(opNecessary and resetAutomatically) { + resetCounter(); + } + else { + counter++; + } + return opNecessary; } bool PeriodicOperationDivider::check() { - if(counter >= divider) { - return true; - } - return false; + if(counter >= divider) { + return true; + } + return false; } - - void PeriodicOperationDivider::resetCounter() { - counter = 0; + counter = 1; } void PeriodicOperationDivider::setDivider(uint32_t newDivider) { - divider = newDivider; + divider = newDivider; } uint32_t PeriodicOperationDivider::getCounter() const { - return counter; + return counter; } uint32_t PeriodicOperationDivider::getDivider() const { - return divider; + return divider; } diff --git a/src/fsfw/globalfunctions/PeriodicOperationDivider.h b/src/fsfw/globalfunctions/PeriodicOperationDivider.h index 7f7fb469..636849c0 100644 --- a/src/fsfw/globalfunctions/PeriodicOperationDivider.h +++ b/src/fsfw/globalfunctions/PeriodicOperationDivider.h @@ -13,51 +13,50 @@ */ class PeriodicOperationDivider { public: - /** - * Initialize with the desired divider and specify whether the internal - * counter will be reset automatically. - * @param divider - * @param resetAutomatically - */ - PeriodicOperationDivider(uint32_t divider, bool resetAutomatically = true); + /** + * Initialize with the desired divider and specify whether the internal + * counter will be reset automatically. + * @param divider Value of 0 or 1 will cause #check and #checkAndIncrement to always return + * true + * @param resetAutomatically + */ + PeriodicOperationDivider(uint32_t divider, bool resetAutomatically = true); + /** + * Check whether operation is necessary. If an operation is necessary and the class has been + * configured to be reset automatically, the counter will be reset to 1 automatically + * + * @return + * -@c true if the counter is larger or equal to the divider + * -@c false otherwise + */ + bool checkAndIncrement(); - /** - * Check whether operation is necessary. - * If an operation is necessary and the class has been - * configured to be reset automatically, the counter will be reset. - * - * @return - * -@c true if the counter is larger or equal to the divider - * -@c false otherwise - */ - bool checkAndIncrement(); + /** + * Checks whether an operation is necessary. This function will not increment the counter. + * @return + * -@c true if the counter is larger or equal to the divider + * -@c false otherwise + */ + bool check(); - /** - * Checks whether an operation is necessary. - * This function will not increment the counter! - * @return - * -@c true if the counter is larger or equal to the divider - * -@c false otherwise - */ - bool check(); + /** + * Can be used to reset the counter to 1 manually + */ + void resetCounter(); + uint32_t getCounter() const; - /** - * Can be used to reset the counter to 0 manually. - */ - void resetCounter(); - uint32_t getCounter() const; + /** + * Can be used to set a new divider value. + * @param newDivider + */ + void setDivider(uint32_t newDivider); + uint32_t getDivider() const; - /** - * Can be used to set a new divider value. - * @param newDivider - */ - void setDivider(uint32_t newDivider); - uint32_t getDivider() const; private: - bool resetAutomatically = true; - uint32_t counter = 0; - uint32_t divider = 0; + bool resetAutomatically = true; + uint32_t counter = 1; + uint32_t divider = 0; }; diff --git a/src/fsfw/globalfunctions/bitutility.cpp b/src/fsfw/globalfunctions/bitutility.cpp index 628e30c2..54e94a95 100644 --- a/src/fsfw/globalfunctions/bitutility.cpp +++ b/src/fsfw/globalfunctions/bitutility.cpp @@ -1,6 +1,6 @@ #include "fsfw/globalfunctions/bitutility.h" -void bitutil::bitSet(uint8_t *byte, uint8_t position) { +void bitutil::set(uint8_t *byte, uint8_t position) { if(position > 7) { return; } @@ -8,7 +8,7 @@ void bitutil::bitSet(uint8_t *byte, uint8_t position) { *byte |= 1 << shiftNumber; } -void bitutil::bitToggle(uint8_t *byte, uint8_t position) { +void bitutil::toggle(uint8_t *byte, uint8_t position) { if(position > 7) { return; } @@ -16,7 +16,7 @@ void bitutil::bitToggle(uint8_t *byte, uint8_t position) { *byte ^= 1 << shiftNumber; } -void bitutil::bitClear(uint8_t *byte, uint8_t position) { +void bitutil::clear(uint8_t *byte, uint8_t position) { if(position > 7) { return; } @@ -24,10 +24,11 @@ void bitutil::bitClear(uint8_t *byte, uint8_t position) { *byte &= ~(1 << shiftNumber); } -bool bitutil::bitGet(const uint8_t *byte, uint8_t position) { +bool bitutil::get(const uint8_t *byte, uint8_t position, bool& bit) { if(position > 7) { return false; } uint8_t shiftNumber = position + (7 - 2 * position); - return *byte & (1 << shiftNumber); + bit = *byte & (1 << shiftNumber); + return true; } diff --git a/src/fsfw/globalfunctions/bitutility.h b/src/fsfw/globalfunctions/bitutility.h index 1fc1290d..00f19310 100644 --- a/src/fsfw/globalfunctions/bitutility.h +++ b/src/fsfw/globalfunctions/bitutility.h @@ -5,13 +5,36 @@ 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); +// 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) + +/** + * @brief Set the bit in a given byte + * @param byte + * @param position + */ +void set(uint8_t* byte, uint8_t position); +/** + * @brief Toggle the bit in a given byte + * @param byte + * @param position + */ +void toggle(uint8_t* byte, uint8_t position); +/** + * @brief Clear the bit in a given byte + * @param byte + * @param position + */ +void clear(uint8_t* byte, uint8_t position); +/** + * @brief Get the bit in a given byte + * @param byte + * @param position + * @param If the input is valid, this will be set to true if the bit is set and false otherwise. + * @return False if position is invalid, True otherwise + */ +bool get(const uint8_t* byte, uint8_t position, bool& bit); } diff --git a/tests/src/fsfw_tests/unit/datapoollocal/DataSetTest.cpp b/tests/src/fsfw_tests/unit/datapoollocal/DataSetTest.cpp index c967b241..e84f07b6 100644 --- a/tests/src/fsfw_tests/unit/datapoollocal/DataSetTest.cpp +++ b/tests/src/fsfw_tests/unit/datapoollocal/DataSetTest.cpp @@ -171,14 +171,19 @@ TEST_CASE("DataSetTest" , "[DataSetTest]") { /* We can do it like this because the buffer only has one byte for less than 8 variables */ uint8_t* validityByte = buffer + sizeof(buffer) - 1; - CHECK(bitutil::bitGet(validityByte, 0) == true); - CHECK(bitutil::bitGet(validityByte, 1) == false); - CHECK(bitutil::bitGet(validityByte, 2) == true); + bool bitSet = false; + bitutil::get(validityByte, 0, bitSet); + + CHECK(bitSet == true); + bitutil::get(validityByte, 1, bitSet); + CHECK(bitSet == false); + bitutil::get(validityByte, 2, bitSet); + CHECK(bitSet == true); /* Now we manipulate the validity buffer for the deserialization */ - bitutil::bitClear(validityByte, 0); - bitutil::bitSet(validityByte, 1); - bitutil::bitClear(validityByte, 2); + bitutil::clear(validityByte, 0); + bitutil::set(validityByte, 1); + bitutil::clear(validityByte, 2); /* Zero out everything except validity buffer */ std::memset(buffer, 0, sizeof(buffer) - 1); sizeToDeserialize = maxSize; @@ -239,8 +244,11 @@ TEST_CASE("DataSetTest" , "[DataSetTest]") { std::memcpy(validityBuffer.data(), buffer + 9 + sizeof(uint16_t) * 3, 2); /* The first 9 variables should be valid */ CHECK(validityBuffer[0] == 0xff); - CHECK(bitutil::bitGet(validityBuffer.data() + 1, 0) == true); - CHECK(bitutil::bitGet(validityBuffer.data() + 1, 1) == false); + bool bitSet = false; + bitutil::get(validityBuffer.data() + 1, 0, bitSet); + CHECK(bitSet == true); + bitutil::get(validityBuffer.data() + 1, 1, bitSet); + CHECK(bitSet == false); /* Now we invert the validity */ validityBuffer[0] = 0; diff --git a/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt b/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt index 209ce75f..3b29f23f 100644 --- a/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt @@ -1,3 +1,5 @@ target_sources(${FSFW_TEST_TGT} PRIVATE testDleEncoder.cpp + testOpDivider.cpp + testBitutil.cpp ) diff --git a/tests/src/fsfw_tests/unit/globalfunctions/testBitutil.cpp b/tests/src/fsfw_tests/unit/globalfunctions/testBitutil.cpp new file mode 100644 index 00000000..2627adcf --- /dev/null +++ b/tests/src/fsfw_tests/unit/globalfunctions/testBitutil.cpp @@ -0,0 +1,64 @@ +#include "fsfw/globalfunctions/bitutility.h" +#include + +TEST_CASE("Bitutility" , "[Bitutility]") { + uint8_t dummyByte = 0; + bool bitSet = false; + for(uint8_t pos = 0; pos < 8; pos++) { + bitutil::set(&dummyByte, pos); + REQUIRE(dummyByte == (1 << (7 - pos))); + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == 1); + dummyByte = 0; + } + + dummyByte = 0xff; + for(uint8_t pos = 0; pos < 8; pos++) { + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == 1); + bitutil::clear(&dummyByte, pos); + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == 0); + dummyByte = 0xff; + } + + dummyByte = 0xf0; + for(uint8_t pos = 0; pos < 8; pos++) { + if(pos < 4) { + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == 1); + bitutil::toggle(&dummyByte, pos); + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == 0); + } + else { + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == false); + bitutil::toggle(&dummyByte, pos); + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == true); + } + } + REQUIRE(dummyByte == 0x0f); + + dummyByte = 0; + bitutil::set(&dummyByte, 8); + REQUIRE(dummyByte == 0); + bitutil::set(&dummyByte, -1); + REQUIRE(dummyByte == 0); + dummyByte = 0xff; + bitutil::clear(&dummyByte, 8); + REQUIRE(dummyByte == 0xff); + bitutil::clear(&dummyByte, -1); + REQUIRE(dummyByte == 0xff); + dummyByte = 0x00; + bitutil::toggle(&dummyByte, 8); + REQUIRE(dummyByte == 0x00); + bitutil::toggle(&dummyByte, -1); + REQUIRE(dummyByte == 0x00); + + REQUIRE(bitutil::get(&dummyByte, 8, bitSet) == false); +} + + + diff --git a/tests/src/fsfw_tests/unit/globalfunctions/testOpDivider.cpp b/tests/src/fsfw_tests/unit/globalfunctions/testOpDivider.cpp new file mode 100644 index 00000000..c02ada83 --- /dev/null +++ b/tests/src/fsfw_tests/unit/globalfunctions/testOpDivider.cpp @@ -0,0 +1,64 @@ +#include "fsfw/globalfunctions/PeriodicOperationDivider.h" +#include + +TEST_CASE("OpDivider" , "[OpDivider]") { + auto opDivider = PeriodicOperationDivider(1); + REQUIRE(opDivider.getDivider() == 1); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.check() == true); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.check() == true); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.checkAndIncrement() == true); + + opDivider.setDivider(0); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.checkAndIncrement() == true); + + opDivider.setDivider(2); + opDivider.resetCounter(); + REQUIRE(opDivider.getDivider() == 2); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.check() == false); + REQUIRE(opDivider.checkAndIncrement() == false); + REQUIRE(opDivider.getCounter() == 2); + REQUIRE(opDivider.check() == true); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.check() == false); + REQUIRE(opDivider.checkAndIncrement() == false); + REQUIRE(opDivider.getCounter() == 2); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.checkAndIncrement() == false); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.checkAndIncrement() == false); + + opDivider.setDivider(3); + opDivider.resetCounter(); + REQUIRE(opDivider.checkAndIncrement() == false); + REQUIRE(opDivider.checkAndIncrement() == false); + REQUIRE(opDivider.getCounter() == 3); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.checkAndIncrement() == false); + + auto opDividerNonResetting = PeriodicOperationDivider(2, false); + REQUIRE(opDividerNonResetting.getCounter() == 1); + REQUIRE(opDividerNonResetting.check() == false); + REQUIRE(opDividerNonResetting.checkAndIncrement() == false); + REQUIRE(opDividerNonResetting.getCounter() == 2); + REQUIRE(opDividerNonResetting.check() == true); + REQUIRE(opDividerNonResetting.checkAndIncrement() == true); + REQUIRE(opDividerNonResetting.getCounter() == 3); + REQUIRE(opDividerNonResetting.checkAndIncrement() == true); + REQUIRE(opDividerNonResetting.getCounter() == 4); + opDividerNonResetting.resetCounter(); + REQUIRE(opDividerNonResetting.getCounter() == 1); + REQUIRE(opDividerNonResetting.check() == false); + REQUIRE(opDividerNonResetting.checkAndIncrement() == false); + REQUIRE(opDividerNonResetting.getCounter() == 2); +} From 0176c07886822b697496bff1afea906b451ac75a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 10 Nov 2021 18:49:29 +0100 Subject: [PATCH 23/23] use IF instead of void pointer --- src/fsfw/memory/FileSystemArgsIF.h | 13 +++++++++++++ src/fsfw/memory/HasFileSystemIF.h | 27 +++++++++++++++++---------- 2 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 src/fsfw/memory/FileSystemArgsIF.h diff --git a/src/fsfw/memory/FileSystemArgsIF.h b/src/fsfw/memory/FileSystemArgsIF.h new file mode 100644 index 00000000..67d423ff --- /dev/null +++ b/src/fsfw/memory/FileSystemArgsIF.h @@ -0,0 +1,13 @@ +#ifndef FSFW_SRC_FSFW_MEMORY_FILESYSTEMARGS_H_ +#define FSFW_SRC_FSFW_MEMORY_FILESYSTEMARGS_H_ + +/** + * Empty base interface which can be implemented by to pass arguments via the HasFileSystemIF. + * Users can then dynamic_cast the base pointer to the require child pointer. + */ +class FileSystemArgsIF { +public: + virtual~ FileSystemArgsIF() {}; +}; + +#endif /* FSFW_SRC_FSFW_MEMORY_FILESYSTEMARGS_H_ */ diff --git a/src/fsfw/memory/HasFileSystemIF.h b/src/fsfw/memory/HasFileSystemIF.h index ec941f59..be2f8923 100644 --- a/src/fsfw/memory/HasFileSystemIF.h +++ b/src/fsfw/memory/HasFileSystemIF.h @@ -1,9 +1,10 @@ #ifndef FSFW_MEMORY_HASFILESYSTEMIF_H_ #define FSFW_MEMORY_HASFILESYSTEMIF_H_ -#include "../returnvalues/HasReturnvaluesIF.h" -#include "../returnvalues/FwClassIds.h" -#include "../ipc/messageQueueDefinitions.h" +#include "FileSystemArgsIF.h" +#include "fsfw/returnvalues/HasReturnvaluesIF.h" +#include "fsfw/returnvalues/FwClassIds.h" +#include "fsfw/ipc/messageQueueDefinitions.h" #include @@ -59,7 +60,7 @@ public: */ virtual ReturnValue_t appendToFile(const char* repositoryPath, const char* filename, const uint8_t* data, size_t size, - uint16_t packetNumber, void* args = nullptr) = 0; + uint16_t packetNumber, FileSystemArgsIF* args = nullptr) = 0; /** * @brief Generic function to create a new file. @@ -72,7 +73,7 @@ public: */ virtual ReturnValue_t createFile(const char* repositoryPath, const char* filename, const uint8_t* data = nullptr, - size_t size = 0, void* args = nullptr) = 0; + size_t size = 0, FileSystemArgsIF* args = nullptr) = 0; /** * @brief Generic function to delete a file. @@ -81,24 +82,30 @@ public: * @param args Any other arguments which an implementation might require * @return */ - virtual ReturnValue_t deleteFile(const char* repositoryPath, - const char* filename, void* args = nullptr) = 0; + virtual ReturnValue_t removeFile(const char* repositoryPath, + const char* filename, FileSystemArgsIF* args = nullptr) = 0; /** * @brief Generic function to create a directory * @param repositoryPath + * @param Equivalent to the -p flag in Unix systems. If some required parent directories + * do not exist, create them as well * @param args Any other arguments which an implementation might require * @return */ - virtual ReturnValue_t createDirectory(const char* repositoryPath, void* args = nullptr) = 0; + virtual ReturnValue_t createDirectory(const char* repositoryPath, const char* dirname, + bool createParentDirs, FileSystemArgsIF* args = nullptr) = 0; /** * @brief Generic function to remove a directory * @param repositoryPath * @param args Any other arguments which an implementation might require */ - virtual ReturnValue_t removeDirectory(const char* repositoryPath, - bool deleteRecurively = false, void* args = nullptr) = 0; + virtual ReturnValue_t removeDirectory(const char* repositoryPath, const char* dirname, + bool deleteRecurively = false, FileSystemArgsIF* args = nullptr) = 0; + + virtual ReturnValue_t renameFile(const char* repositoryPath, const char* oldFilename, + const char* newFilename, FileSystemArgsIF* args = nullptr) = 0; };