From e3fb9d308c6b071124ab391206d316cdeb9e813a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 13 Oct 2021 10:46:45 +0200 Subject: [PATCH 1/3] added integration test modules --- tests/src/fsfw_tests/CMakeLists.txt | 3 + .../src/fsfw_tests/integration/CMakeLists.txt | 2 + .../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 +++ 14 files changed, 1519 insertions(+) create mode 100644 tests/src/fsfw_tests/integration/CMakeLists.txt 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 diff --git a/tests/src/fsfw_tests/CMakeLists.txt b/tests/src/fsfw_tests/CMakeLists.txt index e4a6be80..92cc837d 100644 --- a/tests/src/fsfw_tests/CMakeLists.txt +++ b/tests/src/fsfw_tests/CMakeLists.txt @@ -1,3 +1,6 @@ +if(FSFW_ADD_INTEGRATION_TEST_MODULES) + add_subdirectory(integration) +endif() 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..c38caaee --- /dev/null +++ b/tests/src/fsfw_tests/integration/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(devices) +add_subdirectory(controller) 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..aa76e68d --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestCookie.cpp @@ -0,0 +1,14 @@ +#include "TestCookie.h" + +DummyCookie::DummyCookie(address_t address, size_t replyMaxLen): + address(address), replyMaxLen(replyMaxLen) {} + +DummyCookie::~DummyCookie() {} + +address_t DummyCookie::getAddress() const { + return address; +} + +size_t DummyCookie::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..b954df5d --- /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 DummyCookie: public CookieIF { +public: + DummyCookie(address_t address, size_t maxReplyLen); + virtual ~DummyCookie(); + + 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..0a1e6ca7 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp @@ -0,0 +1,804 @@ +#include "TestDeviceHandler.h" +#include + +#include + +#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..96ac2fad --- /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 +#include +#include + +/** + * @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 */ + static constexpr uint8_t divider1 = 2; + PeriodicOperationDivider opDivider1 = PeriodicOperationDivider(divider1); + static constexpr 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..bb4871f4 --- /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) { + DummyCookie* 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) { + DummyCookie* 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) { + DummyCookie* 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_ */ -- 2.43.0 From c0d560afe4d7cdfb4ac63ef6c726a2b3ce344d03 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 13 Oct 2021 10:47:59 +0200 Subject: [PATCH 2/3] added option to add/not add integration test modules --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ff66a6d..5613e753 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ endif() option(FSFW_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON) # Options to exclude parts of the FSFW from compilation. +option(FSFW_ADD_INTEGRATION_TEST_MODULES "Add modules which can be used for integration tests" OFF) option(FSFW_ADD_INTERNAL_TESTS "Add internal unit tests" ON) option(FSFW_ADD_UNITTESTS "Add regular unittests. Requires Catch2" OFF) option(FSFW_ADD_HAL "Add Hardware Abstraction Layer" ON) -- 2.43.0 From 6978869a42585d32d126d06b0c36996ac990bb2f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 13 Oct 2021 11:35:57 +0200 Subject: [PATCH 3/3] moved examples modules to fsfw --- CMakeLists.txt | 9 +- examples/CMakeLists.txt | 9 + examples/fsfw_examples/CMakeLists.txt | 7 + .../fsfw_examples/assemblies/CMakeLists.txt | 3 + .../fsfw_examples/assemblies/TestAssembly.cpp | 200 +++++++++++++ .../fsfw_examples/assemblies/TestAssembly.h | 53 ++++ examples/fsfw_examples/config/CMakeLists.txt | 11 + .../fsfw_examples/config/ExampleConfig.h.in | 67 +++++ .../fsfw_examples/config/exampleClassIds.h | 14 + .../fsfw_examples/config/exampleObjectIds.h | 32 +++ .../config/examplePollingSequenceFactory.cpp | 82 ++++++ .../config/exampleSubsystemIds.h | 15 + .../fsfw_examples}/controller/CMakeLists.txt | 0 .../controller/TestController.cpp | 0 .../controller/TestController.h | 0 .../ctrldefinitions/testCtrlDefinitions.h | 0 examples/fsfw_examples/core/CMakeLists.txt | 3 + .../fsfw_examples/core/GenericFactory.cpp | 125 +++++++++ examples/fsfw_examples/core/GenericFactory.h | 17 ++ .../fsfw_examples}/devices/CMakeLists.txt | 0 .../fsfw_examples}/devices/TestCookie.cpp | 0 .../fsfw_examples}/devices/TestCookie.h | 0 .../devices/TestDeviceHandler.cpp | 2 +- .../devices/TestDeviceHandler.h | 0 .../fsfw_examples}/devices/TestEchoComIF.cpp | 0 .../fsfw_examples}/devices/TestEchoComIF.h | 0 .../devicedefinitions/testDeviceDefinitions.h | 2 +- examples/fsfw_examples/test/CMakeLists.txt | 7 + .../fsfw_examples/test/FsfwExampleTask.cpp | 264 ++++++++++++++++++ examples/fsfw_examples/test/FsfwExampleTask.h | 116 ++++++++ .../fsfw_examples/test/FsfwReaderTask.cpp | 55 ++++ examples/fsfw_examples/test/FsfwReaderTask.h | 24 ++ examples/fsfw_examples/test/MutexExample.cpp | 47 ++++ examples/fsfw_examples/test/MutexExample.h | 8 + examples/fsfw_examples/test/TestTask.cpp | 80 ++++++ examples/fsfw_examples/test/TestTask.h | 56 ++++ .../test/testdefinitions/demoDefinitions.h | 63 +++++ examples/fsfw_examples/utility/CMakeLists.txt | 4 + examples/fsfw_examples/utility/TaskCreation.h | 22 ++ examples/fsfw_examples/utility/TmFunnel.cpp | 121 ++++++++ examples/fsfw_examples/utility/TmFunnel.h | 51 ++++ examples/fsfw_examples/utility/compile_time.h | 97 +++++++ examples/fsfw_examples/utility/utility.cpp | 22 ++ examples/fsfw_examples/utility/utility.h | 11 + tests/src/fsfw_tests/CMakeLists.txt | 4 - .../src/fsfw_tests/integration/CMakeLists.txt | 2 - 46 files changed, 1696 insertions(+), 9 deletions(-) create mode 100644 examples/CMakeLists.txt create mode 100644 examples/fsfw_examples/CMakeLists.txt create mode 100644 examples/fsfw_examples/assemblies/CMakeLists.txt create mode 100644 examples/fsfw_examples/assemblies/TestAssembly.cpp create mode 100644 examples/fsfw_examples/assemblies/TestAssembly.h create mode 100644 examples/fsfw_examples/config/CMakeLists.txt create mode 100644 examples/fsfw_examples/config/ExampleConfig.h.in create mode 100644 examples/fsfw_examples/config/exampleClassIds.h create mode 100644 examples/fsfw_examples/config/exampleObjectIds.h create mode 100644 examples/fsfw_examples/config/examplePollingSequenceFactory.cpp create mode 100644 examples/fsfw_examples/config/exampleSubsystemIds.h rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/controller/CMakeLists.txt (100%) rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/controller/TestController.cpp (100%) rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/controller/TestController.h (100%) rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/controller/ctrldefinitions/testCtrlDefinitions.h (100%) create mode 100644 examples/fsfw_examples/core/CMakeLists.txt create mode 100644 examples/fsfw_examples/core/GenericFactory.cpp create mode 100644 examples/fsfw_examples/core/GenericFactory.h rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/devices/CMakeLists.txt (100%) rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/devices/TestCookie.cpp (100%) rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/devices/TestCookie.h (100%) rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/devices/TestDeviceHandler.cpp (99%) rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/devices/TestDeviceHandler.h (100%) rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/devices/TestEchoComIF.cpp (100%) rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/devices/TestEchoComIF.h (100%) rename {tests/src/fsfw_tests/integration => examples/fsfw_examples}/devices/devicedefinitions/testDeviceDefinitions.h (99%) create mode 100644 examples/fsfw_examples/test/CMakeLists.txt create mode 100644 examples/fsfw_examples/test/FsfwExampleTask.cpp create mode 100644 examples/fsfw_examples/test/FsfwExampleTask.h create mode 100644 examples/fsfw_examples/test/FsfwReaderTask.cpp create mode 100644 examples/fsfw_examples/test/FsfwReaderTask.h create mode 100644 examples/fsfw_examples/test/MutexExample.cpp create mode 100644 examples/fsfw_examples/test/MutexExample.h create mode 100644 examples/fsfw_examples/test/TestTask.cpp create mode 100644 examples/fsfw_examples/test/TestTask.h create mode 100644 examples/fsfw_examples/test/testdefinitions/demoDefinitions.h create mode 100644 examples/fsfw_examples/utility/CMakeLists.txt create mode 100644 examples/fsfw_examples/utility/TaskCreation.h create mode 100644 examples/fsfw_examples/utility/TmFunnel.cpp create mode 100644 examples/fsfw_examples/utility/TmFunnel.h create mode 100644 examples/fsfw_examples/utility/compile_time.h create mode 100644 examples/fsfw_examples/utility/utility.cpp create mode 100644 examples/fsfw_examples/utility/utility.h delete mode 100644 tests/src/fsfw_tests/integration/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 5613e753..668b05d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ endif() option(FSFW_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON) # Options to exclude parts of the FSFW from compilation. -option(FSFW_ADD_INTEGRATION_TEST_MODULES "Add modules which can be used for integration tests" OFF) +option(FSFW_ADD_EXAMPLES "Add example modules which can also be used as test code" OFF) option(FSFW_ADD_INTERNAL_TESTS "Add internal unit tests" ON) option(FSFW_ADD_UNITTESTS "Add regular unittests. Requires Catch2" OFF) option(FSFW_ADD_HAL "Add Hardware Abstraction Layer" ON) @@ -30,6 +30,10 @@ set(LIB_FSFW_NAME fsfw) add_library(${LIB_FSFW_NAME}) set(FSFW_CORE_INC_PATH "inc") +if(FSFW_ADD_EXAMPLES) + configure_file(examples/fsfw_examples/config/ExampleConfig.h.in ExampleConfig.h) +endif() + set_property(CACHE FSFW_OSAL PROPERTY STRINGS host linux rtems freertos) if(NOT CMAKE_CXX_STANDARD) @@ -93,6 +97,9 @@ add_subdirectory(tests) if(FSFW_ADD_HAL) add_subdirectory(hal) endif() +if(FSFW_ADD_EXAMPLES) + add_subdirectory(examples) +endif() add_subdirectory(contrib) # The project CMakeLists file has to set the FSFW_CONFIG_PATH and add it. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..6ef99c81 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,9 @@ +add_subdirectory(fsfw_examples) + +target_include_directories(${LIB_FSFW_NAME} INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_include_directories(${LIB_FSFW_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/examples/fsfw_examples/CMakeLists.txt b/examples/fsfw_examples/CMakeLists.txt new file mode 100644 index 00000000..3d817cb7 --- /dev/null +++ b/examples/fsfw_examples/CMakeLists.txt @@ -0,0 +1,7 @@ +add_subdirectory(devices) +add_subdirectory(controller) +add_subdirectory(config) +add_subdirectory(assemblies) +add_subdirectory(utility) +add_subdirectory(test) +add_subdirectory(core) diff --git a/examples/fsfw_examples/assemblies/CMakeLists.txt b/examples/fsfw_examples/assemblies/CMakeLists.txt new file mode 100644 index 00000000..d869b9df --- /dev/null +++ b/examples/fsfw_examples/assemblies/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${TARGET_NAME} PRIVATE + TestAssembly.cpp +) \ No newline at end of file diff --git a/examples/fsfw_examples/assemblies/TestAssembly.cpp b/examples/fsfw_examples/assemblies/TestAssembly.cpp new file mode 100644 index 00000000..71846aac --- /dev/null +++ b/examples/fsfw_examples/assemblies/TestAssembly.cpp @@ -0,0 +1,200 @@ +#include "exampleObjectIds.h" + +#include +#include + + +TestAssembly::TestAssembly(object_id_t objectId, object_id_t parentId): + AssemblyBase(objectId, parentId) { + ModeListEntry newModeListEntry; + newModeListEntry.setObject(objects::TEST_DEVICE_HANDLER_0); + newModeListEntry.setMode(MODE_OFF); + newModeListEntry.setSubmode(SUBMODE_NONE); + + commandTable.insert(newModeListEntry); + + newModeListEntry.setObject(objects::TEST_DEVICE_HANDLER_1); + 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(objects::TEST_DEVICE_HANDLER_0)) { + if (childrenMap[objects::TEST_DEVICE_HANDLER_0].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[objects::TEST_DEVICE_HANDLER_1].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[objects::TEST_DEVICE_HANDLER_0].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[objects::TEST_DEVICE_HANDLER_1].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(objects::TEST_DEVICE_HANDLER_0)){ + 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(objects::TEST_DEVICE_HANDLER_0); + handler1 = ObjectManager::instance()->get(objects::TEST_DEVICE_HANDLER_1); + 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/examples/fsfw_examples/assemblies/TestAssembly.h b/examples/fsfw_examples/assemblies/TestAssembly.h new file mode 100644 index 00000000..b4388152 --- /dev/null +++ b/examples/fsfw_examples/assemblies/TestAssembly.h @@ -0,0 +1,53 @@ +#ifndef MISSION_ASSEMBLIES_TESTASSEMBLY_H_ +#define MISSION_ASSEMBLIES_TESTASSEMBLY_H_ + +#include +#include "fsfw_examples/devices/TestDeviceHandler.h" + +class TestAssembly: public AssemblyBase { +public: + TestAssembly(object_id_t objectId, object_id_t parentId); + 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; + TestDevice* handler0 = nullptr; + TestDevice* handler1 = nullptr; + + + bool isDeviceAvailable(object_id_t object); +}; + +#endif /* MISSION_ASSEMBLIES_TESTASSEMBLY_H_ */ diff --git a/examples/fsfw_examples/config/CMakeLists.txt b/examples/fsfw_examples/config/CMakeLists.txt new file mode 100644 index 00000000..7e446e7b --- /dev/null +++ b/examples/fsfw_examples/config/CMakeLists.txt @@ -0,0 +1,11 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE + examplePollingSequenceFactory.cpp +) + +target_include_directories(${LIB_FSFW_NAME} INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_include_directories(${LIB_FSFW_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/examples/fsfw_examples/config/ExampleConfig.h.in b/examples/fsfw_examples/config/ExampleConfig.h.in new file mode 100644 index 00000000..24dc62a3 --- /dev/null +++ b/examples/fsfw_examples/config/ExampleConfig.h.in @@ -0,0 +1,67 @@ +/** + * @brief This file will contain configuration constants which are used across all BSPs + */ +#ifndef COMMON_COMMONCONFIG_H_ +#define COMMON_COMMONCONFIG_H_ + +#include + +//! Specify the debug output verbose level +#define OBSW_VERBOSE_LEVEL 1 + +#define OBSW_PRINT_MISSED_DEADLINES 0 + +//! Perform internal unit testd at application startup +#define OBSW_PERFORM_INTERNAL_UNITTEST 1 + +//! Add core components for the FSFW and for TMTC communication +#define OBSW_ADD_CORE_COMPONENTS 1 + +//! Add the PUS service stack +#define OBSW_ADD_PUS_STACK 1 +#define OBSW_PUS_PRINTOUT 0 + +//! Add the task examples +#define OBSW_ADD_TASK_EXAMPLE 1 +#define OBSW_TASK_EXAMPLE_PRINTOUT 0 + +//! Add the demo device handler object +#define OBSW_ADD_DEVICE_HANDLER_DEMO 1 +#define OBSW_DEVICE_HANDLER_PRINTOUT 1 + +//! Add the demo controller object +#define OBSW_ADD_CONTROLLER_DEMO 1 +#define OBSW_CONTROLLER_PRINTOUT 1 + +/** + * The APID is a 14 bit identifier which can be used to distinguish processes and applications + * on a spacecraft. For more details, see the related ECSS/CCSDS standards. + * For this example, we are going to use a constant APID +*/ +static const uint16_t COMMON_APID = 0xEF; + +#ifdef __cplusplus + +#include +#include + +/** + * Enumerations for used PUS service IDs. + */ +namespace pus { +enum ServiceIds: uint8_t { + PUS_SERVICE_1 = 1, + PUS_SERVICE_2 = 2, + PUS_SERVICE_3 = 3, + PUS_SERVICE_5 = 5, + PUS_SERVICE_8 = 8, + PUS_SERVICE_9 = 9, + PUS_SERVICE_17 = 17, + PUS_SERVICE_20 = 20, + PUS_SERVICE_200 = 200 +}; +} + +#endif /* __cplusplus */ + +#endif /* COMMON_COMMONCONFIG_H_ */ diff --git a/examples/fsfw_examples/config/exampleClassIds.h b/examples/fsfw_examples/config/exampleClassIds.h new file mode 100644 index 00000000..6baae604 --- /dev/null +++ b/examples/fsfw_examples/config/exampleClassIds.h @@ -0,0 +1,14 @@ +#ifndef COMMON_CONFIG_COMMONCLASSIDS_H_ +#define COMMON_CONFIG_COMMONCLASSIDS_H_ + +#include "fsfw/returnvalues/FwClassIds.h" + +namespace CLASS_ID { +enum commonClassIds: uint8_t { + COMMON_CLASS_ID_START = FW_CLASS_ID_COUNT, + DUMMY_HANDLER, //DDH + COMMON_CLASS_ID_END // [EXPORT] : [END] +}; +} + +#endif /* COMMON_CONFIG_COMMONCLASSIDS_H_ */ diff --git a/examples/fsfw_examples/config/exampleObjectIds.h b/examples/fsfw_examples/config/exampleObjectIds.h new file mode 100644 index 00000000..97829578 --- /dev/null +++ b/examples/fsfw_examples/config/exampleObjectIds.h @@ -0,0 +1,32 @@ +#ifndef COMMON_COMMONSYSTEMOBJECTS_H_ +#define COMMON_COMMONSYSTEMOBJECTS_H_ + +#include +#include + +namespace objects { +enum commonObjects: object_id_t { + + EXAMPLE_OBJECTS_START = FSFW_OBJECTS_END, + // 0x54 reserved for example objects + + TEST_ASSEMBLY = 0x5400CAFE, + TEST_CONTROLLER = 0x5401CAFE, + TEST_DEVICE_HANDLER_0 = 0x5401AFFE, + TEST_DEVICE_HANDLER_1 = 0x5402AFFE, + TEST_ECHO_COM_IF = 0x5400AFFE, + CCSDS_DISTRIBUTOR = 0x54000000, + PUS_DISTRIBUTOR = 0x54000001, + TM_FUNNEL = 0x54030002, + TEST_TASK = 0x5402CAFE, + TEST_DUMMY_1 = 0x54000101, + TEST_DUMMY_2 = 0x54000102, + TEST_DUMMY_3= 0x54000103, + TEST_DUMMY_4 = 0x54000104, + TEST_DUMMY_5 = 0x54000105, +}; +} + + + +#endif /* COMMON_COMMONSYSTEMOBJECTS_H_ */ diff --git a/examples/fsfw_examples/config/examplePollingSequenceFactory.cpp b/examples/fsfw_examples/config/examplePollingSequenceFactory.cpp new file mode 100644 index 00000000..a2cb6033 --- /dev/null +++ b/examples/fsfw_examples/config/examplePollingSequenceFactory.cpp @@ -0,0 +1,82 @@ +#include "exampleObjectIds.h" +#include "pollingsequence/pollingSequenceFactory.h" +#include "fsfw_examples/test/FsfwExampleTask.h" + +#include +#include +#include + + +ReturnValue_t pst::pollingSequenceExamples(FixedTimeslotTaskIF* thisSequence) { + uint32_t length = thisSequence->getPeriodMs(); + + thisSequence->addSlot(objects::TEST_DUMMY_1, length * 0, + FsfwExampleTask::OpCodes::SEND_RAND_NUM); + thisSequence->addSlot(objects::TEST_DUMMY_2, length * 0, + FsfwExampleTask::OpCodes::SEND_RAND_NUM); + thisSequence->addSlot(objects::TEST_DUMMY_3, length * 0, + FsfwExampleTask::OpCodes::SEND_RAND_NUM); + + thisSequence->addSlot(objects::TEST_DUMMY_1, length * 0.2, + FsfwExampleTask::OpCodes::RECEIVE_RAND_NUM); + thisSequence->addSlot(objects::TEST_DUMMY_2, length * 0.2, + FsfwExampleTask::OpCodes::RECEIVE_RAND_NUM); + thisSequence->addSlot(objects::TEST_DUMMY_3, length * 0.2, + FsfwExampleTask::OpCodes::RECEIVE_RAND_NUM); + + thisSequence->addSlot(objects::TEST_DUMMY_1, length * 0.5, + FsfwExampleTask::OpCodes::DELAY_SHORT); + thisSequence->addSlot(objects::TEST_DUMMY_2, length * 0.5, + FsfwExampleTask::OpCodes::DELAY_SHORT); + thisSequence->addSlot(objects::TEST_DUMMY_3, length * 0.5, + FsfwExampleTask::OpCodes::DELAY_SHORT); + + if (thisSequence->checkSequence() == HasReturnvaluesIF::RETURN_OK) { + return HasReturnvaluesIF::RETURN_OK; + } + else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "pst::pollingSequenceInitFunction: Initialization errors!" << std::endl; +#else + sif::printError("pst::pollingSequenceInitFunction: Initialization errors!\n"); +#endif + return HasReturnvaluesIF::RETURN_FAILED; + } +} + +ReturnValue_t pst::pollingSequenceDevices(FixedTimeslotTaskIF *thisSequence) { + uint32_t length = thisSequence->getPeriodMs(); + + thisSequence->addSlot(objects::TEST_DEVICE_HANDLER_0, 0, DeviceHandlerIF::PERFORM_OPERATION); + thisSequence->addSlot(objects::TEST_DEVICE_HANDLER_1, 0, DeviceHandlerIF::PERFORM_OPERATION); + + thisSequence->addSlot(objects::TEST_DEVICE_HANDLER_0, 0.3, DeviceHandlerIF::SEND_WRITE); + thisSequence->addSlot(objects::TEST_DEVICE_HANDLER_1, 0.3, DeviceHandlerIF::SEND_WRITE); + + thisSequence->addSlot(objects::TEST_DEVICE_HANDLER_0, 0.45 * length, + DeviceHandlerIF::GET_WRITE); + thisSequence->addSlot(objects::TEST_DEVICE_HANDLER_1, 0.45 * length, + DeviceHandlerIF::GET_WRITE); + + thisSequence->addSlot(objects::TEST_DEVICE_HANDLER_0, 0.6 * length, DeviceHandlerIF::SEND_READ); + thisSequence->addSlot(objects::TEST_DEVICE_HANDLER_1, 0.6 * length, DeviceHandlerIF::SEND_READ); + + thisSequence->addSlot(objects::TEST_DEVICE_HANDLER_0, 0.8 * length, DeviceHandlerIF::GET_READ); + thisSequence->addSlot(objects::TEST_DEVICE_HANDLER_1, 0.8 * length, DeviceHandlerIF::GET_READ); + + if (thisSequence->checkSequence() == HasReturnvaluesIF::RETURN_OK) { + return HasReturnvaluesIF::RETURN_OK; + } + else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "pst::pollingSequenceTestFunction: Initialization errors!" << std::endl; +#else + sif::printError("pst::pollingSequenceTestFunction: Initialization errors!\n"); +#endif + return HasReturnvaluesIF::RETURN_FAILED; + } +} + + + + diff --git a/examples/fsfw_examples/config/exampleSubsystemIds.h b/examples/fsfw_examples/config/exampleSubsystemIds.h new file mode 100644 index 00000000..52b09d3f --- /dev/null +++ b/examples/fsfw_examples/config/exampleSubsystemIds.h @@ -0,0 +1,15 @@ +#ifndef COMMON_CONFIG_COMMONSUBSYSTEMIDS_H_ +#define COMMON_CONFIG_COMMONSUBSYSTEMIDS_H_ + +#include "fsfw/events/fwSubsystemIdRanges.h" + +namespace SUBSYSTEM_ID { +enum commonSubsystemId: uint8_t { + EXAMPLE_SUBSYSTEM_ID_START = FW_SUBSYSTEM_ID_RANGE, + TEST_TASK_ID, + EXAMPLE_SUBSYSTEM_ID_END +}; +} + + +#endif /* COMMON_CONFIG_COMMONSUBSYSTEMIDS_H_ */ diff --git a/tests/src/fsfw_tests/integration/controller/CMakeLists.txt b/examples/fsfw_examples/controller/CMakeLists.txt similarity index 100% rename from tests/src/fsfw_tests/integration/controller/CMakeLists.txt rename to examples/fsfw_examples/controller/CMakeLists.txt diff --git a/tests/src/fsfw_tests/integration/controller/TestController.cpp b/examples/fsfw_examples/controller/TestController.cpp similarity index 100% rename from tests/src/fsfw_tests/integration/controller/TestController.cpp rename to examples/fsfw_examples/controller/TestController.cpp diff --git a/tests/src/fsfw_tests/integration/controller/TestController.h b/examples/fsfw_examples/controller/TestController.h similarity index 100% rename from tests/src/fsfw_tests/integration/controller/TestController.h rename to examples/fsfw_examples/controller/TestController.h diff --git a/tests/src/fsfw_tests/integration/controller/ctrldefinitions/testCtrlDefinitions.h b/examples/fsfw_examples/controller/ctrldefinitions/testCtrlDefinitions.h similarity index 100% rename from tests/src/fsfw_tests/integration/controller/ctrldefinitions/testCtrlDefinitions.h rename to examples/fsfw_examples/controller/ctrldefinitions/testCtrlDefinitions.h diff --git a/examples/fsfw_examples/core/CMakeLists.txt b/examples/fsfw_examples/core/CMakeLists.txt new file mode 100644 index 00000000..4b59881e --- /dev/null +++ b/examples/fsfw_examples/core/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE + GenericFactory.cpp +) diff --git a/examples/fsfw_examples/core/GenericFactory.cpp b/examples/fsfw_examples/core/GenericFactory.cpp new file mode 100644 index 00000000..50c5187b --- /dev/null +++ b/examples/fsfw_examples/core/GenericFactory.cpp @@ -0,0 +1,125 @@ +#include "GenericFactory.h" + +#include "fsfw/ExampleConfig.h" +#include "tmtc/pusIds.h" +#include "exampleObjectIds.h" + +#include "fsfw_tests/internal/InternalUnitTester.h" +#include "fsfw_examples/test/FsfwExampleTask.h" +#include "fsfw_examples/test/FsfwReaderTask.h" +#include "fsfw_examples/assemblies/TestAssembly.h" +#include "fsfw_examples/devices/TestCookie.h" +#include "fsfw_examples/devices/TestDeviceHandler.h" +#include "fsfw_examples/devices/TestEchoComIF.h" +#include "fsfw_examples/utility/TmFunnel.h" +#include "fsfw_examples/controller/TestController.h" + +#include "fsfw/devicehandlers/CookieIF.h" +#include "fsfw/events/EventManager.h" +#include "fsfw/health/HealthTable.h" +#include "fsfw/internalerror/InternalErrorReporter.h" +#include "fsfw/pus/CService200ModeCommanding.h" +#include "fsfw/pus/Service17Test.h" +#include "fsfw/pus/Service1TelecommandVerification.h" +#include "fsfw/pus/Service20ParameterManagement.h" +#include "fsfw/pus/Service2DeviceAccess.h" +#include "fsfw/pus/Service3Housekeeping.h" +#include "fsfw/pus/Service5EventReporting.h" +#include "fsfw/pus/Service8FunctionManagement.h" +#include "fsfw/pus/Service9TimeManagement.h" +#include "fsfw/tcdistribution/CCSDSDistributor.h" +#include "fsfw/tcdistribution/PUSDistributor.h" +#include "fsfw/timemanager/TimeStamper.h" +#include "fsfw/tmtcpacket/pus/tm.h" + + + +void ObjectFactory::produceGenericObjects(uint16_t apid) { +#if OBSW_ADD_CORE_COMPONENTS == 1 + /* Framework objects */ + new EventManager(objects::EVENT_MANAGER); + new HealthTable(objects::HEALTH_TABLE); + new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); + new TimeStamper(objects::TIME_STAMPER); + new CCSDSDistributor(apid, objects::CCSDS_DISTRIBUTOR); + new PUSDistributor(apid, objects::PUS_DISTRIBUTOR, + objects::CCSDS_DISTRIBUTOR); + new TmFunnel(objects::TM_FUNNEL); +#endif /* OBSW_ADD_CORE_COMPONENTS == 1 */ + + /* PUS stack */ +#if OBSW_ADD_PUS_STACK == 1 + new Service1TelecommandVerification(objects::PUS_SERVICE_1_VERIFICATION, + apid, pus::PUS_SERVICE_1, objects::TM_FUNNEL, 5); + new Service2DeviceAccess(objects::PUS_SERVICE_2_DEVICE_ACCESS, + apid, pus::PUS_SERVICE_2, 3, 10); + new Service3Housekeeping(objects::PUS_SERVICE_3_HOUSEKEEPING, apid, pus::PUS_SERVICE_3); + new Service5EventReporting(objects::PUS_SERVICE_5_EVENT_REPORTING, + apid, pus::PUS_SERVICE_5, 50); + new Service8FunctionManagement(objects::PUS_SERVICE_8_FUNCTION_MGMT, + apid, pus::PUS_SERVICE_8, 3, 10); + new Service9TimeManagement(objects::PUS_SERVICE_9_TIME_MGMT, apid, + pus::PUS_SERVICE_9); + new Service17Test(objects::PUS_SERVICE_17_TEST, apid, + pus::PUS_SERVICE_17); + new Service20ParameterManagement(objects::PUS_SERVICE_20_PARAMETERS, apid, + pus::PUS_SERVICE_20); + new CService200ModeCommanding(objects::PUS_SERVICE_200_MODE_MGMT, + apid, pus::PUS_SERVICE_200); +#endif /* OBSW_ADD_PUS_STACK == 1 */ + +#if OBSW_ADD_TASK_EXAMPLE == 1 + /* Demo objects */ + new FsfwExampleTask(objects::TEST_DUMMY_1); + new FsfwExampleTask(objects::TEST_DUMMY_2); + new FsfwExampleTask(objects::TEST_DUMMY_3); + + + bool enablePrintout = false; +#if OBSW_TASK_EXAMPLE_PRINTOUT == 1 + enablePrintout = true; +#endif + new FsfwReaderTask(objects::TEST_DUMMY_4, enablePrintout); +#endif /* OBSW_ADD_TASK_EXAMPLE == 1 */ + +#if OBSW_ADD_DEVICE_HANDLER_DEMO == 1 + +#if OBSW_DEVICE_HANDLER_PRINTOUT == 1 + bool enableInfoPrintout = true; +#else + bool enableInfoPrintout = false; +#endif /* OBSW_DEVICE_HANDLER_PRINTOUT == 1 */ + + /* Demo device handler object */ + size_t expectedMaxReplyLen = 64; + CookieIF* testCookie = new DummyCookie( + static_cast(testdevice::DeviceIndex::DEVICE_0), expectedMaxReplyLen); + new TestEchoComIF(objects::TEST_ECHO_COM_IF); + new TestDevice(objects::TEST_DEVICE_HANDLER_0, objects::TEST_ECHO_COM_IF, testCookie, + testdevice::DeviceIndex::DEVICE_0, enableInfoPrintout); + testCookie = new DummyCookie(static_cast(testdevice::DeviceIndex::DEVICE_1), + expectedMaxReplyLen); + new TestDevice(objects::TEST_DEVICE_HANDLER_1, objects::TEST_ECHO_COM_IF, testCookie, + testdevice::DeviceIndex::DEVICE_1, enableInfoPrintout); + + new TestAssembly(objects::TEST_ASSEMBLY, objects::NO_OBJECT); + +#endif /* OBSW_ADD_DEVICE_HANDLER_DEMO == 1 */ + + + /* Demo controller object */ +#if OBSW_ADD_CONTROLLER_DEMO == 1 + +#if OBSW_CONTROLLER_PRINTOUT == 1 +#endif + new TestController(objects::TEST_CONTROLLER); + +#endif /* OBSW_ADD_CONTROLLER_DEMO == 1 */ + +#if OBSW_PERFORM_INTERNAL_UNITTEST == 1 + InternalUnitTester::TestConfig testCfg; + testCfg.testArrayPrinter = false; + InternalUnitTester unittester; + unittester.performTests(testCfg); +#endif /* OBSW_PERFORM_INTERNAL_UNITTEST == 1 */ +} diff --git a/examples/fsfw_examples/core/GenericFactory.h b/examples/fsfw_examples/core/GenericFactory.h new file mode 100644 index 00000000..0e079240 --- /dev/null +++ b/examples/fsfw_examples/core/GenericFactory.h @@ -0,0 +1,17 @@ +#ifndef MISSION_CORE_GENERICFACTORY_H_ +#define MISSION_CORE_GENERICFACTORY_H_ + +#include + +namespace ObjectFactory { + +/** + * @brief Produce hardware independant objects. Called by bsp specific + * object factory. + */ +void produceGenericObjects(uint16_t apid); + +} + + +#endif /* MISSION_CORE_GENERICFACTORY_H_ */ diff --git a/tests/src/fsfw_tests/integration/devices/CMakeLists.txt b/examples/fsfw_examples/devices/CMakeLists.txt similarity index 100% rename from tests/src/fsfw_tests/integration/devices/CMakeLists.txt rename to examples/fsfw_examples/devices/CMakeLists.txt diff --git a/tests/src/fsfw_tests/integration/devices/TestCookie.cpp b/examples/fsfw_examples/devices/TestCookie.cpp similarity index 100% rename from tests/src/fsfw_tests/integration/devices/TestCookie.cpp rename to examples/fsfw_examples/devices/TestCookie.cpp diff --git a/tests/src/fsfw_tests/integration/devices/TestCookie.h b/examples/fsfw_examples/devices/TestCookie.h similarity index 100% rename from tests/src/fsfw_tests/integration/devices/TestCookie.h rename to examples/fsfw_examples/devices/TestCookie.h diff --git a/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp b/examples/fsfw_examples/devices/TestDeviceHandler.cpp similarity index 99% rename from tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp rename to examples/fsfw_examples/devices/TestDeviceHandler.cpp index 0a1e6ca7..b872ad31 100644 --- a/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp +++ b/examples/fsfw_examples/devices/TestDeviceHandler.cpp @@ -1,5 +1,5 @@ #include "TestDeviceHandler.h" -#include +#include "fsfw/ExampleConfig.h" #include diff --git a/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.h b/examples/fsfw_examples/devices/TestDeviceHandler.h similarity index 100% rename from tests/src/fsfw_tests/integration/devices/TestDeviceHandler.h rename to examples/fsfw_examples/devices/TestDeviceHandler.h diff --git a/tests/src/fsfw_tests/integration/devices/TestEchoComIF.cpp b/examples/fsfw_examples/devices/TestEchoComIF.cpp similarity index 100% rename from tests/src/fsfw_tests/integration/devices/TestEchoComIF.cpp rename to examples/fsfw_examples/devices/TestEchoComIF.cpp diff --git a/tests/src/fsfw_tests/integration/devices/TestEchoComIF.h b/examples/fsfw_examples/devices/TestEchoComIF.h similarity index 100% rename from tests/src/fsfw_tests/integration/devices/TestEchoComIF.h rename to examples/fsfw_examples/devices/TestEchoComIF.h diff --git a/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h b/examples/fsfw_examples/devices/devicedefinitions/testDeviceDefinitions.h similarity index 99% rename from tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h rename to examples/fsfw_examples/devices/devicedefinitions/testDeviceDefinitions.h index 10668b94..fbf71003 100644 --- a/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h +++ b/examples/fsfw_examples/devices/devicedefinitions/testDeviceDefinitions.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace testdevice { diff --git a/examples/fsfw_examples/test/CMakeLists.txt b/examples/fsfw_examples/test/CMakeLists.txt new file mode 100644 index 00000000..952a034a --- /dev/null +++ b/examples/fsfw_examples/test/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(${TARGET_NAME} + PRIVATE + TestTask.cpp + FsfwReaderTask.cpp + FsfwExampleTask.cpp + MutexExample.cpp +) \ No newline at end of file diff --git a/examples/fsfw_examples/test/FsfwExampleTask.cpp b/examples/fsfw_examples/test/FsfwExampleTask.cpp new file mode 100644 index 00000000..00c68eac --- /dev/null +++ b/examples/fsfw_examples/test/FsfwExampleTask.cpp @@ -0,0 +1,264 @@ +#include "FsfwExampleTask.h" +#include "fsfw/ExampleConfig.h" +#include "exampleObjectIds.h" +#include "objects/systemObjectList.h" + +#include +#include +#include +#include +#include + + +FsfwExampleTask::FsfwExampleTask(object_id_t objectId): SystemObject(objectId), + poolManager(this, nullptr), demoSet(this), + monitor(objectId, MONITOR_ID, gp_id_t(objectId, FsfwDemoSet::VARIABLE_LIMIT), 30, 10) +{ + commandQueue = QueueFactory::instance()->createMessageQueue(10, + CommandMessage::MAX_MESSAGE_SIZE); +} + +FsfwExampleTask::~FsfwExampleTask() { +} + +ReturnValue_t FsfwExampleTask::performOperation(uint8_t operationCode) { + if(operationCode == OpCodes::DELAY_SHORT){ + TaskFactory::delayTask(5); + } + + // TODO: Move this to new test controller? + ReturnValue_t result = performMonitoringDemo(); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + if (operationCode == OpCodes::SEND_RAND_NUM) { + result = performSendOperation(); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + + + if (operationCode == OpCodes::RECEIVE_RAND_NUM) { + result = performReceiveOperation(); + + } + + return 0; +} + +object_id_t FsfwExampleTask::getNextRecipient() { + switch(this->getObjectId()) { + case(objects::TEST_DUMMY_1): { + return objects::TEST_DUMMY_2; + } + case(objects::TEST_DUMMY_2): { + return objects::TEST_DUMMY_3; + } + case(objects::TEST_DUMMY_3): { + return objects::TEST_DUMMY_1; + } + default: + return objects::TEST_DUMMY_1; + } +} + +object_id_t FsfwExampleTask::getSender() { + switch(this->getObjectId()) { + case(objects::TEST_DUMMY_1): { + return objects::TEST_DUMMY_3; + } + case(objects::TEST_DUMMY_2): { + return objects::TEST_DUMMY_1; + } + case(objects::TEST_DUMMY_3): { + return objects::TEST_DUMMY_2; + } + default: + return objects::TEST_DUMMY_1; + } +} + + +ReturnValue_t FsfwExampleTask::initialize() { + // Get the dataset of the sender. Will be cached for later checks. + object_id_t sender = getSender(); + HasLocalDataPoolIF* senderIF = ObjectManager::instance()->get(sender); + if(senderIF == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "FsfwDemoTask::initialize: Sender object invalid!" << std::endl; +#else + sif::printError("FsfwDemoTask::initialize: Sender object invalid!\n"); +#endif + return HasReturnvaluesIF::RETURN_FAILED; + } + + // we need a private copy of the previous dataset.. or we use the shared dataset. + senderSet = new FsfwDemoSet(senderIF); + if(senderSet == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "FsfwDemoTask::initialize: Sender dataset invalid!" << std::endl; +#else + sif::printError("FsfwDemoTask::initialize: Sender dataset invalid!\n"); +#endif + return HasReturnvaluesIF::RETURN_FAILED; + } + return poolManager.initialize(commandQueue); +} + +ReturnValue_t FsfwExampleTask::initializeAfterTaskCreation() { + return poolManager.initializeAfterTaskCreation(); +} + +object_id_t FsfwExampleTask::getObjectId() const { + return SystemObject::getObjectId(); +} + +MessageQueueId_t FsfwExampleTask::getMessageQueueId(){ + return commandQueue->getId(); +} + +void FsfwExampleTask::setTaskIF(PeriodicTaskIF* task){ + this->task = task; +} + +LocalPoolDataSetBase* FsfwExampleTask::getDataSetHandle(sid_t sid) { + return &demoSet; +} + +uint32_t FsfwExampleTask::getPeriodicOperationFrequency() const { + return task->getPeriodMs(); +} + +ReturnValue_t FsfwExampleTask::initializeLocalDataPool( + localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { + localDataPoolMap.emplace(FsfwDemoSet::PoolIds::VARIABLE, new PoolEntry({0})); + localDataPoolMap.emplace(FsfwDemoSet::PoolIds::VARIABLE_LIMIT, new PoolEntry({0})); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t FsfwExampleTask::performMonitoringDemo() { + ReturnValue_t result = demoSet.variableLimit.read( + MutexIF::TimeoutType::WAITING, 20); + if(result != HasReturnvaluesIF::RETURN_OK) { + /* Configuration error */ +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "DummyObject::performOperation: Could not read variableLimit!" << std::endl; +#else + sif::printError("DummyObject::performOperation: Could not read variableLimit!\n"); +#endif + return result; + } + if(this->getObjectId() == objects::TEST_DUMMY_5){ + if(demoSet.variableLimit.value > 20){ + demoSet.variableLimit.value = 0; + } + demoSet.variableLimit.value++; + demoSet.variableLimit.commit(20); + monitor.check(); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t FsfwExampleTask::performSendOperation() { + object_id_t nextRecipient = getNextRecipient(); + FsfwExampleTask* target = ObjectManager::instance()->get(nextRecipient); + if (target == nullptr) { + /* Configuration error */ +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "DummyObject::performOperation: Next recipient does not exist!" << std::endl; +#else + sif::printError("DummyObject::performOperation: Next recipient does not exist!\n"); +#endif + return HasReturnvaluesIF::RETURN_FAILED; + } + + + uint32_t randomNumber = rand() % 100; + CommandMessage message; + message.setParameter(randomNumber); + message.setParameter2(this->getMessageQueueId()); + + /* Send message using own message queue */ + ReturnValue_t result = commandQueue->sendMessage(target->getMessageQueueId(), &message); + if (result != HasReturnvaluesIF::RETURN_OK + && result != MessageQueueIF::FULL) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "FsfwDemoTask::performSendOperation: Send failed with " << result << + std::endl; +#else + sif::printError("FsfwDemoTask::performSendOperation: Send failed with %hu\n", result); +#endif + } + + /* Send message without via MessageQueueSenderIF */ + result = MessageQueueSenderIF::sendMessage(target->getMessageQueueId(), &message, + commandQueue->getId()); + if (result != HasReturnvaluesIF::RETURN_OK + && result != MessageQueueIF::FULL) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "FsfwDemoTask::performSendOperation: Send failed with " << result << std::endl; +#else + sif::printError("FsfwDemoTask::performSendOperation: Send failed with %hu\n", result); +#endif + } + + demoSet.variableWrite.value = randomNumber; + result = demoSet.variableWrite.commit(20); + + return result; +} + +ReturnValue_t FsfwExampleTask::performReceiveOperation() { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + while (result != MessageQueueIF::EMPTY) { + CommandMessage receivedMessage; + result = commandQueue->receiveMessage(&receivedMessage); + if (result != HasReturnvaluesIF::RETURN_OK + && result != MessageQueueIF::EMPTY) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::debug << "Receive failed with " << result << std::endl; +#endif + break; + } + if (result != MessageQueueIF::EMPTY) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 +#if OBSW_VERBOSE_LEVEL >= 2 + sif::debug << "Message Received by " << getObjectId() << " from Queue " << + receivedMessage.getSender() << " ObjectId " << receivedMessage.getParameter() << + " Queue " << receivedMessage.getParameter2() << std::endl; +#endif +#endif + + if(senderSet == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + result = senderSet->variableRead.read(MutexIF::TimeoutType::WAITING, + 20); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + if(senderSet->variableRead.value != receivedMessage.getParameter()) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "FsfwDemoTask::performReceiveOperation: Variable " << std::hex << + "0x" << senderSet->variableRead.getDataPoolId() << std::dec << + " has wrong value." << std::endl; + sif::error << "Value: " << demoSet.variableRead.value << ", expected: " << + receivedMessage.getParameter() << std::endl; +#endif + } + + } + } + return result; +} + +MessageQueueId_t FsfwExampleTask::getCommandQueue() const { + return commandQueue->getId(); +} + +LocalDataPoolManager* FsfwExampleTask::getHkManagerHandle() { + return &poolManager; +} diff --git a/examples/fsfw_examples/test/FsfwExampleTask.h b/examples/fsfw_examples/test/FsfwExampleTask.h new file mode 100644 index 00000000..005af8df --- /dev/null +++ b/examples/fsfw_examples/test/FsfwExampleTask.h @@ -0,0 +1,116 @@ +#ifndef MISSION_DEMO_FSFWDEMOTASK_H_ +#define MISSION_DEMO_FSFWDEMOTASK_H_ + +#include "testdefinitions/demoDefinitions.h" + +#include +#include +#include +#include +#include +#include + +class PeriodicTaskIF; + + +/** + * @brief This demo set shows the local data pool functionality and fixed + * timeslot capabilities of the FSFW. + * + * @details + * There will be multiple demo objects. Each demo object will generate a random + * number and send that number via message queues to the next demo object + * (e.g. DUMMY_1 sends the number to DUMMY_2 etc.). The receiving object + * will check the received value against the sent value by extracting the sent + * value directly from the sender via the local data pool interface. + * If the timing is set up correctly, the values will always be the same. + */ +class FsfwExampleTask: + public ExecutableObjectIF, + public SystemObject, + public HasLocalDataPoolIF { +public: + enum OpCodes { + SEND_RAND_NUM, + RECEIVE_RAND_NUM, + DELAY_SHORT + }; + + static constexpr uint8_t MONITOR_ID = 2; + + /** + * @brief Simple constructor, only expects object ID. + * @param objectId + */ + FsfwExampleTask(object_id_t objectId); + + virtual ~FsfwExampleTask(); + + /** + * @brief The performOperation method is executed in a task. + * @details There are no restrictions for calls within this method, so any + * other member of the class can be used. + * @return Currently, the return value is ignored. + */ + virtual ReturnValue_t performOperation(uint8_t operationCode = 0); + + /** + * @brief This function will be called by the global object manager + * @details + * This function will always be called before any tasks are started. + * It can also be used to return error codes in the software initialization + * process cleanly. + * @return + */ + virtual ReturnValue_t initialize() override; + + /** + * @brief This function will be called by the OSAL task handlers + * @details + * This function will be called before the first #performOperation + * call after the tasks have been started. It can be used if some + * initialization process requires task specific properties like + * periodic intervals (by using the PeriodicTaskIF* handle). + * @return + */ + virtual ReturnValue_t initializeAfterTaskCreation() override; + + /** + * This function will be called by the OSAL task handler. The + * task interface handle can be cached to access task specific properties. + * @param task + */ + void setTaskIF(PeriodicTaskIF* task) override; + + object_id_t getObjectId() const override; + + MessageQueueId_t getMessageQueueId(); + + +private: + LocalDataPoolManager poolManager; + FsfwDemoSet* senderSet = nullptr; + FsfwDemoSet demoSet; + AbsLimitMonitor monitor; + PeriodicTaskIF* task = nullptr; + MessageQueueIF* commandQueue = nullptr; + + /* HasLocalDatapoolIF overrides */ + MessageQueueId_t getCommandQueue() const override; + LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; + uint32_t getPeriodicOperationFrequency() const override; + virtual ReturnValue_t initializeLocalDataPool( + localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; + virtual LocalDataPoolManager* getHkManagerHandle() override; + + object_id_t getNextRecipient(); + object_id_t getSender(); + + ReturnValue_t performMonitoringDemo(); + ReturnValue_t performSendOperation(); + ReturnValue_t performReceiveOperation(); + uint8_t execCounter = 0; +}; + +#endif /* MISSION_DEMO_FSFWDEMOTASK_H_ */ diff --git a/examples/fsfw_examples/test/FsfwReaderTask.cpp b/examples/fsfw_examples/test/FsfwReaderTask.cpp new file mode 100644 index 00000000..9a19090d --- /dev/null +++ b/examples/fsfw_examples/test/FsfwReaderTask.cpp @@ -0,0 +1,55 @@ +#include "FsfwReaderTask.h" + +#include +#include +#include +#include + +#include + + +FsfwReaderTask::FsfwReaderTask(object_id_t objectId, bool enablePrintout): + SystemObject(objectId), printoutEnabled(enablePrintout), opDivider(10), + readSet(this->getObjectId(), + gp_id_t(objects::TEST_DUMMY_1, FsfwDemoSet::PoolIds::VARIABLE), + gp_id_t(objects::TEST_DUMMY_2, FsfwDemoSet::PoolIds::VARIABLE), + gp_id_t(objects::TEST_DUMMY_3, FsfwDemoSet::PoolIds::VARIABLE)) { + /* Special protection for set reading because each variable is read from a different pool */ + readSet.setReadCommitProtectionBehaviour(true); +} + +FsfwReaderTask::~FsfwReaderTask() { +} + +ReturnValue_t FsfwReaderTask::initializeAfterTaskCreation() { + /* Give other task some time to set up local data pools. */ + TaskFactory::delayTask(20); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t FsfwReaderTask::performOperation(uint8_t operationCode) { + PoolReadGuard readHelper(&readSet); + + uint32_t variable1 = readSet.variable1.value; + uint32_t variable2 = readSet.variable2.value; + uint32_t variable3 = readSet.variable3.value; + +#if OBSW_VERBOSE_LEVEL >= 1 + if(opDivider.checkAndIncrement() and printoutEnabled) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "FsfwPeriodicTask::performOperation: Reading variables." << std::endl; + sif::info << "Variable read from demo object 1: " << variable1 << std::endl; + sif::info << "Variable read from demo object 2: " << variable2 << std::endl; + sif::info << "Variable read from demo object 3: " << variable3 << std::endl; +#else + sif::printInfo("FsfwPeriodicTask::performOperation: Reading variables.\n\r"); + sif::printInfo("Variable read from demo object 1: %d\n\r", variable1); + sif::printInfo("Variable read from demo object 2: %d\n\r", variable2); + sif::printInfo("Variable read from demo object 3: %d\n\r", variable3); +#endif + } +#else + if(variable1 and variable2 and variable3) {}; +#endif + return HasReturnvaluesIF::RETURN_OK; +} diff --git a/examples/fsfw_examples/test/FsfwReaderTask.h b/examples/fsfw_examples/test/FsfwReaderTask.h new file mode 100644 index 00000000..7f987dae --- /dev/null +++ b/examples/fsfw_examples/test/FsfwReaderTask.h @@ -0,0 +1,24 @@ +#ifndef MISSION_DEMO_FSFWPERIODICTASK_H_ +#define MISSION_DEMO_FSFWPERIODICTASK_H_ + +#include "testdefinitions/demoDefinitions.h" + +#include +#include +#include + +class FsfwReaderTask: public ExecutableObjectIF, public SystemObject { +public: + FsfwReaderTask(object_id_t objectId, bool enablePrintout); + virtual ~FsfwReaderTask(); + + ReturnValue_t initializeAfterTaskCreation() override; + virtual ReturnValue_t performOperation(uint8_t operationCode = 0); + +private: + bool printoutEnabled = false; + PeriodicOperationDivider opDivider; + CompleteDemoReadSet readSet; +}; + +#endif /* MISSION_DEMO_FSFWPERIODICTASK_H_ */ diff --git a/examples/fsfw_examples/test/MutexExample.cpp b/examples/fsfw_examples/test/MutexExample.cpp new file mode 100644 index 00000000..06af15c4 --- /dev/null +++ b/examples/fsfw_examples/test/MutexExample.cpp @@ -0,0 +1,47 @@ +#include "MutexExample.h" + +#include +#include + + +void MutexExample::example(){ + MutexIF* mutex = MutexFactory::instance()->createMutex(); + MutexIF* mutex2 = MutexFactory::instance()->createMutex(); + + ReturnValue_t result = mutex->lockMutex(MutexIF::TimeoutType::WAITING, + 2 * 60 * 1000); + if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "MutexExample::example: Lock Failed with " << result << std::endl; +#else + sif::printError("MutexExample::example: Lock Failed with %hu\n", result); +#endif + } + + result = mutex2->lockMutex(MutexIF::TimeoutType::BLOCKING); + if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "MutexExample::example: Lock Failed with " << result << std::endl; +#else + sif::printError("MutexExample::example: Lock Failed with %hu\n", result); +#endif + } + + result = mutex->unlockMutex(); + if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "MutexExample::example: Unlock Failed with " << result << std::endl; +#else + sif::printError("MutexExample::example: Unlock Failed with %hu\n", result); +#endif + } + + result = mutex2->unlockMutex(); + if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "MutexExample::example: Unlock Failed with " << result << std::endl; +#else + sif::printError("MutexExample::example: Unlock Failed with %hu\n", result); +#endif + } +} diff --git a/examples/fsfw_examples/test/MutexExample.h b/examples/fsfw_examples/test/MutexExample.h new file mode 100644 index 00000000..d10ef2f5 --- /dev/null +++ b/examples/fsfw_examples/test/MutexExample.h @@ -0,0 +1,8 @@ +#ifndef MISSION_DEMO_MUTEXEXAMPLE_H_ +#define MISSION_DEMO_MUTEXEXAMPLE_H_ + +namespace MutexExample { + void example(); +}; + +#endif /* MISSION_DEMO_MUTEXEXAMPLE_H_ */ diff --git a/examples/fsfw_examples/test/TestTask.cpp b/examples/fsfw_examples/test/TestTask.cpp new file mode 100644 index 00000000..c9910e90 --- /dev/null +++ b/examples/fsfw_examples/test/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/examples/fsfw_examples/test/TestTask.h b/examples/fsfw_examples/test/TestTask.h new file mode 100644 index 00000000..95f27bec --- /dev/null +++ b/examples/fsfw_examples/test/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_ */ diff --git a/examples/fsfw_examples/test/testdefinitions/demoDefinitions.h b/examples/fsfw_examples/test/testdefinitions/demoDefinitions.h new file mode 100644 index 00000000..711168fa --- /dev/null +++ b/examples/fsfw_examples/test/testdefinitions/demoDefinitions.h @@ -0,0 +1,63 @@ +#ifndef MISSION_DEMO_DEMODEFINITIONS_H_ +#define MISSION_DEMO_DEMODEFINITIONS_H_ + +#include +#include + +/** + * @brief This demo set showcases the local data pool functionality of the + * FSFW + * @details + * Each demo object will have an own instance of this set class, which contains + * pool variables (for read and write access respectively). + */ +class FsfwDemoSet: public StaticLocalDataSet<3> { +public: + + static constexpr uint32_t DEMO_SET_ID = 0; + + enum PoolIds { + VARIABLE, + VARIABLE_LIMIT + }; + + FsfwDemoSet(HasLocalDataPoolIF* hkOwner): + StaticLocalDataSet(hkOwner, DEMO_SET_ID) {} + + lp_var_t variableRead = lp_var_t(sid.objectId, + PoolIds::VARIABLE, this, pool_rwm_t::VAR_READ); + lp_var_t variableWrite = lp_var_t(sid.objectId, + PoolIds::VARIABLE, this, pool_rwm_t::VAR_WRITE); + lp_var_t variableLimit = lp_var_t(sid.objectId, + PoolIds::VARIABLE_LIMIT, this); +private: + +}; + +/** + * This set will enable object to read the dummy variables from the dataset + * above. An example application would be a consumer object like a controller + * which reads multiple sensor values at once. + */ +class CompleteDemoReadSet: public StaticLocalDataSet<3> { +public: + static constexpr uint32_t DEMO_SET_ID = 0; + + CompleteDemoReadSet(object_id_t owner, gp_id_t variable1, + gp_id_t variable2, gp_id_t variable3): + StaticLocalDataSet(sid_t(owner, DEMO_SET_ID)), + variable1(variable1, this, pool_rwm_t::VAR_READ), + variable2(variable2, this, pool_rwm_t::VAR_READ), + variable3(variable3, this, pool_rwm_t::VAR_READ) {} + + + lp_var_t variable1; + lp_var_t variable2; + lp_var_t variable3; +private: +}; + + + + +#endif /* MISSION_DEMO_DEMODEFINITIONS_H_ */ diff --git a/examples/fsfw_examples/utility/CMakeLists.txt b/examples/fsfw_examples/utility/CMakeLists.txt new file mode 100644 index 00000000..186977e1 --- /dev/null +++ b/examples/fsfw_examples/utility/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${TARGET_NAME} PRIVATE + utility.cpp + TmFunnel.cpp +) diff --git a/examples/fsfw_examples/utility/TaskCreation.h b/examples/fsfw_examples/utility/TaskCreation.h new file mode 100644 index 00000000..d72f6988 --- /dev/null +++ b/examples/fsfw_examples/utility/TaskCreation.h @@ -0,0 +1,22 @@ +#ifndef MISSION_UTILITY_TASKCREATION_H_ +#define MISSION_UTILITY_TASKCREATION_H_ + +#include +#include + +namespace task { + +void printInitError(const char* objName, object_id_t objectId) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "InitMission: Adding object " << objName << "(" + << std::setw(8) << std::setfill('0') << std::hex << objectId + << std::dec << ") failed." << std::endl; +#else + sif::printError("InitMission: Adding object %s (0x%08x) failed.\n", + objName, static_cast(objectId)); +#endif +} + +} + +#endif /* MISSION_UTILITY_TASKCREATION_H_ */ diff --git a/examples/fsfw_examples/utility/TmFunnel.cpp b/examples/fsfw_examples/utility/TmFunnel.cpp new file mode 100644 index 00000000..9cc0cceb --- /dev/null +++ b/examples/fsfw_examples/utility/TmFunnel.cpp @@ -0,0 +1,121 @@ +#include "TmFunnel.h" + +#include +#include +#include +#include + + +object_id_t TmFunnel::downlinkDestination = objects::NO_OBJECT; +object_id_t TmFunnel::storageDestination = objects::NO_OBJECT; + +TmFunnel::TmFunnel(object_id_t objectId, uint32_t messageDepth): + SystemObject(objectId), messageDepth(messageDepth) { + tmQueue = QueueFactory::instance()->createMessageQueue(messageDepth, + MessageQueueMessage::MAX_MESSAGE_SIZE); + storageQueue = QueueFactory::instance()->createMessageQueue(messageDepth, + MessageQueueMessage::MAX_MESSAGE_SIZE); +} + +TmFunnel::~TmFunnel() { +} + +MessageQueueId_t TmFunnel::getReportReceptionQueue(uint8_t virtualChannel) { + return tmQueue->getId(); +} + +ReturnValue_t TmFunnel::performOperation(uint8_t operationCode) { + TmTcMessage currentMessage; + ReturnValue_t status = tmQueue->receiveMessage(¤tMessage); + while(status == HasReturnvaluesIF::RETURN_OK) + { + status = handlePacket(¤tMessage); + if(status != HasReturnvaluesIF::RETURN_OK){ + break; + } + status = tmQueue->receiveMessage(¤tMessage); + } + + if (status == MessageQueueIF::EMPTY) { + return HasReturnvaluesIF::RETURN_OK; + } + else { + return status; + } +} + +ReturnValue_t TmFunnel::handlePacket(TmTcMessage* message) { + uint8_t* packetData = nullptr; + size_t size = 0; + ReturnValue_t result = tmPool->modifyData(message->getStorageId(), + &packetData, &size); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + TmPacketPusC packet(packetData); + packet.setPacketSequenceCount(this->sourceSequenceCount); + sourceSequenceCount++; + sourceSequenceCount = sourceSequenceCount % + SpacePacketBase::LIMIT_SEQUENCE_COUNT; + packet.setErrorControl(); + + result = tmQueue->sendToDefault(message); + if(result != HasReturnvaluesIF::RETURN_OK){ + tmPool->deleteData(message->getStorageId()); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TmFunnel::handlePacket: Error sending to downlink handler" << std::endl; +#endif + return result; + } + + if(storageDestination != objects::NO_OBJECT) { + result = storageQueue->sendToDefault(message); + if(result != HasReturnvaluesIF::RETURN_OK){ + tmPool->deleteData(message->getStorageId()); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TmFunnel::handlePacket: Error sending to storage handler" << std::endl; +#endif + return result; + } + } + return result; +} + +ReturnValue_t TmFunnel::initialize() { + + tmPool = ObjectManager::instance()->get(objects::TM_STORE); + if(tmPool == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TmFunnel::initialize: TM store not set." << std::endl; + sif::error << "Make sure the tm store is set up properly and implements StorageManagerIF" << + std::endl; +#endif + return ObjectManagerIF::CHILD_INIT_FAILED; + } + + AcceptsTelemetryIF* tmTarget = ObjectManager::instance()-> + get(downlinkDestination); + if(tmTarget == nullptr){ +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TmFunnel::initialize: Downlink Destination not set." << std::endl; + sif::error << "Make sure the downlink destination object is set up properly and implements " + "AcceptsTelemetryIF" << std::endl; +#endif + return ObjectManagerIF::CHILD_INIT_FAILED; + } + tmQueue->setDefaultDestination(tmTarget->getReportReceptionQueue()); + + // Storage destination is optional. + if(storageDestination == objects::NO_OBJECT) { + return SystemObject::initialize(); + } + + AcceptsTelemetryIF* storageTarget = ObjectManager::instance()-> + get(storageDestination); + if(storageTarget != nullptr) { + storageQueue->setDefaultDestination( + storageTarget->getReportReceptionQueue()); + } + + return SystemObject::initialize(); +} diff --git a/examples/fsfw_examples/utility/TmFunnel.h b/examples/fsfw_examples/utility/TmFunnel.h new file mode 100644 index 00000000..d9af4a87 --- /dev/null +++ b/examples/fsfw_examples/utility/TmFunnel.h @@ -0,0 +1,51 @@ +#ifndef MISSION_UTILITY_TMFUNNEL_H_ +#define MISSION_UTILITY_TMFUNNEL_H_ + +#include +#include +#include +#include +#include + +namespace Factory{ +void setStaticFrameworkObjectIds(); +} + +/** + * @brief TM Recipient. + * @details + * Main telemetry receiver. All generated telemetry is funneled into + * this object. + * @ingroup utility + * @author J. Meier + */ +class TmFunnel: + public AcceptsTelemetryIF, + public ExecutableObjectIF, + public SystemObject { + friend void (Factory::setStaticFrameworkObjectIds)(); +public: + TmFunnel(object_id_t objectId, uint32_t messageDepth = 20); + virtual ~TmFunnel(); + + virtual MessageQueueId_t getReportReceptionQueue( + uint8_t virtualChannel = 0) override; + virtual ReturnValue_t performOperation(uint8_t operationCode = 0) override; + virtual ReturnValue_t initialize() override; + +protected: + static object_id_t downlinkDestination; + static object_id_t storageDestination; + +private: + uint16_t sourceSequenceCount = 0; + MessageQueueIF* tmQueue = nullptr; + MessageQueueIF* storageQueue = nullptr; + + StorageManagerIF* tmPool = nullptr; + uint32_t messageDepth = 0; + + ReturnValue_t handlePacket(TmTcMessage* message); +}; + +#endif /* MISSION_UTILITY_TMFUNNEL_H_ */ diff --git a/examples/fsfw_examples/utility/compile_time.h b/examples/fsfw_examples/utility/compile_time.h new file mode 100644 index 00000000..c55673f6 --- /dev/null +++ b/examples/fsfw_examples/utility/compile_time.h @@ -0,0 +1,97 @@ +/* + * + * Created: 29.03.2018 + * + * Authors: + * + * Assembled from the code released on Stackoverflow by: + * Dennis (instructable.com/member/nqtronix) | https://stackoverflow.com/questions/23032002/c-c-how-to-get-integer-unix-timestamp-of-build-time-not-string + * and + * Alexis Wilke | https://stackoverflow.com/questions/10538444/do-you-know-of-a-c-macro-to-compute-unix-time-and-date + * + * Assembled by Jean Rabault + * + * UNIX_TIMESTAMP gives the UNIX timestamp (unsigned long integer of seconds since 1st Jan 1970) of compilation from macros using the compiler defined __TIME__ macro. + * This should include Gregorian calendar leap days, in particular the 29ths of February, 100 and 400 years modulo leaps. + * + * Careful: __TIME__ is the local time of the computer, NOT the UTC time in general! + * + */ + +#ifndef COMPILE_TIME_H_ +#define COMPILE_TIME_H_ + +// Some definitions for calculation +#define SEC_PER_MIN 60UL +#define SEC_PER_HOUR 3600UL +#define SEC_PER_DAY 86400UL +#define SEC_PER_YEAR (SEC_PER_DAY*365) + +// extracts 1..4 characters from a string and interprets it as a decimal value +#define CONV_STR2DEC_1(str, i) (str[i]>'0'?str[i]-'0':0) +#define CONV_STR2DEC_2(str, i) (CONV_STR2DEC_1(str, i)*10 + str[i+1]-'0') +#define CONV_STR2DEC_3(str, i) (CONV_STR2DEC_2(str, i)*10 + str[i+2]-'0') +#define CONV_STR2DEC_4(str, i) (CONV_STR2DEC_3(str, i)*10 + str[i+3]-'0') + +// Custom "glue logic" to convert the month name to a usable number +#define GET_MONTH(str, i) (str[i]=='J' && str[i+1]=='a' && str[i+2]=='n' ? 1 : \ + str[i]=='F' && str[i+1]=='e' && str[i+2]=='b' ? 2 : \ + str[i]=='M' && str[i+1]=='a' && str[i+2]=='r' ? 3 : \ + str[i]=='A' && str[i+1]=='p' && str[i+2]=='r' ? 4 : \ + str[i]=='M' && str[i+1]=='a' && str[i+2]=='y' ? 5 : \ + str[i]=='J' && str[i+1]=='u' && str[i+2]=='n' ? 6 : \ + str[i]=='J' && str[i+1]=='u' && str[i+2]=='l' ? 7 : \ + str[i]=='A' && str[i+1]=='u' && str[i+2]=='g' ? 8 : \ + str[i]=='S' && str[i+1]=='e' && str[i+2]=='p' ? 9 : \ + str[i]=='O' && str[i+1]=='c' && str[i+2]=='t' ? 10 : \ + str[i]=='N' && str[i+1]=='o' && str[i+2]=='v' ? 11 : \ + str[i]=='D' && str[i+1]=='e' && str[i+2]=='c' ? 12 : 0) + +// extract the information from the time string given by __TIME__ and __DATE__ +#define __TIME_SECONDS__ CONV_STR2DEC_2(__TIME__, 6) +#define __TIME_MINUTES__ CONV_STR2DEC_2(__TIME__, 3) +#define __TIME_HOURS__ CONV_STR2DEC_2(__TIME__, 0) +#define __TIME_DAYS__ CONV_STR2DEC_2(__DATE__, 4) +#define __TIME_MONTH__ GET_MONTH(__DATE__, 0) +#define __TIME_YEARS__ CONV_STR2DEC_4(__DATE__, 7) + +// Days in February +#define _UNIX_TIMESTAMP_FDAY(year) \ + (((year) % 400) == 0UL ? 29UL : \ + (((year) % 100) == 0UL ? 28UL : \ + (((year) % 4) == 0UL ? 29UL : \ + 28UL))) + +// Days in the year +#define _UNIX_TIMESTAMP_YDAY(year, month, day) \ + ( \ + /* January */ day \ + /* February */ + (month >= 2 ? 31UL : 0UL) \ + /* March */ + (month >= 3 ? _UNIX_TIMESTAMP_FDAY(year) : 0UL) \ + /* April */ + (month >= 4 ? 31UL : 0UL) \ + /* May */ + (month >= 5 ? 30UL : 0UL) \ + /* June */ + (month >= 6 ? 31UL : 0UL) \ + /* July */ + (month >= 7 ? 30UL : 0UL) \ + /* August */ + (month >= 8 ? 31UL : 0UL) \ + /* September */+ (month >= 9 ? 31UL : 0UL) \ + /* October */ + (month >= 10 ? 30UL : 0UL) \ + /* November */ + (month >= 11 ? 31UL : 0UL) \ + /* December */ + (month >= 12 ? 30UL : 0UL) \ + ) + +// get the UNIX timestamp from a digits representation +#define _UNIX_TIMESTAMP(year, month, day, hour, minute, second) \ + ( /* time */ second \ + + minute * SEC_PER_MIN \ + + hour * SEC_PER_HOUR \ + + /* year day (month + day) */ (_UNIX_TIMESTAMP_YDAY(year, month, day) - 1) * SEC_PER_DAY \ + + /* year */ (year - 1970UL) * SEC_PER_YEAR \ + + ((year - 1969UL) / 4UL) * SEC_PER_DAY \ + - ((year - 1901UL) / 100UL) * SEC_PER_DAY \ + + ((year - 1601UL) / 400UL) * SEC_PER_DAY \ + ) + +// the UNIX timestamp +#define UNIX_TIMESTAMP (_UNIX_TIMESTAMP(__TIME_YEARS__, __TIME_MONTH__, __TIME_DAYS__, __TIME_HOURS__, __TIME_MINUTES__, __TIME_SECONDS__)) + +#endif diff --git a/examples/fsfw_examples/utility/utility.cpp b/examples/fsfw_examples/utility/utility.cpp new file mode 100644 index 00000000..26481dbb --- /dev/null +++ b/examples/fsfw_examples/utility/utility.cpp @@ -0,0 +1,22 @@ +#include "utility.h" +#include +#include + +#include + +void utility::commonInitPrint(const char *const os, const char* const board) { + if(os == nullptr or board == nullptr) { + return; + } +#if FSFW_CPP_OSTREAM_ENABLED == 1 + std::cout << "-- FSFW Example (" << os<< ") v" << FSFW_EXAMPLE_VERSION << "." << + FSFW_EXAMPLE_SUBVERSION << "." << FSFW_EXAMPLE_REVISION << " --" << std::endl; + std::cout << "-- Compiled for " << board << " --" << std::endl; + std::cout << "-- Compiled on " << __DATE__ << " " << __TIME__ << " --" << std::endl; +#else + printf("\n\r-- FSFW Example (%s) v%d.%d.%d --\n", os, FSFW_EXAMPLE_VERSION, + FSFW_EXAMPLE_SUBVERSION, FSFW_EXAMPLE_REVISION); + printf("-- Compiled for %s --\n", board); + printf("-- Compiled on %s %s --\n", __DATE__, __TIME__); +#endif +} diff --git a/examples/fsfw_examples/utility/utility.h b/examples/fsfw_examples/utility/utility.h new file mode 100644 index 00000000..6cf6f35b --- /dev/null +++ b/examples/fsfw_examples/utility/utility.h @@ -0,0 +1,11 @@ +#ifndef COMMON_UTILITY_UTILITY_H_ +#define COMMON_UTILITY_UTILITY_H_ + +namespace utility { + +void commonInitPrint(const char *const os, const char* const board); + +} + + +#endif /* COMMON_UTILITY_UTILITY_H_ */ diff --git a/tests/src/fsfw_tests/CMakeLists.txt b/tests/src/fsfw_tests/CMakeLists.txt index 92cc837d..d992847b 100644 --- a/tests/src/fsfw_tests/CMakeLists.txt +++ b/tests/src/fsfw_tests/CMakeLists.txt @@ -1,7 +1,3 @@ -if(FSFW_ADD_INTEGRATION_TEST_MODULES) - add_subdirectory(integration) -endif() - if(FSFW_ADD_INTERNAL_TESTS) add_subdirectory(internal) endif() diff --git a/tests/src/fsfw_tests/integration/CMakeLists.txt b/tests/src/fsfw_tests/integration/CMakeLists.txt deleted file mode 100644 index c38caaee..00000000 --- a/tests/src/fsfw_tests/integration/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_subdirectory(devices) -add_subdirectory(controller) -- 2.43.0